@brytdesigns/shopify-utils 1.2.21 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/effect/Actions/bridge.js +71 -0
  2. package/dist/effect/Actions/bridge.js.map +1 -0
  3. package/dist/effect/Actions/errors.js +46 -0
  4. package/dist/effect/Actions/errors.js.map +1 -0
  5. package/dist/effect/Actions/index.js +23 -0
  6. package/dist/effect/Actions/index.js.map +1 -0
  7. package/dist/effect/Actions/service.js +127 -0
  8. package/dist/effect/Actions/service.js.map +1 -0
  9. package/dist/effect/Actions/trampoline.js +26 -0
  10. package/dist/effect/Actions/trampoline.js.map +1 -0
  11. package/dist/effect/Ajax/Sections/index.js +2 -4
  12. package/dist/effect/Ajax/Sections/schema.js +28 -39
  13. package/dist/effect/Ajax/Sections/schema.js.map +1 -1
  14. package/dist/effect/Ajax/Window/data/AllowedRouteOrigins.js +75 -0
  15. package/dist/effect/Ajax/Window/data/AllowedRouteOrigins.js.map +1 -0
  16. package/dist/effect/Ajax/Window/data/ShopifyRoutes.js +86 -37
  17. package/dist/effect/Ajax/Window/data/ShopifyRoutes.js.map +1 -1
  18. package/dist/effect/Ajax/Window/data/StorefrontClientConfig.js +14 -13
  19. package/dist/effect/Ajax/Window/data/StorefrontClientConfig.js.map +1 -1
  20. package/dist/effect/Ajax/Window/data/StorefrontOrigin.js +32 -0
  21. package/dist/effect/Ajax/Window/data/StorefrontOrigin.js.map +1 -0
  22. package/dist/effect/Ajax/Window/data/index.js +15 -5
  23. package/dist/effect/Ajax/Window/data/index.js.map +1 -1
  24. package/dist/effect/Ajax/Window/index.js +2 -4
  25. package/dist/effect/Ajax/index.js +13 -3
  26. package/dist/effect/Ajax/index.js.map +1 -1
  27. package/dist/effect/Logging/index.js +13 -10
  28. package/dist/effect/Logging/index.js.map +1 -1
  29. package/dist/effect/Resource/data.js +20 -30
  30. package/dist/effect/Resource/data.js.map +1 -1
  31. package/dist/effect/Resource/index.js +14 -3
  32. package/dist/effect/Resource/index.js.map +1 -1
  33. package/dist/effect/Resource/schema.js +45 -45
  34. package/dist/effect/Resource/schema.js.map +1 -1
  35. package/dist/effect/_virtual/_rolldown/runtime.js +13 -0
  36. package/dist/effect/index.d.ts +383 -65
  37. package/dist/effect/index.js +5 -8
  38. package/dist/main/_virtual/_rolldown/runtime.js +13 -0
  39. package/dist/main/actions.js +78 -0
  40. package/dist/main/actions.js.map +1 -0
  41. package/dist/main/effect/Actions/bridge.js +65 -0
  42. package/dist/main/effect/Actions/bridge.js.map +1 -0
  43. package/dist/main/effect/Actions/errors.js +46 -0
  44. package/dist/main/effect/Actions/errors.js.map +1 -0
  45. package/dist/main/effect/Actions/index.js +4 -0
  46. package/dist/main/effect/Actions/service.js +127 -0
  47. package/dist/main/effect/Actions/service.js.map +1 -0
  48. package/dist/main/effect/Actions/trampoline.js +26 -0
  49. package/dist/main/effect/Actions/trampoline.js.map +1 -0
  50. package/dist/main/effect/Ajax/Sections/index.js +1 -4
  51. package/dist/main/effect/Ajax/Sections/schema.js +11 -41
  52. package/dist/main/effect/Ajax/Sections/schema.js.map +1 -1
  53. package/dist/main/effect/Ajax/Window/data/AllowedRouteOrigins.js +18 -0
  54. package/dist/main/effect/Ajax/Window/data/AllowedRouteOrigins.js.map +1 -0
  55. package/dist/main/effect/Ajax/Window/data/ShopifyRoutes.js +37 -37
  56. package/dist/main/effect/Ajax/Window/data/ShopifyRoutes.js.map +1 -1
  57. package/dist/main/effect/Ajax/Window/data/StorefrontClientConfig.js +3 -14
  58. package/dist/main/effect/Ajax/Window/data/StorefrontClientConfig.js.map +1 -1
  59. package/dist/main/effect/Ajax/Window/data/StorefrontOrigin.js +6 -0
  60. package/dist/main/effect/Ajax/Window/data/StorefrontOrigin.js.map +1 -0
  61. package/dist/main/effect/Ajax/Window/data/index.js +4 -6
  62. package/dist/main/effect/Ajax/Window/index.js +1 -4
  63. package/dist/main/effect/Ajax/index.js +2 -4
  64. package/dist/main/effect/Logging/index.js +2 -13
  65. package/dist/main/effect/Resource/data.js +20 -30
  66. package/dist/main/effect/Resource/data.js.map +1 -1
  67. package/dist/main/effect/Resource/index.js +3 -4
  68. package/dist/main/effect/Resource/schema.js +45 -45
  69. package/dist/main/effect/Resource/schema.js.map +1 -1
  70. package/dist/main/effect/index.js +4 -8
  71. package/dist/main/index.d.ts +351 -11
  72. package/dist/main/index.js +3 -3
  73. package/dist/main/resources/index.js +8 -7
  74. package/dist/main/resources/index.js.map +1 -1
  75. package/package.json +9 -9
  76. package/dist/effect/Ajax/Sections/index.js.map +0 -1
  77. package/dist/effect/Ajax/Window/data/types.js +0 -3
  78. package/dist/effect/Ajax/Window/data/types.js.map +0 -1
  79. package/dist/effect/Ajax/Window/index.js.map +0 -1
  80. package/dist/effect/index.js.map +0 -1
  81. package/dist/index.d.ts +0 -77
  82. package/dist/jsdelivr/effect/Ajax/Sections/index.js +0 -4
  83. package/dist/jsdelivr/effect/Ajax/Sections/index.js.map +0 -1
  84. package/dist/jsdelivr/effect/Ajax/Sections/schema.js +0 -20
  85. package/dist/jsdelivr/effect/Ajax/Sections/schema.js.map +0 -1
  86. package/dist/jsdelivr/effect/Ajax/Window/data/ShopifyRoutes.js +0 -38
  87. package/dist/jsdelivr/effect/Ajax/Window/data/ShopifyRoutes.js.map +0 -1
  88. package/dist/jsdelivr/effect/Ajax/Window/data/StorefrontClientConfig.js +0 -16
  89. package/dist/jsdelivr/effect/Ajax/Window/data/StorefrontClientConfig.js.map +0 -1
  90. package/dist/jsdelivr/effect/Ajax/Window/data/index.js +0 -6
  91. package/dist/jsdelivr/effect/Ajax/Window/data/index.js.map +0 -1
  92. package/dist/jsdelivr/effect/Ajax/Window/data/types.js +0 -3
  93. package/dist/jsdelivr/effect/Ajax/Window/data/types.js.map +0 -1
  94. package/dist/jsdelivr/effect/Ajax/Window/index.js +0 -4
  95. package/dist/jsdelivr/effect/Ajax/Window/index.js.map +0 -1
  96. package/dist/jsdelivr/effect/Ajax/index.js +0 -4
  97. package/dist/jsdelivr/effect/Ajax/index.js.map +0 -1
  98. package/dist/jsdelivr/effect/Logging/index.js +0 -13
  99. package/dist/jsdelivr/effect/Logging/index.js.map +0 -1
  100. package/dist/jsdelivr/effect/Resource/data.js +0 -17
  101. package/dist/jsdelivr/effect/Resource/data.js.map +0 -1
  102. package/dist/jsdelivr/effect/Resource/index.js +0 -4
  103. package/dist/jsdelivr/effect/Resource/index.js.map +0 -1
  104. package/dist/jsdelivr/effect/Resource/schema.js +0 -47
  105. package/dist/jsdelivr/effect/Resource/schema.js.map +0 -1
  106. package/dist/jsdelivr/effect/index.js +0 -8
  107. package/dist/jsdelivr/effect/index.js.map +0 -1
  108. package/dist/jsdelivr/index.js +0 -3
  109. package/dist/jsdelivr/index.js.map +0 -1
  110. package/dist/jsdelivr/resources/index.js +0 -9
  111. package/dist/jsdelivr/resources/index.js.map +0 -1
  112. package/dist/main/effect/Ajax/Sections/index.js.map +0 -1
  113. package/dist/main/effect/Ajax/Window/data/index.js.map +0 -1
  114. package/dist/main/effect/Ajax/Window/data/types.js +0 -3
  115. package/dist/main/effect/Ajax/Window/data/types.js.map +0 -1
  116. package/dist/main/effect/Ajax/Window/index.js.map +0 -1
  117. package/dist/main/effect/Ajax/index.js.map +0 -1
  118. package/dist/main/effect/Logging/index.js.map +0 -1
  119. package/dist/main/effect/Resource/index.js.map +0 -1
  120. package/dist/main/effect/index.js.map +0 -1
  121. package/dist/main/index.js.map +0 -1
