@typed/app 1.0.0-beta.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 +166 -0
- package/dist/HttpApiVirtualModulePlugin.d.ts +26 -0
- package/dist/HttpApiVirtualModulePlugin.d.ts.map +1 -0
- package/dist/HttpApiVirtualModulePlugin.js +301 -0
- package/dist/RouterVirtualModulePlugin.d.ts +23 -0
- package/dist/RouterVirtualModulePlugin.d.ts.map +1 -0
- package/dist/RouterVirtualModulePlugin.js +176 -0
- package/dist/createTypeInfoApiSessionForApp.d.ts +29 -0
- package/dist/createTypeInfoApiSessionForApp.d.ts.map +1 -0
- package/dist/createTypeInfoApiSessionForApp.js +46 -0
- package/dist/httpapi/defineApiHandler.d.ts +70 -0
- package/dist/httpapi/defineApiHandler.d.ts.map +1 -0
- package/dist/httpapi/defineApiHandler.js +23 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/internal/appConfigTypes.d.ts +11 -0
- package/dist/internal/appConfigTypes.d.ts.map +1 -0
- package/dist/internal/appConfigTypes.js +1 -0
- package/dist/internal/appLayerTypes.d.ts +24 -0
- package/dist/internal/appLayerTypes.d.ts.map +1 -0
- package/dist/internal/appLayerTypes.js +28 -0
- package/dist/internal/buildRouteDescriptors.d.ts +48 -0
- package/dist/internal/buildRouteDescriptors.d.ts.map +1 -0
- package/dist/internal/buildRouteDescriptors.js +371 -0
- package/dist/internal/emitHttpApiSource.d.ts +18 -0
- package/dist/internal/emitHttpApiSource.d.ts.map +1 -0
- package/dist/internal/emitHttpApiSource.js +404 -0
- package/dist/internal/emitRouterHelpers.d.ts +17 -0
- package/dist/internal/emitRouterHelpers.d.ts.map +1 -0
- package/dist/internal/emitRouterHelpers.js +74 -0
- package/dist/internal/emitRouterSource.d.ts +8 -0
- package/dist/internal/emitRouterSource.d.ts.map +1 -0
- package/dist/internal/emitRouterSource.js +139 -0
- package/dist/internal/extractHttpApiLiterals.d.ts +17 -0
- package/dist/internal/extractHttpApiLiterals.d.ts.map +1 -0
- package/dist/internal/extractHttpApiLiterals.js +45 -0
- package/dist/internal/httpapiDescriptorTree.d.ts +75 -0
- package/dist/internal/httpapiDescriptorTree.d.ts.map +1 -0
- package/dist/internal/httpapiDescriptorTree.js +182 -0
- package/dist/internal/httpapiEndpointContract.d.ts +32 -0
- package/dist/internal/httpapiEndpointContract.d.ts.map +1 -0
- package/dist/internal/httpapiEndpointContract.js +79 -0
- package/dist/internal/httpapiFileRoles.d.ts +67 -0
- package/dist/internal/httpapiFileRoles.d.ts.map +1 -0
- package/dist/internal/httpapiFileRoles.js +145 -0
- package/dist/internal/httpapiId.d.ts +30 -0
- package/dist/internal/httpapiId.d.ts.map +1 -0
- package/dist/internal/httpapiId.js +57 -0
- package/dist/internal/httpapiOpenApiConfig.d.ts +87 -0
- package/dist/internal/httpapiOpenApiConfig.d.ts.map +1 -0
- package/dist/internal/httpapiOpenApiConfig.js +144 -0
- package/dist/internal/httpapiSort.d.ts +16 -0
- package/dist/internal/httpapiSort.d.ts.map +1 -0
- package/dist/internal/httpapiSort.js +29 -0
- package/dist/internal/path.d.ts +16 -0
- package/dist/internal/path.d.ts.map +1 -0
- package/dist/internal/path.js +38 -0
- package/dist/internal/resolveConfig.d.ts +8 -0
- package/dist/internal/resolveConfig.d.ts.map +1 -0
- package/dist/internal/resolveConfig.js +13 -0
- package/dist/internal/routeIdentifiers.d.ts +18 -0
- package/dist/internal/routeIdentifiers.d.ts.map +1 -0
- package/dist/internal/routeIdentifiers.js +90 -0
- package/dist/internal/routeTypeNode.d.ts +45 -0
- package/dist/internal/routeTypeNode.d.ts.map +1 -0
- package/dist/internal/routeTypeNode.js +93 -0
- package/dist/internal/routerDescriptorTree.d.ts +110 -0
- package/dist/internal/routerDescriptorTree.d.ts.map +1 -0
- package/dist/internal/routerDescriptorTree.js +230 -0
- package/dist/internal/typeTargetBootstrap.d.ts +2 -0
- package/dist/internal/typeTargetBootstrap.d.ts.map +1 -0
- package/dist/internal/typeTargetBootstrap.js +23 -0
- package/dist/internal/typeTargetBootstrapHttpApi.d.ts +2 -0
- package/dist/internal/typeTargetBootstrapHttpApi.d.ts.map +1 -0
- package/dist/internal/typeTargetBootstrapHttpApi.js +21 -0
- package/dist/internal/typeTargetSpecs.d.ts +15 -0
- package/dist/internal/typeTargetSpecs.d.ts.map +1 -0
- package/dist/internal/typeTargetSpecs.js +32 -0
- package/dist/internal/validation.d.ts +12 -0
- package/dist/internal/validation.d.ts.map +1 -0
- package/dist/internal/validation.js +32 -0
- package/package.json +45 -0
- package/src/HttpApiVirtualModulePlugin.test.ts +1062 -0
- package/src/HttpApiVirtualModulePlugin.ts +376 -0
- package/src/RouterVirtualModulePlugin.test.ts +1254 -0
- package/src/RouterVirtualModulePlugin.ts +242 -0
- package/src/createTypeInfoApiSessionForApp.ts +57 -0
- package/src/defineApiHandler.test.ts +100 -0
- package/src/httpapi/defineApiHandler.ts +141 -0
- package/src/httpapiDescriptorTree.test.ts +124 -0
- package/src/httpapiEndpointContract.test.ts +160 -0
- package/src/httpapiFileRoles.test.ts +105 -0
- package/src/index.ts +40 -0
- package/src/internal/appConfigTypes.ts +12 -0
- package/src/internal/appLayerTypes.ts +79 -0
- package/src/internal/buildRouteDescriptors.ts +489 -0
- package/src/internal/emitHttpApiSource.ts +563 -0
- package/src/internal/emitRouterHelpers.ts +89 -0
- package/src/internal/emitRouterSource.ts +191 -0
- package/src/internal/extractHttpApiLiterals.ts +67 -0
- package/src/internal/httpapiDescriptorTree.ts +283 -0
- package/src/internal/httpapiEndpointContract.ts +110 -0
- package/src/internal/httpapiFileRoles.ts +204 -0
- package/src/internal/httpapiId.ts +78 -0
- package/src/internal/httpapiOpenApiConfig.ts +228 -0
- package/src/internal/httpapiSort.ts +39 -0
- package/src/internal/path.ts +46 -0
- package/src/internal/resolveConfig.ts +15 -0
- package/src/internal/routeIdentifiers.ts +93 -0
- package/src/internal/routeTypeNode.ts +120 -0
- package/src/internal/routerDescriptorTree.ts +366 -0
- package/src/internal/typeTargetBootstrap.ts +24 -0
- package/src/internal/typeTargetBootstrapHttpApi.ts +22 -0
- package/src/internal/typeTargetSpecs.ts +35 -0
- package/src/internal/validation.ts +46 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/** True when the node represents a callable (function, overload set, or constructor). */
|
|
2
|
+
export function isCallableNode(node) {
|
|
3
|
+
return node.kind === "function" || node.kind === "overloadSet" || node.kind === "constructor";
|
|
4
|
+
}
|
|
5
|
+
/** First signature's parameters for function-like nodes; undefined otherwise. */
|
|
6
|
+
function getCallableParameters(node) {
|
|
7
|
+
if (node.kind === "function")
|
|
8
|
+
return node.parameters;
|
|
9
|
+
if (node.kind === "overloadSet") {
|
|
10
|
+
const sigs = node.signatures;
|
|
11
|
+
return sigs[0]?.parameters;
|
|
12
|
+
}
|
|
13
|
+
if (node.kind === "constructor")
|
|
14
|
+
return node.parameters;
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
/** First signature's return type for function-like nodes; undefined otherwise. */
|
|
18
|
+
export function getCallableReturnType(node) {
|
|
19
|
+
if (node.kind === "function")
|
|
20
|
+
return node.returnType;
|
|
21
|
+
if (node.kind === "overloadSet") {
|
|
22
|
+
const sigs = node.signatures;
|
|
23
|
+
return sigs[0]?.returnType;
|
|
24
|
+
}
|
|
25
|
+
if (node.kind === "constructor")
|
|
26
|
+
return node.returnType;
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* True iff the type is structurally assignable to Route.
|
|
31
|
+
*/
|
|
32
|
+
export function typeNodeIsRouteCompatible(node, api) {
|
|
33
|
+
return api.isAssignableTo(node, "Route");
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Structurally determines runtime kind via api.isAssignableTo. No fallbacks.
|
|
37
|
+
* Never returns "unknown"; use callers' failWhenNoTargetsResolved for that.
|
|
38
|
+
*/
|
|
39
|
+
export function typeNodeToRuntimeKind(node, api) {
|
|
40
|
+
if (api.isAssignableTo(node, "Fx"))
|
|
41
|
+
return "fx";
|
|
42
|
+
if (api.isAssignableTo(node, "Effect"))
|
|
43
|
+
return "effect";
|
|
44
|
+
if (api.isAssignableTo(node, "Stream"))
|
|
45
|
+
return "stream";
|
|
46
|
+
return "plain";
|
|
47
|
+
}
|
|
48
|
+
/** Classify dependency default export for optimal provide lift. Uses api; node.kind "array" for T[]. */
|
|
49
|
+
export function classifyDepsExport(node, api) {
|
|
50
|
+
if (api.isAssignableTo(node, "Layer"))
|
|
51
|
+
return "layer";
|
|
52
|
+
if (api.isAssignableTo(node, "ServiceMap"))
|
|
53
|
+
return "servicemap";
|
|
54
|
+
if (node.kind === "array")
|
|
55
|
+
return "array";
|
|
56
|
+
return "unknown";
|
|
57
|
+
}
|
|
58
|
+
/** True iff the type node is a function whose first parameter expects RefSubject. */
|
|
59
|
+
export function typeNodeExpectsRefSubjectParam(node, api) {
|
|
60
|
+
if (!isCallableNode(node))
|
|
61
|
+
return false;
|
|
62
|
+
const params = getCallableParameters(node);
|
|
63
|
+
if (!params || params.length === 0)
|
|
64
|
+
return false;
|
|
65
|
+
return api.isAssignableTo(node, "RefSubject", [{ kind: "param", index: 0 }]);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* True iff the function's return type is Effect and Effect's success type (first type arg) is assignable to Option.
|
|
69
|
+
*/
|
|
70
|
+
export function typeNodeIsEffectOptionReturn(node, api) {
|
|
71
|
+
return api.isAssignableTo(node, "Option", [
|
|
72
|
+
{ kind: "returnType" },
|
|
73
|
+
{ kind: "ensure", targetId: "Effect" },
|
|
74
|
+
{ kind: "typeArg", index: 0 },
|
|
75
|
+
]);
|
|
76
|
+
}
|
|
77
|
+
export function classifyCatchForm(node, api) {
|
|
78
|
+
const returnType = getCallableReturnType(node);
|
|
79
|
+
const returnKind = returnType
|
|
80
|
+
? typeNodeToRuntimeKind(returnType, api)
|
|
81
|
+
: typeNodeToRuntimeKind(node, api);
|
|
82
|
+
if (!isCallableNode(node)) {
|
|
83
|
+
return { form: "value", returnKind };
|
|
84
|
+
}
|
|
85
|
+
const params = getCallableParameters(node);
|
|
86
|
+
if (!params || params.length === 0)
|
|
87
|
+
return { form: "fn-error", returnKind };
|
|
88
|
+
if (api.isAssignableTo(node, "RefSubject", [{ kind: "param", index: 0 }]))
|
|
89
|
+
return { form: "native", returnKind };
|
|
90
|
+
if (api.isAssignableTo(node, "Cause", [{ kind: "param", index: 0 }]))
|
|
91
|
+
return { form: "fn-cause", returnKind };
|
|
92
|
+
return { form: "fn-error", returnKind };
|
|
93
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declarative descriptor tree for router virtual module emission.
|
|
3
|
+
* Describes ALL route structure and metadata; rendering is a separate phase.
|
|
4
|
+
*
|
|
5
|
+
* Optimizations in renderer:
|
|
6
|
+
* - Router.merge only when 2+ siblings
|
|
7
|
+
* - No Router.merge() for single child
|
|
8
|
+
* - Parens only when chaining on multi-arg call expressions
|
|
9
|
+
* - Positional match when no extra opts
|
|
10
|
+
*/
|
|
11
|
+
import type { CatchForm, DepsExportKind, RuntimeKind } from "./routeTypeNode.js";
|
|
12
|
+
/** Resolved companion paths per concern (guard, deps, layout, catch). */
|
|
13
|
+
export type ComposedConcernsShape = {
|
|
14
|
+
readonly guard: readonly string[];
|
|
15
|
+
readonly dependencies: readonly string[];
|
|
16
|
+
readonly layout: readonly string[];
|
|
17
|
+
readonly catch: readonly string[];
|
|
18
|
+
};
|
|
19
|
+
/** Path-based refs (relative to baseDir); var names resolved at render time. */
|
|
20
|
+
export type PathRef = string;
|
|
21
|
+
/** Per-route match configuration (declarative, path-based). */
|
|
22
|
+
export type RouteMatchDescriptor = {
|
|
23
|
+
readonly routePath: PathRef;
|
|
24
|
+
readonly entrypointExport: "handler" | "template" | "default";
|
|
25
|
+
readonly runtimeKind: RuntimeKind;
|
|
26
|
+
readonly entrypointIsFunction: boolean;
|
|
27
|
+
readonly entrypointExpectsRefSubject: boolean;
|
|
28
|
+
readonly inFileConcerns: {
|
|
29
|
+
readonly layout?: boolean;
|
|
30
|
+
readonly dependencies?: boolean;
|
|
31
|
+
readonly catch?: boolean;
|
|
32
|
+
readonly guard?: boolean;
|
|
33
|
+
};
|
|
34
|
+
readonly composedConcerns: ComposedConcernsShape;
|
|
35
|
+
/** Guard from in-file, sibling, or directory (path). */
|
|
36
|
+
readonly guardPath?: PathRef;
|
|
37
|
+
/** Catch from in-file or sibling (path). */
|
|
38
|
+
readonly catchPath?: PathRef;
|
|
39
|
+
readonly catchExport?: "catch" | "catchFn";
|
|
40
|
+
readonly catchForm?: CatchForm;
|
|
41
|
+
/** Layout from in-file or sibling (path). */
|
|
42
|
+
readonly layoutPath?: PathRef;
|
|
43
|
+
/** Dependencies from in-file or sibling (path). */
|
|
44
|
+
readonly depsPath?: PathRef;
|
|
45
|
+
};
|
|
46
|
+
/** Directory companions (path-based). */
|
|
47
|
+
export type DirectoryCompanions = {
|
|
48
|
+
readonly layout?: PathRef;
|
|
49
|
+
readonly catch?: PathRef;
|
|
50
|
+
readonly dependencies?: PathRef;
|
|
51
|
+
};
|
|
52
|
+
/** Declarative tree node: route leaf or directory with children. */
|
|
53
|
+
export type RouterDescriptorNode = {
|
|
54
|
+
readonly type: "route";
|
|
55
|
+
readonly match: RouteMatchDescriptor;
|
|
56
|
+
} | {
|
|
57
|
+
readonly type: "directory";
|
|
58
|
+
readonly dirPath: string;
|
|
59
|
+
readonly children: readonly RouterDescriptorNode[];
|
|
60
|
+
readonly companions?: DirectoryCompanions;
|
|
61
|
+
};
|
|
62
|
+
/** Root descriptor tree. */
|
|
63
|
+
export type RouterDescriptorTree = {
|
|
64
|
+
readonly root: RouterDescriptorNode;
|
|
65
|
+
readonly rootCompanions?: Pick<DirectoryCompanions, "dependencies">;
|
|
66
|
+
};
|
|
67
|
+
/** Input for building the descriptor tree from route descriptors. */
|
|
68
|
+
export type BuildDescriptorTreeInput = {
|
|
69
|
+
readonly descriptors: readonly {
|
|
70
|
+
readonly filePath: string;
|
|
71
|
+
readonly entrypointExport: "handler" | "template" | "default";
|
|
72
|
+
readonly runtimeKind: RuntimeKind;
|
|
73
|
+
readonly entrypointIsFunction: boolean;
|
|
74
|
+
readonly entrypointExpectsRefSubject: boolean;
|
|
75
|
+
readonly composedConcerns: ComposedConcernsShape;
|
|
76
|
+
readonly inFileConcerns: {
|
|
77
|
+
layout?: boolean;
|
|
78
|
+
dependencies?: boolean;
|
|
79
|
+
catch?: boolean;
|
|
80
|
+
guard?: boolean;
|
|
81
|
+
};
|
|
82
|
+
}[];
|
|
83
|
+
readonly dirToCompanions: ReadonlyMap<string, {
|
|
84
|
+
layout?: string;
|
|
85
|
+
dependencies?: string;
|
|
86
|
+
catch?: string;
|
|
87
|
+
}>;
|
|
88
|
+
readonly guardExportByPath: Readonly<Record<string, "default" | "guard">>;
|
|
89
|
+
readonly catchExportByPath: Readonly<Record<string, "catch" | "catchFn">>;
|
|
90
|
+
readonly catchFormByPath: Readonly<Record<string, CatchForm>>;
|
|
91
|
+
readonly normalizeDir: (dir: string) => string;
|
|
92
|
+
readonly isDirectoryCompanion: (path: string) => boolean;
|
|
93
|
+
readonly siblingCompanionPath: (leafPath: string, kind: "guard" | "dependencies" | "layout" | "catch") => string;
|
|
94
|
+
};
|
|
95
|
+
/** Build declarative tree from route descriptors. */
|
|
96
|
+
export declare function buildRouterDescriptorTree(input: BuildDescriptorTreeInput): RouterDescriptorTree;
|
|
97
|
+
/** Context for rendering: var names and expr generators. */
|
|
98
|
+
export type RenderContext = {
|
|
99
|
+
readonly varNameByPath: ReadonlyMap<string, string>;
|
|
100
|
+
readonly guardExportByPath: Readonly<Record<string, "default" | "guard">>;
|
|
101
|
+
readonly catchExportByPath: Readonly<Record<string, "catch" | "catchFn">>;
|
|
102
|
+
readonly catchFormByPath: Readonly<Record<string, CatchForm>>;
|
|
103
|
+
readonly depsFormByPath: Readonly<Record<string, DepsExportKind>>;
|
|
104
|
+
readonly handlerExprFor: (match: RouteMatchDescriptor, varName: string) => string;
|
|
105
|
+
readonly catchExprFor: (form: CatchForm, varName: string, exportName: string) => string;
|
|
106
|
+
readonly depsExprFor: (kind: DepsExportKind, varName: string) => string;
|
|
107
|
+
};
|
|
108
|
+
/** Render tree to source. Optimizes: no merge for single child, minimal parens. */
|
|
109
|
+
export declare function renderRouterDescriptorTree(tree: RouterDescriptorTree, ctx: RenderContext): string;
|
|
110
|
+
//# sourceMappingURL=routerDescriptorTree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routerDescriptorTree.d.ts","sourceRoot":"","sources":["../../src/internal/routerDescriptorTree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declarative descriptor tree for router virtual module emission.
|
|
3
|
+
* Describes ALL route structure and metadata; rendering is a separate phase.
|
|
4
|
+
*
|
|
5
|
+
* Optimizations in renderer:
|
|
6
|
+
* - Router.merge only when 2+ siblings
|
|
7
|
+
* - No Router.merge() for single child
|
|
8
|
+
* - Parens only when chaining on multi-arg call expressions
|
|
9
|
+
* - Positional match when no extra opts
|
|
10
|
+
*/
|
|
11
|
+
/** Build declarative tree from route descriptors. */
|
|
12
|
+
export function buildRouterDescriptorTree(input) {
|
|
13
|
+
const { descriptors, dirToCompanions, catchExportByPath, catchFormByPath, normalizeDir, isDirectoryCompanion, siblingCompanionPath, } = input;
|
|
14
|
+
const toMatchDescriptor = (d) => {
|
|
15
|
+
const sibling = (k) => siblingCompanionPath(d.filePath, k);
|
|
16
|
+
const hasSibling = (k) => d.composedConcerns[k].includes(sibling(k));
|
|
17
|
+
const dirGuardPath = d.composedConcerns.guard.find(isDirectoryCompanion);
|
|
18
|
+
const guardPath = d.inFileConcerns.guard
|
|
19
|
+
? d.filePath
|
|
20
|
+
: hasSibling("guard")
|
|
21
|
+
? sibling("guard")
|
|
22
|
+
: dirGuardPath;
|
|
23
|
+
const catchPath = d.inFileConcerns.catch
|
|
24
|
+
? d.filePath
|
|
25
|
+
: hasSibling("catch")
|
|
26
|
+
? sibling("catch")
|
|
27
|
+
: undefined;
|
|
28
|
+
return {
|
|
29
|
+
routePath: d.filePath,
|
|
30
|
+
entrypointExport: d.entrypointExport,
|
|
31
|
+
runtimeKind: d.runtimeKind,
|
|
32
|
+
entrypointIsFunction: d.entrypointIsFunction,
|
|
33
|
+
entrypointExpectsRefSubject: d.entrypointExpectsRefSubject,
|
|
34
|
+
inFileConcerns: d.inFileConcerns,
|
|
35
|
+
composedConcerns: d.composedConcerns,
|
|
36
|
+
guardPath,
|
|
37
|
+
catchPath,
|
|
38
|
+
catchExport: catchPath
|
|
39
|
+
? (() => {
|
|
40
|
+
const exp = catchExportByPath[catchPath];
|
|
41
|
+
if (!exp)
|
|
42
|
+
throw new Error(`RVM: catch export missing for ${catchPath}`);
|
|
43
|
+
return exp;
|
|
44
|
+
})()
|
|
45
|
+
: undefined,
|
|
46
|
+
catchForm: catchPath
|
|
47
|
+
? (() => {
|
|
48
|
+
const form = catchFormByPath[catchPath];
|
|
49
|
+
if (!form)
|
|
50
|
+
throw new Error(`RVM: catch form missing for ${catchPath}`);
|
|
51
|
+
return form;
|
|
52
|
+
})()
|
|
53
|
+
: undefined,
|
|
54
|
+
layoutPath: d.inFileConcerns.layout
|
|
55
|
+
? d.filePath
|
|
56
|
+
: hasSibling("layout")
|
|
57
|
+
? sibling("layout")
|
|
58
|
+
: undefined,
|
|
59
|
+
depsPath: d.inFileConcerns.dependencies
|
|
60
|
+
? d.filePath
|
|
61
|
+
: hasSibling("dependencies")
|
|
62
|
+
? sibling("dependencies")
|
|
63
|
+
: undefined,
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
const allDirs = new Set(descriptors.map((d) => normalizeDir(dirname(d.filePath))));
|
|
67
|
+
for (const [dir] of dirToCompanions)
|
|
68
|
+
allDirs.add(dir);
|
|
69
|
+
if (allDirs.size > 0)
|
|
70
|
+
allDirs.add("");
|
|
71
|
+
const sortedDirs = [...allDirs].sort((a, b) => b.split("/").filter(Boolean).length - a.split("/").filter(Boolean).length);
|
|
72
|
+
const dirNodeByPath = new Map();
|
|
73
|
+
for (const dir of sortedDirs) {
|
|
74
|
+
const directRoutes = descriptors.filter((d) => normalizeDir(dirname(d.filePath)) === dir);
|
|
75
|
+
const isImmediateChild = (s) => s !== dir &&
|
|
76
|
+
(dir === ""
|
|
77
|
+
? s.indexOf("/") === -1
|
|
78
|
+
: s.startsWith(dir + "/") && s.slice((dir + "/").length).indexOf("/") === -1);
|
|
79
|
+
const childDirs = sortedDirs.filter(isImmediateChild);
|
|
80
|
+
const routeChildren = directRoutes.map((d) => ({
|
|
81
|
+
type: "route",
|
|
82
|
+
match: toMatchDescriptor(d),
|
|
83
|
+
}));
|
|
84
|
+
const dirChildren = childDirs.map((s) => dirNodeByPath.get(s));
|
|
85
|
+
const children = [...routeChildren, ...dirChildren];
|
|
86
|
+
const companions = dirToCompanions.get(dir);
|
|
87
|
+
const hasCompanions = companions &&
|
|
88
|
+
(companions.layout || companions.catch || (dir !== "" && companions.dependencies));
|
|
89
|
+
const node = children.length === 0
|
|
90
|
+
? {
|
|
91
|
+
type: "directory",
|
|
92
|
+
dirPath: dir,
|
|
93
|
+
children: [],
|
|
94
|
+
companions: hasCompanions
|
|
95
|
+
? {
|
|
96
|
+
layout: companions.layout,
|
|
97
|
+
catch: companions.catch,
|
|
98
|
+
dependencies: dir !== "" ? companions.dependencies : undefined,
|
|
99
|
+
}
|
|
100
|
+
: undefined,
|
|
101
|
+
}
|
|
102
|
+
: children.length === 1 && !hasCompanions
|
|
103
|
+
? children[0]
|
|
104
|
+
: {
|
|
105
|
+
type: "directory",
|
|
106
|
+
dirPath: dir,
|
|
107
|
+
children,
|
|
108
|
+
companions: hasCompanions
|
|
109
|
+
? {
|
|
110
|
+
layout: companions.layout,
|
|
111
|
+
catch: companions.catch,
|
|
112
|
+
dependencies: dir !== "" ? companions.dependencies : undefined,
|
|
113
|
+
}
|
|
114
|
+
: undefined,
|
|
115
|
+
};
|
|
116
|
+
dirNodeByPath.set(dir, node);
|
|
117
|
+
}
|
|
118
|
+
let root = dirNodeByPath.get("");
|
|
119
|
+
if (!root || (root.type === "directory" && root.children.length === 0)) {
|
|
120
|
+
const flatChildren = descriptors.map((d) => ({
|
|
121
|
+
type: "route",
|
|
122
|
+
match: toMatchDescriptor(d),
|
|
123
|
+
}));
|
|
124
|
+
root =
|
|
125
|
+
flatChildren.length === 0
|
|
126
|
+
? { type: "directory", dirPath: "", children: [] }
|
|
127
|
+
: flatChildren.length === 1
|
|
128
|
+
? flatChildren[0]
|
|
129
|
+
: { type: "directory", dirPath: "", children: flatChildren };
|
|
130
|
+
}
|
|
131
|
+
const rootCompanions = dirToCompanions.get("");
|
|
132
|
+
return {
|
|
133
|
+
root,
|
|
134
|
+
rootCompanions: rootCompanions?.dependencies
|
|
135
|
+
? { dependencies: rootCompanions.dependencies }
|
|
136
|
+
: undefined,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function dirname(p) {
|
|
140
|
+
const i = p.lastIndexOf("/");
|
|
141
|
+
return i < 0 ? "" : p.slice(0, i);
|
|
142
|
+
}
|
|
143
|
+
/** Render tree to source. Optimizes: no merge for single child, minimal parens. */
|
|
144
|
+
export function renderRouterDescriptorTree(tree, ctx) {
|
|
145
|
+
const emit = (node) => {
|
|
146
|
+
if (node.type === "route") {
|
|
147
|
+
return emitRoute(node.match, ctx);
|
|
148
|
+
}
|
|
149
|
+
const { children, companions } = node;
|
|
150
|
+
let inner;
|
|
151
|
+
if (children.length === 0) {
|
|
152
|
+
throw new Error("RVM: directory node with no children should not be rendered");
|
|
153
|
+
}
|
|
154
|
+
if (children.length === 1) {
|
|
155
|
+
inner = emit(children[0]);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
inner = `Router.merge(\n ${children.map((c) => emit(c)).join(",\n ")}\n)`;
|
|
159
|
+
}
|
|
160
|
+
if (companions?.layout) {
|
|
161
|
+
const v = ctx.varNameByPath.get(companions.layout);
|
|
162
|
+
inner = `${inner}.layout(${v}.layout)`;
|
|
163
|
+
}
|
|
164
|
+
if (companions?.catch) {
|
|
165
|
+
const v = ctx.varNameByPath.get(companions.catch);
|
|
166
|
+
const exp = ctx.catchExportByPath[companions.catch];
|
|
167
|
+
if (!exp)
|
|
168
|
+
throw new Error(`RVM: catch export missing for ${companions.catch}`);
|
|
169
|
+
const form = ctx.catchFormByPath[companions.catch];
|
|
170
|
+
if (!form)
|
|
171
|
+
throw new Error(`RVM: catch form missing for ${companions.catch}`);
|
|
172
|
+
const catchExpr = ctx.catchExprFor(form, v, exp);
|
|
173
|
+
inner = `${inner}.catchCause(${catchExpr})`;
|
|
174
|
+
}
|
|
175
|
+
if (companions?.dependencies) {
|
|
176
|
+
const v = ctx.varNameByPath.get(companions.dependencies);
|
|
177
|
+
const depsKind = ctx.depsFormByPath[companions.dependencies];
|
|
178
|
+
if (depsKind === undefined)
|
|
179
|
+
throw new Error(`RVM: dependency form unknown for ${companions.dependencies}; validation should have failed (RVM-DEPS-001)`);
|
|
180
|
+
const depsExpr = ctx.depsExprFor(depsKind, v);
|
|
181
|
+
inner = `${inner}.provide(${depsExpr})`;
|
|
182
|
+
}
|
|
183
|
+
return inner;
|
|
184
|
+
};
|
|
185
|
+
let result = emit(tree.root);
|
|
186
|
+
if (tree.rootCompanions?.dependencies) {
|
|
187
|
+
const v = ctx.varNameByPath.get(tree.rootCompanions.dependencies);
|
|
188
|
+
const depsKind = ctx.depsFormByPath[tree.rootCompanions.dependencies];
|
|
189
|
+
if (depsKind === undefined)
|
|
190
|
+
throw new Error(`RVM: dependency form unknown for ${tree.rootCompanions.dependencies}; validation should have failed (RVM-DEPS-001)`);
|
|
191
|
+
const depsExpr = ctx.depsExprFor(depsKind, v);
|
|
192
|
+
result = `${result}.provide(${depsExpr})`;
|
|
193
|
+
}
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
/** Emit Router.match(...) for a route. Favors positional when no extra opts. */
|
|
197
|
+
function emitRoute(match, ctx) {
|
|
198
|
+
const routeVar = ctx.varNameByPath.get(match.routePath);
|
|
199
|
+
const routeRef = `${routeVar}.route`;
|
|
200
|
+
const handlerExpr = ctx.handlerExprFor(match, routeVar);
|
|
201
|
+
const hasExtraOpts = match.layoutPath || match.depsPath || match.catchPath;
|
|
202
|
+
const guardPath = match.guardPath;
|
|
203
|
+
const guardExport = guardPath ? ctx.guardExportByPath[guardPath] : undefined;
|
|
204
|
+
const guardExpr = guardPath && guardExport ? `${ctx.varNameByPath.get(guardPath)}.${guardExport}` : undefined;
|
|
205
|
+
const opts = [`handler: ${handlerExpr}`];
|
|
206
|
+
if (match.depsPath) {
|
|
207
|
+
opts.push(`dependencies: ${ctx.varNameByPath.get(match.depsPath)}.dependencies`);
|
|
208
|
+
}
|
|
209
|
+
if (match.layoutPath) {
|
|
210
|
+
opts.push(`layout: ${ctx.varNameByPath.get(match.layoutPath)}.layout`);
|
|
211
|
+
}
|
|
212
|
+
if (match.catchPath && match.catchExport) {
|
|
213
|
+
const catchVar = ctx.varNameByPath.get(match.catchPath);
|
|
214
|
+
const form = match.catchForm;
|
|
215
|
+
if (!form)
|
|
216
|
+
throw new Error(`RVM: catch form missing for ${match.catchPath}`);
|
|
217
|
+
opts.push(`catch: ${ctx.catchExprFor(form, catchVar, match.catchExport)}`);
|
|
218
|
+
}
|
|
219
|
+
if (guardExpr && !hasExtraOpts) {
|
|
220
|
+
return `Router.match(${routeRef}, ${guardExpr}, ${handlerExpr})`;
|
|
221
|
+
}
|
|
222
|
+
if (guardExpr && hasExtraOpts) {
|
|
223
|
+
opts.unshift(`guard: ${guardExpr}`);
|
|
224
|
+
return `Router.match(${routeRef}, ${guardExpr}, { ${opts.join(", ")} })`;
|
|
225
|
+
}
|
|
226
|
+
if (!hasExtraOpts) {
|
|
227
|
+
return `Router.match(${routeRef}, ${handlerExpr})`;
|
|
228
|
+
}
|
|
229
|
+
return `Router.match(${routeRef}, { ${opts.join(", ")} })`;
|
|
230
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typeTargetBootstrap.d.ts","sourceRoot":"","sources":["../../src/internal/typeTargetBootstrap.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootstrap file for TypeInfoApi type target resolution in tests.
|
|
3
|
+
* Exists so resolveTypeTargetsFromSpecs can find canonical Effect/Fx/RefSubject/Stream/Route types
|
|
4
|
+
* when the program includes this file. Do not use; for test harness only.
|
|
5
|
+
*/
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Fx from "@typed/fx/Fx";
|
|
8
|
+
import * as RefSubject from "@typed/fx/RefSubject";
|
|
9
|
+
import * as Stream from "effect/Stream";
|
|
10
|
+
import * as Route from "@typed/router";
|
|
11
|
+
import * as Option from "effect/Option";
|
|
12
|
+
import * as Cause from "effect/Cause";
|
|
13
|
+
import * as Layer from "effect/Layer";
|
|
14
|
+
import * as ServiceMap from "effect/ServiceMap";
|
|
15
|
+
void Effect;
|
|
16
|
+
void Fx;
|
|
17
|
+
void RefSubject;
|
|
18
|
+
void Stream;
|
|
19
|
+
void Route;
|
|
20
|
+
void Option;
|
|
21
|
+
void Cause;
|
|
22
|
+
void Layer;
|
|
23
|
+
void ServiceMap;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typeTargetBootstrapHttpApi.d.ts","sourceRoot":"","sources":["../../src/internal/typeTargetBootstrapHttpApi.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootstrap file for TypeInfoApi type target resolution in HttpApi tests.
|
|
3
|
+
* Exists so resolveTypeTargetsFromSpecs can find canonical HttpApi/Route/Effect/Schema types
|
|
4
|
+
* when the program includes this file. Do not use; for test harness only.
|
|
5
|
+
*/
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Schema from "effect/Schema";
|
|
8
|
+
import * as Route from "@typed/router";
|
|
9
|
+
import * as HttpApiModule from "effect/unstable/httpapi/HttpApi";
|
|
10
|
+
import * as HttpApiGroupModule from "effect/unstable/httpapi/HttpApiGroup";
|
|
11
|
+
import * as HttpApiEndpointModule from "effect/unstable/httpapi/HttpApiEndpoint";
|
|
12
|
+
import * as HttpApiBuilderModule from "effect/unstable/httpapi/HttpApiBuilder";
|
|
13
|
+
import * as HttpServerResponseModule from "effect/unstable/http/HttpServerResponse";
|
|
14
|
+
void Effect;
|
|
15
|
+
void Schema;
|
|
16
|
+
void Route;
|
|
17
|
+
void HttpApiModule;
|
|
18
|
+
void HttpApiGroupModule;
|
|
19
|
+
void HttpApiEndpointModule;
|
|
20
|
+
void HttpApiBuilderModule;
|
|
21
|
+
void HttpServerResponseModule;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TypeTargetSpec } from "@typed/virtual-modules";
|
|
2
|
+
/**
|
|
3
|
+
* Type target specs for router virtual module structural validation.
|
|
4
|
+
* Pass to createTypeInfoApiSession typeTargetSpecs option.
|
|
5
|
+
*
|
|
6
|
+
* IMPORTANT: Each spec's `module` must exactly match the import string in user source.
|
|
7
|
+
* Specs are canonical-only (no fallbacks or alternate import paths).
|
|
8
|
+
*/
|
|
9
|
+
export declare const ROUTER_TYPE_TARGET_SPECS: readonly TypeTargetSpec[];
|
|
10
|
+
/**
|
|
11
|
+
* Type target specs for HttpApi virtual module structural validation.
|
|
12
|
+
* Pass to createTypeInfoApiSession typeTargetSpecs option.
|
|
13
|
+
*/
|
|
14
|
+
export declare const HTTPAPI_TYPE_TARGET_SPECS: readonly TypeTargetSpec[];
|
|
15
|
+
//# sourceMappingURL=typeTargetSpecs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typeTargetSpecs.d.ts","sourceRoot":"","sources":["../../src/internal/typeTargetSpecs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,cAAc,EAU7D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,EAAE,SAAS,cAAc,EAS9D,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type target specs for router virtual module structural validation.
|
|
3
|
+
* Pass to createTypeInfoApiSession typeTargetSpecs option.
|
|
4
|
+
*
|
|
5
|
+
* IMPORTANT: Each spec's `module` must exactly match the import string in user source.
|
|
6
|
+
* Specs are canonical-only (no fallbacks or alternate import paths).
|
|
7
|
+
*/
|
|
8
|
+
export const ROUTER_TYPE_TARGET_SPECS = [
|
|
9
|
+
{ id: "Fx", module: "@typed/fx/Fx", exportName: "Fx" },
|
|
10
|
+
{ id: "Effect", module: "effect/Effect", exportName: "Effect" },
|
|
11
|
+
{ id: "Stream", module: "effect/Stream", exportName: "Stream" },
|
|
12
|
+
{ id: "RefSubject", module: "@typed/fx/RefSubject", exportName: "RefSubject" },
|
|
13
|
+
{ id: "Cause", module: "effect/Cause", exportName: "Cause" },
|
|
14
|
+
{ id: "Option", module: "effect/Option", exportName: "Option" },
|
|
15
|
+
{ id: "Layer", module: "effect/Layer", exportName: "Layer" },
|
|
16
|
+
{ id: "ServiceMap", module: "effect/ServiceMap", exportName: "ServiceMap" },
|
|
17
|
+
{ id: "Route", module: "@typed/router", exportName: "Route" },
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* Type target specs for HttpApi virtual module structural validation.
|
|
21
|
+
* Pass to createTypeInfoApiSession typeTargetSpecs option.
|
|
22
|
+
*/
|
|
23
|
+
export const HTTPAPI_TYPE_TARGET_SPECS = [
|
|
24
|
+
{ id: "HttpApi", module: "effect/unstable/httpapi/HttpApi", exportName: "HttpApi" },
|
|
25
|
+
{ id: "HttpApiGroup", module: "effect/unstable/httpapi/HttpApiGroup", exportName: "HttpApiGroup" },
|
|
26
|
+
{ id: "HttpApiEndpoint", module: "effect/unstable/httpapi/HttpApiEndpoint", exportName: "HttpApiEndpoint" },
|
|
27
|
+
{ id: "HttpApiBuilder", module: "effect/unstable/httpapi/HttpApiBuilder", exportName: "HttpApiBuilder" },
|
|
28
|
+
{ id: "Schema", module: "effect/Schema", exportName: "Top" },
|
|
29
|
+
{ id: "Effect", module: "effect/Effect", exportName: "Effect" },
|
|
30
|
+
{ id: "Route", module: "@typed/router", exportName: "Route" },
|
|
31
|
+
{ id: "HttpServerResponse", module: "effect/unstable/http/HttpServerResponse", exportName: "HttpServerResponse" },
|
|
32
|
+
];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type ValidationOk<T> = {
|
|
2
|
+
ok: true;
|
|
3
|
+
value: T;
|
|
4
|
+
};
|
|
5
|
+
export type ValidationFail = {
|
|
6
|
+
ok: false;
|
|
7
|
+
reason: string;
|
|
8
|
+
};
|
|
9
|
+
export type ValidationResult<T> = ValidationOk<T> | ValidationFail;
|
|
10
|
+
export declare function validateNonEmptyString(value: unknown, name: string, maxLength?: number): ValidationResult<string>;
|
|
11
|
+
export declare function validatePathSegment(value: unknown, name: string, maxLength?: number): ValidationResult<string>;
|
|
12
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/internal/validation.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC;AACrD,MAAM,MAAM,cAAc,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAC3D,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC;AAEnE,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,MAA2B,GACrC,gBAAgB,CAAC,MAAM,CAAC,CAe1B;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,MAA2B,GACrC,gBAAgB,CAAC,MAAM,CAAC,CAc1B"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const DEFAULT_MAX_LENGTH = 4096;
|
|
2
|
+
export function validateNonEmptyString(value, name, maxLength = DEFAULT_MAX_LENGTH) {
|
|
3
|
+
if (typeof value !== "string") {
|
|
4
|
+
return { ok: false, reason: `${name} must be a string` };
|
|
5
|
+
}
|
|
6
|
+
const trimmed = value.trim();
|
|
7
|
+
if (trimmed === "") {
|
|
8
|
+
return { ok: false, reason: `${name} must be non-empty` };
|
|
9
|
+
}
|
|
10
|
+
if (value.includes("\0")) {
|
|
11
|
+
return { ok: false, reason: `${name} must not contain null bytes` };
|
|
12
|
+
}
|
|
13
|
+
if (value.length > maxLength) {
|
|
14
|
+
return { ok: false, reason: `${name} exceeds max length ${maxLength}` };
|
|
15
|
+
}
|
|
16
|
+
return { ok: true, value: trimmed };
|
|
17
|
+
}
|
|
18
|
+
export function validatePathSegment(value, name, maxLength = DEFAULT_MAX_LENGTH) {
|
|
19
|
+
if (typeof value !== "string") {
|
|
20
|
+
return { ok: false, reason: `${name} must be a string` };
|
|
21
|
+
}
|
|
22
|
+
if (value.length === 0 || value.trim() === "") {
|
|
23
|
+
return { ok: false, reason: `${name} must be non-empty` };
|
|
24
|
+
}
|
|
25
|
+
if (value.includes("\0")) {
|
|
26
|
+
return { ok: false, reason: `${name} must not contain null bytes` };
|
|
27
|
+
}
|
|
28
|
+
if (value.length > maxLength) {
|
|
29
|
+
return { ok: false, reason: `${name} exceeds max length ${maxLength}` };
|
|
30
|
+
}
|
|
31
|
+
return { ok: true, value };
|
|
32
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@typed/app",
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"import": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"./*": {
|
|
11
|
+
"types": "./dist/*.d.ts",
|
|
12
|
+
"import": "./dist/*.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "[ -d dist ] || rm -f tsconfig.tsbuildinfo; tsc",
|
|
20
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
21
|
+
"test": "vitest run --passWithNoTests",
|
|
22
|
+
"test:coverage": "vitest run --coverage"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@typed/router": "workspace:*",
|
|
26
|
+
"@typed/virtual-modules": "workspace:*",
|
|
27
|
+
"effect": "catalog:"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@effect/platform-node": "catalog:",
|
|
31
|
+
"@typed/fx": "workspace:*",
|
|
32
|
+
"@types/node": "^25.3.0",
|
|
33
|
+
"@vitest/coverage-v8": "^4.0.0",
|
|
34
|
+
"effect": "catalog:",
|
|
35
|
+
"typescript": "catalog:",
|
|
36
|
+
"vitest": "catalog:"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"typescript": "catalog:"
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist",
|
|
43
|
+
"src"
|
|
44
|
+
]
|
|
45
|
+
}
|