@epoch32/visage 0.0.0-stage → 0.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.
Files changed (138) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +110 -2
  3. package/dist/bundle.dev.js +4875 -0
  4. package/dist/index.js +9633 -0
  5. package/dist/src/api/index.js +4002 -0
  6. package/dist/src/charts/index.js +2717 -0
  7. package/dist/src/devtools/index.js +4677 -0
  8. package/dist/src/dom/index.js +1550 -0
  9. package/dist/src/form/index.js +1415 -0
  10. package/dist/src/index.js +11826 -0
  11. package/dist/src/jsx-runtime/index.js +1630 -0
  12. package/dist/src/query/index.js +2422 -0
  13. package/dist/src/router/index.js +1856 -0
  14. package/dist/src/rpc/index.js +3484 -0
  15. package/dist/src/server/index.js +1173 -0
  16. package/dist/src/signals/index.js +1077 -0
  17. package/dist/src/style/index.js +2340 -0
  18. package/dist/src/template/index.js +1149 -0
  19. package/dist/src/ui/index.js +4155 -0
  20. package/dist/src/virtual/index.js +1955 -0
  21. package/dist/src/vite/index.js +984 -0
  22. package/dist/vite/index.js +707 -0
  23. package/package.json +140 -4
  24. package/src/api/client.ts +371 -0
  25. package/src/api/file-router.ts +183 -0
  26. package/src/api/handler.ts +326 -0
  27. package/src/api/index.ts +71 -0
  28. package/src/api/middleware.ts +54 -0
  29. package/src/api/pragma.ts +155 -0
  30. package/src/api/procedure.ts +172 -0
  31. package/src/api/types.ts +249 -0
  32. package/src/charts/Area.tsx +111 -0
  33. package/src/charts/Axis.tsx +126 -0
  34. package/src/charts/Bar.tsx +86 -0
  35. package/src/charts/CartesianGrid.tsx +72 -0
  36. package/src/charts/Chart.tsx +269 -0
  37. package/src/charts/Cursor.tsx +42 -0
  38. package/src/charts/Legend.tsx +56 -0
  39. package/src/charts/Line.tsx +106 -0
  40. package/src/charts/Pie.tsx +88 -0
  41. package/src/charts/Tooltip.tsx +127 -0
  42. package/src/charts/context.ts +30 -0
  43. package/src/charts/index.ts +14 -0
  44. package/src/charts/path.ts +155 -0
  45. package/src/charts/scale.ts +133 -0
  46. package/src/charts/types.ts +50 -0
  47. package/src/devtools/DomTab.tsx +134 -0
  48. package/src/devtools/GraphInspector.tsx +196 -0
  49. package/src/devtools/GraphTab.tsx +315 -0
  50. package/src/devtools/QueryTab.tsx +200 -0
  51. package/src/devtools/RpcTab.tsx +190 -0
  52. package/src/devtools/SignalsTab.tsx +129 -0
  53. package/src/devtools/component.tsx +243 -0
  54. package/src/devtools/highlighter.ts +134 -0
  55. package/src/devtools/hub.ts +175 -0
  56. package/src/devtools/index.ts +20 -0
  57. package/src/devtools/styles.ts +313 -0
  58. package/src/devtools/types.ts +45 -0
  59. package/src/dom/async.ts +232 -0
  60. package/src/dom/control-flow.ts +353 -0
  61. package/src/dom/index.ts +32 -0
  62. package/src/dom/lazy.ts +82 -0
  63. package/src/dom/render.ts +286 -0
  64. package/src/dom/resource.ts +120 -0
  65. package/src/form/form.ts +218 -0
  66. package/src/form/index.ts +17 -0
  67. package/src/form/schema.ts +299 -0
  68. package/src/index.ts +413 -0
  69. package/src/jsx-dev-runtime/index.ts +1 -0
  70. package/src/jsx-runtime/index.ts +112 -0
  71. package/src/jsx-runtime/jsx.d.ts +278 -0
  72. package/src/query/create-mutation.ts +64 -0
  73. package/src/query/create-query.ts +426 -0
  74. package/src/query/focus-manager.ts +68 -0
  75. package/src/query/index.ts +63 -0
  76. package/src/query/mutation-cache.ts +138 -0
  77. package/src/query/online-manager.ts +67 -0
  78. package/src/query/query-cache.ts +369 -0
  79. package/src/query/query-client.ts +248 -0
  80. package/src/query/types.ts +221 -0
  81. package/src/query/utils.ts +132 -0
  82. package/src/router/index.ts +19 -0
  83. package/src/router/router.tsx +352 -0
  84. package/src/rpc/client.ts +317 -0
  85. package/src/rpc/error.ts +70 -0
  86. package/src/rpc/index.ts +52 -0
  87. package/src/rpc/procedure.ts +88 -0
  88. package/src/rpc/router.ts +105 -0
  89. package/src/rpc/server.ts +511 -0
  90. package/src/rpc/types.ts +328 -0
  91. package/src/server/hydrate.ts +21 -0
  92. package/src/server/index.ts +10 -0
  93. package/src/server/render-to-stream.ts +31 -0
  94. package/src/server/render-to-string.ts +125 -0
  95. package/src/signals/computed.ts +217 -0
  96. package/src/signals/context.ts +89 -0
  97. package/src/signals/effect.ts +161 -0
  98. package/src/signals/index.ts +57 -0
  99. package/src/signals/scope.ts +132 -0
  100. package/src/signals/signal.ts +203 -0
  101. package/src/signals/store.ts +163 -0
  102. package/src/signals/system.ts +124 -0
  103. package/src/signals/types.ts +64 -0
  104. package/src/style/component.tsx +122 -0
  105. package/src/style/create-style.ts +229 -0
  106. package/src/style/cx.ts +75 -0
  107. package/src/style/hash.ts +19 -0
  108. package/src/style/index.ts +8 -0
  109. package/src/style/layout.tsx +406 -0
  110. package/src/style/manager.ts +87 -0
  111. package/src/style/parser.ts +257 -0
  112. package/src/style/scale.ts +237 -0
  113. package/src/template/index.ts +4 -0
  114. package/src/template/template.ts +126 -0
  115. package/src/ui/accordion.tsx +286 -0
  116. package/src/ui/command-dialog.tsx +49 -0
  117. package/src/ui/command.tsx +400 -0
  118. package/src/ui/data-table.tsx +322 -0
  119. package/src/ui/dialog.tsx +362 -0
  120. package/src/ui/dropdown-menu.tsx +381 -0
  121. package/src/ui/icon.tsx +69 -0
  122. package/src/ui/index.ts +138 -0
  123. package/src/ui/layout.tsx +13 -0
  124. package/src/ui/popover.tsx +249 -0
  125. package/src/ui/switch.tsx +114 -0
  126. package/src/ui/tabs.tsx +297 -0
  127. package/src/ui/toast.tsx +280 -0
  128. package/src/ui/tooltip.tsx +262 -0
  129. package/src/ui/utils.ts +110 -0
  130. package/src/virtual/index.ts +12 -0
  131. package/src/virtual/virtual-list.tsx +185 -0
  132. package/src/virtual/virtualizer.ts +274 -0
  133. package/src/vite/hmr.ts +38 -0
  134. package/src/vite/index.ts +3 -0
  135. package/src/vite/lucide.ts +124 -0
  136. package/src/vite/plugin.ts +102 -0
  137. package/src/vite/rpc-middleware.ts +82 -0
  138. package/src/vite/types.ts +65 -0
@@ -0,0 +1,707 @@
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
+ };