tsdkarc 1.1.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -7
- package/esm/index.d.ts +84 -5
- package/esm/index.js +9 -14
- package/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/lib/index.d.ts +84 -5
- package/lib/index.js +9 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,13 +24,14 @@ npm install tsdkarc
|
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
```ts
|
|
27
|
-
import start, { defineModule, type
|
|
27
|
+
import start, { defineModule, type ContextOf, type ContextWriterOf, type SetOf } from "tsdkarc";
|
|
28
28
|
|
|
29
|
-
interface ConfigSlice {
|
|
30
|
-
|
|
31
|
-
}
|
|
29
|
+
// interface ConfigSlice {
|
|
30
|
+
// config: { port: number; env: string };
|
|
31
|
+
// }
|
|
32
32
|
|
|
33
|
-
const configModule = defineModule<ConfigSlice>()({
|
|
33
|
+
// const configModule = defineModule<ConfigSlice>()({
|
|
34
|
+
const configModule = defineModule()({
|
|
34
35
|
name: "config",
|
|
35
36
|
modules: [],
|
|
36
37
|
boot(ctx) {
|
|
@@ -50,8 +51,12 @@ const configModule = defineModule<ConfigSlice>()({
|
|
|
50
51
|
},
|
|
51
52
|
});
|
|
52
53
|
|
|
53
|
-
//
|
|
54
|
-
type ConfigModuleCtx =
|
|
54
|
+
// Get the module's context type(include the dependencies modules)
|
|
55
|
+
type ConfigModuleCtx = ContextOf<typeof configModule>; // same as `ConfigSlice`
|
|
56
|
+
|
|
57
|
+
// Get the `set` type of the module
|
|
58
|
+
type ConfigModuleSet = ContextWriterOf<typeof configModule>['set'];
|
|
59
|
+
type ConfigModuleSet = SetOf<typeof configModule>
|
|
55
60
|
|
|
56
61
|
// Run
|
|
57
62
|
(async () => {
|
package/esm/index.d.ts
CHANGED
|
@@ -116,11 +116,15 @@ export const moduleA = defineModule<AContext>()({
|
|
|
116
116
|
},
|
|
117
117
|
});
|
|
118
118
|
// Get context type by module
|
|
119
|
-
type AContext2 =
|
|
119
|
+
type AContext2 = ContextOf<typeof moduleA> // same as above `AContext`
|
|
120
120
|
*/
|
|
121
|
-
export type
|
|
121
|
+
export type ContextOf<M> = M extends Module<infer Full, any> ? {
|
|
122
122
|
[K in keyof Full]: Full[K];
|
|
123
123
|
} : never;
|
|
124
|
+
/** Get context writter type by typeof module*/
|
|
125
|
+
export type ContextWriterOf<M> = ContextWriter<M extends Module<any, infer Ctx> ? Ctx : never>;
|
|
126
|
+
/** Get context writter's `set` type by typeof module*/
|
|
127
|
+
export type ContextSetOf<M> = ContextWriter<M extends Module<any, infer Ctx> ? Ctx : never>['set'];
|
|
124
128
|
/**
|
|
125
129
|
* Define a module with full context inferred from its dependency tuple.
|
|
126
130
|
*
|
|
@@ -137,17 +141,92 @@ export type InferContextBy<M> = M extends Module<infer Full, any> ? {
|
|
|
137
141
|
*
|
|
138
142
|
* @param def module definition
|
|
139
143
|
*/
|
|
140
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Extracts the non-void return type of a boot function.
|
|
146
|
+
* Returns Record<never, never> when boot is absent or returns void.
|
|
147
|
+
*/
|
|
148
|
+
type BootReturn<T> = T extends (...args: any[]) => Promise<infer R> ? Exclude<R, void> extends never ? Record<never, never> : Exclude<R, void> : T extends (...args: any[]) => infer R ? Exclude<R, void> extends never ? Record<never, never> : Exclude<R, void> : Record<never, never>;
|
|
149
|
+
/**
|
|
150
|
+
* Infers OwnSlice from a def object's boot return type.
|
|
151
|
+
* Falls back to Record<never, never> if no boot or boot returns void.
|
|
152
|
+
*/
|
|
153
|
+
type InferredOwnSlice<Def> = Def extends {
|
|
154
|
+
boot: infer B;
|
|
155
|
+
} ? BootReturn<B> : Record<never, never>;
|
|
156
|
+
/**
|
|
157
|
+
* Infers the Deps tuple from a def object's modules field.
|
|
158
|
+
* Falls back to [] if modules is absent.
|
|
159
|
+
*/
|
|
160
|
+
type InferredDeps<Def> = Def extends {
|
|
161
|
+
modules: infer M;
|
|
162
|
+
} ? M extends readonly AnyModule[] ? M : [] : [];
|
|
163
|
+
/**
|
|
164
|
+
* Module def shape when OwnSlice is explicitly provided.
|
|
165
|
+
*/
|
|
166
|
+
type ModuleDef<Deps extends readonly AnyModule[], OwnSlice extends object, R extends object> = {
|
|
141
167
|
name: string;
|
|
142
168
|
description?: string;
|
|
143
169
|
modules?: Deps;
|
|
144
|
-
boot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, NoOverlap<MergeSlices<Deps>, OwnSlice>>):
|
|
170
|
+
boot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, NoOverlap<MergeSlices<Deps>, OwnSlice>>): void | Promise<void> | (R & Exact<NoOverlap<MergeSlices<Deps>, OwnSlice>, R>) | Promise<R & Exact<NoOverlap<MergeSlices<Deps>, OwnSlice>, R>>;
|
|
145
171
|
shutdown?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
|
|
146
172
|
beforeBoot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
|
|
147
173
|
afterBoot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
|
|
148
174
|
beforeShutdown?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
|
|
149
175
|
afterShutdown?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
|
|
150
|
-
}
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* Loose module def shape used when OwnSlice is inferred.
|
|
179
|
+
*
|
|
180
|
+
* modules is typed as readonly AnyModule[] so TypeScript preserves the literal
|
|
181
|
+
* tuple — Deps is extracted post-hoc via InferredDeps<Def>, not constrained here.
|
|
182
|
+
*
|
|
183
|
+
* boot returns `any` intentionally — Exact<object, R> would collapse every
|
|
184
|
+
* property to never if we used `object`. Enforcement is on the output Module<>.
|
|
185
|
+
*/
|
|
186
|
+
type ModuleDefLoose = {
|
|
187
|
+
name: string;
|
|
188
|
+
description?: string;
|
|
189
|
+
modules?: readonly AnyModule[];
|
|
190
|
+
boot?(ctx: ContextWriter<any, any>): any;
|
|
191
|
+
shutdown?(ctx: ContextWriter<any, any>): Promise<void> | void;
|
|
192
|
+
beforeBoot?(ctx: ContextWriter<any, any>): Promise<void> | void;
|
|
193
|
+
afterBoot?(ctx: ContextWriter<any, any>): Promise<void> | void;
|
|
194
|
+
beforeShutdown?(ctx: ContextWriter<any, any>): Promise<void> | void;
|
|
195
|
+
afterShutdown?(ctx: ContextWriter<any, any>): Promise<void> | void;
|
|
196
|
+
};
|
|
197
|
+
/**
|
|
198
|
+
* Sentinel type used to detect whether OwnSlice was explicitly supplied.
|
|
199
|
+
* When defineModule() is called with no type argument, OwnSlice defaults to
|
|
200
|
+
* this value, triggering the infer-from-boot path.
|
|
201
|
+
*/
|
|
202
|
+
declare const INFER: unique symbol;
|
|
203
|
+
type Infer = typeof INFER;
|
|
204
|
+
/**
|
|
205
|
+
* Selects the inner function signature based on whether OwnSlice was supplied.
|
|
206
|
+
*
|
|
207
|
+
* OwnSlice = Infer (no type arg given):
|
|
208
|
+
* Captures the whole def as const Def, extracts both Deps and OwnSlice
|
|
209
|
+
* from the def object post-hoc via InferredDeps<Def> and InferredOwnSlice<Def>.
|
|
210
|
+
*
|
|
211
|
+
* OwnSlice = explicit type:
|
|
212
|
+
* Enforces boot return against the supplied OwnSlice via Exact/NoOverlap.
|
|
213
|
+
*/
|
|
214
|
+
type DefineModuleInner<OwnSlice> = OwnSlice extends Infer ? <const Def extends ModuleDefLoose>(def: Def) => Module<FullContext<InferredDeps<Def>, Exclude<InferredOwnSlice<Def>, void>>, Exclude<InferredOwnSlice<Def>, void>> : 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
|
+
/**
|
|
216
|
+
* Defines a module with optional dependency wiring and lifecycle hooks.
|
|
217
|
+
*
|
|
218
|
+
* Explicit OwnSlice — boot return is enforced against the supplied type:
|
|
219
|
+
* defineModule<{ count: number }>()({ name: "counter", boot: () => ({ count: 0 }) })
|
|
220
|
+
*
|
|
221
|
+
* Inferred OwnSlice — OwnSlice is derived from boot's non-void return type,
|
|
222
|
+
* and FullContext includes all dep module contexts via InferredDeps:
|
|
223
|
+
* defineModule()({ name: "C", modules: [A, B] as const, boot: () => ({ c: 1 }) })
|
|
224
|
+
* // Module<ContextOf<A> & ContextOf<B> & { c: number }, { c: number }>
|
|
225
|
+
*
|
|
226
|
+
* Void / absent boot — OwnSlice becomes Record<never, never>:
|
|
227
|
+
* defineModule()({ name: "logger", boot: () => { console.log("up") } })
|
|
228
|
+
*/
|
|
229
|
+
export declare function defineModule<OwnSlice = Infer>(): DefineModuleInner<OwnSlice>;
|
|
151
230
|
/**
|
|
152
231
|
* Errors if U has any key not in T by mapping extra keys to never.
|
|
153
232
|
*/
|
package/esm/index.js
CHANGED
|
@@ -23,24 +23,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
23
23
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
24
24
|
});
|
|
25
25
|
};
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
// defineModule
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
26
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* OwnSlice first — pass only what this module contributes:
|
|
33
|
-
* defineModule<AuthSlice>()({ modules: [db, redis] as const, ... })
|
|
27
|
+
* Defines a module with optional dependency wiring and lifecycle hooks.
|
|
34
28
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* defineModule()({ modules: [db] as const, ... })
|
|
29
|
+
* Explicit OwnSlice — boot return is enforced against the supplied type:
|
|
30
|
+
* defineModule<{ count: number }>()({ name: "counter", boot: () => ({ count: 0 }) })
|
|
38
31
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
32
|
+
* Inferred OwnSlice — OwnSlice is derived from boot's non-void return type,
|
|
33
|
+
* and FullContext includes all dep module contexts via InferredDeps:
|
|
34
|
+
* defineModule()({ name: "C", modules: [A, B] as const, boot: () => ({ c: 1 }) })
|
|
35
|
+
* // Module<ContextOf<A> & ContextOf<B> & { c: number }, { c: number }>
|
|
42
36
|
*
|
|
43
|
-
*
|
|
37
|
+
* Void / absent boot — OwnSlice becomes Record<never, never>:
|
|
38
|
+
* defineModule()({ name: "logger", boot: () => { console.log("up") } })
|
|
44
39
|
*/
|
|
45
40
|
export function defineModule() {
|
|
46
41
|
return function (def) {
|
|
@@ -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":"dc1e8f6c60e1772cd804d39523944b7c8e2f5e843cfed2bdc80670e99a459ab4","signature":"fe902df7306c6cdfbb631ee5fd2ae6ddc1d97e5834d91dd6107f07244cc8f45d"},{"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
|
@@ -116,11 +116,15 @@ export const moduleA = defineModule<AContext>()({
|
|
|
116
116
|
},
|
|
117
117
|
});
|
|
118
118
|
// Get context type by module
|
|
119
|
-
type AContext2 =
|
|
119
|
+
type AContext2 = ContextOf<typeof moduleA> // same as above `AContext`
|
|
120
120
|
*/
|
|
121
|
-
export type
|
|
121
|
+
export type ContextOf<M> = M extends Module<infer Full, any> ? {
|
|
122
122
|
[K in keyof Full]: Full[K];
|
|
123
123
|
} : never;
|
|
124
|
+
/** Get context writter type by typeof module*/
|
|
125
|
+
export type ContextWriterOf<M> = ContextWriter<M extends Module<any, infer Ctx> ? Ctx : never>;
|
|
126
|
+
/** Get context writter's `set` type by typeof module*/
|
|
127
|
+
export type ContextSetOf<M> = ContextWriter<M extends Module<any, infer Ctx> ? Ctx : never>['set'];
|
|
124
128
|
/**
|
|
125
129
|
* Define a module with full context inferred from its dependency tuple.
|
|
126
130
|
*
|
|
@@ -137,17 +141,92 @@ export type InferContextBy<M> = M extends Module<infer Full, any> ? {
|
|
|
137
141
|
*
|
|
138
142
|
* @param def module definition
|
|
139
143
|
*/
|
|
140
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Extracts the non-void return type of a boot function.
|
|
146
|
+
* Returns Record<never, never> when boot is absent or returns void.
|
|
147
|
+
*/
|
|
148
|
+
type BootReturn<T> = T extends (...args: any[]) => Promise<infer R> ? Exclude<R, void> extends never ? Record<never, never> : Exclude<R, void> : T extends (...args: any[]) => infer R ? Exclude<R, void> extends never ? Record<never, never> : Exclude<R, void> : Record<never, never>;
|
|
149
|
+
/**
|
|
150
|
+
* Infers OwnSlice from a def object's boot return type.
|
|
151
|
+
* Falls back to Record<never, never> if no boot or boot returns void.
|
|
152
|
+
*/
|
|
153
|
+
type InferredOwnSlice<Def> = Def extends {
|
|
154
|
+
boot: infer B;
|
|
155
|
+
} ? BootReturn<B> : Record<never, never>;
|
|
156
|
+
/**
|
|
157
|
+
* Infers the Deps tuple from a def object's modules field.
|
|
158
|
+
* Falls back to [] if modules is absent.
|
|
159
|
+
*/
|
|
160
|
+
type InferredDeps<Def> = Def extends {
|
|
161
|
+
modules: infer M;
|
|
162
|
+
} ? M extends readonly AnyModule[] ? M : [] : [];
|
|
163
|
+
/**
|
|
164
|
+
* Module def shape when OwnSlice is explicitly provided.
|
|
165
|
+
*/
|
|
166
|
+
type ModuleDef<Deps extends readonly AnyModule[], OwnSlice extends object, R extends object> = {
|
|
141
167
|
name: string;
|
|
142
168
|
description?: string;
|
|
143
169
|
modules?: Deps;
|
|
144
|
-
boot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, NoOverlap<MergeSlices<Deps>, OwnSlice>>):
|
|
170
|
+
boot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, NoOverlap<MergeSlices<Deps>, OwnSlice>>): void | Promise<void> | (R & Exact<NoOverlap<MergeSlices<Deps>, OwnSlice>, R>) | Promise<R & Exact<NoOverlap<MergeSlices<Deps>, OwnSlice>, R>>;
|
|
145
171
|
shutdown?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
|
|
146
172
|
beforeBoot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
|
|
147
173
|
afterBoot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
|
|
148
174
|
beforeShutdown?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
|
|
149
175
|
afterShutdown?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
|
|
150
|
-
}
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* Loose module def shape used when OwnSlice is inferred.
|
|
179
|
+
*
|
|
180
|
+
* modules is typed as readonly AnyModule[] so TypeScript preserves the literal
|
|
181
|
+
* tuple — Deps is extracted post-hoc via InferredDeps<Def>, not constrained here.
|
|
182
|
+
*
|
|
183
|
+
* boot returns `any` intentionally — Exact<object, R> would collapse every
|
|
184
|
+
* property to never if we used `object`. Enforcement is on the output Module<>.
|
|
185
|
+
*/
|
|
186
|
+
type ModuleDefLoose = {
|
|
187
|
+
name: string;
|
|
188
|
+
description?: string;
|
|
189
|
+
modules?: readonly AnyModule[];
|
|
190
|
+
boot?(ctx: ContextWriter<any, any>): any;
|
|
191
|
+
shutdown?(ctx: ContextWriter<any, any>): Promise<void> | void;
|
|
192
|
+
beforeBoot?(ctx: ContextWriter<any, any>): Promise<void> | void;
|
|
193
|
+
afterBoot?(ctx: ContextWriter<any, any>): Promise<void> | void;
|
|
194
|
+
beforeShutdown?(ctx: ContextWriter<any, any>): Promise<void> | void;
|
|
195
|
+
afterShutdown?(ctx: ContextWriter<any, any>): Promise<void> | void;
|
|
196
|
+
};
|
|
197
|
+
/**
|
|
198
|
+
* Sentinel type used to detect whether OwnSlice was explicitly supplied.
|
|
199
|
+
* When defineModule() is called with no type argument, OwnSlice defaults to
|
|
200
|
+
* this value, triggering the infer-from-boot path.
|
|
201
|
+
*/
|
|
202
|
+
declare const INFER: unique symbol;
|
|
203
|
+
type Infer = typeof INFER;
|
|
204
|
+
/**
|
|
205
|
+
* Selects the inner function signature based on whether OwnSlice was supplied.
|
|
206
|
+
*
|
|
207
|
+
* OwnSlice = Infer (no type arg given):
|
|
208
|
+
* Captures the whole def as const Def, extracts both Deps and OwnSlice
|
|
209
|
+
* from the def object post-hoc via InferredDeps<Def> and InferredOwnSlice<Def>.
|
|
210
|
+
*
|
|
211
|
+
* OwnSlice = explicit type:
|
|
212
|
+
* Enforces boot return against the supplied OwnSlice via Exact/NoOverlap.
|
|
213
|
+
*/
|
|
214
|
+
type DefineModuleInner<OwnSlice> = OwnSlice extends Infer ? <const Def extends ModuleDefLoose>(def: Def) => Module<FullContext<InferredDeps<Def>, Exclude<InferredOwnSlice<Def>, void>>, Exclude<InferredOwnSlice<Def>, void>> : 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
|
+
/**
|
|
216
|
+
* Defines a module with optional dependency wiring and lifecycle hooks.
|
|
217
|
+
*
|
|
218
|
+
* Explicit OwnSlice — boot return is enforced against the supplied type:
|
|
219
|
+
* defineModule<{ count: number }>()({ name: "counter", boot: () => ({ count: 0 }) })
|
|
220
|
+
*
|
|
221
|
+
* Inferred OwnSlice — OwnSlice is derived from boot's non-void return type,
|
|
222
|
+
* and FullContext includes all dep module contexts via InferredDeps:
|
|
223
|
+
* defineModule()({ name: "C", modules: [A, B] as const, boot: () => ({ c: 1 }) })
|
|
224
|
+
* // Module<ContextOf<A> & ContextOf<B> & { c: number }, { c: number }>
|
|
225
|
+
*
|
|
226
|
+
* Void / absent boot — OwnSlice becomes Record<never, never>:
|
|
227
|
+
* defineModule()({ name: "logger", boot: () => { console.log("up") } })
|
|
228
|
+
*/
|
|
229
|
+
export declare function defineModule<OwnSlice = Infer>(): DefineModuleInner<OwnSlice>;
|
|
151
230
|
/**
|
|
152
231
|
* Errors if U has any key not in T by mapping extra keys to never.
|
|
153
232
|
*/
|
package/lib/index.js
CHANGED
|
@@ -30,24 +30,19 @@ exports.default = start;
|
|
|
30
30
|
exports.resolveModules = resolveModules;
|
|
31
31
|
exports.topoSort = topoSort;
|
|
32
32
|
exports.rollback = rollback;
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
// defineModule
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
33
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* OwnSlice first — pass only what this module contributes:
|
|
40
|
-
* defineModule<AuthSlice>()({ modules: [db, redis] as const, ... })
|
|
34
|
+
* Defines a module with optional dependency wiring and lifecycle hooks.
|
|
41
35
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* defineModule()({ modules: [db] as const, ... })
|
|
36
|
+
* Explicit OwnSlice — boot return is enforced against the supplied type:
|
|
37
|
+
* defineModule<{ count: number }>()({ name: "counter", boot: () => ({ count: 0 }) })
|
|
45
38
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
39
|
+
* Inferred OwnSlice — OwnSlice is derived from boot's non-void return type,
|
|
40
|
+
* and FullContext includes all dep module contexts via InferredDeps:
|
|
41
|
+
* defineModule()({ name: "C", modules: [A, B] as const, boot: () => ({ c: 1 }) })
|
|
42
|
+
* // Module<ContextOf<A> & ContextOf<B> & { c: number }, { c: number }>
|
|
49
43
|
*
|
|
50
|
-
*
|
|
44
|
+
* Void / absent boot — OwnSlice becomes Record<never, never>:
|
|
45
|
+
* defineModule()({ name: "logger", boot: () => { console.log("up") } })
|
|
51
46
|
*/
|
|
52
47
|
function defineModule() {
|
|
53
48
|
return function (def) {
|
package/package.json
CHANGED