@canmi/seam-server 0.2.3
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 +38 -0
- package/dist/index.d.ts +231 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +752 -0
- package/dist/index.js.map +1 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @canmi/seam-server
|
|
2
|
+
|
|
3
|
+
Framework-agnostic server core that defines procedures, subscriptions, pages, and the HTTP protocol layer used by all adapters.
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
|
|
7
|
+
- `src/index.ts` — Public API barrel export
|
|
8
|
+
- `src/http.ts` — `createHttpHandler`, SSE helpers, `serialize`, `toWebResponse`
|
|
9
|
+
- `src/proxy.ts` — `createDevProxy`, `createStaticHandler` for dev and static file serving
|
|
10
|
+
- `src/procedure.ts` — Internal procedure types
|
|
11
|
+
- `src/types/` — JTD schema type system (`t.string()`, `t.object()`, etc.)
|
|
12
|
+
- `src/router/` — `createRouter` wiring procedures, subscriptions, and pages together
|
|
13
|
+
- `src/page/` — `definePage()`, loader functions, route matching
|
|
14
|
+
- `src/manifest/` — `buildManifest` generates manifest from definitions
|
|
15
|
+
- `src/validation/` — JTD input validation
|
|
16
|
+
|
|
17
|
+
## Key Exports
|
|
18
|
+
|
|
19
|
+
| Export | Purpose |
|
|
20
|
+
| ------------------- | ------------------------------------------------------- |
|
|
21
|
+
| `createRouter` | Wire procedures, subscriptions, and pages into a router |
|
|
22
|
+
| `createHttpHandler` | Create HTTP request handler from a router |
|
|
23
|
+
| `definePage` | Define a page with loaders and route patterns |
|
|
24
|
+
| `t` | JTD schema builder (`t.string()`, `t.object()`, etc.) |
|
|
25
|
+
| `toWebResponse` | Convert internal response to Web `Response` |
|
|
26
|
+
| `serialize` | Serialize response body to JSON |
|
|
27
|
+
| `loadBuildOutput` | Load pre-built skeleton templates |
|
|
28
|
+
|
|
29
|
+
## Development
|
|
30
|
+
|
|
31
|
+
- Build: `bun run --filter '@canmi/seam-server' build`
|
|
32
|
+
- Test: `bun run --filter '@canmi/seam-server' test`
|
|
33
|
+
|
|
34
|
+
## Notes
|
|
35
|
+
|
|
36
|
+
- Adapters depend on this package; it has no framework-specific code
|
|
37
|
+
- SSE subscriptions use `text/event-stream` with JSON-encoded data fields
|
|
38
|
+
- JTD validation runs at the protocol boundary before procedure handlers execute
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { Schema } from "jtd";
|
|
2
|
+
|
|
3
|
+
//#region src/types/schema.d.ts
|
|
4
|
+
type JTDSchema = Schema;
|
|
5
|
+
interface SchemaNode<TOutput = unknown> {
|
|
6
|
+
readonly _schema: JTDSchema;
|
|
7
|
+
/** Phantom type marker — never exists at runtime */
|
|
8
|
+
readonly _output: TOutput;
|
|
9
|
+
}
|
|
10
|
+
interface OptionalSchemaNode<TOutput = unknown> extends SchemaNode<TOutput> {
|
|
11
|
+
readonly _optional: true;
|
|
12
|
+
}
|
|
13
|
+
type Infer<T extends SchemaNode> = T["_output"];
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/types/primitives.d.ts
|
|
16
|
+
declare function string(): SchemaNode<string>;
|
|
17
|
+
declare function boolean(): SchemaNode<boolean>;
|
|
18
|
+
declare function int8(): SchemaNode<number>;
|
|
19
|
+
declare function int16(): SchemaNode<number>;
|
|
20
|
+
declare function int32(): SchemaNode<number>;
|
|
21
|
+
declare function uint8(): SchemaNode<number>;
|
|
22
|
+
declare function uint16(): SchemaNode<number>;
|
|
23
|
+
declare function uint32(): SchemaNode<number>;
|
|
24
|
+
declare function float32(): SchemaNode<number>;
|
|
25
|
+
declare function float64(): SchemaNode<number>;
|
|
26
|
+
declare function timestamp(): SchemaNode<string>;
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/types/composites.d.ts
|
|
29
|
+
type Simplify<T> = { [K in keyof T]: T[K] } & {};
|
|
30
|
+
type RequiredKeys<T extends Record<string, SchemaNode>> = { [K in keyof T]: T[K] extends OptionalSchemaNode ? never : K }[keyof T];
|
|
31
|
+
type OptionalKeys<T extends Record<string, SchemaNode>> = { [K in keyof T]: T[K] extends OptionalSchemaNode ? K : never }[keyof T];
|
|
32
|
+
type InferObject<T extends Record<string, SchemaNode>> = Simplify<{ [K in RequiredKeys<T>]: Infer<T[K]> } & { [K in OptionalKeys<T>]?: Infer<T[K]> }>;
|
|
33
|
+
declare function object<T extends Record<string, SchemaNode>>(fields: T): SchemaNode<InferObject<T>>;
|
|
34
|
+
declare function optional<T>(node: SchemaNode<T>): OptionalSchemaNode<T>;
|
|
35
|
+
declare function array<T>(node: SchemaNode<T>): SchemaNode<T[]>;
|
|
36
|
+
declare function nullable<T>(node: SchemaNode<T>): SchemaNode<T | null>;
|
|
37
|
+
declare function enumType<const T extends readonly string[]>(values: T): SchemaNode<T[number]>;
|
|
38
|
+
declare function values<T>(node: SchemaNode<T>): SchemaNode<Record<string, T>>;
|
|
39
|
+
type DiscriminatorUnion<TTag extends string, TMapping extends Record<string, SchemaNode>> = { [K in keyof TMapping & string]: Simplify<{ [P in TTag]: K } & Infer<TMapping[K]>> }[keyof TMapping & string];
|
|
40
|
+
declare function discriminator<TTag extends string, TMapping extends Record<string, SchemaNode<Record<string, unknown>>>>(tag: TTag, mapping: TMapping): SchemaNode<DiscriminatorUnion<TTag, TMapping>>;
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/types/index.d.ts
|
|
43
|
+
declare const t: {
|
|
44
|
+
readonly object: typeof object;
|
|
45
|
+
readonly optional: typeof optional;
|
|
46
|
+
readonly array: typeof array;
|
|
47
|
+
readonly nullable: typeof nullable;
|
|
48
|
+
readonly enum: typeof enumType;
|
|
49
|
+
readonly values: typeof values;
|
|
50
|
+
readonly discriminator: typeof discriminator;
|
|
51
|
+
readonly string: typeof string;
|
|
52
|
+
readonly boolean: typeof boolean;
|
|
53
|
+
readonly int8: typeof int8;
|
|
54
|
+
readonly int16: typeof int16;
|
|
55
|
+
readonly int32: typeof int32;
|
|
56
|
+
readonly uint8: typeof uint8;
|
|
57
|
+
readonly uint16: typeof uint16;
|
|
58
|
+
readonly uint32: typeof uint32;
|
|
59
|
+
readonly float32: typeof float32;
|
|
60
|
+
readonly float64: typeof float64;
|
|
61
|
+
readonly timestamp: typeof timestamp;
|
|
62
|
+
};
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/manifest/index.d.ts
|
|
65
|
+
type ProcedureType = "query" | "subscription";
|
|
66
|
+
interface ProcedureEntry {
|
|
67
|
+
type: ProcedureType;
|
|
68
|
+
input: Schema;
|
|
69
|
+
output: Schema;
|
|
70
|
+
}
|
|
71
|
+
interface ProcedureManifest {
|
|
72
|
+
version: string;
|
|
73
|
+
procedures: Record<string, ProcedureEntry>;
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/procedure.d.ts
|
|
77
|
+
interface HandleResult {
|
|
78
|
+
status: number;
|
|
79
|
+
body: unknown;
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/page/index.d.ts
|
|
83
|
+
interface LoaderResult {
|
|
84
|
+
procedure: string;
|
|
85
|
+
input: unknown;
|
|
86
|
+
}
|
|
87
|
+
type LoaderFn = (params: Record<string, string>) => LoaderResult;
|
|
88
|
+
interface LayoutDef {
|
|
89
|
+
id: string;
|
|
90
|
+
template: string;
|
|
91
|
+
loaders: Record<string, LoaderFn>;
|
|
92
|
+
}
|
|
93
|
+
interface PageDef {
|
|
94
|
+
template: string;
|
|
95
|
+
loaders: Record<string, LoaderFn>;
|
|
96
|
+
layoutChain?: LayoutDef[];
|
|
97
|
+
}
|
|
98
|
+
declare function definePage(config: PageDef): PageDef;
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/page/handler.d.ts
|
|
101
|
+
interface PageTiming {
|
|
102
|
+
/** Procedure execution time in milliseconds */
|
|
103
|
+
dataFetch: number;
|
|
104
|
+
/** Template injection time in milliseconds */
|
|
105
|
+
inject: number;
|
|
106
|
+
}
|
|
107
|
+
interface HandlePageResult {
|
|
108
|
+
status: number;
|
|
109
|
+
html: string;
|
|
110
|
+
timing?: PageTiming;
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
//#region src/router/index.d.ts
|
|
114
|
+
interface ProcedureDef<TIn = unknown, TOut = unknown> {
|
|
115
|
+
input: SchemaNode<TIn>;
|
|
116
|
+
output: SchemaNode<TOut>;
|
|
117
|
+
handler: (params: {
|
|
118
|
+
input: TIn;
|
|
119
|
+
}) => TOut | Promise<TOut>;
|
|
120
|
+
}
|
|
121
|
+
interface SubscriptionDef<TIn = unknown, TOut = unknown> {
|
|
122
|
+
type: "subscription";
|
|
123
|
+
input: SchemaNode<TIn>;
|
|
124
|
+
output: SchemaNode<TOut>;
|
|
125
|
+
handler: (params: {
|
|
126
|
+
input: TIn;
|
|
127
|
+
}) => AsyncIterable<TOut>;
|
|
128
|
+
}
|
|
129
|
+
type DefinitionMap = Record<string, ProcedureDef<any, any> | SubscriptionDef<any, any>>;
|
|
130
|
+
interface RouterOptions {
|
|
131
|
+
pages?: Record<string, PageDef>;
|
|
132
|
+
validateOutput?: boolean;
|
|
133
|
+
}
|
|
134
|
+
interface Router<T extends DefinitionMap> {
|
|
135
|
+
manifest(): ProcedureManifest;
|
|
136
|
+
handle(procedureName: string, body: unknown): Promise<HandleResult>;
|
|
137
|
+
handleSubscription(name: string, input: unknown): AsyncIterable<unknown>;
|
|
138
|
+
handlePage(path: string): Promise<HandlePageResult | null>;
|
|
139
|
+
readonly hasPages: boolean;
|
|
140
|
+
/** Exposed for adapter access to the definitions */
|
|
141
|
+
readonly procedures: T;
|
|
142
|
+
}
|
|
143
|
+
declare function createRouter<T extends DefinitionMap>(procedures: T, opts?: RouterOptions): Router<T>;
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/errors.d.ts
|
|
146
|
+
type ErrorCode = "VALIDATION_ERROR" | "NOT_FOUND" | "UNAUTHORIZED" | "FORBIDDEN" | "RATE_LIMITED" | "INTERNAL_ERROR" | (string & {});
|
|
147
|
+
declare class SeamError extends Error {
|
|
148
|
+
readonly code: string;
|
|
149
|
+
readonly status: number;
|
|
150
|
+
constructor(code: string, message: string, status?: number);
|
|
151
|
+
toJSON(): {
|
|
152
|
+
error: {
|
|
153
|
+
code: string;
|
|
154
|
+
message: string;
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
//#endregion
|
|
159
|
+
//#region src/http.d.ts
|
|
160
|
+
interface HttpRequest {
|
|
161
|
+
method: string;
|
|
162
|
+
url: string;
|
|
163
|
+
body: () => Promise<unknown>;
|
|
164
|
+
}
|
|
165
|
+
interface HttpBodyResponse {
|
|
166
|
+
status: number;
|
|
167
|
+
headers: Record<string, string>;
|
|
168
|
+
body: unknown;
|
|
169
|
+
}
|
|
170
|
+
interface HttpStreamResponse {
|
|
171
|
+
status: number;
|
|
172
|
+
headers: Record<string, string>;
|
|
173
|
+
stream: AsyncIterable<string>;
|
|
174
|
+
}
|
|
175
|
+
type HttpResponse = HttpBodyResponse | HttpStreamResponse;
|
|
176
|
+
type HttpHandler = (req: HttpRequest) => Promise<HttpResponse>;
|
|
177
|
+
interface HttpHandlerOptions {
|
|
178
|
+
staticDir?: string;
|
|
179
|
+
fallback?: HttpHandler;
|
|
180
|
+
}
|
|
181
|
+
/** Format a single SSE data event */
|
|
182
|
+
declare function sseDataEvent(data: unknown): string;
|
|
183
|
+
/** Format an SSE error event */
|
|
184
|
+
declare function sseErrorEvent(code: string, message: string): string;
|
|
185
|
+
/** Format an SSE complete event */
|
|
186
|
+
declare function sseCompleteEvent(): string;
|
|
187
|
+
declare function createHttpHandler<T extends DefinitionMap>(router: Router<T>, opts?: HttpHandlerOptions): HttpHandler;
|
|
188
|
+
declare function serialize(body: unknown): string;
|
|
189
|
+
/** Consume an async stream chunk-by-chunk; return false from write to stop early. */
|
|
190
|
+
declare function drainStream(stream: AsyncIterable<string>, write: (chunk: string) => boolean | void): Promise<void>;
|
|
191
|
+
/** Convert an HttpResponse to a Web API Response (for adapters using fetch-compatible runtimes) */
|
|
192
|
+
declare function toWebResponse(result: HttpResponse): Response;
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region src/page/build-loader.d.ts
|
|
195
|
+
declare function loadBuildOutput(distDir: string): Record<string, PageDef>;
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region src/subscription.d.ts
|
|
198
|
+
/**
|
|
199
|
+
* Bridge callback-style event sources to an AsyncGenerator.
|
|
200
|
+
*
|
|
201
|
+
* Usage:
|
|
202
|
+
* const stream = fromCallback<string>(({ emit, end, error }) => {
|
|
203
|
+
* emitter.on("data", emit);
|
|
204
|
+
* emitter.on("end", end);
|
|
205
|
+
* emitter.on("error", error);
|
|
206
|
+
* return () => emitter.removeAllListeners();
|
|
207
|
+
* });
|
|
208
|
+
*/
|
|
209
|
+
interface CallbackSink<T> {
|
|
210
|
+
emit: (value: T) => void;
|
|
211
|
+
end: () => void;
|
|
212
|
+
error: (err: Error) => void;
|
|
213
|
+
}
|
|
214
|
+
declare function fromCallback<T>(setup: (sink: CallbackSink<T>) => (() => void) | void): AsyncGenerator<T, void, undefined>;
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/proxy.d.ts
|
|
217
|
+
interface DevProxyOptions {
|
|
218
|
+
/** Target URL to forward requests to (e.g. "http://localhost:5173") */
|
|
219
|
+
target: string;
|
|
220
|
+
}
|
|
221
|
+
interface StaticHandlerOptions {
|
|
222
|
+
/** Directory to serve static files from */
|
|
223
|
+
dir: string;
|
|
224
|
+
}
|
|
225
|
+
/** Forward non-seam requests to a dev server (e.g. Vite) */
|
|
226
|
+
declare function createDevProxy(opts: DevProxyOptions): HttpHandler;
|
|
227
|
+
/** Serve static files from a directory, with index.html fallback for directories */
|
|
228
|
+
declare function createStaticHandler(opts: StaticHandlerOptions): HttpHandler;
|
|
229
|
+
//#endregion
|
|
230
|
+
export { type CallbackSink, type DefinitionMap, type DevProxyOptions, type ErrorCode, type HandlePageResult, type HandleResult, type HttpBodyResponse, type HttpHandler, type HttpHandlerOptions, type HttpRequest, type HttpResponse, type HttpStreamResponse, type Infer, type LayoutDef, type LoaderFn, type OptionalSchemaNode, type PageDef, type PageTiming, type ProcedureDef, type ProcedureEntry, type ProcedureManifest, type ProcedureType, type Router, type RouterOptions, type SchemaNode, SeamError, type StaticHandlerOptions, type SubscriptionDef, createDevProxy, createHttpHandler, createRouter, createStaticHandler, definePage, drainStream, fromCallback, loadBuildOutput, serialize, sseCompleteEvent, sseDataEvent, sseErrorEvent, t, toWebResponse };
|
|
231
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types/schema.ts","../src/types/primitives.ts","../src/types/composites.ts","../src/types/index.ts","../src/manifest/index.ts","../src/procedure.ts","../src/page/index.ts","../src/page/handler.ts","../src/router/index.ts","../src/errors.ts","../src/http.ts","../src/page/build-loader.ts","../src/subscription.ts","../src/proxy.ts"],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
import { validate } from "jtd";
|
|
2
|
+
import { escapeHtml, inject } from "@canmi/seam-injector";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { extname, join } from "node:path";
|
|
5
|
+
import { readFileSync } from "node:fs";
|
|
6
|
+
|
|
7
|
+
//#region \0rolldown/runtime.js
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __exportAll = (all, no_symbols) => {
|
|
10
|
+
let target = {};
|
|
11
|
+
for (var name in all) {
|
|
12
|
+
__defProp(target, name, {
|
|
13
|
+
get: all[name],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
if (!no_symbols) {
|
|
18
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
19
|
+
}
|
|
20
|
+
return target;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/types/schema.ts
|
|
25
|
+
function createSchemaNode(schema) {
|
|
26
|
+
return { _schema: schema };
|
|
27
|
+
}
|
|
28
|
+
function createOptionalSchemaNode(schema) {
|
|
29
|
+
return {
|
|
30
|
+
_schema: schema,
|
|
31
|
+
_optional: true
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/types/primitives.ts
|
|
37
|
+
var primitives_exports = /* @__PURE__ */ __exportAll({
|
|
38
|
+
boolean: () => boolean,
|
|
39
|
+
float32: () => float32,
|
|
40
|
+
float64: () => float64,
|
|
41
|
+
int16: () => int16,
|
|
42
|
+
int32: () => int32,
|
|
43
|
+
int8: () => int8,
|
|
44
|
+
string: () => string,
|
|
45
|
+
timestamp: () => timestamp,
|
|
46
|
+
uint16: () => uint16,
|
|
47
|
+
uint32: () => uint32,
|
|
48
|
+
uint8: () => uint8
|
|
49
|
+
});
|
|
50
|
+
function string() {
|
|
51
|
+
return createSchemaNode({ type: "string" });
|
|
52
|
+
}
|
|
53
|
+
function boolean() {
|
|
54
|
+
return createSchemaNode({ type: "boolean" });
|
|
55
|
+
}
|
|
56
|
+
function int8() {
|
|
57
|
+
return createSchemaNode({ type: "int8" });
|
|
58
|
+
}
|
|
59
|
+
function int16() {
|
|
60
|
+
return createSchemaNode({ type: "int16" });
|
|
61
|
+
}
|
|
62
|
+
function int32() {
|
|
63
|
+
return createSchemaNode({ type: "int32" });
|
|
64
|
+
}
|
|
65
|
+
function uint8() {
|
|
66
|
+
return createSchemaNode({ type: "uint8" });
|
|
67
|
+
}
|
|
68
|
+
function uint16() {
|
|
69
|
+
return createSchemaNode({ type: "uint16" });
|
|
70
|
+
}
|
|
71
|
+
function uint32() {
|
|
72
|
+
return createSchemaNode({ type: "uint32" });
|
|
73
|
+
}
|
|
74
|
+
function float32() {
|
|
75
|
+
return createSchemaNode({ type: "float32" });
|
|
76
|
+
}
|
|
77
|
+
function float64() {
|
|
78
|
+
return createSchemaNode({ type: "float64" });
|
|
79
|
+
}
|
|
80
|
+
function timestamp() {
|
|
81
|
+
return createSchemaNode({ type: "timestamp" });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/types/composites.ts
|
|
86
|
+
function object(fields) {
|
|
87
|
+
const properties = {};
|
|
88
|
+
const optionalProperties = {};
|
|
89
|
+
for (const [key, node] of Object.entries(fields)) if ("_optional" in node && node._optional === true) optionalProperties[key] = node._schema;
|
|
90
|
+
else properties[key] = node._schema;
|
|
91
|
+
const schema = {};
|
|
92
|
+
if (Object.keys(properties).length > 0 || Object.keys(optionalProperties).length === 0) schema.properties = properties;
|
|
93
|
+
if (Object.keys(optionalProperties).length > 0) schema.optionalProperties = optionalProperties;
|
|
94
|
+
return createSchemaNode(schema);
|
|
95
|
+
}
|
|
96
|
+
function optional(node) {
|
|
97
|
+
return createOptionalSchemaNode(node._schema);
|
|
98
|
+
}
|
|
99
|
+
function array(node) {
|
|
100
|
+
return createSchemaNode({ elements: node._schema });
|
|
101
|
+
}
|
|
102
|
+
function nullable(node) {
|
|
103
|
+
return createSchemaNode({
|
|
104
|
+
...node._schema,
|
|
105
|
+
nullable: true
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function enumType(values) {
|
|
109
|
+
return createSchemaNode({ enum: [...values] });
|
|
110
|
+
}
|
|
111
|
+
function values(node) {
|
|
112
|
+
return createSchemaNode({ values: node._schema });
|
|
113
|
+
}
|
|
114
|
+
function discriminator(tag, mapping) {
|
|
115
|
+
const jtdMapping = {};
|
|
116
|
+
for (const [key, node] of Object.entries(mapping)) jtdMapping[key] = node._schema;
|
|
117
|
+
return createSchemaNode({
|
|
118
|
+
discriminator: tag,
|
|
119
|
+
mapping: jtdMapping
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/types/index.ts
|
|
125
|
+
const t = {
|
|
126
|
+
...primitives_exports,
|
|
127
|
+
object,
|
|
128
|
+
optional,
|
|
129
|
+
array,
|
|
130
|
+
nullable,
|
|
131
|
+
enum: enumType,
|
|
132
|
+
values,
|
|
133
|
+
discriminator
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/manifest/index.ts
|
|
138
|
+
function buildManifest(definitions) {
|
|
139
|
+
const mapped = {};
|
|
140
|
+
for (const [name, def] of Object.entries(definitions)) mapped[name] = {
|
|
141
|
+
type: def.type === "subscription" ? "subscription" : "query",
|
|
142
|
+
input: def.input._schema,
|
|
143
|
+
output: def.output._schema
|
|
144
|
+
};
|
|
145
|
+
return {
|
|
146
|
+
version: "0.1.0",
|
|
147
|
+
procedures: mapped
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/errors.ts
|
|
153
|
+
const DEFAULT_STATUS = {
|
|
154
|
+
VALIDATION_ERROR: 400,
|
|
155
|
+
UNAUTHORIZED: 401,
|
|
156
|
+
FORBIDDEN: 403,
|
|
157
|
+
NOT_FOUND: 404,
|
|
158
|
+
RATE_LIMITED: 429,
|
|
159
|
+
INTERNAL_ERROR: 500
|
|
160
|
+
};
|
|
161
|
+
var SeamError = class extends Error {
|
|
162
|
+
code;
|
|
163
|
+
status;
|
|
164
|
+
constructor(code, message, status) {
|
|
165
|
+
super(message);
|
|
166
|
+
this.code = code;
|
|
167
|
+
this.status = status ?? DEFAULT_STATUS[code] ?? 500;
|
|
168
|
+
this.name = "SeamError";
|
|
169
|
+
}
|
|
170
|
+
toJSON() {
|
|
171
|
+
return { error: {
|
|
172
|
+
code: this.code,
|
|
173
|
+
message: this.message
|
|
174
|
+
} };
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/validation/index.ts
|
|
180
|
+
function validateInput(schema, data) {
|
|
181
|
+
const errors = validate(schema, data, {
|
|
182
|
+
maxDepth: 32,
|
|
183
|
+
maxErrors: 10
|
|
184
|
+
});
|
|
185
|
+
return {
|
|
186
|
+
valid: errors.length === 0,
|
|
187
|
+
errors
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function formatValidationErrors(errors) {
|
|
191
|
+
return errors.map((e) => {
|
|
192
|
+
return `${e.instancePath.length > 0 ? e.instancePath.join("/") : "(root)"} (schema: ${e.schemaPath.join("/")})`;
|
|
193
|
+
}).join("; ");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region src/router/handler.ts
|
|
198
|
+
async function handleRequest(procedures, procedureName, rawBody, validateOutput) {
|
|
199
|
+
const procedure = procedures.get(procedureName);
|
|
200
|
+
if (!procedure) return {
|
|
201
|
+
status: 404,
|
|
202
|
+
body: new SeamError("NOT_FOUND", `Procedure '${procedureName}' not found`).toJSON()
|
|
203
|
+
};
|
|
204
|
+
const validation = validateInput(procedure.inputSchema, rawBody);
|
|
205
|
+
if (!validation.valid) return {
|
|
206
|
+
status: 400,
|
|
207
|
+
body: new SeamError("VALIDATION_ERROR", `Input validation failed: ${formatValidationErrors(validation.errors)}`).toJSON()
|
|
208
|
+
};
|
|
209
|
+
try {
|
|
210
|
+
const result = await procedure.handler({ input: rawBody });
|
|
211
|
+
if (validateOutput) {
|
|
212
|
+
const outValidation = validateInput(procedure.outputSchema, result);
|
|
213
|
+
if (!outValidation.valid) return {
|
|
214
|
+
status: 500,
|
|
215
|
+
body: new SeamError("INTERNAL_ERROR", `Output validation failed: ${formatValidationErrors(outValidation.errors)}`).toJSON()
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
status: 200,
|
|
220
|
+
body: result
|
|
221
|
+
};
|
|
222
|
+
} catch (error) {
|
|
223
|
+
if (error instanceof SeamError) return {
|
|
224
|
+
status: error.status,
|
|
225
|
+
body: error.toJSON()
|
|
226
|
+
};
|
|
227
|
+
return {
|
|
228
|
+
status: 500,
|
|
229
|
+
body: new SeamError("INTERNAL_ERROR", error instanceof Error ? error.message : "Unknown error").toJSON()
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async function* handleSubscription(subscriptions, name, rawInput, validateOutput) {
|
|
234
|
+
const sub = subscriptions.get(name);
|
|
235
|
+
if (!sub) throw new SeamError("NOT_FOUND", `Subscription '${name}' not found`);
|
|
236
|
+
const validation = validateInput(sub.inputSchema, rawInput);
|
|
237
|
+
if (!validation.valid) throw new SeamError("VALIDATION_ERROR", `Input validation failed: ${formatValidationErrors(validation.errors)}`);
|
|
238
|
+
for await (const value of sub.handler({ input: rawInput })) {
|
|
239
|
+
if (validateOutput) {
|
|
240
|
+
const outValidation = validateInput(sub.outputSchema, value);
|
|
241
|
+
if (!outValidation.valid) throw new SeamError("INTERNAL_ERROR", `Output validation failed: ${formatValidationErrors(outValidation.errors)}`);
|
|
242
|
+
}
|
|
243
|
+
yield value;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region src/page/handler.ts
|
|
249
|
+
/** Flatten keyed loader results: spread object values into a flat map for slot resolution */
|
|
250
|
+
function flattenForSlots(keyed) {
|
|
251
|
+
const merged = { ...keyed };
|
|
252
|
+
for (const value of Object.values(keyed)) if (value && typeof value === "object" && !Array.isArray(value)) Object.assign(merged, value);
|
|
253
|
+
return merged;
|
|
254
|
+
}
|
|
255
|
+
/** Execute loaders, returning keyed results */
|
|
256
|
+
async function executeLoaders(loaders, params, procedures) {
|
|
257
|
+
const entries = Object.entries(loaders);
|
|
258
|
+
const results = await Promise.all(entries.map(async ([key, loader]) => {
|
|
259
|
+
const { procedure, input } = loader(params);
|
|
260
|
+
const proc = procedures.get(procedure);
|
|
261
|
+
if (!proc) throw new SeamError("INTERNAL_ERROR", `Procedure '${procedure}' not found`);
|
|
262
|
+
return [key, await proc.handler({ input })];
|
|
263
|
+
}));
|
|
264
|
+
return Object.fromEntries(results);
|
|
265
|
+
}
|
|
266
|
+
/** Split-inject a layout template around its outlet marker */
|
|
267
|
+
function injectLayout(template, data, inner) {
|
|
268
|
+
const outletIdx = template.indexOf("<!--seam:outlet-->");
|
|
269
|
+
if (outletIdx === -1) return inject(template, data, { skipDataScript: true });
|
|
270
|
+
const before = template.slice(0, outletIdx);
|
|
271
|
+
const after = template.slice(outletIdx + 18);
|
|
272
|
+
const injectedBefore = inject(before, data, { skipDataScript: true });
|
|
273
|
+
const injectedAfter = inject(after, data, { skipDataScript: true });
|
|
274
|
+
return injectedBefore + inner + injectedAfter;
|
|
275
|
+
}
|
|
276
|
+
async function handlePageRequest(page, params, procedures) {
|
|
277
|
+
try {
|
|
278
|
+
const t0 = performance.now();
|
|
279
|
+
const layoutChain = page.layoutChain ?? [];
|
|
280
|
+
const loaderResults = await Promise.all([...layoutChain.map((layout) => executeLoaders(layout.loaders, params, procedures)), executeLoaders(page.loaders, params, procedures)]);
|
|
281
|
+
const t1 = performance.now();
|
|
282
|
+
const layoutResults = loaderResults.slice(0, layoutChain.length);
|
|
283
|
+
const pageKeyed = loaderResults[loaderResults.length - 1];
|
|
284
|
+
let innerContent = inject(page.template, flattenForSlots(pageKeyed), { skipDataScript: true });
|
|
285
|
+
const layoutKeyed = {};
|
|
286
|
+
for (let i = layoutChain.length - 1; i >= 0; i--) {
|
|
287
|
+
const layout = layoutChain[i];
|
|
288
|
+
const data = layoutResults[i];
|
|
289
|
+
layoutKeyed[layout.id] = data;
|
|
290
|
+
innerContent = injectLayout(layout.template, flattenForSlots(data), innerContent);
|
|
291
|
+
}
|
|
292
|
+
const seamData = { ...pageKeyed };
|
|
293
|
+
if (Object.keys(layoutKeyed).length > 0) seamData._layouts = layoutKeyed;
|
|
294
|
+
const script = `<script id="__SEAM_DATA__" type="application/json">${JSON.stringify(seamData)}<\/script>`;
|
|
295
|
+
const bodyClose = innerContent.lastIndexOf("</body>");
|
|
296
|
+
let html;
|
|
297
|
+
if (bodyClose !== -1) html = innerContent.slice(0, bodyClose) + script + innerContent.slice(bodyClose);
|
|
298
|
+
else html = innerContent + script;
|
|
299
|
+
const t2 = performance.now();
|
|
300
|
+
return {
|
|
301
|
+
status: 200,
|
|
302
|
+
html,
|
|
303
|
+
timing: {
|
|
304
|
+
dataFetch: t1 - t0,
|
|
305
|
+
inject: t2 - t1
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
} catch (error) {
|
|
309
|
+
return {
|
|
310
|
+
status: 500,
|
|
311
|
+
html: `<!DOCTYPE html><html><body><h1>500 Internal Server Error</h1><p>${escapeHtml(error instanceof Error ? error.message : "Unknown error")}</p></body></html>`
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
//#endregion
|
|
317
|
+
//#region src/page/route-matcher.ts
|
|
318
|
+
function compileRoute(pattern) {
|
|
319
|
+
return { segments: pattern.split("/").filter(Boolean).map((seg) => seg.startsWith(":") ? {
|
|
320
|
+
kind: "param",
|
|
321
|
+
name: seg.slice(1)
|
|
322
|
+
} : {
|
|
323
|
+
kind: "static",
|
|
324
|
+
value: seg
|
|
325
|
+
}) };
|
|
326
|
+
}
|
|
327
|
+
function matchRoute(segments, pathParts) {
|
|
328
|
+
if (segments.length !== pathParts.length) return null;
|
|
329
|
+
const params = {};
|
|
330
|
+
for (let i = 0; i < segments.length; i++) {
|
|
331
|
+
const seg = segments[i];
|
|
332
|
+
if (seg.kind === "static") {
|
|
333
|
+
if (seg.value !== pathParts[i]) return null;
|
|
334
|
+
} else params[seg.name] = pathParts[i];
|
|
335
|
+
}
|
|
336
|
+
return params;
|
|
337
|
+
}
|
|
338
|
+
var RouteMatcher = class {
|
|
339
|
+
routes = [];
|
|
340
|
+
add(pattern, value) {
|
|
341
|
+
this.routes.push({
|
|
342
|
+
compiled: compileRoute(pattern),
|
|
343
|
+
value
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
match(path) {
|
|
347
|
+
const parts = path.split("/").filter(Boolean);
|
|
348
|
+
for (const route of this.routes) {
|
|
349
|
+
const params = matchRoute(route.compiled.segments, parts);
|
|
350
|
+
if (params) return {
|
|
351
|
+
value: route.value,
|
|
352
|
+
params
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
//#endregion
|
|
360
|
+
//#region src/router/index.ts
|
|
361
|
+
function isSubscriptionDef(def) {
|
|
362
|
+
return "type" in def && def.type === "subscription";
|
|
363
|
+
}
|
|
364
|
+
function createRouter(procedures, opts) {
|
|
365
|
+
const procedureMap = /* @__PURE__ */ new Map();
|
|
366
|
+
const subscriptionMap = /* @__PURE__ */ new Map();
|
|
367
|
+
for (const [name, def] of Object.entries(procedures)) if (isSubscriptionDef(def)) subscriptionMap.set(name, {
|
|
368
|
+
inputSchema: def.input._schema,
|
|
369
|
+
outputSchema: def.output._schema,
|
|
370
|
+
handler: def.handler
|
|
371
|
+
});
|
|
372
|
+
else procedureMap.set(name, {
|
|
373
|
+
inputSchema: def.input._schema,
|
|
374
|
+
outputSchema: def.output._schema,
|
|
375
|
+
handler: def.handler
|
|
376
|
+
});
|
|
377
|
+
const shouldValidateOutput = opts?.validateOutput ?? (typeof process !== "undefined" && process.env.NODE_ENV !== "production");
|
|
378
|
+
const pageMatcher = new RouteMatcher();
|
|
379
|
+
const pages = opts?.pages;
|
|
380
|
+
if (pages) for (const [pattern, page] of Object.entries(pages)) pageMatcher.add(pattern, page);
|
|
381
|
+
return {
|
|
382
|
+
procedures,
|
|
383
|
+
hasPages: !!pages && Object.keys(pages).length > 0,
|
|
384
|
+
manifest() {
|
|
385
|
+
return buildManifest(procedures);
|
|
386
|
+
},
|
|
387
|
+
handle(procedureName, body) {
|
|
388
|
+
return handleRequest(procedureMap, procedureName, body, shouldValidateOutput);
|
|
389
|
+
},
|
|
390
|
+
handleSubscription(name, input) {
|
|
391
|
+
return handleSubscription(subscriptionMap, name, input, shouldValidateOutput);
|
|
392
|
+
},
|
|
393
|
+
async handlePage(path) {
|
|
394
|
+
const match = pageMatcher.match(path);
|
|
395
|
+
if (!match) return null;
|
|
396
|
+
return handlePageRequest(match.value, match.params, procedureMap);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
//#endregion
|
|
402
|
+
//#region src/page/index.ts
|
|
403
|
+
function definePage(config) {
|
|
404
|
+
return {
|
|
405
|
+
...config,
|
|
406
|
+
layoutChain: config.layoutChain ?? []
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
//#endregion
|
|
411
|
+
//#region src/mime.ts
|
|
412
|
+
const MIME_TYPES = {
|
|
413
|
+
".js": "application/javascript",
|
|
414
|
+
".mjs": "application/javascript",
|
|
415
|
+
".css": "text/css",
|
|
416
|
+
".html": "text/html",
|
|
417
|
+
".json": "application/json",
|
|
418
|
+
".svg": "image/svg+xml",
|
|
419
|
+
".png": "image/png",
|
|
420
|
+
".jpg": "image/jpeg",
|
|
421
|
+
".jpeg": "image/jpeg",
|
|
422
|
+
".gif": "image/gif",
|
|
423
|
+
".woff": "font/woff",
|
|
424
|
+
".woff2": "font/woff2",
|
|
425
|
+
".ttf": "font/ttf",
|
|
426
|
+
".ico": "image/x-icon",
|
|
427
|
+
".map": "application/json",
|
|
428
|
+
".ts": "application/javascript",
|
|
429
|
+
".tsx": "application/javascript"
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
//#endregion
|
|
433
|
+
//#region src/http.ts
|
|
434
|
+
const RPC_PREFIX = "/_seam/rpc/";
|
|
435
|
+
const PAGE_PREFIX = "/_seam/page/";
|
|
436
|
+
const STATIC_PREFIX = "/_seam/static/";
|
|
437
|
+
const SUBSCRIBE_PREFIX = "/_seam/subscribe/";
|
|
438
|
+
const MANIFEST_PATH = "/_seam/manifest.json";
|
|
439
|
+
const JSON_HEADER = { "Content-Type": "application/json" };
|
|
440
|
+
const HTML_HEADER = { "Content-Type": "text/html; charset=utf-8" };
|
|
441
|
+
const SSE_HEADER = {
|
|
442
|
+
"Content-Type": "text/event-stream",
|
|
443
|
+
"Cache-Control": "no-cache",
|
|
444
|
+
Connection: "keep-alive"
|
|
445
|
+
};
|
|
446
|
+
const IMMUTABLE_CACHE = "public, max-age=31536000, immutable";
|
|
447
|
+
function jsonResponse(status, body) {
|
|
448
|
+
return {
|
|
449
|
+
status,
|
|
450
|
+
headers: JSON_HEADER,
|
|
451
|
+
body
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
function errorResponse(status, code, message) {
|
|
455
|
+
return jsonResponse(status, new SeamError(code, message).toJSON());
|
|
456
|
+
}
|
|
457
|
+
async function handleStaticAsset(assetPath, staticDir) {
|
|
458
|
+
if (assetPath.includes("..")) return errorResponse(403, "VALIDATION_ERROR", "Forbidden");
|
|
459
|
+
const filePath = join(staticDir, assetPath);
|
|
460
|
+
try {
|
|
461
|
+
const content = await readFile(filePath, "utf-8");
|
|
462
|
+
const contentType = MIME_TYPES[extname(filePath)] || "application/octet-stream";
|
|
463
|
+
return {
|
|
464
|
+
status: 200,
|
|
465
|
+
headers: {
|
|
466
|
+
"Content-Type": contentType,
|
|
467
|
+
"Cache-Control": IMMUTABLE_CACHE
|
|
468
|
+
},
|
|
469
|
+
body: content
|
|
470
|
+
};
|
|
471
|
+
} catch {
|
|
472
|
+
return errorResponse(404, "NOT_FOUND", "Asset not found");
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
/** Format a single SSE data event */
|
|
476
|
+
function sseDataEvent(data) {
|
|
477
|
+
return `event: data\ndata: ${JSON.stringify(data)}\n\n`;
|
|
478
|
+
}
|
|
479
|
+
/** Format an SSE error event */
|
|
480
|
+
function sseErrorEvent(code, message) {
|
|
481
|
+
return `event: error\ndata: ${JSON.stringify({
|
|
482
|
+
code,
|
|
483
|
+
message
|
|
484
|
+
})}\n\n`;
|
|
485
|
+
}
|
|
486
|
+
/** Format an SSE complete event */
|
|
487
|
+
function sseCompleteEvent() {
|
|
488
|
+
return "event: complete\ndata: {}\n\n";
|
|
489
|
+
}
|
|
490
|
+
async function* sseStream(router, name, input) {
|
|
491
|
+
try {
|
|
492
|
+
for await (const value of router.handleSubscription(name, input)) yield sseDataEvent(value);
|
|
493
|
+
yield sseCompleteEvent();
|
|
494
|
+
} catch (error) {
|
|
495
|
+
if (error instanceof SeamError) yield sseErrorEvent(error.code, error.message);
|
|
496
|
+
else yield sseErrorEvent("INTERNAL_ERROR", error instanceof Error ? error.message : "Unknown error");
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
function createHttpHandler(router, opts) {
|
|
500
|
+
return async (req) => {
|
|
501
|
+
const url = new URL(req.url, "http://localhost");
|
|
502
|
+
const { pathname } = url;
|
|
503
|
+
if (req.method === "GET" && pathname === MANIFEST_PATH) return jsonResponse(200, router.manifest());
|
|
504
|
+
if (req.method === "POST" && pathname.startsWith(RPC_PREFIX)) {
|
|
505
|
+
const name = pathname.slice(11);
|
|
506
|
+
if (!name) return errorResponse(404, "NOT_FOUND", "Empty procedure name");
|
|
507
|
+
let body;
|
|
508
|
+
try {
|
|
509
|
+
body = await req.body();
|
|
510
|
+
} catch {
|
|
511
|
+
return errorResponse(400, "VALIDATION_ERROR", "Invalid JSON body");
|
|
512
|
+
}
|
|
513
|
+
const result = await router.handle(name, body);
|
|
514
|
+
return jsonResponse(result.status, result.body);
|
|
515
|
+
}
|
|
516
|
+
if (req.method === "GET" && pathname.startsWith(SUBSCRIBE_PREFIX)) {
|
|
517
|
+
const name = pathname.slice(17);
|
|
518
|
+
if (!name) return errorResponse(404, "NOT_FOUND", "Empty subscription name");
|
|
519
|
+
const rawInput = url.searchParams.get("input");
|
|
520
|
+
let input;
|
|
521
|
+
try {
|
|
522
|
+
input = rawInput ? JSON.parse(rawInput) : {};
|
|
523
|
+
} catch {
|
|
524
|
+
return errorResponse(400, "VALIDATION_ERROR", "Invalid input query parameter");
|
|
525
|
+
}
|
|
526
|
+
return {
|
|
527
|
+
status: 200,
|
|
528
|
+
headers: SSE_HEADER,
|
|
529
|
+
stream: sseStream(router, name, input)
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
if (req.method === "GET" && pathname.startsWith(PAGE_PREFIX) && router.hasPages) {
|
|
533
|
+
const pagePath = "/" + pathname.slice(12);
|
|
534
|
+
const result = await router.handlePage(pagePath);
|
|
535
|
+
if (result) return {
|
|
536
|
+
status: result.status,
|
|
537
|
+
headers: HTML_HEADER,
|
|
538
|
+
body: result.html
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
if (req.method === "GET" && pathname.startsWith(STATIC_PREFIX) && opts?.staticDir) return handleStaticAsset(pathname.slice(14), opts.staticDir);
|
|
542
|
+
if (opts?.fallback) return opts.fallback(req);
|
|
543
|
+
return errorResponse(404, "NOT_FOUND", "Not found");
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
function serialize(body) {
|
|
547
|
+
return typeof body === "string" ? body : JSON.stringify(body);
|
|
548
|
+
}
|
|
549
|
+
/** Consume an async stream chunk-by-chunk; return false from write to stop early. */
|
|
550
|
+
async function drainStream(stream, write) {
|
|
551
|
+
try {
|
|
552
|
+
for await (const chunk of stream) if (write(chunk) === false) break;
|
|
553
|
+
} catch {}
|
|
554
|
+
}
|
|
555
|
+
/** Convert an HttpResponse to a Web API Response (for adapters using fetch-compatible runtimes) */
|
|
556
|
+
function toWebResponse(result) {
|
|
557
|
+
if ("stream" in result) {
|
|
558
|
+
const stream = result.stream;
|
|
559
|
+
const encoder = new TextEncoder();
|
|
560
|
+
const readable = new ReadableStream({ async start(controller) {
|
|
561
|
+
await drainStream(stream, (chunk) => {
|
|
562
|
+
controller.enqueue(encoder.encode(chunk));
|
|
563
|
+
});
|
|
564
|
+
controller.close();
|
|
565
|
+
} });
|
|
566
|
+
return new Response(readable, {
|
|
567
|
+
status: result.status,
|
|
568
|
+
headers: result.headers
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
return new Response(serialize(result.body), {
|
|
572
|
+
status: result.status,
|
|
573
|
+
headers: result.headers
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
//#endregion
|
|
578
|
+
//#region src/page/build-loader.ts
|
|
579
|
+
function buildLoaderFn(config) {
|
|
580
|
+
return (params) => {
|
|
581
|
+
const input = {};
|
|
582
|
+
if (config.params) for (const [key, mapping] of Object.entries(config.params)) {
|
|
583
|
+
const raw = params[key];
|
|
584
|
+
input[key] = mapping.type === "int" ? Number(raw) : raw;
|
|
585
|
+
}
|
|
586
|
+
return {
|
|
587
|
+
procedure: config.procedure,
|
|
588
|
+
input
|
|
589
|
+
};
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
function buildLoaderFns(configs) {
|
|
593
|
+
const fns = {};
|
|
594
|
+
for (const [key, config] of Object.entries(configs)) fns[key] = buildLoaderFn(config);
|
|
595
|
+
return fns;
|
|
596
|
+
}
|
|
597
|
+
/** Resolve parent chain for a layout, returning outer-to-inner order */
|
|
598
|
+
function resolveLayoutChain(layoutId, layoutEntries, templates) {
|
|
599
|
+
const chain = [];
|
|
600
|
+
let currentId = layoutId;
|
|
601
|
+
while (currentId) {
|
|
602
|
+
const entry = layoutEntries[currentId];
|
|
603
|
+
if (!entry) break;
|
|
604
|
+
chain.push({
|
|
605
|
+
id: currentId,
|
|
606
|
+
template: templates[currentId],
|
|
607
|
+
loaders: buildLoaderFns(entry.loaders ?? {})
|
|
608
|
+
});
|
|
609
|
+
currentId = entry.parent;
|
|
610
|
+
}
|
|
611
|
+
chain.reverse();
|
|
612
|
+
return chain;
|
|
613
|
+
}
|
|
614
|
+
function loadBuildOutput(distDir) {
|
|
615
|
+
const raw = readFileSync(join(distDir, "route-manifest.json"), "utf-8");
|
|
616
|
+
const manifest = JSON.parse(raw);
|
|
617
|
+
const layoutTemplates = {};
|
|
618
|
+
const layoutEntries = manifest.layouts ?? {};
|
|
619
|
+
for (const [id, entry] of Object.entries(layoutEntries)) layoutTemplates[id] = readFileSync(join(distDir, entry.template), "utf-8");
|
|
620
|
+
const pages = {};
|
|
621
|
+
for (const [path, entry] of Object.entries(manifest.routes)) pages[path] = {
|
|
622
|
+
template: readFileSync(join(distDir, entry.template), "utf-8"),
|
|
623
|
+
loaders: buildLoaderFns(entry.loaders),
|
|
624
|
+
layoutChain: entry.layout ? resolveLayoutChain(entry.layout, layoutEntries, layoutTemplates) : []
|
|
625
|
+
};
|
|
626
|
+
return pages;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
//#endregion
|
|
630
|
+
//#region src/subscription.ts
|
|
631
|
+
function fromCallback(setup) {
|
|
632
|
+
const queue = [];
|
|
633
|
+
let resolve = null;
|
|
634
|
+
let done = false;
|
|
635
|
+
function notify() {
|
|
636
|
+
if (resolve) {
|
|
637
|
+
const r = resolve;
|
|
638
|
+
resolve = null;
|
|
639
|
+
r();
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
const cleanup = setup({
|
|
643
|
+
emit(value) {
|
|
644
|
+
if (done) return;
|
|
645
|
+
queue.push({
|
|
646
|
+
type: "value",
|
|
647
|
+
value
|
|
648
|
+
});
|
|
649
|
+
notify();
|
|
650
|
+
},
|
|
651
|
+
end() {
|
|
652
|
+
if (done) return;
|
|
653
|
+
done = true;
|
|
654
|
+
queue.push({ type: "end" });
|
|
655
|
+
notify();
|
|
656
|
+
},
|
|
657
|
+
error(err) {
|
|
658
|
+
if (done) return;
|
|
659
|
+
done = true;
|
|
660
|
+
queue.push({
|
|
661
|
+
type: "error",
|
|
662
|
+
error: err
|
|
663
|
+
});
|
|
664
|
+
notify();
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
async function* generate() {
|
|
668
|
+
try {
|
|
669
|
+
while (true) {
|
|
670
|
+
if (queue.length === 0) await new Promise((r) => {
|
|
671
|
+
resolve = r;
|
|
672
|
+
});
|
|
673
|
+
while (queue.length > 0) {
|
|
674
|
+
const item = queue.shift();
|
|
675
|
+
if (item.type === "value") yield item.value;
|
|
676
|
+
else if (item.type === "error") throw item.error;
|
|
677
|
+
else return;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
} finally {
|
|
681
|
+
done = true;
|
|
682
|
+
if (cleanup) cleanup();
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return generate();
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
//#endregion
|
|
689
|
+
//#region src/proxy.ts
|
|
690
|
+
/** Forward non-seam requests to a dev server (e.g. Vite) */
|
|
691
|
+
function createDevProxy(opts) {
|
|
692
|
+
const target = opts.target.replace(/\/$/, "");
|
|
693
|
+
return async (req) => {
|
|
694
|
+
const url = new URL(req.url, "http://localhost");
|
|
695
|
+
const proxyUrl = `${target}${url.pathname}${url.search}`;
|
|
696
|
+
try {
|
|
697
|
+
const resp = await fetch(proxyUrl, {
|
|
698
|
+
method: req.method,
|
|
699
|
+
headers: { Accept: "*/*" }
|
|
700
|
+
});
|
|
701
|
+
const body = await resp.text();
|
|
702
|
+
const headers = {};
|
|
703
|
+
resp.headers.forEach((value, key) => {
|
|
704
|
+
headers[key] = value;
|
|
705
|
+
});
|
|
706
|
+
return {
|
|
707
|
+
status: resp.status,
|
|
708
|
+
headers,
|
|
709
|
+
body
|
|
710
|
+
};
|
|
711
|
+
} catch {
|
|
712
|
+
return {
|
|
713
|
+
status: 502,
|
|
714
|
+
headers: { "Content-Type": "text/plain" },
|
|
715
|
+
body: `Bad Gateway: failed to connect to ${target}`
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
/** Serve static files from a directory, with index.html fallback for directories */
|
|
721
|
+
function createStaticHandler(opts) {
|
|
722
|
+
const dir = opts.dir;
|
|
723
|
+
return async (req) => {
|
|
724
|
+
let filePath = new URL(req.url, "http://localhost").pathname;
|
|
725
|
+
if (filePath.includes("..")) return {
|
|
726
|
+
status: 403,
|
|
727
|
+
headers: { "Content-Type": "text/plain" },
|
|
728
|
+
body: "Forbidden"
|
|
729
|
+
};
|
|
730
|
+
if (filePath.endsWith("/")) filePath += "index.html";
|
|
731
|
+
const fullPath = join(dir, filePath);
|
|
732
|
+
try {
|
|
733
|
+
const content = await readFile(fullPath);
|
|
734
|
+
const contentType = MIME_TYPES[extname(fullPath)] || "application/octet-stream";
|
|
735
|
+
return {
|
|
736
|
+
status: 200,
|
|
737
|
+
headers: { "Content-Type": contentType },
|
|
738
|
+
body: content.toString()
|
|
739
|
+
};
|
|
740
|
+
} catch {
|
|
741
|
+
return {
|
|
742
|
+
status: 404,
|
|
743
|
+
headers: { "Content-Type": "text/plain" },
|
|
744
|
+
body: "Not found"
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
//#endregion
|
|
751
|
+
export { SeamError, createDevProxy, createHttpHandler, createRouter, createStaticHandler, definePage, drainStream, fromCallback, loadBuildOutput, serialize, sseCompleteEvent, sseDataEvent, sseErrorEvent, t, toWebResponse };
|
|
752
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["primitives"],"sources":["../src/types/schema.ts","../src/types/primitives.ts","../src/types/composites.ts","../src/types/index.ts","../src/manifest/index.ts","../src/errors.ts","../src/validation/index.ts","../src/router/handler.ts","../src/page/handler.ts","../src/page/route-matcher.ts","../src/router/index.ts","../src/page/index.ts","../src/mime.ts","../src/http.ts","../src/page/build-loader.ts","../src/subscription.ts","../src/proxy.ts"],"sourcesContent":["/* packages/server/core/typescript/src/types/schema.ts */\n\nimport type { Schema } from \"jtd\";\n\nexport type JTDSchema = Schema;\n\nexport interface SchemaNode<TOutput = unknown> {\n readonly _schema: JTDSchema;\n /** Phantom type marker — never exists at runtime */\n readonly _output: TOutput;\n}\n\nexport interface OptionalSchemaNode<TOutput = unknown> extends SchemaNode<TOutput> {\n readonly _optional: true;\n}\n\nexport type Infer<T extends SchemaNode> = T[\"_output\"];\n\nexport function createSchemaNode<T>(schema: JTDSchema): SchemaNode<T> {\n return { _schema: schema } as SchemaNode<T>;\n}\n\nexport function createOptionalSchemaNode<T>(schema: JTDSchema): OptionalSchemaNode<T> {\n return { _schema: schema, _optional: true } as OptionalSchemaNode<T>;\n}\n","/* packages/server/core/typescript/src/types/primitives.ts */\n\nimport type { SchemaNode } from \"./schema.js\";\nimport { createSchemaNode } from \"./schema.js\";\n\nexport function string(): SchemaNode<string> {\n return createSchemaNode<string>({ type: \"string\" });\n}\n\nexport function boolean(): SchemaNode<boolean> {\n return createSchemaNode<boolean>({ type: \"boolean\" });\n}\n\nexport function int8(): SchemaNode<number> {\n return createSchemaNode<number>({ type: \"int8\" });\n}\n\nexport function int16(): SchemaNode<number> {\n return createSchemaNode<number>({ type: \"int16\" });\n}\n\nexport function int32(): SchemaNode<number> {\n return createSchemaNode<number>({ type: \"int32\" });\n}\n\nexport function uint8(): SchemaNode<number> {\n return createSchemaNode<number>({ type: \"uint8\" });\n}\n\nexport function uint16(): SchemaNode<number> {\n return createSchemaNode<number>({ type: \"uint16\" });\n}\n\nexport function uint32(): SchemaNode<number> {\n return createSchemaNode<number>({ type: \"uint32\" });\n}\n\nexport function float32(): SchemaNode<number> {\n return createSchemaNode<number>({ type: \"float32\" });\n}\n\nexport function float64(): SchemaNode<number> {\n return createSchemaNode<number>({ type: \"float64\" });\n}\n\nexport function timestamp(): SchemaNode<string> {\n return createSchemaNode<string>({ type: \"timestamp\" });\n}\n","/* packages/server/core/typescript/src/types/composites.ts */\n\nimport type { SchemaNode, OptionalSchemaNode, Infer, JTDSchema } from \"./schema.js\";\nimport { createSchemaNode, createOptionalSchemaNode } from \"./schema.js\";\n\n// -- Type-level utilities --\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\ntype RequiredKeys<T extends Record<string, SchemaNode>> = {\n [K in keyof T]: T[K] extends OptionalSchemaNode ? never : K;\n}[keyof T];\n\ntype OptionalKeys<T extends Record<string, SchemaNode>> = {\n [K in keyof T]: T[K] extends OptionalSchemaNode ? K : never;\n}[keyof T];\n\ntype InferObject<T extends Record<string, SchemaNode>> = Simplify<\n { [K in RequiredKeys<T>]: Infer<T[K]> } & { [K in OptionalKeys<T>]?: Infer<T[K]> }\n>;\n\n// -- Builders --\n\nexport function object<T extends Record<string, SchemaNode>>(\n fields: T,\n): SchemaNode<InferObject<T>> {\n const properties: Record<string, JTDSchema> = {};\n const optionalProperties: Record<string, JTDSchema> = {};\n\n for (const [key, node] of Object.entries(fields)) {\n if (\"_optional\" in node && node._optional === true) {\n optionalProperties[key] = node._schema;\n } else {\n properties[key] = node._schema;\n }\n }\n\n const schema: Record<string, unknown> = {};\n if (Object.keys(properties).length > 0 || Object.keys(optionalProperties).length === 0) {\n schema.properties = properties;\n }\n if (Object.keys(optionalProperties).length > 0) {\n schema.optionalProperties = optionalProperties;\n }\n\n return createSchemaNode<InferObject<T>>(schema as JTDSchema);\n}\n\nexport function optional<T>(node: SchemaNode<T>): OptionalSchemaNode<T> {\n return createOptionalSchemaNode<T>(node._schema);\n}\n\nexport function array<T>(node: SchemaNode<T>): SchemaNode<T[]> {\n return createSchemaNode<T[]>({ elements: node._schema });\n}\n\nexport function nullable<T>(node: SchemaNode<T>): SchemaNode<T | null> {\n return createSchemaNode<T | null>({ ...node._schema, nullable: true } as JTDSchema);\n}\n\nexport function enumType<const T extends readonly string[]>(values: T): SchemaNode<T[number]> {\n return createSchemaNode<T[number]>({ enum: [...values] } as JTDSchema);\n}\n\nexport function values<T>(node: SchemaNode<T>): SchemaNode<Record<string, T>> {\n return createSchemaNode<Record<string, T>>({ values: node._schema });\n}\n\ntype DiscriminatorUnion<TTag extends string, TMapping extends Record<string, SchemaNode>> = {\n [K in keyof TMapping & string]: Simplify<{ [P in TTag]: K } & Infer<TMapping[K]>>;\n}[keyof TMapping & string];\n\nexport function discriminator<\n TTag extends string,\n TMapping extends Record<string, SchemaNode<Record<string, unknown>>>,\n>(tag: TTag, mapping: TMapping): SchemaNode<DiscriminatorUnion<TTag, TMapping>> {\n const jtdMapping: Record<string, JTDSchema> = {};\n for (const [key, node] of Object.entries(mapping)) {\n jtdMapping[key] = node._schema;\n }\n return createSchemaNode<DiscriminatorUnion<TTag, TMapping>>({\n discriminator: tag,\n mapping: jtdMapping,\n } as JTDSchema);\n}\n","/* packages/server/core/typescript/src/types/index.ts */\n\nimport * as primitives from \"./primitives.js\";\nimport {\n object,\n optional,\n array,\n nullable,\n enumType,\n values,\n discriminator,\n} from \"./composites.js\";\n\nexport const t = {\n ...primitives,\n object,\n optional,\n array,\n nullable,\n enum: enumType,\n values,\n discriminator,\n} as const;\n","/* packages/server/core/typescript/src/manifest/index.ts */\n\nimport type { Schema } from \"jtd\";\nimport type { SchemaNode } from \"../types/schema.js\";\n\nexport type ProcedureType = \"query\" | \"subscription\";\n\nexport interface ProcedureEntry {\n type: ProcedureType;\n input: Schema;\n output: Schema;\n}\n\nexport interface ProcedureManifest {\n version: string;\n procedures: Record<string, ProcedureEntry>;\n}\n\nexport function buildManifest(\n definitions: Record<string, { input: SchemaNode; output: SchemaNode; type?: string }>,\n): ProcedureManifest {\n const mapped: ProcedureManifest[\"procedures\"] = {};\n\n for (const [name, def] of Object.entries(definitions)) {\n mapped[name] = {\n type: def.type === \"subscription\" ? \"subscription\" : \"query\",\n input: def.input._schema,\n output: def.output._schema,\n };\n }\n\n return { version: \"0.1.0\", procedures: mapped };\n}\n","/* packages/server/core/typescript/src/errors.ts */\n\nexport type ErrorCode =\n | \"VALIDATION_ERROR\"\n | \"NOT_FOUND\"\n | \"UNAUTHORIZED\"\n | \"FORBIDDEN\"\n | \"RATE_LIMITED\"\n | \"INTERNAL_ERROR\"\n | (string & {});\n\nexport const DEFAULT_STATUS: Record<string, number> = {\n VALIDATION_ERROR: 400,\n UNAUTHORIZED: 401,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n RATE_LIMITED: 429,\n INTERNAL_ERROR: 500,\n};\n\nexport class SeamError extends Error {\n readonly code: string;\n readonly status: number;\n\n constructor(code: string, message: string, status?: number) {\n super(message);\n this.code = code;\n this.status = status ?? DEFAULT_STATUS[code] ?? 500;\n this.name = \"SeamError\";\n }\n\n toJSON() {\n return {\n error: {\n code: this.code,\n message: this.message,\n },\n };\n }\n}\n","/* packages/server/core/typescript/src/validation/index.ts */\n\nimport { validate } from \"jtd\";\nimport type { Schema, ValidationError as JTDValidationError } from \"jtd\";\n\nexport interface ValidationResult {\n valid: boolean;\n errors: JTDValidationError[];\n}\n\nexport function validateInput(schema: Schema, data: unknown): ValidationResult {\n const errors = validate(schema, data, { maxDepth: 32, maxErrors: 10 });\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n\nexport function formatValidationErrors(errors: JTDValidationError[]): string {\n return errors\n .map((e) => {\n const path = e.instancePath.length > 0 ? e.instancePath.join(\"/\") : \"(root)\";\n const schema = e.schemaPath.join(\"/\");\n return `${path} (schema: ${schema})`;\n })\n .join(\"; \");\n}\n","/* packages/server/core/typescript/src/router/handler.ts */\n\nimport { SeamError } from \"../errors.js\";\nimport type { HandleResult, InternalProcedure, InternalSubscription } from \"../procedure.js\";\nimport { validateInput, formatValidationErrors } from \"../validation/index.js\";\n\nexport type { HandleResult, InternalProcedure } from \"../procedure.js\";\n\nexport async function handleRequest(\n procedures: Map<string, InternalProcedure>,\n procedureName: string,\n rawBody: unknown,\n validateOutput?: boolean,\n): Promise<HandleResult> {\n const procedure = procedures.get(procedureName);\n if (!procedure) {\n return {\n status: 404,\n body: new SeamError(\"NOT_FOUND\", `Procedure '${procedureName}' not found`).toJSON(),\n };\n }\n\n const validation = validateInput(procedure.inputSchema, rawBody);\n if (!validation.valid) {\n const details = formatValidationErrors(validation.errors);\n return {\n status: 400,\n body: new SeamError(\"VALIDATION_ERROR\", `Input validation failed: ${details}`).toJSON(),\n };\n }\n\n try {\n const result = await procedure.handler({ input: rawBody });\n\n if (validateOutput) {\n const outValidation = validateInput(procedure.outputSchema, result);\n if (!outValidation.valid) {\n const details = formatValidationErrors(outValidation.errors);\n return {\n status: 500,\n body: new SeamError(\"INTERNAL_ERROR\", `Output validation failed: ${details}`).toJSON(),\n };\n }\n }\n\n return { status: 200, body: result };\n } catch (error) {\n if (error instanceof SeamError) {\n return { status: error.status, body: error.toJSON() };\n }\n const message = error instanceof Error ? error.message : \"Unknown error\";\n return {\n status: 500,\n body: new SeamError(\"INTERNAL_ERROR\", message).toJSON(),\n };\n }\n}\n\nexport async function* handleSubscription(\n subscriptions: Map<string, InternalSubscription>,\n name: string,\n rawInput: unknown,\n validateOutput?: boolean,\n): AsyncIterable<unknown> {\n const sub = subscriptions.get(name);\n if (!sub) {\n throw new SeamError(\"NOT_FOUND\", `Subscription '${name}' not found`);\n }\n\n const validation = validateInput(sub.inputSchema, rawInput);\n if (!validation.valid) {\n const details = formatValidationErrors(validation.errors);\n throw new SeamError(\"VALIDATION_ERROR\", `Input validation failed: ${details}`);\n }\n\n for await (const value of sub.handler({ input: rawInput })) {\n if (validateOutput) {\n const outValidation = validateInput(sub.outputSchema, value);\n if (!outValidation.valid) {\n const details = formatValidationErrors(outValidation.errors);\n throw new SeamError(\"INTERNAL_ERROR\", `Output validation failed: ${details}`);\n }\n }\n yield value;\n }\n}\n","/* packages/server/core/typescript/src/page/handler.ts */\n\nimport { inject, escapeHtml } from \"@canmi/seam-injector\";\nimport { SeamError } from \"../errors.js\";\nimport type { InternalProcedure } from \"../procedure.js\";\nimport type { PageDef, LoaderFn } from \"./index.js\";\n\nexport interface PageTiming {\n /** Procedure execution time in milliseconds */\n dataFetch: number;\n /** Template injection time in milliseconds */\n inject: number;\n}\n\nexport interface HandlePageResult {\n status: number;\n html: string;\n timing?: PageTiming;\n}\n\n/** Flatten keyed loader results: spread object values into a flat map for slot resolution */\nfunction flattenForSlots(keyed: Record<string, unknown>): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...keyed };\n for (const value of Object.values(keyed)) {\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n Object.assign(merged, value as Record<string, unknown>);\n }\n }\n return merged;\n}\n\n/** Execute loaders, returning keyed results */\nasync function executeLoaders(\n loaders: Record<string, LoaderFn>,\n params: Record<string, string>,\n procedures: Map<string, InternalProcedure>,\n): Promise<Record<string, unknown>> {\n const entries = Object.entries(loaders);\n const results = await Promise.all(\n entries.map(async ([key, loader]) => {\n const { procedure, input } = loader(params);\n const proc = procedures.get(procedure);\n if (!proc) throw new SeamError(\"INTERNAL_ERROR\", `Procedure '${procedure}' not found`);\n // Skip JTD validation -- loader input is trusted server-side code\n const result = await proc.handler({ input });\n return [key, result] as const;\n }),\n );\n return Object.fromEntries(results);\n}\n\n/** Split-inject a layout template around its outlet marker */\nfunction injectLayout(template: string, data: Record<string, unknown>, inner: string): string {\n const outletMarker = \"<!--seam:outlet-->\";\n const outletIdx = template.indexOf(outletMarker);\n if (outletIdx === -1) {\n return inject(template, data, { skipDataScript: true });\n }\n const before = template.slice(0, outletIdx);\n const after = template.slice(outletIdx + outletMarker.length);\n const injectedBefore = inject(before, data, { skipDataScript: true });\n const injectedAfter = inject(after, data, { skipDataScript: true });\n return injectedBefore + inner + injectedAfter;\n}\n\nexport async function handlePageRequest(\n page: PageDef,\n params: Record<string, string>,\n procedures: Map<string, InternalProcedure>,\n): Promise<HandlePageResult> {\n try {\n const t0 = performance.now();\n const layoutChain = page.layoutChain ?? [];\n\n // Execute all loaders (layout chain + page) in parallel\n const loaderResults = await Promise.all([\n ...layoutChain.map((layout) => executeLoaders(layout.loaders, params, procedures)),\n executeLoaders(page.loaders, params, procedures),\n ]);\n\n const t1 = performance.now();\n\n // Partition: first N results are layout, last is page\n const layoutResults = loaderResults.slice(0, layoutChain.length);\n const pageKeyed = loaderResults[loaderResults.length - 1];\n\n // Inject page template\n let innerContent = inject(page.template, flattenForSlots(pageKeyed), { skipDataScript: true });\n\n // Compose layouts from innermost to outermost\n const layoutKeyed: Record<string, Record<string, unknown>> = {};\n for (let i = layoutChain.length - 1; i >= 0; i--) {\n const layout = layoutChain[i];\n const data = layoutResults[i];\n layoutKeyed[layout.id] = data;\n innerContent = injectLayout(layout.template, flattenForSlots(data), innerContent);\n }\n\n // Build __SEAM_DATA__: page data at top level, layout data under _layouts\n const seamData: Record<string, unknown> = { ...pageKeyed };\n if (Object.keys(layoutKeyed).length > 0) {\n seamData._layouts = layoutKeyed;\n }\n\n const script = `<script id=\"__SEAM_DATA__\" type=\"application/json\">${JSON.stringify(seamData)}</script>`;\n const bodyClose = innerContent.lastIndexOf(\"</body>\");\n let html: string;\n if (bodyClose !== -1) {\n html = innerContent.slice(0, bodyClose) + script + innerContent.slice(bodyClose);\n } else {\n html = innerContent + script;\n }\n\n const t2 = performance.now();\n\n return {\n status: 200,\n html,\n timing: { dataFetch: t1 - t0, inject: t2 - t1 },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n return {\n status: 500,\n html: `<!DOCTYPE html><html><body><h1>500 Internal Server Error</h1><p>${escapeHtml(message)}</p></body></html>`,\n };\n }\n}\n","/* packages/server/core/typescript/src/page/route-matcher.ts */\n\ninterface CompiledRoute {\n segments: RouteSegment[];\n}\n\ntype RouteSegment = { kind: \"static\"; value: string } | { kind: \"param\"; name: string };\n\nfunction compileRoute(pattern: string): CompiledRoute {\n const segments: RouteSegment[] = pattern\n .split(\"/\")\n .filter(Boolean)\n .map((seg) =>\n seg.startsWith(\":\") ? { kind: \"param\", name: seg.slice(1) } : { kind: \"static\", value: seg },\n );\n return { segments };\n}\n\nfunction matchRoute(segments: RouteSegment[], pathParts: string[]): Record<string, string> | null {\n if (segments.length !== pathParts.length) return null;\n const params: Record<string, string> = {};\n for (let i = 0; i < segments.length; i++) {\n const seg = segments[i];\n if (seg.kind === \"static\") {\n if (seg.value !== pathParts[i]) return null;\n } else {\n params[seg.name] = pathParts[i];\n }\n }\n return params;\n}\n\nexport class RouteMatcher<T> {\n private routes: { compiled: CompiledRoute; value: T }[] = [];\n\n add(pattern: string, value: T): void {\n this.routes.push({ compiled: compileRoute(pattern), value });\n }\n\n match(path: string): { value: T; params: Record<string, string> } | null {\n const parts = path.split(\"/\").filter(Boolean);\n for (const route of this.routes) {\n const params = matchRoute(route.compiled.segments, parts);\n if (params) return { value: route.value, params };\n }\n return null;\n }\n}\n","/* packages/server/core/typescript/src/router/index.ts */\n\nimport type { SchemaNode } from \"../types/schema.js\";\nimport type { ProcedureManifest } from \"../manifest/index.js\";\nimport type { HandleResult, InternalProcedure } from \"./handler.js\";\nimport type { InternalSubscription } from \"../procedure.js\";\nimport type { HandlePageResult } from \"../page/handler.js\";\nimport type { PageDef } from \"../page/index.js\";\nimport { buildManifest } from \"../manifest/index.js\";\nimport { handleRequest, handleSubscription } from \"./handler.js\";\nimport { handlePageRequest } from \"../page/handler.js\";\nimport { RouteMatcher } from \"../page/route-matcher.js\";\n\nexport interface ProcedureDef<TIn = unknown, TOut = unknown> {\n input: SchemaNode<TIn>;\n output: SchemaNode<TOut>;\n handler: (params: { input: TIn }) => TOut | Promise<TOut>;\n}\n\nexport interface SubscriptionDef<TIn = unknown, TOut = unknown> {\n type: \"subscription\";\n input: SchemaNode<TIn>;\n output: SchemaNode<TOut>;\n handler: (params: { input: TIn }) => AsyncIterable<TOut>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type DefinitionMap = Record<string, ProcedureDef<any, any> | SubscriptionDef<any, any>>;\n\nfunction isSubscriptionDef(def: ProcedureDef | SubscriptionDef): def is SubscriptionDef {\n return \"type\" in def && def.type === \"subscription\";\n}\n\nexport interface RouterOptions {\n pages?: Record<string, PageDef>;\n validateOutput?: boolean;\n}\n\nexport interface Router<T extends DefinitionMap> {\n manifest(): ProcedureManifest;\n handle(procedureName: string, body: unknown): Promise<HandleResult>;\n handleSubscription(name: string, input: unknown): AsyncIterable<unknown>;\n handlePage(path: string): Promise<HandlePageResult | null>;\n readonly hasPages: boolean;\n /** Exposed for adapter access to the definitions */\n readonly procedures: T;\n}\n\nexport function createRouter<T extends DefinitionMap>(\n procedures: T,\n opts?: RouterOptions,\n): Router<T> {\n const procedureMap = new Map<string, InternalProcedure>();\n const subscriptionMap = new Map<string, InternalSubscription>();\n\n for (const [name, def] of Object.entries(procedures)) {\n if (isSubscriptionDef(def)) {\n subscriptionMap.set(name, {\n inputSchema: def.input._schema,\n outputSchema: def.output._schema,\n handler: def.handler as InternalSubscription[\"handler\"],\n });\n } else {\n procedureMap.set(name, {\n inputSchema: def.input._schema,\n outputSchema: def.output._schema,\n handler: def.handler as InternalProcedure[\"handler\"],\n });\n }\n }\n\n const shouldValidateOutput =\n opts?.validateOutput ??\n (typeof process !== \"undefined\" && process.env.NODE_ENV !== \"production\");\n\n const pageMatcher = new RouteMatcher<PageDef>();\n const pages = opts?.pages;\n if (pages) {\n for (const [pattern, page] of Object.entries(pages)) {\n pageMatcher.add(pattern, page);\n }\n }\n\n return {\n procedures,\n hasPages: !!pages && Object.keys(pages).length > 0,\n manifest() {\n return buildManifest(procedures);\n },\n handle(procedureName, body) {\n return handleRequest(procedureMap, procedureName, body, shouldValidateOutput);\n },\n handleSubscription(name, input) {\n return handleSubscription(subscriptionMap, name, input, shouldValidateOutput);\n },\n async handlePage(path) {\n const match = pageMatcher.match(path);\n if (!match) return null;\n return handlePageRequest(match.value, match.params, procedureMap);\n },\n };\n}\n","/* packages/server/core/typescript/src/page/index.ts */\n\nexport interface LoaderResult {\n procedure: string;\n input: unknown;\n}\n\nexport type LoaderFn = (params: Record<string, string>) => LoaderResult;\n\nexport interface LayoutDef {\n id: string;\n template: string;\n loaders: Record<string, LoaderFn>;\n}\n\nexport interface PageDef {\n template: string;\n loaders: Record<string, LoaderFn>;\n layoutChain?: LayoutDef[];\n}\n\nexport function definePage(config: PageDef): PageDef {\n return { ...config, layoutChain: config.layoutChain ?? [] };\n}\n","/* packages/server/core/typescript/src/mime.ts */\n\nexport const MIME_TYPES: Record<string, string> = {\n \".js\": \"application/javascript\",\n \".mjs\": \"application/javascript\",\n \".css\": \"text/css\",\n \".html\": \"text/html\",\n \".json\": \"application/json\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".ttf\": \"font/ttf\",\n \".ico\": \"image/x-icon\",\n \".map\": \"application/json\",\n \".ts\": \"application/javascript\",\n \".tsx\": \"application/javascript\",\n};\n","/* packages/server/core/typescript/src/http.ts */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join, extname } from \"node:path\";\nimport type { Router, DefinitionMap } from \"./router/index.js\";\nimport { SeamError } from \"./errors.js\";\nimport { MIME_TYPES } from \"./mime.js\";\n\nexport interface HttpRequest {\n method: string;\n url: string;\n body: () => Promise<unknown>;\n}\n\nexport interface HttpBodyResponse {\n status: number;\n headers: Record<string, string>;\n body: unknown;\n}\n\nexport interface HttpStreamResponse {\n status: number;\n headers: Record<string, string>;\n stream: AsyncIterable<string>;\n}\n\nexport type HttpResponse = HttpBodyResponse | HttpStreamResponse;\n\nexport type HttpHandler = (req: HttpRequest) => Promise<HttpResponse>;\n\nexport interface HttpHandlerOptions {\n staticDir?: string;\n fallback?: HttpHandler;\n}\n\nconst RPC_PREFIX = \"/_seam/rpc/\";\nconst PAGE_PREFIX = \"/_seam/page/\";\nconst STATIC_PREFIX = \"/_seam/static/\";\nconst SUBSCRIBE_PREFIX = \"/_seam/subscribe/\";\nconst MANIFEST_PATH = \"/_seam/manifest.json\";\n\nconst JSON_HEADER = { \"Content-Type\": \"application/json\" };\nconst HTML_HEADER = { \"Content-Type\": \"text/html; charset=utf-8\" };\nconst SSE_HEADER = {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n};\nconst IMMUTABLE_CACHE = \"public, max-age=31536000, immutable\";\n\nfunction jsonResponse(status: number, body: unknown): HttpBodyResponse {\n return { status, headers: JSON_HEADER, body };\n}\n\nfunction errorResponse(status: number, code: string, message: string): HttpBodyResponse {\n return jsonResponse(status, new SeamError(code, message).toJSON());\n}\n\nasync function handleStaticAsset(assetPath: string, staticDir: string): Promise<HttpBodyResponse> {\n if (assetPath.includes(\"..\")) {\n return errorResponse(403, \"VALIDATION_ERROR\", \"Forbidden\");\n }\n\n const filePath = join(staticDir, assetPath);\n try {\n const content = await readFile(filePath, \"utf-8\");\n const ext = extname(filePath);\n const contentType = MIME_TYPES[ext] || \"application/octet-stream\";\n return {\n status: 200,\n headers: {\n \"Content-Type\": contentType,\n \"Cache-Control\": IMMUTABLE_CACHE,\n },\n body: content,\n };\n } catch {\n return errorResponse(404, \"NOT_FOUND\", \"Asset not found\");\n }\n}\n\n/** Format a single SSE data event */\nexport function sseDataEvent(data: unknown): string {\n return `event: data\\ndata: ${JSON.stringify(data)}\\n\\n`;\n}\n\n/** Format an SSE error event */\nexport function sseErrorEvent(code: string, message: string): string {\n return `event: error\\ndata: ${JSON.stringify({ code, message })}\\n\\n`;\n}\n\n/** Format an SSE complete event */\nexport function sseCompleteEvent(): string {\n return \"event: complete\\ndata: {}\\n\\n\";\n}\n\nasync function* sseStream<T extends DefinitionMap>(\n router: Router<T>,\n name: string,\n input: unknown,\n): AsyncIterable<string> {\n try {\n for await (const value of router.handleSubscription(name, input)) {\n yield sseDataEvent(value);\n }\n yield sseCompleteEvent();\n } catch (error) {\n if (error instanceof SeamError) {\n yield sseErrorEvent(error.code, error.message);\n } else {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n yield sseErrorEvent(\"INTERNAL_ERROR\", message);\n }\n }\n}\n\nexport function createHttpHandler<T extends DefinitionMap>(\n router: Router<T>,\n opts?: HttpHandlerOptions,\n): HttpHandler {\n return async (req) => {\n const url = new URL(req.url, \"http://localhost\");\n const { pathname } = url;\n\n if (req.method === \"GET\" && pathname === MANIFEST_PATH) {\n return jsonResponse(200, router.manifest());\n }\n\n if (req.method === \"POST\" && pathname.startsWith(RPC_PREFIX)) {\n const name = pathname.slice(RPC_PREFIX.length);\n if (!name) {\n return errorResponse(404, \"NOT_FOUND\", \"Empty procedure name\");\n }\n\n let body: unknown;\n try {\n body = await req.body();\n } catch {\n return errorResponse(400, \"VALIDATION_ERROR\", \"Invalid JSON body\");\n }\n\n const result = await router.handle(name, body);\n return jsonResponse(result.status, result.body);\n }\n\n if (req.method === \"GET\" && pathname.startsWith(SUBSCRIBE_PREFIX)) {\n const name = pathname.slice(SUBSCRIBE_PREFIX.length);\n if (!name) {\n return errorResponse(404, \"NOT_FOUND\", \"Empty subscription name\");\n }\n\n const rawInput = url.searchParams.get(\"input\");\n let input: unknown;\n try {\n input = rawInput ? JSON.parse(rawInput) : {};\n } catch {\n return errorResponse(400, \"VALIDATION_ERROR\", \"Invalid input query parameter\");\n }\n\n return { status: 200, headers: SSE_HEADER, stream: sseStream(router, name, input) };\n }\n\n if (req.method === \"GET\" && pathname.startsWith(PAGE_PREFIX) && router.hasPages) {\n const pagePath = \"/\" + pathname.slice(PAGE_PREFIX.length);\n const result = await router.handlePage(pagePath);\n if (result) {\n return { status: result.status, headers: HTML_HEADER, body: result.html };\n }\n }\n\n if (req.method === \"GET\" && pathname.startsWith(STATIC_PREFIX) && opts?.staticDir) {\n const assetPath = pathname.slice(STATIC_PREFIX.length);\n return handleStaticAsset(assetPath, opts.staticDir);\n }\n\n if (opts?.fallback) return opts.fallback(req);\n return errorResponse(404, \"NOT_FOUND\", \"Not found\");\n };\n}\n\nexport function serialize(body: unknown): string {\n return typeof body === \"string\" ? body : JSON.stringify(body);\n}\n\n/** Consume an async stream chunk-by-chunk; return false from write to stop early. */\nexport async function drainStream(\n stream: AsyncIterable<string>,\n write: (chunk: string) => boolean | void,\n): Promise<void> {\n try {\n for await (const chunk of stream) {\n if (write(chunk) === false) break;\n }\n } catch {\n // Client disconnected\n }\n}\n\n/** Convert an HttpResponse to a Web API Response (for adapters using fetch-compatible runtimes) */\nexport function toWebResponse(result: HttpResponse): Response {\n if (\"stream\" in result) {\n const stream = result.stream;\n const encoder = new TextEncoder();\n const readable = new ReadableStream({\n async start(controller) {\n await drainStream(stream, (chunk) => {\n controller.enqueue(encoder.encode(chunk));\n });\n controller.close();\n },\n });\n return new Response(readable, { status: result.status, headers: result.headers });\n }\n return new Response(serialize(result.body), { status: result.status, headers: result.headers });\n}\n","/* packages/server/core/typescript/src/page/build-loader.ts */\n\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { PageDef, LayoutDef, LoaderFn, LoaderResult } from \"./index.js\";\n\ninterface RouteManifest {\n layouts?: Record<string, LayoutManifestEntry>;\n routes: Record<string, RouteManifestEntry>;\n}\n\ninterface LayoutManifestEntry {\n template: string;\n loaders?: Record<string, LoaderConfig>;\n parent?: string;\n}\n\ninterface RouteManifestEntry {\n template: string;\n layout?: string;\n loaders: Record<string, LoaderConfig>;\n}\n\ninterface LoaderConfig {\n procedure: string;\n params?: Record<string, ParamConfig>;\n}\n\ninterface ParamConfig {\n from: \"route\";\n type?: \"string\" | \"int\";\n}\n\nfunction buildLoaderFn(config: LoaderConfig): LoaderFn {\n return (params: Record<string, string>): LoaderResult => {\n const input: Record<string, unknown> = {};\n if (config.params) {\n for (const [key, mapping] of Object.entries(config.params)) {\n const raw = params[key];\n input[key] = mapping.type === \"int\" ? Number(raw) : raw;\n }\n }\n return { procedure: config.procedure, input };\n };\n}\n\nfunction buildLoaderFns(configs: Record<string, LoaderConfig>): Record<string, LoaderFn> {\n const fns: Record<string, LoaderFn> = {};\n for (const [key, config] of Object.entries(configs)) {\n fns[key] = buildLoaderFn(config);\n }\n return fns;\n}\n\n/** Resolve parent chain for a layout, returning outer-to-inner order */\nfunction resolveLayoutChain(\n layoutId: string,\n layoutEntries: Record<string, LayoutManifestEntry>,\n templates: Record<string, string>,\n): LayoutDef[] {\n const chain: LayoutDef[] = [];\n let currentId: string | undefined = layoutId;\n\n while (currentId) {\n const entry: LayoutManifestEntry | undefined = layoutEntries[currentId];\n if (!entry) break;\n chain.push({\n id: currentId,\n template: templates[currentId],\n loaders: buildLoaderFns(entry.loaders ?? {}),\n });\n currentId = entry.parent;\n }\n\n // Reverse: we walked inner→outer, but want outer→inner\n chain.reverse();\n return chain;\n}\n\nexport function loadBuildOutput(distDir: string): Record<string, PageDef> {\n const manifestPath = join(distDir, \"route-manifest.json\");\n const raw = readFileSync(manifestPath, \"utf-8\");\n const manifest = JSON.parse(raw) as RouteManifest;\n\n // Load layout templates\n const layoutTemplates: Record<string, string> = {};\n const layoutEntries = manifest.layouts ?? {};\n for (const [id, entry] of Object.entries(layoutEntries)) {\n layoutTemplates[id] = readFileSync(join(distDir, entry.template), \"utf-8\");\n }\n\n const pages: Record<string, PageDef> = {};\n for (const [path, entry] of Object.entries(manifest.routes)) {\n const templatePath = join(distDir, entry.template);\n const template = readFileSync(templatePath, \"utf-8\");\n\n const loaders = buildLoaderFns(entry.loaders);\n const layoutChain = entry.layout\n ? resolveLayoutChain(entry.layout, layoutEntries, layoutTemplates)\n : [];\n\n pages[path] = { template, loaders, layoutChain };\n }\n return pages;\n}\n","/* packages/server/core/typescript/src/subscription.ts */\n\n/**\n * Bridge callback-style event sources to an AsyncGenerator.\n *\n * Usage:\n * const stream = fromCallback<string>(({ emit, end, error }) => {\n * emitter.on(\"data\", emit);\n * emitter.on(\"end\", end);\n * emitter.on(\"error\", error);\n * return () => emitter.removeAllListeners();\n * });\n */\nexport interface CallbackSink<T> {\n emit: (value: T) => void;\n end: () => void;\n error: (err: Error) => void;\n}\n\ntype QueueItem<T> = { type: \"value\"; value: T } | { type: \"end\" } | { type: \"error\"; error: Error };\n\nexport function fromCallback<T>(\n setup: (sink: CallbackSink<T>) => (() => void) | void,\n): AsyncGenerator<T, void, undefined> {\n const queue: QueueItem<T>[] = [];\n let resolve: (() => void) | null = null;\n let done = false;\n function notify() {\n if (resolve) {\n const r = resolve;\n resolve = null;\n r();\n }\n }\n\n const sink: CallbackSink<T> = {\n emit(value) {\n if (done) return;\n queue.push({ type: \"value\", value });\n notify();\n },\n end() {\n if (done) return;\n done = true;\n queue.push({ type: \"end\" });\n notify();\n },\n error(err) {\n if (done) return;\n done = true;\n queue.push({ type: \"error\", error: err });\n notify();\n },\n };\n\n const cleanup = setup(sink);\n\n async function* generate(): AsyncGenerator<T, void, undefined> {\n try {\n while (true) {\n if (queue.length === 0) {\n await new Promise<void>((r) => {\n resolve = r;\n });\n }\n\n while (queue.length > 0) {\n const item = queue.shift()!;\n if (item.type === \"value\") {\n yield item.value;\n } else if (item.type === \"error\") {\n throw item.error;\n } else {\n return;\n }\n }\n }\n } finally {\n done = true;\n if (cleanup) cleanup();\n }\n }\n\n return generate();\n}\n","/* packages/server/core/typescript/src/proxy.ts */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join, extname } from \"node:path\";\nimport type { HttpHandler, HttpBodyResponse } from \"./http.js\";\nimport { MIME_TYPES } from \"./mime.js\";\n\nexport interface DevProxyOptions {\n /** Target URL to forward requests to (e.g. \"http://localhost:5173\") */\n target: string;\n}\n\nexport interface StaticHandlerOptions {\n /** Directory to serve static files from */\n dir: string;\n}\n\n/** Forward non-seam requests to a dev server (e.g. Vite) */\nexport function createDevProxy(opts: DevProxyOptions): HttpHandler {\n const target = opts.target.replace(/\\/$/, \"\");\n\n return async (req) => {\n const url = new URL(req.url, \"http://localhost\");\n const proxyUrl = `${target}${url.pathname}${url.search}`;\n\n try {\n const resp = await fetch(proxyUrl, {\n method: req.method,\n headers: { Accept: \"*/*\" },\n });\n\n const body = await resp.text();\n const headers: Record<string, string> = {};\n resp.headers.forEach((value, key) => {\n headers[key] = value;\n });\n\n return { status: resp.status, headers, body };\n } catch {\n return {\n status: 502,\n headers: { \"Content-Type\": \"text/plain\" },\n body: `Bad Gateway: failed to connect to ${target}`,\n };\n }\n };\n}\n\n/** Serve static files from a directory, with index.html fallback for directories */\nexport function createStaticHandler(opts: StaticHandlerOptions): HttpHandler {\n const dir = opts.dir;\n\n return async (req): Promise<HttpBodyResponse> => {\n const url = new URL(req.url, \"http://localhost\");\n let filePath = url.pathname;\n\n if (filePath.includes(\"..\")) {\n return {\n status: 403,\n headers: { \"Content-Type\": \"text/plain\" },\n body: \"Forbidden\",\n };\n }\n\n // Serve index.html for directory paths\n if (filePath.endsWith(\"/\")) {\n filePath += \"index.html\";\n }\n\n const fullPath = join(dir, filePath);\n try {\n const content = await readFile(fullPath);\n const ext = extname(fullPath);\n const contentType = MIME_TYPES[ext] || \"application/octet-stream\";\n return {\n status: 200,\n headers: { \"Content-Type\": contentType },\n body: content.toString(),\n };\n } catch {\n return {\n status: 404,\n headers: { \"Content-Type\": \"text/plain\" },\n body: \"Not found\",\n };\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAkBA,SAAgB,iBAAoB,QAAkC;AACpE,QAAO,EAAE,SAAS,QAAQ;;AAG5B,SAAgB,yBAA4B,QAA0C;AACpF,QAAO;EAAE,SAAS;EAAQ,WAAW;EAAM;;;;;;;;;;;;;;;;;;AClB7C,SAAgB,SAA6B;AAC3C,QAAO,iBAAyB,EAAE,MAAM,UAAU,CAAC;;AAGrD,SAAgB,UAA+B;AAC7C,QAAO,iBAA0B,EAAE,MAAM,WAAW,CAAC;;AAGvD,SAAgB,OAA2B;AACzC,QAAO,iBAAyB,EAAE,MAAM,QAAQ,CAAC;;AAGnD,SAAgB,QAA4B;AAC1C,QAAO,iBAAyB,EAAE,MAAM,SAAS,CAAC;;AAGpD,SAAgB,QAA4B;AAC1C,QAAO,iBAAyB,EAAE,MAAM,SAAS,CAAC;;AAGpD,SAAgB,QAA4B;AAC1C,QAAO,iBAAyB,EAAE,MAAM,SAAS,CAAC;;AAGpD,SAAgB,SAA6B;AAC3C,QAAO,iBAAyB,EAAE,MAAM,UAAU,CAAC;;AAGrD,SAAgB,SAA6B;AAC3C,QAAO,iBAAyB,EAAE,MAAM,UAAU,CAAC;;AAGrD,SAAgB,UAA8B;AAC5C,QAAO,iBAAyB,EAAE,MAAM,WAAW,CAAC;;AAGtD,SAAgB,UAA8B;AAC5C,QAAO,iBAAyB,EAAE,MAAM,WAAW,CAAC;;AAGtD,SAAgB,YAAgC;AAC9C,QAAO,iBAAyB,EAAE,MAAM,aAAa,CAAC;;;;;ACvBxD,SAAgB,OACd,QAC4B;CAC5B,MAAM,aAAwC,EAAE;CAChD,MAAM,qBAAgD,EAAE;AAExD,MAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,OAAO,CAC9C,KAAI,eAAe,QAAQ,KAAK,cAAc,KAC5C,oBAAmB,OAAO,KAAK;KAE/B,YAAW,OAAO,KAAK;CAI3B,MAAM,SAAkC,EAAE;AAC1C,KAAI,OAAO,KAAK,WAAW,CAAC,SAAS,KAAK,OAAO,KAAK,mBAAmB,CAAC,WAAW,EACnF,QAAO,aAAa;AAEtB,KAAI,OAAO,KAAK,mBAAmB,CAAC,SAAS,EAC3C,QAAO,qBAAqB;AAG9B,QAAO,iBAAiC,OAAoB;;AAG9D,SAAgB,SAAY,MAA4C;AACtE,QAAO,yBAA4B,KAAK,QAAQ;;AAGlD,SAAgB,MAAS,MAAsC;AAC7D,QAAO,iBAAsB,EAAE,UAAU,KAAK,SAAS,CAAC;;AAG1D,SAAgB,SAAY,MAA2C;AACrE,QAAO,iBAA2B;EAAE,GAAG,KAAK;EAAS,UAAU;EAAM,CAAc;;AAGrF,SAAgB,SAA4C,QAAkC;AAC5F,QAAO,iBAA4B,EAAE,MAAM,CAAC,GAAG,OAAO,EAAE,CAAc;;AAGxE,SAAgB,OAAU,MAAoD;AAC5E,QAAO,iBAAoC,EAAE,QAAQ,KAAK,SAAS,CAAC;;AAOtE,SAAgB,cAGd,KAAW,SAAmE;CAC9E,MAAM,aAAwC,EAAE;AAChD,MAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,QAAQ,CAC/C,YAAW,OAAO,KAAK;AAEzB,QAAO,iBAAqD;EAC1D,eAAe;EACf,SAAS;EACV,CAAc;;;;;ACtEjB,MAAa,IAAI;CACf,GAAGA;CACH;CACA;CACA;CACA;CACA,MAAM;CACN;CACA;CACD;;;;ACJD,SAAgB,cACd,aACmB;CACnB,MAAM,SAA0C,EAAE;AAElD,MAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,YAAY,CACnD,QAAO,QAAQ;EACb,MAAM,IAAI,SAAS,iBAAiB,iBAAiB;EACrD,OAAO,IAAI,MAAM;EACjB,QAAQ,IAAI,OAAO;EACpB;AAGH,QAAO;EAAE,SAAS;EAAS,YAAY;EAAQ;;;;;ACpBjD,MAAa,iBAAyC;CACpD,kBAAkB;CAClB,cAAc;CACd,WAAW;CACX,WAAW;CACX,cAAc;CACd,gBAAgB;CACjB;AAED,IAAa,YAAb,cAA+B,MAAM;CACnC,AAAS;CACT,AAAS;CAET,YAAY,MAAc,SAAiB,QAAiB;AAC1D,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,SAAS,UAAU,eAAe,SAAS;AAChD,OAAK,OAAO;;CAGd,SAAS;AACP,SAAO,EACL,OAAO;GACL,MAAM,KAAK;GACX,SAAS,KAAK;GACf,EACF;;;;;;AC3BL,SAAgB,cAAc,QAAgB,MAAiC;CAC7E,MAAM,SAAS,SAAS,QAAQ,MAAM;EAAE,UAAU;EAAI,WAAW;EAAI,CAAC;AACtE,QAAO;EACL,OAAO,OAAO,WAAW;EACzB;EACD;;AAGH,SAAgB,uBAAuB,QAAsC;AAC3E,QAAO,OACJ,KAAK,MAAM;AAGV,SAAO,GAFM,EAAE,aAAa,SAAS,IAAI,EAAE,aAAa,KAAK,IAAI,GAAG,SAErD,YADA,EAAE,WAAW,KAAK,IAAI,CACH;GAClC,CACD,KAAK,KAAK;;;;;ACjBf,eAAsB,cACpB,YACA,eACA,SACA,gBACuB;CACvB,MAAM,YAAY,WAAW,IAAI,cAAc;AAC/C,KAAI,CAAC,UACH,QAAO;EACL,QAAQ;EACR,MAAM,IAAI,UAAU,aAAa,cAAc,cAAc,aAAa,CAAC,QAAQ;EACpF;CAGH,MAAM,aAAa,cAAc,UAAU,aAAa,QAAQ;AAChE,KAAI,CAAC,WAAW,MAEd,QAAO;EACL,QAAQ;EACR,MAAM,IAAI,UAAU,oBAAoB,4BAH1B,uBAAuB,WAAW,OAAO,GAGuB,CAAC,QAAQ;EACxF;AAGH,KAAI;EACF,MAAM,SAAS,MAAM,UAAU,QAAQ,EAAE,OAAO,SAAS,CAAC;AAE1D,MAAI,gBAAgB;GAClB,MAAM,gBAAgB,cAAc,UAAU,cAAc,OAAO;AACnE,OAAI,CAAC,cAAc,MAEjB,QAAO;IACL,QAAQ;IACR,MAAM,IAAI,UAAU,kBAAkB,6BAHxB,uBAAuB,cAAc,OAAO,GAGmB,CAAC,QAAQ;IACvF;;AAIL,SAAO;GAAE,QAAQ;GAAK,MAAM;GAAQ;UAC7B,OAAO;AACd,MAAI,iBAAiB,UACnB,QAAO;GAAE,QAAQ,MAAM;GAAQ,MAAM,MAAM,QAAQ;GAAE;AAGvD,SAAO;GACL,QAAQ;GACR,MAAM,IAAI,UAAU,kBAHN,iBAAiB,QAAQ,MAAM,UAAU,gBAGT,CAAC,QAAQ;GACxD;;;AAIL,gBAAuB,mBACrB,eACA,MACA,UACA,gBACwB;CACxB,MAAM,MAAM,cAAc,IAAI,KAAK;AACnC,KAAI,CAAC,IACH,OAAM,IAAI,UAAU,aAAa,iBAAiB,KAAK,aAAa;CAGtE,MAAM,aAAa,cAAc,IAAI,aAAa,SAAS;AAC3D,KAAI,CAAC,WAAW,MAEd,OAAM,IAAI,UAAU,oBAAoB,4BADxB,uBAAuB,WAAW,OAAO,GACqB;AAGhF,YAAW,MAAM,SAAS,IAAI,QAAQ,EAAE,OAAO,UAAU,CAAC,EAAE;AAC1D,MAAI,gBAAgB;GAClB,MAAM,gBAAgB,cAAc,IAAI,cAAc,MAAM;AAC5D,OAAI,CAAC,cAAc,MAEjB,OAAM,IAAI,UAAU,kBAAkB,6BADtB,uBAAuB,cAAc,OAAO,GACiB;;AAGjF,QAAM;;;;;;;AC9DV,SAAS,gBAAgB,OAAyD;CAChF,MAAM,SAAkC,EAAE,GAAG,OAAO;AACpD,MAAK,MAAM,SAAS,OAAO,OAAO,MAAM,CACtC,KAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM,CAC7D,QAAO,OAAO,QAAQ,MAAiC;AAG3D,QAAO;;;AAIT,eAAe,eACb,SACA,QACA,YACkC;CAClC,MAAM,UAAU,OAAO,QAAQ,QAAQ;CACvC,MAAM,UAAU,MAAM,QAAQ,IAC5B,QAAQ,IAAI,OAAO,CAAC,KAAK,YAAY;EACnC,MAAM,EAAE,WAAW,UAAU,OAAO,OAAO;EAC3C,MAAM,OAAO,WAAW,IAAI,UAAU;AACtC,MAAI,CAAC,KAAM,OAAM,IAAI,UAAU,kBAAkB,cAAc,UAAU,aAAa;AAGtF,SAAO,CAAC,KADO,MAAM,KAAK,QAAQ,EAAE,OAAO,CAAC,CACxB;GACpB,CACH;AACD,QAAO,OAAO,YAAY,QAAQ;;;AAIpC,SAAS,aAAa,UAAkB,MAA+B,OAAuB;CAE5F,MAAM,YAAY,SAAS,QADN,qBAC2B;AAChD,KAAI,cAAc,GAChB,QAAO,OAAO,UAAU,MAAM,EAAE,gBAAgB,MAAM,CAAC;CAEzD,MAAM,SAAS,SAAS,MAAM,GAAG,UAAU;CAC3C,MAAM,QAAQ,SAAS,MAAM,YAAY,GAAoB;CAC7D,MAAM,iBAAiB,OAAO,QAAQ,MAAM,EAAE,gBAAgB,MAAM,CAAC;CACrE,MAAM,gBAAgB,OAAO,OAAO,MAAM,EAAE,gBAAgB,MAAM,CAAC;AACnE,QAAO,iBAAiB,QAAQ;;AAGlC,eAAsB,kBACpB,MACA,QACA,YAC2B;AAC3B,KAAI;EACF,MAAM,KAAK,YAAY,KAAK;EAC5B,MAAM,cAAc,KAAK,eAAe,EAAE;EAG1C,MAAM,gBAAgB,MAAM,QAAQ,IAAI,CACtC,GAAG,YAAY,KAAK,WAAW,eAAe,OAAO,SAAS,QAAQ,WAAW,CAAC,EAClF,eAAe,KAAK,SAAS,QAAQ,WAAW,CACjD,CAAC;EAEF,MAAM,KAAK,YAAY,KAAK;EAG5B,MAAM,gBAAgB,cAAc,MAAM,GAAG,YAAY,OAAO;EAChE,MAAM,YAAY,cAAc,cAAc,SAAS;EAGvD,IAAI,eAAe,OAAO,KAAK,UAAU,gBAAgB,UAAU,EAAE,EAAE,gBAAgB,MAAM,CAAC;EAG9F,MAAM,cAAuD,EAAE;AAC/D,OAAK,IAAI,IAAI,YAAY,SAAS,GAAG,KAAK,GAAG,KAAK;GAChD,MAAM,SAAS,YAAY;GAC3B,MAAM,OAAO,cAAc;AAC3B,eAAY,OAAO,MAAM;AACzB,kBAAe,aAAa,OAAO,UAAU,gBAAgB,KAAK,EAAE,aAAa;;EAInF,MAAM,WAAoC,EAAE,GAAG,WAAW;AAC1D,MAAI,OAAO,KAAK,YAAY,CAAC,SAAS,EACpC,UAAS,WAAW;EAGtB,MAAM,SAAS,sDAAsD,KAAK,UAAU,SAAS,CAAC;EAC9F,MAAM,YAAY,aAAa,YAAY,UAAU;EACrD,IAAI;AACJ,MAAI,cAAc,GAChB,QAAO,aAAa,MAAM,GAAG,UAAU,GAAG,SAAS,aAAa,MAAM,UAAU;MAEhF,QAAO,eAAe;EAGxB,MAAM,KAAK,YAAY,KAAK;AAE5B,SAAO;GACL,QAAQ;GACR;GACA,QAAQ;IAAE,WAAW,KAAK;IAAI,QAAQ,KAAK;IAAI;GAChD;UACM,OAAO;AAEd,SAAO;GACL,QAAQ;GACR,MAAM,mEAAmE,WAH3D,iBAAiB,QAAQ,MAAM,UAAU,gBAGqC,CAAC;GAC9F;;;;;;ACrHL,SAAS,aAAa,SAAgC;AAOpD,QAAO,EAAE,UANwB,QAC9B,MAAM,IAAI,CACV,OAAO,QAAQ,CACf,KAAK,QACJ,IAAI,WAAW,IAAI,GAAG;EAAE,MAAM;EAAS,MAAM,IAAI,MAAM,EAAE;EAAE,GAAG;EAAE,MAAM;EAAU,OAAO;EAAK,CAC7F,EACgB;;AAGrB,SAAS,WAAW,UAA0B,WAAoD;AAChG,KAAI,SAAS,WAAW,UAAU,OAAQ,QAAO;CACjD,MAAM,SAAiC,EAAE;AACzC,MAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,MAAM,SAAS;AACrB,MAAI,IAAI,SAAS,UACf;OAAI,IAAI,UAAU,UAAU,GAAI,QAAO;QAEvC,QAAO,IAAI,QAAQ,UAAU;;AAGjC,QAAO;;AAGT,IAAa,eAAb,MAA6B;CAC3B,AAAQ,SAAkD,EAAE;CAE5D,IAAI,SAAiB,OAAgB;AACnC,OAAK,OAAO,KAAK;GAAE,UAAU,aAAa,QAAQ;GAAE;GAAO,CAAC;;CAG9D,MAAM,MAAmE;EACvE,MAAM,QAAQ,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ;AAC7C,OAAK,MAAM,SAAS,KAAK,QAAQ;GAC/B,MAAM,SAAS,WAAW,MAAM,SAAS,UAAU,MAAM;AACzD,OAAI,OAAQ,QAAO;IAAE,OAAO,MAAM;IAAO;IAAQ;;AAEnD,SAAO;;;;;;AChBX,SAAS,kBAAkB,KAA6D;AACtF,QAAO,UAAU,OAAO,IAAI,SAAS;;AAkBvC,SAAgB,aACd,YACA,MACW;CACX,MAAM,+BAAe,IAAI,KAAgC;CACzD,MAAM,kCAAkB,IAAI,KAAmC;AAE/D,MAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,WAAW,CAClD,KAAI,kBAAkB,IAAI,CACxB,iBAAgB,IAAI,MAAM;EACxB,aAAa,IAAI,MAAM;EACvB,cAAc,IAAI,OAAO;EACzB,SAAS,IAAI;EACd,CAAC;KAEF,cAAa,IAAI,MAAM;EACrB,aAAa,IAAI,MAAM;EACvB,cAAc,IAAI,OAAO;EACzB,SAAS,IAAI;EACd,CAAC;CAIN,MAAM,uBACJ,MAAM,mBACL,OAAO,YAAY,eAAe,QAAQ,IAAI,aAAa;CAE9D,MAAM,cAAc,IAAI,cAAuB;CAC/C,MAAM,QAAQ,MAAM;AACpB,KAAI,MACF,MAAK,MAAM,CAAC,SAAS,SAAS,OAAO,QAAQ,MAAM,CACjD,aAAY,IAAI,SAAS,KAAK;AAIlC,QAAO;EACL;EACA,UAAU,CAAC,CAAC,SAAS,OAAO,KAAK,MAAM,CAAC,SAAS;EACjD,WAAW;AACT,UAAO,cAAc,WAAW;;EAElC,OAAO,eAAe,MAAM;AAC1B,UAAO,cAAc,cAAc,eAAe,MAAM,qBAAqB;;EAE/E,mBAAmB,MAAM,OAAO;AAC9B,UAAO,mBAAmB,iBAAiB,MAAM,OAAO,qBAAqB;;EAE/E,MAAM,WAAW,MAAM;GACrB,MAAM,QAAQ,YAAY,MAAM,KAAK;AACrC,OAAI,CAAC,MAAO,QAAO;AACnB,UAAO,kBAAkB,MAAM,OAAO,MAAM,QAAQ,aAAa;;EAEpE;;;;;AC/EH,SAAgB,WAAW,QAA0B;AACnD,QAAO;EAAE,GAAG;EAAQ,aAAa,OAAO,eAAe,EAAE;EAAE;;;;;ACpB7D,MAAa,aAAqC;CAChD,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,SAAS;CACT,UAAU;CACV,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,OAAO;CACP,QAAQ;CACT;;;;ACeD,MAAM,aAAa;AACnB,MAAM,cAAc;AACpB,MAAM,gBAAgB;AACtB,MAAM,mBAAmB;AACzB,MAAM,gBAAgB;AAEtB,MAAM,cAAc,EAAE,gBAAgB,oBAAoB;AAC1D,MAAM,cAAc,EAAE,gBAAgB,4BAA4B;AAClE,MAAM,aAAa;CACjB,gBAAgB;CAChB,iBAAiB;CACjB,YAAY;CACb;AACD,MAAM,kBAAkB;AAExB,SAAS,aAAa,QAAgB,MAAiC;AACrE,QAAO;EAAE;EAAQ,SAAS;EAAa;EAAM;;AAG/C,SAAS,cAAc,QAAgB,MAAc,SAAmC;AACtF,QAAO,aAAa,QAAQ,IAAI,UAAU,MAAM,QAAQ,CAAC,QAAQ,CAAC;;AAGpE,eAAe,kBAAkB,WAAmB,WAA8C;AAChG,KAAI,UAAU,SAAS,KAAK,CAC1B,QAAO,cAAc,KAAK,oBAAoB,YAAY;CAG5D,MAAM,WAAW,KAAK,WAAW,UAAU;AAC3C,KAAI;EACF,MAAM,UAAU,MAAM,SAAS,UAAU,QAAQ;EAEjD,MAAM,cAAc,WADR,QAAQ,SAAS,KACU;AACvC,SAAO;GACL,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,iBAAiB;IAClB;GACD,MAAM;GACP;SACK;AACN,SAAO,cAAc,KAAK,aAAa,kBAAkB;;;;AAK7D,SAAgB,aAAa,MAAuB;AAClD,QAAO,sBAAsB,KAAK,UAAU,KAAK,CAAC;;;AAIpD,SAAgB,cAAc,MAAc,SAAyB;AACnE,QAAO,uBAAuB,KAAK,UAAU;EAAE;EAAM;EAAS,CAAC,CAAC;;;AAIlE,SAAgB,mBAA2B;AACzC,QAAO;;AAGT,gBAAgB,UACd,QACA,MACA,OACuB;AACvB,KAAI;AACF,aAAW,MAAM,SAAS,OAAO,mBAAmB,MAAM,MAAM,CAC9D,OAAM,aAAa,MAAM;AAE3B,QAAM,kBAAkB;UACjB,OAAO;AACd,MAAI,iBAAiB,UACnB,OAAM,cAAc,MAAM,MAAM,MAAM,QAAQ;MAG9C,OAAM,cAAc,kBADJ,iBAAiB,QAAQ,MAAM,UAAU,gBACX;;;AAKpD,SAAgB,kBACd,QACA,MACa;AACb,QAAO,OAAO,QAAQ;EACpB,MAAM,MAAM,IAAI,IAAI,IAAI,KAAK,mBAAmB;EAChD,MAAM,EAAE,aAAa;AAErB,MAAI,IAAI,WAAW,SAAS,aAAa,cACvC,QAAO,aAAa,KAAK,OAAO,UAAU,CAAC;AAG7C,MAAI,IAAI,WAAW,UAAU,SAAS,WAAW,WAAW,EAAE;GAC5D,MAAM,OAAO,SAAS,MAAM,GAAkB;AAC9C,OAAI,CAAC,KACH,QAAO,cAAc,KAAK,aAAa,uBAAuB;GAGhE,IAAI;AACJ,OAAI;AACF,WAAO,MAAM,IAAI,MAAM;WACjB;AACN,WAAO,cAAc,KAAK,oBAAoB,oBAAoB;;GAGpE,MAAM,SAAS,MAAM,OAAO,OAAO,MAAM,KAAK;AAC9C,UAAO,aAAa,OAAO,QAAQ,OAAO,KAAK;;AAGjD,MAAI,IAAI,WAAW,SAAS,SAAS,WAAW,iBAAiB,EAAE;GACjE,MAAM,OAAO,SAAS,MAAM,GAAwB;AACpD,OAAI,CAAC,KACH,QAAO,cAAc,KAAK,aAAa,0BAA0B;GAGnE,MAAM,WAAW,IAAI,aAAa,IAAI,QAAQ;GAC9C,IAAI;AACJ,OAAI;AACF,YAAQ,WAAW,KAAK,MAAM,SAAS,GAAG,EAAE;WACtC;AACN,WAAO,cAAc,KAAK,oBAAoB,gCAAgC;;AAGhF,UAAO;IAAE,QAAQ;IAAK,SAAS;IAAY,QAAQ,UAAU,QAAQ,MAAM,MAAM;IAAE;;AAGrF,MAAI,IAAI,WAAW,SAAS,SAAS,WAAW,YAAY,IAAI,OAAO,UAAU;GAC/E,MAAM,WAAW,MAAM,SAAS,MAAM,GAAmB;GACzD,MAAM,SAAS,MAAM,OAAO,WAAW,SAAS;AAChD,OAAI,OACF,QAAO;IAAE,QAAQ,OAAO;IAAQ,SAAS;IAAa,MAAM,OAAO;IAAM;;AAI7E,MAAI,IAAI,WAAW,SAAS,SAAS,WAAW,cAAc,IAAI,MAAM,UAEtE,QAAO,kBADW,SAAS,MAAM,GAAqB,EAClB,KAAK,UAAU;AAGrD,MAAI,MAAM,SAAU,QAAO,KAAK,SAAS,IAAI;AAC7C,SAAO,cAAc,KAAK,aAAa,YAAY;;;AAIvD,SAAgB,UAAU,MAAuB;AAC/C,QAAO,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,KAAK;;;AAI/D,eAAsB,YACpB,QACA,OACe;AACf,KAAI;AACF,aAAW,MAAM,SAAS,OACxB,KAAI,MAAM,MAAM,KAAK,MAAO;SAExB;;;AAMV,SAAgB,cAAc,QAAgC;AAC5D,KAAI,YAAY,QAAQ;EACtB,MAAM,SAAS,OAAO;EACtB,MAAM,UAAU,IAAI,aAAa;EACjC,MAAM,WAAW,IAAI,eAAe,EAClC,MAAM,MAAM,YAAY;AACtB,SAAM,YAAY,SAAS,UAAU;AACnC,eAAW,QAAQ,QAAQ,OAAO,MAAM,CAAC;KACzC;AACF,cAAW,OAAO;KAErB,CAAC;AACF,SAAO,IAAI,SAAS,UAAU;GAAE,QAAQ,OAAO;GAAQ,SAAS,OAAO;GAAS,CAAC;;AAEnF,QAAO,IAAI,SAAS,UAAU,OAAO,KAAK,EAAE;EAAE,QAAQ,OAAO;EAAQ,SAAS,OAAO;EAAS,CAAC;;;;;ACpLjG,SAAS,cAAc,QAAgC;AACrD,SAAQ,WAAiD;EACvD,MAAM,QAAiC,EAAE;AACzC,MAAI,OAAO,OACT,MAAK,MAAM,CAAC,KAAK,YAAY,OAAO,QAAQ,OAAO,OAAO,EAAE;GAC1D,MAAM,MAAM,OAAO;AACnB,SAAM,OAAO,QAAQ,SAAS,QAAQ,OAAO,IAAI,GAAG;;AAGxD,SAAO;GAAE,WAAW,OAAO;GAAW;GAAO;;;AAIjD,SAAS,eAAe,SAAiE;CACvF,MAAM,MAAgC,EAAE;AACxC,MAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAAQ,QAAQ,CACjD,KAAI,OAAO,cAAc,OAAO;AAElC,QAAO;;;AAIT,SAAS,mBACP,UACA,eACA,WACa;CACb,MAAM,QAAqB,EAAE;CAC7B,IAAI,YAAgC;AAEpC,QAAO,WAAW;EAChB,MAAM,QAAyC,cAAc;AAC7D,MAAI,CAAC,MAAO;AACZ,QAAM,KAAK;GACT,IAAI;GACJ,UAAU,UAAU;GACpB,SAAS,eAAe,MAAM,WAAW,EAAE,CAAC;GAC7C,CAAC;AACF,cAAY,MAAM;;AAIpB,OAAM,SAAS;AACf,QAAO;;AAGT,SAAgB,gBAAgB,SAA0C;CAExE,MAAM,MAAM,aADS,KAAK,SAAS,sBAAsB,EAClB,QAAQ;CAC/C,MAAM,WAAW,KAAK,MAAM,IAAI;CAGhC,MAAM,kBAA0C,EAAE;CAClD,MAAM,gBAAgB,SAAS,WAAW,EAAE;AAC5C,MAAK,MAAM,CAAC,IAAI,UAAU,OAAO,QAAQ,cAAc,CACrD,iBAAgB,MAAM,aAAa,KAAK,SAAS,MAAM,SAAS,EAAE,QAAQ;CAG5E,MAAM,QAAiC,EAAE;AACzC,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,SAAS,OAAO,CASzD,OAAM,QAAQ;EAAE,UAPC,aADI,KAAK,SAAS,MAAM,SAAS,EACN,QAAQ;EAO1B,SALV,eAAe,MAAM,QAAQ;EAKV,aAJf,MAAM,SACtB,mBAAmB,MAAM,QAAQ,eAAe,gBAAgB,GAChE,EAAE;EAE0C;AAElD,QAAO;;;;;AClFT,SAAgB,aACd,OACoC;CACpC,MAAM,QAAwB,EAAE;CAChC,IAAI,UAA+B;CACnC,IAAI,OAAO;CACX,SAAS,SAAS;AAChB,MAAI,SAAS;GACX,MAAM,IAAI;AACV,aAAU;AACV,MAAG;;;CAwBP,MAAM,UAAU,MApBc;EAC5B,KAAK,OAAO;AACV,OAAI,KAAM;AACV,SAAM,KAAK;IAAE,MAAM;IAAS;IAAO,CAAC;AACpC,WAAQ;;EAEV,MAAM;AACJ,OAAI,KAAM;AACV,UAAO;AACP,SAAM,KAAK,EAAE,MAAM,OAAO,CAAC;AAC3B,WAAQ;;EAEV,MAAM,KAAK;AACT,OAAI,KAAM;AACV,UAAO;AACP,SAAM,KAAK;IAAE,MAAM;IAAS,OAAO;IAAK,CAAC;AACzC,WAAQ;;EAEX,CAE0B;CAE3B,gBAAgB,WAA+C;AAC7D,MAAI;AACF,UAAO,MAAM;AACX,QAAI,MAAM,WAAW,EACnB,OAAM,IAAI,SAAe,MAAM;AAC7B,eAAU;MACV;AAGJ,WAAO,MAAM,SAAS,GAAG;KACvB,MAAM,OAAO,MAAM,OAAO;AAC1B,SAAI,KAAK,SAAS,QAChB,OAAM,KAAK;cACF,KAAK,SAAS,QACvB,OAAM,KAAK;SAEX;;;YAIE;AACR,UAAO;AACP,OAAI,QAAS,UAAS;;;AAI1B,QAAO,UAAU;;;;;;ACjEnB,SAAgB,eAAe,MAAoC;CACjE,MAAM,SAAS,KAAK,OAAO,QAAQ,OAAO,GAAG;AAE7C,QAAO,OAAO,QAAQ;EACpB,MAAM,MAAM,IAAI,IAAI,IAAI,KAAK,mBAAmB;EAChD,MAAM,WAAW,GAAG,SAAS,IAAI,WAAW,IAAI;AAEhD,MAAI;GACF,MAAM,OAAO,MAAM,MAAM,UAAU;IACjC,QAAQ,IAAI;IACZ,SAAS,EAAE,QAAQ,OAAO;IAC3B,CAAC;GAEF,MAAM,OAAO,MAAM,KAAK,MAAM;GAC9B,MAAM,UAAkC,EAAE;AAC1C,QAAK,QAAQ,SAAS,OAAO,QAAQ;AACnC,YAAQ,OAAO;KACf;AAEF,UAAO;IAAE,QAAQ,KAAK;IAAQ;IAAS;IAAM;UACvC;AACN,UAAO;IACL,QAAQ;IACR,SAAS,EAAE,gBAAgB,cAAc;IACzC,MAAM,qCAAqC;IAC5C;;;;;AAMP,SAAgB,oBAAoB,MAAyC;CAC3E,MAAM,MAAM,KAAK;AAEjB,QAAO,OAAO,QAAmC;EAE/C,IAAI,WADQ,IAAI,IAAI,IAAI,KAAK,mBAAmB,CAC7B;AAEnB,MAAI,SAAS,SAAS,KAAK,CACzB,QAAO;GACL,QAAQ;GACR,SAAS,EAAE,gBAAgB,cAAc;GACzC,MAAM;GACP;AAIH,MAAI,SAAS,SAAS,IAAI,CACxB,aAAY;EAGd,MAAM,WAAW,KAAK,KAAK,SAAS;AACpC,MAAI;GACF,MAAM,UAAU,MAAM,SAAS,SAAS;GAExC,MAAM,cAAc,WADR,QAAQ,SAAS,KACU;AACvC,UAAO;IACL,QAAQ;IACR,SAAS,EAAE,gBAAgB,aAAa;IACxC,MAAM,QAAQ,UAAU;IACzB;UACK;AACN,UAAO;IACL,QAAQ;IACR,SAAS,EAAE,gBAAgB,cAAc;IACzC,MAAM;IACP"}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@canmi/seam-server",
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist"
|
|
6
|
+
],
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsdown",
|
|
16
|
+
"test": "vitest run"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@canmi/seam-injector": "workspace:*",
|
|
20
|
+
"jtd": "^0.1.1"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^22.0.0",
|
|
24
|
+
"tsdown": "^0.20.0",
|
|
25
|
+
"typescript": "^5.7.0",
|
|
26
|
+
"vitest": "^3.0.0"
|
|
27
|
+
}
|
|
28
|
+
}
|