@fragno-dev/stripe 1.0.0 → 2.0.2

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,4 +1,5 @@
1
1
  import { defineFragment, defineRoutes } from "@fragno-dev/core";
2
+ import { column, idColumn, schema } from "@fragno-dev/db/schema";
2
3
  import { z } from "zod";
3
4
 
4
5
  //#region ../fragno/dist/api/route.js
@@ -15,6 +16,80 @@ function resolveRouteFactories(context, routesOrFactories) {
15
16
  return routes$1;
16
17
  }
17
18
 
19
+ //#endregion
20
+ //#region ../fragno/dist/api/internal/path.js
21
+ /**
22
+ * Extract parameter names from a path pattern at runtime.
23
+ * Examples:
24
+ * - "/users/:id" => ["id"]
25
+ * - "/files/**" => ["**"]
26
+ * - "/files/**:rest" => ["rest"]
27
+ */
28
+ function extractPathParams(pathPattern) {
29
+ const segments = pathPattern.split("/").filter((s) => s.length > 0);
30
+ const names = [];
31
+ for (const segment of segments) {
32
+ if (segment.startsWith(":")) {
33
+ names.push(segment.slice(1));
34
+ continue;
35
+ }
36
+ if (segment === "**") {
37
+ names.push("**");
38
+ continue;
39
+ }
40
+ if (segment.startsWith("**:")) {
41
+ names.push(segment.slice(3));
42
+ continue;
43
+ }
44
+ }
45
+ return names;
46
+ }
47
+ /**
48
+ * Build a concrete path by replacing placeholders in a path pattern with values.
49
+ *
50
+ * Supports the same placeholder syntax as the matcher:
51
+ * - Named parameter ":name" is URL-encoded as a single segment
52
+ * - Anonymous wildcard "**" inserts the remainder as-is (slashes preserved)
53
+ * - Named wildcard "**:name" inserts the remainder from the named key
54
+ *
55
+ * Examples:
56
+ * - buildPath("/users/:id", { id: "123" }) => "/users/123"
57
+ * - buildPath("/files/**", { "**": "a/b" }) => "/files/a/b"
58
+ * - buildPath("/files/**:rest", { rest: "a/b" }) => "/files/a/b"
59
+ */
60
+ function buildPath(pathPattern, params) {
61
+ const patternSegments = pathPattern.split("/");
62
+ const builtSegments = [];
63
+ for (const segment of patternSegments) {
64
+ if (segment.length === 0) {
65
+ builtSegments.push("");
66
+ continue;
67
+ }
68
+ if (segment.startsWith(":")) {
69
+ const name = segment.slice(1);
70
+ const value = params[name];
71
+ if (value === void 0) throw new Error(`Missing value for path parameter :${name}`);
72
+ builtSegments.push(encodeURIComponent(value));
73
+ continue;
74
+ }
75
+ if (segment === "**") {
76
+ const value = params["**"];
77
+ if (value === void 0) throw new Error("Missing value for path wildcard **");
78
+ builtSegments.push(value);
79
+ continue;
80
+ }
81
+ if (segment.startsWith("**:")) {
82
+ const name = segment.slice(3);
83
+ const value = params[name];
84
+ if (value === void 0) throw new Error(`Missing value for path wildcard **:${name}`);
85
+ builtSegments.push(value);
86
+ continue;
87
+ }
88
+ builtSegments.push(segment);
89
+ }
90
+ return builtSegments.join("/");
91
+ }
92
+
18
93
  //#endregion
19
94
  //#region ../fragno/dist/api/internal/route.js
