syncorejs 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +2 -1
  2. package/dist/_vendor/cli/app.d.mts.map +1 -1
  3. package/dist/_vendor/cli/app.mjs +323 -42
  4. package/dist/_vendor/cli/app.mjs.map +1 -1
  5. package/dist/_vendor/cli/context.mjs +27 -9
  6. package/dist/_vendor/cli/context.mjs.map +1 -1
  7. package/dist/_vendor/cli/doctor.mjs +513 -46
  8. package/dist/_vendor/cli/doctor.mjs.map +1 -1
  9. package/dist/_vendor/cli/messages.mjs +5 -4
  10. package/dist/_vendor/cli/messages.mjs.map +1 -1
  11. package/dist/_vendor/cli/project.mjs +110 -12
  12. package/dist/_vendor/cli/project.mjs.map +1 -1
  13. package/dist/_vendor/cli/render.mjs +57 -9
  14. package/dist/_vendor/cli/render.mjs.map +1 -1
  15. package/dist/_vendor/cli/targets.mjs +4 -3
  16. package/dist/_vendor/cli/targets.mjs.map +1 -1
  17. package/dist/_vendor/core/cli.d.mts +13 -3
  18. package/dist/_vendor/core/cli.d.mts.map +1 -1
  19. package/dist/_vendor/core/cli.mjs +242 -91
  20. package/dist/_vendor/core/cli.mjs.map +1 -1
  21. package/dist/_vendor/core/devtools-auth.mjs +60 -0
  22. package/dist/_vendor/core/devtools-auth.mjs.map +1 -0
  23. package/dist/_vendor/core/index.d.mts +5 -3
  24. package/dist/_vendor/core/index.mjs +22 -2
  25. package/dist/_vendor/core/index.mjs.map +1 -1
  26. package/dist/_vendor/core/runtime/components.d.mts +111 -0
  27. package/dist/_vendor/core/runtime/components.d.mts.map +1 -0
  28. package/dist/_vendor/core/runtime/components.mjs +186 -0
  29. package/dist/_vendor/core/runtime/components.mjs.map +1 -0
  30. package/dist/_vendor/core/runtime/devtools.d.mts +4 -4
  31. package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
  32. package/dist/_vendor/core/runtime/devtools.mjs +52 -41
  33. package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
  34. package/dist/_vendor/core/runtime/functions.d.mts +10 -10
  35. package/dist/_vendor/core/runtime/functions.d.mts.map +1 -1
  36. package/dist/_vendor/core/runtime/functions.mjs +2 -2
  37. package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
  38. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs +77 -0
  39. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs.map +1 -0
  40. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs +617 -0
  41. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs.map +1 -0
  42. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs +186 -0
  43. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs.map +1 -0
  44. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs +220 -0
  45. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs.map +1 -0
  46. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs +203 -0
  47. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs.map +1 -0
  48. package/dist/_vendor/core/runtime/internal/engines/shared.mjs +177 -0
  49. package/dist/_vendor/core/runtime/internal/engines/shared.mjs.map +1 -0
  50. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs +144 -0
  51. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs.map +1 -0
  52. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs +220 -0
  53. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs.map +1 -0
  54. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs +32 -0
  55. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs.map +1 -0
  56. package/dist/_vendor/core/runtime/internal/systemMeta.mjs +61 -0
  57. package/dist/_vendor/core/runtime/internal/systemMeta.mjs.map +1 -0
  58. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs +37 -0
  59. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs.map +1 -0
  60. package/dist/_vendor/core/runtime/runtime.d.mts +159 -205
  61. package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
  62. package/dist/_vendor/core/runtime/runtime.mjs +16 -1371
  63. package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
  64. package/dist/_vendor/core/transport.d.mts +111 -0
  65. package/dist/_vendor/core/transport.d.mts.map +1 -0
  66. package/dist/_vendor/core/transport.mjs +419 -0
  67. package/dist/_vendor/core/transport.mjs.map +1 -0
  68. package/dist/_vendor/devtools-protocol/index.d.ts +39 -1
  69. package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
  70. package/dist/_vendor/devtools-protocol/index.js +25 -9
  71. package/dist/_vendor/devtools-protocol/index.js.map +1 -1
  72. package/dist/_vendor/next/index.d.ts +1 -1
  73. package/dist/_vendor/next/index.d.ts.map +1 -1
  74. package/dist/_vendor/next/index.js +31 -13
  75. package/dist/_vendor/next/index.js.map +1 -1
  76. package/dist/_vendor/platform-expo/index.d.ts +12 -12
  77. package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
  78. package/dist/_vendor/platform-expo/index.js +4 -2
  79. package/dist/_vendor/platform-expo/index.js.map +1 -1
  80. package/dist/_vendor/platform-expo/react.d.ts.map +1 -1
  81. package/dist/_vendor/platform-expo/react.js +11 -10
  82. package/dist/_vendor/platform-expo/react.js.map +1 -1
  83. package/dist/_vendor/platform-node/index.d.mts +23 -19
  84. package/dist/_vendor/platform-node/index.d.mts.map +1 -1
  85. package/dist/_vendor/platform-node/index.mjs +13 -5
  86. package/dist/_vendor/platform-node/index.mjs.map +1 -1
  87. package/dist/_vendor/platform-node/ipc-react.d.mts.map +1 -1
  88. package/dist/_vendor/platform-node/ipc-react.mjs +15 -2
  89. package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
  90. package/dist/_vendor/platform-node/ipc.d.mts +11 -35
  91. package/dist/_vendor/platform-node/ipc.d.mts.map +1 -1
  92. package/dist/_vendor/platform-node/ipc.mjs +3 -273
  93. package/dist/_vendor/platform-node/ipc.mjs.map +1 -1
  94. package/dist/_vendor/platform-web/external-change.d.ts +2 -1
  95. package/dist/_vendor/platform-web/external-change.d.ts.map +1 -1
  96. package/dist/_vendor/platform-web/external-change.js +2 -1
  97. package/dist/_vendor/platform-web/external-change.js.map +1 -1
  98. package/dist/_vendor/platform-web/index.d.ts +21 -21
  99. package/dist/_vendor/platform-web/index.d.ts.map +1 -1
  100. package/dist/_vendor/platform-web/index.js +44 -7
  101. package/dist/_vendor/platform-web/index.js.map +1 -1
  102. package/dist/_vendor/platform-web/react.d.ts.map +1 -1
  103. package/dist/_vendor/platform-web/react.js +29 -13
  104. package/dist/_vendor/platform-web/react.js.map +1 -1
  105. package/dist/_vendor/platform-web/worker.d.ts +11 -35
  106. package/dist/_vendor/platform-web/worker.d.ts.map +1 -1
  107. package/dist/_vendor/platform-web/worker.js +3 -267
  108. package/dist/_vendor/platform-web/worker.js.map +1 -1
  109. package/dist/_vendor/react/index.d.ts +36 -20
  110. package/dist/_vendor/react/index.d.ts.map +1 -1
  111. package/dist/_vendor/react/index.js +279 -57
  112. package/dist/_vendor/react/index.js.map +1 -1
  113. package/dist/_vendor/schema/definition.d.ts +48 -63
  114. package/dist/_vendor/schema/definition.d.ts.map +1 -1
  115. package/dist/_vendor/schema/definition.js +22 -39
  116. package/dist/_vendor/schema/definition.js.map +1 -1
  117. package/dist/_vendor/schema/index.d.ts +4 -4
  118. package/dist/_vendor/schema/index.js +2 -2
  119. package/dist/_vendor/schema/planner.d.ts +19 -2
  120. package/dist/_vendor/schema/planner.d.ts.map +1 -1
  121. package/dist/_vendor/schema/planner.js +79 -3
  122. package/dist/_vendor/schema/planner.js.map +1 -1
  123. package/dist/_vendor/schema/validators.d.ts +141 -121
  124. package/dist/_vendor/schema/validators.d.ts.map +1 -1
  125. package/dist/_vendor/schema/validators.js +300 -42
  126. package/dist/_vendor/schema/validators.js.map +1 -1
  127. package/dist/_vendor/svelte/index.d.ts +47 -19
  128. package/dist/_vendor/svelte/index.d.ts.map +1 -1
  129. package/dist/_vendor/svelte/index.js +250 -20
  130. package/dist/_vendor/svelte/index.js.map +1 -1
  131. package/dist/components.d.ts +2 -0
  132. package/dist/components.js +2 -0
  133. package/dist/index.d.ts +3 -2
  134. package/dist/index.js +2 -1
  135. package/package.json +8 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executionEngine.mjs","names":[],"sources":["../../../../src/runtime/internal/engines/executionEngine.ts"],"sourcesContent":["import type {\n FunctionArgsFromDefinition,\n FunctionKindFromDefinition,\n FunctionReference,\n FunctionResultFromDefinition,\n MisfirePolicy,\n SyncoreFunctionKind\n} from \"../../functions.js\";\nimport {\n toCanonicalComponentFunctionName,\n type SyncoreComponentFunctionMetadata\n} from \"../../components.js\";\nimport type {\n ActionCtx,\n CapabilityDescriptor,\n DevtoolsLiveQueryScope,\n DocumentForTable,\n ExecutionResult,\n FilterBuilder,\n ImpactScope,\n IndexRangeBuilder,\n InsertValueForTable,\n JsonObject,\n MutationCtx,\n PatchValueForTable,\n PaginationOptions,\n PaginationResult,\n QueryBuilder,\n QueryCtx,\n QueryExpression,\n RegisteredSyncoreFunction,\n SchedulerApi,\n SearchIndexBuilder,\n SearchQuery,\n SyncoreCapabilities,\n SyncoreClient,\n SyncoreDataModel,\n SyncoreDatabaseReader,\n SyncoreDatabaseWriter,\n SyncoreFunctionRegistry,\n SyncoreSqlDriver,\n SyncoreWatch,\n TableNames\n} from \"../../runtime.js\";\nimport type {\n AnyTableDefinition as SchemaAnyTableDefinition,\n TableIndexFields,\n TableIndexNames,\n TableSearchIndexConfig,\n TableSearchIndexNames\n} from \"@syncore/schema\";\nimport { DevtoolsEngine } from \"./devtoolsEngine.js\";\nimport { SchemaEngine } from \"./schemaEngine.js\";\nimport { StorageEngine } from \"./storageEngine.js\";\nimport { SchedulerEngine } from \"./schedulerEngine.js\";\nimport { ReactivityEngine } from \"./reactivityEngine.js\";\nimport {\n fieldExpression,\n normalizeOptionalArgs,\n omitSystemFields,\n quoteIdentifier,\n resolveSearchIndexTableName,\n splitSchedulerArgs,\n stableStringify,\n type DatabaseRow,\n type DependencyKey,\n type DevtoolsEventMeta,\n type ExecuteQueryBuilderOptions,\n type QuerySource,\n type RuntimeExecutionState\n} from \"./shared.js\";\nimport { generateId } from \"../../id.js\";\nimport type { Validator } from \"@syncore/schema\";\nimport {\n TransactionCoordinator,\n createEmptyExecutionResult\n} from \"../transactionCoordinator.js\";\nimport type { RuntimeStatusController } from \"../runtimeStatus.js\";\n\nconst DEFAULT_MISFIRE_POLICY: MisfirePolicy = { type: \"catch_up\" };\n\ntype OptionalArgsTuple<TArgs> =\n Record<never, never> extends TArgs ? [args?: TArgs] : [args: TArgs];\n\ntype ExecutionEngineDeps<TSchema extends SyncoreDataModel> = {\n runtimeId: string;\n functions: SyncoreFunctionRegistry;\n driver: SyncoreSqlDriver;\n capabilities: Readonly<SyncoreCapabilities>;\n capabilityDescriptors: ReadonlyArray<CapabilityDescriptor>;\n schema: SchemaEngine<TSchema>;\n storage: StorageEngine;\n scheduler: SchedulerEngine;\n reactivity: ReactivityEngine;\n devtools: DevtoolsEngine;\n transactionCoordinator: TransactionCoordinator;\n runtimeStatus: RuntimeStatusController;\n};\n\nclass RuntimeFilterBuilder implements FilterBuilder {\n eq(field: string, value: unknown): QueryExpression {\n return { type: \"condition\", condition: { field, operator: \"=\", value } };\n }\n\n gt(field: string, value: unknown): QueryExpression {\n return { type: \"condition\", condition: { field, operator: \">\", value } };\n }\n\n gte(field: string, value: unknown): QueryExpression {\n return { type: \"condition\", condition: { field, operator: \">=\", value } };\n }\n\n lt(field: string, value: unknown): QueryExpression {\n return { type: \"condition\", condition: { field, operator: \"<\", value } };\n }\n\n lte(field: string, value: unknown): QueryExpression {\n return { type: \"condition\", condition: { field, operator: \"<=\", value } };\n }\n\n and(...expressions: QueryExpression[]): QueryExpression {\n return { type: \"and\", expressions };\n }\n\n or(...expressions: QueryExpression[]): QueryExpression {\n return { type: \"or\", expressions };\n }\n}\n\nclass RuntimeIndexRangeBuilder<\n TFieldName extends string = string\n> implements IndexRangeBuilder<TFieldName> {\n private readonly conditions: Array<{\n field: string;\n operator: \"=\" | \">\" | \">=\" | \"<\" | \"<=\";\n value: unknown;\n }> = [];\n\n eq(field: TFieldName, value: unknown): IndexRangeBuilder<TFieldName> {\n this.conditions.push({ field, operator: \"=\", value });\n return this;\n }\n\n gt(field: TFieldName, value: unknown): IndexRangeBuilder<TFieldName> {\n this.conditions.push({ field, operator: \">\", value });\n return this;\n }\n\n gte(field: TFieldName, value: unknown): IndexRangeBuilder<TFieldName> {\n this.conditions.push({ field, operator: \">=\", value });\n return this;\n }\n\n lt(field: TFieldName, value: unknown): IndexRangeBuilder<TFieldName> {\n this.conditions.push({ field, operator: \"<\", value });\n return this;\n }\n\n lte(field: TFieldName, value: unknown): IndexRangeBuilder<TFieldName> {\n this.conditions.push({ field, operator: \"<=\", value });\n return this;\n }\n\n build() {\n return [...this.conditions];\n }\n}\n\nclass RuntimeSearchIndexBuilder<\n TSearchField extends string = string,\n TFilterField extends string = string\n> implements SearchIndexBuilder<TSearchField, TFilterField> {\n private searchField: string | undefined;\n private searchText: string | undefined;\n private readonly filters: Array<{\n field: string;\n operator: \"=\";\n value: unknown;\n }> = [];\n\n search(\n field: TSearchField,\n value: string\n ): SearchIndexBuilder<TSearchField, TFilterField> {\n this.searchField = field;\n this.searchText = value;\n return this;\n }\n\n eq(\n field: TFilterField,\n value: unknown\n ): SearchIndexBuilder<TSearchField, TFilterField> {\n this.filters.push({ field, operator: \"=\", value });\n return this;\n }\n\n build(): SearchQuery {\n if (!this.searchField || !this.searchText) {\n throw new Error(\"Search queries require a search field and search text.\");\n }\n return {\n searchField: this.searchField,\n searchText: this.searchText,\n filters: [...this.filters]\n };\n }\n}\n\nclass RuntimeQueryBuilder<\n TTable extends SchemaAnyTableDefinition,\n TDocument\n> implements QueryBuilder<TTable, TDocument> {\n private orderDirection: \"asc\" | \"desc\" = \"asc\";\n private source: QuerySource = { type: \"table\" };\n private filterExpression: QueryExpression | undefined;\n\n constructor(\n private readonly executeQuery: (\n options: ExecuteQueryBuilderOptions\n ) => Promise<TDocument[]>,\n private readonly tableName: string,\n private readonly dependencyCollector?: Set<DependencyKey>\n ) {}\n\n withIndex<TIndexName extends TableIndexNames<TTable>>(\n indexName: TIndexName,\n builder?: (\n range: IndexRangeBuilder<TableIndexFields<TTable, TIndexName>[number]>\n ) => IndexRangeBuilder<TableIndexFields<TTable, TIndexName>[number]>\n ): this {\n const indexRange = builder?.(new RuntimeIndexRangeBuilder()).build() ?? [];\n this.source = { type: \"index\", name: indexName, range: indexRange };\n return this;\n }\n\n withSearchIndex<TIndexName extends TableSearchIndexNames<TTable>>(\n indexName: TIndexName,\n builder: (\n search: SearchIndexBuilder<\n TableSearchIndexConfig<TTable, TIndexName>[\"searchField\"],\n TableSearchIndexConfig<TTable, TIndexName>[\"filterFields\"]\n >\n ) => SearchIndexBuilder<\n TableSearchIndexConfig<TTable, TIndexName>[\"searchField\"],\n TableSearchIndexConfig<TTable, TIndexName>[\"filterFields\"]\n >\n ): this {\n this.source = {\n type: \"search\",\n name: indexName,\n query: builder(new RuntimeSearchIndexBuilder()).build()\n };\n return this;\n }\n\n order(order: \"asc\" | \"desc\"): this {\n this.orderDirection = order;\n return this;\n }\n\n filter(builder: (filter: FilterBuilder) => QueryExpression): this {\n this.filterExpression = builder(new RuntimeFilterBuilder());\n return this;\n }\n\n async collect(): Promise<TDocument[]> {\n return this.execute();\n }\n\n async take(count: number): Promise<TDocument[]> {\n return this.execute({ limit: count });\n }\n\n async first(): Promise<TDocument | null> {\n const results = await this.execute({ limit: 1 });\n return results[0] ?? null;\n }\n\n async unique(): Promise<TDocument | null> {\n const results = await this.execute({ limit: 2 });\n if (results.length > 1) {\n throw new Error(\"Expected a unique result but found multiple rows.\");\n }\n return results[0] ?? null;\n }\n\n async paginate(\n options: PaginationOptions\n ): Promise<PaginationResult<TDocument>> {\n const offset = options.cursor ? Number.parseInt(options.cursor, 10) : 0;\n const page = await this.execute({ limit: options.numItems, offset });\n const nextCursor =\n page.length < options.numItems ? null : String(offset + page.length);\n return {\n page,\n cursor: nextCursor,\n isDone: nextCursor === null\n };\n }\n\n private async execute(options?: {\n limit?: number;\n offset?: number;\n }): Promise<TDocument[]> {\n this.dependencyCollector?.add(`table:${this.tableName}`);\n const queryOptions: ExecuteQueryBuilderOptions = {\n tableName: this.tableName,\n source: this.source,\n filterExpression: this.filterExpression,\n orderDirection: this.orderDirection\n };\n if (this.dependencyCollector) {\n queryOptions.dependencyCollector = this.dependencyCollector;\n }\n if (options?.limit !== undefined) {\n queryOptions.limit = options.limit;\n }\n if (options?.offset !== undefined) {\n queryOptions.offset = options.offset;\n }\n return this.executeQuery(queryOptions);\n }\n}\n\nexport class ExecutionEngine<\n TSchema extends SyncoreDataModel\n> {\n constructor(private readonly deps: ExecutionEngineDeps<TSchema>) {}\n\n createClient(): SyncoreClient {\n return {\n query: (reference, ...args) =>\n this.runQuery(reference, normalizeOptionalArgs(args) as JsonObject),\n mutation: (reference, ...args) =>\n this.runMutation(reference, normalizeOptionalArgs(args) as JsonObject),\n action: (reference, ...args) =>\n this.runAction(reference, normalizeOptionalArgs(args) as JsonObject),\n watchQuery: (reference, ...args) =>\n this.watchQuery(reference, normalizeOptionalArgs(args) as JsonObject),\n watchRuntimeStatus: () => this.deps.runtimeStatus.watch()\n };\n }\n\n watchQuery<TArgs, TResult>(\n reference: FunctionReference<\"query\", TArgs, TResult>,\n args: JsonObject = {}\n ): SyncoreWatch<TResult> {\n return this.deps.reactivity.watchQuery(reference, args);\n }\n\n async runQuery<TArgs, TResult>(\n reference: FunctionReference<\"query\", TArgs, TResult>,\n args: JsonObject = {},\n meta: DevtoolsEventMeta = {}\n ): Promise<TResult> {\n const definition = this.resolveFunction(reference, \"query\");\n const dependencyCollector = new Set<DependencyKey>();\n const startedAt = Date.now();\n const result = await this.invokeFunction<TResult>(definition, args, {\n mutationDepth: 0,\n changedTables: new Set<string>(),\n storageChanges: [],\n dependencyCollector,\n componentMetadata: definition.__syncoreComponent\n });\n\n this.deps.devtools.emit({\n type: \"query.executed\",\n runtimeId: this.deps.runtimeId,\n queryId: reference.name,\n functionName: reference.name,\n ...(definition.__syncoreComponent\n ? {\n componentPath: definition.__syncoreComponent.componentPath,\n componentName: definition.__syncoreComponent.componentName\n }\n : {}),\n dependencies: [...dependencyCollector],\n durationMs: Date.now() - startedAt,\n timestamp: Date.now(),\n ...(meta.origin ? { origin: meta.origin } : {})\n });\n\n return result;\n }\n\n async runMutation<TArgs, TResult>(\n reference: FunctionReference<\"mutation\", TArgs, TResult>,\n args: JsonObject = {},\n meta: DevtoolsEventMeta = {}\n ): Promise<TResult> {\n const definition = this.resolveFunction(reference, \"mutation\");\n const mutationId = generateId();\n const startedAt = Date.now();\n const execution = await this.deps.transactionCoordinator.runInTransaction(\n async (transactionState) =>\n this.invokeFunction<TResult>(definition, args, {\n mutationDepth: 1,\n changedTables: transactionState.changedTables,\n storageChanges: transactionState.storageChanges,\n componentMetadata: definition.__syncoreComponent\n })\n );\n\n await this.finalizeStatefulExecution(\n mutationId,\n execution,\n Date.now() - startedAt\n );\n\n this.deps.devtools.emit({\n type: \"mutation.committed\",\n runtimeId: this.deps.runtimeId,\n mutationId,\n functionName: reference.name,\n ...(definition.__syncoreComponent\n ? {\n componentPath: definition.__syncoreComponent.componentPath,\n componentName: definition.__syncoreComponent.componentName\n }\n : {}),\n changedTables: [...execution.changedTables],\n durationMs: Date.now() - startedAt,\n timestamp: Date.now(),\n ...(meta.origin ? { origin: meta.origin } : {})\n });\n\n return execution.result;\n }\n\n async runAction<TArgs, TResult>(\n reference: FunctionReference<\"action\", TArgs, TResult>,\n args: JsonObject = {},\n meta: DevtoolsEventMeta = {}\n ): Promise<TResult> {\n const definition = this.resolveFunction(reference, \"action\");\n const actionId = generateId();\n const startedAt = Date.now();\n const state = this.deps.transactionCoordinator.createState();\n\n try {\n const result = await this.invokeFunction<TResult>(definition, args, {\n mutationDepth: 0,\n changedTables: state.changedTables,\n storageChanges: state.storageChanges,\n componentMetadata: definition.__syncoreComponent\n });\n await this.finalizeStatefulExecution(\n actionId,\n createEmptyExecutionResult(result, state),\n Date.now() - startedAt\n );\n this.deps.devtools.emit({\n type: \"action.completed\",\n runtimeId: this.deps.runtimeId,\n actionId,\n functionName: reference.name,\n ...(definition.__syncoreComponent\n ? {\n componentPath: definition.__syncoreComponent.componentPath,\n componentName: definition.__syncoreComponent.componentName\n }\n : {}),\n durationMs: Date.now() - startedAt,\n timestamp: Date.now(),\n ...(meta.origin ? { origin: meta.origin } : {})\n });\n return result;\n } catch (error) {\n this.deps.devtools.emit({\n type: \"action.completed\",\n runtimeId: this.deps.runtimeId,\n actionId,\n functionName: reference.name,\n ...(definition.__syncoreComponent\n ? {\n componentPath: definition.__syncoreComponent.componentPath,\n componentName: definition.__syncoreComponent.componentName\n }\n : {}),\n durationMs: Date.now() - startedAt,\n timestamp: Date.now(),\n ...(meta.origin ? { origin: meta.origin } : {}),\n error: error instanceof Error ? error.message : String(error)\n });\n throw error;\n }\n }\n\n async runDevtoolsMutation<TResult>(\n callback: (ctx: { db: SyncoreDatabaseWriter<TSchema> }) => Promise<TResult>,\n meta: DevtoolsEventMeta = {}\n ): Promise<TResult> {\n const mutationId = generateId();\n const startedAt = Date.now();\n const execution = await this.deps.transactionCoordinator.runInTransaction(\n async (transactionState) =>\n callback({\n db: this.createDatabaseWriter({\n mutationDepth: 1,\n changedTables: transactionState.changedTables,\n storageChanges: transactionState.storageChanges\n })\n })\n );\n\n await this.finalizeStatefulExecution(\n mutationId,\n execution,\n Date.now() - startedAt\n );\n\n this.deps.devtools.emit({\n type: \"mutation.committed\",\n runtimeId: this.deps.runtimeId,\n mutationId,\n functionName: \"__devtools__/mutation\",\n changedTables: [...execution.changedTables],\n durationMs: Date.now() - startedAt,\n timestamp: Date.now(),\n ...(meta.origin ? { origin: meta.origin } : {})\n });\n return execution.result;\n }\n\n private async finalizeStatefulExecution<TResult>(\n executionId: string,\n execution: ExecutionResult<TResult>,\n durationMs: number\n ): Promise<void> {\n const changedScopes = collectChangedScopes(\n execution.changedTables,\n execution.storageChanges\n );\n if (changedScopes.size > 0) {\n await this.deps.reactivity.refreshQueriesForScopes(\n changedScopes,\n `Execution ${executionId} touched ${[...changedScopes].join(\", \")}`\n );\n }\n if (execution.changedTables.size > 0) {\n await this.deps.reactivity.publishExternalChange({\n scope: \"database\",\n reason: \"commit\",\n changedScopes: [...changedScopes].filter((scope) =>\n scope.startsWith(\"table:\")\n ),\n changedTables: [...execution.changedTables]\n });\n }\n await this.deps.reactivity.publishStorageChanges(execution.storageChanges);\n }\n\n async collectQueryDependencies(\n functionName: string,\n args: JsonObject\n ): Promise<Set<DependencyKey>> {\n const definition = this.resolveFunction(\n { kind: \"query\", name: functionName },\n \"query\"\n );\n const dependencyCollector = new Set<DependencyKey>();\n await this.invokeFunction(definition, args, {\n mutationDepth: 0,\n changedTables: new Set<string>(),\n storageChanges: [],\n dependencyCollector,\n componentMetadata: definition.__syncoreComponent\n });\n return dependencyCollector;\n }\n\n private async executeQueryBuilder<TDocument>(\n options: ExecuteQueryBuilderOptions\n ): Promise<TDocument[]> {\n const table = this.deps.schema.getTableDefinition(options.tableName);\n const params: unknown[] = [];\n const whereClauses: string[] = [];\n const orderClauses: string[] = [];\n let joinClause = \"\";\n const source = options.source;\n\n if (source.type === \"index\") {\n const index = table.indexes.find(\n (candidate) => candidate.name === source.name\n );\n if (!index) {\n throw new Error(\n `Unknown index \"${source.name}\" on table \"${options.tableName}\".`\n );\n }\n for (const condition of source.range) {\n whereClauses.push(this.renderCondition(\"t\", condition, params));\n }\n const primaryField = index.fields[0];\n if (primaryField) {\n orderClauses.push(\n `${fieldExpression(\"t\", primaryField)} ${options.orderDirection.toUpperCase()}`\n );\n }\n }\n\n if (source.type === \"search\") {\n const searchIndex = table.searchIndexes.find(\n (candidate) => candidate.name === source.name\n );\n if (!searchIndex) {\n throw new Error(\n `Unknown search index \"${source.name}\" on table \"${options.tableName}\".`\n );\n }\n if (searchIndex.searchField !== source.query.searchField) {\n throw new Error(\n `Search index \"${searchIndex.name}\" expects field \"${searchIndex.searchField}\".`\n );\n }\n if (this.deps.schema.isSearchIndexDisabled(options.tableName, searchIndex.name)) {\n whereClauses.push(\n `${fieldExpression(\"t\", searchIndex.searchField)} LIKE ?`\n );\n params.push(`%${source.query.searchText}%`);\n } else {\n const searchTableName = resolveSearchIndexTableName(\n options.tableName,\n searchIndex.name\n );\n joinClause = `JOIN ${quoteIdentifier(searchTableName)} s ON s._id = t._id`;\n whereClauses.push(`s.search_value MATCH ?`);\n params.push(source.query.searchText);\n }\n for (const condition of source.query.filters) {\n whereClauses.push(this.renderCondition(\"t\", condition, params));\n }\n }\n\n if (options.filterExpression) {\n whereClauses.push(\n this.renderExpression(\"t\", options.filterExpression, params)\n );\n }\n\n if (orderClauses.length === 0) {\n orderClauses.push(\n `t._creationTime ${options.orderDirection.toUpperCase()}`\n );\n }\n orderClauses.push(`t._id ${options.orderDirection.toUpperCase()}`);\n\n const sql = [\n `SELECT t._id, t._creationTime, t._json FROM ${quoteIdentifier(options.tableName)} t`,\n joinClause,\n whereClauses.length > 0 ? `WHERE ${whereClauses.join(\" AND \")}` : \"\",\n `ORDER BY ${orderClauses.join(\", \")}`,\n options.limit !== undefined ? `LIMIT ${options.limit}` : \"\",\n options.offset !== undefined ? `OFFSET ${options.offset}` : \"\"\n ]\n .filter(Boolean)\n .join(\" \");\n\n const rows = await this.deps.driver.all<DatabaseRow>(sql, params);\n return rows.map((row) =>\n this.deps.schema.deserializeDocument<TDocument>(options.tableName, row)\n );\n }\n\n private async invokeFunction<TResult>(\n definition: RegisteredSyncoreFunction,\n rawArgs: JsonObject,\n state: RuntimeExecutionState\n ): Promise<TResult> {\n const args = definition.argsValidator.parse(rawArgs) as JsonObject;\n const ctx = this.createContext(definition.kind, {\n ...state,\n componentMetadata:\n definition.__syncoreComponent ?? state.componentMetadata\n });\n const result = (await definition.handler(ctx, args)) as TResult;\n if (definition.returnsValidator) {\n return definition.returnsValidator.parse(result) as TResult;\n }\n return result;\n }\n\n private createContext(\n kind: SyncoreFunctionKind,\n state: RuntimeExecutionState\n ): QueryCtx<TSchema> | MutationCtx<TSchema> | ActionCtx<TSchema> {\n const db =\n kind === \"mutation\"\n ? this.createDatabaseWriter(state)\n : this.createDatabaseReader(state);\n const storage = this.deps.storage.createStorageApi(state);\n const scheduler = this.createSchedulerApi(state.componentMetadata);\n const callerMetadata = state.componentMetadata;\n\n return {\n db,\n storage,\n capabilities: this.deps.capabilities,\n capabilityDescriptors: this.deps.capabilityDescriptors,\n ...(callerMetadata\n ? {\n component: {\n path: callerMetadata.componentPath,\n name: callerMetadata.componentName,\n version: callerMetadata.version,\n capabilities: callerMetadata.grantedCapabilities\n }\n }\n : {}),\n scheduler,\n runQuery: <TArgs, TResult>(\n reference: FunctionReference<\"query\", TArgs, TResult>,\n ...args: OptionalArgsTuple<TArgs>\n ) =>\n this.runQuery(\n this.resolveReferenceForCaller(reference, \"query\", callerMetadata),\n normalizeOptionalArgs(args) as JsonObject\n ),\n runMutation: <TArgs, TResult>(\n reference: FunctionReference<\"mutation\", TArgs, TResult>,\n ...args: OptionalArgsTuple<TArgs>\n ) => {\n const resolvedReference = this.resolveReferenceForCaller(\n reference,\n \"mutation\",\n callerMetadata\n );\n const normalizedArgs = normalizeOptionalArgs(args);\n if (kind === \"mutation\") {\n return this.deps.driver.withSavepoint(\n `sp_${generateId().replace(/-/g, \"_\")}`,\n () =>\n this.invokeFunction<TResult>(\n this.resolveFunction(\n resolvedReference,\n \"mutation\",\n callerMetadata\n ),\n normalizedArgs as JsonObject,\n {\n mutationDepth: state.mutationDepth + 1,\n changedTables: state.changedTables,\n storageChanges: state.storageChanges,\n componentMetadata: callerMetadata\n }\n )\n );\n }\n return this.runMutation(resolvedReference, normalizedArgs as JsonObject);\n },\n runAction: <TArgs, TResult>(\n reference: FunctionReference<\"action\", TArgs, TResult>,\n ...args: OptionalArgsTuple<TArgs>\n ) =>\n this.runAction(\n this.resolveReferenceForCaller(reference, \"action\", callerMetadata),\n normalizeOptionalArgs(args) as JsonObject\n )\n } as QueryCtx<TSchema> | MutationCtx<TSchema> | ActionCtx<TSchema>;\n }\n\n private createDatabaseReader(\n state: RuntimeExecutionState\n ): SyncoreDatabaseReader<TSchema> {\n return {\n get: async <TTableName extends TableNames<TSchema>>(\n tableName: TTableName,\n id: string\n ) => {\n const scopedTableName = this.resolveTableName(\n tableName,\n state.componentMetadata\n );\n state.dependencyCollector?.add(`table:${scopedTableName}`);\n state.dependencyCollector?.add(`row:${scopedTableName}:${id}`);\n const row = await this.deps.driver.get<DatabaseRow>(\n `SELECT _id, _creationTime, _json FROM ${quoteIdentifier(scopedTableName)} WHERE _id = ?`,\n [id]\n );\n return row\n ? this.deps.schema.deserializeDocument<\n DocumentForTable<TSchema, TTableName>\n >(scopedTableName, row)\n : null;\n },\n query: <TTableName extends TableNames<TSchema>>(tableName: TTableName) =>\n new RuntimeQueryBuilder<\n TSchema[\"tables\"][TTableName],\n DocumentForTable<TSchema, TTableName>\n >(\n (options) =>\n this.executeQueryBuilder<DocumentForTable<TSchema, TTableName>>(\n {\n ...options,\n tableName: this.resolveTableName(\n tableName,\n state.componentMetadata\n )\n }\n ),\n this.resolveTableName(tableName, state.componentMetadata),\n state.dependencyCollector\n ),\n raw: <TValue>(sql: string, params?: unknown[]) =>\n this.deps.driver.all<TValue>(sql, params)\n };\n }\n\n private createDatabaseWriter(\n state: RuntimeExecutionState\n ): SyncoreDatabaseWriter<TSchema> {\n const reader = this.createDatabaseReader(state);\n\n return {\n ...reader,\n insert: async <TTableName extends TableNames<TSchema>>(\n tableName: TTableName,\n value: InsertValueForTable<TSchema, TTableName>\n ) => {\n const scopedTableName = this.resolveTableName(\n tableName,\n state.componentMetadata\n );\n const validated = this.deps.schema.validateDocument(\n scopedTableName,\n value as JsonObject\n );\n const id = generateId();\n const creationTime = Date.now();\n const json = stableStringify(validated);\n await this.deps.driver.run(\n `INSERT INTO ${quoteIdentifier(scopedTableName)} (_id, _creationTime, _json) VALUES (?, ?, ?)`,\n [id, creationTime, json]\n );\n await this.deps.schema.syncSearchIndexes(scopedTableName, {\n _id: id,\n _creationTime: creationTime,\n _json: json\n });\n state.changedTables.add(scopedTableName);\n return id;\n },\n patch: async <TTableName extends TableNames<TSchema>>(\n tableName: TTableName,\n id: string,\n value: PatchValueForTable<TSchema, TTableName>\n ) => {\n const scopedTableName = this.resolveTableName(\n tableName,\n state.componentMetadata\n );\n const current = await reader.get(tableName, id);\n if (!current) {\n throw new Error(`Document \"${id}\" does not exist in \"${scopedTableName}\".`);\n }\n const merged: JsonObject = { ...omitSystemFields(current), ...value };\n for (const key of Object.keys(merged)) {\n if (merged[key] === undefined) {\n delete merged[key];\n }\n }\n const validated = this.deps.schema.validateDocument(\n scopedTableName,\n merged\n );\n await this.deps.driver.run(\n `UPDATE ${quoteIdentifier(scopedTableName)} SET _json = ? WHERE _id = ?`,\n [stableStringify(validated), id]\n );\n const row = await this.deps.driver.get<DatabaseRow>(\n `SELECT _id, _creationTime, _json FROM ${quoteIdentifier(scopedTableName)} WHERE _id = ?`,\n [id]\n );\n if (row) {\n await this.deps.schema.syncSearchIndexes(scopedTableName, row);\n }\n state.changedTables.add(scopedTableName);\n },\n replace: async <TTableName extends TableNames<TSchema>>(\n tableName: TTableName,\n id: string,\n value: InsertValueForTable<TSchema, TTableName>\n ) => {\n const scopedTableName = this.resolveTableName(\n tableName,\n state.componentMetadata\n );\n const validated = this.deps.schema.validateDocument(\n scopedTableName,\n value as JsonObject\n );\n await this.deps.driver.run(\n `UPDATE ${quoteIdentifier(scopedTableName)} SET _json = ? WHERE _id = ?`,\n [stableStringify(validated), id]\n );\n const row = await this.deps.driver.get<DatabaseRow>(\n `SELECT _id, _creationTime, _json FROM ${quoteIdentifier(scopedTableName)} WHERE _id = ?`,\n [id]\n );\n if (!row) {\n throw new Error(`Document \"${id}\" does not exist in \"${scopedTableName}\".`);\n }\n await this.deps.schema.syncSearchIndexes(scopedTableName, row);\n state.changedTables.add(scopedTableName);\n },\n delete: async <TTableName extends TableNames<TSchema>>(\n tableName: TTableName,\n id: string\n ) => {\n const scopedTableName = this.resolveTableName(\n tableName,\n state.componentMetadata\n );\n await this.deps.driver.run(\n `DELETE FROM ${quoteIdentifier(scopedTableName)} WHERE _id = ?`,\n [id]\n );\n await this.deps.schema.removeSearchIndexes(scopedTableName, id);\n state.changedTables.add(scopedTableName);\n }\n };\n }\n\n private createSchedulerApi(\n componentMetadata?: SyncoreComponentFunctionMetadata\n ): SchedulerApi {\n return {\n runAfter: async (delayMs, reference, ...args) => {\n if (\n componentMetadata &&\n !componentMetadata.grantedCapabilities.includes(\"scheduler\")\n ) {\n throw new Error(\n `Component ${JSON.stringify(componentMetadata.componentPath)} does not have scheduler capability.`\n );\n }\n const schedulerArgs = splitSchedulerArgs(args);\n const functionArgs = schedulerArgs[0];\n const misfirePolicy = schedulerArgs[1] ?? DEFAULT_MISFIRE_POLICY;\n const resolvedReference = this.resolveReferenceForCaller(\n reference,\n reference.kind,\n componentMetadata\n );\n return this.deps.scheduler.scheduleJob(\n Date.now() + delayMs,\n resolvedReference,\n functionArgs,\n misfirePolicy,\n componentMetadata\n ? `component:${componentMetadata.componentPath}:`\n : undefined\n );\n },\n runAt: async (timestamp, reference, ...args) => {\n if (\n componentMetadata &&\n !componentMetadata.grantedCapabilities.includes(\"scheduler\")\n ) {\n throw new Error(\n `Component ${JSON.stringify(componentMetadata.componentPath)} does not have scheduler capability.`\n );\n }\n const schedulerArgs = splitSchedulerArgs(args);\n const functionArgs = schedulerArgs[0];\n const misfirePolicy = schedulerArgs[1] ?? DEFAULT_MISFIRE_POLICY;\n const value =\n timestamp instanceof Date ? timestamp.getTime() : timestamp;\n const resolvedReference = this.resolveReferenceForCaller(\n reference,\n reference.kind,\n componentMetadata\n );\n return this.deps.scheduler.scheduleJob(\n value,\n resolvedReference,\n functionArgs,\n misfirePolicy,\n componentMetadata\n ? `component:${componentMetadata.componentPath}:`\n : undefined\n );\n },\n cancel: async (id) => {\n await this.deps.scheduler.cancelScheduledJob(id);\n }\n };\n }\n\n private resolveFunction<TKind extends SyncoreFunctionKind>(\n reference: FunctionReference<TKind, unknown, unknown>,\n expectedKind: TKind,\n callerMetadata?: SyncoreComponentFunctionMetadata\n ): RegisteredSyncoreFunction & {\n kind: TKind;\n } {\n const resolvedReference = this.resolveReferenceForCaller(\n reference,\n expectedKind,\n callerMetadata\n );\n const definition = this.deps.functions[resolvedReference.name];\n if (!definition) {\n throw new Error(`Unknown function \"${resolvedReference.name}\".`);\n }\n if (definition.kind !== expectedKind) {\n throw new Error(\n `Function \"${resolvedReference.name}\" is a ${definition.kind}, expected ${expectedKind}.`\n );\n }\n const metadata = definition.__syncoreComponent;\n if (metadata?.visibility === \"internal\") {\n if (!callerMetadata) {\n throw new Error(\n `Function \"${resolvedReference.name}\" is internal to component \"${metadata.componentPath}\".`\n );\n }\n if (callerMetadata.componentPath !== metadata.componentPath) {\n throw new Error(\n `Function \"${resolvedReference.name}\" is internal to component \"${metadata.componentPath}\" and cannot be called from \"${callerMetadata.componentPath}\".`\n );\n }\n }\n return definition as RegisteredSyncoreFunction & {\n kind: TKind;\n };\n }\n\n private renderExpression(\n tableAlias: string,\n expression: QueryExpression,\n params: unknown[]\n ): string {\n if (expression.type === \"condition\") {\n return this.renderCondition(tableAlias, expression.condition, params);\n }\n const separator = expression.type === \"and\" ? \" AND \" : \" OR \";\n return `(${expression.expressions\n .map((child) => this.renderExpression(tableAlias, child, params))\n .join(separator)})`;\n }\n\n private renderCondition(\n tableAlias: string,\n condition: {\n field: string;\n operator: \"=\" | \">\" | \">=\" | \"<\" | \"<=\";\n value: unknown;\n },\n params: unknown[]\n ): string {\n params.push(condition.value);\n return `${fieldExpression(tableAlias, condition.field)} ${condition.operator} ?`;\n }\n\n private resolveReferenceForCaller<TKind extends SyncoreFunctionKind>(\n reference: FunctionReference<TKind, unknown, unknown>,\n expectedKind: TKind,\n callerMetadata?: SyncoreComponentFunctionMetadata\n ): FunctionReference<TKind, unknown, unknown> {\n if (!callerMetadata) {\n return reference;\n }\n\n if (reference.name.startsWith(\"components/\")) {\n return reference;\n }\n\n const bindingMatch = /^binding:([^/]+)\\/(.+)$/.exec(reference.name);\n if (bindingMatch) {\n const bindingName = bindingMatch[1]!;\n const localName = bindingMatch[2]!;\n const targetComponentPath = callerMetadata.bindings[bindingName];\n if (!targetComponentPath) {\n throw new Error(\n `Component ${JSON.stringify(callerMetadata.componentPath)} does not define binding ${JSON.stringify(bindingName)}.`\n );\n }\n const canonicalName = toCanonicalComponentFunctionName(\n targetComponentPath,\n \"public\",\n localName\n );\n return {\n kind: expectedKind,\n name: canonicalName\n };\n }\n\n const internalName = toCanonicalComponentFunctionName(\n callerMetadata.componentPath,\n \"internal\",\n reference.name\n );\n if (this.deps.functions[internalName]) {\n return {\n kind: expectedKind,\n name: internalName\n };\n }\n\n const publicName = toCanonicalComponentFunctionName(\n callerMetadata.componentPath,\n \"public\",\n reference.name\n );\n if (this.deps.functions[publicName]) {\n return {\n kind: expectedKind,\n name: publicName\n };\n }\n\n return reference;\n }\n\n private resolveTableName<TTableName extends string>(\n tableName: TTableName,\n componentMetadata?: SyncoreComponentFunctionMetadata\n ): TTableName {\n if (!componentMetadata) {\n return tableName;\n }\n\n const scopedTableName = componentMetadata.localTables[tableName];\n if (!scopedTableName) {\n throw new Error(\n `Table ${JSON.stringify(tableName)} is not available inside component ${JSON.stringify(componentMetadata.componentPath)}.`\n );\n }\n\n return scopedTableName as TTableName;\n }\n}\n\nfunction collectChangedScopes(\n changedTables: Set<string>,\n storageChanges: Array<{\n storageId: string;\n reason: \"storage-put\" | \"storage-delete\";\n }>\n): Set<ImpactScope> {\n return new Set<ImpactScope>([\n ...[...changedTables].map((tableName) => `table:${tableName}` as ImpactScope),\n ...storageChanges.map(\n (change) => `storage:${change.storageId}` as ImpactScope\n )\n ]);\n}\n"],"mappings":"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"}
