@epoch32/visage 1.1.3 → 1.1.4
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/dist/api/index.js +55 -4085
- package/dist/charts/index.js +48 -2806
- package/dist/devtools/index.js +18 -4771
- package/dist/dom/index.js +32 -1639
- package/dist/form/index.js +13 -1498
- package/dist/index-061jf06a.js +848 -0
- package/dist/index-0madxn60.js +442 -0
- package/dist/index-1xtrtz1t.js +256 -0
- package/dist/index-2zz0g2n2.js +212 -0
- package/dist/index-5s255b3w.js +303 -0
- package/dist/index-6kfe1nr4.js +1179 -0
- package/dist/index-8czhpea8.js +338 -0
- package/dist/index-8x8xz80v.js +727 -0
- package/dist/index-921pn75b.js +0 -0
- package/dist/index-965ryh36.js +537 -0
- package/dist/index-9fvarkrx.js +98 -0
- package/dist/index-a4xns6cc.js +219 -0
- package/dist/index-b5wc752a.js +1696 -0
- package/dist/index-df5e614s.js +334 -0
- package/dist/index-dyvgtp43.js +1094 -0
- package/dist/index-grhdtcb5.js +234 -0
- package/dist/index-h6tjqk6e.js +158 -0
- package/dist/index-js199er4.js +101 -0
- package/dist/index-x2rs3vab.js +124 -0
- package/dist/index-y9bq7h9g.js +1162 -0
- package/dist/index-zr6cvset.js +2092 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +458 -11920
- package/dist/jsx-runtime/index.js +13 -1719
- package/dist/query/index.js +47 -2505
- package/dist/router/index.js +25 -1945
- package/dist/rpc/index.js +39 -3567
- package/dist/server/index.js +11 -1256
- package/dist/signals/index.js +52 -1160
- package/dist/style/index.js +77 -2429
- package/dist/template/index.js +7 -1232
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +150 -4249
- package/dist/ui/tabs.d.ts +23 -1
- package/dist/virtual/index.js +12 -2044
- package/dist/vite/index.js +5 -744
- package/package.json +2 -2
- package/src/dom/render.ts +20 -7
- package/src/index.ts +2 -0
- package/src/signals/computed.ts +19 -12
- package/src/signals/effect.ts +7 -7
- package/src/signals/signal.ts +16 -12
- package/src/signals/system.ts +50 -21
- package/src/ui/index.ts +2 -0
- package/src/ui/tabs.tsx +211 -10
- package/src/ui/utils.ts +1 -3
package/dist/vite/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createFetchHandler2
|
|
3
|
+
} from "../index-965ryh36.js";
|
|
4
|
+
|
|
1
5
|
// src/vite/lucide.ts
|
|
2
6
|
function parseNamedImports(importClause) {
|
|
3
7
|
return importClause.split(",").map((s) => s.trim()).filter(Boolean).map((item) => {
|
|
@@ -97,753 +101,10 @@ if (import.meta.hot) {
|
|
|
97
101
|
};
|
|
98
102
|
}
|
|
99
103
|
|
|
100
|
-
// src/rpc/error.ts
|
|
101
|
-
var ERROR_CODE_TO_STATUS = {
|
|
102
|
-
BAD_REQUEST: 400,
|
|
103
|
-
UNAUTHORIZED: 401,
|
|
104
|
-
FORBIDDEN: 403,
|
|
105
|
-
NOT_FOUND: 404,
|
|
106
|
-
METHOD_NOT_ALLOWED: 405,
|
|
107
|
-
TIMEOUT: 408,
|
|
108
|
-
CONFLICT: 409,
|
|
109
|
-
UNPROCESSABLE_CONTENT: 422,
|
|
110
|
-
INTERNAL_SERVER_ERROR: 500
|
|
111
|
-
};
|
|
112
|
-
var STATUS_TO_ERROR_CODE = {
|
|
113
|
-
400: "BAD_REQUEST",
|
|
114
|
-
401: "UNAUTHORIZED",
|
|
115
|
-
403: "FORBIDDEN",
|
|
116
|
-
404: "NOT_FOUND",
|
|
117
|
-
405: "METHOD_NOT_ALLOWED",
|
|
118
|
-
408: "TIMEOUT",
|
|
119
|
-
409: "CONFLICT",
|
|
120
|
-
422: "UNPROCESSABLE_CONTENT",
|
|
121
|
-
500: "INTERNAL_SERVER_ERROR"
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
class RPCError extends Error {
|
|
125
|
-
code;
|
|
126
|
-
status;
|
|
127
|
-
issues;
|
|
128
|
-
constructor(codeOrStatusOrOptions, message) {
|
|
129
|
-
if (typeof codeOrStatusOrOptions === "number") {
|
|
130
|
-
const code = STATUS_TO_ERROR_CODE[codeOrStatusOrOptions] || "INTERNAL_SERVER_ERROR";
|
|
131
|
-
super(message || code);
|
|
132
|
-
this.name = "RPCError";
|
|
133
|
-
this.code = code;
|
|
134
|
-
this.status = codeOrStatusOrOptions;
|
|
135
|
-
} else if (typeof codeOrStatusOrOptions === "string") {
|
|
136
|
-
super(message || codeOrStatusOrOptions);
|
|
137
|
-
this.name = "RPCError";
|
|
138
|
-
this.code = codeOrStatusOrOptions;
|
|
139
|
-
this.status = ERROR_CODE_TO_STATUS[codeOrStatusOrOptions] || 500;
|
|
140
|
-
} else {
|
|
141
|
-
super(codeOrStatusOrOptions.message ?? codeOrStatusOrOptions.code, {
|
|
142
|
-
cause: codeOrStatusOrOptions.cause
|
|
143
|
-
});
|
|
144
|
-
this.name = "RPCError";
|
|
145
|
-
this.code = codeOrStatusOrOptions.code;
|
|
146
|
-
this.status = ERROR_CODE_TO_STATUS[codeOrStatusOrOptions.code] ?? 500;
|
|
147
|
-
this.issues = codeOrStatusOrOptions.issues;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
get statusCode() {
|
|
151
|
-
return this.status;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// src/form/schema.ts
|
|
156
|
-
class Schema {
|
|
157
|
-
"~standard" = {
|
|
158
|
-
version: 1,
|
|
159
|
-
vendor: "visage",
|
|
160
|
-
validate: (val) => {
|
|
161
|
-
const res = this.validate(val);
|
|
162
|
-
if (res.success) {
|
|
163
|
-
return { value: res.data };
|
|
164
|
-
}
|
|
165
|
-
return {
|
|
166
|
-
issues: (res.issues || []).map((i) => ({
|
|
167
|
-
path: [i.path],
|
|
168
|
-
message: i.message
|
|
169
|
-
}))
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
class StringSchema extends Schema {
|
|
176
|
-
_min;
|
|
177
|
-
_max;
|
|
178
|
-
_emailMsg;
|
|
179
|
-
_regex;
|
|
180
|
-
_isOptional = false;
|
|
181
|
-
min(length, message) {
|
|
182
|
-
this._min = { val: length, msg: message };
|
|
183
|
-
return this;
|
|
184
|
-
}
|
|
185
|
-
max(length, message) {
|
|
186
|
-
this._max = { val: length, msg: message };
|
|
187
|
-
return this;
|
|
188
|
-
}
|
|
189
|
-
email(message) {
|
|
190
|
-
this._emailMsg = message ?? "Invalid email address";
|
|
191
|
-
return this;
|
|
192
|
-
}
|
|
193
|
-
regex(pattern, message) {
|
|
194
|
-
this._regex = { pattern, msg: message };
|
|
195
|
-
return this;
|
|
196
|
-
}
|
|
197
|
-
optional() {
|
|
198
|
-
this._isOptional = true;
|
|
199
|
-
return this;
|
|
200
|
-
}
|
|
201
|
-
validate(value, path = "") {
|
|
202
|
-
if (value === undefined || value === null || value === "") {
|
|
203
|
-
if (this._isOptional)
|
|
204
|
-
return { success: true, data: undefined };
|
|
205
|
-
return { success: false, issues: [{ path, message: "This field is required" }] };
|
|
206
|
-
}
|
|
207
|
-
if (typeof value !== "string") {
|
|
208
|
-
return { success: false, issues: [{ path, message: "Must be a string" }] };
|
|
209
|
-
}
|
|
210
|
-
if (this._min && value.length < this._min.val) {
|
|
211
|
-
return {
|
|
212
|
-
success: false,
|
|
213
|
-
issues: [{ path, message: this._min.msg ?? `Must be at least ${this._min.val} characters` }]
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
if (this._max && value.length > this._max.val) {
|
|
217
|
-
return {
|
|
218
|
-
success: false,
|
|
219
|
-
issues: [{ path, message: this._max.msg ?? `Must be at most ${this._max.val} characters` }]
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
if (this._emailMsg) {
|
|
223
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
224
|
-
if (!emailRegex.test(value)) {
|
|
225
|
-
return { success: false, issues: [{ path, message: this._emailMsg }] };
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
if (this._regex && !this._regex.pattern.test(value)) {
|
|
229
|
-
return {
|
|
230
|
-
success: false,
|
|
231
|
-
issues: [{ path, message: this._regex.msg ?? "Invalid format" }]
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
return { success: true, data: value };
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
class NumberSchema extends Schema {
|
|
239
|
-
_min;
|
|
240
|
-
_max;
|
|
241
|
-
_integer;
|
|
242
|
-
_isOptional = false;
|
|
243
|
-
min(value, message) {
|
|
244
|
-
this._min = { val: value, msg: message };
|
|
245
|
-
return this;
|
|
246
|
-
}
|
|
247
|
-
max(value, message) {
|
|
248
|
-
this._max = { val: value, msg: message };
|
|
249
|
-
return this;
|
|
250
|
-
}
|
|
251
|
-
integer(message) {
|
|
252
|
-
this._integer = { msg: message };
|
|
253
|
-
return this;
|
|
254
|
-
}
|
|
255
|
-
optional() {
|
|
256
|
-
this._isOptional = true;
|
|
257
|
-
return this;
|
|
258
|
-
}
|
|
259
|
-
validate(value, path = "") {
|
|
260
|
-
if (value === undefined || value === null || value === "") {
|
|
261
|
-
if (this._isOptional)
|
|
262
|
-
return { success: true, data: undefined };
|
|
263
|
-
return { success: false, issues: [{ path, message: "This field is required" }] };
|
|
264
|
-
}
|
|
265
|
-
const num = Number(value);
|
|
266
|
-
if (Number.isNaN(num) || typeof value === "boolean") {
|
|
267
|
-
return { success: false, issues: [{ path, message: "Must be a valid number" }] };
|
|
268
|
-
}
|
|
269
|
-
if (this._integer && !Number.isInteger(num)) {
|
|
270
|
-
return {
|
|
271
|
-
success: false,
|
|
272
|
-
issues: [{ path, message: this._integer.msg ?? "Must be an integer" }]
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
if (this._min && num < this._min.val) {
|
|
276
|
-
return {
|
|
277
|
-
success: false,
|
|
278
|
-
issues: [{ path, message: this._min.msg ?? `Must be at least ${this._min.val}` }]
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
if (this._max && num > this._max.val) {
|
|
282
|
-
return {
|
|
283
|
-
success: false,
|
|
284
|
-
issues: [{ path, message: this._max.msg ?? `Must be at most ${this._max.val}` }]
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
return { success: true, data: num };
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
class BooleanSchema extends Schema {
|
|
292
|
-
validate(value, _path = "") {
|
|
293
|
-
return { success: true, data: Boolean(value) };
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
class ObjectSchema extends Schema {
|
|
298
|
-
shape;
|
|
299
|
-
constructor(shape) {
|
|
300
|
-
super();
|
|
301
|
-
this.shape = shape;
|
|
302
|
-
}
|
|
303
|
-
validate(value, basePath = "") {
|
|
304
|
-
if (typeof value !== "object" || value === null) {
|
|
305
|
-
return { success: false, issues: [{ path: basePath, message: "Must be an object" }] };
|
|
306
|
-
}
|
|
307
|
-
const issues = [];
|
|
308
|
-
const data = {};
|
|
309
|
-
for (const key in this.shape) {
|
|
310
|
-
const fieldSchema = this.shape[key];
|
|
311
|
-
const fieldVal = value[key];
|
|
312
|
-
const fieldPath = basePath ? `${basePath}.${key}` : key;
|
|
313
|
-
const res = validateWithSchema(fieldSchema, fieldVal, fieldPath);
|
|
314
|
-
if (res.success) {
|
|
315
|
-
data[key] = res.data;
|
|
316
|
-
} else if (res.issues) {
|
|
317
|
-
issues.push(...res.issues);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
if (issues.length > 0) {
|
|
321
|
-
return { success: false, issues };
|
|
322
|
-
}
|
|
323
|
-
return { success: true, data };
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
function validateWithSchema(schema, value, path = "") {
|
|
327
|
-
if (!schema)
|
|
328
|
-
return { success: true, data: value };
|
|
329
|
-
if (schema instanceof Schema) {
|
|
330
|
-
return schema.validate(value, path);
|
|
331
|
-
}
|
|
332
|
-
if (typeof schema === "object" && "~standard" in schema) {
|
|
333
|
-
const res = schema["~standard"].validate(value);
|
|
334
|
-
if ("value" in res) {
|
|
335
|
-
return { success: true, data: res.value };
|
|
336
|
-
}
|
|
337
|
-
return {
|
|
338
|
-
success: false,
|
|
339
|
-
issues: res.issues.map((i) => ({
|
|
340
|
-
path: i.path ? i.path.join(".") : path,
|
|
341
|
-
message: i.message
|
|
342
|
-
}))
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
if (typeof schema === "function") {
|
|
346
|
-
const res = schema(value);
|
|
347
|
-
if (res === true || res === null || res === undefined) {
|
|
348
|
-
return { success: true, data: value };
|
|
349
|
-
}
|
|
350
|
-
if (typeof res === "string") {
|
|
351
|
-
return { success: false, issues: [{ path, message: res }] };
|
|
352
|
-
}
|
|
353
|
-
if (typeof res === "object") {
|
|
354
|
-
return res;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
return { success: true, data: value };
|
|
358
|
-
}
|
|
359
|
-
var v = {
|
|
360
|
-
string: () => new StringSchema,
|
|
361
|
-
number: () => new NumberSchema,
|
|
362
|
-
boolean: () => new BooleanSchema,
|
|
363
|
-
object: (shape) => new ObjectSchema(shape)
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
// src/rpc/procedure.ts
|
|
367
|
-
function createProcedure(type, options, middlewares = []) {
|
|
368
|
-
return {
|
|
369
|
-
type,
|
|
370
|
-
inputSchema: options.input,
|
|
371
|
-
outputSchema: options.output,
|
|
372
|
-
middlewares: [...middlewares],
|
|
373
|
-
resolve: options.resolve
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
async function executeProcedure(procedure, opts) {
|
|
377
|
-
let input = opts.rawInput;
|
|
378
|
-
if (procedure.inputSchema) {
|
|
379
|
-
const res = validateWithSchema(procedure.inputSchema, opts.rawInput);
|
|
380
|
-
if (!res.success) {
|
|
381
|
-
throw new RPCError({
|
|
382
|
-
code: "BAD_REQUEST",
|
|
383
|
-
message: "Invalid procedure input",
|
|
384
|
-
issues: res.issues
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
input = res.data;
|
|
388
|
-
}
|
|
389
|
-
let currentIndex = 0;
|
|
390
|
-
let currentContext = opts.ctx;
|
|
391
|
-
const runPipeline = async (ctx) => {
|
|
392
|
-
currentContext = ctx;
|
|
393
|
-
if (currentIndex < procedure.middlewares.length) {
|
|
394
|
-
const mw = procedure.middlewares[currentIndex++];
|
|
395
|
-
return mw({
|
|
396
|
-
ctx: currentContext,
|
|
397
|
-
req: opts.req,
|
|
398
|
-
signal: opts.signal,
|
|
399
|
-
next: async (nextOpts) => {
|
|
400
|
-
return runPipeline(nextOpts?.ctx ?? currentContext);
|
|
401
|
-
}
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
return procedure.resolve({
|
|
405
|
-
input,
|
|
406
|
-
ctx: currentContext,
|
|
407
|
-
req: opts.req,
|
|
408
|
-
signal: opts.signal
|
|
409
|
-
});
|
|
410
|
-
};
|
|
411
|
-
const output = await runPipeline(currentContext);
|
|
412
|
-
if (procedure.outputSchema && procedure.type !== "subscription") {
|
|
413
|
-
const res = validateWithSchema(procedure.outputSchema, output);
|
|
414
|
-
if (!res.success) {
|
|
415
|
-
throw new RPCError({
|
|
416
|
-
code: "INTERNAL_SERVER_ERROR",
|
|
417
|
-
message: "Invalid procedure output",
|
|
418
|
-
issues: res.issues
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
return res.data;
|
|
422
|
-
}
|
|
423
|
-
return output;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// src/rpc/server.ts
|
|
427
|
-
function getCORSHeaders(cors, req) {
|
|
428
|
-
const headers = new Headers;
|
|
429
|
-
if (!cors)
|
|
430
|
-
return headers;
|
|
431
|
-
const opts = typeof cors === "object" ? cors : {};
|
|
432
|
-
const originHeader = req.headers.get("origin") ?? "*";
|
|
433
|
-
let allowOrigin = "*";
|
|
434
|
-
if (typeof opts.origin === "function") {
|
|
435
|
-
const res = opts.origin(originHeader);
|
|
436
|
-
if (typeof res === "string")
|
|
437
|
-
allowOrigin = res;
|
|
438
|
-
else if (res)
|
|
439
|
-
allowOrigin = originHeader;
|
|
440
|
-
} else if (typeof opts.origin === "string") {
|
|
441
|
-
allowOrigin = opts.origin;
|
|
442
|
-
} else if (Array.isArray(opts.origin)) {
|
|
443
|
-
if (opts.origin.includes(originHeader))
|
|
444
|
-
allowOrigin = originHeader;
|
|
445
|
-
} else {
|
|
446
|
-
allowOrigin = originHeader || "*";
|
|
447
|
-
}
|
|
448
|
-
headers.set("Access-Control-Allow-Origin", allowOrigin);
|
|
449
|
-
headers.set("Access-Control-Allow-Methods", (opts.methods ?? ["GET", "POST", "OPTIONS", "HEAD"]).join(", "));
|
|
450
|
-
headers.set("Access-Control-Allow-Headers", (opts.allowedHeaders ?? ["Content-Type", "Authorization", "X-Requested-With"]).join(", "));
|
|
451
|
-
if (opts.exposedHeaders) {
|
|
452
|
-
headers.set("Access-Control-Expose-Headers", opts.exposedHeaders.join(", "));
|
|
453
|
-
}
|
|
454
|
-
if (opts.credentials) {
|
|
455
|
-
headers.set("Access-Control-Allow-Credentials", "true");
|
|
456
|
-
}
|
|
457
|
-
if (opts.maxAge !== undefined) {
|
|
458
|
-
headers.set("Access-Control-Max-Age", opts.maxAge.toString());
|
|
459
|
-
}
|
|
460
|
-
return headers;
|
|
461
|
-
}
|
|
462
|
-
function resolveProcedureFromPath(router, path) {
|
|
463
|
-
const parts = path.split(/[./]/).filter(Boolean);
|
|
464
|
-
let current = router._def.record;
|
|
465
|
-
for (let i = 0;i < parts.length; i++) {
|
|
466
|
-
const part = parts[i];
|
|
467
|
-
if (!current || typeof current !== "object") {
|
|
468
|
-
throw new RPCError({
|
|
469
|
-
code: "NOT_FOUND",
|
|
470
|
-
message: `Procedure '${path}' not found`
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
if (i === parts.length - 1) {
|
|
474
|
-
if (part in current && "type" in current[part]) {
|
|
475
|
-
return current[part];
|
|
476
|
-
}
|
|
477
|
-
throw new RPCError({
|
|
478
|
-
code: "NOT_FOUND",
|
|
479
|
-
message: `Procedure '${path}' not found`
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
if (part in current && "_def" in current[part]) {
|
|
483
|
-
current = current[part]._def.record;
|
|
484
|
-
} else if (part in current && typeof current[part] === "object") {
|
|
485
|
-
current = current[part];
|
|
486
|
-
} else {
|
|
487
|
-
throw new RPCError({
|
|
488
|
-
code: "NOT_FOUND",
|
|
489
|
-
message: `Procedure '${path}' not found`
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
throw new RPCError({
|
|
494
|
-
code: "NOT_FOUND",
|
|
495
|
-
message: `Procedure '${path}' not found`
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
function matchesRulePattern(pattern, urlPath, procedurePath) {
|
|
499
|
-
if (pattern === "*")
|
|
500
|
-
return true;
|
|
501
|
-
const cleanUrl = urlPath.replace(/^\/+|\/+$/g, "");
|
|
502
|
-
const cleanPattern = pattern.replace(/^\/+|\/+$/g, "");
|
|
503
|
-
const cleanProc = procedurePath.replace(/^\/+|\/+$/g, "");
|
|
504
|
-
if (cleanUrl === cleanPattern || cleanProc === cleanPattern || cleanProc === cleanPattern.replace(/\//g, ".")) {
|
|
505
|
-
return true;
|
|
506
|
-
}
|
|
507
|
-
if (cleanPattern.endsWith("*")) {
|
|
508
|
-
const prefix = cleanPattern.slice(0, -1);
|
|
509
|
-
if (cleanUrl.startsWith(prefix) || cleanProc.startsWith(prefix)) {
|
|
510
|
-
return true;
|
|
511
|
-
}
|
|
512
|
-
const dotPrefix = prefix.replace(/\//g, ".");
|
|
513
|
-
if (cleanProc.startsWith(dotPrefix)) {
|
|
514
|
-
return true;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
try {
|
|
518
|
-
const regexStr = cleanPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
519
|
-
const regex = new RegExp(`^${regexStr}$`);
|
|
520
|
-
return regex.test(cleanUrl) || regex.test(cleanProc);
|
|
521
|
-
} catch {
|
|
522
|
-
return false;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
function createFetchHandler(options) {
|
|
526
|
-
const {
|
|
527
|
-
router,
|
|
528
|
-
endpoint = "",
|
|
529
|
-
createContext,
|
|
530
|
-
onError,
|
|
531
|
-
cors = true,
|
|
532
|
-
securityHeaders = true,
|
|
533
|
-
rules
|
|
534
|
-
} = options;
|
|
535
|
-
return async (request, ...extra) => {
|
|
536
|
-
const corsHeaders = getCORSHeaders(cors, request);
|
|
537
|
-
if (request.method === "OPTIONS") {
|
|
538
|
-
return new Response(null, {
|
|
539
|
-
status: 204,
|
|
540
|
-
headers: corsHeaders
|
|
541
|
-
});
|
|
542
|
-
}
|
|
543
|
-
const baseHeaders = new Headers(corsHeaders);
|
|
544
|
-
if (securityHeaders) {
|
|
545
|
-
if (typeof securityHeaders === "object") {
|
|
546
|
-
for (const [k, v] of Object.entries(securityHeaders)) {
|
|
547
|
-
baseHeaders.set(k, v);
|
|
548
|
-
}
|
|
549
|
-
} else {
|
|
550
|
-
baseHeaders.set("X-Content-Type-Options", "nosniff");
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
const url = new URL(request.url);
|
|
554
|
-
let pathname = url.pathname;
|
|
555
|
-
if (endpoint && pathname.startsWith(endpoint)) {
|
|
556
|
-
pathname = pathname.slice(endpoint.length);
|
|
557
|
-
}
|
|
558
|
-
pathname = pathname.replace(/^\/+|\/+$/g, "");
|
|
559
|
-
const procedurePath = pathname || url.searchParams.get("procedure") || "";
|
|
560
|
-
let ctx = {};
|
|
561
|
-
try {
|
|
562
|
-
if (createContext) {
|
|
563
|
-
ctx = await createContext({ req: request, extra });
|
|
564
|
-
}
|
|
565
|
-
} catch (err) {
|
|
566
|
-
const error = err instanceof RPCError ? err : new RPCError({
|
|
567
|
-
code: "UNAUTHORIZED",
|
|
568
|
-
message: err.message ?? "Context initialization failed",
|
|
569
|
-
cause: err
|
|
570
|
-
});
|
|
571
|
-
onError?.({ error, req: request });
|
|
572
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
573
|
-
return new Response(JSON.stringify({
|
|
574
|
-
error: {
|
|
575
|
-
code: error.code,
|
|
576
|
-
message: error.message,
|
|
577
|
-
issues: error.issues
|
|
578
|
-
}
|
|
579
|
-
}), {
|
|
580
|
-
status: error.status,
|
|
581
|
-
headers: baseHeaders
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
const rulesList = [];
|
|
585
|
-
if (rules) {
|
|
586
|
-
if (Array.isArray(rules)) {
|
|
587
|
-
rulesList.push(...rules);
|
|
588
|
-
} else {
|
|
589
|
-
for (const [match, rule] of Object.entries(rules)) {
|
|
590
|
-
rulesList.push({ match, ...rule });
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
const matchingRules = rulesList.filter((r) => matchesRulePattern(r.match, url.pathname, procedurePath));
|
|
595
|
-
for (const rule of matchingRules) {
|
|
596
|
-
if (rule.requireHeaders) {
|
|
597
|
-
for (const headerName of rule.requireHeaders) {
|
|
598
|
-
if (!request.headers.get(headerName)) {
|
|
599
|
-
const error = new RPCError({
|
|
600
|
-
code: "UNAUTHORIZED",
|
|
601
|
-
message: `Missing required header '${headerName}' for rule '${rule.match}'`
|
|
602
|
-
});
|
|
603
|
-
onError?.({ error, path: procedurePath, req: request });
|
|
604
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
605
|
-
return new Response(JSON.stringify({
|
|
606
|
-
error: {
|
|
607
|
-
code: error.code,
|
|
608
|
-
message: error.message
|
|
609
|
-
}
|
|
610
|
-
}), {
|
|
611
|
-
status: 401,
|
|
612
|
-
headers: baseHeaders
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
if (rule.requireParams) {
|
|
618
|
-
for (const paramName of rule.requireParams) {
|
|
619
|
-
if (!url.searchParams.has(paramName)) {
|
|
620
|
-
const error = new RPCError({
|
|
621
|
-
code: "BAD_REQUEST",
|
|
622
|
-
message: `Missing required query parameter '${paramName}' for rule '${rule.match}'`
|
|
623
|
-
});
|
|
624
|
-
onError?.({ error, path: procedurePath, req: request });
|
|
625
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
626
|
-
return new Response(JSON.stringify({
|
|
627
|
-
error: {
|
|
628
|
-
code: error.code,
|
|
629
|
-
message: error.message
|
|
630
|
-
}
|
|
631
|
-
}), {
|
|
632
|
-
status: 400,
|
|
633
|
-
headers: baseHeaders
|
|
634
|
-
});
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
if (rule.guard) {
|
|
639
|
-
try {
|
|
640
|
-
const guardResult = await rule.guard({ req: request, ctx, path: procedurePath });
|
|
641
|
-
if (guardResult === false) {
|
|
642
|
-
throw new RPCError({
|
|
643
|
-
code: "UNAUTHORIZED",
|
|
644
|
-
message: `Access denied by route rule guard for '${rule.match}'`
|
|
645
|
-
});
|
|
646
|
-
}
|
|
647
|
-
} catch (err) {
|
|
648
|
-
const error = err instanceof RPCError ? err : new RPCError({
|
|
649
|
-
code: "UNAUTHORIZED",
|
|
650
|
-
message: err.message ?? "Unauthorized",
|
|
651
|
-
cause: err
|
|
652
|
-
});
|
|
653
|
-
onError?.({ error, path: procedurePath, req: request });
|
|
654
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
655
|
-
return new Response(JSON.stringify({
|
|
656
|
-
error: {
|
|
657
|
-
code: error.code,
|
|
658
|
-
message: error.message
|
|
659
|
-
}
|
|
660
|
-
}), {
|
|
661
|
-
status: error.status,
|
|
662
|
-
headers: baseHeaders
|
|
663
|
-
});
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
if (rule.headers) {
|
|
667
|
-
const resHeaders = typeof rule.headers === "function" ? await rule.headers(request, ctx) : rule.headers;
|
|
668
|
-
for (const [k, v] of Object.entries(resHeaders)) {
|
|
669
|
-
baseHeaders.set(k, v);
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
if (request.method === "POST" && (!pathname || pathname === "batch")) {
|
|
674
|
-
try {
|
|
675
|
-
const body = await request.json();
|
|
676
|
-
if (Array.isArray(body)) {
|
|
677
|
-
const batchResults = await Promise.all(body.map(async (item) => {
|
|
678
|
-
try {
|
|
679
|
-
const proc = resolveProcedureFromPath(router, item.path);
|
|
680
|
-
const data = await executeProcedure(proc, {
|
|
681
|
-
rawInput: item.input,
|
|
682
|
-
ctx,
|
|
683
|
-
req: request,
|
|
684
|
-
signal: request.signal
|
|
685
|
-
});
|
|
686
|
-
return {
|
|
687
|
-
id: item.id,
|
|
688
|
-
result: { data }
|
|
689
|
-
};
|
|
690
|
-
} catch (err) {
|
|
691
|
-
const error = err instanceof RPCError ? err : new RPCError({
|
|
692
|
-
code: "INTERNAL_SERVER_ERROR",
|
|
693
|
-
message: err.message ?? "Internal Server Error",
|
|
694
|
-
cause: err
|
|
695
|
-
});
|
|
696
|
-
onError?.({ error, path: item.path, req: request });
|
|
697
|
-
return {
|
|
698
|
-
id: item.id,
|
|
699
|
-
error: {
|
|
700
|
-
code: error.code,
|
|
701
|
-
message: error.message,
|
|
702
|
-
issues: error.issues
|
|
703
|
-
}
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
|
-
}));
|
|
707
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
708
|
-
return new Response(JSON.stringify(batchResults), {
|
|
709
|
-
status: 200,
|
|
710
|
-
headers: baseHeaders
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
|
-
} catch (err) {
|
|
714
|
-
const error = new RPCError({
|
|
715
|
-
code: "BAD_REQUEST",
|
|
716
|
-
message: "Malformed batch request body",
|
|
717
|
-
cause: err
|
|
718
|
-
});
|
|
719
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
720
|
-
return new Response(JSON.stringify({
|
|
721
|
-
error: {
|
|
722
|
-
code: error.code,
|
|
723
|
-
message: error.message
|
|
724
|
-
}
|
|
725
|
-
}), {
|
|
726
|
-
status: 400,
|
|
727
|
-
headers: baseHeaders
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
try {
|
|
732
|
-
const procedure = resolveProcedureFromPath(router, procedurePath);
|
|
733
|
-
if (procedure.type === "mutation" && request.method !== "POST") {
|
|
734
|
-
throw new RPCError({
|
|
735
|
-
code: "METHOD_NOT_ALLOWED",
|
|
736
|
-
message: "Mutations must use POST method"
|
|
737
|
-
});
|
|
738
|
-
}
|
|
739
|
-
let rawInput;
|
|
740
|
-
if (request.method === "GET") {
|
|
741
|
-
const inputParam = url.searchParams.get("input");
|
|
742
|
-
if (inputParam !== null) {
|
|
743
|
-
try {
|
|
744
|
-
rawInput = JSON.parse(inputParam);
|
|
745
|
-
} catch {
|
|
746
|
-
rawInput = inputParam;
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
} else if (request.method === "POST") {
|
|
750
|
-
try {
|
|
751
|
-
const body = await request.json();
|
|
752
|
-
rawInput = body && typeof body === "object" && "input" in body ? body.input : body;
|
|
753
|
-
} catch {
|
|
754
|
-
rawInput = undefined;
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
if (procedure.type === "subscription") {
|
|
758
|
-
const iterable = await executeProcedure(procedure, {
|
|
759
|
-
rawInput,
|
|
760
|
-
ctx,
|
|
761
|
-
req: request,
|
|
762
|
-
signal: request.signal
|
|
763
|
-
});
|
|
764
|
-
if (!iterable || typeof iterable[Symbol.asyncIterator] !== "function") {
|
|
765
|
-
throw new RPCError({
|
|
766
|
-
code: "INTERNAL_SERVER_ERROR",
|
|
767
|
-
message: "Subscription procedure must return an AsyncIterable"
|
|
768
|
-
});
|
|
769
|
-
}
|
|
770
|
-
const encoder = new TextEncoder;
|
|
771
|
-
const stream = new ReadableStream({
|
|
772
|
-
async start(controller) {
|
|
773
|
-
try {
|
|
774
|
-
for await (const chunk of iterable) {
|
|
775
|
-
if (request.signal.aborted)
|
|
776
|
-
break;
|
|
777
|
-
const message = `data: ${JSON.stringify(chunk)}
|
|
778
|
-
|
|
779
|
-
`;
|
|
780
|
-
controller.enqueue(encoder.encode(message));
|
|
781
|
-
}
|
|
782
|
-
controller.close();
|
|
783
|
-
} catch (err) {
|
|
784
|
-
const errorMessage = `event: error
|
|
785
|
-
data: ${JSON.stringify({ message: err.message ?? "Stream error" })}
|
|
786
|
-
|
|
787
|
-
`;
|
|
788
|
-
controller.enqueue(encoder.encode(errorMessage));
|
|
789
|
-
controller.close();
|
|
790
|
-
}
|
|
791
|
-
},
|
|
792
|
-
cancel() {
|
|
793
|
-
if (typeof iterable.return === "function") {
|
|
794
|
-
iterable.return();
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
});
|
|
798
|
-
baseHeaders.set("Content-Type", "text/event-stream; charset=utf-8");
|
|
799
|
-
baseHeaders.set("Cache-Control", "no-cache, no-transform");
|
|
800
|
-
baseHeaders.set("Connection", "keep-alive");
|
|
801
|
-
return new Response(stream, {
|
|
802
|
-
status: 200,
|
|
803
|
-
headers: baseHeaders
|
|
804
|
-
});
|
|
805
|
-
}
|
|
806
|
-
const data = await executeProcedure(procedure, {
|
|
807
|
-
rawInput,
|
|
808
|
-
ctx,
|
|
809
|
-
req: request,
|
|
810
|
-
signal: request.signal
|
|
811
|
-
});
|
|
812
|
-
const responsePayload = {
|
|
813
|
-
result: { data }
|
|
814
|
-
};
|
|
815
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
816
|
-
return new Response(JSON.stringify(responsePayload), {
|
|
817
|
-
status: 200,
|
|
818
|
-
headers: baseHeaders
|
|
819
|
-
});
|
|
820
|
-
} catch (err) {
|
|
821
|
-
const error = err instanceof RPCError ? err : new RPCError({
|
|
822
|
-
code: "INTERNAL_SERVER_ERROR",
|
|
823
|
-
message: err.message ?? "Internal Server Error",
|
|
824
|
-
cause: err
|
|
825
|
-
});
|
|
826
|
-
onError?.({ error, path: procedurePath, req: request });
|
|
827
|
-
const responsePayload = {
|
|
828
|
-
error: {
|
|
829
|
-
code: error.code,
|
|
830
|
-
message: error.message,
|
|
831
|
-
issues: error.issues
|
|
832
|
-
}
|
|
833
|
-
};
|
|
834
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
835
|
-
return new Response(JSON.stringify(responsePayload), {
|
|
836
|
-
status: ERROR_CODE_TO_STATUS[error.code] ?? 500,
|
|
837
|
-
headers: baseHeaders
|
|
838
|
-
});
|
|
839
|
-
}
|
|
840
|
-
};
|
|
841
|
-
}
|
|
842
|
-
|
|
843
104
|
// src/vite/rpc-middleware.ts
|
|
844
105
|
function createViteRpcMiddleware(options) {
|
|
845
106
|
const endpoint = options.endpoint ?? "/api/rpc";
|
|
846
|
-
const handler =
|
|
107
|
+
const handler = createFetchHandler2({
|
|
847
108
|
router: options.router,
|
|
848
109
|
endpoint,
|
|
849
110
|
cors: true
|