stitchkit 0.66.0 → 0.67.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.
@@ -1,4 +1,4 @@
1
- import type { ManagedResource } from './resource';
1
+ import { type ManagedResource } from './resource';
2
2
  export interface ResolvedManagedResource {
3
3
  readonly resource: ManagedResource;
4
4
  readonly id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/application/graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAED,2FAA2F;AAC3F,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,SAAS,eAAe,EAAE,GACpC,SAAS,uBAAuB,EAAE,CAkDpC"}
1
+ {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/application/graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAA+B,MAAM,YAAY,CAAC;AAG/E,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAED,2FAA2F;AAC3F,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,SAAS,eAAe,EAAE,GACpC,SAAS,uBAAuB,EAAE,CAoDpC"}
@@ -1 +1 @@
1
- {"version":3,"file":"kernel.d.ts","sourceRoot":"","sources":["../../src/application/kernel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAG9C,OAAO,KAAK,EACV,eAAe,EAGhB,MAAM,YAAY,CAAC;AACpB,OAAO,EAIL,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,EAE9B,KAAK,mBAAmB,EAGzB,MAAM,WAAW,CAAC;AAEnB,6EAA6E;AAC7E,MAAM,MAAM,wBAAwB,GAAG,2BAA2B,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AA8DvF,wEAAwE;AACxE,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAC;IACzC,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,gCAAgC;;;;iBAI3C,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAE1F;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,+BAA+B;;;iBAG1C,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAChD;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,mBAAmB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,0BAA0B,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5F;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,IAAI,yBAAyB,GAAG,IAAI,CAAC;IAC5C,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,oBAAoB,CAAC;IACzC,KAAK,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACtC,WAAW,IAAI,mBAAmB,CAAC;IACnC,SAAS,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,mBAAmB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACzE,QAAQ,CAAC,OAAO,CAAC,EAAE,0BAA0B,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC;CACpF;AAED;;;;;;;;;GASG;AACH,qBAAa,yBAA0B,SAAQ,QAAQ,CAAC,2BAA2B,CAAC;IAClF,cAGC;CACF;AA2BD,mFAAmF;AACnF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,CAgzB9E"}
1
+ {"version":3,"file":"kernel.d.ts","sourceRoot":"","sources":["../../src/application/kernel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAG9C,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,YAAY,CAAC;AACpB,OAAO,EAIL,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,EAE9B,KAAK,mBAAmB,EAGzB,MAAM,WAAW,CAAC;AAEnB,6EAA6E;AAC7E,MAAM,MAAM,wBAAwB,GAAG,2BAA2B,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AA8DvF,wEAAwE;AACxE,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAC;IACzC,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,gCAAgC;;;;iBAI3C,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAE1F;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,+BAA+B;;;iBAG1C,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAChD;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,mBAAmB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,0BAA0B,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5F;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,IAAI,yBAAyB,GAAG,IAAI,CAAC;IAC5C,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,oBAAoB,CAAC;IACzC,KAAK,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACtC,WAAW,IAAI,mBAAmB,CAAC;IACnC,SAAS,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,mBAAmB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACzE,QAAQ,CAAC,OAAO,CAAC,EAAE,0BAA0B,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC;CACpF;AAED;;;;;;;;;GASG;AACH,qBAAa,yBAA0B,SAAQ,QAAQ,CAAC,2BAA2B,CAAC;IAClF,cAGC;CACF;AA2BD,mFAAmF;AACnF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,CAs1B9E"}
@@ -1,4 +1,50 @@
1
1
  import type { ApplicationHealth } from './schemas';
2
+ /**
3
+ * What a resource declares it must start after.
4
+ *
5
+ * A string names the dependency; the resource object *is* the dependency, and
6
+ * that is the form `use()` can type. Both are accepted because ordering and
7
+ * value-passing are separate needs: a resource that only has to start later
8
+ * still says so with a string, and nothing forces a graph to be rewritten.
9
+ */
10
+ export type ManagedResourceDependency = string | ManagedResource;
11
+ /**
12
+ * What `use()` returns for a resource whose `start` publishes no value.
13
+ *
14
+ * Deliberately a branded interface rather than `never`. `never` is assignable
15
+ * to every type, so reading a value off a resource that never published one
16
+ * would compile everywhere and fail only at runtime — the exact silent shape
17
+ * this whole mechanism exists to remove. The property name is the message: it
18
+ * is what the compiler prints when the assignment is refused.
19
+ */
20
+ export interface ManagedResourcePublishesNoValue {
21
+ readonly 'this managed resource publishes no value from start()': never;
22
+ }
23
+ /**
24
+ * The value a resource's `start` publishes, recovered from its own literal type.
25
+ *
26
+ * Recovered by a conditional type on purpose. The obvious alternative —
27
+ * `use<TValue>(resource: ManagedResource<TValue>): TValue` — is unsound: with
28
+ * no inference site in the argument, TypeScript happily infers `TValue` from
29
+ * the *assignment context*, so `const port: string = context.use(database)`
30
+ * compiles and the annotation becomes self-fulfilling. Reading the type out of
31
+ * `start`'s return leaves the argument as the only source.
32
+ *
33
+ * A resource whose `start` is annotated with the wide
34
+ * `ManagedResourceStartResult` publishes nothing as far as this type is
35
+ * concerned — `value` is optional there, and an optional property cannot be
36
+ * distinguished from an absent one. That is why `managedServerResource` and
37
+ * anything else meant to publish declares a precise return type.
38
+ */
39
+ export type ManagedResourcePublished<TResource> = TResource extends {
40
+ start(...args: never): infer TStart;
41
+ } ? [Extract<Awaited<TStart>, {
42
+ value: unknown;
43
+ }>] extends [never] ? ManagedResourcePublishesNoValue : Extract<Awaited<TStart>, {
44
+ value: unknown;
45
+ }> extends {
46
+ value: infer TValue;
47
+ } ? TValue : ManagedResourcePublishesNoValue : ManagedResourcePublishesNoValue;
2
48
  export interface ManagedResourceContext {
3
49
  readonly applicationId: string;
4
50
  readonly signal: AbortSignal;
@@ -6,16 +52,39 @@ export interface ManagedResourceContext {
6
52
  readonly forceDeadlineAt?: number;
7
53
  now(): number;
8
54
  reportHealth(health: Exclude<ApplicationHealth, 'unknown'>): void;
55
+ /**
56
+ * The value a declared dependency published from its `start`.
57
+ *
58
+ * `dependsOn` carries ordering; this carries the object. Without it every
59
+ * dependency that is a real thing — a connection, a socket server, a client —
60
+ * travels through a module-local `let handle: T | null` whose null guard the
61
+ * graph makes unreachable: the type is lost, the guard is noise, and half the
62
+ * invariant lives in the order of assignments rather than in the graph.
63
+ *
64
+ * Refuses a resource this one did not declare a dependency on, and refuses a
65
+ * resource that published nothing. Both are ordering bugs the graph can see
66
+ * and a mutable module-local cannot.
67
+ */
68
+ use<TResource extends ManagedResource>(resource: TResource): ManagedResourcePublished<TResource>;
9
69
  }
10
70
  export interface ManagedResourceStartResult {
11
71
  /** Resolves when the resource is ready for dependants. */
12
72
  readonly ready?: Promise<void>;
13
73
  /** Long-lived completion; settling before shutdown marks the resource unhealthy. */
14
74
  readonly completion?: Promise<void>;
75
+ /**
76
+ * What this resource hands to the resources that depend on it.
77
+ *
78
+ * Read with `context.use(thisResource)`. Published once, when `start`
79
+ * resolves, and readable for the rest of the application's life — including
80
+ * from `activate` and the shutdown phases, where a dependant may still need
81
+ * the handle it was given.
82
+ */
83
+ readonly value?: unknown;
15
84
  }
16
85
  export interface ManagedResource {
17
86
  readonly id: string;
18
- readonly dependsOn?: readonly string[];
87
+ readonly dependsOn?: readonly ManagedResourceDependency[];
19
88
  readonly required?: boolean;
20
89
  start(context: ManagedResourceContext): void | ManagedResourceStartResult | Promise<void> | Promise<ManagedResourceStartResult | undefined>;
21
90
  /** Runs only after the whole required graph reached readiness. */
@@ -26,5 +95,7 @@ export interface ManagedResource {
26
95
  close?(context: ManagedResourceContext): void | Promise<void>;
27
96
  force?(context: ManagedResourceContext): void | Promise<void>;
28
97
  }
98
+ /** The id of a dependency declared either way. */
99
+ export declare function managedResourceDependencyId(dependency: ManagedResourceDependency): string;
29
100
  export declare function defineManagedResource<const TResource extends ManagedResource>(resource: TResource): TResource;
30
101
  //# sourceMappingURL=resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/application/resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEnD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,GAAG,IAAI,MAAM,CAAC;IACd,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,iBAAiB,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC;CACnE;AAED,MAAM,WAAW,0BAA0B;IACzC,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,oFAAoF;IACpF,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,KAAK,CACH,OAAO,EAAE,sBAAsB,GAE7B,IAAI,GACJ,0BAA0B,GAC1B,OAAO,CAAC,IAAI,CAAC,GACb,OAAO,CAAC,0BAA0B,GAAG,SAAS,CAAC,CAAC;IACpD,kEAAkE;IAClE,QAAQ,CAAC,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,aAAa,CAAC,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,KAAK,CAAC,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,gFAAgF;IAChF,KAAK,CAAC,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,KAAK,CAAC,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/D;AAED,wBAAgB,qBAAqB,CAAC,KAAK,CAAC,SAAS,SAAS,eAAe,EAC3E,QAAQ,EAAE,SAAS,GAClB,SAAS,CAEX"}
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/application/resource.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,33 @@
1
1
  import type { ManagedServerHandle } from '../server/shutdown';
2
- import { type ManagedResource } from './resource';
2
+ import { type ManagedResource, type ManagedResourceContext, type ManagedResourceDependency } from './resource';
3
3
  export interface ManagedServerResourceConfig<TRuntime> {
4
4
  readonly id: string;
5
- readonly server: ManagedServerHandle<TRuntime> | (() => ManagedServerHandle<TRuntime>);
6
- readonly dependsOn?: readonly string[];
5
+ /**
6
+ * The server, or how to make one.
7
+ *
8
+ * A handle is adopted as it is. A thunk is called during `start`, which is
9
+ * the only way to say "bind the port after the database is up" — and the only
10
+ * reading of a thunk that is not a trap. It used to be called during
11
+ * *shutdown*, so a graph that delegated creation here started clean, reported
12
+ * `healthy`, and had nothing listening on the port; the failure surfaced as a
13
+ * request that never arrived.
14
+ */
15
+ readonly server: ManagedServerHandle<TRuntime> | (() => ManagedServerHandle<TRuntime> | Promise<ManagedServerHandle<TRuntime>>);
16
+ readonly dependsOn?: readonly ManagedResourceDependency[];
7
17
  readonly required?: boolean;
8
18
  readonly retryAfterSeconds?: number;
9
19
  }
10
- /** Adapt an existing managed server without duplicating its shutdown state machine. */
11
- export declare function managedServerResource<TRuntime>(config: ManagedServerResourceConfig<TRuntime>): ManagedResource;
20
+ /**
21
+ * A managed resource that owns a managed server.
22
+ *
23
+ * Its `start` publishes the handle, so a resource that depends on this one
24
+ * reads the running server with `context.use(...)` instead of a module-local.
25
+ */
26
+ export interface ManagedServerResource<TRuntime> extends ManagedResource {
27
+ start(context: ManagedResourceContext): Promise<{
28
+ readonly value: ManagedServerHandle<TRuntime>;
29
+ }>;
30
+ }
31
+ /** Own a managed server's lifecycle without duplicating its shutdown state machine. */
32
+ export declare function managedServerResource<TRuntime>(config: ManagedServerResourceConfig<TRuntime>): ManagedServerResource<TRuntime>;
12
33
  //# sourceMappingURL=server-resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server-resource.d.ts","sourceRoot":"","sources":["../../src/application/server-resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAkB,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAEzE,MAAM,WAAW,2BAA2B,CAAC,QAAQ;IACnD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC;IACvF,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,uFAAuF;AACvF,wBAAgB,qBAAqB,CAAC,QAAQ,EAC5C,MAAM,EAAE,2BAA2B,CAAC,QAAQ,CAAC,GAC5C,eAAe,CAmGjB"}
1
+ {"version":3,"file":"server-resource.d.ts","sourceRoot":"","sources":["../../src/application/server-resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAmC,MAAM,oBAAoB,CAAC;AAC/F,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC/B,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,2BAA2B,CAAC,QAAQ;IACnD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB;;;;;;;;;OASG;IACH,QAAQ,CAAC,MAAM,EACX,mBAAmB,CAAC,QAAQ,CAAC,GAC7B,CAAC,MAAM,mBAAmB,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnF,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACrC;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB,CAAC,QAAQ,CAAE,SAAQ,eAAe;IACtE,KAAK,CACH,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC;QAAE,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAC,CAAC;CAC/D;AAED,uFAAuF;AACvF,wBAAgB,qBAAqB,CAAC,QAAQ,EAC5C,MAAM,EAAE,2BAA2B,CAAC,QAAQ,CAAC,GAC5C,qBAAqB,CAAC,QAAQ,CAAC,CAwIjC"}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  defineManagedResource
3
- } from "./index-yr276yz0.js";
3
+ } from "./index-2k4yrqkc.js";
4
4
  import {
5
5
  AppError
6
6
  } from "./index-xxye8j3k.js";
@@ -3,8 +3,8 @@ export { type ApplicationEventSink, type ApplicationEventSinkConfig, type Applic
3
3
  export { type ApplicationHealthHandlerOptions, ApplicationHealthHandlerOptionsSchema, type ApplicationOperationalHandlers, type ApplicationOperationalHandlersOptions, ApplicationOperationalHandlersOptionsSchema, createApplicationHealthHandler, createApplicationOperationalHandlers, } from './application/health';
4
4
  export { type ApplicationAdmission, ApplicationAdmissionError, type ApplicationConfig, type ApplicationHandle, type ApplicationOperationLease, type ApplicationResourceFailure, type ApplicationResourcePhase, type ApplicationShutdownBudget, ApplicationShutdownBudgetSchema, type ApplicationShutdownOptions, ApplicationShutdownOptionsSchema, createApplication, } from './application/kernel';
5
5
  export { type ApplicationSnapshotSink, type ApplicationSnapshotSinkConfig, type ApplicationSnapshotSinkError, type ApplicationSnapshotSinkStatus, ApplicationSnapshotSinkStatusSchema, createApplicationSnapshotSink, type RevisionedApplicationSnapshot, } from './application/latest-sink';
6
- export { defineManagedResource, type ManagedResource, type ManagedResourceContext, type ManagedResourceStartResult, } from './application/resource';
6
+ export { defineManagedResource, type ManagedResource, type ManagedResourceContext, type ManagedResourceDependency, type ManagedResourcePublished, type ManagedResourcePublishesNoValue, type ManagedResourceStartResult, managedResourceDependencyId, } from './application/resource';
7
7
  export { createManagedSchedule, type ManagedSchedule, type ManagedScheduleClock, type ManagedScheduleConfig, type ManagedScheduleDescriptor, ManagedScheduleDescriptorSchema, type ManagedScheduleErrorPolicy, ManagedScheduleErrorPolicySchema, type ManagedScheduleOverlap, ManagedScheduleOverlapSchema, type ManagedScheduleRunContext, type ManagedScheduleStatus, ManagedScheduleStatusSchema, type ManagedScheduleTimer, } from './application/schedule';
8
8
  export { type ApplicationAdmissionSnapshot, ApplicationAdmissionSnapshotSchema, type ApplicationHealth, ApplicationHealthSchema, type ApplicationId, ApplicationIdSchema, type ApplicationLifecycle, ApplicationLifecycleSchema, type ApplicationResourceShutdown, ApplicationResourceShutdownSchema, type ApplicationShutdownResult, ApplicationShutdownResultSchema, type ApplicationSnapshot, ApplicationSnapshotSchema, type ApplicationStatusProjection, ApplicationStatusProjectionSchema, type ManagedResourceSnapshot, ManagedResourceSnapshotSchema, type ManagedResourceState, ManagedResourceStateSchema, projectApplicationStatus, } from './application/schemas';
9
- export { type ManagedServerResourceConfig, managedServerResource, } from './application/server-resource';
9
+ export { type ManagedServerResource, type ManagedServerResourceConfig, managedServerResource, } from './application/server-resource';
10
10
  //# sourceMappingURL=application.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"application.d.ts","sourceRoot":"","sources":["../src/application.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,uBAAuB,EACvB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,iCAAiC,EACtC,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,qBAAqB,EAC1B,2BAA2B,EAC3B,KAAK,aAAa,EAClB,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAC9B,+BAA+B,EAC/B,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,+BAA+B,EACpC,qCAAqC,EACrC,KAAK,8BAA8B,EACnC,KAAK,qCAAqC,EAC1C,2CAA2C,EAC3C,8BAA8B,EAC9B,oCAAoC,GACrC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,oBAAoB,EACzB,yBAAyB,EACzB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,+BAA+B,EAC/B,KAAK,0BAA0B,EAC/B,gCAAgC,EAChC,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,6BAA6B,EAClC,KAAK,4BAA4B,EACjC,KAAK,6BAA6B,EAClC,mCAAmC,EACnC,6BAA6B,EAC7B,KAAK,6BAA6B,GACnC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,GAChC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,+BAA+B,EAC/B,KAAK,0BAA0B,EAC/B,gCAAgC,EAChC,KAAK,sBAAsB,EAC3B,4BAA4B,EAC5B,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC1B,2BAA2B,EAC3B,KAAK,oBAAoB,GAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,4BAA4B,EACjC,kCAAkC,EAClC,KAAK,iBAAiB,EACtB,uBAAuB,EACvB,KAAK,aAAa,EAClB,mBAAmB,EACnB,KAAK,oBAAoB,EACzB,0BAA0B,EAC1B,KAAK,2BAA2B,EAChC,iCAAiC,EACjC,KAAK,yBAAyB,EAC9B,+BAA+B,EAC/B,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,KAAK,2BAA2B,EAChC,iCAAiC,EACjC,KAAK,uBAAuB,EAC5B,6BAA6B,EAC7B,KAAK,oBAAoB,EACzB,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,KAAK,2BAA2B,EAChC,qBAAqB,GACtB,MAAM,+BAA+B,CAAC"}
1
+ {"version":3,"file":"application.d.ts","sourceRoot":"","sources":["../src/application.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,uBAAuB,EACvB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,iCAAiC,EACtC,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,qBAAqB,EAC1B,2BAA2B,EAC3B,KAAK,aAAa,EAClB,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAC9B,+BAA+B,EAC/B,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,+BAA+B,EACpC,qCAAqC,EACrC,KAAK,8BAA8B,EACnC,KAAK,qCAAqC,EAC1C,2CAA2C,EAC3C,8BAA8B,EAC9B,oCAAoC,GACrC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,oBAAoB,EACzB,yBAAyB,EACzB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,+BAA+B,EAC/B,KAAK,0BAA0B,EAC/B,gCAAgC,EAChC,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,6BAA6B,EAClC,KAAK,4BAA4B,EACjC,KAAK,6BAA6B,EAClC,mCAAmC,EACnC,6BAA6B,EAC7B,KAAK,6BAA6B,GACnC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,+BAA+B,EACpC,KAAK,0BAA0B,EAC/B,2BAA2B,GAC5B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,+BAA+B,EAC/B,KAAK,0BAA0B,EAC/B,gCAAgC,EAChC,KAAK,sBAAsB,EAC3B,4BAA4B,EAC5B,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC1B,2BAA2B,EAC3B,KAAK,oBAAoB,GAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,4BAA4B,EACjC,kCAAkC,EAClC,KAAK,iBAAiB,EACtB,uBAAuB,EACvB,KAAK,aAAa,EAClB,mBAAmB,EACnB,KAAK,oBAAoB,EACzB,0BAA0B,EAC1B,KAAK,2BAA2B,EAChC,iCAAiC,EACjC,KAAK,yBAAyB,EAC9B,+BAA+B,EAC/B,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,KAAK,2BAA2B,EAChC,iCAAiC,EACjC,KAAK,uBAAuB,EAC5B,6BAA6B,EAC7B,KAAK,oBAAoB,EACzB,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,qBAAqB,GACtB,MAAM,+BAA+B,CAAC"}
@@ -1,6 +1,3 @@
1
- import {
2
- defineManagedResource
3
- } from "./index-yr276yz0.js";
4
1
  import {
5
2
  ActivityIdSchema,
6
3
  ActivityLiveStateSchema,
@@ -22,7 +19,11 @@ import {
22
19
  ApplicationShutdownBudgetSchema,
23
20
  ApplicationShutdownOptionsSchema,
24
21
  createApplication
25
- } from "./index-eabpd4tb.js";
22
+ } from "./index-physbf4z.js";
23
+ import {
24
+ defineManagedResource,
25
+ managedResourceDependencyId
26
+ } from "./index-2k4yrqkc.js";
26
27
  import {
27
28
  ApplicationAdmissionSnapshotSchema,
28
29
  ApplicationHealthSchema,
@@ -119,7 +120,10 @@ function createApplicationOperationalHandlers(application, options = {}) {
119
120
  // src/application/server-resource.ts
120
121
  function managedServerResource(config) {
121
122
  let shutdownPromise;
122
- const getServer = () => typeof config.server === "function" ? config.server() : config.server;
123
+ let started;
124
+ let startAttempted = false;
125
+ const resolveServer = () => typeof config.server === "function" ? config.server() : config.server;
126
+ const isHandle = (value) => ("shutdown" in value);
123
127
  const whole = (milliseconds, round) => Number.isFinite(milliseconds) ? Math.max(0, round(milliseconds)) : milliseconds;
124
128
  const grace = (milliseconds) => whole(milliseconds, Math.floor);
125
129
  const force = (milliseconds) => whole(milliseconds, Math.ceil);
@@ -129,12 +133,18 @@ function managedServerResource(config) {
129
133
  const now = context.now();
130
134
  const gracePeriodMs = phase === "force" ? 0 : context.deadlineAt === undefined ? undefined : grace(context.deadlineAt - now);
131
135
  const forceTimeoutMs = context.forceDeadlineAt === undefined ? undefined : phase === "force" ? force(context.forceDeadlineAt - now) : force(context.forceDeadlineAt - (context.deadlineAt ?? now));
132
- shutdownPromise = getServer().shutdown({
136
+ const server = started ?? (startAttempted ? undefined : resolveServer());
137
+ if (server === undefined) {
138
+ shutdownPromise = Promise.resolve(undefined);
139
+ return shutdownPromise;
140
+ }
141
+ const options = {
133
142
  ...gracePeriodMs !== undefined && { gracePeriodMs },
134
143
  ...forceTimeoutMs !== undefined && { forceTimeoutMs },
135
144
  retryAfterSeconds: grace(config.retryAfterSeconds ?? 5),
136
145
  signal: context.signal
137
- });
146
+ };
147
+ shutdownPromise = isHandle(server) ? server.shutdown(options) : server.then((handle) => handle.shutdown(options));
138
148
  shutdownPromise.catch(() => {
139
149
  return;
140
150
  });
@@ -144,7 +154,11 @@ function managedServerResource(config) {
144
154
  id: config.id,
145
155
  ...config.dependsOn && { dependsOn: config.dependsOn },
146
156
  ...config.required !== undefined && { required: config.required },
147
- start() {},
157
+ async start() {
158
+ startAttempted = true;
159
+ started = await resolveServer();
160
+ return { value: started };
161
+ },
148
162
  stopAdmission(context) {
149
163
  ensureShutdown(context);
150
164
  },
@@ -196,6 +210,7 @@ export {
196
210
  createApplicationSnapshotSink,
197
211
  createManagedSchedule,
198
212
  defineManagedResource,
213
+ managedResourceDependencyId,
199
214
  managedServerResource,
200
215
  projectApplicationStatus
201
216
  };
@@ -0,0 +1,9 @@
1
+ // src/application/resource.ts
2
+ function managedResourceDependencyId(dependency) {
3
+ return typeof dependency === "string" ? dependency : dependency.id;
4
+ }
5
+ function defineManagedResource(resource) {
6
+ return resource;
7
+ }
8
+
9
+ export { managedResourceDependencyId, defineManagedResource };
@@ -1465,7 +1465,17 @@ function bindProcessSignals(handle, options = {}) {
1465
1465
  bound.add(handle);
1466
1466
  const signals = [...new Set(options.signals ?? DEFAULT_PROCESS_SIGNALS)];
1467
1467
  const source = options.signalSource ?? defaultSignalSource;
1468
- const budgets = ShutdownOptionsSchema.omit({ signal: true }).parse(options.shutdown ?? {});
1468
+ const requested = options.shutdown ?? {};
1469
+ const validated = ShutdownOptionsSchema.omit({ signal: true }).parse(requested);
1470
+ const budgets = {
1471
+ ...requested.gracePeriodMs !== undefined && { gracePeriodMs: validated.gracePeriodMs },
1472
+ ...requested.forceTimeoutMs !== undefined && {
1473
+ forceTimeoutMs: validated.forceTimeoutMs
1474
+ },
1475
+ ...requested.retryAfterSeconds !== undefined && {
1476
+ retryAfterSeconds: validated.retryAfterSeconds
1477
+ }
1478
+ };
1469
1479
  const controller = new AbortController;
1470
1480
  const handlers = [];
1471
1481
  let started = false;
@@ -1,3 +1,6 @@
1
+ import {
2
+ managedResourceDependencyId
3
+ } from "./index-2k4yrqkc.js";
1
4
  import {
2
5
  ApplicationIdSchema,
3
6
  ApplicationShutdownResultSchema,
@@ -15,7 +18,7 @@ function resolveResourceGraph(resources) {
15
18
  const entries = resources.map((resource, declarationIndex) => ({
16
19
  resource,
17
20
  id: ApplicationIdSchema.parse(resource.id),
18
- dependsOn: [...resource.dependsOn ?? []].map((id) => ApplicationIdSchema.parse(id)),
21
+ dependsOn: [...resource.dependsOn ?? []].map((dependency) => ApplicationIdSchema.parse(managedResourceDependencyId(dependency))),
19
22
  required: resource.required ?? true,
20
23
  declarationIndex
21
24
  }));
@@ -115,6 +118,7 @@ function createApplication(config) {
115
118
  });
116
119
  } catch {}
117
120
  };
121
+ const published = new Map;
118
122
  const records = new Map;
119
123
  for (const entry of ordered) {
120
124
  records.set(entry.id, {
@@ -206,6 +210,19 @@ function createApplication(config) {
206
210
  forceDeadlineAt: options.forceDeadlineAt
207
211
  },
208
212
  now: () => performance.now(),
213
+ use(resource) {
214
+ const dependencyId = resource?.id;
215
+ if (typeof dependencyId !== "string" || dependencyId.length === 0) {
216
+ throw new Error(`[stitchkit] resource "${record.entry.id}": use() takes a managed resource, and this one has no id`);
217
+ }
218
+ if (!record.entry.dependsOn.includes(dependencyId)) {
219
+ throw new Error(`[stitchkit] resource "${record.entry.id}" used "${dependencyId}" without declaring it in dependsOn`);
220
+ }
221
+ if (!published.has(dependencyId)) {
222
+ throw new Error(`[stitchkit] resource "${record.entry.id}" used "${dependencyId}", which published no value from start()`);
223
+ }
224
+ return published.get(dependencyId);
225
+ },
209
226
  reportHealth(health) {
210
227
  if (record.state === "stopped")
211
228
  return;
@@ -310,6 +327,8 @@ function createApplication(config) {
310
327
  }
311
328
  if (isStartResult(started)) {
312
329
  record.runtime = started;
330
+ if (started.value !== undefined)
331
+ published.set(entry.id, started.value);
313
332
  let resourceReady = started.ready === undefined;
314
333
  let completionSettled = false;
315
334
  let completionFailure;
package/dist/node.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  bindRealtimeServer,
4
4
  createHandler,
5
5
  createSocketIOServer
6
- } from "./index-s1tywej8.js";
6
+ } from "./index-8mxadm34.js";
7
7
  import {
8
8
  createImplement,
9
9
  createImplementRegistry,
@@ -7,7 +7,7 @@ import {
7
7
  parseMultipart,
8
8
  socketIoLane,
9
9
  webSocketLane
10
- } from "../index-s1tywej8.js";
10
+ } from "../index-8mxadm34.js";
11
11
  import {
12
12
  composeAuthHooks,
13
13
  createAuthHook,
@@ -1 +1 @@
1
- {"version":3,"file":"process-signals.d.ts","sourceRoot":"","sources":["../../src/server/process-signals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGlE;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GACzB,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,SAAS,GACT,SAAS,GACT,UAAU,CAAC;AAEf,wDAAwD;AACxD,MAAM,WAAW,cAAc,CAAC,OAAO,GAAG,cAAc;IACtD,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACvD;AAED,gEAAgE;AAChE,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AAE3E;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IACzD,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC1D;;;;;;;;OAQG;IACH,YAAY,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC;CAClD;AAED,MAAM,WAAW,qBAAqB,CAAC,OAAO,GAAG,cAAc;IAC7D;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;IAC3C,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,kEAAkE;IAClE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACpE,sFAAsF;IACtF,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,OAAO,GAAG,UAAU,KAAK,IAAI,CAAC;IACpF;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC3D;AAED,MAAM,WAAW,qBAAqB,CAAC,OAAO,GAAG,cAAc;IAC7D;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IAC/C,wCAAwC;IACxC,KAAK,IAAI,IAAI,CAAC;CACf;AAUD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAG,cAAc,EACzD,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,EAC/B,OAAO,GAAE,qBAAqB,CAAC,OAAO,CAAM,GAC3C,qBAAqB,CAAC,OAAO,CAAC,CA0IhC"}
1
+ {"version":3,"file":"process-signals.d.ts","sourceRoot":"","sources":["../../src/server/process-signals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGlE;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GACzB,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,SAAS,GACT,SAAS,GACT,UAAU,CAAC;AAEf,wDAAwD;AACxD,MAAM,WAAW,cAAc,CAAC,OAAO,GAAG,cAAc;IACtD,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACvD;AAED,gEAAgE;AAChE,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AAE3E;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IACzD,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC1D;;;;;;;;OAQG;IACH,YAAY,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC;CAClD;AAED,MAAM,WAAW,qBAAqB,CAAC,OAAO,GAAG,cAAc;IAC7D;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;IAC3C,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,kEAAkE;IAClE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACpE,sFAAsF;IACtF,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,OAAO,GAAG,UAAU,KAAK,IAAI,CAAC;IACpF;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC3D;AAED,MAAM,WAAW,qBAAqB,CAAC,OAAO,GAAG,cAAc;IAC7D;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IAC/C,wCAAwC;IACxC,KAAK,IAAI,IAAI,CAAC;CACf;AAUD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAG,cAAc,EACzD,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,EAC/B,OAAO,GAAE,qBAAqB,CAAC,OAAO,CAAM,GAC3C,qBAAqB,CAAC,OAAO,CAAC,CAqKhC"}
@@ -1,6 +1,39 @@
1
1
  import type { AgentRuntimeStore } from '../agent-runtime/store';
2
+ /**
3
+ * What the scenario is about to touch, handed over before the first mutation.
4
+ *
5
+ * The kit used to pick its conversation identities *after* `createStore()`
6
+ * returned, which locked out exactly the adapters it exists to certify: a
7
+ * durable store whose runtime rows hang off an application-owned conversation
8
+ * row cannot serve the first admission, because nobody ever told it which
9
+ * parent to provision. Running the kit against the memory reference store
10
+ * instead proves the reducer — which is not the thing under test.
11
+ */
12
+ export interface AgentStoreConformanceContext {
13
+ /**
14
+ * Every conversation the scenario mutates, in the order it first touches
15
+ * them. Provision one parent per id before returning the store, and remove
16
+ * them again in `cleanup`.
17
+ */
18
+ readonly conversationIds: readonly string[];
19
+ }
2
20
  export interface AgentStoreConformanceConfig {
3
- createStore(): AgentRuntimeStore | Promise<AgentRuntimeStore>;
21
+ /**
22
+ * Build the store under test. The context arrives first so an adapter can
23
+ * provision fixture state; a factory that needs none may ignore it, and an
24
+ * existing zero-argument factory stays valid unchanged.
25
+ */
26
+ createStore(context: AgentStoreConformanceContext): AgentRuntimeStore | Promise<AgentRuntimeStore>;
27
+ /**
28
+ * Remove whatever `createStore` provisioned.
29
+ *
30
+ * Runs exactly once, after the scenario, whether it passed or failed — a kit
31
+ * that only cleans up on success leaks a row for every red run, which is the
32
+ * shape that makes a failing suite un-rerunnable. A failure here never
33
+ * replaces the scenario's own: the answer to "does this adapter conform" is
34
+ * not overwritten by the answer to "did the teardown work".
35
+ */
36
+ cleanup?(context: AgentStoreConformanceContext): void | Promise<void>;
4
37
  }
5
38
  /** Black-box contract shared by memory and third-party durable agent stores. */
6
39
  export declare function runAgentStoreConformance(config: AgentStoreConformanceConfig): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"agent-store-conformance.d.ts","sourceRoot":"","sources":["../../src/testing/agent-store-conformance.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,MAAM,WAAW,2BAA2B;IAC1C,WAAW,IAAI,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC/D;AAwCD,gFAAgF;AAChF,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,IAAI,CAAC,CAobf"}
1
+ {"version":3,"file":"agent-store-conformance.d.ts","sourceRoot":"","sources":["../../src/testing/agent-store-conformance.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE;;;;;;;;;GASG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,WAAW,CACT,OAAO,EAAE,4BAA4B,GACpC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAClD;;;;;;;;OAQG;IACH,OAAO,CAAC,CAAC,OAAO,EAAE,4BAA4B,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE;AAwCD,gFAAgF;AAChF,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,IAAI,CAAC,CA2Bf"}
package/dist/testing.d.ts CHANGED
@@ -32,7 +32,7 @@ export declare function createHandlerTestClients<T extends Record<string, Contra
32
32
  [P in keyof T]: ScopedHttpClient<T[P]['endpoints'], ScopedKeys<K>>;
33
33
  };
34
34
  export { type AgentRaceBarrier, type AgentRaceDriver, type AgentRaceTrace, type AgentRaceTraceEntry, createAgentRaceBarrier, createAgentRaceDriver, createAgentRaceTrace, } from './agent-runtime/testing';
35
- export { type AgentStoreConformanceConfig, runAgentStoreConformance, } from './testing/agent-store-conformance';
35
+ export { type AgentStoreConformanceConfig, type AgentStoreConformanceContext, runAgentStoreConformance, } from './testing/agent-store-conformance';
36
36
  export { type ManagedResourceConformanceConfig, type ManagedResourceConformanceControls, ManagedResourceConformanceError, type ManagedResourceConformanceFactoryInput, type ManagedResourceConformanceFixture, type ManagedResourceConformancePhase, ManagedResourceConformancePhaseSchema, type ManagedResourceConformanceScenario, type ManagedResourceConformanceScenarioId, ManagedResourceConformanceScenarioIdSchema, ManagedResourceConformanceScenarioSchema, type ManagedResourceConformanceTraceEntry, ManagedResourceConformanceTraceEntrySchema, type ManagedResourceConformanceTraceOutcome, ManagedResourceConformanceTraceOutcomeSchema, runManagedResourceConformance, } from './testing/managed-resource-conformance';
37
37
  export { assertSurfaceDiscovery, type ConformanceTransport, type CreateRealtimeProbeDriverConfig, createRealtimeProbeDriver, type DefineRealtimeProbeConfig, defineRealtimeProbe, type RealtimeDisconnectObservation, RealtimeDisconnectObservationSchema, type RealtimeProbeAdapter, type RealtimeProbeFixture, type RealtimeProbeScenario, type RealtimeRejectionObservation, RealtimeRejectionObservationSchema, type RunSurfaceProbesConfig, runSurfaceProbes, type SurfaceDiscoveryObservation, type SurfaceProbe, type SurfaceProbeDriver, type SurfaceRealtimeDiscoveryObservation, type SurfaceToolDiscoveryObservation, type TransportObservation, TransportObservationSchema, } from './testing/surface-conformance';
38
38
  export { assertSurfaceManifestSnapshot, buildSurfaceManifest, type IncompatibleSchemaPolicy, type McpSchemaValidationConfig, type SurfaceAgentProjection, type SurfaceManifest, type SurfaceManifestConfig, type SurfaceManifestExtension, SurfaceManifestExtensionSchema, type SurfaceManifestOperation, SurfaceManifestOperationSchema, type SurfaceManifestRealtimeEvent, SurfaceManifestRealtimeEventSchema, SurfaceManifestSchema, type SurfaceManifestTool, SurfaceManifestToolSchema, type SurfaceManifestToolSurface, SurfaceManifestToolSurfaceSchema, type SurfaceMcpPreparation, SurfaceRealtimeSchemaPairSchema, type SurfaceRuntimeToolDefinition, SurfaceSchemaDigestsSchema, type SurfaceToolDefinition, type SurfaceToolExtension, serializeSurfaceValue, } from './testing/surface-manifest';
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
package/dist/testing.js CHANGED
@@ -4,7 +4,8 @@ import {
4
4
  } from "./index-fhsmrzj7.js";
5
5
  import {
6
6
  createApplication
7
- } from "./index-eabpd4tb.js";
7
+ } from "./index-physbf4z.js";
8
+ import"./index-2k4yrqkc.js";
8
9
  import"./index-8eywc9zv.js";
9
10
  import {
10
11
  RealtimeRequestDisconnectedError,
@@ -141,8 +142,33 @@ function requireOutcome(actual, expected) {
141
142
  }
142
143
  }
143
144
  async function runAgentStoreConformance(config) {
144
- const store = await config.createStore();
145
- const conversationId = `conformance-${crypto.randomUUID()}`;
145
+ const run = `conformance-${crypto.randomUUID()}`;
146
+ const context = {
147
+ conversationIds: [run, `${run}-recovery`, `${run}-absorb`]
148
+ };
149
+ const store = await config.createStore(context);
150
+ let failure;
151
+ try {
152
+ await conformanceScenario(store, context.conversationIds);
153
+ } catch (error) {
154
+ failure = error;
155
+ }
156
+ try {
157
+ await config.cleanup?.(context);
158
+ } catch (cleanupError) {
159
+ if (failure === undefined)
160
+ throw cleanupError;
161
+ throw new AggregateError([failure, cleanupError], `${failure instanceof Error ? failure.message : String(failure)} (cleanup also failed: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)})`);
162
+ }
163
+ if (failure !== undefined)
164
+ throw failure;
165
+ }
166
+ async function conformanceScenario(store, conversationIds) {
167
+ const [conversationId, recoveryConversationId, absorbConversationId] = conversationIds;
168
+ if (!conversationId || !recoveryConversationId || !absorbConversationId) {
169
+ throw new Error("Agent store conformance requires three conversation identities");
170
+ }
171
+ const absentConversationId = `${conversationId}-absent`;
146
172
  const firstInput = userMessage(conversationId, "input-1");
147
173
  const firstRun = queuedRun(conversationId, firstInput.id, "run-1");
148
174
  const accepted = await store.acceptInputAndAssignRun({
@@ -373,7 +399,7 @@ async function runAgentStoreConformance(config) {
373
399
  if (await store.loadRun({ conversationId, runId: "no-such-run" })) {
374
400
  throw new Error("loadRun must return undefined for an unknown run");
375
401
  }
376
- if (await store.loadRun({ conversationId: "no-such-conversation", runId: running.id })) {
402
+ if (await store.loadRun({ conversationId: absentConversationId, runId: running.id })) {
377
403
  throw new Error("loadRun must not cross conversation boundaries");
378
404
  }
379
405
  const activeRuns = await store.listActiveRuns(conversationId);
@@ -393,7 +419,7 @@ async function runAgentStoreConformance(config) {
393
419
  throw new Error("listActiveRuns must order by createdAt and then by id");
394
420
  }
395
421
  }
396
- if ((await store.listActiveRuns("no-such-conversation")).length !== 0) {
422
+ if ((await store.listActiveRuns(absentConversationId)).length !== 0) {
397
423
  throw new Error("listActiveRuns must be empty for an unknown conversation");
398
424
  }
399
425
  const recoverable = await store.scanRecoverable({ limit: 10 });
@@ -488,7 +514,6 @@ async function runAgentStoreConformance(config) {
488
514
  if (duplicateTerminal.run.terminalReason !== "success" || JSON.stringify(duplicateTerminal.assistant) !== JSON.stringify(terminalAssistant)) {
489
515
  throw new Error("Compaction discarded the canonical duplicate terminal result");
490
516
  }
491
- const recoveryConversationId = `${conversationId}-recovery`;
492
517
  const recoveryInput = userMessage(recoveryConversationId, "recovery-input");
493
518
  const recoveryRun = queuedRun(recoveryConversationId, recoveryInput.id, "recovery-run");
494
519
  const recoveryAccepted = await store.acceptInputAndAssignRun({
@@ -522,10 +547,9 @@ async function runAgentStoreConformance(config) {
522
547
  if (terminalRun?.state !== "abandoned" || terminalMessage?.status !== "failed") {
523
548
  throw new Error("Abandon recovery did not atomically terminalize its assistant record");
524
549
  }
525
- await assertAbsorptionIsAtomic(store);
550
+ await assertAbsorptionIsAtomic(store, absorbConversationId);
526
551
  }
527
- async function assertAbsorptionIsAtomic(store) {
528
- const conversationId = `conformance-absorb-${crypto.randomUUID()}`;
552
+ async function assertAbsorptionIsAtomic(store, conversationId) {
529
553
  const leadInput = userMessage(conversationId, "absorb-input-1");
530
554
  const leadRun = queuedRun(conversationId, leadInput.id, "absorb-run-1");
531
555
  const lead = await store.acceptInputAndAssignRun({
package/llms-full.txt CHANGED
@@ -58,9 +58,9 @@ own, recorded as an ADR.
58
58
  | `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the store and managed-resource conformance kits |
59
59
  | `stitchkit/declaration` | build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
60
60
  | `stitchkit/react` | browser | stable | `createCursorQuery`, `createCacheBridge` |
61
- | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 9 of the 11 minors since 0.56.2, most recently 0.66.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
61
+ | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 9 of the 12 minors since 0.56.2, most recently 0.66.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
62
62
  | `stitchkit/agent-runtime/openrouter` | server | evolving | isolated OpenRouter language-model adapter |
63
- | `stitchkit/application` | server | evolving | managed resource graph, readiness, admission, schedules and bounded shutdown |
63
+ | `stitchkit/application` | server | evolving<br>_redefined in 3 of the 12 minors since 0.56.2, most recently 0.67.0_ | managed resource graph, readiness, admission, schedules and bounded shutdown |
64
64
  | `stitchkit/application/grammy` | server | evolving | isolated grammY polling and webhook lifecycle adapters |
65
65
  | `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
66
66
 
@@ -4171,8 +4171,14 @@ outbox.
4171
4171
  `stitchkit/testing` exports `createAgentRaceBarrier`, `createAgentRaceDriver` and
4172
4172
  `createAgentRaceTrace`. Barriers have bounded teardown, traces assert exact partial order, and the
4173
4173
  helpers are exercised from packed Bun and Node consumers. `runAgentStoreConformance` runs duplicate,
4174
- coalescing, collision, stale checkpoint, replay safety, terminal race, compaction and recovery
4175
- invariants against any fresh durable adapter.
4174
+ coalescing, collision, stale checkpoint, replay safety, terminal race, absorption, bounded reads,
4175
+ compaction and recovery invariants against any fresh durable adapter.
4176
+
4177
+ It picks its conversation identities itself and passes them to `createStore(context)` **before the
4178
+ first mutation**, so an adapter whose runtime rows reference an application-owned conversation row
4179
+ can provision those parents; the optional `cleanup(context)` removes them again, and runs once
4180
+ whether the scenario passed or failed. A factory that owns no fixture state ignores the argument and
4181
+ keeps working unchanged.
4176
4182
 
4177
4183
 
4178
4184
  ==============================================================================
@@ -4237,17 +4243,24 @@ const cleanup = createManagedSchedule({
4237
4243
  run: ({ signal }) => removeExpiredRecords(signal),
4238
4244
  })
4239
4245
 
4246
+ const http = managedServerResource({
4247
+ id: 'http',
4248
+ dependsOn: [database],
4249
+ // Called during `start`, after `database` is ready — which is how "bind the
4250
+ // port once the database is up" is expressed. Pass an already-running server
4251
+ // instead when the application created it itself.
4252
+ server: () => createServer({ port: env.PORT, services }),
4253
+ })
4254
+
4240
4255
  const app = createApplication({
4241
4256
  id: 'service',
4242
- resources: [
4243
- database,
4244
- managedServerResource({ id: 'http', server, dependsOn: ['database'] }),
4245
- cleanup,
4246
- ],
4257
+ resources: [database, http, cleanup],
4258
+ // One number for how long this application may take to stop. `shutdown()`
4259
+ // with no options and the signal path both spend it.
4260
+ shutdown: { gracePeriodMs: 30_000, forceTimeoutMs: 5_000 },
4247
4261
  })
4248
4262
 
4249
4263
  const signals = bindProcessSignals(app, {
4250
- shutdown: { gracePeriodMs: 30_000, forceTimeoutMs: 5_000 },
4251
4264
  onComplete: (result) => {
4252
4265
  process.exitCode = result.outcome === 'clean' ? 0 : 1
4253
4266
  },
@@ -4257,9 +4270,10 @@ await app.start()
4257
4270
  ```
4258
4271
 
4259
4272
  The exact resource callbacks are typed by their public configuration. The
4260
- important ownership rule is stable: the application creates and configures the
4261
- database/server/provider objects; Stitchkit orders their process-local
4262
- lifecycle.
4273
+ important ownership rule is stable: the application decides what the
4274
+ database/server/provider objects are and how they are configured; Stitchkit
4275
+ orders their process-local lifecycle and decides *when* they come into
4276
+ existence.
4263
4277
 
4264
4278
  ## Resource authoring
4265
4279
 
@@ -4298,6 +4312,40 @@ Invoking `start` makes the descriptor rollback-eligible immediately. Its
4298
4312
  before returning a runtime handle. The kernel calls cleanup once and continues
4299
4313
  cleaning other attempted resources even if one close fails.
4300
4314
 
4315
+ ### Handing a resource to the resources that depend on it
4316
+
4317
+ `dependsOn` says *when*. To say *what*, return a `value` from `start` and read
4318
+ it with `context.use(...)`:
4319
+
4320
+ ```ts
4321
+ const database = defineManagedResource({
4322
+ id: 'database',
4323
+ start: async ({ signal }) => ({ value: await connect(env.DATABASE_URL, signal) }),
4324
+ })
4325
+
4326
+ const worker = defineManagedResource({
4327
+ id: 'worker',
4328
+ dependsOn: [database],
4329
+ start: (context) => {
4330
+ const db = context.use(database) // Connection — not Connection | null
4331
+ return { completion: consume(db, context.signal) }
4332
+ },
4333
+ })
4334
+ ```
4335
+
4336
+ Declare the dependency with the **resource**, not its id, whenever you intend to
4337
+ read from it: that is the form `use` can type, and it keeps the declaration and
4338
+ the read from drifting apart. A string still works when all you need is order.
4339
+
4340
+ The value is published when `start` resolves and stays readable for the rest of
4341
+ the application's life — from `activate` and from the shutdown phases too, where
4342
+ a dependant may still need the handle it was given.
4343
+
4344
+ `use` refuses two things, both loudly: a resource that was never declared in
4345
+ `dependsOn` (it happens to work whenever declaration order is lucky), and a
4346
+ resource that published nothing. The second is refused by the compiler as well —
4347
+ reading a value off a resource with no `value` in its `start` does not type.
4348
+
4301
4349
  ## Readiness and health
4302
4350
 
4303
4351
  `await app.start()` resolves only after every required resource is ready and
@@ -4492,6 +4540,15 @@ Do not add a second `process.on('SIGTERM')` handler around the application.
4492
4540
  `bindProcessSignals(app)` is the force/escalation owner. Exit code and hard-exit
4493
4541
  policy remain application/supervisor choices.
4494
4542
 
4543
+ **Where the budget comes from on the signal path.** `bindProcessSignals(app)`
4544
+ with no `shutdown` forwards nothing, so the application spends the budget it
4545
+ declared in `createApplication({ shutdown })`. Pass `shutdown` to the binding
4546
+ only to override that budget for signals specifically; passing one key overrides
4547
+ that key alone and leaves the other at the declaration. Declare the budget once,
4548
+ on the application — repeating it in both places is how the two numbers start to
4549
+ disagree, and the operator's supervisor timeout is calculated from the one they
4550
+ can read.
4551
+
4495
4552
  ## Managed schedules
4496
4553
 
4497
4554
  Schedules activate only after top-level readiness:
@@ -4747,6 +4804,65 @@ asynchronous subscriber callback racing cleanup: publish `getSnapshot()` before
4747
4804
  closing the outer sink, so any older or duplicate late delivery is rejected as
4748
4805
  stale instead of dropping the final state.
4749
4806
 
4807
+ ## Handing a handle to the resources that depend on it
4808
+
4809
+ `dependsOn` carries ordering. To carry the object as well, return a `value` from
4810
+ `start` and read it with `context.use(...)`. This replaces the module-local
4811
+ `let handle: T | null` with the guard the graph makes unreachable.
4812
+
4813
+ ```ts
4814
+ const database = defineManagedResource({
4815
+ id: 'database',
4816
+ start: async () => ({ value: await connect(env.DATABASE_URL) }),
4817
+ })
4818
+
4819
+ const worker = defineManagedResource({
4820
+ id: 'worker',
4821
+ dependsOn: [database],
4822
+ start(context) {
4823
+ const db = context.use(database) // Connection — not Connection | null
4824
+ },
4825
+ })
4826
+ ```
4827
+
4828
+ Declare the dependency with the **resource** whenever you intend to read from
4829
+ it: that is the form `use` can type, and it stops the declaration and the read
4830
+ from drifting. A string still expresses order on its own.
4831
+
4832
+ The value is published when `start` resolves and stays readable from `activate`
4833
+ and from the shutdown phases. `use` refuses a resource that was not declared in
4834
+ `dependsOn` — that only ever worked by luck of declaration order — and refuses a
4835
+ resource that published nothing, which the compiler refuses too. The executable
4836
+ recipe proves both the read and the refusal.
4837
+
4838
+ ## Managed HTTP server
4839
+
4840
+ The main resource of a web backend, and the one whose ownership rule is
4841
+ counter-intuitive: give `managedServerResource` a **thunk** and it creates the
4842
+ server during `start`, after its dependencies are ready. Give it an
4843
+ already-listening server and it adopts that one instead.
4844
+
4845
+ ```ts
4846
+ const http = managedServerResource({
4847
+ id: 'http',
4848
+ dependsOn: [database],
4849
+ server: () => createServer({ port: env.PORT, services }),
4850
+ })
4851
+
4852
+ const app = createApplication({ id: 'service', resources: [database, http] })
4853
+ await app.start() // the port is bound; the snapshot means it
4854
+ ```
4855
+
4856
+ The server resource publishes its `ManagedServerHandle`, so anything that needs
4857
+ the running server — a Socket.IO attachment, a URL to log, a probe — reads it
4858
+ with `context.use(http)` rather than through a module-local.
4859
+
4860
+ Do not spread this resource over your own `start` to control creation order:
4861
+ that shape exists only as a workaround for the version whose `start` was empty,
4862
+ and `dependsOn` now expresses the order directly. The executable recipe binds a
4863
+ real port, proves a request reaches it after `start()` resolves, and proves the
4864
+ port is closed after `shutdown()`.
4865
+
4750
4866
  ## Deletion checklist
4751
4867
 
4752
4868
  After the cutover, remove the old generic lifecycle path completely:
@@ -7856,9 +7972,28 @@ Prisma adapter, an in-memory one, anything — run the conformance kit against i
7856
7972
  ```ts
7857
7973
  import { runAgentStoreConformance } from 'stitchkit/testing'
7858
7974
 
7859
- await runAgentStoreConformance({ store: yourStore, conversationId: 'conformance' })
7975
+ await runAgentStoreConformance({ createStore: () => yourStore })
7976
+ ```
7977
+
7978
+ The kit picks the conversation identities itself and hands them over **before**
7979
+ the first mutation, so a store whose runtime rows hang off an application-owned
7980
+ conversation row can provision the parents — and take them away again:
7981
+
7982
+ ```ts
7983
+ await runAgentStoreConformance({
7984
+ createStore: (context) => {
7985
+ for (const conversationId of context.conversationIds) createConversationRow(conversationId)
7986
+ return yourStore
7987
+ },
7988
+ cleanup: (context) => {
7989
+ for (const conversationId of context.conversationIds) deleteConversationRow(conversationId)
7990
+ },
7991
+ })
7860
7992
  ```
7861
7993
 
7994
+ `cleanup` runs exactly once, after the scenario, whether it passed or failed —
7995
+ and a failure in it never replaces the scenario's own.
7996
+
7862
7997
  Green before and red after tells you the contract grew and where, in one run,
7863
7998
  instead of one failure at a time in production. Green both times means the
7864
7999
  upgrade owes you nothing on that surface — which is the usual answer if you
@@ -7903,6 +8038,136 @@ implement `AgentRuntimeStoreDriver` and compose the aggregate with
7903
8038
  runtime): bootstrap the server, one HTTP request, and any feature you rely on
7904
8039
  (Socket.IO connect, an MCP tool call, a multipart upload, …).
7905
8040
 
8041
+ ## Released migration: 0.67.0
8042
+
8043
+ Three application-kernel changes. Two of them fix silent failures, so the most
8044
+ important part of this migration is not what the compiler points at — it is the
8045
+ two things that change with no compile error at all: **when your server is
8046
+ created**, and **which shutdown budget a signal spends**.
8047
+
8048
+ ### If you gave `managedServerResource` a thunk
8049
+
8050
+ It is now called during `start`, after the resource's dependencies are ready —
8051
+ the reading the type always suggested. Before, it was called on the way *down*,
8052
+ so `app.start()` resolved, the snapshot said `healthy` and `ready`, and nothing
8053
+ was bound to the port.
8054
+
8055
+ If your application looked like this, it was never listening:
8056
+
8057
+ ```ts
8058
+ // before — resolves clean, listens on nothing
8059
+ managedServerResource({ id: 'http', dependsOn: ['database'], server: () => createServer(config) })
8060
+ ```
8061
+
8062
+ Nothing to change: the same code now binds the port. Check your startup logs for
8063
+ a healthy report you never actually verified with a request.
8064
+
8065
+ If you used the spread workaround — this resource's phases over your own
8066
+ `start` — it keeps working, and you can now delete it:
8067
+
8068
+ ```ts
8069
+ // before
8070
+ let handle: ManagedServerHandle<T> | null = null
8071
+ const shutdown = managedServerResource({ id: 'http', server: () => handle! })
8072
+ const http = defineManagedResource({
8073
+ ...shutdown,
8074
+ dependsOn: ['database', 'socket-io'],
8075
+ start: () => { handle = createServer(config) },
8076
+ })
8077
+
8078
+ // after
8079
+ const http = managedServerResource({
8080
+ id: 'http',
8081
+ dependsOn: [database, socketIo],
8082
+ server: () => createServer(config),
8083
+ })
8084
+ ```
8085
+
8086
+ An already-created handle is adopted exactly as before.
8087
+
8088
+ ### If you passed `shutdown` to `bindProcessSignals`
8089
+
8090
+ `bindProcessSignals` used to fill in the schema's defaults for every budget you
8091
+ omitted, which made `createApplication({ shutdown })` unreachable on the signal
8092
+ path — the one path production stops through. An application declaring five
8093
+ seconds took thirty-five.
8094
+
8095
+ Declare the budget once, on the application, and delete the repetition:
8096
+
8097
+ ```ts
8098
+ // before — the same two numbers in two places, and only one of them applied
8099
+ const app = createApplication({ id: 'svc', resources, shutdown: SHUTDOWN_BUDGET })
8100
+ bindProcessSignals(app, { shutdown: SHUTDOWN_BUDGET })
8101
+
8102
+ // after
8103
+ const app = createApplication({ id: 'svc', resources, shutdown: SHUTDOWN_BUDGET })
8104
+ bindProcessSignals(app)
8105
+ ```
8106
+
8107
+ **Check this even if you change nothing.** If you declared a budget on the
8108
+ application and did not repeat it on the binding, your process has been stopping
8109
+ on 30 s / 5 s and will now stop on what you declared. Compare it against the
8110
+ supervisor timeout that watches it — `kill_timeout`, `TimeoutStopSec` — because
8111
+ that number was probably calculated from the declaration.
8112
+
8113
+ Passing `shutdown` to the binding still works and still wins, key by key: one
8114
+ key overrides that key alone and leaves the other at the declaration.
8115
+
8116
+ ### If you read `resource.dependsOn`
8117
+
8118
+ Its type widened from `readonly string[]` to `readonly ManagedResourceDependency[]`
8119
+ — `string | ManagedResource` — so a dependency can be declared as the resource
8120
+ itself. Declaring stays compatible; reading needs one call:
8121
+
8122
+ ```ts
8123
+ // before
8124
+ const ids: readonly string[] = resource.dependsOn ?? []
8125
+
8126
+ // after
8127
+ import { managedResourceDependencyId } from 'stitchkit/application'
8128
+ const ids = (resource.dependsOn ?? []).map(managedResourceDependencyId)
8129
+ ```
8130
+
8131
+ ### If you thread a handle through a module-local
8132
+
8133
+ This is the pattern the change exists to remove, and it is opt-in — nothing
8134
+ breaks if you keep yours:
8135
+
8136
+ ```ts
8137
+ // before
8138
+ let socket: SocketHandle | null = null
8139
+ const socketIo = defineManagedResource({
8140
+ id: 'socket-io',
8141
+ start: async () => { socket = await createSocketServer(config) },
8142
+ })
8143
+ const http = defineManagedResource({
8144
+ id: 'http',
8145
+ dependsOn: ['socket-io'],
8146
+ start: () => {
8147
+ if (!socket) throw new Error('socket is not initialized') // unreachable
8148
+ server = createServer({ socket })
8149
+ },
8150
+ })
8151
+
8152
+ // after
8153
+ const socketIo = defineManagedResource({
8154
+ id: 'socket-io',
8155
+ start: async () => ({ value: await createSocketServer(config) }),
8156
+ })
8157
+ const http = defineManagedResource({
8158
+ id: 'http',
8159
+ dependsOn: [socketIo],
8160
+ start: (context) => {
8161
+ server = createServer({ socket: context.use(socketIo) }) // SocketHandle
8162
+ },
8163
+ })
8164
+ ```
8165
+
8166
+ Declare the dependency with the **resource** when you intend to read from it —
8167
+ that is the form `use` can type. `use` refuses a resource missing from
8168
+ `dependsOn`, and refuses one that published no value; the second refusal is a
8169
+ compile error too.
8170
+
7906
8171
  ## Released migration: 0.66.0
7907
8172
 
7908
8173
  Three changes, and only one of them is a feature. The other two are shapes that
@@ -10559,14 +10824,19 @@ cutovers are covered by the executable
10559
10824
  | `ApplicationShutdownBudgetSchema` / `ApplicationShutdownBudget` | schema / _type_ | the same two budgets without a signal — `ApplicationConfig.shutdown`, the default for `shutdown()` and the only budget a failed startup's rollback can read |
10560
10825
  | `ActivityTokenBrand` | const | the brand symbol `ActivityToken` carries, exported so `ActivityProjection` is implementable |
10561
10826
  | `defineManagedResource` | function | retain the exact typed resource declaration; every invoked start is rollback-eligible |
10562
- | `managedServerResource` | function | adapt an existing managed server without copying its HTTP/WebSocket shutdown machine |
10827
+ | `managedServerResource` | function | own a managed server's lifecycle without copying its HTTP/WebSocket shutdown machine — a thunk is called during `start`, and the handle is published to dependants |
10828
+ | `managedResourceDependencyId` | function | the id of a dependency declared either as a string or as the resource itself |
10563
10829
  | `createApplicationHealthHandler` | function | build a Fetch-clean liveness or readiness response from the sanitized application snapshot |
10564
10830
  | `createApplicationOperationalHandlers` | function | compose always-readable status plus the canonical readiness/liveness handlers |
10565
10831
  | `ApplicationAdmissionError` | class | stable `APPLICATION_NOT_ACCEPTING` rejection from `admission.run(...)` |
10566
10832
  | `ApplicationConfig` / `ApplicationHandle` | _type_ | application declaration and its start/snapshot/subscription/admission/shutdown handle |
10567
10833
  | `ApplicationAdmission` / `ApplicationOperationLease` | _type_ | atomic process-local admission and idempotent release primitive |
10568
- | `ManagedResource` / `ManagedResourceContext` / `ManagedResourceStartResult` | _type_ | resource lifecycle callbacks, shared deadlines, health reporting and separate readiness/completion promises |
10569
- | `ManagedServerResourceConfig` | _type_ | existing managed server, stable ID, dependencies and policy for `managedServerResource` |
10834
+ | `ManagedResource` / `ManagedResourceContext` / `ManagedResourceStartResult` | _type_ | resource lifecycle callbacks, shared deadlines, health reporting, separate readiness/completion promises and the value a resource publishes to its dependants |
10835
+ | `ManagedResourceDependency` | _type_ | a dependency named by id or given as the resource itself — the second form is what `context.use(...)` can type |
10836
+ | `ManagedResourcePublished` | _type_ | the value type `context.use(resource)` returns, recovered from that resource's own `start` |
10837
+ | `ManagedResourcePublishesNoValue` | _type_ | what `context.use(...)` returns for a resource that publishes nothing — a branded refusal rather than `never`, so reading it does not silently compile |
10838
+ | `ManagedServerResourceConfig` | _type_ | the server or a thunk that makes one, stable ID, dependencies and policy for `managedServerResource` |
10839
+ | `ManagedServerResource` | _type_ | the resource `managedServerResource` returns, whose `start` publishes the `ManagedServerHandle` |
10570
10840
  | `ApplicationHealthHandlerOptions` / `ApplicationHealthHandlerOptionsSchema` | _type_ / schema | liveness/readiness selection and sanitized `Retry-After` policy |
10571
10841
  | `ApplicationOperationalHandlers` / `ApplicationOperationalHandlersOptions` / `ApplicationOperationalHandlersOptionsSchema` | _type_ / schema | conventional status/readiness/liveness route surface and shared retry policy |
10572
10842
 
@@ -11126,7 +11396,8 @@ handler pipeline without opening a TCP port.
11126
11396
  | `createHandlerTestClient` | function | one contract client backed by an in-process `FetchHandler` |
11127
11397
  | `createHandlerTestClients` | function | exact contract-registry batch form |
11128
11398
  | `runAgentStoreConformance` | function | reusable black-box duplicate/coalescing/stale/recovery contract for durable agent-store adapters |
11129
- | `AgentStoreConformanceConfig` | _type_ | factory configuration for running the same contract against a fresh adapter |
11399
+ | `AgentStoreConformanceConfig` | _type_ | `{ createStore(context), cleanup?(context) }` the factory the contract runs against, plus a teardown that runs once whether the scenario passed or failed |
11400
+ | `AgentStoreConformanceContext` | _type_ | `{ conversationIds }` — every conversation the scenario will mutate, handed over **before** the first mutation so an adapter can provision application-owned parent rows; a zero-argument factory stays valid |
11130
11401
  | `runManagedResourceConformance` | function | run the canonical deterministic lifecycle matrix against a fresh consumer-owned `ManagedResource` fixture; resolves `void` or throws `ManagedResourceConformanceError` with a stable scenario ID and normalized trace |
11131
11402
  | `ManagedResourceConformanceScenarioIdSchema` / `ManagedResourceConformanceScenarioId` | schema / _type_ | stable clean, rollback, readiness/completion, activation, shutdown-race and forced-cleanup scenario vocabulary |
11132
11403
  | `ManagedResourceConformanceScenarioSchema` / `ManagedResourceConformanceScenario` | schema / _type_ | discriminated scenario record including whether the controlled resource is required |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.66.0",
3
+ "version": "0.67.0",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",
@@ -1,6 +0,0 @@
1
- // src/application/resource.ts
2
- function defineManagedResource(resource) {
3
- return resource;
4
- }
5
-
6
- export { defineManagedResource };