@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.
Files changed (116) hide show
  1. package/README.md +166 -0
  2. package/dist/HttpApiVirtualModulePlugin.d.ts +26 -0
  3. package/dist/HttpApiVirtualModulePlugin.d.ts.map +1 -0
  4. package/dist/HttpApiVirtualModulePlugin.js +301 -0
  5. package/dist/RouterVirtualModulePlugin.d.ts +23 -0
  6. package/dist/RouterVirtualModulePlugin.d.ts.map +1 -0
  7. package/dist/RouterVirtualModulePlugin.js +176 -0
  8. package/dist/createTypeInfoApiSessionForApp.d.ts +29 -0
  9. package/dist/createTypeInfoApiSessionForApp.d.ts.map +1 -0
  10. package/dist/createTypeInfoApiSessionForApp.js +46 -0
  11. package/dist/httpapi/defineApiHandler.d.ts +70 -0
  12. package/dist/httpapi/defineApiHandler.d.ts.map +1 -0
  13. package/dist/httpapi/defineApiHandler.js +23 -0
  14. package/dist/index.d.ts +9 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +7 -0
  17. package/dist/internal/appConfigTypes.d.ts +11 -0
  18. package/dist/internal/appConfigTypes.d.ts.map +1 -0
  19. package/dist/internal/appConfigTypes.js +1 -0
  20. package/dist/internal/appLayerTypes.d.ts +24 -0
  21. package/dist/internal/appLayerTypes.d.ts.map +1 -0
  22. package/dist/internal/appLayerTypes.js +28 -0
  23. package/dist/internal/buildRouteDescriptors.d.ts +48 -0
  24. package/dist/internal/buildRouteDescriptors.d.ts.map +1 -0
  25. package/dist/internal/buildRouteDescriptors.js +371 -0
  26. package/dist/internal/emitHttpApiSource.d.ts +18 -0
  27. package/dist/internal/emitHttpApiSource.d.ts.map +1 -0
  28. package/dist/internal/emitHttpApiSource.js +404 -0
  29. package/dist/internal/emitRouterHelpers.d.ts +17 -0
  30. package/dist/internal/emitRouterHelpers.d.ts.map +1 -0
  31. package/dist/internal/emitRouterHelpers.js +74 -0
  32. package/dist/internal/emitRouterSource.d.ts +8 -0
  33. package/dist/internal/emitRouterSource.d.ts.map +1 -0
  34. package/dist/internal/emitRouterSource.js +139 -0
  35. package/dist/internal/extractHttpApiLiterals.d.ts +17 -0
  36. package/dist/internal/extractHttpApiLiterals.d.ts.map +1 -0
  37. package/dist/internal/extractHttpApiLiterals.js +45 -0
  38. package/dist/internal/httpapiDescriptorTree.d.ts +75 -0
  39. package/dist/internal/httpapiDescriptorTree.d.ts.map +1 -0
  40. package/dist/internal/httpapiDescriptorTree.js +182 -0
  41. package/dist/internal/httpapiEndpointContract.d.ts +32 -0
  42. package/dist/internal/httpapiEndpointContract.d.ts.map +1 -0
  43. package/dist/internal/httpapiEndpointContract.js +79 -0
  44. package/dist/internal/httpapiFileRoles.d.ts +67 -0
  45. package/dist/internal/httpapiFileRoles.d.ts.map +1 -0
  46. package/dist/internal/httpapiFileRoles.js +145 -0
  47. package/dist/internal/httpapiId.d.ts +30 -0
  48. package/dist/internal/httpapiId.d.ts.map +1 -0
  49. package/dist/internal/httpapiId.js +57 -0
  50. package/dist/internal/httpapiOpenApiConfig.d.ts +87 -0
  51. package/dist/internal/httpapiOpenApiConfig.d.ts.map +1 -0
  52. package/dist/internal/httpapiOpenApiConfig.js +144 -0
  53. package/dist/internal/httpapiSort.d.ts +16 -0
  54. package/dist/internal/httpapiSort.d.ts.map +1 -0
  55. package/dist/internal/httpapiSort.js +29 -0
  56. package/dist/internal/path.d.ts +16 -0
  57. package/dist/internal/path.d.ts.map +1 -0
  58. package/dist/internal/path.js +38 -0
  59. package/dist/internal/resolveConfig.d.ts +8 -0
  60. package/dist/internal/resolveConfig.d.ts.map +1 -0
  61. package/dist/internal/resolveConfig.js +13 -0
  62. package/dist/internal/routeIdentifiers.d.ts +18 -0
  63. package/dist/internal/routeIdentifiers.d.ts.map +1 -0
  64. package/dist/internal/routeIdentifiers.js +90 -0
  65. package/dist/internal/routeTypeNode.d.ts +45 -0
  66. package/dist/internal/routeTypeNode.d.ts.map +1 -0
  67. package/dist/internal/routeTypeNode.js +93 -0
  68. package/dist/internal/routerDescriptorTree.d.ts +110 -0
  69. package/dist/internal/routerDescriptorTree.d.ts.map +1 -0
  70. package/dist/internal/routerDescriptorTree.js +230 -0
  71. package/dist/internal/typeTargetBootstrap.d.ts +2 -0
  72. package/dist/internal/typeTargetBootstrap.d.ts.map +1 -0
  73. package/dist/internal/typeTargetBootstrap.js +23 -0
  74. package/dist/internal/typeTargetBootstrapHttpApi.d.ts +2 -0
  75. package/dist/internal/typeTargetBootstrapHttpApi.d.ts.map +1 -0
  76. package/dist/internal/typeTargetBootstrapHttpApi.js +21 -0
  77. package/dist/internal/typeTargetSpecs.d.ts +15 -0
  78. package/dist/internal/typeTargetSpecs.d.ts.map +1 -0
  79. package/dist/internal/typeTargetSpecs.js +32 -0
  80. package/dist/internal/validation.d.ts +12 -0
  81. package/dist/internal/validation.d.ts.map +1 -0
  82. package/dist/internal/validation.js +32 -0
  83. package/package.json +45 -0
  84. package/src/HttpApiVirtualModulePlugin.test.ts +1062 -0
  85. package/src/HttpApiVirtualModulePlugin.ts +376 -0
  86. package/src/RouterVirtualModulePlugin.test.ts +1254 -0
  87. package/src/RouterVirtualModulePlugin.ts +242 -0
  88. package/src/createTypeInfoApiSessionForApp.ts +57 -0
  89. package/src/defineApiHandler.test.ts +100 -0
  90. package/src/httpapi/defineApiHandler.ts +141 -0
  91. package/src/httpapiDescriptorTree.test.ts +124 -0
  92. package/src/httpapiEndpointContract.test.ts +160 -0
  93. package/src/httpapiFileRoles.test.ts +105 -0
  94. package/src/index.ts +40 -0
  95. package/src/internal/appConfigTypes.ts +12 -0
  96. package/src/internal/appLayerTypes.ts +79 -0
  97. package/src/internal/buildRouteDescriptors.ts +489 -0
  98. package/src/internal/emitHttpApiSource.ts +563 -0
  99. package/src/internal/emitRouterHelpers.ts +89 -0
  100. package/src/internal/emitRouterSource.ts +191 -0
  101. package/src/internal/extractHttpApiLiterals.ts +67 -0
  102. package/src/internal/httpapiDescriptorTree.ts +283 -0
  103. package/src/internal/httpapiEndpointContract.ts +110 -0
  104. package/src/internal/httpapiFileRoles.ts +204 -0
  105. package/src/internal/httpapiId.ts +78 -0
  106. package/src/internal/httpapiOpenApiConfig.ts +228 -0
  107. package/src/internal/httpapiSort.ts +39 -0
  108. package/src/internal/path.ts +46 -0
  109. package/src/internal/resolveConfig.ts +15 -0
  110. package/src/internal/routeIdentifiers.ts +93 -0
  111. package/src/internal/routeTypeNode.ts +120 -0
  112. package/src/internal/routerDescriptorTree.ts +366 -0
  113. package/src/internal/typeTargetBootstrap.ts +24 -0
  114. package/src/internal/typeTargetBootstrapHttpApi.ts +22 -0
  115. package/src/internal/typeTargetSpecs.ts +35 -0
  116. package/src/internal/validation.ts +46 -0
