@fragno-dev/stripe 0.0.2 → 0.1.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.
@@ -1,18 +1,30 @@
1
- import { createFragment, defineFragment, defineRoute, defineRoutes } from "@fragno-dev/core";
1
+ import { defineFragment, defineRoutes } from "@fragno-dev/core";
2
2
  import { z } from "zod";
3
3
 
4
- //#region ../fragno/dist/route-C4CyNHkC.js
4
+ //#region ../fragno/dist/api/route.js
5
+ /**
6
+ * Helper to resolve route factories into routes
7
+ * @internal
8
+ */
5
9
  function resolveRouteFactories(context, routesOrFactories) {
6
- const routes = [];
10
+ const routes$1 = [];
7
11
  for (const item of routesOrFactories) if (typeof item === "function") {
8
12
  const factoryRoutes = item(context);
9
- routes.push(...factoryRoutes);
10
- } else routes.push(item);
11
- return routes;
13
+ routes$1.push(...factoryRoutes);
14
+ } else routes$1.push(item);
15
+ return routes$1;
16
+ }
17
+
18
+ //#endregion
19
+ //#region ../fragno/dist/api/internal/route.js
20
+ function getMountRoute(opts) {
21
+ const mountRoute = opts.mountRoute ?? `/api/${opts.name}`;
22
+ if (mountRoute.endsWith("/")) return mountRoute.slice(0, -1);
23
+ return mountRoute;
12
24
  }
13
25
 
14
26
  //#endregion
15
- //#region ../fragno/dist/fragment-instantiation-DUT-HLl1.js
27
+ //#region ../fragno/dist/api/error.js
16
28
  var FragnoApiError = class extends Error {
17
29
  #status;
18
30
  #code;
@@ -56,11 +68,9 @@ var FragnoApiValidationError = class extends FragnoApiError {
56
68
  }, { status: this.status });
57
69
  }
58
70
  };
59
- function getMountRoute(opts) {
60
- const mountRoute = opts.mountRoute ?? `/api/${opts.name}`;
61
- if (mountRoute.endsWith("/")) return mountRoute.slice(0, -1);
62
- return mountRoute;
63
- }
71
+
72
+ //#endregion
73
+ //#region ../fragno/dist/api/request-input-context.js
64
74
  var RequestInputContext = class RequestInputContext$1 {
65
75
  #path;
66
76
  #method;
@@ -172,6 +182,9 @@ var RequestInputContext = class RequestInputContext$1 {
172
182
  return result.value;
173
183
  }
174
184
  };
185
+
186
+ //#endregion
187
+ //#region ../fragno/dist/api/internal/response-stream.js
175
188
  var ResponseStream = class {
176
189
  #writer;
177
190
  #encoder;
@@ -248,6 +261,9 @@ var ResponseStream = class {
248
261
  }
249
262
  }
250
263
  };
264
+
265
+ //#endregion
266
+ //#region ../fragno/dist/api/request-output-context.js
251
267
  /**
252
268
  * Utility function to merge headers from multiple sources.
253
269
  * Later headers override earlier ones.
@@ -362,188 +378,534 @@ var RequestOutputContext = class extends OutputContext {
362
378
  };
363
379
 
364
380
  //#endregion
365
- //#region ../../node_modules/.pnpm/nanostores@1.0.1/node_modules/nanostores/task/index.js
366
- let tasks = 0;
367
- let resolves = [];
368
- function startTask() {
369
- tasks += 1;
370
- return () => {
371
- tasks -= 1;
372
- if (tasks === 0) {
373
- let prevResolves = resolves;
374
- resolves = [];
375
- for (let i of prevResolves) i();
381
+ //#region ../fragno/dist/api/internal/path.js
382
+ /**
383
+ * Extract parameter names from a path pattern at runtime.
384
+ * Examples:
385
+ * - "/users/:id" => ["id"]
386
+ * - "/files/**" => ["**"]
387
+ * - "/files/**:rest" => ["rest"]
388
+ */
389
+ function extractPathParams(pathPattern) {
390
+ const segments = pathPattern.split("/").filter((s) => s.length > 0);
391
+ const names = [];
392
+ for (const segment of segments) {
393
+ if (segment.startsWith(":")) {
394
+ names.push(segment.slice(1));
395
+ continue;
376
396
  }
377
- };
397
+ if (segment === "**") {
398
+ names.push("**");
399
+ continue;
400
+ }
401
+ if (segment.startsWith("**:")) {
402
+ names.push(segment.slice(3));
403
+ continue;
404
+ }
405
+ }
406
+ return names;
378
407
  }
