tsdkarc 1.0.1 → 1.1.1

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 CHANGED
@@ -1,19 +1,21 @@
1
- # <a href="https://arc.tsdk.dev" align="center"><img src="./assets/logo.jpg" align="center" width="30px" height="30px" style="border-radius: 4px;margin-right:4px;" alt="TsdkArc: the Elegant, fully type-safe composable module library" /></a> TsdkArc
1
+ # <a href="https://arc.tsdk.dev" align="center"><img src="./assets/logo.jpg" align="center" width="30px" height="30px" style="border-radius: 4px;margin-right:4px;" alt="TsdkArc: the Elegant, fully type-safe module composable library" /></a> TsdkArc
2
2
 
3
3
  <a href="https://arc.tsdk.dev">
4
- <img src="./assets/banner.jpg" width="100%" style="border-radius: 24px" alt="TsdkArc: the Elegant, fully type-safe composable module library" /></a>
4
+ <img src="./assets/banner.jpg" width="100%" style="border-radius: 24px" alt="TsdkArc: the Elegant, fully type-safe module composable library" /></a>
5
5
 
6
- <div align="center">The Elegant, Fully Type-safe Composable Module Library.
6
+ <div align="center">The Elegant, Fully Type-safe Module Composable Library.
7
7
  </div>
8
8
 
9
9
  ---
10
10
 
