stitchkit 0.66.1 → 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":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;;;GAOG;AACH,MAAM,MAAM,yBAAyB,GAAG,MAAM,GAAG,eAAe,CAAC;AAEjE;;;;;;;;GAQG;AACH,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,uDAAuD,EAAE,KAAK,CAAC;CACzE;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,wBAAwB,CAAC,SAAS,IAAI,SAAS,SAAS;IAClE,KAAK,CAAC,GAAG,IAAI,EAAE,KAAK,GAAG,MAAM,MAAM,CAAC;CACrC,GACG,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAC5D,+BAA+B,GAC/B,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,MAAM,CAAA;CAAE,GAC1E,MAAM,GACN,+BAA+B,GACnC,+BAA+B,CAAC;AAEpC,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;IAClE;;;;;;;;;;;;OAYG;IACH,GAAG,CAAC,SAAS,SAAS,eAAe,EACnC,QAAQ,EAAE,SAAS,GAClB,wBAAwB,CAAC,SAAS,CAAC,CAAC;CACxC;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;IACpC;;;;;;;OAOG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC1D,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,kDAAkD;AAClD,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,yBAAyB,GAAG,MAAM,CAEzF;AAED,wBAAgB,qBAAqB,CAAC,KAAK,CAAC,SAAS,SAAS,eAAe,EAC3E,QAAQ,EAAE,SAAS,GAClB,SAAS,CAEX"}
@@ -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"}
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,
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
 
@@ -4243,17 +4243,24 @@ const cleanup = createManagedSchedule({
4243
4243
  run: ({ signal }) => removeExpiredRecords(signal),
4244
4244
  })
4245
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
+
4246
4255
  const app = createApplication({
4247
4256
  id: 'service',
4248
- resources: [
4249
- database,
4250
- managedServerResource({ id: 'http', server, dependsOn: ['database'] }),
4251
- cleanup,
4252
- ],
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 },
4253
4261
  })
4254
4262
 
4255
4263
  const signals = bindProcessSignals(app, {
4256
- shutdown: { gracePeriodMs: 30_000, forceTimeoutMs: 5_000 },
4257
4264
  onComplete: (result) => {
4258
4265
  process.exitCode = result.outcome === 'clean' ? 0 : 1
4259
4266
  },
@@ -4263,9 +4270,10 @@ await app.start()
4263
4270
  ```
4264
4271
 
4265
4272
  The exact resource callbacks are typed by their public configuration. The
4266
- important ownership rule is stable: the application creates and configures the
4267
- database/server/provider objects; Stitchkit orders their process-local
4268
- 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.
4269
4277
 
4270
4278
  ## Resource authoring
4271
4279
 
@@ -4304,6 +4312,40 @@ Invoking `start` makes the descriptor rollback-eligible immediately. Its
4304
4312
  before returning a runtime handle. The kernel calls cleanup once and continues
4305
4313
  cleaning other attempted resources even if one close fails.
4306
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
+
4307
4349
  ## Readiness and health
4308
4350
 
4309
4351
  `await app.start()` resolves only after every required resource is ready and
@@ -4498,6 +4540,15 @@ Do not add a second `process.on('SIGTERM')` handler around the application.
4498
4540
  `bindProcessSignals(app)` is the force/escalation owner. Exit code and hard-exit
4499
4541
  policy remain application/supervisor choices.
4500
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
+
4501
4552
  ## Managed schedules
4502
4553
 
4503
4554
  Schedules activate only after top-level readiness:
@@ -4753,6 +4804,65 @@ asynchronous subscriber callback racing cleanup: publish `getSnapshot()` before
4753
4804
  closing the outer sink, so any older or duplicate late delivery is rejected as
4754
4805
  stale instead of dropping the final state.
4755
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
+
4756
4866
  ## Deletion checklist
4757
4867
 
4758
4868
  After the cutover, remove the old generic lifecycle path completely:
@@ -7928,6 +8038,136 @@ implement `AgentRuntimeStoreDriver` and compose the aggregate with
7928
8038
  runtime): bootstrap the server, one HTTP request, and any feature you rely on
7929
8039
  (Socket.IO connect, an MCP tool call, a multipart upload, …).
7930
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
+
7931
8171
  ## Released migration: 0.66.0
7932
8172
 
7933
8173
  Three changes, and only one of them is a feature. The other two are shapes that
@@ -10584,14 +10824,19 @@ cutovers are covered by the executable
10584
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 |
10585
10825
  | `ActivityTokenBrand` | const | the brand symbol `ActivityToken` carries, exported so `ActivityProjection` is implementable |
10586
10826
  | `defineManagedResource` | function | retain the exact typed resource declaration; every invoked start is rollback-eligible |
10587
- | `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 |
10588
10829
  | `createApplicationHealthHandler` | function | build a Fetch-clean liveness or readiness response from the sanitized application snapshot |
10589
10830
  | `createApplicationOperationalHandlers` | function | compose always-readable status plus the canonical readiness/liveness handlers |
10590
10831
  | `ApplicationAdmissionError` | class | stable `APPLICATION_NOT_ACCEPTING` rejection from `admission.run(...)` |
10591
10832
  | `ApplicationConfig` / `ApplicationHandle` | _type_ | application declaration and its start/snapshot/subscription/admission/shutdown handle |
10592
10833
  | `ApplicationAdmission` / `ApplicationOperationLease` | _type_ | atomic process-local admission and idempotent release primitive |
10593
- | `ManagedResource` / `ManagedResourceContext` / `ManagedResourceStartResult` | _type_ | resource lifecycle callbacks, shared deadlines, health reporting and separate readiness/completion promises |
10594
- | `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` |
10595
10840
  | `ApplicationHealthHandlerOptions` / `ApplicationHealthHandlerOptionsSchema` | _type_ / schema | liveness/readiness selection and sanitized `Retry-After` policy |
10596
10841
  | `ApplicationOperationalHandlers` / `ApplicationOperationalHandlersOptions` / `ApplicationOperationalHandlersOptionsSchema` | _type_ / schema | conventional status/readiness/liveness route surface and shared retry policy |
10597
10842
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.66.1",
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 };