@@ -0,0 +1,176 @@
1
+ import { readdirSync, statSync } from "node:fs";
2
+ import { dirname, extname, join } from "node:path";
3
+ import { buildRouteDescriptors, } from "./internal/buildRouteDescriptors.js";
4
+ import { emitRouterMatchSource } from "./internal/emitRouterSource.js";
5
+ import { pathIsUnderBase, resolvePathUnderBase, resolveRelativePath, toPosixPath, } from "./internal/path.js";
6
+ import { validateNonEmptyString, validatePathSegment } from "./internal/validation.js";
7
+ import { ROUTER_TYPE_TARGET_SPECS } from "./internal/typeTargetSpecs.js";
8
+ const DEFAULT_PREFIX = "router:";
9
+ const DEFAULT_PLUGIN_NAME = "router-virtual-module";
10
+ /** Extensions that count as route/script files when checking if a directory should resolve. */
11
+ const SCRIPT_EXTENSION_SET = new Set([
12
+ ".ts",
13
+ ".tsx",
14
+ ".js",
15
+ ".jsx",
16
+ ".mts",
17
+ ".cts",
18
+ ".mjs",
19
+ ".cjs",
20
+ ]);
21
+ /** Glob patterns for discovering route files. */
22
+ const ROUTE_FILE_GLOBS = [
23
+ "**/*.ts",
24
+ "**/*.tsx",
25
+ "**/*.js",
26
+ "**/*.jsx",
27
+ "**/*.mts",
28
+ "**/*.cts",
29
+ "**/*.mjs",
30
+ "**/*.cjs",
31
+ ];
32
+ export function parseRouterVirtualModuleId(id, prefix = DEFAULT_PREFIX) {
33
+ const idResult = validateNonEmptyString(id, "id");
34
+ if (!idResult.ok)
35
+ return { ok: false, reason: idResult.reason };
36
+ const prefixResult = validateNonEmptyString(prefix, "prefix");
37
+ if (!prefixResult.ok)
38
+ return { ok: false, reason: prefixResult.reason };
39
+ if (!id.startsWith(prefix)) {
40
+ return { ok: false, reason: `id must start with "${prefix}"` };
41
+ }
42
+ let relativeDirectory = id.slice(prefix.length);
43
+ if (relativeDirectory.length > 0 &&
44
+ relativeDirectory !== "." &&
45
+ relativeDirectory !== ".." &&
46
+ !relativeDirectory.startsWith("./") &&
47
+ !relativeDirectory.startsWith("../") &&
48
+ !relativeDirectory.startsWith("/")) {
49
+ relativeDirectory = `./${relativeDirectory}`;
50
+ }
51
+ const relativeResult = validatePathSegment(relativeDirectory, "relativeDirectory");
52
+ if (!relativeResult.ok)
53
+ return { ok: false, reason: relativeResult.reason };
54
+ return { ok: true, relativeDirectory: relativeResult.value };
55
+ }
56
+ export function resolveRouterTargetDirectory(id, importer, prefix = DEFAULT_PREFIX) {
57
+ const parsed = parseRouterVirtualModuleId(id, prefix);
58
+ if (!parsed.ok)
59
+ return parsed;
60
+ const importerResult = validatePathSegment(importer, "importer");
61
+ if (!importerResult.ok)
62
+ return { ok: false, reason: importerResult.reason };
63
+ const importerDir = dirname(toPosixPath(importerResult.value));
64
+ const resolved = resolvePathUnderBase(importerDir, parsed.relativeDirectory);
65
+ if (!resolved.ok) {
66
+ return { ok: false, reason: "resolved target directory escapes importer base directory" };
67
+ }
68
+ if (!pathIsUnderBase(importerDir, resolved.path)) {
69
+ return { ok: false, reason: "resolved target directory is outside importer base directory" };
70
+ }
71
+ return { ok: true, targetDirectory: toPosixPath(resolved.path) };
72
+ }
73
+ function isExistingDirectory(absolutePath) {
74
+ try {
75
+ return statSync(absolutePath).isDirectory();
76
+ }
77
+ catch {
78
+ return false;
79
+ }
80
+ }
81
+ function directoryHasScriptFiles(dir) {
82
+ try {
83
+ const items = readdirSync(dir, { withFileTypes: true });
84
+ for (const e of items) {
85
+ if (e.isFile() &&
86
+ SCRIPT_EXTENSION_SET.has(extname(e.name).toLowerCase()) &&
87
+ !e.name.toLowerCase().endsWith(".d.ts"))
88
+ return true;
89
+ if (e.isDirectory() && directoryHasScriptFiles(join(dir, e.name)))
90
+ return true;
91
+ }
92
+ return false;
93
+ }
94
+ catch {
95
+ return false;
96
+ }
97
+ }
98
+ const FAIL_ORDER = [
99
+ "RVM-AMBIGUOUS-001",
100
+ "RVM-GUARD-001",
101
+ "RVM-CATCH-001",
102
+ "RVM-DEPS-001",
103
+ "RVM-KIND-001",
104
+ ];
105
+ function failOnViolations(violations, toDiagnostic) {
106
+ for (const code of FAIL_ORDER) {
107
+ const found = violations.filter((v) => v.code === code);
108
+ if (found.length > 0)
109
+ return { errors: found.map(toDiagnostic) };
110
+ }
111
+ return null;
112
+ }
113
+ export const createRouterVirtualModulePlugin = (options = {}) => {
114
+ const prefix = options.prefix ?? DEFAULT_PREFIX;
115
+ const name = options.name ?? DEFAULT_PLUGIN_NAME;
116
+ return {
117
+ name,
118
+ typeTargetSpecs: ROUTER_TYPE_TARGET_SPECS,
119
+ shouldResolve(id, importer) {
120
+ const resolved = resolveRouterTargetDirectory(id, importer, prefix);
121
+ if (!resolved.ok)
122
+ return false;
123
+ if (!isExistingDirectory(resolved.targetDirectory))
124
+ return false;
125
+ return directoryHasScriptFiles(resolved.targetDirectory);
126
+ },
127
+ build(id, importer, api) {
128
+ const resolved = resolveRouterTargetDirectory(id, importer, prefix);
129
+ if (!resolved.ok) {
130
+ return {
131
+ errors: [{ code: "RVM-ID-001", message: resolved.reason, pluginName: name }],
132
+ };
133
+ }
134
+ if (!isExistingDirectory(resolved.targetDirectory)) {
135
+ return {
136
+ errors: [
137
+ {
138
+ code: "RVM-DISC-001",
139
+ message: `target directory does not exist: ${resolveRelativePath(dirname(importer), resolved.targetDirectory)}`,
140
+ pluginName: name,
141
+ },
142
+ ],
143
+ };
144
+ }
145
+ const snapshots = api.directory(ROUTE_FILE_GLOBS, {
146
+ baseDir: resolved.targetDirectory,
147
+ recursive: true,
148
+ watch: true,
149
+ });
150
+ const { descriptors, violations, guardExportByPath, catchExportByPath, catchFormByPath, depsFormByPath, } = buildRouteDescriptors(snapshots, resolved.targetDirectory, api);
151
+ const toDiagnostic = (v) => ({
152
+ code: v.code,
153
+ message: v.message,
154
+ pluginName: name,
155
+ });
156
+ const err = failOnViolations(violations, toDiagnostic);
157
+ if (err)
158
+ return err;
159
+ if (descriptors.length === 0) {
160
+ if (violations.length > 0) {
161
+ return { errors: violations.map(toDiagnostic) };
162
+ }
163
+ return {
164
+ errors: [
165
+ {
166
+ code: "RVM-LEAF-001",
167
+ message: `no valid route leaves discovered in ${resolved.targetDirectory}`,
168
+ pluginName: name,
169
+ },
170
+ ],
171
+ };
172
+ }
173
+ return emitRouterMatchSource(descriptors, resolved.targetDirectory, importer, guardExportByPath, catchExportByPath, catchFormByPath, depsFormByPath);
174
+ },
175
+ };
176
+ };
@@ -0,0 +1,29 @@
1
+ import type { CreateTypeInfoApiSessionOptions } from "@typed/virtual-modules";
2
+ /** Bootstrap content for app type targets. Include in program rootNames when creating programs. */
3
+ export declare const APP_TYPE_TARGET_BOOTSTRAP_CONTENT: string;
4
+ /**
5
+ * Creates a TypeInfo API session with type target specs for router and HttpApi
6
+ * virtual modules. Use when providing createTypeInfoApiSession to typedVitePlugin
7
+ * or other integrations that need structural assignability (assignableTo) checks.
8
+ *
9
+ * The program must include imports from canonical type target modules (effect/Effect,
10
+ * @typed/router, etc.). If the program has no such imports, add a bootstrap file:
11
+ * write APP_TYPE_TARGET_BOOTSTRAP_CONTENT to a file and include it in rootNames.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { createTypeInfoApiSessionForApp, APP_TYPE_TARGET_BOOTSTRAP_CONTENT } from "@typed/app";
16
+ * import { writeFileSync } from "node:fs";
17
+ * import { join } from "node:path";
18
+ * import ts from "typescript";
19
+ *
20
+ * const bootstrapPath = join(tmpDir, "__typeTargetBootstrap.ts");
21
+ * writeFileSync(bootstrapPath, APP_TYPE_TARGET_BOOTSTRAP_CONTENT);
22
+ * const program = ts.createProgram(["src/main.ts", bootstrapPath], options, host);
23
+ * typedVitePlugin({
24
+ * createTypeInfoApiSession: () => createTypeInfoApiSessionForApp({ ts, program }),
25
+ * });
26
+ * ```
27
+ */
28
+ export declare function createTypeInfoApiSessionForApp(options: Omit<CreateTypeInfoApiSessionOptions, "typeTargetSpecs">): import("@typed/virtual-modules").TypeInfoApiSession;
29
+ //# sourceMappingURL=createTypeInfoApiSessionForApp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createTypeInfoApiSessionForApp.d.ts","sourceRoot":"","sources":["../src/createTypeInfoApiSessionForApp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,+BAA+B,EAAkB,MAAM,wBAAwB,CAAC;AAoB9F,mGAAmG;AACnG,eAAO,MAAM,iCAAiC,QAE7C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,IAAI,CAAC,+BAA+B,EAAE,iBAAiB,CAAC,uDAMlE"}
@@ -0,0 +1,46 @@
1
+ import { createTypeInfoApiSession, createTypeTargetBootstrapContent, } from "@typed/virtual-modules";
2
+ import { HTTPAPI_TYPE_TARGET_SPECS, ROUTER_TYPE_TARGET_SPECS } from "./internal/typeTargetSpecs.js";
3
+ /** Merged type target specs for router + HttpApi plugins. Dedupes by id. */
4
+ const APP_TYPE_TARGET_SPECS = (() => {
5
+ const seen = new Set();
6
+ const out = [];
7
+ for (const spec of [...ROUTER_TYPE_TARGET_SPECS, ...HTTPAPI_TYPE_TARGET_SPECS]) {
8
+ if (!seen.has(spec.id)) {
9
+ seen.add(spec.id);
10
+ out.push(spec);
11
+ }
12
+ }
13
+ return out;
14
+ })();
15
+ /** Bootstrap content for app type targets. Include in program rootNames when creating programs. */
16
+ export const APP_TYPE_TARGET_BOOTSTRAP_CONTENT = createTypeTargetBootstrapContent(APP_TYPE_TARGET_SPECS);
17
+ /**
18
+ * Creates a TypeInfo API session with type target specs for router and HttpApi
19
+ * virtual modules. Use when providing createTypeInfoApiSession to typedVitePlugin
20
+ * or other integrations that need structural assignability (assignableTo) checks.
21
+ *
22
+ * The program must include imports from canonical type target modules (effect/Effect,
23
+ * @typed/router, etc.). If the program has no such imports, add a bootstrap file:
24
+ * write APP_TYPE_TARGET_BOOTSTRAP_CONTENT to a file and include it in rootNames.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * import { createTypeInfoApiSessionForApp, APP_TYPE_TARGET_BOOTSTRAP_CONTENT } from "@typed/app";
29
+ * import { writeFileSync } from "node:fs";
30
+ * import { join } from "node:path";
31
+ * import ts from "typescript";
32
+ *
33
+ * const bootstrapPath = join(tmpDir, "__typeTargetBootstrap.ts");
34
+ * writeFileSync(bootstrapPath, APP_TYPE_TARGET_BOOTSTRAP_CONTENT);
35
+ * const program = ts.createProgram(["src/main.ts", bootstrapPath], options, host);
36
+ * typedVitePlugin({
37
+ * createTypeInfoApiSession: () => createTypeInfoApiSessionForApp({ ts, program }),
38
+ * });
39
+ * ```
40
+ */
41
+ export function createTypeInfoApiSessionForApp(options) {
42
+ return createTypeInfoApiSession({
43
+ ...options,
44
+ typeTargetSpecs: APP_TYPE_TARGET_SPECS,
45
+ });
46
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Typed handler helper for HttpApi endpoint contracts.
3
+ * Uses Router.Route from @typed/router (Router.Parse, Route.Join, etc.) for type-safe path/query.
4
+ * Curried form: defineApiHandler(route, method, schemas?)(handler)
5
+ * Handler receives { path, query, headers, body } for type-safe decoding;
6
+ * error/success schemas encode response payloads into HttpServerResponse with annotated status codes.
7
+ * Structural type checking: handler return must match success schema; errors must match error schema.
8
+ *
9
+ * @see .docs/specs/httpapi-virtual-module-plugin/spec.md (Typed handler helper)
10
+ */
11
+ import type * as Effect from "effect/Effect";
12
+ import type * as Schema from "effect/Schema";
13
+ import type { HttpMethod as EffectHttpMethod } from "effect/unstable/http/HttpMethod";
14
+ import type * as Route from "@typed/router";
15
+ /** Re-export Effect HttpMethod for helper consumers. */
16
+ export type HttpMethod = EffectHttpMethod;
17
+ /** Typed empty record for path params and headers when none are defined. Used by emitted handler adapter. */
18
+ export declare const emptyRecordString: Record<string, string>;
19
+ /** Typed empty record for query params when none are defined. Used by emitted handler adapter. */
20
+ export declare const emptyRecordStringArray: Record<string, string | string[] | undefined>;
21
+ /** Route MUST be Router.Route (from Router.Parse, Route.Join, Route.Param, etc.). */
22
+ export type ApiRoute = Route.Route.Any;
23
+ /**
24
+ * Infers handler params from endpoint contract. Use with handler:
25
+ * handler: (params: ApiHandlerParams<{ route, method?, success?, error?, headers?, body? }>) => Effect
26
+ */
27
+ export type ApiHandlerParams<T extends {
28
+ readonly route: ApiRoute;
29
+ readonly method: HttpMethod | "*";
30
+ readonly success?: Schema.Schema<any>;
31
+ readonly error?: Schema.Schema<any>;
32
+ readonly headers?: Schema.Schema<any>;
33
+ readonly body?: Schema.Schema<any>;
34
+ }> = {
35
+ readonly path: Route.Route.PathType<T["route"]>;
36
+ readonly query: Route.Route.QueryType<T["route"]>;
37
+ readonly headers: T["headers"] extends Schema.Schema<infer H> ? H : Record<string, string>;
38
+ readonly body: T["body"] extends Schema.Schema<infer B> ? B : unknown;
39
+ };
40
+ /**
41
+ * Optional schemas: headers, body (request decoders); error, success (response encoders).
42
+ * Use HttpApiSchema.status(code)(schema) to annotate status codes, e.g.:
43
+ * success: HttpApiSchema.status(200)(Schema.Struct({ ok: Schema.Boolean }))
44
+ * error: HttpApiSchema.status(400)(Schema.Struct({ message: Schema.String }))
45
+ */
46
+ export interface EndpointSchemas<THeaders = unknown, TBody = unknown, TSuccess = unknown, TError = unknown> {
47
+ readonly headers?: Schema.Schema<THeaders>;
48
+ readonly body?: Schema.Schema<TBody>;
49
+ readonly success?: Schema.Schema<TSuccess>;
50
+ readonly error?: Schema.Schema<TError>;
51
+ }
52
+ /** Handler context: path params, query, headers, body (mirrors HttpServerRequest decoders). */
53
+ export interface ApiHandlerContext<TPath = Record<string, string>, TQuery = Record<string, string | string[] | undefined>, THeaders = Record<string, string>, TBody = unknown> {
54
+ readonly path: TPath;
55
+ readonly query: TQuery;
56
+ readonly headers: THeaders;
57
+ readonly body: TBody;
58
+ }
59
+ /** Handler function type: context -> Effect<Success, Error, Requirements> */
60
+ export type ApiHandlerFn<TPath = Record<string, string>, TQuery = Record<string, string | string[] | undefined>, THeaders = Record<string, string>, TBody = unknown, TSuccess = unknown, TError = unknown, Requirements = never> = (ctx: ApiHandlerContext<TPath, TQuery, THeaders, TBody>) => Effect.Effect<TSuccess, TError, Requirements>;
61
+ /** Typed handler as returned by defineApiHandler */
62
+ export type TypedApiHandler<TPath = Record<string, string>, TQuery = Record<string, string | string[] | undefined>, THeaders = Record<string, string>, TBody = unknown, TSuccess = unknown, TError = unknown, Requirements = never> = ApiHandlerFn<TPath, TQuery, THeaders, TBody, TSuccess, TError, Requirements>;
63
+ /**
64
+ * Curried helper: (route, method, schemas?) => (handler) => typed handler.
65
+ * Route MUST be Router.Route (Router.Parse, Route.Join, Route.Param, etc.).
66
+ * Enforces at compile time that the handler receives { path, query, headers, body }
67
+ * and returns Effect<Success, Error> compatible with success/error schemas.
68
+ */
69
+ export declare function defineApiHandler<TRoute extends ApiRoute, Method extends HttpMethod, THeaders = Record<string, string>, TBody = unknown, TSuccess = unknown, TError = unknown>(_route: TRoute, _method: Method, _schemas?: EndpointSchemas<THeaders, TBody, TSuccess, TError>): <Requirements = never>(handler: ApiHandlerFn<Route.Route.PathType<TRoute>, Route.Route.QueryType<TRoute>, THeaders, TBody, TSuccess, TError, Requirements>) => TypedApiHandler<Route.Route.PathType<TRoute>, Route.Route.QueryType<TRoute>, THeaders, TBody, TSuccess, TError, Requirements>;
70
+ //# sourceMappingURL=defineApiHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defineApiHandler.d.ts","sourceRoot":"","sources":["../../src/httpapi/defineApiHandler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Typed handler helper for HttpApi endpoint contracts.
3
+ * Uses Router.Route from @typed/router (Router.Parse, Route.Join, etc.) for type-safe path/query.
4
+ * Curried form: defineApiHandler(route, method, schemas?)(handler)
5
+ * Handler receives { path, query, headers, body } for type-safe decoding;
6
+ * error/success schemas encode response payloads into HttpServerResponse with annotated status codes.
7
+ * Structural type checking: handler return must match success schema; errors must match error schema.
8
+ *
9
+ * @see .docs/specs/httpapi-virtual-module-plugin/spec.md (Typed handler helper)
10
+ */
11
+ /** Typed empty record for path params and headers when none are defined. Used by emitted handler adapter. */
12
+ export const emptyRecordString = {};
13
+ /** Typed empty record for query params when none are defined. Used by emitted handler adapter. */
14
+ export const emptyRecordStringArray = {};
15
+ /**
16
+ * Curried helper: (route, method, schemas?) => (handler) => typed handler.
17
+ * Route MUST be Router.Route (Router.Parse, Route.Join, Route.Param, etc.).
18
+ * Enforces at compile time that the handler receives { path, query, headers, body }
19
+ * and returns Effect<Success, Error> compatible with success/error schemas.
20
+ */
21
+ export function defineApiHandler(_route, _method, _schemas) {
22
+ return (handler) => handler;
23
+ }
@@ -0,0 +1,9 @@
1
+ export { createRouterVirtualModulePlugin, parseRouterVirtualModuleId, resolveRouterTargetDirectory, type ParseRouterVirtualModuleIdResult, type ResolveRouterTargetDirectoryResult, type RouterVirtualModulePluginOptions, } from "./RouterVirtualModulePlugin.js";
2
+ export { ROUTER_TYPE_TARGET_SPECS, HTTPAPI_TYPE_TARGET_SPECS, } from "./internal/typeTargetSpecs.js";
3
+ export { createTypeInfoApiSessionForApp, APP_TYPE_TARGET_BOOTSTRAP_CONTENT, } from "./createTypeInfoApiSessionForApp.js";
4
+ export { createHttpApiVirtualModulePlugin, parseHttpApiVirtualModuleId, resolveHttpApiTargetDirectory, type HttpApiVirtualModulePluginOptions, type ParseHttpApiVirtualModuleIdResult, type ResolveHttpApiTargetDirectoryResult, } from "./HttpApiVirtualModulePlugin.js";
5
+ export { defineApiHandler, emptyRecordString, emptyRecordStringArray, type ApiHandlerContext, type ApiHandlerFn, type ApiHandlerParams, type ApiRoute, type EndpointSchemas, type HttpMethod, type TypedApiHandler, } from "./httpapi/defineApiHandler.js";
6
+ export { resolveConfig } from "./internal/resolveConfig.js";
7
+ export type { AppConfig, RunConfig } from "./internal/appConfigTypes.js";
8
+ export * from "./internal/appLayerTypes.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,+BAA+B,EAC/B,0BAA0B,EAC1B,4BAA4B,EAC5B,KAAK,gCAAgC,EACrC,KAAK,kCAAkC,EACvC,KAAK,gCAAgC,GACtC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,8BAA8B,EAC9B,iCAAiC,GAClC,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EACL,gCAAgC,EAChC,2BAA2B,EAC3B,6BAA6B,EAC7B,KAAK,iCAAiC,EACtC,KAAK,iCAAiC,EACtC,KAAK,mCAAmC,GACzC,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,eAAe,GACrB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzE,cAAc,6BAA6B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { createRouterVirtualModulePlugin, parseRouterVirtualModuleId, resolveRouterTargetDirectory, } from "./RouterVirtualModulePlugin.js";
2
+ export { ROUTER_TYPE_TARGET_SPECS, HTTPAPI_TYPE_TARGET_SPECS, } from "./internal/typeTargetSpecs.js";
3
+ export { createTypeInfoApiSessionForApp, APP_TYPE_TARGET_BOOTSTRAP_CONTENT, } from "./createTypeInfoApiSessionForApp.js";
4
+ export { createHttpApiVirtualModulePlugin, parseHttpApiVirtualModuleId, resolveHttpApiTargetDirectory, } from "./HttpApiVirtualModulePlugin.js";
5
+ export { defineApiHandler, emptyRecordString, emptyRecordStringArray, } from "./httpapi/defineApiHandler.js";
6
+ export { resolveConfig } from "./internal/resolveConfig.js";
7
+ export * from "./internal/appLayerTypes.js";
@@ -0,0 +1,11 @@
1
+ import type * as Config from "effect/Config";
2
+ /** Config for App (sync). disableListenLog matches HttpRouter.serve option. Use raw values. */
3
+ export type AppConfig = {
4
+ readonly disableListenLog?: boolean;
5
+ };
6
+ /** Config for run(). Extends AppConfig; host/port may be raw or Config (yield* Config.*). Supply ConfigProvider.layer when using Config. */
7
+ export type RunConfig = AppConfig & {
8
+ readonly host?: string | Config.Config<string>;
9
+ readonly port?: number | Config.Config<number>;
10
+ };
11
+ //# sourceMappingURL=appConfigTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appConfigTypes.d.ts","sourceRoot":"","sources":["../../src/internal/appConfigTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAC;AAE7C,+FAA+F;AAC/F,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CACrC,CAAC;AAEF,4IAA4I;AAC5I,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG;IAClC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;CAChD,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Type-safe Layer composition helpers for App/run.
3
+ * Uses Effect's Layer.Success, Layer.Error, Layer.Services, Layer.Any directly.
4
+ */
5
+ import * as Layer from "effect/Layer";
6
+ export type LayerAny = Layer.Layer<any, any, any> | Layer.Layer<never, any, any>;
7
+ /** Single layer or non-empty array (uses Layer.mergeAll internally). */
8
+ export type LayerOrGroup = LayerAny | readonly [LayerAny, ...ReadonlyArray<LayerAny>];
9
+ /**
10
+ * Composes base layer with layers via provideMerge. Layers are provided TO the base.
11
+ * For precise output type, use ComposeWithLayers<Base, NormalizedLayers<Layers>>.
12
+ */
13
+ export declare function composeWithLayers<Base extends LayerAny, const Layers extends ReadonlyArray<LayerOrGroup>>(base: Base, layers: Layers): ComputeLayers<Layers, Base>;
14
+ export type ComputeLayers<Layers extends ReadonlyArray<LayerOrGroup>, R extends LayerAny> = readonly [] extends Layers ? R : Layers extends readonly [
15
+ infer Head extends LayerOrGroup,
16
+ ...infer Tail extends ReadonlyArray<LayerOrGroup>
17
+ ] ? ComputeLayers<Tail, ProvideMerge<R, ComputeLayer<Head>>> : R;
18
+ export type ProvideMerge<A extends LayerAny, B extends LayerAny> = Layer.Layer<Layer.Success<A | B>, Layer.Error<A | B>, Exclude<Layer.Services<A>, Layer.Success<B>> | Layer.Services<B>>;
19
+ export type Provide<A extends LayerAny, B extends LayerAny> = Layer.Layer<Layer.Success<A | B>, Layer.Error<A | B>, Exclude<Layer.Services<A>, Layer.Success<B>> | Layer.Services<B>>;
20
+ type ComputeLayer<L extends LayerOrGroup> = L extends Layer.Layer<infer A, infer E, infer R> ? Layer.Layer<A, E, R> : L extends ReadonlyArray<Layer.Layer<infer A, infer E, infer R>> ? Layer.Layer<A, E, R> : never;
21
+ /** Normalizes LayerOrGroup to a single Layer at runtime. Arrays use Layer.mergeAll. */
22
+ export declare function normalizeLayerInput<L extends LayerOrGroup>(input: L): LayerAny;
23
+ export {};
24
+ //# sourceMappingURL=appLayerTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appLayerTypes.d.ts","sourceRoot":"","sources":["../../src/internal/appLayerTypes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,MAAM,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAEjF,wEAAwE;AACxE,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,SAAS,CAAC,QAAQ,EAAE,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;AActF;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,SAAS,QAAQ,EACrB,KAAK,CAAC,MAAM,SAAS,aAAa,CAAC,YAAY,CAAC,EAChD,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAc3D,MAAM,MAAM,aAAa,CACvB,MAAM,SAAS,aAAa,CAAC,YAAY,CAAC,EAC1C,CAAC,SAAS,QAAQ,IAChB,SAAS,EAAE,SAAS,MAAM,GAC1B,CAAC,GACD,MAAM,SAAS,SAAS;IACpB,MAAM,IAAI,SAAS,YAAY;IAC/B,GAAG,MAAM,IAAI,SAAS,aAAa,CAAC,YAAY,CAAC;CAClD,GACD,aAAa,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,GACxD,CAAC,CAAC;AAER,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,QAAQ,IAAI,KAAK,CAAC,KAAK,CAC5E,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,EACpB,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAClB,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAC/D,CAAC;AAEJ,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,QAAQ,IAAI,KAAK,CAAC,KAAK,CACvE,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,EACpB,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAClB,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CACjE,CAAC;AAEF,KAAK,YAAY,CAAC,CAAC,SAAS,YAAY,IACtC,CAAC,SAAS,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,GAC5C,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GACpB,CAAC,SAAS,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,GAC7D,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GACpB,KAAK,CAAC;AAEd,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,YAAY,EAAE,KAAK,EAAE,CAAC,GAAG,QAAQ,CAE9E"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Type-safe Layer composition helpers for App/run.
3
+ * Uses Effect's Layer.Success, Layer.Error, Layer.Services, Layer.Any directly.
4
+ */
5
+ import * as Layer from "effect/Layer";
6
+ function isLayerArray(l) {
7
+ return Array.isArray(l) && l.length > 0;
8
+ }
9
+ function toLayer(l) {
10
+ if (isLayerArray(l)) {
11
+ const [first, ...rest] = l;
12
+ return Layer.mergeAll(first, ...rest);
13
+ }
14
+ return l;
15
+ }
16
+ export function composeWithLayers(base, layers) {
17
+ if (layers.length === 0)
18
+ return base;
19
+ let out = base;
20
+ for (const layer of layers) {
21
+ out = Layer.provideMerge(out, toLayer(layer));
22
+ }
23
+ return out;
24
+ }
25
+ /** Normalizes LayerOrGroup to a single Layer at runtime. Arrays use Layer.mergeAll. */
26
+ export function normalizeLayerInput(input) {
27
+ return toLayer(input);
28
+ }
@@ -0,0 +1,48 @@
1
+ import type { TypeInfoApi, TypeInfoFileSnapshot } from "@typed/virtual-modules";
2
+ import { type CatchForm, type DepsExportKind, type RuntimeKind } from "./routeTypeNode.js";
3
+ export type ConcernKind = "guard" | "dependencies" | "layout" | "catch";
4
+ export type ComposedConcerns = {
5
+ readonly guard: readonly string[];
6
+ readonly dependencies: readonly string[];
7
+ readonly layout: readonly string[];
8
+ readonly catch: readonly string[];
9
+ };
10
+ /** Which concerns (layout, dependencies, catch, guard) are exported from the route file itself. */
11
+ export type InFileConcerns = Partial<Record<ConcernKind, true>>;
12
+ declare const ENTRYPOINT_EXPORTS: readonly ["handler", "template", "default"];
13
+ type EntryPointExport = (typeof ENTRYPOINT_EXPORTS)[number];
14
+ export type RouteDescriptor = {
15
+ readonly filePath: string;
16
+ readonly entrypointExport: EntryPointExport;
17
+ readonly runtimeKind: RuntimeKind;
18
+ readonly entrypointIsFunction: boolean;
19
+ readonly entrypointExpectsRefSubject: boolean;
20
+ readonly composedConcerns: ComposedConcerns;
21
+ readonly inFileConcerns: InFileConcerns;
22
+ readonly routeTypeText: string;
23
+ };
24
+ export type RouteContractViolation = {
25
+ readonly code: "RVM-ROUTE-001" | "RVM-ROUTE-002" | "RVM-ENTRY-001" | "RVM-ENTRY-002" | "RVM-ENTRY-003" | "RVM-LEAF-001" | "RVM-AMBIGUOUS-001" | "RVM-GUARD-001" | "RVM-CATCH-001" | "RVM-DEPS-001" | "RVM-INFILE-COMPANION-001" | "RVM-KIND-001";
26
+ readonly message: string;
27
+ };
28
+ export type GuardExportByPath = Readonly<Record<string, "default" | "guard">>;
29
+ export type CatchExportByPath = Readonly<Record<string, "catch" | "catchFn">>;
30
+ export type CatchFormByPath = Readonly<Record<string, CatchForm>>;
31
+ export type DepsFormByPath = Readonly<Record<string, DepsExportKind>>;
32
+ /**
33
+ * Sibling companion path for a leaf file (e.g. "nested/Y.tsx" + "layout" → "nested/Y.layout.ts").
34
+ */
35
+ export declare function siblingCompanionPath(leafFilePath: string, kind: ConcernKind): string;
36
+ /**
37
+ * Build route descriptors and validate guards, catches, and dependencies from type info snapshots.
38
+ */
39
+ export declare function buildRouteDescriptors(snapshots: readonly TypeInfoFileSnapshot[], baseDir: string, api: TypeInfoApi): {
40
+ readonly descriptors: readonly RouteDescriptor[];
41
+ readonly violations: readonly RouteContractViolation[];
42
+ readonly guardExportByPath: GuardExportByPath;
43
+ readonly catchExportByPath: CatchExportByPath;
44
+ readonly catchFormByPath: CatchFormByPath;
45
+ readonly depsFormByPath: DepsFormByPath;
46
+ };
47
+ export {};
48
+ //# sourceMappingURL=buildRouteDescriptors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildRouteDescriptors.d.ts","sourceRoot":"","sources":["../../src/internal/buildRouteDescriptors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,WAAW,EACX,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,WAAW,EASjB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,cAAc,GAAG,QAAQ,GAAG,OAAO,CAAC;AAExE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC,CAAC;AAEF,mGAAmG;AACnG,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC;AAEhE,QAAA,MAAM,kBAAkB,6CAA8C,CAAC;AAOvE,KAAK,gBAAgB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AA8B5D,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC;IACvC,QAAQ,CAAC,2BAA2B,EAAE,OAAO,CAAC;IAC9C,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,IAAI,EACT,eAAe,GACf,eAAe,GACf,eAAe,GACf,eAAe,GACf,eAAe,GACf,cAAc,GACd,mBAAmB,GACnB,eAAe,GACf,eAAe,GACf,cAAc,GACd,0BAA0B,GAC1B,cAAc,CAAC;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC;AAC9E,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC;AAC9E,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAClE,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;AAwFtE;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAKpF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,SAAS,oBAAoB,EAAE,EAC1C,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,WAAW,GACf;IACD,QAAQ,CAAC,WAAW,EAAE,SAAS,eAAe,EAAE,CAAC;IACjD,QAAQ,CAAC,UAAU,EAAE,SAAS,sBAAsB,EAAE,CAAC;IACvD,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAC9C,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAC9C,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;CACzC,CAmRA"}