@spaceteams/warp 0.2.0 → 0.3.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/dist/index.d.mts CHANGED
@@ -1,6 +1,20 @@
1
+ //#region src/component/component-meta.d.ts
2
+ type ComponentKind = "repo" | "service" | "usecase" | "client";
3
+ type ComponentMeta = {
4
+ name?: string;
5
+ kind?: ComponentKind;
6
+ tags?: string[];
7
+ };
8
+ //#endregion
1
9
  //#region src/run.d.ts
10
+ type WarpMeta = {
11
+ component?: ComponentMeta;
12
+ componentPath?: string;
13
+ componentKey?: string;
14
+ };
2
15
  type Run<AmbientContext, ScopeContext = unknown, RunOptions = unknown> = AmbientContext & {
3
16
  run: <T>(options: RunOptions, inner: (app: Run<AmbientContext & ScopeContext, ScopeContext, RunOptions>) => Promise<T> | T) => Promise<T> | T;
17
+ warp?: WarpMeta;
4
18
  };
5
19
  //#endregion
6
20
  //#region src/component/index.d.ts
@@ -13,38 +27,45 @@ type ComponentRef<Ctx, ScopeContext, RunOptions, Out> = {
13
27
  readonly __runOptions?: RunOptions;
14
28
  readonly __out?: Out;
15
29
  };
16
- type ComponentFactory<Ctx, ScopeContext, RunOptions, Deps, Out> = (ctx: Run<Ctx & Deps, ScopeContext, RunOptions>) => Out;
30
+ type ComponentFactoryFn<Ctx, ScopeContext, RunOptions, Deps, Out> = (ctx: Run<Ctx & Deps, ScopeContext, RunOptions>) => Out;
31
+ type ComponentFactory<Ctx, ScopeContext, RunOptions, Deps, Out> = ComponentFactoryFn<Ctx, ScopeContext, RunOptions, Deps, Out> & {
32
+ meta?: ComponentMeta;
33
+ };
17
34
  type ComponentInput<Ctx, ScopeContext, RunOptions, Out> = ComponentRef<Ctx, ScopeContext, RunOptions, Out> | Out;
18
35
  type Component<Ctx, ScopeContext, RunOptions, Deps, Out> = ComponentRef<Ctx, ScopeContext, RunOptions, Out> & {
19
36
  factory: ComponentFactory<Ctx, ScopeContext, RunOptions, Deps, Out>;
20
37
  deps?: { [K in keyof Deps]: ComponentRef<Ctx, ScopeContext, RunOptions, Deps[K]> };
21
- name?: string;
38
+ meta?: ComponentMeta;
22
39
  };
23
40
  type ComponentDefinition<Ctx, ScopeContext, RunOptions, Deps, Out> = {
24
41
  factory: ComponentFactory<Ctx, ScopeContext, RunOptions, Deps, Out>;
25
42
  deps?: { [K in keyof Deps]: ComponentInput<Ctx, ScopeContext, RunOptions, Deps[K]> };
26
- name?: string;
43
+ meta?: ComponentMeta;
27
44
  };
28
- type InferComponentParams<T> = T extends Component<infer Ctx, infer ScopeContext, infer RunOptions, infer Deps, infer Out> ? [Ctx, ScopeContext, RunOptions, Deps, Out] : never;
29
- type InferComponentCtx<T> = InferComponentParams<T>[0];
30
- type InferComponentScopeContext<T> = InferComponentParams<T>[1];
31
- type InferComponentRunOptions<T> = InferComponentParams<T>[2];
32
- type InferComponentDeps<T> = InferComponentParams<T>[3];
33
- type InferComponentOut<T> = InferComponentParams<T>[4];
34
45
  declare function brandComponent<T extends object, Ctx, ScopeContext, RunOptions, Out>(obj: T): T & ComponentRef<Ctx, ScopeContext, RunOptions, Out>;
35
46
  declare function isComponent(value: unknown): value is Component<unknown, unknown, unknown, unknown, unknown>;
36
47
  //#endregion
37
48
  //#region src/middleware.d.ts
38
49
  type NoRunOptions = NonNullable<unknown>;
39
50
  type NoScopeContext = NonNullable<unknown>;