@@ -0,0 +1,186 @@
1
+ import { parseCanonicalComponentFunctionName } from "./shared.mjs";
2
+ //#region src/runtime/internal/engines/reactivityEngine.ts
3
+ var ReactivityEngine = class {
4
+ activeQueries = /* @__PURE__ */ new Map();
5
+ detachExternalChangeListener;
6
+ pendingExternalChangePromise;
7
+ queuedExternalChange;
8
+ constructor(deps) {
9
+ this.deps = deps;
10
+ }
11
+ start() {
12
+ this.detachExternalChangeListener = this.deps.externalChangeSignal?.subscribe((event) => {
13
+ this.handleExternalChangeEvent(event);
14
+ });
15
+ }
16
+ stop() {
17
+ this.detachExternalChangeListener?.();
18
+ this.detachExternalChangeListener = void 0;
19
+ }
20
+ getActiveQueryInfos() {
21
+ return [...this.activeQueries.values()].map((query) => {
22
+ const componentFunction = parseCanonicalComponentFunctionName(query.functionName);
23
+ return {
24
+ id: query.id,
25
+ functionName: query.functionName,
26
+ ...componentFunction ? {
27
+ owner: "component",
28
+ componentPath: componentFunction.componentPath
29
+ } : { owner: "root" },
30
+ dependencyKeys: [...query.dependencyKeys],
31
+ lastRunAt: query.lastRunAt
32
+ };
33
+ });
34
+ }
35
+ watchQuery(reference, args = {}) {
36
+ const key = this.createActiveQueryKey(reference.name, args);
37
+ let record = this.activeQueries.get(key);
38
+ if (!record) {
39
+ record = {
40
+ id: key,
41
+ functionName: reference.name,
42
+ args,
43
+ listeners: /* @__PURE__ */ new Set(),
44
+ consumers: 0,
45
+ dependencyKeys: /* @__PURE__ */ new Set(),
46
+ lastResult: void 0,
47
+ lastError: void 0,
48
+ lastRunAt: 0
49
+ };
50
+ this.activeQueries.set(key, record);
51
+ this.rerunActiveQuery(record);
52
+ }
53
+ const activeRecord = record;
54
+ activeRecord.consumers += 1;
55
+ let disposed = false;
56
+ const ownedListeners = /* @__PURE__ */ new Set();
57
+ return {
58
+ onUpdate: (callback) => {
59
+ activeRecord.listeners.add(callback);
60
+ ownedListeners.add(callback);
61
+ queueMicrotask(callback);
62
+ return () => {
63
+ activeRecord.listeners.delete(callback);
64
+ ownedListeners.delete(callback);
65
+ };
66
+ },
67
+ localQueryResult: () => activeRecord.lastResult,
68
+ localQueryError: () => activeRecord.lastError,
69
+ dispose: () => {
70
+ if (disposed) return;
71
+ disposed = true;
72
+ for (const callback of ownedListeners) activeRecord.listeners.delete(callback);
73
+ ownedListeners.clear();
74
+ activeRecord.consumers = Math.max(0, activeRecord.consumers - 1);
75
+ if (activeRecord.consumers === 0) this.activeQueries.delete(key);
76
+ }
77
+ };
78
+ }
79
+ async refreshInvalidatedQueries(changedTables, mutationId) {
80
+ const impactedScopes = new Set([...changedTables].map((tableName) => `table:${tableName}`));
81
+ await this.refreshQueriesForScopes(impactedScopes, `Mutation ${mutationId} changed ${[...changedTables].join(", ")}`);
82
+ }
83
+ async refreshQueriesForScopes(scopes, reason) {
84
+ const scopeSet = new Set(scopes);
85
+ if (scopeSet.size === 0) return;
86
+ for (const query of this.activeQueries.values()) {
87
+ if (![...scopeSet].some((scope) => query.dependencyKeys.has(scope))) continue;
88
+ this.deps.devtools.emit({
89
+ type: "query.invalidated",
90
+ runtimeId: this.deps.runtimeId,
91
+ queryId: query.id,
92
+ ...parseCanonicalComponentFunctionName(query.functionName) ? { componentPath: parseCanonicalComponentFunctionName(query.functionName).componentPath } : {},
93
+ reason,
94
+ timestamp: Date.now()
95
+ });
96
+ await this.rerunActiveQuery(query);
97
+ }
98
+ }
99
+ async publishExternalChange(event) {
100
+ const changedScopes = resolveChangedScopes(event);
101
+ if (changedScopes.size === 0) throw new Error(`Syncore cannot publish external change "${event.reason}" without precise impact scopes.`);
102
+ await this.deps.externalChangeSignal?.publish({
103
+ ...event,
104
+ changedScopes: [...changedScopes],
105
+ sourceId: this.deps.externalChangeSourceId,
106
+ timestamp: Date.now()
107
+ });
108
+ }
109
+ async publishStorageChanges(storageChanges) {
110
+ for (const change of storageChanges) await this.publishExternalChange({
111
+ scope: "storage",
112
+ reason: change.reason,
113
+ changedScopes: [`storage:${change.storageId}`],
114
+ storageIds: [change.storageId]
115
+ });
116
+ }
117
+ async publishDatabaseReconcile() {
118
+ throw new Error("Syncore database reconcile without precise impact scopes is unsupported.");
119
+ }
120
+ async rerunActiveQuery(record) {
121
+ record.dependencyKeys.clear();
122
+ try {
123
+ record.lastResult = await this.deps.runQuery({
124
+ kind: "query",
125
+ name: record.functionName
126
+ }, record.args);
127
+ record.lastError = void 0;
128
+ record.lastRunAt = Date.now();
129
+ record.dependencyKeys = await this.deps.collectQueryDependencies(record.functionName, record.args);
130
+ } catch (error) {
131
+ record.lastError = error;
132
+ }
133
+ for (const listener of record.listeners) listener();
134
+ }
135
+ async handleExternalChangeEvent(event) {
136
+ if (event.sourceId === this.deps.externalChangeSourceId) return;
137
+ const result = this.deps.externalChangeApplier ? await this.deps.externalChangeApplier.applyExternalChange(event) : {
138
+ databaseChanged: event.scope === "database" || event.scope === "all",
139
+ storageChanged: event.scope === "storage" || event.scope === "all",
140
+ changedScopes: [...resolveChangedScopes(event)]
141
+ };
142
+ await this.processExternalChangeResult(result);
143
+ }
144
+ async processExternalChangeResult(result) {
145
+ const changedScopes = new Set(result.changedScopes);
146
+ if (changedScopes.size === 0) return;
147
+ if (this.pendingExternalChangePromise) {
148
+ this.queuedExternalChange = { changedScopes: new Set([...this.queuedExternalChange?.changedScopes ?? [], ...changedScopes]) };
149
+ return this.pendingExternalChangePromise;
150
+ }
151
+ this.pendingExternalChangePromise = (async () => {
152
+ await this.refreshQueriesForScopes(changedScopes, `External change touched ${[...changedScopes].join(", ")}`);
153
+ })();
154
+ try {
155
+ await this.pendingExternalChangePromise;
156
+ } finally {
157
+ this.pendingExternalChangePromise = void 0;
158
+ const queued = this.queuedExternalChange;
159
+ this.queuedExternalChange = void 0;
160
+ if (queued) await this.processExternalChangeResult({ changedScopes: [...queued.changedScopes] });
161
+ }
162
+ }
163
+ createActiveQueryKey(name, args) {
164
+ return `${name}:${stableStringify(args)}`;
165
+ }
166
+ };
167
+ function resolveChangedScopes(event) {
168
+ if (Array.isArray(event.changedScopes) && event.changedScopes.length > 0) return new Set(event.changedScopes);
169
+ const scopes = /* @__PURE__ */ new Set();
170
+ for (const tableName of event.changedTables ?? []) scopes.add(`table:${tableName}`);
171
+ for (const storageId of event.storageIds ?? []) scopes.add(`storage:${storageId}`);
172
+ if (scopes.size === 0 && event.scope !== void 0) throw new Error(`Syncore external change scope "${event.scope}" did not provide precise impact scopes.`);
173
+ return scopes;
174
+ }
175
+ function stableStringify(value) {
176
+ return JSON.stringify(sortValue(value));
177
+ }
178
+ function sortValue(value) {
179
+ if (Array.isArray(value)) return value.map(sortValue);
180
+ if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, nested]) => [key, sortValue(nested)]));
181
+ return value;
182
+ }
183
+ //#endregion
184
+ export { ReactivityEngine };
185
+
186
+ //# sourceMappingURL=reactivityEngine.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reactivityEngine.mjs","names":[],"sources":["../../../../src/runtime/internal/engines/reactivityEngine.ts"],"sourcesContent":["import type {\n SyncoreActiveQueryInfo,\n SyncoreDevtoolsEvent\n} from \"@syncore/devtools-protocol\";\nimport type { FunctionReference } from \"../../functions.js\";\nimport type {\n ImpactScope,\n JsonObject,\n SyncoreExternalChangeApplier,\n SyncoreExternalChangeEvent,\n SyncoreExternalChangeSignal,\n SyncoreWatch\n} from \"../../runtime.js\";\nimport { DevtoolsEngine } from \"./devtoolsEngine.js\";\nimport type {\n ActiveQueryRecord,\n DependencyKey\n} from \"./shared.js\";\nimport { parseCanonicalComponentFunctionName } from \"./shared.js\";\n\ntype ReactivityEngineDeps = {\n runtimeId: string;\n externalChangeSourceId: string;\n externalChangeSignal?: SyncoreExternalChangeSignal;\n externalChangeApplier?: SyncoreExternalChangeApplier;\n devtools: DevtoolsEngine;\n runQuery: <TResult>(\n reference: FunctionReference<\"query\", unknown, TResult>,\n args: JsonObject\n ) => Promise<TResult>;\n collectQueryDependencies: (\n functionName: string,\n args: JsonObject\n ) => Promise<Set<DependencyKey>>;\n};\n\nexport class ReactivityEngine {\n private readonly activeQueries = new Map<string, ActiveQueryRecord>();\n private detachExternalChangeListener: (() => void) | undefined;\n private pendingExternalChangePromise: Promise<void> | undefined;\n private queuedExternalChange:\n | {\n changedScopes: Set<ImpactScope>;\n }\n | undefined;\n\n constructor(private readonly deps: ReactivityEngineDeps) {}\n\n start(): void {\n this.detachExternalChangeListener =\n this.deps.externalChangeSignal?.subscribe((event) => {\n void this.handleExternalChangeEvent(event);\n });\n }\n\n stop(): void {\n this.detachExternalChangeListener?.();\n this.detachExternalChangeListener = undefined;\n }\n\n getActiveQueryInfos(): SyncoreActiveQueryInfo[] {\n return [...this.activeQueries.values()].map((query) => {\n const componentFunction = parseCanonicalComponentFunctionName(\n query.functionName\n );\n return {\n id: query.id,\n functionName: query.functionName,\n ...(componentFunction\n ? {\n owner: \"component\" as const,\n componentPath: componentFunction.componentPath\n }\n : {\n owner: \"root\" as const\n }),\n dependencyKeys: [...query.dependencyKeys],\n lastRunAt: query.lastRunAt\n };\n });\n }\n\n watchQuery<TArgs, TResult>(\n reference: FunctionReference<\"query\", TArgs, TResult>,\n args: JsonObject = {}\n ): SyncoreWatch<TResult> {\n const key = this.createActiveQueryKey(reference.name, args);\n let record = this.activeQueries.get(key);\n\n if (!record) {\n record = {\n id: key,\n functionName: reference.name,\n args,\n listeners: new Set<() => void>(),\n consumers: 0,\n dependencyKeys: new Set<DependencyKey>(),\n lastResult: undefined,\n lastError: undefined,\n lastRunAt: 0\n };\n this.activeQueries.set(key, record);\n void this.rerunActiveQuery(record);\n }\n\n const activeRecord = record;\n activeRecord.consumers += 1;\n let disposed = false;\n const ownedListeners = new Set<() => void>();\n\n return {\n onUpdate: (callback) => {\n activeRecord.listeners.add(callback);\n ownedListeners.add(callback);\n queueMicrotask(callback);\n return () => {\n activeRecord.listeners.delete(callback);\n ownedListeners.delete(callback);\n };\n },\n localQueryResult: () => activeRecord.lastResult as TResult | undefined,\n localQueryError: () => activeRecord.lastError,\n dispose: () => {\n if (disposed) {\n return;\n }\n disposed = true;\n for (const callback of ownedListeners) {\n activeRecord.listeners.delete(callback);\n }\n ownedListeners.clear();\n activeRecord.consumers = Math.max(0, activeRecord.consumers - 1);\n if (activeRecord.consumers === 0) {\n this.activeQueries.delete(key);\n }\n }\n };\n }\n\n async refreshInvalidatedQueries(\n changedTables: Set<string>,\n mutationId: string\n ): Promise<void> {\n const impactedScopes = new Set(\n [...changedTables].map((tableName) => `table:${tableName}` as ImpactScope)\n );\n await this.refreshQueriesForScopes(\n impactedScopes,\n `Mutation ${mutationId} changed ${[...changedTables].join(\", \")}`\n );\n }\n\n async refreshQueriesForScopes(\n scopes: Iterable<ImpactScope>,\n reason: string\n ): Promise<void> {\n const scopeSet = new Set(scopes);\n if (scopeSet.size === 0) {\n return;\n }\n for (const query of this.activeQueries.values()) {\n const needsRefresh = [...scopeSet].some((scope) =>\n query.dependencyKeys.has(scope)\n );\n if (!needsRefresh) {\n continue;\n }\n this.deps.devtools.emit({\n type: \"query.invalidated\",\n runtimeId: this.deps.runtimeId,\n queryId: query.id,\n ...(parseCanonicalComponentFunctionName(query.functionName)\n ? {\n componentPath: parseCanonicalComponentFunctionName(query.functionName)!\n .componentPath\n }\n : {}),\n reason,\n timestamp: Date.now()\n });\n await this.rerunActiveQuery(query);\n }\n }\n\n async publishExternalChange(\n event: Omit<SyncoreExternalChangeEvent, \"sourceId\" | \"timestamp\">\n ): Promise<void> {\n const changedScopes = resolveChangedScopes(event);\n if (changedScopes.size === 0) {\n throw new Error(\n `Syncore cannot publish external change \"${event.reason}\" without precise impact scopes.`\n );\n }\n await this.deps.externalChangeSignal?.publish({\n ...event,\n changedScopes: [...changedScopes],\n sourceId: this.deps.externalChangeSourceId,\n timestamp: Date.now()\n });\n }\n\n async publishStorageChanges(\n storageChanges: Array<{\n storageId: string;\n reason: \"storage-put\" | \"storage-delete\";\n }>\n ): Promise<void> {\n for (const change of storageChanges) {\n await this.publishExternalChange({\n scope: \"storage\",\n reason: change.reason,\n changedScopes: [`storage:${change.storageId}`],\n storageIds: [change.storageId]\n });\n }\n }\n\n async publishDatabaseReconcile(): Promise<void> {\n throw new Error(\n \"Syncore database reconcile without precise impact scopes is unsupported.\"\n );\n }\n\n private async rerunActiveQuery(record: ActiveQueryRecord): Promise<void> {\n record.dependencyKeys.clear();\n try {\n const result = await this.deps.runQuery(\n { kind: \"query\", name: record.functionName },\n record.args\n );\n record.lastResult = result;\n record.lastError = undefined;\n record.lastRunAt = Date.now();\n record.dependencyKeys = await this.deps.collectQueryDependencies(\n record.functionName,\n record.args\n );\n } catch (error) {\n record.lastError = error as Error;\n }\n for (const listener of record.listeners) {\n listener();\n }\n }\n\n private async handleExternalChangeEvent(\n event: SyncoreExternalChangeEvent\n ): Promise<void> {\n if (event.sourceId === this.deps.externalChangeSourceId) {\n return;\n }\n const result = this.deps.externalChangeApplier\n ? await this.deps.externalChangeApplier.applyExternalChange(event)\n : {\n databaseChanged: event.scope === \"database\" || event.scope === \"all\",\n storageChanged: event.scope === \"storage\" || event.scope === \"all\",\n changedScopes: [...resolveChangedScopes(event)]\n };\n await this.processExternalChangeResult(result);\n }\n\n private async processExternalChangeResult(result: {\n changedScopes: ImpactScope[];\n }): Promise<void> {\n const changedScopes = new Set(result.changedScopes);\n if (changedScopes.size === 0) {\n return;\n }\n if (this.pendingExternalChangePromise) {\n this.queuedExternalChange = {\n changedScopes: new Set([\n ...(this.queuedExternalChange?.changedScopes ?? []),\n ...changedScopes\n ])\n };\n return this.pendingExternalChangePromise;\n }\n\n this.pendingExternalChangePromise = (async () => {\n await this.refreshQueriesForScopes(\n changedScopes,\n `External change touched ${[...changedScopes].join(\", \")}`\n );\n })();\n\n try {\n await this.pendingExternalChangePromise;\n } finally {\n this.pendingExternalChangePromise = undefined;\n const queued = this.queuedExternalChange;\n this.queuedExternalChange = undefined;\n if (queued) {\n await this.processExternalChangeResult({\n changedScopes: [...queued.changedScopes]\n });\n }\n }\n }\n\n private createActiveQueryKey(name: string, args: JsonObject): string {\n return `${name}:${stableStringify(args)}`;\n }\n}\n\nfunction resolveChangedScopes(\n event: Pick<\n SyncoreExternalChangeEvent,\n \"scope\" | \"changedScopes\" | \"changedTables\" | \"storageIds\"\n >\n): Set<ImpactScope> {\n if (Array.isArray(event.changedScopes) && event.changedScopes.length > 0) {\n return new Set(event.changedScopes);\n }\n\n const scopes = new Set<ImpactScope>();\n for (const tableName of event.changedTables ?? []) {\n scopes.add(`table:${tableName}`);\n }\n for (const storageId of event.storageIds ?? []) {\n scopes.add(`storage:${storageId}`);\n }\n\n if (scopes.size === 0 && event.scope !== undefined) {\n throw new Error(\n `Syncore external change scope \"${event.scope}\" did not provide precise impact scopes.`\n );\n }\n\n return scopes;\n}\n\nfunction stableStringify(value: unknown): string {\n return JSON.stringify(sortValue(value));\n}\n\nfunction sortValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(sortValue);\n }\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, sortValue(nested)])\n );\n }\n return value;\n}\n"],"mappings":"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"}
@@ -0,0 +1,220 @@
1
+ import { generateId } from "../../id.mjs";
2
+ import { computeNextRun, parseMisfirePolicy, shouldRunMissedJob, stableStringify } from "./shared.mjs";
3
+ //#region src/runtime/internal/engines/schedulerEngine.ts
4
+ var SchedulerEngine = class {
5
+ timer;
6
+ constructor(deps) {
7
+ this.deps = deps;
8
+ }
9
+ async prepare() {
10
+ await this.deps.driver.exec(`
11
+ CREATE TABLE IF NOT EXISTS "_scheduled_functions" (
12
+ id TEXT PRIMARY KEY,
13
+ function_name TEXT NOT NULL,
14
+ function_kind TEXT NOT NULL,
15
+ args_json TEXT NOT NULL,
16
+ status TEXT NOT NULL,
17
+ run_at INTEGER NOT NULL,
18
+ created_at INTEGER NOT NULL,
19
+ updated_at INTEGER NOT NULL,
20
+ recurring_name TEXT,
21
+ schedule_json TEXT,
22
+ timezone TEXT,
23
+ misfire_policy TEXT NOT NULL,
24
+ last_run_at INTEGER,
25
+ window_ms INTEGER
26
+ );
27
+ `);
28
+ }
29
+ startPolling() {
30
+ if (this.timer) return;
31
+ this.timer = setInterval(() => {
32
+ this.processDueJobs();
33
+ }, this.deps.pollIntervalMs);
34
+ }
35
+ stopPolling() {
36
+ if (!this.timer) return;
37
+ clearInterval(this.timer);
38
+ this.timer = void 0;
39
+ }
40
+ async scheduleJob(runAt, reference, args, misfirePolicy, namespacePrefix) {
41
+ const id = `${namespacePrefix ?? ""}${generateId()}`;
42
+ const now = Date.now();
43
+ await this.deps.driver.run(`INSERT INTO "_scheduled_functions"
44
+ (id, function_name, function_kind, args_json, status, run_at, created_at, updated_at, recurring_name, schedule_json, timezone, misfire_policy, last_run_at, window_ms)
45
+ VALUES (?, ?, ?, ?, 'scheduled', ?, ?, ?, NULL, NULL, NULL, ?, NULL, ?)`, [
46
+ id,
47
+ reference.name,
48
+ reference.kind,
49
+ stableStringify(args),
50
+ runAt,
51
+ now,
52
+ now,
53
+ misfirePolicy.type,
54
+ misfirePolicy.type === "windowed" ? misfirePolicy.windowMs : null
55
+ ]);
56
+ this.notifySchedulerJobsChanged();
57
+ return id;
58
+ }
59
+ async cancelScheduledJob(id) {
60
+ if (((await this.deps.driver.run(`UPDATE "_scheduled_functions"
61
+ SET status = 'cancelled', updated_at = ?
62
+ WHERE id = ? AND status = 'scheduled'`, [Date.now(), id])).changes ?? 0) > 0) {
63
+ this.notifySchedulerJobsChanged();
64
+ return true;
65
+ }
66
+ return false;
67
+ }
68
+ async updateScheduledJob(options) {
69
+ const existing = await this.deps.driver.get(`SELECT status, recurring_name FROM "_scheduled_functions" WHERE id = ?`, [options.id]);
70
+ if (!existing || existing.status !== "scheduled" || !existing.recurring_name) return false;
71
+ const now = Date.now();
72
+ const runAt = options.runAt ?? computeNextRun(options.schedule, now);
73
+ if (((await this.deps.driver.run(`UPDATE "_scheduled_functions"
74
+ SET args_json = ?, run_at = ?, updated_at = ?, schedule_json = ?, timezone = ?, misfire_policy = ?, window_ms = ?
75
+ WHERE id = ? AND status = 'scheduled' AND recurring_name IS NOT NULL`, [
76
+ stableStringify(options.args),
77
+ runAt,
78
+ now,
79
+ stableStringify(options.schedule),
80
+ "timezone" in options.schedule ? options.schedule.timezone ?? null : null,
81
+ options.misfirePolicy.type,
82
+ options.misfirePolicy.type === "windowed" ? options.misfirePolicy.windowMs : null,
83
+ options.id
84
+ ])).changes ?? 0) > 0) {
85
+ this.notifySchedulerJobsChanged();
86
+ return true;
87
+ }
88
+ return false;
89
+ }
90
+ async syncRecurringJobs() {
91
+ for (const job of this.deps.recurringJobs) {
92
+ const id = `recurring:${job.name}`;
93
+ if (await this.deps.driver.get(`SELECT * FROM "_scheduled_functions" WHERE id = ?`, [id])) continue;
94
+ const nextRunAt = computeNextRun(job.schedule, Date.now());
95
+ await this.deps.driver.run(`INSERT INTO "_scheduled_functions"
96
+ (id, function_name, function_kind, args_json, status, run_at, created_at, updated_at, recurring_name, schedule_json, timezone, misfire_policy, last_run_at, window_ms)
97
+ VALUES (?, ?, ?, ?, 'scheduled', ?, ?, ?, ?, ?, ?, ?, NULL, ?)`, [
98
+ id,
99
+ job.function.name,
100
+ job.function.kind,
101
+ stableStringify(job.args),
102
+ nextRunAt,
103
+ Date.now(),
104
+ Date.now(),
105
+ job.name,
106
+ stableStringify(job.schedule),
107
+ "timezone" in job.schedule ? job.schedule.timezone ?? null : null,
108
+ job.misfirePolicy.type,
109
+ job.misfirePolicy.type === "windowed" ? job.misfirePolicy.windowMs : null
110
+ ]);
111
+ this.notifySchedulerJobsChanged();
112
+ }
113
+ }
114
+ notifySchedulerJobsChanged() {
115
+ this.deps.devtools.notifyScopes(["scheduler.jobs"]);
116
+ }
117
+ async processDueJobs() {
118
+ const now = Date.now();
119
+ const dueJobs = await this.deps.driver.all(`SELECT * FROM "_scheduled_functions" WHERE status = 'scheduled' AND run_at <= ? ORDER BY run_at ASC`, [now]);
120
+ const executedJobIds = [];
121
+ for (const job of dueJobs) {
122
+ const misfirePolicy = parseMisfirePolicy(job.misfire_policy, job.window_ms);
123
+ if (!shouldRunMissedJob(job.run_at, now, misfirePolicy)) {
124
+ await this.advanceOrFinalizeJob(job, "skipped", now);
125
+ continue;
126
+ }
127
+ try {
128
+ if (job.function_kind === "mutation") await this.deps.runMutation({
129
+ kind: "mutation",
130
+ name: job.function_name
131
+ }, JSON.parse(job.args_json));
132
+ else await this.deps.runAction({
133
+ kind: "action",
134
+ name: job.function_name
135
+ }, JSON.parse(job.args_json));
136
+ executedJobIds.push(job.id);
137
+ await this.advanceOrFinalizeJob(job, "completed", now);
138
+ } catch (error) {
139
+ await this.deps.driver.run(`UPDATE "_scheduled_functions" SET status = 'failed', updated_at = ? WHERE id = ?`, [Date.now(), job.id]);
140
+ this.notifySchedulerJobsChanged();
141
+ this.deps.devtools.emit({
142
+ type: "log",
143
+ runtimeId: this.deps.runtimeId,
144
+ level: "error",
145
+ message: `Scheduled job ${job.id} failed: ${error instanceof Error ? error.message : String(error)}`,
146
+ timestamp: Date.now()
147
+ });
148
+ }
149
+ }
150
+ if (executedJobIds.length > 0) {
151
+ this.deps.devtools.emit({
152
+ type: "scheduler.tick",
153
+ runtimeId: this.deps.runtimeId,
154
+ executedJobIds,
155
+ timestamp: Date.now()
156
+ });
157
+ this.notifySchedulerJobsChanged();
158
+ }
159
+ }
160
+ async advanceOrFinalizeJob(job, terminalStatus, executedAt) {
161
+ if (!job.recurring_name || !job.schedule_json) {
162
+ await this.deps.driver.run(`UPDATE "_scheduled_functions" SET status = ?, updated_at = ?, last_run_at = ? WHERE id = ?`, [
163
+ terminalStatus,
164
+ executedAt,
165
+ executedAt,
166
+ job.id
167
+ ]);
168
+ this.notifySchedulerJobsChanged();
169
+ return;
170
+ }
171
+ const schedule = readRecurringSchedule(job.schedule_json);
172
+ if (!schedule) {
173
+ await this.deps.driver.run(`UPDATE "_scheduled_functions" SET status = ?, updated_at = ?, last_run_at = ? WHERE id = ?`, [
174
+ terminalStatus,
175
+ executedAt,
176
+ executedAt,
177
+ job.id
178
+ ]);
179
+ this.notifySchedulerJobsChanged();
180
+ return;
181
+ }
182
+ const nextRunAt = computeNextRun(schedule, executedAt + 1);
183
+ await this.deps.driver.run(`UPDATE "_scheduled_functions"
184
+ SET status = 'scheduled', run_at = ?, updated_at = ?, last_run_at = ?
185
+ WHERE id = ?`, [
186
+ nextRunAt,
187
+ executedAt,
188
+ executedAt,
189
+ job.id
190
+ ]);
191
+ this.notifySchedulerJobsChanged();
192
+ }
193
+ };
194
+ function readRecurringSchedule(scheduleJson) {
195
+ if (!scheduleJson) return;
196
+ try {
197
+ const parsed = JSON.parse(scheduleJson);
198
+ if (!isRecurringSchedule(parsed)) return;
199
+ return parsed;
200
+ } catch {
201
+ return;
202
+ }
203
+ }
204
+ function isRecurringSchedule(value) {
205
+ if (!value || typeof value !== "object" || !("type" in value)) return false;
206
+ const schedule = value;
207
+ switch (schedule.type) {
208
+ case "interval": return (schedule.seconds === void 0 || typeof schedule.seconds === "number") && (schedule.minutes === void 0 || typeof schedule.minutes === "number") && (schedule.hours === void 0 || typeof schedule.hours === "number");
209
+ case "daily": return typeof schedule.hour === "number" && typeof schedule.minute === "number" && (schedule.timezone === void 0 || typeof schedule.timezone === "string");
210
+ case "weekly": return isDayOfWeek(schedule.dayOfWeek) && typeof schedule.hour === "number" && typeof schedule.minute === "number" && (schedule.timezone === void 0 || typeof schedule.timezone === "string");
211
+ default: return false;
212
+ }
213
+ }
214
+ function isDayOfWeek(value) {
215
+ return value === "sunday" || value === "monday" || value === "tuesday" || value === "wednesday" || value === "thursday" || value === "friday" || value === "saturday";
216
+ }
217
+ //#endregion
218
+ export { SchedulerEngine };
219
+
220
+ //# sourceMappingURL=schedulerEngine.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedulerEngine.mjs","names":[],"sources":["../../../../src/runtime/internal/engines/schedulerEngine.ts"],"sourcesContent":["import type {\n FunctionReference,\n MisfirePolicy,\n RecurringJobDefinition\n} from \"../../functions.js\";\nimport type {\n JsonObject,\n SyncoreSqlDriver,\n UpdateScheduledJobOptions\n} from \"../../runtime.js\";\nimport { type DevtoolsEngine } from \"./devtoolsEngine.js\";\nimport {\n computeNextRun,\n parseMisfirePolicy,\n shouldRunMissedJob,\n stableStringify,\n type ScheduledJobRow\n} from \"./shared.js\";\nimport { generateId } from \"../../id.js\";\n\ntype SchedulerEngineDeps = {\n driver: SyncoreSqlDriver;\n runtimeId: string;\n devtools: DevtoolsEngine;\n recurringJobs: RecurringJobDefinition[];\n pollIntervalMs: number;\n runMutation: (\n reference: FunctionReference<\"mutation\", unknown, unknown>,\n args: JsonObject\n ) => Promise<unknown>;\n runAction: (\n reference: FunctionReference<\"action\", unknown, unknown>,\n args: JsonObject\n ) => Promise<unknown>;\n};\n\nexport class SchedulerEngine {\n private timer: ReturnType<typeof setInterval> | undefined;\n\n constructor(private readonly deps: SchedulerEngineDeps) {}\n\n async prepare(): Promise<void> {\n await this.deps.driver.exec(`\n CREATE TABLE IF NOT EXISTS \"_scheduled_functions\" (\n id TEXT PRIMARY KEY,\n function_name TEXT NOT NULL,\n function_kind TEXT NOT NULL,\n args_json TEXT NOT NULL,\n status TEXT NOT NULL,\n run_at INTEGER NOT NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n recurring_name TEXT,\n schedule_json TEXT,\n timezone TEXT,\n misfire_policy TEXT NOT NULL,\n last_run_at INTEGER,\n window_ms INTEGER\n );\n `);\n }\n\n startPolling(): void {\n if (this.timer) {\n return;\n }\n this.timer = setInterval(() => {\n void this.processDueJobs();\n }, this.deps.pollIntervalMs);\n }\n\n stopPolling(): void {\n if (!this.timer) {\n return;\n }\n clearInterval(this.timer);\n this.timer = undefined;\n }\n\n async scheduleJob(\n runAt: number,\n reference: FunctionReference<\"mutation\" | \"action\", unknown, unknown>,\n args: JsonObject,\n misfirePolicy: MisfirePolicy,\n namespacePrefix?: string\n ): Promise<string> {\n const id = `${namespacePrefix ?? \"\"}${generateId()}`;\n const now = Date.now();\n await this.deps.driver.run(\n `INSERT INTO \"_scheduled_functions\"\n (id, function_name, function_kind, args_json, status, run_at, created_at, updated_at, recurring_name, schedule_json, timezone, misfire_policy, last_run_at, window_ms)\n VALUES (?, ?, ?, ?, 'scheduled', ?, ?, ?, NULL, NULL, NULL, ?, NULL, ?)`,\n [\n id,\n reference.name,\n reference.kind,\n stableStringify(args),\n runAt,\n now,\n now,\n misfirePolicy.type,\n misfirePolicy.type === \"windowed\" ? misfirePolicy.windowMs : null\n ]\n );\n this.notifySchedulerJobsChanged();\n return id;\n }\n\n async cancelScheduledJob(id: string): Promise<boolean> {\n const result = await this.deps.driver.run(\n `UPDATE \"_scheduled_functions\"\n SET status = 'cancelled', updated_at = ?\n WHERE id = ? AND status = 'scheduled'`,\n [Date.now(), id]\n );\n if ((result.changes ?? 0) > 0) {\n this.notifySchedulerJobsChanged();\n return true;\n }\n return false;\n }\n\n async updateScheduledJob(options: UpdateScheduledJobOptions): Promise<boolean> {\n const existing = await this.deps.driver.get<{\n status: string;\n recurring_name: string | null;\n }>(\n `SELECT status, recurring_name FROM \"_scheduled_functions\" WHERE id = ?`,\n [options.id]\n );\n if (!existing || existing.status !== \"scheduled\" || !existing.recurring_name) {\n return false;\n }\n const now = Date.now();\n const runAt = options.runAt ?? computeNextRun(options.schedule, now);\n const result = await this.deps.driver.run(\n `UPDATE \"_scheduled_functions\"\n SET args_json = ?, run_at = ?, updated_at = ?, schedule_json = ?, timezone = ?, misfire_policy = ?, window_ms = ?\n WHERE id = ? AND status = 'scheduled' AND recurring_name IS NOT NULL`,\n [\n stableStringify(options.args),\n runAt,\n now,\n stableStringify(options.schedule),\n \"timezone\" in options.schedule ? (options.schedule.timezone ?? null) : null,\n options.misfirePolicy.type,\n options.misfirePolicy.type === \"windowed\"\n ? options.misfirePolicy.windowMs\n : null,\n options.id\n ]\n );\n if ((result.changes ?? 0) > 0) {\n this.notifySchedulerJobsChanged();\n return true;\n }\n return false;\n }\n\n async syncRecurringJobs(): Promise<void> {\n for (const job of this.deps.recurringJobs) {\n const id = `recurring:${job.name}`;\n const existing = await this.deps.driver.get<ScheduledJobRow>(\n `SELECT * FROM \"_scheduled_functions\" WHERE id = ?`,\n [id]\n );\n if (existing) {\n continue;\n }\n const nextRunAt = computeNextRun(job.schedule, Date.now());\n await this.deps.driver.run(\n `INSERT INTO \"_scheduled_functions\"\n (id, function_name, function_kind, args_json, status, run_at, created_at, updated_at, recurring_name, schedule_json, timezone, misfire_policy, last_run_at, window_ms)\n VALUES (?, ?, ?, ?, 'scheduled', ?, ?, ?, ?, ?, ?, ?, NULL, ?)`,\n [\n id,\n job.function.name,\n job.function.kind,\n stableStringify(job.args),\n nextRunAt,\n Date.now(),\n Date.now(),\n job.name,\n stableStringify(job.schedule),\n \"timezone\" in job.schedule ? (job.schedule.timezone ?? null) : null,\n job.misfirePolicy.type,\n job.misfirePolicy.type === \"windowed\"\n ? job.misfirePolicy.windowMs\n : null\n ]\n );\n this.notifySchedulerJobsChanged();\n }\n }\n\n private notifySchedulerJobsChanged(): void {\n this.deps.devtools.notifyScopes([\"scheduler.jobs\"]);\n }\n\n private async processDueJobs(): Promise<void> {\n const now = Date.now();\n const dueJobs = await this.deps.driver.all<ScheduledJobRow>(\n `SELECT * FROM \"_scheduled_functions\" WHERE status = 'scheduled' AND run_at <= ? ORDER BY run_at ASC`,\n [now]\n );\n const executedJobIds: string[] = [];\n\n for (const job of dueJobs) {\n const misfirePolicy = parseMisfirePolicy(\n job.misfire_policy,\n job.window_ms\n );\n if (!shouldRunMissedJob(job.run_at, now, misfirePolicy)) {\n await this.advanceOrFinalizeJob(job, \"skipped\", now);\n continue;\n }\n\n try {\n if (job.function_kind === \"mutation\") {\n await this.deps.runMutation(\n { kind: \"mutation\", name: job.function_name },\n JSON.parse(job.args_json) as JsonObject\n );\n } else {\n await this.deps.runAction(\n { kind: \"action\", name: job.function_name },\n JSON.parse(job.args_json) as JsonObject\n );\n }\n executedJobIds.push(job.id);\n await this.advanceOrFinalizeJob(job, \"completed\", now);\n } catch (error) {\n await this.deps.driver.run(\n `UPDATE \"_scheduled_functions\" SET status = 'failed', updated_at = ? WHERE id = ?`,\n [Date.now(), job.id]\n );\n this.notifySchedulerJobsChanged();\n this.deps.devtools.emit({\n type: \"log\",\n runtimeId: this.deps.runtimeId,\n level: \"error\",\n message: `Scheduled job ${job.id} failed: ${\n error instanceof Error ? error.message : String(error)\n }`,\n timestamp: Date.now()\n });\n }\n }\n\n if (executedJobIds.length > 0) {\n this.deps.devtools.emit({\n type: \"scheduler.tick\",\n runtimeId: this.deps.runtimeId,\n executedJobIds,\n timestamp: Date.now()\n });\n this.notifySchedulerJobsChanged();\n }\n }\n\n private async advanceOrFinalizeJob(\n job: ScheduledJobRow,\n terminalStatus: ScheduledJobRow[\"status\"],\n executedAt: number\n ): Promise<void> {\n if (!job.recurring_name || !job.schedule_json) {\n await this.deps.driver.run(\n `UPDATE \"_scheduled_functions\" SET status = ?, updated_at = ?, last_run_at = ? WHERE id = ?`,\n [terminalStatus, executedAt, executedAt, job.id]\n );\n this.notifySchedulerJobsChanged();\n return;\n }\n\n const schedule = readRecurringSchedule(job.schedule_json);\n if (!schedule) {\n await this.deps.driver.run(\n `UPDATE \"_scheduled_functions\" SET status = ?, updated_at = ?, last_run_at = ? WHERE id = ?`,\n [terminalStatus, executedAt, executedAt, job.id]\n );\n this.notifySchedulerJobsChanged();\n return;\n }\n const nextRunAt = computeNextRun(schedule, executedAt + 1);\n await this.deps.driver.run(\n `UPDATE \"_scheduled_functions\"\n SET status = 'scheduled', run_at = ?, updated_at = ?, last_run_at = ?\n WHERE id = ?`,\n [nextRunAt, executedAt, executedAt, job.id]\n );\n this.notifySchedulerJobsChanged();\n }\n}\n\nfunction readRecurringSchedule(\n scheduleJson: string | null\n): RecurringJobDefinition[\"schedule\"] | undefined {\n if (!scheduleJson) {\n return undefined;\n }\n try {\n const parsed: unknown = JSON.parse(scheduleJson);\n if (!isRecurringSchedule(parsed)) {\n return undefined;\n }\n return parsed;\n } catch {\n return undefined;\n }\n}\n\nfunction isRecurringSchedule(\n value: unknown\n): value is RecurringJobDefinition[\"schedule\"] {\n if (!value || typeof value !== \"object\" || !(\"type\" in value)) {\n return false;\n }\n const schedule = value as Record<string, unknown>;\n switch (schedule.type) {\n case \"interval\":\n return (\n (schedule.seconds === undefined || typeof schedule.seconds === \"number\") &&\n (schedule.minutes === undefined || typeof schedule.minutes === \"number\") &&\n (schedule.hours === undefined || typeof schedule.hours === \"number\")\n );\n case \"daily\":\n return (\n typeof schedule.hour === \"number\" &&\n typeof schedule.minute === \"number\" &&\n (schedule.timezone === undefined || typeof schedule.timezone === \"string\")\n );\n case \"weekly\":\n return (\n isDayOfWeek(schedule.dayOfWeek) &&\n typeof schedule.hour === \"number\" &&\n typeof schedule.minute === \"number\" &&\n (schedule.timezone === undefined || typeof schedule.timezone === \"string\")\n );\n default:\n return false;\n }\n}\n\nfunction isDayOfWeek(\n value: unknown\n): value is Extract<\n RecurringJobDefinition[\"schedule\"],\n { type: \"weekly\" }\n>[\"dayOfWeek\"] {\n return (\n value === \"sunday\" ||\n value === \"monday\" ||\n value === \"tuesday\" ||\n value === \"wednesday\" ||\n value === \"thursday\" ||\n value === \"friday\" ||\n value === \"saturday\"\n );\n}\n"],"mappings":"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"}