@tsplc/server 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/dist/hmi.js +132 -0
- package/dist/hmi.js.map +11 -0
- package/dist/index.js +320 -0
- package/dist/index.js.map +11 -0
- package/dist/opcua.js +484 -0
- package/dist/opcua.js.map +12 -0
- package/dist/runtime.js +293 -0
- package/dist/runtime.js.map +12 -0
- package/dist/serve.js +711 -0
- package/dist/serve.js.map +14 -0
- package/package.json +48 -4
- package/src/contract.ts +366 -0
- package/src/hmi-page.ts +111 -0
- package/src/hmi.ts +127 -0
- package/src/index.ts +106 -0
- package/src/opcua.ts +211 -0
- package/src/serve.ts +170 -0
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
// packages/server/src/runtime.ts
|
|
2
|
+
import { z as z2 } from "zod";
|
|
3
|
+
|
|
4
|
+
// packages/server/src/index.ts
|
|
5
|
+
import { TRPCError } from "@trpc/server";
|
|
6
|
+
|
|
7
|
+
// packages/server/src/contract.ts
|
|
8
|
+
import { createHash } from "node:crypto";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
var identifier = /^(?!__proto__$)[A-Za-z_][A-Za-z0-9_]*$/;
|
|
11
|
+
var pascal = (value) => value.split(/[^A-Za-z0-9]+/).map((part) => part[0].toUpperCase() + part.slice(1)).join("");
|
|
12
|
+
var schemaValue = (value) => typeof value === "object" && value !== null && ("_zod" in value) && ("safeParse" in value);
|
|
13
|
+
var schema = (value, label) => {
|
|
14
|
+
if (!schemaValue(value))
|
|
15
|
+
throw new Error(`${label}: provide a bounded Zod input/output schema; TypeScript number/string inference has no IEC memory layout.`);
|
|
16
|
+
return value;
|
|
17
|
+
};
|
|
18
|
+
var jsonSchema = z.looseObject({
|
|
19
|
+
type: z.string().optional(),
|
|
20
|
+
minimum: z.number().optional(),
|
|
21
|
+
maximum: z.number().optional(),
|
|
22
|
+
maxLength: z.number().optional(),
|
|
23
|
+
minItems: z.number().optional(),
|
|
24
|
+
maxItems: z.number().optional(),
|
|
25
|
+
iec: z.string().optional(),
|
|
26
|
+
id: z.string().optional()
|
|
27
|
+
});
|
|
28
|
+
var integerTypes = {
|
|
29
|
+
SINT: [-128, 127],
|
|
30
|
+
USINT: [0, 255],
|
|
31
|
+
BYTE: [0, 255],
|
|
32
|
+
INT: [-32768, 32767],
|
|
33
|
+
UINT: [0, 65535],
|
|
34
|
+
WORD: [0, 65535],
|
|
35
|
+
DINT: [-2147483648, 2147483647],
|
|
36
|
+
UDINT: [0, 4294967295],
|
|
37
|
+
DWORD: [0, 4294967295]
|
|
38
|
+
};
|
|
39
|
+
var scalar = (value, label) => {
|
|
40
|
+
const json = jsonSchema.parse(z.toJSONSchema(value, { io: "input", unrepresentable: "throw" }));
|
|
41
|
+
const definition = value._zod.def;
|
|
42
|
+
if (["optional", "nullable", "union", "default", "prefault", "pipe", "catch"].includes(definition.type))
|
|
43
|
+
throw new Error(`${label}: optional, nullable, transformed and defaulted wire fields are unsupported.`);
|
|
44
|
+
if (definition.type === "enum") {
|
|
45
|
+
const entries = z.record(z.string(), z.union([z.number().int(), z.string()])).parse(Reflect.get(definition, "entries"));
|
|
46
|
+
if (Object.entries(entries).some(([name, value]) => identifier.test(name) && typeof value !== "number"))
|
|
47
|
+
throw new Error(`${label}: PLC enums require only numeric members.`);
|
|
48
|
+
const values = [];
|
|
49
|
+
for (const [name, value] of Object.entries(entries))
|
|
50
|
+
if (identifier.test(name) && typeof value === "number")
|
|
51
|
+
values.push([name, value]);
|
|
52
|
+
values.sort((left, right) => {
|
|
53
|
+
if (left[1] === 0)
|
|
54
|
+
return -1;
|
|
55
|
+
if (right[1] === 0)
|
|
56
|
+
return 1;
|
|
57
|
+
return left[1] - right[1];
|
|
58
|
+
});
|
|
59
|
+
const members = Object.fromEntries(values);
|
|
60
|
+
if (new Set(values.map(([, value]) => value)).size !== values.length)
|
|
61
|
+
throw new Error(`${label}: PLC enum values must be distinct.`);
|
|
62
|
+
if (!Object.keys(members).length || Object.values(members).some((value) => value < -2147483648 || value > 2147483647))
|
|
63
|
+
throw new Error(`${label}: PLC enums require named DINT values.`);
|
|
64
|
+
const name = json.id ?? pascal(label);
|
|
65
|
+
if (!identifier.test(name))
|
|
66
|
+
throw new Error(`${label}: schema id must be an IEC identifier.`);
|
|
67
|
+
return { kind: "enum", name, members };
|
|
68
|
+
}
|
|
69
|
+
if (json.type === "boolean")
|
|
70
|
+
return { kind: "boolean" };
|
|
71
|
+
if (json.type === "string" && json.maxLength !== undefined && Number.isInteger(json.maxLength) && json.maxLength > 0 && json.maxLength <= 2048)
|
|
72
|
+
return { kind: "string", capacity: json.maxLength };
|
|
73
|
+
if (json.type === "integer" && json.minimum !== undefined && json.maximum !== undefined && Number.isInteger(json.minimum) && Number.isInteger(json.maximum)) {
|
|
74
|
+
const iec = json.iec ?? (json.minimum < 0 ? json.minimum >= -32768 && json.maximum <= 32767 ? "INT" : "DINT" : json.maximum <= 65535 ? "UINT" : "UDINT");
|
|
75
|
+
const range = integerTypes[iec];
|
|
76
|
+
if (json.minimum > json.maximum)
|
|
77
|
+
throw new Error(`${label}: integer bounds are contradictory.`);
|
|
78
|
+
if (!range || json.minimum < range[0] || json.maximum > range[1])
|
|
79
|
+
throw new Error(`${label}: integer bounds do not fit ${iec}.`);
|
|
80
|
+
return { kind: "integer", iec, minimum: json.minimum, maximum: json.maximum };
|
|
81
|
+
}
|
|
82
|
+
throw new Error(`${label}: use BOOL, a numeric enum, an integer with min/max, or a string with max length. Optional, nullable, union, float and dynamic layouts are unsupported.`);
|
|
83
|
+
};
|
|
84
|
+
var record = (value, label, output) => {
|
|
85
|
+
const def = value._zod.def;
|
|
86
|
+
if (def.type !== "object")
|
|
87
|
+
throw new Error(`${label}: HTTP services require a closed object schema.`);
|
|
88
|
+
const shape = z.record(z.string(), z.custom(schemaValue)).parse(Reflect.get(def, "shape"));
|
|
89
|
+
const json = jsonSchema.parse(z.toJSONSchema(value, { io: "input", unrepresentable: "throw" }));
|
|
90
|
+
const name = json.id ?? pascal(label);
|
|
91
|
+
if (!identifier.test(name))
|
|
92
|
+
throw new Error(`${label}: schema id must be an IEC identifier.`);
|
|
93
|
+
const fields = {};
|
|
94
|
+
for (const key of Object.keys(shape).sort()) {
|
|
95
|
+
if (!identifier.test(key))
|
|
96
|
+
throw new Error(`${label}.${key}: use an IEC field identifier.`);
|
|
97
|
+
const item = shape[key];
|
|
98
|
+
if (item._zod.def.type === "array") {
|
|
99
|
+
if (output)
|
|
100
|
+
throw new Error(`${label}.${key}: the native HTTP decoder currently supports scalar response fields only.`);
|
|
101
|
+
const bounds = jsonSchema.parse(z.toJSONSchema(item, { io: "input" }));
|
|
102
|
+
if (!bounds.maxItems || bounds.maxItems !== bounds.minItems || bounds.maxItems > 256)
|
|
103
|
+
throw new Error(`${label}.${key}: arrays require one fixed length from 1 to 256.`);
|
|
104
|
+
fields[key] = {
|
|
105
|
+
kind: "array",
|
|
106
|
+
length: bounds.maxItems,
|
|
107
|
+
element: scalar(schema(Reflect.get(item._zod.def, "element"), `${label}.${key}`), `${label}.${key}`)
|
|
108
|
+
};
|
|
109
|
+
} else
|
|
110
|
+
fields[key] = scalar(item, `${label}.${key}`);
|
|
111
|
+
}
|
|
112
|
+
if (Object.keys(fields).length > 31 || output && !Object.keys(fields).length)
|
|
113
|
+
throw new Error(`${label}: response records require 1..31 fields; requests allow 0..31.`);
|
|
114
|
+
return { name, fields };
|
|
115
|
+
};
|
|
116
|
+
var serviceContracts = (router) => {
|
|
117
|
+
const contracts = [];
|
|
118
|
+
const procedures = z.record(z.string(), z.unknown()).parse(router._def.procedures);
|
|
119
|
+
for (const [path, procedure] of Object.entries(procedures)) {
|
|
120
|
+
if (typeof procedure !== "function" || !("_def" in procedure))
|
|
121
|
+
throw new Error("Unsupported tRPC router metadata.");
|
|
122
|
+
const reflected = z.custom((value) => typeof value === "function" && ("_def" in value)).parse(procedure);
|
|
123
|
+
const def = reflected._def;
|
|
124
|
+
if (typeof def.meta !== "object" || def.meta === null || !("plc" in def.meta) || def.meta.plc !== true)
|
|
125
|
+
continue;
|
|
126
|
+
if (path.split(".").some((part) => !identifier.test(part)))
|
|
127
|
+
throw new Error(`${path}: PLC procedure paths require identifier segments.`);
|
|
128
|
+
if (def.type !== "query" && def.type !== "mutation")
|
|
129
|
+
throw new Error(`${path}: subscriptions use a different transport; expose a query or mutation to the PLC.`);
|
|
130
|
+
if (def.inputs.length > 1)
|
|
131
|
+
throw new Error(`${path}: merge the input into one bounded object schema.`);
|
|
132
|
+
const input = record(def.inputs.length ? schema(def.inputs[0], `${path}.input`) : z.strictObject({}), `${path}.Input`, false);
|
|
133
|
+
const output = record(schema(Reflect.get(def, "output"), `${path}.output`), `${path}.Output`, true);
|
|
134
|
+
const version = createHash("sha256").update(JSON.stringify({ abi: 1, path, kind: def.type, input, output })).digest("hex").slice(0, 24);
|
|
135
|
+
contracts.push({ path, kind: def.type, input, output, version });
|
|
136
|
+
}
|
|
137
|
+
return contracts.sort((a, b) => a.path.localeCompare(b.path));
|
|
138
|
+
};
|
|
139
|
+
var servicePath = (contract) => `/tsplc/services/${contract.version}/${contract.path}`;
|
|
140
|
+
var validateWireRecord = (record, value) => {
|
|
141
|
+
const scalarValue = (field, value) => {
|
|
142
|
+
switch (field.kind) {
|
|
143
|
+
case "boolean":
|
|
144
|
+
return typeof value === "boolean";
|
|
145
|
+
case "integer":
|
|
146
|
+
return typeof value === "number" && Number.isInteger(value) && value >= field.minimum && value <= field.maximum;
|
|
147
|
+
case "string":
|
|
148
|
+
return typeof value === "string" && !value.includes("\x00") && new TextEncoder().encode(value).length <= field.capacity;
|
|
149
|
+
case "enum":
|
|
150
|
+
return typeof value === "number" && Object.values(field.members).includes(value);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
154
|
+
throw new Error("Expected a closed wire record.");
|
|
155
|
+
const entries = Object.entries(value);
|
|
156
|
+
if (entries.length !== Object.keys(record.fields).length)
|
|
157
|
+
throw new Error("Wire record fields differ from the contract.");
|
|
158
|
+
for (const [name, field] of Object.entries(record.fields)) {
|
|
159
|
+
const item = Reflect.get(value, name);
|
|
160
|
+
const valid = field.kind === "array" ? Array.isArray(item) && item.length === field.length && item.every((value) => scalarValue(field.element, value)) : scalarValue(field, item);
|
|
161
|
+
if (!valid)
|
|
162
|
+
throw new Error(`Wire field ${name} violates its PLC representation.`);
|
|
163
|
+
}
|
|
164
|
+
if (new TextEncoder().encode(JSON.stringify(value)).length > 2048)
|
|
165
|
+
throw new Error("Wire record exceeds the 2048-byte native JSON limit.");
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// packages/server/src/index.ts
|
|
169
|
+
var createServiceHandler = (options) => {
|
|
170
|
+
const contracts = serviceContracts(options.router);
|
|
171
|
+
const paths = new Map(contracts.map((contract) => [servicePath(contract), contract]));
|
|
172
|
+
const response = (value, status = 200) => {
|
|
173
|
+
const body = JSON.stringify(value);
|
|
174
|
+
return new Response(body, {
|
|
175
|
+
status,
|
|
176
|
+
headers: {
|
|
177
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
178
|
+
"Content-Length": String(new TextEncoder().encode(body).length),
|
|
179
|
+
"Cache-Control": "no-store"
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
return async (request) => {
|
|
184
|
+
const path = new URL(request.url).pathname;
|
|
185
|
+
const contract = paths.get(path);
|
|
186
|
+
if (!contract)
|
|
187
|
+
return response({ error: "Unknown procedure or incompatible contract" }, 409);
|
|
188
|
+
if (request.method !== "POST")
|
|
189
|
+
return response({ error: "Use POST" }, 405);
|
|
190
|
+
const length = request.headers.get("content-length");
|
|
191
|
+
if (length !== null && (!/^\d+$/.test(length) || Number(length) > 2048))
|
|
192
|
+
return response({ error: "Request exceeds the PLC wire limit" }, 413);
|
|
193
|
+
const reader = request.body?.getReader();
|
|
194
|
+
const chunks = [];
|
|
195
|
+
let size = 0;
|
|
196
|
+
if (reader)
|
|
197
|
+
try {
|
|
198
|
+
for (;; ) {
|
|
199
|
+
const chunk = await reader.read();
|
|
200
|
+
if (chunk.done)
|
|
201
|
+
break;
|
|
202
|
+
size += chunk.value.length;
|
|
203
|
+
if (size > 2048) {
|
|
204
|
+
await reader.cancel();
|
|
205
|
+
return response({ error: "Request exceeds the PLC wire limit" }, 413);
|
|
206
|
+
}
|
|
207
|
+
chunks.push(chunk.value);
|
|
208
|
+
}
|
|
209
|
+
} finally {
|
|
210
|
+
reader.releaseLock();
|
|
211
|
+
}
|
|
212
|
+
const bytes = new Uint8Array(size);
|
|
213
|
+
let offset = 0;
|
|
214
|
+
for (const chunk of chunks) {
|
|
215
|
+
bytes.set(chunk, offset);
|
|
216
|
+
offset += chunk.length;
|
|
217
|
+
}
|
|
218
|
+
let input;
|
|
219
|
+
try {
|
|
220
|
+
input = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
221
|
+
validateWireRecord(contract.input, input);
|
|
222
|
+
} catch {
|
|
223
|
+
return response({ error: "Invalid procedure input" }, 400);
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
const context = await options.createContext(request);
|
|
227
|
+
const caller = options.router.createCaller(context, { signal: request.signal });
|
|
228
|
+
const invoke = Reflect.get(caller, contract.path);
|
|
229
|
+
if (typeof invoke !== "function")
|
|
230
|
+
throw new Error("Invalid tRPC caller.");
|
|
231
|
+
const output = await invoke(input);
|
|
232
|
+
validateWireRecord(contract.output, output);
|
|
233
|
+
return response(output);
|
|
234
|
+
} catch (cause) {
|
|
235
|
+
if (cause instanceof TRPCError) {
|
|
236
|
+
const statuses = {
|
|
237
|
+
UNAUTHORIZED: 401,
|
|
238
|
+
FORBIDDEN: 403,
|
|
239
|
+
BAD_REQUEST: 400,
|
|
240
|
+
NOT_FOUND: 404,
|
|
241
|
+
CONFLICT: 409,
|
|
242
|
+
TOO_MANY_REQUESTS: 429
|
|
243
|
+
};
|
|
244
|
+
const status = statuses[cause.code];
|
|
245
|
+
return response({ error: cause.code }, status ?? 500);
|
|
246
|
+
}
|
|
247
|
+
return response({ error: "Procedure failed" }, 500);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// packages/server/src/runtime.ts
|
|
253
|
+
var tlsSettings = (settings) => ({
|
|
254
|
+
cert: Bun.file(z2.string().nonempty().parse(process.env[settings.certificateEnv])),
|
|
255
|
+
key: Bun.file(z2.string().nonempty().parse(process.env[settings.privateKeyEnv]))
|
|
256
|
+
});
|
|
257
|
+
var serverSettings = (settings) => ({
|
|
258
|
+
...settings.tls ? { tls: tlsSettings(settings.tls) } : {},
|
|
259
|
+
port: z2.coerce.number().int().min(1).max(65535).parse(settings.portEnv ? process.env[settings.portEnv] ?? settings.port : settings.port),
|
|
260
|
+
hostname: z2.string().nonempty().parse(settings.hostnameEnv ? process.env[settings.hostnameEnv] ?? settings.hostname : settings.hostname)
|
|
261
|
+
});
|
|
262
|
+
var serveServices = (options) => {
|
|
263
|
+
const token = z2.string().nonempty().parse(process.env[options.bearerTokenEnv]);
|
|
264
|
+
const handler = createServiceHandler(options);
|
|
265
|
+
const server = Bun.serve({
|
|
266
|
+
...serverSettings(options),
|
|
267
|
+
fetch: async (request) => {
|
|
268
|
+
if (request.headers.get("authorization") !== `Bearer ${token}`)
|
|
269
|
+
return new Response("Unauthorized", { status: 401 });
|
|
270
|
+
return handler(request);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
const stop = () => {
|
|
274
|
+
server.stop(true);
|
|
275
|
+
};
|
|
276
|
+
process.once("SIGTERM", stop);
|
|
277
|
+
process.once("SIGINT", stop);
|
|
278
|
+
return {
|
|
279
|
+
server,
|
|
280
|
+
stop: async () => {
|
|
281
|
+
process.off("SIGTERM", stop);
|
|
282
|
+
process.off("SIGINT", stop);
|
|
283
|
+
await server.stop(true);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
};
|
|
287
|
+
export {
|
|
288
|
+
serveServices,
|
|
289
|
+
serverSettings
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
//# debugId=5E60470060A963DA64756E2164756E21
|
|
293
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/runtime.ts", "../src/index.ts", "../src/contract.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { z } from \"zod\";\nimport type { AnyRouter, inferRouterContext } from \"@trpc/server\";\nimport { createServiceHandler } from \"./index\";\n\nexport interface ServerSettings {\n transport?: \"http\" | \"opcua\";\n port?: number;\n hostname?: string;\n portEnv?: string;\n hostnameEnv?: string;\n tls?: { certificateEnv: string; privateKeyEnv: string };\n}\nconst tlsSettings = (settings: NonNullable<ServerSettings[\"tls\"]>) => ({\n cert: Bun.file(z.string().nonempty().parse(process.env[settings.certificateEnv])),\n key: Bun.file(z.string().nonempty().parse(process.env[settings.privateKeyEnv])),\n});\nexport const serverSettings = (settings: ServerSettings) => ({\n ...(settings.tls ? { tls: tlsSettings(settings.tls) } : {}),\n port: z.coerce\n .number()\n .int()\n .min(1)\n .max(65535)\n .parse(settings.portEnv ? (process.env[settings.portEnv] ?? settings.port) : settings.port),\n hostname: z\n .string()\n .nonempty()\n .parse(\n settings.hostnameEnv\n ? (process.env[settings.hostnameEnv] ?? settings.hostname)\n : settings.hostname,\n ),\n});\n\n/** Embedded hosts can use createServiceHandler instead of starting a listener. */\nexport const serveServices = <Router extends AnyRouter>(\n options: ServerSettings & {\n router: Router;\n createContext: (\n request: Request,\n ) => inferRouterContext<Router> | Promise<inferRouterContext<Router>>;\n bearerTokenEnv: string;\n },\n) => {\n const token = z.string().nonempty().parse(process.env[options.bearerTokenEnv]);\n const handler = createServiceHandler(options);\n const server = Bun.serve({\n ...serverSettings(options),\n fetch: async (request: Request) => {\n if (request.headers.get(\"authorization\") !== `Bearer ${token}`)\n return new Response(\"Unauthorized\", { status: 401 });\n return handler(request);\n },\n });\n const stop = () => {\n void server.stop(true);\n };\n process.once(\"SIGTERM\", stop);\n process.once(\"SIGINT\", stop);\n return {\n server,\n stop: async () => {\n process.off(\"SIGTERM\", stop);\n process.off(\"SIGINT\", stop);\n await server.stop(true);\n },\n };\n};\n",
|
|
6
|
+
"import { TRPCError, type AnyRouter, type inferRouterContext } from \"@trpc/server\";\nimport { serviceContracts, servicePath, validateWireRecord } from \"./contract\";\nexport {\n serviceContracts,\n servicePath,\n type PlcMeta,\n type ServiceContract,\n type WireField,\n type WireRecord,\n type WireScalar,\n} from \"./contract\";\n\n/** Mount in an authenticated Fetch host; createContext must resolve the caller's identity. */\nexport const createServiceHandler = <Router extends AnyRouter>(options: {\n router: Router;\n createContext: (\n request: Request,\n ) => inferRouterContext<Router> | Promise<inferRouterContext<Router>>;\n}) => {\n const contracts = serviceContracts(options.router);\n const paths = new Map(contracts.map((contract) => [servicePath(contract), contract]));\n const response = (value: unknown, status = 200) => {\n const body = JSON.stringify(value);\n return new Response(body, {\n status,\n headers: {\n \"Content-Type\": \"application/json; charset=utf-8\",\n \"Content-Length\": String(new TextEncoder().encode(body).length),\n \"Cache-Control\": \"no-store\",\n },\n });\n };\n return async (request: Request): Promise<Response> => {\n const path = new URL(request.url).pathname;\n const contract = paths.get(path);\n if (!contract) return response({ error: \"Unknown procedure or incompatible contract\" }, 409);\n if (request.method !== \"POST\") return response({ error: \"Use POST\" }, 405);\n const length = request.headers.get(\"content-length\");\n if (length !== null && (!/^\\d+$/.test(length) || Number(length) > 2048))\n return response({ error: \"Request exceeds the PLC wire limit\" }, 413);\n const reader = request.body?.getReader();\n const chunks: Uint8Array[] = [];\n let size = 0;\n if (reader)\n try {\n for (;;) {\n const chunk = await reader.read();\n if (chunk.done) break;\n size += chunk.value.length;\n if (size > 2048) {\n await reader.cancel();\n return response({ error: \"Request exceeds the PLC wire limit\" }, 413);\n }\n chunks.push(chunk.value);\n }\n } finally {\n reader.releaseLock();\n }\n const bytes = new Uint8Array(size);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.length;\n }\n let input: unknown;\n try {\n input = JSON.parse(new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes));\n validateWireRecord(contract.input, input);\n } catch {\n return response({ error: \"Invalid procedure input\" }, 400);\n }\n try {\n const context = await options.createContext(request);\n const caller = options.router.createCaller(context, { signal: request.signal });\n const invoke: unknown = Reflect.get(caller, contract.path);\n if (typeof invoke !== \"function\") throw new Error(\"Invalid tRPC caller.\");\n const output: unknown = await invoke(input);\n validateWireRecord(contract.output, output);\n return response(output);\n } catch (cause) {\n if (cause instanceof TRPCError) {\n const statuses: Partial<Record<TRPCError[\"code\"], number>> = {\n UNAUTHORIZED: 401,\n FORBIDDEN: 403,\n BAD_REQUEST: 400,\n NOT_FOUND: 404,\n CONFLICT: 409,\n TOO_MANY_REQUESTS: 429,\n };\n const status = statuses[cause.code];\n return response({ error: cause.code }, status ?? 500);\n }\n return response({ error: \"Procedure failed\" }, 500);\n }\n };\n};\n",
|
|
7
|
+
"import { createHash } from \"node:crypto\";\nimport type { AnyRouter, AnyProcedure } from \"@trpc/server\";\nimport { z } from \"zod\";\n\nexport interface PlcMeta {\n plc?: boolean;\n}\nexport type WireScalar =\n | { kind: \"boolean\" }\n | { kind: \"integer\"; iec: string; minimum: number; maximum: number }\n | { kind: \"string\"; capacity: number }\n | { kind: \"enum\"; name: string; members: Record<string, number> };\nexport type WireField = WireScalar | { kind: \"array\"; length: number; element: WireScalar };\nexport interface WireRecord {\n name: string;\n fields: Record<string, WireField>;\n}\nexport interface ServiceContract {\n path: string;\n kind: \"query\" | \"mutation\";\n input: WireRecord;\n output: WireRecord;\n version: string;\n}\nconst identifier = /^(?!__proto__$)[A-Za-z_][A-Za-z0-9_]*$/;\nconst pascal = (value: string) =>\n value\n .split(/[^A-Za-z0-9]+/)\n .map((part) => part[0]!.toUpperCase() + part.slice(1))\n .join(\"\");\nconst schemaValue = (value: unknown): value is z.ZodType =>\n typeof value === \"object\" && value !== null && \"_zod\" in value && \"safeParse\" in value;\nconst schema = (value: unknown, label: string): z.ZodType => {\n if (!schemaValue(value))\n throw new Error(\n `${label}: provide a bounded Zod input/output schema; TypeScript number/string inference has no IEC memory layout.`,\n );\n return value;\n};\nconst jsonSchema = z.looseObject({\n type: z.string().optional(),\n minimum: z.number().optional(),\n maximum: z.number().optional(),\n maxLength: z.number().optional(),\n minItems: z.number().optional(),\n maxItems: z.number().optional(),\n iec: z.string().optional(),\n id: z.string().optional(),\n});\nconst integerTypes: Record<string, readonly [number, number]> = {\n SINT: [-128, 127],\n USINT: [0, 255],\n BYTE: [0, 255],\n INT: [-32768, 32767],\n UINT: [0, 65535],\n WORD: [0, 65535],\n DINT: [-2147483648, 2147483647],\n UDINT: [0, 4294967295],\n DWORD: [0, 4294967295],\n};\nconst scalar = (value: z.ZodType, label: string): WireScalar => {\n const json = jsonSchema.parse(z.toJSONSchema(value, { io: \"input\", unrepresentable: \"throw\" }));\n const definition = value._zod.def;\n if (\n [\"optional\", \"nullable\", \"union\", \"default\", \"prefault\", \"pipe\", \"catch\"].includes(\n definition.type,\n )\n )\n throw new Error(\n `${label}: optional, nullable, transformed and defaulted wire fields are unsupported.`,\n );\n if (definition.type === \"enum\") {\n const entries = z\n .record(z.string(), z.union([z.number().int(), z.string()]))\n .parse(Reflect.get(definition, \"entries\"));\n if (\n Object.entries(entries).some(\n ([name, value]) => identifier.test(name) && typeof value !== \"number\",\n )\n )\n throw new Error(`${label}: PLC enums require only numeric members.`);\n const values: [string, number][] = [];\n for (const [name, value] of Object.entries(entries))\n if (identifier.test(name) && typeof value === \"number\") values.push([name, value]);\n values.sort((left, right) => {\n if (left[1] === 0) return -1;\n if (right[1] === 0) return 1;\n return left[1] - right[1];\n });\n const members = Object.fromEntries(values);\n if (new Set(values.map(([, value]) => value)).size !== values.length)\n throw new Error(`${label}: PLC enum values must be distinct.`);\n if (\n !Object.keys(members).length ||\n Object.values(members).some((value) => value < -2147483648 || value > 2147483647)\n )\n throw new Error(`${label}: PLC enums require named DINT values.`);\n const name = json.id ?? pascal(label);\n if (!identifier.test(name)) throw new Error(`${label}: schema id must be an IEC identifier.`);\n return { kind: \"enum\", name, members };\n }\n if (json.type === \"boolean\") return { kind: \"boolean\" };\n if (\n json.type === \"string\" &&\n json.maxLength !== undefined &&\n Number.isInteger(json.maxLength) &&\n json.maxLength > 0 &&\n json.maxLength <= 2048\n )\n return { kind: \"string\", capacity: json.maxLength };\n if (\n json.type === \"integer\" &&\n json.minimum !== undefined &&\n json.maximum !== undefined &&\n Number.isInteger(json.minimum) &&\n Number.isInteger(json.maximum)\n ) {\n const iec =\n json.iec ??\n (json.minimum < 0\n ? json.minimum >= -32768 && json.maximum <= 32767\n ? \"INT\"\n : \"DINT\"\n : json.maximum <= 65535\n ? \"UINT\"\n : \"UDINT\");\n const range = integerTypes[iec];\n if (json.minimum > json.maximum) throw new Error(`${label}: integer bounds are contradictory.`);\n if (!range || json.minimum < range[0] || json.maximum > range[1])\n throw new Error(`${label}: integer bounds do not fit ${iec}.`);\n return { kind: \"integer\", iec, minimum: json.minimum, maximum: json.maximum };\n }\n throw new Error(\n `${label}: use BOOL, a numeric enum, an integer with min/max, or a string with max length. Optional, nullable, union, float and dynamic layouts are unsupported.`,\n );\n};\nconst record = (value: z.ZodType, label: string, output: boolean): WireRecord => {\n const def = value._zod.def;\n if (def.type !== \"object\")\n throw new Error(`${label}: HTTP services require a closed object schema.`);\n const shape = z\n .record(z.string(), z.custom<z.ZodType>(schemaValue))\n .parse(Reflect.get(def, \"shape\"));\n const json = jsonSchema.parse(z.toJSONSchema(value, { io: \"input\", unrepresentable: \"throw\" }));\n const name = json.id ?? pascal(label);\n if (!identifier.test(name)) throw new Error(`${label}: schema id must be an IEC identifier.`);\n const fields: Record<string, WireField> = {};\n for (const key of Object.keys(shape).sort()) {\n if (!identifier.test(key)) throw new Error(`${label}.${key}: use an IEC field identifier.`);\n const item = shape[key]!;\n if (item._zod.def.type === \"array\") {\n if (output)\n throw new Error(\n `${label}.${key}: the native HTTP decoder currently supports scalar response fields only.`,\n );\n const bounds = jsonSchema.parse(z.toJSONSchema(item, { io: \"input\" }));\n if (!bounds.maxItems || bounds.maxItems !== bounds.minItems || bounds.maxItems > 256)\n throw new Error(`${label}.${key}: arrays require one fixed length from 1 to 256.`);\n fields[key] = {\n kind: \"array\",\n length: bounds.maxItems,\n element: scalar(\n schema(Reflect.get(item._zod.def, \"element\"), `${label}.${key}`),\n `${label}.${key}`,\n ),\n };\n } else fields[key] = scalar(item, `${label}.${key}`);\n }\n if (Object.keys(fields).length > 31 || (output && !Object.keys(fields).length))\n throw new Error(`${label}: response records require 1..31 fields; requests allow 0..31.`);\n return { name, fields };\n};\n\n/** Reflection is isolated here because tRPC v11 keeps parsers on procedure._def. */\nexport const serviceContracts = (router: AnyRouter): ServiceContract[] => {\n const contracts: ServiceContract[] = [];\n const procedures = z.record(z.string(), z.unknown()).parse(router._def.procedures);\n for (const [path, procedure] of Object.entries(procedures)) {\n if (typeof procedure !== \"function\" || !(\"_def\" in procedure))\n throw new Error(\"Unsupported tRPC router metadata.\");\n const reflected = z\n .custom<AnyProcedure>((value) => typeof value === \"function\" && \"_def\" in value)\n .parse(procedure);\n const def = reflected._def;\n if (\n typeof def.meta !== \"object\" ||\n def.meta === null ||\n !(\"plc\" in def.meta) ||\n def.meta.plc !== true\n )\n continue;\n if (path.split(\".\").some((part) => !identifier.test(part)))\n throw new Error(`${path}: PLC procedure paths require identifier segments.`);\n if (def.type !== \"query\" && def.type !== \"mutation\")\n throw new Error(\n `${path}: subscriptions use a different transport; expose a query or mutation to the PLC.`,\n );\n if (def.inputs.length > 1)\n throw new Error(`${path}: merge the input into one bounded object schema.`);\n const input = record(\n def.inputs.length ? schema(def.inputs[0], `${path}.input`) : z.strictObject({}),\n `${path}.Input`,\n false,\n );\n const output = record(\n schema(Reflect.get(def, \"output\"), `${path}.output`),\n `${path}.Output`,\n true,\n );\n const version = createHash(\"sha256\")\n .update(JSON.stringify({ abi: 1, path, kind: def.type, input, output }))\n .digest(\"hex\")\n .slice(0, 24);\n contracts.push({ path, kind: def.type, input, output, version });\n }\n return contracts.sort((a, b) => a.path.localeCompare(b.path));\n};\n\nexport const servicePath = (contract: ServiceContract) =>\n `/tsplc/services/${contract.version}/${contract.path}`;\n\nexport const validateWireRecord = (record: WireRecord, value: unknown): void => {\n const scalarValue = (field: WireScalar, value: unknown): boolean => {\n switch (field.kind) {\n case \"boolean\":\n return typeof value === \"boolean\";\n case \"integer\":\n return (\n typeof value === \"number\" &&\n Number.isInteger(value) &&\n value >= field.minimum &&\n value <= field.maximum\n );\n case \"string\":\n return (\n typeof value === \"string\" &&\n !value.includes(\"\\0\") &&\n new TextEncoder().encode(value).length <= field.capacity\n );\n case \"enum\":\n return typeof value === \"number\" && Object.values(field.members).includes(value);\n }\n };\n if (typeof value !== \"object\" || value === null || Array.isArray(value))\n throw new Error(\"Expected a closed wire record.\");\n const entries = Object.entries(value);\n if (entries.length !== Object.keys(record.fields).length)\n throw new Error(\"Wire record fields differ from the contract.\");\n for (const [name, field] of Object.entries(record.fields)) {\n const item: unknown = Reflect.get(value, name);\n const valid =\n field.kind === \"array\"\n ? Array.isArray(item) &&\n item.length === field.length &&\n item.every((value) => scalarValue(field.element, value))\n : scalarValue(field, item);\n if (!valid) throw new Error(`Wire field ${name} violates its PLC representation.`);\n }\n if (new TextEncoder().encode(JSON.stringify(value)).length > 2048)\n throw new Error(\"Wire record exceeds the 2048-byte native JSON limit.\");\n};\n"
|
|
8
|
+
],
|
|
9
|
+
"mappings": "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",
|
|
10
|
+
"debugId": "5E60470060A963DA64756E2164756E21",
|
|
11
|
+
"names": []
|
|
12
|
+
}
|