40
- type Middleware<AmbientContext, RunOptions = NoRunOptions, ScopeContext = NoScopeContext> = <T>(ctx: AmbientContext, options: Partial<RunOptions>, next: (ctx: AmbientContext & ScopeContext) => Promise<T> | T) => Promise<T> | T;
51
+ type Middleware<AmbientContext, RunOptions = NoRunOptions, ScopeContext = NoScopeContext> = <T>(ctx: AmbientContext, options: Partial<RunOptions>, next: (ctx: AmbientContext & ScopeContext) => Promise<T> | T, warp?: WarpMeta | undefined) => Promise<T> | T;
41
52
  //#endregion
42
- //#region src/runtime/explain.d.ts
53
+ //#region src/explain/index.d.ts
43
54
  type ExplainResult = {
44
55
  name: string | undefined;
56
+ kind?: string;
57
+ tags?: string[];
45
58
  deps?: Record<string, ExplainResult>;
46
59
  };
47
60
  //#endregion
61
+ //#region src/lazy.d.ts
62
+ type Lazy<T, Args extends unknown[] = []> = (...args: Args) => T;
63
+ declare const Lazy: {
64
+ cached<T, Args extends unknown[] = []>(factory: Lazy<T, Args>): Lazy<T, Args>;
65
+ and<S, T, Args extends unknown[] = []>(l: Lazy<S, Args>, r: Lazy<T, Args>): Lazy<S & T, Args>;
66
+ map<S, T, Args extends unknown[] = []>(l: Lazy<S, Args>, fn: (v: S) => T): Lazy<T, Args>;
67
+ };
68
+ //#endregion
48
69
  //#region src/runtime/runtime.d.ts
49
70
  type SafeIntersect<A, B> = A extends undefined ? B : A & B;
50
71
  type OptionalArg<A> = A extends undefined ? [] : [A];
@@ -52,19 +73,22 @@ declare class Runtime<Ctx, ActualContext extends Ctx, ScopeContext, RunOptions,
52
73
  private readonly middleware;
53
74
  private readonly ctx;
54
75
  private readonly resolveFn;
55
- constructor(middleware: Middleware<Ctx, RunOptions, ScopeContext>, ctx: ActualContext);
76
+ constructor(middleware: Middleware<Ctx, RunOptions, ScopeContext>, ctx: Lazy<ActualContext>);
56
77
  provide<Extension>(ext: Extension): Runtime<Ctx, ActualContext & Extension, ScopeContext, RunOptions, Requirements>;
78
+ provideLazy<Extension>(ext: () => Extension): Runtime<Ctx, ActualContext & Extension, ScopeContext, RunOptions, Requirements>;
57
79
  require<Extension>(): Runtime<Ctx, ActualContext, ScopeContext, RunOptions, SafeIntersect<Requirements, Extension>>;
58
80
  resolve: <Deps, Out>(component: Component<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps, Out>, ...requirements: OptionalArg<Requirements>) => Out | Promise<Out>;
