diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml deleted file mode 100644 index 26591bb..0000000 --- a/.github/workflows/node.js.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Run tests -on: - push: - branches: ["main"] - pull_request: - branches: ["main"] -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - - name: Use Node.js - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5 - - run: npm i - - run: npm run build - - run: npx pkg-pr-new publish - - run: npm run typecheck - - run: npm test diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..117b322 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,38 @@ +name: Test and lint +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +on: + push: + branches: [main] + pull_request: + branches: ["**"] + +jobs: + check: + name: Test and lint + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + + - name: Node setup + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5 + with: + cache-dependency-path: package.json + node-version: "20.x" + cache: "npm" + + - name: Install and build + run: | + npm i + npm run build + - name: Publish package for testing branch + run: npx pkg-pr-new publish || echo "Have you set up pkg-pr-new for this repo?" + - name: Test + run: | + npm run test + npm run typecheck + npm run lint diff --git a/.gitignore b/.gitignore index dbffb69..3a547cc 100644 --- a/.gitignore +++ b/.gitignore @@ -9,8 +9,6 @@ dist-ssr explorations node_modules .eslintcache -# components are libraries! -.package-lock.json # this is a package-json-redirect stub dir, see https://github.com/andrewbranch/example-subpath-exports-ts-compat?tab=readme-ov-file frontend/package.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..f3877f7 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "trailingComma": "all", + "proseWrap": "always" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f862fd..3087c07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,26 @@ # Changelog +## 0.3.0 + +- Adds /test and /\_generated/component.js entrypoints +- Drops commonjs support +- Improves source mapping for generated files +- Changes to a statically generated component API + ## 0.2.8 alpha -- Adds asynchronous events - wait for an event in a workflow, send - events asynchronously - allows pause/resume, human-in-loop, etc. +- Adds asynchronous events - wait for an event in a workflow, send events + asynchronously - allows pause/resume, human-in-loop, etc. - Supports nested workflows with step.runWorkflow. - Surfaces return value of the workflow in the status - You can start a workflow directly from the CLI / dashboard without having to make a mutation to call workflow.start: - `{ fn: "path/to/file:workflowName", args: { ...your workflow args } }` -- Reduces read bandwidth when reading the journal after running many steps in parallel. -- Simplifies the onComplete type requirement so you can accept a workflowId as a string. - This helps when you have statically generated types which can't do branded strings. +- Reduces read bandwidth when reading the journal after running many steps in + parallel. +- Simplifies the onComplete type requirement so you can accept a workflowId as a + string. This helps when you have statically generated types which can't do + branded strings. - Adds a /test entrypoint to make testing easier - Exports the `WorkflowCtx` and `WorkflowStep` types - Support for Math.random via seeded PRNG. @@ -23,8 +32,8 @@ - Batches the call to start steps - Adds the workflow name to the workpool execution for observability - Logs any error that shows up in the workflow body -- Will call onComplete for Workflows with startAsync that fail - on their first invocation. +- Will call onComplete for Workflows with startAsync that fail on their first + invocation. - Increases the max journal size from 1MB to 8MB - Adds the WorkflowId type to step.workflowId - Exposes /test entrypoint to make testing easier @@ -39,5 +48,5 @@ - Call the onComplete handler for canceled workflows - Canceling is more graceful - canceled steps generally won't print errors -- Allow `startAsync` to enqueue the starting of the workflow - to allow starting many workflows safely. +- Allow `startAsync` to enqueue the starting of the workflow to allow starting + many workflows safely. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d1fd47d..b59c18f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,7 +23,7 @@ npm run test ```sh npm run clean -npm run build +npm ci npm pack ``` @@ -33,7 +33,7 @@ npm pack npm run release ``` -#### Alpha release +or for alpha release: ```sh npm run alpha diff --git a/README.md b/README.md index 0402c02..bd24b3b 100644 --- a/README.md +++ b/README.md @@ -71,11 +71,12 @@ export const userOnboarding = workflow.define({ }); ``` -This component adds durably executed _workflows_ to Convex. Combine Convex queries, mutations, -and actions into long-lived workflows, and the system will always fully execute a workflow -to completion. +This component adds durably executed _workflows_ to Convex. Combine Convex +queries, mutations, and actions into long-lived workflows, and the system will +always fully execute a workflow to completion. -Open a [GitHub issue](https://github.com/get-convex/workflow/issues) with any feedback or bugs you find. +Open a [GitHub issue](https://github.com/get-convex/workflow/issues) with any +feedback or bugs you find. ## Installation @@ -89,7 +90,7 @@ Then, install the component within your `convex/convex.config.ts` file: ```ts // convex/convex.config.ts -import workflow from "@convex-dev/workflow/convex.config"; +import workflow from "@convex-dev/workflow/convex.config.js"; import { defineApp } from "convex/server"; const app = defineApp(); @@ -97,8 +98,8 @@ app.use(workflow); export default app; ``` -Finally, create a workflow manager within your `convex/` folder, and point it -to the installed component: +Finally, create a workflow manager within your `convex/` folder, and point it to +the installed component: ```ts // convex/index.ts @@ -114,13 +115,13 @@ The first step is to define a workflow using `workflow.define()`. This function is designed to feel like a Convex action but with a few restrictions: 1. The workflow runs in the background, so it can't return a value. -2. The workflow must be _deterministic_, so it should implement most of its logic - by calling out to other Convex functions. We restrict access to some +2. The workflow must be _deterministic_, so it should implement most of its + logic by calling out to other Convex functions. We restrict access to some non-deterministic functions like `fetch` and `crypto`. Others we patch, such as `console` for logging, `Math.random()` (seeded PRNG) and `Date` for time. -Note: To help avoid type cycles, always annotate the return type of the `handler` -with the return type of the workflow. +Note: To help avoid type cycles, always annotate the return type of the +`handler` with the return type of the workflow. ```ts export const exampleWorkflow = workflow.define({ @@ -157,8 +158,8 @@ export const exampleAction = internalAction({ ### Starting a workflow -Once you've defined a workflow, you can start it from a mutation or action -using `workflow.start()`. +Once you've defined a workflow, you can start it from a mutation or action using +`workflow.start()`. ```ts export const kickoffWorkflow = mutation({ @@ -228,8 +229,8 @@ export const handleOnComplete = mutation({ ### Running steps in parallel -You can run steps in parallel by calling `step.runAction()` multiple times in -a `Promise.all()` call. +You can run steps in parallel by calling `step.runAction()` multiple times in a +`Promise.all()` call. ```ts export const exampleWorkflow = workflow.define({ @@ -243,20 +244,21 @@ export const exampleWorkflow = workflow.define({ }); ``` -Note: The workflow will not proceed until all steps fired off at once have completed. +Note: The workflow will not proceed until all steps fired off at once have +completed. ### Specifying retry behavior Sometimes actions fail due to transient errors, whether it was an unreliable third-party API or a server restart. You can have the workflow automatically -retry actions using best practices (exponential backoff & jitter). -By default there are no retries, and the workflow will fail. +retry actions using best practices (exponential backoff & jitter). By default +there are no retries, and the workflow will fail. You can specify default retry behavior for all workflows on the WorkflowManager, or override it on a per-workflow basis. -You can also specify a custom retry behavior per-step, to opt-out of retries -for actions that may want at-most-once semantics. +You can also specify a custom retry behavior per-step, to opt-out of retries for +actions that may want at-most-once semantics. Workpool options: @@ -270,8 +272,8 @@ If you specify any of these, it will override the - `retryActionsByDefault`: Whether to retry actions, by default is false. - If you specify a retry behavior at the step level, it will always retry. -At the step level, you can also specify `true` or `false` to disable or use -the default policy. +At the step level, you can also specify `true` or `false` to disable or use the +default policy. ```ts const workflow = new WorkflowManager(components.workflow, { @@ -306,11 +308,10 @@ export const exampleWorkflow = workflow.define({ ### Specifying step parallelism You can specify how many steps can run in parallel by setting the -`maxParallelism` workpool option. It has a reasonable default. -On the free tier, you should not exceed 20, otherwise your other scheduled -functions may become delayed while competing for available functions with your -workflow steps. -On a Pro account, you should not exceed 100 across all your workflows and workpools. +`maxParallelism` workpool option. It has a reasonable default. On the free tier, +you should not exceed 20, otherwise your other scheduled functions may become +delayed while competing for available functions with your workflow steps. On a +Pro account, you should not exceed 100 across all your workflows and workpools. If you want to do a lot of work in parallel, you should employ batching, where each workflow operates on a batch of work, e.g. scraping a list of links instead of one link per workflow. @@ -328,8 +329,8 @@ const workflow = new WorkflowManager(components.workflow, { ### Checking a workflow's status -The `workflow.start()` method returns a `WorkflowId`, which can then be used for querying -a workflow's status. +The `workflow.start()` method returns a `WorkflowId`, which can then be used for +querying a workflow's status. ```ts export const kickoffWorkflow = action({ @@ -349,8 +350,9 @@ export const kickoffWorkflow = action({ ### Canceling a workflow -You can cancel a workflow with `workflow.cancel()`, halting the workflow's execution immmediately. -In-progress calls to `step.runAction()`, however, will finish executing. +You can cancel a workflow with `workflow.cancel()`, halting the workflow's +execution immmediately. In-progress calls to `step.runAction()`, however, will +finish executing. ```ts export const kickoffWorkflow = action({ @@ -370,8 +372,9 @@ export const kickoffWorkflow = action({ ### Cleaning up a workflow -After a workflow has completed, you can clean up its storage with `workflow.cleanup()`. -Completed workflows are not automatically cleaned up by the system. +After a workflow has completed, you can clean up its storage with +`workflow.cleanup()`. Completed workflows are not automatically cleaned up by +the system. ```ts export const kickoffWorkflow = action({ @@ -402,8 +405,8 @@ export const kickoffWorkflow = action({ You can specify a custom name for a step by passing a `name` option to the step. -This allows the events emitted to your logs to be more descriptive. -By default it uses the `file/folder:function` name. +This allows the events emitted to your logs to be more descriptive. By default +it uses the `file/folder:function` name. ```ts export const exampleWorkflow = workflow.define({ @@ -418,9 +421,11 @@ export const exampleWorkflow = workflow.define({ ### Circular dependencies -Having the return value of workflows depend on other Convex functions can lead to circular dependencies due to the -`internal.foo.bar` way of specifying functions. The way to fix this is to explicitly type the return value of the -workflow. When in doubt, add return types to more `handler` functions, like this: +Having the return value of workflows depend on other Convex functions can lead +to circular dependencies due to the `internal.foo.bar` way of specifying +functions. The way to fix this is to explicitly type the return value of the +workflow. When in doubt, add return types to more `handler` functions, like +this: ```diff export const supportAgentWorkflow = workflow.define({ @@ -441,8 +446,8 @@ workflow. When in doubt, add return types to more `handler` functions, like this ### More concise workflows -To avoid the noise of `internal.foo.*` syntax, you can use a variable. -For instance, if you define all your steps in `convex/steps.ts`, you can do this: +To avoid the noise of `internal.foo.*` syntax, you can use a variable. For +instance, if you define all your steps in `convex/steps.ts`, you can do this: ```diff const s = internal.steps; @@ -469,15 +474,16 @@ Here are a few limitations to keep in mind: mutation apply and limit the number and size of steps you can perform to 16MiB (including the workflow state overhead). See more about mutation limits here: https://docs.convex.dev/production/state/limits#transactions -- We currently do not collect backtraces from within function calls from workflows. +- We currently do not collect backtraces from within function calls from + workflows. - If you need to use side effects like `fetch` or use cryptographic randomness, you'll need to do that in a step, not in the workflow definition. - `Math.random` is deterministic and not suitable for cryptographic use. It is, however, useful for sharding, jitter, and other pseudo-random applications. - If the implementation of the workflow meaningfully changes (steps added, - removed, or reordered) then it will fail with a determinism violation. - The implementation should stay stable for the lifetime of active workflows. - See [this issue](https://github.com/get-convex/workflow/issues/35) for ideas - on how to make this better. + removed, or reordered) then it will fail with a determinism violation. The + implementation should stay stable for the lifetime of active workflows. See + [this issue](https://github.com/get-convex/workflow/issues/35) for ideas on + how to make this better. diff --git a/convex.json b/convex.json index 1704a04..c8dda28 100644 --- a/convex.json +++ b/convex.json @@ -1,3 +1,7 @@ { - "functions": "example/convex" + "functions": "example/convex", + "$schema": "./node_modules/convex/schemas/convex.schema.json", + "codegen": { + "legacyComponentApi": false + } } diff --git a/eslint.config.js b/eslint.config.js index 4eb2464..636051d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,41 +1,41 @@ import globals from "globals"; import pluginJs from "@eslint/js"; -import typescriptEslint from "@typescript-eslint/eslint-plugin"; -import typescriptParser from "@typescript-eslint/parser"; +import tseslint from "typescript-eslint"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; export default [ - { - files: ["src/**/*.{js,mjs,cjs,ts,tsx}", "example/**/*.{js,mjs,cjs,ts,tsx}"], - }, { ignores: [ "dist/**", "eslint.config.js", - "**/_generated/", "vitest.config.ts", + "**/_generated/", + "node10stubs.mjs", ], }, { + files: ["src/**/*.{js,mjs,cjs,ts,tsx}", "example/**/*.{js,mjs,cjs,ts,tsx}"], languageOptions: { - globals: { - ...globals.worker, - ...globals.node, - }, - parser: typescriptParser, + parser: tseslint.parser, parserOptions: { - project: true, - tsconfigRootDir: ".", + project: ["./tsconfig.json", "./example/convex/tsconfig.json"], + tsconfigRootDir: import.meta.dirname, }, }, - plugins: { - "@typescript-eslint": typescriptEslint, + }, + pluginJs.configs.recommended, + ...tseslint.configs.recommended, + // Convex code - Worker environment + { + files: ["src/**/*.{ts,tsx}", "example/convex/**/*.{ts,tsx}"], + ignores: ["src/react/**"], + languageOptions: { + globals: globals.worker, }, rules: { - ...typescriptEslint.configs["recommended"].rules, - ...pluginJs.configs.recommended.rules, "@typescript-eslint/no-floating-promises": "error", "@typescript-eslint/no-explicit-any": "off", - // allow (_arg: number) => {} and const _foo = 1; "no-unused-vars": "off", "@typescript-eslint/no-unused-vars": [ "warn", @@ -44,6 +44,52 @@ export default [ varsIgnorePattern: "^_", }, ], + "@typescript-eslint/no-unused-expressions": [ + "error", + { + allowShortCircuit: true, + allowTernary: true, + allowTaggedTemplates: true, + }, + ], + }, + }, + // React app code - Browser environment + { + files: ["src/react/**/*.{ts,tsx}", "example/src/**/*.{ts,tsx}"], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + "@typescript-eslint/no-explicit-any": "off", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "warn", + { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + }, + ], + }, + }, + // Example config files (vite.config.ts, etc.) - Node environment + { + files: ["example/vite.config.ts", "example/**/*.config.{js,ts}"], + languageOptions: { + globals: { + ...globals.node, + ...globals.browser, + }, }, }, ]; diff --git a/example/convex/_generated/api.d.ts b/example/convex/_generated/api.d.ts index 02f0590..ca3ee05 100644 --- a/example/convex/_generated/api.d.ts +++ b/example/convex/_generated/api.d.ts @@ -21,14 +21,6 @@ import type { FunctionReference, } from "convex/server"; -/** - * A utility for referencing Convex functions in your app's API. - * - * Usage: - * ```js - * const myFunctionReference = api.myModule.myFunction; - * ``` - */ declare const fullApi: ApiFromModules<{ admin: typeof admin; example: typeof example; @@ -37,407 +29,33 @@ declare const fullApi: ApiFromModules<{ transcription: typeof transcription; userConfirmation: typeof userConfirmation; }>; -declare const fullApiWithMounts: typeof fullApi; +/** + * A utility for referencing Convex functions in your app's public API. + * + * Usage: + * ```js + * const myFunctionReference = api.myModule.myFunction; + * ``` + */ export declare const api: FilterApi< - typeof fullApiWithMounts, + typeof fullApi, FunctionReference >; + +/** + * A utility for referencing Convex functions in your app's internal API. + * + * Usage: + * ```js + * const myFunctionReference = internal.myModule.myFunction; + * ``` + */ export declare const internal: FilterApi< - typeof fullApiWithMounts, + typeof fullApi, FunctionReference >; export declare const components: { - workflow: { - event: { - create: FunctionReference< - "mutation", - "internal", - { name: string; workflowId: string }, - string - >; - send: FunctionReference< - "mutation", - "internal", - { - eventId?: string; - name?: string; - result: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - workflowId?: string; - workpoolOptions?: { - defaultRetryBehavior?: { - base: number; - initialBackoffMs: number; - maxAttempts: number; - }; - logLevel?: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; - maxParallelism?: number; - retryActionsByDefault?: boolean; - }; - }, - string - >; - }; - journal: { - load: FunctionReference< - "query", - "internal", - { shortCircuit?: boolean; workflowId: string }, - { - blocked?: boolean; - journalEntries: Array<{ - _creationTime: number; - _id: string; - step: - | { - args: any; - argsSize: number; - completedAt?: number; - functionType: "query" | "mutation" | "action"; - handle: string; - inProgress: boolean; - kind?: "function"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workId?: string; - } - | { - args: any; - argsSize: number; - completedAt?: number; - handle: string; - inProgress: boolean; - kind: "workflow"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workflowId?: string; - } - | { - args: { eventId?: string }; - argsSize: number; - completedAt?: number; - eventId?: string; - inProgress: boolean; - kind: "event"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - }; - stepNumber: number; - workflowId: string; - }>; - logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; - ok: boolean; - workflow: { - _creationTime: number; - _id: string; - args: any; - generationNumber: number; - logLevel?: any; - name?: string; - onComplete?: { context?: any; fnHandle: string }; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt?: any; - state?: any; - workflowHandle: string; - }; - } - >; - startSteps: FunctionReference< - "mutation", - "internal", - { - generationNumber: number; - steps: Array<{ - retry?: - | boolean - | { base: number; initialBackoffMs: number; maxAttempts: number }; - schedulerOptions?: { runAt?: number } | { runAfter?: number }; - step: - | { - args: any; - argsSize: number; - completedAt?: number; - functionType: "query" | "mutation" | "action"; - handle: string; - inProgress: boolean; - kind?: "function"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workId?: string; - } - | { - args: any; - argsSize: number; - completedAt?: number; - handle: string; - inProgress: boolean; - kind: "workflow"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workflowId?: string; - } - | { - args: { eventId?: string }; - argsSize: number; - completedAt?: number; - eventId?: string; - inProgress: boolean; - kind: "event"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - }; - }>; - workflowId: string; - workpoolOptions?: { - defaultRetryBehavior?: { - base: number; - initialBackoffMs: number; - maxAttempts: number; - }; - logLevel?: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; - maxParallelism?: number; - retryActionsByDefault?: boolean; - }; - }, - Array<{ - _creationTime: number; - _id: string; - step: - | { - args: any; - argsSize: number; - completedAt?: number; - functionType: "query" | "mutation" | "action"; - handle: string; - inProgress: boolean; - kind?: "function"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workId?: string; - } - | { - args: any; - argsSize: number; - completedAt?: number; - handle: string; - inProgress: boolean; - kind: "workflow"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workflowId?: string; - } - | { - args: { eventId?: string }; - argsSize: number; - completedAt?: number; - eventId?: string; - inProgress: boolean; - kind: "event"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - }; - stepNumber: number; - workflowId: string; - }> - >; - }; - workflow: { - cancel: FunctionReference< - "mutation", - "internal", - { workflowId: string }, - null - >; - cleanup: FunctionReference< - "mutation", - "internal", - { workflowId: string }, - boolean - >; - complete: FunctionReference< - "mutation", - "internal", - { - generationNumber: number; - runResult: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - workflowId: string; - }, - null - >; - create: FunctionReference< - "mutation", - "internal", - { - maxParallelism?: number; - onComplete?: { context?: any; fnHandle: string }; - startAsync?: boolean; - workflowArgs: any; - workflowHandle: string; - workflowName: string; - }, - string - >; - getStatus: FunctionReference< - "query", - "internal", - { workflowId: string }, - { - inProgress: Array<{ - _creationTime: number; - _id: string; - step: - | { - args: any; - argsSize: number; - completedAt?: number; - functionType: "query" | "mutation" | "action"; - handle: string; - inProgress: boolean; - kind?: "function"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workId?: string; - } - | { - args: any; - argsSize: number; - completedAt?: number; - handle: string; - inProgress: boolean; - kind: "workflow"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workflowId?: string; - } - | { - args: { eventId?: string }; - argsSize: number; - completedAt?: number; - eventId?: string; - inProgress: boolean; - kind: "event"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - }; - stepNumber: number; - workflowId: string; - }>; - logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; - workflow: { - _creationTime: number; - _id: string; - args: any; - generationNumber: number; - logLevel?: any; - name?: string; - onComplete?: { context?: any; fnHandle: string }; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt?: any; - state?: any; - workflowHandle: string; - }; - } - >; - listSteps: FunctionReference< - "query", - "internal", - { - order: "asc" | "desc"; - paginationOpts: { - cursor: string | null; - endCursor?: string | null; - id?: number; - maximumBytesRead?: number; - maximumRowsRead?: number; - numItems: number; - }; - workflowId: string; - }, - { - continueCursor: string; - isDone: boolean; - page: Array<{ - args: any; - completedAt?: number; - eventId?: string; - kind: "function" | "workflow" | "event"; - name: string; - nestedWorkflowId?: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - stepId: string; - stepNumber: number; - workId?: string; - workflowId: string; - }>; - pageStatus?: "SplitRecommended" | "SplitRequired" | null; - splitCursor?: string | null; - } - >; - }; - }; + workflow: import("@convex-dev/workflow/_generated/component.js").ComponentApi<"workflow">; }; diff --git a/example/convex/_generated/server.d.ts b/example/convex/_generated/server.d.ts index b5c6828..bec05e6 100644 --- a/example/convex/_generated/server.d.ts +++ b/example/convex/_generated/server.d.ts @@ -10,7 +10,6 @@ import { ActionBuilder, - AnyComponents, HttpActionBuilder, MutationBuilder, QueryBuilder, @@ -19,15 +18,9 @@ import { GenericQueryCtx, GenericDatabaseReader, GenericDatabaseWriter, - FunctionReference, } from "convex/server"; import type { DataModel } from "./dataModel.js"; -type GenericCtx = - | GenericActionCtx - | GenericMutationCtx - | GenericQueryCtx; - /** * Define a query in this Convex app's public API. * @@ -92,11 +85,12 @@ export declare const internalAction: ActionBuilder; /** * Define an HTTP action. * - * This function will be used to respond to HTTP requests received by a Convex - * deployment if the requests matches the path and method where this action - * is routed. Be sure to route your action in `convex/http.js`. + * The wrapped function will be used to respond to HTTP requests received + * by a Convex deployment if the requests matches the path and method where + * this action is routed. Be sure to route your httpAction in `convex/http.js`. * - * @param func - The function. It receives an {@link ActionCtx} as its first argument. + * @param func - The function. It receives an {@link ActionCtx} as its first argument + * and a Fetch API `Request` object as its second. * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up. */ export declare const httpAction: HttpActionBuilder; diff --git a/example/convex/_generated/server.js b/example/convex/_generated/server.js index 4a21df4..bf3d25a 100644 --- a/example/convex/_generated/server.js +++ b/example/convex/_generated/server.js @@ -16,7 +16,6 @@ import { internalActionGeneric, internalMutationGeneric, internalQueryGeneric, - componentsGeneric, } from "convex/server"; /** @@ -81,10 +80,14 @@ export const action = actionGeneric; export const internalAction = internalActionGeneric; /** - * Define a Convex HTTP action. + * Define an HTTP action. * - * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object - * as its second. - * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`. + * The wrapped function will be used to respond to HTTP requests received + * by a Convex deployment if the requests matches the path and method where + * this action is routed. Be sure to route your httpAction in `convex/http.js`. + * + * @param func - The function. It receives an {@link ActionCtx} as its first argument + * and a Fetch API `Request` object as its second. + * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up. */ export const httpAction = httpActionGeneric; diff --git a/example/convex/setup.test.ts b/example/convex/setup.test.ts index 847f74d..bdd1af3 100644 --- a/example/convex/setup.test.ts +++ b/example/convex/setup.test.ts @@ -8,7 +8,7 @@ export const modules = import.meta.glob("./**/*.*s"); export function initConvexTest() { const t = convexTest(schema, modules); - t.registerComponent("workflow", workflow.schema, workflow.modules); + workflow.register(t); return t; } diff --git a/example/convex/tsconfig.json b/example/convex/tsconfig.json index 4c79336..bab30c5 100644 --- a/example/convex/tsconfig.json +++ b/example/convex/tsconfig.json @@ -1,30 +1,17 @@ { - /* This TypeScript project config describes the environment that - * Convex functions run in and is used to typecheck them. - * You can modify it, but some settings required to use Convex. - */ "compilerOptions": { - /* These settings are not required by Convex and can be modified. */ - "allowJs": true, - "strict": true, - "skipLibCheck": true, - - /* These compiler options are required by Convex */ "target": "ESNext", - "lib": ["ES2021", "dom", "ESNext.Array"], - "forceConsistentCasingInFileNames": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "skipLibCheck": false, "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, "module": "ESNext", "moduleResolution": "Bundler", "isolatedModules": true, - "noEmit": true, - - /* This should only be used in this example. Real apps should not attempt - * to compile TypeScript because differences between tsconfig.json files can - * cause the code to be compiled differently. - */ - "customConditions": ["@convex-dev/component-source"] + "jsx": "react-jsx", + "noEmit": true }, - "include": ["./**/*"], - "exclude": ["./_generated"] + "include": ["."], + "exclude": ["_generated"] } diff --git a/package-lock.json b/package-lock.json index 8f7e55a..c6e1756 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@convex-dev/workflow", - "version": "0.2.8-alpha.10", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@convex-dev/workflow", - "version": "0.2.8-alpha.10", + "version": "0.3.0", "license": "Apache-2.0", "dependencies": { "async-channel": "^0.2.0" @@ -25,6 +25,8 @@ "convex-test": "0.0.38", "cpy-cli": "6.0.0", "eslint": "9.39.1", + "eslint-plugin-react-hooks": "7.0.1", + "eslint-plugin-react-refresh": "0.4.24", "globals": "16.5.0", "npm-run-all2": "8.0.4", "openai": "6.8.0", @@ -36,7 +38,7 @@ }, "peerDependencies": { "@convex-dev/workpool": "^0.2.19", - "convex": ">=1.25.0 <1.35.0", + "convex": "^1.24.8", "convex-helpers": "^0.1.99" } }, @@ -92,6 +94,274 @@ "dev": true, "license": "MIT" }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@convex-dev/workpool": { "version": "0.2.19", "resolved": "https://registry.npmjs.org/@convex-dev/workpool/-/workpool-0.2.19.tgz", @@ -782,6 +1052,38 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.3.tgz", @@ -789,6 +1091,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@jsdevtools/ez-spawn": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@jsdevtools/ez-spawn/-/ez-spawn-3.0.4.tgz", @@ -1917,6 +2230,16 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.23", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.23.tgz", + "integrity": "sha512-616V5YX4bepJFzNyOfce5Fa8fDJMfoxzOIzDCZwaGL8MKVpFrXqfNUoIpRn9YMI5pXf/VKgzjB4htFMsFKKdiQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", @@ -1959,6 +2282,41 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", + "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.8.19", + "caniuse-lite": "^1.0.30001751", + "electron-to-chromium": "^1.5.238", + "node-releases": "^2.0.26", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -1995,6 +2353,27 @@ "node": ">=6" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001753", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001753.tgz", + "integrity": "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, "node_modules/chai": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", @@ -2161,6 +2540,13 @@ "dev": true, "license": "MIT" }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/convex": { "version": "1.28.2", "resolved": "https://registry.npmjs.org/convex/-/convex-1.28.2.tgz", @@ -2375,6 +2761,13 @@ "dev": true, "license": "ISC" }, + "node_modules/electron-to-chromium": { + "version": "1.5.244", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.244.tgz", + "integrity": "sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==", + "dev": true, + "license": "ISC" + }, "node_modules/emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -2430,6 +2823,16 @@ "@esbuild/win32-x64": "0.25.4" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2503,6 +2906,36 @@ } } }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.24.tgz", + "integrity": "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, "node_modules/eslint-scope": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", @@ -2757,6 +3190,16 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2845,6 +3288,23 @@ "node": ">=8" } }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2970,6 +3430,19 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -2999,6 +3472,19 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/junk": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/junk/-/junk-4.0.1.tgz", @@ -3077,6 +3563,16 @@ "dev": true, "license": "MIT" }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, "node_modules/magic-string": { "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", @@ -3187,6 +3683,13 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -4236,6 +4739,37 @@ "dev": true, "license": "ISC" }, + "node_modules/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -4607,6 +5141,13 @@ "dev": true, "license": "ISC" }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, "node_modules/yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", @@ -4738,6 +5279,19 @@ "engines": { "node": ">=20" } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } } } } diff --git a/package.json b/package.json index 860be6d..d384867 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@convex-dev/workflow", - "version": "0.2.8-alpha.10", + "version": "0.3.0", "description": "Convex component for durably executing workflows.", "keywords": [ "convex", @@ -13,17 +13,16 @@ "license": "Apache-2.0", "type": "module", "scripts": { - "dev": "run-p -r 'dev:backend' 'dev:frontend' 'build:watch'", - "dev:backend": "convex dev --live-component-sources --typecheck-components", + "dev": "run-p -r 'dev:*'", + "dev:backend": "convex dev --typecheck-components", "dev:frontend": "cd example && vite --clearScreen false", + "dev:build": "chokidar 'tsconfig*.json' 'src/**/*.ts' -i '**/*.test.ts' -c 'convex codegen --component-dir ./src/component && npm run build' --initial", "predev": "npm run dev:backend -- --until-success", - "clean": "rm -rf dist tsconfig.build.tsbuildinfo", - "build": "tsc --project ./tsconfig.build.json && npm run copy:dts", - "copy:dts": "rsync -a --include='*/' --include='*.d.ts' --exclude='*' src/ dist/ || cpy 'src/**/*.d.ts' 'dist/' --parents", - "build:watch": "npx chokidar 'tsconfig*.json' 'src/**/*.ts' -i '**/*.test.ts' -c 'npm run build' --initial", + "clean": "rm -rf dist *.tsbuildinfo", + "build": "tsc --project ./tsconfig.build.json", "typecheck": "tsc --noEmit && tsc -p example/convex", - "lint": "eslint src && eslint example/convex", - "all": "run-p -r 'dev:backend' 'dev:frontend' 'build:watch' 'test:watch'", + "lint": "eslint .", + "all": "run-p -r 'dev:*' 'test:watch'", "test": "vitest run --typecheck", "test:watch": "vitest --typecheck --clearScreen false", "test:debug": "vitest --inspect-brk --no-file-parallelism", @@ -40,20 +39,25 @@ "exports": { "./package.json": "./package.json", ".": { - "@convex-dev/component-source": "./src/client/index.ts", "types": "./dist/client/index.d.ts", "default": "./dist/client/index.js" }, "./test": "./src/test.ts", + "./_generated/component.js": { + "types": "./dist/component/_generated/component.d.ts" + }, "./convex.config": { - "@convex-dev/component-source": "./src/component/convex.config.ts", + "types": "./dist/component/convex.config.d.ts", + "default": "./dist/component/convex.config.js" + }, + "./convex.config.js": { "types": "./dist/component/convex.config.d.ts", "default": "./dist/component/convex.config.js" } }, "peerDependencies": { "@convex-dev/workpool": "^0.2.19", - "convex": ">=1.25.0 <1.35.0", + "convex": "^1.24.8", "convex-helpers": "^0.1.99" }, "dependencies": { @@ -73,6 +77,8 @@ "convex-test": "0.0.38", "cpy-cli": "6.0.0", "eslint": "9.39.1", + "eslint-plugin-react-hooks": "7.0.1", + "eslint-plugin-react-refresh": "0.4.24", "globals": "16.5.0", "npm-run-all2": "8.0.4", "openai": "6.8.0", @@ -82,7 +88,6 @@ "typescript-eslint": "8.46.3", "vitest": "3.2.4" }, - "main": "./dist/client/index.js", "types": "./dist/client/index.d.ts", "module": "./dist/client/index.js" } diff --git a/src/client/environment.test.ts b/src/client/environment.test.ts index 4644eb0..d3c9ae1 100644 --- a/src/client/environment.test.ts +++ b/src/client/environment.test.ts @@ -138,14 +138,23 @@ describe("environment patching units", () => { describe("behavior validation vs original Date", () => { it("should produce identical outputs for specific dates", () => { - const DeterministicDate = createDeterministicDate(Date, mockGetGenerationState); + const DeterministicDate = createDeterministicDate( + Date, + mockGetGenerationState, + ); // Test with specific timestamps const timestamps = [ 0, // Unix epoch 946684800000, // Y2K 1640995200000, // 2022-01-01 - 2023, 0, 15, 10, 30, 45, 123 // Feb 15, 2023 10:30:45.123 + 2023, + 0, + 15, + 10, + 30, + 45, + 123, // Feb 15, 2023 10:30:45.123 ]; for (const ts of [timestamps[0], timestamps[1], timestamps[2]]) { @@ -159,12 +168,22 @@ describe("environment patching units", () => { expect(deterministic.getHours()).toBe(original.getHours()); expect(deterministic.getMinutes()).toBe(original.getMinutes()); expect(deterministic.getSeconds()).toBe(original.getSeconds()); - expect(deterministic.getMilliseconds()).toBe(original.getMilliseconds()); + expect(deterministic.getMilliseconds()).toBe( + original.getMilliseconds(), + ); } // Test with constructor args const original = new Date(2023, 0, 15, 10, 30, 45, 123); - const deterministic = new DeterministicDate(2023, 0, 15, 10, 30, 45, 123); + const deterministic = new DeterministicDate( + 2023, + 0, + 15, + 10, + 30, + 45, + 123, + ); expect(deterministic.getFullYear()).toBe(original.getFullYear()); expect(deterministic.getMonth()).toBe(original.getMonth()); @@ -172,11 +191,16 @@ describe("environment patching units", () => { expect(deterministic.getHours()).toBe(original.getHours()); expect(deterministic.getMinutes()).toBe(original.getMinutes()); expect(deterministic.getSeconds()).toBe(original.getSeconds()); - expect(deterministic.getMilliseconds()).toBe(original.getMilliseconds()); + expect(deterministic.getMilliseconds()).toBe( + original.getMilliseconds(), + ); }); it("should produce identical string representations for deterministic dates", () => { - const DeterministicDate = createDeterministicDate(Date, mockGetGenerationState); + const DeterministicDate = createDeterministicDate( + Date, + mockGetGenerationState, + ); const timestamp = 1640995200000; // 2022-01-01T00:00:00.000Z const original = new Date(timestamp); @@ -192,7 +216,10 @@ describe("environment patching units", () => { }); it("should handle UTC methods identically", () => { - const DeterministicDate = createDeterministicDate(Date, mockGetGenerationState); + const DeterministicDate = createDeterministicDate( + Date, + mockGetGenerationState, + ); const timestamp = 1640995200123; // 2022-01-01T00:00:00.123Z const original = new Date(timestamp); @@ -204,12 +231,17 @@ describe("environment patching units", () => { expect(deterministic.getUTCHours()).toBe(original.getUTCHours()); expect(deterministic.getUTCMinutes()).toBe(original.getUTCMinutes()); expect(deterministic.getUTCSeconds()).toBe(original.getUTCSeconds()); - expect(deterministic.getUTCMilliseconds()).toBe(original.getUTCMilliseconds()); + expect(deterministic.getUTCMilliseconds()).toBe( + original.getUTCMilliseconds(), + ); expect(deterministic.getUTCDay()).toBe(original.getUTCDay()); }); it("should handle static methods identically", () => { - const DeterministicDate = createDeterministicDate(Date, mockGetGenerationState); + const DeterministicDate = createDeterministicDate( + Date, + mockGetGenerationState, + ); const dateString = "2023-01-15T10:30:45.123Z"; const year = 2023; @@ -220,13 +252,19 @@ describe("environment patching units", () => { const second = 45; const ms = 123; - expect(DeterministicDate.parse(dateString)).toBe(Date.parse(dateString)); - expect(DeterministicDate.UTC(year, month, day, hour, minute, second, ms)) - .toBe(Date.UTC(year, month, day, hour, minute, second, ms)); + expect(DeterministicDate.parse(dateString)).toBe( + Date.parse(dateString), + ); + expect( + DeterministicDate.UTC(year, month, day, hour, minute, second, ms), + ).toBe(Date.UTC(year, month, day, hour, minute, second, ms)); }); it("should maintain Date compatibility", () => { - const DeterministicDate = createDeterministicDate(Date, mockGetGenerationState); + const DeterministicDate = createDeterministicDate( + Date, + mockGetGenerationState, + ); const date = new DeterministicDate(2023, 0, 1); @@ -242,7 +280,10 @@ describe("environment patching units", () => { }); it("should handle Date modification methods correctly", () => { - const DeterministicDate = createDeterministicDate(Date, mockGetGenerationState); + const DeterministicDate = createDeterministicDate( + Date, + mockGetGenerationState, + ); const timestamp = 1640995200000; // 2022-01-01 const original = new Date(timestamp); @@ -264,7 +305,10 @@ describe("environment patching units", () => { }); it("should have timezone and locale methods available for future patching", () => { - const DeterministicDate = createDeterministicDate(Date, mockGetGenerationState); + const DeterministicDate = createDeterministicDate( + Date, + mockGetGenerationState, + ); const date = new DeterministicDate(1640995200000); // 2022-01-01T00:00:00.000Z // These methods exist but are not yet fully patched for determinism diff --git a/src/client/index.ts b/src/client/index.ts index 1a69e2a..c2ced62 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -31,7 +31,7 @@ import type { WorkflowStep, } from "../types.js"; import { safeFunctionName } from "./safeFunctionName.js"; -import type { OpaqueIds, WorkflowComponent } from "./types.js"; +import type { IdsToStrings, WorkflowComponent } from "./types.js"; import type { WorkflowCtx } from "./workflowContext.js"; import { workflowMutation } from "./workflowMutation.js"; @@ -88,7 +88,7 @@ export type WorkflowDefinition< }; export type WorkflowStatus = - | { type: "inProgress"; running: OpaqueIds[] } + | { type: "inProgress"; running: IdsToStrings[] } | { type: "completed"; result: unknown } | { type: "canceled" } | { type: "failed"; error: string }; @@ -191,7 +191,7 @@ export class WorkflowManager { this.component.workflow.getStatus, { workflowId }, ); - const running = inProgress.map((entry) => entry.step); + const running = inProgress.map((entry) => entry.step as IdsToStrings); switch (workflow.runResult?.kind) { case undefined: return { type: "inProgress", running }; @@ -279,7 +279,7 @@ export class WorkflowManager { | { error: string; value?: undefined } ), ): Promise> { - let result: RunResult = + const result: RunResult = "error" in args ? { kind: "failed", diff --git a/src/client/types.ts b/src/client/types.ts index f0f4279..334ec2e 100644 --- a/src/client/types.ts +++ b/src/client/types.ts @@ -1,38 +1,13 @@ -import type { Expand, FunctionReference } from "convex/server"; -import type { api } from "../component/_generated/api.js"; -import type { GenericId } from "convex/values"; +import type { ComponentApi } from "../component/_generated/component.js"; +import type { GenericId, Value } from "convex/values"; -export type WorkflowComponent = UseApi; +export type WorkflowComponent = ComponentApi; -export type UseApi = Expand<{ - [mod in keyof API]: API[mod] extends FunctionReference< - infer FType, - "public", - infer FArgs, - infer FReturnType, - infer FComponentPath - > - ? FunctionReference< - FType, - "internal", - OpaqueIds, - OpaqueIds, - FComponentPath - > - : UseApi; -}>; - -export type OpaqueIds = - T extends GenericId +export type IdsToStrings = + T extends GenericId ? string - : T extends string - ? `${T}` extends T - ? T - : string - : T extends (infer U)[] - ? OpaqueIds[] - : T extends ArrayBuffer - ? ArrayBuffer - : T extends object - ? { [K in keyof T]: OpaqueIds } - : T; + : T extends (infer U)[] + ? IdsToStrings[] + : T extends Record + ? { [K in keyof T]: IdsToStrings } + : T; diff --git a/src/component/_generated/api.d.ts b/src/component/_generated/api.d.ts deleted file mode 100644 index 4a58fb0..0000000 --- a/src/component/_generated/api.d.ts +++ /dev/null @@ -1,535 +0,0 @@ -/* eslint-disable */ -/** - * Generated `api` utility. - * - * THIS CODE IS AUTOMATICALLY GENERATED. - * - * To regenerate, run `npx convex dev`. - * @module - */ - -import type * as event from "../event.js"; -import type * as journal from "../journal.js"; -import type * as logging from "../logging.js"; -import type * as model from "../model.js"; -import type * as pool from "../pool.js"; -import type * as utils from "../utils.js"; -import type * as workflow from "../workflow.js"; - -import type { - ApiFromModules, - FilterApi, - FunctionReference, -} from "convex/server"; - -/** - * A utility for referencing Convex functions in your app's API. - * - * Usage: - * ```js - * const myFunctionReference = api.myModule.myFunction; - * ``` - */ -declare const fullApi: ApiFromModules<{ - event: typeof event; - journal: typeof journal; - logging: typeof logging; - model: typeof model; - pool: typeof pool; - utils: typeof utils; - workflow: typeof workflow; -}>; -export type Mounts = { - event: { - create: FunctionReference< - "mutation", - "public", - { name: string; workflowId: string }, - string - >; - send: FunctionReference< - "mutation", - "public", - { - eventId?: string; - name?: string; - result: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - workflowId?: string; - workpoolOptions?: { - defaultRetryBehavior?: { - base: number; - initialBackoffMs: number; - maxAttempts: number; - }; - logLevel?: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; - maxParallelism?: number; - retryActionsByDefault?: boolean; - }; - }, - string - >; - }; - journal: { - load: FunctionReference< - "query", - "public", - { shortCircuit?: boolean; workflowId: string }, - { - blocked?: boolean; - journalEntries: Array<{ - _creationTime: number; - _id: string; - step: - | { - args: any; - argsSize: number; - completedAt?: number; - functionType: "query" | "mutation" | "action"; - handle: string; - inProgress: boolean; - kind?: "function"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workId?: string; - } - | { - args: any; - argsSize: number; - completedAt?: number; - handle: string; - inProgress: boolean; - kind: "workflow"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workflowId?: string; - } - | { - args: { eventId?: string }; - argsSize: number; - completedAt?: number; - eventId?: string; - inProgress: boolean; - kind: "event"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - }; - stepNumber: number; - workflowId: string; - }>; - logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; - ok: boolean; - workflow: { - _creationTime: number; - _id: string; - args: any; - generationNumber: number; - logLevel?: any; - name?: string; - onComplete?: { context?: any; fnHandle: string }; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt?: any; - state?: any; - workflowHandle: string; - }; - } - >; - startSteps: FunctionReference< - "mutation", - "public", - { - generationNumber: number; - steps: Array<{ - retry?: - | boolean - | { base: number; initialBackoffMs: number; maxAttempts: number }; - schedulerOptions?: { runAt?: number } | { runAfter?: number }; - step: - | { - args: any; - argsSize: number; - completedAt?: number; - functionType: "query" | "mutation" | "action"; - handle: string; - inProgress: boolean; - kind?: "function"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workId?: string; - } - | { - args: any; - argsSize: number; - completedAt?: number; - handle: string; - inProgress: boolean; - kind: "workflow"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workflowId?: string; - } - | { - args: { eventId?: string }; - argsSize: number; - completedAt?: number; - eventId?: string; - inProgress: boolean; - kind: "event"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - }; - }>; - workflowId: string; - workpoolOptions?: { - defaultRetryBehavior?: { - base: number; - initialBackoffMs: number; - maxAttempts: number; - }; - logLevel?: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; - maxParallelism?: number; - retryActionsByDefault?: boolean; - }; - }, - Array<{ - _creationTime: number; - _id: string; - step: - | { - args: any; - argsSize: number; - completedAt?: number; - functionType: "query" | "mutation" | "action"; - handle: string; - inProgress: boolean; - kind?: "function"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workId?: string; - } - | { - args: any; - argsSize: number; - completedAt?: number; - handle: string; - inProgress: boolean; - kind: "workflow"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workflowId?: string; - } - | { - args: { eventId?: string }; - argsSize: number; - completedAt?: number; - eventId?: string; - inProgress: boolean; - kind: "event"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - }; - stepNumber: number; - workflowId: string; - }> - >; - }; - workflow: { - cancel: FunctionReference< - "mutation", - "public", - { workflowId: string }, - null - >; - cleanup: FunctionReference< - "mutation", - "public", - { workflowId: string }, - boolean - >; - complete: FunctionReference< - "mutation", - "public", - { - generationNumber: number; - runResult: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - workflowId: string; - }, - null - >; - create: FunctionReference< - "mutation", - "public", - { - maxParallelism?: number; - onComplete?: { context?: any; fnHandle: string }; - startAsync?: boolean; - workflowArgs: any; - workflowHandle: string; - workflowName: string; - }, - string - >; - getStatus: FunctionReference< - "query", - "public", - { workflowId: string }, - { - inProgress: Array<{ - _creationTime: number; - _id: string; - step: - | { - args: any; - argsSize: number; - completedAt?: number; - functionType: "query" | "mutation" | "action"; - handle: string; - inProgress: boolean; - kind?: "function"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workId?: string; - } - | { - args: any; - argsSize: number; - completedAt?: number; - handle: string; - inProgress: boolean; - kind: "workflow"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - workflowId?: string; - } - | { - args: { eventId?: string }; - argsSize: number; - completedAt?: number; - eventId?: string; - inProgress: boolean; - kind: "event"; - name: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - }; - stepNumber: number; - workflowId: string; - }>; - logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; - workflow: { - _creationTime: number; - _id: string; - args: any; - generationNumber: number; - logLevel?: any; - name?: string; - onComplete?: { context?: any; fnHandle: string }; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt?: any; - state?: any; - workflowHandle: string; - }; - } - >; - listSteps: FunctionReference< - "query", - "public", - { - order: "asc" | "desc"; - paginationOpts: { - cursor: string | null; - endCursor?: string | null; - id?: number; - maximumBytesRead?: number; - maximumRowsRead?: number; - numItems: number; - }; - workflowId: string; - }, - { - continueCursor: string; - isDone: boolean; - page: Array<{ - args: any; - completedAt?: number; - eventId?: string; - kind: "function" | "workflow" | "event"; - name: string; - nestedWorkflowId?: string; - runResult?: - | { kind: "success"; returnValue: any } - | { error: string; kind: "failed" } - | { kind: "canceled" }; - startedAt: number; - stepId: string; - stepNumber: number; - workId?: string; - workflowId: string; - }>; - pageStatus?: "SplitRecommended" | "SplitRequired" | null; - splitCursor?: string | null; - } - >; - }; -}; -// For now fullApiWithMounts is only fullApi which provides -// jump-to-definition in component client code. -// Use Mounts for the same type without the inference. -declare const fullApiWithMounts: typeof fullApi; - -export declare const api: FilterApi< - typeof fullApiWithMounts, - FunctionReference ->; -export declare const internal: FilterApi< - typeof fullApiWithMounts, - FunctionReference ->; - -export declare const components: { - workpool: { - lib: { - cancel: FunctionReference< - "mutation", - "internal", - { - id: string; - logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; - }, - any - >; - cancelAll: FunctionReference< - "mutation", - "internal", - { - before?: number; - limit?: number; - logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; - }, - any - >; - enqueue: FunctionReference< - "mutation", - "internal", - { - config: { - logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; - maxParallelism: number; - }; - fnArgs: any; - fnHandle: string; - fnName: string; - fnType: "action" | "mutation" | "query"; - onComplete?: { context?: any; fnHandle: string }; - retryBehavior?: { - base: number; - initialBackoffMs: number; - maxAttempts: number; - }; - runAt: number; - }, - string - >; - enqueueBatch: FunctionReference< - "mutation", - "internal", - { - config: { - logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; - maxParallelism: number; - }; - items: Array<{ - fnArgs: any; - fnHandle: string; - fnName: string; - fnType: "action" | "mutation" | "query"; - onComplete?: { context?: any; fnHandle: string }; - retryBehavior?: { - base: number; - initialBackoffMs: number; - maxAttempts: number; - }; - runAt: number; - }>; - }, - Array - >; - status: FunctionReference< - "query", - "internal", - { id: string }, - | { previousAttempts: number; state: "pending" } - | { previousAttempts: number; state: "running" } - | { state: "finished" } - >; - statusBatch: FunctionReference< - "query", - "internal", - { ids: Array }, - Array< - | { previousAttempts: number; state: "pending" } - | { previousAttempts: number; state: "running" } - | { state: "finished" } - > - >; - }; - }; -}; diff --git a/src/component/_generated/api.js b/src/component/_generated/api.js deleted file mode 100644 index 44bf985..0000000 --- a/src/component/_generated/api.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -/** - * Generated `api` utility. - * - * THIS CODE IS AUTOMATICALLY GENERATED. - * - * To regenerate, run `npx convex dev`. - * @module - */ - -import { anyApi, componentsGeneric } from "convex/server"; - -/** - * A utility for referencing Convex functions in your app's API. - * - * Usage: - * ```js - * const myFunctionReference = api.myModule.myFunction; - * ``` - */ -export const api = anyApi; -export const internal = anyApi; -export const components = componentsGeneric(); diff --git a/src/component/_generated/api.ts b/src/component/_generated/api.ts new file mode 100644 index 0000000..8151e93 --- /dev/null +++ b/src/component/_generated/api.ts @@ -0,0 +1,150 @@ +/* eslint-disable */ +/** + * Generated `api` utility. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * To regenerate, run `npx convex dev`. + * @module + */ + +import type * as event from "../event.js"; +import type * as journal from "../journal.js"; +import type * as logging from "../logging.js"; +import type * as model from "../model.js"; +import type * as pool from "../pool.js"; +import type * as utils from "../utils.js"; +import type * as workflow from "../workflow.js"; + +import type { + ApiFromModules, + FilterApi, + FunctionReference, +} from "convex/server"; +import { anyApi, componentsGeneric } from "convex/server"; + +const fullApi: ApiFromModules<{ + event: typeof event; + journal: typeof journal; + logging: typeof logging; + model: typeof model; + pool: typeof pool; + utils: typeof utils; + workflow: typeof workflow; +}> = anyApi as any; + +/** + * A utility for referencing Convex functions in your app's public API. + * + * Usage: + * ```js + * const myFunctionReference = api.myModule.myFunction; + * ``` + */ +export const api: FilterApi< + typeof fullApi, + FunctionReference +> = anyApi as any; + +/** + * A utility for referencing Convex functions in your app's internal API. + * + * Usage: + * ```js + * const myFunctionReference = internal.myModule.myFunction; + * ``` + */ +export const internal: FilterApi< + typeof fullApi, + FunctionReference +> = anyApi as any; + +export const components = componentsGeneric() as unknown as { + workpool: { + lib: { + cancel: FunctionReference< + "mutation", + "internal", + { + id: string; + logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; + }, + any + >; + cancelAll: FunctionReference< + "mutation", + "internal", + { + before?: number; + limit?: number; + logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; + }, + any + >; + enqueue: FunctionReference< + "mutation", + "internal", + { + config: { + logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; + maxParallelism: number; + }; + fnArgs: any; + fnHandle: string; + fnName: string; + fnType: "action" | "mutation" | "query"; + onComplete?: { context?: any; fnHandle: string }; + retryBehavior?: { + base: number; + initialBackoffMs: number; + maxAttempts: number; + }; + runAt: number; + }, + string + >; + enqueueBatch: FunctionReference< + "mutation", + "internal", + { + config: { + logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; + maxParallelism: number; + }; + items: Array<{ + fnArgs: any; + fnHandle: string; + fnName: string; + fnType: "action" | "mutation" | "query"; + onComplete?: { context?: any; fnHandle: string }; + retryBehavior?: { + base: number; + initialBackoffMs: number; + maxAttempts: number; + }; + runAt: number; + }>; + }, + Array + >; + status: FunctionReference< + "query", + "internal", + { id: string }, + | { previousAttempts: number; state: "pending" } + | { previousAttempts: number; state: "running" } + | { state: "finished" } + >; + statusBatch: FunctionReference< + "query", + "internal", + { ids: Array }, + Array< + | { previousAttempts: number; state: "pending" } + | { previousAttempts: number; state: "running" } + | { state: "finished" } + > + >; + }; + }; +}; diff --git a/src/component/_generated/component.ts b/src/component/_generated/component.ts new file mode 100644 index 0000000..328854f --- /dev/null +++ b/src/component/_generated/component.ts @@ -0,0 +1,425 @@ +/* eslint-disable */ +/** + * Generated `ComponentApi` utility. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * To regenerate, run `npx convex dev`. + * @module + */ + +import type { FunctionReference } from "convex/server"; + +/** + * A utility for referencing a Convex component's exposed API. + * + * Useful when expecting a parameter like `components.myComponent`. + * Usage: + * ```ts + * async function myFunction(ctx: QueryCtx, component: ComponentApi) { + * return ctx.runQuery(component.someFile.someQuery, { ...args }); + * } + * ``` + */ +export type ComponentApi = + { + event: { + create: FunctionReference< + "mutation", + "internal", + { name: string; workflowId: string }, + string, + Name + >; + send: FunctionReference< + "mutation", + "internal", + { + eventId?: string; + name?: string; + result: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + workflowId?: string; + workpoolOptions?: { + defaultRetryBehavior?: { + base: number; + initialBackoffMs: number; + maxAttempts: number; + }; + logLevel?: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; + maxParallelism?: number; + retryActionsByDefault?: boolean; + }; + }, + string, + Name + >; + }; + journal: { + load: FunctionReference< + "query", + "internal", + { shortCircuit?: boolean; workflowId: string }, + { + blocked?: boolean; + journalEntries: Array<{ + _creationTime: number; + _id: string; + step: + | { + args: any; + argsSize: number; + completedAt?: number; + functionType: "query" | "mutation" | "action"; + handle: string; + inProgress: boolean; + kind?: "function"; + name: string; + runResult?: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + startedAt: number; + workId?: string; + } + | { + args: any; + argsSize: number; + completedAt?: number; + handle: string; + inProgress: boolean; + kind: "workflow"; + name: string; + runResult?: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + startedAt: number; + workflowId?: string; + } + | { + args: { eventId?: string }; + argsSize: number; + completedAt?: number; + eventId?: string; + inProgress: boolean; + kind: "event"; + name: string; + runResult?: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + startedAt: number; + }; + stepNumber: number; + workflowId: string; + }>; + logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; + ok: boolean; + workflow: { + _creationTime: number; + _id: string; + args: any; + generationNumber: number; + logLevel?: any; + name?: string; + onComplete?: { context?: any; fnHandle: string }; + runResult?: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + startedAt?: any; + state?: any; + workflowHandle: string; + }; + }, + Name + >; + startSteps: FunctionReference< + "mutation", + "internal", + { + generationNumber: number; + steps: Array<{ + retry?: + | boolean + | { base: number; initialBackoffMs: number; maxAttempts: number }; + schedulerOptions?: { runAt?: number } | { runAfter?: number }; + step: + | { + args: any; + argsSize: number; + completedAt?: number; + functionType: "query" | "mutation" | "action"; + handle: string; + inProgress: boolean; + kind?: "function"; + name: string; + runResult?: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + startedAt: number; + workId?: string; + } + | { + args: any; + argsSize: number; + completedAt?: number; + handle: string; + inProgress: boolean; + kind: "workflow"; + name: string; + runResult?: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + startedAt: number; + workflowId?: string; + } + | { + args: { eventId?: string }; + argsSize: number; + completedAt?: number; + eventId?: string; + inProgress: boolean; + kind: "event"; + name: string; + runResult?: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + startedAt: number; + }; + }>; + workflowId: string; + workpoolOptions?: { + defaultRetryBehavior?: { + base: number; + initialBackoffMs: number; + maxAttempts: number; + }; + logLevel?: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; + maxParallelism?: number; + retryActionsByDefault?: boolean; + }; + }, + Array<{ + _creationTime: number; + _id: string; + step: + | { + args: any; + argsSize: number; + completedAt?: number; + functionType: "query" | "mutation" | "action"; + handle: string; + inProgress: boolean; + kind?: "function"; + name: string; + runResult?: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + startedAt: number; + workId?: string; + } + | { + args: any; + argsSize: number; + completedAt?: number; + handle: string; + inProgress: boolean; + kind: "workflow"; + name: string; + runResult?: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + startedAt: number; + workflowId?: string; + } + | { + args: { eventId?: string }; + argsSize: number; + completedAt?: number; + eventId?: string; + inProgress: boolean; + kind: "event"; + name: string; + runResult?: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + startedAt: number; + }; + stepNumber: number; + workflowId: string; + }>, + Name + >; + }; + workflow: { + cancel: FunctionReference< + "mutation", + "internal", + { workflowId: string }, + null, + Name + >; + cleanup: FunctionReference< + "mutation", + "internal", + { workflowId: string }, + boolean, + Name + >; + complete: FunctionReference< + "mutation", + "internal", + { + generationNumber: number; + runResult: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + workflowId: string; + }, + null, + Name + >; + create: FunctionReference< + "mutation", + "internal", + { + maxParallelism?: number; + onComplete?: { context?: any; fnHandle: string }; + startAsync?: boolean; + workflowArgs: any; + workflowHandle: string; + workflowName: string; + }, + string, + Name + >; + getStatus: FunctionReference< + "query", + "internal", + { workflowId: string }, + { + inProgress: Array<{ + _creationTime: number; + _id: string; + step: + | { + args: any; + argsSize: number; + completedAt?: number; + functionType: "query" | "mutation" | "action"; + handle: string; + inProgress: boolean; + kind?: "function"; + name: string; + runResult?: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + startedAt: number; + workId?: string; + } + | { + args: any; + argsSize: number; + completedAt?: number; + handle: string; + inProgress: boolean; + kind: "workflow"; + name: string; + runResult?: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + startedAt: number; + workflowId?: string; + } + | { + args: { eventId?: string }; + argsSize: number; + completedAt?: number; + eventId?: string; + inProgress: boolean; + kind: "event"; + name: string; + runResult?: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + startedAt: number; + }; + stepNumber: number; + workflowId: string; + }>; + logLevel: "DEBUG" | "TRACE" | "INFO" | "REPORT" | "WARN" | "ERROR"; + workflow: { + _creationTime: number; + _id: string; + args: any; + generationNumber: number; + logLevel?: any; + name?: string; + onComplete?: { context?: any; fnHandle: string }; + runResult?: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + startedAt?: any; + state?: any; + workflowHandle: string; + }; + }, + Name + >; + listSteps: FunctionReference< + "query", + "internal", + { + order: "asc" | "desc"; + paginationOpts: { + cursor: string | null; + endCursor?: string | null; + id?: number; + maximumBytesRead?: number; + maximumRowsRead?: number; + numItems: number; + }; + workflowId: string; + }, + { + continueCursor: string; + isDone: boolean; + page: Array<{ + args: any; + completedAt?: number; + eventId?: string; + kind: "function" | "workflow" | "event"; + name: string; + nestedWorkflowId?: string; + runResult?: + | { kind: "success"; returnValue: any } + | { error: string; kind: "failed" } + | { kind: "canceled" }; + startedAt: number; + stepId: string; + stepNumber: number; + workId?: string; + workflowId: string; + }>; + pageStatus?: "SplitRecommended" | "SplitRequired" | null; + splitCursor?: string | null; + }, + Name + >; + }; + }; diff --git a/src/component/_generated/dataModel.d.ts b/src/component/_generated/dataModel.ts similarity index 100% rename from src/component/_generated/dataModel.d.ts rename to src/component/_generated/dataModel.ts diff --git a/src/component/_generated/server.js b/src/component/_generated/server.js deleted file mode 100644 index 4a21df4..0000000 --- a/src/component/_generated/server.js +++ /dev/null @@ -1,90 +0,0 @@ -/* eslint-disable */ -/** - * Generated utilities for implementing server-side Convex query and mutation functions. - * - * THIS CODE IS AUTOMATICALLY GENERATED. - * - * To regenerate, run `npx convex dev`. - * @module - */ - -import { - actionGeneric, - httpActionGeneric, - queryGeneric, - mutationGeneric, - internalActionGeneric, - internalMutationGeneric, - internalQueryGeneric, - componentsGeneric, -} from "convex/server"; - -/** - * Define a query in this Convex app's public API. - * - * This function will be allowed to read your Convex database and will be accessible from the client. - * - * @param func - The query function. It receives a {@link QueryCtx} as its first argument. - * @returns The wrapped query. Include this as an `export` to name it and make it accessible. - */ -export const query = queryGeneric; - -/** - * Define a query that is only accessible from other Convex functions (but not from the client). - * - * This function will be allowed to read from your Convex database. It will not be accessible from the client. - * - * @param func - The query function. It receives a {@link QueryCtx} as its first argument. - * @returns The wrapped query. Include this as an `export` to name it and make it accessible. - */ -export const internalQuery = internalQueryGeneric; - -/** - * Define a mutation in this Convex app's public API. - * - * This function will be allowed to modify your Convex database and will be accessible from the client. - * - * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. - * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. - */ -export const mutation = mutationGeneric; - -/** - * Define a mutation that is only accessible from other Convex functions (but not from the client). - * - * This function will be allowed to modify your Convex database. It will not be accessible from the client. - * - * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. - * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. - */ -export const internalMutation = internalMutationGeneric; - -/** - * Define an action in this Convex app's public API. - * - * An action is a function which can execute any JavaScript code, including non-deterministic - * code and code with side-effects, like calling third-party services. - * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive. - * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}. - * - * @param func - The action. It receives an {@link ActionCtx} as its first argument. - * @returns The wrapped action. Include this as an `export` to name it and make it accessible. - */ -export const action = actionGeneric; - -/** - * Define an action that is only accessible from other Convex functions (but not from the client). - * - * @param func - The function. It receives an {@link ActionCtx} as its first argument. - * @returns The wrapped function. Include this as an `export` to name it and make it accessible. - */ -export const internalAction = internalActionGeneric; - -/** - * Define a Convex HTTP action. - * - * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object - * as its second. - * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`. - */ -export const httpAction = httpActionGeneric; diff --git a/src/component/_generated/server.d.ts b/src/component/_generated/server.ts similarity index 79% rename from src/component/_generated/server.d.ts rename to src/component/_generated/server.ts index b5c6828..24994e4 100644 --- a/src/component/_generated/server.d.ts +++ b/src/component/_generated/server.ts @@ -8,9 +8,8 @@ * @module */ -import { +import type { ActionBuilder, - AnyComponents, HttpActionBuilder, MutationBuilder, QueryBuilder, @@ -19,15 +18,18 @@ import { GenericQueryCtx, GenericDatabaseReader, GenericDatabaseWriter, - FunctionReference, +} from "convex/server"; +import { + actionGeneric, + httpActionGeneric, + queryGeneric, + mutationGeneric, + internalActionGeneric, + internalMutationGeneric, + internalQueryGeneric, } from "convex/server"; import type { DataModel } from "./dataModel.js"; -type GenericCtx = - | GenericActionCtx - | GenericMutationCtx - | GenericQueryCtx; - /** * Define a query in this Convex app's public API. * @@ -36,7 +38,7 @@ type GenericCtx = * @param func - The query function. It receives a {@link QueryCtx} as its first argument. * @returns The wrapped query. Include this as an `export` to name it and make it accessible. */ -export declare const query: QueryBuilder; +export const query: QueryBuilder = queryGeneric; /** * Define a query that is only accessible from other Convex functions (but not from the client). @@ -46,7 +48,8 @@ export declare const query: QueryBuilder; * @param func - The query function. It receives a {@link QueryCtx} as its first argument. * @returns The wrapped query. Include this as an `export` to name it and make it accessible. */ -export declare const internalQuery: QueryBuilder; +export const internalQuery: QueryBuilder = + internalQueryGeneric; /** * Define a mutation in this Convex app's public API. @@ -56,7 +59,7 @@ export declare const internalQuery: QueryBuilder; * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. */ -export declare const mutation: MutationBuilder; +export const mutation: MutationBuilder = mutationGeneric; /** * Define a mutation that is only accessible from other Convex functions (but not from the client). @@ -66,7 +69,8 @@ export declare const mutation: MutationBuilder; * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. */ -export declare const internalMutation: MutationBuilder; +export const internalMutation: MutationBuilder = + internalMutationGeneric; /** * Define an action in this Convex app's public API. @@ -79,7 +83,7 @@ export declare const internalMutation: MutationBuilder; * @param func - The action. It receives an {@link ActionCtx} as its first argument. * @returns The wrapped action. Include this as an `export` to name it and make it accessible. */ -export declare const action: ActionBuilder; +export const action: ActionBuilder = actionGeneric; /** * Define an action that is only accessible from other Convex functions (but not from the client). @@ -87,19 +91,26 @@ export declare const action: ActionBuilder; * @param func - The function. It receives an {@link ActionCtx} as its first argument. * @returns The wrapped function. Include this as an `export` to name it and make it accessible. */ -export declare const internalAction: ActionBuilder; +export const internalAction: ActionBuilder = + internalActionGeneric; /** * Define an HTTP action. * - * This function will be used to respond to HTTP requests received by a Convex - * deployment if the requests matches the path and method where this action - * is routed. Be sure to route your action in `convex/http.js`. + * The wrapped function will be used to respond to HTTP requests received + * by a Convex deployment if the requests matches the path and method where + * this action is routed. Be sure to route your httpAction in `convex/http.js`. * - * @param func - The function. It receives an {@link ActionCtx} as its first argument. + * @param func - The function. It receives an {@link ActionCtx} as its first argument + * and a Fetch API `Request` object as its second. * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up. */ -export declare const httpAction: HttpActionBuilder; +export const httpAction: HttpActionBuilder = httpActionGeneric; + +type GenericCtx = + | GenericActionCtx + | GenericMutationCtx + | GenericQueryCtx; /** * A set of services for use within Convex query functions. @@ -107,8 +118,7 @@ export declare const httpAction: HttpActionBuilder; * The query context is passed as the first argument to any Convex query * function run on the server. * - * This differs from the {@link MutationCtx} because all of the services are - * read-only. + * If you're using code generation, use the `QueryCtx` type in `convex/_generated/server.d.ts` instead. */ export type QueryCtx = GenericQueryCtx; @@ -117,6 +127,8 @@ export type QueryCtx = GenericQueryCtx; * * The mutation context is passed as the first argument to any Convex mutation * function run on the server. + * + * If you're using code generation, use the `MutationCtx` type in `convex/_generated/server.d.ts` instead. */ export type MutationCtx = GenericMutationCtx; diff --git a/src/test.ts b/src/test.ts index 7a340a7..737dd80 100644 --- a/src/test.ts +++ b/src/test.ts @@ -1,6 +1,6 @@ +/// import type { TestConvex } from "convex-test"; import type { GenericSchema, SchemaDefinition } from "convex/server"; -import workpool from "@convex-dev/workpool/test"; import schema from "./component/schema.js"; const modules = import.meta.glob("./component/**/*.ts"); @@ -9,11 +9,10 @@ const modules = import.meta.glob("./component/**/*.ts"); * @param t - The test convex instance, e.g. from calling `convexTest`. * @param name - The name of the component, as registered in convex.config.ts. */ -function register( +export function register( t: TestConvex>, name: string = "workflow", ) { t.registerComponent(name, schema, modules); - workpool.register(t, `${name}/workpool`); } export default { register, schema, modules }; diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000..aff5230 --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "include": [ + "src/**/*.ts", + "src/**/*.tsx", + "example/src/**/*.ts", + "example/src/**/*.tsx", + "example/convex/**/*.ts" + ], + "exclude": ["node_modules", "dist", "**/_generated"] +} diff --git a/vitest.config.ts b/vitest.config.ts index 28ce6fa..e9408f8 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -3,5 +3,8 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { environment: "edge-runtime", + typecheck: { + tsconfig: "./tsconfig.test.json", + }, }, });