20
95
  function getMountRoute(opts) {
@@ -161,6 +236,72 @@ var RequestInputContext = class RequestInputContext$1 {
161
236
  return this.#body;
162
237
  }
163
238
  /**
239
+ * Access the request body as FormData.
240
+ *
241
+ * Use this method when handling file uploads or multipart form submissions.
242
+ * The request must have been sent with Content-Type: multipart/form-data.
243
+ *
244
+ * @throws Error if the request body is not FormData
245
+ *
246
+ * @example
247
+ * ```typescript
248
+ * defineRoute({
249
+ * method: "POST",
250
+ * path: "/upload",
251
+ * async handler(ctx, res) {
252
+ * const formData = ctx.formData();
253
+ * const file = formData.get("file") as File;
254
+ * const description = formData.get("description") as string;
255
+ * // ... process file
256
+ * }
257
+ * });
258
+ * ```
259
+ */
260
+ formData() {
261
+ if (!(this.#parsedBody instanceof FormData)) throw new Error("Request body is not FormData. Ensure the request was sent with Content-Type: multipart/form-data.");
262
+ return this.#parsedBody;
263
+ }
264
+ /**
265
+ * Check if the request body is FormData.
266
+ *
267
+ * Useful for routes that accept both JSON and FormData payloads.
268
+ *
269
+ * @example
270
+ * ```typescript
271
+ * defineRoute({
272
+ * method: "POST",
273
+ * path: "/upload",
274
+ * async handler(ctx, res) {
275
+ * if (ctx.isFormData()) {
276
+ * const formData = ctx.formData();
277
+ * // handle file upload
278
+ * } else {
279
+ * const json = await ctx.input.valid();
280
+ * // handle JSON payload
281
+ * }
282
+ * }
283
+ * });
284
+ * ```
285
+ */
286
+ isFormData() {
287
+ return this.#parsedBody instanceof FormData;
288
+ }
289
+ /**
290
+ * Access the request body as a ReadableStream (application/octet-stream).
291
+ *
292
+ * @throws Error if the request body is not a ReadableStream
293
+ */
294
+ bodyStream() {
295
+ if (!(this.#parsedBody instanceof ReadableStream)) throw new Error("Request body is not a ReadableStream. Ensure the request was sent with Content-Type: application/octet-stream.");
296
+ return this.#parsedBody;
297
+ }
298
+ /**
299
+ * Check if the request body is a ReadableStream.
300
+ */
301
+ isBodyStream() {
302
+ return this.#parsedBody instanceof ReadableStream;
303
+ }
304
+ /**
164
305
  * Input validation context (only if inputSchema is defined)
165
306
  * @remarks `InputContext`
166
307
  */
@@ -177,6 +318,7 @@ var RequestInputContext = class RequestInputContext$1 {
177
318
  async #validateInput() {
178
319
  if (!this.#inputSchema) throw new Error("No input schema defined for this route");
179
320
  if (this.#parsedBody instanceof FormData || this.#parsedBody instanceof Blob) throw new Error("Schema validation is only supported for JSON data, not FormData or Blob");
321
+ if (this.#parsedBody instanceof ReadableStream) throw new Error("Schema validation is only supported for JSON data, not FormData, Blob, or ReadableStream");
180
322
  const result = await this.#inputSchema["~standard"].validate(this.#parsedBody);
181
323
  if (result.issues) throw new FragnoApiValidationError("Validation failed", result.issues);
182
324
  return result.value;
@@ -377,170 +519,6 @@ var RequestOutputContext = class extends OutputContext {
377
519
  }
378
520
  };
379
521
 
380
- //#endregion
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;
396
- }
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;
407
- }
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("/");
452
- }
453
-
454
- //#endregion
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
- }
542
- };
543
-
544
522
  //#endregion
545
523
  //#region ../fragno/dist/util/content-type.js
546
524
  /**
@@ -589,142 +567,36 @@ function parseContentType(contentType) {
589
567
  }
590
568
 
591
569
  //#endregion
592
- //#region ../fragno/dist/client/internal/ndjson-streaming.js
570
+ //#region ../fragno/dist/util/nanostores.js
593
571
  /**
594
- * Creates a promise that rejects when the abort signal is triggered
572
+ * Normalizes a value that could be a plain value, an Atom, or a Vue Ref to a plain value.
595
573
  */
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
- });
574
+ function unwrapAtom(value) {
575
+ if (value && typeof value === "object" && "get" in value && typeof value.get === "function") return value.get();
576
+ return value;
604
577
  }
605
578
  /**
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
579
+ * Normalizes an object where values can be plain values, Atoms, or Vue Refs.
580
+ * Returns a new object with all values normalized to plain values.
617
581
  */
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
- }
655
- }
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
- }
582
+ function unwrapObject(params) {
583
+ if (!params) return;
584
+ return Object.fromEntries(Object.entries(params).map(([key, value]) => [key, unwrapAtom(value)]));
672
585
  }
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;
697
- }
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 });
709
- }
710
- }
711
- }
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;
586
+ function isReadableAtom(value) {
587
+ if (!value) return false;
588
+ if (typeof value !== "object" || value === null) return false;
589
+ if (!("get" in value) || typeof value.get !== "function") return false;
590
+ if (!("lc" in value) || typeof value.lc !== "number") return false;
591
+ if (!("notify" in value) || typeof value.notify !== "function") return false;
592
+ if (!("off" in value) || typeof value.off !== "function") return false;
593
+ if (!("subscribe" in value) || typeof value.subscribe !== "function") return false;
594
+ if (!("value" in value)) return false;
595
+ return true;
724
596
  }
725
597
 
726
598
  //#endregion
727
- //#region ../../node_modules/.pnpm/nanostores@1.1.0/node_modules/nanostores/task/index.js
599
+ //#region ../../node_modules/.pnpm/nanostores@1.2.0/node_modules/nanostores/task/index.js
728
600
  let tasks = 0;
729
601
  let resolves = [];