59
- get component(): <Deps, F extends ComponentFactory<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps, ReturnType<F>>>(factory: F, deps?: { [T in keyof Deps]: ComponentInput<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps[T]> } | undefined, opts?: {
60
- name?: string | undefined;
61
- } | undefined) => Component<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps, ReturnType<F>>;
62
- get classComponent(): <Deps, Ctor extends new (deps: Run<SafeIntersect<Requirements, ActualContext> & Deps, ScopeContext, RunOptions>) => InstanceType<Ctor>>(ctor: Ctor, deps?: { [K in keyof Deps]: ComponentInput<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps[K]> } | undefined, opts?: {
63
- name?: string | undefined;
64
- } | undefined) => Component<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps, InstanceType<Ctor>>;
65
- explain<Deps, Out>(component: Component<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps, Out>, format: "native"): ExplainResult;
66
- explain<Deps, Out>(component: Component<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps, Out>, format: "ascii"): string;
67
- explain<Deps, Out>(component: Component<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps, Out>, format: "mermaid"): string;
81
+ get component(): <Deps, F extends ComponentFactory<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps, ReturnType<F>>>(factory: F, deps?: { [T in keyof Deps]: ComponentInput<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps[T]> } | undefined, meta?: ComponentMeta | undefined) => Component<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps, ReturnType<F>>;
82
+ get singleton(): <Deps, F extends ComponentFactory<SafeIntersect<Requirements, ActualContext>, {}, RunOptions, Deps, ReturnType<F>>>(factory: F, deps?: { [T in keyof Deps]: ComponentInput<SafeIntersect<Requirements, ActualContext>, {}, RunOptions, Deps[T]> } | undefined, meta?: ComponentMeta | undefined) => Component<SafeIntersect<Requirements, ActualContext>, {}, RunOptions, Deps, ReturnType<F>>;
83
+ get classComponent(): <Deps, Ctor extends (new (deps: Run<SafeIntersect<Requirements, ActualContext> & Deps, ScopeContext, RunOptions>) => InstanceType<Ctor>) & {
84
+ meta?: ComponentMeta | undefined;
85
+ }>(ctor: Ctor, deps?: { [K in keyof Deps]: ComponentInput<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps[K]> } | undefined, meta?: ComponentMeta | undefined) => Component<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps, InstanceType<Ctor>>;
86
+ get classSingleton(): <Deps, Ctor extends (new (deps: Run<SafeIntersect<Requirements, ActualContext> & Deps, {}, RunOptions>) => InstanceType<Ctor>) & {
87
+ meta?: ComponentMeta | undefined;
88
+ }>(ctor: Ctor, deps?: { [K in keyof Deps]: ComponentInput<SafeIntersect<Requirements, ActualContext>, {}, RunOptions, Deps[K]> } | undefined, meta?: ComponentMeta | undefined) => Component<SafeIntersect<Requirements, ActualContext>, {}, RunOptions, Deps, InstanceType<Ctor>>;
89
+ explain<Deps, Out>(component: Component<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps, Out>, format: "native", showMeta?: boolean): ExplainResult;
90
+ explain<Deps, Out>(component: Component<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps, Out>, format: "ascii", showMeta?: boolean): string;
91
+ explain<Deps, Out>(component: Component<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps, Out>, format: "mermaid", showMeta?: boolean): string;
68
92
  explain<Deps, Out>(component: Component<SafeIntersect<Requirements, ActualContext>, ScopeContext, RunOptions, Deps, Out>): ExplainResult;
69
93
  }
70
94
  //#endregion
@@ -74,10 +98,36 @@ declare class RuntimeBuilder<AmbientContext, ScopeContext = unknown, Options = u
74
98
  constructor(middlewares?: Middleware<AmbientContext, Options, ScopeContext>[]);
75
99
  use<A, S, H>(mw: Middleware<A, H, S>): RuntimeBuilder<AmbientContext & A, ScopeContext & S, Options & H>;
76
100
  provide<ActualCtx extends AmbientContext>(ctx: ActualCtx): Runtime<AmbientContext, ActualCtx, ScopeContext, Options, undefined>;
101
+ provideLazy<ActualCtx extends AmbientContext>(ctx: () => ActualCtx): Runtime<AmbientContext, ActualCtx, ScopeContext, Options, undefined>;
77
102
  private buildMiddleware;
78
103
  }
79
104
  //#endregion
80
105
  //#region src/runtime/index.d.ts
81
106
  declare function buildRuntime(): RuntimeBuilder<Record<string, unknown>, unknown>;
82
107
  //#endregion
