orpc-fn 0.1.0 → 0.3.0
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.
- package/README.md +70 -33
- package/dist/assemble-procedure.d.ts +13 -0
- package/dist/assemble-procedure.d.ts.map +1 -0
- package/dist/assemble-procedure.js +29 -0
- package/dist/assemble-procedure.js.map +1 -0
- package/dist/bound-call.d.ts +1 -1
- package/dist/bound-call.d.ts.map +1 -1
- package/dist/bound-call.js.map +1 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +56 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +4 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -2
- package/dist/client.js.map +1 -1
- package/dist/compatibility.d.ts +16 -0
- package/dist/compatibility.d.ts.map +1 -0
- package/dist/compatibility.js +4 -0
- package/dist/compatibility.js.map +1 -0
- package/dist/create-fn.d.ts +30 -34
- package/dist/create-fn.d.ts.map +1 -1
- package/dist/create-fn.js +177 -121
- package/dist/create-fn.js.map +1 -1
- package/dist/expo.d.ts +11 -25
- package/dist/expo.d.ts.map +1 -1
- package/dist/expo.js +25 -30
- package/dist/expo.js.map +1 -1
- package/dist/hono.d.ts +12 -4
- package/dist/hono.d.ts.map +1 -1
- package/dist/hono.js +29 -12
- package/dist/hono.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/json-schema.d.ts +2 -0
- package/dist/json-schema.d.ts.map +1 -1
- package/dist/json-schema.js +11 -0
- package/dist/json-schema.js.map +1 -1
- package/dist/live/codec.d.ts +6 -1
- package/dist/live/codec.d.ts.map +1 -1
- package/dist/live/codec.js +14 -3
- package/dist/live/codec.js.map +1 -1
- package/dist/live/fn-live.d.ts +67 -22
- package/dist/live/fn-live.d.ts.map +1 -1
- package/dist/live/fn-live.js +98 -16
- package/dist/live/fn-live.js.map +1 -1
- package/dist/live/index.d.ts +1 -1
- package/dist/live/index.d.ts.map +1 -1
- package/dist/live/index.js.map +1 -1
- package/dist/live/ioredis.d.ts +4 -1
- package/dist/live/ioredis.d.ts.map +1 -1
- package/dist/live/ioredis.js +21 -2
- package/dist/live/ioredis.js.map +1 -1
- package/dist/live/lifecycle.d.ts +7 -0
- package/dist/live/lifecycle.d.ts.map +1 -0
- package/dist/live/lifecycle.js +36 -0
- package/dist/live/lifecycle.js.map +1 -0
- package/dist/live/memory.d.ts.map +1 -1
- package/dist/live/memory.js +4 -0
- package/dist/live/memory.js.map +1 -1
- package/dist/live/pub-sub.d.ts +81 -13
- package/dist/live/pub-sub.d.ts.map +1 -1
- package/dist/live/pub-sub.js +437 -126
- package/dist/live/pub-sub.js.map +1 -1
- package/dist/live/redis-bun.d.ts +1 -1
- package/dist/live/redis-bun.d.ts.map +1 -1
- package/dist/live/redis-bun.js +3 -5
- package/dist/live/redis-bun.js.map +1 -1
- package/dist/live/redis.d.ts +2 -2
- package/dist/live/redis.d.ts.map +1 -1
- package/dist/live/redis.js +36 -9
- package/dist/live/redis.js.map +1 -1
- package/dist/live/transport.d.ts +5 -1
- package/dist/live/transport.d.ts.map +1 -1
- package/dist/live/transport.js.map +1 -1
- package/dist/logger.d.ts +2 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +21 -0
- package/dist/logger.js.map +1 -1
- package/dist/mastra.d.ts.map +1 -1
- package/dist/mastra.js +16 -7
- package/dist/mastra.js.map +1 -1
- package/dist/mcp/core.d.ts +41 -0
- package/dist/mcp/core.d.ts.map +1 -0
- package/dist/mcp/core.js +61 -0
- package/dist/mcp/core.js.map +1 -0
- package/dist/mcp/sdk.d.ts +12 -0
- package/dist/mcp/sdk.d.ts.map +1 -0
- package/dist/mcp/sdk.js +14 -0
- package/dist/mcp/sdk.js.map +1 -0
- package/dist/mcp/server.d.ts +11 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +13 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp.d.ts +3 -2
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +22 -7
- package/dist/mcp.js.map +1 -1
- package/dist/meta.d.ts +15 -7
- package/dist/meta.d.ts.map +1 -1
- package/dist/meta.js +9 -2
- package/dist/meta.js.map +1 -1
- package/dist/otel.d.ts.map +1 -1
- package/dist/otel.js +77 -6
- package/dist/otel.js.map +1 -1
- package/dist/route-options.d.ts +22 -0
- package/dist/route-options.d.ts.map +1 -0
- package/dist/route-options.js +67 -0
- package/dist/route-options.js.map +1 -0
- package/dist/router.d.ts +13 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +35 -0
- package/dist/router.js.map +1 -1
- package/dist/timing.d.ts +8 -0
- package/dist/timing.d.ts.map +1 -0
- package/dist/timing.js +4 -0
- package/dist/timing.js.map +1 -0
- package/dist/types.d.ts +96 -41
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +6 -2
- package/dist/types.js.map +1 -1
- package/package.json +28 -3
- package/src/assemble-procedure.ts +42 -0
- package/src/bound-call.ts +4 -3
- package/src/cli.ts +68 -0
- package/src/client.ts +9 -2
- package/src/compatibility.ts +39 -0
- package/src/create-fn.ts +295 -182
- package/src/expo.ts +49 -44
- package/src/hono.ts +36 -17
- package/src/index.ts +7 -2
- package/src/json-schema.ts +16 -0
- package/src/live/codec.ts +22 -3
- package/src/live/fn-live.ts +248 -69
- package/src/live/index.ts +2 -0
- package/src/live/ioredis.ts +22 -2
- package/src/live/lifecycle.ts +44 -0
- package/src/live/memory.ts +4 -0
- package/src/live/pub-sub.ts +619 -168
- package/src/live/redis-bun.ts +7 -5
- package/src/live/redis.ts +37 -11
- package/src/live/transport.ts +4 -1
- package/src/logger.ts +20 -0
- package/src/mastra.ts +15 -7
- package/src/mcp/core.ts +102 -0
- package/src/mcp/sdk.ts +36 -0
- package/src/mcp/server.ts +35 -0
- package/src/mcp.ts +48 -20
- package/src/meta.ts +40 -24
- package/src/otel.ts +77 -10
- package/src/route-options.ts +100 -0
- package/src/router.ts +58 -1
- package/src/timing.ts +5 -0
- package/src/types.ts +186 -62
package/dist/json-schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-schema.js","sourceRoot":"","sources":["../src/json-schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AA6BzD,MAAM,SAAS,GAAG,IAAI,wBAAwB,EAAE,CAAA;AAChD,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAE,CAAA;AAEvC,SAAS,YAAY,CACpB,MAAiB,EACjB,QAA4B;IAE5B,OAAO,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAe,CAAA;AAChE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAM,MAAiB;IACpD,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChD,OAAO;QACN,WAAW,EAAE;YACZ,OAAO,EAAE,CAAC;YACV,MAAM,EAAE,SAAS;YACjB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;gBACnB,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,CAAQ,CAAA;gBACxD,MAAM,MAAM,GAAG,CACd,MAA+D,EAC9D,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;gBACrE,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;gBACpD,OAAO,MAAM,YAAY,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YACxE,CAAC;YACD,UAAU,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE;SACjE;KACD,CAAA;AACF,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,uBAAuB,CACtC,MAAiB;IAEjB,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IACjD,OAAO;QACN,WAAW,EAAE;YACZ,OAAO,EAAE,CAAC;YACV,MAAM,EAAE,SAAS;YACjB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAa,EAAE,CAAC;YAC/C,UAAU,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE;SACjE;KACD,CAAA;AACF,CAAC","sourcesContent":["import type { AnySchema } from '@orpc/contract'\nimport { JsonSchemaCoercer } from '@orpc/json-schema'\nimport { ZodToJsonSchemaConverter } from '@orpc/zod/zod4'\n\nexport type JsonSchema = Record<string, unknown>\n\n/**\n * A Standard Schema with JSON Schema attached, as MCP SDKs and Mastra expect.\n * `TIn` is what is validated and what `validate` returns.\n */\nexport type ToolSchema<TIn = unknown, TOut = TIn> = {\n\t'~standard': {\n\t\tversion: 1\n\t\tvendor: string\n\t\tvalidate: (\n\t\t\tvalue: unknown\n\t\t) =>\n\t\t\t| { value: TOut; issues?: undefined }\n\t\t\t| { issues: ReadonlyArray<{ message: string }> }\n\t\t\t| Promise<\n\t\t\t\t\t| { value: TOut; issues?: undefined }\n\t\t\t\t\t| { issues: ReadonlyArray<{ message: string }> }\n\t\t\t >\n\t\ttypes?: { input: TIn; output: TOut }\n\t\tjsonSchema: {\n\t\t\tinput: (options?: unknown) => JsonSchema\n\t\t\toutput: (options?: unknown) => JsonSchema\n\t\t}\n\t}\n}\n\nconst converter = new ZodToJsonSchemaConverter()\nconst coercer = new JsonSchemaCoercer()\n\nfunction toJsonSchema(\n\tschema: AnySchema,\n\tstrategy: 'input' | 'output'\n): JsonSchema {\n\treturn converter.convert(schema, { strategy })[1] as JsonSchema\n}\n\n/**\n * Tool input that validates like the procedure but returns the RAW input for\n * the procedure's own input schema to parse. Transforms run in both passes\n * but never on their own output, which handing over the parsed value would do. Zod's JSON Schema export rejects types like\n * `z.date()`; oRPC's converter gives the same JSON Schema as the OpenAPI docs\n * (dates become string/date-time), and clients send those dates as strings,\n * so values are coerced back before validating.\n */\nexport function rawInputSchema<TIn>(schema: AnySchema): ToolSchema<TIn> {\n\tconst jsonSchema = toJsonSchema(schema, 'input')\n\treturn {\n\t\t'~standard': {\n\t\t\tversion: 1,\n\t\t\tvendor: 'orpc-fn',\n\t\t\tvalidate: (value) => {\n\t\t\t\tconst coerced = coercer.coerce(jsonSchema, value) as TIn\n\t\t\t\tconst finish = (\n\t\t\t\t\tresult: Awaited<ReturnType<AnySchema['~standard']['validate']>>\n\t\t\t\t) => (result.issues ? { issues: result.issues } : { value: coerced })\n\t\t\t\tconst result = schema['~standard'].validate(coerced)\n\t\t\t\treturn result instanceof Promise ? result.then(finish) : finish(result)\n\t\t\t},\n\t\t\tjsonSchema: { input: () => jsonSchema, output: () => jsonSchema }\n\t\t}\n\t}\n}\n\n/** Tool output the procedure already validated: described, passed through. */\nexport function passThroughOutputSchema<TOut>(\n\tschema: AnySchema\n): ToolSchema<TOut> {\n\tconst jsonSchema = toJsonSchema(schema, 'output')\n\treturn {\n\t\t'~standard': {\n\t\t\tversion: 1,\n\t\t\tvendor: 'orpc-fn',\n\t\t\tvalidate: (value) => ({ value: value as TOut }),\n\t\t\tjsonSchema: { input: () => jsonSchema, output: () => jsonSchema }\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"json-schema.js","sourceRoot":"","sources":["../src/json-schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AA6BzD,MAAM,SAAS,GAAG,IAAI,wBAAwB,EAAE,CAAA;AAChD,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAE,CAAA;AAEvC,SAAS,YAAY,CACpB,MAAiB,EACjB,QAA4B;IAE5B,OAAO,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAe,CAAA;AAChE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAM,MAAiB;IACpD,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChD,OAAO;QACN,WAAW,EAAE;YACZ,OAAO,EAAE,CAAC;YACV,MAAM,EAAE,SAAS;YACjB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;gBACnB,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,CAAQ,CAAA;gBACxD,MAAM,MAAM,GAAG,CACd,MAA+D,EAC9D,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;gBACrE,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;gBACpD,OAAO,MAAM,YAAY,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YACxE,CAAC;YACD,UAAU,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE;SACjE;KACD,CAAA;AACF,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,uBAAuB,CACtC,MAAiB;IAEjB,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IACjD,OAAO;QACN,WAAW,EAAE;YACZ,OAAO,EAAE,CAAC;YACV,MAAM,EAAE,SAAS;YACjB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAa,EAAE,CAAC;YAC/C,UAAU,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE;SACjE;KACD,CAAA;AACF,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,kBAAkB,CAAC,MAAkB;IACpD,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IACzC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAA;IAC1C,OAAO,CACN,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACpB,KAAK,CAAC,MAAM,GAAG,CAAC;QAChB,KAAK,CAAC,KAAK,CACV,CAAC,MAAM,EAAE,EAAE,CACV,OAAO,MAAM,KAAK,QAAQ;YAC1B,MAAM,KAAK,IAAI;YACf,kBAAkB,CAAC,MAAM,CAAC,CAC3B,CACD,CAAA;AACF,CAAC","sourcesContent":["import type { AnySchema } from '@orpc/contract'\nimport { JsonSchemaCoercer } from '@orpc/json-schema'\nimport { ZodToJsonSchemaConverter } from '@orpc/zod/zod4'\n\nexport type JsonSchema = Record<string, unknown>\n\n/**\n * A Standard Schema with JSON Schema attached, as MCP SDKs and Mastra expect.\n * `TIn` is what is validated and what `validate` returns.\n */\nexport type ToolSchema<TIn = unknown, TOut = TIn> = {\n\t'~standard': {\n\t\tversion: 1\n\t\tvendor: string\n\t\tvalidate: (\n\t\t\tvalue: unknown\n\t\t) =>\n\t\t\t| { value: TOut; issues?: undefined }\n\t\t\t| { issues: ReadonlyArray<{ message: string }> }\n\t\t\t| Promise<\n\t\t\t\t\t| { value: TOut; issues?: undefined }\n\t\t\t\t\t| { issues: ReadonlyArray<{ message: string }> }\n\t\t\t >\n\t\ttypes?: { input: TIn; output: TOut }\n\t\tjsonSchema: {\n\t\t\tinput: (options?: unknown) => JsonSchema\n\t\t\toutput: (options?: unknown) => JsonSchema\n\t\t}\n\t}\n}\n\nconst converter = new ZodToJsonSchemaConverter()\nconst coercer = new JsonSchemaCoercer()\n\nfunction toJsonSchema(\n\tschema: AnySchema,\n\tstrategy: 'input' | 'output'\n): JsonSchema {\n\treturn converter.convert(schema, { strategy })[1] as JsonSchema\n}\n\n/**\n * Tool input that validates like the procedure but returns the RAW input for\n * the procedure's own input schema to parse. Transforms run in both passes\n * but never on their own output, which handing over the parsed value would do. Zod's JSON Schema export rejects types like\n * `z.date()`; oRPC's converter gives the same JSON Schema as the OpenAPI docs\n * (dates become string/date-time), and clients send those dates as strings,\n * so values are coerced back before validating.\n */\nexport function rawInputSchema<TIn>(schema: AnySchema): ToolSchema<TIn> {\n\tconst jsonSchema = toJsonSchema(schema, 'input')\n\treturn {\n\t\t'~standard': {\n\t\t\tversion: 1,\n\t\t\tvendor: 'orpc-fn',\n\t\t\tvalidate: (value) => {\n\t\t\t\tconst coerced = coercer.coerce(jsonSchema, value) as TIn\n\t\t\t\tconst finish = (\n\t\t\t\t\tresult: Awaited<ReturnType<AnySchema['~standard']['validate']>>\n\t\t\t\t) => (result.issues ? { issues: result.issues } : { value: coerced })\n\t\t\t\tconst result = schema['~standard'].validate(coerced)\n\t\t\t\treturn result instanceof Promise ? result.then(finish) : finish(result)\n\t\t\t},\n\t\t\tjsonSchema: { input: () => jsonSchema, output: () => jsonSchema }\n\t\t}\n\t}\n}\n\n/** Tool output the procedure already validated: described, passed through. */\nexport function passThroughOutputSchema<TOut>(\n\tschema: AnySchema\n): ToolSchema<TOut> {\n\tconst jsonSchema = toJsonSchema(schema, 'output')\n\treturn {\n\t\t'~standard': {\n\t\t\tversion: 1,\n\t\t\tvendor: 'orpc-fn',\n\t\t\tvalidate: (value) => ({ value: value as TOut }),\n\t\t\tjsonSchema: { input: () => jsonSchema, output: () => jsonSchema }\n\t\t}\n\t}\n}\n\n/** Tool protocols permit unions only when every branch accepts an object. */\nexport function isObjectJsonSchema(schema: JsonSchema): boolean {\n\tif (schema.type === 'object') return true\n\tconst union = schema.anyOf ?? schema.oneOf\n\treturn (\n\t\tArray.isArray(union) &&\n\t\tunion.length > 0 &&\n\t\tunion.every(\n\t\t\t(branch) =>\n\t\t\t\ttypeof branch === 'object' &&\n\t\t\t\tbranch !== null &&\n\t\t\t\tisObjectJsonSchema(branch)\n\t\t)\n\t)\n}\n"]}
|
package/dist/live/codec.d.ts
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
* intact. Payloads without the envelope are read as plain JSON, so a
|
|
5
5
|
* publisher on the old format keeps working during a rolling deploy.
|
|
6
6
|
*/
|
|
7
|
-
export declare function encodePayload(value: unknown): string;
|
|
7
|
+
export declare function encodePayload(value: unknown, id?: string): string;
|
|
8
8
|
export declare function decodePayload(payload: string): unknown;
|
|
9
|
+
/** IDs distinguish replay/live overlap; legacy payloads remain readable. */
|
|
10
|
+
export declare function decodeMessage(payload: string): {
|
|
11
|
+
id: string | undefined;
|
|
12
|
+
value: unknown;
|
|
13
|
+
};
|
|
9
14
|
//# sourceMappingURL=codec.d.ts.map
|
package/dist/live/codec.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../src/live/codec.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,wBAAgB,aAAa,
|
|
1
|
+
{"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../src/live/codec.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,wBAAgB,aAAa,CAC5B,KAAK,EAAE,OAAO,EACd,EAAE,GAAE,MAA4B,GAC9B,MAAM,CAMR;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAWtD;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;IAC/C,EAAE,EAAE,MAAM,GAAG,SAAS,CAAA;IACtB,KAAK,EAAE,OAAO,CAAA;CACd,CAUA"}
|
package/dist/live/codec.js
CHANGED
|
@@ -7,21 +7,32 @@ const MARKER = 'orpc-fn';
|
|
|
7
7
|
* intact. Payloads without the envelope are read as plain JSON, so a
|
|
8
8
|
* publisher on the old format keeps working during a rolling deploy.
|
|
9
9
|
*/
|
|
10
|
-
export function encodePayload(value) {
|
|
10
|
+
export function encodePayload(value, id = crypto.randomUUID()) {
|
|
11
11
|
const [json, meta, maps, blobs] = serializer.serialize(value);
|
|
12
12
|
if (maps.length > 0 || blobs.length > 0) {
|
|
13
13
|
throw new TypeError('orpc-fn: pub/sub events cannot contain files or blobs');
|
|
14
14
|
}
|
|
15
|
-
return JSON.stringify({ [MARKER]:
|
|
15
|
+
return JSON.stringify({ [MARKER]: 2, id, json, meta });
|
|
16
16
|
}
|
|
17
17
|
export function decodePayload(payload) {
|
|
18
18
|
const parsed = JSON.parse(payload);
|
|
19
19
|
if (typeof parsed === 'object' &&
|
|
20
20
|
parsed !== null &&
|
|
21
|
-
parsed[MARKER]
|
|
21
|
+
[1, 2].includes(Number(parsed[MARKER]))) {
|
|
22
22
|
const { json, meta } = parsed;
|
|
23
23
|
return serializer.deserialize(json, meta);
|
|
24
24
|
}
|
|
25
25
|
return parsed;
|
|
26
26
|
}
|
|
27
|
+
/** IDs distinguish replay/live overlap; legacy payloads remain readable. */
|
|
28
|
+
export function decodeMessage(payload) {
|
|
29
|
+
const envelope = JSON.parse(payload);
|
|
30
|
+
const id = typeof envelope === 'object' &&
|
|
31
|
+
envelope !== null &&
|
|
32
|
+
envelope[MARKER] === 2 &&
|
|
33
|
+
typeof envelope.id === 'string'
|
|
34
|
+
? envelope.id
|
|
35
|
+
: undefined;
|
|
36
|
+
return { id, value: decodePayload(payload) };
|
|
37
|
+
}
|
|
27
38
|
//# sourceMappingURL=codec.js.map
|
package/dist/live/codec.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec.js","sourceRoot":"","sources":["../../src/live/codec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAA;AAEjE,MAAM,UAAU,GAAG,IAAI,yBAAyB,EAAE,CAAA;AAClD,MAAM,MAAM,GAAG,SAAS,CAAA;AAExB;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"codec.js","sourceRoot":"","sources":["../../src/live/codec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAA;AAEjE,MAAM,UAAU,GAAG,IAAI,yBAAyB,EAAE,CAAA;AAClD,MAAM,MAAM,GAAG,SAAS,CAAA;AAExB;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC5B,KAAc,EACd,EAAE,GAAW,MAAM,CAAC,UAAU,EAAE;IAEhC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC7D,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,SAAS,CAAC,uDAAuD,CAAC,CAAA;IAC7E,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;AACvD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe;IAC5C,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC3C,IACC,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,KAAK,IAAI;QACf,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAE,MAAkC,CAAC,MAAM,CAAC,CAAC,CAAC,EACnE,CAAC;QACF,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAqC,CAAA;QAC5D,OAAO,UAAU,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IAC1C,CAAC;IACD,OAAO,MAAM,CAAA;AACd,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,aAAa,CAAC,OAAe;IAI5C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAY,CAAA;IAC/C,MAAM,EAAE,GACP,OAAO,QAAQ,KAAK,QAAQ;QAC5B,QAAQ,KAAK,IAAI;QAChB,QAAoC,CAAC,MAAM,CAAC,KAAK,CAAC;QACnD,OAAQ,QAAoC,CAAC,EAAE,KAAK,QAAQ;QAC3D,CAAC,CAAE,QAA2B,CAAC,EAAE;QACjC,CAAC,CAAC,SAAS,CAAA;IACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,CAAA;AAC7C,CAAC","sourcesContent":["import { StandardRPCJsonSerializer } from '@orpc/client/standard'\n\nconst serializer = new StandardRPCJsonSerializer()\nconst MARKER = 'orpc-fn'\n\n/**\n * Wire format for pub/sub payloads: oRPC's own JSON serializer, so values\n * plain JSON loses (`Date`, `Map`, `Set`, `BigInt`, `URL`, `undefined`) arrive\n * intact. Payloads without the envelope are read as plain JSON, so a\n * publisher on the old format keeps working during a rolling deploy.\n */\nexport function encodePayload(\n\tvalue: unknown,\n\tid: string = crypto.randomUUID()\n): string {\n\tconst [json, meta, maps, blobs] = serializer.serialize(value)\n\tif (maps.length > 0 || blobs.length > 0) {\n\t\tthrow new TypeError('orpc-fn: pub/sub events cannot contain files or blobs')\n\t}\n\treturn JSON.stringify({ [MARKER]: 2, id, json, meta })\n}\n\nexport function decodePayload(payload: string): unknown {\n\tconst parsed: unknown = JSON.parse(payload)\n\tif (\n\t\ttypeof parsed === 'object' &&\n\t\tparsed !== null &&\n\t\t[1, 2].includes(Number((parsed as Record<string, unknown>)[MARKER]))\n\t) {\n\t\tconst { json, meta } = parsed as { json: unknown; meta: [] }\n\t\treturn serializer.deserialize(json, meta)\n\t}\n\treturn parsed\n}\n\n/** IDs distinguish replay/live overlap; legacy payloads remain readable. */\nexport function decodeMessage(payload: string): {\n\tid: string | undefined\n\tvalue: unknown\n} {\n\tconst envelope = JSON.parse(payload) as unknown\n\tconst id =\n\t\ttypeof envelope === 'object' &&\n\t\tenvelope !== null &&\n\t\t(envelope as Record<string, unknown>)[MARKER] === 2 &&\n\t\ttypeof (envelope as Record<string, unknown>).id === 'string'\n\t\t\t? (envelope as { id: string }).id\n\t\t\t: undefined\n\treturn { id, value: decodePayload(payload) }\n}\n"]}
|
package/dist/live/fn-live.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { Schema } from '@orpc/contract';
|
|
1
|
+
import type { AnySchema, ErrorMap, Schema } from '@orpc/contract';
|
|
2
2
|
import { type AnyProcedure } from '@orpc/server';
|
|
3
3
|
import type { ZodType, z } from 'zod';
|
|
4
|
+
import { RAW_INPUT } from '../timing.js';
|
|
4
5
|
import type { FnLogger } from '../logger.js';
|
|
5
6
|
import { type SpanLike } from '../otel.js';
|
|
6
7
|
import type { FnContext, FnDefinition, FnProcedure, FnRouteOptions, HandlerParams, MaybePromise, ProcedureKey } from '../types.js';
|
|
7
|
-
import type { AuthFn, Channel, ChannelDefinition, ChannelOptions, ObjectSchema, PublisherConfig } from './pub-sub.js';
|
|
8
|
+
import type { AuthFn, Channel, ChannelDefinition, ChannelOptions, ChannelQueueOptions, ObjectSchema, PublisherConfig } from './pub-sub.js';
|
|
8
9
|
declare const fnLivePatchMarker: unique symbol;
|
|
9
10
|
/**
|
|
10
11
|
* Transformer result that splits the internal accumulator from the wire
|
|
@@ -18,27 +19,35 @@ export type FnLivePatch<TOutput, TEmit> = {
|
|
|
18
19
|
emit: TEmit;
|
|
19
20
|
};
|
|
20
21
|
export declare function fnLivePatch<TOutput, TEmit>(state: TOutput, emit: TEmit): FnLivePatch<TOutput, TEmit>;
|
|
21
|
-
export type FnLiveConfig<TDef extends FnDefinition, TInput, TEventSchema extends ZodType, TOutput, TKey extends ProcedureKey<TDef>, TEmit = never
|
|
22
|
+
export type FnLiveConfig<TDef extends FnDefinition, TInput, TEventSchema extends ZodType, TOutput, TKey extends ProcedureKey<TDef>, TEmit = never, TErrors extends ErrorMap = Record<never, never>> = Omit<PublisherConfig, 'name'> & ChannelQueueOptions & {
|
|
22
23
|
/** Subscribe route name; default `${name}.live`. */
|
|
23
24
|
name?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Validates reducer state without reapplying an output transform. Required with a
|
|
27
|
+
* transformer. Its input may be looser than its output (e.g. branded IDs); it is
|
|
28
|
+
* checked at runtime.
|
|
29
|
+
*/
|
|
30
|
+
stateSchema?: Schema<unknown, TOutput>;
|
|
31
|
+
/** Validates patch payloads. Required when a transformer returns fnLivePatch. */
|
|
32
|
+
emitSchema?: Schema<unknown, TEmit>;
|
|
33
|
+
/** Optional access check before each update; idle streams keep their initial authorization. */
|
|
34
|
+
reauthorize?: AuthFn<TInput, FnContext<TDef, TKey>>;
|
|
24
35
|
eventSchema: TEventSchema;
|
|
25
36
|
channel: ChannelDefinition<TInput, z.output<TEventSchema>, FnContext<TDef, TKey>>;
|
|
26
37
|
/** Fold an event into the snapshot; without it every event re-runs the handler. */
|
|
27
|
-
transformerFn?:
|
|
28
|
-
event: z.output<TEventSchema>;
|
|
29
|
-
previous: TOutput | undefined;
|
|
30
|
-
rerun: (input?: TInput) => Promise<TOutput>;
|
|
31
|
-
}) => MaybePromise<TOutput | FnLivePatch<TOutput, TEmit> | undefined>;
|
|
38
|
+
transformerFn?: FnLiveConfigTransformer<TDef, TInput, TEventSchema, TOutput, TKey, TEmit, TErrors>;
|
|
32
39
|
/** Return false to skip an event. */
|
|
33
|
-
shouldUpdate?: (params: HandlerParams<TDef, TInput, TKey> & {
|
|
40
|
+
shouldUpdate?: (params: HandlerParams<TDef, TInput, TKey, TErrors> & {
|
|
34
41
|
event: z.output<TEventSchema>;
|
|
35
|
-
previous: TOutput
|
|
42
|
+
previous: TOutput;
|
|
36
43
|
}) => MaybePromise<boolean>;
|
|
37
44
|
authFn?: AuthFn<TInput, FnContext<TDef, TKey>>;
|
|
38
45
|
/** Log publish failures instead of throwing them into the publishing route. */
|
|
39
46
|
safePublish?: boolean;
|
|
40
47
|
/** Batch events arriving within this window into one snapshot update. */
|
|
41
48
|
coalesceMs?: number;
|
|
49
|
+
/** Maximum events collected in one coalescing batch (default 1000). */
|
|
50
|
+
maxBatchSize?: number;
|
|
42
51
|
/**
|
|
43
52
|
* When a slow subscriber's queue overflows (drop-oldest), this marker is
|
|
44
53
|
* enqueued INSTEAD of the dropped event so the client resyncs. Return
|
|
@@ -47,11 +56,29 @@ export type FnLiveConfig<TDef extends FnDefinition, TInput, TEventSchema extends
|
|
|
47
56
|
overflowMarker?: (input: TInput) => z.output<TEventSchema> | null;
|
|
48
57
|
summary?: string;
|
|
49
58
|
description?: string;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
} & ({
|
|
60
|
+
transformerFn?: undefined;
|
|
61
|
+
} | {
|
|
62
|
+
transformerFn: FnLiveConfigTransformer<TDef, TInput, TEventSchema, TOutput, TKey, TEmit, TErrors>;
|
|
63
|
+
stateSchema: Schema<unknown, TOutput>;
|
|
64
|
+
}) & ([TEmit] extends [never] ? object : {
|
|
65
|
+
emitSchema: Schema<unknown, TEmit>;
|
|
66
|
+
});
|
|
67
|
+
type FnLiveConfigTransformer<TDef extends FnDefinition, TInput, TEventSchema extends ZodType, TOutput, TKey extends ProcedureKey<TDef>, TEmit, TErrors extends ErrorMap> = (params: HandlerParams<TDef, TInput, TKey, TErrors> & {
|
|
68
|
+
event: z.output<TEventSchema>;
|
|
69
|
+
previous: TOutput;
|
|
70
|
+
rerun: () => Promise<TOutput>;
|
|
71
|
+
}) => MaybePromise<TOutput | FnLivePatch<TOutput, TEmit> | undefined>;
|
|
72
|
+
export type FnLiveReturn<TDef extends FnDefinition, TKey extends ProcedureKey<TDef>, TInput extends ObjectSchema, TOutputSchema extends Schema<unknown, unknown>, TOutput, TEventSchema extends ZodType, TEmit = never, TErrors extends ErrorMap = Record<never, never>> = {
|
|
73
|
+
procedure: FnProcedure<TDef, TKey, TInput, TOutputSchema, TErrors>;
|
|
74
|
+
subscribe: FnProcedure<TDef, TKey, TInput, Schema<AsyncGenerator<TOutput | TEmit>, AsyncGenerator<TOutput | TEmit>>, TErrors>;
|
|
54
75
|
publish: (event: z.input<TEventSchema>) => Promise<void>;
|
|
76
|
+
getSubscriptionChannelName: (params: {
|
|
77
|
+
input: z.output<TInput>;
|
|
78
|
+
context: FnContext<TDef, TKey>;
|
|
79
|
+
}) => string;
|
|
80
|
+
getPublishChannelName: (event: z.output<TEventSchema>) => string;
|
|
81
|
+
/** @deprecated Prefer the separate required-input resolver methods. */
|
|
55
82
|
getChannelName: (params: Partial<z.output<TInput>> | Partial<z.output<TEventSchema>>, context?: FnContext<TDef, TKey>) => string;
|
|
56
83
|
};
|
|
57
84
|
/**
|
|
@@ -59,22 +86,22 @@ export type FnLiveReturn<TDef extends FnDefinition, TKey extends ProcedureKey<TD
|
|
|
59
86
|
* snapshot, then an update for every event published on the channel.
|
|
60
87
|
*/
|
|
61
88
|
export interface FnLive<TDef extends FnDefinition> {
|
|
62
|
-
<TInput extends ObjectSchema, TOutputSchema extends ZodType, TEventSchema extends ZodType, TKey extends ProcedureKey<TDef> = TDef['default'], TEmit = never
|
|
89
|
+
<TInput extends ObjectSchema, TOutputSchema extends ZodType, TEventSchema extends ZodType, TKey extends ProcedureKey<TDef> = TDef['default'], TEmit = never, TErrors extends ErrorMap = Record<never, never>>(options: {
|
|
63
90
|
input: TInput;
|
|
64
91
|
output: TOutputSchema;
|
|
65
|
-
handler: (params: HandlerParams<TDef, z.output<TInput>, NoInfer<TKey
|
|
92
|
+
handler: (params: HandlerParams<TDef, z.output<TInput>, NoInfer<TKey>, TErrors> & {
|
|
66
93
|
publish: (event: z.input<TEventSchema>) => Promise<void>;
|
|
67
94
|
}) => MaybePromise<z.input<TOutputSchema>>;
|
|
68
|
-
live: FnLiveConfig<TDef, z.output<TInput>, TEventSchema, z.output<TOutputSchema>, NoInfer<TKey>, TEmit>;
|
|
69
|
-
} & FnRouteOptions<TDef, TKey>): FnLiveReturn<TDef, TKey, TInput, TOutputSchema, z.output<TOutputSchema>, TEventSchema, TEmit>;
|
|
70
|
-
<TInput extends ObjectSchema, TOutput, TEventSchema extends ZodType, TKey extends ProcedureKey<TDef> = TDef['default'], TEmit = never
|
|
95
|
+
live: FnLiveConfig<TDef, z.output<TInput>, TEventSchema, z.output<TOutputSchema>, NoInfer<TKey>, TEmit, TErrors>;
|
|
96
|
+
} & FnRouteOptions<TDef, TKey, z.output<TInput>, TErrors>): FnLiveReturn<TDef, TKey, TInput, TOutputSchema, z.output<TOutputSchema>, TEventSchema, TEmit, TErrors>;
|
|
97
|
+
<TInput extends ObjectSchema, TOutput, TEventSchema extends ZodType, TKey extends ProcedureKey<TDef> = TDef['default'], TEmit = never, TErrors extends ErrorMap = Record<never, never>>(options: {
|
|
71
98
|
input: TInput;
|
|
72
99
|
output?: undefined;
|
|
73
|
-
handler: (params: HandlerParams<TDef, z.output<TInput>, NoInfer<TKey
|
|
100
|
+
handler: (params: HandlerParams<TDef, z.output<TInput>, NoInfer<TKey>, TErrors> & {
|
|
74
101
|
publish: (event: z.input<TEventSchema>) => Promise<void>;
|
|
75
102
|
}) => MaybePromise<TOutput>;
|
|
76
|
-
live: FnLiveConfig<TDef, z.output<TInput>, TEventSchema, TOutput, NoInfer<TKey>, TEmit>;
|
|
77
|
-
} & FnRouteOptions<TDef, TKey>): FnLiveReturn<TDef, TKey, TInput, Schema<TOutput, TOutput>, TOutput, TEventSchema, TEmit>;
|
|
103
|
+
live: FnLiveConfig<TDef, z.output<TInput>, TEventSchema, TOutput, NoInfer<TKey>, TEmit, TErrors>;
|
|
104
|
+
} & FnRouteOptions<TDef, TKey, z.output<TInput>, TErrors>): FnLiveReturn<TDef, TKey, TInput, Schema<TOutput, TOutput>, TOutput, TEventSchema, TEmit, TErrors>;
|
|
78
105
|
}
|
|
79
106
|
/**
|
|
80
107
|
* Fail a live subscription whose initial snapshot could not be built.
|
|
@@ -104,6 +131,9 @@ isExpected?: (error: unknown) => boolean): never;
|
|
|
104
131
|
*/
|
|
105
132
|
export declare function streamLiveSnapshots<TEvent, TOutput, TEmit = never>(opts: {
|
|
106
133
|
source: AsyncIterator<TEvent>;
|
|
134
|
+
/** Maximum events retained in one coalescing batch. */
|
|
135
|
+
maxBatchSize?: number;
|
|
136
|
+
applyBatch?: (events: readonly TEvent[], previous: TOutput) => MaybePromise<TOutput | undefined>;
|
|
107
137
|
initial: TOutput;
|
|
108
138
|
coalesceMs?: number | undefined;
|
|
109
139
|
apply: (event: TEvent, previous: TOutput) => MaybePromise<TOutput | FnLivePatch<TOutput, TEmit> | undefined>;
|
|
@@ -113,14 +143,20 @@ type RuntimeParams = Record<string, unknown> & {
|
|
|
113
143
|
context: unknown;
|
|
114
144
|
signal: AbortSignal | undefined;
|
|
115
145
|
logger: FnLogger;
|
|
146
|
+
call: (procedure: AnyProcedure, input: unknown) => Promise<unknown>;
|
|
147
|
+
[RAW_INPUT]: unknown;
|
|
116
148
|
};
|
|
117
149
|
/** `FnLiveConfig` with the generics erased: what the implementation reads. */
|
|
118
150
|
type RuntimeLiveConfig = Omit<ChannelOptions, 'name'> & {
|
|
119
151
|
name?: string;
|
|
152
|
+
stateSchema?: AnySchema;
|
|
153
|
+
emitSchema?: AnySchema;
|
|
154
|
+
reauthorize?: AuthFn<Record<string, unknown>, unknown>;
|
|
120
155
|
transformerFn?: (params: RuntimeParams & Record<string, unknown>) => unknown;
|
|
121
156
|
shouldUpdate?: (params: RuntimeParams & Record<string, unknown>) => unknown;
|
|
122
157
|
safePublish?: boolean;
|
|
123
158
|
coalesceMs?: number;
|
|
159
|
+
maxBatchSize?: number;
|
|
124
160
|
summary?: string;
|
|
125
161
|
description?: string;
|
|
126
162
|
};
|
|
@@ -150,6 +186,15 @@ export declare function createFnLive(runtime: {
|
|
|
150
186
|
getChannelName: (params: {
|
|
151
187
|
[x: string]: unknown;
|
|
152
188
|
}, context?: unknown) => string;
|
|
189
|
+
getSubscriptionChannelName: (params: {
|
|
190
|
+
input: {
|
|
191
|
+
[x: string]: unknown;
|
|
192
|
+
};
|
|
193
|
+
context: unknown;
|
|
194
|
+
}) => string;
|
|
195
|
+
getPublishChannelName: (event: {
|
|
196
|
+
[x: string]: unknown;
|
|
197
|
+
}) => string;
|
|
153
198
|
};
|
|
154
199
|
export {};
|
|
155
200
|
//# sourceMappingURL=fn-live.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fn-live.d.ts","sourceRoot":"","sources":["../../src/live/fn-live.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"fn-live.d.ts","sourceRoot":"","sources":["../../src/live/fn-live.ts"],"names":[],"mappings":"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"}
|
package/dist/live/fn-live.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { ORPCError } from '@orpc/server';
|
|
2
|
+
import { RAW_INPUT } from '../timing.js';
|
|
3
|
+
import { LIVE_GAP, positiveInteger } from './lifecycle.js';
|
|
2
4
|
import { errorMessageOf } from '../otel.js';
|
|
3
5
|
/** Marks a coalesce window that ended before the next event arrived. */
|
|
4
6
|
const TIMEOUT = Symbol('timeout');
|
|
@@ -54,6 +56,10 @@ function toSnapshotError(err, liveName) {
|
|
|
54
56
|
*/
|
|
55
57
|
export async function* streamLiveSnapshots(opts) {
|
|
56
58
|
const { source, initial, coalesceMs, apply } = opts;
|
|
59
|
+
const maxBatchSize = positiveInteger(opts.maxBatchSize ?? 1000, 'maxBatchSize');
|
|
60
|
+
if (coalesceMs !== undefined &&
|
|
61
|
+
(!Number.isFinite(coalesceMs) || coalesceMs < 0))
|
|
62
|
+
throw new RangeError('orpc-fn: coalesceMs must be finite and nonnegative');
|
|
57
63
|
let pending = null;
|
|
58
64
|
const readNext = () => {
|
|
59
65
|
const read = pending ?? source.next();
|
|
@@ -69,7 +75,7 @@ export async function* streamLiveSnapshots(opts) {
|
|
|
69
75
|
const batch = [current.value];
|
|
70
76
|
if (coalesceMs) {
|
|
71
77
|
const endAt = Date.now() + coalesceMs;
|
|
72
|
-
while (Date.now() < endAt) {
|
|
78
|
+
while (Date.now() < endAt && batch.length < maxBatchSize) {
|
|
73
79
|
const remainingMs = endAt - Date.now();
|
|
74
80
|
const read = readNext();
|
|
75
81
|
let timer;
|
|
@@ -93,18 +99,33 @@ export async function* streamLiveSnapshots(opts) {
|
|
|
93
99
|
batch.push(result.value);
|
|
94
100
|
}
|
|
95
101
|
}
|
|
102
|
+
if (opts.applyBatch) {
|
|
103
|
+
const next = await opts.applyBatch(batch, previous);
|
|
104
|
+
if (next !== undefined) {
|
|
105
|
+
previous = next;
|
|
106
|
+
yield previous;
|
|
107
|
+
}
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
let changed = false;
|
|
96
111
|
for (const event of batch) {
|
|
97
112
|
const next = await apply(event, previous);
|
|
98
113
|
if (next === undefined)
|
|
99
114
|
continue;
|
|
100
115
|
if (isFnLivePatch(next)) {
|
|
116
|
+
if (changed) {
|
|
117
|
+
yield previous;
|
|
118
|
+
changed = false;
|
|
119
|
+
}
|
|
101
120
|
previous = next.state;
|
|
102
121
|
yield next.emit;
|
|
103
122
|
continue;
|
|
104
123
|
}
|
|
105
124
|
previous = next;
|
|
106
|
-
|
|
125
|
+
changed = true;
|
|
107
126
|
}
|
|
127
|
+
if (changed)
|
|
128
|
+
yield previous;
|
|
108
129
|
}
|
|
109
130
|
}
|
|
110
131
|
/**
|
|
@@ -116,6 +137,13 @@ export function createFnLive(runtime) {
|
|
|
116
137
|
const liveLogger = runtime.createLogger('fn-live', undefined);
|
|
117
138
|
return (options) => {
|
|
118
139
|
const { live, handler, input, output, name, ...routeConfig } = options;
|
|
140
|
+
if (live.transformerFn && !live.stateSchema)
|
|
141
|
+
throw new TypeError('orpc-fn: live transformers require a stateSchema for parsed state');
|
|
142
|
+
if (live.coalesceMs !== undefined &&
|
|
143
|
+
(!Number.isFinite(live.coalesceMs) || live.coalesceMs < 0))
|
|
144
|
+
throw new RangeError('orpc-fn: coalesceMs must be finite and nonnegative');
|
|
145
|
+
if (live.maxBatchSize !== undefined)
|
|
146
|
+
positiveInteger(live.maxBatchSize, 'maxBatchSize');
|
|
119
147
|
const liveName = live.name ?? `${name}.live`;
|
|
120
148
|
const liveSummary = live.summary ?? `Live updates of ${routeConfig.summary ?? name}`;
|
|
121
149
|
const liveDescription = live.description ?? routeConfig.description;
|
|
@@ -137,11 +165,6 @@ export function createFnLive(runtime) {
|
|
|
137
165
|
}
|
|
138
166
|
};
|
|
139
167
|
const runHandler = (params) => handler({ ...params, publish });
|
|
140
|
-
// Snapshots go through the output schema like the route's own result.
|
|
141
|
-
const runSnapshot = async (params) => {
|
|
142
|
-
const result = await runHandler(params);
|
|
143
|
-
return output ? output.parseAsync(result) : result;
|
|
144
|
-
};
|
|
145
168
|
const procedure = fn({
|
|
146
169
|
...routeConfig,
|
|
147
170
|
name,
|
|
@@ -149,10 +172,24 @@ export function createFnLive(runtime) {
|
|
|
149
172
|
output,
|
|
150
173
|
handler: runHandler
|
|
151
174
|
});
|
|
152
|
-
|
|
175
|
+
const validate = async (schema, value) => {
|
|
176
|
+
const result = await schema['~standard'].validate(value);
|
|
177
|
+
if (result.issues)
|
|
178
|
+
throw new ORPCError('INTERNAL_SERVER_ERROR', {
|
|
179
|
+
message: 'Invalid live state or patch',
|
|
180
|
+
cause: result.issues
|
|
181
|
+
});
|
|
182
|
+
return result.value;
|
|
183
|
+
};
|
|
184
|
+
const runSnapshot = (params) => params.call(procedure, params[RAW_INPUT]);
|
|
185
|
+
// Generated streams inherit access policies and metadata; tool adapters reject streams.
|
|
153
186
|
const guardOptions = Object.fromEntries(Object.entries(routeConfig).filter(([option]) => isGuard(option)));
|
|
154
187
|
const subscribe = fn({
|
|
155
188
|
...guardOptions,
|
|
189
|
+
guardResolvers: routeConfig.guardResolvers,
|
|
190
|
+
errors: routeConfig.errors,
|
|
191
|
+
meta: routeConfig.meta,
|
|
192
|
+
stream: true,
|
|
156
193
|
procedure: routeConfig.procedure,
|
|
157
194
|
tags: routeConfig.tags,
|
|
158
195
|
name: liveName,
|
|
@@ -166,9 +203,12 @@ export function createFnLive(runtime) {
|
|
|
166
203
|
const subscription = await channel.open({
|
|
167
204
|
input: params.input,
|
|
168
205
|
context: params.context,
|
|
169
|
-
signal: params.signal
|
|
206
|
+
signal: params.signal,
|
|
207
|
+
recoverGaps: true
|
|
170
208
|
});
|
|
171
209
|
try {
|
|
210
|
+
if (subscription.isClosed())
|
|
211
|
+
return;
|
|
172
212
|
let initial;
|
|
173
213
|
try {
|
|
174
214
|
initial = await runSnapshot(params);
|
|
@@ -177,21 +217,61 @@ export function createFnLive(runtime) {
|
|
|
177
217
|
// `fn.completed` logs it, at the level `isExpectedError` picks.
|
|
178
218
|
throw toSnapshotError(err, liveName);
|
|
179
219
|
}
|
|
180
|
-
const rerun = (
|
|
220
|
+
const rerun = () => runSnapshot(params);
|
|
221
|
+
const authorize = async () => {
|
|
222
|
+
if (live.reauthorize &&
|
|
223
|
+
live.reauthorize !== true &&
|
|
224
|
+
!(await live.reauthorize({
|
|
225
|
+
input: params.input,
|
|
226
|
+
ctx: params.context
|
|
227
|
+
})))
|
|
228
|
+
throw new ORPCError('FORBIDDEN');
|
|
229
|
+
};
|
|
181
230
|
const apply = async (event, previous) => {
|
|
231
|
+
await authorize();
|
|
232
|
+
if (event === LIVE_GAP)
|
|
233
|
+
return rerun();
|
|
182
234
|
if (live.shouldUpdate &&
|
|
183
|
-
!(await live.shouldUpdate({ ...params, event, previous })))
|
|
235
|
+
!(await live.shouldUpdate({ ...params, event, previous })))
|
|
184
236
|
return undefined;
|
|
185
|
-
}
|
|
186
237
|
if (!live.transformerFn)
|
|
187
|
-
return
|
|
188
|
-
|
|
238
|
+
return rerun();
|
|
239
|
+
const next = await live.transformerFn({
|
|
240
|
+
...params,
|
|
241
|
+
event,
|
|
242
|
+
previous,
|
|
243
|
+
rerun
|
|
244
|
+
});
|
|
245
|
+
if (next === undefined)
|
|
246
|
+
return undefined;
|
|
247
|
+
if (isFnLivePatch(next)) {
|
|
248
|
+
if (!live.emitSchema)
|
|
249
|
+
throw new TypeError('orpc-fn: live patches require emitSchema');
|
|
250
|
+
return fnLivePatch(await validate(live.stateSchema, next.state), await validate(live.emitSchema, next.emit));
|
|
251
|
+
}
|
|
252
|
+
return validate(live.stateSchema, next);
|
|
189
253
|
};
|
|
254
|
+
const applyBatch = live.transformerFn
|
|
255
|
+
? undefined
|
|
256
|
+
: async (events, previous) => {
|
|
257
|
+
await authorize();
|
|
258
|
+
let invalidated = false;
|
|
259
|
+
for (const event of events)
|
|
260
|
+
if (event === LIVE_GAP ||
|
|
261
|
+
!live.shouldUpdate ||
|
|
262
|
+
(await live.shouldUpdate({ ...params, event, previous })))
|
|
263
|
+
invalidated = true;
|
|
264
|
+
return invalidated ? rerun() : undefined;
|
|
265
|
+
};
|
|
190
266
|
yield* streamLiveSnapshots({
|
|
191
267
|
source: subscription.events,
|
|
192
268
|
initial,
|
|
193
269
|
coalesceMs: live.coalesceMs,
|
|
194
|
-
|
|
270
|
+
...(live.maxBatchSize !== undefined
|
|
271
|
+
? { maxBatchSize: live.maxBatchSize }
|
|
272
|
+
: {}),
|
|
273
|
+
apply,
|
|
274
|
+
...(applyBatch ? { applyBatch } : {})
|
|
195
275
|
});
|
|
196
276
|
}
|
|
197
277
|
finally {
|
|
@@ -203,7 +283,9 @@ export function createFnLive(runtime) {
|
|
|
203
283
|
procedure,
|
|
204
284
|
subscribe,
|
|
205
285
|
publish,
|
|
206
|
-
getChannelName: channel.getChannelName
|
|
286
|
+
getChannelName: channel.getChannelName,
|
|
287
|
+
getSubscriptionChannelName: channel.getSubscriptionChannelName,
|
|
288
|
+
getPublishChannelName: channel.getPublishChannelName
|
|
207
289
|
};
|
|
208
290
|
};
|
|
209
291
|
}
|