tsdkarc 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/index.d.ts +65 -103
- package/esm/index.js +8 -5
- package/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/lib/index.d.ts +65 -103
- package/lib/index.js +8 -5
- package/package.json +1 -1
package/esm/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Minimal module lifecycle manager.
|
|
5
5
|
*
|
|
6
|
-
* Exports: Module, defineModule, ContextWriter,
|
|
6
|
+
* Exports: Module, defineModule, ContextWriter, LifecycleHooks, StartOptions, start.
|
|
7
7
|
*
|
|
8
8
|
* Type helpers (internal):
|
|
9
9
|
* UnionToIntersection<U> — U1 | U2 | U3 → U1 & U2 & U3
|
|
@@ -61,24 +61,14 @@ export interface LifecycleHooks<S extends object = Record<never, never>> {
|
|
|
61
61
|
/** Called once after the last module has finished shutting down. */
|
|
62
62
|
afterShutdown?(ctx: ContextWriter<S>): Promise<void> | void;
|
|
63
63
|
/** Called before each individual module boots, in boot order. */
|
|
64
|
-
beforeEachBoot?(ctx: ContextWriter<S>,
|
|
65
|
-
/** The module about to boot. */
|
|
66
|
-
module: Module<any>): Promise<void> | void;
|
|
64
|
+
beforeEachBoot?(ctx: ContextWriter<S>, module: Module<any>): Promise<void> | void;
|
|
67
65
|
/** Called after each individual module finishes booting, in boot order. */
|
|
68
|
-
afterEachBoot?(ctx: ContextWriter<S>,
|
|
69
|
-
/** The module that just finished booting. */
|
|
70
|
-
module: Module<any>): Promise<void> | void;
|
|
66
|
+
afterEachBoot?(ctx: ContextWriter<S>, module: Module<any>): Promise<void> | void;
|
|
71
67
|
/** Called before each individual module shuts down, in shutdown order. */
|
|
72
|
-
beforeEachShutdown?(ctx: ContextWriter<S>,
|
|
73
|
-
/** The module about to shut down. */
|
|
74
|
-
module: Module<any>): Promise<void> | void;
|
|
68
|
+
beforeEachShutdown?(ctx: ContextWriter<S>, module: Module<any>): Promise<void> | void;
|
|
75
69
|
/** Called after each individual module finishes shutting down, in shutdown order. */
|
|
76
|
-
afterEachShutdown?(ctx: ContextWriter<S>,
|
|
77
|
-
|
|
78
|
-
module: Module<any>): Promise<void> | void;
|
|
79
|
-
onError?(error: Error, ctx: ContextWriter<S>,
|
|
80
|
-
/** The module that just finished shutting down. */
|
|
81
|
-
module: Module<any>): Promise<void> | void;
|
|
70
|
+
afterEachShutdown?(ctx: ContextWriter<S>, module: Module<any>): Promise<void> | void;
|
|
71
|
+
onError?(error: Error, ctx: ContextWriter<S>, module: Module<any>): Promise<void> | void;
|
|
82
72
|
}
|
|
83
73
|
/**
|
|
84
74
|
* Module<S, Sl>
|
|
@@ -102,64 +92,38 @@ export interface Module<S extends object, Sl extends object = S> {
|
|
|
102
92
|
afterShutdown?(ctx: ContextWriter<S, Sl>): Promise<void> | void;
|
|
103
93
|
}
|
|
104
94
|
/**
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export const moduleA = defineModule<AContext>()({
|
|
111
|
-
name: "A",
|
|
112
|
-
modules: [] as const,
|
|
113
|
-
boot: () => ({ value: "module:A" }),
|
|
114
|
-
async shutdown(ctx) {
|
|
115
|
-
console.log(`shutdown ${ctx.value}`);
|
|
116
|
-
},
|
|
117
|
-
});
|
|
118
|
-
// Get context type by module
|
|
119
|
-
type AContext2 = ContextOf<typeof moduleA> // same as above `AContext`
|
|
95
|
+
* Extracts the full context type from a module.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* type AContext = ContextOf<typeof moduleA> // { value: string }
|
|
120
99
|
*/
|
|
121
100
|
export type ContextOf<M> = M extends Module<infer Full, any> ? {
|
|
122
101
|
[K in keyof Full]: Full[K];
|
|
123
102
|
} : never;
|
|
124
|
-
/** Get
|
|
103
|
+
/** Get ContextWriter type by typeof module. */
|
|
125
104
|
export type ContextWriterOf<M> = ContextWriter<M extends Module<any, infer Ctx> ? Ctx : never>;
|
|
126
|
-
/** Get
|
|
127
|
-
export type SetOf<M> = ContextWriterOf<M>[
|
|
105
|
+
/** Get the `set` function type from a module's ContextWriter. */
|
|
106
|
+
export type SetOf<M> = ContextWriterOf<M>["set"];
|
|
128
107
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* OwnSlice first — pass only what this module contributes:
|
|
132
|
-
* defineModule<AuthSlice>()({ modules: [db, redis] as const, ... })
|
|
133
|
-
*
|
|
134
|
-
* Deps is inferred from modules — no need to pass it explicitly.
|
|
135
|
-
* Omit OwnSlice entirely if this module contributes nothing to context:
|
|
136
|
-
* defineModule()({ modules: [db] as const, ... })
|
|
137
|
-
*
|
|
138
|
-
* Inside boot/shutdown:
|
|
139
|
-
* ctx.db — read dep directly (Readonly<S>)
|
|
140
|
-
* ctx.set('auth') — write own slice only
|
|
141
|
-
*
|
|
142
|
-
* @param def module definition
|
|
143
|
-
*/
|
|
144
|
-
/**
|
|
145
|
-
* Extracts the non-void return type of a boot function.
|
|
146
|
-
* Returns Record<never, never> when boot is absent or returns void.
|
|
108
|
+
* Errors if U has any key not in T by mapping extra keys to never.
|
|
147
109
|
*/
|
|
148
|
-
type
|
|
110
|
+
type Exact<T, U> = {
|
|
111
|
+
[K in keyof U]: K extends keyof T ? T[K] : never;
|
|
112
|
+
};
|
|
149
113
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
114
|
+
* Produces a readable type error message instead of `never`.
|
|
115
|
+
* Msg appears directly in the IDE error output.
|
|
152
116
|
*/
|
|
153
|
-
type
|
|
154
|
-
|
|
155
|
-
}
|
|
117
|
+
type TypeError<Msg extends string> = {
|
|
118
|
+
readonly __error__: Msg;
|
|
119
|
+
};
|
|
156
120
|
/**
|
|
157
|
-
*
|
|
158
|
-
*
|
|
121
|
+
* Errors if OwnSlice declares a key that already exists in DepCtx.
|
|
122
|
+
* Overlapping keys show a readable message instead of `never`.
|
|
159
123
|
*/
|
|
160
|
-
type
|
|
161
|
-
|
|
162
|
-
}
|
|
124
|
+
type NoOverlap<DepCtx, OwnSlice> = {
|
|
125
|
+
[K in keyof OwnSlice]: K extends keyof DepCtx ? TypeError<`Key "${K & string}" is already owned by a dependency module`> : OwnSlice[K];
|
|
126
|
+
};
|
|
163
127
|
/**
|
|
164
128
|
* Module def shape when OwnSlice is explicitly provided.
|
|
165
129
|
*/
|
|
@@ -175,24 +139,36 @@ type ModuleDef<Deps extends readonly AnyModule[], OwnSlice extends object, R ext
|
|
|
175
139
|
afterShutdown?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
|
|
176
140
|
};
|
|
177
141
|
/**
|
|
178
|
-
*
|
|
142
|
+
* Module def shape when OwnSlice is inferred from boot's return type.
|
|
179
143
|
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
144
|
+
* Deps and Slice are independent generics — TypeScript infers Deps from the
|
|
145
|
+
* `modules` field and Slice from boot's return value in a single pass, with
|
|
146
|
+
* no mutual dependency between them.
|
|
182
147
|
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
148
|
+
* Hook ctx types:
|
|
149
|
+
* boot — ContextWriter<MergeSlices<Deps>> (dep context only)
|
|
150
|
+
* boot produces Slice, so Slice cannot appear in its own param
|
|
151
|
+
* type without creating a cycle that collapses ctx to `any`.
|
|
152
|
+
* all other — ContextWriter<FullContext<Deps, Slice>, Slice>
|
|
153
|
+
* these run after boot has written Slice into context, so the
|
|
154
|
+
* full context including own slice is available and safe to type.
|
|
185
155
|
*/
|
|
186
|
-
type
|
|
156
|
+
type ModuleDefInfer<Deps extends readonly AnyModule[], Slice extends Record<string, unknown>> = {
|
|
187
157
|
name: string;
|
|
188
158
|
description?: string;
|
|
189
|
-
modules?:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
159
|
+
modules?: Deps;
|
|
160
|
+
/** ctx = dep context only — own slice not available (produced by this function). */
|
|
161
|
+
boot?(ctx: ContextWriter<MergeSlices<Deps>, MergeSlices<Deps>>): void | Promise<void> | Slice | Promise<Slice>;
|
|
162
|
+
/** ctx = full context including own slice — runs after boot. */
|
|
163
|
+
shutdown?(ctx: ContextWriter<FullContext<Deps, Slice>, Slice>): Promise<void> | void;
|
|
164
|
+
/** ctx = dep context only — runs before boot. */
|
|
165
|
+
beforeBoot?(ctx: ContextWriter<MergeSlices<Deps>, MergeSlices<Deps>>): Promise<void> | void;
|
|
166
|
+
/** ctx = full context including own slice — runs after boot. */
|
|
167
|
+
afterBoot?(ctx: ContextWriter<FullContext<Deps, Slice>, Slice>): Promise<void> | void;
|
|
168
|
+
/** ctx = full context including own slice — runs after boot. */
|
|
169
|
+
beforeShutdown?(ctx: ContextWriter<FullContext<Deps, Slice>, Slice>): Promise<void> | void;
|
|
170
|
+
/** ctx = full context including own slice — runs after boot. */
|
|
171
|
+
afterShutdown?(ctx: ContextWriter<FullContext<Deps, Slice>, Slice>): Promise<void> | void;
|
|
196
172
|
};
|
|
197
173
|
/**
|
|
198
174
|
* Sentinel type used to detect whether OwnSlice was explicitly supplied.
|
|
@@ -205,48 +181,34 @@ type Infer = typeof INFER;
|
|
|
205
181
|
* Selects the inner function signature based on whether OwnSlice was supplied.
|
|
206
182
|
*
|
|
207
183
|
* OwnSlice = Infer (no type arg given):
|
|
208
|
-
*
|
|
209
|
-
*
|
|
184
|
+
* Deps is inferred from `modules`. Slice is inferred from boot's return.
|
|
185
|
+
* Both are independent generics — no cycle, no `any`.
|
|
186
|
+
* boot ctx = dep context only (cycle prevention).
|
|
187
|
+
* all other hook ctx = full context including own slice.
|
|
210
188
|
*
|
|
211
189
|
* OwnSlice = explicit type:
|
|
212
190
|
* Enforces boot return against the supplied OwnSlice via Exact/NoOverlap.
|
|
191
|
+
* Unchanged from original — no regression.
|
|
213
192
|
*/
|
|
214
|
-
type DefineModuleInner<OwnSlice> = OwnSlice extends Infer ? <const
|
|
193
|
+
type DefineModuleInner<OwnSlice> = OwnSlice extends Infer ? <const Deps extends readonly AnyModule[], Slice extends Record<string, unknown> = Record<never, never>>(def: ModuleDefInfer<Deps, Slice>) => Module<FullContext<Deps, Slice>, Slice> : OwnSlice extends object ? <const Deps extends readonly AnyModule[], R extends NoOverlap<MergeSlices<Deps>, OwnSlice>>(def: ModuleDef<Deps, OwnSlice, R>) => Module<FullContext<Deps, OwnSlice>, OwnSlice> : never;
|
|
215
194
|
/**
|
|
216
195
|
* Defines a module with optional dependency wiring and lifecycle hooks.
|
|
217
196
|
*
|
|
218
197
|
* Explicit OwnSlice — boot return is enforced against the supplied type:
|
|
219
198
|
* defineModule<{ count: number }>()({ name: "counter", boot: () => ({ count: 0 }) })
|
|
220
199
|
*
|
|
221
|
-
* Inferred OwnSlice — OwnSlice
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
200
|
+
* Inferred OwnSlice — Deps inferred from modules, OwnSlice inferred from boot return.
|
|
201
|
+
* ctx types per hook:
|
|
202
|
+
* boot — dep context only (own slice is being produced here, not available yet)
|
|
203
|
+
* shutdown — full context including own slice
|
|
204
|
+
* beforeBoot — dep context only (own slice not yet written)
|
|
205
|
+
* afterBoot — full context including own slice
|
|
206
|
+
* beforeShutdown / afterShutdown — full context including own slice
|
|
225
207
|
*
|
|
226
208
|
* Void / absent boot — OwnSlice becomes Record<never, never>:
|
|
227
209
|
* defineModule()({ name: "logger", boot: () => { console.log("up") } })
|
|
228
210
|
*/
|
|
229
211
|
export declare function defineModule<OwnSlice = Infer>(): DefineModuleInner<OwnSlice>;
|
|
230
|
-
/**
|
|
231
|
-
* Errors if U has any key not in T by mapping extra keys to never.
|
|
232
|
-
*/
|
|
233
|
-
type Exact<T, U> = {
|
|
234
|
-
[K in keyof U]: K extends keyof T ? T[K] : never;
|
|
235
|
-
};
|
|
236
|
-
/**
|
|
237
|
-
* Produces a readable type error message instead of `never`.
|
|
238
|
-
* Msg appears directly in the IDE error output.
|
|
239
|
-
*/
|
|
240
|
-
type TypeError<Msg extends string> = {
|
|
241
|
-
readonly __error__: Msg;
|
|
242
|
-
};
|
|
243
|
-
/**
|
|
244
|
-
* Errors if OwnSlice declares a key that already exists in DepCtx.
|
|
245
|
-
* Overlapping keys show a readable message instead of `never`.
|
|
246
|
-
*/
|
|
247
|
-
type NoOverlap<DepCtx, OwnSlice> = {
|
|
248
|
-
[K in keyof OwnSlice]: K extends keyof DepCtx ? TypeError<`Key "${K & string}" is already owned by a dependency module`> : OwnSlice[K];
|
|
249
|
-
};
|
|
250
212
|
/**
|
|
251
213
|
* Options for start(). Flat intersection — no nesting required:
|
|
252
214
|
* start([db], { log, afterBoot: async (ctx) => ... })
|
package/esm/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Minimal module lifecycle manager.
|
|
5
5
|
*
|
|
6
|
-
* Exports: Module, defineModule, ContextWriter,
|
|
6
|
+
* Exports: Module, defineModule, ContextWriter, LifecycleHooks, StartOptions, start.
|
|
7
7
|
*
|
|
8
8
|
* Type helpers (internal):
|
|
9
9
|
* UnionToIntersection<U> — U1 | U2 | U3 → U1 & U2 & U3
|
|
@@ -29,10 +29,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
29
29
|
* Explicit OwnSlice — boot return is enforced against the supplied type:
|
|
30
30
|
* defineModule<{ count: number }>()({ name: "counter", boot: () => ({ count: 0 }) })
|
|
31
31
|
*
|
|
32
|
-
* Inferred OwnSlice — OwnSlice
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
* Inferred OwnSlice — Deps inferred from modules, OwnSlice inferred from boot return.
|
|
33
|
+
* ctx types per hook:
|
|
34
|
+
* boot — dep context only (own slice is being produced here, not available yet)
|
|
35
|
+
* shutdown — full context including own slice
|
|
36
|
+
* beforeBoot — dep context only (own slice not yet written)
|
|
37
|
+
* afterBoot — full context including own slice
|
|
38
|
+
* beforeShutdown / afterShutdown — full context including own slice
|
|
36
39
|
*
|
|
37
40
|
* Void / absent boot — OwnSlice becomes Record<never, never>:
|
|
38
41
|
* defineModule()({ name: "logger", boot: () => { console.log("up") } })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"fileNames":["../node_modules/typescript/lib/lib.es5.d.ts","../node_modules/typescript/lib/lib.es2015.d.ts","../node_modules/typescript/lib/lib.dom.d.ts","../node_modules/typescript/lib/lib.es2015.core.d.ts","../node_modules/typescript/lib/lib.es2015.collection.d.ts","../node_modules/typescript/lib/lib.es2015.generator.d.ts","../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../node_modules/typescript/lib/lib.es2015.promise.d.ts","../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.decorators.d.ts","../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../src/index.ts","../node_modules/@types/deep-eql/index.d.ts","../node_modules/assertion-error/index.d.ts","../node_modules/@types/chai/index.d.ts","../node_modules/@types/estree/index.d.ts"],"fileIdsList":[[16,17]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"
|
|
1
|
+
{"fileNames":["../node_modules/typescript/lib/lib.es5.d.ts","../node_modules/typescript/lib/lib.es2015.d.ts","../node_modules/typescript/lib/lib.dom.d.ts","../node_modules/typescript/lib/lib.es2015.core.d.ts","../node_modules/typescript/lib/lib.es2015.collection.d.ts","../node_modules/typescript/lib/lib.es2015.generator.d.ts","../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../node_modules/typescript/lib/lib.es2015.promise.d.ts","../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.decorators.d.ts","../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../src/index.ts","../node_modules/@types/deep-eql/index.d.ts","../node_modules/assertion-error/index.d.ts","../node_modules/@types/chai/index.d.ts","../node_modules/@types/estree/index.d.ts"],"fileIdsList":[[16,17]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"c855ee070a85ffc84384d7aef265f2b6b6c1ccf6999d69a97940f35cdad9da0b","signature":"671fb5916c01ae3e4bb091627db88d76ce1cebbd493f4855a9d85079774f382a"},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","impliedFormat":1}],"root":[15],"options":{"declaration":true,"downlevelIteration":true,"emitDecoratorMetadata":true,"esModuleInterop":true,"experimentalDecorators":true,"module":5,"noImplicitAny":true,"outDir":"./","skipLibCheck":true,"strict":true,"strictPropertyInitialization":false,"target":2},"referencedMap":[[18,1]],"version":"5.9.3"}
|
package/lib/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Minimal module lifecycle manager.
|
|
5
5
|
*
|
|
6
|
-
* Exports: Module, defineModule, ContextWriter,
|
|
6
|
+
* Exports: Module, defineModule, ContextWriter, LifecycleHooks, StartOptions, start.
|
|
7
7
|
*
|
|
8
8
|
* Type helpers (internal):
|
|
9
9
|
* UnionToIntersection<U> — U1 | U2 | U3 → U1 & U2 & U3
|
|
@@ -61,24 +61,14 @@ export interface LifecycleHooks<S extends object = Record<never, never>> {
|
|
|
61
61
|
/** Called once after the last module has finished shutting down. */
|
|
62
62
|
afterShutdown?(ctx: ContextWriter<S>): Promise<void> | void;
|
|
63
63
|
/** Called before each individual module boots, in boot order. */
|
|
64
|
-
beforeEachBoot?(ctx: ContextWriter<S>,
|
|
65
|
-
/** The module about to boot. */
|
|
66
|
-
module: Module<any>): Promise<void> | void;
|
|
64
|
+
beforeEachBoot?(ctx: ContextWriter<S>, module: Module<any>): Promise<void> | void;
|
|
67
65
|
/** Called after each individual module finishes booting, in boot order. */
|
|
68
|
-
afterEachBoot?(ctx: ContextWriter<S>,
|
|
69
|
-
/** The module that just finished booting. */
|
|
70
|
-
module: Module<any>): Promise<void> | void;
|
|
66
|
+
afterEachBoot?(ctx: ContextWriter<S>, module: Module<any>): Promise<void> | void;
|
|
71
67
|
/** Called before each individual module shuts down, in shutdown order. */
|
|
72
|
-
beforeEachShutdown?(ctx: ContextWriter<S>,
|
|
73
|
-
/** The module about to shut down. */
|
|
74
|
-
module: Module<any>): Promise<void> | void;
|
|
68
|
+
beforeEachShutdown?(ctx: ContextWriter<S>, module: Module<any>): Promise<void> | void;
|
|
75
69
|
/** Called after each individual module finishes shutting down, in shutdown order. */
|
|
76
|
-
afterEachShutdown?(ctx: ContextWriter<S>,
|
|
77
|
-
|
|
78
|
-
module: Module<any>): Promise<void> | void;
|
|
79
|
-
onError?(error: Error, ctx: ContextWriter<S>,
|
|
80
|
-
/** The module that just finished shutting down. */
|
|
81
|
-
module: Module<any>): Promise<void> | void;
|
|
70
|
+
afterEachShutdown?(ctx: ContextWriter<S>, module: Module<any>): Promise<void> | void;
|
|
71
|
+
onError?(error: Error, ctx: ContextWriter<S>, module: Module<any>): Promise<void> | void;
|
|
82
72
|
}
|
|
83
73
|
/**
|
|
84
74
|
* Module<S, Sl>
|
|
@@ -102,64 +92,38 @@ export interface Module<S extends object, Sl extends object = S> {
|
|
|
102
92
|
afterShutdown?(ctx: ContextWriter<S, Sl>): Promise<void> | void;
|
|
103
93
|
}
|
|
104
94
|
/**
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export const moduleA = defineModule<AContext>()({
|
|
111
|
-
name: "A",
|
|
112
|
-
modules: [] as const,
|
|
113
|
-
boot: () => ({ value: "module:A" }),
|
|
114
|
-
async shutdown(ctx) {
|
|
115
|
-
console.log(`shutdown ${ctx.value}`);
|
|
116
|
-
},
|
|
117
|
-
});
|
|
118
|
-
// Get context type by module
|
|
119
|
-
type AContext2 = ContextOf<typeof moduleA> // same as above `AContext`
|
|
95
|
+
* Extracts the full context type from a module.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* type AContext = ContextOf<typeof moduleA> // { value: string }
|
|
120
99
|
*/
|
|
121
100
|
export type ContextOf<M> = M extends Module<infer Full, any> ? {
|
|
122
101
|
[K in keyof Full]: Full[K];
|
|
123
102
|
} : never;
|
|
124
|
-
/** Get
|
|
103
|
+
/** Get ContextWriter type by typeof module. */
|
|
125
104
|
export type ContextWriterOf<M> = ContextWriter<M extends Module<any, infer Ctx> ? Ctx : never>;
|
|
126
|
-
/** Get
|
|
127
|
-
export type SetOf<M> = ContextWriterOf<M>[
|
|
105
|
+
/** Get the `set` function type from a module's ContextWriter. */
|
|
106
|
+
export type SetOf<M> = ContextWriterOf<M>["set"];
|
|
128
107
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* OwnSlice first — pass only what this module contributes:
|
|
132
|
-
* defineModule<AuthSlice>()({ modules: [db, redis] as const, ... })
|
|
133
|
-
*
|
|
134
|
-
* Deps is inferred from modules — no need to pass it explicitly.
|
|
135
|
-
* Omit OwnSlice entirely if this module contributes nothing to context:
|
|
136
|
-
* defineModule()({ modules: [db] as const, ... })
|
|
137
|
-
*
|
|
138
|
-
* Inside boot/shutdown:
|
|
139
|
-
* ctx.db — read dep directly (Readonly<S>)
|
|
140
|
-
* ctx.set('auth') — write own slice only
|
|
141
|
-
*
|
|
142
|
-
* @param def module definition
|
|
143
|
-
*/
|
|
144
|
-
/**
|
|
145
|
-
* Extracts the non-void return type of a boot function.
|
|
146
|
-
* Returns Record<never, never> when boot is absent or returns void.
|
|
108
|
+
* Errors if U has any key not in T by mapping extra keys to never.
|
|
147
109
|
*/
|
|
148
|
-
type
|
|
110
|
+
type Exact<T, U> = {
|
|
111
|
+
[K in keyof U]: K extends keyof T ? T[K] : never;
|
|
112
|
+
};
|
|
149
113
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
114
|
+
* Produces a readable type error message instead of `never`.
|
|
115
|
+
* Msg appears directly in the IDE error output.
|
|
152
116
|
*/
|
|
153
|
-
type
|
|
154
|
-
|
|
155
|
-
}
|
|
117
|
+
type TypeError<Msg extends string> = {
|
|
118
|
+
readonly __error__: Msg;
|
|
119
|
+
};
|
|
156
120
|
/**
|
|
157
|
-
*
|
|
158
|
-
*
|
|
121
|
+
* Errors if OwnSlice declares a key that already exists in DepCtx.
|
|
122
|
+
* Overlapping keys show a readable message instead of `never`.
|
|
159
123
|
*/
|
|
160
|
-
type
|
|
161
|
-
|
|
162
|
-
}
|
|
124
|
+
type NoOverlap<DepCtx, OwnSlice> = {
|
|
125
|
+
[K in keyof OwnSlice]: K extends keyof DepCtx ? TypeError<`Key "${K & string}" is already owned by a dependency module`> : OwnSlice[K];
|
|
126
|
+
};
|
|
163
127
|
/**
|
|
164
128
|
* Module def shape when OwnSlice is explicitly provided.
|
|
165
129
|
*/
|
|
@@ -175,24 +139,36 @@ type ModuleDef<Deps extends readonly AnyModule[], OwnSlice extends object, R ext
|
|
|
175
139
|
afterShutdown?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
|
|
176
140
|
};
|
|
177
141
|
/**
|
|
178
|
-
*
|
|
142
|
+
* Module def shape when OwnSlice is inferred from boot's return type.
|
|
179
143
|
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
144
|
+
* Deps and Slice are independent generics — TypeScript infers Deps from the
|
|
145
|
+
* `modules` field and Slice from boot's return value in a single pass, with
|
|
146
|
+
* no mutual dependency between them.
|
|
182
147
|
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
148
|
+
* Hook ctx types:
|
|
149
|
+
* boot — ContextWriter<MergeSlices<Deps>> (dep context only)
|
|
150
|
+
* boot produces Slice, so Slice cannot appear in its own param
|
|
151
|
+
* type without creating a cycle that collapses ctx to `any`.
|
|
152
|
+
* all other — ContextWriter<FullContext<Deps, Slice>, Slice>
|
|
153
|
+
* these run after boot has written Slice into context, so the
|
|
154
|
+
* full context including own slice is available and safe to type.
|
|
185
155
|
*/
|
|
186
|
-
type
|
|
156
|
+
type ModuleDefInfer<Deps extends readonly AnyModule[], Slice extends Record<string, unknown>> = {
|
|
187
157
|
name: string;
|
|
188
158
|
description?: string;
|
|
189
|
-
modules?:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
159
|
+
modules?: Deps;
|
|
160
|
+
/** ctx = dep context only — own slice not available (produced by this function). */
|
|
161
|
+
boot?(ctx: ContextWriter<MergeSlices<Deps>, MergeSlices<Deps>>): void | Promise<void> | Slice | Promise<Slice>;
|
|
162
|
+
/** ctx = full context including own slice — runs after boot. */
|
|
163
|
+
shutdown?(ctx: ContextWriter<FullContext<Deps, Slice>, Slice>): Promise<void> | void;
|
|
164
|
+
/** ctx = dep context only — runs before boot. */
|
|
165
|
+
beforeBoot?(ctx: ContextWriter<MergeSlices<Deps>, MergeSlices<Deps>>): Promise<void> | void;
|
|
166
|
+
/** ctx = full context including own slice — runs after boot. */
|
|
167
|
+
afterBoot?(ctx: ContextWriter<FullContext<Deps, Slice>, Slice>): Promise<void> | void;
|
|
168
|
+
/** ctx = full context including own slice — runs after boot. */
|
|
169
|
+
beforeShutdown?(ctx: ContextWriter<FullContext<Deps, Slice>, Slice>): Promise<void> | void;
|
|
170
|
+
/** ctx = full context including own slice — runs after boot. */
|
|
171
|
+
afterShutdown?(ctx: ContextWriter<FullContext<Deps, Slice>, Slice>): Promise<void> | void;
|
|
196
172
|
};
|
|
197
173
|
/**
|
|
198
174
|
* Sentinel type used to detect whether OwnSlice was explicitly supplied.
|
|
@@ -205,48 +181,34 @@ type Infer = typeof INFER;
|
|
|
205
181
|
* Selects the inner function signature based on whether OwnSlice was supplied.
|
|
206
182
|
*
|
|
207
183
|
* OwnSlice = Infer (no type arg given):
|
|
208
|
-
*
|
|
209
|
-
*
|
|
184
|
+
* Deps is inferred from `modules`. Slice is inferred from boot's return.
|
|
185
|
+
* Both are independent generics — no cycle, no `any`.
|
|
186
|
+
* boot ctx = dep context only (cycle prevention).
|
|
187
|
+
* all other hook ctx = full context including own slice.
|
|
210
188
|
*
|
|
211
189
|
* OwnSlice = explicit type:
|
|
212
190
|
* Enforces boot return against the supplied OwnSlice via Exact/NoOverlap.
|
|
191
|
+
* Unchanged from original — no regression.
|
|
213
192
|
*/
|
|
214
|
-
type DefineModuleInner<OwnSlice> = OwnSlice extends Infer ? <const
|
|
193
|
+
type DefineModuleInner<OwnSlice> = OwnSlice extends Infer ? <const Deps extends readonly AnyModule[], Slice extends Record<string, unknown> = Record<never, never>>(def: ModuleDefInfer<Deps, Slice>) => Module<FullContext<Deps, Slice>, Slice> : OwnSlice extends object ? <const Deps extends readonly AnyModule[], R extends NoOverlap<MergeSlices<Deps>, OwnSlice>>(def: ModuleDef<Deps, OwnSlice, R>) => Module<FullContext<Deps, OwnSlice>, OwnSlice> : never;
|
|
215
194
|
/**
|
|
216
195
|
* Defines a module with optional dependency wiring and lifecycle hooks.
|
|
217
196
|
*
|
|
218
197
|
* Explicit OwnSlice — boot return is enforced against the supplied type:
|
|
219
198
|
* defineModule<{ count: number }>()({ name: "counter", boot: () => ({ count: 0 }) })
|
|
220
199
|
*
|
|
221
|
-
* Inferred OwnSlice — OwnSlice
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
200
|
+
* Inferred OwnSlice — Deps inferred from modules, OwnSlice inferred from boot return.
|
|
201
|
+
* ctx types per hook:
|
|
202
|
+
* boot — dep context only (own slice is being produced here, not available yet)
|
|
203
|
+
* shutdown — full context including own slice
|
|
204
|
+
* beforeBoot — dep context only (own slice not yet written)
|
|
205
|
+
* afterBoot — full context including own slice
|
|
206
|
+
* beforeShutdown / afterShutdown — full context including own slice
|
|
225
207
|
*
|
|
226
208
|
* Void / absent boot — OwnSlice becomes Record<never, never>:
|
|
227
209
|
* defineModule()({ name: "logger", boot: () => { console.log("up") } })
|
|
228
210
|
*/
|
|
229
211
|
export declare function defineModule<OwnSlice = Infer>(): DefineModuleInner<OwnSlice>;
|
|
230
|
-
/**
|
|
231
|
-
* Errors if U has any key not in T by mapping extra keys to never.
|
|
232
|
-
*/
|
|
233
|
-
type Exact<T, U> = {
|
|
234
|
-
[K in keyof U]: K extends keyof T ? T[K] : never;
|
|
235
|
-
};
|
|
236
|
-
/**
|
|
237
|
-
* Produces a readable type error message instead of `never`.
|
|
238
|
-
* Msg appears directly in the IDE error output.
|
|
239
|
-
*/
|
|
240
|
-
type TypeError<Msg extends string> = {
|
|
241
|
-
readonly __error__: Msg;
|
|
242
|
-
};
|
|
243
|
-
/**
|
|
244
|
-
* Errors if OwnSlice declares a key that already exists in DepCtx.
|
|
245
|
-
* Overlapping keys show a readable message instead of `never`.
|
|
246
|
-
*/
|
|
247
|
-
type NoOverlap<DepCtx, OwnSlice> = {
|
|
248
|
-
[K in keyof OwnSlice]: K extends keyof DepCtx ? TypeError<`Key "${K & string}" is already owned by a dependency module`> : OwnSlice[K];
|
|
249
|
-
};
|
|
250
212
|
/**
|
|
251
213
|
* Options for start(). Flat intersection — no nesting required:
|
|
252
214
|
* start([db], { log, afterBoot: async (ctx) => ... })
|
package/lib/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Minimal module lifecycle manager.
|
|
6
6
|
*
|
|
7
|
-
* Exports: Module, defineModule, ContextWriter,
|
|
7
|
+
* Exports: Module, defineModule, ContextWriter, LifecycleHooks, StartOptions, start.
|
|
8
8
|
*
|
|
9
9
|
* Type helpers (internal):
|
|
10
10
|
* UnionToIntersection<U> — U1 | U2 | U3 → U1 & U2 & U3
|
|
@@ -36,10 +36,13 @@ exports.rollback = rollback;
|
|
|
36
36
|
* Explicit OwnSlice — boot return is enforced against the supplied type:
|
|
37
37
|
* defineModule<{ count: number }>()({ name: "counter", boot: () => ({ count: 0 }) })
|
|
38
38
|
*
|
|
39
|
-
* Inferred OwnSlice — OwnSlice
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
39
|
+
* Inferred OwnSlice — Deps inferred from modules, OwnSlice inferred from boot return.
|
|
40
|
+
* ctx types per hook:
|
|
41
|
+
* boot — dep context only (own slice is being produced here, not available yet)
|
|
42
|
+
* shutdown — full context including own slice
|
|
43
|
+
* beforeBoot — dep context only (own slice not yet written)
|
|
44
|
+
* afterBoot — full context including own slice
|
|
45
|
+
* beforeShutdown / afterShutdown — full context including own slice
|
|
43
46
|
*
|
|
44
47
|
* Void / absent boot — OwnSlice becomes Record<never, never>:
|
|
45
48
|
* defineModule()({ name: "logger", boot: () => { console.log("up") } })
|
package/package.json
CHANGED