83
- export { Component, ComponentDefinition, ComponentFactory, ComponentInput, ComponentRef, InferComponentCtx, InferComponentDeps, InferComponentOut, InferComponentParams, InferComponentRunOptions, InferComponentScopeContext, Middleware, NoDeps, NoRunOptions, NoScopeContext, Run, brandComponent, buildRuntime, isComponent };
108
+ //#region src/semantic/callable.d.ts
109
+ type Callable<Ctx, ScopeContext, Args extends unknown[], Result, RunOptions> = ComponentFactory<Ctx, ScopeContext, RunOptions, unknown, (...args: Args) => Promise<Result>>;
110
+ declare function callable<Ctx, Args extends unknown[], Result, RunOptions = NoRunOptions, ScopeContext = NoScopeContext>(options: RunOptions & ComponentMeta, fn: (app: Run<Ctx, ScopeContext, RunOptions>) => (...args: Args) => Promise<Result>): Callable<Ctx, ScopeContext, Args, Result, RunOptions>;
111
+ type InferCallable<T> = T extends Callable<infer _Ctx, infer _ScopeContext, infer Args, infer Result, infer _RunOptions> ? (...args: Args) => Promise<Result> : never;
112
+ //#endregion
113
+ //#region src/semantic/client.d.ts
114
+ type Client<Ctx, ScopeContext, Out, RunOptions> = ComponentFactory<Ctx, ScopeContext, RunOptions, unknown, Out>;
115
+ declare function client<Ctx, Out, RunOptions = NoRunOptions, ScopeContext = NoScopeContext>(options: Omit<ComponentMeta, "kind">, fn: (app: Run<Ctx, ScopeContext, RunOptions>) => Out): Client<Ctx, ScopeContext, Out, RunOptions>;
116
+ type InferClient<T> = T extends Client<infer _Ctx, infer _ScopeContext, infer Out, infer _RunOptions> ? Out : never;
117
+ //#endregion
118
+ //#region src/semantic/repo.d.ts
119
+ type Repo<Ctx, ScopeContext, Out, RunOptions> = ComponentFactory<Ctx, ScopeContext, RunOptions, unknown, Out>;
120
+ declare function repo<Ctx, Out, RunOptions = NoRunOptions, ScopeContext = NoScopeContext>(options: Omit<ComponentMeta, "kind">, fn: (app: Run<Ctx, ScopeContext, RunOptions>) => Out): Repo<Ctx, ScopeContext, Out, RunOptions>;
121
+ type InferRepo<T> = T extends Repo<infer _Ctx, infer _ScopeContext, infer Out, infer _RunOptions> ? Out : never;
122
+ //#endregion
123
+ //#region src/semantic/service.d.ts
124
+ type Service<Ctx, ScopeContext, Out, RunOptions> = ComponentFactory<Ctx, ScopeContext, RunOptions, unknown, Out>;
125
+ declare function service<Ctx, Out, RunOptions = NoRunOptions, ScopeContext = NoScopeContext>(options: Omit<ComponentMeta, "kind"> | undefined, fn: (app: Run<Ctx, ScopeContext, RunOptions>) => Out): Service<Ctx, ScopeContext, Out, RunOptions>;
126
+ type InferService<T> = T extends Service<infer _Ctx, infer _ScopeContext, infer Out, infer _RunOptions> ? Out : never;
127
+ //#endregion
128
+ //#region src/semantic/usecase.d.ts
129
+ type Usecase<Ctx, ScopeContext, Args extends unknown[], Result, RunOptions> = Callable<Ctx, ScopeContext, Args, Result, RunOptions>;
130
+ declare function usecase<Ctx, Args extends unknown[], Result, RunOptions = NoRunOptions, ScopeContext = NoScopeContext>(options: RunOptions & Omit<ComponentMeta, "kind">, fn: (app: Run<Ctx, ScopeContext, RunOptions>) => (...args: Args) => Promise<Result>): Usecase<Ctx, ScopeContext, Args, Result, RunOptions>;
131
+ type InferUsecase<T> = InferCallable<T>;
132
+ //#endregion
133
+ export { Callable, Client, Component, ComponentDefinition, ComponentFactory, ComponentFactoryFn, ComponentInput, ComponentKind, ComponentMeta, ComponentRef, InferCallable, InferClient, InferRepo, InferService, InferUsecase, Middleware, NoDeps, NoRunOptions, NoScopeContext, Repo, Run, Service, Usecase, WarpMeta, brandComponent, buildRuntime, callable, client, isComponent, repo, service, usecase };
package/dist/index.mjs CHANGED
@@ -13,31 +13,148 @@ function isComponent(value) {
13
13
  return typeof value === "object" && value !== null && value[COMPONENT] === true;
14
14
  }
15
15
  //#endregion
