@ts-pf/server 0.1.0-beta.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 +7 -0
- package/dist/call-interceptor.d.ts +28 -0
- package/dist/call-interceptor.d.ts.map +1 -0
- package/dist/call-interceptor.js +40 -0
- package/dist/call-interceptor.js.map +1 -0
- package/dist/caller.d.ts +10 -0
- package/dist/caller.d.ts.map +1 -0
- package/dist/caller.js +53 -0
- package/dist/caller.js.map +1 -0
- package/dist/dedupe-plugin.d.ts +15 -0
- package/dist/dedupe-plugin.d.ts.map +1 -0
- package/dist/dedupe-plugin.js +118 -0
- package/dist/dedupe-plugin.js.map +1 -0
- package/dist/error-factory.d.ts +5 -0
- package/dist/error-factory.d.ts.map +1 -0
- package/dist/error-factory.js +39 -0
- package/dist/error-factory.js.map +1 -0
- package/dist/events.d.ts +32 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +59 -0
- package/dist/events.js.map +1 -0
- package/dist/implement.d.ts +28 -0
- package/dist/implement.d.ts.map +1 -0
- package/dist/implement.js +132 -0
- package/dist/implement.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +18 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +2 -0
- package/dist/middleware.js.map +1 -0
- package/dist/plugin.d.ts +7 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +4 -0
- package/dist/plugin.js.map +1 -0
- package/dist/runtime.d.ts +37 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +165 -0
- package/dist/runtime.js.map +1 -0
- package/package.json +49 -0
- package/skills/ts-pf-server/SKILL.md +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# @ts-pf/server
|
|
2
|
+
|
|
3
|
+
`createImplementer()`, middleware, `runProcedure`, `lookupProcedure`, `createLocalClient()`, and server call interceptors / `DedupePlugin`. No `Request` / `Response`.
|
|
4
|
+
|
|
5
|
+
Agent skill: [`skills/ts-pf-server/`](skills/ts-pf-server/). Sync with `npx skills experimental_sync -y`.
|
|
6
|
+
|
|
7
|
+
HTTP serving is [`@ts-pf/server-http`](../server-http). See the [root README](../../README.md) for the Fetch happy path.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ImplementedProcedure } from './runtime.js';
|
|
2
|
+
export type CallInterceptor = (ctx: {
|
|
3
|
+
procedure: ImplementedProcedure;
|
|
4
|
+
path: string[];
|
|
5
|
+
input: unknown;
|
|
6
|
+
context: unknown;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
next: (opts?: {
|
|
9
|
+
input?: unknown;
|
|
10
|
+
/** Replaces context entirely; does not merge. */
|
|
11
|
+
context?: unknown;
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
}) => Promise<unknown>;
|
|
14
|
+
}) => Promise<unknown>;
|
|
15
|
+
type CallState = {
|
|
16
|
+
input: unknown;
|
|
17
|
+
context: unknown;
|
|
18
|
+
signal?: AbortSignal;
|
|
19
|
+
};
|
|
20
|
+
export declare function runCallInterceptors(interceptors: readonly CallInterceptor[], ctx: {
|
|
21
|
+
procedure: ImplementedProcedure;
|
|
22
|
+
path: string[];
|
|
23
|
+
input: unknown;
|
|
24
|
+
context: unknown;
|
|
25
|
+
signal?: AbortSignal;
|
|
26
|
+
}, execute: (current: CallState) => Promise<unknown>): Promise<unknown>;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=call-interceptor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-interceptor.d.ts","sourceRoot":"","sources":["../src/call-interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAExD,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE;IAClC,SAAS,EAAE,oBAAoB,CAAA;IAC/B,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE;QACZ,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,iDAAiD;QACjD,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,MAAM,CAAC,EAAE,WAAW,CAAA;KACrB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CACvB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAEtB,KAAK,SAAS,GAAG;IACf,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,CAAA;AAED,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,SAAS,eAAe,EAAE,EACxC,GAAG,EAAE;IACH,SAAS,EAAE,oBAAoB,CAAA;IAC/B,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,EACD,OAAO,EAAE,CAAC,OAAO,EAAE,SAAS,KAAK,OAAO,CAAC,OAAO,CAAC,GAChD,OAAO,CAAC,OAAO,CAAC,CAsClB"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export function runCallInterceptors(interceptors, ctx, execute) {
|
|
2
|
+
const interceptorPath = ctx.path.slice();
|
|
3
|
+
const run = (index, current) => {
|
|
4
|
+
const interceptor = interceptors[index];
|
|
5
|
+
if (!interceptor) {
|
|
6
|
+
return execute(current);
|
|
7
|
+
}
|
|
8
|
+
return interceptor({
|
|
9
|
+
procedure: ctx.procedure,
|
|
10
|
+
path: interceptorPath,
|
|
11
|
+
input: current.input,
|
|
12
|
+
context: current.context,
|
|
13
|
+
...(current.signal ? { signal: current.signal } : {}),
|
|
14
|
+
next: (opts) => {
|
|
15
|
+
const nextCurrent = {
|
|
16
|
+
input: opts && 'input' in opts ? opts.input : current.input,
|
|
17
|
+
context: opts && 'context' in opts ? opts.context : current.context,
|
|
18
|
+
};
|
|
19
|
+
if (opts && 'signal' in opts) {
|
|
20
|
+
if (opts.signal) {
|
|
21
|
+
nextCurrent.signal = opts.signal;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else if (current.signal) {
|
|
25
|
+
nextCurrent.signal = current.signal;
|
|
26
|
+
}
|
|
27
|
+
return run(index + 1, nextCurrent);
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
const initial = {
|
|
32
|
+
input: ctx.input,
|
|
33
|
+
context: ctx.context,
|
|
34
|
+
};
|
|
35
|
+
if (ctx.signal) {
|
|
36
|
+
initial.signal = ctx.signal;
|
|
37
|
+
}
|
|
38
|
+
return run(0, initial);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=call-interceptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-interceptor.js","sourceRoot":"","sources":["../src/call-interceptor.ts"],"names":[],"mappings":"AAsBA,MAAM,UAAU,mBAAmB,CACjC,YAAwC,EACxC,GAMC,EACD,OAAiD;IAEjD,MAAM,eAAe,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,CAAA;IACxC,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,OAAkB,EAAoB,EAAE;QAClE,MAAM,WAAW,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;QACvC,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,OAAO,CAAC,OAAO,CAAC,CAAA;QACzB,CAAC;QACD,OAAO,WAAW,CAAC;YACjB,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;gBACb,MAAM,WAAW,GAAc;oBAC7B,KAAK,EAAE,IAAI,IAAI,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK;oBAC3D,OAAO,EAAE,IAAI,IAAI,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO;iBACpE,CAAA;gBACD,IAAI,IAAI,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;oBAC7B,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;wBAChB,WAAW,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;oBAClC,CAAC;gBACH,CAAC;qBAAM,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;oBAC1B,WAAW,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;gBACrC,CAAC;gBACD,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,WAAW,CAAC,CAAA;YACpC,CAAC;SACF,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,MAAM,OAAO,GAAc;QACzB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,OAAO,EAAE,GAAG,CAAC,OAAO;KACrB,CAAA;IACD,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAA;IAC7B,CAAC;IACD,OAAO,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;AACxB,CAAC"}
|
package/dist/caller.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ContractClient } from '@ts-pf/contract';
|
|
2
|
+
import type { CallInterceptor } from './call-interceptor.js';
|
|
3
|
+
import { type CallPlugin } from './plugin.js';
|
|
4
|
+
import { type ImplementedRouter } from './runtime.js';
|
|
5
|
+
export declare function createLocalClient<T>(router: ImplementedRouter<T>, opts: {
|
|
6
|
+
context: unknown;
|
|
7
|
+
interceptors?: readonly CallInterceptor[];
|
|
8
|
+
plugins?: readonly CallPlugin[];
|
|
9
|
+
}): ContractClient<T>;
|
|
10
|
+
//# sourceMappingURL=caller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caller.d.ts","sourceRoot":"","sources":["../src/caller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAC3D,OAAO,EAEL,KAAK,iBAAiB,EAIvB,MAAM,cAAc,CAAA;AA2DrB,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAC5B,IAAI,EAAE;IACJ,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;IACzC,OAAO,CAAC,EAAE,SAAS,UAAU,EAAE,CAAA;CAChC,GACA,cAAc,CAAC,CAAC,CAAC,CAGnB"}
|
package/dist/caller.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { applyPlugins } from './plugin.js';
|
|
2
|
+
import { isImplementedProcedure, runProcedure, } from './runtime.js';
|
|
3
|
+
function buildRunOptions(signal, interceptors) {
|
|
4
|
+
if (!signal && interceptors.length === 0) {
|
|
5
|
+
return undefined;
|
|
6
|
+
}
|
|
7
|
+
const options = {};
|
|
8
|
+
if (signal) {
|
|
9
|
+
options.signal = signal;
|
|
10
|
+
}
|
|
11
|
+
if (interceptors.length > 0) {
|
|
12
|
+
options.interceptors = interceptors;
|
|
13
|
+
}
|
|
14
|
+
return options;
|
|
15
|
+
}
|
|
16
|
+
function createNode(node, context, interceptors) {
|
|
17
|
+
if (isImplementedProcedure(node)) {
|
|
18
|
+
return (...args) => {
|
|
19
|
+
const first = args[0];
|
|
20
|
+
const second = args[1];
|
|
21
|
+
if (args.length < 2 &&
|
|
22
|
+
typeof first === 'object' &&
|
|
23
|
+
first !== null &&
|
|
24
|
+
'signal' in first &&
|
|
25
|
+
first.signal instanceof AbortSignal) {
|
|
26
|
+
const signal = first.signal;
|
|
27
|
+
const options = buildRunOptions(signal, interceptors);
|
|
28
|
+
return options
|
|
29
|
+
? runProcedure(node, undefined, context, options)
|
|
30
|
+
: runProcedure(node, undefined, context);
|
|
31
|
+
}
|
|
32
|
+
const options = buildRunOptions(second?.signal, interceptors);
|
|
33
|
+
return options
|
|
34
|
+
? runProcedure(node, first, context, options)
|
|
35
|
+
: runProcedure(node, first, context);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const nested = {};
|
|
39
|
+
for (const [key, child] of Object.entries(node)) {
|
|
40
|
+
if (key === '~pf') {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (child) {
|
|
44
|
+
nested[key] = createNode(child, context, interceptors);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return nested;
|
|
48
|
+
}
|
|
49
|
+
export function createLocalClient(router, opts) {
|
|
50
|
+
const interceptors = applyPlugins(opts.plugins ?? [], opts.interceptors);
|
|
51
|
+
return createNode(router, opts.context, interceptors);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=caller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caller.js","sourceRoot":"","sources":["../src/caller.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAmB,MAAM,aAAa,CAAA;AAC3D,OAAO,EAGL,sBAAsB,EAEtB,YAAY,GACb,MAAM,cAAc,CAAA;AAErB,SAAS,eAAe,CACtB,MAA+B,EAC/B,YAAwC;IAExC,IAAI,CAAC,MAAM,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzC,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,OAAO,GAAwB,EAAE,CAAA;IACvC,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,MAAM,GAAG,MAAM,CAAA;IACzB,CAAC;IACD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,YAAY,GAAG,YAAY,CAAA;IACrC,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,SAAS,UAAU,CACjB,IAAuD,EACvD,OAAgB,EAChB,YAAwC;IAExC,IAAI,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,OAAO,CAAC,GAAG,IAAe,EAAE,EAAE;YAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACrB,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAyC,CAAA;YAC9D,IACE,IAAI,CAAC,MAAM,GAAG,CAAC;gBACf,OAAO,KAAK,KAAK,QAAQ;gBACzB,KAAK,KAAK,IAAI;gBACd,QAAQ,IAAI,KAAK;gBAChB,KAA8B,CAAC,MAAM,YAAY,WAAW,EAC7D,CAAC;gBACD,MAAM,MAAM,GAAI,KAAiC,CAAC,MAAM,CAAA;gBACxD,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;gBACrD,OAAO,OAAO;oBACZ,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC;oBACjD,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,CAAA;YAC5C,CAAC;YACD,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,CAAA;YAC7D,OAAO,OAAO;gBACZ,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC;gBAC7C,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;QACxC,CAAC,CAAA;IACH,CAAC;IACD,MAAM,MAAM,GAA4B,EAAE,CAAA;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;YAClB,SAAQ;QACV,CAAC;QACD,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;QACxD,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,MAA4B,EAC5B,IAIC;IAED,MAAM,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;IACxE,OAAO,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,CAAsB,CAAA;AAC5E,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CallInterceptor } from './call-interceptor.js';
|
|
2
|
+
import type { CallPlugin } from './plugin.js';
|
|
3
|
+
export type DedupePluginOptions = {
|
|
4
|
+
key?: (ctx: {
|
|
5
|
+
path: string[];
|
|
6
|
+
input: unknown;
|
|
7
|
+
}) => string | undefined;
|
|
8
|
+
};
|
|
9
|
+
/** In-flight coalescing around runProcedure. Default keys every unary call; pass `key` to restrict to reads — unsafe for non-idempotent writes. */
|
|
10
|
+
export declare class DedupePlugin implements CallPlugin {
|
|
11
|
+
readonly name = "dedupe";
|
|
12
|
+
readonly intercept: CallInterceptor;
|
|
13
|
+
constructor(options?: DedupePluginOptions);
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=dedupe-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedupe-plugin.d.ts","sourceRoot":"","sources":["../src/dedupe-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE7C,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,KAAK,MAAM,GAAG,SAAS,CAAA;CACtE,CAAA;AAuFD,mJAAmJ;AACnJ,qBAAa,YAAa,YAAW,UAAU;IAC7C,QAAQ,CAAC,IAAI,YAAW;IACxB,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAA;gBAEvB,OAAO,CAAC,EAAE,mBAAmB;CAmD1C"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
function defaultKey(ctx) {
|
|
2
|
+
if (typeof ctx.input === 'object' &&
|
|
3
|
+
ctx.input !== null &&
|
|
4
|
+
Symbol.asyncIterator in ctx.input) {
|
|
5
|
+
return undefined;
|
|
6
|
+
}
|
|
7
|
+
try {
|
|
8
|
+
return JSON.stringify([ctx.path, ctx.input]);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function joinInflight(entry, inflight, key, signal) {
|
|
15
|
+
signal?.throwIfAborted();
|
|
16
|
+
const waiter = {};
|
|
17
|
+
entry.waiters.add(waiter);
|
|
18
|
+
const dropIfLast = () => {
|
|
19
|
+
entry.waiters.delete(waiter);
|
|
20
|
+
if (entry.waiters.size === 0) {
|
|
21
|
+
if (inflight.get(key) === entry) {
|
|
22
|
+
inflight.delete(key);
|
|
23
|
+
}
|
|
24
|
+
entry.controller.abort();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
if (!signal) {
|
|
28
|
+
return entry.promise.finally(() => {
|
|
29
|
+
entry.waiters.delete(waiter);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
let done = false;
|
|
34
|
+
const finish = (fn) => {
|
|
35
|
+
if (done) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
done = true;
|
|
39
|
+
signal.removeEventListener('abort', onAbort);
|
|
40
|
+
fn();
|
|
41
|
+
};
|
|
42
|
+
const onAbort = () => {
|
|
43
|
+
finish(() => {
|
|
44
|
+
dropIfLast();
|
|
45
|
+
reject(signal.reason);
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
49
|
+
if (signal.aborted) {
|
|
50
|
+
onAbort();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
entry.promise.then((value) => {
|
|
54
|
+
finish(() => {
|
|
55
|
+
entry.waiters.delete(waiter);
|
|
56
|
+
resolve(value);
|
|
57
|
+
});
|
|
58
|
+
}, (error) => {
|
|
59
|
+
finish(() => {
|
|
60
|
+
entry.waiters.delete(waiter);
|
|
61
|
+
reject(error);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/** In-flight coalescing around runProcedure. Default keys every unary call; pass `key` to restrict to reads — unsafe for non-idempotent writes. */
|
|
67
|
+
export class DedupePlugin {
|
|
68
|
+
name = 'dedupe';
|
|
69
|
+
intercept;
|
|
70
|
+
constructor(options) {
|
|
71
|
+
const keyFn = options?.key ?? defaultKey;
|
|
72
|
+
const byContext = new WeakMap();
|
|
73
|
+
const anonymous = new Map();
|
|
74
|
+
const bucket = (context) => {
|
|
75
|
+
if (typeof context === 'object' && context !== null) {
|
|
76
|
+
let map = byContext.get(context);
|
|
77
|
+
if (!map) {
|
|
78
|
+
map = new Map();
|
|
79
|
+
byContext.set(context, map);
|
|
80
|
+
}
|
|
81
|
+
return map;
|
|
82
|
+
}
|
|
83
|
+
return anonymous;
|
|
84
|
+
};
|
|
85
|
+
this.intercept = async (ctx) => {
|
|
86
|
+
if (typeof ctx.input === 'object' &&
|
|
87
|
+
ctx.input !== null &&
|
|
88
|
+
Symbol.asyncIterator in ctx.input) {
|
|
89
|
+
return ctx.next();
|
|
90
|
+
}
|
|
91
|
+
const key = keyFn({ path: ctx.path, input: ctx.input });
|
|
92
|
+
if (key === undefined) {
|
|
93
|
+
return ctx.next();
|
|
94
|
+
}
|
|
95
|
+
const inflight = bucket(ctx.context);
|
|
96
|
+
const existing = inflight.get(key);
|
|
97
|
+
if (existing) {
|
|
98
|
+
return joinInflight(existing, inflight, key, ctx.signal);
|
|
99
|
+
}
|
|
100
|
+
ctx.signal?.throwIfAborted();
|
|
101
|
+
const controller = new AbortController();
|
|
102
|
+
const waiters = new Set();
|
|
103
|
+
const inner = ctx.next({ signal: controller.signal });
|
|
104
|
+
const entry = {
|
|
105
|
+
controller,
|
|
106
|
+
waiters,
|
|
107
|
+
promise: inner.finally(() => {
|
|
108
|
+
if (inflight.get(key) === entry) {
|
|
109
|
+
inflight.delete(key);
|
|
110
|
+
}
|
|
111
|
+
}),
|
|
112
|
+
};
|
|
113
|
+
inflight.set(key, entry);
|
|
114
|
+
return joinInflight(entry, inflight, key, ctx.signal);
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=dedupe-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedupe-plugin.js","sourceRoot":"","sources":["../src/dedupe-plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ErrorMap } from '@ts-pf/contract';
|
|
2
|
+
import type { ErrorFactory } from './middleware.js';
|
|
3
|
+
export declare function createErrorFactory(map: ErrorMap): ErrorFactory<ErrorMap>;
|
|
4
|
+
export declare function finalizeDeclaredError(error: unknown, map: ErrorMap): Promise<never>;
|
|
5
|
+
//# sourceMappingURL=error-factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-factory.d.ts","sourceRoot":"","sources":["../src/error-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAkB,MAAM,iBAAiB,CAAA;AAE/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEnD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAaxE;AAED,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,QAAQ,GACZ,OAAO,CAAC,KAAK,CAAC,CAqBhB"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { validateSchema } from '@ts-pf/contract';
|
|
2
|
+
import { isPFError, PFError } from '@ts-pf/protocol';
|
|
3
|
+
export function createErrorFactory(map) {
|
|
4
|
+
const factory = {};
|
|
5
|
+
for (const [code, def] of Object.entries(map)) {
|
|
6
|
+
factory[code] = ((data) => {
|
|
7
|
+
throw new PFError({
|
|
8
|
+
code,
|
|
9
|
+
status: def.status ?? 400,
|
|
10
|
+
message: def.message ?? code,
|
|
11
|
+
...(data !== undefined ? { data } : {}),
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
return factory;
|
|
16
|
+
}
|
|
17
|
+
export async function finalizeDeclaredError(error, map) {
|
|
18
|
+
if (!isPFError(error)) {
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
const def = map[error.code];
|
|
22
|
+
if (def?.data === undefined) {
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
const result = await validateSchema(def.data, error.data);
|
|
26
|
+
if (!result.success) {
|
|
27
|
+
throw new PFError({
|
|
28
|
+
code: 'INTERNAL',
|
|
29
|
+
message: 'Internal server error',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
throw new PFError({
|
|
33
|
+
code: error.code,
|
|
34
|
+
status: error.status,
|
|
35
|
+
message: error.message,
|
|
36
|
+
...(result.value !== undefined ? { data: result.value } : {}),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=error-factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-factory.js","sourceRoot":"","sources":["../src/error-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAC/D,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAGpD,MAAM,UAAU,kBAAkB,CAAC,GAAa;IAC9C,MAAM,OAAO,GAA2B,EAAE,CAAA;IAC1C,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9C,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAc,EAAE,EAAE;YAClC,MAAM,IAAI,OAAO,CAAC;gBAChB,IAAI;gBACJ,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,GAAG;gBACzB,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,IAAI;gBAC5B,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxC,CAAC,CAAA;QACJ,CAAC,CAAyB,CAAA;IAC5B,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,KAAc,EACd,GAAa;IAEb,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,KAAK,CAAA;IACb,CAAC;IACD,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC3B,IAAI,GAAG,EAAE,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,MAAM,KAAK,CAAA;IACb,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;IACzD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,OAAO,CAAC;YAChB,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,uBAAuB;SACjC,CAAC,CAAA;IACJ,CAAC;IACD,MAAM,IAAI,OAAO,CAAC;QAChB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9D,CAAC,CAAA;AACJ,CAAC"}
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CallInterceptor } from './call-interceptor.js';
|
|
2
|
+
export declare function onStart(fn: (ctx: {
|
|
3
|
+
path: string[];
|
|
4
|
+
input: unknown;
|
|
5
|
+
context: unknown;
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
}) => void | Promise<void>): CallInterceptor;
|
|
8
|
+
export declare function onSuccess(fn: (ctx: {
|
|
9
|
+
path: string[];
|
|
10
|
+
input: unknown;
|
|
11
|
+
context: unknown;
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
}, output: unknown) => void | Promise<void>): CallInterceptor;
|
|
14
|
+
export declare function onError(fn: (ctx: {
|
|
15
|
+
path: string[];
|
|
16
|
+
input: unknown;
|
|
17
|
+
context: unknown;
|
|
18
|
+
signal?: AbortSignal;
|
|
19
|
+
}, error: unknown) => void | Promise<void>): CallInterceptor;
|
|
20
|
+
export declare function onFinish(fn: (ctx: {
|
|
21
|
+
path: string[];
|
|
22
|
+
input: unknown;
|
|
23
|
+
context: unknown;
|
|
24
|
+
signal?: AbortSignal;
|
|
25
|
+
}, result: {
|
|
26
|
+
ok: true;
|
|
27
|
+
output: unknown;
|
|
28
|
+
} | {
|
|
29
|
+
ok: false;
|
|
30
|
+
error: unknown;
|
|
31
|
+
}) => void | Promise<void>): CallInterceptor;
|
|
32
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAoB5D,wBAAgB,OAAO,CACrB,EAAE,EAAE,CAAC,GAAG,EAAE;IACR,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACzB,eAAe,CAKjB;AAED,wBAAgB,SAAS,CACvB,EAAE,EAAE,CACF,GAAG,EAAE;IACH,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,EACD,MAAM,EAAE,OAAO,KACZ,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACxB,eAAe,CAMjB;AAED,wBAAgB,OAAO,CACrB,EAAE,EAAE,CACF,GAAG,EAAE;IACH,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,EACD,KAAK,EAAE,OAAO,KACX,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACxB,eAAe,CAajB;AAED,wBAAgB,QAAQ,CACtB,EAAE,EAAE,CACF,GAAG,EAAE;IACH,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,EACD,MAAM,EAAE;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,KAClE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACxB,eAAe,CAgBjB"}
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
function eventCtx(ctx) {
|
|
2
|
+
return ctx.signal
|
|
3
|
+
? {
|
|
4
|
+
path: ctx.path,
|
|
5
|
+
input: ctx.input,
|
|
6
|
+
context: ctx.context,
|
|
7
|
+
signal: ctx.signal,
|
|
8
|
+
}
|
|
9
|
+
: { path: ctx.path, input: ctx.input, context: ctx.context };
|
|
10
|
+
}
|
|
11
|
+
export function onStart(fn) {
|
|
12
|
+
return async (ctx) => {
|
|
13
|
+
await fn(eventCtx(ctx));
|
|
14
|
+
return ctx.next();
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function onSuccess(fn) {
|
|
18
|
+
return async (ctx) => {
|
|
19
|
+
const output = await ctx.next();
|
|
20
|
+
await fn(eventCtx(ctx), output);
|
|
21
|
+
return output;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function onError(fn) {
|
|
25
|
+
return async (ctx) => {
|
|
26
|
+
try {
|
|
27
|
+
return await ctx.next();
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
try {
|
|
31
|
+
await fn(eventCtx(ctx), error);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Observe-only: always rethrow the original call error.
|
|
35
|
+
}
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function onFinish(fn) {
|
|
41
|
+
return async (ctx) => {
|
|
42
|
+
let output;
|
|
43
|
+
try {
|
|
44
|
+
output = await ctx.next();
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
try {
|
|
48
|
+
await fn(eventCtx(ctx), { ok: false, error });
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Always rethrow the original call error.
|
|
52
|
+
}
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
await fn(eventCtx(ctx), { ok: true, output });
|
|
56
|
+
return output;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AASA,SAAS,QAAQ,CAAC,GAAa;IAC7B,OAAO,GAAG,CAAC,MAAM;QACf,CAAC,CAAC;YACE,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,MAAM,EAAE,GAAG,CAAC,MAAM;SACnB;QACH,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAA;AAChE,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,EAK0B;IAE1B,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAA;QACvB,OAAO,GAAG,CAAC,IAAI,EAAE,CAAA;IACnB,CAAC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,EAQyB;IAEzB,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;QAC/B,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAA;QAC/B,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,EAQyB;IAEzB,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,IAAI,CAAC;YACH,OAAO,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;QACzB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAA;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,wDAAwD;YAC1D,CAAC;YACD,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CACtB,EAQyB;IAEzB,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,IAAI,MAAe,CAAA;QACnB,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;QAC3B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;YAC/C,CAAC;YAAC,MAAM,CAAC;gBACP,0CAA0C;YAC5C,CAAC;YACD,MAAM,KAAK,CAAA;QACb,CAAC;QACD,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAA;QAC7C,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type ContractBuilder, type ContractProcedure } from '@ts-pf/contract';
|
|
2
|
+
import type { MiddlewareFn } from './middleware.js';
|
|
3
|
+
import { type ImplementedProcedure, type ImplementedRouter } from './runtime.js';
|
|
4
|
+
export declare function createImplementer<T>(contract: T): Implementer<T, object>;
|
|
5
|
+
export type ProcedureBuilder<TInput, TOutput, TCtx, TErrors extends Record<string, unknown> = Record<string, unknown>> = {
|
|
6
|
+
use: (mw: MiddlewareFn<TCtx, unknown, TOutput>) => ProcedureBuilder<TInput, TOutput, TCtx, TErrors>;
|
|
7
|
+
useAfter: (mw: MiddlewareFn<TCtx, TInput, TOutput>) => ProcedureBuilder<TInput, TOutput, TCtx, TErrors>;
|
|
8
|
+
handler: (fn: (opts: {
|
|
9
|
+
input: TInput;
|
|
10
|
+
context: TCtx;
|
|
11
|
+
errors: import('./middleware.js').ErrorFactory<TErrors>;
|
|
12
|
+
path: string[];
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
}) => TOutput | Promise<TOutput>) => ImplementedProcedure;
|
|
15
|
+
};
|
|
16
|
+
export type Implementer<T, TCtx> = {
|
|
17
|
+
$context: <C>() => Implementer<T, C>;
|
|
18
|
+
use: (mw: MiddlewareFn<TCtx, unknown, unknown>) => Implementer<T, TCtx>;
|
|
19
|
+
useAfter: (mw: MiddlewareFn<TCtx, unknown, unknown>) => Implementer<T, TCtx>;
|
|
20
|
+
middleware: (fn: MiddlewareFn<TCtx, unknown, unknown>) => MiddlewareFn<TCtx, unknown, unknown>;
|
|
21
|
+
router: (impl: RouterImpl<T, TCtx>) => ImplementedRouter<T>;
|
|
22
|
+
} & {
|
|
23
|
+
[K in keyof T as K extends '~pf' ? never : K]: T[K] extends ContractBuilder<infer I, infer O, infer _E, infer _M> ? ProcedureBuilder<I, O, TCtx, _E> : T[K] extends ContractProcedure<infer I, infer O, infer _E, infer _M> ? ProcedureBuilder<I, O, TCtx, _E> : Implementer<T[K], TCtx>;
|
|
24
|
+
};
|
|
25
|
+
export type RouterImpl<T, TCtx> = {
|
|
26
|
+
[K in keyof T as K extends '~pf' ? never : K]: T[K] extends ContractBuilder<infer _I, infer _O, infer _E, infer _M> ? ImplementedProcedure : T[K] extends ContractProcedure<infer _I, infer _O, infer _E, infer _M> ? ImplementedProcedure : RouterImpl<T[K], TCtx>;
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=implement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"implement.d.ts","sourceRoot":"","sources":["../src/implement.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EAEvB,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EAEvB,MAAM,cAAc,CAAA;AAgKrB,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GAKxC,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,CAC7B;AAED,MAAM,MAAM,gBAAgB,CAC1B,MAAM,EACN,OAAO,EACP,IAAI,EACJ,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAC/D;IACF,GAAG,EAAE,CACH,EAAE,EAAE,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,KACrC,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;IACrD,QAAQ,EAAE,CACR,EAAE,EAAE,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,KACpC,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;IACrD,OAAO,EAAE,CACP,EAAE,EAAE,CAAC,IAAI,EAAE;QACT,KAAK,EAAE,MAAM,CAAA;QACb,OAAO,EAAE,IAAI,CAAA;QACb,MAAM,EAAE,OAAO,iBAAiB,EAAE,YAAY,CAAC,OAAO,CAAC,CAAA;QACvD,IAAI,EAAE,MAAM,EAAE,CAAA;QACd,MAAM,CAAC,EAAE,WAAW,CAAA;KACrB,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAC7B,oBAAoB,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,EAAE,IAAI,IAAI;IACjC,QAAQ,EAAE,CAAC,CAAC,OAAO,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACpC,GAAG,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;IACvE,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;IAC5E,UAAU,EAAE,CACV,EAAE,EAAE,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,KACrC,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACzC,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,iBAAiB,CAAC,CAAC,CAAC,CAAA;CAC5D,GAAG;KACD,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,KAAK,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,eAAe,CACzE,MAAM,CAAC,EACP,MAAM,CAAC,EACP,MAAM,EAAE,EACR,MAAM,EAAE,CACT,GACG,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,GAChC,CAAC,CAAC,CAAC,CAAC,SAAS,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,GAClE,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,GAChC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;CAC9B,CAAA;AAED,MAAM,MAAM,UAAU,CAAC,CAAC,EAAE,IAAI,IAAI;KAC/B,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,KAAK,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,eAAe,CACzE,MAAM,EAAE,EACR,MAAM,EAAE,EACR,MAAM,EAAE,EACR,MAAM,EAAE,CACT,GACG,oBAAoB,GACpB,CAAC,CAAC,CAAC,CAAC,SAAS,iBAAiB,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,GACpE,oBAAoB,GACpB,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;CAC7B,CAAA"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { isContractProcedure, } from '@ts-pf/contract';
|
|
2
|
+
import { isImplementedProcedure, } from './runtime.js';
|
|
3
|
+
function createImplemented(contract, inherited, extraUse, extraUseAfter, handler) {
|
|
4
|
+
return {
|
|
5
|
+
'~pf': {
|
|
6
|
+
type: 'implemented-procedure',
|
|
7
|
+
contract,
|
|
8
|
+
use: [...inherited.use, ...extraUse],
|
|
9
|
+
useAfter: [...inherited.useAfter, ...extraUseAfter],
|
|
10
|
+
handler,
|
|
11
|
+
path: inherited.path,
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function applyInherited(node, inherited) {
|
|
16
|
+
if (isImplementedProcedure(node)) {
|
|
17
|
+
return {
|
|
18
|
+
'~pf': {
|
|
19
|
+
...node['~pf'],
|
|
20
|
+
use: [...inherited.use, ...node['~pf'].use],
|
|
21
|
+
useAfter: [...inherited.useAfter, ...node['~pf'].useAfter],
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const result = {};
|
|
26
|
+
for (const [key, child] of Object.entries(node)) {
|
|
27
|
+
if (key === '~pf') {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (child) {
|
|
31
|
+
result[key] = applyInherited(child, inherited);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
function assertComplete(contract, impl, path) {
|
|
37
|
+
if (isContractProcedure(contract)) {
|
|
38
|
+
if (!isImplementedProcedure(impl)) {
|
|
39
|
+
throw new Error(`Missing implementation at "${path.join('.') || '<root>'}"`);
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (typeof contract !== 'object' || contract === null) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (typeof impl !== 'object' || impl === null) {
|
|
47
|
+
throw new Error(`Missing implementation at "${path.join('.') || '<root>'}"`);
|
|
48
|
+
}
|
|
49
|
+
const contractKeys = Object.keys(contract).filter((key) => key !== '~pf');
|
|
50
|
+
const implKeys = Object.keys(impl).filter((key) => key !== '~pf');
|
|
51
|
+
for (const key of contractKeys) {
|
|
52
|
+
if (!(key in impl)) {
|
|
53
|
+
throw new Error(`Missing implementation at "${[...path, key].join('.')}"`);
|
|
54
|
+
}
|
|
55
|
+
assertComplete(contract[key], impl[key], [...path, key]);
|
|
56
|
+
}
|
|
57
|
+
for (const key of implKeys) {
|
|
58
|
+
if (!(key in contract)) {
|
|
59
|
+
throw new Error(`Unexpected implementation key "${[...path, key].join('.')}"`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function createNode(contract, inherited) {
|
|
64
|
+
const extraUse = [];
|
|
65
|
+
const extraUseAfter = [];
|
|
66
|
+
const self = {
|
|
67
|
+
$context() {
|
|
68
|
+
return createNode(contract, inherited);
|
|
69
|
+
},
|
|
70
|
+
use(mw) {
|
|
71
|
+
return createNode(contract, { ...inherited, use: [...inherited.use, mw] });
|
|
72
|
+
},
|
|
73
|
+
useAfter(mw) {
|
|
74
|
+
return createNode(contract, {
|
|
75
|
+
...inherited,
|
|
76
|
+
useAfter: [...inherited.useAfter, mw],
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
middleware(fn) {
|
|
80
|
+
return fn;
|
|
81
|
+
},
|
|
82
|
+
router(impl) {
|
|
83
|
+
assertComplete(contract, impl, inherited.path);
|
|
84
|
+
return applyInherited(impl, inherited);
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
if (isContractProcedure(contract)) {
|
|
88
|
+
self.handler = (fn) => createImplemented(contract, inherited, extraUse, extraUseAfter, fn);
|
|
89
|
+
self.use = (mw) => {
|
|
90
|
+
extraUse.push(mw);
|
|
91
|
+
return new Proxy(self, procedureProxy);
|
|
92
|
+
};
|
|
93
|
+
self.useAfter = (mw) => {
|
|
94
|
+
extraUseAfter.push(mw);
|
|
95
|
+
return new Proxy(self, procedureProxy);
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const procedureProxy = {
|
|
99
|
+
get(target, key) {
|
|
100
|
+
if (key in target) {
|
|
101
|
+
return target[key];
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
return new Proxy(self, {
|
|
107
|
+
get(target, key) {
|
|
108
|
+
if (key in target) {
|
|
109
|
+
return target[key];
|
|
110
|
+
}
|
|
111
|
+
if (typeof key === 'symbol' || key === '~pf') {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
if (typeof contract !== 'object' || contract === null) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
const child = contract[key];
|
|
118
|
+
if (child === undefined) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
return createNode(child, { ...inherited, path: [...inherited.path, key] });
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
export function createImplementer(contract) {
|
|
126
|
+
return createNode(contract, {
|
|
127
|
+
use: [],
|
|
128
|
+
useAfter: [],
|
|
129
|
+
path: [],
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=implement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"implement.js","sourceRoot":"","sources":["../src/implement.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type { CallInterceptor } from './call-interceptor.js';
|
|
2
|
+
export { createLocalClient } from './caller.js';
|
|
3
|
+
export { DedupePlugin, type DedupePluginOptions } from './dedupe-plugin.js';
|
|
4
|
+
export { onError, onFinish, onStart, onSuccess } from './events.js';
|
|
5
|
+
export { createImplementer, type Implementer, type ProcedureBuilder, type RouterImpl, } from './implement.js';
|
|
6
|
+
export type { ErrorFactory, MiddlewareFn, NextFn } from './middleware.js';
|
|
7
|
+
export { applyPlugins, type CallPlugin } from './plugin.js';
|
|
8
|
+
export { type ImplementedProcedure, type ImplementedRouter, isImplementedProcedure, lookupProcedure, type RunProcedureOptions, runProcedure, } from './runtime.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAC3E,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACnE,OAAO,EACL,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAA;AACvB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACzE,OAAO,EAAE,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAC3D,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,sBAAsB,EACtB,eAAe,EACf,KAAK,mBAAmB,EACxB,YAAY,GACb,MAAM,cAAc,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createLocalClient } from './caller.js';
|
|
2
|
+
export { DedupePlugin } from './dedupe-plugin.js';
|
|
3
|
+
export { onError, onFinish, onStart, onSuccess } from './events.js';
|
|
4
|
+
export { createImplementer, } from './implement.js';
|
|
5
|
+
export { applyPlugins } from './plugin.js';
|
|
6
|
+
export { isImplementedProcedure, lookupProcedure, runProcedure, } from './runtime.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,YAAY,EAA4B,MAAM,oBAAoB,CAAA;AAC3E,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACnE,OAAO,EACL,iBAAiB,GAIlB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAE,YAAY,EAAmB,MAAM,aAAa,CAAA;AAC3D,OAAO,EAGL,sBAAsB,EACtB,eAAe,EAEf,YAAY,GACb,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ErrorDef, InferErrorData } from '@ts-pf/contract';
|
|
2
|
+
export type NextFn<TCtx, TInput, TOutput> = (opts?: {
|
|
3
|
+
context?: Partial<TCtx>;
|
|
4
|
+
input?: TInput;
|
|
5
|
+
}) => Promise<TOutput>;
|
|
6
|
+
export type MiddlewareFn<TCtx = unknown, TInput = unknown, TOutput = unknown> = (opts: {
|
|
7
|
+
context: TCtx;
|
|
8
|
+
input: TInput;
|
|
9
|
+
next: NextFn<TCtx, TInput, TOutput>;
|
|
10
|
+
errors: ErrorFactory;
|
|
11
|
+
path: string[];
|
|
12
|
+
}) => Promise<TOutput>;
|
|
13
|
+
export type ErrorFactory<E extends Record<string, unknown> = Record<string, unknown>> = string extends keyof E ? {
|
|
14
|
+
[K in keyof E]: (data?: unknown) => never;
|
|
15
|
+
} : {
|
|
16
|
+
[K in keyof E]: [InferErrorData<Extract<E[K], ErrorDef>>] extends [never] ? () => never : (data: InferErrorData<Extract<E[K], ErrorDef>>) => never;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAE/D,MAAM,MAAM,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE;IAClD,OAAO,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAEtB,MAAM,MAAM,YAAY,CACtB,IAAI,GAAG,OAAO,EACd,MAAM,GAAG,OAAO,EAChB,OAAO,GAAG,OAAO,IACf,CAAC,IAAI,EAAE;IACT,OAAO,EAAE,IAAI,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;IACnC,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,MAAM,EAAE,CAAA;CACf,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAEtB,MAAM,MAAM,YAAY,CACtB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACzD,MAAM,SAAS,MAAM,CAAC,GACtB;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,KAAK,KAAK;CAAE,GAC7C;KACG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACrE,MAAM,KAAK,GACX,CAAC,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK;CAC7D,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":""}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CallInterceptor } from './call-interceptor.js';
|
|
2
|
+
export type CallPlugin = {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly intercept: CallInterceptor;
|
|
5
|
+
};
|
|
6
|
+
export declare function applyPlugins(plugins: readonly CallPlugin[], interceptors?: readonly CallInterceptor[]): CallInterceptor[];
|
|
7
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAE5D,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAA;CACpC,CAAA;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,SAAS,UAAU,EAAE,EAC9B,YAAY,CAAC,EAAE,SAAS,eAAe,EAAE,GACxC,eAAe,EAAE,CAEnB"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,YAAY,CAC1B,OAA8B,EAC9B,YAAyC;IAEzC,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,CAAA;AAChF,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type ContractProcedure } from '@ts-pf/contract';
|
|
2
|
+
import { type CallInterceptor } from './call-interceptor.js';
|
|
3
|
+
import { createErrorFactory } from './error-factory.js';
|
|
4
|
+
import type { MiddlewareFn } from './middleware.js';
|
|
5
|
+
export type HandlerFn = (opts: {
|
|
6
|
+
input: unknown;
|
|
7
|
+
context: unknown;
|
|
8
|
+
errors: ReturnType<typeof createErrorFactory>;
|
|
9
|
+
path: string[];
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
}) => unknown | Promise<unknown>;
|
|
12
|
+
export type ImplementedProcedure = {
|
|
13
|
+
readonly '~pf': {
|
|
14
|
+
type: 'implemented-procedure';
|
|
15
|
+
contract: ContractProcedure;
|
|
16
|
+
use: MiddlewareFn[];
|
|
17
|
+
useAfter: MiddlewareFn[];
|
|
18
|
+
handler: HandlerFn;
|
|
19
|
+
path: string[];
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
declare const contractBrand: unique symbol;
|
|
23
|
+
export type ImplementedRouter<T = unknown> = {
|
|
24
|
+
readonly [key: string]: ImplementedProcedure | ImplementedRouter | undefined;
|
|
25
|
+
} & {
|
|
26
|
+
readonly [contractBrand]?: T;
|
|
27
|
+
};
|
|
28
|
+
export declare function isImplementedProcedure(value: unknown): value is ImplementedProcedure;
|
|
29
|
+
export type RunProcedureOptions = {
|
|
30
|
+
signal?: AbortSignal;
|
|
31
|
+
interceptors?: readonly CallInterceptor[];
|
|
32
|
+
};
|
|
33
|
+
export declare function runProcedure(proc: ImplementedProcedure, rawInput: unknown, context: unknown, options?: RunProcedureOptions): Promise<unknown>;
|
|
34
|
+
export declare function lookupProcedure(router: ImplementedRouter, path: string[]): ImplementedProcedure | undefined;
|
|
35
|
+
export declare function assertContractProcedure(value: unknown): asserts value is ContractProcedure;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,iBAAiB,EAIvB,MAAM,iBAAiB,CAAA;AAExB,OAAO,EACL,KAAK,eAAe,EAErB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,kBAAkB,EAAyB,MAAM,oBAAoB,CAAA;AAC9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEnD,MAAM,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE;IAC7B,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAA;IAC7C,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;AAEhC,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,KAAK,EAAE;QACd,IAAI,EAAE,uBAAuB,CAAA;QAC7B,QAAQ,EAAE,iBAAiB,CAAA;QAC3B,GAAG,EAAE,YAAY,EAAE,CAAA;QACnB,QAAQ,EAAE,YAAY,EAAE,CAAA;QACxB,OAAO,EAAE,SAAS,CAAA;QAClB,IAAI,EAAE,MAAM,EAAE,CAAA;KACf,CAAA;CACF,CAAA;AAED,OAAO,CAAC,MAAM,aAAa,EAAE,OAAO,MAAM,CAAA;AAE1C,MAAM,MAAM,iBAAiB,CAAC,CAAC,GAAG,OAAO,IAAI;IAC3C,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,oBAAoB,GAAG,iBAAiB,GAAG,SAAS,CAAA;CAC7E,GAAG;IAAE,QAAQ,CAAC,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAA;CAAE,CAAA;AAEpC,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,oBAAoB,CAO/B;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,YAAY,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;CAC1C,CAAA;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,oBAAoB,EAC1B,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAiHlB;AAwCD,wBAAgB,eAAe,CAC7B,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,MAAM,EAAE,GACb,oBAAoB,GAAG,SAAS,CASlC;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,KAAK,IAAI,iBAAiB,CAIpC"}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { isContractProcedure, validateSchema, } from '@ts-pf/contract';
|
|
2
|
+
import { PFError } from '@ts-pf/protocol';
|
|
3
|
+
import { runCallInterceptors, } from './call-interceptor.js';
|
|
4
|
+
import { createErrorFactory, finalizeDeclaredError } from './error-factory.js';
|
|
5
|
+
export function isImplementedProcedure(value) {
|
|
6
|
+
return (typeof value === 'object' &&
|
|
7
|
+
value !== null &&
|
|
8
|
+
'~pf' in value &&
|
|
9
|
+
value['~pf']?.type === 'implemented-procedure');
|
|
10
|
+
}
|
|
11
|
+
export async function runProcedure(proc, rawInput, context, options) {
|
|
12
|
+
const def = proc['~pf'];
|
|
13
|
+
const errors = createErrorFactory(def.contract['~pf'].errors);
|
|
14
|
+
let ctx = { ...context };
|
|
15
|
+
let input = rawInput;
|
|
16
|
+
let signal = options?.signal;
|
|
17
|
+
const runUseAfter = async (index) => {
|
|
18
|
+
const mw = def.useAfter[index];
|
|
19
|
+
if (!mw) {
|
|
20
|
+
const output = await def.handler({
|
|
21
|
+
input,
|
|
22
|
+
context: ctx,
|
|
23
|
+
errors,
|
|
24
|
+
path: def.path,
|
|
25
|
+
...(signal ? { signal } : {}),
|
|
26
|
+
});
|
|
27
|
+
const outputSchema = def.contract['~pf'].output;
|
|
28
|
+
if (outputSchema) {
|
|
29
|
+
const result = await validateSchema(outputSchema, output);
|
|
30
|
+
if (!result.success) {
|
|
31
|
+
throw new PFError({
|
|
32
|
+
code: 'INTERNAL',
|
|
33
|
+
message: 'Internal server error',
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return result.value;
|
|
37
|
+
}
|
|
38
|
+
return output;
|
|
39
|
+
}
|
|
40
|
+
return mw({
|
|
41
|
+
context: ctx,
|
|
42
|
+
input,
|
|
43
|
+
errors,
|
|
44
|
+
path: def.path,
|
|
45
|
+
next: async (opts) => {
|
|
46
|
+
if (opts?.context) {
|
|
47
|
+
ctx = { ...ctx, ...opts.context };
|
|
48
|
+
}
|
|
49
|
+
if (opts && 'input' in opts) {
|
|
50
|
+
input = opts.input;
|
|
51
|
+
}
|
|
52
|
+
return runUseAfter(index + 1);
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const runUse = async (index) => {
|
|
57
|
+
const mw = def.use[index];
|
|
58
|
+
if (!mw) {
|
|
59
|
+
const inputSchema = def.contract['~pf'].input;
|
|
60
|
+
if (inputSchema) {
|
|
61
|
+
const result = await validateSchema(inputSchema, input);
|
|
62
|
+
if (!result.success) {
|
|
63
|
+
throw new PFError({
|
|
64
|
+
code: 'VALIDATION',
|
|
65
|
+
message: 'Validation failed',
|
|
66
|
+
data: { issues: result.issues },
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
input = result.value;
|
|
70
|
+
}
|
|
71
|
+
return runUseAfter(0);
|
|
72
|
+
}
|
|
73
|
+
return mw({
|
|
74
|
+
context: ctx,
|
|
75
|
+
input,
|
|
76
|
+
errors,
|
|
77
|
+
path: def.path,
|
|
78
|
+
next: async (opts) => {
|
|
79
|
+
if (opts?.context) {
|
|
80
|
+
ctx = { ...ctx, ...opts.context };
|
|
81
|
+
}
|
|
82
|
+
if (opts && 'input' in opts) {
|
|
83
|
+
input = opts.input;
|
|
84
|
+
}
|
|
85
|
+
return runUse(index + 1);
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
const map = def.contract['~pf'].errors;
|
|
90
|
+
const interceptors = options?.interceptors;
|
|
91
|
+
if (!interceptors?.length) {
|
|
92
|
+
try {
|
|
93
|
+
const output = await runUse(0);
|
|
94
|
+
return wrapIfAsyncIterable(output, map);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
return await finalizeDeclaredError(error, map);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return runCallInterceptors(interceptors, {
|
|
101
|
+
procedure: proc,
|
|
102
|
+
path: def.path,
|
|
103
|
+
input: rawInput,
|
|
104
|
+
context,
|
|
105
|
+
...(signal ? { signal } : {}),
|
|
106
|
+
}, async (current) => {
|
|
107
|
+
input = current.input;
|
|
108
|
+
ctx = { ...current.context };
|
|
109
|
+
signal = current.signal;
|
|
110
|
+
try {
|
|
111
|
+
const output = await runUse(0);
|
|
112
|
+
return wrapIfAsyncIterable(output, map);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
return await finalizeDeclaredError(error, map);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function isAsyncIterable(value) {
|
|
120
|
+
return (typeof value === 'object' && value !== null && Symbol.asyncIterator in value);
|
|
121
|
+
}
|
|
122
|
+
function wrapIfAsyncIterable(value, map) {
|
|
123
|
+
if (!isAsyncIterable(value)) {
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
return wrapAsyncIterable(value, map);
|
|
127
|
+
}
|
|
128
|
+
function wrapAsyncIterable(value, map) {
|
|
129
|
+
return {
|
|
130
|
+
[Symbol.asyncIterator]() {
|
|
131
|
+
const it = value[Symbol.asyncIterator]();
|
|
132
|
+
return {
|
|
133
|
+
async next() {
|
|
134
|
+
try {
|
|
135
|
+
return await it.next();
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
return await finalizeDeclaredError(error, map);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
async return() {
|
|
142
|
+
return it.return
|
|
143
|
+
? it.return()
|
|
144
|
+
: { done: true, value: undefined };
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
export function lookupProcedure(router, path) {
|
|
151
|
+
let current = router;
|
|
152
|
+
for (const segment of path) {
|
|
153
|
+
if (!current || isImplementedProcedure(current)) {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
current = current[segment];
|
|
157
|
+
}
|
|
158
|
+
return current && isImplementedProcedure(current) ? current : undefined;
|
|
159
|
+
}
|
|
160
|
+
export function assertContractProcedure(value) {
|
|
161
|
+
if (!isContractProcedure(value)) {
|
|
162
|
+
throw new Error('Expected a contract procedure');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ts-pf/server",
|
|
3
|
+
"version": "0.1.0-beta.0",
|
|
4
|
+
"description": "Implementer, middleware, runProcedure, and in-process local client",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/crobinson42/ts-pf.git",
|
|
9
|
+
"directory": "packages/server"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/crobinson42/ts-pf/tree/main/packages/server#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/crobinson42/ts-pf/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./src/index.ts",
|
|
20
|
+
"import": "./src/index.ts"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"skills"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc -p tsconfig.build.json",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"type-check": "tsc --noEmit",
|
|
40
|
+
"lint": "biome check src tests"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@ts-pf/contract": "0.1.0-beta.0",
|
|
44
|
+
"@ts-pf/protocol": "0.1.0-beta.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"zod": "^4.0.0"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ts-pf-server
|
|
3
|
+
description: Use when implementing a ts-pf contract with createImplementer, middleware, runProcedure, createLocalClient, or server DedupePlugin / CallInterceptor. Triggers: @ts-pf/server, createImplementer, .use(), .useAfter(), createLocalClient, applyPlugins.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @ts-pf/server
|
|
7
|
+
|
|
8
|
+
Implementer, middleware, `runProcedure`. No `Request` / `Response`.
|
|
9
|
+
|
|
10
|
+
Install: `npm i @ts-pf/server@beta` (needs `@ts-pf/contract` + `@ts-pf/protocol`)
|
|
11
|
+
|
|
12
|
+
Link for agents: `npx skills experimental_sync -y`
|
|
13
|
+
|
|
14
|
+
## Do
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { createImplementer, DedupePlugin } from '@ts-pf/server'
|
|
18
|
+
import { PFError } from '@ts-pf/protocol'
|
|
19
|
+
|
|
20
|
+
const impl = createImplementer(contract).$context<{ db: Db }>()
|
|
21
|
+
|
|
22
|
+
const requireUser = impl.middleware(async ({ context, next }) => {
|
|
23
|
+
const user = await auth(context)
|
|
24
|
+
if (!user) throw new PFError({ code: 'UNAUTHORIZED', status: 401 })
|
|
25
|
+
return next({ context: { user } })
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
export const app = impl.use(requireUser).router({
|
|
29
|
+
planet: {
|
|
30
|
+
find: impl.planet.find.handler(async ({ input, context, errors }) => {
|
|
31
|
+
const row = await context.db.planets.get(input.id)
|
|
32
|
+
if (!row) throw errors.NOT_FOUND({ id: input.id })
|
|
33
|
+
return row
|
|
34
|
+
}),
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`.use()` runs before input validation (`input: unknown`). `.useAfter()` runs after (typed input). `$context<C>()` is the context type source; middleware runtime-merges and does not infer added keys. Pass HTTP `Request` in `FetchHandler.handle(..., { context })`, not via `$context` alone. Name the implemented router `app`. Default `DedupePlugin` keys every unary call — pass `key` to restrict to reads.
|
|
40
|
+
|
|
41
|
+
## API
|
|
42
|
+
|
|
43
|
+
- `createImplementer`, `createLocalClient`, `runProcedure`, `lookupProcedure`
|
|
44
|
+
- `applyPlugins`, `CallPlugin`, `CallInterceptor`, `DedupePlugin`
|
|
45
|
+
- `onStart` / `onSuccess` / `onError` / `onFinish`
|
|
46
|
+
|
|
47
|
+
## Pair with
|
|
48
|
+
|
|
49
|
+
- HTTP: `ts-pf-server-http` (`FetchHandler`)
|
|
50
|
+
- Message: `ts-pf-message-server`
|
|
51
|
+
- In-process: `createLocalClient(app, { context, plugins, interceptors })`
|
|
52
|
+
|
|
53
|
+
## Don't
|
|
54
|
+
|
|
55
|
+
- `FetchHandler` or HTTP plugins here.
|
|
56
|
+
- Import `CallInterceptor` from `@ts-pf/client` (duplicate types).
|
|
57
|
+
- Attach interceptors on `createImplementer` — they attach per caller (`createLocalClient` / handler `{ interceptors }`).
|
|
58
|
+
- Default `DedupePlugin` on non-idempotent writes.
|