@wyrly/next 1.0.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.
Files changed (69) hide show
  1. package/LICENSE +202 -0
  2. package/esm/_dnt.shims.d.ts +6 -0
  3. package/esm/_dnt.shims.d.ts.map +1 -0
  4. package/esm/_dnt.shims.js +61 -0
  5. package/esm/core/container.d.ts +54 -0
  6. package/esm/core/container.d.ts.map +1 -0
  7. package/esm/core/container.js +282 -0
  8. package/esm/core/decorators.d.ts +8 -0
  9. package/esm/core/decorators.d.ts.map +1 -0
  10. package/esm/core/decorators.js +9 -0
  11. package/esm/core/errors.d.ts +33 -0
  12. package/esm/core/errors.d.ts.map +1 -0
  13. package/esm/core/errors.js +109 -0
  14. package/esm/core/graph.d.ts +26 -0
  15. package/esm/core/graph.d.ts.map +1 -0
  16. package/esm/core/graph.js +75 -0
  17. package/esm/core/graph_format.d.ts +17 -0
  18. package/esm/core/graph_format.d.ts.map +1 -0
  19. package/esm/core/graph_format.js +49 -0
  20. package/esm/core/i18n.d.ts +15 -0
  21. package/esm/core/i18n.d.ts.map +1 -0
  22. package/esm/core/i18n.js +122 -0
  23. package/esm/core/internal_keys.d.ts +6 -0
  24. package/esm/core/internal_keys.d.ts.map +1 -0
  25. package/esm/core/internal_keys.js +18 -0
  26. package/esm/core/lifetime.d.ts +2 -0
  27. package/esm/core/lifetime.d.ts.map +1 -0
  28. package/esm/core/lifetime.js +1 -0
  29. package/esm/core/metadata.d.ts +10 -0
  30. package/esm/core/metadata.d.ts.map +1 -0
  31. package/esm/core/metadata.js +7 -0
  32. package/esm/core/mod.d.ts +14 -0
  33. package/esm/core/mod.d.ts.map +1 -0
  34. package/esm/core/mod.js +7 -0
  35. package/esm/core/provider.d.ts +42 -0
  36. package/esm/core/provider.d.ts.map +1 -0
  37. package/esm/core/provider.js +69 -0
  38. package/esm/core/scope.d.ts +10 -0
  39. package/esm/core/scope.d.ts.map +1 -0
  40. package/esm/core/scope.js +1 -0
  41. package/esm/core/token.d.ts +12 -0
  42. package/esm/core/token.d.ts.map +1 -0
  43. package/esm/core/token.js +7 -0
  44. package/esm/core/types_decorator.d.ts +11 -0
  45. package/esm/core/types_decorator.d.ts.map +1 -0
  46. package/esm/core/types_decorator.js +1 -0
  47. package/esm/core/validate.d.ts +19 -0
  48. package/esm/core/validate.d.ts.map +1 -0
  49. package/esm/core/validate.js +127 -0
  50. package/esm/next/mod.d.ts +6 -0
  51. package/esm/next/mod.d.ts.map +1 -0
  52. package/esm/next/mod.js +4 -0
  53. package/esm/next/server_di.d.ts +14 -0
  54. package/esm/next/server_di.d.ts.map +1 -0
  55. package/esm/next/server_di.js +20 -0
  56. package/esm/next/tokens.d.ts +4 -0
  57. package/esm/next/tokens.d.ts.map +1 -0
  58. package/esm/next/tokens.js +3 -0
  59. package/esm/next/types.d.ts +19 -0
  60. package/esm/next/types.d.ts.map +1 -0
  61. package/esm/next/types.js +1 -0
  62. package/esm/next/with_action_di.d.ts +11 -0
  63. package/esm/next/with_action_di.d.ts.map +1 -0
  64. package/esm/next/with_action_di.js +19 -0
  65. package/esm/next/with_di.d.ts +14 -0
  66. package/esm/next/with_di.d.ts.map +1 -0
  67. package/esm/next/with_di.js +24 -0
  68. package/esm/package.json +3 -0
  69. package/package.json +33 -0