@@ -0,0 +1,65 @@
1
+ import * as Cause from "effect/Cause";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Exit from "effect/Exit";
4
+ //#region src/effect/Actions/bridge.ts
5
+ /**
6
+ * Discharges Effects across a callback boundary owned by foreign code.
7
+ *
8
+ * Shopify's `configure()` stores plain callbacks and invokes them later, at
9
+ * which point there is no fiber and no services. A `Context` captured while the
10
+ * caller's services were still in scope is the only thing that can carry them
11
+ * across that gap, so every helper here takes one.
12
+ *
13
+ * These helpers are deliberately free of Shopify specifics — they apply to any
14
+ * register-a-callback API.
15
+ *
16
+ * Note that a captured `Context` freezes references as well as services, so the
17
+ * log level, spans, and annotations in force at capture time are the ones the
18
+ * callbacks run under — changing them afterwards does not reach back into an
19
+ * already-registered callback.
20
+ */
21
+ /**
22
+ * Turn an Effect-returning function into the Promise-returning callback a
23
+ * foreign API expects.
24
+ *
25
+ * A failed Effect becomes a rejected Promise, which is the correct signal for
26
+ * an action handler: Shopify treats rejection as handler failure.
27
+ */
28
+ var toAsyncCallback = (services, f) => (...args) => Effect.runPromiseWith(services)(f(...args));
29
+ /**
30
+ * Turn an Effect-returning function into a *synchronous* callback.
31
+ *
32
+ * `Effect<A, E, R>` does not track sync-vs-async at the type level, so nothing
33
+ * stops a caller handing us an Effect that suspends. When that happens there is
34
+ * no value to return and no error channel to use — the foreign API demands a
35
+ * value right now. `onAbnormal` supplies it.
36
+ *
37
+ * Both failure shapes are covered: `runSyncExit` reports an async effect as a
38
+ * failed `Exit` in some paths and throws in others depending on where the
39
+ * suspension occurs, so we handle the thrown case too rather than letting a
40
+ * defect escape into third-party code.
41
+ */
42
+ var toSyncCallback = (services, f, onAbnormal) => (...args) => {
43
+ let exit;
44
+ try {
45
+ exit = Effect.runSyncExitWith(services)(f(...args));
46
+ } catch (defect) {
47
+ return onAbnormal(Cause.die(defect));
48
+ }
49
+ return Exit.isSuccess(exit) ? exit.value : onAbnormal(exit.cause);
50
+ };
51
+ /**
52
+ * Lift a foreign Promise thunk back into Effect-land.
53
+ *
54
+ * This is the opposite crossing from {@link toAsyncCallback}: Shopify hands our
55
+ * handler a `defaultHandler(): Promise<A>`, and the caller's handler is written
56
+ * in Effect, so the thunk has to become an Effect before it can be composed.
57
+ */
58
+ var fromPromiseThunk = (thunk, onReject) => Effect.tryPromise({
59
+ try: () => thunk(),
60
+ catch: onReject
61
+ });
62
+ //#endregion
63
+ export { fromPromiseThunk, toAsyncCallback, toSyncCallback };
64
+
65
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","names":[],"sources":["../../../../src/effect/Actions/bridge.ts"],"sourcesContent":["import * as Cause from \"effect/Cause\";\nimport type * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport * as Exit from \"effect/Exit\";\n\n/**\n * Discharges Effects across a callback boundary owned by foreign code.\n *\n * Shopify's `configure()` stores plain callbacks and invokes them later, at\n * which point there is no fiber and no services. A `Context` captured while the\n * caller's services were still in scope is the only thing that can carry them\n * across that gap, so every helper here takes one.\n *\n * These helpers are deliberately free of Shopify specifics — they apply to any\n * register-a-callback API.\n *\n * Note that a captured `Context` freezes references as well as services, so the\n * log level, spans, and annotations in force at capture time are the ones the\n * callbacks run under — changing them afterwards does not reach back into an\n * already-registered callback.\n */\n\n/**\n * Turn an Effect-returning function into the Promise-returning callback a\n * foreign API expects.\n *\n * A failed Effect becomes a rejected Promise, which is the correct signal for\n * an action handler: Shopify treats rejection as handler failure.\n */\nexport const toAsyncCallback =\n\t<Args extends ReadonlyArray<unknown>, A, E, R>(\n\t\tservices: Context.Context<R>,\n\t\tf: (...args: Args) => Effect.Effect<A, E, R>,\n\t) =>\n\t(...args: Args): Promise<A> =>\n\t\tEffect.runPromiseWith(services)(f(...args));\n\n/**\n * Turn an Effect-returning function into a *synchronous* callback.\n *\n * `Effect<A, E, R>` does not track sync-vs-async at the type level, so nothing\n * stops a caller handing us an Effect that suspends. When that happens there is\n * no value to return and no error channel to use — the foreign API demands a\n * value right now. `onAbnormal` supplies it.\n *\n * Both failure shapes are covered: `runSyncExit` reports an async effect as a\n * failed `Exit` in some paths and throws in others depending on where the\n * suspension occurs, so we handle the thrown case too rather than letting a\n * defect escape into third-party code.\n */\nexport const toSyncCallback =\n\t<Args extends ReadonlyArray<unknown>, A, R>(\n\t\tservices: Context.Context<R>,\n\t\tf: (...args: Args) => Effect.Effect<A, never, R>,\n\t\tonAbnormal: (cause: Cause.Cause<never>) => A,\n\t) =>\n\t(...args: Args): A => {\n\t\tlet exit: Exit.Exit<A, never>;\n\n\t\ttry {\n\t\t\texit = Effect.runSyncExitWith(services)(f(...args));\n\t\t} catch (defect) {\n\t\t\treturn onAbnormal(Cause.die(defect));\n\t\t}\n\n\t\treturn Exit.isSuccess(exit) ? exit.value : onAbnormal(exit.cause);\n\t};\n\n/**\n * Lift a foreign Promise thunk back into Effect-land.\n *\n * This is the opposite crossing from {@link toAsyncCallback}: Shopify hands our\n * handler a `defaultHandler(): Promise<A>`, and the caller's handler is written\n * in Effect, so the thunk has to become an Effect before it can be composed.\n */\nexport const fromPromiseThunk = <A, E>(\n\tthunk: () => Promise<A>,\n\tonReject: (cause: unknown) => E,\n): Effect.Effect<A, E> =>\n\tEffect.tryPromise({ try: () => thunk(), catch: onReject });\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,IAAa,mBAEX,UACA,OAEA,GAAG,SACH,OAAO,eAAe,QAAQ,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC;;;;;;;;;;;;;;AAe5C,IAAa,kBAEX,UACA,GACA,gBAEA,GAAG,SAAkB;CACrB,IAAI;CAEJ,IAAI;EACH,OAAO,OAAO,gBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC;CACnD,SAAS,QAAQ;EAChB,OAAO,WAAW,MAAM,IAAI,MAAM,CAAC;CACpC;CAEA,OAAO,KAAK,UAAU,IAAI,IAAI,KAAK,QAAQ,WAAW,KAAK,KAAK;AACjE;;;;;;;;AASD,IAAa,oBACZ,OACA,aAEA,OAAO,WAAW;CAAE,WAAW,MAAM;CAAG,OAAO;AAAS,CAAC"}
@@ -0,0 +1,46 @@
1
+ import * as Data from "effect/Data";
2
+ //#region src/effect/Actions/errors.ts
3
+ /**
4
+ * The storefront actions runtime was not present on `window`.
5
+ *
6
+ * Shopify injects it on Liquid storefronts only, so this is the expected
7
+ * failure in tests, in checkout extensions, and during SSR — not a defect.
8
+ */
9
+ var ActionsUnavailableError = class extends Data.TaggedError("@brytdesigns/shopify-utils/ActionsUnavailableError") {
10
+ constructor(options) {
11
+ super({
12
+ action: options.action,
13
+ message: `window.Shopify.actions.${options.action} is unavailable. The storefront standard actions runtime is only injected on Liquid storefronts.`
14
+ });
15
+ }
16
+ };
17
+ /**
18
+ * `configure()` returned `false`, meaning the action refused the registration
19
+ * (most commonly because something else already claimed it).
20
+ *
21
+ * Surfacing this as a typed failure is deliberate: a silently discarded
22
+ * `false` leaves the caller believing their handler is installed when it is
23
+ * not.
24
+ */
25
+ var ActionConfigurationRejectedError = class extends Data.TaggedError("@brytdesigns/shopify-utils/ActionConfigurationRejectedError") {
26
+ constructor(options) {
27
+ super({
28
+ action: options.action,
29
+ message: `window.Shopify.actions.${options.action}.configure() returned false. The action already has a configuration installed.`
30
+ });
31
+ }
32
+ };
33
+ /** The underlying action promise rejected. */
34
+ var ActionInvocationError = class extends Data.TaggedError("@brytdesigns/shopify-utils/ActionInvocationError") {
35
+ constructor(options) {
36
+ super({
37
+ action: options.action,
38
+ cause: options.cause,
39
+ message: `window.Shopify.actions.${options.action} rejected.`
40
+ });
41
+ }
42
+ };
43
+ //#endregion
44
+ export { ActionConfigurationRejectedError, ActionInvocationError, ActionsUnavailableError };
45
+
46
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../../../../src/effect/Actions/errors.ts"],"sourcesContent":["import * as Data from \"effect/Data\";\n\n/** Every action exposed on `window.Shopify.actions`. */\nexport type ActionName = \"getCart\" | \"updateCart\" | \"openCart\";\n\n/**\n * Only `updateCart` and `openCart` are `ActionFunction`s — `getCart` is a bare\n * callable with no `configure()` / `isDefault()`.\n */\nexport type ConfigurableActionName = Exclude<ActionName, \"getCart\">;\n\n/**\n * The storefront actions runtime was not present on `window`.\n *\n * Shopify injects it on Liquid storefronts only, so this is the expected\n * failure in tests, in checkout extensions, and during SSR — not a defect.\n */\nexport class ActionsUnavailableError extends Data.TaggedError(\n\t\"@brytdesigns/shopify-utils/ActionsUnavailableError\",\n)<{\n\treadonly action: ActionName;\n\treadonly message: string;\n}> {\n\tconstructor(options: { readonly action: ActionName }) {\n\t\tsuper({\n\t\t\taction: options.action,\n\t\t\tmessage: `window.Shopify.actions.${options.action} is unavailable. The storefront standard actions runtime is only injected on Liquid storefronts.`,\n\t\t});\n\t}\n}\n\n/**\n * `configure()` returned `false`, meaning the action refused the registration\n * (most commonly because something else already claimed it).\n *\n * Surfacing this as a typed failure is deliberate: a silently discarded\n * `false` leaves the caller believing their handler is installed when it is\n * not.\n */\nexport class ActionConfigurationRejectedError extends Data.TaggedError(\n\t\"@brytdesigns/shopify-utils/ActionConfigurationRejectedError\",\n)<{\n\treadonly action: ConfigurableActionName;\n\treadonly message: string;\n}> {\n\tconstructor(options: { readonly action: ConfigurableActionName }) {\n\t\tsuper({\n\t\t\taction: options.action,\n\t\t\tmessage: `window.Shopify.actions.${options.action}.configure() returned false. The action already has a configuration installed.`,\n\t\t});\n\t}\n}\n\n/** The underlying action promise rejected. */\nexport class ActionInvocationError extends Data.TaggedError(\n\t\"@brytdesigns/shopify-utils/ActionInvocationError\",\n)<{\n\treadonly action: ActionName;\n\treadonly cause: unknown;\n\treadonly message: string;\n}> {\n\tconstructor(options: {\n\t\treadonly action: ActionName;\n\t\treadonly cause: unknown;\n\t}) {\n\t\tsuper({\n\t\t\taction: options.action,\n\t\t\tcause: options.cause,\n\t\t\tmessage: `window.Shopify.actions.${options.action} rejected.`,\n\t\t});\n\t}\n}\n"],"mappings":";;;;;;;;AAiBA,IAAa,0BAAb,cAA6C,KAAK,YACjD,oDACD,CAAC,CAGE;CACF,YAAY,SAA0C;EACrD,MAAM;GACL,QAAQ,QAAQ;GAChB,SAAS,0BAA0B,QAAQ,OAAO;EACnD,CAAC;CACF;AACD;;;;;;;;;AAUA,IAAa,mCAAb,cAAsD,KAAK,YAC1D,6DACD,CAAC,CAGE;CACF,YAAY,SAAsD;EACjE,MAAM;GACL,QAAQ,QAAQ;GAChB,SAAS,0BAA0B,QAAQ,OAAO;EACnD,CAAC;CACF;AACD;;AAGA,IAAa,wBAAb,cAA2C,KAAK,YAC/C,kDACD,CAAC,CAIE;CACF,YAAY,SAGT;EACF,MAAM;GACL,QAAQ,QAAQ;GAChB,OAAO,QAAQ;GACf,SAAS,0BAA0B,QAAQ,OAAO;EACnD,CAAC;CACF;AACD"}
@@ -0,0 +1,4 @@
1
+ import "./bridge.js";
2
+ import { ActionConfigurationRejectedError, ActionInvocationError, ActionsUnavailableError } from "./errors.js";
3
+ import { ShopifyStorefrontActions, configureOpenCart, configureUpdateCart, getCart, isDefault, layer, openCart, updateCart } from "./service.js";
4
+ export { ActionConfigurationRejectedError, ActionInvocationError, ActionsUnavailableError, ShopifyStorefrontActions, configureOpenCart, configureUpdateCart, getCart, isDefault, layer, openCart, updateCart };
@@ -0,0 +1,127 @@
1
+ import { fromPromiseThunk, toAsyncCallback, toSyncCallback } from "./bridge.js";
2
+ import { ActionConfigurationRejectedError, ActionInvocationError, ActionsUnavailableError } from "./errors.js";
3
+ import { makeRegistry } from "./trampoline.js";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Context from "effect/Context";
6
+ import * as Layer from "effect/Layer";
7
+ //#region src/effect/Actions/service.ts
8
+ var updateCartRegistry = makeRegistry();
9
+ var openCartRegistry = makeRegistry();
10
+ var ShopifyStorefrontActions = class extends Context.Service()("@brytdesigns/shopify-utils/ShopifyStorefrontActions") {};
11
+ /**
12
+ * Resolve one action off `window` at call time.
13
+ *
14
+ * Deliberately not hoisted to layer construction: Shopify injects the runtime
15
+ * during page load, so a snapshot taken when the layer is built can be stale.
16
+ */
17
+ var requireAction = (action) => Effect.suspend(() => {
18
+ const target = globalThis.window?.Shopify?.actions?.[action];
19
+ return target === void 0 ? Effect.fail(new ActionsUnavailableError({ action })) : Effect.succeed(target);
20
+ });
21
+ /**
22
+ * Bind `bridged` for the lifetime of the enclosing scope.
23
+ *
24
+ * The finalizer only clears the cell if it still holds *our* configuration. A
25
+ * later `configure` supersedes an earlier one, and when the earlier one's scope
26
+ * closes it must not clobber the newer binding.
27
+ */
28
+ var bindForScope = (registration, bridged) => Effect.acquireRelease(Effect.sync(() => {
29
+ registration.active = bridged;
30
+ }), () => Effect.sync(() => {
31
+ if (registration.active === bridged) registration.active = null;
32
+ }));
33
+ var make = () => ({
34
+ getCart: (payload, options) => Effect.flatMap(requireAction("getCart"), (getCart) => Effect.tryPromise({
35
+ try: (signal) => getCart(payload, {
36
+ ...options,
37
+ signal: options?.signal ?? signal
38
+ }),
39
+ catch: (cause) => new ActionInvocationError({
40
+ action: "getCart",
41
+ cause
42
+ })
43
+ })),
44
+ updateCart: (payload, options) => Effect.flatMap(requireAction("updateCart"), (updateCart) => Effect.tryPromise({
45
+ try: (signal) => updateCart(payload, {
46
+ ...options,
47
+ signal: options?.signal ?? signal
48
+ }),
49
+ catch: (cause) => new ActionInvocationError({
50
+ action: "updateCart",
51
+ cause
52
+ })
53
+ })),
54
+ openCart: Effect.flatMap(requireAction("openCart"), (openCart) => Effect.tryPromise({
55
+ try: () => openCart(),
56
+ catch: (cause) => new ActionInvocationError({
57
+ action: "openCart",
58
+ cause
59
+ })
60
+ })),
61
+ configureUpdateCart: (config) => Effect.gen(function* () {
62
+ const action = yield* requireAction("updateCart");
63
+ const services = yield* Effect.context();
64
+ const registration = updateCartRegistry(action);
65
+ if (!registration.claimed) {
66
+ if (!action.configure({
67
+ eventTarget: (meta) => registration.active?.eventTarget(meta) ?? null,
68
+ handler: (defaultHandler, ...args) => {
69
+ const handler = registration.active?.handler;
70
+ return handler ? handler(defaultHandler, ...args) : defaultHandler();
71
+ }
72
+ })) return yield* new ActionConfigurationRejectedError({ action: "updateCart" });
73
+ registration.claimed = true;
74
+ }
75
+ const handler = config.handler;
76
+ yield* bindForScope(registration, {
77
+ eventTarget: toSyncCallback(services, config.eventTarget, (cause) => {
78
+ Effect.runSyncWith(services)(Effect.logError("updateCart eventTarget did not resolve synchronously; dispatching on document instead", cause));
79
+ return null;
80
+ }),
81
+ handler: handler === void 0 ? null : toAsyncCallback(services, (defaultHandler, ...args) => handler(fromPromiseThunk(defaultHandler, (cause) => new ActionInvocationError({
82
+ action: "updateCart",
83
+ cause
84
+ })), ...args))
85
+ });
86
+ }),
87
+ configureOpenCart: (config) => Effect.gen(function* () {
88
+ const action = yield* requireAction("openCart");
89
+ const services = yield* Effect.context();
90
+ const registration = openCartRegistry(action);
91
+ if (!registration.claimed) {
92
+ if (!action.configure({ handler: (defaultHandler, ...args) => {
93
+ const handler = registration.active?.handler;
94
+ return handler ? handler(defaultHandler, ...args) : defaultHandler();
95
+ } })) return yield* new ActionConfigurationRejectedError({ action: "openCart" });
96
+ registration.claimed = true;
97
+ }
98
+ const handler = config.handler;
99
+ yield* bindForScope(registration, { handler: toAsyncCallback(services, (defaultHandler, ...args) => handler(fromPromiseThunk(defaultHandler, (cause) => new ActionInvocationError({
100
+ action: "openCart",
101
+ cause
102
+ })), ...args)) });
103
+ }),
104
+ isDefault: (action) => Effect.map(requireAction(action), (target) => target.isDefault())
105
+ });
106
+ /**
107
+ * Live layer, backed by `window.Shopify.actions`.
108
+ *
109
+ * Named `layer` per the v4 convention, which replaces v3's `Default`/`Live`.
110
+ */
111
+ var layer = Layer.succeed(ShopifyStorefrontActions, make());
112
+ /**
113
+ * Accessors so consumers can compose without pulling the service out by hand.
114
+ *
115
+ * `yield* Actions.configureUpdateCart({ ... })` reads better than
116
+ * `yield* (yield* Actions.ShopifyStorefrontActions).configureUpdateCart({ ... })`.
117
+ */
118
+ var getCart = (payload, options) => Effect.flatMap(ShopifyStorefrontActions, (actions) => actions.getCart(payload, options));
119
+ var updateCart = (payload, options) => Effect.flatMap(ShopifyStorefrontActions, (actions) => actions.updateCart(payload, options));
120
+ var openCart = Effect.flatMap(ShopifyStorefrontActions, (actions) => actions.openCart);
121
+ var configureUpdateCart = (config) => Effect.flatMap(ShopifyStorefrontActions, (actions) => actions.configureUpdateCart(config));
122
+ var configureOpenCart = (config) => Effect.flatMap(ShopifyStorefrontActions, (actions) => actions.configureOpenCart(config));
123
+ var isDefault = (action) => Effect.flatMap(ShopifyStorefrontActions, (actions) => actions.isDefault(action));
124
+ //#endregion
125
+ export { ShopifyStorefrontActions, configureOpenCart, configureUpdateCart, getCart, isDefault, layer, openCart, updateCart };
126
+
127
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","names":[],"sources":["../../../../src/effect/Actions/service.ts"],"sourcesContent":["import type {\n\tActionArgs,\n\tDefaultActionHandler,\n\tGetCartOptions,\n\tGetCartPayload,\n\tGetCartResult,\n\tUpdateCartEventTargetMeta,\n\tUpdateCartOptions,\n\tUpdateCartPayload,\n\tUpdateCartResult,\n} from \"@theme/standard-actions\";\n\nimport * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport * as Layer from \"effect/Layer\";\nimport type * as Scope from \"effect/Scope\";\n\nimport { fromPromiseThunk, toAsyncCallback, toSyncCallback } from \"./bridge.js\";\nimport {\n\tActionConfigurationRejectedError,\n\tActionInvocationError,\n\ttype ActionName,\n\tActionsUnavailableError,\n\ttype ConfigurableActionName,\n} from \"./errors.js\";\nimport { makeRegistry } from \"./trampoline.js\";\n\n/** The runtime object Shopify injects, as described by the vendored types. */\ntype StandardActions = NonNullable<Window[\"Shopify\"][\"actions\"]>;\n\n/**\n * The default handler, lifted into Effect-land.\n *\n * Handlers receive this rather than a pre-computed result so they keep the full\n * range of the upstream contract: run the default, wrap it, retry it, race it,\n * or skip it entirely and synthesise a result.\n */\nexport type DefaultHandler<A> = Effect.Effect<A, ActionInvocationError>;\n\n/**\n * An action handler written in Effect.\n *\n * The returned Effect's success value is what the action resolves with, and its\n * failure becomes a rejected promise for Shopify.\n *\n * Note that `E` is checked but never surfaces to the fiber that called\n * `configure` — the handler runs long afterwards, when a shopper triggers the\n * action, so its failure has nowhere to go but the promise Shopify awaits. If\n * you want handler failures observed, observe them inside the handler.\n */\nexport type Handler<P, A, O, E, R> = (\n\tdefaultHandler: DefaultHandler<A>,\n\t...args: ActionArgs<P, O>\n) => Effect.Effect<A, E, R>;\n\n/**\n * Resolves the element that `shopify:cart:*` events are dispatched from.\n *\n * The error channel is `never` on purpose. Shopify calls this synchronously and\n * uses the return value immediately, so there is nowhere for a failure to go —\n * handle errors inside the Effect and fall back to an element or `null`.\n *\n * For the same reason this Effect must complete synchronously. Anything that\n * suspends (`Effect.sleep`, an HTTP call, an async service) cannot produce a\n * value in time and is reported as `null` with a logged defect, which makes\n * Shopify dispatch on `document`. If you need async work, resolve it ahead of\n * time into a `Ref` and read the `Ref` here.\n */\nexport type EventTargetResolver<R> = (\n\tmeta: UpdateCartEventTargetMeta,\n) => Effect.Effect<EventTarget | null, never, R>;\n\nexport interface UpdateCartConfiguration<E, R> {\n\treadonly eventTarget: EventTargetResolver<R>;\n\t/** Optional: routing events without changing cart behaviour is valid. */\n\treadonly handler?: Handler<\n\t\tUpdateCartPayload,\n\t\tUpdateCartResult,\n\t\tUpdateCartOptions,\n\t\tE,\n\t\tR\n\t>;\n}\n\nexport interface OpenCartConfiguration<E, R> {\n\t/**\n\t * Required, unlike `updateCart`'s. `openCart` has no event target, so a\n\t * configuration without a handler would claim the one-shot upstream slot\n\t * while contributing nothing — locking everyone else out for the life of\n\t * the page in exchange for default behaviour.\n\t */\n\treadonly handler: Handler<void, void, void, E, R>;\n}\n\n/** The bridged (plain-callback) shape our trampoline reads. */\ninterface BridgedUpdateCart {\n\treadonly eventTarget: (meta: UpdateCartEventTargetMeta) => EventTarget | null;\n\treadonly handler:\n\t\t| ((\n\t\t\t\tdefaultHandler: DefaultActionHandler<UpdateCartResult>,\n\t\t\t\t...args: ActionArgs<UpdateCartPayload, UpdateCartOptions>\n\t\t ) => Promise<UpdateCartResult>)\n\t\t| null;\n}\n\ninterface BridgedOpenCart {\n\treadonly handler: (\n\t\tdefaultHandler: DefaultActionHandler<void>,\n\t\t...args: ActionArgs<void, void>\n\t) => Promise<void>;\n}\n\nconst updateCartRegistry = makeRegistry<BridgedUpdateCart>();\nconst openCartRegistry = makeRegistry<BridgedOpenCart>();\n\nexport interface ShopifyStorefrontActionsImpl {\n\t/**\n\t * Read the current cart. `getCart` is a bare callable upstream — it has no\n\t * `configure()`, so there is no `configureGetCart`.\n\t */\n\treadonly getCart: (\n\t\tpayload?: GetCartPayload,\n\t\toptions?: GetCartOptions,\n\t) => Effect.Effect<\n\t\tGetCartResult,\n\t\tActionsUnavailableError | ActionInvocationError\n\t>;\n\n\treadonly updateCart: (\n\t\tpayload: UpdateCartPayload,\n\t\toptions?: UpdateCartOptions,\n\t) => Effect.Effect<\n\t\tUpdateCartResult,\n\t\tActionsUnavailableError | ActionInvocationError\n\t>;\n\n\treadonly openCart: Effect.Effect<\n\t\tvoid,\n\t\tActionsUnavailableError | ActionInvocationError\n\t>;\n\n\t/**\n\t * Install an Effect-based `updateCart` configuration for the lifetime of the\n\t * enclosing `Scope`.\n\t *\n\t * The `R` of the supplied Effects flows out into the returned Effect's\n\t * requirements, so the services your handler needs stay explicit and must be\n\t * provided before this runs — they are captured, not smuggled.\n\t *\n\t * Calling this more than once rebinds rather than failing: we claim the\n\t * upstream slot once and swap our own configuration behind it. A\n\t * `ActionConfigurationRejectedError` therefore means one specific thing —\n\t * something outside this library claimed the action first.\n\t *\n\t * For page-lifetime registration, build it into a layer with\n\t * `Layer.effectDiscard(Actions.configureUpdateCart({ ... }))` so the\n\t * registration lives exactly as long as the runtime does.\n\t */\n\treadonly configureUpdateCart: <E, R>(\n\t\tconfig: UpdateCartConfiguration<E, R>,\n\t) => Effect.Effect<\n\t\tvoid,\n\t\tActionsUnavailableError | ActionConfigurationRejectedError,\n\t\tR | Scope.Scope\n\t>;\n\n\treadonly configureOpenCart: <E, R>(\n\t\tconfig: OpenCartConfiguration<E, R>,\n\t) => Effect.Effect<\n\t\tvoid,\n\t\tActionsUnavailableError | ActionConfigurationRejectedError,\n\t\tR | Scope.Scope\n\t>;\n\n\t/**\n\t * Upstream's own notion: `true` until the action's slot is claimed.\n\t *\n\t * Because the slot can never be released, this stays `false` once we have\n\t * claimed it, even after every configuration has been deactivated.\n\t */\n\treadonly isDefault: (\n\t\taction: ConfigurableActionName,\n\t) => Effect.Effect<boolean, ActionsUnavailableError>;\n}\n\nexport class ShopifyStorefrontActions extends Context.Service<\n\tShopifyStorefrontActions,\n\tShopifyStorefrontActionsImpl\n>()(\"@brytdesigns/shopify-utils/ShopifyStorefrontActions\") {}\n\n/**\n * Resolve one action off `window` at call time.\n *\n * Deliberately not hoisted to layer construction: Shopify injects the runtime\n * during page load, so a snapshot taken when the layer is built can be stale.\n */\nconst requireAction = <K extends ActionName>(\n\taction: K,\n): Effect.Effect<StandardActions[K], ActionsUnavailableError> =>\n\tEffect.suspend(() => {\n\t\tconst target = globalThis.window?.Shopify?.actions?.[action];\n\n\t\treturn target === undefined\n\t\t\t? Effect.fail(new ActionsUnavailableError({ action }))\n\t\t\t: Effect.succeed(target);\n\t});\n\n/**\n * Bind `bridged` for the lifetime of the enclosing scope.\n *\n * The finalizer only clears the cell if it still holds *our* configuration. A\n * later `configure` supersedes an earlier one, and when the earlier one's scope\n * closes it must not clobber the newer binding.\n */\nconst bindForScope = <Config>(\n\tregistration: { active: Config | null },\n\tbridged: Config,\n) =>\n\tEffect.acquireRelease(\n\t\tEffect.sync(() => {\n\t\t\tregistration.active = bridged;\n\t\t}),\n\t\t() =>\n\t\t\tEffect.sync(() => {\n\t\t\t\tif (registration.active === bridged) registration.active = null;\n\t\t\t}),\n\t);\n\nconst make = (): ShopifyStorefrontActionsImpl => ({\n\tgetCart: (payload, options) =>\n\t\tEffect.flatMap(requireAction(\"getCart\"), (getCart) =>\n\t\t\tEffect.tryPromise({\n\t\t\t\t// Effect's interruption signal is forwarded when the caller has not\n\t\t\t\t// supplied one, so an interrupted fiber aborts the in-flight request.\n\t\t\t\ttry: (signal) =>\n\t\t\t\t\tgetCart(payload, { ...options, signal: options?.signal ?? signal }),\n\t\t\t\tcatch: (cause) =>\n\t\t\t\t\tnew ActionInvocationError({ action: \"getCart\", cause }),\n\t\t\t}),\n\t\t),\n\n\tupdateCart: (payload, options) =>\n\t\tEffect.flatMap(requireAction(\"updateCart\"), (updateCart) =>\n\t\t\tEffect.tryPromise({\n\t\t\t\ttry: (signal) =>\n\t\t\t\t\tupdateCart(payload, {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\tsignal: options?.signal ?? signal,\n\t\t\t\t\t}),\n\t\t\t\tcatch: (cause) =>\n\t\t\t\t\tnew ActionInvocationError({ action: \"updateCart\", cause }),\n\t\t\t}),\n\t\t),\n\n\topenCart: Effect.flatMap(requireAction(\"openCart\"), (openCart) =>\n\t\tEffect.tryPromise({\n\t\t\ttry: () => openCart(),\n\t\t\tcatch: (cause) =>\n\t\t\t\tnew ActionInvocationError({ action: \"openCart\", cause }),\n\t\t}),\n\t),\n\n\tconfigureUpdateCart: <E, R>(config: UpdateCartConfiguration<E, R>) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst action = yield* requireAction(\"updateCart\");\n\t\t\tconst services = yield* Effect.context<R>();\n\t\t\tconst registration = updateCartRegistry(action);\n\n\t\t\tif (!registration.claimed) {\n\t\t\t\t// One stable trampoline, installed once. Everything after this\n\t\t\t\t// rebinds `registration.active` instead of calling configure again.\n\t\t\t\tconst accepted = action.configure({\n\t\t\t\t\teventTarget: (meta) => registration.active?.eventTarget(meta) ?? null,\n\t\t\t\t\thandler: (defaultHandler, ...args) => {\n\t\t\t\t\t\tconst handler = registration.active?.handler;\n\n\t\t\t\t\t\treturn handler\n\t\t\t\t\t\t\t? handler(defaultHandler, ...args)\n\t\t\t\t\t\t\t: defaultHandler();\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tif (!accepted) {\n\t\t\t\t\treturn yield* new ActionConfigurationRejectedError({\n\t\t\t\t\t\taction: \"updateCart\",\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tregistration.claimed = true;\n\t\t\t}\n\n\t\t\tconst handler = config.handler;\n\n\t\t\tconst bridged: BridgedUpdateCart = {\n\t\t\t\teventTarget: toSyncCallback(services, config.eventTarget, (cause) => {\n\t\t\t\t\tEffect.runSyncWith(services)(\n\t\t\t\t\t\tEffect.logError(\n\t\t\t\t\t\t\t\"updateCart eventTarget did not resolve synchronously; dispatching on document instead\",\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\treturn null;\n\t\t\t\t}),\n\t\t\t\thandler:\n\t\t\t\t\thandler === undefined\n\t\t\t\t\t\t? null\n\t\t\t\t\t\t: toAsyncCallback(\n\t\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\tdefaultHandler: DefaultActionHandler<UpdateCartResult>,\n\t\t\t\t\t\t\t\t\t...args: ActionArgs<UpdateCartPayload, UpdateCartOptions>\n\t\t\t\t\t\t\t\t) =>\n\t\t\t\t\t\t\t\t\thandler(\n\t\t\t\t\t\t\t\t\t\tfromPromiseThunk(\n\t\t\t\t\t\t\t\t\t\t\tdefaultHandler,\n\t\t\t\t\t\t\t\t\t\t\t(cause) =>\n\t\t\t\t\t\t\t\t\t\t\t\tnew ActionInvocationError({\n\t\t\t\t\t\t\t\t\t\t\t\t\taction: \"updateCart\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t...args,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t),\n\t\t\t};\n\n\t\t\tyield* bindForScope(registration, bridged);\n\t\t}),\n\n\tconfigureOpenCart: <E, R>(config: OpenCartConfiguration<E, R>) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst action = yield* requireAction(\"openCart\");\n\t\t\tconst services = yield* Effect.context<R>();\n\t\t\tconst registration = openCartRegistry(action);\n\n\t\t\tif (!registration.claimed) {\n\t\t\t\tconst accepted = action.configure({\n\t\t\t\t\thandler: (defaultHandler, ...args) => {\n\t\t\t\t\t\tconst handler = registration.active?.handler;\n\n\t\t\t\t\t\treturn handler\n\t\t\t\t\t\t\t? handler(defaultHandler, ...args)\n\t\t\t\t\t\t\t: defaultHandler();\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tif (!accepted) {\n\t\t\t\t\treturn yield* new ActionConfigurationRejectedError({\n\t\t\t\t\t\taction: \"openCart\",\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tregistration.claimed = true;\n\t\t\t}\n\n\t\t\tconst handler = config.handler;\n\n\t\t\tconst bridged: BridgedOpenCart = {\n\t\t\t\thandler: toAsyncCallback(\n\t\t\t\t\tservices,\n\t\t\t\t\t(\n\t\t\t\t\t\tdefaultHandler: DefaultActionHandler<void>,\n\t\t\t\t\t\t...args: ActionArgs<void, void>\n\t\t\t\t\t) =>\n\t\t\t\t\t\thandler(\n\t\t\t\t\t\t\tfromPromiseThunk(\n\t\t\t\t\t\t\t\tdefaultHandler,\n\t\t\t\t\t\t\t\t(cause) =>\n\t\t\t\t\t\t\t\t\tnew ActionInvocationError({ action: \"openCart\", cause }),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t...args,\n\t\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t};\n\n\t\t\tyield* bindForScope(registration, bridged);\n\t\t}),\n\n\tisDefault: (action) =>\n\t\tEffect.map(requireAction(action), (target) => target.isDefault()),\n});\n\n/**\n * Live layer, backed by `window.Shopify.actions`.\n *\n * Named `layer` per the v4 convention, which replaces v3's `Default`/`Live`.\n */\nexport const layer = Layer.succeed(ShopifyStorefrontActions, make());\n\n/**\n * Accessors so consumers can compose without pulling the service out by hand.\n *\n * `yield* Actions.configureUpdateCart({ ... })` reads better than\n * `yield* (yield* Actions.ShopifyStorefrontActions).configureUpdateCart({ ... })`.\n */\n\nexport const getCart = (payload?: GetCartPayload, options?: GetCartOptions) =>\n\tEffect.flatMap(ShopifyStorefrontActions, (actions) =>\n\t\tactions.getCart(payload, options),\n\t);\n\nexport const updateCart = (\n\tpayload: UpdateCartPayload,\n\toptions?: UpdateCartOptions,\n) =>\n\tEffect.flatMap(ShopifyStorefrontActions, (actions) =>\n\t\tactions.updateCart(payload, options),\n\t);\n\nexport const openCart = Effect.flatMap(\n\tShopifyStorefrontActions,\n\t(actions) => actions.openCart,\n);\n\nexport const configureUpdateCart = <E, R>(\n\tconfig: UpdateCartConfiguration<E, R>,\n) =>\n\tEffect.flatMap(ShopifyStorefrontActions, (actions) =>\n\t\tactions.configureUpdateCart(config),\n\t);\n\nexport const configureOpenCart = <E, R>(config: OpenCartConfiguration<E, R>) =>\n\tEffect.flatMap(ShopifyStorefrontActions, (actions) =>\n\t\tactions.configureOpenCart(config),\n\t);\n\nexport const isDefault = (action: ConfigurableActionName) =>\n\tEffect.flatMap(ShopifyStorefrontActions, (actions) =>\n\t\tactions.isDefault(action),\n\t);\n"],"mappings":";;;;;;;AAgHA,IAAM,qBAAqB,aAAgC;AAC3D,IAAM,mBAAmB,aAA8B;AAwEvD,IAAa,2BAAb,cAA8C,QAAQ,QAGpD,CAAC,CAAC,qDAAqD,CAAC,CAAC,CAAC;;;;;;;AAQ5D,IAAM,iBACL,WAEA,OAAO,cAAc;CACpB,MAAM,SAAS,WAAW,QAAQ,SAAS,UAAU;CAErD,OAAO,WAAW,KAAA,IACf,OAAO,KAAK,IAAI,wBAAwB,EAAE,OAAO,CAAC,CAAC,IACnD,OAAO,QAAQ,MAAM;AACzB,CAAC;;;;;;;;AASF,IAAM,gBACL,cACA,YAEA,OAAO,eACN,OAAO,WAAW;CACjB,aAAa,SAAS;AACvB,CAAC,SAEA,OAAO,WAAW;CACjB,IAAI,aAAa,WAAW,SAAS,aAAa,SAAS;AAC5D,CAAC,CACH;AAED,IAAM,cAA4C;CACjD,UAAU,SAAS,YAClB,OAAO,QAAQ,cAAc,SAAS,IAAI,YACzC,OAAO,WAAW;EAGjB,MAAM,WACL,QAAQ,SAAS;GAAE,GAAG;GAAS,QAAQ,SAAS,UAAU;EAAO,CAAC;EACnE,QAAQ,UACP,IAAI,sBAAsB;GAAE,QAAQ;GAAW;EAAM,CAAC;CACxD,CAAC,CACF;CAED,aAAa,SAAS,YACrB,OAAO,QAAQ,cAAc,YAAY,IAAI,eAC5C,OAAO,WAAW;EACjB,MAAM,WACL,WAAW,SAAS;GACnB,GAAG;GACH,QAAQ,SAAS,UAAU;EAC5B,CAAC;EACF,QAAQ,UACP,IAAI,sBAAsB;GAAE,QAAQ;GAAc;EAAM,CAAC;CAC3D,CAAC,CACF;CAED,UAAU,OAAO,QAAQ,cAAc,UAAU,IAAI,aACpD,OAAO,WAAW;EACjB,WAAW,SAAS;EACpB,QAAQ,UACP,IAAI,sBAAsB;GAAE,QAAQ;GAAY;EAAM,CAAC;CACzD,CAAC,CACF;CAEA,sBAA4B,WAC3B,OAAO,IAAI,aAAa;EACvB,MAAM,SAAS,OAAO,cAAc,YAAY;EAChD,MAAM,WAAW,OAAO,OAAO,QAAW;EAC1C,MAAM,eAAe,mBAAmB,MAAM;EAE9C,IAAI,CAAC,aAAa,SAAS;GAc1B,IAAI,CAXa,OAAO,UAAU;IACjC,cAAc,SAAS,aAAa,QAAQ,YAAY,IAAI,KAAK;IACjE,UAAU,gBAAgB,GAAG,SAAS;KACrC,MAAM,UAAU,aAAa,QAAQ;KAErC,OAAO,UACJ,QAAQ,gBAAgB,GAAG,IAAI,IAC/B,eAAe;IACnB;GACD,CAEK,GACJ,OAAO,OAAO,IAAI,iCAAiC,EAClD,QAAQ,aACT,CAAC;GAGF,aAAa,UAAU;EACxB;EAEA,MAAM,UAAU,OAAO;EAmCvB,OAAO,aAAa,cAAc;GAhCjC,aAAa,eAAe,UAAU,OAAO,cAAc,UAAU;IACpE,OAAO,YAAY,QAAQ,CAAC,CAC3B,OAAO,SACN,yFACA,KACD,CACD;IACA,OAAO;GACR,CAAC;GACD,SACC,YAAY,KAAA,IACT,OACA,gBACA,WAEC,gBACA,GAAG,SAEH,QACC,iBACC,iBACC,UACA,IAAI,sBAAsB;IACzB,QAAQ;IACR;GACD,CAAC,CACH,GACA,GAAG,IACJ,CACF;EAG8B,CAAO;CAC1C,CAAC;CAEF,oBAA0B,WACzB,OAAO,IAAI,aAAa;EACvB,MAAM,SAAS,OAAO,cAAc,UAAU;EAC9C,MAAM,WAAW,OAAO,OAAO,QAAW;EAC1C,MAAM,eAAe,iBAAiB,MAAM;EAE5C,IAAI,CAAC,aAAa,SAAS;GAW1B,IAAI,CAVa,OAAO,UAAU,EACjC,UAAU,gBAAgB,GAAG,SAAS;IACrC,MAAM,UAAU,aAAa,QAAQ;IAErC,OAAO,UACJ,QAAQ,gBAAgB,GAAG,IAAI,IAC/B,eAAe;GACnB,EACD,CAEK,GACJ,OAAO,OAAO,IAAI,iCAAiC,EAClD,QAAQ,WACT,CAAC;GAGF,aAAa,UAAU;EACxB;EAEA,MAAM,UAAU,OAAO;EAoBvB,OAAO,aAAa,cAAc,EAjBjC,SAAS,gBACR,WAEC,gBACA,GAAG,SAEH,QACC,iBACC,iBACC,UACA,IAAI,sBAAsB;GAAE,QAAQ;GAAY;EAAM,CAAC,CACzD,GACA,GAAG,IACJ,CACF,EAGiC,CAAO;CAC1C,CAAC;CAEF,YAAY,WACX,OAAO,IAAI,cAAc,MAAM,IAAI,WAAW,OAAO,UAAU,CAAC;AAClE;;;;;;AAOA,IAAa,QAAQ,MAAM,QAAQ,0BAA0B,KAAK,CAAC;;;;;;;AASnE,IAAa,WAAW,SAA0B,YACjD,OAAO,QAAQ,2BAA2B,YACzC,QAAQ,QAAQ,SAAS,OAAO,CACjC;AAED,IAAa,cACZ,SACA,YAEA,OAAO,QAAQ,2BAA2B,YACzC,QAAQ,WAAW,SAAS,OAAO,CACpC;AAED,IAAa,WAAW,OAAO,QAC9B,2BACC,YAAY,QAAQ,QACtB;AAEA,IAAa,uBACZ,WAEA,OAAO,QAAQ,2BAA2B,YACzC,QAAQ,oBAAoB,MAAM,CACnC;AAED,IAAa,qBAA2B,WACvC,OAAO,QAAQ,2BAA2B,YACzC,QAAQ,kBAAkB,MAAM,CACjC;AAED,IAAa,aAAa,WACzB,OAAO,QAAQ,2BAA2B,YACzC,QAAQ,UAAU,MAAM,CACzB"}
@@ -0,0 +1,26 @@
1
+ //#region src/effect/Actions/trampoline.ts
2
+ /**
3
+ * A registry keyed on the action object itself rather than on the layer.
4
+ *
5
+ * Action identity is the correct key: the upstream slot belongs to that object,
6
+ * so two independently built layers on one page must share a registration,
7
+ * while a freshly stubbed action in a test must get a clean one. Keying on the
8
+ * layer would get both of those backwards.
9
+ */
10
+ var makeRegistry = () => {
11
+ const registrations = /* @__PURE__ */ new WeakMap();
12
+ return (action) => {
13
+ const existing = registrations.get(action);
14
+ if (existing !== void 0) return existing;
15
+ const created = {
16
+ claimed: false,
17
+ active: null
18
+ };
19
+ registrations.set(action, created);
20
+ return created;
21
+ };
22
+ };
23
+ //#endregion
24
+ export { makeRegistry };
25
+
26
+ //# sourceMappingURL=trampoline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trampoline.js","names":[],"sources":["../../../../src/effect/Actions/trampoline.ts"],"sourcesContent":["/**\n * Registration bookkeeping that turns Shopify's one-shot `configure()` into\n * something re-bindable.\n *\n * The shipped runtime stores the configuration in a closure variable and\n * refuses every later call:\n *\n * ```js\n * configure(e) { return t || !e || typeof e !== \"object\" ? false : (t = e, true) }\n * ```\n *\n * There is no reset path, so the slot can never be released. What we *can* do\n * is claim it once with a stable trampoline that reads a mutable cell, then\n * rebind that cell as often as we like. Deactivating restores the runtime's\n * default behaviour exactly — the trampoline falls through to the default\n * handler, and a `null` event target makes Shopify dispatch on `document`,\n * which is what it does when unconfigured.\n *\n * The one residue we cannot undo is `isDefault()`, which reports `false` from\n * the moment we claim the slot, whether or not a configuration is active.\n */\nexport interface Registration<Config> {\n\t/** Whether our trampoline currently occupies the upstream slot. */\n\tclaimed: boolean;\n\t/** The live bridged configuration, or `null` when deactivated. */\n\tactive: Config | null;\n}\n\n/**\n * A registry keyed on the action object itself rather than on the layer.\n *\n * Action identity is the correct key: the upstream slot belongs to that object,\n * so two independently built layers on one page must share a registration,\n * while a freshly stubbed action in a test must get a clean one. Keying on the\n * layer would get both of those backwards.\n */\nexport const makeRegistry = <Config>() => {\n\tconst registrations = new WeakMap<object, Registration<Config>>();\n\n\treturn (action: object): Registration<Config> => {\n\t\tconst existing = registrations.get(action);\n\n\t\tif (existing !== undefined) return existing;\n\n\t\tconst created: Registration<Config> = { claimed: false, active: null };\n\t\tregistrations.set(action, created);\n\n\t\treturn created;\n\t};\n};\n"],"mappings":";;;;;;;;;AAoCA,IAAa,qBAA6B;CACzC,MAAM,gCAAgB,IAAI,QAAsC;CAEhE,QAAQ,WAAyC;EAChD,MAAM,WAAW,cAAc,IAAI,MAAM;EAEzC,IAAI,aAAa,KAAA,GAAW,OAAO;EAEnC,MAAM,UAAgC;GAAE,SAAS;GAAO,QAAQ;EAAK;EACrE,cAAc,IAAI,QAAQ,OAAO;EAEjC,OAAO;CACR;AACD"}
@@ -1,4 +1 @@
1
- import * as schema_js from './schema.js';
2
- export { schema_js as Sections };
3
- //# sourceMappingURL=index.js.map
4
- //# sourceMappingURL=index.js.map
1
+ import "./schema.js";
@@ -1,44 +1,14 @@
1
- import * as Schema from 'effect/Schema';
2
- import * as ParseResult from 'effect/ParseResult';
3
- import 'effect/Function';
4
-
5
- const Input = Schema.Struct({
6
- sections: Schema.optional(
7
- Schema.transform(Schema.Array(Schema.String), Schema.String, {
8
- decode: (sections) => sections.join(","),
9
- encode: (str) => str.split(",")
10
- })
11
- ),
12
- sections_url: Schema.optional(Schema.String)
1
+ import "effect/Effect";
2
+ import * as Schema from "effect/Schema";
3
+ import * as SchemaGetter from "effect/SchemaGetter";
4
+ import "effect/SchemaIssue";
5
+ Schema.Struct({
6
+ sections: Schema.optional(Schema.Array(Schema.String).pipe(Schema.decodeTo(Schema.String, {
7
+ decode: SchemaGetter.transform((sections) => sections.join(",")),
8
+ encode: SchemaGetter.transform((str) => str.split(","))
9
+ }))),
10
+ sections_url: Schema.optional(Schema.String)
13
11
  });
14
- const parseKeys = (keys) => Array.from(
15
- new Set(keys.split(",").map((key) => key.trim()))
16
- );
17
- function makeResponseSchema(input) {
18
- const keys = parseKeys(input);
19
- return Schema.Record({
20
- key: Schema.Literal(...keys),
21
- value: Schema.transformOrFail(Schema.String, Schema.instanceOf(Element), {
22
- decode: (htmlString, _, ast) => {
23
- const parser = new DOMParser();
24
- const doc = parser.parseFromString(htmlString, "text/html");
25
- const root = doc.body.firstElementChild;
26
- if (!root) {
27
- return ParseResult.fail(
28
- new ParseResult.Type(
29
- ast,
30
- htmlString,
31
- "Failed to convert HTML string to Element"
32
- )
33
- );
34
- }
35
- return ParseResult.succeed(root);
36
- },
37
- encode: (element, options, ast) => ParseResult.succeed(element.outerHTML)
38
- }).pipe(Schema.NullOr)
39
- });
40
- }
12
+ //#endregion
41
13
 
42
- export { Input, makeResponseSchema };
43
- //# sourceMappingURL=schema.js.map
44
14
  //# sourceMappingURL=schema.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/effect/Ajax/Sections/schema.ts"],"names":[],"mappings":";;;;AAKa,MAAA,KAAA,GAAQ,OAAO,MAAO,CAAA;AAAA,EACjC,UAAU,MAAO,CAAA,QAAA;AAAA,IACf,MAAA,CAAO,UAAU,MAAO,CAAA,KAAA,CAAM,OAAO,MAAM,CAAA,EAAG,OAAO,MAAQ,EAAA;AAAA,MAC3D,MAAQ,EAAA,CAAC,QAAa,KAAA,QAAA,CAAS,KAAK,GAAG,CAAA;AAAA,MACvC,MAAQ,EAAA,CAAC,GAAQ,KAAA,GAAA,CAAI,MAAM,GAAG;AAAA,KAC/B;AAAA,GACH;AAAA,EACA,YAAc,EAAA,MAAA,CAAO,QAAS,CAAA,MAAA,CAAO,MAAM;AAC7C,CAAC;AAOD,MAAM,SAAA,GAAY,CAAyB,IAAA,KACzC,KAAM,CAAA,IAAA;AAAA,EACJ,IAAI,GAAA,CAAI,IAAK,CAAA,KAAA,CAAM,GAAG,CAAA,CAAE,GAAI,CAAA,CAAC,GAAQ,KAAA,GAAA,CAAI,IAAK,EAAC,CAAC;AAClD,CAAA;AAEK,SAAS,mBAA2C,KAAU,EAAA;AACnE,EAAM,MAAA,IAAA,GAAO,UAAU,KAAK,CAAA;AAC5B,EAAA,OAAO,OAAO,MAAO,CAAA;AAAA,IACnB,GAAK,EAAA,MAAA,CAAO,OAAQ,CAAA,GAAG,IAAI,CAAA;AAAA,IAC3B,KAAA,EAAO,OAAO,eAAgB,CAAA,MAAA,CAAO,QAAQ,MAAO,CAAA,UAAA,CAAW,OAAO,CAAG,EAAA;AAAA,MACvE,MAAQ,EAAA,CAAC,UAAY,EAAA,CAAA,EAAG,GAAQ,KAAA;AAC9B,QAAM,MAAA,MAAA,GAAS,IAAI,SAAU,EAAA;AAC7B,QAAA,MAAM,GAAM,GAAA,MAAA,CAAO,eAAgB,CAAA,UAAA,EAAY,WAAW,CAAA;AAC1D,QAAM,MAAA,IAAA,GAAO,IAAI,IAAK,CAAA,iBAAA;AACtB,QAAA,IAAI,CAAC,IAAM,EAAA;AACT,UAAA,OAAO,WAAY,CAAA,IAAA;AAAA,YACjB,IAAI,WAAY,CAAA,IAAA;AAAA,cACd,GAAA;AAAA,cACA,UAAA;AAAA,cACA;AAAA;AACF,WACF;AAAA;AAEF,QAAO,OAAA,WAAA,CAAY,QAAQ,IAAI,CAAA;AAAA,OACjC;AAAA,MACA,MAAA,EAAQ,CAAC,OAAS,EAAA,OAAA,EAAS,QAAQ,WAAY,CAAA,OAAA,CAAQ,QAAQ,SAAS;AAAA,KACzE,CAAA,CAAE,IAAK,CAAA,MAAA,CAAO,MAAM;AAAA,GACtB,CAAA;AACH","file":"schema.js","sourcesContent":["import * as Schema from \"effect/Schema\";\nimport * as ParseResult from \"effect/ParseResult\";\nimport * as Function from \"effect/Function\";\n\nexport type Input = typeof Input;\nexport const Input = Schema.Struct({\n sections: Schema.optional(\n Schema.transform(Schema.Array(Schema.String), Schema.String, {\n decode: (sections) => sections.join(\",\"),\n encode: (str) => str.split(\",\"),\n }),\n ),\n sections_url: Schema.optional(Schema.String),\n});\n\n// Helper type to convert a string to a tuple of unique literals\ntype UniqueKeys<T extends string> = T extends `${infer K},${infer Rest}`\n ? K | UniqueKeys<Rest>\n : T;\n\nconst parseKeys = <const T extends string>(keys: T) =>\n Array.from(\n new Set(keys.split(\",\").map((key) => key.trim())),\n ) as UniqueKeys<T>[];\n\nexport function makeResponseSchema<const T extends string>(input: T) {\n const keys = parseKeys(input);\n return Schema.Record({\n key: Schema.Literal(...keys),\n value: Schema.transformOrFail(Schema.String, Schema.instanceOf(Element), {\n decode: (htmlString, _, ast) => {\n const parser = new DOMParser();\n const doc = parser.parseFromString(htmlString, \"text/html\");\n const root = doc.body.firstElementChild;\n if (!root) {\n return ParseResult.fail(\n new ParseResult.Type(\n ast,\n htmlString,\n \"Failed to convert HTML string to Element\",\n ),\n );\n }\n return ParseResult.succeed(root);\n },\n encode: (element, options, ast) => ParseResult.succeed(element.outerHTML),\n }).pipe(Schema.NullOr),\n });\n}\n"]}
1
+ {"version":3,"file":"schema.js","names":[],"sources":["../../../../../src/effect/Ajax/Sections/schema.ts"],"sourcesContent":["import * as Effect from \"effect/Effect\";\nimport * as Schema from \"effect/Schema\";\nimport * as SchemaGetter from \"effect/SchemaGetter\";\nimport * as SchemaIssue from \"effect/SchemaIssue\";\n\nexport type Input = typeof Input;\nexport const Input = Schema.Struct({\n\tsections: Schema.optional(\n\t\tSchema.Array(Schema.String).pipe(\n\t\t\tSchema.decodeTo(Schema.String, {\n\t\t\t\tdecode: SchemaGetter.transform((sections: ReadonlyArray<string>) =>\n\t\t\t\t\tsections.join(\",\"),\n\t\t\t\t),\n\t\t\t\tencode: SchemaGetter.transform((str: string) => str.split(\",\")),\n\t\t\t}),\n\t\t),\n\t),\n\tsections_url: Schema.optional(Schema.String),\n});\n\n// Helper type to convert a string to a tuple of unique literals\ntype UniqueKeys<T extends string> = T extends `${infer K},${infer Rest}`\n\t? K | UniqueKeys<Rest>\n\t: T;\n\nconst parseKeys = <const T extends string>(keys: T) =>\n\tArray.from(\n\t\tnew Set(keys.split(\",\").map((key) => key.trim())),\n\t) as UniqueKeys<T>[];\n\nexport function makeResponseSchema<const T extends string>(input: T) {\n\tconst keys = parseKeys(input);\n\treturn Schema.Record(\n\t\tSchema.Literals(keys),\n\t\tSchema.String.pipe(\n\t\t\tSchema.decodeTo(Schema.instanceOf(Element), {\n\t\t\t\tdecode: SchemaGetter.transformEffect((htmlString: string) => {\n\t\t\t\t\tconst parser = new DOMParser();\n\t\t\t\t\tconst doc = parser.parseFromString(htmlString, \"text/html\");\n\t\t\t\t\tconst root = doc.body.firstElementChild;\n\n\t\t\t\t\tif (!root) {\n\t\t\t\t\t\t// InvalidValue rather than InvalidType: the input really is a\n\t\t\t\t\t\t// string, it just does not parse to an Element. The v3 code used\n\t\t\t\t\t\t// the type-mismatch issue for this.\n\t\t\t\t\t\treturn Effect.fail(\n\t\t\t\t\t\t\tnew SchemaIssue.InvalidValue(\n\t\t\t\t\t\t\t\t{ message: \"Failed to convert HTML string to Element\" },\n\t\t\t\t\t\t\t\thtmlString,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn Effect.succeed(root);\n\t\t\t\t}),\n\t\t\t\tencode: SchemaGetter.transform((element: Element) => element.outerHTML),\n\t\t\t}),\n\t\t\tSchema.NullOr,\n\t\t),\n\t);\n}\n"],"mappings":";;;;AAMqB,OAAO,OAAO;CAClC,UAAU,OAAO,SAChB,OAAO,MAAM,OAAO,MAAM,CAAC,CAAC,KAC3B,OAAO,SAAS,OAAO,QAAQ;EAC9B,QAAQ,aAAa,WAAW,aAC/B,SAAS,KAAK,GAAG,CAClB;EACA,QAAQ,aAAa,WAAW,QAAgB,IAAI,MAAM,GAAG,CAAC;CAC/D,CAAC,CACF,CACD;CACA,cAAc,OAAO,SAAS,OAAO,MAAM;AAC5C,CAAC"}
@@ -0,0 +1,18 @@
1
+ import * as Context from "effect/Context";
2
+ import "effect/Layer";
3
+ Context.Reference("@brytdesigns/shopify-utils/AllowedRouteOrigins", { defaultValue: () => {
4
+ if (typeof window === "undefined") return [];
5
+ const configured = window?.BrytDesigns?.allowedRouteOrigins;
6
+ if (!Array.isArray(configured)) return [];
7
+ return configured.flatMap((value) => {
8
+ try {
9
+ return [new URL(String(value)).origin];
10
+ } catch {
11
+ console.warn(`[brytdesigns] ignoring unparseable entry in window.BrytDesigns.allowedRouteOrigins: ${String(value)}`);
12
+ return [];
13
+ }
14
+ });
15
+ } });
16
+ //#endregion
17
+
18
+ //# sourceMappingURL=AllowedRouteOrigins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AllowedRouteOrigins.js","names":[],"sources":["../../../../../../src/effect/Ajax/Window/data/AllowedRouteOrigins.ts"],"sourcesContent":["import * as Context from \"effect/Context\";\nimport * as Layer from \"effect/Layer\";\n\n/**\n * Origins, besides the storefront's own, that a route in the table may point\n * at.\n *\n * ## Why this exists\n *\n * The route table is `{ ...Defaults, ...window.BrytDesigns.routes }`, and the\n * URL used to be built by concatenating it onto the origin. A route of\n * `\"@evil.com/collect\"` therefore produced\n * `https://shop.myshopify.com@evil.com/collect`, which parses the storefront\n * as *userinfo* and `evil.com` as the host — every cart request, with the\n * shopper's session cookies, sent off-origin. Routes are resolved with `new\n * URL` now and checked against the storefront origin.\n *\n * That check would otherwise make a *deliberate* off-origin route impossible,\n * which is a real thing to want: a storefront fronting its cart endpoints\n * through a proxy or an edge worker on another host. This reference is the\n * seam for it.\n *\n * ## What it is and is not\n *\n * It is not a privilege boundary. Whoever can write `window.BrytDesigns` can\n * write both the routes and this list — and could patch `fetch` besides. What\n * the pair buys is that going off-origin has to be **stated**: a typo\n * (`\"cart.js\"` without its leading slash) and a smuggled host both fail\n * loudly, while the supported case is one explicit line that shows up in a\n * code review and in the page source.\n *\n * Empty by default, so the storefront origin is the only one accepted.\n *\n * @example\n * ```html\n * <!-- In the theme, before the modules load. -->\n * <script>\n * window.BrytDesigns = window.BrytDesigns || {};\n * window.BrytDesigns.allowedRouteOrigins = [\"https://cart-proxy.example\"];\n * window.BrytDesigns.routes = { cart_url: \"https://cart-proxy.example/cart.js\" };\n * </script>\n * ```\n *\n * @example\n * ```ts\n * // From Effect, without a DOM.\n * Effect.provideService(effect, Ajax.Window.AllowedRouteOrigins.Origins, [\n * \"https://cart-proxy.example\",\n * ]);\n * ```\n */\nexport const Origins: Context.Reference<readonly string[]> = Context.Reference<\n\treadonly string[]\n>(\"@brytdesigns/shopify-utils/AllowedRouteOrigins\", {\n\t// Read lazily and defensively, for the same reasons as the route table:\n\t// `window?.` does not protect against the identifier being undeclared, and\n\t// the default has to work from Node so `ajax-client`'s tests need no jsdom.\n\tdefaultValue: () => {\n\t\tif (typeof window === \"undefined\") return [];\n\t\tconst configured = window?.BrytDesigns?.allowedRouteOrigins;\n\t\tif (!Array.isArray(configured)) return [];\n\t\t// Normalised through `URL` so that a trailing slash or a different case\n\t\t// in the theme's string still matches what `URL.origin` reports.\n\t\treturn configured.flatMap((value) => {\n\t\t\ttry {\n\t\t\t\treturn [new URL(String(value)).origin];\n\t\t\t} catch {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[brytdesigns] ignoring unparseable entry in window.BrytDesigns.allowedRouteOrigins: ${String(value)}`,\n\t\t\t\t);\n\t\t\t\treturn [];\n\t\t\t}\n\t\t});\n\t},\n});\n\n/** Pins the allow-list for a whole program. */\nexport const layer = (origins: readonly string[]): Layer.Layer<never> =>\n\tLayer.succeed(Origins, origins);\n"],"mappings":";;AAmD6D,QAAQ,UAEnE,kDAAkD,EAInD,oBAAoB;CACnB,IAAI,OAAO,WAAW,aAAa,OAAO,CAAC;CAC3C,MAAM,aAAa,QAAQ,aAAa;CACxC,IAAI,CAAC,MAAM,QAAQ,UAAU,GAAG,OAAO,CAAC;CAGxC,OAAO,WAAW,SAAS,UAAU;EACpC,IAAI;GACH,OAAO,CAAC,IAAI,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM;EACtC,QAAQ;GACP,QAAQ,KACP,uFAAuF,OAAO,KAAK,GACpG;GACA,OAAO,CAAC;EACT;CACD,CAAC;AACF,EACD,CAAC"}
@@ -1,40 +1,40 @@
1
- import * as Data from 'effect/Data';
2
-
3
- const Defaults = {
4
- account_addresses_url: "/account/addresses",
5
- account_login_url: "/account/login",
6
- account_logout_url: "/account/logout",
7
- account_recover_url: "/account/recover",
8
- account_register_url: "/account/register",
9
- account_url: "/account",
10
- all_products_collection_url: "/collections/all",
11
- product_url: "/products/:handle",
12
- products_url: "/products",
13
- cart_add_url: "/cart/add.js",
14
- cart_change_url: "/cart/change.js",
15
- cart_clear_url: "/cart/clear.js",
16
- cart_update_url: "/cart/update.js",
17
- cart_url: "/cart.js",
18
- collections_url: "/collections",
19
- predictive_search_url: "/search/suggest",
20
- product_recommendations_url: "/recommendations/products",
21
- root_url: "/",
22
- search_url: "/search",
23
- storefront_login_url: "/customer_authentication/login?return_to=%2Fservices%2Fliquid_rendering%2Fresource%3Ffast_storefront_renderer%3D1&locale=en"
1
+ import * as Data from "effect/Data";
2
+ import * as Context from "effect/Context";
3
+ import "effect/Layer";
4
+ //#region src/effect/Ajax/Window/data/ShopifyRoutes.ts
5
+ var Defaults = {
6
+ account_addresses_url: "/account/addresses",
7
+ account_login_url: "/account/login",
8
+ account_logout_url: "/account/logout",
9
+ account_recover_url: "/account/recover",
10
+ account_register_url: "/account/register",
11
+ account_url: "/account",
12
+ all_products_collection_url: "/collections/all",
13
+ product_url: "/products/:handle",
14
+ products_url: "/products",
15
+ cart_add_url: "/cart/add.js",
16
+ cart_change_url: "/cart/change.js",
17
+ cart_clear_url: "/cart/clear.js",
18
+ cart_update_url: "/cart/update.js",
19
+ cart_url: "/cart.js",
20
+ collections_url: "/collections",
21
+ predictive_search_url: "/search/suggest",
22
+ product_recommendations_url: "/recommendations/products",
23
+ root_url: "/",
24
+ search_url: "/search",
25
+ storefront_login_url: "/customer_authentication/login?return_to=%2Fservices%2Fliquid_rendering%2Fresource%3Ffast_storefront_renderer%3D1&locale=en"
24
26
  };
25
- class ShopifyRoutes extends Data.Class {
26
- constructor() {
27
- super({
28
- ...Defaults,
29
- ...window?.BrytDesigns?.routes || {}
30
- });
31
- }
32
- }
33
- const make = () => Data.case()({
34
- ...Defaults,
35
- ...window?.BrytDesigns?.routes || {}
36
- });
37
-
27
+ var ShopifyRoutes = class extends Data.Class {
28
+ constructor() {
29
+ super({
30
+ ...Defaults,
31
+ ...window?.BrytDesigns?.routes || {}
32
+ });
33
+ }
34
+ };
35
+ var make = () => new ShopifyRoutes();
36
+ Context.Reference("@brytdesigns/shopify-utils/ShopifyRoutes", { defaultValue: () => typeof window === "undefined" ? Defaults : make() });
37
+ //#endregion
38
38
  export { Defaults, ShopifyRoutes, make };
39
- //# sourceMappingURL=ShopifyRoutes.js.map
39
+
40
40
  //# sourceMappingURL=ShopifyRoutes.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../src/effect/Ajax/Window/data/ShopifyRoutes.ts"],"names":[],"mappings":";;AAIO,MAAM,QAA2B,GAAA;AAAA,EACtC,qBAAuB,EAAA,oBAAA;AAAA,EACvB,iBAAmB,EAAA,gBAAA;AAAA,EACnB,kBAAoB,EAAA,iBAAA;AAAA,EACpB,mBAAqB,EAAA,kBAAA;AAAA,EACrB,oBAAsB,EAAA,mBAAA;AAAA,EACtB,WAAa,EAAA,UAAA;AAAA,EACb,2BAA6B,EAAA,kBAAA;AAAA,EAC7B,WAAa,EAAA,mBAAA;AAAA,EACb,YAAc,EAAA,WAAA;AAAA,EACd,YAAc,EAAA,cAAA;AAAA,EACd,eAAiB,EAAA,iBAAA;AAAA,EACjB,cAAgB,EAAA,gBAAA;AAAA,EAChB,eAAiB,EAAA,iBAAA;AAAA,EACjB,QAAU,EAAA,UAAA;AAAA,EACV,eAAiB,EAAA,cAAA;AAAA,EACjB,qBAAuB,EAAA,iBAAA;AAAA,EACvB,2BAA6B,EAAA,2BAAA;AAAA,EAC7B,QAAU,EAAA,GAAA;AAAA,EACV,UAAY,EAAA,SAAA;AAAA,EACZ,oBACE,EAAA;AACJ;AAEO,MAAM,aAAA,SAAsB,KAAK,KAAsB,CAAA;AAAA,EAC5D,WAAc,GAAA;AACZ,IAAM,KAAA,CAAA;AAAA,MACJ,GAAG,QAAA;AAAA,MACH,GAAI,MAAA,EAAQ,WAAa,EAAA,MAAA,IAAU;AAAC,KACrC,CAAA;AAAA;AAEL;AAEO,MAAM,IAAO,GAAA,MAClB,IAAK,CAAA,IAAA,EAAsB,CAAA;AAAA,EACzB,GAAG,QAAA;AAAA,EACH,GAAI,MAAA,EAAQ,WAAa,EAAA,MAAA,IAAU;AACrC,CAAC","file":"ShopifyRoutes.js","sourcesContent":["import type { IShopifyRoutes } from \"./types.js\";\n\nimport * as Data from \"effect/Data\";\n\nexport const Defaults: IShopifyRoutes = {\n account_addresses_url: \"/account/addresses\",\n account_login_url: \"/account/login\",\n account_logout_url: \"/account/logout\",\n account_recover_url: \"/account/recover\",\n account_register_url: \"/account/register\",\n account_url: \"/account\",\n all_products_collection_url: \"/collections/all\",\n product_url: \"/products/:handle\",\n products_url: \"/products\",\n cart_add_url: \"/cart/add.js\",\n cart_change_url: \"/cart/change.js\",\n cart_clear_url: \"/cart/clear.js\",\n cart_update_url: \"/cart/update.js\",\n cart_url: \"/cart.js\",\n collections_url: \"/collections\",\n predictive_search_url: \"/search/suggest\",\n product_recommendations_url: \"/recommendations/products\",\n root_url: \"/\",\n search_url: \"/search\",\n storefront_login_url:\n \"/customer_authentication/login?return_to=%2Fservices%2Fliquid_rendering%2Fresource%3Ffast_storefront_renderer%3D1&locale=en\",\n};\n\nexport class ShopifyRoutes extends Data.Class<IShopifyRoutes> {\n constructor() {\n super({\n ...Defaults,\n ...(window?.BrytDesigns?.routes || {}),\n });\n }\n}\n\nexport const make = () =>\n Data.case<ShopifyRoutes>()({\n ...Defaults,\n ...(window?.BrytDesigns?.routes || {}),\n });\n"]}
1
+ {"version":3,"file":"ShopifyRoutes.js","names":[],"sources":["../../../../../../src/effect/Ajax/Window/data/ShopifyRoutes.ts"],"sourcesContent":["import * as Context from \"effect/Context\";\nimport * as Data from \"effect/Data\";\nimport * as Layer from \"effect/Layer\";\nimport type { IShopifyRoutes } from \"./types.js\";\n\nexport const Defaults: IShopifyRoutes = {\n\taccount_addresses_url: \"/account/addresses\",\n\taccount_login_url: \"/account/login\",\n\taccount_logout_url: \"/account/logout\",\n\taccount_recover_url: \"/account/recover\",\n\taccount_register_url: \"/account/register\",\n\taccount_url: \"/account\",\n\tall_products_collection_url: \"/collections/all\",\n\tproduct_url: \"/products/:handle\",\n\tproducts_url: \"/products\",\n\tcart_add_url: \"/cart/add.js\",\n\tcart_change_url: \"/cart/change.js\",\n\tcart_clear_url: \"/cart/clear.js\",\n\tcart_update_url: \"/cart/update.js\",\n\tcart_url: \"/cart.js\",\n\tcollections_url: \"/collections\",\n\tpredictive_search_url: \"/search/suggest\",\n\tproduct_recommendations_url: \"/recommendations/products\",\n\troot_url: \"/\",\n\tsearch_url: \"/search\",\n\tstorefront_login_url:\n\t\t\"/customer_authentication/login?return_to=%2Fservices%2Fliquid_rendering%2Fresource%3Ffast_storefront_renderer%3D1&locale=en\",\n};\n\nexport class ShopifyRoutes extends Data.Class<IShopifyRoutes> {\n\tconstructor() {\n\t\tsuper({\n\t\t\t...Defaults,\n\t\t\t...(window?.BrytDesigns?.routes || {}),\n\t\t});\n\t}\n}\n\n// v4 removed Data.case. The class constructor already applies exactly these\n// defaults, so the factory collapses to it.\nexport const make = (): ShopifyRoutes => new ShopifyRoutes();\n\n/**\n * The route table, as a service.\n *\n * Deliberately a `Context.Reference` rather than a `Context.Service`: a\n * `Reference` carries its own default, so reading it leaves the effect's `R`\n * channel at `never`. A `Service` would push `ShopifyRoutes` into `R` for\n * every request effect in every downstream package, and those effects are run\n * by plain Shopify themes through `Effect.runPromise` — which cannot provide\n * anything. The default has to be the common path, and overriding it the\n * exception.\n *\n * The default is evaluated lazily, on first read — but only once. Effect\n * memoises a reference's default onto the reference itself\n * (`Context.getDefaultValue`), so the table is read from `window` on the first\n * Ajax call of the page and reused from then on.\n *\n * That is a deliberate change from the previous behaviour, where `make()` ran\n * on every request and so picked up a later mutation of\n * `window.BrytDesigns.routes`. Laziness buys the common case — a theme's inline\n * script sets the routes before the first request, which module-scope\n * evaluation would have missed — but a theme that rebinds routes *after* the\n * first request must now provide them explicitly, with {@link layer} or\n * `Effect.provideService`.\n *\n * @example\n * ```ts\n * // Override in a test, with no DOM in sight.\n * Effect.provideService(effect, ShopifyRoutes.Routes, {\n * ...ShopifyRoutes.Defaults,\n * cart_url: \"/fixtures/cart.js\",\n * });\n * ```\n */\nexport const Routes: Context.Reference<IShopifyRoutes> =\n\tContext.Reference<IShopifyRoutes>(\n\t\t\"@brytdesigns/shopify-utils/ShopifyRoutes\",\n\t\t{\n\t\t\t// Guarded rather than just `make()`: the class constructor reads `window`\n\t\t\t// unconditionally, and `window?.` does not protect against the identifier\n\t\t\t// being undeclared. Falling back to `Defaults` keeps the reference usable\n\t\t\t// from Node - which is what lets `ajax-client`'s tests run without jsdom.\n\t\t\tdefaultValue: () => (typeof window === \"undefined\" ? Defaults : make()),\n\t\t},\n\t);\n\n/**\n * Pins the route table to an explicit value.\n *\n * Only needed when something must override the window-derived default for a\n * whole program rather than a single effect; `Effect.provideService` covers\n * the single-effect case without building a layer.\n */\nexport const layer = (routes: IShopifyRoutes): Layer.Layer<never> =>\n\tLayer.succeed(Routes, routes);\n"],"mappings":";;;;AAKA,IAAa,WAA2B;CACvC,uBAAuB;CACvB,mBAAmB;CACnB,oBAAoB;CACpB,qBAAqB;CACrB,sBAAsB;CACtB,aAAa;CACb,6BAA6B;CAC7B,aAAa;CACb,cAAc;CACd,cAAc;CACd,iBAAiB;CACjB,gBAAgB;CAChB,iBAAiB;CACjB,UAAU;CACV,iBAAiB;CACjB,uBAAuB;CACvB,6BAA6B;CAC7B,UAAU;CACV,YAAY;CACZ,sBACC;AACF;AAEA,IAAa,gBAAb,cAAmC,KAAK,MAAsB;CAC7D,cAAc;EACb,MAAM;GACL,GAAG;GACH,GAAI,QAAQ,aAAa,UAAU,CAAC;EACrC,CAAC;CACF;AACD;AAIA,IAAa,aAA4B,IAAI,cAAc;AAoC1D,QAAQ,UACP,4CACA,EAKC,oBAAqB,OAAO,WAAW,cAAc,WAAW,KAAK,EACtE,CACD"}
@@ -1,16 +1,5 @@
1
- import * as Data from 'effect/Data';
1
+ import * as Data from "effect/Data";
2
+ Data.Class;
3
+ //#endregion
2
4
 
3
- class StorefrontClientConfig extends Data.Class {
4
- constructor() {
5
- super({
6
- ...window.BrytDesigns.storefront.config
7
- });
8
- }
9
- }
10
- const make = () => Data.case()({
11
- ...window.BrytDesigns.storefront.config
12
- });
13
-
14
- export { StorefrontClientConfig, make };
15
- //# sourceMappingURL=StorefrontClientConfig.js.map
16
5
  //# sourceMappingURL=StorefrontClientConfig.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../src/effect/Ajax/Window/data/StorefrontClientConfig.ts"],"names":[],"mappings":";;AAIO,MAAM,sBAAA,SAA+B,KAAK,KAA+B,CAAA;AAAA,EAC9E,WAAc,GAAA;AACZ,IAAM,KAAA,CAAA;AAAA,MACJ,GAAG,MAAO,CAAA,WAAA,CAAY,UAAW,CAAA;AAAA,KAClC,CAAA;AAAA;AAEL;AAEO,MAAM,IAAO,GAAA,MAClB,IAAK,CAAA,IAAA,EAA+B,CAAA;AAAA,EAClC,GAAG,MAAO,CAAA,WAAA,CAAY,UAAW,CAAA;AACnC,CAAC","file":"StorefrontClientConfig.js","sourcesContent":["import type { IStorefrontClientConfig } from \"./types.js\";\n\nimport * as Data from \"effect/Data\";\n\nexport class StorefrontClientConfig extends Data.Class<IStorefrontClientConfig> {\n constructor() {\n super({\n ...window.BrytDesigns.storefront.config,\n });\n }\n}\n\nexport const make = () =>\n Data.case<StorefrontClientConfig>()({\n ...window.BrytDesigns.storefront.config,\n });\n"]}
1
+ {"version":3,"file":"StorefrontClientConfig.js","names":[],"sources":["../../../../../../src/effect/Ajax/Window/data/StorefrontClientConfig.ts"],"sourcesContent":["import * as Data from \"effect/Data\";\nimport type { IStorefrontClientConfig } from \"./types.js\";\n\nexport class StorefrontClientConfig extends Data.Class<IStorefrontClientConfig> {\n\tconstructor() {\n\t\tsuper({\n\t\t\t...window.BrytDesigns.storefront.config,\n\t\t});\n\t}\n}\n\n// v4 removed Data.case. The class constructor already applies exactly these\n// defaults, so the factory collapses to it.\nexport const make = (): StorefrontClientConfig => new StorefrontClientConfig();\n"],"mappings":";AAG4C,KAAK"}
@@ -0,0 +1,6 @@
1
+ import * as Context from "effect/Context";
2
+ import "effect/Layer";
3
+ Context.Reference("@brytdesigns/shopify-utils/StorefrontOrigin", { defaultValue: () => typeof window === "undefined" ? "" : window.location.origin });
4
+ //#endregion
5
+
6
+ //# sourceMappingURL=StorefrontOrigin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StorefrontOrigin.js","names":[],"sources":["../../../../../../src/effect/Ajax/Window/data/StorefrontOrigin.ts"],"sourcesContent":["import * as Context from \"effect/Context\";\nimport * as Layer from \"effect/Layer\";\n\n/**\n * The origin every Ajax route is resolved against.\n *\n * Shopify's Ajax endpoints are same-origin paths (`/cart.js`), so this is\n * `window.location.origin` on a storefront and the only reason the request\n * layer needed a DOM at all. Lifting it to a `Context.Reference` means\n * `ajax-client` can build a request without one, which is what makes its\n * tests runnable outside jsdom.\n *\n * A `Reference` rather than a `Service` for the same reason as\n * {@link ShopifyRoutes.Routes}: the default must work with no provision, so\n * themes can keep calling `Effect.runPromise` directly.\n *\n * Read lazily. `window.location.origin` is stable within a page, but reading\n * it at module scope would still break any environment that imports this\n * module before a DOM exists.\n */\nexport const Origin: Context.Reference<string> = Context.Reference<string>(\n\t\"@brytdesigns/shopify-utils/StorefrontOrigin\",\n\t{\n\t\tdefaultValue: () =>\n\t\t\ttypeof window === \"undefined\" ? \"\" : window.location.origin,\n\t},\n);\n\n/** Pins the origin for a whole program. */\nexport const layer = (origin: string): Layer.Layer<never> =>\n\tLayer.succeed(Origin, origin);\n"],"mappings":";;AAoBiD,QAAQ,UACxD,+CACA,EACC,oBACC,OAAO,WAAW,cAAc,KAAK,OAAO,SAAS,OACvD,CACD"}
@@ -1,6 +1,4 @@
1
- import * as ShopifyRoutes_js from './ShopifyRoutes.js';
2
- export { ShopifyRoutes_js as ShopifyRoutes };
3
- import * as StorefrontClientConfig_js from './StorefrontClientConfig.js';
4
- export { StorefrontClientConfig_js as StorefrontClientConfig };
5
- //# sourceMappingURL=index.js.map
6
- //# sourceMappingURL=index.js.map
1
+ import "./AllowedRouteOrigins.js";
2
+ import "./ShopifyRoutes.js";
3
+ import "./StorefrontClientConfig.js";
4
+ import "./StorefrontOrigin.js";
@@ -1,4 +1 @@
1
- import * as index_js from './data/index.js';
2
- export { index_js as Window };
3
- //# sourceMappingURL=index.js.map
4
- //# sourceMappingURL=index.js.map
1
+ import "./data/index.js";