379
- function task(cb) {
380
- let endTask = startTask();
381
- let promise = cb().finally(endTask);
382
- promise.t = true;
383
- return promise;
408
+ /**
409
+ * Build a concrete path by replacing placeholders in a path pattern with values.
410
+ *
411
+ * Supports the same placeholder syntax as the matcher:
412
+ * - Named parameter ":name" is URL-encoded as a single segment
413
+ * - Anonymous wildcard "**" inserts the remainder as-is (slashes preserved)
414
+ * - Named wildcard "**:name" inserts the remainder from the named key
415
+ *
416
+ * Examples:
417
+ * - buildPath("/users/:id", { id: "123" }) => "/users/123"
418
+ * - buildPath("/files/**", { "**": "a/b" }) => "/files/a/b"
419
+ * - buildPath("/files/**:rest", { rest: "a/b" }) => "/files/a/b"
420
+ */
421
+ function buildPath(pathPattern, params) {
422
+ const patternSegments = pathPattern.split("/");
423
+ const builtSegments = [];
424
+ for (const segment of patternSegments) {
425
+ if (segment.length === 0) {
426
+ builtSegments.push("");
427
+ continue;
428
+ }
429
+ if (segment.startsWith(":")) {
430
+ const name = segment.slice(1);
431
+ const value = params[name];
432
+ if (value === void 0) throw new Error(`Missing value for path parameter :${name}`);
433
+ builtSegments.push(encodeURIComponent(value));
434
+ continue;
435
+ }
436
+ if (segment === "**") {
437
+ const value = params["**"];
438
+ if (value === void 0) throw new Error("Missing value for path wildcard **");
439
+ builtSegments.push(value);
440
+ continue;
441
+ }
442
+ if (segment.startsWith("**:")) {
443
+ const name = segment.slice(3);
444
+ const value = params[name];
445
+ if (value === void 0) throw new Error(`Missing value for path wildcard **:${name}`);
446
+ builtSegments.push(value);
447
+ continue;
448
+ }
449
+ builtSegments.push(segment);
450
+ }
451
+ return builtSegments.join("/");
384
452
  }
385
453
 
386
454
  //#endregion
387
- //#region ../../node_modules/.pnpm/nanostores@1.0.1/node_modules/nanostores/clean-stores/index.js
388
- let clean = Symbol("clean");
389
-
390
- //#endregion
391
- //#region ../../node_modules/.pnpm/nanostores@1.0.1/node_modules/nanostores/atom/index.js
392
- let listenerQueue = [];
393
- let lqIndex = 0;
394
- const QUEUE_ITEMS_PER_LISTENER = 4;
395
- let epoch = 0;
396
- let atom = (initialValue) => {
397
- let listeners = [];
398
- let $atom = {
399
- get() {
400
- if (!$atom.lc) $atom.listen(() => {})();
401
- return $atom.value;
402
- },
403
- lc: 0,
404
- listen(listener) {
405
- $atom.lc = listeners.push(listener);
406
- return () => {
407
- for (let i = lqIndex + QUEUE_ITEMS_PER_LISTENER; i < listenerQueue.length;) if (listenerQueue[i] === listener) listenerQueue.splice(i, QUEUE_ITEMS_PER_LISTENER);
408
- else i += QUEUE_ITEMS_PER_LISTENER;
409
- let index = listeners.indexOf(listener);
410
- if (~index) {
411
- listeners.splice(index, 1);
412
- if (!--$atom.lc) $atom.off();
413
- }
414
- };
415
- },
416
- notify(oldValue, changedKey) {
417
- epoch++;
418
- let runListenerQueue = !listenerQueue.length;
419
- for (let listener of listeners) listenerQueue.push(listener, $atom.value, oldValue, changedKey);
420
- if (runListenerQueue) {
421
- for (lqIndex = 0; lqIndex < listenerQueue.length; lqIndex += QUEUE_ITEMS_PER_LISTENER) listenerQueue[lqIndex](listenerQueue[lqIndex + 1], listenerQueue[lqIndex + 2], listenerQueue[lqIndex + 3]);
422
- listenerQueue.length = 0;
423
- }
424
- },
425
- off() {},
426
- set(newValue) {
427
- let oldValue = $atom.value;
428
- if (oldValue !== newValue) {
429
- $atom.value = newValue;
430
- $atom.notify(oldValue);
431
- }
432
- },
433
- subscribe(listener) {
434
- let unbind = $atom.listen(listener);
435
- listener($atom.value);
436
- return unbind;
437
- },
438
- value: initialValue
439
- };
440
- $atom[clean] = () => {
441
- listeners = [];
442
- $atom.lc = 0;
443
- $atom.off();
444
- };
445
- return $atom;
455
+ //#region ../fragno/dist/client/client-error.js
456
+ /**
457
+ * Base error class for all Fragno client errors.
458
+ */
459
+ var FragnoClientError = class extends Error {
460
+ #code;
461
+ constructor(message, code, options = {}) {
462
+ super(message, { cause: options.cause });
463
+ this.name = "FragnoClientError";
464
+ this.#code = code;
465
+ }
466
+ get code() {
467
+ return this.#code;
468
+ }
469
+ };
470
+ var FragnoClientFetchError = class extends FragnoClientError {
471
+ constructor(message, code, options = {}) {
472
+ super(message, code, options);
473
+ this.name = "FragnoClientFetchError";
474
+ }
475
+ static fromUnknownFetchError(error) {
476
+ if (!(error instanceof Error)) return new FragnoClientFetchNetworkError("Network request failed", { cause: error });
477
+ if (error.name === "AbortError") return new FragnoClientFetchAbortError("Request was aborted", { cause: error });
478
+ return new FragnoClientFetchNetworkError("Network request failed", { cause: error });
479
+ }
480
+ };
481
+ /**
482
+ * Error thrown when a network request fails (e.g., no internet connection, DNS failure).
483
+ */
484
+ var FragnoClientFetchNetworkError = class extends FragnoClientFetchError {
485
+ constructor(message = "Network request failed", options = {}) {
486
+ super(message, "NETWORK_ERROR", options);
487
+ this.name = "FragnoClientFetchNetworkError";
488
+ }
489
+ };
490
+ /**
491
+ * Error thrown when a request is aborted (e.g., user cancels request, timeout).
492
+ */
493
+ var FragnoClientFetchAbortError = class extends FragnoClientFetchError {
494
+ constructor(message = "Request was aborted", options = {}) {
495
+ super(message, "ABORT_ERROR", options);
496
+ this.name = "FragnoClientFetchAbortError";
497
+ }
498
+ };
499
+ /**
500
+ * Error thrown when the API result is unexpected, e.g. no json is returned.
501
+ */
502
+ var FragnoClientUnknownApiError = class extends FragnoClientError {
503
+ #status;
504
+ constructor(message = "Unknown API error", status, options = {}) {
505
+ super(message, "UNKNOWN_API_ERROR", options);
506
+ this.name = "FragnoClientUnknownApiError";
507
+ this.#status = status;
508
+ }
509
+ get status() {
510
+ return this.#status;
511
+ }
512
+ };
513
+ var FragnoClientApiError = class FragnoClientApiError$1 extends FragnoClientError {
514
+ #status;
515
+ constructor({ message, code }, status, options = {}) {
516
+ super(message, code, options);
517
+ this.name = "FragnoClientApiError";
518
+ this.#status = status;
519
+ }
520
+ get status() {
521
+ return this.#status;
522
+ }
523
+ /**
524
+ * The error code returned by the API.
525
+ *
526
+ * The type is `TErrorCode` (the set of known error codes for this route), but may also be a string
527
+ * for forward compatibility with future error codes.
528
+ */
529
+ get code() {
530
+ return super.code;
531
+ }
532
+ static async fromResponse(response) {
533
+ const unknown = await response.json();
534
+ const status = response.status;
535
+ if (!("message" in unknown || "code" in unknown)) return new FragnoClientUnknownApiError("Unknown API error", status);
536
+ if (!(typeof unknown.message === "string" && typeof unknown.code === "string")) return new FragnoClientUnknownApiError("Unknown API error", status);
537
+ return new FragnoClientApiError$1({
538
+ message: unknown.message,
539
+ code: unknown.code
540
+ }, status);
541
+ }
446
542
  };
447
543
 
448
544
  //#endregion
449
- //#region ../../node_modules/.pnpm/nanostores@1.0.1/node_modules/nanostores/lifecycle/index.js
450
- const START = 0;
451
- const STOP = 1;
452
- const MOUNT = 5;
453
- const UNMOUNT = 6;
454
- const REVERT_MUTATION = 10;
455
- let on = (object, listener, eventKey, mutateStore) => {
456
- object.events = object.events || {};
457
- if (!object.events[eventKey + REVERT_MUTATION]) object.events[eventKey + REVERT_MUTATION] = mutateStore((eventProps) => {
458
- object.events[eventKey].reduceRight((event, l) => (l(event), event), {
459
- shared: {},
460
- ...eventProps
461
- });
462
- });
463
- object.events[eventKey] = object.events[eventKey] || [];
464
- object.events[eventKey].push(listener);
465
- return () => {
466
- let currentListeners = object.events[eventKey];
467
- let index = currentListeners.indexOf(listener);
468
- currentListeners.splice(index, 1);
469
- if (!currentListeners.length) {
470
- delete object.events[eventKey];
471
- object.events[eventKey + REVERT_MUTATION]();
472
- delete object.events[eventKey + REVERT_MUTATION];
545
+ //#region ../fragno/dist/util/content-type.js
546
+ /**
547
+ * Parses a content-type header string into its components
548
+ *
549
+ * @param contentType - The content-type header value to parse
550
+ * @returns A ParsedContentType object or null if the input is invalid
551
+ *
552
+ * @example
553
+ * ```ts
554
+ * const { type, subtype, mediaType, parameters }
555
+ * = parseContentType("application/json; charset=utf-8");
556
+ * console.assert(type === "application");
557
+ * console.assert(subtype === "json");
558
+ * console.assert(mediaType === "application/json");
559
+ * console.assert(parameters["charset"] === "utf-8");
560
+ */
561
+ function parseContentType(contentType) {
562
+ if (!contentType || typeof contentType !== "string") return null;
563
+ const trimmed = contentType.trim();
564
+ if (!trimmed) return null;
565
+ const parts = trimmed.split(";").map((part) => part.trim());
566
+ const mediaType = parts[0];
567
+ if (!mediaType) return null;
568
+ const typeParts = mediaType.split("/");
569
+ if (typeParts.length !== 2) return null;
570
+ const [type, subtype] = typeParts.map((part) => part.trim().toLowerCase());
571
+ if (!type || !subtype) return null;
572
+ const parameters = {};
573
+ for (let i = 1; i < parts.length; i++) {
574
+ const param = parts[i];
575
+ const equalIndex = param.indexOf("=");
576
+ if (equalIndex > 0) {
577
+ const key = param.slice(0, equalIndex).trim().toLowerCase();
578
+ let value = param.slice(equalIndex + 1).trim();
579
+ if (value.startsWith("\"") && value.endsWith("\"")) value = value.slice(1, -1);
580
+ if (key) parameters[key] = value;
473
581
  }
582
+ }
583
+ return {
584
+ type,
585
+ subtype,
586
+ mediaType: `${type}/${subtype}`,
587
+ parameters
474
588
  };
475
- };
476
- let onStart = ($store, listener) => on($store, listener, START, (runListeners) => {
477
- let originListen = $store.listen;
478
- $store.listen = (arg) => {
479
- if (!$store.lc && !$store.starting) {
480
- $store.starting = true;
481
- runListeners();
482
- delete $store.starting;
589
+ }
590
+
591
+ //#endregion
592
+ //#region ../fragno/dist/client/internal/ndjson-streaming.js
593
+ /**
594
+ * Creates a promise that rejects when the abort signal is triggered
595
+ */
596
+ function createAbortPromise(abortSignal) {
597
+ return new Promise((_, reject) => {
598
+ const abortHandler = () => {
599
+ reject(new FragnoClientFetchAbortError("Operation was aborted"));
600
+ };
601
+ if (abortSignal.aborted) abortHandler();
602
+ else abortSignal.addEventListener("abort", abortHandler, { once: true });
603
+ });
604
+ }
605
+ /**
606
+ * Handles NDJSON streaming responses by returning the first item from the fetcher
607
+ * and then continuing to stream updates via the store's mutate method.
608
+ *
609
+ * This makes it so that we can wait until the first chunk before updating the store, if we did
610
+ * not do this, `loading` would briefly be false before the first item would be populated in the
611
+ * result.
612
+ *
613
+ * @param response - The fetch Response object containing the NDJSON stream
614
+ * @param store - The fetcher store to update with streaming data
615
+ * @param abortSignal - Optional AbortSignal to cancel the streaming operation
616
+ * @returns A promise that resolves to an object containing the first item and a streaming promise
617
+ */
618
+ async function handleNdjsonStreamingFirstItem(response, store, options = {}) {
619
+ if (!response.body) throw new FragnoClientFetchError("Streaming response has no body", "NO_BODY");
620
+ const { abortSignal } = options;
621
+ if (abortSignal?.aborted) throw new FragnoClientFetchAbortError("Operation was aborted");
622
+ const decoder = new TextDecoder();
623
+ const reader = response.body.getReader();
624
+ let buffer = "";
625
+ let firstItem = null;
626
+ const items = [];
627
+ try {
628
+ while (firstItem === null) {
629
+ if (abortSignal?.aborted) {
630
+ reader.releaseLock();
631
+ throw new FragnoClientFetchAbortError("Operation was aborted");
632
+ }
633
+ const { done, value } = await (abortSignal ? Promise.race([reader.read(), createAbortPromise(abortSignal)]) : reader.read());
634
+ if (done) break;
635
+ buffer += decoder.decode(value, { stream: true });
636
+ const lines = buffer.split("\n");
637
+ buffer = lines.pop() || "";
638
+ for (const line of lines) {
639
+ if (!line.trim()) continue;
640
+ try {
641
+ const jsonObject = JSON.parse(line);
642
+ items.push(jsonObject);
643
+ if (firstItem === null) {
644
+ firstItem = jsonObject;
645
+ const streamingPromise = continueStreaming(reader, decoder, buffer, items, store, abortSignal);
646
+ return {
647
+ firstItem,
648
+ streamingPromise
649
+ };
650
+ }
651
+ } catch (parseError) {
652
+ throw new FragnoClientUnknownApiError("Failed to parse NDJSON line", 500, { cause: parseError });
653
+ }
654
+ }
483
655
  }
484
- return originListen(arg);
485
- };
486
- return () => {
487
- $store.listen = originListen;
488
- };
489
- });
490
- let onStop = ($store, listener) => on($store, listener, STOP, (runListeners) => {
491
- let originOff = $store.off;
492
- $store.off = () => {
493
- runListeners();
494
- originOff();
495
- };
496
- return () => {
497
- $store.off = originOff;
498
- };
499
- });
500
- let STORE_UNMOUNT_DELAY = 1e3;
501
- let onMount = ($store, initialize) => {
502
- let listener = (payload) => {
503
- let destroy = initialize(payload);
504
- if (destroy) $store.events[UNMOUNT].push(destroy);
505
- };
506
- return on($store, listener, MOUNT, (runListeners) => {
507
- let originListen = $store.listen;
508
- $store.listen = (...args) => {
509
- if (!$store.lc && !$store.active) {
510
- $store.active = true;
511
- runListeners();
656
+ if (firstItem === null) {
657
+ reader.releaseLock();
658
+ throw new FragnoClientUnknownApiError("NDJSON stream contained no valid items", 500);
659
+ }
660
+ reader.releaseLock();
661
+ throw new FragnoClientFetchError("Unexpected end of stream processing", "NO_BODY");
662
+ } catch (error) {
663
+ if (error instanceof FragnoClientError) {
664
+ store?.setError(error);
665
+ throw error;
666
+ } else {
667
+ const clientError = new FragnoClientUnknownApiError("Unknown streaming error", 500, { cause: error });
668
+ store?.setError(clientError);
669
+ throw clientError;
670
+ }
671
+ }
672
+ }
673
+ /**
674
+ * Continues streaming the remaining items in the background
675
+ */
676
+ async function continueStreaming(reader, decoder, initialBuffer, items, store, abortSignal) {
677
+ let buffer = initialBuffer;
678
+ try {
679
+ while (true) {
680
+ if (abortSignal?.aborted) throw new FragnoClientFetchAbortError("Operation was aborted");
681
+ const { done, value } = await (abortSignal ? Promise.race([reader.read(), createAbortPromise(abortSignal)]) : reader.read());
682
+ if (done) {
683
+ if (buffer.trim()) {
684
+ const lines$1 = buffer.split("\n");
685
+ for (const line of lines$1) {
686
+ if (!line.trim()) continue;
687
+ try {
688
+ const jsonObject = JSON.parse(line);
689
+ items.push(jsonObject);
690
+ store?.setData([...items]);
691
+ } catch (parseError) {
692
+ throw new FragnoClientUnknownApiError("Failed to parse NDJSON line", 400, { cause: parseError });
693
+ }
694
+ }
695
+ }
696
+ break;
512
697
  }
513
- return originListen(...args);
514
- };
515
- let originOff = $store.off;
516
- $store.events[UNMOUNT] = [];
517
- $store.off = () => {
518
- originOff();
519
- setTimeout(() => {
520
- if ($store.active && !$store.lc) {
521
- $store.active = false;
522
- for (let destroy of $store.events[UNMOUNT]) destroy();
523
- $store.events[UNMOUNT] = [];
698
+ buffer += decoder.decode(value, { stream: true });
699
+ const lines = buffer.split("\n");
700
+ buffer = lines.pop() || "";
701
+ for (const line of lines) {
702
+ if (!line.trim()) continue;
703
+ try {
704
+ const jsonObject = JSON.parse(line);
705
+ items.push(jsonObject);
706
+ store?.setData([...items]);
707
+ } catch (parseError) {
708
+ throw new FragnoClientUnknownApiError("Failed to parse NDJSON line", 400, { cause: parseError });
524
709
  }
525
- }, STORE_UNMOUNT_DELAY);
526
- };
527
- {
528
- let originClean = $store[clean];
529
- $store[clean] = () => {
530
- for (let destroy of $store.events[UNMOUNT]) destroy();
531
- $store.events[UNMOUNT] = [];
532
- $store.active = false;
533
- originClean();
534
- };
710
+ }
535
711
  }
536
- return () => {
537
- $store.listen = originListen;
538
- $store.off = originOff;
539
- };
540
- });
541
- };
712
+ } catch (error) {
713
+ if (error instanceof FragnoClientError) store?.setError(error);
714
+ else {
715
+ const clientError = new FragnoClientUnknownApiError("Unknown streaming error", 400, { cause: error });
716
+ store?.setError(clientError);
717
+ throw clientError;
718
+ }
719
+ throw error;
720
+ } finally {
721
+ reader.releaseLock();
722
+ }
723
+ return items;
724
+ }
542
725
 
543
726
  //#endregion
544
- //#region ../../node_modules/.pnpm/nanostores@1.0.1/node_modules/nanostores/computed/index.js
545
- let computedStore = (stores$1, cb, batched$1) => {
546
- if (!Array.isArray(stores$1)) stores$1 = [stores$1];
727
+ //#region ../../node_modules/.pnpm/nanostores@1.0.1/node_modules/nanostores/task/index.js
728
+ let tasks = 0;
729
+ let resolves = [];
730
+ function startTask() {
731
+ tasks += 1;
732
+ return () => {
733
+ tasks -= 1;
734
+ if (tasks === 0) {
735
+ let prevResolves = resolves;
736
+ resolves = [];
737
+ for (let i of prevResolves) i();
738
+ }
739
+ };
740
+ }
741
+ function task(cb) {
742
+ let endTask = startTask();
743
+ let promise = cb().finally(endTask);
744
+ promise.t = true;
745
+ return promise;
746
+ }
747
+
748
+ //#endregion
749
+ //#region ../../node_modules/.pnpm/nanostores@1.0.1/node_modules/nanostores/clean-stores/index.js
750
+ let clean = Symbol("clean");
751
+
752
+ //#endregion
753
+ //#region ../../node_modules/.pnpm/nanostores@1.0.1/node_modules/nanostores/atom/index.js
754
+ let listenerQueue = [];
755
+ let lqIndex = 0;
756
+ const QUEUE_ITEMS_PER_LISTENER = 4;
757
+ let epoch = 0;
758
+ let atom = (initialValue) => {
759
+ let listeners = [];
760
+ let $atom = {
761
+ get() {
762
+ if (!$atom.lc) $atom.listen(() => {})();
763
+ return $atom.value;
764
+ },
765
+ lc: 0,
766
+ listen(listener) {
767
+ $atom.lc = listeners.push(listener);
768
+ return () => {
769
+ for (let i = lqIndex + QUEUE_ITEMS_PER_LISTENER; i < listenerQueue.length;) if (listenerQueue[i] === listener) listenerQueue.splice(i, QUEUE_ITEMS_PER_LISTENER);
770
+ else i += QUEUE_ITEMS_PER_LISTENER;
771
+ let index = listeners.indexOf(listener);
772
+ if (~index) {
773
+ listeners.splice(index, 1);
774
+ if (!--$atom.lc) $atom.off();
775
+ }
776
+ };
777
+ },
778
+ notify(oldValue, changedKey) {
779
+ epoch++;
780
+ let runListenerQueue = !listenerQueue.length;
781
+ for (let listener of listeners) listenerQueue.push(listener, $atom.value, oldValue, changedKey);
782
+ if (runListenerQueue) {
783
+ for (lqIndex = 0; lqIndex < listenerQueue.length; lqIndex += QUEUE_ITEMS_PER_LISTENER) listenerQueue[lqIndex](listenerQueue[lqIndex + 1], listenerQueue[lqIndex + 2], listenerQueue[lqIndex + 3]);
784
+ listenerQueue.length = 0;
785
+ }
786
+ },
787
+ off() {},
788
+ set(newValue) {
789
+ let oldValue = $atom.value;
790
+ if (oldValue !== newValue) {
791
+ $atom.value = newValue;
792
+ $atom.notify(oldValue);
793
+ }
794
+ },
795
+ subscribe(listener) {
796
+ let unbind = $atom.listen(listener);
797
+ listener($atom.value);
798
+ return unbind;
799
+ },
800
+ value: initialValue
801
+ };
802
+ $atom[clean] = () => {
803
+ listeners = [];
804
+ $atom.lc = 0;
805
+ $atom.off();
806
+ };
807
+ return $atom;
808
+ };
809
+
810
+ //#endregion
811
+ //#region ../../node_modules/.pnpm/nanostores@1.0.1/node_modules/nanostores/lifecycle/index.js
812
+ const START = 0;
813
+ const STOP = 1;
814
+ const MOUNT = 5;
815
+ const UNMOUNT = 6;
816
+ const REVERT_MUTATION = 10;
817
+ let on = (object, listener, eventKey, mutateStore) => {
818
+ object.events = object.events || {};
819
+ if (!object.events[eventKey + REVERT_MUTATION]) object.events[eventKey + REVERT_MUTATION] = mutateStore((eventProps) => {
820
+ object.events[eventKey].reduceRight((event, l) => (l(event), event), {
821
+ shared: {},
822
+ ...eventProps
823
+ });
824
+ });
825
+ object.events[eventKey] = object.events[eventKey] || [];
826
+ object.events[eventKey].push(listener);
827
+ return () => {
828
+ let currentListeners = object.events[eventKey];
829
+ let index = currentListeners.indexOf(listener);
830
+ currentListeners.splice(index, 1);
831
+ if (!currentListeners.length) {
832
+ delete object.events[eventKey];
833
+ object.events[eventKey + REVERT_MUTATION]();
834
+ delete object.events[eventKey + REVERT_MUTATION];
835
+ }
836
+ };
837
+ };
838
+ let onStart = ($store, listener) => on($store, listener, START, (runListeners) => {
839
+ let originListen = $store.listen;
840
+ $store.listen = (arg) => {
841
+ if (!$store.lc && !$store.starting) {
842
+ $store.starting = true;
843
+ runListeners();
844
+ delete $store.starting;
845
+ }
846
+ return originListen(arg);
847
+ };
848
+ return () => {
849
+ $store.listen = originListen;
850
+ };
851
+ });
852
+ let onStop = ($store, listener) => on($store, listener, STOP, (runListeners) => {
853
+ let originOff = $store.off;
854
+ $store.off = () => {
855
+ runListeners();
856
+ originOff();
857
+ };
858
+ return () => {
859
+ $store.off = originOff;
860
+ };
861
+ });
862
+ let STORE_UNMOUNT_DELAY = 1e3;
863
+ let onMount = ($store, initialize) => {
864
+ let listener = (payload) => {
865
+ let destroy = initialize(payload);
866
+ if (destroy) $store.events[UNMOUNT].push(destroy);
867
+ };
868
+ return on($store, listener, MOUNT, (runListeners) => {
869
+ let originListen = $store.listen;
870
+ $store.listen = (...args) => {
871
+ if (!$store.lc && !$store.active) {
872
+ $store.active = true;
873
+ runListeners();
874
+ }
875
+ return originListen(...args);
876
+ };
877
+ let originOff = $store.off;
878
+ $store.events[UNMOUNT] = [];
879
+ $store.off = () => {
880
+ originOff();
881
+ setTimeout(() => {
882
+ if ($store.active && !$store.lc) {
883
+ $store.active = false;
884
+ for (let destroy of $store.events[UNMOUNT]) destroy();
885
+ $store.events[UNMOUNT] = [];
886
+ }
887
+ }, STORE_UNMOUNT_DELAY);
888
+ };
889
+ {
890
+ let originClean = $store[clean];
891
+ $store[clean] = () => {
892
+ for (let destroy of $store.events[UNMOUNT]) destroy();
893
+ $store.events[UNMOUNT] = [];
894
+ $store.active = false;
895
+ originClean();
896
+ };
897
+ }
898
+ return () => {
899
+ $store.listen = originListen;
900
+ $store.off = originOff;
901
+ };
902
+ });
903
+ };
904
+
905
+ //#endregion
906
+ //#region ../../node_modules/.pnpm/nanostores@1.0.1/node_modules/nanostores/computed/index.js
907
+ let computedStore = (stores$1, cb, batched$1) => {
908
+ if (!Array.isArray(stores$1)) stores$1 = [stores$1];
547
909
  let previousArgs;
548
910
  let currentEpoch;
549
911
  let set = () => {
@@ -607,7 +969,7 @@ let map = (initial = {}) => {
607
969
  };
608
970
 
609
971
  //#endregion
610
- //#region ../fragno/dist/ssr-kyKI7pqH.js
972
+ //#region ../fragno/dist/util/ssr.js
611
973
  let stores = [];
612
974
  const SSR_ENABLED = false;
613
975
  function addStore(store) {
@@ -622,6 +984,69 @@ function getInitialData(key) {
622
984
  }
623
985
  }
624
986
 
987
+ //#endregion
988
+ //#region ../fragno/dist/util/nanostores.js
989
+ /**
990
+ * Normalizes a value that could be a plain value, an Atom, or a Vue Ref to a plain value.
991
+ */
992
+ function unwrapAtom(value) {
993
+ if (value && typeof value === "object" && "get" in value && typeof value.get === "function") return value.get();
994
+ return value;
995
+ }
996
+ /**
997
+ * Normalizes an object where values can be plain values, Atoms, or Vue Refs.
998
+ * Returns a new object with all values normalized to plain values.
999
+ */
1000
+ function unwrapObject(params) {
1001
+ if (!params) return;
1002
+ return Object.fromEntries(Object.entries(params).map(([key, value]) => [key, unwrapAtom(value)]));
1003
+ }
1004
+ function isReadableAtom(value) {
1005
+ if (!value) return false;
1006
+ if (typeof value !== "object" || value === null) return false;
1007
+ if (!("get" in value) || typeof value.get !== "function") return false;
1008
+ if (!("lc" in value) || typeof value.lc !== "number") return false;
1009
+ if (!("notify" in value) || typeof value.notify !== "function") return false;
1010
+ if (!("off" in value) || typeof value.off !== "function") return false;
1011
+ if (!("subscribe" in value) || typeof value.subscribe !== "function") return false;
1012
+ if (!("value" in value)) return false;
1013
+ return true;
1014
+ }
1015
+
1016
+ //#endregion
1017
+ //#region ../fragno/dist/client/internal/fetcher-merge.js
1018
+ /**
1019
+ * Merge two fetcher configurations, with user config taking precedence.
1020
+ * If user provides a custom function, it takes full precedence.
1021
+ * Otherwise, deep merge RequestInit options.
1022
+ */
1023
+ function mergeFetcherConfigs(authorConfig, userConfig) {
1024
+ if (userConfig?.type === "function") return userConfig;
1025
+ if (!userConfig && authorConfig?.type === "function") return authorConfig;
1026
+ const authorOpts = authorConfig?.type === "options" ? authorConfig.options : {};
1027
+ const userOpts = userConfig?.type === "options" ? userConfig.options : {};
1028
+ if (Object.keys(authorOpts).length === 0 && Object.keys(userOpts).length === 0) return;
1029
+ return {
1030
+ type: "options",
1031
+ options: {
1032
+ ...authorOpts,
1033
+ ...userOpts,
1034
+ headers: mergeHeaders(authorOpts.headers, userOpts.headers)
1035
+ }
1036
+ };
1037
+ }
1038
+ /**
1039
+ * Merge headers from author and user configs.
1040
+ * User headers override author headers.
1041
+ */
1042
+ function mergeHeaders(author, user) {
1043
+ if (!author && !user) return;
1044
+ const merged = new Headers(author);
1045
+ new Headers(user).forEach((value, key) => merged.set(key, value));
1046
+ if (merged.keys().next().done) return;
1047
+ return merged;
1048
+ }
1049
+
625
1050
  //#endregion
626
1051
  //#region ../../node_modules/.pnpm/nanoevents@9.1.0/node_modules/nanoevents/index.js
627
1052
  let createNanoEvents = () => ({
@@ -816,575 +1241,187 @@ const nanoqueryFactory = ([isAppVisible, visibilityChangeSubscribe, reconnectCha
816
1241
  if (prevKey && prevKeyParts) runFetcher([prevKey, prevKeyParts], fetcherStore, settings);
817
1242
  };
818
1243
  const originListen = fetcherStore.listen;
819
- fetcherStore.listen = (listener) => {
820
- const unsub = originListen(listener);
821
- listener(fetcherStore.value);
822
- handleNewListener();
823
- return unsub;
824
- };
825
- onStop(fetcherStore, () => {
826
- fetcherStore.value = { ...notLoading };
827
- keysInternalUnsub?.();
828
- evtUnsubs.forEach((fn) => fn());
829
- evtUnsubs = [];
830
- keyUnsub?.();
831
- clearInterval(_revalidateOnInterval.get(keyInput));
832
- });
833
- return fetcherStore;
834
- };
835
- const iterOverCache = (keySelector, cb) => {
836
- for (const key of cache.keys()) if (testKeyAgainstSelector(key, keySelector)) cb(key);
837
- };
838
- const invalidateKeys = (keySelector) => {
839
- iterOverCache(keySelector, (key) => {
840
- cache.delete(key);
841
- });
842
- events.emit(INVALIDATE_KEYS, keySelector);
843
- };
844
- const revalidateKeys = (keySelector) => {
845
- iterOverCache(keySelector, (key) => {
846
- const cached = cache.get(key);
847
- if (cached) cache.set(key, {
848
- ...cached,
849
- created: -Infinity
850
- });
851
- });
852
- events.emit(REVALIDATE_KEYS, keySelector);
853
- };
854
- const mutateCache = (keySelector, data) => {
855
- iterOverCache(keySelector, (key) => {
856
- if (data === void 0) cache.delete(key);
857
- else cache.set(key, {
858
- data,
859
- created: getNow(),
860
- expires: getNow() + (globalSettings.cacheLifetime ?? 8e3)
861
- });
862
- });
863
- events.emit(SET_CACHE, keySelector, data);
864
- };
865
- function createMutatorStore(mutator, opts) {
866
- const { throttleCalls, onError } = opts ?? {
867
- throttleCalls: true,
868
- onError: globalSettings?.onError
869
- };
870
- const mutate = async (data) => {
871
- if (throttleCalls && store.value?.loading) return;
872
- const newMutator = rewrittenSettings.fetcher ?? mutator;
873
- const keysToInvalidate = [], keysToRevalidate = [];
874
- const safeKeySet = (k, v) => {
875
- if (store.lc) store.setKey(k, v);
876
- };
877
- try {
878
- store.set({
879
- error: void 0,
880
- data: void 0,
881
- mutate,
882
- ...loading
883
- });
884
- const result = await newMutator({
885
- data,
886
- invalidate: (key) => {
887
- keysToInvalidate.push(key);
888
- },
889
- revalidate: (key) => {
890
- keysToRevalidate.push(key);
891
- },
892
- getCacheUpdater: (key, shouldRevalidate = true) => [(newVal) => {
893
- mutateCache(key, newVal);
894
- if (shouldRevalidate) keysToRevalidate.push(key);
895
- }, cache.get(key)?.data]
896
- });
897
- safeKeySet("data", result);
898
- return result;
899
- } catch (error) {
900
- onError?.(error);
901
- safeKeySet("error", error);
902
- store.setKey("error", error);
903
- } finally {
904
- safeKeySet("loading", false);
905
- keysToInvalidate.forEach(invalidateKeys);
906
- keysToRevalidate.forEach(revalidateKeys);
907
- }
908
- };
909
- const store = map({
910
- mutate,
911
- ...notLoading
912
- });
913
- onStop(store, () => store.set({
914
- mutate,
915
- ...notLoading
916
- }));
917
- store.mutate = mutate;
918
- return store;
919
- }
920
- const __unsafeOverruleSettings = (data) => {
921
- console.warn(`You should only use __unsafeOverruleSettings in test environment`);
922
- rewrittenSettings = data;
923
- };
924
- return [
925
- createFetcherStore,
926
- createMutatorStore,
927
- {
928
- __unsafeOverruleSettings,
929
- invalidateKeys,
930
- revalidateKeys,
931
- mutateCache
932
- }
933
- ];
934
- };
935
- function isSomeKey(key) {
936
- return typeof key === "string" || typeof key === "number" || key === true;
937
- }
938
- const getKeyStore = (keys) => {
939
- if (isSomeKey(keys)) return [atom(["" + keys, [keys]]), () => {}];
940
- const keyParts = [];
941
- const $key = atom(null);
942
- const keysAsStoresToIndexes = /* @__PURE__ */ new Map();
943
- const setKeyStoreValue = () => {
944
- if (keyParts.some((v) => v === null || v === void 0 || v === false)) $key.set(null);
945
- else $key.set([keyParts.join(""), keyParts]);
946
- };
947
- for (let i = 0; i < keys.length; i++) {
948
- const keyOrStore = keys[i];
949
- if (isSomeKey(keyOrStore)) keyParts.push(keyOrStore);
950
- else {
951
- keyParts.push(null);
952
- keysAsStoresToIndexes.set(keyOrStore, i);
953
- }
954
- }
955
- const storesAsArray = [...keysAsStoresToIndexes.keys()];
956
- const $storeKeys = batched(storesAsArray, (...storeValues) => {
957
- for (let i = 0; i < storeValues.length; i++) {
958
- const store = storesAsArray[i], partIndex = keysAsStoresToIndexes.get(store);
959
- keyParts[partIndex] = store._ === fetcherSymbol ? store.value && "data" in store.value ? store.key : null : storeValues[i];
960
- }
961
- setKeyStoreValue();
962
- });
963
- setKeyStoreValue();
964
- return [$key, $storeKeys.subscribe(noop)];
965
- };
966
- function noop() {}
967
- const FOCUS = 1, RECONNECT = 2, INVALIDATE_KEYS = 3, REVALIDATE_KEYS = 4, SET_CACHE = 5;
968
- const testKeyAgainstSelector = (key, selector) => {
969
- if (Array.isArray(selector)) return selector.includes(key);
970
- else if (typeof selector === "function") return selector(key);
971
- else return key === selector;
972
- };
973
- const getNow = () => (/* @__PURE__ */ new Date()).getTime();
974
- const fetcherSymbol = Symbol();
975
- const loading = { loading: true }, notLoading = { loading: false };
976
- return nanoquery$1;
977
- };
978
- const subscribe = (name, fn) => {
979
- const isServer = typeof window === "undefined";
980
- if (!isServer) addEventListener(name, fn);
981
- };
982
- const browserCompat = [
983
- () => !document.hidden,
984
- (cb) => subscribe("visibilitychange", cb),
985
- (cb) => subscribe("online", cb)
986
- ];
987
- const nanoquery = nanoqueryFactory(browserCompat);
988
-
989
- //#endregion
990
- //#region ../fragno/dist/client-DAFHcKqA.js
991
- /**
992
- * Extract parameter names from a path pattern at runtime.
993
- * Examples:
994
- * - "/users/:id" => ["id"]
995
- * - "/files/**" => ["**"]
996
- * - "/files/**:rest" => ["rest"]
997
- */
998
- function extractPathParams(pathPattern) {
999
- const segments = pathPattern.split("/").filter((s) => s.length > 0);
1000
- const names = [];
1001
- for (const segment of segments) {
1002
- if (segment.startsWith(":")) {
1003
- names.push(segment.slice(1));
1004
- continue;
1005
- }
1006
- if (segment === "**") {
1007
- names.push("**");
1008
- continue;
1009
- }
1010
- if (segment.startsWith("**:")) {
1011
- names.push(segment.slice(3));
1012
- continue;
1013
- }
1014
- }
1015
- return names;
1016
- }
1017
- /**
1018
- * Build a concrete path by replacing placeholders in a path pattern with values.
1019
- *
1020
- * Supports the same placeholder syntax as the matcher:
1021
- * - Named parameter ":name" is URL-encoded as a single segment
1022
- * - Anonymous wildcard "**" inserts the remainder as-is (slashes preserved)
1023
- * - Named wildcard "**:name" inserts the remainder from the named key
1024
- *
1025
- * Examples:
1026
- * - buildPath("/users/:id", { id: "123" }) => "/users/123"
1027
- * - buildPath("/files/**", { "**": "a/b" }) => "/files/a/b"
1028
- * - buildPath("/files/**:rest", { rest: "a/b" }) => "/files/a/b"
1029
- */
1030
- function buildPath(pathPattern, params) {
1031
- const patternSegments = pathPattern.split("/");
1032
- const builtSegments = [];
1033
- for (const segment of patternSegments) {
1034
- if (segment.length === 0) {
1035
- builtSegments.push("");
1036
- continue;
1037
- }
1038
- if (segment.startsWith(":")) {
1039
- const name = segment.slice(1);
1040
- const value = params[name];
1041
- if (value === void 0) throw new Error(`Missing value for path parameter :${name}`);
1042
- builtSegments.push(encodeURIComponent(value));
1043
- continue;
1044
- }
1045
- if (segment === "**") {
1046
- const value = params["**"];
1047
- if (value === void 0) throw new Error("Missing value for path wildcard **");
1048
- builtSegments.push(value);
1049
- continue;
1050
- }
1051
- if (segment.startsWith("**:")) {
1052
- const name = segment.slice(3);
1053
- const value = params[name];
1054
- if (value === void 0) throw new Error(`Missing value for path wildcard **:${name}`);
1055
- builtSegments.push(value);
1056
- continue;
1057
- }
1058
- builtSegments.push(segment);
1059
- }
1060
- return builtSegments.join("/");
1061
- }
1062
- /**
1063
- * Base error class for all Fragno client errors.
1064
- */
1065
- var FragnoClientError = class extends Error {
1066
- #code;
1067
- constructor(message, code, options = {}) {
1068
- super(message, { cause: options.cause });
1069
- this.name = "FragnoClientError";
1070
- this.#code = code;
1071
- }
1072
- get code() {
1073
- return this.#code;
1074
- }
1075
- };
1076
- var FragnoClientFetchError = class extends FragnoClientError {
1077
- constructor(message, code, options = {}) {
1078
- super(message, code, options);
1079
- this.name = "FragnoClientFetchError";
1080
- }
1081
- static fromUnknownFetchError(error) {
1082
- if (!(error instanceof Error)) return new FragnoClientFetchNetworkError("Network request failed", { cause: error });
1083
- if (error.name === "AbortError") return new FragnoClientFetchAbortError("Request was aborted", { cause: error });
1084
- return new FragnoClientFetchNetworkError("Network request failed", { cause: error });
1085
- }
1086
- };
1087
- /**
1088
- * Error thrown when a network request fails (e.g., no internet connection, DNS failure).
1089
- */
1090
- var FragnoClientFetchNetworkError = class extends FragnoClientFetchError {
1091
- constructor(message = "Network request failed", options = {}) {
1092
- super(message, "NETWORK_ERROR", options);
1093
- this.name = "FragnoClientFetchNetworkError";
1094
- }
1095
- };
1096
- /**
1097
- * Error thrown when a request is aborted (e.g., user cancels request, timeout).
1098
- */
1099
- var FragnoClientFetchAbortError = class extends FragnoClientFetchError {
1100
- constructor(message = "Request was aborted", options = {}) {
1101
- super(message, "ABORT_ERROR", options);
1102
- this.name = "FragnoClientFetchAbortError";
1103
- }
1104
- };
1105
- /**
1106
- * Error thrown when the API result is unexpected, e.g. no json is returned.
1107
- */
1108
- var FragnoClientUnknownApiError = class extends FragnoClientError {
1109
- #status;
1110
- constructor(message = "Unknown API error", status, options = {}) {
1111
- super(message, "UNKNOWN_API_ERROR", options);
1112
- this.name = "FragnoClientUnknownApiError";
1113
- this.#status = status;
1114
- }
1115
- get status() {
1116
- return this.#status;
1117
- }
1118
- };
1119
- var FragnoClientApiError = class FragnoClientApiError$1 extends FragnoClientError {
1120
- #status;
1121
- constructor({ message, code }, status, options = {}) {
1122
- super(message, code, options);
1123
- this.name = "FragnoClientApiError";
1124
- this.#status = status;
1125
- }
1126
- get status() {
1127
- return this.#status;
1128
- }
1129
- /**
1130
- * The error code returned by the API.
1131
- *
1132
- * The type is `TErrorCode` (the set of known error codes for this route), but may also be a string
1133
- * for forward compatibility with future error codes.
1134
- */
1135
- get code() {
1136
- return super.code;
1137
- }
1138
- static async fromResponse(response) {
1139
- const unknown = await response.json();
1140
- const status = response.status;
1141
- if (!("message" in unknown || "code" in unknown)) return new FragnoClientUnknownApiError("Unknown API error", status);
1142
- if (!(typeof unknown.message === "string" && typeof unknown.code === "string")) return new FragnoClientUnknownApiError("Unknown API error", status);
1143
- return new FragnoClientApiError$1({
1144
- message: unknown.message,
1145
- code: unknown.code
1146
- }, status);
1147
- }
1148
- };
1149
- /**
1150
- * Parses a content-type header string into its components
1151
- *
1152
- * @param contentType - The content-type header value to parse
1153
- * @returns A ParsedContentType object or null if the input is invalid
1154
- *
1155
- * @example
1156
- * ```ts
1157
- * const { type, subtype, mediaType, parameters }
1158
- * = parseContentType("application/json; charset=utf-8");
1159
- * console.assert(type === "application");
1160
- * console.assert(subtype === "json");
1161
- * console.assert(mediaType === "application/json");
1162
- * console.assert(parameters["charset"] === "utf-8");
1163
- */
1164
- function parseContentType(contentType) {
1165
- if (!contentType || typeof contentType !== "string") return null;
1166
- const trimmed = contentType.trim();
1167
- if (!trimmed) return null;
1168
- const parts = trimmed.split(";").map((part) => part.trim());
1169
- const mediaType = parts[0];
1170
- if (!mediaType) return null;
1171
- const typeParts = mediaType.split("/");
1172
- if (typeParts.length !== 2) return null;
1173
- const [type, subtype] = typeParts.map((part) => part.trim().toLowerCase());
1174
- if (!type || !subtype) return null;
1175
- const parameters = {};
1176
- for (let i = 1; i < parts.length; i++) {
1177
- const param = parts[i];
1178
- const equalIndex = param.indexOf("=");
1179
- if (equalIndex > 0) {
1180
- const key = param.slice(0, equalIndex).trim().toLowerCase();
1181
- let value = param.slice(equalIndex + 1).trim();
1182
- if (value.startsWith("\"") && value.endsWith("\"")) value = value.slice(1, -1);
1183
- if (key) parameters[key] = value;
1184
- }
1185
- }
1186
- return {
1187
- type,
1188
- subtype,
1189
- mediaType: `${type}/${subtype}`,
1190
- parameters
1191
- };
1192
- }
1193
- /**
1194
- * Creates a promise that rejects when the abort signal is triggered
1195
- */
1196
- function createAbortPromise(abortSignal) {
1197
- return new Promise((_, reject) => {
1198
- const abortHandler = () => {
1199
- reject(new FragnoClientFetchAbortError("Operation was aborted"));
1200
- };
1201
- if (abortSignal.aborted) abortHandler();
1202
- else abortSignal.addEventListener("abort", abortHandler, { once: true });
1203
- });
1204
- }
1205
- /**
1206
- * Handles NDJSON streaming responses by returning the first item from the fetcher
1207
- * and then continuing to stream updates via the store's mutate method.
1208
- *
1209
- * This makes it so that we can wait until the first chunk before updating the store, if we did
1210
- * not do this, `loading` would briefly be false before the first item would be populated in the
1211
- * result.
1212
- *
1213
- * @param response - The fetch Response object containing the NDJSON stream
1214
- * @param store - The fetcher store to update with streaming data
1215
- * @param abortSignal - Optional AbortSignal to cancel the streaming operation
1216
- * @returns A promise that resolves to an object containing the first item and a streaming promise
1217
- */
1218
- async function handleNdjsonStreamingFirstItem(response, store, options = {}) {
1219
- if (!response.body) throw new FragnoClientFetchError("Streaming response has no body", "NO_BODY");
1220
- const { abortSignal } = options;
1221
- if (abortSignal?.aborted) throw new FragnoClientFetchAbortError("Operation was aborted");
1222
- const decoder = new TextDecoder();
1223
- const reader = response.body.getReader();
1224
- let buffer = "";
1225
- let firstItem = null;
1226
- const items = [];
1227
- try {
1228
- while (firstItem === null) {
1229
- if (abortSignal?.aborted) {
1230
- reader.releaseLock();
1231
- throw new FragnoClientFetchAbortError("Operation was aborted");
1232
- }
1233
- const { done, value } = await (abortSignal ? Promise.race([reader.read(), createAbortPromise(abortSignal)]) : reader.read());
1234
- if (done) break;
1235
- buffer += decoder.decode(value, { stream: true });
1236
- const lines = buffer.split("\n");
1237
- buffer = lines.pop() || "";
1238
- for (const line of lines) {
1239
- if (!line.trim()) continue;
1240
- try {
1241
- const jsonObject = JSON.parse(line);
1242
- items.push(jsonObject);
1243
- if (firstItem === null) {
1244
- firstItem = jsonObject;
1245
- const streamingPromise = continueStreaming(reader, decoder, buffer, items, store, abortSignal);
1246
- return {
1247
- firstItem,
1248
- streamingPromise
1249
- };
1250
- }
1251
- } catch (parseError) {
1252
- throw new FragnoClientUnknownApiError("Failed to parse NDJSON line", 500, { cause: parseError });
1253
- }
1254
- }
1255
- }
1256
- if (firstItem === null) {
1257
- reader.releaseLock();
1258
- throw new FragnoClientUnknownApiError("NDJSON stream contained no valid items", 500);
1259
- }
1260
- reader.releaseLock();
1261
- throw new FragnoClientFetchError("Unexpected end of stream processing", "NO_BODY");
1262
- } catch (error) {
1263
- if (error instanceof FragnoClientError) {
1264
- store?.setError(error);
1265
- throw error;
1266
- } else {
1267
- const clientError = new FragnoClientUnknownApiError("Unknown streaming error", 500, { cause: error });
1268
- store?.setError(clientError);
1269
- throw clientError;
1270
- }
1271
- }
1272
- }
1273
- /**
1274
- * Continues streaming the remaining items in the background
1275
- */
1276
- async function continueStreaming(reader, decoder, initialBuffer, items, store, abortSignal) {
1277
- let buffer = initialBuffer;
1278
- try {
1279
- while (true) {
1280
- if (abortSignal?.aborted) throw new FragnoClientFetchAbortError("Operation was aborted");
1281
- const { done, value } = await (abortSignal ? Promise.race([reader.read(), createAbortPromise(abortSignal)]) : reader.read());
1282
- if (done) {
1283
- if (buffer.trim()) {
1284
- const lines$1 = buffer.split("\n");
1285
- for (const line of lines$1) {
1286
- if (!line.trim()) continue;
1287
- try {
1288
- const jsonObject = JSON.parse(line);
1289
- items.push(jsonObject);
1290
- store?.setData([...items]);
1291
- } catch (parseError) {
1292
- throw new FragnoClientUnknownApiError("Failed to parse NDJSON line", 400, { cause: parseError });
1293
- }
1294
- }
1295
- }
1296
- break;
1297
- }
1298
- buffer += decoder.decode(value, { stream: true });
1299
- const lines = buffer.split("\n");
1300
- buffer = lines.pop() || "";
1301
- for (const line of lines) {
1302
- if (!line.trim()) continue;
1244
+ fetcherStore.listen = (listener) => {
1245
+ const unsub = originListen(listener);
1246
+ listener(fetcherStore.value);
1247
+ handleNewListener();
1248
+ return unsub;
1249
+ };
1250
+ onStop(fetcherStore, () => {
1251
+ fetcherStore.value = { ...notLoading };
1252
+ keysInternalUnsub?.();
1253
+ evtUnsubs.forEach((fn) => fn());
1254
+ evtUnsubs = [];
1255
+ keyUnsub?.();
1256
+ clearInterval(_revalidateOnInterval.get(keyInput));
1257
+ });
1258
+ return fetcherStore;
1259
+ };
1260
+ const iterOverCache = (keySelector, cb) => {
1261
+ for (const key of cache.keys()) if (testKeyAgainstSelector(key, keySelector)) cb(key);
1262
+ };
1263
+ const invalidateKeys = (keySelector) => {
1264
+ iterOverCache(keySelector, (key) => {
1265
+ cache.delete(key);
1266
+ });
1267
+ events.emit(INVALIDATE_KEYS, keySelector);
1268
+ };
1269
+ const revalidateKeys = (keySelector) => {
1270
+ iterOverCache(keySelector, (key) => {
1271
+ const cached = cache.get(key);
1272
+ if (cached) cache.set(key, {
1273
+ ...cached,
1274
+ created: -Infinity
1275
+ });
1276
+ });
1277
+ events.emit(REVALIDATE_KEYS, keySelector);
1278
+ };
1279
+ const mutateCache = (keySelector, data) => {
1280
+ iterOverCache(keySelector, (key) => {
1281
+ if (data === void 0) cache.delete(key);
1282
+ else cache.set(key, {
1283
+ data,
1284
+ created: getNow(),
1285
+ expires: getNow() + (globalSettings.cacheLifetime ?? 8e3)
1286
+ });
1287
+ });
1288
+ events.emit(SET_CACHE, keySelector, data);
1289
+ };
1290
+ function createMutatorStore(mutator, opts) {
1291
+ const { throttleCalls, onError } = opts ?? {
1292
+ throttleCalls: true,
1293
+ onError: globalSettings?.onError
1294
+ };
1295
+ const mutate = async (data) => {
1296
+ if (throttleCalls && store.value?.loading) return;
1297
+ const newMutator = rewrittenSettings.fetcher ?? mutator;
1298
+ const keysToInvalidate = [], keysToRevalidate = [];
1299
+ const safeKeySet = (k, v) => {
1300
+ if (store.lc) store.setKey(k, v);
1301
+ };
1303
1302
  try {
1304
- const jsonObject = JSON.parse(line);
1305
- items.push(jsonObject);
1306
- store?.setData([...items]);
1307
- } catch (parseError) {
1308
- throw new FragnoClientUnknownApiError("Failed to parse NDJSON line", 400, { cause: parseError });
1303
+ store.set({
1304
+ error: void 0,
1305
+ data: void 0,
1306
+ mutate,
1307
+ ...loading
1308
+ });
1309
+ const result = await newMutator({
1310
+ data,
1311
+ invalidate: (key) => {
1312
+ keysToInvalidate.push(key);
1313
+ },
1314
+ revalidate: (key) => {
1315
+ keysToRevalidate.push(key);
1316
+ },
1317
+ getCacheUpdater: (key, shouldRevalidate = true) => [(newVal) => {
1318
+ mutateCache(key, newVal);
1319
+ if (shouldRevalidate) keysToRevalidate.push(key);
1320
+ }, cache.get(key)?.data]
1321
+ });
1322
+ safeKeySet("data", result);
1323
+ return result;
1324
+ } catch (error) {
1325
+ onError?.(error);
1326
+ safeKeySet("error", error);
1327
+ store.setKey("error", error);
1328
+ } finally {
1329
+ safeKeySet("loading", false);
1330
+ keysToInvalidate.forEach(invalidateKeys);
1331
+ keysToRevalidate.forEach(revalidateKeys);
1309
1332
  }
1310
- }
1311
- }
1312
- } catch (error) {
1313
- if (error instanceof FragnoClientError) store?.setError(error);
1314
- else {
1315
- const clientError = new FragnoClientUnknownApiError("Unknown streaming error", 400, { cause: error });
1316
- store?.setError(clientError);
1317
- throw clientError;
1333
+ };
1334
+ const store = map({
1335
+ mutate,
1336
+ ...notLoading
1337
+ });
1338
+ onStop(store, () => store.set({
1339
+ mutate,
1340
+ ...notLoading
1341
+ }));
1342
+ store.mutate = mutate;
1343
+ return store;
1318
1344
  }
1319
- throw error;
1320
- } finally {
1321
- reader.releaseLock();
1345
+ const __unsafeOverruleSettings = (data) => {
1346
+ console.warn(`You should only use __unsafeOverruleSettings in test environment`);
1347
+ rewrittenSettings = data;
1348
+ };
1349
+ return [
1350
+ createFetcherStore,
1351
+ createMutatorStore,
1352
+ {
1353
+ __unsafeOverruleSettings,
1354
+ invalidateKeys,
1355
+ revalidateKeys,
1356
+ mutateCache
1357
+ }
1358
+ ];
1359
+ };
1360
+ function isSomeKey(key) {
1361
+ return typeof key === "string" || typeof key === "number" || key === true;
1322
1362
  }
1323
- return items;
1324
- }
1325
- /**
1326
- * Normalizes a value that could be a plain value, an Atom, or a Vue Ref to a plain value.
1327
- */
1328
- function unwrapAtom(value) {
1329
- if (value && typeof value === "object" && "get" in value && typeof value.get === "function") return value.get();
1330
- return value;
1331
- }
1332
- /**
1333
- * Normalizes an object where values can be plain values, Atoms, or Vue Refs.
1334
- * Returns a new object with all values normalized to plain values.
1335
- */
1336
- function unwrapObject(params) {
1337
- if (!params) return;
1338
- return Object.fromEntries(Object.entries(params).map(([key, value]) => [key, unwrapAtom(value)]));
1339
- }
1340
- function isReadableAtom(value) {
1341
- if (!value) return false;
1342
- if (typeof value !== "object" || value === null) return false;
1343
- if (!("get" in value) || typeof value.get !== "function") return false;
1344
- if (!("lc" in value) || typeof value.lc !== "number") return false;
1345
- if (!("notify" in value) || typeof value.notify !== "function") return false;
1346
- if (!("off" in value) || typeof value.off !== "function") return false;
1347
- if (!("subscribe" in value) || typeof value.subscribe !== "function") return false;
1348
- if (!("value" in value)) return false;
1349
- return true;
1350
- }
1351
- /**
1352
- * Merge two fetcher configurations, with user config taking precedence.
1353
- * If user provides a custom function, it takes full precedence.
1354
- * Otherwise, deep merge RequestInit options.
1355
- */
1356
- function mergeFetcherConfigs(authorConfig, userConfig) {
1357
- if (userConfig?.type === "function") return userConfig;
1358
- if (!userConfig && authorConfig?.type === "function") return authorConfig;
1359
- const authorOpts = authorConfig?.type === "options" ? authorConfig.options : {};
1360
- const userOpts = userConfig?.type === "options" ? userConfig.options : {};
1361
- if (Object.keys(authorOpts).length === 0 && Object.keys(userOpts).length === 0) return;
1362
- return {
1363
- type: "options",
1364
- options: {
1365
- ...authorOpts,
1366
- ...userOpts,
1367
- headers: mergeHeaders(authorOpts.headers, userOpts.headers)
1363
+ const getKeyStore = (keys) => {
1364
+ if (isSomeKey(keys)) return [atom(["" + keys, [keys]]), () => {}];
1365
+ const keyParts = [];
1366
+ const $key = atom(null);
1367
+ const keysAsStoresToIndexes = /* @__PURE__ */ new Map();
1368
+ const setKeyStoreValue = () => {
1369
+ if (keyParts.some((v) => v === null || v === void 0 || v === false)) $key.set(null);
1370
+ else $key.set([keyParts.join(""), keyParts]);
1371
+ };
1372
+ for (let i = 0; i < keys.length; i++) {
1373
+ const keyOrStore = keys[i];
1374
+ if (isSomeKey(keyOrStore)) keyParts.push(keyOrStore);
1375
+ else {
1376
+ keyParts.push(null);
1377
+ keysAsStoresToIndexes.set(keyOrStore, i);
1378
+ }
1368
1379
  }
1380
+ const storesAsArray = [...keysAsStoresToIndexes.keys()];
1381
+ const $storeKeys = batched(storesAsArray, (...storeValues) => {
1382
+ for (let i = 0; i < storeValues.length; i++) {
1383
+ const store = storesAsArray[i], partIndex = keysAsStoresToIndexes.get(store);
1384
+ keyParts[partIndex] = store._ === fetcherSymbol ? store.value && "data" in store.value ? store.key : null : storeValues[i];
1385
+ }
1386
+ setKeyStoreValue();
1387
+ });
1388
+ setKeyStoreValue();
1389
+ return [$key, $storeKeys.subscribe(noop)];
1369
1390
  };
1370
- }
1371
- /**
1372
- * Merge headers from author and user configs.
1373
- * User headers override author headers.
1374
- */
1375
- function mergeHeaders(author, user) {
1376
- if (!author && !user) return;
1377
- const merged = new Headers(author);
1378
- new Headers(user).forEach((value, key) => merged.set(key, value));
1379
- if (merged.keys().next().done) return;
1380
- return merged;
1381
- }
1391
+ function noop() {}
1392
+ const FOCUS = 1, RECONNECT = 2, INVALIDATE_KEYS = 3, REVALIDATE_KEYS = 4, SET_CACHE = 5;
1393
+ const testKeyAgainstSelector = (key, selector) => {
1394
+ if (Array.isArray(selector)) return selector.includes(key);
1395
+ else if (typeof selector === "function") return selector(key);
1396
+ else return key === selector;
1397
+ };
1398
+ const getNow = () => (/* @__PURE__ */ new Date()).getTime();
1399
+ const fetcherSymbol = Symbol();
1400
+ const loading = { loading: true }, notLoading = { loading: false };
1401
+ return nanoquery$1;
1402
+ };
1403
+ const subscribe = (name, fn) => {
1404
+ const isServer = typeof window === "undefined";
1405
+ if (!isServer) addEventListener(name, fn);
1406
+ };
1407
+ const browserCompat = [
1408
+ () => !document.hidden,
1409
+ (cb) => subscribe("visibilitychange", cb),
1410
+ (cb) => subscribe("online", cb)
1411
+ ];
1412
+ const nanoquery = nanoqueryFactory(browserCompat);
1413
+
1414
+ //#endregion
1415
+ //#region ../fragno/dist/client/client.js
1382
1416
  /**
1383
1417
  * Symbols used to identify hook types
1384
1418
  */
1385
1419
  const GET_HOOK_SYMBOL = Symbol("fragno-get-hook");
1386
1420
  const MUTATOR_HOOK_SYMBOL = Symbol("fragno-mutator-hook");
1387
1421
  const STORE_SYMBOL = Symbol("fragno-store");
1422
+ /**
1423
+ * @internal
1424
+ */
1388
1425
  function buildUrl(config, params) {
1389
1426
  const { baseUrl = "", mountRoute, path } = config;
1390
1427
  const { pathParams, queryParams } = params ?? {};
@@ -1403,6 +1440,7 @@ function buildUrl(config, params) {
1403
1440
  * @param path
1404
1441
  * @param params
1405
1442
  * @returns
1443
+ * @internal
1406
1444
  */
1407
1445
  function getCacheKey(method, path, params) {
1408
1446
  if (!params) return [method, path];
@@ -1428,12 +1466,21 @@ function isStreamingResponse(response) {
1428
1466
  if (contentType.subtype === "x-ndjson") return "ndjson";
1429
1467
  return false;
1430
1468
  }
1469
+ /**
1470
+ * @internal
1471
+ */
1431
1472
  function isGetHook(hook) {
1432
1473
  return typeof hook === "object" && hook !== null && GET_HOOK_SYMBOL in hook && hook[GET_HOOK_SYMBOL] === true;
1433
1474
  }
1475
+ /**
1476
+ * @internal
1477
+ */
1434
1478
  function isMutatorHook(hook) {
1435
1479
  return typeof hook === "object" && hook !== null && MUTATOR_HOOK_SYMBOL in hook && hook[MUTATOR_HOOK_SYMBOL] === true;
1436
1480
  }
1481
+ /**
1482
+ * @internal
1483
+ */
1437
1484
  function isStore(obj) {
1438
1485
  return typeof obj === "object" && obj !== null && STORE_SYMBOL in obj && obj[STORE_SYMBOL] === true;
1439
1486
  }
@@ -1722,16 +1769,20 @@ var ClientBuilder = class {
1722
1769
  this.#invalidateKeys((key) => key.startsWith(prefix));
1723
1770
  }
1724
1771
  };
1725
- function createClientBuilder(fragmentBuilder, publicConfig, routesOrFactories, authorFetcherConfig) {
1726
- const definition = fragmentBuilder.definition;
1727
- const routes = resolveRouteFactories({
1772
+ /**
1773
+ * Create a client builder for fragments using the new fragment definition API.
1774
+ * This is the same as createClientBuilder but works with FragmentDefinition.
1775
+ */
1776
+ function createClientBuilder(definition, publicConfig, routesOrFactories, authorFetcherConfig) {
1777
+ const routes$1 = resolveRouteFactories({
1728
1778
  config: {},
1729
1779
  deps: {},
1730
- services: {}
1780
+ services: {},
1781
+ serviceDeps: {}
1731
1782
  }, routesOrFactories);
1732
1783
  const fragmentConfig = {
1733
1784
  name: definition.name,
1734
- routes
1785
+ routes: routes$1
1735
1786
  };
1736
1787
  const mountRoute = publicConfig.mountRoute ?? `/${definition.name}`;
1737
1788
  const mergedFetcherConfig = mergeFetcherConfigs(authorFetcherConfig, publicConfig.fetcherConfig);
@@ -1742,9 +1793,13 @@ function createClientBuilder(fragmentBuilder, publicConfig, routesOrFactories, a
1742
1793
  }, fragmentConfig);
1743
1794
  }
1744
1795
 
1796
+ //#endregion
1797
+ //#region src/definition.ts
1798
+ const stripeFragmentDefinition = defineFragment("stripe").extend().withDependencies(() => {}).providesBaseService().build();
1799
+
1745
1800
  //#endregion
1746
1801
  //#region src/routes/webhooks.ts
1747
- const webhookRoutesFactory = defineRoutes().create(({ config, deps, services }) => {
1802
+ const webhookRoutesFactory = defineRoutes(stripeFragmentDefinition).create(({ config, deps, services, defineRoute }) => {
1748
1803
  return [defineRoute({
1749
1804
  method: "POST",
1750
1805
  path: "/webhook",
@@ -1784,7 +1839,7 @@ const CustomerResponseSchema = z.object({
1784
1839
 
1785
1840
  //#endregion
1786
1841
  //#region src/routes/customers.ts
1787
- const customersRoutesFactory = defineRoutes().create(({ deps, config }) => {
1842
+ const customersRoutesFactory = defineRoutes(stripeFragmentDefinition).create(({ deps, config, defineRoute }) => {
1788
1843
  return [defineRoute({
1789
1844
  method: "GET",
1790
1845
  path: "/admin/customers",
@@ -1797,6 +1852,16 @@ const customersRoutesFactory = defineRoutes().create(({ deps, config }) => {
1797
1852
  hasMore: z.boolean().describe("Whether there are more customers to fetch")
1798
1853
  }),
1799
1854
  handler: () => {}
1855
+ }), defineRoute({
1856
+ method: "POST",
1857
+ path: "/portal",
1858
+ inputSchema: z.object({ returnUrl: z.url().describe("URL to redirect to after completing billing portal") }),
1859
+ outputSchema: z.object({
1860
+ url: z.url().describe("URL to redirect to after cancellation"),
1861
+ redirect: z.boolean().describe("Whether to redirect to the URL")
1862
+ }),
1863
+ errorCodes: ["NO_STRIPE_CUSTOMER_FOR_ENTITY"],
1864
+ handler: () => {}
1800
1865
  })];
1801
1866
  });
1802
1867
 
@@ -1833,12 +1898,14 @@ const SubscriptionUpgradeRequestSchema = z.object({
1833
1898
  quantity: z.number().positive().describe("Number of seats"),
1834
1899
  successUrl: z.url().describe("Redirect URL after successful checkout"),
1835
1900
  cancelUrl: z.url().describe("Redirect URL if checkout is cancelled"),
1836
- returnUrl: z.string().optional().describe("Return URL for billing portal")
1901
+ returnUrl: z.string().optional().describe("Return URL for billing portal"),
1902
+ promotionCode: z.string().optional().describe("Promotion code to apply"),
1903
+ subscriptionId: z.string().optional().describe("Subscription ID to upgrade, if none provided assume the active subscription of the user.")
1837
1904
  });
1838
1905
 
1839
1906
  //#endregion
1840
1907
  //#region src/routes/subscriptions.ts
1841
- const subscriptionsRoutesFactory = defineRoutes().create(({ deps, services, config }) => {
1908
+ const subscriptionsRoutesFactory = defineRoutes(stripeFragmentDefinition).create(({ deps, services, config, defineRoute }) => {
1842
1909
  return [
1843
1910
  defineRoute({
1844
1911
  method: "GET",
@@ -1860,14 +1927,21 @@ const subscriptionsRoutesFactory = defineRoutes().create(({ deps, services, conf
1860
1927
  "SUBSCRIPTION_NOT_FOUND",
1861
1928
  "CUSTOMER_SUBSCRIPTION_MISMATCH",
1862
1929
  "UPGRADE_HAS_NO_EFFECT",
1863
- "SUBSCRIPTION_UPDATE_NOT_ALLOWED"
1930
+ "SUBSCRIPTION_UPDATE_NOT_ALLOWED",
1931
+ "SUBSCRIPTION_UPDATE_PROMO_CODE_NOT_ALLOWED",
1932
+ "PROMOTION_CODE_CUSTOMER_NOT_FIRST_TIME",
1933
+ "MULTIPLE_ACTIVE_SUBSCRIPTIONS",
1934
+ "NO_ACTIVE_SUBSCRIPTIONS"
1864
1935
  ],
1865
1936
  handler: () => {}
1866
1937
  }),
1867
1938
  defineRoute({
1868
1939
  method: "POST",
1869
1940
  path: "/subscription/cancel",
1870
- inputSchema: z.object({ returnUrl: z.url().describe("URL to redirect to after cancellation is complete") }),
1941
+ inputSchema: z.object({
1942
+ returnUrl: z.url().describe("URL to redirect to after cancellation is complete"),
1943
+ subscriptionId: z.string().optional().describe("Which subscription to cancel, if there are multiple active subscriptions")
1944
+ }),
1871
1945
  outputSchema: z.object({
1872
1946
  url: z.url().describe("URL to redirect to after cancellation"),
1873
1947
  redirect: z.boolean().describe("Whether to redirect to the URL")
@@ -1875,7 +1949,9 @@ const subscriptionsRoutesFactory = defineRoutes().create(({ deps, services, conf
1875
1949
  errorCodes: [
1876
1950
  "SUBSCRIPTION_NOT_FOUND",
1877
1951
  "NO_SUBSCRIPTION_TO_CANCEL",
1878
- "SUBSCRIPTION_ALREADY_CANCELED"
1952
+ "SUBSCRIPTION_ALREADY_CANCELED",
1953
+ "NO_STRIPE_CUSTOMER_LINKED",
1954
+ "MULTIPLE_SUBSCRIPTIONS_FOUND"
1879
1955
  ],
1880
1956
  handler: () => {}
1881
1957
  })
@@ -1909,7 +1985,7 @@ const ProductResponseSchema = z.object({
1909
1985
 
1910
1986
  //#endregion
1911
1987
  //#region src/routes/products.ts
1912
- const productsRoutesFactory = defineRoutes().create(({ deps, config }) => {
1988
+ const productsRoutesFactory = defineRoutes(stripeFragmentDefinition).create(({ deps, config, defineRoute }) => {
1913
1989
  return [defineRoute({
1914
1990
  method: "GET",
1915
1991
  path: "/admin/products",
@@ -1964,7 +2040,7 @@ const PriceResponseSchema = z.object({
1964
2040
 
1965
2041
  //#endregion
1966
2042
  //#region src/routes/prices.ts
1967
- const pricesRoutesFactory = defineRoutes().create(({ deps, config }) => {
2043
+ const pricesRoutesFactory = defineRoutes(stripeFragmentDefinition).create(({ deps, config, defineRoute }) => {
1968
2044
  return [defineRoute({
1969
2045
  method: "GET",
1970
2046
  path: "/admin/products/:productId/prices",
@@ -1982,29 +2058,24 @@ const pricesRoutesFactory = defineRoutes().create(({ deps, config }) => {
1982
2058
 
1983
2059
  //#endregion
1984
2060
  //#region src/index.ts
1985
- const stripeFragmentDefinition = defineFragment("stripe").withDependencies(() => {}).providesService(() => {});
2061
+ const routes = [
2062
+ webhookRoutesFactory,
2063
+ customersRoutesFactory,
2064
+ subscriptionsRoutesFactory,
2065
+ productsRoutesFactory,
2066
+ pricesRoutesFactory
2067
+ ];
1986
2068
  function createStripeFragment(config, fragnoConfig) {
1987
- return createFragment(stripeFragmentDefinition, config, [
1988
- webhookRoutesFactory,
1989
- customersRoutesFactory,
1990
- subscriptionsRoutesFactory,
1991
- productsRoutesFactory,
1992
- pricesRoutesFactory
1993
- ], fragnoConfig);
2069
+ return {};
1994
2070
  }
1995
2071
  function createStripeFragmentClients(fragnoConfig = {}) {
1996
- const builder = createClientBuilder(stripeFragmentDefinition, fragnoConfig, [
1997
- webhookRoutesFactory,
1998
- customersRoutesFactory,
1999
- subscriptionsRoutesFactory,
2000
- productsRoutesFactory,
2001
- pricesRoutesFactory
2002
- ]);
2072
+ const builder = createClientBuilder(stripeFragmentDefinition, fragnoConfig, routes);
2003
2073
  return {
2004
2074
  useCustomers: builder.createHook("/admin/customers"),
2005
2075
  useProducts: builder.createHook("/admin/products"),
2006
2076
  usePrices: builder.createHook("/admin/products/:productId/prices"),
2007
2077
  useSubscription: builder.createHook("/admin/subscriptions"),
2078
+ useBillingPortal: builder.createMutator("POST", "/portal"),
2008
2079
  upgradeSubscription: builder.createMutator("POST", "/subscription/upgrade"),
2009
2080
  cancelSubscription: builder.createMutator("POST", "/subscription/cancel")
2010
2081
  };
@@ -2012,4 +2083,4 @@ function createStripeFragmentClients(fragnoConfig = {}) {
2012
2083
 
2013
2084
  //#endregion
2014
2085
  export { atom, createStripeFragment, createStripeFragmentClients, isGetHook, isMutatorHook, isReadableAtom, isStore, stripeFragmentDefinition };
2015
- //# sourceMappingURL=src-k2PmVjgJ.js.map
2086
+ //# sourceMappingURL=src-BQ175qUc.js.map