@telorun/sdk 0.31.0 → 0.33.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/dist/cancellation.d.ts +33 -0
- package/dist/cancellation.d.ts.map +1 -1
- package/dist/dispatch-invoke-ref.d.ts +17 -0
- package/dist/dispatch-invoke-ref.d.ts.map +1 -0
- package/dist/dispatch-invoke-ref.js +27 -0
- package/dist/duration.d.ts +8 -0
- package/dist/duration.d.ts.map +1 -0
- package/dist/duration.js +33 -0
- package/dist/evaluation-context.d.ts +14 -2
- package/dist/evaluation-context.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/invoke-step.d.ts +1 -1
- package/dist/invoke-step.d.ts.map +1 -1
- package/dist/invoke-step.js +9 -1
- package/dist/resource-context.d.ts +15 -1
- package/dist/resource-context.d.ts.map +1 -1
- package/dist/resource-instance.d.ts +18 -0
- package/dist/resource-instance.d.ts.map +1 -1
- package/dist/resource-instance.js +24 -1
- package/package.json +1 -1
- package/src/cancellation.ts +30 -0
- package/src/dispatch-invoke-ref.ts +48 -0
- package/src/duration.ts +37 -0
- package/src/evaluation-context.ts +14 -1
- package/src/index.ts +2 -0
- package/src/invoke-step.ts +9 -2
- package/src/resource-context.ts +15 -1
- package/src/resource-instance.ts +33 -0
package/dist/cancellation.d.ts
CHANGED
|
@@ -43,6 +43,39 @@ export interface InvokeContext {
|
|
|
43
43
|
/** The {@link invocationId} of the invocation that dispatched this one, or
|
|
44
44
|
* `undefined` at a trace root. Reconstructs the call tree on the consumer. */
|
|
45
45
|
readonly parentInvocationId?: number;
|
|
46
|
+
/** The trace this invocation belongs to — minted at the root span and inherited
|
|
47
|
+
* by every descendant. Present only while tracing. Groups all spans of one
|
|
48
|
+
* trace the way OpenTelemetry's `trace_id` does (an exporter maps it directly),
|
|
49
|
+
* independent of the parent chain. */
|
|
50
|
+
readonly traceId?: string;
|
|
51
|
+
}
|
|
52
|
+
/** Terminal status of a span — maps to OpenTelemetry span status. */
|
|
53
|
+
export type SpanOutcome = "ok" | "failed" | "rejected" | "cancelled";
|
|
54
|
+
/** Options for {@link ResourceContext.openSpan}. */
|
|
55
|
+
export interface OpenSpanOptions {
|
|
56
|
+
/** The resource the span is attributed to (an inbound transport's listener,
|
|
57
|
+
* e.g. the `Http.Api`). Becomes the span's `ref`. */
|
|
58
|
+
ref: {
|
|
59
|
+
kind: string;
|
|
60
|
+
name: string;
|
|
61
|
+
};
|
|
62
|
+
/** Human label for the span (e.g. `"POST /feedback"`). */
|
|
63
|
+
label?: string;
|
|
64
|
+
/** Structured attributes (e.g. `{ method, path }`). Map to OTel span attributes. */
|
|
65
|
+
attributes?: Record<string, unknown>;
|
|
66
|
+
/** Continue an upstream distributed trace instead of rooting a new one — e.g.
|
|
67
|
+
* seeded from a W3C `traceparent` header. */
|
|
68
|
+
inbound?: {
|
|
69
|
+
traceId: string;
|
|
70
|
+
parentSpanId?: number;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** Handle returned by {@link ResourceContext.openSpan}. */
|
|
74
|
+
export interface OpenSpan {
|
|
75
|
+
/** Thread into `invokeResolved` so the handler nests under the span. */
|
|
76
|
+
readonly context: InvokeContext;
|
|
77
|
+
/** Close the span with an outcome (emits the span's `end` event). */
|
|
78
|
+
settle(outcome: SpanOutcome, detail?: Record<string, unknown>): Promise<void>;
|
|
46
79
|
}
|
|
47
80
|
export interface CancellationSource {
|
|
48
81
|
readonly token: CancellationToken;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cancellation.d.ts","sourceRoot":"","sources":["../src/cancellation.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC;yDACqD;IACrD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B;2CACuC;IACvC,WAAW,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACxE,kEAAkE;IAClE,gBAAgB,IAAI,IAAI,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAAC;IACzC;;4CAEwC;IACxC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;mFAC+E;IAC/E,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"cancellation.d.ts","sourceRoot":"","sources":["../src/cancellation.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC;yDACqD;IACrD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B;2CACuC;IACvC,WAAW,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACxE,kEAAkE;IAClE,gBAAgB,IAAI,IAAI,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAAC;IACzC;;4CAEwC;IACxC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;mFAC+E;IAC/E,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC;;;2CAGuC;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qEAAqE;AACrE,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC;AAErE,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B;0DACsD;IACtD,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC;kDAC8C;IAC9C,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACtD;AAED,2DAA2D;AAC3D,MAAM,WAAW,QAAQ;IACvB,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,qEAAqE;IACrE,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAClC;4EACwE;IACxE,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,uCAAuC;IACvC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;mEAG+D;IAC/D,OAAO,IAAI,IAAI,CAAC;CACjB;AAiFD,wBAAgB,wBAAwB,IAAI,kBAAkB,CAE7D;AAMD;2EAC2E;AAC3E,eAAO,MAAM,eAAe,EAAE,iBAU7B,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,qBAAqB,EAAE,aAAiD,CAAC;AAEtF,oFAAoF;AACpF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAMzD"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ModuleContext } from "./module-context.js";
|
|
2
|
+
import { type ResourceInstance } from "./resource-instance.js";
|
|
3
|
+
/** The context a decorator kind composes to dispatch its wrapped target. */
|
|
4
|
+
export interface DispatchContext {
|
|
5
|
+
invokeResolved<TInputs>(kind: string, name: string, instance: ResourceInstance, inputs: TInputs): Promise<unknown>;
|
|
6
|
+
readonly moduleContext: ModuleContext;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a decorator's `invoke:` field to a live invocable and return a thunk
|
|
10
|
+
* that dispatches it through the traced chokepoint. The field is either a
|
|
11
|
+
* Phase-5-injected instance or a raw `{ kind, name, alias }` ref resolved
|
|
12
|
+
* against the module context. Resolution is eager (fail-fast on a bad ref);
|
|
13
|
+
* dispatch is deferred, so a caller can run it synchronously (Cache.View) or
|
|
14
|
+
* detached (Run.Detach). `describe` labels the error with the owning resource.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveInvocableDispatcher(field: unknown, ctx: DispatchContext, describe: () => string): (inputs: Record<string, unknown>) => Promise<unknown>;
|
|
17
|
+
//# sourceMappingURL=dispatch-invoke-ref.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch-invoke-ref.d.ts","sourceRoot":"","sources":["../src/dispatch-invoke-ref.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/E,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,OAAO,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;CACvC;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,MAAM,MAAM,GACrB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAoBvD"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getRefIdentity } from "./resource-instance.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve a decorator's `invoke:` field to a live invocable and return a thunk
|
|
4
|
+
* that dispatches it through the traced chokepoint. The field is either a
|
|
5
|
+
* Phase-5-injected instance or a raw `{ kind, name, alias }` ref resolved
|
|
6
|
+
* against the module context. Resolution is eager (fail-fast on a bad ref);
|
|
7
|
+
* dispatch is deferred, so a caller can run it synchronously (Cache.View) or
|
|
8
|
+
* detached (Run.Detach). `describe` labels the error with the owning resource.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveInvocableDispatcher(field, ctx, describe) {
|
|
11
|
+
if (field && typeof field.invoke === "function") {
|
|
12
|
+
const instance = field;
|
|
13
|
+
const id = getRefIdentity(field);
|
|
14
|
+
return (inputs) => id ? ctx.invokeResolved(id.kind, id.name, instance, inputs) : instance.invoke(inputs);
|
|
15
|
+
}
|
|
16
|
+
const ref = field;
|
|
17
|
+
if (!ref || typeof ref.name !== "string") {
|
|
18
|
+
throw new Error(`${describe()}: 'invoke' must reference an invocable.`);
|
|
19
|
+
}
|
|
20
|
+
const resolved = (ref.alias && ref.alias !== "Self"
|
|
21
|
+
? ctx.moduleContext.resolveImportedInstance(ref.alias, ref.name)
|
|
22
|
+
: ctx.moduleContext.getInstance(ref.name));
|
|
23
|
+
if (!resolved || typeof resolved.invoke !== "function") {
|
|
24
|
+
throw new Error(`${describe()}: 'invoke' reference '${ref.name}' did not resolve to an invocable.`);
|
|
25
|
+
}
|
|
26
|
+
return (inputs) => ctx.invokeResolved(ref.kind, ref.name, resolved, inputs);
|
|
27
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Parse a duration string to milliseconds, or `null` if it is not a valid
|
|
2
|
+
* duration. Lets the caller own the error message and error type. */
|
|
3
|
+
export declare function tryParseDurationMs(value: string): number | null;
|
|
4
|
+
/** Parse a duration string (`300s`, `5m`, `1h`, `50ms`) to milliseconds. An
|
|
5
|
+
* `undefined` value yields `fallback` (default `0`) for optional fields; an
|
|
6
|
+
* invalid string throws a plain `Error`. */
|
|
7
|
+
export declare function parseDurationMs(value: string | undefined, fallback?: number): number;
|
|
8
|
+
//# sourceMappingURL=duration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":"AAeA;sEACsE;AACtE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI/D;AAED;;6CAE6C;AAC7C,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,SAAI,GAAG,MAAM,CAU/E"}
|
package/dist/duration.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duration parsing — the shared time helper for controllers. Telo writes human
|
|
3
|
+
* durations (`250ms`, `2s`, `1.5m`, `1h`) in manifests for timers, TTLs,
|
|
4
|
+
* windows, connect timeouts, backoff, and so on; this is the single place that
|
|
5
|
+
* turns one into a millisecond count. Consumers own their own error UX:
|
|
6
|
+
* `tryParseDurationMs` returns `null` on a bad string so a caller can throw its
|
|
7
|
+
* own typed error (e.g. an `InvokeError` for invoke-time input); `parseDurationMs`
|
|
8
|
+
* throws a coded `RuntimeError` and treats `undefined` as a default (for optional
|
|
9
|
+
* fields), so a bad duration in a manifest surfaces as a structured config error.
|
|
10
|
+
*/
|
|
11
|
+
import { RuntimeError } from "./types.js";
|
|
12
|
+
const DURATION_PATTERN = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h)$/;
|
|
13
|
+
const UNIT_MS = { ms: 1, s: 1_000, m: 60_000, h: 3_600_000 };
|
|
14
|
+
/** Parse a duration string to milliseconds, or `null` if it is not a valid
|
|
15
|
+
* duration. Lets the caller own the error message and error type. */
|
|
16
|
+
export function tryParseDurationMs(value) {
|
|
17
|
+
const match = DURATION_PATTERN.exec(value.trim());
|
|
18
|
+
if (!match)
|
|
19
|
+
return null;
|
|
20
|
+
return Number(match[1]) * UNIT_MS[match[2]];
|
|
21
|
+
}
|
|
22
|
+
/** Parse a duration string (`300s`, `5m`, `1h`, `50ms`) to milliseconds. An
|
|
23
|
+
* `undefined` value yields `fallback` (default `0`) for optional fields; an
|
|
24
|
+
* invalid string throws a plain `Error`. */
|
|
25
|
+
export function parseDurationMs(value, fallback = 0) {
|
|
26
|
+
if (value === undefined)
|
|
27
|
+
return fallback;
|
|
28
|
+
const ms = tryParseDurationMs(value);
|
|
29
|
+
if (ms === null) {
|
|
30
|
+
throw new RuntimeError("ERR_INVALID_VALUE", `Invalid duration ${JSON.stringify(value)}; use a number with a unit, e.g. "250ms", "2s", "1.5m", "1h".`);
|
|
31
|
+
}
|
|
32
|
+
return ms;
|
|
33
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { InvokeContext } from "./cancellation.js";
|
|
1
|
+
import type { InvokeContext, OpenSpan, OpenSpanOptions } from "./cancellation.js";
|
|
2
2
|
import type { ScopeHandle } from "./ref.js";
|
|
3
3
|
import type { ResourceInstance } from "./resource-instance.js";
|
|
4
4
|
import type { ResourceManifest } from "./resource-manifest.js";
|
|
@@ -13,6 +13,9 @@ export interface Tracer {
|
|
|
13
13
|
readonly enabled: boolean;
|
|
14
14
|
/** Mint the next monotonic invocation id (unique within the kernel run). */
|
|
15
15
|
next(): number;
|
|
16
|
+
/** Mint a fresh trace id (OTel-compatible 16-byte hex) for a new root trace.
|
|
17
|
+
* Children inherit it via {@link InvokeContext.traceId}. */
|
|
18
|
+
newTraceId(): string;
|
|
16
19
|
}
|
|
17
20
|
/** Four-stage resource lifecycle defined in resource-lifecycle.md */
|
|
18
21
|
export type LifecycleState = "Pending" | "Validated" | "Initialized" | "Draining" | "Teardown";
|
|
@@ -43,8 +46,12 @@ export type InstanceFactory = (context: EvaluationContext, resource: ResourceMan
|
|
|
43
46
|
* non-`Self` `alias`, resolves a cross-module reference into that
|
|
44
47
|
* import's published exported instances instead of the local context.
|
|
45
48
|
* Returns undefined when the named resource is not yet initialized.
|
|
49
|
+
* @param isPending Reports whether a local (no-alias) reference names a resource that is
|
|
50
|
+
* registered in this context but not yet initialized. Injection uses it
|
|
51
|
+
* to defer a resource whose dependency exists but hasn't inited yet —
|
|
52
|
+
* rather than leaving the slot unresolved — so the init loop retries it.
|
|
46
53
|
*/
|
|
47
|
-
export type PreInitHook = (resource: ResourceManifest, getInstance: (name: string, alias?: string) => ResourceInstance | undefined) => void;
|
|
54
|
+
export type PreInitHook = (resource: ResourceManifest, getInstance: (name: string, alias?: string) => ResourceInstance | undefined, isPending?: (name: string) => boolean) => void;
|
|
48
55
|
/** Canonical key for a resource instance: "<module>.<kind>.<name>" */
|
|
49
56
|
export declare function resourceKey(r: ResourceManifest): string;
|
|
50
57
|
/**
|
|
@@ -86,6 +93,11 @@ export interface EvaluationContext {
|
|
|
86
93
|
invoke<TInputs>(kind: string, name: string, inputs: TInputs, ctx?: InvokeContext): Promise<any>;
|
|
87
94
|
invokeResolved<TInputs>(kind: string, name: string, instance: ResourceInstance, inputs: TInputs, ctx?: InvokeContext): Promise<any>;
|
|
88
95
|
run(name: string, ctx?: InvokeContext): Promise<void>;
|
|
96
|
+
openSpan(base: InvokeContext | undefined, opts: OpenSpanOptions): Promise<OpenSpan>;
|
|
97
|
+
/** Bare scope-detach primitive: run `fn` outside the caller's cancellation/
|
|
98
|
+
* trace scope. Tracking/draining the task is the owning `ResourceContext`'s
|
|
99
|
+
* concern (see {@link ResourceContext.runDetached}). */
|
|
100
|
+
runDetached<T>(fn: () => Promise<T>): Promise<T>;
|
|
89
101
|
expand(value: unknown): unknown;
|
|
90
102
|
expandWith(value: unknown, extraContext: Record<string, unknown>): unknown;
|
|
91
103
|
expandPaths(value: Record<string, unknown>, paths: string[], excludePaths?: string[]): Record<string, unknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluation-context.d.ts","sourceRoot":"","sources":["../src/evaluation-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"evaluation-context.d.ts","sourceRoot":"","sources":["../src/evaluation-context.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,9 +3,11 @@ export * from "./compiled-value.js";
|
|
|
3
3
|
export * from "./capabilities/invokable.js";
|
|
4
4
|
export * from "./ref.js";
|
|
5
5
|
export * from "./invoke-step.js";
|
|
6
|
+
export * from "./dispatch-invoke-ref.js";
|
|
6
7
|
export * from "./capabilities/provider.js";
|
|
7
8
|
export * from "./capabilities/runnable.js";
|
|
8
9
|
export * from "./context-provider.js";
|
|
10
|
+
export * from "./duration.js";
|
|
9
11
|
export * from "./controller-context.js";
|
|
10
12
|
export * from "./controller-policy.js";
|
|
11
13
|
export * from "./evaluation-context.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC;AACjC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3,9 +3,11 @@ export * from "./compiled-value.js";
|
|
|
3
3
|
export * from "./capabilities/invokable.js";
|
|
4
4
|
export * from "./ref.js";
|
|
5
5
|
export * from "./invoke-step.js";
|
|
6
|
+
export * from "./dispatch-invoke-ref.js";
|
|
6
7
|
export * from "./capabilities/provider.js";
|
|
7
8
|
export * from "./capabilities/runnable.js";
|
|
8
9
|
export * from "./context-provider.js";
|
|
10
|
+
export * from "./duration.js";
|
|
9
11
|
export * from "./controller-context.js";
|
|
10
12
|
export * from "./controller-policy.js";
|
|
11
13
|
export * from "./evaluation-context.js";
|
package/dist/invoke-step.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Invocable } from "./capabilities/invokable.js";
|
|
2
2
|
import type { KindRef, ScopeContext } from "./ref.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type ResourceInstance } from "./resource-instance.js";
|
|
4
4
|
/** Retry policy for a single invoke step, passed through to `ctx.invoke`. */
|
|
5
5
|
export interface InvokeStepRetry {
|
|
6
6
|
attempts?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoke-step.d.ts","sourceRoot":"","sources":["../src/invoke-step.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"invoke-step.d.ts","sourceRoot":"","sources":["../src/invoke-step.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/E,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAED;;;;qDAIqD;AACrD,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;;mFAGmF;AACnF,MAAM,MAAM,UAAU,GAClB,MAAM,GACN;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,GAAG,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/D,kBAAkB,CAAC;AAEvB;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;IAC3D,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1F,cAAc,CAAC,OAAO,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,GAAG,CAAC,CAAC;IAChB;;qDAEiD;IACjD,uBAAuB,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAAC;CACrF;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,IAAI,CAAC,CAuCf"}
|
package/dist/invoke-step.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getRefIdentity } from "./resource-instance.js";
|
|
1
2
|
/**
|
|
2
3
|
* Execute one invoke step: evaluate the `when` guard, expand `inputs`, resolve
|
|
3
4
|
* and invoke the target, then record `steps[step.name] = { result }`. Knows
|
|
@@ -12,7 +13,14 @@ export async function executeInvokeStep(step, ctx, state) {
|
|
|
12
13
|
const raw = step.invoke;
|
|
13
14
|
let result;
|
|
14
15
|
if (raw && typeof raw.invoke === "function") {
|
|
15
|
-
|
|
16
|
+
// A pre-injected live instance (a `!ref` resolved at Phase 5). Route it
|
|
17
|
+
// through the traced chokepoint using the identity the kernel stamped at
|
|
18
|
+
// injection, so the call is instrumented exactly like a by-name dispatch.
|
|
19
|
+
// A truly anonymous instance (no stamp) falls back to a direct call.
|
|
20
|
+
const identity = getRefIdentity(raw);
|
|
21
|
+
result = identity
|
|
22
|
+
? await ctx.invokeResolved(identity.kind, identity.name, raw, inputs)
|
|
23
|
+
: await raw.invoke(inputs);
|
|
16
24
|
}
|
|
17
25
|
else {
|
|
18
26
|
const ref = raw;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CancellationSource, InvokeContext } from "./cancellation.js";
|
|
1
|
+
import type { CancellationSource, InvokeContext, OpenSpan, OpenSpanOptions } from "./cancellation.js";
|
|
2
2
|
import { ControllerContext } from "./controller-context.js";
|
|
3
3
|
import { ControllerPolicy } from "./controller-policy.js";
|
|
4
4
|
import { EvaluationContext } from "./evaluation-context.js";
|
|
@@ -40,6 +40,20 @@ export interface ResourceContext extends ControllerContext {
|
|
|
40
40
|
* lambda budget). Pass `source.context` into `invokeResolved` to scope an
|
|
41
41
|
* invocation tree to it. */
|
|
42
42
|
createCancellationSource(): CancellationSource;
|
|
43
|
+
/** Run `fn` detached from the caller's cancellation/trace scope: the ambient
|
|
44
|
+
* request token + span are replaced with the uncancellable root, so request
|
|
45
|
+
* teardown cannot abort the work and it does not nest under the request's
|
|
46
|
+
* trace. Fire-and-forget — the call returns nothing; the task is tracked
|
|
47
|
+
* against this resource and drained (bounded) when the resource tears down,
|
|
48
|
+
* and a failure (no caller to throw to) is routed to the EventBus. */
|
|
49
|
+
runDetached(fn: () => Promise<unknown>): void;
|
|
50
|
+
/** Open a trace span for an inbound boundary (an HTTP request, a queue message).
|
|
51
|
+
* Returns a child {@link InvokeContext} to thread into `invokeResolved` so the
|
|
52
|
+
* handler nests under this span, plus `settle` to close it with an outcome.
|
|
53
|
+
* The span roots a fresh trace unless `inbound` continues an upstream one
|
|
54
|
+
* (e.g. a W3C `traceparent`). A no-op (returns `base` unchanged) when tracing
|
|
55
|
+
* is off. */
|
|
56
|
+
openSpan(base: InvokeContext | undefined, opts: OpenSpanOptions): Promise<OpenSpan>;
|
|
43
57
|
invoke<TInputs>(kind: string, name: string, inputs: TInputs, options?: any): Promise<any>;
|
|
44
58
|
invokeResolved<TInputs>(kind: string, name: string, instance: ResourceInstance, inputs: TInputs, ctx?: InvokeContext): Promise<any>;
|
|
45
59
|
run(kind: string, name: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-context.d.ts","sourceRoot":"","sources":["../src/resource-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"resource-context.d.ts","sourceRoot":"","sources":["../src/resource-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACtG,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B;gFAC4E;IAC5E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;4EAGwE;IACxE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,aAAc,YAAW,aAAa;IACjD,OAAO;IAIP,QAAQ;CAGT;AAED,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAC,GAAG;IAAE,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEhG,MAAM,WAAW,eAAgB,SAAQ,iBAAiB;IACxD,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,IAAI,CAAC;IACzC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD;;iCAE6B;IAC7B,wBAAwB,IAAI,kBAAkB,CAAC;IAC/C;;;;;2EAKuE;IACvE,WAAW,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAC9C;;;;;kBAKc;IACd,QAAQ,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpF,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1F,cAAc,CAAC,OAAO,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,EAAE,OAAO,EACf,GAAG,CAAC,EAAE,aAAa,GAClB,OAAO,CAAC,GAAG,CAAC,CAAC;IAChB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAAC;IACvE,gBAAgB,CAAC,QAAQ,EAAE,GAAG,GAAG,IAAI,CAAC;IACtC,iBAAiB,IAAI,iBAAiB,CAAC;IACvC,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,iBAAiB,CAAC;IAChE,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IACnD,eAAe,CAAC,QAAQ,EAAE,GAAG,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtF,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC;IAC9C,qBAAqB,CAAC,MAAM,EAAE,GAAG,GAAG,aAAa,CAAC;IAClD,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACnD,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC/C,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IACzD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,SAAS,CAAC;IACtD,kFAAkF;IAClF,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,GAAG,aAAa,CAAC;IACtF,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,kBAAkB,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjG,kBAAkB,CAAC,UAAU,EAAE,GAAG,GAAG,IAAI,CAAC;IAC1C,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACjF;;;;OAIG;IACH,mBAAmB,IAAI,gBAAgB,GAAG,SAAS,CAAC;IACpD;;;;;;;;;;;OAWG;IACH,WAAW,IAAI,MAAM,GAAG,SAAS,CAAC;IAClC;;;;iEAI6D;IAC7D,cAAc,IAAI,MAAM,GAAG,SAAS,CAAC;IACrC;wDACoD;IACpD,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC5E;;;sBAGkB;IAClB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACxD,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,cAAc,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;CACxC"}
|
|
@@ -7,4 +7,22 @@ export type ResourceInstance<TInput = Record<string, any>, TOutput = any> = Part
|
|
|
7
7
|
teardown?(): void | Promise<void>;
|
|
8
8
|
snapshot?(): Record<string, any> | Promise<Record<string, any>>;
|
|
9
9
|
};
|
|
10
|
+
/** The kind+name an instance was resolved from. */
|
|
11
|
+
export interface RefIdentity {
|
|
12
|
+
kind: string;
|
|
13
|
+
name: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Non-enumerable identity tag the kernel stamps on a live instance when it
|
|
17
|
+
* injects a resolved `!ref` into a slot. A consumer that holds only the bare
|
|
18
|
+
* instance (an `executeInvokeStep` target whose `!ref` was pre-injected) can
|
|
19
|
+
* then recover the kind+name and dispatch through the traced chokepoint
|
|
20
|
+
* (`invokeResolved`) instead of calling `.invoke()` directly and escaping it.
|
|
21
|
+
*/
|
|
22
|
+
export declare const REF_IDENTITY: unique symbol;
|
|
23
|
+
/** Stamp the resolved kind+name onto an injected instance. Idempotent — an
|
|
24
|
+
* instance has exactly one identity, so re-injection into other slots is a no-op. */
|
|
25
|
+
export declare function stampRefIdentity(instance: object, kind: string, name: string): void;
|
|
26
|
+
/** Read the identity stamped by {@link stampRefIdentity}, if any. */
|
|
27
|
+
export declare function getRefIdentity(instance: object): RefIdentity | undefined;
|
|
10
28
|
//# sourceMappingURL=resource-instance.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-instance.d.ts","sourceRoot":"","sources":["../src/resource-instance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,gBAAgB,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG,GAAG,IAAI,OAAO,CACjF,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAC3B,GACC,OAAO,CAAC,QAAQ,CAAC,GACjB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,GAAG;IAC3B,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,QAAQ,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,QAAQ,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;CACjE,CAAC"}
|
|
1
|
+
{"version":3,"file":"resource-instance.d.ts","sourceRoot":"","sources":["../src/resource-instance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,gBAAgB,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG,GAAG,IAAI,OAAO,CACjF,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAC3B,GACC,OAAO,CAAC,QAAQ,CAAC,GACjB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,GAAG;IAC3B,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,QAAQ,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,QAAQ,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;CACjE,CAAC;AAEJ,mDAAmD;AACnD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,EAAE,OAAO,MAAuC,CAAC;AAE1E;sFACsF;AACtF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CASnF;AAED,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAExE"}
|
|
@@ -1 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Non-enumerable identity tag the kernel stamps on a live instance when it
|
|
3
|
+
* injects a resolved `!ref` into a slot. A consumer that holds only the bare
|
|
4
|
+
* instance (an `executeInvokeStep` target whose `!ref` was pre-injected) can
|
|
5
|
+
* then recover the kind+name and dispatch through the traced chokepoint
|
|
6
|
+
* (`invokeResolved`) instead of calling `.invoke()` directly and escaping it.
|
|
7
|
+
*/
|
|
8
|
+
export const REF_IDENTITY = Symbol.for("telo.refIdentity");
|
|
9
|
+
/** Stamp the resolved kind+name onto an injected instance. Idempotent — an
|
|
10
|
+
* instance has exactly one identity, so re-injection into other slots is a no-op. */
|
|
11
|
+
export function stampRefIdentity(instance, kind, name) {
|
|
12
|
+
if (!(REF_IDENTITY in instance)) {
|
|
13
|
+
Object.defineProperty(instance, REF_IDENTITY, {
|
|
14
|
+
value: { kind, name },
|
|
15
|
+
enumerable: false,
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: false,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Read the identity stamped by {@link stampRefIdentity}, if any. */
|
|
22
|
+
export function getRefIdentity(instance) {
|
|
23
|
+
return instance[REF_IDENTITY];
|
|
24
|
+
}
|
package/package.json
CHANGED
package/src/cancellation.ts
CHANGED
|
@@ -48,6 +48,36 @@ export interface InvokeContext {
|
|
|
48
48
|
/** The {@link invocationId} of the invocation that dispatched this one, or
|
|
49
49
|
* `undefined` at a trace root. Reconstructs the call tree on the consumer. */
|
|
50
50
|
readonly parentInvocationId?: number;
|
|
51
|
+
/** The trace this invocation belongs to — minted at the root span and inherited
|
|
52
|
+
* by every descendant. Present only while tracing. Groups all spans of one
|
|
53
|
+
* trace the way OpenTelemetry's `trace_id` does (an exporter maps it directly),
|
|
54
|
+
* independent of the parent chain. */
|
|
55
|
+
readonly traceId?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Terminal status of a span — maps to OpenTelemetry span status. */
|
|
59
|
+
export type SpanOutcome = "ok" | "failed" | "rejected" | "cancelled";
|
|
60
|
+
|
|
61
|
+
/** Options for {@link ResourceContext.openSpan}. */
|
|
62
|
+
export interface OpenSpanOptions {
|
|
63
|
+
/** The resource the span is attributed to (an inbound transport's listener,
|
|
64
|
+
* e.g. the `Http.Api`). Becomes the span's `ref`. */
|
|
65
|
+
ref: { kind: string; name: string };
|
|
66
|
+
/** Human label for the span (e.g. `"POST /feedback"`). */
|
|
67
|
+
label?: string;
|
|
68
|
+
/** Structured attributes (e.g. `{ method, path }`). Map to OTel span attributes. */
|
|
69
|
+
attributes?: Record<string, unknown>;
|
|
70
|
+
/** Continue an upstream distributed trace instead of rooting a new one — e.g.
|
|
71
|
+
* seeded from a W3C `traceparent` header. */
|
|
72
|
+
inbound?: { traceId: string; parentSpanId?: number };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Handle returned by {@link ResourceContext.openSpan}. */
|
|
76
|
+
export interface OpenSpan {
|
|
77
|
+
/** Thread into `invokeResolved` so the handler nests under the span. */
|
|
78
|
+
readonly context: InvokeContext;
|
|
79
|
+
/** Close the span with an outcome (emits the span's `end` event). */
|
|
80
|
+
settle(outcome: SpanOutcome, detail?: Record<string, unknown>): Promise<void>;
|
|
51
81
|
}
|
|
52
82
|
|
|
53
83
|
export interface CancellationSource {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Invocable } from "./capabilities/invokable.js";
|
|
2
|
+
import type { ModuleContext } from "./module-context.js";
|
|
3
|
+
import { getRefIdentity, type ResourceInstance } from "./resource-instance.js";
|
|
4
|
+
|
|
5
|
+
/** The context a decorator kind composes to dispatch its wrapped target. */
|
|
6
|
+
export interface DispatchContext {
|
|
7
|
+
invokeResolved<TInputs>(
|
|
8
|
+
kind: string,
|
|
9
|
+
name: string,
|
|
10
|
+
instance: ResourceInstance,
|
|
11
|
+
inputs: TInputs,
|
|
12
|
+
): Promise<unknown>;
|
|
13
|
+
readonly moduleContext: ModuleContext;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a decorator's `invoke:` field to a live invocable and return a thunk
|
|
18
|
+
* that dispatches it through the traced chokepoint. The field is either a
|
|
19
|
+
* Phase-5-injected instance or a raw `{ kind, name, alias }` ref resolved
|
|
20
|
+
* against the module context. Resolution is eager (fail-fast on a bad ref);
|
|
21
|
+
* dispatch is deferred, so a caller can run it synchronously (Cache.View) or
|
|
22
|
+
* detached (Run.Detach). `describe` labels the error with the owning resource.
|
|
23
|
+
*/
|
|
24
|
+
export function resolveInvocableDispatcher(
|
|
25
|
+
field: unknown,
|
|
26
|
+
ctx: DispatchContext,
|
|
27
|
+
describe: () => string,
|
|
28
|
+
): (inputs: Record<string, unknown>) => Promise<unknown> {
|
|
29
|
+
if (field && typeof (field as Invocable).invoke === "function") {
|
|
30
|
+
const instance = field as ResourceInstance & Invocable;
|
|
31
|
+
const id = getRefIdentity(field as object);
|
|
32
|
+
return (inputs) =>
|
|
33
|
+
id ? ctx.invokeResolved(id.kind, id.name, instance, inputs) : instance.invoke(inputs);
|
|
34
|
+
}
|
|
35
|
+
const ref = field as { kind: string; name: string; alias?: string } | undefined;
|
|
36
|
+
if (!ref || typeof ref.name !== "string") {
|
|
37
|
+
throw new Error(`${describe()}: 'invoke' must reference an invocable.`);
|
|
38
|
+
}
|
|
39
|
+
const resolved = (
|
|
40
|
+
ref.alias && ref.alias !== "Self"
|
|
41
|
+
? ctx.moduleContext.resolveImportedInstance(ref.alias, ref.name)
|
|
42
|
+
: ctx.moduleContext.getInstance(ref.name)
|
|
43
|
+
) as ResourceInstance | undefined;
|
|
44
|
+
if (!resolved || typeof resolved.invoke !== "function") {
|
|
45
|
+
throw new Error(`${describe()}: 'invoke' reference '${ref.name}' did not resolve to an invocable.`);
|
|
46
|
+
}
|
|
47
|
+
return (inputs) => ctx.invokeResolved(ref.kind, ref.name, resolved, inputs);
|
|
48
|
+
}
|
package/src/duration.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duration parsing — the shared time helper for controllers. Telo writes human
|
|
3
|
+
* durations (`250ms`, `2s`, `1.5m`, `1h`) in manifests for timers, TTLs,
|
|
4
|
+
* windows, connect timeouts, backoff, and so on; this is the single place that
|
|
5
|
+
* turns one into a millisecond count. Consumers own their own error UX:
|
|
6
|
+
* `tryParseDurationMs` returns `null` on a bad string so a caller can throw its
|
|
7
|
+
* own typed error (e.g. an `InvokeError` for invoke-time input); `parseDurationMs`
|
|
8
|
+
* throws a coded `RuntimeError` and treats `undefined` as a default (for optional
|
|
9
|
+
* fields), so a bad duration in a manifest surfaces as a structured config error.
|
|
10
|
+
*/
|
|
11
|
+
import { RuntimeError } from "./types.js";
|
|
12
|
+
|
|
13
|
+
const DURATION_PATTERN = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h)$/;
|
|
14
|
+
const UNIT_MS = { ms: 1, s: 1_000, m: 60_000, h: 3_600_000 } as const;
|
|
15
|
+
|
|
16
|
+
/** Parse a duration string to milliseconds, or `null` if it is not a valid
|
|
17
|
+
* duration. Lets the caller own the error message and error type. */
|
|
18
|
+
export function tryParseDurationMs(value: string): number | null {
|
|
19
|
+
const match = DURATION_PATTERN.exec(value.trim());
|
|
20
|
+
if (!match) return null;
|
|
21
|
+
return Number(match[1]) * UNIT_MS[match[2] as keyof typeof UNIT_MS];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Parse a duration string (`300s`, `5m`, `1h`, `50ms`) to milliseconds. An
|
|
25
|
+
* `undefined` value yields `fallback` (default `0`) for optional fields; an
|
|
26
|
+
* invalid string throws a plain `Error`. */
|
|
27
|
+
export function parseDurationMs(value: string | undefined, fallback = 0): number {
|
|
28
|
+
if (value === undefined) return fallback;
|
|
29
|
+
const ms = tryParseDurationMs(value);
|
|
30
|
+
if (ms === null) {
|
|
31
|
+
throw new RuntimeError(
|
|
32
|
+
"ERR_INVALID_VALUE",
|
|
33
|
+
`Invalid duration ${JSON.stringify(value)}; use a number with a unit, e.g. "250ms", "2s", "1.5m", "1h".`,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return ms;
|
|
37
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { InvokeContext } from "./cancellation.js";
|
|
1
|
+
import type { InvokeContext, OpenSpan, OpenSpanOptions } from "./cancellation.js";
|
|
2
2
|
import type { ScopeHandle } from "./ref.js";
|
|
3
3
|
import type { ResourceInstance } from "./resource-instance.js";
|
|
4
4
|
import type { ResourceManifest } from "./resource-manifest.js";
|
|
@@ -19,6 +19,9 @@ export interface Tracer {
|
|
|
19
19
|
readonly enabled: boolean;
|
|
20
20
|
/** Mint the next monotonic invocation id (unique within the kernel run). */
|
|
21
21
|
next(): number;
|
|
22
|
+
/** Mint a fresh trace id (OTel-compatible 16-byte hex) for a new root trace.
|
|
23
|
+
* Children inherit it via {@link InvokeContext.traceId}. */
|
|
24
|
+
newTraceId(): string;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
/** Four-stage resource lifecycle defined in resource-lifecycle.md */
|
|
@@ -60,10 +63,15 @@ export type InstanceFactory = (
|
|
|
60
63
|
* non-`Self` `alias`, resolves a cross-module reference into that
|
|
61
64
|
* import's published exported instances instead of the local context.
|
|
62
65
|
* Returns undefined when the named resource is not yet initialized.
|
|
66
|
+
* @param isPending Reports whether a local (no-alias) reference names a resource that is
|
|
67
|
+
* registered in this context but not yet initialized. Injection uses it
|
|
68
|
+
* to defer a resource whose dependency exists but hasn't inited yet —
|
|
69
|
+
* rather than leaving the slot unresolved — so the init loop retries it.
|
|
63
70
|
*/
|
|
64
71
|
export type PreInitHook = (
|
|
65
72
|
resource: ResourceManifest,
|
|
66
73
|
getInstance: (name: string, alias?: string) => ResourceInstance | undefined,
|
|
74
|
+
isPending?: (name: string) => boolean,
|
|
67
75
|
) => void;
|
|
68
76
|
|
|
69
77
|
/** Canonical key for a resource instance: "<module>.<kind>.<name>" */
|
|
@@ -124,6 +132,11 @@ export interface EvaluationContext {
|
|
|
124
132
|
ctx?: InvokeContext,
|
|
125
133
|
): Promise<any>;
|
|
126
134
|
run(name: string, ctx?: InvokeContext): Promise<void>;
|
|
135
|
+
openSpan(base: InvokeContext | undefined, opts: OpenSpanOptions): Promise<OpenSpan>;
|
|
136
|
+
/** Bare scope-detach primitive: run `fn` outside the caller's cancellation/
|
|
137
|
+
* trace scope. Tracking/draining the task is the owning `ResourceContext`'s
|
|
138
|
+
* concern (see {@link ResourceContext.runDetached}). */
|
|
139
|
+
runDetached<T>(fn: () => Promise<T>): Promise<T>;
|
|
127
140
|
expand(value: unknown): unknown;
|
|
128
141
|
expandWith(value: unknown, extraContext: Record<string, unknown>): unknown;
|
|
129
142
|
expandPaths(
|
package/src/index.ts
CHANGED
|
@@ -3,9 +3,11 @@ export * from "./compiled-value.js";
|
|
|
3
3
|
export * from "./capabilities/invokable.js";
|
|
4
4
|
export * from "./ref.js";
|
|
5
5
|
export * from "./invoke-step.js";
|
|
6
|
+
export * from "./dispatch-invoke-ref.js";
|
|
6
7
|
export * from "./capabilities/provider.js";
|
|
7
8
|
export * from "./capabilities/runnable.js";
|
|
8
9
|
export * from "./context-provider.js";
|
|
10
|
+
export * from "./duration.js";
|
|
9
11
|
export * from "./controller-context.js";
|
|
10
12
|
export * from "./controller-policy.js";
|
|
11
13
|
export * from "./evaluation-context.js";
|
package/src/invoke-step.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Invocable } from "./capabilities/invokable.js";
|
|
2
2
|
import type { KindRef, ScopeContext } from "./ref.js";
|
|
3
|
-
import type
|
|
3
|
+
import { getRefIdentity, type ResourceInstance } from "./resource-instance.js";
|
|
4
4
|
|
|
5
5
|
/** Retry policy for a single invoke step, passed through to `ctx.invoke`. */
|
|
6
6
|
export interface InvokeStepRetry {
|
|
@@ -94,7 +94,14 @@ export async function executeInvokeStep(
|
|
|
94
94
|
let result: unknown;
|
|
95
95
|
|
|
96
96
|
if (raw && typeof (raw as Invocable).invoke === "function") {
|
|
97
|
-
|
|
97
|
+
// A pre-injected live instance (a `!ref` resolved at Phase 5). Route it
|
|
98
|
+
// through the traced chokepoint using the identity the kernel stamped at
|
|
99
|
+
// injection, so the call is instrumented exactly like a by-name dispatch.
|
|
100
|
+
// A truly anonymous instance (no stamp) falls back to a direct call.
|
|
101
|
+
const identity = getRefIdentity(raw as object);
|
|
102
|
+
result = identity
|
|
103
|
+
? await ctx.invokeResolved(identity.kind, identity.name, raw as ResourceInstance, inputs)
|
|
104
|
+
: await (raw as Invocable).invoke(inputs);
|
|
98
105
|
} else {
|
|
99
106
|
const ref = raw as KindRef<Invocable>;
|
|
100
107
|
if (ref.alias && ref.alias !== "Self") {
|
package/src/resource-context.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CancellationSource, InvokeContext } from "./cancellation.js";
|
|
1
|
+
import type { CancellationSource, InvokeContext, OpenSpan, OpenSpanOptions } from "./cancellation.js";
|
|
2
2
|
import { ControllerContext } from "./controller-context.js";
|
|
3
3
|
import { ControllerPolicy } from "./controller-policy.js";
|
|
4
4
|
import { EvaluationContext } from "./evaluation-context.js";
|
|
@@ -49,6 +49,20 @@ export interface ResourceContext extends ControllerContext {
|
|
|
49
49
|
* lambda budget). Pass `source.context` into `invokeResolved` to scope an
|
|
50
50
|
* invocation tree to it. */
|
|
51
51
|
createCancellationSource(): CancellationSource;
|
|
52
|
+
/** Run `fn` detached from the caller's cancellation/trace scope: the ambient
|
|
53
|
+
* request token + span are replaced with the uncancellable root, so request
|
|
54
|
+
* teardown cannot abort the work and it does not nest under the request's
|
|
55
|
+
* trace. Fire-and-forget — the call returns nothing; the task is tracked
|
|
56
|
+
* against this resource and drained (bounded) when the resource tears down,
|
|
57
|
+
* and a failure (no caller to throw to) is routed to the EventBus. */
|
|
58
|
+
runDetached(fn: () => Promise<unknown>): void;
|
|
59
|
+
/** Open a trace span for an inbound boundary (an HTTP request, a queue message).
|
|
60
|
+
* Returns a child {@link InvokeContext} to thread into `invokeResolved` so the
|
|
61
|
+
* handler nests under this span, plus `settle` to close it with an outcome.
|
|
62
|
+
* The span roots a fresh trace unless `inbound` continues an upstream one
|
|
63
|
+
* (e.g. a W3C `traceparent`). A no-op (returns `base` unchanged) when tracing
|
|
64
|
+
* is off. */
|
|
65
|
+
openSpan(base: InvokeContext | undefined, opts: OpenSpanOptions): Promise<OpenSpan>;
|
|
52
66
|
invoke<TInputs>(kind: string, name: string, inputs: TInputs, options?: any): Promise<any>;
|
|
53
67
|
invokeResolved<TInputs>(
|
|
54
68
|
kind: string,
|
package/src/resource-instance.ts
CHANGED
|
@@ -12,3 +12,36 @@ export type ResourceInstance<TInput = Record<string, any>, TOutput = any> = Part
|
|
|
12
12
|
teardown?(): void | Promise<void>;
|
|
13
13
|
snapshot?(): Record<string, any> | Promise<Record<string, any>>;
|
|
14
14
|
};
|
|
15
|
+
|
|
16
|
+
/** The kind+name an instance was resolved from. */
|
|
17
|
+
export interface RefIdentity {
|
|
18
|
+
kind: string;
|
|
19
|
+
name: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Non-enumerable identity tag the kernel stamps on a live instance when it
|
|
24
|
+
* injects a resolved `!ref` into a slot. A consumer that holds only the bare
|
|
25
|
+
* instance (an `executeInvokeStep` target whose `!ref` was pre-injected) can
|
|
26
|
+
* then recover the kind+name and dispatch through the traced chokepoint
|
|
27
|
+
* (`invokeResolved`) instead of calling `.invoke()` directly and escaping it.
|
|
28
|
+
*/
|
|
29
|
+
export const REF_IDENTITY: unique symbol = Symbol.for("telo.refIdentity");
|
|
30
|
+
|
|
31
|
+
/** Stamp the resolved kind+name onto an injected instance. Idempotent — an
|
|
32
|
+
* instance has exactly one identity, so re-injection into other slots is a no-op. */
|
|
33
|
+
export function stampRefIdentity(instance: object, kind: string, name: string): void {
|
|
34
|
+
if (!(REF_IDENTITY in instance)) {
|
|
35
|
+
Object.defineProperty(instance, REF_IDENTITY, {
|
|
36
|
+
value: { kind, name } satisfies RefIdentity,
|
|
37
|
+
enumerable: false,
|
|
38
|
+
configurable: true,
|
|
39
|
+
writable: false,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Read the identity stamped by {@link stampRefIdentity}, if any. */
|
|
45
|
+
export function getRefIdentity(instance: object): RefIdentity | undefined {
|
|
46
|
+
return (instance as Record<symbol, RefIdentity | undefined>)[REF_IDENTITY];
|
|
47
|
+
}
|