16
+ //#region src/lazy.ts
17
+ const Lazy = {
18
+ cached(factory) {
19
+ let cached;
20
+ return (...args) => {
21
+ if (cached === void 0) cached = factory(...args);
22
+ return cached;
23
+ };
24
+ },
25
+ and(l, r) {
26
+ return (...args) => ({
27
+ ...l(...args),
28
+ ...r(...args)
29
+ });
30
+ },
31
+ map(l, fn) {
32
+ return (...args) => fn(l(...args));
33
+ }
34
+ };
35
+ //#endregion
16
36
  //#region src/component/class-component.ts
17
37
  function defineClassComponent() {
18
- return (ctor, deps, opts) => {
38
+ return (ctor, deps, meta) => {
19
39
  const factory = (ctx) => {
20
40
  return new ctor(ctx);
21
41
  };
22
42
  return brandComponent({
23
43
  factory,
24
44
  deps,
25
- name: opts?.name ?? ctor.name
45
+ meta: {
46
+ name: (meta?.name ?? ctor.meta?.name) || void 0,
47
+ tags: (meta?.tags ?? ctor.meta?.tags) || void 0,
48
+ kind: (meta?.kind ?? ctor.meta?.kind) || void 0
49
+ }
50
+ });
51
+ };
52
+ }
53
+ //#endregion
54
+ //#region src/component/class-singleton.ts
55
+ function defineClassSingleton() {
56
+ return (ctor, deps, meta) => {
57
+ const factory = (ctx) => {
58
+ return new ctor(ctx);
59
+ };
60
+ return brandComponent({
61
+ factory: Lazy.cached(factory),
62
+ deps,
63
+ meta: {
64
+ name: (meta?.name ?? ctor.meta?.name) || void 0,
65
+ tags: (meta?.tags ?? ctor.meta?.tags) || void 0,
66
+ kind: (meta?.kind ?? ctor.meta?.kind) || void 0
67
+ }
26
68
  });
27
69
  };
28
70
  }
29
71
  //#endregion
30
72
  //#region src/component/functional-component.ts
31
73
  function defineFunctionalComponent() {
32
- return (factory, deps, opts) => {
74
+ return (factory, deps, meta) => {
33
75
  return brandComponent({
34
76
  factory,
35
77
  deps,
36
- name: opts?.name
78
+ meta: {
79
+ name: (meta?.name ?? factory.meta?.name) || void 0,
80
+ tags: (meta?.tags ?? factory.meta?.tags) || void 0,
81
+ kind: (meta?.kind ?? factory.meta?.kind) || void 0
82
+ }
83
+ });
84
+ };
85
+ }
86
+ //#endregion
87
+ //#region src/component/functional-singleton.ts
88
+ function defineFunctionalSingleton() {
89
+ return (factory, deps, meta) => {
90
+ return brandComponent({
91
+ factory: Lazy.cached(factory),
92
+ deps,
93
+ meta: {
94
+ name: (meta?.name ?? factory.meta?.name) || void 0,
95
+ tags: (meta?.tags ?? factory.meta?.tags) || void 0,
96
+ kind: (meta?.kind ?? factory.meta?.kind) || void 0
97
+ }
37
98
  });
38
99
  };
39
100
  }
40
101
  //#endregion
102
+ //#region src/explain/index.ts
103
+ const explain = (c) => {
104
+ const deps = {};
105
+ for (const [key, comp] of Object.entries(c.deps ?? {})) deps[key] = explain(comp);
106
+ return {
107
+ name: c.meta?.name,
108
+ kind: c.meta?.kind,
109
+ tags: c.meta?.tags,
110
+ deps
111
+ };
112
+ };
113
+ //#endregion
114
+ //#region src/explain/ascii.ts
115
+ const formatExplainLabel = (node, showMeta, edgeLabel) => {
116
+ let label = edgeLabel ?? node.name ?? "unnamed";
117
+ if (edgeLabel && showMeta && node.name) label += ` -> ${node.name}`;
118
+ if (showMeta && node.kind) label += ` [${node.kind}]`;
119
+ if (showMeta && node.tags?.length) label += ` {${node.tags.join(", ")}}`;
120
+ return label;
121
+ };
122
+ const toAsciiTree = (result, prefix = "", isLast = true, showMeta = false, edgeLabel) => {
123
+ const line = `${prefix}${isLast ? "└── " : "├── "}${formatExplainLabel(result, showMeta, edgeLabel)}`;
124
+ const deps = Object.entries(result.deps ?? {});
125
+ if (deps.length === 0) return line;
126
+ const childPrefix = prefix + (isLast ? " " : "│ ");
127
+ return [line, ...deps.map(([key, dep], index) => toAsciiTree(dep, childPrefix, index === deps.length - 1, showMeta, key))].join("\n");
128
+ };
129
+ //#endregion
130
+ //#region src/explain/mermaid.ts
131
+ const sanitizeMermaidId = (value) => value.replace(/[^a-zA-Z0-9_]/g, "_");
132
+ const escapeMermaidLabel = (value) => value.replace(/"/g, "\\\"");
133
+ const formatMermaidLabel = (node, showMeta, fallbackName) => {
134
+ let label = node.name ?? fallbackName;
135
+ if (showMeta) {
136
+ const metaParts = [];
137
+ if (node.kind) metaParts.push(node.kind);
138
+ if (node.tags?.length) metaParts.push(node.tags.join(", "));
139
+ if (metaParts.length > 0) label += `<br/>[${metaParts.join(" | ")}]`;
140
+ }
141
+ return escapeMermaidLabel(label);
142
+ };
143
+ const collectMermaidLines = (node, nodeId, showMeta, fallbackName) => {
144
+ const lines = [];
145
+ lines.push(` ${nodeId}["${formatMermaidLabel(node, showMeta, fallbackName)}"]`);
146
+ Object.entries(node.deps ?? {}).forEach(([key, dep], index) => {
147
+ const childId = sanitizeMermaidId(`${nodeId}__${key}__${dep.name ?? key ?? `node_${index}`}`);
148
+ lines.push(` ${nodeId} -->|${escapeMermaidLabel(key)}| ${childId}`);
149
+ lines.push(...collectMermaidLines(dep, childId, showMeta, key));
150
+ });
151
+ return lines;
152
+ };
153
+ const toMermaid = (result, showMeta = false) => {
154
+ const rootName = result.name ?? "root";
155
+ return ["graph TD", ...collectMermaidLines(result, sanitizeMermaidId(rootName), showMeta, rootName)].join("\n");
156
+ };
157
+ //#endregion
41
158
  //#region src/runtime/create-resolver.ts
42
159
  function createResolver(mw) {
43
160
  return (root, ctx) => {
@@ -62,7 +179,7 @@ function createResolver(mw) {
62
179
  get() {
63
180
  if (cache.has(depName)) return cache.get(depName);
64
181
  checkCyclicDependency(depPath);
65
- const out = withStackTracking(depPath, () => bindInScope(depComp, scopeCtx, depPath));
182
+ const out = withStackTracking(depPath, () => bindInScope(depComp, scopeCtx, depPath, depName));
66
183
  cache.set(depName, out);
67
184
  return out;
68
185
  }
@@ -71,77 +188,39 @@ function createResolver(mw) {
71
188
  const attachDependencies = (runCtx, deps, scopeCtx, pathPrefix, cache) => {
72
189
  for (const [depName, depComp] of Object.entries(deps)) defineDependencyProperty(runCtx, depName, pathPrefix ? `${pathPrefix}.${depName}` : depName, depComp, scopeCtx, cache);
73
190
  };
74
- const createBaseRunContext = (scopeCtx) => {
191
+ const createBaseRunContext = (scopeCtx, warp) => {
75
192
  return {
76
193
  ...scopeCtx,
77
- run: (nestedOptions, nestedInner) => runWithContext(scopeCtx, nestedOptions, nestedInner)
194
+ warp,
195
+ run: (nestedOptions, nestedInner) => runWithContext(scopeCtx, nestedOptions, nestedInner, warp)
78
196
  };
79
197
  };
80
- const bindInScope = (comp, scopeCtx, path) => {
198
+ const bindInScope = (comp, scopeCtx, path, depName) => {
81
199
  const localDepCache = /* @__PURE__ */ new Map();
82
- const runCtx = createBaseRunContext(scopeCtx);
83
200
  if (!isComponent(comp)) return comp;
201
+ const runCtx = createBaseRunContext(scopeCtx, {
202
+ component: comp.meta,
203
+ componentPath: path,
204
+ componentKey: depName
205
+ });
84
206
  attachDependencies(runCtx, comp.deps ?? {}, scopeCtx, path, localDepCache);
85
207
  return comp.factory(runCtx);
86
208
  };
87
209
  const makeRootRunContext = (scopeCtx) => {
88
210
  const rootCache = /* @__PURE__ */ new Map();
89
- const runCtx = createBaseRunContext(scopeCtx);
211
+ const runCtx = createBaseRunContext(scopeCtx, { component: root.meta });
90
212
  attachDependencies(runCtx, root.deps ?? {}, scopeCtx, "", rootCache);
91
213
  return runCtx;
92
214
  };
93
- const runWithContext = async (currentCtx, options, inner) => {
215
+ const runWithContext = async (currentCtx, options, inner, warp) => {
94
216
  return await mw(currentCtx, options, (scopedCtx) => {
95
217
  return inner(makeRootRunContext(scopedCtx));
96
- });
218
+ }, warp);
97
219
  };
98
220
  return root.factory(makeRootRunContext(ctx));
99
221
  };
100
222
  }
101
223
  //#endregion
102
- //#region src/runtime/explain.ts
103
- const explain = (c) => {
104
- const deps = {};
105
- for (const [key, comp] of Object.entries(c.deps ?? {})) deps[key] = explain(comp);
106
- return {
107
- name: c.name,
108
- deps
109
- };
110
- };
111
- const toAsciiTree = (result, prefix = "", isLast = true) => {
112
- const lines = [];
113
- const connector = isLast ? "└── " : "├── ";
114
- lines.push(prefix + connector + result.name);
115
- const deps = Object.entries(result.deps ?? {});
116
- const newPrefix = prefix + (isLast ? " " : "│ ");
117
- deps.forEach(([key, dep], index) => {
118
- const isLastDep = index === deps.length - 1;
119
- const connector = isLastDep ? "└── " : "├── ";
120
- lines.push(`${newPrefix}${connector}[${key}]`);
121
- const childLines = toAsciiTree(dep, newPrefix + (isLastDep ? " " : "│ "), true).split("\n").slice(1);
122
- lines.push(...childLines);
123
- });
124
- return lines.join("\n");
125
- };
126
- const toMermaid = (result, nodeId, isRoot = true) => {
127
- const lines = [];
128
- if (isRoot) {
129
- lines.push("graph TD");
130
- nodeId = result.name ?? "root";
131
- }
132
- const sanitizedId = nodeId.replace(/[^a-zA-Z0-9_]/g, "_");
133
- if (isRoot) lines.push(` ${sanitizedId}["${result.name}"]`);
134
- Object.entries(result.deps ?? {}).forEach(([key, dep]) => {
135
- const sanitizedDepId = (dep.name ? `${sanitizedId}_${dep.name}` : `${sanitizedId}_${key}`).replace(/[^a-zA-Z0-9_]/g, "_");
136
- const displayName = dep.name ?? key;
137
- lines.push(` ${sanitizedDepId}["${displayName}"]`);
138
- lines.push(` ${sanitizedId} -->|${key}| ${sanitizedDepId}`);
139
- const childLines = toMermaid(dep, sanitizedDepId, false).split("\n").slice(1);
140
- lines.push(...childLines);
141
- });
142
- return lines.join("\n");
143
- };
144
- //#endregion
145
224
  //#region src/runtime/runtime.ts
146
225
  var Runtime = class Runtime {
147
226
  resolveFn;
@@ -151,10 +230,10 @@ var Runtime = class Runtime {
151
230
  this.resolveFn = createResolver(middleware);
152
231
  }
153
232
  provide(ext) {
154
- return new Runtime(this.middleware, {
155
- ...this.ctx,
156
- ...ext
157
- });
233
+ return new Runtime(this.middleware, Lazy.and(this.ctx, () => ext));
234
+ }
235
+ provideLazy(ext) {
236
+ return new Runtime(this.middleware, Lazy.and(this.ctx, Lazy.cached(ext)));
158
237
  }
159
238
  require() {
160
239
  return new Runtime(this.middleware, this.ctx);
@@ -162,22 +241,28 @@ var Runtime = class Runtime {
162
241
  resolve = (component, ...requirements) => {
163
242
  const req = requirements[0];
164
243
  const ctx = req ? {
165
- ...this.ctx,
244
+ ...this.ctx(),
166
245
  ...req
167
- } : { ...this.ctx };
246
+ } : { ...this.ctx() };
168
247
  return this.resolveFn(component, ctx);
169
248
  };
170
249
  get component() {
171
250
  return defineFunctionalComponent();
172
251
  }
252
+ get singleton() {
253
+ return defineFunctionalSingleton();
254
+ }
173
255
  get classComponent() {
174
256
  return defineClassComponent();
175
257
  }
176
- explain(component, format = "native") {
258
+ get classSingleton() {
259
+ return defineClassSingleton();
260
+ }
261
+ explain(component, format = "native", showMeta = false) {
177
262
  const result = explain(component);
178
263
  switch (format) {
179
- case "ascii": return toAsciiTree(result);
180
- case "mermaid": return toMermaid(result);
264
+ case "ascii": return toAsciiTree(result, "", true, showMeta);
265
+ case "mermaid": return toMermaid(result, showMeta);
181
266
  case "native": return result;
182
267
  }
183
268
  }
@@ -193,7 +278,10 @@ var RuntimeBuilder = class RuntimeBuilder {
193
278
  return new RuntimeBuilder([...this.middlewares, mw]);
194
279
  }
195
280
  provide(ctx) {
196
- return new Runtime(this.buildMiddleware(), ctx);
281
+ return new Runtime(this.buildMiddleware(), () => ctx);
282
+ }
283
+ provideLazy(ctx) {
284
+ return new Runtime(this.buildMiddleware(), Lazy.cached(ctx));
197
285
  }
198
286
  buildMiddleware() {
199
287
  const middlewares = this.middlewares;
@@ -216,4 +304,58 @@ function buildRuntime() {
216
304
  return new RuntimeBuilder();
217
305
  }
218
306
  //#endregion
219
- export { brandComponent, buildRuntime, isComponent };
307
+ //#region src/semantic/callable.ts
308
+ function callable(options, fn) {
309
+ const factory = (ctx) => (...args) => {
310
+ return ctx.run(options, (inner) => fn(inner)(...args));
311
+ };
312
+ Object.assign(factory, { meta: {
313
+ kind: options.kind,
314
+ name: options.name,
315
+ tags: options.tags
316
+ } });
317
+ return factory;
318
+ }
319
+ //#endregion
320
+ //#region src/semantic/client.ts
321
+ function client(options, fn) {
322
+ const factory = fn;
323
+ Object.assign(factory, { meta: {
324
+ kind: "client",
325
+ name: options.name,
326
+ tags: options.tags
327
+ } });
328
+ return factory;
329
+ }
330
+ //#endregion
331
+ //#region src/semantic/repo.ts
332
+ function repo(options, fn) {
333
+ const factory = fn;
334
+ Object.assign(factory, { meta: {
335
+ kind: "repo",
336
+ name: options.name,
337
+ tags: options.tags
338
+ } });
339
+ return factory;
340
+ }
341
+ //#endregion
342
+ //#region src/semantic/service.ts
343
+ function service(options = {}, fn) {
344
+ const factory = fn;
345
+ Object.assign(factory, { meta: {
346
+ kind: "service",
347
+ name: options.name,
348
+ tags: options.tags
349
+ } });
350
+ return factory;
351
+ }
352
+ //#endregion
353
+ //#region src/semantic/usecase.ts
354
+ function usecase(options, fn) {
355
+ return callable({
356
+ kind: "usecase",
357
+ ...options
358
+ }, fn);
359
+ }
360
+ //#endregion
361
+ export { brandComponent, buildRuntime, callable, client, isComponent, repo, service, usecase };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spaceteams/warp",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Type-safe composition with execution scopes ",
5
5
  "type": "module",
6
6
  "files": [