diff --git a/.gitignore b/.gitignore index 926bde277..855d4040c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,13 +11,13 @@ docs/docs/.vitepress/temp/ ui/dist/ build/ dist/ -src/streamsync.egg-info/ +src/writer.egg-info/ *:Zone.Identifier *.DS_Store -src/streamsync/static -src/streamsync/app_templates/* -!src/streamsync/app_templates/.gitkeep -src/streamsync/ui.py +src/writer/static +src/writer/app_templates/* +!src/writer/app_templates/.gitkeep +src/writer/ui.py src/ui/src/stories/** src/ui/components.codegen.json playground/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 840c91fe7..e1a4f6ba5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,17 +1,17 @@ -# Contributing to Streamsync +# Contributing to Writer Framework -Thank you for your interest in contributing to Streamsync. +Thank you for your interest in contributing to Writer Framework. ## Ways to contribute -Beyond contributing to the repository, some ways to contribute to this project include: +Beyond contributing to the repository, some ways to contribute to this project include: -- *Reporting bugs*. Bug reports are relatively easy to write, but have a big impact. Please include the steps required to reproduce the bug. Use "Issues" on GitHub. This is an example of a [wonderful bug report](https://github.com/streamsync-cloud/streamsync/issues/24). -- *Creating content*. Think articles or tutorials. It doesn't have to be overwhelmingly positive; constructive criticism is appreciated. A great example is [this review](https://jreyesr.github.io/posts/streamsync-review/). A YouTube tutorial would be fantastic! -- *Browse Issues and Discussions*. Browse these sections on GitHub and see if you can help. -- *Suggesting valuable enhancements*. If you think of a feature that can have a positive impact, suggest it. Please use the "Discussions" on GitHub. -- *Sponsoring the project*. Helps offset hosting and other expenses. -- *Promoting the project*. Star it, share on LinkedIn or other social media. +- _Reporting bugs_. Bug reports are relatively easy to write, but have a big impact. Please include the steps required to reproduce the bug. Use "Issues" on GitHub. This is an example of a [wonderful bug report](https://github.com/streamsync-cloud/streamsync/issues/24). +- _Creating content_. Think articles or tutorials. It doesn't have to be overwhelmingly positive; constructive criticism is appreciated. A great example is [this review](https://jreyesr.github.io/posts/streamsync-review/). A YouTube tutorial would be fantastic! +- _Browse Issues and Discussions_. Browse these sections on GitHub and see if you can help. +- _Suggesting valuable enhancements_. If you think of a feature that can have a positive impact, suggest it. Please use the "Discussions" on GitHub. +- _Sponsoring the project_. Helps offset hosting and other expenses. +- _Promoting the project_. Star it, share on LinkedIn or other social media. ## Contributing to the repository @@ -23,8 +23,9 @@ Pull requests should be done on the `dev` branch. When the release is finalised, ## Setting up a development environment -Whether you're interested in contributing to the repository, creating a fork, or just improving your understanding of Streamsync, these are the suggested steps for setting up a development environment. +Whether you're interested in contributing to the repository, creating a fork, or just improving your understanding of Writer Framework, these are the suggested steps for setting up a development environment. + - You can install the package in editable mode using `poetry install` - Enable the virtual environment with `poetry shell` - Install all the dev dependencies with `alfred install.dev` -- Run streamsync in port 5000. For example, `streamsync edit apps/hello --port 5000`. +- Run Writer Framework on port 5000. For example, `writer edit apps/hello --port 5000`. diff --git a/README.md b/README.md index 06c9edb6c..b87039c47 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ ## What is Streamsync? [![PyPi](https://img.shields.io/pypi/v/streamsync.svg?label=Version)](https://pypi.org/project/streamsync/) -[![CI](https://github.com/streamsync-cloud/streamsync/actions/workflows/ci.yml/badge.svg)](https://github.com/streamsync-cloud/streamsync/actions/workflows/ci.yml) +[![CI](https://github.com/streamsync-cloud/streamsync/actions/workflows/ci.yml/badge.svg)](https://github.com/streamsync-cloud/streamsync/actions/workflows/ci.yml) [![Discord](https://img.shields.io/badge/discord-streamsync-sn677E3Pd3?logo=discord&logoColor=white)](https://discord.gg/sn677E3Pd3) [![License](https://img.shields.io/pypi/l/streamsync)](LICENSE) -Streamsync is an open-source framework for creating data apps. Build user interfaces using a visual editor; write the backend code in Python. +Writer Framework is an open-source framework for creating data apps. Build user interfaces using a visual editor; write the backend code in Python. -![Streamsync Builder screenshot](https://raw.githubusercontent.com/streamsync-cloud/streamsync/master/docs/docs/public/sc1.png "Streamsync Builder screenshot") +![Writer Framework Builder screenshot](https://raw.githubusercontent.com/streamsync-cloud/streamsync/master/docs/docs/public/sc1.png "Writer Framework Builder screenshot") - [Live demo](https://hello.streamsync.cloud/) of an app. [Source code](https://github.com/streamsync-cloud/streamsync/blob/master/apps/hello/main.py). - [1 minute introduction video](https://youtu.be/XBAPBy_zf8s) on YouTube @@ -20,15 +20,15 @@ It aims to be as simple as Streamlit, but faster, more flexible and with a clean ### Reactive and state-driven -Streamsync is **fully state-driven** and provides **separation of concerns** between user interface and business logic. +Writer Framework is **fully state-driven** and provides **separation of concerns** between user interface and business logic. ```py -import streamsync as ss +import writer as wf def handle_increment(state): state["counter"] += 1 -ss.init_state({ +wf.init_state({ "counter": 0 }) ``` @@ -36,18 +36,21 @@ ss.init_state({ The user interface is a template, which is defined visually. The template contains reactive references to state, e.g. `@{counter}`, and references to event handlers, e.g. when _Button_ is clicked, trigger `handle_increment`. ### Flexible + - Elements are highly customisable with no CSS required, allowing for shadows, button icons, background colours, etc. - HTML elements with custom CSS can be included using the _HTML Element_ component. They can serve as containers for built-in components. ### Fast -- Event handling adds minimal overhead to your Python code (~1-2ms*). + +- Event handling adds minimal overhead to your Python code (~1-2ms\*). - Streaming (WebSockets) is used to synchronise frontend and backend states. - The script only runs once. - Non-blocking by default. Events are handled asynchronously in a thread pool running in a dedicated process. -*End-to-end figure, including DOM mutation. Tested locally on a Macbook Air M2. [Measurement methodology](https://medium.com/@ramiromedina/measuring-time-elapsed-between-an-event-and-its-associated-dom-mutation-80431ad576e1). +\*End-to-end figure, including DOM mutation. Tested locally on a Macbook Air M2. [Measurement methodology](https://medium.com/@ramiromedina/measuring-time-elapsed-between-an-event-and-its-associated-dom-mutation-80431ad576e1). ### Developer-friendly + - It's all contained in a standard Python package, just one `pip install` away. - User interfaces are saved as JSON, so they can be version controlled together with the rest of the application. - Use your local code editor and get instant refreshes when you save your code. Alternatively, use the provided web-based editor. @@ -55,17 +58,17 @@ The user interface is a template, which is defined visually. The template contai ## Installation and Quickstart -Getting started with Streamsync is easy. It works on Linux, Mac and Windows. +Getting started with Writer Framework is easy. It works on Linux, Mac and Windows. ```sh pip install "streamsync[ds]" streamsync hello ``` -- The first command will install Streamsync using `pip` and include the optional data science dependencies. -- The second command will create a demo application in the subfolder "hello" and start Streamsync Builder, the framework's visual editor, which will be accessible via a local URL. +- The first command will install Writer Framework using `pip` and include the optional data science dependencies. +- The second command will create a demo application in the subfolder "hello" and start Writer Framework Builder, the framework's visual editor, which will be accessible via a local URL. -The following commands can be used to create, launch Streamsync Builder and run an application. +The following commands can be used to create, launch Writer Framework Builder and run an application. ```sh streamsync create my_app diff --git a/alfred/build.py b/alfred/build.py index efbba5978..b2d04f076 100644 --- a/alfred/build.py +++ b/alfred/build.py @@ -17,11 +17,10 @@ def build(ignore_ci: bool = False): @alfred.command("build.app_provisionning", help="update app templates using ./apps", hidden=True) def build_app_provisionning(): - if os.path.isdir('src/streamsync/app_templates'): - shutil.rmtree('src/streamsync/app_templates') + if os.path.isdir('src/writer/app_templates'): + shutil.rmtree('src/writer/app_templates') - shutil.copytree( 'apps/default', 'src/streamsync/app_templates/default') - shutil.copytree( 'apps/hello', 'src/streamsync/app_templates/hello') + shutil.copytree( 'apps', 'src/writer/app_templates') @alfred.command("build.poetry", help="build python packages with poetry", hidden=True) def build_poetry(): diff --git a/alfred/ci.py b/alfred/ci.py index dc06509a9..87cab2d8d 100644 --- a/alfred/ci.py +++ b/alfred/ci.py @@ -28,9 +28,9 @@ def ci(front, back, e2e, docs): if e2e: alfred.invoke_command("npm.e2e", browser=e2e) -@alfred.command("ci.mypy", help="typing checking with mypy on ./src/streamsync") +@alfred.command("ci.mypy", help="typing checking with mypy on ./src/writer") def ci_mypy(): - alfred.run("mypy ./src/streamsync --exclude app_templates/*") + alfred.run("mypy ./src/writer --exclude app_templates/*") @alfred.command("ci.ruff", help="linting with ruff") def ci_ruff(): @@ -41,24 +41,4 @@ def ci_test(): os.chdir("tests") alfred.run("pytest") -@contextlib.contextmanager -def _preserve_files(path: List[str]): - """ - Preserve files in a temporary directory and restore them after the context - - :param path: list of files to preserve - """ - tmpdir = tempfile.mkdtemp() - try: - for p in path: - os.makedirs(os.path.dirname(os.path.join(tmpdir, p)), exist_ok=True) - shutil.copy(p, os.path.join(tmpdir, p)) - - yield - finally: - for p in path: - shutil.copy(os.path.join(tmpdir, p), p) - - shutil.rmtree(tmpdir) - diff --git a/alfred/publish.py b/alfred/publish.py index cdd912f52..9dcf70d9e 100644 --- a/alfred/publish.py +++ b/alfred/publish.py @@ -22,8 +22,8 @@ def publish(): >>> $ alfred publish """ - import streamsync - VERSION = f"v{streamsync.VERSION}" + import writer + VERSION = f"v{writer.VERSION}" git = alfred.sh("git", "git should be present") os.chdir(ROOT_DIR) diff --git a/apps/default/main.py b/apps/default/main.py index abfb8734b..9e48fb9c7 100644 --- a/apps/default/main.py +++ b/apps/default/main.py @@ -1,4 +1,4 @@ -import streamsync as ss +import writer as wf # This is a placeholder to get you started or refresh your memory. # Delete it or adapt it as necessary. @@ -28,7 +28,7 @@ def increment(state): # "_my_private_element" won't be serialised or sent to the frontend, # because it starts with an underscore -initial_state = ss.init_state({ +initial_state = wf.init_state({ "my_app": { "title": "MY APP" }, diff --git a/apps/default/ui.json b/apps/default/ui.json index 611a89d89..9e210de40 100644 --- a/apps/default/ui.json +++ b/apps/default/ui.json @@ -1,6 +1,6 @@ { "metadata": { - "streamsync_version": "0.5.0" + "writer_version": "0.5.0" }, "components": { "root": { diff --git a/apps/hello/Dockerfile b/apps/hello/Dockerfile index f63e29a7e..913b5e8c6 100644 --- a/apps/hello/Dockerfile +++ b/apps/hello/Dockerfile @@ -3,7 +3,7 @@ RUN apt-get update -y && mkdir /app && python3 -m venv /app/venv ENV PATH="/app/venv/bin:$PATH" COPY . /app WORKDIR /app -RUN pip3 install 'streamsync[ds]' && pip3 install -r requirements.txt +RUN pip3 install 'writer' && pip3 install -r requirements.txt FROM python:3.10-slim AS run-image RUN apt-get update -y && mkdir /app @@ -11,6 +11,6 @@ COPY --from=compile-image /app /app ENV PATH="/app/venv/bin:$PATH" WORKDIR /app -ENTRYPOINT [ "streamsync", "run" ] +ENTRYPOINT [ "writer", "run" ] EXPOSE 5000 CMD [ ".", "--port", "5000", "--host", "0.0.0.0" ] diff --git a/apps/hello/main.py b/apps/hello/main.py index 5fae9ada3..fb11ecac0 100644 --- a/apps/hello/main.py +++ b/apps/hello/main.py @@ -3,12 +3,12 @@ import numpy as np import pandas as pd import plotly.express as px -import streamsync as ss -from streamsync.core import StreamsyncState +import writer as wf +from writer.core import WriterState # EVENT HANDLERS -def handle_timer_tick(state: StreamsyncState): +def handle_timer_tick(state: WriterState): df = state["random_df"] for i in range(5): df[f'pgcf_{i+1}'] = np.around(np.random.rand(10, 1), decimals=9) @@ -30,7 +30,7 @@ def update(state, session): def handle_story_download(state): - data = ss.pack_file("assets/story.txt", "text/plain") + data = wf.pack_file("assets/story.txt", "text/plain") file_name = "hacker_pigeons.txt" state.file_download(data, file_name) @@ -129,7 +129,7 @@ def _update_scatter_chart(state): # STATE INIT -initial_state = ss.init_state({ +initial_state = wf.init_state({ "main_df": _get_main_df(), "highlighted_members": _get_highlighted_members(), "random_df": _generate_random_df(), diff --git a/apps/hello/ui.json b/apps/hello/ui.json index 7c252a569..42858f31c 100644 --- a/apps/hello/ui.json +++ b/apps/hello/ui.json @@ -1,6 +1,6 @@ { "metadata": { - "streamsync_version": "0.5.0" + "writer_version": "0.5.0" }, "components": { "root": { @@ -170,10 +170,10 @@ "position": 1, "parentId": "7e625201-20c2-4b05-951c-d825de28b216", "handlers": { - "ss-number-change": "update" + "wf-number-change": "update" }, "binding": { - "eventType": "ss-number-change", + "eventType": "wf-number-change", "stateRef": "filter.min_weight" } }, @@ -190,10 +190,10 @@ "position": 0, "parentId": "7e625201-20c2-4b05-951c-d825de28b216", "handlers": { - "ss-number-change": "update" + "wf-number-change": "update" }, "binding": { - "eventType": "ss-number-change", + "eventType": "wf-number-change", "stateRef": "filter.min_length" } }, @@ -363,7 +363,7 @@ "id": "5da5e007-d60a-4313-9d21-885deae7b37d", "type": "text", "content": { - "text": "You can put other Streamsync components inside HTML Elements." + "text": "You can put other Writer Framework components inside HTML Elements." }, "isCodeManaged": false, "position": 1, @@ -413,7 +413,7 @@ "position": 0, "parentId": "09ddb2da-6fa3-4157-8da3-4d5d44a6a58d", "handlers": { - "ss-tick": "handle_timer_tick" + "wf-tick": "handle_timer_tick" } }, "e296866a-75d2-4677-b55d-3c1456113b89": { @@ -475,7 +475,7 @@ "position": 2, "parentId": "b1ee642e-f2e7-453b-a6ef-3d96eea37140", "binding": { - "eventType": "ss-number-change", + "eventType": "wf-number-change", "stateRef": "hue_rotation" } }, @@ -950,7 +950,7 @@ "id": "77cb256b-ef12-4a55-a051-500497f41302", "type": "text", "content": { - "text": "It's easy to switch between pages and it can be done from the frontend (via Streamsync Builder) and from the backend (via Python)." + "text": "It's easy to switch between pages and it can be done from the frontend (via Writer Framework Builder) and from the backend (via Python)." }, "isCodeManaged": false, "position": 2, @@ -1039,8 +1039,8 @@ "position": 1, "parentId": "e1ax8ctt8lrao0e4", "handlers": { - "ss-change-page": "handle_paginated_members_page_change", - "ss-change-page-size": "handle_paginated_members_page_size_change" + "wf-change-page": "handle_paginated_members_page_change", + "wf-change-page-size": "handle_paginated_members_page_size_change" }, "visible": true }, diff --git a/apps/quickstart/main.py b/apps/quickstart/main.py index bbeff594d..c73e9cf1b 100644 --- a/apps/quickstart/main.py +++ b/apps/quickstart/main.py @@ -1,5 +1,5 @@ import plotly.graph_objects as go -import streamsync as ss +import writer as wf from sklearn.datasets import make_blobs from sklearn.linear_model import LogisticRegression @@ -98,7 +98,7 @@ def update(state): state['figure'] = fig -initial_state = ss.init_state({ +initial_state = wf.init_state({ "my_app": { "title": "Logistic regression visualizer" }, diff --git a/apps/quickstart/ui.json b/apps/quickstart/ui.json index 6843cdb3f..efe6ca0b4 100644 --- a/apps/quickstart/ui.json +++ b/apps/quickstart/ui.json @@ -1,6 +1,6 @@ { "metadata": { - "streamsync_version": "0.3.0" + "writer_version": "0.3.0" }, "components": { "root": { @@ -75,10 +75,10 @@ "parentId": "t3zfxwztd4cozu6v", "position": 0, "handlers": { - "ss-number-change": "update" + "wf-number-change": "update" }, "binding": { - "eventType": "ss-number-change", + "eventType": "wf-number-change", "stateRef": "number_of_groups" }, "visible": true @@ -106,10 +106,10 @@ "parentId": "t3zfxwztd4cozu6v", "position": 1, "handlers": { - "ss-number-change": "update" + "wf-number-change": "update" }, "binding": { - "eventType": "ss-number-change", + "eventType": "wf-number-change", "stateRef": "number_of_points" }, "visible": true @@ -136,10 +136,10 @@ "parentId": "t3zfxwztd4cozu6v", "position": 3, "handlers": { - "ss-option-change": "update" + "wf-option-change": "update" }, "binding": { - "eventType": "ss-option-change", + "eventType": "wf-option-change", "stateRef": "multi_class" }, "visible": true @@ -156,10 +156,10 @@ "parentId": "t3zfxwztd4cozu6v", "position": 2, "handlers": { - "ss-number-change": "update" + "wf-number-change": "update" }, "binding": { - "eventType": "ss-number-change", + "eventType": "wf-number-change", "stateRef": "cluster_std" }, "visible": true @@ -173,7 +173,7 @@ "parentId": "t3zfxwztd4cozu6v", "position": 4, "handlers": { - "ss-click": "update" + "wf-click": "update" }, "visible": true }, diff --git a/docs/codegen/generator_components_pages.mjs b/docs/codegen/generator_components_pages.mjs index 1d42cef6d..9f1e3446d 100644 --- a/docs/codegen/generator_components_pages.mjs +++ b/docs/codegen/generator_components_pages.mjs @@ -4,7 +4,7 @@ */ /* eslint-disable @typescript-eslint/no-var-requires */ -import components from "streamsync-ui/components.codegen.json" with { type: "json" }; +import components from "writer-ui/components.codegen.json" with { type: "json" }; import * as fs from "fs"; import * as path from "path"; import { fileURLToPath } from "url"; diff --git a/docs/docs/.vitepress/config.ts b/docs/docs/.vitepress/config.ts index 2e3b2eca8..09ae35aa8 100644 --- a/docs/docs/.vitepress/config.ts +++ b/docs/docs/.vitepress/config.ts @@ -74,6 +74,7 @@ export default { {text: "Custom server", link: "/custom-server"}, {text: "State schema", link: "/state-schema"}, {text: "Backend-driven UI", link: "/backend-driven-ui"}, + {text: "Authentication", link: "/authentication"}, ], }, { diff --git a/docs/docs/ai-module.md b/docs/docs/ai-module.md new file mode 100644 index 000000000..ed3693867 --- /dev/null +++ b/docs/docs/ai-module.md @@ -0,0 +1,97 @@ +# AI module +This module leverages the Writer SDK to enable applications to interact with large language models (LLMs) in chat or text completion formats. It provides tools to manage conversation states and to dynamically interact with LLMs using both synchronous and asynchronous methods. + +## `Conversation` class +The Conversation class manages LLM communications within a chat framework, storing the conversation history and handling the interactions. + +```python +import streamsync as ss +import streamsync.ai + +def handle_simple_message(state, payload): + state["conversation"] += payload # Adding user's message to the conversation + for chunk in state["conversation"].stream_complete(): + # Streaming model's reply + state["conversation"] += chunk + +initial_state = ss.init_state({ + "conversation": streamsync.ai.Conversation(), +}) +``` + +### Initializing a `Conversation` +A `Conversation` can be initialized with either a system prompt or a list of previous messages. It can also accept a default configuration dictionary that sets parameters for all interactions. + + +```python +# Initialize with a system prompt +conversation = Conversation(system_prompt="You assist Chinese-speaking clients with their questions") + +# Initialize with a history of messages +history = [ + {"role": "user", "content": "Hello"}, + {"role": "assistant", "content": "Hi, how can I help?"} +] +conversation = Conversation(history_import=history) + +# Initialize with a configuration +config = {'max_tokens': 150, 'temperature': 0.7} +conversation = Conversation(system_prompt="You are a social media expert in the financial industry", config=config) + +``` + +### Adding messages to `Conversation` +Messages can be added to a `Conversation` instance using the `+` operator or the `add` method. + +```python +# Using the `+` operator +conversation += {"role": "user", "content": "What's the weather like?"} + +# Using the `add` method +conversation.add(role="user", content="What's the weather like?") + +``` + +Addition to `Conversation` only works against `dict` objects that contain `"role"` and `"content"` items. Providing a `"chunk": True` flag into the object will merge it against the last message – appending `"content"` and replacing other values. + +### Completing and Streaming Conversations + +The `complete` and `stream_complete` methods facilitate interaction with the LLM based on the accumulated messages and configuration. These methods execute calls to generate responses and return them in form of a message object, but do not alter the conversation's `messages` list, allowing you to validate or modify the output before deciding to add it to the history. + +```python +# Using `complete` to get a single response +response = conversation.complete() +print("LLM Response:", response) + +# Using `stream_complete` to get streamed responses +for chunk in conversation.stream_complete(): + print("Streamed Message:", chunk) + # Manually adding to the conversation + conversation += chunk +``` + +:::tip First chunk of `stream_complete` is not flagged +When using `stream_complete`, the first chunk returned by the stream is not flagged explicitly as a "chunk." This behavior is by design, to seamlessly add this initial chunk into the conversation history if it is appended using the `+` operator. +::: + +Instance-wide configuration parameters can be complemented or overriden on individual call's level, if a `data` dictionary is provided to the method: + +```python +# Overriding configuration for a specific call +response = conversation.complete(data={'max_tokens': 200, 'temperature': 0.5}) +``` + +# `complete` and `stream_complete` methods +These functions are designed for one-off text completions without the need to manage a conversation state. They return model's response as a string. + +```python +# Using `complete` for a single completion +text_response = complete("Explore the benefits of AI.", data={'temperature': 0.3}) +print("Completion:", text_response) + +# Using `stream_complete` for streamed text completions +for text_chunk in stream_complete("Explore the benefits of AI.", data={'temperature': 0.3}): + print("Streamed Text:", text_chunk) +``` + +Each function accepts a `data` dictionary allowing call-specific configurations. \ No newline at end of file diff --git a/docs/docs/authentication.md b/docs/docs/authentication.md new file mode 100644 index 000000000..3e543c23e --- /dev/null +++ b/docs/docs/authentication.md @@ -0,0 +1,214 @@ +# Authentication + +The StreamSync authentication module allows you to restrict access to your application. + +Streamsync will be able to authenticate a user through an identity provider such as Google, Microsoft, Facebook, Github, Auth0, etc. + +::: warning Authentication is done before accessing the application +Authentication is done before accessing the application. It is not possible to trigger authentication for certain pages exclusively. +::: + +## Use OIDC provider + +Authentication configuration is done in [the `server_setup.py` module](custom-server.md). The configuration depends on your identity provider. +Here is an example configuration for Google. + + + +*server_setup.py* +```python +import os +import streamsync.serve +import streamsync.auth + +oidc = streamsync.auth.Oidc( + client_id="1xxxxxxxxx-qxxxxxxxxxxxxxxx.apps.googleusercontent.com", + client_secret="GOxxxx-xxxxxxxxxxxxxxxxxxxxx", + host_url=os.getenv('HOST_URL', "http://localhost:5000"), + url_authorize="https://accounts.google.com/o/oauth2/auth", + url_oauthtoken="https://oauth2.googleapis.com/token", + url_userinfo='https://www.googleapis.com/oauth2/v1/userinfo?alt=json' +) + +streamsync.serve.register_auth(oidc) +``` +### Use pre-configured OIDC + +StreamSync provides pre-configured OIDC providers. You can use them directly in your application. + +| | Provider | Function | Description | +|------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|----------|-------------------------------------------------------------------------------------------------| +| | Google | `streamsync.auth.Google` | Allow your users to login with their Google Account | +| | Github | `streamsync.auth.Github` | Allow your users to login with their Github Account | +| | Auth0 | `streamsync.auth.Auth0` | Allow your users to login with different providers or with login password through Auth0 | + + +#### Google + +You have to register your application into [Google Cloud Console](https://console.cloud.google.com/). + +*server_setup.py* +```python +import os +import streamsync.serve +import streamsync.auth + +oidc = streamsync.auth.Google( + client_id="1xxxxxxxxx-qxxxxxxxxxxxxxxx.apps.googleusercontent.com", + client_secret="GOxxxx-xxxxxxxxxxxxxxxxxxxxx", + host_url=os.getenv('HOST_URL', "http://localhost:5000") +) + +streamsync.serve.register_auth(oidc) +``` + +#### Github + +You have to register your application into [Github](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app#registering-a-github-app) + +*server_setup.py* +```python +import os +import streamsync.serve +import streamsync.auth + +oidc = streamsync.auth.Github( + client_id="xxxxxxx", + client_secret="xxxxxxxxxxxxx", + host_url=os.getenv('HOST_URL', "http://localhost:5000") +) + +streamsync.serve.register_auth(oidc) +``` + +#### Auth0 + +You have to register your application into [Auth0](https://auth0.com/). + +*server_setup.py* +```python +import os +import streamsync.serve +import streamsync.auth + +oidc = streamsync.auth.Auth0( + client_id="xxxxxxx", + client_secret="xxxxxxxxxxxxx", + domain="xxx-xxxxx.eu.auth0.com", + host_url=os.getenv('HOST_URL', "http://localhost:5000") +) + +streamsync.serve.register_auth(oidc) +``` + +### Authentication workflow + + + +## User information in event handler + +When the `user_info` route is configured, user information will be accessible +in the event handler through the `session` argument. + +```python +def on_page_load(state, session): + email = session['userinfo'].get('email', None) + state['email'] = email +``` + + +## Unauthorize access + +It is possible to reject a user who, for example, does not have the correct email address. + +::: tip you can also use userinfo inside app +You can restrict access to certain pages inside the application by using the `session` object. +See [User information in event handler](#user-information-in-event-handler) +::: + +```python +from fastapi import Request + +import streamsync.serve +import streamsync.auth + +oidc = ... + +def callback(request: Request, session_id: str, userinfo: dict): + if userinfo['email'] not in ['nom.prenom123@example.com']: + raise streamsync.auth.Unauthorized(more_info="You can contact the administrator at support.example.com") + +streamsync.serve.register_auth(oidc, callback=callback) +``` + +The default authentication error page look like this: + + + +*streamsync.auth.Unauthorized* + +| Parameter | Description | +|-----------|-------------| +| status_code | HTTP status code | +| message | Error message | +| more_info | Additional information | + +## Modify user info + +User info can be modified in the callback. + +```python +from fastapi import Request + +import streamsync.serve +import streamsync.auth + +oidc = ... + +def callback(request: Request, session_id: str, userinfo: dict): + userinfo['group'] = [] + if userinfo['email'] in ['fabien@example.com']: + userinfo['group'].append('admin') + userinfo['group'].append('user') + else: + userinfo['group'].append('user') + +streamsync.serve.register_auth(oidc, callback=callback) +``` +from fastapi import Request + +## Custom unauthorized page + +You can customize the access denial page using your own template. + +```python +import os + +from fastapi import Request, Response +from fastapi.templating import Jinja2Templates + +import streamsync.serve +import streamsync.auth + +oidc = ... + +def unauthorized(request: Request, exc: streamsync.auth.Unauthorized) -> Response: + templates = Jinja2Templates(directory=os.path.join(os.path.dirname(__file__), "templates")) + return templates.TemplateResponse(request=request, name="unauthorized.html", status_code=exc.status_code, context={ + "status_code": exc.status_code, + "message": exc.message, + "more_info": exc.more_info + }) + +streamsync.serve.register_auth(oidc, unauthorized_action=unauthorized) +``` + +## Enable in edit mode + +Authentication is disabled in edit mode. To activate it, +you must trigger the loading of the server_setup module in edition mode. + +```bash +streamsync edit --enable-server-setup +``` + diff --git a/docs/docs/components/component-list.md b/docs/docs/components/component-list.md index 5b78e7052..81674ca3b 100644 --- a/docs/docs/components/component-list.md +++ b/docs/docs/components/component-list.md @@ -3,7 +3,7 @@ outline: [2, 2] --- diff --git a/docs/docs/core.ts b/docs/docs/core.ts index 9e8bca173..23c1e6e55 100644 --- a/docs/docs/core.ts +++ b/docs/docs/core.ts @@ -1,7 +1,7 @@ import hljs from 'highlight.js/lib/core'; import python from 'highlight.js/lib/languages/python'; import { marked } from 'marked'; -import components from "streamsync-ui/components.codegen.json" assert { type: "json" }; +import components from "writer-ui/components.codegen.json" assert { type: "json" }; // @ts-ignore hljs.registerLanguage('python', python); diff --git a/docs/docs/custom-server.md b/docs/docs/custom-server.md index a5f7549fa..d6c4bd102 100644 --- a/docs/docs/custom-server.md +++ b/docs/docs/custom-server.md @@ -2,7 +2,37 @@ Streamsync uses Uvicorn and serves the app in the root path i.e. `/`. If you need to use another ASGI-compatible server or fine-tune Uvicorn, you can easily do so. -## Getting the ASGI app +## Configure webserver + +You can tune your server by adding a `server_setup.py` file to the root +of your application, next to the `main.py` and `ui.json` files. + +This file is executed before starting streamsync. It allows you to configure [authentication](./authentication.md), +add your own routes and middlewares on FastAPI. + +```python +# server_setup.py +import typing + +import streamsync.serve + +if typing.TYPE_CHECKING: + from fastapi import FastAPI + +# Returns the FastAPI application associated with the streamsync server. +asgi_app: FastAPI = streamsync.serve.app + +@asgi_app.get("/probes/healthcheck") +def hello(): + return "1" +``` + +::: warning Use `server_setup.py` in `edit` mode +If you want to use in `edit` mode, +you can launch `streamsync edit --enable-server-setup `. +::: + +## Implement custom server You can import `streamsync.serve` and use the function `get_asgi_app`. This returns an ASGI app created by FastAPI, which you can choose how to serve. @@ -28,6 +58,12 @@ Note the inclusion of the imported `ws_max_size` setting. This is important for Fine-tuning Uvicorn allows you to set up SSL, configure proxy headers, etc, which can prove vital in complex deployments. +::: tip Use server setup hook +```python +asgi_app = streamsync.serve.get_asgi_app(app_path, mode, enable_server_setup=True) +``` +::: + ## Multiple apps at once Streamsync is built using relative paths, so it can be served from any path. This allows multiple apps to be simultaneously served on different paths. diff --git a/docs/docs/images/auth_auth0_fill.png b/docs/docs/images/auth_auth0_fill.png new file mode 100644 index 000000000..a99088db5 Binary files /dev/null and b/docs/docs/images/auth_auth0_fill.png differ diff --git a/docs/docs/images/auth_github_fill.png b/docs/docs/images/auth_github_fill.png new file mode 100644 index 000000000..7b7a6fe16 Binary files /dev/null and b/docs/docs/images/auth_github_fill.png differ diff --git a/docs/docs/images/auth_google_fill.png b/docs/docs/images/auth_google_fill.png new file mode 100644 index 000000000..e1d897aa0 Binary files /dev/null and b/docs/docs/images/auth_google_fill.png differ diff --git a/docs/docs/images/auth_microsoft_fill.png b/docs/docs/images/auth_microsoft_fill.png new file mode 100644 index 000000000..ed9750efd Binary files /dev/null and b/docs/docs/images/auth_microsoft_fill.png differ diff --git a/docs/docs/images/auth_unauthorized_default.png b/docs/docs/images/auth_unauthorized_default.png new file mode 100644 index 000000000..280ebeb6c Binary files /dev/null and b/docs/docs/images/auth_unauthorized_default.png differ diff --git a/docs/docs/images/authentication_oidc.png b/docs/docs/images/authentication_oidc.png new file mode 100644 index 000000000..ad3982978 Binary files /dev/null and b/docs/docs/images/authentication_oidc.png differ diff --git a/docs/docs/images/authentication_oidc_principle.png b/docs/docs/images/authentication_oidc_principle.png new file mode 100644 index 000000000..5e28c9f5c Binary files /dev/null and b/docs/docs/images/authentication_oidc_principle.png differ diff --git a/docs/docs/images/quickstart/img.png b/docs/docs/images/quickstart/img.png new file mode 100644 index 000000000..e565d03db Binary files /dev/null and b/docs/docs/images/quickstart/img.png differ diff --git a/docs/package.json b/docs/package.json index ef1d13bca..785c83b09 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,5 +1,5 @@ { - "name": "streamsync-docs", + "name": "writer-docs", "version": "0.2.4", "description": "", "main": "index.js", @@ -15,7 +15,7 @@ "highlight.js": "^11.9.0", "marked": "^12.0.1", "monaco-editor": "^0.47.0", - "streamsync-ui": "*", + "writer-ui": "*", "vite-plugin-monaco-editor": "^1.1.0", "vitepress": "^1.0.2", "vue": "^3.4.21" diff --git a/docs/tests/componentImages.spec.js b/docs/tests/componentImages.spec.js index 4156f2d2d..902850bad 100644 --- a/docs/tests/componentImages.spec.js +++ b/docs/tests/componentImages.spec.js @@ -1,5 +1,5 @@ const fs = require('fs'); -const components = require('streamsync-ui/components.codegen.json'); +const components = require('writer-ui/components.codegen.json'); describe('Components docs', () => { for (const component of components) { diff --git a/package-lock.json b/package-lock.json index f700ffc9f..d25a14a51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "Streamsync", + "name": "writer", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "Streamsync", + "name": "writer", "version": "1.0.0", "workspaces": [ "src/ui", @@ -18,16 +18,16 @@ } }, "docs": { - "name": "streamsync-docs", + "name": "writer-docs", "version": "0.2.4", "dependencies": { "highlight.js": "^11.9.0", "marked": "^12.0.1", "monaco-editor": "^0.47.0", - "streamsync-ui": "*", "vite-plugin-monaco-editor": "^1.1.0", "vitepress": "^1.0.2", - "vue": "^3.4.21" + "vue": "^3.4.21", + "writer-ui": "*" }, "devDependencies": { "jest": "29.7.0" @@ -15624,18 +15624,6 @@ "dev": true, "license": "MIT" }, - "node_modules/streamsync-docs": { - "resolved": "docs", - "link": true - }, - "node_modules/streamsync-e2e": { - "resolved": "tests/e2e", - "link": true - }, - "node_modules/streamsync-ui": { - "resolved": "src/ui", - "link": true - }, "node_modules/string_decoder": { "version": "1.3.0", "devOptional": true, @@ -17575,6 +17563,18 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/writer-docs": { + "resolved": "docs", + "link": true + }, + "node_modules/writer-e2e": { + "resolved": "tests/e2e", + "link": true + }, + "node_modules/writer-ui": { + "resolved": "src/ui", + "link": true + }, "node_modules/ws": { "version": "8.16.0", "dev": true, @@ -17657,7 +17657,7 @@ } }, "src/ui": { - "name": "streamsync-ui", + "name": "writer-ui", "version": "0.0.0", "dependencies": { "@apache-arrow/ts": "^15.0.2", @@ -17705,12 +17705,12 @@ } }, "tests/e2e": { - "name": "streamsync-e2e", + "name": "writer-e2e", "version": "1.0.0", "dependencies": { "express": "4.19.2", "http-proxy": "1.18.1", - "streamsync-ui": "*" + "writer-ui": "*" }, "devDependencies": { "@playwright/test": "1.42.1", diff --git a/package.json b/package.json index 25b82282a..7925299d4 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "Streamsync", + "name": "writer", "version": "1.0.0", "description": "", "workspaces": [ @@ -15,38 +15,38 @@ "build": "npm run ui:build && npm run apps:build && npm run ui:codegen && npm run docs:codegen && npm run docs:build", "test": "npm run --if-present -ws test", "lint": "npm run --if-present -ws lint", - "dev": "npm run -w streamsync-ui dev", - "storybook": "npm run -w streamsync-ui storybook", - "storybook.build": "npm run -w streamsync-ui storybook.build", - "custom.dev": "npm run -w streamsync-ui dev", + "dev": "npm run -w writer-ui dev", + "storybook": "npm run -w writer-ui storybook", + "storybook.build": "npm run -w writer-ui storybook.build", + "custom.dev": "npm run -w writer-ui dev", "cli:test": "pytest tests -o log_cli=true ", - "cli:lint": "mypy ./src/streamsync --exclude app_templates/* && ruff check", + "cli:lint": "mypy ./src/writer --exclude app_templates/* && ruff check", "cli:build": "npm run ui:codegen", - "ui:codegen": "npm run -w streamsync-ui codegen", - "ui:dev": "npm run -w streamsync-ui dev", - "ui:build": "npm run -w streamsync-ui build", - "ui:preview": "npm run -w streamsync-ui preview", - "ui:custom.build": "npm run -w streamsync-ui custom.build", - "ui:lint": "npm run -w streamsync-ui lint", - "ui:lint:ci": "npm run -w streamsync-ui lint:ci", + "ui:codegen": "npm run -w writer-ui codegen", + "ui:dev": "npm run -w writer-ui dev", + "ui:build": "npm run -w writer-ui build", + "ui:preview": "npm run -w writer-ui preview", + "ui:custom.build": "npm run -w writer-ui custom.build", + "ui:lint": "npm run -w writer-ui lint", + "ui:lint:ci": "npm run -w writer-ui lint:ci", - "docs:codegen": "npm run -w streamsync-docs codegen", - "docs:dev": "npm run -w streamsync-docs dev", - "docs:build": "npm run -w streamsync-docs build", - "docs:preview": "npm run -w streamsync-docs preview", - "docs:test": "npm run -w streamsync-docs test", + "docs:codegen": "npm run -w writer-docs codegen", + "docs:dev": "npm run -w writer-docs dev", + "docs:build": "npm run -w writer-docs build", + "docs:preview": "npm run -w writer-docs preview", + "docs:test": "npm run -w writer-docs test", - "e2e": "npm run -w streamsync-e2e e2e", - "e2e:setup": "npm run -w streamsync-e2e e2e:setup", - "e2e:ui": "npm run -w streamsync-e2e e2e:ui", - "e2e:ci": "npm run -w streamsync-e2e e2e:ci", - "e2e:firefox": "npm run -w streamsync-e2e e2e:firefox", - "e2e:chromium": "npm run -w streamsync-e2e e2e:chromium", - "e2e:webkit": "npm run -w streamsync-e2e e2e:webkit", + "e2e": "npm run -w writer-e2e e2e", + "e2e:setup": "npm run -w writer-e2e e2e:setup", + "e2e:ui": "npm run -w writer-e2e e2e:ui", + "e2e:ci": "npm run -w writer-e2e e2e:ci", + "e2e:firefox": "npm run -w writer-e2e e2e:firefox", + "e2e:chromium": "npm run -w writer-e2e e2e:chromium", + "e2e:webkit": "npm run -w writer-e2e e2e:webkit", - "apps:build": "cp -R ./apps/hello ./src/streamsync/app_templates/ && cp -R ./apps/default ./src/streamsync/app_templates/", + "apps:build": "cp -R ./apps/hello ./src/writer/app_templates/ && cp -R ./apps/default ./src/writer/app_templates/", "codegen": "npm run ui:codegen && npm run docs:codegen" } } diff --git a/poetry.lock b/poetry.lock index e18bd42bb..cec3a9586 100644 --- a/poetry.lock +++ b/poetry.lock @@ -94,6 +94,20 @@ tests = ["attrs[tests-no-zope]", "zope-interface"] tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] +[[package]] +name = "authlib" +version = "1.3.0" +description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Authlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:9637e4de1fb498310a56900b3e2043a206b03cb11c05422014b0302cbc814be3"}, + {file = "Authlib-1.3.0.tar.gz", hash = "sha256:959ea62a5b7b5123c5059758296122b57cd2585ae2ed1c0622c21b371ffdae06"}, +] + +[package.dependencies] +cryptography = "*" + [[package]] name = "certifi" version = "2024.2.2" @@ -105,6 +119,169 @@ files = [ {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + [[package]] name = "click" version = "8.1.7" @@ -130,6 +307,60 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "cryptography" +version = "42.0.7" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477"}, + {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7"}, + {file = "cryptography-42.0.7-cp37-abi3-win32.whl", hash = "sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b"}, + {file = "cryptography-42.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678"}, + {file = "cryptography-42.0.7-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886"}, + {file = "cryptography-42.0.7-cp39-abi3-win32.whl", hash = "sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda"}, + {file = "cryptography-42.0.7-cp39-abi3-win_amd64.whl", hash = "sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68"}, + {file = "cryptography-42.0.7.tar.gz", hash = "sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + [[package]] name = "distro" version = "1.9.0" @@ -904,6 +1135,17 @@ files = [ [package.dependencies] numpy = ">=1.16.6,<2" +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + [[package]] name = "pydantic" version = "2.7.1" @@ -1196,6 +1438,27 @@ files = [ attrs = ">=22.2.0" rpds-py = ">=0.7.0" +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + [[package]] name = "rich" version = "13.7.1" @@ -1584,6 +1847,23 @@ files = [ {file = "ujson-5.10.0.tar.gz", hash = "sha256:b3cd8f3c5d8c7738257f1018880444f7b7d9b66232c64649f562d7ba86ad4bc1"}, ] +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + [[package]] name = "uvicorn" version = "0.29.0" @@ -1874,13 +2154,13 @@ files = [ [[package]] name = "writer-sdk" -version = "0.1.0a1" +version = "0.1.0a2" description = "The official Python library for the writer API" optional = false python-versions = ">=3.7" files = [ - {file = "writer_sdk-0.1.0a1-py3-none-any.whl", hash = "sha256:bba646d3eff598a08f756537bb45ad5b1d2e2deb36b719b1d9141fc186e9478e"}, - {file = "writer_sdk-0.1.0a1.tar.gz", hash = "sha256:a1a12dede41817016192e83bae674254b00de0c7fef853f722baaaf29dc12649"}, + {file = "writer_sdk-0.1.0a2-py3-none-any.whl", hash = "sha256:ffbfcc51aa03b36445e19e4d59632cb2f286eb812517aa5cc485b6639279c410"}, + {file = "writer_sdk-0.1.0a2.tar.gz", hash = "sha256:2ee574325c561f8fe02633d208d176571e258786626dae2bdb0cb2df1fe48793"}, ] [package.dependencies] @@ -1891,10 +2171,7 @@ pydantic = ">=1.9.0,<3" sniffio = "*" typing-extensions = ">=4.7,<5" -[extras] -ds = ["pandas", "plotly", "pyarrow"] - [metadata] lock-version = "2.0" python-versions = ">=3.9.2, <4.0" -content-hash = "7e07bc698f2f4f308882092e1af307007d0948c3cdd502d698493481353a3184" +content-hash = "b98581abdb0e0272d272ae876ae13efa28878ef7f03a4cdc57f7bf8d70eca1d3" diff --git a/pyproject.toml b/pyproject.toml index 4f6d67bbd..c25878303 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,13 +3,12 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.poetry] -name = "streamsync" -version = "0.6.0rc2" -description = "Streamsync helps you create performant data apps, via Python code and its built-in visual UI editor." -authors = ["Ramiro Medina "] -license = "Apache 2.0" +name = "writer" +version = "0.6.0rc3" +description = "Writer Framework helps you create performant data apps, via Python code and its built-in visual UI editor." +authors = ["Writer, Inc. "] readme = "README.md" -homepage = "https://www.streamsync.cloud" +homepage = "https://www.writer.com" repository = "https://www.github.com/streamsync-cloud/streamsync" documentation = "https://www.streamsync.cloud/getting-started.html" keywords = ["data apps", "gui", "ui"] @@ -21,12 +20,13 @@ classifiers = [ "Development Status :: 4 - Beta" ] packages = [ - { include = "streamsync", from = "src" } + { include = "writer", from = "src" } ] include = [ - "src/streamsync/*.py", - "src/streamsync/static/**/*", - "src/streamsync/app_templates/**/*" + "src/writer/*.py", + "src/writer/static/**/*", + "src/writer/templates/**/*", + "src/writer/app_templates/**/*" ] [tool.poetry.dependencies] @@ -36,10 +36,13 @@ fastapi = ">= 0.89.1, < 1" websockets = ">= 12, < 13" uvicorn = ">= 0.20.0, < 1" watchdog = ">= 3.0.0, < 4" -writer-sdk = "0.1.0a1" -pandas = {version = ">= 2.2.0, < 3", optional = true} -pyarrow = {version = ">= 15.0.0, < 16.0.0",optional = true} -plotly = {version = ">= 5.18.0, < 6", optional = true} +writer-sdk = "0.1.0a2" +pandas = ">= 2.2.0, < 3" +pyarrow = ">= 15.0.0, < 16.0.0" +plotly = ">= 5.18.0, < 6" +authlib = "^1.3.0" +requests = "^2.31.0" +jinja2 = "^3.1.4" [tool.poetry.group.build] @@ -59,17 +62,14 @@ alfred-cli = "^2.2.7" polars = "^0.20.15" ruff = "^0.3.4" -[tool.poetry.extras] -ds = ["pandas", "pyarrow", "plotly"] - [tool.poetry.scripts] -streamsync = 'streamsync.command_line:main' +writer = 'writer.command_line:main' [tool.ruff] exclude = [ "src/ui", - "src/streamsync/ui.py", + "src/writer/ui.py", "docs", "tests/e2e", ".git", diff --git a/src/ui/package.json b/src/ui/package.json index 49918dc4a..04cf954d5 100644 --- a/src/ui/package.json +++ b/src/ui/package.json @@ -1,5 +1,5 @@ { - "name": "streamsync-ui", + "name": "writer-ui", "version": "0.0.0", "type": "module", "scripts": { diff --git a/src/ui/src/ambientTypes.ts b/src/ui/src/ambientTypes.ts index 12449a20a..f5f09f230 100644 --- a/src/ui/src/ambientTypes.ts +++ b/src/ui/src/ambientTypes.ts @@ -1,9 +1,9 @@ -import { StreamsyncComponentDefinition } from "./streamsyncTypes"; +import { WriterComponentDefinition } from "./writerTypes"; declare module "marked"; declare module "vue" { interface ComponentCustomOptions { - streamsync?: StreamsyncComponentDefinition; + writer?: WriterComponentDefinition; } } diff --git a/src/ui/src/builder/BuilderApp.vue b/src/ui/src/builder/BuilderApp.vue index 68bc65da7..4d6fb34c8 100644 --- a/src/ui/src/builder/BuilderApp.vue +++ b/src/ui/src/builder/BuilderApp.vue @@ -79,7 +79,7 @@ :prevent-settings-bar-overlap="true" :instance-path="selectedInstancePath" :vertical-offset-pixels="-48" - data-streamsync-cage + data-writer-cage @dragstart="handleRendererDragStart" @dragend="handleRendererDragEnd" > @@ -106,8 +106,8 @@ > {{ - ss.getComponentDefinition( - ss.getComponentById(candidateId).type, + wf.getComponentDefinition( + wf.getComponentById(candidateId).type, ).name }} @@ -136,7 +136,7 @@ import BuilderInsertionOverlay from "./BuilderInsertionOverlay.vue"; import BuilderInsertionLabel from "./BuilderInsertionLabel.vue"; import { isPlatformMac } from "../core/detectPlatform"; -const ss = inject(injectionKeys.core); +const wf = inject(injectionKeys.core); const ssbm = inject(injectionKeys.builderManager); const { @@ -146,7 +146,7 @@ const { dropComponent, assignInsertionCandidacy, removeInsertionCandidacy, -} = useDragDropComponent(ss); +} = useDragDropComponent(wf); const { createAndInsertComponent, undo, @@ -164,7 +164,7 @@ const { copyComponent, removeComponentSubtree, goToParent, -} = useComponentActions(ss, ssbm); +} = useComponentActions(wf, ssbm); const builderMode = computed(() => ssbm.getMode()); @@ -254,11 +254,11 @@ function handleRendererClick(ev: PointerEvent): void { if (builderMode.value === "preview") return; const targetEl: HTMLElement = (ev.target as HTMLElement).closest( - "[data-streamsync-id]", + "[data-writer-id]", ); if (!targetEl) return; - const targetId = targetEl.dataset.streamsyncId; - const targetInstancePath = targetEl.dataset.streamsyncInstancePath; + const targetId = targetEl.dataset.writerId; + const targetInstancePath = targetEl.dataset.writerInstancePath; if (targetId !== ssbm.getSelectedId()) { ev.preventDefault(); ev.stopPropagation(); @@ -270,14 +270,14 @@ const handleRendererDragStart = (ev: DragEvent) => { if (builderMode.value === "preview") return; const targetEl: HTMLElement = (ev.target as HTMLElement).closest( - "[data-streamsync-id]", + "[data-writer-id]", ); - const componentId = targetEl.dataset.streamsyncId; - const { type } = ss.getComponentById(componentId); + const componentId = targetEl.dataset.writerId; + const { type } = wf.getComponentById(componentId); ev.dataTransfer.setData( - `application/json;streamsync=${type},${componentId}`, + `application/json;writer=${type},${componentId}`, "{}", ); }; diff --git a/src/ui/src/builder/BuilderComponentShortcuts.vue b/src/ui/src/builder/BuilderComponentShortcuts.vue index 5aff4a7ae..29b72c2fb 100644 --- a/src/ui/src/builder/BuilderComponentShortcuts.vue +++ b/src/ui/src/builder/BuilderComponentShortcuts.vue @@ -3,7 +3,7 @@ v-if="shortcutsInfo" :draggable="shortcutsInfo?.isDraggable" class="BuilderComponentShortcuts" - :data-streamsync-id="componentId" + :data-writer-id="componentId" >
{{ shortcutsInfo?.componentTypeName }} @@ -149,11 +149,11 @@ diff --git a/src/ui/src/builder/BuilderFieldsPadding.vue b/src/ui/src/builder/BuilderFieldsPadding.vue index 81293575a..53b2967a5 100644 --- a/src/ui/src/builder/BuilderFieldsPadding.vue +++ b/src/ui/src/builder/BuilderFieldsPadding.vue @@ -135,16 +135,16 @@ import { ref, toRefs, } from "vue"; -import { Component } from "../streamsyncTypes"; +import { Component } from "../writerTypes"; import { useComponentActions } from "./useComponentActions"; import injectionKeys from "../injectionKeys"; import BuilderSelect from "./BuilderSelect.vue"; import { languages } from "monaco-editor"; import css = languages.css; -const ss = inject(injectionKeys.core); +const wf = inject(injectionKeys.core); const ssbm = inject(injectionKeys.builderManager); -const { setContentValue } = useComponentActions(ss, ssbm); +const { setContentValue } = useComponentActions(wf, ssbm); const rootEl: Ref = ref(null); const pickerEl: Ref = ref(null); @@ -209,7 +209,7 @@ const props = defineProps<{ }>(); const { componentId, fieldKey } = toRefs(props); -const component = computed(() => ss.getComponentById(componentId.value)); +const component = computed(() => wf.getComponentById(componentId.value)); const selectOptions = computed(() => { return subModes.map((m) => { diff --git a/src/ui/src/builder/BuilderFieldsShadow.vue b/src/ui/src/builder/BuilderFieldsShadow.vue index 4ccadd0d1..6a38a2c58 100644 --- a/src/ui/src/builder/BuilderFieldsShadow.vue +++ b/src/ui/src/builder/BuilderFieldsShadow.vue @@ -135,13 +135,13 @@ import { ref, toRefs, } from "vue"; -import { Component } from "../streamsyncTypes"; +import { Component } from "../writerTypes"; import { useComponentActions } from "./useComponentActions"; import injectionKeys from "../injectionKeys"; -const ss = inject(injectionKeys.core); +const wf = inject(injectionKeys.core); const ssbm = inject(injectionKeys.builderManager); -const { setContentValue } = useComponentActions(ss, ssbm); +const { setContentValue } = useComponentActions(wf, ssbm); const rootEl: Ref = ref(null); const freehandInputEl: Ref = ref(null); @@ -164,7 +164,7 @@ const props = defineProps<{ fieldKey: string; }>(); const { componentId, fieldKey } = toRefs(props); -const component = computed(() => ss.getComponentById(componentId.value)); +const component = computed(() => wf.getComponentById(componentId.value)); const boxShadowRegex = /^(?[0-9]+)px (?[0-9]+)px (?[0-9]+)px (?[0-9-]+)px (?#[A-Fa-f0-9]{6})$/; diff --git a/src/ui/src/builder/BuilderFieldsText.vue b/src/ui/src/builder/BuilderFieldsText.vue index 951309495..d7a96493b 100644 --- a/src/ui/src/builder/BuilderFieldsText.vue +++ b/src/ui/src/builder/BuilderFieldsText.vue @@ -56,23 +56,23 @@ diff --git a/src/ui/src/core_components/other/CoreReuse.vue b/src/ui/src/core_components/other/CoreReuse.vue index a710dc5ec..46ac9e571 100644 --- a/src/ui/src/core_components/other/CoreReuse.vue +++ b/src/ui/src/core_components/other/CoreReuse.vue @@ -3,12 +3,12 @@