@@ -0,0 +1,69 @@
1
+ import { getInjectableMetadata } from "./metadata.js";
2
+ import { graphNodeId, tokenLabel } from "./internal_keys.js";
3
+ import { InvalidProviderError } from "./errors.js";
4
+ export function normalizeProvider(token, provider) {
5
+ if ("useValue" in provider) {
6
+ const lt = provider.lifetime ?? "singleton";
7
+ if (lt !== "singleton") {
8
+ throw new InvalidProviderError("useValue lifetime must be singleton only.");
9
+ }
10
+ return {
11
+ token,
12
+ providerType: "value",
13
+ deps: [],
14
+ lifetime: "singleton",
15
+ displayName: tokenLabel(token),
16
+ useValue: provider.useValue,
17
+ };
18
+ }
19
+ if ("useFactory" in provider) {
20
+ const deps = provider.deps ?? [];
21
+ const lifetime = provider.lifetime ?? "singleton";
22
+ return {
23
+ token,
24
+ providerType: "factory",
25
+ deps,
26
+ lifetime,
27
+ displayName: tokenLabel(token),
28
+ useFactory: provider.useFactory,
29
+ };
30
+ }
31
+ if ("useExisting" in provider) {
32
+ const lifetime = provider.lifetime ?? "singleton";
33
+ return {
34
+ token,
35
+ providerType: "existing",
36
+ deps: [provider.useExisting],
37
+ lifetime,
38
+ displayName: tokenLabel(token),
39
+ useExisting: provider.useExisting,
40
+ };
41
+ }
42
+ if ("useClass" in provider) {
43
+ const meta = getInjectableMetadata(provider.useClass);
44
+ const deps = provider.deps ?? meta?.deps ?? [];
45
+ const lifetime = provider.lifetime ?? meta?.lifetime ?? "singleton";
46
+ return {
47
+ token,
48
+ providerType: "class",
49
+ deps,
50
+ lifetime,
51
+ displayName: graphNodeId(provider.useClass),
52
+ useClass: provider.useClass,
53
+ };
54
+ }
55
+ throw new InvalidProviderError("Unknown provider shape.");
56
+ }
57
+ export function syntheticClassProvider(token) {
58
+ const meta = getInjectableMetadata(token);
59
+ const deps = meta?.deps ?? [];
60
+ const lifetime = meta?.lifetime ?? "singleton";
61
+ return {
62
+ token,
63
+ providerType: "class",
64
+ deps,
65
+ lifetime,
66
+ displayName: graphNodeId(token),
67
+ useClass: token,
68
+ };
69
+ }
@@ -0,0 +1,10 @@
1
+ import type { Provider } from "./provider.js";
2
+ import type { InjectionToken } from "./token.js";
3
+ export interface Scope {
4
+ resolve<T>(token: InjectionToken<T>): T;
5
+ register<T>(token: InjectionToken<T>, provider: Provider<T>): void;
6
+ set<T>(token: InjectionToken<T>, value: T): void;
7
+ dispose(): Promise<void>;
8
+ isDisposed(): boolean;
9
+ }
10
+ //# sourceMappingURL=scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/core/scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,KAAK;IACpB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACxC,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACnE,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACjD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,UAAU,IAAI,OAAO,CAAC;CACvB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ /** Typed DI token (at runtime only `id` and `name` carry meaning) */
2
+ export interface Token<T> {
3
+ readonly kind: "token";
4
+ readonly id: symbol;
5
+ readonly name: string;
6
+ readonly __type?: T;
7
+ }
8
+ /** Class constructor used as an injection token */
9
+ export type ClassToken<T> = abstract new (...args: never[]) => T;
10
+ export type InjectionToken<T> = Token<T> | ClassToken<T>;
11
+ export declare function token<T>(name: string): Token<T>;
12
+ //# sourceMappingURL=token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/core/token.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,MAAM,WAAW,KAAK,CAAC,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;CACrB;AAED,mDAAmD;AACnD,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAEjE,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;AAEzD,wBAAgB,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAM/C"}
@@ -0,0 +1,7 @@
1
+ export function token(name) {
2
+ return {
3
+ kind: "token",
4
+ id: Symbol(name),
5
+ name,
6
+ };
7
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Minimal surface compatible with Deno / TypeScript ClassDecoratorContext.
3
+ * Defined locally so types work in environments without DOM in `lib`.
4
+ */
5
+ export interface ClassDecoratorContext<T extends abstract new (...args: never[]) => unknown = abstract new (...args: never[]) => unknown> {
6
+ readonly kind: "class";
7
+ readonly name: string | undefined;
8
+ readonly metadata: unknown;
9
+ addInitializer(initializer: (this: T) => void): void;
10
+ }
11
+ //# sourceMappingURL=types_decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types_decorator.d.ts","sourceRoot":"","sources":["../../src/core/types_decorator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,qBAAqB,CACpC,CAAC,SAAS,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,GAAG,QAAQ,MAC7D,GAAG,IAAI,EAAE,KAAK,EAAE,KACb,OAAO;IAEZ,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;CACtD"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import type { NormalizedProvider } from "./provider.js";
2
+ import { type Locale } from "./i18n.js";
3
+ export interface ValidationIssue {
4
+ severity: "error" | "warning";
5
+ code: string;
6
+ message: string;
7
+ }
8
+ export interface ValidationResult {
9
+ ok: boolean;
10
+ issues: ValidationIssue[];
11
+ }
12
+ export interface ValidateOptions {
13
+ locale?: Locale;
14
+ }
15
+ /**
16
+ * Build a graph from registered providers and check cycles, lifetimes, and orphans.
17
+ */
18
+ export declare function validateNormalizedProviders(providers: NormalizedProvider<unknown>[], options?: ValidateOptions): ValidationResult;
19
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/core/validate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAIxD,OAAO,EACL,KAAK,MAAM,EAIZ,MAAM,WAAW,CAAC;AAEnB,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAiDD;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,kBAAkB,CAAC,OAAO,CAAC,EAAE,EACxC,OAAO,CAAC,EAAE,eAAe,GACxB,gBAAgB,CAiGlB"}
@@ -0,0 +1,127 @@
1
+ import { buildGraph } from "./graph.js";
2
+ import { getInjectableMetadata } from "./metadata.js";
3
+ import { graphNodeId } from "./internal_keys.js";
4
+ import { resolveLocale, validationMessage, } from "./i18n.js";
5
+ function providerMap(providers) {
6
+ const m = new Map();
7
+ for (const p of providers) {
8
+ m.set(graphNodeId(p.token), p);
9
+ }
10
+ return m;
11
+ }
12
+ function isResolvableDependency(dep, byKey) {
13
+ const id = graphNodeId(dep);
14
+ if (byKey.has(id))
15
+ return true;
16
+ if (typeof dep === "function" && getInjectableMetadata(dep))
17
+ return true;
18
+ return false;
19
+ }
20
+ function lifetimeForDepToken(dep, byKey) {
21
+ const id = graphNodeId(dep);
22
+ const hit = byKey.get(id);
23
+ if (hit)
24
+ return hit.lifetime;
25
+ if (typeof dep === "function") {
26
+ const meta = getInjectableMetadata(dep);
27
+ if (meta)
28
+ return meta.lifetime ?? "singleton";
29
+ }
30
+ return undefined;
31
+ }
32
+ function issue(severity, code, params, locale) {
33
+ return {
34
+ severity,
35
+ code,
36
+ message: validationMessage(code, params, locale),
37
+ };
38
+ }
39
+ /**
40
+ * Build a graph from registered providers and check cycles, lifetimes, and orphans.
41
+ */
42
+ export function validateNormalizedProviders(providers, options) {
43
+ const locale = resolveLocale(options);
44
+ const issues = [];
45
+ const graph = buildGraph(providers);
46
+ const byId = providerMap(providers);
47
+ for (const p of providers) {
48
+ const fromLt = p.lifetime;
49
+ const fromId = graphNodeId(p.token);
50
+ for (const dep of p.deps) {
51
+ if (!isResolvableDependency(dep, byId)) {
52
+ issues.push(issue("error", "unresolved_dependency", {
53
+ fromId,
54
+ depId: graphNodeId(dep),
55
+ }, locale));
56
+ continue;
57
+ }
58
+ const toLt = lifetimeForDepToken(dep, byId);
59
+ if (toLt === undefined)
60
+ continue;
61
+ if (fromLt === "singleton" && toLt === "scoped") {
62
+ issues.push(issue("error", "singleton_depends_on_scoped", {
63
+ fromId,
64
+ depId: graphNodeId(dep),
65
+ }, locale));
66
+ }
67
+ if (fromLt === "transient" && toLt === "scoped") {
68
+ issues.push(issue("warning", "transient_depends_on_scoped", {
69
+ fromId,
70
+ depId: graphNodeId(dep),
71
+ }, locale));
72
+ }
73
+ }
74
+ }
75
+ const adj = new Map();
76
+ for (const e of graph.edges) {
77
+ if (!adj.has(e.from))
78
+ adj.set(e.from, []);
79
+ adj.get(e.from).push(e.to);
80
+ }
81
+ const visiting = new Set();
82
+ const visited = new Set();
83
+ const dfs = (id, stack) => {
84
+ if (visiting.has(id)) {
85
+ const idx = stack.indexOf(id);
86
+ return idx >= 0 ? [...stack.slice(idx), id] : [...stack, id];
87
+ }
88
+ if (visited.has(id))
89
+ return null;
90
+ visiting.add(id);
91
+ stack.push(id);
92
+ for (const to of adj.get(id) ?? []) {
93
+ const c = dfs(to, stack);
94
+ if (c)
95
+ return c;
96
+ }
97
+ stack.pop();
98
+ visiting.delete(id);
99
+ visited.add(id);
100
+ return null;
101
+ };
102
+ for (const n of graph.nodes) {
103
+ if (visited.has(n.id))
104
+ continue;
105
+ const cycle = dfs(n.id, []);
106
+ if (cycle && cycle.length > 0) {
107
+ issues.push(issue("error", "circular_dependency", {
108
+ fromId: "",
109
+ depId: "",
110
+ cycle: cycle.join(" -> "),
111
+ }, locale));
112
+ break;
113
+ }
114
+ }
115
+ const hasIncoming = new Set();
116
+ for (const e of graph.edges) {
117
+ hasIncoming.add(e.to);
118
+ }
119
+ for (const p of providers) {
120
+ const id = graphNodeId(p.token);
121
+ if (!hasIncoming.has(id)) {
122
+ issues.push(issue("warning", "unused_provider", { fromId: id, depId: "" }, locale));
123
+ }
124
+ }
125
+ const errors = issues.filter((i) => i.severity === "error");
126
+ return { ok: errors.length === 0, issues };
127
+ }
@@ -0,0 +1,6 @@
1
+ export { withDI } from "./with_di.js";
2
+ export { withActionDI } from "./with_action_di.js";
3
+ export { createServerDI } from "./server_di.js";
4
+ export { NextRequestToken } from "./tokens.js";
5
+ export type { AfterScheduler, CreateServerDIOptions, RouteHandlerContext, WithDIOptions, } from "./types.js";
6
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../src/next/mod.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EACV,cAAc,EACd,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,GACd,MAAM,YAAY,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { withDI } from "./with_di.js";
2
+ export { withActionDI } from "./with_action_di.js";
3
+ export { createServerDI } from "./server_di.js";
4
+ export { NextRequestToken } from "./tokens.js";
@@ -0,0 +1,14 @@
1
+ import type { Container } from "../core/mod.js";
2
+ import type { CreateServerDIOptions } from "./types.js";
3
+ import type { Scope } from "../core/mod.js";
4
+ /**
5
+ * DI factory for Server Components.
6
+ * Shares one scope per request via `cache()` and disposes after the response with `after()`.
7
+ *
8
+ * Requires Next.js 15+ App Router. Call `getDI()` within the request context (Page, layout, etc.).
9
+ * In the domain layer, map framework tokens to port tokens instead of injecting them directly.
10
+ */
11
+ export declare function createServerDI(container: Container, options?: CreateServerDIOptions): {
12
+ getDI: () => Scope;
13
+ };
14
+ //# sourceMappingURL=server_di.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server_di.d.ts","sourceRoot":"","sources":["../../src/next/server_di.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,SAAS,EACpB,OAAO,CAAC,EAAE,qBAAqB,GAC9B;IAAE,KAAK,EAAE,MAAM,KAAK,CAAA;CAAE,CAYxB"}
@@ -0,0 +1,20 @@
1
+ import { cache } from "react";
2
+ import { after } from "next/server.js";
3
+ /**
4
+ * DI factory for Server Components.
5
+ * Shares one scope per request via `cache()` and disposes after the response with `after()`.
6
+ *
7
+ * Requires Next.js 15+ App Router. Call `getDI()` within the request context (Page, layout, etc.).
8
+ * In the domain layer, map framework tokens to port tokens instead of injecting them directly.
9
+ */
10
+ export function createServerDI(container, options) {
11
+ const scheduleAfter = options?.after ?? after;
12
+ const getScope = cache(() => {
13
+ const scope = container.createScope();
14
+ scheduleAfter(() => scope.dispose());
15
+ return scope;
16
+ });
17
+ return {
18
+ getDI: () => getScope(),
19
+ };
20
+ }
@@ -0,0 +1,4 @@
1
+ import type { NextRequest } from "next/server.js";
2
+ /** Current Next.js `NextRequest` (resolvable only in request scope) */
3
+ export declare const NextRequestToken: import("../core/token.js").Token<NextRequest>;
4
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/next/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGlD,uEAAuE;AACvE,eAAO,MAAM,gBAAgB,+CAAoC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { token } from "../core/mod.js";
2
+ /** Current Next.js `NextRequest` (resolvable only in request scope) */
3
+ export const NextRequestToken = token("NextRequest");
@@ -0,0 +1,19 @@
1
+ import type { Scope } from "../core/mod.js";
2
+ /** Same shape as `after()` from `next/server` (for test substitution) */
3
+ export type AfterScheduler = (callback: () => void | Promise<void>) => void;
4
+ export interface RouteHandlerContext<TParams extends Record<string, string | string[]>> {
5
+ di: Scope;
6
+ params: TParams;
7
+ }
8
+ export interface WithDIOptions {
9
+ /**
10
+ * Called after scope creation to `scope.set` / `scope.register`.
11
+ * Example: map to `CurrentUserToken`.
12
+ */
13
+ configureScope?: (scope: Scope) => void | Promise<void>;
14
+ }
15
+ export interface CreateServerDIOptions {
16
+ /** For tests. Defaults to `after` from `next/server` when omitted */
17
+ after?: AfterScheduler;
18
+ }
19
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/next/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C,yEAAyE;AACzE,MAAM,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;AAE5E,MAAM,WAAW,mBAAmB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACpF,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAED,MAAM,WAAW,qBAAqB;IACpC,qEAAqE;IACrE,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ import type { Container, Scope } from "../core/mod.js";
2
+ /**
3
+ * Creates a DI scope per Server Action and passes `di` as the first argument.
4
+ * Disposes the scope after completion (including on error, via `finally`).
5
+ *
6
+ * Request is not a standard argument, so no token is set.
7
+ * Use `headers()` etc. inside the action if needed; equivalent to `configureScope` via
8
+ * container registration or `scope.set` at the start of the action.
9
+ */
10
+ export declare function withActionDI<T>(container: Container, action: (di: Scope, formData: FormData) => T | Promise<T>): (formData: FormData) => Promise<T>;
11
+ //# sourceMappingURL=with_action_di.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with_action_di.d.ts","sourceRoot":"","sources":["../../src/next/with_action_di.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEvD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAC5B,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACxD,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CASpC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Creates a DI scope per Server Action and passes `di` as the first argument.
3
+ * Disposes the scope after completion (including on error, via `finally`).
4
+ *
5
+ * Request is not a standard argument, so no token is set.
6
+ * Use `headers()` etc. inside the action if needed; equivalent to `configureScope` via
7
+ * container registration or `scope.set` at the start of the action.
8
+ */
9
+ export function withActionDI(container, action) {
10
+ return async (formData) => {
11
+ const scope = container.createScope();
12
+ try {
13
+ return await action(scope, formData);
14
+ }
15
+ finally {
16
+ await scope.dispose();
17
+ }
18
+ };
19
+ }
@@ -0,0 +1,14 @@
1
+ import type { NextRequest } from "next/server.js";
2
+ import type { Container } from "../core/mod.js";
3
+ import type { RouteHandlerContext, WithDIOptions } from "./types.js";
4
+ /**
5
+ * Creates a DI scope per Route Handler and passes `{ di, params }`.
6
+ * Disposes the scope after the handler completes (including on error, via `finally`).
7
+ *
8
+ * In the domain layer, avoid injecting `NextRequestToken` directly;
9
+ * map it to port tokens via `configureScope` instead.
10
+ */
11
+ export declare function withDI<TParams extends Record<string, string | string[]>>(container: Container, handler: (req: NextRequest, ctx: RouteHandlerContext<TParams>) => Response | Promise<Response>, options?: WithDIOptions): (req: NextRequest, routeCtx: {
12
+ params: TParams | Promise<TParams>;
13
+ }) => Promise<Response>;
14
+ //# sourceMappingURL=with_di.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with_di.d.ts","sourceRoot":"","sources":["../../src/next/with_di.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAErE;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EACtE,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,CACP,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,mBAAmB,CAAC,OAAO,CAAC,KAC9B,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,EACjC,OAAO,CAAC,EAAE,aAAa,GACtB,CACD,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE;IAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,KAC7C,OAAO,CAAC,QAAQ,CAAC,CAcrB"}
@@ -0,0 +1,24 @@
1
+ import { NextRequestToken } from "./tokens.js";
2
+ /**
3
+ * Creates a DI scope per Route Handler and passes `{ di, params }`.
4
+ * Disposes the scope after the handler completes (including on error, via `finally`).
5
+ *
6
+ * In the domain layer, avoid injecting `NextRequestToken` directly;
7
+ * map it to port tokens via `configureScope` instead.
8
+ */
9
+ export function withDI(container, handler, options) {
10
+ return async (req, routeCtx) => {
11
+ const scope = container.createScope();
12
+ scope.set(NextRequestToken, req);
13
+ if (options?.configureScope) {
14
+ await options.configureScope(scope);
15
+ }
16
+ try {
17
+ const params = await Promise.resolve(routeCtx.params);
18
+ return await handler(req, { di: scope, params });
19
+ }
20
+ finally {
21
+ await scope.dispose();
22
+ }
23
+ };
24
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@wyrly/next",
3
+ "version": "1.0.0",
4
+ "description": "Wyrly DI adapter for Next.js App Router",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/wyrly/wyrly.git",
8
+ "directory": "oss/packages/next"
9
+ },
10
+ "license": "Apache-2.0",
11
+ "module": "./esm/next/mod.js",
12
+ "exports": {
13
+ ".": {
14
+ "import": "./esm/next/mod.js"
15
+ }
16
+ },
17
+ "scripts": {},
18
+ "sideEffects": false,
19
+ "dependencies": {
20
+ "next": "15",
21
+ "react": "19",
22
+ "@deno/shim-deno": "~0.18.0",
23
+ "@wyrly/core": "^1.0.0"
24
+ },
25
+ "peerDependencies": {
26
+ "next": "^15.0.0",
27
+ "react": "^19.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^20.9.0"
31
+ },
32
+ "_generatedBy": "dnt@dev"
33
+ }