@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,4002 @@
1
+ // src/form/schema.ts
2
+ class Schema {
3
+ "~standard" = {
4
+ version: 1,
5
+ vendor: "visage",
6
+ validate: (val) => {
7
+ const res = this.validate(val);
8
+ if (res.success) {
9
+ return { value: res.data };
10
+ }
11
+ return {
12
+ issues: (res.issues || []).map((i) => ({
13
+ path: [i.path],
14
+ message: i.message
15
+ }))
16
+ };
17
+ }
18
+ };
19
+ }
20
+
21
+ class StringSchema extends Schema {
22
+ _min;
23
+ _max;
24
+ _emailMsg;
25
+ _regex;
26
+ _isOptional = false;
27
+ min(length, message) {
28
+ this._min = { val: length, msg: message };
29
+ return this;
30
+ }
31
+ max(length, message) {
32
+ this._max = { val: length, msg: message };
33
+ return this;
34
+ }
35
+ email(message) {
36
+ this._emailMsg = message ?? "Invalid email address";
37
+ return this;
38
+ }
39
+ regex(pattern, message) {
40
+ this._regex = { pattern, msg: message };
41
+ return this;
42
+ }
43
+ optional() {
44
+ this._isOptional = true;
45
+ return this;
46
+ }
47
+ validate(value, path = "") {
48
+ if (value === undefined || value === null || value === "") {
49
+ if (this._isOptional)
50
+ return { success: true, data: undefined };
51
+ return { success: false, issues: [{ path, message: "This field is required" }] };
52
+ }
53
+ if (typeof value !== "string") {
54
+ return { success: false, issues: [{ path, message: "Must be a string" }] };
55
+ }
56
+ if (this._min && value.length < this._min.val) {
57
+ return {
58
+ success: false,
59
+ issues: [{ path, message: this._min.msg ?? `Must be at least ${this._min.val} characters` }]
60
+ };
61
+ }
62
+ if (this._max && value.length > this._max.val) {
63
+ return {
64
+ success: false,
65
+ issues: [{ path, message: this._max.msg ?? `Must be at most ${this._max.val} characters` }]
66
+ };
67
+ }
68
+ if (this._emailMsg) {
69
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
70
+ if (!emailRegex.test(value)) {
71
+ return { success: false, issues: [{ path, message: this._emailMsg }] };
72
+ }
73
+ }
74
+ if (this._regex && !this._regex.pattern.test(value)) {
75
+ return {
76
+ success: false,
77
+ issues: [{ path, message: this._regex.msg ?? "Invalid format" }]
78
+ };
79
+ }
80
+ return { success: true, data: value };
81
+ }
82
+ }
83
+
84
+ class NumberSchema extends Schema {
85
+ _min;
86
+ _max;
87
+ _integer;
88
+ _isOptional = false;
89
+ min(value, message) {
90
+ this._min = { val: value, msg: message };
91
+ return this;
92
+ }
93
+ max(value, message) {
94
+ this._max = { val: value, msg: message };
95
+ return this;
96
+ }
97
+ integer(message) {
98
+ this._integer = { msg: message };
99
+ return this;
100
+ }
101
+ optional() {
102
+ this._isOptional = true;
103
+ return this;
104
+ }
105
+ validate(value, path = "") {
106
+ if (value === undefined || value === null || value === "") {
107
+ if (this._isOptional)
108
+ return { success: true, data: undefined };
109
+ return { success: false, issues: [{ path, message: "This field is required" }] };
110
+ }
111
+ const num = Number(value);
112
+ if (Number.isNaN(num) || typeof value === "boolean") {
113
+ return { success: false, issues: [{ path, message: "Must be a valid number" }] };
114
+ }
115
+ if (this._integer && !Number.isInteger(num)) {
116
+ return {
117
+ success: false,
118
+ issues: [{ path, message: this._integer.msg ?? "Must be an integer" }]
119
+ };
120
+ }
121
+ if (this._min && num < this._min.val) {
122
+ return {
123
+ success: false,
124
+ issues: [{ path, message: this._min.msg ?? `Must be at least ${this._min.val}` }]
125
+ };
126
+ }
127
+ if (this._max && num > this._max.val) {
128
+ return {
129
+ success: false,
130
+ issues: [{ path, message: this._max.msg ?? `Must be at most ${this._max.val}` }]
131
+ };
132
+ }
133
+ return { success: true, data: num };
134
+ }
135
+ }
136
+
137
+ class BooleanSchema extends Schema {
138
+ validate(value, _path = "") {
139
+ return { success: true, data: Boolean(value) };
140
+ }
141
+ }
142
+
143
+ class ObjectSchema extends Schema {
144
+ shape;
145
+ constructor(shape) {
146
+ super();
147
+ this.shape = shape;
148
+ }
149
+ validate(value, basePath = "") {
150
+ if (typeof value !== "object" || value === null) {
151
+ return { success: false, issues: [{ path: basePath, message: "Must be an object" }] };
152
+ }
153
+ const issues = [];
154
+ const data = {};
155
+ for (const key in this.shape) {
156
+ const fieldSchema = this.shape[key];
157
+ const fieldVal = value[key];
158
+ const fieldPath = basePath ? `${basePath}.${key}` : key;
159
+ const res = validateWithSchema(fieldSchema, fieldVal, fieldPath);
160
+ if (res.success) {
161
+ data[key] = res.data;
162
+ } else if (res.issues) {
163
+ issues.push(...res.issues);
164
+ }
165
+ }
166
+ if (issues.length > 0) {
167
+ return { success: false, issues };
168
+ }
169
+ return { success: true, data };
170
+ }
171
+ }
172
+ function validateWithSchema(schema, value, path = "") {
173
+ if (!schema)
174
+ return { success: true, data: value };
175
+ if (schema instanceof Schema) {
176
+ return schema.validate(value, path);
177
+ }
178
+ if (typeof schema === "object" && "~standard" in schema) {
179
+ const res = schema["~standard"].validate(value);
180
+ if ("value" in res) {
181
+ return { success: true, data: res.value };
182
+ }
183
+ return {
184
+ success: false,
185
+ issues: res.issues.map((i) => ({
186
+ path: i.path ? i.path.join(".") : path,
187
+ message: i.message
188
+ }))
189
+ };
190
+ }
191
+ if (typeof schema === "function") {
192
+ const res = schema(value);
193
+ if (res === true || res === null || res === undefined) {
194
+ return { success: true, data: value };
195
+ }
196
+ if (typeof res === "string") {
197
+ return { success: false, issues: [{ path, message: res }] };
198
+ }
199
+ if (typeof res === "object") {
200
+ return res;
201
+ }
202
+ }
203
+ return { success: true, data: value };
204
+ }
205
+ var v = {
206
+ string: () => new StringSchema,
207
+ number: () => new NumberSchema,
208
+ boolean: () => new BooleanSchema,
209
+ object: (shape) => new ObjectSchema(shape)
210
+ };
211
+ // src/rpc/error.ts
212
+ var ERROR_CODE_TO_STATUS = {
213
+ BAD_REQUEST: 400,
214
+ UNAUTHORIZED: 401,
215
+ FORBIDDEN: 403,
216
+ NOT_FOUND: 404,
217
+ METHOD_NOT_ALLOWED: 405,
218
+ TIMEOUT: 408,
219
+ CONFLICT: 409,
220
+ UNPROCESSABLE_CONTENT: 422,
221
+ INTERNAL_SERVER_ERROR: 500
222
+ };
223
+ var STATUS_TO_ERROR_CODE = {
224
+ 400: "BAD_REQUEST",
225
+ 401: "UNAUTHORIZED",
226
+ 403: "FORBIDDEN",
227
+ 404: "NOT_FOUND",
228
+ 405: "METHOD_NOT_ALLOWED",
229
+ 408: "TIMEOUT",
230
+ 409: "CONFLICT",
231
+ 422: "UNPROCESSABLE_CONTENT",
232
+ 500: "INTERNAL_SERVER_ERROR"
233
+ };
234
+
235
+ class RPCError extends Error {
236
+ code;
237
+ status;
238
+ issues;
239
+ constructor(codeOrStatusOrOptions, message) {
240
+ if (typeof codeOrStatusOrOptions === "number") {
241
+ const code = STATUS_TO_ERROR_CODE[codeOrStatusOrOptions] || "INTERNAL_SERVER_ERROR";
242
+ super(message || code);
243
+ this.name = "RPCError";
244
+ this.code = code;
245
+ this.status = codeOrStatusOrOptions;
246
+ } else if (typeof codeOrStatusOrOptions === "string") {
247
+ super(message || codeOrStatusOrOptions);
248
+ this.name = "RPCError";
249
+ this.code = codeOrStatusOrOptions;
250
+ this.status = ERROR_CODE_TO_STATUS[codeOrStatusOrOptions] || 500;
251
+ } else {
252
+ super(codeOrStatusOrOptions.message ?? codeOrStatusOrOptions.code, {
253
+ cause: codeOrStatusOrOptions.cause
254
+ });
255
+ this.name = "RPCError";
256
+ this.code = codeOrStatusOrOptions.code;
257
+ this.status = ERROR_CODE_TO_STATUS[codeOrStatusOrOptions.code] ?? 500;
258
+ this.issues = codeOrStatusOrOptions.issues;
259
+ }
260
+ }
261
+ get statusCode() {
262
+ return this.status;
263
+ }
264
+ }
265
+ // src/signals/types.ts
266
+ var SubscriberState;
267
+ ((SubscriberState) => {
268
+ SubscriberState[SubscriberState["CLEAN"] = 0] = "CLEAN";
269
+ SubscriberState[SubscriberState["CHECK_DIRTY"] = 1] = "CHECK_DIRTY";
270
+ SubscriberState[SubscriberState["DIRTY"] = 2] = "DIRTY";
271
+ SubscriberState[SubscriberState["DISPOSED"] = 3] = "DISPOSED";
272
+ })(SubscriberState ||= {});
273
+ var SIGNAL_BRAND = Symbol("visage.signal");
274
+ var COMPUTED_BRAND = Symbol("visage.computed");
275
+ function isSignal(value) {
276
+ return typeof value === "function" && ((SIGNAL_BRAND in value) || (COMPUTED_BRAND in value));
277
+ }
278
+
279
+ // src/signals/system.ts
280
+ var currentSubscriber = null;
281
+ var currentScope = null;
282
+ var batchDepth = 0;
283
+ var pendingEffects = new Set;
284
+ var warnedCyclicSubscribers = new WeakSet;
285
+ var isFlushing = false;
286
+ function setCurrentSubscriber(sub) {
287
+ currentSubscriber = sub;
288
+ }
289
+ function setCurrentScope(scope) {
290
+ currentScope = scope;
291
+ }
292
+ function getCurrentScope() {
293
+ return currentScope;
294
+ }
295
+ function addPendingEffect(eff) {
296
+ pendingEffects.add(eff);
297
+ }
298
+ function removePendingEffect(eff) {
299
+ pendingEffects.delete(eff);
300
+ }
301
+ var MAX_FLUSH_COUNT = 100;
302
+ function flushEffects() {
303
+ if (isFlushing || batchDepth > 0)
304
+ return;
305
+ isFlushing = true;
306
+ let iterations = 0;
307
+ try {
308
+ while (pendingEffects.size > 0) {
309
+ if (++iterations > MAX_FLUSH_COUNT) {
310
+ pendingEffects.clear();
311
+ throw new Error(`[Visage Reactivity] Maximum update depth exceeded (${MAX_FLUSH_COUNT} cycles). ` + `An effect continuously triggered state mutations (infinite loop detected). ` + `Ensure effects that modify signals do not unconditionally depend on them, or use untrack() to read state.`);
312
+ }
313
+ const effectsToRun = Array.from(pendingEffects);
314
+ pendingEffects.clear();
315
+ for (const eff of effectsToRun) {
316
+ if (eff.state === 2 /* DIRTY */ || eff.state === 1 /* CHECK_DIRTY */) {
317
+ eff.run();
318
+ }
319
+ }
320
+ }
321
+ } finally {
322
+ isFlushing = false;
323
+ }
324
+ }
325
+ function batch(fn) {
326
+ batchDepth++;
327
+ try {
328
+ return fn();
329
+ } finally {
330
+ batchDepth--;
331
+ if (batchDepth === 0) {
332
+ flushEffects();
333
+ }
334
+ }
335
+ }
336
+ function untrack(fn) {
337
+ const prev = currentSubscriber;
338
+ currentSubscriber = null;
339
+ try {
340
+ return fn();
341
+ } finally {
342
+ currentSubscriber = prev;
343
+ }
344
+ }
345
+
346
+ // src/signals/computed.ts
347
+ class Computed {
348
+ [COMPUTED_BRAND] = true;
349
+ _fn;
350
+ _value;
351
+ _subscribers = new Set;
352
+ sources = new Set;
353
+ sourceVersions = new Map;
354
+ state = 2 /* DIRTY */;
355
+ scope;
356
+ version = 0;
357
+ constructor(fn) {
358
+ this._fn = fn;
359
+ this.scope = currentScope;
360
+ if (currentScope) {
361
+ currentScope.addSubscriber(this);
362
+ }
363
+ }
364
+ get() {
365
+ if (this.state === 3 /* DISPOSED */) {
366
+ return this._fn();
367
+ }
368
+ if (currentSubscriber !== null && currentSubscriber.state !== 3 /* DISPOSED */) {
369
+ this._subscribers.add(currentSubscriber);
370
+ currentSubscriber.sources.add(this);
371
+ currentSubscriber.sourceVersions.set(this, this.version);
372
+ }
373
+ if (this.state === 2 /* DIRTY */ || this.state === 1 /* CHECK_DIRTY */) {
374
+ this.updateIfNecessary();
375
+ }
376
+ return this._value;
377
+ }
378
+ get value() {
379
+ return this.get();
380
+ }
381
+ removeSubscriber(sub) {
382
+ this._subscribers.delete(sub);
383
+ }
384
+ markDirty(targetState = 1 /* CHECK_DIRTY */) {
385
+ if (this.state >= targetState || this.state === 3 /* DISPOSED */) {
386
+ return;
387
+ }
388
+ this.state = targetState;
389
+ for (const sub of this._subscribers) {
390
+ sub.markDirty(1 /* CHECK_DIRTY */);
391
+ }
392
+ }
393
+ updateIfNecessary() {
394
+ if (this.state === 2 /* DIRTY */) {
395
+ const prev = this._value;
396
+ this.recompute();
397
+ return !Object.is(prev, this._value);
398
+ }
399
+ if (this.state === 1 /* CHECK_DIRTY */) {
400
+ let anyChanged = false;
401
+ for (const source of this.sources) {
402
+ if (source instanceof Computed) {
403
+ source.updateIfNecessary();
404
+ }
405
+ const lastVersion = this.sourceVersions.get(source);
406
+ if (lastVersion !== undefined && lastVersion !== source.version) {
407
+ anyChanged = true;
408
+ }
409
+ }
410
+ if (anyChanged) {
411
+ const prev = this._value;
412
+ this.recompute();
413
+ return !Object.is(prev, this._value);
414
+ }
415
+ this.state = 0 /* CLEAN */;
416
+ return false;
417
+ }
418
+ return false;
419
+ }
420
+ recompute() {
421
+ this.cleanupSources();
422
+ const prevSubscriber = currentSubscriber;
423
+ setCurrentSubscriber(this);
424
+ const prevScope = currentScope;
425
+ setCurrentScope(this.scope);
426
+ try {
427
+ const next = this._fn();
428
+ if (!Object.is(this._value, next)) {
429
+ this._value = next;
430
+ this.version++;
431
+ }
432
+ this.state = 0 /* CLEAN */;
433
+ } finally {
434
+ setCurrentSubscriber(prevSubscriber);
435
+ setCurrentScope(prevScope);
436
+ }
437
+ }
438
+ cleanupSources() {
439
+ for (const source of this.sources) {
440
+ source.removeSubscriber(this);
441
+ }
442
+ this.sources.clear();
443
+ this.sourceVersions.clear();
444
+ }
445
+ dispose() {
446
+ if (this.state === 3 /* DISPOSED */)
447
+ return;
448
+ this.state = 3 /* DISPOSED */;
449
+ this.cleanupSources();
450
+ this._subscribers.clear();
451
+ }
452
+ }
453
+ function computed(fn, options) {
454
+ const isInternal = options?.internal ?? false;
455
+ const customName = options?.name;
456
+ const c = new Computed(fn);
457
+ const accessor = () => c.get();
458
+ Object.defineProperties(accessor, {
459
+ [COMPUTED_BRAND]: { value: true },
460
+ get: { value: () => c.get() },
461
+ value: { get: () => c.get() }
462
+ });
463
+ if (!isInternal && typeof window !== "undefined" && window.__VISAGE_DEVTOOLS__) {
464
+ const devtools = window.__VISAGE_DEVTOOLS__;
465
+ const sigId = devtools.registerSignal({
466
+ name: customName,
467
+ type: "computed",
468
+ getValue: () => c.get(),
469
+ version: () => c.version,
470
+ subscribersCount: () => c._subscribers?.size ?? 0,
471
+ dependencies: () => Array.from(c.sources ?? []).map((src) => src._devId).filter(Boolean),
472
+ dependents: () => Array.from(c._subscribers ?? []).map((sub) => sub._devId).filter(Boolean)
473
+ });
474
+ c._devId = sigId;
475
+ if (currentScope) {
476
+ currentScope.addCleanup(() => {
477
+ devtools.unregisterSignal(sigId);
478
+ });
479
+ }
480
+ }
481
+ return accessor;
482
+ }
483
+ var createMemo = computed;
484
+ // src/style/cx.ts
485
+ function hasReactiveClass(val) {
486
+ if (typeof val === "function")
487
+ return true;
488
+ if (Array.isArray(val)) {
489
+ for (let i = 0;i < val.length; i++) {
490
+ if (hasReactiveClass(val[i]))
491
+ return true;
492
+ }
493
+ return false;
494
+ }
495
+ if (typeof val === "object" && val !== null) {
496
+ for (const k in val) {
497
+ if (typeof val[k] === "function")
498
+ return true;
499
+ }
500
+ }
501
+ return false;
502
+ }
503
+ function normalizeClass(val) {
504
+ if (!val)
505
+ return "";
506
+ if (typeof val === "function")
507
+ return normalizeClass(val());
508
+ if (typeof val === "string")
509
+ return val.trim();
510
+ if (typeof val === "number")
511
+ return String(val);
512
+ if (Array.isArray(val)) {
513
+ let res = "";
514
+ for (let i = 0;i < val.length; i++) {
515
+ const item = typeof val[i] === "function" ? val[i]() : val[i];
516
+ const itemStr = normalizeClass(item);
517
+ if (itemStr) {
518
+ res = res ? `${res} ${itemStr}` : itemStr;
519
+ }
520
+ }
521
+ return res;
522
+ }
523
+ if (typeof val === "object") {
524
+ let res = "";
525
+ for (const key in val) {
526
+ const condition = typeof val[key] === "function" ? val[key]() : val[key];
527
+ if (condition) {
528
+ res = res ? `${res} ${key}` : key;
529
+ }
530
+ }
531
+ return res;
532
+ }
533
+ return "";
534
+ }
535
+ function cx(...args) {
536
+ return normalizeClass(args);
537
+ }
538
+
539
+ // src/style/scale.ts
540
+ var UNITLESS_PROPERTIES = new Set([
541
+ "animationIterationCount",
542
+ "aspectRatio",
543
+ "borderImageSlice",
544
+ "columnCount",
545
+ "columns",
546
+ "flex",
547
+ "flexGrow",
548
+ "flexShrink",
549
+ "flexOrder",
550
+ "fontWeight",
551
+ "lineHeight",
552
+ "opacity",
553
+ "order",
554
+ "orphans",
555
+ "scale",
556
+ "tabSize",
557
+ "widows",
558
+ "zIndex",
559
+ "zoom",
560
+ "fillOpacity",
561
+ "strokeOpacity"
562
+ ]);
563
+ var DIMENSION_KEYWORDS = {
564
+ screen: "100dvh",
565
+ "screen-h": "100dvh",
566
+ "screen-w": "100dvw",
567
+ full: "100%",
568
+ half: "50%",
569
+ third: "33.333333%",
570
+ "two-thirds": "66.666667%",
571
+ quarter: "25%",
572
+ "three-quarters": "75%",
573
+ auto: "auto",
574
+ min: "min-content",
575
+ max: "max-content",
576
+ fit: "fit-content"
577
+ };
578
+ function space(val) {
579
+ if (val === undefined || val === null || val === "")
580
+ return "";
581
+ if (typeof val === "number") {
582
+ if (val === 0)
583
+ return "0px";
584
+ return `${val * 4}px`;
585
+ }
586
+ const str = String(val).trim();
587
+ if (str in DIMENSION_KEYWORDS) {
588
+ return DIMENSION_KEYWORDS[str];
589
+ }
590
+ return str;
591
+ }
592
+ function resolveDimension(val) {
593
+ if (val === undefined || val === null || val === "")
594
+ return "";
595
+ if (typeof val === "number") {
596
+ if (val === 0)
597
+ return "0px";
598
+ if (val > 48)
599
+ return `${val}px`;
600
+ return `${val * 4}px`;
601
+ }
602
+ const str = String(val).trim();
603
+ if (str in DIMENSION_KEYWORDS) {
604
+ return DIMENSION_KEYWORDS[str];
605
+ }
606
+ return str;
607
+ }
608
+ function px(val) {
609
+ return `${val}px`;
610
+ }
611
+ function rem(val) {
612
+ return `${val}rem`;
613
+ }
614
+ function em(val) {
615
+ return `${val}em`;
616
+ }
617
+ function fluid(minPx, maxPx, options) {
618
+ const minVw = options?.minVw ?? 375;
619
+ const maxVw = options?.maxVw ?? 1280;
620
+ const slope = (maxPx - minPx) / (maxVw - minVw);
621
+ const yIntercept = -minVw * slope + minPx;
622
+ const slopeVw = slope * 100;
623
+ const preferred = `${yIntercept.toFixed(2)}px + ${slopeVw.toFixed(2)}vw`;
624
+ const min = Math.min(minPx, maxPx);
625
+ const max = Math.max(minPx, maxPx);
626
+ return `clamp(${min}px, ${preferred}, ${max}px)`;
627
+ }
628
+ var SHADOW_PRESETS = {
629
+ none: "none",
630
+ sm: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
631
+ md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)",
632
+ lg: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)",
633
+ xl: "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)",
634
+ soft: "0 8px 30px rgba(0, 0, 0, 0.06)",
635
+ glow: "0 0 24px rgba(168, 85, 247, 0.35)",
636
+ inner: "inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)"
637
+ };
638
+ function shadow(variant) {
639
+ if (variant in SHADOW_PRESETS) {
640
+ return SHADOW_PRESETS[variant];
641
+ }
642
+ return variant;
643
+ }
644
+ var RADIUS_PRESETS = {
645
+ none: "0px",
646
+ sm: "4px",
647
+ md: "8px",
648
+ lg: "12px",
649
+ xl: "16px",
650
+ "2xl": "20px",
651
+ "3xl": "24px",
652
+ full: "9999px"
653
+ };
654
+ function radius(variant) {
655
+ if (typeof variant === "number") {
656
+ return `${variant}px`;
657
+ }
658
+ if (variant in RADIUS_PRESETS) {
659
+ return RADIUS_PRESETS[variant];
660
+ }
661
+ return String(variant);
662
+ }
663
+ function glass(options = {}) {
664
+ const blurVal = typeof options.blur === "number" ? `${options.blur}px` : options.blur ?? "20px";
665
+ const tintVal = options.tint ?? "rgba(255, 255, 255, 0.65)";
666
+ const borderVal = options.border ?? "1px solid rgba(255, 255, 255, 0.75)";
667
+ return {
668
+ backdropFilter: `blur(${blurVal})`,
669
+ WebkitBackdropFilter: `blur(${blurVal})`,
670
+ backgroundColor: tintVal,
671
+ border: borderVal
672
+ };
673
+ }
674
+ function formatStyleProp(prop, val) {
675
+ if (val === null || val === undefined || val === "")
676
+ return "";
677
+ if (typeof val === "number") {
678
+ if (val === 0 || UNITLESS_PROPERTIES.has(prop)) {
679
+ return String(val);
680
+ }
681
+ return `${val}px`;
682
+ }
683
+ const str = String(val).trim();
684
+ if (str in DIMENSION_KEYWORDS) {
685
+ const lowerProp = prop.toLowerCase();
686
+ if (str === "screen") {
687
+ if (lowerProp.includes("width"))
688
+ return "100dvw";
689
+ return "100dvh";
690
+ }
691
+ return DIMENSION_KEYWORDS[str];
692
+ }
693
+ return str;
694
+ }
695
+
696
+ // src/dom/render.ts
697
+ function isReactive(value) {
698
+ return isSignal(value) || typeof value === "function";
699
+ }
700
+ function resolveValue(val) {
701
+ if (typeof val === "function") {
702
+ return val();
703
+ }
704
+ return val;
705
+ }
706
+ function render(fn, container) {
707
+ return createRoot((dispose) => {
708
+ container.textContent = "";
709
+ const node = insertChild(container, fn());
710
+ onCleanup(() => {
711
+ if (node && node instanceof Node && node.parentNode === container) {
712
+ container.removeChild(node);
713
+ }
714
+ });
715
+ return dispose;
716
+ });
717
+ }
718
+ function setAttribute(el, key, value) {
719
+ if (key.startsWith("on") && typeof value === "function") {
720
+ const eventName = key.startsWith("onCapture") ? key.slice(9).toLowerCase() : key.slice(2).toLowerCase();
721
+ const useCapture = key.startsWith("onCapture");
722
+ el.addEventListener(eventName, value, useCapture);
723
+ onCleanup(() => {
724
+ el.removeEventListener(eventName, value, useCapture);
725
+ });
726
+ return;
727
+ }
728
+ if (key === "ref" && typeof value === "function") {
729
+ value(el);
730
+ return;
731
+ }
732
+ if (key === "class" || key === "className") {
733
+ if (hasReactiveClass(value)) {
734
+ effect(() => {
735
+ const cls = normalizeClass(value);
736
+ if (cls)
737
+ el.setAttribute("class", cls);
738
+ else
739
+ el.removeAttribute("class");
740
+ });
741
+ } else {
742
+ const cls = normalizeClass(value);
743
+ if (cls)
744
+ el.setAttribute("class", cls);
745
+ else
746
+ el.removeAttribute("class");
747
+ }
748
+ return;
749
+ }
750
+ if (isReactive(value)) {
751
+ effect(() => {
752
+ const resolved = resolveValue(value);
753
+ applyProp(el, key, resolved);
754
+ });
755
+ } else {
756
+ applyProp(el, key, value);
757
+ }
758
+ }
759
+ function setStyleProperty(el, prop, val) {
760
+ const formatted = formatStyleProp(prop, val);
761
+ if (prop.startsWith("--") || prop.includes("-")) {
762
+ if (formatted === "") {
763
+ el.style.removeProperty(prop);
764
+ } else {
765
+ el.style.setProperty(prop, formatted);
766
+ }
767
+ } else {
768
+ el.style[prop] = formatted;
769
+ }
770
+ }
771
+ function applyProp(el, key, value) {
772
+ if (value === null || value === undefined || value === false && !key.startsWith("aria-") && !key.startsWith("data-")) {
773
+ if (key === "className" || key === "class") {
774
+ el.removeAttribute("class");
775
+ } else if (key === "style" && typeof value === "string") {
776
+ el.removeAttribute("style");
777
+ } else if (key in el && !(el instanceof SVGElement)) {
778
+ try {
779
+ el[key] = typeof el[key] === "boolean" ? false : "";
780
+ } catch {
781
+ el.removeAttribute(key);
782
+ }
783
+ } else {
784
+ el.removeAttribute(key);
785
+ }
786
+ return;
787
+ }
788
+ if (key === "className" || key === "class") {
789
+ const cls = normalizeClass(value);
790
+ if (cls) {
791
+ el.setAttribute("class", cls);
792
+ } else {
793
+ el.removeAttribute("class");
794
+ }
795
+ } else if (key === "style") {
796
+ if (typeof value === "string") {
797
+ el.setAttribute("style", value);
798
+ } else if (typeof value === "object" && value !== null) {
799
+ for (const styleKey in value) {
800
+ const styleVal = value[styleKey];
801
+ if (isReactive(styleVal)) {
802
+ effect(() => {
803
+ setStyleProperty(el, styleKey, resolveValue(styleVal));
804
+ });
805
+ } else {
806
+ setStyleProperty(el, styleKey, styleVal);
807
+ }
808
+ }
809
+ }
810
+ } else if (key === "dangerouslySetInnerHTML" && value && typeof value.__html === "string") {
811
+ el.innerHTML = value.__html;
812
+ } else if (key in el && !(el instanceof SVGElement) && key !== "list" && key !== "form") {
813
+ try {
814
+ el[key] = value;
815
+ } catch {
816
+ el.setAttribute(key, String(value));
817
+ }
818
+ } else {
819
+ if (key.startsWith("aria-") || key.startsWith("data-")) {
820
+ el.setAttribute(key, String(value));
821
+ } else if (value === true) {
822
+ el.setAttribute(key, "");
823
+ } else {
824
+ el.setAttribute(key, String(value));
825
+ }
826
+ }
827
+ }
828
+ function insertChild(parent, child, before = null) {
829
+ if (child === null || child === undefined || child === false || child === true) {
830
+ return null;
831
+ }
832
+ if (child instanceof Node) {
833
+ parent.insertBefore(child, before);
834
+ return child;
835
+ }
836
+ if (Array.isArray(child)) {
837
+ const fragment = document.createDocumentFragment();
838
+ for (const item of child) {
839
+ insertChild(fragment, item);
840
+ }
841
+ parent.insertBefore(fragment, before);
842
+ return fragment;
843
+ }
844
+ if (isReactive(child)) {
845
+ const marker = document.createTextNode("");
846
+ parent.insertBefore(marker, before);
847
+ let childScope = null;
848
+ let currentNodes = [];
849
+ effect(() => {
850
+ const resolved = resolveValue(child);
851
+ if ((typeof resolved === "string" || typeof resolved === "number") && currentNodes.length === 1 && currentNodes[0].nodeType === Node.TEXT_NODE) {
852
+ currentNodes[0].data = String(resolved);
853
+ return;
854
+ }
855
+ if (childScope) {
856
+ childScope.dispose();
857
+ }
858
+ childScope = new Scope(getActiveScope());
859
+ runInScope(childScope, () => {
860
+ for (const node of currentNodes) {
861
+ if (node.parentNode) {
862
+ node.parentNode.removeChild(node);
863
+ }
864
+ }
865
+ currentNodes = [];
866
+ if (resolved === null || resolved === undefined || resolved === false) {
867
+ return;
868
+ }
869
+ const frag = document.createDocumentFragment();
870
+ if (typeof resolved === "string" || typeof resolved === "number" || typeof resolved === "boolean") {
871
+ frag.appendChild(document.createTextNode(String(resolved)));
872
+ } else {
873
+ insertChild(frag, resolved);
874
+ }
875
+ currentNodes = Array.from(frag.childNodes);
876
+ if (marker.parentNode) {
877
+ marker.parentNode.insertBefore(frag, marker);
878
+ }
879
+ });
880
+ });
881
+ onCleanup(() => {
882
+ if (childScope) {
883
+ childScope.dispose();
884
+ }
885
+ for (const node of currentNodes) {
886
+ if (node.parentNode) {
887
+ node.parentNode.removeChild(node);
888
+ }
889
+ }
890
+ if (marker.parentNode) {
891
+ marker.parentNode.removeChild(marker);
892
+ }
893
+ });
894
+ return marker;
895
+ }
896
+ const textNode = document.createTextNode(String(child));
897
+ parent.insertBefore(textNode, before);
898
+ return textNode;
899
+ }
900
+
901
+ // src/signals/scope.ts
902
+ class Scope {
903
+ subscribers = new Set;
904
+ cleanups = [];
905
+ parent = null;
906
+ isDisposed = false;
907
+ context;
908
+ constructor(parent = currentScope) {
909
+ this.parent = parent;
910
+ if (parent && !parent.isDisposed) {
911
+ parent.cleanups.push(() => this.dispose());
912
+ if (parent.context) {
913
+ this.context = new Map(parent.context);
914
+ }
915
+ }
916
+ }
917
+ addSubscriber(sub) {
918
+ if (this.isDisposed)
919
+ return;
920
+ this.subscribers.add(sub);
921
+ }
922
+ addCleanup(fn) {
923
+ if (this.isDisposed) {
924
+ fn();
925
+ return;
926
+ }
927
+ this.cleanups.push(fn);
928
+ }
929
+ dispose() {
930
+ if (this.isDisposed)
931
+ return;
932
+ this.isDisposed = true;
933
+ for (const sub of this.subscribers) {
934
+ sub.dispose();
935
+ }
936
+ this.subscribers.clear();
937
+ for (let i = this.cleanups.length - 1;i >= 0; i--) {
938
+ try {
939
+ this.cleanups[i]();
940
+ } catch (err) {
941
+ console.error("Error during cleanup:", err);
942
+ }
943
+ }
944
+ this.cleanups = [];
945
+ }
946
+ }
947
+ function onCleanup(fn) {
948
+ if (currentSubscriber && "addCleanup" in currentSubscriber) {
949
+ currentSubscriber.addCleanup(fn);
950
+ } else if (currentScope) {
951
+ currentScope.addCleanup(fn);
952
+ }
953
+ }
954
+ function createRoot(fn) {
955
+ const prevScope = currentScope;
956
+ const scope = new Scope(null);
957
+ setCurrentScope(scope);
958
+ try {
959
+ return fn(() => scope.dispose());
960
+ } finally {
961
+ setCurrentScope(prevScope);
962
+ }
963
+ }
964
+ function runInScope(scope, fn) {
965
+ const prevScope = currentScope;
966
+ setCurrentScope(scope);
967
+ try {
968
+ return fn();
969
+ } finally {
970
+ setCurrentScope(prevScope);
971
+ }
972
+ }
973
+ function getActiveScope() {
974
+ return getCurrentScope();
975
+ }
976
+
977
+ // src/signals/context.ts
978
+ function createContext(defaultValue) {
979
+ const id = Symbol("visage.context");
980
+ const context = {
981
+ id,
982
+ defaultValue,
983
+ Provider: (props) => {
984
+ const parentScope = getActiveScope();
985
+ const scope = new Scope(parentScope);
986
+ if (!scope.context) {
987
+ scope.context = new Map(parentScope?.context);
988
+ }
989
+ scope.context.set(id, props.value);
990
+ return runInScope(scope, () => {
991
+ const content = typeof props.children === "function" ? untrack(() => props.children()) : props.children;
992
+ if (typeof document !== "undefined") {
993
+ const frag = document.createDocumentFragment();
994
+ insertChild(frag, content);
995
+ return frag;
996
+ }
997
+ return content;
998
+ });
999
+ }
1000
+ };
1001
+ return context;
1002
+ }
1003
+ function useContext(context) {
1004
+ let scope = getActiveScope();
1005
+ while (scope) {
1006
+ if (scope.context?.has(context.id)) {
1007
+ return scope.context.get(context.id);
1008
+ }
1009
+ scope = scope.parent;
1010
+ }
1011
+ return context.defaultValue;
1012
+ }
1013
+ // src/signals/effect.ts
1014
+ class Effect {
1015
+ _fn;
1016
+ sources = new Set;
1017
+ sourceVersions = new Map;
1018
+ cleanups = [];
1019
+ state = 2 /* DIRTY */;
1020
+ scope;
1021
+ constructor(fn) {
1022
+ this._fn = fn;
1023
+ this.scope = currentScope;
1024
+ if (currentScope) {
1025
+ currentScope.addSubscriber(this);
1026
+ }
1027
+ this.run();
1028
+ }
1029
+ addCleanup(fn) {
1030
+ this.cleanups.push(fn);
1031
+ }
1032
+ runCleanups() {
1033
+ for (let i = this.cleanups.length - 1;i >= 0; i--) {
1034
+ try {
1035
+ this.cleanups[i]();
1036
+ } catch (e) {
1037
+ console.error("Error in effect cleanup:", e);
1038
+ }
1039
+ }
1040
+ this.cleanups = [];
1041
+ }
1042
+ markDirty(targetState = 2 /* DIRTY */) {
1043
+ if (this.state >= targetState || this.state === 3 /* DISPOSED */) {
1044
+ return;
1045
+ }
1046
+ this.state = targetState;
1047
+ addPendingEffect(this);
1048
+ if (batchDepth === 0) {
1049
+ flushEffects();
1050
+ }
1051
+ }
1052
+ run() {
1053
+ if (this.state === 3 /* DISPOSED */)
1054
+ return;
1055
+ if (this.state === 1 /* CHECK_DIRTY */) {
1056
+ let isDirty = false;
1057
+ for (const source of this.sources) {
1058
+ if (source instanceof Computed) {
1059
+ source.updateIfNecessary();
1060
+ }
1061
+ const lastVersion = this.sourceVersions.get(source);
1062
+ if (lastVersion !== undefined && lastVersion !== source.version) {
1063
+ isDirty = true;
1064
+ }
1065
+ }
1066
+ if (!isDirty) {
1067
+ this.state = 0 /* CLEAN */;
1068
+ return;
1069
+ }
1070
+ }
1071
+ this.runCleanups();
1072
+ this.cleanupSources();
1073
+ const prevSubscriber = currentSubscriber;
1074
+ setCurrentSubscriber(this);
1075
+ const prevScope = currentScope;
1076
+ setCurrentScope(this.scope);
1077
+ try {
1078
+ this.state = 0 /* CLEAN */;
1079
+ const res = this._fn();
1080
+ if (typeof res === "function") {
1081
+ this.cleanups.push(res);
1082
+ }
1083
+ } finally {
1084
+ setCurrentSubscriber(prevSubscriber);
1085
+ setCurrentScope(prevScope);
1086
+ }
1087
+ }
1088
+ cleanupSources() {
1089
+ for (const source of this.sources) {
1090
+ source.removeSubscriber(this);
1091
+ }
1092
+ this.sources.clear();
1093
+ this.sourceVersions.clear();
1094
+ }
1095
+ dispose() {
1096
+ if (this.state === 3 /* DISPOSED */)
1097
+ return;
1098
+ this.state = 3 /* DISPOSED */;
1099
+ this.runCleanups();
1100
+ this.cleanupSources();
1101
+ removePendingEffect(this);
1102
+ }
1103
+ }
1104
+ function effect(fn) {
1105
+ const eff = new Effect(fn);
1106
+ return () => eff.dispose();
1107
+ }
1108
+ var createEffect = effect;
1109
+ // src/signals/signal.ts
1110
+ class Signal {
1111
+ [SIGNAL_BRAND] = true;
1112
+ _value;
1113
+ _subscribers = new Set;
1114
+ _equals;
1115
+ version = 0;
1116
+ constructor(initialValue, equals) {
1117
+ this._value = initialValue;
1118
+ this._equals = equals ?? Object.is;
1119
+ }
1120
+ get() {
1121
+ if (currentSubscriber !== null && currentSubscriber.state !== 3 /* DISPOSED */) {
1122
+ this._subscribers.add(currentSubscriber);
1123
+ currentSubscriber.sources.add(this);
1124
+ currentSubscriber.sourceVersions.set(this, this.version);
1125
+ }
1126
+ return this._value;
1127
+ }
1128
+ set(newValue) {
1129
+ if (this._equals(this._value, newValue)) {
1130
+ return;
1131
+ }
1132
+ if (currentSubscriber !== null && this._subscribers.has(currentSubscriber)) {
1133
+ if (!warnedCyclicSubscribers.has(currentSubscriber)) {
1134
+ warnedCyclicSubscribers.add(currentSubscriber);
1135
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
1136
+ console.warn(`[Visage Reactivity] Cyclic update detected: An active subscriber modified a signal that it depends on. ` + `To read a signal inside an effect/computed without creating a circular dependency, wrap the read in untrack():
1137
+ ` + ` untrack(() => signal())`);
1138
+ }
1139
+ }
1140
+ }
1141
+ this._value = newValue;
1142
+ this.version++;
1143
+ batch(() => {
1144
+ this.notify();
1145
+ });
1146
+ }
1147
+ update(fn) {
1148
+ this.set(fn(this._value));
1149
+ }
1150
+ get value() {
1151
+ return this.get();
1152
+ }
1153
+ set value(val) {
1154
+ this.set(val);
1155
+ }
1156
+ removeSubscriber(sub) {
1157
+ this._subscribers.delete(sub);
1158
+ }
1159
+ notify() {
1160
+ if (this._subscribers.size === 0)
1161
+ return;
1162
+ const subs = Array.from(this._subscribers);
1163
+ for (const sub of subs) {
1164
+ sub.markDirty(2 /* DIRTY */);
1165
+ }
1166
+ }
1167
+ }
1168
+ function signal(initialValue, optionsOrEquals) {
1169
+ const equals = typeof optionsOrEquals === "function" ? optionsOrEquals : optionsOrEquals?.equals;
1170
+ const isInternal = typeof optionsOrEquals === "object" ? optionsOrEquals?.internal : false;
1171
+ const customName = typeof optionsOrEquals === "object" ? optionsOrEquals?.name : undefined;
1172
+ const s = new Signal(initialValue, equals);
1173
+ const accessor = (...args) => {
1174
+ if (args.length === 0) {
1175
+ return s.get();
1176
+ }
1177
+ s.set(args[0]);
1178
+ return args[0];
1179
+ };
1180
+ Object.defineProperties(accessor, {
1181
+ [SIGNAL_BRAND]: { value: true },
1182
+ get: { value: () => s.get() },
1183
+ set: { value: (v) => s.set(v) },
1184
+ update: { value: (fn) => s.update(fn) },
1185
+ value: {
1186
+ get: () => s.get(),
1187
+ set: (v) => s.set(v)
1188
+ }
1189
+ });
1190
+ if (!isInternal && typeof window !== "undefined" && window.__VISAGE_DEVTOOLS__) {
1191
+ const devtools = window.__VISAGE_DEVTOOLS__;
1192
+ const sigId = devtools.registerSignal({
1193
+ name: customName,
1194
+ type: "signal",
1195
+ getValue: () => s.get(),
1196
+ version: () => s.version,
1197
+ subscribersCount: () => s._subscribers?.size ?? 0,
1198
+ dependents: () => Array.from(s._subscribers ?? []).map((sub) => sub._devId).filter(Boolean)
1199
+ });
1200
+ s._devId = sigId;
1201
+ if (currentScope) {
1202
+ currentScope.addCleanup(() => {
1203
+ devtools.unregisterSignal(sigId);
1204
+ });
1205
+ }
1206
+ }
1207
+ return accessor;
1208
+ }
1209
+ function createSignal(initialValue, optionsOrEquals) {
1210
+ const s = signal(initialValue, optionsOrEquals);
1211
+ const setter = (value) => {
1212
+ if (typeof value === "function") {
1213
+ s.update(value);
1214
+ } else {
1215
+ s.set(value);
1216
+ }
1217
+ };
1218
+ return [s, setter];
1219
+ }
1220
+ // src/signals/store.ts
1221
+ var STORE_RAW = Symbol("visage.store.raw");
1222
+ var STORE_SIGNALS = Symbol("visage.store.signals");
1223
+ var STORE_PROXY = Symbol("visage.store.proxy");
1224
+ function isObject(val) {
1225
+ return typeof val === "object" && val !== null && (typeof Node === "undefined" || !(val instanceof Node)) && !isSignal(val);
1226
+ }
1227
+ function getOrCreatePropertySignal(target, prop) {
1228
+ let signalsMap = target[STORE_SIGNALS];
1229
+ if (!signalsMap) {
1230
+ signalsMap = new Map;
1231
+ Object.defineProperty(target, STORE_SIGNALS, {
1232
+ value: signalsMap,
1233
+ enumerable: false,
1234
+ writable: true
1235
+ });
1236
+ }
1237
+ let sig = signalsMap.get(prop);
1238
+ if (!sig) {
1239
+ sig = new Signal(target[prop]);
1240
+ signalsMap.set(prop, sig);
1241
+ }
1242
+ return sig;
1243
+ }
1244
+ function createStoreProxy(target) {
1245
+ if (!isObject(target))
1246
+ return target;
1247
+ if (target[STORE_PROXY])
1248
+ return target[STORE_PROXY];
1249
+ const proxy = new Proxy(target, {
1250
+ get(target, prop, receiver) {
1251
+ if (prop === STORE_RAW)
1252
+ return target;
1253
+ if (prop === STORE_PROXY)
1254
+ return proxy;
1255
+ if (typeof prop === "string" || typeof prop === "symbol") {
1256
+ const sig = getOrCreatePropertySignal(target, prop);
1257
+ sig.get();
1258
+ }
1259
+ const value = Reflect.get(target, prop, receiver);
1260
+ if (isObject(value)) {
1261
+ return createStoreProxy(value);
1262
+ }
1263
+ return value;
1264
+ },
1265
+ set(target, prop, value, receiver) {
1266
+ const prevValue = Reflect.get(target, prop, receiver);
1267
+ if (Object.is(prevValue, value)) {
1268
+ return true;
1269
+ }
1270
+ const success = Reflect.set(target, prop, value, receiver);
1271
+ if (success) {
1272
+ const sig = getOrCreatePropertySignal(target, prop);
1273
+ sig.set(value);
1274
+ if (Array.isArray(target) && prop !== "length") {
1275
+ const lenSig = getOrCreatePropertySignal(target, "length");
1276
+ lenSig.set(target.length);
1277
+ }
1278
+ }
1279
+ return success;
1280
+ },
1281
+ deleteProperty(target, prop) {
1282
+ const hadProp = Reflect.has(target, prop);
1283
+ const success = Reflect.deleteProperty(target, prop);
1284
+ if (hadProp && success) {
1285
+ const sig = getOrCreatePropertySignal(target, prop);
1286
+ sig.set(undefined);
1287
+ }
1288
+ return success;
1289
+ }
1290
+ });
1291
+ Object.defineProperty(target, STORE_PROXY, {
1292
+ value: proxy,
1293
+ enumerable: false,
1294
+ writable: true
1295
+ });
1296
+ return proxy;
1297
+ }
1298
+ function createStore(initialState) {
1299
+ if (!isObject(initialState)) {
1300
+ throw new Error("createStore requires an object or array as initial state");
1301
+ }
1302
+ return createStoreProxy(initialState);
1303
+ }
1304
+ function mutateStore(store, mutator) {
1305
+ batch(() => {
1306
+ mutator(store);
1307
+ });
1308
+ }
1309
+ function unwrapStore(store) {
1310
+ if (isObject(store) && store[STORE_RAW]) {
1311
+ return store[STORE_RAW];
1312
+ }
1313
+ return store;
1314
+ }
1315
+ // src/query/focus-manager.ts
1316
+ class FocusManager {
1317
+ _focused;
1318
+ _listeners = new Set;
1319
+ _cleanup;
1320
+ constructor() {
1321
+ this.setupEventListener();
1322
+ }
1323
+ setupEventListener() {
1324
+ if (typeof window === "undefined" || typeof window.addEventListener === "undefined") {
1325
+ return;
1326
+ }
1327
+ const listener = () => {
1328
+ this.notify(this.isFocused());
1329
+ };
1330
+ window.addEventListener("visibilitychange", listener, false);
1331
+ window.addEventListener("focus", listener, false);
1332
+ this._cleanup = () => {
1333
+ window.removeEventListener("visibilitychange", listener);
1334
+ window.removeEventListener("focus", listener);
1335
+ };
1336
+ }
1337
+ setFocused(focused) {
1338
+ this._focused = focused;
1339
+ this.notify(this.isFocused());
1340
+ }
1341
+ isFocused() {
1342
+ if (typeof this._focused === "boolean") {
1343
+ return this._focused;
1344
+ }
1345
+ if (typeof document !== "undefined" && typeof document.visibilityState === "string") {
1346
+ return document.visibilityState !== "hidden";
1347
+ }
1348
+ return true;
1349
+ }
1350
+ subscribe(listener) {
1351
+ this._listeners.add(listener);
1352
+ return () => {
1353
+ this._listeners.delete(listener);
1354
+ };
1355
+ }
1356
+ notify(focused) {
1357
+ for (const listener of this._listeners) {
1358
+ try {
1359
+ listener(focused);
1360
+ } catch (err) {
1361
+ console.error("Error in focus listener:", err);
1362
+ }
1363
+ }
1364
+ }
1365
+ cleanup() {
1366
+ this._cleanup?.();
1367
+ this._listeners.clear();
1368
+ }
1369
+ }
1370
+ var focusManager = new FocusManager;
1371
+
1372
+ // src/query/mutation-cache.ts
1373
+ class Mutation {
1374
+ options;
1375
+ data;
1376
+ error;
1377
+ status;
1378
+ submittedAt;
1379
+ variables;
1380
+ isIdle;
1381
+ isPending;
1382
+ isLoading;
1383
+ isSuccess;
1384
+ isError;
1385
+ constructor(options) {
1386
+ this.options = options;
1387
+ this.data = signal(undefined);
1388
+ this.error = signal(null);
1389
+ this.status = signal("idle");
1390
+ this.submittedAt = signal(0);
1391
+ this.variables = signal(undefined);
1392
+ this.isIdle = computed(() => this.status() === "idle");
1393
+ this.isPending = computed(() => this.status() === "pending");
1394
+ this.isLoading = this.isPending;
1395
+ this.isSuccess = computed(() => this.status() === "success");
1396
+ this.isError = computed(() => this.status() === "error");
1397
+ }
1398
+ async execute(variables, mutateOptions) {
1399
+ this.status.set("pending");
1400
+ this.error.set(null);
1401
+ this.variables.set(variables);
1402
+ this.submittedAt.set(Date.now());
1403
+ let context;
1404
+ try {
1405
+ if (this.options.onMutate) {
1406
+ const res = await this.options.onMutate(variables);
1407
+ if (res !== undefined) {
1408
+ context = res;
1409
+ }
1410
+ }
1411
+ const result = await this.options.mutationFn(variables);
1412
+ this.data.set(result);
1413
+ this.status.set("success");
1414
+ try {
1415
+ await this.options.onSuccess?.(result, variables, context);
1416
+ } catch (err) {
1417
+ console.error("Error in mutation onSuccess:", err);
1418
+ }
1419
+ try {
1420
+ mutateOptions?.onSuccess?.(result, variables, context);
1421
+ } catch (err) {
1422
+ console.error("Error in mutateOptions onSuccess:", err);
1423
+ }
1424
+ try {
1425
+ await this.options.onSettled?.(result, null, variables, context);
1426
+ } catch (err) {
1427
+ console.error("Error in mutation onSettled:", err);
1428
+ }
1429
+ try {
1430
+ mutateOptions?.onSettled?.(result, null, variables, context);
1431
+ } catch (err) {
1432
+ console.error("Error in mutateOptions onSettled:", err);
1433
+ }
1434
+ return result;
1435
+ } catch (err) {
1436
+ this.error.set(err);
1437
+ this.status.set("error");
1438
+ try {
1439
+ await this.options.onError?.(err, variables, context);
1440
+ } catch (e) {
1441
+ console.error("Error in mutation onError:", e);
1442
+ }
1443
+ try {
1444
+ mutateOptions?.onError?.(err, variables, context);
1445
+ } catch (e) {
1446
+ console.error("Error in mutateOptions onError:", e);
1447
+ }
1448
+ try {
1449
+ await this.options.onSettled?.(undefined, err, variables, context);
1450
+ } catch (e) {
1451
+ console.error("Error in mutation onSettled:", e);
1452
+ }
1453
+ try {
1454
+ mutateOptions?.onSettled?.(undefined, err, variables, context);
1455
+ } catch (e) {
1456
+ console.error("Error in mutateOptions onSettled:", e);
1457
+ }
1458
+ throw err;
1459
+ }
1460
+ }
1461
+ reset() {
1462
+ this.data.set(undefined);
1463
+ this.error.set(null);
1464
+ this.status.set("idle");
1465
+ this.submittedAt.set(0);
1466
+ this.variables.set(undefined);
1467
+ }
1468
+ }
1469
+
1470
+ class MutationCache {
1471
+ mutations = new Set;
1472
+ build(options) {
1473
+ const mutation = new Mutation(options);
1474
+ this.mutations.add(mutation);
1475
+ return mutation;
1476
+ }
1477
+ remove(mutation) {
1478
+ this.mutations.delete(mutation);
1479
+ }
1480
+ clear() {
1481
+ this.mutations.clear();
1482
+ }
1483
+ }
1484
+
1485
+ // src/query/online-manager.ts
1486
+ class OnlineManager {
1487
+ _online;
1488
+ _listeners = new Set;
1489
+ _cleanup;
1490
+ constructor() {
1491
+ this.setupEventListener();
1492
+ }
1493
+ setupEventListener() {
1494
+ if (typeof window === "undefined" || typeof window.addEventListener === "undefined") {
1495
+ return;
1496
+ }
1497
+ const onlineListener = () => this.notify(true);
1498
+ const offlineListener = () => this.notify(false);
1499
+ window.addEventListener("online", onlineListener, false);
1500
+ window.addEventListener("offline", offlineListener, false);
1501
+ this._cleanup = () => {
1502
+ window.removeEventListener("online", onlineListener);
1503
+ window.removeEventListener("offline", offlineListener);
1504
+ };
1505
+ }
1506
+ setOnline(online) {
1507
+ this._online = online;
1508
+ this.notify(this.isOnline());
1509
+ }
1510
+ isOnline() {
1511
+ if (typeof this._online === "boolean") {
1512
+ return this._online;
1513
+ }
1514
+ if (typeof navigator !== "undefined" && typeof navigator.onLine === "boolean") {
1515
+ return navigator.onLine;
1516
+ }
1517
+ return true;
1518
+ }
1519
+ subscribe(listener) {
1520
+ this._listeners.add(listener);
1521
+ return () => {
1522
+ this._listeners.delete(listener);
1523
+ };
1524
+ }
1525
+ notify(online) {
1526
+ for (const listener of this._listeners) {
1527
+ try {
1528
+ listener(online);
1529
+ } catch (err) {
1530
+ console.error("Error in online listener:", err);
1531
+ }
1532
+ }
1533
+ }
1534
+ cleanup() {
1535
+ this._cleanup?.();
1536
+ this._listeners.clear();
1537
+ }
1538
+ }
1539
+ var onlineManager = new OnlineManager;
1540
+
1541
+ // src/query/utils.ts
1542
+ function isPlainObject(value) {
1543
+ if (value === null || typeof value !== "object")
1544
+ return false;
1545
+ const proto = Object.getPrototypeOf(value);
1546
+ return proto === null || proto === Object.prototype;
1547
+ }
1548
+ function hashKey(queryKey) {
1549
+ return JSON.stringify(queryKey, (_key, val) => {
1550
+ if (isPlainObject(val)) {
1551
+ return Object.keys(val).sort().reduce((result, k) => {
1552
+ result[k] = val[k];
1553
+ return result;
1554
+ }, {});
1555
+ }
1556
+ return val;
1557
+ });
1558
+ }
1559
+ function deepEqual(a, b) {
1560
+ if (Object.is(a, b))
1561
+ return true;
1562
+ if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
1563
+ return false;
1564
+ }
1565
+ if (Array.isArray(a) !== Array.isArray(b))
1566
+ return false;
1567
+ if (Array.isArray(a) && Array.isArray(b)) {
1568
+ if (a.length !== b.length)
1569
+ return false;
1570
+ for (let i = 0;i < a.length; i++) {
1571
+ if (!deepEqual(a[i], b[i]))
1572
+ return false;
1573
+ }
1574
+ return true;
1575
+ }
1576
+ const keysA = Object.keys(a);
1577
+ const keysB = Object.keys(b);
1578
+ if (keysA.length !== keysB.length)
1579
+ return false;
1580
+ for (const key of keysA) {
1581
+ if (!Object.hasOwn(b, key))
1582
+ return false;
1583
+ if (!deepEqual(a[key], b[key])) {
1584
+ return false;
1585
+ }
1586
+ }
1587
+ return true;
1588
+ }
1589
+ function partialDeepEqual(a, b) {
1590
+ if (Object.is(a, b))
1591
+ return true;
1592
+ if (a === undefined || a === null || b === undefined || b === null) {
1593
+ return a === b;
1594
+ }
1595
+ if (Array.isArray(a) && Array.isArray(b)) {
1596
+ if (a.length > b.length)
1597
+ return false;
1598
+ for (let i = 0;i < a.length; i++) {
1599
+ if (!partialDeepEqual(a[i], b[i]))
1600
+ return false;
1601
+ }
1602
+ return true;
1603
+ }
1604
+ if (isPlainObject(a) && isPlainObject(b)) {
1605
+ for (const key of Object.keys(a)) {
1606
+ if (!(key in b))
1607
+ return false;
1608
+ if (!partialDeepEqual(a[key], b[key]))
1609
+ return false;
1610
+ }
1611
+ return true;
1612
+ }
1613
+ return false;
1614
+ }
1615
+ function matchQuery(filters, query) {
1616
+ if (!filters)
1617
+ return true;
1618
+ if (filters.queryKey !== undefined) {
1619
+ const filterKey = Array.isArray(filters.queryKey) ? filters.queryKey : [filters.queryKey];
1620
+ const queryKey = Array.isArray(query.queryKey) ? query.queryKey : [query.queryKey];
1621
+ if (filters.exact) {
1622
+ if (filterKey.length !== queryKey.length)
1623
+ return false;
1624
+ if (!deepEqual(filterKey, queryKey))
1625
+ return false;
1626
+ } else {
1627
+ if (filterKey.length > queryKey.length)
1628
+ return false;
1629
+ for (let i = 0;i < filterKey.length; i++) {
1630
+ if (!partialDeepEqual(filterKey[i], queryKey[i])) {
1631
+ return false;
1632
+ }
1633
+ }
1634
+ }
1635
+ }
1636
+ if (filters.type === "active" && query.observers.size === 0) {
1637
+ return false;
1638
+ }
1639
+ if (filters.type === "inactive" && query.observers.size > 0) {
1640
+ return false;
1641
+ }
1642
+ if (filters.stale !== undefined && query.isStale() !== filters.stale) {
1643
+ return false;
1644
+ }
1645
+ if (filters.fetchStatus !== undefined && query.fetchStatus() !== filters.fetchStatus) {
1646
+ return false;
1647
+ }
1648
+ if (filters.predicate && !filters.predicate(query)) {
1649
+ return false;
1650
+ }
1651
+ return true;
1652
+ }
1653
+ function sleep(ms) {
1654
+ return new Promise((resolve) => setTimeout(resolve, ms));
1655
+ }
1656
+
1657
+ // src/query/query-cache.ts
1658
+ function shouldRetry(failureCount, error, retry) {
1659
+ if (retry === false)
1660
+ return false;
1661
+ if (retry === true)
1662
+ return true;
1663
+ if (typeof retry === "number")
1664
+ return failureCount <= retry;
1665
+ if (typeof retry === "function")
1666
+ return retry(failureCount, error);
1667
+ return failureCount < 3;
1668
+ }
1669
+ function getRetryDelay(attempt, error, retryDelay) {
1670
+ if (typeof retryDelay === "function")
1671
+ return retryDelay(attempt, error);
1672
+ if (typeof retryDelay === "number")
1673
+ return retryDelay;
1674
+ return Math.min(1000 * 2 ** attempt, 30000);
1675
+ }
1676
+
1677
+ class Query {
1678
+ queryKey;
1679
+ queryHash;
1680
+ cache;
1681
+ data;
1682
+ error;
1683
+ status;
1684
+ fetchStatus;
1685
+ dataUpdatedAt;
1686
+ errorUpdatedAt;
1687
+ failureCount;
1688
+ isInvalidated;
1689
+ observers = new Set;
1690
+ _gcTimeout = null;
1691
+ _abortController = null;
1692
+ promise = null;
1693
+ defaultGcTime = 5 * 60 * 1000;
1694
+ constructor(options) {
1695
+ this.queryKey = options.queryKey;
1696
+ this.queryHash = options.queryHash;
1697
+ this.cache = options.cache;
1698
+ const initialData = typeof options.initialData === "function" ? options.initialData() : options.initialData;
1699
+ const initialUpdatedAt = initialData !== undefined ? typeof options.initialDataUpdatedAt === "function" ? options.initialDataUpdatedAt() : options.initialDataUpdatedAt ?? Date.now() : 0;
1700
+ this.data = signal(initialData);
1701
+ this.error = signal(null);
1702
+ this.status = signal(initialData !== undefined ? "success" : "pending");
1703
+ this.fetchStatus = signal("idle");
1704
+ this.dataUpdatedAt = signal(initialUpdatedAt);
1705
+ this.errorUpdatedAt = signal(0);
1706
+ this.failureCount = signal(0);
1707
+ this.isInvalidated = signal(false);
1708
+ }
1709
+ get state() {
1710
+ return {
1711
+ data: this.data(),
1712
+ error: this.error(),
1713
+ status: this.status(),
1714
+ fetchStatus: this.fetchStatus(),
1715
+ dataUpdatedAt: this.dataUpdatedAt(),
1716
+ errorUpdatedAt: this.errorUpdatedAt(),
1717
+ failureCount: this.failureCount(),
1718
+ isInvalidated: this.isInvalidated()
1719
+ };
1720
+ }
1721
+ isStale(staleTime = 0) {
1722
+ if (this.isInvalidated())
1723
+ return true;
1724
+ if (this.dataUpdatedAt() === 0)
1725
+ return true;
1726
+ if (staleTime === Number.POSITIVE_INFINITY)
1727
+ return false;
1728
+ return Date.now() - this.dataUpdatedAt() >= staleTime;
1729
+ }
1730
+ addObserver(observer) {
1731
+ this.observers.add(observer);
1732
+ this.clearGcTimeout();
1733
+ }
1734
+ removeObserver(observer) {
1735
+ this.observers.delete(observer);
1736
+ if (this.observers.size === 0) {
1737
+ this.scheduleGc(observer.options.gcTime);
1738
+ }
1739
+ }
1740
+ scheduleGc(gcTime) {
1741
+ this.clearGcTimeout();
1742
+ const duration = gcTime !== undefined ? gcTime : this.defaultGcTime;
1743
+ if (duration === Number.POSITIVE_INFINITY)
1744
+ return;
1745
+ if (duration <= 0) {
1746
+ this.cache.remove(this);
1747
+ return;
1748
+ }
1749
+ this._gcTimeout = setTimeout(() => {
1750
+ if (this.observers.size === 0) {
1751
+ this.cache.remove(this);
1752
+ }
1753
+ }, duration);
1754
+ }
1755
+ clearGcTimeout() {
1756
+ if (this._gcTimeout !== null) {
1757
+ clearTimeout(this._gcTimeout);
1758
+ this._gcTimeout = null;
1759
+ }
1760
+ }
1761
+ setData(updater, options) {
1762
+ const prev = this.data();
1763
+ const nextData = typeof updater === "function" ? updater(prev) : updater;
1764
+ this.data.set(nextData);
1765
+ this.error.set(null);
1766
+ this.status.set("success");
1767
+ this.isInvalidated.set(false);
1768
+ this.dataUpdatedAt.set(options?.updatedAt ?? Date.now());
1769
+ this.notifyObservers();
1770
+ this.cache.notify({ type: "updated", query: this });
1771
+ return nextData;
1772
+ }
1773
+ invalidate() {
1774
+ this.isInvalidated.set(true);
1775
+ if (this.observers.size > 0) {
1776
+ for (const observer of this.observers) {
1777
+ observer.onQueryUpdate();
1778
+ }
1779
+ }
1780
+ }
1781
+ cancel() {
1782
+ if (this._abortController) {
1783
+ this._abortController.abort();
1784
+ this._abortController = null;
1785
+ }
1786
+ this.fetchStatus.set("idle");
1787
+ this.promise = null;
1788
+ }
1789
+ reset() {
1790
+ this.cancel();
1791
+ this.data.set(undefined);
1792
+ this.error.set(null);
1793
+ this.status.set("pending");
1794
+ this.fetchStatus.set("idle");
1795
+ this.dataUpdatedAt.set(0);
1796
+ this.errorUpdatedAt.set(0);
1797
+ this.failureCount.set(0);
1798
+ this.isInvalidated.set(false);
1799
+ this.notifyObservers();
1800
+ }
1801
+ async fetch(options, fetchOptions) {
1802
+ if (this.promise && !fetchOptions?.cancelRefetch) {
1803
+ return this.promise;
1804
+ }
1805
+ if (this._abortController && fetchOptions?.cancelRefetch) {
1806
+ this.cancel();
1807
+ }
1808
+ const controller = new AbortController;
1809
+ this._abortController = controller;
1810
+ this.fetchStatus.set("fetching");
1811
+ const queryFn = options?.queryFn;
1812
+ if (!queryFn) {
1813
+ this.fetchStatus.set("idle");
1814
+ throw new Error(`Missing queryFn for queryKey: ${JSON.stringify(this.queryKey)}`);
1815
+ }
1816
+ const execFetch = async () => {
1817
+ let attempt = 0;
1818
+ while (true) {
1819
+ if (controller.signal.aborted) {
1820
+ this.fetchStatus.set("idle");
1821
+ this.promise = null;
1822
+ throw new Error("Query was cancelled");
1823
+ }
1824
+ try {
1825
+ const result = await queryFn({
1826
+ queryKey: this.queryKey,
1827
+ signal: controller.signal,
1828
+ meta: options?.meta
1829
+ });
1830
+ if (controller.signal.aborted) {
1831
+ throw new Error("Query was cancelled");
1832
+ }
1833
+ this.setData(result);
1834
+ this.failureCount.set(0);
1835
+ this.fetchStatus.set("idle");
1836
+ this.promise = null;
1837
+ return result;
1838
+ } catch (err) {
1839
+ if (controller.signal.aborted) {
1840
+ this.fetchStatus.set("idle");
1841
+ this.promise = null;
1842
+ throw err;
1843
+ }
1844
+ attempt++;
1845
+ this.failureCount.set(attempt);
1846
+ const canRetry = shouldRetry(attempt, err, options?.retry);
1847
+ if (canRetry) {
1848
+ const delay = getRetryDelay(attempt, err, options?.retryDelay);
1849
+ if (delay > 0) {
1850
+ await sleep(delay);
1851
+ }
1852
+ continue;
1853
+ }
1854
+ this.error.set(err);
1855
+ this.errorUpdatedAt.set(Date.now());
1856
+ this.status.set("error");
1857
+ this.fetchStatus.set("idle");
1858
+ this.promise = null;
1859
+ this.notifyObservers();
1860
+ throw err;
1861
+ }
1862
+ }
1863
+ };
1864
+ this.promise = execFetch();
1865
+ return this.promise;
1866
+ }
1867
+ notifyObservers() {
1868
+ for (const observer of this.observers) {
1869
+ observer.onQueryUpdate();
1870
+ }
1871
+ }
1872
+ destroy() {
1873
+ this.cancel();
1874
+ this.clearGcTimeout();
1875
+ this.observers.clear();
1876
+ }
1877
+ }
1878
+
1879
+ class QueryCache {
1880
+ queries = new Map;
1881
+ _subscribers = new Set;
1882
+ subscribe(listener) {
1883
+ this._subscribers.add(listener);
1884
+ return () => this._subscribers.delete(listener);
1885
+ }
1886
+ notify(event) {
1887
+ for (const sub of this._subscribers) {
1888
+ sub(event);
1889
+ }
1890
+ }
1891
+ getAll() {
1892
+ return Array.from(this.queries.values());
1893
+ }
1894
+ build(_client, options) {
1895
+ const queryHash = hashKey(options.queryKey);
1896
+ let query = this.queries.get(queryHash);
1897
+ if (!query) {
1898
+ query = new Query({
1899
+ queryKey: options.queryKey,
1900
+ queryHash,
1901
+ cache: this,
1902
+ initialData: options.initialData,
1903
+ initialDataUpdatedAt: options.initialDataUpdatedAt
1904
+ });
1905
+ this.add(query);
1906
+ }
1907
+ return query;
1908
+ }
1909
+ get(queryHash) {
1910
+ return this.queries.get(queryHash);
1911
+ }
1912
+ add(query) {
1913
+ this.queries.set(query.queryHash, query);
1914
+ this.notify({ type: "added", query });
1915
+ }
1916
+ remove(query) {
1917
+ const existing = this.queries.get(query.queryHash);
1918
+ if (existing) {
1919
+ existing.destroy();
1920
+ this.queries.delete(query.queryHash);
1921
+ this.notify({ type: "removed", query: existing });
1922
+ }
1923
+ }
1924
+ find(filters) {
1925
+ return this.findAll(filters)[0];
1926
+ }
1927
+ findAll(filters) {
1928
+ const results = [];
1929
+ for (const query of this.queries.values()) {
1930
+ if (matchQuery(filters, query)) {
1931
+ results.push(query);
1932
+ }
1933
+ }
1934
+ return results;
1935
+ }
1936
+ clear() {
1937
+ for (const query of this.queries.values()) {
1938
+ query.destroy();
1939
+ }
1940
+ this.queries.clear();
1941
+ this.notify({ type: "cleared" });
1942
+ }
1943
+ }
1944
+
1945
+ // src/query/query-client.ts
1946
+ class QueryClient {
1947
+ queryCache;
1948
+ mutationCache;
1949
+ defaultOptions;
1950
+ _unsubscribeFocus;
1951
+ _unsubscribeOnline;
1952
+ _isMounted = false;
1953
+ constructor(config = {}) {
1954
+ this.defaultOptions = config.defaultOptions ?? {};
1955
+ this.queryCache = config.queryCache ?? new QueryCache;
1956
+ this.mutationCache = config.mutationCache ?? new MutationCache;
1957
+ this.mount();
1958
+ if (typeof window !== "undefined" && window.__VISAGE_DEVTOOLS__) {
1959
+ window.__VISAGE_DEVTOOLS__.attachQueryClient(this);
1960
+ }
1961
+ }
1962
+ mount() {
1963
+ if (this._isMounted)
1964
+ return;
1965
+ this._isMounted = true;
1966
+ this._unsubscribeFocus = focusManager.subscribe((focused) => {
1967
+ if (focused) {
1968
+ this.onWindowFocus();
1969
+ }
1970
+ });
1971
+ this._unsubscribeOnline = onlineManager.subscribe((online) => {
1972
+ if (online) {
1973
+ this.onOnline();
1974
+ }
1975
+ });
1976
+ }
1977
+ unmount() {
1978
+ this._isMounted = false;
1979
+ this._unsubscribeFocus?.();
1980
+ this._unsubscribeOnline?.();
1981
+ }
1982
+ onWindowFocus() {
1983
+ const activeQueries = this.queryCache.findAll({ type: "active" });
1984
+ for (const query of activeQueries) {
1985
+ const observer = Array.from(query.observers)[0];
1986
+ const options = observer?.options ?? this.defaultOptions.queries ?? {};
1987
+ const refetchOnFocus = options.refetchOnWindowFocus ?? true;
1988
+ if (refetchOnFocus === "always" || refetchOnFocus && query.isStale(options.staleTime)) {
1989
+ query.fetch(options).catch(() => {});
1990
+ }
1991
+ }
1992
+ }
1993
+ onOnline() {
1994
+ const activeQueries = this.queryCache.findAll({ type: "active" });
1995
+ for (const query of activeQueries) {
1996
+ const observer = Array.from(query.observers)[0];
1997
+ const options = observer?.options ?? this.defaultOptions.queries ?? {};
1998
+ const refetchOnReconnect = options.refetchOnReconnect ?? true;
1999
+ if (refetchOnReconnect === "always" || refetchOnReconnect && query.isStale(options.staleTime)) {
2000
+ query.fetch(options).catch(() => {});
2001
+ }
2002
+ }
2003
+ }
2004
+ getDefaultOptions() {
2005
+ return this.defaultOptions;
2006
+ }
2007
+ setDefaultOptions(options) {
2008
+ this.defaultOptions = options;
2009
+ }
2010
+ getQueryCache() {
2011
+ return this.queryCache;
2012
+ }
2013
+ getMutationCache() {
2014
+ return this.mutationCache;
2015
+ }
2016
+ getQueryData(queryKey) {
2017
+ const queryHash = hashKey(queryKey);
2018
+ const query = this.queryCache.get(queryHash);
2019
+ return query?.data();
2020
+ }
2021
+ setQueryData(queryKey, updater, options) {
2022
+ const query = this.queryCache.build(this, {
2023
+ queryKey,
2024
+ ...this.defaultOptions.queries
2025
+ });
2026
+ return query.setData(updater, options);
2027
+ }
2028
+ getQueryState(queryKey) {
2029
+ const queryHash = hashKey(queryKey);
2030
+ const query = this.queryCache.get(queryHash);
2031
+ return query?.state;
2032
+ }
2033
+ async fetchQuery(options) {
2034
+ const mergedOptions = {
2035
+ ...this.defaultOptions.queries,
2036
+ ...options
2037
+ };
2038
+ const query = this.queryCache.build(this, mergedOptions);
2039
+ if (!query.isStale(mergedOptions.staleTime) && query.data() !== undefined) {
2040
+ return query.data();
2041
+ }
2042
+ return query.fetch(mergedOptions);
2043
+ }
2044
+ async prefetchQuery(options) {
2045
+ try {
2046
+ await this.fetchQuery(options);
2047
+ } catch {}
2048
+ }
2049
+ async invalidateQueries(filters) {
2050
+ const queries = this.queryCache.findAll(filters);
2051
+ const promises = [];
2052
+ for (const query of queries) {
2053
+ query.invalidate();
2054
+ if (query.observers.size > 0) {
2055
+ const observer = Array.from(query.observers)[0];
2056
+ const options = observer?.options ?? this.defaultOptions.queries ?? {};
2057
+ promises.push(query.fetch(options).catch(() => {}));
2058
+ }
2059
+ }
2060
+ await Promise.all(promises);
2061
+ }
2062
+ async refetchQueries(filters) {
2063
+ const queries = this.queryCache.findAll(filters);
2064
+ const promises = [];
2065
+ for (const query of queries) {
2066
+ if (query.observers.size > 0) {
2067
+ const observer = Array.from(query.observers)[0];
2068
+ const options = observer?.options ?? this.defaultOptions.queries ?? {};
2069
+ promises.push(query.fetch(options, { cancelRefetch: true }).catch(() => {}));
2070
+ }
2071
+ }
2072
+ await Promise.all(promises);
2073
+ }
2074
+ cancelQueries(filters) {
2075
+ const queries = this.queryCache.findAll(filters);
2076
+ for (const query of queries) {
2077
+ query.cancel();
2078
+ }
2079
+ }
2080
+ removeQueries(filters) {
2081
+ const queries = this.queryCache.findAll(filters);
2082
+ for (const query of queries) {
2083
+ this.queryCache.remove(query);
2084
+ }
2085
+ }
2086
+ async resetQueries(filters) {
2087
+ const queries = this.queryCache.findAll(filters);
2088
+ const promises = [];
2089
+ for (const query of queries) {
2090
+ query.reset();
2091
+ if (query.observers.size > 0) {
2092
+ const observer = Array.from(query.observers)[0];
2093
+ const options = observer?.options ?? this.defaultOptions.queries ?? {};
2094
+ promises.push(query.fetch(options).catch(() => {}));
2095
+ }
2096
+ }
2097
+ await Promise.all(promises);
2098
+ }
2099
+ clear() {
2100
+ this.queryCache.clear();
2101
+ this.mutationCache.clear();
2102
+ }
2103
+ }
2104
+ var QueryClientContext = createContext(null);
2105
+ var defaultGlobalClient = null;
2106
+ function getDefaultGlobalClient() {
2107
+ if (!defaultGlobalClient) {
2108
+ defaultGlobalClient = new QueryClient;
2109
+ }
2110
+ return defaultGlobalClient;
2111
+ }
2112
+ function QueryClientProvider(props) {
2113
+ return QueryClientContext.Provider({
2114
+ value: props.client,
2115
+ children: props.children
2116
+ });
2117
+ }
2118
+ function useQueryClient(client) {
2119
+ if (client)
2120
+ return client;
2121
+ const ctxClient = useContext(QueryClientContext);
2122
+ return ctxClient ?? getDefaultGlobalClient();
2123
+ }
2124
+
2125
+ // src/query/create-mutation.ts
2126
+ function createMutation(options, client) {
2127
+ const queryClient = useQueryClient(client);
2128
+ const resolveOptions = () => typeof options === "function" ? options() : options;
2129
+ const defaults = queryClient.getDefaultOptions().mutations ?? {};
2130
+ const mergedOpts = { ...defaults, ...resolveOptions() };
2131
+ const mutation = queryClient.mutationCache.build(mergedOpts);
2132
+ const mutate = (variables, mutateOptions) => {
2133
+ mutation.execute(variables, mutateOptions).catch(() => {});
2134
+ };
2135
+ const mutateAsync = (variables, mutateOptions) => {
2136
+ return mutation.execute(variables, mutateOptions);
2137
+ };
2138
+ const accessor = () => {
2139
+ return mutation.data();
2140
+ };
2141
+ Object.defineProperties(accessor, {
2142
+ [SIGNAL_BRAND]: { value: true },
2143
+ [COMPUTED_BRAND]: { value: true },
2144
+ value: { get: () => mutation.data() },
2145
+ mutate: { value: mutate },
2146
+ mutateAsync: { value: mutateAsync },
2147
+ data: { get: () => mutation.data },
2148
+ error: { get: () => mutation.error },
2149
+ status: { get: () => mutation.status },
2150
+ isIdle: { get: () => mutation.isIdle },
2151
+ isPending: { get: () => mutation.isPending },
2152
+ isLoading: { get: () => mutation.isLoading },
2153
+ isSuccess: { get: () => mutation.isSuccess },
2154
+ isError: { get: () => mutation.isError },
2155
+ reset: { value: () => mutation.reset() },
2156
+ variables: { get: () => mutation.variables },
2157
+ submittedAt: { get: () => mutation.submittedAt }
2158
+ });
2159
+ return accessor;
2160
+ }
2161
+
2162
+ // src/dom/resource.ts
2163
+ var SuspenseContext = createContext(null);
2164
+ function createResource(sourceOrFetcher, maybeFetcher) {
2165
+ const isDirect = typeof maybeFetcher !== "function";
2166
+ const fetcher = isDirect ? sourceOrFetcher : maybeFetcher;
2167
+ const source = isDirect ? null : sourceOrFetcher;
2168
+ const data = signal(undefined);
2169
+ const loading = signal(true);
2170
+ const error = signal(null);
2171
+ let currentPromise = null;
2172
+ let requestId = 0;
2173
+ const load = async (arg) => {
2174
+ const currentReq = ++requestId;
2175
+ loading.set(true);
2176
+ error.set(null);
2177
+ const promise = isDirect ? fetcher() : fetcher(arg);
2178
+ currentPromise = promise;
2179
+ const suspense = useContext(SuspenseContext);
2180
+ if (suspense) {
2181
+ suspense.registerPromise(promise);
2182
+ }
2183
+ try {
2184
+ const res = await promise;
2185
+ if (currentReq === requestId) {
2186
+ data.set(res);
2187
+ loading.set(false);
2188
+ currentPromise = null;
2189
+ }
2190
+ return res;
2191
+ } catch (err) {
2192
+ if (currentReq === requestId) {
2193
+ error.set(err);
2194
+ loading.set(false);
2195
+ currentPromise = null;
2196
+ }
2197
+ throw err;
2198
+ }
2199
+ };
2200
+ if (source) {
2201
+ effect(() => {
2202
+ const sourceVal = resolveValue(source);
2203
+ if (sourceVal !== false && sourceVal !== null && sourceVal !== undefined) {
2204
+ load(sourceVal).catch(() => {});
2205
+ }
2206
+ });
2207
+ } else {
2208
+ load().catch(() => {});
2209
+ }
2210
+ const accessor = () => {
2211
+ if (loading() && currentPromise) {
2212
+ const suspense = useContext(SuspenseContext);
2213
+ if (suspense) {
2214
+ suspense.registerPromise(currentPromise);
2215
+ }
2216
+ }
2217
+ return data();
2218
+ };
2219
+ Object.defineProperties(accessor, {
2220
+ loading: { get: () => loading },
2221
+ error: { get: () => error },
2222
+ mutate: { value: (val) => data.set(val) },
2223
+ refetch: { value: () => source ? load(resolveValue(source)) : load() }
2224
+ });
2225
+ return accessor;
2226
+ }
2227
+ // src/dom/async.ts
2228
+ function Suspense(props) {
2229
+ if (typeof document === "undefined") {
2230
+ return typeof props.children === "function" ? props.children() : props.children;
2231
+ }
2232
+ const marker = document.createTextNode("");
2233
+ const initialFrag = document.createDocumentFragment();
2234
+ const pendingCount = signal(0);
2235
+ let currentScope = null;
2236
+ let currentNodes = [];
2237
+ let isMounted = false;
2238
+ const suspenseValue = {
2239
+ registerPromise(p) {
2240
+ pendingCount.update((c) => c + 1);
2241
+ p.finally(() => {
2242
+ pendingCount.update((c) => Math.max(0, c - 1));
2243
+ });
2244
+ }
2245
+ };
2246
+ const renderContent = (content) => {
2247
+ const parent = marker.parentNode ?? (!isMounted ? initialFrag : null);
2248
+ if (!parent)
2249
+ return;
2250
+ for (const n of currentNodes) {
2251
+ if (n.parentNode)
2252
+ n.parentNode.removeChild(n);
2253
+ }
2254
+ currentNodes = [];
2255
+ const frag = document.createDocumentFragment();
2256
+ insertChild(frag, content);
2257
+ currentNodes = Array.from(frag.childNodes);
2258
+ if (marker.parentNode) {
2259
+ marker.parentNode.insertBefore(frag, marker);
2260
+ } else if (!isMounted) {
2261
+ initialFrag.appendChild(frag);
2262
+ }
2263
+ };
2264
+ effect(() => {
2265
+ if (currentScope) {
2266
+ currentScope.dispose();
2267
+ currentScope = null;
2268
+ }
2269
+ currentScope = new Scope(getActiveScope());
2270
+ runInScope(currentScope, () => {
2271
+ let threwPromise = null;
2272
+ let rendered = null;
2273
+ try {
2274
+ const SuspenseProvider = SuspenseContext.Provider;
2275
+ rendered = SuspenseProvider({
2276
+ value: suspenseValue,
2277
+ children: props.children
2278
+ });
2279
+ } catch (err) {
2280
+ if (err && typeof err.then === "function") {
2281
+ threwPromise = err;
2282
+ suspenseValue.registerPromise(err);
2283
+ } else {
2284
+ throw err;
2285
+ }
2286
+ }
2287
+ if (pendingCount() > 0 || threwPromise) {
2288
+ renderContent(props.fallback);
2289
+ } else {
2290
+ renderContent(rendered);
2291
+ }
2292
+ });
2293
+ });
2294
+ isMounted = true;
2295
+ initialFrag.appendChild(marker);
2296
+ onCleanup(() => {
2297
+ if (currentScope)
2298
+ currentScope.dispose();
2299
+ });
2300
+ return initialFrag;
2301
+ }
2302
+ function ErrorBoundary(props) {
2303
+ if (typeof document === "undefined") {
2304
+ try {
2305
+ return typeof props.children === "function" ? props.children() : props.children;
2306
+ } catch (err) {
2307
+ return typeof props.fallback === "function" ? props.fallback(err, () => {}) : props.fallback;
2308
+ }
2309
+ }
2310
+ const marker = document.createTextNode("");
2311
+ const initialFrag = document.createDocumentFragment();
2312
+ const currentError = signal(null);
2313
+ let currentScope = null;
2314
+ let currentNodes = [];
2315
+ let isMounted = false;
2316
+ const reset = () => {
2317
+ currentError.set(null);
2318
+ };
2319
+ const renderNodes = (child) => {
2320
+ const parent = marker.parentNode ?? (!isMounted ? initialFrag : null);
2321
+ if (!parent)
2322
+ return;
2323
+ for (const n of currentNodes) {
2324
+ if (n.parentNode)
2325
+ n.parentNode.removeChild(n);
2326
+ }
2327
+ currentNodes = [];
2328
+ const frag = document.createDocumentFragment();
2329
+ insertChild(frag, child);
2330
+ currentNodes = Array.from(frag.childNodes);
2331
+ if (marker.parentNode) {
2332
+ marker.parentNode.insertBefore(frag, marker);
2333
+ } else if (!isMounted) {
2334
+ initialFrag.appendChild(frag);
2335
+ }
2336
+ };
2337
+ effect(() => {
2338
+ const err = currentError();
2339
+ if (currentScope) {
2340
+ currentScope.dispose();
2341
+ currentScope = null;
2342
+ }
2343
+ currentScope = new Scope(getActiveScope());
2344
+ runInScope(currentScope, () => {
2345
+ if (err !== null) {
2346
+ const fallbackNode = typeof props.fallback === "function" ? props.fallback(err, reset) : props.fallback;
2347
+ renderNodes(fallbackNode);
2348
+ } else {
2349
+ try {
2350
+ const content = typeof props.children === "function" ? props.children() : props.children;
2351
+ renderNodes(content);
2352
+ } catch (catchedErr) {
2353
+ currentError.set(catchedErr);
2354
+ }
2355
+ }
2356
+ });
2357
+ });
2358
+ isMounted = true;
2359
+ initialFrag.appendChild(marker);
2360
+ onCleanup(() => {
2361
+ if (currentScope)
2362
+ currentScope.dispose();
2363
+ });
2364
+ return initialFrag;
2365
+ }
2366
+
2367
+ // src/query/create-query.ts
2368
+ function createQuery(options, client) {
2369
+ const queryClient = useQueryClient(client);
2370
+ const resolveOptions = () => typeof options === "function" ? options() : options;
2371
+ const currentQuery = signal(null);
2372
+ const version = signal(0);
2373
+ let activeObserver = null;
2374
+ let intervalTimer = null;
2375
+ const clearIntervalTimer = () => {
2376
+ if (intervalTimer !== null) {
2377
+ clearInterval(intervalTimer);
2378
+ intervalTimer = null;
2379
+ }
2380
+ };
2381
+ effect(() => {
2382
+ const rawOpts = resolveOptions();
2383
+ const defaults = queryClient.getDefaultOptions().queries ?? {};
2384
+ const mergedOpts = {
2385
+ ...defaults,
2386
+ ...rawOpts
2387
+ };
2388
+ const isEnabled = typeof mergedOpts.enabled === "function" ? mergedOpts.enabled() : mergedOpts.enabled !== false;
2389
+ const targetQuery = queryClient.queryCache.build(queryClient, mergedOpts);
2390
+ if (untrack(currentQuery) !== targetQuery || !activeObserver) {
2391
+ const prevQ = untrack(currentQuery);
2392
+ if (prevQ && activeObserver) {
2393
+ prevQ.removeObserver(activeObserver);
2394
+ }
2395
+ const observer = {
2396
+ options: mergedOpts,
2397
+ onQueryUpdate: () => {
2398
+ version.update((v) => v + 1);
2399
+ }
2400
+ };
2401
+ targetQuery.addObserver(observer);
2402
+ activeObserver = observer;
2403
+ currentQuery.set(targetQuery);
2404
+ } else if (activeObserver) {
2405
+ activeObserver.options = mergedOpts;
2406
+ }
2407
+ clearIntervalTimer();
2408
+ const refetchInterval = typeof mergedOpts.refetchInterval === "function" ? mergedOpts.refetchInterval() : mergedOpts.refetchInterval;
2409
+ if (refetchInterval && refetchInterval > 0) {
2410
+ intervalTimer = setInterval(() => {
2411
+ if (focusManager.isFocused() || mergedOpts.refetchIntervalInBackground) {
2412
+ targetQuery.fetch(mergedOpts).catch(() => {});
2413
+ }
2414
+ }, refetchInterval);
2415
+ }
2416
+ if (isEnabled) {
2417
+ untrack(() => {
2418
+ if (targetQuery.isStale(mergedOpts.staleTime) && targetQuery.fetchStatus() === "idle") {
2419
+ const fetchPromise = targetQuery.fetch(mergedOpts);
2420
+ fetchPromise.catch(() => {});
2421
+ const suspense = useContext(SuspenseContext);
2422
+ if (suspense) {
2423
+ suspense.registerPromise(fetchPromise);
2424
+ }
2425
+ }
2426
+ });
2427
+ }
2428
+ });
2429
+ onCleanup(() => {
2430
+ clearIntervalTimer();
2431
+ if (currentQuery() && activeObserver) {
2432
+ currentQuery().removeObserver(activeObserver);
2433
+ }
2434
+ });
2435
+ const data = computed(() => {
2436
+ version();
2437
+ const q = currentQuery();
2438
+ const rawOpts = resolveOptions();
2439
+ const rawData = q?.data();
2440
+ if (rawData !== undefined) {
2441
+ if (rawOpts.select) {
2442
+ return rawOpts.select(rawData);
2443
+ }
2444
+ return rawData;
2445
+ }
2446
+ if (rawOpts.placeholderData !== undefined) {
2447
+ return typeof rawOpts.placeholderData === "function" ? rawOpts.placeholderData(undefined) : rawOpts.placeholderData;
2448
+ }
2449
+ return;
2450
+ });
2451
+ const error = computed(() => {
2452
+ version();
2453
+ return currentQuery()?.error() ?? null;
2454
+ });
2455
+ const status = computed(() => {
2456
+ version();
2457
+ return currentQuery()?.status() ?? "pending";
2458
+ });
2459
+ const fetchStatus = computed(() => {
2460
+ version();
2461
+ return currentQuery()?.fetchStatus() ?? "idle";
2462
+ });
2463
+ const isLoading = computed(() => {
2464
+ return status() === "pending" && fetchStatus() === "fetching";
2465
+ });
2466
+ const isFetching = computed(() => {
2467
+ return fetchStatus() === "fetching";
2468
+ });
2469
+ const isSuccess = computed(() => {
2470
+ return status() === "success";
2471
+ });
2472
+ const isError = computed(() => {
2473
+ return status() === "error";
2474
+ });
2475
+ const isStale = computed(() => {
2476
+ version();
2477
+ const q = currentQuery();
2478
+ const rawOpts = resolveOptions();
2479
+ return q?.isStale(rawOpts.staleTime) ?? true;
2480
+ });
2481
+ const dataUpdatedAt = computed(() => {
2482
+ version();
2483
+ return currentQuery()?.dataUpdatedAt() ?? 0;
2484
+ });
2485
+ const errorUpdatedAt = computed(() => {
2486
+ version();
2487
+ return currentQuery()?.errorUpdatedAt() ?? 0;
2488
+ });
2489
+ const refetch = async (refetchOpts) => {
2490
+ const q = untrack(() => currentQuery());
2491
+ if (!q)
2492
+ return;
2493
+ const rawOpts = resolveOptions();
2494
+ const defaults = queryClient.getDefaultOptions().queries ?? {};
2495
+ const mergedOpts = { ...defaults, ...rawOpts };
2496
+ const rawResult = await q.fetch(mergedOpts, { cancelRefetch: refetchOpts?.cancelRefetch });
2497
+ if (rawOpts.select) {
2498
+ return rawOpts.select(rawResult);
2499
+ }
2500
+ return rawResult;
2501
+ };
2502
+ const accessor = () => {
2503
+ const q = currentQuery();
2504
+ const rawOpts = resolveOptions();
2505
+ if ((rawOpts.suspense || status() === "pending") && q?.promise) {
2506
+ const suspense = useContext(SuspenseContext);
2507
+ if (suspense) {
2508
+ suspense.registerPromise(q.promise);
2509
+ }
2510
+ }
2511
+ return data();
2512
+ };
2513
+ Object.defineProperties(accessor, {
2514
+ [SIGNAL_BRAND]: { value: true },
2515
+ [COMPUTED_BRAND]: { value: true },
2516
+ value: { get: () => data() },
2517
+ data: { get: () => data },
2518
+ error: { get: () => error },
2519
+ status: { get: () => status },
2520
+ fetchStatus: { get: () => fetchStatus },
2521
+ isLoading: { get: () => isLoading },
2522
+ isFetching: { get: () => isFetching },
2523
+ isSuccess: { get: () => isSuccess },
2524
+ isError: { get: () => isError },
2525
+ isStale: { get: () => isStale },
2526
+ dataUpdatedAt: { get: () => dataUpdatedAt },
2527
+ errorUpdatedAt: { get: () => errorUpdatedAt },
2528
+ refetch: { value: refetch }
2529
+ });
2530
+ return accessor;
2531
+ }
2532
+ function createInfiniteQuery(options, client) {
2533
+ const queryClient = useQueryClient(client);
2534
+ const resolveOptions = () => typeof options === "function" ? options() : options;
2535
+ const isFetchingNextPage = signal(false);
2536
+ const isFetchingPreviousPage = signal(false);
2537
+ const baseQuery = createQuery(() => {
2538
+ const rawOpts = resolveOptions();
2539
+ return {
2540
+ ...rawOpts,
2541
+ queryFn: async (ctx) => {
2542
+ const pageParam = ctx.pageParam ?? rawOpts.initialPageParam;
2543
+ const page = await rawOpts.queryFn({
2544
+ ...ctx,
2545
+ pageParam
2546
+ });
2547
+ return {
2548
+ pages: [page],
2549
+ pageParams: [pageParam]
2550
+ };
2551
+ },
2552
+ select: undefined
2553
+ };
2554
+ }, queryClient);
2555
+ const hasNextPage = computed(() => {
2556
+ const current = baseQuery.data();
2557
+ if (!current || current.pages.length === 0)
2558
+ return false;
2559
+ const rawOpts = resolveOptions();
2560
+ const lastPage = current.pages[current.pages.length - 1];
2561
+ const lastParam = current.pageParams[current.pageParams.length - 1];
2562
+ const nextParam = rawOpts.getNextPageParam(lastPage, current.pages, lastParam, current.pageParams);
2563
+ return nextParam !== undefined && nextParam !== null;
2564
+ });
2565
+ const hasPreviousPage = computed(() => {
2566
+ const current = baseQuery.data();
2567
+ if (!current || current.pages.length === 0)
2568
+ return false;
2569
+ const rawOpts = resolveOptions();
2570
+ if (!rawOpts.getPreviousPageParam)
2571
+ return false;
2572
+ const firstPage = current.pages[0];
2573
+ const firstParam = current.pageParams[0];
2574
+ const prevParam = rawOpts.getPreviousPageParam(firstPage, current.pages, firstParam, current.pageParams);
2575
+ return prevParam !== undefined && prevParam !== null;
2576
+ });
2577
+ const fetchNextPage = async () => {
2578
+ const current = baseQuery.data();
2579
+ if (!current || current.pages.length === 0)
2580
+ return;
2581
+ const rawOpts = resolveOptions();
2582
+ const lastPage = current.pages[current.pages.length - 1];
2583
+ const lastParam = current.pageParams[current.pageParams.length - 1];
2584
+ const nextParam = rawOpts.getNextPageParam(lastPage, current.pages, lastParam, current.pageParams);
2585
+ if (nextParam === undefined || nextParam === null) {
2586
+ return current;
2587
+ }
2588
+ isFetchingNextPage.set(true);
2589
+ try {
2590
+ const newPage = await rawOpts.queryFn({
2591
+ queryKey: rawOpts.queryKey,
2592
+ signal: new AbortController().signal,
2593
+ pageParam: nextParam,
2594
+ meta: rawOpts.meta
2595
+ });
2596
+ const nextData = {
2597
+ pages: [...current.pages, newPage],
2598
+ pageParams: [...current.pageParams, nextParam]
2599
+ };
2600
+ queryClient.setQueryData(rawOpts.queryKey, nextData);
2601
+ return nextData;
2602
+ } finally {
2603
+ isFetchingNextPage.set(false);
2604
+ }
2605
+ };
2606
+ const fetchPreviousPage = async () => {
2607
+ const current = baseQuery.data();
2608
+ if (!current || current.pages.length === 0)
2609
+ return;
2610
+ const rawOpts = resolveOptions();
2611
+ if (!rawOpts.getPreviousPageParam)
2612
+ return current;
2613
+ const firstPage = current.pages[0];
2614
+ const firstParam = current.pageParams[0];
2615
+ const prevParam = rawOpts.getPreviousPageParam(firstPage, current.pages, firstParam, current.pageParams);
2616
+ if (prevParam === undefined || prevParam === null) {
2617
+ return current;
2618
+ }
2619
+ isFetchingPreviousPage.set(true);
2620
+ try {
2621
+ const newPage = await rawOpts.queryFn({
2622
+ queryKey: rawOpts.queryKey,
2623
+ signal: new AbortController().signal,
2624
+ pageParam: prevParam,
2625
+ meta: rawOpts.meta
2626
+ });
2627
+ const nextData = {
2628
+ pages: [newPage, ...current.pages],
2629
+ pageParams: [prevParam, ...current.pageParams]
2630
+ };
2631
+ queryClient.setQueryData(rawOpts.queryKey, nextData);
2632
+ return nextData;
2633
+ } finally {
2634
+ isFetchingPreviousPage.set(false);
2635
+ }
2636
+ };
2637
+ const accessor = () => {
2638
+ return baseQuery();
2639
+ };
2640
+ Object.defineProperties(accessor, {
2641
+ [SIGNAL_BRAND]: { value: true },
2642
+ [COMPUTED_BRAND]: { value: true },
2643
+ value: { get: () => baseQuery.data() },
2644
+ data: { get: () => baseQuery.data },
2645
+ error: { get: () => baseQuery.error },
2646
+ status: { get: () => baseQuery.status },
2647
+ fetchStatus: { get: () => baseQuery.fetchStatus },
2648
+ isLoading: { get: () => baseQuery.isLoading },
2649
+ isFetching: { get: () => baseQuery.isFetching },
2650
+ isSuccess: { get: () => baseQuery.isSuccess },
2651
+ isError: { get: () => baseQuery.isError },
2652
+ isStale: { get: () => baseQuery.isStale },
2653
+ dataUpdatedAt: { get: () => baseQuery.dataUpdatedAt },
2654
+ errorUpdatedAt: { get: () => baseQuery.errorUpdatedAt },
2655
+ refetch: { value: baseQuery.refetch },
2656
+ fetchNextPage: { value: fetchNextPage },
2657
+ fetchPreviousPage: { value: fetchPreviousPage },
2658
+ hasNextPage: { get: () => hasNextPage },
2659
+ hasPreviousPage: { get: () => hasPreviousPage },
2660
+ isFetchingNextPage: { get: () => isFetchingNextPage },
2661
+ isFetchingPreviousPage: { get: () => isFetchingPreviousPage }
2662
+ });
2663
+ return accessor;
2664
+ }
2665
+
2666
+ // src/rpc/procedure.ts
2667
+ function createProcedure(type, options, middlewares = []) {
2668
+ return {
2669
+ type,
2670
+ inputSchema: options.input,
2671
+ outputSchema: options.output,
2672
+ middlewares: [...middlewares],
2673
+ resolve: options.resolve
2674
+ };
2675
+ }
2676
+ async function executeProcedure(procedure, opts) {
2677
+ let input = opts.rawInput;
2678
+ if (procedure.inputSchema) {
2679
+ const res = validateWithSchema(procedure.inputSchema, opts.rawInput);
2680
+ if (!res.success) {
2681
+ throw new RPCError({
2682
+ code: "BAD_REQUEST",
2683
+ message: "Invalid procedure input",
2684
+ issues: res.issues
2685
+ });
2686
+ }
2687
+ input = res.data;
2688
+ }
2689
+ let currentIndex = 0;
2690
+ let currentContext = opts.ctx;
2691
+ const runPipeline = async (ctx) => {
2692
+ currentContext = ctx;
2693
+ if (currentIndex < procedure.middlewares.length) {
2694
+ const mw = procedure.middlewares[currentIndex++];
2695
+ return mw({
2696
+ ctx: currentContext,
2697
+ req: opts.req,
2698
+ signal: opts.signal,
2699
+ next: async (nextOpts) => {
2700
+ return runPipeline(nextOpts?.ctx ?? currentContext);
2701
+ }
2702
+ });
2703
+ }
2704
+ return procedure.resolve({
2705
+ input,
2706
+ ctx: currentContext,
2707
+ req: opts.req,
2708
+ signal: opts.signal
2709
+ });
2710
+ };
2711
+ const output = await runPipeline(currentContext);
2712
+ if (procedure.outputSchema && procedure.type !== "subscription") {
2713
+ const res = validateWithSchema(procedure.outputSchema, output);
2714
+ if (!res.success) {
2715
+ throw new RPCError({
2716
+ code: "INTERNAL_SERVER_ERROR",
2717
+ message: "Invalid procedure output",
2718
+ issues: res.issues
2719
+ });
2720
+ }
2721
+ return res.data;
2722
+ }
2723
+ return output;
2724
+ }
2725
+
2726
+ // src/rpc/server.ts
2727
+ function getCORSHeaders(cors, req) {
2728
+ const headers = new Headers;
2729
+ if (!cors)
2730
+ return headers;
2731
+ const opts = typeof cors === "object" ? cors : {};
2732
+ const originHeader = req.headers.get("origin") ?? "*";
2733
+ let allowOrigin = "*";
2734
+ if (typeof opts.origin === "function") {
2735
+ const res = opts.origin(originHeader);
2736
+ if (typeof res === "string")
2737
+ allowOrigin = res;
2738
+ else if (res)
2739
+ allowOrigin = originHeader;
2740
+ } else if (typeof opts.origin === "string") {
2741
+ allowOrigin = opts.origin;
2742
+ } else if (Array.isArray(opts.origin)) {
2743
+ if (opts.origin.includes(originHeader))
2744
+ allowOrigin = originHeader;
2745
+ } else {
2746
+ allowOrigin = originHeader || "*";
2747
+ }
2748
+ headers.set("Access-Control-Allow-Origin", allowOrigin);
2749
+ headers.set("Access-Control-Allow-Methods", (opts.methods ?? ["GET", "POST", "OPTIONS", "HEAD"]).join(", "));
2750
+ headers.set("Access-Control-Allow-Headers", (opts.allowedHeaders ?? ["Content-Type", "Authorization", "X-Requested-With"]).join(", "));
2751
+ if (opts.exposedHeaders) {
2752
+ headers.set("Access-Control-Expose-Headers", opts.exposedHeaders.join(", "));
2753
+ }
2754
+ if (opts.credentials) {
2755
+ headers.set("Access-Control-Allow-Credentials", "true");
2756
+ }
2757
+ if (opts.maxAge !== undefined) {
2758
+ headers.set("Access-Control-Max-Age", opts.maxAge.toString());
2759
+ }
2760
+ return headers;
2761
+ }
2762
+ function resolveProcedureFromPath(router, path) {
2763
+ const parts = path.split(/[./]/).filter(Boolean);
2764
+ let current = router._def.record;
2765
+ for (let i = 0;i < parts.length; i++) {
2766
+ const part = parts[i];
2767
+ if (!current || typeof current !== "object") {
2768
+ throw new RPCError({
2769
+ code: "NOT_FOUND",
2770
+ message: `Procedure '${path}' not found`
2771
+ });
2772
+ }
2773
+ if (i === parts.length - 1) {
2774
+ if (part in current && "type" in current[part]) {
2775
+ return current[part];
2776
+ }
2777
+ throw new RPCError({
2778
+ code: "NOT_FOUND",
2779
+ message: `Procedure '${path}' not found`
2780
+ });
2781
+ }
2782
+ if (part in current && "_def" in current[part]) {
2783
+ current = current[part]._def.record;
2784
+ } else if (part in current && typeof current[part] === "object") {
2785
+ current = current[part];
2786
+ } else {
2787
+ throw new RPCError({
2788
+ code: "NOT_FOUND",
2789
+ message: `Procedure '${path}' not found`
2790
+ });
2791
+ }
2792
+ }
2793
+ throw new RPCError({
2794
+ code: "NOT_FOUND",
2795
+ message: `Procedure '${path}' not found`
2796
+ });
2797
+ }
2798
+ function matchesRulePattern(pattern, urlPath, procedurePath) {
2799
+ if (pattern === "*")
2800
+ return true;
2801
+ const cleanUrl = urlPath.replace(/^\/+|\/+$/g, "");
2802
+ const cleanPattern = pattern.replace(/^\/+|\/+$/g, "");
2803
+ const cleanProc = procedurePath.replace(/^\/+|\/+$/g, "");
2804
+ if (cleanUrl === cleanPattern || cleanProc === cleanPattern || cleanProc === cleanPattern.replace(/\//g, ".")) {
2805
+ return true;
2806
+ }
2807
+ if (cleanPattern.endsWith("*")) {
2808
+ const prefix = cleanPattern.slice(0, -1);
2809
+ if (cleanUrl.startsWith(prefix) || cleanProc.startsWith(prefix)) {
2810
+ return true;
2811
+ }
2812
+ const dotPrefix = prefix.replace(/\//g, ".");
2813
+ if (cleanProc.startsWith(dotPrefix)) {
2814
+ return true;
2815
+ }
2816
+ }
2817
+ try {
2818
+ const regexStr = cleanPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
2819
+ const regex = new RegExp(`^${regexStr}$`);
2820
+ return regex.test(cleanUrl) || regex.test(cleanProc);
2821
+ } catch {
2822
+ return false;
2823
+ }
2824
+ }
2825
+ function createFetchHandler(options) {
2826
+ const {
2827
+ router,
2828
+ endpoint = "",
2829
+ createContext,
2830
+ onError,
2831
+ cors = true,
2832
+ securityHeaders = true,
2833
+ rules
2834
+ } = options;
2835
+ return async (request, ...extra) => {
2836
+ const corsHeaders = getCORSHeaders(cors, request);
2837
+ if (request.method === "OPTIONS") {
2838
+ return new Response(null, {
2839
+ status: 204,
2840
+ headers: corsHeaders
2841
+ });
2842
+ }
2843
+ const baseHeaders = new Headers(corsHeaders);
2844
+ if (securityHeaders) {
2845
+ if (typeof securityHeaders === "object") {
2846
+ for (const [k, v] of Object.entries(securityHeaders)) {
2847
+ baseHeaders.set(k, v);
2848
+ }
2849
+ } else {
2850
+ baseHeaders.set("X-Content-Type-Options", "nosniff");
2851
+ }
2852
+ }
2853
+ const url = new URL(request.url);
2854
+ let pathname = url.pathname;
2855
+ if (endpoint && pathname.startsWith(endpoint)) {
2856
+ pathname = pathname.slice(endpoint.length);
2857
+ }
2858
+ pathname = pathname.replace(/^\/+|\/+$/g, "");
2859
+ const procedurePath = pathname || url.searchParams.get("procedure") || "";
2860
+ let ctx = {};
2861
+ try {
2862
+ if (createContext) {
2863
+ ctx = await createContext({ req: request, extra });
2864
+ }
2865
+ } catch (err) {
2866
+ const error = err instanceof RPCError ? err : new RPCError({
2867
+ code: "UNAUTHORIZED",
2868
+ message: err.message ?? "Context initialization failed",
2869
+ cause: err
2870
+ });
2871
+ onError?.({ error, req: request });
2872
+ baseHeaders.set("Content-Type", "application/json");
2873
+ return new Response(JSON.stringify({
2874
+ error: {
2875
+ code: error.code,
2876
+ message: error.message,
2877
+ issues: error.issues
2878
+ }
2879
+ }), {
2880
+ status: error.status,
2881
+ headers: baseHeaders
2882
+ });
2883
+ }
2884
+ const rulesList = [];
2885
+ if (rules) {
2886
+ if (Array.isArray(rules)) {
2887
+ rulesList.push(...rules);
2888
+ } else {
2889
+ for (const [match, rule] of Object.entries(rules)) {
2890
+ rulesList.push({ match, ...rule });
2891
+ }
2892
+ }
2893
+ }
2894
+ const matchingRules = rulesList.filter((r) => matchesRulePattern(r.match, url.pathname, procedurePath));
2895
+ for (const rule of matchingRules) {
2896
+ if (rule.requireHeaders) {
2897
+ for (const headerName of rule.requireHeaders) {
2898
+ if (!request.headers.get(headerName)) {
2899
+ const error = new RPCError({
2900
+ code: "UNAUTHORIZED",
2901
+ message: `Missing required header '${headerName}' for rule '${rule.match}'`
2902
+ });
2903
+ onError?.({ error, path: procedurePath, req: request });
2904
+ baseHeaders.set("Content-Type", "application/json");
2905
+ return new Response(JSON.stringify({
2906
+ error: {
2907
+ code: error.code,
2908
+ message: error.message
2909
+ }
2910
+ }), {
2911
+ status: 401,
2912
+ headers: baseHeaders
2913
+ });
2914
+ }
2915
+ }
2916
+ }
2917
+ if (rule.requireParams) {
2918
+ for (const paramName of rule.requireParams) {
2919
+ if (!url.searchParams.has(paramName)) {
2920
+ const error = new RPCError({
2921
+ code: "BAD_REQUEST",
2922
+ message: `Missing required query parameter '${paramName}' for rule '${rule.match}'`
2923
+ });
2924
+ onError?.({ error, path: procedurePath, req: request });
2925
+ baseHeaders.set("Content-Type", "application/json");
2926
+ return new Response(JSON.stringify({
2927
+ error: {
2928
+ code: error.code,
2929
+ message: error.message
2930
+ }
2931
+ }), {
2932
+ status: 400,
2933
+ headers: baseHeaders
2934
+ });
2935
+ }
2936
+ }
2937
+ }
2938
+ if (rule.guard) {
2939
+ try {
2940
+ const guardResult = await rule.guard({ req: request, ctx, path: procedurePath });
2941
+ if (guardResult === false) {
2942
+ throw new RPCError({
2943
+ code: "UNAUTHORIZED",
2944
+ message: `Access denied by route rule guard for '${rule.match}'`
2945
+ });
2946
+ }
2947
+ } catch (err) {
2948
+ const error = err instanceof RPCError ? err : new RPCError({
2949
+ code: "UNAUTHORIZED",
2950
+ message: err.message ?? "Unauthorized",
2951
+ cause: err
2952
+ });
2953
+ onError?.({ error, path: procedurePath, req: request });
2954
+ baseHeaders.set("Content-Type", "application/json");
2955
+ return new Response(JSON.stringify({
2956
+ error: {
2957
+ code: error.code,
2958
+ message: error.message
2959
+ }
2960
+ }), {
2961
+ status: error.status,
2962
+ headers: baseHeaders
2963
+ });
2964
+ }
2965
+ }
2966
+ if (rule.headers) {
2967
+ const resHeaders = typeof rule.headers === "function" ? await rule.headers(request, ctx) : rule.headers;
2968
+ for (const [k, v] of Object.entries(resHeaders)) {
2969
+ baseHeaders.set(k, v);
2970
+ }
2971
+ }
2972
+ }
2973
+ if (request.method === "POST" && (!pathname || pathname === "batch")) {
2974
+ try {
2975
+ const body = await request.json();
2976
+ if (Array.isArray(body)) {
2977
+ const batchResults = await Promise.all(body.map(async (item) => {
2978
+ try {
2979
+ const proc = resolveProcedureFromPath(router, item.path);
2980
+ const data = await executeProcedure(proc, {
2981
+ rawInput: item.input,
2982
+ ctx,
2983
+ req: request,
2984
+ signal: request.signal
2985
+ });
2986
+ return {
2987
+ id: item.id,
2988
+ result: { data }
2989
+ };
2990
+ } catch (err) {
2991
+ const error = err instanceof RPCError ? err : new RPCError({
2992
+ code: "INTERNAL_SERVER_ERROR",
2993
+ message: err.message ?? "Internal Server Error",
2994
+ cause: err
2995
+ });
2996
+ onError?.({ error, path: item.path, req: request });
2997
+ return {
2998
+ id: item.id,
2999
+ error: {
3000
+ code: error.code,
3001
+ message: error.message,
3002
+ issues: error.issues
3003
+ }
3004
+ };
3005
+ }
3006
+ }));
3007
+ baseHeaders.set("Content-Type", "application/json");
3008
+ return new Response(JSON.stringify(batchResults), {
3009
+ status: 200,
3010
+ headers: baseHeaders
3011
+ });
3012
+ }
3013
+ } catch (err) {
3014
+ const error = new RPCError({
3015
+ code: "BAD_REQUEST",
3016
+ message: "Malformed batch request body",
3017
+ cause: err
3018
+ });
3019
+ baseHeaders.set("Content-Type", "application/json");
3020
+ return new Response(JSON.stringify({
3021
+ error: {
3022
+ code: error.code,
3023
+ message: error.message
3024
+ }
3025
+ }), {
3026
+ status: 400,
3027
+ headers: baseHeaders
3028
+ });
3029
+ }
3030
+ }
3031
+ try {
3032
+ const procedure = resolveProcedureFromPath(router, procedurePath);
3033
+ if (procedure.type === "mutation" && request.method !== "POST") {
3034
+ throw new RPCError({
3035
+ code: "METHOD_NOT_ALLOWED",
3036
+ message: "Mutations must use POST method"
3037
+ });
3038
+ }
3039
+ let rawInput;
3040
+ if (request.method === "GET") {
3041
+ const inputParam = url.searchParams.get("input");
3042
+ if (inputParam !== null) {
3043
+ try {
3044
+ rawInput = JSON.parse(inputParam);
3045
+ } catch {
3046
+ rawInput = inputParam;
3047
+ }
3048
+ }
3049
+ } else if (request.method === "POST") {
3050
+ try {
3051
+ const body = await request.json();
3052
+ rawInput = body && typeof body === "object" && "input" in body ? body.input : body;
3053
+ } catch {
3054
+ rawInput = undefined;
3055
+ }
3056
+ }
3057
+ if (procedure.type === "subscription") {
3058
+ const iterable = await executeProcedure(procedure, {
3059
+ rawInput,
3060
+ ctx,
3061
+ req: request,
3062
+ signal: request.signal
3063
+ });
3064
+ if (!iterable || typeof iterable[Symbol.asyncIterator] !== "function") {
3065
+ throw new RPCError({
3066
+ code: "INTERNAL_SERVER_ERROR",
3067
+ message: "Subscription procedure must return an AsyncIterable"
3068
+ });
3069
+ }
3070
+ const encoder = new TextEncoder;
3071
+ const stream = new ReadableStream({
3072
+ async start(controller) {
3073
+ try {
3074
+ for await (const chunk of iterable) {
3075
+ if (request.signal.aborted)
3076
+ break;
3077
+ const message = `data: ${JSON.stringify(chunk)}
3078
+
3079
+ `;
3080
+ controller.enqueue(encoder.encode(message));
3081
+ }
3082
+ controller.close();
3083
+ } catch (err) {
3084
+ const errorMessage = `event: error
3085
+ data: ${JSON.stringify({ message: err.message ?? "Stream error" })}
3086
+
3087
+ `;
3088
+ controller.enqueue(encoder.encode(errorMessage));
3089
+ controller.close();
3090
+ }
3091
+ },
3092
+ cancel() {
3093
+ if (typeof iterable.return === "function") {
3094
+ iterable.return();
3095
+ }
3096
+ }
3097
+ });
3098
+ baseHeaders.set("Content-Type", "text/event-stream; charset=utf-8");
3099
+ baseHeaders.set("Cache-Control", "no-cache, no-transform");
3100
+ baseHeaders.set("Connection", "keep-alive");
3101
+ return new Response(stream, {
3102
+ status: 200,
3103
+ headers: baseHeaders
3104
+ });
3105
+ }
3106
+ const data = await executeProcedure(procedure, {
3107
+ rawInput,
3108
+ ctx,
3109
+ req: request,
3110
+ signal: request.signal
3111
+ });
3112
+ const responsePayload = {
3113
+ result: { data }
3114
+ };
3115
+ baseHeaders.set("Content-Type", "application/json");
3116
+ return new Response(JSON.stringify(responsePayload), {
3117
+ status: 200,
3118
+ headers: baseHeaders
3119
+ });
3120
+ } catch (err) {
3121
+ const error = err instanceof RPCError ? err : new RPCError({
3122
+ code: "INTERNAL_SERVER_ERROR",
3123
+ message: err.message ?? "Internal Server Error",
3124
+ cause: err
3125
+ });
3126
+ onError?.({ error, path: procedurePath, req: request });
3127
+ const responsePayload = {
3128
+ error: {
3129
+ code: error.code,
3130
+ message: error.message,
3131
+ issues: error.issues
3132
+ }
3133
+ };
3134
+ baseHeaders.set("Content-Type", "application/json");
3135
+ return new Response(JSON.stringify(responsePayload), {
3136
+ status: ERROR_CODE_TO_STATUS[error.code] ?? 500,
3137
+ headers: baseHeaders
3138
+ });
3139
+ }
3140
+ };
3141
+ }
3142
+
3143
+ // src/api/client.ts
3144
+ async function resolveHeaders(headers, fullPath = "") {
3145
+ if (!headers)
3146
+ return {};
3147
+ if (typeof headers === "function") {
3148
+ return await headers();
3149
+ }
3150
+ const result = {};
3151
+ for (const [key, val] of Object.entries(headers)) {
3152
+ if (typeof val === "object" || typeof val === "function") {
3153
+ if (matchesRulePattern(key, fullPath, fullPath)) {
3154
+ const resolved = typeof val === "function" ? await val() : val;
3155
+ if (resolved && typeof resolved === "object") {
3156
+ Object.assign(result, resolved);
3157
+ }
3158
+ }
3159
+ } else if (typeof val === "string") {
3160
+ result[key] = val;
3161
+ }
3162
+ }
3163
+ return result;
3164
+ }
3165
+ function buildUrl(baseUrl, pathSegments, queryParams) {
3166
+ const cleanBase = baseUrl.replace(/\/+$/, "");
3167
+ const cleanPath = pathSegments.filter(Boolean).join("/");
3168
+ let url = cleanPath ? `${cleanBase}/${cleanPath}` : cleanBase;
3169
+ if (queryParams !== undefined && queryParams !== null) {
3170
+ if (typeof queryParams === "object") {
3171
+ const params = new URLSearchParams;
3172
+ for (const [k, v] of Object.entries(queryParams)) {
3173
+ if (v !== undefined && v !== null) {
3174
+ if (typeof v === "object") {
3175
+ params.set(k, JSON.stringify(v));
3176
+ } else {
3177
+ params.set(k, String(v));
3178
+ }
3179
+ }
3180
+ }
3181
+ const qs = params.toString();
3182
+ if (qs) {
3183
+ url += `${url.includes("?") ? "&" : "?"}${qs}`;
3184
+ }
3185
+ } else {
3186
+ url += `${url.includes("?") ? "&" : "?"}input=${encodeURIComponent(String(queryParams))}`;
3187
+ }
3188
+ }
3189
+ return url;
3190
+ }
3191
+ function createApiProxy(options, path = []) {
3192
+ const handlerFn = (...args) => {
3193
+ if (args.length > 0) {
3194
+ return createApiProxy(options, [...path, String(args[0])]);
3195
+ }
3196
+ return createApiProxy(options, path);
3197
+ };
3198
+ return new Proxy(handlerFn, {
3199
+ get(_target, prop) {
3200
+ if (prop === "then")
3201
+ return;
3202
+ const fullPath = path.join("/");
3203
+ if (prop === "get") {
3204
+ return async (input, callOpts) => {
3205
+ const fetchFn = options.fetch ?? globalThis.fetch;
3206
+ const url = buildUrl(options.baseUrl, path, input);
3207
+ const dynamicHeaders = await resolveHeaders(options.headers, fullPath);
3208
+ const headers = {
3209
+ Accept: "application/json",
3210
+ ...dynamicHeaders,
3211
+ ...callOpts?.headers
3212
+ };
3213
+ const devtools = typeof window !== "undefined" ? window.__VISAGE_DEVTOOLS__ : null;
3214
+ const logId = devtools?.logRpcCall({
3215
+ id: `api_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
3216
+ path: fullPath,
3217
+ type: "query",
3218
+ input
3219
+ });
3220
+ try {
3221
+ const res = await fetchFn(url, {
3222
+ method: "GET",
3223
+ headers,
3224
+ signal: callOpts?.signal
3225
+ });
3226
+ const contentType = res.headers.get("Content-Type") || "";
3227
+ if (contentType.includes("application/json")) {
3228
+ const body = await res.json();
3229
+ if (body && typeof body === "object" && "ok" in body) {
3230
+ if (!body.ok) {
3231
+ devtools?.updateRpcCall(logId, { status: "error", error: body.error });
3232
+ throw new RPCError({
3233
+ code: body.error?.code || "INTERNAL_SERVER_ERROR",
3234
+ message: body.error?.message || "Request failed",
3235
+ issues: body.error?.issues
3236
+ });
3237
+ }
3238
+ devtools?.updateRpcCall(logId, { status: "success", response: body.data });
3239
+ return body.data;
3240
+ }
3241
+ if (!res.ok) {
3242
+ throw new Error(`Request failed with status ${res.status}`);
3243
+ }
3244
+ devtools?.updateRpcCall(logId, { status: "success", response: body });
3245
+ return body;
3246
+ }
3247
+ if (!res.ok) {
3248
+ throw new Error(`Request failed with status ${res.status}`);
3249
+ }
3250
+ const text = await res.text();
3251
+ devtools?.updateRpcCall(logId, { status: "success", response: text });
3252
+ return text;
3253
+ } catch (err) {
3254
+ devtools?.updateRpcCall(logId, { status: "error", error: err });
3255
+ throw err;
3256
+ }
3257
+ };
3258
+ }
3259
+ if (prop === "post" || prop === "put" || prop === "patch" || prop === "delete" || prop === "del") {
3260
+ const httpMethod = prop === "del" ? "DELETE" : prop.toUpperCase();
3261
+ return async (body, callOpts) => {
3262
+ const fetchFn = options.fetch ?? globalThis.fetch;
3263
+ const url = buildUrl(options.baseUrl, path);
3264
+ const dynamicHeaders = await resolveHeaders(options.headers, fullPath);
3265
+ const headers = {
3266
+ "Content-Type": "application/json",
3267
+ Accept: "application/json",
3268
+ ...dynamicHeaders,
3269
+ ...callOpts?.headers
3270
+ };
3271
+ const devtools = typeof window !== "undefined" ? window.__VISAGE_DEVTOOLS__ : null;
3272
+ const logId = devtools?.logRpcCall({
3273
+ id: `api_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
3274
+ path: fullPath,
3275
+ type: "mutation",
3276
+ input: body
3277
+ });
3278
+ try {
3279
+ const res = await fetchFn(url, {
3280
+ method: httpMethod,
3281
+ headers,
3282
+ body: body !== undefined ? JSON.stringify(body) : undefined,
3283
+ signal: callOpts?.signal
3284
+ });
3285
+ const contentType = res.headers.get("Content-Type") || "";
3286
+ if (contentType.includes("application/json")) {
3287
+ const resBody = await res.json();
3288
+ if (resBody && typeof resBody === "object" && "ok" in resBody) {
3289
+ if (!resBody.ok) {
3290
+ devtools?.updateRpcCall(logId, { status: "error", error: resBody.error });
3291
+ throw new RPCError({
3292
+ code: resBody.error?.code || "INTERNAL_SERVER_ERROR",
3293
+ message: resBody.error?.message || "Request failed",
3294
+ issues: resBody.error?.issues
3295
+ });
3296
+ }
3297
+ devtools?.updateRpcCall(logId, { status: "success", response: resBody.data });
3298
+ return resBody.data;
3299
+ }
3300
+ if (!res.ok) {
3301
+ throw new Error(`Request failed with status ${res.status}`);
3302
+ }
3303
+ devtools?.updateRpcCall(logId, { status: "success", response: resBody });
3304
+ return resBody;
3305
+ }
3306
+ if (!res.ok) {
3307
+ throw new Error(`Request failed with status ${res.status}`);
3308
+ }
3309
+ const text = await res.text();
3310
+ devtools?.updateRpcCall(logId, { status: "success", response: text });
3311
+ return text;
3312
+ } catch (err) {
3313
+ devtools?.updateRpcCall(logId, { status: "error", error: err });
3314
+ throw err;
3315
+ }
3316
+ };
3317
+ }
3318
+ if (prop === "subscribe") {
3319
+ return (input, callbacks) => {
3320
+ const fetchFn = options.fetch ?? globalThis.fetch;
3321
+ const url = buildUrl(options.baseUrl, path, input);
3322
+ const controller = new AbortController;
3323
+ const devtools = typeof window !== "undefined" ? window.__VISAGE_DEVTOOLS__ : null;
3324
+ const logId = devtools?.logRpcCall({
3325
+ id: `api_sse_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
3326
+ path: fullPath,
3327
+ type: "subscription",
3328
+ input
3329
+ });
3330
+ (async () => {
3331
+ try {
3332
+ const dynamicHeaders = await resolveHeaders(options.headers, fullPath);
3333
+ const headers = {
3334
+ Accept: "text/event-stream",
3335
+ ...dynamicHeaders
3336
+ };
3337
+ const res = await fetchFn(url, {
3338
+ method: "GET",
3339
+ headers,
3340
+ signal: controller.signal
3341
+ });
3342
+ if (!res.ok || !res.body) {
3343
+ throw new Error(`SSE stream failed with status ${res.status}`);
3344
+ }
3345
+ const reader = res.body.getReader();
3346
+ const decoder = new TextDecoder;
3347
+ let buffer = "";
3348
+ while (true) {
3349
+ const { done, value } = await reader.read();
3350
+ if (done)
3351
+ break;
3352
+ buffer += decoder.decode(value, { stream: true });
3353
+ const lines = buffer.split(`
3354
+
3355
+ `);
3356
+ buffer = lines.pop() ?? "";
3357
+ for (const line of lines) {
3358
+ const trimmed = line.trim();
3359
+ if (trimmed.startsWith("data:")) {
3360
+ const jsonStr = trimmed.slice(5).trim();
3361
+ try {
3362
+ const parsed = JSON.parse(jsonStr);
3363
+ devtools?.logRpcEvent(logId, parsed);
3364
+ callbacks.onData(parsed);
3365
+ } catch (parseErr) {
3366
+ devtools?.updateRpcCall(logId, { status: "error", error: parseErr });
3367
+ callbacks.onError?.(parseErr);
3368
+ }
3369
+ } else if (trimmed.startsWith("event: error")) {
3370
+ devtools?.updateRpcCall(logId, { status: "error" });
3371
+ callbacks.onError?.(new Error("Server SSE error"));
3372
+ }
3373
+ }
3374
+ }
3375
+ callbacks.onComplete?.();
3376
+ } catch (err) {
3377
+ if (!controller.signal.aborted) {
3378
+ callbacks.onError?.(err);
3379
+ }
3380
+ }
3381
+ })();
3382
+ return {
3383
+ unsubscribe: () => {
3384
+ controller.abort();
3385
+ }
3386
+ };
3387
+ };
3388
+ }
3389
+ if (prop === "queryKey") {
3390
+ return (input) => {
3391
+ const resolved = typeof input === "function" ? input() : input;
3392
+ const key = [...path];
3393
+ if (resolved !== undefined)
3394
+ key.push(resolved);
3395
+ return key;
3396
+ };
3397
+ }
3398
+ if (prop === "queryOptions") {
3399
+ return (input, extra) => {
3400
+ const resolved = typeof input === "function" ? input() : input;
3401
+ const key = [...path];
3402
+ if (resolved !== undefined)
3403
+ key.push(resolved);
3404
+ return {
3405
+ queryKey: key,
3406
+ queryFn: ({ signal }) => {
3407
+ const proxy = createApiProxy(options, path);
3408
+ return proxy.get(resolved, { signal });
3409
+ },
3410
+ ...extra
3411
+ };
3412
+ };
3413
+ }
3414
+ if (prop === "createQuery" || prop === "useQuery") {
3415
+ return (input, extra) => {
3416
+ const proxy = createApiProxy(options, path);
3417
+ return createQuery(() => proxy.queryOptions(input, extra));
3418
+ };
3419
+ }
3420
+ if (prop === "mutationKey") {
3421
+ return () => [...path];
3422
+ }
3423
+ if (prop === "mutationOptions") {
3424
+ return (extra) => {
3425
+ return {
3426
+ mutationKey: [...path],
3427
+ mutationFn: (body) => {
3428
+ const proxy = createApiProxy(options, path);
3429
+ return proxy.post(body);
3430
+ },
3431
+ ...extra
3432
+ };
3433
+ };
3434
+ }
3435
+ if (prop === "createMutation" || prop === "useMutation") {
3436
+ return (extra) => {
3437
+ const proxy = createApiProxy(options, path);
3438
+ return createMutation(proxy.mutationOptions(extra));
3439
+ };
3440
+ }
3441
+ return createApiProxy(options, [...path, prop]);
3442
+ }
3443
+ });
3444
+ }
3445
+ function createApiClient(options) {
3446
+ return createApiProxy(options, []);
3447
+ }
3448
+ // src/api/pragma.ts
3449
+ var PRAGMA_REGEX = /(?:\/\/\s*@visage-middleware\s+([^\r\n]+))|(?:\/\*\s*@visage-middleware\s+([\s\S]*?)\*\/)/i;
3450
+ function parsePragma(sourceCode) {
3451
+ if (!sourceCode || typeof sourceCode !== "string") {
3452
+ return { cascade: true, match: ["*"], exclude: [], priority: 0 };
3453
+ }
3454
+ const match = sourceCode.match(PRAGMA_REGEX);
3455
+ if (!match) {
3456
+ return { cascade: true, match: ["*"], exclude: [], priority: 0 };
3457
+ }
3458
+ const rawDirective = (match[1] || match[2] || "").trim();
3459
+ const pragma = {
3460
+ cascade: true,
3461
+ match: ["*"],
3462
+ exclude: [],
3463
+ priority: 0
3464
+ };
3465
+ const pairs = rawDirective.split(/,(?![^[]*\])/);
3466
+ for (const pair of pairs) {
3467
+ const colonIdx = pair.indexOf(":");
3468
+ if (colonIdx === -1)
3469
+ continue;
3470
+ const key = pair.slice(0, colonIdx).trim().toLowerCase();
3471
+ const valStr = pair.slice(colonIdx + 1).trim();
3472
+ if (key === "cascade") {
3473
+ pragma.cascade = valStr === "true" || valStr === "1";
3474
+ } else if (key === "priority") {
3475
+ const num = Number.parseInt(valStr, 10);
3476
+ if (!Number.isNaN(num))
3477
+ pragma.priority = num;
3478
+ } else if (key === "match" || key === "exclude") {
3479
+ let items = [];
3480
+ if (valStr.startsWith("[") && valStr.endsWith("]")) {
3481
+ try {
3482
+ items = JSON.parse(valStr.replace(/'/g, '"'));
3483
+ } catch {
3484
+ items = valStr.slice(1, -1).split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
3485
+ }
3486
+ } else {
3487
+ items = [valStr.replace(/^['"]|['"]$/g, "")];
3488
+ }
3489
+ if (key === "match")
3490
+ pragma.match = items;
3491
+ if (key === "exclude")
3492
+ pragma.exclude = items;
3493
+ }
3494
+ }
3495
+ return pragma;
3496
+ }
3497
+ function globToRegExp(pattern) {
3498
+ const normalized = pattern.trim().replace(/^\/+|\/+$/g, "");
3499
+ if (normalized === "*" || normalized === "**" || normalized === "") {
3500
+ return /^.*$/;
3501
+ }
3502
+ const escaped = normalized.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "___DOUBLE_STAR___").replace(/\*/g, "[^/]+").replace(/___DOUBLE_STAR___/g, ".*");
3503
+ return new RegExp(`^(?:/)?${escaped}(?:/)?$`);
3504
+ }
3505
+ function matchesAnyPattern(routePath, patterns) {
3506
+ if (!patterns || patterns.length === 0)
3507
+ return false;
3508
+ const cleanPath = routePath.replace(/^\/+|\/+$/g, "");
3509
+ for (const pat of patterns) {
3510
+ const cleanPat = pat.replace(/^\/+|\/+$/g, "");
3511
+ if (cleanPat === "*" || cleanPat === "**")
3512
+ return true;
3513
+ if (cleanPat === cleanPath)
3514
+ return true;
3515
+ if (globToRegExp(cleanPat).test(cleanPath))
3516
+ return true;
3517
+ }
3518
+ return false;
3519
+ }
3520
+ function shouldApplyMiddleware(pragma, routePath, routeDir, middlewareDir) {
3521
+ const normRouteDir = routeDir.replace(/^\/+|\/+$/g, "");
3522
+ const normMwDir = middlewareDir.replace(/^\/+|\/+$/g, "");
3523
+ const isDirectDir = normRouteDir === normMwDir;
3524
+ const isSubDir = normMwDir === "" ? normRouteDir !== "" : normRouteDir.startsWith(`${normMwDir}/`);
3525
+ if (!isDirectDir && !isSubDir) {
3526
+ return false;
3527
+ }
3528
+ const isCascade = pragma.cascade ?? true;
3529
+ if (isSubDir && !isCascade) {
3530
+ return false;
3531
+ }
3532
+ let relativePath = routePath.replace(/^\/+|\/+$/g, "");
3533
+ if (normMwDir && relativePath.startsWith(normMwDir)) {
3534
+ relativePath = relativePath.slice(normMwDir.length).replace(/^\/+/, "");
3535
+ }
3536
+ if (pragma.exclude && pragma.exclude.length > 0) {
3537
+ if (matchesAnyPattern(routePath, pragma.exclude) || matchesAnyPattern(relativePath, pragma.exclude)) {
3538
+ return false;
3539
+ }
3540
+ }
3541
+ if (!pragma.match || pragma.match.length === 0 || pragma.match.includes("*")) {
3542
+ return true;
3543
+ }
3544
+ return matchesAnyPattern(routePath, pragma.match) || matchesAnyPattern(relativePath, pragma.match);
3545
+ }
3546
+
3547
+ // src/api/file-router.ts
3548
+ function compileRoutePattern(path) {
3549
+ const paramNames = [];
3550
+ const normalized = path.replace(/\/+$/, "") || "/";
3551
+ const regexStr = normalized.replace(/:([a-zA-Z0-9_]+)/g, (_, name) => {
3552
+ paramNames.push(name);
3553
+ return "([^/]+)";
3554
+ }).replace(/\*([a-zA-Z0-9_]+)/g, (_, name) => {
3555
+ paramNames.push(name);
3556
+ return "(.*)";
3557
+ });
3558
+ const pattern = new RegExp(`^${regexStr}(?:/)?$`);
3559
+ return { pattern, paramNames };
3560
+ }
3561
+ function normalizeFilePath(rawPath) {
3562
+ const cleaned = rawPath.replace(/^\.\//, "").replace(/\.(ts|tsx|js|jsx)$/, "").replace(/^(src\/)?(routes|api)\//, "");
3563
+ const parts = cleaned.split("/").filter(Boolean);
3564
+ const fileName = parts.pop() || "";
3565
+ const isMiddleware = fileName === "_middleware";
3566
+ const dirParts = [...parts];
3567
+ const directory = `/${dirParts.join("/")}`.replace(/\/+$/, "") || "/";
3568
+ if (isMiddleware) {
3569
+ return {
3570
+ isMiddleware: true,
3571
+ routePath: directory,
3572
+ directory
3573
+ };
3574
+ }
3575
+ if (fileName !== "index") {
3576
+ const transformedFile = fileName.replace(/\[\.\.\.([a-zA-Z0-9_]+)\]/g, "*$1").replace(/\[([a-zA-Z0-9_]+)\]/g, ":$1");
3577
+ parts.push(transformedFile);
3578
+ }
3579
+ const routePath = `/${parts.join("/")}`.replace(/\/+$/, "") || "/";
3580
+ return {
3581
+ isMiddleware: false,
3582
+ routePath,
3583
+ directory
3584
+ };
3585
+ }
3586
+ function buildFileRouter(files, sourceMap = {}) {
3587
+ const middlewaresByDir = [];
3588
+ const rawRoutes = [];
3589
+ for (const [filePath, mod] of Object.entries(files)) {
3590
+ const { isMiddleware, routePath, directory } = normalizeFilePath(filePath);
3591
+ const sourceCode = sourceMap[filePath] || "";
3592
+ const pragma = mod.pragma || parsePragma(sourceCode);
3593
+ if (isMiddleware) {
3594
+ const mwFn = typeof mod === "function" ? mod : mod.middleware || mod.default;
3595
+ if (typeof mwFn === "function") {
3596
+ middlewaresByDir.push({
3597
+ fn: mwFn,
3598
+ pragma,
3599
+ filePath,
3600
+ directory
3601
+ });
3602
+ }
3603
+ } else {
3604
+ rawRoutes.push({
3605
+ filePath,
3606
+ routePath,
3607
+ directory,
3608
+ module: mod
3609
+ });
3610
+ }
3611
+ }
3612
+ middlewaresByDir.sort((a, b) => {
3613
+ const dirA = a.directory || "/";
3614
+ const dirB = b.directory || "/";
3615
+ const depthA = dirA.split("/").filter(Boolean).length;
3616
+ const depthB = dirB.split("/").filter(Boolean).length;
3617
+ if (depthA !== depthB)
3618
+ return depthA - depthB;
3619
+ return (b.pragma.priority ?? 0) - (a.pragma.priority ?? 0);
3620
+ });
3621
+ const routes = [];
3622
+ for (const raw of rawRoutes) {
3623
+ const applicableMiddlewares = middlewaresByDir.filter((mw) => shouldApplyMiddleware(mw.pragma, raw.routePath, raw.directory, mw.directory || "/"));
3624
+ const { pattern, paramNames } = compileRoutePattern(raw.routePath);
3625
+ const procedures = {};
3626
+ const mod = raw.module;
3627
+ if (mod.default && typeof mod.default === "object" && "type" in mod.default) {
3628
+ procedures.default = mod.default;
3629
+ }
3630
+ for (const [key, val] of Object.entries(mod)) {
3631
+ if (val && typeof val === "object" && "type" in val && "options" in val) {
3632
+ procedures[key] = val;
3633
+ }
3634
+ }
3635
+ const handler = typeof mod.default === "function" && !("type" in mod.default) ? mod.default : undefined;
3636
+ routes.push({
3637
+ pattern,
3638
+ paramNames,
3639
+ routePath: raw.routePath,
3640
+ filePath: raw.filePath,
3641
+ directory: raw.directory,
3642
+ procedures,
3643
+ middlewares: applicableMiddlewares,
3644
+ handler
3645
+ });
3646
+ }
3647
+ routes.sort((a, b) => {
3648
+ const isParamA = a.routePath.includes(":") || a.routePath.includes("*");
3649
+ const isParamB = b.routePath.includes(":") || b.routePath.includes("*");
3650
+ if (isParamA !== isParamB)
3651
+ return isParamA ? 1 : -1;
3652
+ return b.routePath.length - a.routePath.length;
3653
+ });
3654
+ return routes;
3655
+ }
3656
+ // src/api/middleware.ts
3657
+ async function executeMiddlewareChain(middlewares, req, url, params, initialCtx = {}) {
3658
+ let currentCtx = { ...initialCtx };
3659
+ let earlyResponse;
3660
+ let index = 0;
3661
+ const runner = async () => {
3662
+ if (index >= middlewares.length) {
3663
+ return;
3664
+ }
3665
+ const currentMw = middlewares[index++];
3666
+ const next = async (enriched) => {
3667
+ if (enriched && typeof enriched === "object") {
3668
+ currentCtx = { ...currentCtx, ...enriched };
3669
+ }
3670
+ await runner();
3671
+ return currentCtx;
3672
+ };
3673
+ const result = await currentMw({
3674
+ req,
3675
+ ctx: currentCtx,
3676
+ url,
3677
+ params,
3678
+ next
3679
+ });
3680
+ if (result instanceof Response) {
3681
+ earlyResponse = result;
3682
+ }
3683
+ };
3684
+ await runner();
3685
+ return {
3686
+ response: earlyResponse,
3687
+ ctx: currentCtx
3688
+ };
3689
+ }
3690
+
3691
+ // src/api/handler.ts
3692
+ function handleCors(req, config) {
3693
+ if (!config)
3694
+ return null;
3695
+ const origin = req.headers.get("Origin") || "*";
3696
+ const isAllowed = !config.origin || config.origin === "*" || typeof config.origin === "function" && config.origin(origin) || Array.isArray(config.origin) && config.origin.includes(origin) || config.origin === origin;
3697
+ if (req.method === "OPTIONS") {
3698
+ const headers = new Headers;
3699
+ if (isAllowed) {
3700
+ headers.set("Access-Control-Allow-Origin", origin);
3701
+ }
3702
+ headers.set("Access-Control-Allow-Methods", (config.methods || ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"]).join(", "));
3703
+ headers.set("Access-Control-Allow-Headers", (config.allowedHeaders || ["Content-Type", "Authorization", "x-visage-rpc"]).join(", "));
3704
+ if (config.credentials) {
3705
+ headers.set("Access-Control-Allow-Credentials", "true");
3706
+ }
3707
+ if (config.maxAge) {
3708
+ headers.set("Access-Control-Max-Age", config.maxAge.toString());
3709
+ }
3710
+ return new Response(null, { status: 204, headers });
3711
+ }
3712
+ return null;
3713
+ }
3714
+ function applyCorsHeaders(res, req, config) {
3715
+ if (!config)
3716
+ return res;
3717
+ const origin = req.headers.get("Origin") || "*";
3718
+ res.headers.set("Access-Control-Allow-Origin", origin);
3719
+ if (config.credentials) {
3720
+ res.headers.set("Access-Control-Allow-Credentials", "true");
3721
+ }
3722
+ return res;
3723
+ }
3724
+ function createApiHandler(routes, config = {}) {
3725
+ const prefix = config.prefix ? `/${config.prefix.replace(/^\/+|\/+$/g, "")}` : "";
3726
+ return async (req) => {
3727
+ const corsPreflight = handleCors(req, config.cors);
3728
+ if (corsPreflight)
3729
+ return corsPreflight;
3730
+ const url = new URL(req.url);
3731
+ let pathname = url.pathname;
3732
+ if (prefix && pathname.startsWith(prefix)) {
3733
+ pathname = pathname.slice(prefix.length) || "/";
3734
+ }
3735
+ let matchedRoute = null;
3736
+ const pathParams = {};
3737
+ for (const route of routes) {
3738
+ const match = pathname.match(route.pattern);
3739
+ if (match) {
3740
+ matchedRoute = route;
3741
+ route.paramNames.forEach((name, i) => {
3742
+ pathParams[name] = decodeURIComponent(match[i + 1] || "");
3743
+ });
3744
+ break;
3745
+ }
3746
+ }
3747
+ if (!matchedRoute) {
3748
+ return applyCorsHeaders(new Response(JSON.stringify({
3749
+ ok: false,
3750
+ error: { code: "NOT_FOUND", message: `Route not found: ${pathname}` }
3751
+ }), { status: 404, headers: { "Content-Type": "application/json" } }), req, config.cors);
3752
+ }
3753
+ try {
3754
+ const method = req.method.toUpperCase();
3755
+ let procedure = matchedRoute.procedures.default;
3756
+ if (!procedure) {
3757
+ if (method === "GET") {
3758
+ procedure = matchedRoute.procedures.GET || matchedRoute.procedures.get;
3759
+ } else if (method === "POST") {
3760
+ procedure = matchedRoute.procedures.POST || matchedRoute.procedures.post;
3761
+ } else if (method === "PUT") {
3762
+ procedure = matchedRoute.procedures.PUT || matchedRoute.procedures.put;
3763
+ } else if (method === "DELETE") {
3764
+ procedure = matchedRoute.procedures.DELETE || matchedRoute.procedures.del || matchedRoute.procedures.delete;
3765
+ } else if (method === "PATCH") {
3766
+ procedure = matchedRoute.procedures.PATCH || matchedRoute.procedures.patch;
3767
+ }
3768
+ }
3769
+ const mwFns = [
3770
+ ...matchedRoute.middlewares.map((m) => m.fn),
3771
+ ...procedure?.middlewares || []
3772
+ ];
3773
+ const { response: earlyResponse, ctx } = await executeMiddlewareChain(mwFns, req, url, pathParams);
3774
+ if (earlyResponse) {
3775
+ return applyCorsHeaders(earlyResponse, req, config.cors);
3776
+ }
3777
+ if (matchedRoute.handler) {
3778
+ const res = await matchedRoute.handler(req, ctx);
3779
+ return applyCorsHeaders(res, req, config.cors);
3780
+ }
3781
+ if (!procedure) {
3782
+ return applyCorsHeaders(new Response(JSON.stringify({
3783
+ ok: false,
3784
+ error: {
3785
+ code: "METHOD_NOT_ALLOWED",
3786
+ message: `Method ${method} not allowed on ${pathname}`
3787
+ }
3788
+ }), { status: 405, headers: { "Content-Type": "application/json" } }), req, config.cors);
3789
+ }
3790
+ let rawInput = {};
3791
+ if (method === "GET") {
3792
+ const inputQuery = url.searchParams.get("input");
3793
+ if (inputQuery) {
3794
+ try {
3795
+ rawInput = JSON.parse(inputQuery);
3796
+ } catch {
3797
+ rawInput = inputQuery;
3798
+ }
3799
+ } else {
3800
+ const spObj = {};
3801
+ url.searchParams.forEach((val, key) => {
3802
+ spObj[key] = val;
3803
+ });
3804
+ if (Object.keys(spObj).length > 0) {
3805
+ rawInput = spObj;
3806
+ }
3807
+ }
3808
+ } else {
3809
+ const contentType = req.headers.get("Content-Type") || "";
3810
+ if (contentType.includes("application/json")) {
3811
+ try {
3812
+ rawInput = await req.json();
3813
+ } catch {
3814
+ rawInput = {};
3815
+ }
3816
+ }
3817
+ }
3818
+ if (typeof rawInput === "object" && rawInput !== null) {
3819
+ rawInput = { ...pathParams, ...rawInput };
3820
+ }
3821
+ let validatedInput = rawInput;
3822
+ if (procedure.options.input) {
3823
+ const validation = await validateWithSchema(procedure.options.input, rawInput);
3824
+ if (!validation.success) {
3825
+ return applyCorsHeaders(new Response(JSON.stringify({
3826
+ ok: false,
3827
+ error: {
3828
+ code: "BAD_REQUEST",
3829
+ message: "Validation Error",
3830
+ issues: validation.issues
3831
+ }
3832
+ }), { status: 400, headers: { "Content-Type": "application/json" } }), req, config.cors);
3833
+ }
3834
+ validatedInput = validation.data;
3835
+ }
3836
+ if (procedure.type === "subscription") {
3837
+ const generator = procedure.options.resolve({
3838
+ input: validatedInput,
3839
+ ctx,
3840
+ req,
3841
+ params: pathParams
3842
+ });
3843
+ const stream = new ReadableStream({
3844
+ async start(controller) {
3845
+ const encoder = new TextEncoder;
3846
+ try {
3847
+ for await (const chunk of generator) {
3848
+ const payload = `data: ${JSON.stringify(chunk)}
3849
+
3850
+ `;
3851
+ controller.enqueue(encoder.encode(payload));
3852
+ }
3853
+ controller.close();
3854
+ } catch (err) {
3855
+ controller.error(err);
3856
+ }
3857
+ }
3858
+ });
3859
+ const sseRes = new Response(stream, {
3860
+ headers: {
3861
+ "Content-Type": "text/event-stream",
3862
+ "Cache-Control": "no-cache",
3863
+ Connection: "keep-alive"
3864
+ }
3865
+ });
3866
+ return applyCorsHeaders(sseRes, req, config.cors);
3867
+ }
3868
+ const result = await procedure.options.resolve({
3869
+ input: validatedInput,
3870
+ ctx,
3871
+ req,
3872
+ params: pathParams
3873
+ });
3874
+ const successRes = new Response(JSON.stringify({ ok: true, data: result }), {
3875
+ status: 200,
3876
+ headers: { "Content-Type": "application/json" }
3877
+ });
3878
+ return applyCorsHeaders(successRes, req, config.cors);
3879
+ } catch (err) {
3880
+ if (config.onError) {
3881
+ const customRes = await config.onError(err, req);
3882
+ return applyCorsHeaders(customRes, req, config.cors);
3883
+ }
3884
+ const statusCode = err instanceof RPCError ? err.statusCode : ERROR_CODE_TO_STATUS[err?.code] || 500;
3885
+ const code = err instanceof RPCError ? err.code : "INTERNAL_SERVER_ERROR";
3886
+ const message = err?.message || "Internal Server Error";
3887
+ const errRes = new Response(JSON.stringify({ ok: false, error: { code, message } }), {
3888
+ status: statusCode,
3889
+ headers: { "Content-Type": "application/json" }
3890
+ });
3891
+ return applyCorsHeaders(errRes, req, config.cors);
3892
+ }
3893
+ };
3894
+ }
3895
+ function createApi(files, sourceMapOrConfig, maybeConfig) {
3896
+ const sourceMap = sourceMapOrConfig && !("prefix" in sourceMapOrConfig) && !("cors" in sourceMapOrConfig) ? sourceMapOrConfig : {};
3897
+ const config = maybeConfig || (sourceMapOrConfig && (("prefix" in sourceMapOrConfig) || ("cors" in sourceMapOrConfig)) ? sourceMapOrConfig : {});
3898
+ const routes = buildFileRouter(files, sourceMap);
3899
+ const fetchHandler = createApiHandler(routes, config);
3900
+ return {
3901
+ routes,
3902
+ fetch: fetchHandler
3903
+ };
3904
+ }
3905
+ // src/api/procedure.ts
3906
+ function query(options) {
3907
+ return {
3908
+ type: "query",
3909
+ options
3910
+ };
3911
+ }
3912
+ function mutation(options) {
3913
+ return {
3914
+ type: "mutation",
3915
+ options
3916
+ };
3917
+ }
3918
+ function subscription(options) {
3919
+ return {
3920
+ type: "subscription",
3921
+ options: {
3922
+ ...options,
3923
+ resolve: options.resolve
3924
+ }
3925
+ };
3926
+ }
3927
+ function defineMiddleware(fn, pragma = { cascade: true, match: ["*"] }) {
3928
+ return {
3929
+ fn,
3930
+ pragma
3931
+ };
3932
+ }
3933
+
3934
+ class ProcedureBuilder {
3935
+ _middlewares = [];
3936
+ _inputSchema;
3937
+ constructor(middlewares = [], inputSchema) {
3938
+ this._middlewares = middlewares;
3939
+ this._inputSchema = inputSchema;
3940
+ }
3941
+ use(middleware) {
3942
+ return new ProcedureBuilder([...this._middlewares, middleware], this._inputSchema);
3943
+ }
3944
+ input(schema) {
3945
+ return new ProcedureBuilder(this._middlewares, schema);
3946
+ }
3947
+ query(resolve) {
3948
+ return {
3949
+ type: "query",
3950
+ options: {
3951
+ input: this._inputSchema,
3952
+ resolve
3953
+ },
3954
+ middlewares: this._middlewares
3955
+ };
3956
+ }
3957
+ mutation(resolve) {
3958
+ return {
3959
+ type: "mutation",
3960
+ options: {
3961
+ input: this._inputSchema,
3962
+ resolve
3963
+ },
3964
+ middlewares: this._middlewares
3965
+ };
3966
+ }
3967
+ subscription(resolve) {
3968
+ return {
3969
+ type: "subscription",
3970
+ options: {
3971
+ resolve
3972
+ },
3973
+ middlewares: this._middlewares
3974
+ };
3975
+ }
3976
+ }
3977
+ var procedure = new ProcedureBuilder;
3978
+ export {
3979
+ ERROR_CODE_TO_STATUS,
3980
+ ProcedureBuilder,
3981
+ RPCError,
3982
+ STATUS_TO_ERROR_CODE,
3983
+ Schema,
3984
+ buildFileRouter,
3985
+ compileRoutePattern,
3986
+ createApi,
3987
+ createApiClient,
3988
+ createApiHandler,
3989
+ defineMiddleware,
3990
+ executeMiddlewareChain,
3991
+ globToRegExp,
3992
+ matchesAnyPattern,
3993
+ mutation,
3994
+ normalizeFilePath,
3995
+ parsePragma,
3996
+ procedure,
3997
+ query,
3998
+ shouldApplyMiddleware,
3999
+ subscription,
4000
+ v,
4001
+ validateWithSchema
4002
+ };