730
602
  function startTask() {
@@ -746,11 +618,11 @@ function task(cb) {
746
618
  }
747
619
 
748
620
  //#endregion
749
- //#region ../../node_modules/.pnpm/nanostores@1.1.0/node_modules/nanostores/clean-stores/index.js
621
+ //#region ../../node_modules/.pnpm/nanostores@1.2.0/node_modules/nanostores/clean-stores/index.js
750
622
  let clean = Symbol("clean");
751
623
 
752
624
  //#endregion
753
- //#region ../../node_modules/.pnpm/nanostores@1.1.0/node_modules/nanostores/atom/index.js
625
+ //#region ../../node_modules/.pnpm/nanostores@1.2.0/node_modules/nanostores/atom/index.js
754
626
  let listenerQueue = [];
755
627
  let lqIndex = 0;
756
628
  const QUEUE_ITEMS_PER_LISTENER = 4;
@@ -762,6 +634,7 @@ const atom = /* @__NO_SIDE_EFFECTS__ */ (initialValue) => {
762
634
  if (!$atom.lc) $atom.listen(() => {})();
763
635
  return $atom.value;
764
636
  },
637
+ init: initialValue,
765
638
  lc: 0,
766
639
  listen(listener) {
767
640
  $atom.lc = listeners.push(listener);
@@ -808,7 +681,7 @@ const atom = /* @__NO_SIDE_EFFECTS__ */ (initialValue) => {
808
681
  };
809
682
 
810
683
  //#endregion
811
- //#region ../../node_modules/.pnpm/nanostores@1.1.0/node_modules/nanostores/lifecycle/index.js
684
+ //#region ../../node_modules/.pnpm/nanostores@1.2.0/node_modules/nanostores/lifecycle/index.js
812
685
  const START = 0;
813
686
  const STOP = 1;
814
687
  const MOUNT = 5;
@@ -903,7 +776,21 @@ let onMount = ($store, initialize) => {
903
776
  };
904
777
 
905
778
  //#endregion
906
- //#region ../../node_modules/.pnpm/nanostores@1.1.0/node_modules/nanostores/computed/index.js
779
+ //#region ../../node_modules/.pnpm/nanostores@1.2.0/node_modules/nanostores/warn/index.js
780
+ let warned = {};
781
+ function warn(text) {
782
+ if (!warned[text]) {
783
+ warned[text] = true;
784
+ if (typeof console !== "undefined" && console.warn) {
785
+ console.groupCollapsed("Nano Stores: " + text);
786
+ console.trace("Source of deprecated call");
787
+ console.groupEnd();
788
+ }
789
+ }
790
+ }
791
+
792
+ //#endregion
793
+ //#region ../../node_modules/.pnpm/nanostores@1.2.0/node_modules/nanostores/computed/index.js
907
794
  let computedStore = (stores$1, cb, batched$1) => {
908
795
  if (!Array.isArray(stores$1)) stores$1 = [stores$1];
909
796
  let previousArgs;
@@ -915,10 +802,12 @@ let computedStore = (stores$1, cb, batched$1) => {
915
802
  if (!previousArgs || args.some((arg, i) => arg !== previousArgs[i])) {
916
803
  previousArgs = args;
917
804
  let value = cb(...args);
918
- if (value && value.then && value.t) value.then((asyncValue) => {
919
- if (previousArgs === args) $computed.set(asyncValue);
920
- });
921
- else {
805
+ if (value && value.then && value.t) {
806
+ warn("Use @nanostores/async for async computed. We will remove Promise support in computed() in Nano Stores 2.0");
807
+ value.then((asyncValue) => {
808
+ if (previousArgs === args) $computed.set(asyncValue);
809
+ });
810
+ } else {
922
811
  $computed.set(value);
923
812
  currentEpoch = epoch;
924
813
  }
@@ -948,7 +837,7 @@ const computed = /* @__NO_SIDE_EFFECTS__ */ (stores$1, fn) => computedStore(stor
948
837
  const batched = /* @__NO_SIDE_EFFECTS__ */ (stores$1, fn) => computedStore(stores$1, fn, true);
949
838
 
950
839
  //#endregion
951
- //#region ../../node_modules/.pnpm/nanostores@1.1.0/node_modules/nanostores/map/index.js
840
+ //#region ../../node_modules/.pnpm/nanostores@1.2.0/node_modules/nanostores/map/index.js
952
841
  const map = /* @__NO_SIDE_EFFECTS__ */ (initial = {}) => {
953
842
  let $map = atom(initial);
954
843
  $map.setKey = function(key, value) {
@@ -985,33 +874,94 @@ function getInitialData(key) {
985
874
  }
986
875
 
987
876
  //#endregion
988
- //#region ../fragno/dist/util/nanostores.js
877
+ //#region ../fragno/dist/client/client-error.js
989
878
  /**
990
- * Normalizes a value that could be a plain value, an Atom, or a Vue Ref to a plain value.
879
+ * Base error class for all Fragno client errors.
991
880
  */
992
- function unwrapAtom(value) {
993
- if (value && typeof value === "object" && "get" in value && typeof value.get === "function") return value.get();
994
- return value;
995
- }
881
+ var FragnoClientError = class extends Error {
882
+ #code;
883
+ constructor(message, code, options = {}) {
884
+ super(message, { cause: options.cause });
885
+ this.name = "FragnoClientError";
886
+ this.#code = code;
887
+ }
888
+ get code() {
889
+ return this.#code;
890
+ }
891
+ };
892
+ var FragnoClientFetchError = class extends FragnoClientError {
893
+ constructor(message, code, options = {}) {
894
+ super(message, code, options);
895
+ this.name = "FragnoClientFetchError";
896
+ }
897
+ static fromUnknownFetchError(error) {
898
+ if (!(error instanceof Error)) return new FragnoClientFetchNetworkError("Network request failed", { cause: error });
899
+ if (error.name === "AbortError") return new FragnoClientFetchAbortError("Request was aborted", { cause: error });
900
+ return new FragnoClientFetchNetworkError("Network request failed", { cause: error });
901
+ }
902
+ };
996
903
  /**
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.
904
+ * Error thrown when a network request fails (e.g., no internet connection, DNS failure).
999
905
  */
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
- }
906
+ var FragnoClientFetchNetworkError = class extends FragnoClientFetchError {
907
+ constructor(message = "Network request failed", options = {}) {
908
+ super(message, "NETWORK_ERROR", options);
909
+ this.name = "FragnoClientFetchNetworkError";
910
+ }
911
+ };
912
+ /**
913
+ * Error thrown when a request is aborted (e.g., user cancels request, timeout).
914
+ */
915
+ var FragnoClientFetchAbortError = class extends FragnoClientFetchError {
916
+ constructor(message = "Request was aborted", options = {}) {
917
+ super(message, "ABORT_ERROR", options);
918
+ this.name = "FragnoClientFetchAbortError";
919
+ }
920
+ };
921
+ /**
922
+ * Error thrown when the API result is unexpected, e.g. no json is returned.
923
+ */
924
+ var FragnoClientUnknownApiError = class extends FragnoClientError {
925
+ #status;
926
+ constructor(message = "Unknown API error", status, options = {}) {
927
+ super(message, "UNKNOWN_API_ERROR", options);
928
+ this.name = "FragnoClientUnknownApiError";
929
+ this.#status = status;
930
+ }
931
+ get status() {
932
+ return this.#status;
933
+ }
934
+ };
935
+ var FragnoClientApiError = class FragnoClientApiError$1 extends FragnoClientError {
936
+ #status;
937
+ constructor({ message, code }, status, options = {}) {
938
+ super(message, code, options);
939
+ this.name = "FragnoClientApiError";
940
+ this.#status = status;
941
+ }
942
+ get status() {
943
+ return this.#status;
944
+ }
945
+ /**
946
+ * The error code returned by the API.
947
+ *
948
+ * The type is `TErrorCode` (the set of known error codes for this route), but may also be a string
949
+ * for forward compatibility with future error codes.
950
+ */
951
+ get code() {
952
+ return super.code;
953
+ }
954
+ static async fromResponse(response) {
955
+ const unknown = await response.json();
956
+ const status = response.status;
957
+ if (!("message" in unknown || "code" in unknown)) return new FragnoClientUnknownApiError("Unknown API error", status);
958
+ if (!(typeof unknown.message === "string" && typeof unknown.code === "string")) return new FragnoClientUnknownApiError("Unknown API error", status);
959
+ return new FragnoClientApiError$1({
960
+ message: unknown.message,
961
+ code: unknown.code
962
+ }, status);
963
+ }
964
+ };
1015
965
 
1016
966
  //#endregion
1017
967
  //#region ../fragno/dist/client/internal/fetcher-merge.js
@@ -1047,6 +997,141 @@ function mergeHeaders(author, user) {
1047
997
  return merged;
1048
998
  }
1049
999
 
1000
+ //#endregion
1001
+ //#region ../fragno/dist/client/internal/ndjson-streaming.js
1002
+ /**
1003
+ * Creates a promise that rejects when the abort signal is triggered
1004
+ */
1005
+ function createAbortPromise(abortSignal) {
1006
+ return new Promise((_, reject) => {
1007
+ const abortHandler = () => {
1008
+ reject(new FragnoClientFetchAbortError("Operation was aborted"));
1009
+ };
1010
+ if (abortSignal.aborted) abortHandler();
1011
+ else abortSignal.addEventListener("abort", abortHandler, { once: true });
1012
+ });
1013
+ }
1014
+ /**
1015
+ * Handles NDJSON streaming responses by returning the first item from the fetcher
1016
+ * and then continuing to stream updates via the store's mutate method.
1017
+ *
1018
+ * This makes it so that we can wait until the first chunk before updating the store, if we did
1019
+ * not do this, `loading` would briefly be false before the first item would be populated in the
1020
+ * result.
1021
+ *
1022
+ * @param response - The fetch Response object containing the NDJSON stream
1023
+ * @param store - The fetcher store to update with streaming data
1024
+ * @param abortSignal - Optional AbortSignal to cancel the streaming operation
1025
+ * @returns A promise that resolves to an object containing the first item and a streaming promise
1026
+ */
1027
+ async function handleNdjsonStreamingFirstItem(response, store, options = {}) {
1028
+ if (!response.body) throw new FragnoClientFetchError("Streaming response has no body", "NO_BODY");
1029
+ const { abortSignal } = options;
1030
+ if (abortSignal?.aborted) throw new FragnoClientFetchAbortError("Operation was aborted");
1031
+ const decoder = new TextDecoder();
1032
+ const reader = response.body.getReader();
1033
+ let buffer = "";
1034
+ let firstItem = null;
1035
+ const items = [];
1036
+ try {
1037
+ while (firstItem === null) {
1038
+ if (abortSignal?.aborted) {
1039
+ reader.releaseLock();
1040
+ throw new FragnoClientFetchAbortError("Operation was aborted");
1041
+ }
1042
+ const { done, value } = await (abortSignal ? Promise.race([reader.read(), createAbortPromise(abortSignal)]) : reader.read());
1043
+ if (done) break;
1044
+ buffer += decoder.decode(value, { stream: true });
1045
+ const lines = buffer.split("\n");
1046
+ buffer = lines.pop() || "";
1047
+ for (const line of lines) {
1048
+ if (!line.trim()) continue;
1049
+ try {
1050
+ const jsonObject = JSON.parse(line);
1051
+ items.push(jsonObject);
1052
+ if (firstItem === null) {
1053
+ firstItem = jsonObject;
1054
+ const streamingPromise = continueStreaming(reader, decoder, buffer, items, store, abortSignal);
1055
+ return {
1056
+ firstItem,
1057
+ streamingPromise
1058
+ };
1059
+ }
1060
+ } catch (parseError) {
1061
+ throw new FragnoClientUnknownApiError("Failed to parse NDJSON line", 500, { cause: parseError });
1062
+ }
1063
+ }
1064
+ }
1065
+ if (firstItem === null) {
1066
+ reader.releaseLock();
1067
+ throw new FragnoClientUnknownApiError("NDJSON stream contained no valid items", 500);
1068
+ }
1069
+ reader.releaseLock();
1070
+ throw new FragnoClientFetchError("Unexpected end of stream processing", "NO_BODY");
1071
+ } catch (error) {
1072
+ if (error instanceof FragnoClientError) {
1073
+ store?.setError(error);
1074
+ throw error;
1075
+ } else {
1076
+ const clientError = new FragnoClientUnknownApiError("Unknown streaming error", 500, { cause: error });
1077
+ store?.setError(clientError);
1078
+ throw clientError;
1079
+ }
1080
+ }
1081
+ }
1082
+ /**
1083
+ * Continues streaming the remaining items in the background
1084
+ */
1085
+ async function continueStreaming(reader, decoder, initialBuffer, items, store, abortSignal) {
1086
+ let buffer = initialBuffer;
1087
+ try {
1088
+ while (true) {
1089
+ if (abortSignal?.aborted) throw new FragnoClientFetchAbortError("Operation was aborted");
1090
+ const { done, value } = await (abortSignal ? Promise.race([reader.read(), createAbortPromise(abortSignal)]) : reader.read());
1091
+ if (done) {
1092
+ if (buffer.trim()) {
1093
+ const lines$1 = buffer.split("\n");
1094
+ for (const line of lines$1) {
1095
+ if (!line.trim()) continue;
1096
+ try {
1097
+ const jsonObject = JSON.parse(line);
1098
+ items.push(jsonObject);
1099
+ store?.setData([...items]);
1100
+ } catch (parseError) {
1101
+ throw new FragnoClientUnknownApiError("Failed to parse NDJSON line", 400, { cause: parseError });
1102
+ }
1103
+ }
1104
+ }
1105
+ break;
1106
+ }
1107
+ buffer += decoder.decode(value, { stream: true });
1108
+ const lines = buffer.split("\n");
1109
+ buffer = lines.pop() || "";
1110
+ for (const line of lines) {
1111
+ if (!line.trim()) continue;
1112
+ try {
1113
+ const jsonObject = JSON.parse(line);
1114
+ items.push(jsonObject);
1115
+ store?.setData([...items]);
1116
+ } catch (parseError) {
1117
+ throw new FragnoClientUnknownApiError("Failed to parse NDJSON line", 400, { cause: parseError });
1118
+ }
1119
+ }
1120
+ }
1121
+ } catch (error) {
1122
+ if (error instanceof FragnoClientError) store?.setError(error);
1123
+ else {
1124
+ const clientError = new FragnoClientUnknownApiError("Unknown streaming error", 400, { cause: error });
1125
+ store?.setError(clientError);
1126
+ throw clientError;
1127
+ }
1128
+ throw error;
1129
+ } finally {
1130
+ reader.releaseLock();
1131
+ }
1132
+ return items;
1133
+ }
1134
+
1050
1135
  //#endregion
1051
1136
  //#region ../../node_modules/.pnpm/nanoevents@9.1.0/node_modules/nanoevents/index.js
1052
1137
  let createNanoEvents = () => ({
@@ -1063,7 +1148,7 @@ let createNanoEvents = () => ({
1063
1148
  });
1064
1149
 
1065
1150
  //#endregion
1066
- //#region ../../node_modules/.pnpm/@nanostores+query@0.3.4_nanostores@1.1.0/node_modules/@nanostores/query/dist/nanoquery.js
1151
+ //#region ../../node_modules/.pnpm/@nanostores+query@0.3.4_nanostores@1.2.0/node_modules/@nanostores/query/dist/nanoquery.js
1067
1152
  function defaultOnErrorRetry({ retryCount }) {
1068
1153
  return ~~((Math.random() + .5) * (1 << (retryCount < 8 ? retryCount : 8))) * 2e3;
1069
1154
  }
@@ -1414,11 +1499,102 @@ const nanoquery = nanoqueryFactory(browserCompat);
1414
1499
  //#endregion
1415
1500
  //#region ../fragno/dist/client/client.js
1416
1501
  /**
1417
- * Symbols used to identify hook types
1502
+ * Symbols used to identify hook types
1503
+ */
1504
+ const GET_HOOK_SYMBOL = Symbol("fragno-get-hook");
1505
+ const MUTATOR_HOOK_SYMBOL = Symbol("fragno-mutator-hook");
1506
+ const STORE_SYMBOL = Symbol("fragno-store");
1507
+ /**
1508
+ * Check if a value contains files that should be sent as FormData.
1509
+ * @internal
1510
+ */
1511
+ function containsFiles(value) {
1512
+ if (value instanceof File || value instanceof Blob) return true;
1513
+ if (value instanceof FormData) return true;
1514
+ if (typeof value === "object" && value !== null) return Object.values(value).some((v) => v instanceof File || v instanceof Blob || v instanceof FormData);
1515
+ return false;
1516
+ }
1517
+ /**
1518
+ * Convert an object containing files to FormData.
1519
+ * Handles nested File/Blob values by appending them directly.
1520
+ * Other values are JSON-stringified.
1521
+ * @internal
1522
+ */
1523
+ function toFormData(value) {
1524
+ const formData = new FormData();
1525
+ for (const [key, val] of Object.entries(value)) if (val instanceof File) formData.append(key, val, val.name);
1526
+ else if (val instanceof Blob) formData.append(key, val);
1527
+ else if (val !== void 0 && val !== null) formData.append(key, typeof val === "string" ? val : JSON.stringify(val));
1528
+ return formData;
1529
+ }
1530
+ /**
1531
+ * Prepare request body and headers for sending.
1532
+ * Handles FormData (file uploads) vs JSON data.
1533
+ * @internal
1418
1534
  */
1419
- const GET_HOOK_SYMBOL = Symbol("fragno-get-hook");
1420
- const MUTATOR_HOOK_SYMBOL = Symbol("fragno-mutator-hook");
1421
- const STORE_SYMBOL = Symbol("fragno-store");
1535
+ function prepareRequestBody(body, contentType) {
1536
+ if (body === void 0) return { body: void 0 };
1537
+ if (contentType === "application/octet-stream") {
1538
+ if (body instanceof ReadableStream || body instanceof Blob || body instanceof File || body instanceof ArrayBuffer || body instanceof Uint8Array) return {
1539
+ body,
1540
+ headers: { "Content-Type": "application/octet-stream" }
1541
+ };
1542
+ throw new Error("Octet-stream routes only accept Blob, File, ArrayBuffer, Uint8Array, or ReadableStream bodies.");
1543
+ }
1544
+ if (body instanceof FormData) return { body };
1545
+ if (body instanceof File) {
1546
+ const formData = new FormData();
1547
+ formData.append("file", body, body.name);
1548
+ return { body: formData };
1549
+ }
1550
+ if (body instanceof Blob) {
1551
+ const formData = new FormData();
1552
+ formData.append("file", body);
1553
+ return { body: formData };
1554
+ }
1555
+ if (typeof body === "object" && body !== null && containsFiles(body)) return { body: toFormData(body) };
1556
+ return {
1557
+ body: JSON.stringify(body),
1558
+ headers: { "Content-Type": "application/json" }
1559
+ };
1560
+ }
1561
+ async function schemaAllowsUndefined(schema$1) {
1562
+ try {
1563
+ return !(await schema$1["~standard"].validate(void 0)).issues;
1564
+ } catch {
1565
+ return false;
1566
+ }
1567
+ }
1568
+ async function assertBodyProvided(body, inputSchema, errorMessage) {
1569
+ if (typeof body !== "undefined" || inputSchema === void 0) return;
1570
+ if (await schemaAllowsUndefined(inputSchema)) return;
1571
+ throw new Error(errorMessage);
1572
+ }
1573
+ /**
1574
+ * Merge request headers from multiple sources.
1575
+ * Returns undefined if there are no headers to merge.
1576
+ * @internal
1577
+ */
1578
+ function mergeRequestHeaders(...headerSources) {
1579
+ const result = {};
1580
+ let hasHeaders = false;
1581
+ for (const source of headerSources) {
1582
+ if (!source) continue;
1583
+ if (source instanceof Headers) for (const [key, value] of source.entries()) {
1584
+ result[key] = value;
1585
+ hasHeaders = true;
1586
+ }
1587
+ else if (Array.isArray(source)) for (const [key, value] of source) {
1588
+ result[key] = value;
1589
+ hasHeaders = true;
1590
+ }
1591
+ else for (const [key, value] of Object.entries(source)) {
1592
+ result[key] = value;
1593
+ hasHeaders = true;
1594
+ }
1595
+ }
1596
+ return hasHeaders ? result : void 0;
1597
+ }
1422
1598
  /**
1423
1599
  * @internal
1424
1600
  */
@@ -1504,9 +1680,13 @@ var ClientBuilder = class {
1504
1680
  get cacheEntries() {
1505
1681
  return Object.fromEntries(this.#cache.entries());
1506
1682
  }
1507
- createStore(obj) {
1683
+ createStore(input) {
1684
+ if (typeof input === "function") return {
1685
+ factory: input,
1686
+ [STORE_SYMBOL]: true
1687
+ };
1508
1688
  return {
1509
- obj,
1689
+ obj: input,
1510
1690
  [STORE_SYMBOL]: true
1511
1691
  };
1512
1692
  }
@@ -1517,7 +1697,10 @@ var ClientBuilder = class {
1517
1697
  buildUrl(path, params) {
1518
1698
  return buildUrl({
1519
1699
  baseUrl: this.#publicConfig.baseUrl ?? "",
1520
- mountRoute: getMountRoute(this.#fragmentConfig),
1700
+ mountRoute: getMountRoute({
1701
+ name: this.#fragmentConfig.name,
1702
+ mountRoute: this.#publicConfig.mountRoute
1703
+ }),
1521
1704
  path
1522
1705
  }, {
1523
1706
  pathParams: params?.path,
@@ -1536,7 +1719,7 @@ var ClientBuilder = class {
1536
1719
  }
1537
1720
  #getFetcher() {
1538
1721
  if (this.#fetcherConfig?.type === "function") return this.#fetcherConfig.fetcher;
1539
- return fetch;
1722
+ return globalThis.fetch.bind(globalThis);
1540
1723
  }
1541
1724
  #getFetcherOptions() {
1542
1725
  if (this.#fetcherConfig?.type === "options") return this.#fetcherConfig.options;
@@ -1555,7 +1738,10 @@ var ClientBuilder = class {
1555
1738
  if (route.method !== "GET") throw new Error(`Only GET routes are supported for hooks. Route '${route.path}' is a ${route.method} route.`);
1556
1739
  if (!route.outputSchema) throw new Error(`Output schema is required for GET routes. Route '${route.path}' has no output schema.`);
1557
1740
  const baseUrl = this.#publicConfig.baseUrl ?? "";
1558
- const mountRoute = getMountRoute(this.#fragmentConfig);
1741
+ const mountRoute = getMountRoute({
1742
+ name: this.#fragmentConfig.name,
1743
+ mountRoute: this.#publicConfig.mountRoute
1744
+ });
1559
1745
  const fetcher = this.#getFetcher();
1560
1746
  const fetcherOptions = this.#getFetcherOptions();
1561
1747
  async function callServerSideHandler(params) {
@@ -1663,7 +1849,10 @@ var ClientBuilder = class {
1663
1849
  #createRouteQueryMutator(route, onInvalidate = (invalidate, params) => invalidate("GET", route.path, params)) {
1664
1850
  const method = route.method;
1665
1851
  const baseUrl = this.#publicConfig.baseUrl ?? "";
1666
- const mountRoute = getMountRoute(this.#fragmentConfig);
1852
+ const mountRoute = getMountRoute({
1853
+ name: this.#fragmentConfig.name,
1854
+ mountRoute: this.#publicConfig.mountRoute
1855
+ });
1667
1856
  const fetcher = this.#getFetcher();
1668
1857
  const fetcherOptions = this.#getFetcherOptions();
1669
1858
  async function executeMutateQuery({ body, path, query }) {
@@ -1685,11 +1874,16 @@ var ClientBuilder = class {
1685
1874
  });
1686
1875
  let response;
1687
1876
  try {
1688
- response = await fetcher(url, {
1877
+ const { body: preparedBody, headers: bodyHeaders } = prepareRequestBody(body, route.contentType);
1878
+ const mergedHeaders = mergeRequestHeaders(fetcherOptions?.headers, bodyHeaders);
1879
+ const requestOptions = {
1689
1880
  ...fetcherOptions,
1690
1881
  method,
1691
- body: body !== void 0 ? JSON.stringify(body) : void 0
1692
- });
1882
+ body: preparedBody,
1883
+ ...mergedHeaders ? { headers: mergedHeaders } : {}
1884
+ };
1885
+ if (preparedBody instanceof ReadableStream) requestOptions.duplex = "half";
1886
+ response = await fetcher(url, requestOptions);
1693
1887
  } catch (error) {
1694
1888
  throw FragnoClientFetchError.fromUnknownFetchError(error);
1695
1889
  }
@@ -1699,7 +1893,7 @@ var ClientBuilder = class {
1699
1893
  const mutatorStore = this.#createMutatorStore(async ({ data }) => {
1700
1894
  if (typeof window === "undefined") {}
1701
1895
  const { body, path, query } = data;
1702
- if (typeof body === "undefined" && route.inputSchema !== void 0) throw new Error("Body is required.");
1896
+ await assertBodyProvided(body, route.inputSchema, "Body is required.");
1703
1897
  const response = await executeMutateQuery({
1704
1898
  body,
1705
1899
  path,
@@ -1738,7 +1932,7 @@ var ClientBuilder = class {
1738
1932
  } });
1739
1933
  const mutateQuery = async (data) => {
1740
1934
  const { body, path, query } = data;
1741
- if (typeof body === "undefined" && route.inputSchema !== void 0) throw new Error("Body is required for mutateQuery");
1935
+ await assertBodyProvided(body, route.inputSchema, "Body is required for mutateQuery");
1742
1936
  const response = await executeMutateQuery({
1743
1937
  body,
1744
1938
  path,
@@ -1784,7 +1978,10 @@ function createClientBuilder(definition, publicConfig, routesOrFactories, author
1784
1978
  name: definition.name,
1785
1979
  routes: routes$1
1786
1980
  };
1787
- const mountRoute = publicConfig.mountRoute ?? `/${definition.name}`;
1981
+ const mountRoute = getMountRoute({
1982
+ name: definition.name,
1983
+ mountRoute: publicConfig.mountRoute
1984
+ });
1788
1985
  const mergedFetcherConfig = mergeFetcherConfigs(authorFetcherConfig, publicConfig.fetcherConfig);
1789
1986
  return new ClientBuilder({
1790
1987
  ...publicConfig,
@@ -1794,24 +1991,19 @@ function createClientBuilder(definition, publicConfig, routesOrFactories, author
1794
1991
  }
1795
1992
 
1796
1993
  //#endregion
1797
- //#region src/definition.ts
1798
- const stripeFragmentDefinition = defineFragment("stripe").extend((x) => x).withDependencies(() => {}).providesBaseService(() => {}).build();
1994
+ //#region src/database/schema.ts
1995
+ /**
1996
+ * Database schema for subscriptions that are linked to external stripe subscriptions.
1997
+ */
1998
+ const stripeSchema = schema("stripe", (s) => {
1999
+ return s.addTable("subscription", (t) => {
2000
+ return t.addColumn("id", idColumn()).addColumn("referenceId", column("string").nullable()).addColumn("stripePriceId", column("string")).addColumn("stripeCustomerId", column("string")).addColumn("stripeSubscriptionId", column("string")).addColumn("status", column("string").defaultTo("incomplete")).addColumn("periodStart", column("timestamp").nullable()).addColumn("periodEnd", column("timestamp").nullable()).addColumn("trialStart", column("timestamp").nullable()).addColumn("trialEnd", column("timestamp").nullable()).addColumn("cancelAtPeriodEnd", column("bool").defaultTo(false)).addColumn("cancelAt", column("timestamp").nullable()).addColumn("seats", column("integer").nullable()).addColumn("createdAt", column("timestamp").defaultTo((b) => b.now())).addColumn("updatedAt", column("timestamp").defaultTo((b) => b.now())).createIndex("idx_stripe_customer_id", ["stripeCustomerId"]).createIndex("idx_stripe_subscription_id", ["stripeSubscriptionId"]).createIndex("idx_reference_id", ["referenceId"]);
2001
+ });
2002
+ });
1799
2003
 
1800
2004
  //#endregion
1801
- //#region src/routes/webhooks.ts
1802
- const webhookRoutesFactory = defineRoutes(stripeFragmentDefinition).create(({ config, deps, services, defineRoute }) => {
1803
- return [defineRoute({
1804
- method: "POST",
1805
- path: "/webhook",
1806
- outputSchema: z.object({ success: z.boolean() }),
1807
- errorCodes: [
1808
- "MISSING_SIGNATURE",
1809
- "WEBHOOK_SIGNATURE_INVALID",
1810
- "WEBHOOK_ERROR"
1811
- ],
1812
- handler: () => {}
1813
- })];
1814
- });
2005
+ //#region src/definition.ts
2006
+ const stripeFragmentDefinition = defineFragment("stripe").extend((x) => x).withDependencies(() => {}).providesBaseService(() => {}).build();
1815
2007
 
1816
2008
  //#endregion
1817
2009
  //#region src/models/customers.ts
@@ -1865,6 +2057,104 @@ const customersRoutesFactory = defineRoutes(stripeFragmentDefinition).create(({
1865
2057
  })];
1866
2058
  });
1867
2059
 
2060
+ //#endregion
2061
+ //#region src/models/prices.ts
2062
+ const PriceResponseSchema = z.object({
2063
+ id: z.string(),
2064
+ object: z.literal("price"),
2065
+ active: z.boolean(),
2066
+ billing_scheme: z.string(),
2067
+ created: z.number(),
2068
+ currency: z.string(),
2069
+ custom_unit_amount: z.any().nullable().optional(),
2070
+ deleted: z.void().optional(),
2071
+ livemode: z.boolean(),
2072
+ lookup_key: z.string().nullable().optional(),
2073
+ metadata: z.any(),
2074
+ nickname: z.string().nullable().optional(),
2075
+ product: z.union([z.string(), z.any()]),
2076
+ recurring: z.object({
2077
+ aggregate_usage: z.string().nullable().optional(),
2078
+ interval: z.enum([
2079
+ "day",
2080
+ "week",
2081
+ "month",
2082
+ "year"
2083
+ ]),
2084
+ interval_count: z.number(),
2085
+ meter: z.string().nullable().optional(),
2086
+ trial_period_days: z.number().nullable().optional(),
2087
+ usage_type: z.string()
2088
+ }).nullable().optional(),
2089
+ tax_behavior: z.string().nullable().optional(),
2090
+ tiers_mode: z.string().nullable().optional(),
2091
+ transform_quantity: z.any().nullable().optional(),
2092
+ type: z.enum(["one_time", "recurring"]),
2093
+ unit_amount: z.number().nullable().optional(),
2094
+ unit_amount_decimal: z.string().nullable().optional()
2095
+ });
2096
+
2097
+ //#endregion
2098
+ //#region src/routes/prices.ts
2099
+ const pricesRoutesFactory = defineRoutes(stripeFragmentDefinition).create(({ deps, config, defineRoute }) => {
2100
+ return [defineRoute({
2101
+ method: "GET",
2102
+ path: "/admin/products/:productId/prices",
2103
+ inputSchema: z.object({
2104
+ limit: z.number().int().positive().max(100).optional().default(50).describe("Number of prices to return (max 100)"),
2105
+ startingAfter: z.string().optional().describe("Price ID to start after for pagination")
2106
+ }),
2107
+ outputSchema: z.object({
2108
+ prices: z.array(PriceResponseSchema),
2109
+ hasMore: z.boolean().describe("Whether there are more items to fetch")
2110
+ }),
2111
+ handler: () => {}
2112
+ })];
2113
+ });
2114
+
2115
+ //#endregion
2116
+ //#region src/models/products.ts
2117
+ const ProductResponseSchema = z.object({
2118
+ id: z.string(),
2119
+ object: z.literal("product"),
2120
+ active: z.boolean(),
2121
+ created: z.number(),
2122
+ default_price: z.union([z.string(), z.any()]).nullable().optional(),
2123
+ deleted: z.void().optional(),
2124
+ description: z.string().nullable(),
2125
+ images: z.array(z.string()),
2126
+ livemode: z.boolean(),
2127
+ marketing_features: z.array(z.any()),
2128
+ metadata: z.any(),
2129
+ name: z.string(),
2130
+ package_dimensions: z.any().nullable(),
2131
+ shippable: z.boolean().nullable(),
2132
+ statement_descriptor: z.string().nullable().optional(),
2133
+ tax_code: z.union([z.string(), z.any()]).nullable(),
2134
+ type: z.string(),
2135
+ unit_label: z.string().nullable().optional(),
2136
+ updated: z.number(),
2137
+ url: z.string().nullable()
2138
+ });
2139
+
2140
+ //#endregion
2141
+ //#region src/routes/products.ts
2142
+ const productsRoutesFactory = defineRoutes(stripeFragmentDefinition).create(({ deps, config, defineRoute }) => {
2143
+ return [defineRoute({
2144
+ method: "GET",
2145
+ path: "/admin/products",
2146
+ inputSchema: z.object({
2147
+ limit: z.number().int().positive().max(100).optional().default(50).describe("Number of products to return (max 100)"),
2148
+ startingAfter: z.string().optional().describe("Product ID to start after for pagination")
2149
+ }),
2150
+ outputSchema: z.object({
2151
+ products: z.array(ProductResponseSchema),
2152
+ hasMore: z.boolean().describe("Whether there are more items to fetch")
2153
+ }),
2154
+ handler: () => {}
2155
+ })];
2156
+ });
2157
+
1868
2158
  //#endregion
1869
2159
  //#region src/models/subscriptions.ts
1870
2160
  const SubscriptionReponseSchema = z.object({
@@ -1910,6 +2200,7 @@ const subscriptionsRoutesFactory = defineRoutes(stripeFragmentDefinition).create
1910
2200
  defineRoute({
1911
2201
  method: "GET",
1912
2202
  path: "/admin/subscriptions",
2203
+ errorCodes: ["UNAUTHORIZED"],
1913
2204
  outputSchema: z.object({ subscriptions: z.array(SubscriptionReponseSchema) }),
1914
2205
  handler: () => {}
1915
2206
  }),
@@ -1959,99 +2250,17 @@ const subscriptionsRoutesFactory = defineRoutes(stripeFragmentDefinition).create
1959
2250
  });
1960
2251
 
1961
2252
  //#endregion
1962
- //#region src/models/products.ts
1963
- const ProductResponseSchema = z.object({
1964
- id: z.string(),
1965
- object: z.literal("product"),
1966
- active: z.boolean(),
1967
- created: z.number(),
1968
- default_price: z.union([z.string(), z.any()]).nullable().optional(),
1969
- deleted: z.void().optional(),
1970
- description: z.string().nullable(),
1971
- images: z.array(z.string()),
1972
- livemode: z.boolean(),
1973
- marketing_features: z.array(z.any()),
1974
- metadata: z.any(),
1975
- name: z.string(),
1976
- package_dimensions: z.any().nullable(),
1977
- shippable: z.boolean().nullable(),
1978
- statement_descriptor: z.string().nullable().optional(),
1979
- tax_code: z.union([z.string(), z.any()]).nullable(),
1980
- type: z.string(),
1981
- unit_label: z.string().nullable().optional(),
1982
- updated: z.number(),
1983
- url: z.string().nullable()
1984
- });
1985
-
1986
- //#endregion
1987
- //#region src/routes/products.ts
1988
- const productsRoutesFactory = defineRoutes(stripeFragmentDefinition).create(({ deps, config, defineRoute }) => {
1989
- return [defineRoute({
1990
- method: "GET",
1991
- path: "/admin/products",
1992
- inputSchema: z.object({
1993
- limit: z.number().int().positive().max(100).optional().default(50).describe("Number of products to return (max 100)"),
1994
- startingAfter: z.string().optional().describe("Product ID to start after for pagination")
1995
- }),
1996
- outputSchema: z.object({
1997
- products: z.array(ProductResponseSchema),
1998
- hasMore: z.boolean().describe("Whether there are more items to fetch")
1999
- }),
2000
- handler: () => {}
2001
- })];
2002
- });
2003
-
2004
- //#endregion
2005
- //#region src/models/prices.ts
2006
- const PriceResponseSchema = z.object({
2007
- id: z.string(),
2008
- object: z.literal("price"),
2009
- active: z.boolean(),
2010
- billing_scheme: z.string(),
2011
- created: z.number(),
2012
- currency: z.string(),
2013
- custom_unit_amount: z.any().nullable().optional(),
2014
- deleted: z.void().optional(),
2015
- livemode: z.boolean(),
2016
- lookup_key: z.string().nullable().optional(),
2017
- metadata: z.any(),
2018
- nickname: z.string().nullable().optional(),
2019
- product: z.union([z.string(), z.any()]),
2020
- recurring: z.object({
2021
- aggregate_usage: z.string().nullable().optional(),
2022
- interval: z.enum([
2023
- "day",
2024
- "week",
2025
- "month",
2026
- "year"
2027
- ]),
2028
- interval_count: z.number(),
2029
- meter: z.string().nullable().optional(),
2030
- trial_period_days: z.number().nullable().optional(),
2031
- usage_type: z.string()
2032
- }).nullable().optional(),
2033
- tax_behavior: z.string().nullable().optional(),
2034
- tiers_mode: z.string().nullable().optional(),
2035
- transform_quantity: z.any().nullable().optional(),
2036
- type: z.enum(["one_time", "recurring"]),
2037
- unit_amount: z.number().nullable().optional(),
2038
- unit_amount_decimal: z.string().nullable().optional()
2039
- });
2040
-
2041
- //#endregion
2042
- //#region src/routes/prices.ts
2043
- const pricesRoutesFactory = defineRoutes(stripeFragmentDefinition).create(({ deps, config, defineRoute }) => {
2253
+ //#region src/routes/webhooks.ts
2254
+ const webhookRoutesFactory = defineRoutes(stripeFragmentDefinition).create(({ config, deps, services, defineRoute }) => {
2044
2255
  return [defineRoute({
2045
- method: "GET",
2046
- path: "/admin/products/:productId/prices",
2047
- inputSchema: z.object({
2048
- limit: z.number().int().positive().max(100).optional().default(50).describe("Number of prices to return (max 100)"),
2049
- startingAfter: z.string().optional().describe("Price ID to start after for pagination")
2050
- }),
2051
- outputSchema: z.object({
2052
- prices: z.array(PriceResponseSchema),
2053
- hasMore: z.boolean().describe("Whether there are more items to fetch")
2054
- }),
2256
+ method: "POST",
2257
+ path: "/webhook",
2258
+ outputSchema: z.object({ success: z.boolean() }),
2259
+ errorCodes: [
2260
+ "MISSING_SIGNATURE",
2261
+ "WEBHOOK_SIGNATURE_INVALID",
2262
+ "WEBHOOK_ERROR"
2263
+ ],
2055
2264
  handler: () => {}
2056
2265
  })];
2057
2266
  });
@@ -2083,4 +2292,4 @@ function createStripeFragmentClients(fragnoConfig = {}) {
2083
2292
 
2084
2293
  //#endregion
2085
2294
  export { atom, createStripeFragment, createStripeFragmentClients, isGetHook, isMutatorHook, isReadableAtom, isStore, stripeFragmentDefinition };
2086
- //# sourceMappingURL=src-Cw3xL5oQ.js.map
2295
+ //# sourceMappingURL=src-D6VWnvEs.js.map