@epoch32/visage 0.0.0 → 1.0.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/package.json +1 -1
- package/dist/bundle.dev.js +0 -4875
- package/dist/index.js +0 -9633
- package/dist/vite/index.js +0 -707
package/dist/vite/index.js
DELETED
|
@@ -1,707 +0,0 @@
|
|
|
1
|
-
// src/vite/hmr.ts
|
|
2
|
-
function transformHmr(code, id) {
|
|
3
|
-
if (!/\.[jt]sx$/.test(id) || id.includes("node_modules")) {
|
|
4
|
-
return null;
|
|
5
|
-
}
|
|
6
|
-
const hasExports = /\bexport\s+(?:default\s+)?(?:function|const|class)\s+([A-Z][A-Za-z0-9_]*)/.test(code) || /\bexport\s+\{\s*([A-Za-z0-9_,\s]+)\s*\}/.test(code);
|
|
7
|
-
if (!hasExports) {
|
|
8
|
-
return null;
|
|
9
|
-
}
|
|
10
|
-
const hmrFooter = `
|
|
11
|
-
/* @visage/vite-hmr */
|
|
12
|
-
if (import.meta.hot) {
|
|
13
|
-
import.meta.hot.accept((newMod) => {
|
|
14
|
-
if (newMod) {
|
|
15
|
-
// Re-trigger reactive components without wiping signal stores
|
|
16
|
-
if (typeof window !== "undefined" && (window as any).__VISAGE_DEVTOOLS__) {
|
|
17
|
-
(window as any).__VISAGE_DEVTOOLS__.refresh?.();
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
`;
|
|
23
|
-
return {
|
|
24
|
-
code: `${code}
|
|
25
|
-
${hmrFooter}`,
|
|
26
|
-
map: null
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// src/rpc/error.ts
|
|
31
|
-
var ERROR_CODE_TO_STATUS = {
|
|
32
|
-
BAD_REQUEST: 400,
|
|
33
|
-
UNAUTHORIZED: 401,
|
|
34
|
-
FORBIDDEN: 403,
|
|
35
|
-
NOT_FOUND: 404,
|
|
36
|
-
METHOD_NOT_ALLOWED: 405,
|
|
37
|
-
TIMEOUT: 408,
|
|
38
|
-
CONFLICT: 409,
|
|
39
|
-
UNPROCESSABLE_CONTENT: 422,
|
|
40
|
-
INTERNAL_SERVER_ERROR: 500
|
|
41
|
-
};
|
|
42
|
-
class RPCError extends Error {
|
|
43
|
-
code;
|
|
44
|
-
status;
|
|
45
|
-
issues;
|
|
46
|
-
constructor(options) {
|
|
47
|
-
super(options.message ?? options.code, { cause: options.cause });
|
|
48
|
-
this.name = "RPCError";
|
|
49
|
-
this.code = options.code;
|
|
50
|
-
this.status = ERROR_CODE_TO_STATUS[options.code] ?? 500;
|
|
51
|
-
this.issues = options.issues;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// src/form/schema.ts
|
|
56
|
-
class Schema {
|
|
57
|
-
"~standard" = {
|
|
58
|
-
version: 1,
|
|
59
|
-
vendor: "visage",
|
|
60
|
-
validate: (val) => {
|
|
61
|
-
const res = this.validate(val);
|
|
62
|
-
if (res.success) {
|
|
63
|
-
return { value: res.data };
|
|
64
|
-
}
|
|
65
|
-
return {
|
|
66
|
-
issues: (res.issues || []).map((i) => ({
|
|
67
|
-
path: [i.path],
|
|
68
|
-
message: i.message
|
|
69
|
-
}))
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
function validateWithSchema(schema, value, path = "") {
|
|
75
|
-
if (!schema)
|
|
76
|
-
return { success: true, data: value };
|
|
77
|
-
if (schema instanceof Schema) {
|
|
78
|
-
return schema.validate(value, path);
|
|
79
|
-
}
|
|
80
|
-
if (typeof schema === "object" && "~standard" in schema) {
|
|
81
|
-
const res = schema["~standard"].validate(value);
|
|
82
|
-
if ("value" in res) {
|
|
83
|
-
return { success: true, data: res.value };
|
|
84
|
-
}
|
|
85
|
-
return {
|
|
86
|
-
success: false,
|
|
87
|
-
issues: res.issues.map((i) => ({
|
|
88
|
-
path: i.path ? i.path.join(".") : path,
|
|
89
|
-
message: i.message
|
|
90
|
-
}))
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
if (typeof schema === "function") {
|
|
94
|
-
const res = schema(value);
|
|
95
|
-
if (res === true || res === null || res === undefined) {
|
|
96
|
-
return { success: true, data: value };
|
|
97
|
-
}
|
|
98
|
-
if (typeof res === "string") {
|
|
99
|
-
return { success: false, issues: [{ path, message: res }] };
|
|
100
|
-
}
|
|
101
|
-
if (typeof res === "object") {
|
|
102
|
-
return res;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return { success: true, data: value };
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// src/rpc/procedure.ts
|
|
109
|
-
async function executeProcedure(procedure, opts) {
|
|
110
|
-
let input = opts.rawInput;
|
|
111
|
-
if (procedure.inputSchema) {
|
|
112
|
-
const res = validateWithSchema(procedure.inputSchema, opts.rawInput);
|
|
113
|
-
if (!res.success) {
|
|
114
|
-
throw new RPCError({
|
|
115
|
-
code: "BAD_REQUEST",
|
|
116
|
-
message: "Invalid procedure input",
|
|
117
|
-
issues: res.issues
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
input = res.data;
|
|
121
|
-
}
|
|
122
|
-
let currentIndex = 0;
|
|
123
|
-
let currentContext = opts.ctx;
|
|
124
|
-
const runPipeline = async (ctx) => {
|
|
125
|
-
currentContext = ctx;
|
|
126
|
-
if (currentIndex < procedure.middlewares.length) {
|
|
127
|
-
const mw = procedure.middlewares[currentIndex++];
|
|
128
|
-
return mw({
|
|
129
|
-
ctx: currentContext,
|
|
130
|
-
req: opts.req,
|
|
131
|
-
signal: opts.signal,
|
|
132
|
-
next: async (nextOpts) => {
|
|
133
|
-
return runPipeline(nextOpts?.ctx ?? currentContext);
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
return procedure.resolve({
|
|
138
|
-
input,
|
|
139
|
-
ctx: currentContext,
|
|
140
|
-
req: opts.req,
|
|
141
|
-
signal: opts.signal
|
|
142
|
-
});
|
|
143
|
-
};
|
|
144
|
-
const output = await runPipeline(currentContext);
|
|
145
|
-
if (procedure.outputSchema && procedure.type !== "subscription") {
|
|
146
|
-
const res = validateWithSchema(procedure.outputSchema, output);
|
|
147
|
-
if (!res.success) {
|
|
148
|
-
throw new RPCError({
|
|
149
|
-
code: "INTERNAL_SERVER_ERROR",
|
|
150
|
-
message: "Invalid procedure output",
|
|
151
|
-
issues: res.issues
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
return res.data;
|
|
155
|
-
}
|
|
156
|
-
return output;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// src/rpc/server.ts
|
|
160
|
-
function getCORSHeaders(cors, req) {
|
|
161
|
-
const headers = new Headers;
|
|
162
|
-
if (!cors)
|
|
163
|
-
return headers;
|
|
164
|
-
const opts = typeof cors === "object" ? cors : {};
|
|
165
|
-
const originHeader = req.headers.get("origin") ?? "*";
|
|
166
|
-
let allowOrigin = "*";
|
|
167
|
-
if (typeof opts.origin === "function") {
|
|
168
|
-
const res = opts.origin(originHeader);
|
|
169
|
-
if (typeof res === "string")
|
|
170
|
-
allowOrigin = res;
|
|
171
|
-
else if (res)
|
|
172
|
-
allowOrigin = originHeader;
|
|
173
|
-
} else if (typeof opts.origin === "string") {
|
|
174
|
-
allowOrigin = opts.origin;
|
|
175
|
-
} else if (Array.isArray(opts.origin)) {
|
|
176
|
-
if (opts.origin.includes(originHeader))
|
|
177
|
-
allowOrigin = originHeader;
|
|
178
|
-
} else {
|
|
179
|
-
allowOrigin = originHeader || "*";
|
|
180
|
-
}
|
|
181
|
-
headers.set("Access-Control-Allow-Origin", allowOrigin);
|
|
182
|
-
headers.set("Access-Control-Allow-Methods", (opts.methods ?? ["GET", "POST", "OPTIONS", "HEAD"]).join(", "));
|
|
183
|
-
headers.set("Access-Control-Allow-Headers", (opts.allowedHeaders ?? ["Content-Type", "Authorization", "X-Requested-With"]).join(", "));
|
|
184
|
-
if (opts.exposedHeaders) {
|
|
185
|
-
headers.set("Access-Control-Expose-Headers", opts.exposedHeaders.join(", "));
|
|
186
|
-
}
|
|
187
|
-
if (opts.credentials) {
|
|
188
|
-
headers.set("Access-Control-Allow-Credentials", "true");
|
|
189
|
-
}
|
|
190
|
-
if (opts.maxAge !== undefined) {
|
|
191
|
-
headers.set("Access-Control-Max-Age", opts.maxAge.toString());
|
|
192
|
-
}
|
|
193
|
-
return headers;
|
|
194
|
-
}
|
|
195
|
-
function resolveProcedureFromPath(router, path) {
|
|
196
|
-
const parts = path.split(/[./]/).filter(Boolean);
|
|
197
|
-
let current = router._def.record;
|
|
198
|
-
for (let i = 0;i < parts.length; i++) {
|
|
199
|
-
const part = parts[i];
|
|
200
|
-
if (!current || typeof current !== "object") {
|
|
201
|
-
throw new RPCError({
|
|
202
|
-
code: "NOT_FOUND",
|
|
203
|
-
message: `Procedure '${path}' not found`
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
if (i === parts.length - 1) {
|
|
207
|
-
if (part in current && "type" in current[part]) {
|
|
208
|
-
return current[part];
|
|
209
|
-
}
|
|
210
|
-
throw new RPCError({
|
|
211
|
-
code: "NOT_FOUND",
|
|
212
|
-
message: `Procedure '${path}' not found`
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
if (part in current && "_def" in current[part]) {
|
|
216
|
-
current = current[part]._def.record;
|
|
217
|
-
} else if (part in current && typeof current[part] === "object") {
|
|
218
|
-
current = current[part];
|
|
219
|
-
} else {
|
|
220
|
-
throw new RPCError({
|
|
221
|
-
code: "NOT_FOUND",
|
|
222
|
-
message: `Procedure '${path}' not found`
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
throw new RPCError({
|
|
227
|
-
code: "NOT_FOUND",
|
|
228
|
-
message: `Procedure '${path}' not found`
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
function matchesRulePattern(pattern, urlPath, procedurePath) {
|
|
232
|
-
if (pattern === "*")
|
|
233
|
-
return true;
|
|
234
|
-
const cleanUrl = urlPath.replace(/^\/+|\/+$/g, "");
|
|
235
|
-
const cleanPattern = pattern.replace(/^\/+|\/+$/g, "");
|
|
236
|
-
const cleanProc = procedurePath.replace(/^\/+|\/+$/g, "");
|
|
237
|
-
if (cleanUrl === cleanPattern || cleanProc === cleanPattern || cleanProc === cleanPattern.replace(/\//g, ".")) {
|
|
238
|
-
return true;
|
|
239
|
-
}
|
|
240
|
-
if (cleanPattern.endsWith("*")) {
|
|
241
|
-
const prefix = cleanPattern.slice(0, -1);
|
|
242
|
-
if (cleanUrl.startsWith(prefix) || cleanProc.startsWith(prefix)) {
|
|
243
|
-
return true;
|
|
244
|
-
}
|
|
245
|
-
const dotPrefix = prefix.replace(/\//g, ".");
|
|
246
|
-
if (cleanProc.startsWith(dotPrefix)) {
|
|
247
|
-
return true;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
try {
|
|
251
|
-
const regexStr = cleanPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
252
|
-
const regex = new RegExp(`^${regexStr}$`);
|
|
253
|
-
return regex.test(cleanUrl) || regex.test(cleanProc);
|
|
254
|
-
} catch {
|
|
255
|
-
return false;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
function createFetchHandler(options) {
|
|
259
|
-
const {
|
|
260
|
-
router,
|
|
261
|
-
endpoint = "",
|
|
262
|
-
createContext,
|
|
263
|
-
onError,
|
|
264
|
-
cors = true,
|
|
265
|
-
securityHeaders = true,
|
|
266
|
-
rules
|
|
267
|
-
} = options;
|
|
268
|
-
return async (request, ...extra) => {
|
|
269
|
-
const corsHeaders = getCORSHeaders(cors, request);
|
|
270
|
-
if (request.method === "OPTIONS") {
|
|
271
|
-
return new Response(null, {
|
|
272
|
-
status: 204,
|
|
273
|
-
headers: corsHeaders
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
const baseHeaders = new Headers(corsHeaders);
|
|
277
|
-
if (securityHeaders) {
|
|
278
|
-
if (typeof securityHeaders === "object") {
|
|
279
|
-
for (const [k, v] of Object.entries(securityHeaders)) {
|
|
280
|
-
baseHeaders.set(k, v);
|
|
281
|
-
}
|
|
282
|
-
} else {
|
|
283
|
-
baseHeaders.set("X-Content-Type-Options", "nosniff");
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
const url = new URL(request.url);
|
|
287
|
-
let pathname = url.pathname;
|
|
288
|
-
if (endpoint && pathname.startsWith(endpoint)) {
|
|
289
|
-
pathname = pathname.slice(endpoint.length);
|
|
290
|
-
}
|
|
291
|
-
pathname = pathname.replace(/^\/+|\/+$/g, "");
|
|
292
|
-
const procedurePath = pathname || url.searchParams.get("procedure") || "";
|
|
293
|
-
let ctx = {};
|
|
294
|
-
try {
|
|
295
|
-
if (createContext) {
|
|
296
|
-
ctx = await createContext({ req: request, extra });
|
|
297
|
-
}
|
|
298
|
-
} catch (err) {
|
|
299
|
-
const error = err instanceof RPCError ? err : new RPCError({
|
|
300
|
-
code: "UNAUTHORIZED",
|
|
301
|
-
message: err.message ?? "Context initialization failed",
|
|
302
|
-
cause: err
|
|
303
|
-
});
|
|
304
|
-
onError?.({ error, req: request });
|
|
305
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
306
|
-
return new Response(JSON.stringify({
|
|
307
|
-
error: {
|
|
308
|
-
code: error.code,
|
|
309
|
-
message: error.message,
|
|
310
|
-
issues: error.issues
|
|
311
|
-
}
|
|
312
|
-
}), {
|
|
313
|
-
status: error.status,
|
|
314
|
-
headers: baseHeaders
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
const rulesList = [];
|
|
318
|
-
if (rules) {
|
|
319
|
-
if (Array.isArray(rules)) {
|
|
320
|
-
rulesList.push(...rules);
|
|
321
|
-
} else {
|
|
322
|
-
for (const [match, rule] of Object.entries(rules)) {
|
|
323
|
-
rulesList.push({ match, ...rule });
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
const matchingRules = rulesList.filter((r) => matchesRulePattern(r.match, url.pathname, procedurePath));
|
|
328
|
-
for (const rule of matchingRules) {
|
|
329
|
-
if (rule.requireHeaders) {
|
|
330
|
-
for (const headerName of rule.requireHeaders) {
|
|
331
|
-
if (!request.headers.get(headerName)) {
|
|
332
|
-
const error = new RPCError({
|
|
333
|
-
code: "UNAUTHORIZED",
|
|
334
|
-
message: `Missing required header '${headerName}' for rule '${rule.match}'`
|
|
335
|
-
});
|
|
336
|
-
onError?.({ error, path: procedurePath, req: request });
|
|
337
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
338
|
-
return new Response(JSON.stringify({
|
|
339
|
-
error: {
|
|
340
|
-
code: error.code,
|
|
341
|
-
message: error.message
|
|
342
|
-
}
|
|
343
|
-
}), {
|
|
344
|
-
status: 401,
|
|
345
|
-
headers: baseHeaders
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
if (rule.requireParams) {
|
|
351
|
-
for (const paramName of rule.requireParams) {
|
|
352
|
-
if (!url.searchParams.has(paramName)) {
|
|
353
|
-
const error = new RPCError({
|
|
354
|
-
code: "BAD_REQUEST",
|
|
355
|
-
message: `Missing required query parameter '${paramName}' for rule '${rule.match}'`
|
|
356
|
-
});
|
|
357
|
-
onError?.({ error, path: procedurePath, req: request });
|
|
358
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
359
|
-
return new Response(JSON.stringify({
|
|
360
|
-
error: {
|
|
361
|
-
code: error.code,
|
|
362
|
-
message: error.message
|
|
363
|
-
}
|
|
364
|
-
}), {
|
|
365
|
-
status: 400,
|
|
366
|
-
headers: baseHeaders
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
if (rule.guard) {
|
|
372
|
-
try {
|
|
373
|
-
const guardResult = await rule.guard({ req: request, ctx, path: procedurePath });
|
|
374
|
-
if (guardResult === false) {
|
|
375
|
-
throw new RPCError({
|
|
376
|
-
code: "UNAUTHORIZED",
|
|
377
|
-
message: `Access denied by route rule guard for '${rule.match}'`
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
} catch (err) {
|
|
381
|
-
const error = err instanceof RPCError ? err : new RPCError({
|
|
382
|
-
code: "UNAUTHORIZED",
|
|
383
|
-
message: err.message ?? "Unauthorized",
|
|
384
|
-
cause: err
|
|
385
|
-
});
|
|
386
|
-
onError?.({ error, path: procedurePath, req: request });
|
|
387
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
388
|
-
return new Response(JSON.stringify({
|
|
389
|
-
error: {
|
|
390
|
-
code: error.code,
|
|
391
|
-
message: error.message
|
|
392
|
-
}
|
|
393
|
-
}), {
|
|
394
|
-
status: error.status,
|
|
395
|
-
headers: baseHeaders
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
if (rule.headers) {
|
|
400
|
-
const resHeaders = typeof rule.headers === "function" ? await rule.headers(request, ctx) : rule.headers;
|
|
401
|
-
for (const [k, v] of Object.entries(resHeaders)) {
|
|
402
|
-
baseHeaders.set(k, v);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
if (request.method === "POST" && (!pathname || pathname === "batch")) {
|
|
407
|
-
try {
|
|
408
|
-
const body = await request.json();
|
|
409
|
-
if (Array.isArray(body)) {
|
|
410
|
-
const batchResults = await Promise.all(body.map(async (item) => {
|
|
411
|
-
try {
|
|
412
|
-
const proc = resolveProcedureFromPath(router, item.path);
|
|
413
|
-
const data = await executeProcedure(proc, {
|
|
414
|
-
rawInput: item.input,
|
|
415
|
-
ctx,
|
|
416
|
-
req: request,
|
|
417
|
-
signal: request.signal
|
|
418
|
-
});
|
|
419
|
-
return {
|
|
420
|
-
id: item.id,
|
|
421
|
-
result: { data }
|
|
422
|
-
};
|
|
423
|
-
} catch (err) {
|
|
424
|
-
const error = err instanceof RPCError ? err : new RPCError({
|
|
425
|
-
code: "INTERNAL_SERVER_ERROR",
|
|
426
|
-
message: err.message ?? "Internal Server Error",
|
|
427
|
-
cause: err
|
|
428
|
-
});
|
|
429
|
-
onError?.({ error, path: item.path, req: request });
|
|
430
|
-
return {
|
|
431
|
-
id: item.id,
|
|
432
|
-
error: {
|
|
433
|
-
code: error.code,
|
|
434
|
-
message: error.message,
|
|
435
|
-
issues: error.issues
|
|
436
|
-
}
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
}));
|
|
440
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
441
|
-
return new Response(JSON.stringify(batchResults), {
|
|
442
|
-
status: 200,
|
|
443
|
-
headers: baseHeaders
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
} catch (err) {
|
|
447
|
-
const error = new RPCError({
|
|
448
|
-
code: "BAD_REQUEST",
|
|
449
|
-
message: "Malformed batch request body",
|
|
450
|
-
cause: err
|
|
451
|
-
});
|
|
452
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
453
|
-
return new Response(JSON.stringify({
|
|
454
|
-
error: {
|
|
455
|
-
code: error.code,
|
|
456
|
-
message: error.message
|
|
457
|
-
}
|
|
458
|
-
}), {
|
|
459
|
-
status: 400,
|
|
460
|
-
headers: baseHeaders
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
try {
|
|
465
|
-
const procedure = resolveProcedureFromPath(router, procedurePath);
|
|
466
|
-
if (procedure.type === "mutation" && request.method !== "POST") {
|
|
467
|
-
throw new RPCError({
|
|
468
|
-
code: "METHOD_NOT_ALLOWED",
|
|
469
|
-
message: "Mutations must use POST method"
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
let rawInput;
|
|
473
|
-
if (request.method === "GET") {
|
|
474
|
-
const inputParam = url.searchParams.get("input");
|
|
475
|
-
if (inputParam !== null) {
|
|
476
|
-
try {
|
|
477
|
-
rawInput = JSON.parse(inputParam);
|
|
478
|
-
} catch {
|
|
479
|
-
rawInput = inputParam;
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
} else if (request.method === "POST") {
|
|
483
|
-
try {
|
|
484
|
-
const body = await request.json();
|
|
485
|
-
rawInput = body && typeof body === "object" && "input" in body ? body.input : body;
|
|
486
|
-
} catch {
|
|
487
|
-
rawInput = undefined;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
if (procedure.type === "subscription") {
|
|
491
|
-
const iterable = await executeProcedure(procedure, {
|
|
492
|
-
rawInput,
|
|
493
|
-
ctx,
|
|
494
|
-
req: request,
|
|
495
|
-
signal: request.signal
|
|
496
|
-
});
|
|
497
|
-
if (!iterable || typeof iterable[Symbol.asyncIterator] !== "function") {
|
|
498
|
-
throw new RPCError({
|
|
499
|
-
code: "INTERNAL_SERVER_ERROR",
|
|
500
|
-
message: "Subscription procedure must return an AsyncIterable"
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
const encoder = new TextEncoder;
|
|
504
|
-
const stream = new ReadableStream({
|
|
505
|
-
async start(controller) {
|
|
506
|
-
try {
|
|
507
|
-
for await (const chunk of iterable) {
|
|
508
|
-
if (request.signal.aborted)
|
|
509
|
-
break;
|
|
510
|
-
const message = `data: ${JSON.stringify(chunk)}
|
|
511
|
-
|
|
512
|
-
`;
|
|
513
|
-
controller.enqueue(encoder.encode(message));
|
|
514
|
-
}
|
|
515
|
-
controller.close();
|
|
516
|
-
} catch (err) {
|
|
517
|
-
const errorMessage = `event: error
|
|
518
|
-
data: ${JSON.stringify({ message: err.message ?? "Stream error" })}
|
|
519
|
-
|
|
520
|
-
`;
|
|
521
|
-
controller.enqueue(encoder.encode(errorMessage));
|
|
522
|
-
controller.close();
|
|
523
|
-
}
|
|
524
|
-
},
|
|
525
|
-
cancel() {
|
|
526
|
-
if (typeof iterable.return === "function") {
|
|
527
|
-
iterable.return();
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
});
|
|
531
|
-
baseHeaders.set("Content-Type", "text/event-stream; charset=utf-8");
|
|
532
|
-
baseHeaders.set("Cache-Control", "no-cache, no-transform");
|
|
533
|
-
baseHeaders.set("Connection", "keep-alive");
|
|
534
|
-
return new Response(stream, {
|
|
535
|
-
status: 200,
|
|
536
|
-
headers: baseHeaders
|
|
537
|
-
});
|
|
538
|
-
}
|
|
539
|
-
const data = await executeProcedure(procedure, {
|
|
540
|
-
rawInput,
|
|
541
|
-
ctx,
|
|
542
|
-
req: request,
|
|
543
|
-
signal: request.signal
|
|
544
|
-
});
|
|
545
|
-
const responsePayload = {
|
|
546
|
-
result: { data }
|
|
547
|
-
};
|
|
548
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
549
|
-
return new Response(JSON.stringify(responsePayload), {
|
|
550
|
-
status: 200,
|
|
551
|
-
headers: baseHeaders
|
|
552
|
-
});
|
|
553
|
-
} catch (err) {
|
|
554
|
-
const error = err instanceof RPCError ? err : new RPCError({
|
|
555
|
-
code: "INTERNAL_SERVER_ERROR",
|
|
556
|
-
message: err.message ?? "Internal Server Error",
|
|
557
|
-
cause: err
|
|
558
|
-
});
|
|
559
|
-
onError?.({ error, path: procedurePath, req: request });
|
|
560
|
-
const responsePayload = {
|
|
561
|
-
error: {
|
|
562
|
-
code: error.code,
|
|
563
|
-
message: error.message,
|
|
564
|
-
issues: error.issues
|
|
565
|
-
}
|
|
566
|
-
};
|
|
567
|
-
baseHeaders.set("Content-Type", "application/json");
|
|
568
|
-
return new Response(JSON.stringify(responsePayload), {
|
|
569
|
-
status: ERROR_CODE_TO_STATUS[error.code] ?? 500,
|
|
570
|
-
headers: baseHeaders
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// src/vite/rpc-middleware.ts
|
|
577
|
-
function createViteRpcMiddleware(options) {
|
|
578
|
-
const endpoint = options.endpoint ?? "/api/rpc";
|
|
579
|
-
const handler = createFetchHandler({
|
|
580
|
-
router: options.router,
|
|
581
|
-
endpoint,
|
|
582
|
-
cors: true
|
|
583
|
-
});
|
|
584
|
-
return async (req, res, next) => {
|
|
585
|
-
const url = req.url || "";
|
|
586
|
-
if (!url.startsWith(endpoint)) {
|
|
587
|
-
return next();
|
|
588
|
-
}
|
|
589
|
-
try {
|
|
590
|
-
const chunks = [];
|
|
591
|
-
for await (const chunk of req) {
|
|
592
|
-
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
593
|
-
}
|
|
594
|
-
const rawBody = chunks.length > 0 ? Buffer.concat(chunks).toString("utf-8") : undefined;
|
|
595
|
-
const protocol = req.headers["x-forwarded-proto"] || "http";
|
|
596
|
-
const host = req.headers.host || "localhost";
|
|
597
|
-
const fullUrl = `${protocol}://${host}${url}`;
|
|
598
|
-
const headers = new Headers;
|
|
599
|
-
for (const [k, v] of Object.entries(req.headers)) {
|
|
600
|
-
if (v !== undefined) {
|
|
601
|
-
if (Array.isArray(v)) {
|
|
602
|
-
for (const item of v)
|
|
603
|
-
headers.append(k, item);
|
|
604
|
-
} else {
|
|
605
|
-
headers.set(k, String(v));
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
const webReq = new Request(fullUrl, {
|
|
610
|
-
method: req.method,
|
|
611
|
-
headers,
|
|
612
|
-
body: req.method !== "GET" && req.method !== "HEAD" ? rawBody : undefined
|
|
613
|
-
});
|
|
614
|
-
const webRes = await handler(webReq);
|
|
615
|
-
res.statusCode = webRes.status;
|
|
616
|
-
webRes.headers.forEach((val, key) => {
|
|
617
|
-
res.setHeader(key, val);
|
|
618
|
-
});
|
|
619
|
-
const isStream = webRes.headers.get("content-type")?.includes("text/event-stream");
|
|
620
|
-
if (isStream && webRes.body) {
|
|
621
|
-
const reader = webRes.body.getReader();
|
|
622
|
-
while (true) {
|
|
623
|
-
const { done, value } = await reader.read();
|
|
624
|
-
if (done)
|
|
625
|
-
break;
|
|
626
|
-
res.write(value);
|
|
627
|
-
}
|
|
628
|
-
res.end();
|
|
629
|
-
} else {
|
|
630
|
-
const text = await webRes.text();
|
|
631
|
-
res.end(text);
|
|
632
|
-
}
|
|
633
|
-
} catch (err) {
|
|
634
|
-
res.statusCode = 500;
|
|
635
|
-
res.setHeader("Content-Type", "application/json");
|
|
636
|
-
res.end(JSON.stringify({ error: err?.message || "Internal Server Error" }));
|
|
637
|
-
}
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
// src/vite/plugin.ts
|
|
642
|
-
function visage(options = {}) {
|
|
643
|
-
const isDev = typeof process !== "undefined" ? process.env?.NODE_ENV !== "production" : true;
|
|
644
|
-
const configPlugin = {
|
|
645
|
-
name: "visage:config",
|
|
646
|
-
config(_userConfig, { command }) {
|
|
647
|
-
return {
|
|
648
|
-
esbuild: {
|
|
649
|
-
jsx: "automatic",
|
|
650
|
-
jsxImportSource: "visage",
|
|
651
|
-
jsxDev: command === "serve"
|
|
652
|
-
},
|
|
653
|
-
optimizeDeps: {
|
|
654
|
-
include: [
|
|
655
|
-
"visage",
|
|
656
|
-
"visage/signals",
|
|
657
|
-
"visage/dom",
|
|
658
|
-
"visage/query",
|
|
659
|
-
"visage/rpc",
|
|
660
|
-
"visage/style",
|
|
661
|
-
"visage/router",
|
|
662
|
-
"visage/form",
|
|
663
|
-
"visage/virtual",
|
|
664
|
-
"visage/devtools"
|
|
665
|
-
]
|
|
666
|
-
},
|
|
667
|
-
resolve: {
|
|
668
|
-
dedupe: ["visage"]
|
|
669
|
-
}
|
|
670
|
-
};
|
|
671
|
-
}
|
|
672
|
-
};
|
|
673
|
-
const transformPlugin = {
|
|
674
|
-
name: "visage:transform",
|
|
675
|
-
transform(code, id) {
|
|
676
|
-
if (options.hmr !== false && isDev) {
|
|
677
|
-
return transformHmr(code, id);
|
|
678
|
-
}
|
|
679
|
-
return null;
|
|
680
|
-
}
|
|
681
|
-
};
|
|
682
|
-
const devServerPlugin = {
|
|
683
|
-
name: "visage:server",
|
|
684
|
-
apply: "serve",
|
|
685
|
-
configureServer(server) {
|
|
686
|
-
if (options.rpc) {
|
|
687
|
-
server.middlewares.use(createViteRpcMiddleware(options.rpc));
|
|
688
|
-
}
|
|
689
|
-
},
|
|
690
|
-
transformIndexHtml(html) {
|
|
691
|
-
if (options.devtools === false || !isDev) {
|
|
692
|
-
return html;
|
|
693
|
-
}
|
|
694
|
-
if (!html.includes('name="visage-devtools"')) {
|
|
695
|
-
return html.replace("<head>", `<head>
|
|
696
|
-
<meta name="visage-devtools" content="enabled" />`);
|
|
697
|
-
}
|
|
698
|
-
return html;
|
|
699
|
-
}
|
|
700
|
-
};
|
|
701
|
-
return [configPlugin, transformPlugin, devServerPlugin];
|
|
702
|
-
}
|
|
703
|
-
var plugin_default = visage;
|
|
704
|
-
export {
|
|
705
|
-
plugin_default as default,
|
|
706
|
-
visage
|
|
707
|
-
};
|