11
+ [![npm version](https://img.shields.io/npm/v/tsdkarc.svg?style=flat)](https://www.npmjs.com/package/tsdkarc) [![Size](https://deno.bundlejs.com/badge?q=tsdkarc&config={%22esbuild%22:{%22external%22:[%22react%22,%22react-dom%22,%22react/jsx-runtime%22]}})](https://bundlejs.com/?q=tsdkarc&treeshake=%5B%7Blittkk%7D%5D&config=%7B%22esbuild%22:%7B%22external%22:%5B%22react%22,%22react-dom%22,%22react/jsx-runtime%22%5D%7D%7D) ![0 dependencies](https://img.shields.io/badge/0-dependencies!-brightgreen) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/suhaotian/tsdkarc/pulls) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/suhaotian/tsdkarc/blob/main/LICENSE) [![jsDocs.io](https://img.shields.io/badge/jsDocs.io-reference-blue)](https://www.jsdocs.io/package/tsdkarc) ![typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label&color=blue)
12
+
11
13
  ## Why `TsdkArc`
12
14
 
13
- Application codebases grow, the code become coupled and messy — hard to reuse, hard to share.
15
+ Your application codebases grow, the code become coupled and messy — hard to reuse, hard to share.
14
16
  `TsdkArc` lets you compose modules like building blocks, nest them, and share them type safely across projects.
15
17
 
16
- In **tsdkrc**, Each module declares what it needs and what it provides. Then call `start([modules])` will resolves the full dependency graph, boots modules in order, and returns a typed context.
18
+ In **tsdkarc**, Each module declares what it needs and what it provides. Then call `start([modules])` will resolves the full dependency graph, boots modules in order, and returns a typed context.
17
19
 
18
20
  ## Quick Start
19
21
 
@@ -32,16 +34,33 @@ const configModule = defineModule<ConfigSlice>()({
32
34
  name: "config",
33
35
  modules: [],
34
36
  boot(ctx) {
35
- ctx.set("config", {
36
- env: process.env.NODE_ENV ?? "development",
37
- port: Number(process.env.PORT) || 3000,
38
- });
37
+ return {
38
+ config: {
39
+ env: process.env.NODE_ENV ?? "development",
40
+ port: Number(process.env.PORT) || 3000,
41
+ },
42
+ };
43
+ // Or:
44
+ /*
45
+ ctx.set("config", {
46
+ env: process.env.NODE_ENV ?? "development",
47
+ port: Number(process.env.PORT) || 3000,
48
+ });
49
+ */
39
50
  },
40
51
  });
41
52
 
42
53
  // Run
43
54
  (async () => {
44
- const app = await start([configModule]);
55
+ const app = await start([serverModule], {
56
+ afterBoot() {
57
+ console.log("The app is running");
58
+ },
59
+ onError(error, ctx, mod) {
60
+ console.log(`${mod.name} error`, error.message);
61
+ // throw error;
62
+ },
63
+ });
45
64
  console.log(app.ctx.config.port); // 3000
46
65
  await app.stop();
47
66
  })();
@@ -63,7 +82,7 @@ const configModule = defineModule<ConfigSlice>()({
63
82
  defineModule<OwnSlice>()({
64
83
  name: string,
65
84
  modules: Module[],
66
- boot?(ctx): void | Promise<void>,
85
+ boot?(ctx): OwnSlice | Promise<OwnSlice> | void | Promise<void>,
67
86
  beforeBoot?(ctx): void | Promise<void>,
68
87
  afterBoot?(ctx): void | Promise<void>,
69
88
  shutdown?(ctx): void | Promise<void>,
@@ -76,6 +95,13 @@ start(modules: Module[], hooks?: {
76
95
  afterBoot?(ctx): void | Promise<void>,
77
96
  beforeShutdown?(ctx): void | Promise<void>,
78
97
  afterShutdown?(ctx): void | Promise<void>,
98
+
99
+ beforeEachBoot?(ctx): void | Promise<void>,
100
+ afterEachBoot?(ctx): void | Promise<void>,
101
+ beforeEachShutdown?(ctx): void | Promise<void>,
102
+ afterEachShutdown?(ctx): void | Promise<void>,
103
+
104
+ onError?(error: Error, ctx, mod): void | Promise<void>,
79
105
  }): Promise<{ ctx, stop() }>
80
106
  ```
81
107
 
@@ -191,7 +217,7 @@ beforeBoot → boot → afterBoot → [running] → beforeShutdown → shutdown
191
217
  | Hook | Purpose |
192
218
  | -------------------- | ------------------------------------------------------------------------------ |
193
219
  | `beforeBoot` | Pre-setup, Called once before the first module begins booting. |
194
- | `boot` | Register values on `ctx` via `ctx.set()` |
220
+ | `boot` | Register values on `ctx` via return ctx or call `ctx.set()` |
195
221
  | `afterBoot` | Called once after the last module has finished booting. |
196
222
  | `beforeShutdown` | Called once before the first module begins shutting down. |
197
223
  | `shutdown` | Release resources |
package/esm/index.d.ts CHANGED
@@ -27,6 +27,7 @@
27
27
  */
28
28
  export type ContextWriter<S extends object, Sl extends object = S> = Readonly<S> & {
29
29
  set<K extends Exclude<keyof Sl, "set">>(key: K, value: Sl[K]): void;
30
+ set(ctx: Sl): void;
30
31
  };
31
32
  /** Converts U1 | U2 | U3 into U1 & U2 & U3 via contravariance. */
32
33
  type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
@@ -89,7 +90,7 @@ export interface Module<S extends object, Sl extends object = S> {
89
90
  name: string;
90
91
  description?: string;
91
92
  modules?: AnyModule[];
92
- boot?(ctx: ContextWriter<S, Sl>): Promise<void> | void;
93
+ boot?(ctx: ContextWriter<S, Sl>): Promise<void> | void | Sl | Promise<Sl>;
93
94
  shutdown?(ctx: ContextWriter<S, Sl>): Promise<void> | void;
94
95
  /** Called immediately before this module's own boot(). */
95
96
  beforeBoot?(ctx: ContextWriter<S, Sl>): Promise<void> | void;
@@ -116,17 +117,37 @@ export interface Module<S extends object, Sl extends object = S> {
116
117
  *
117
118
  * @param def module definition
118
119
  */
119
- export declare function defineModule<OwnSlice extends object = Record<never, never>>(): <const Deps extends readonly AnyModule[] = []>(def: {
120
+ export declare function defineModule<OwnSlice extends object = Record<never, never>>(): <const Deps extends readonly AnyModule[] = [], R extends OwnSlice = OwnSlice>(def: {
120
121
  name: string;
121
122
  description?: string;
122
123
  modules?: Deps;
123
- boot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
124
+ boot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, NoOverlap<MergeSlices<Deps>, OwnSlice>>): Promise<void> | void | (R & Exact<NoOverlap<MergeSlices<Deps>, OwnSlice>, R>) | Promise<R & Exact<NoOverlap<MergeSlices<Deps>, OwnSlice>, R>>;
124
125
  shutdown?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
125
126
  beforeBoot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
126
127
  afterBoot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
127
128
  beforeShutdown?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
128
129
  afterShutdown?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
129
130
  }) => Module<FullContext<Deps, OwnSlice>, OwnSlice>;
131
+ /**
132
+ * Errors if U has any key not in T by mapping extra keys to never.
133
+ */
134
+ type Exact<T, U> = {
135
+ [K in keyof U]: K extends keyof T ? T[K] : never;
136
+ };
137
+ /**
138
+ * Produces a readable type error message instead of `never`.
139
+ * Msg appears directly in the IDE error output.
140
+ */
141
+ type TypeError<Msg extends string> = {
142
+ readonly __error__: Msg;
143
+ };
144
+ /**
145
+ * Errors if OwnSlice declares a key that already exists in DepCtx.
146
+ * Overlapping keys show a readable message instead of `never`.
147
+ */
148
+ type NoOverlap<DepCtx, OwnSlice> = {
149
+ [K in keyof OwnSlice]: K extends keyof DepCtx ? TypeError<`Key "${K & string}" is already owned by a dependency module`> : OwnSlice[K];
150
+ };
130
151
  /**
131
152
  * Options for start(). Flat intersection — no nesting required:
132
153
  * start([db], { log, afterBoot: async (ctx) => ... })
package/esm/index.js CHANGED
@@ -114,7 +114,9 @@ export default function start(roots_1) {
114
114
  try {
115
115
  yield (beforeEachBoot === null || beforeEachBoot === void 0 ? void 0 : beforeEachBoot(writer, mod));
116
116
  yield ((_a = mod.beforeBoot) === null || _a === void 0 ? void 0 : _a.call(mod, modWriter));
117
- yield ((_b = mod.boot) === null || _b === void 0 ? void 0 : _b.call(mod, modWriter));
117
+ const modCtx = yield ((_b = mod.boot) === null || _b === void 0 ? void 0 : _b.call(mod, modWriter));
118
+ if (modCtx)
119
+ modWriter.set(modCtx);
118
120
  yield (afterEachBoot === null || afterEachBoot === void 0 ? void 0 : afterEachBoot(writer, mod));
119
121
  yield ((_c = mod.afterBoot) === null || _c === void 0 ? void 0 : _c.call(mod, modWriter));
120
122
  booted.push(mod);
@@ -217,7 +219,11 @@ export function rollback(booted, modWriter) {
217
219
  function makeWriter(ctx) {
218
220
  return Object.assign(ctx, {
219
221
  set(key, value) {
222
+ if (typeof key === "object") {
223
+ return Object.assign(ctx, key);
224
+ }
220
225
  ctx[key] = value;
226
+ return ctx;
221
227
  },
222
228
  });
223
229
  }
@@ -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":"8c889829892f006580f4127d8aca6046c7902c4d55e8caefb7e885da10847f39","signature":"02a34f653ee8320a827383436b9bdfbc07b00f901b0894fbfda47a3fc85286f2"},{"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"}
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":"3bd478e1d8330528504d334d98a052dd592414da970e9090aa7d1d949c01a61a","signature":"564bb0ef8a8688b87eda9682a18fdd1224e753852226a5827f822a6066804ac1"},{"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
@@ -27,6 +27,7 @@
27
27
  */
28
28
  export type ContextWriter<S extends object, Sl extends object = S> = Readonly<S> & {
29
29
  set<K extends Exclude<keyof Sl, "set">>(key: K, value: Sl[K]): void;
30
+ set(ctx: Sl): void;
30
31
  };
31
32
  /** Converts U1 | U2 | U3 into U1 & U2 & U3 via contravariance. */
32
33
  type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
@@ -89,7 +90,7 @@ export interface Module<S extends object, Sl extends object = S> {
89
90
  name: string;
90
91
  description?: string;
91
92
  modules?: AnyModule[];
92
- boot?(ctx: ContextWriter<S, Sl>): Promise<void> | void;
93
+ boot?(ctx: ContextWriter<S, Sl>): Promise<void> | void | Sl | Promise<Sl>;
93
94
  shutdown?(ctx: ContextWriter<S, Sl>): Promise<void> | void;
94
95
  /** Called immediately before this module's own boot(). */
95
96
  beforeBoot?(ctx: ContextWriter<S, Sl>): Promise<void> | void;
@@ -116,17 +117,37 @@ export interface Module<S extends object, Sl extends object = S> {
116
117
  *
117
118
  * @param def module definition
118
119
  */
119
- export declare function defineModule<OwnSlice extends object = Record<never, never>>(): <const Deps extends readonly AnyModule[] = []>(def: {
120
+ export declare function defineModule<OwnSlice extends object = Record<never, never>>(): <const Deps extends readonly AnyModule[] = [], R extends OwnSlice = OwnSlice>(def: {
120
121
  name: string;
121
122
  description?: string;
122
123
  modules?: Deps;
123
- boot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
124
+ boot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, NoOverlap<MergeSlices<Deps>, OwnSlice>>): Promise<void> | void | (R & Exact<NoOverlap<MergeSlices<Deps>, OwnSlice>, R>) | Promise<R & Exact<NoOverlap<MergeSlices<Deps>, OwnSlice>, R>>;
124
125
  shutdown?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
125
126
  beforeBoot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
126
127
  afterBoot?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
127
128
  beforeShutdown?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
128
129
  afterShutdown?(ctx: ContextWriter<FullContext<Deps, OwnSlice>, OwnSlice>): Promise<void> | void;
129
130
  }) => Module<FullContext<Deps, OwnSlice>, OwnSlice>;
131
+ /**
132
+ * Errors if U has any key not in T by mapping extra keys to never.
133
+ */
134
+ type Exact<T, U> = {
135
+ [K in keyof U]: K extends keyof T ? T[K] : never;
136
+ };
137
+ /**
138
+ * Produces a readable type error message instead of `never`.
139
+ * Msg appears directly in the IDE error output.
140
+ */
141
+ type TypeError<Msg extends string> = {
142
+ readonly __error__: Msg;
143
+ };
144
+ /**
145
+ * Errors if OwnSlice declares a key that already exists in DepCtx.
146
+ * Overlapping keys show a readable message instead of `never`.
147
+ */
148
+ type NoOverlap<DepCtx, OwnSlice> = {
149
+ [K in keyof OwnSlice]: K extends keyof DepCtx ? TypeError<`Key "${K & string}" is already owned by a dependency module`> : OwnSlice[K];
150
+ };
130
151
  /**
131
152
  * Options for start(). Flat intersection — no nesting required:
132
153
  * start([db], { log, afterBoot: async (ctx) => ... })
package/lib/index.js CHANGED
@@ -121,7 +121,9 @@ function start(roots_1) {
121
121
  try {
122
122
  yield (beforeEachBoot === null || beforeEachBoot === void 0 ? void 0 : beforeEachBoot(writer, mod));
123
123
  yield ((_a = mod.beforeBoot) === null || _a === void 0 ? void 0 : _a.call(mod, modWriter));
124
- yield ((_b = mod.boot) === null || _b === void 0 ? void 0 : _b.call(mod, modWriter));
124
+ const modCtx = yield ((_b = mod.boot) === null || _b === void 0 ? void 0 : _b.call(mod, modWriter));
125
+ if (modCtx)
126
+ modWriter.set(modCtx);
125
127
  yield (afterEachBoot === null || afterEachBoot === void 0 ? void 0 : afterEachBoot(writer, mod));
126
128
  yield ((_c = mod.afterBoot) === null || _c === void 0 ? void 0 : _c.call(mod, modWriter));
127
129
  booted.push(mod);
@@ -224,7 +226,11 @@ function rollback(booted, modWriter) {
224
226
  function makeWriter(ctx) {
225
227
  return Object.assign(ctx, {
226
228
  set(key, value) {
229
+ if (typeof key === "object") {
230
+ return Object.assign(ctx, key);
231
+ }
227
232
  ctx[key] = value;
233
+ return ctx;
228
234
  },
229
235
  });
230
236
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tsdkarc",
3
- "version": "1.0.1",
4
- "description": "Elegant and Fully type-safe composable module library",
3
+ "version": "1.1.1",
4
+ "description": "TsdkArc is an elegant and fully type-safe module composable library",
5
5
  "repository": "tsdk-monorepo/tsdkarc",
6
6
  "bugs": "https://github.com/tsdk-monorepo/tsdkarc/issues",
7
7
  "homepage": "https://arc.tsdk.dev",
@@ -17,7 +17,7 @@
17
17
  "keywords": [
18
18
  "typesafe",
19
19
  "tsdk",
20
- "composable module",
20
+ "module composable",
21
21
  "dependency injection"
22
22
  ],
23
23
  "license": "MIT"