alchemy 2.0.0-beta.43 → 2.0.0-beta.44

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 (207) hide show
  1. package/bin/cli.js +18 -5
  2. package/bin/exec.js +315 -233
  3. package/bin/exec.js.map +1 -1
  4. package/lib/Auth/Lock.js +2 -2
  5. package/lib/Auth/Lock.js.map +1 -1
  6. package/lib/Bundle/Bundle.d.ts +11 -0
  7. package/lib/Bundle/Bundle.d.ts.map +1 -1
  8. package/lib/Bundle/Bundle.js +19 -12
  9. package/lib/Bundle/Bundle.js.map +1 -1
  10. package/lib/Bundle/BundleAnalyzerPlugin.d.ts +25 -0
  11. package/lib/Bundle/BundleAnalyzerPlugin.d.ts.map +1 -0
  12. package/lib/Bundle/BundleAnalyzerPlugin.js +16 -0
  13. package/lib/Bundle/BundleAnalyzerPlugin.js.map +1 -0
  14. package/lib/Bundle/RawPlugin.d.ts +36 -0
  15. package/lib/Bundle/RawPlugin.d.ts.map +1 -0
  16. package/lib/Bundle/RawPlugin.js +80 -0
  17. package/lib/Bundle/RawPlugin.js.map +1 -0
  18. package/lib/Bundle/index.d.ts +2 -0
  19. package/lib/Bundle/index.d.ts.map +1 -1
  20. package/lib/Bundle/index.js +2 -0
  21. package/lib/Bundle/index.js.map +1 -1
  22. package/lib/Cli/commands/dev.d.ts +3 -2
  23. package/lib/Cli/commands/dev.d.ts.map +1 -1
  24. package/lib/Cli/commands/dev.js +9 -1
  25. package/lib/Cli/commands/dev.js.map +1 -1
  26. package/lib/Cli/exec.d.ts.map +1 -1
  27. package/lib/Cli/exec.js +2 -1
  28. package/lib/Cli/exec.js.map +1 -1
  29. package/lib/Cloudflare/Local.d.ts +2 -0
  30. package/lib/Cloudflare/Local.d.ts.map +1 -0
  31. package/lib/Cloudflare/Local.js +9 -0
  32. package/lib/Cloudflare/Local.js.map +1 -0
  33. package/lib/Cloudflare/Providers.d.ts.map +1 -1
  34. package/lib/Cloudflare/Providers.js +3 -1
  35. package/lib/Cloudflare/Providers.js.map +1 -1
  36. package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts.map +1 -1
  37. package/lib/Cloudflare/Workers/DurableObjectBridge.js +0 -1
  38. package/lib/Cloudflare/Workers/DurableObjectBridge.js.map +1 -1
  39. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts +22 -3
  40. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
  41. package/lib/Cloudflare/Workers/LocalWorkerProvider.js +413 -21
  42. package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
  43. package/lib/Cloudflare/Workers/Worker.d.ts +6 -19
  44. package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
  45. package/lib/Cloudflare/Workers/Worker.js +3 -4
  46. package/lib/Cloudflare/Workers/Worker.js.map +1 -1
  47. package/lib/Cloudflare/Workers/WorkerBridge.d.ts.map +1 -1
  48. package/lib/Cloudflare/Workers/WorkerBridge.js +3 -5
  49. package/lib/Cloudflare/Workers/WorkerBridge.js.map +1 -1
  50. package/lib/Cloudflare/Workers/WorkerBundle.d.ts +1 -2
  51. package/lib/Cloudflare/Workers/WorkerBundle.d.ts.map +1 -1
  52. package/lib/Cloudflare/Workers/WorkerBundle.js +3 -6
  53. package/lib/Cloudflare/Workers/WorkerBundle.js.map +1 -1
  54. package/lib/Cloudflare/Zaraz/ZarazConfig.d.ts +184 -0
  55. package/lib/Cloudflare/Zaraz/ZarazConfig.d.ts.map +1 -0
  56. package/lib/Cloudflare/Zaraz/ZarazConfig.js +216 -0
  57. package/lib/Cloudflare/Zaraz/ZarazConfig.js.map +1 -0
  58. package/lib/Cloudflare/Zaraz/ZarazEventTypes.d.ts +188 -0
  59. package/lib/Cloudflare/Zaraz/ZarazEventTypes.d.ts.map +1 -0
  60. package/lib/Cloudflare/Zaraz/ZarazEventTypes.js +4 -0
  61. package/lib/Cloudflare/Zaraz/ZarazEventTypes.js.map +1 -0
  62. package/lib/Cloudflare/Zaraz/index.d.ts +3 -0
  63. package/lib/Cloudflare/Zaraz/index.d.ts.map +1 -0
  64. package/lib/Cloudflare/Zaraz/index.js +2 -0
  65. package/lib/Cloudflare/Zaraz/index.js.map +1 -0
  66. package/lib/Cloudflare/index.d.ts +1 -0
  67. package/lib/Cloudflare/index.d.ts.map +1 -1
  68. package/lib/Cloudflare/index.js +1 -0
  69. package/lib/Cloudflare/index.js.map +1 -1
  70. package/lib/Local/RpcProvider.d.ts +54 -0
  71. package/lib/Local/RpcProvider.d.ts.map +1 -0
  72. package/lib/Local/RpcProvider.js +57 -0
  73. package/lib/Local/RpcProvider.js.map +1 -0
  74. package/lib/Local/RpcProviderProxy.d.ts +19 -0
  75. package/lib/Local/RpcProviderProxy.d.ts.map +1 -0
  76. package/lib/Local/RpcProviderProxy.js +48 -0
  77. package/lib/Local/RpcProviderProxy.js.map +1 -0
  78. package/lib/Local/RpcSerialization.d.ts +34 -0
  79. package/lib/Local/RpcSerialization.d.ts.map +1 -0
  80. package/lib/Local/RpcSerialization.js +120 -0
  81. package/lib/Local/RpcSerialization.js.map +1 -0
  82. package/lib/Local/RpcServer.d.ts +63 -0
  83. package/lib/Local/RpcServer.d.ts.map +1 -0
  84. package/lib/Local/RpcServer.js +72 -0
  85. package/lib/Local/RpcServer.js.map +1 -0
  86. package/lib/Local/RpcServerBun.d.ts +3 -0
  87. package/lib/Local/RpcServerBun.d.ts.map +1 -0
  88. package/lib/Local/RpcServerBun.js +47 -0
  89. package/lib/Local/RpcServerBun.js.map +1 -0
  90. package/lib/Local/RpcServerEnvironment.d.ts +20 -0
  91. package/lib/Local/RpcServerEnvironment.d.ts.map +1 -0
  92. package/lib/Local/RpcServerEnvironment.js +22 -0
  93. package/lib/Local/RpcServerEnvironment.js.map +1 -0
  94. package/lib/Local/RpcServerNode.d.ts +3 -0
  95. package/lib/Local/RpcServerNode.d.ts.map +1 -0
  96. package/lib/Local/RpcServerNode.js +43 -0
  97. package/lib/Local/RpcServerNode.js.map +1 -0
  98. package/lib/Local/RpcServerSession.d.ts +27 -0
  99. package/lib/Local/RpcServerSession.d.ts.map +1 -0
  100. package/lib/Local/RpcServerSession.js +68 -0
  101. package/lib/Local/RpcServerSession.js.map +1 -0
  102. package/lib/Local/RpcSpawner.d.ts +21 -0
  103. package/lib/Local/RpcSpawner.d.ts.map +1 -0
  104. package/lib/Local/RpcSpawner.js +132 -0
  105. package/lib/Local/RpcSpawner.js.map +1 -0
  106. package/lib/Util/PlatformServices.d.ts +7 -0
  107. package/lib/Util/PlatformServices.d.ts.map +1 -1
  108. package/lib/Util/PlatformServices.js +22 -11
  109. package/lib/Util/PlatformServices.js.map +1 -1
  110. package/lib/Util/camel.d.ts.map +1 -1
  111. package/lib/Util/camel.js +2 -3
  112. package/lib/Util/camel.js.map +1 -1
  113. package/lib/Util/data.d.ts +5 -0
  114. package/lib/Util/data.d.ts.map +1 -1
  115. package/lib/Util/data.js +29 -0
  116. package/lib/Util/data.js.map +1 -1
  117. package/lib/Util/stable.d.ts.map +1 -1
  118. package/lib/Util/stable.js +3 -6
  119. package/lib/Util/stable.js.map +1 -1
  120. package/lib/tsconfig.test.tsbuildinfo +1 -1
  121. package/package.json +12 -14
  122. package/src/Auth/Lock.ts +2 -2
  123. package/src/Bundle/Bundle.ts +35 -13
  124. package/src/Bundle/BundleAnalyzerPlugin.ts +33 -0
  125. package/src/Bundle/RawPlugin.ts +82 -0
  126. package/src/Bundle/index.ts +2 -0
  127. package/src/Cli/commands/dev.ts +51 -37
  128. package/src/Cli/exec.ts +12 -7
  129. package/src/Cloudflare/Local.ts +23 -0
  130. package/src/Cloudflare/Providers.ts +3 -0
  131. package/src/Cloudflare/Workers/DurableObjectBridge.ts +0 -1
  132. package/src/Cloudflare/Workers/LocalWorkerProvider.ts +547 -21
  133. package/src/Cloudflare/Workers/Worker.ts +11 -22
  134. package/src/Cloudflare/Workers/WorkerBridge.ts +3 -5
  135. package/src/Cloudflare/Workers/WorkerBundle.ts +4 -8
  136. package/src/Cloudflare/Zaraz/ZarazConfig.ts +403 -0
  137. package/src/Cloudflare/Zaraz/ZarazEventTypes.ts +257 -0
  138. package/src/Cloudflare/Zaraz/index.ts +25 -0
  139. package/src/Cloudflare/index.ts +1 -0
  140. package/src/Local/RpcProvider.ts +102 -0
  141. package/src/Local/RpcProviderProxy.ts +86 -0
  142. package/src/Local/RpcSerialization.ts +225 -0
  143. package/src/Local/RpcServer.ts +138 -0
  144. package/src/Local/RpcServerBun.ts +59 -0
  145. package/src/Local/RpcServerEnvironment.ts +55 -0
  146. package/src/Local/RpcServerNode.ts +58 -0
  147. package/src/Local/RpcServerSession.ts +87 -0
  148. package/src/Local/RpcSpawner.ts +196 -0
  149. package/src/Util/PlatformServices.ts +41 -27
  150. package/src/Util/camel.ts +3 -5
  151. package/src/Util/data.ts +38 -0
  152. package/src/Util/stable.ts +4 -10
  153. package/lib/Cloudflare/Local/Sidecar.d.ts +0 -63
  154. package/lib/Cloudflare/Local/Sidecar.d.ts.map +0 -1
  155. package/lib/Cloudflare/Local/Sidecar.js +0 -50
  156. package/lib/Cloudflare/Local/Sidecar.js.map +0 -1
  157. package/lib/Cloudflare/Local/SidecarHandlers.d.ts +0 -8
  158. package/lib/Cloudflare/Local/SidecarHandlers.d.ts.map +0 -1
  159. package/lib/Cloudflare/Local/SidecarHandlers.js +0 -381
  160. package/lib/Cloudflare/Local/SidecarHandlers.js.map +0 -1
  161. package/lib/Cloudflare/Local/SidecarServer.d.ts +0 -2
  162. package/lib/Cloudflare/Local/SidecarServer.d.ts.map +0 -1
  163. package/lib/Cloudflare/Local/SidecarServer.js +0 -34
  164. package/lib/Cloudflare/Local/SidecarServer.js.map +0 -1
  165. package/lib/Sidecar/Lock.d.ts +0 -23
  166. package/lib/Sidecar/Lock.d.ts.map +0 -1
  167. package/lib/Sidecar/Lock.js +0 -39
  168. package/lib/Sidecar/Lock.js.map +0 -1
  169. package/lib/Sidecar/RpcClient.d.ts +0 -15
  170. package/lib/Sidecar/RpcClient.d.ts.map +0 -1
  171. package/lib/Sidecar/RpcClient.js +0 -78
  172. package/lib/Sidecar/RpcClient.js.map +0 -1
  173. package/lib/Sidecar/RpcHandler.d.ts +0 -51
  174. package/lib/Sidecar/RpcHandler.d.ts.map +0 -1
  175. package/lib/Sidecar/RpcHandler.js +0 -87
  176. package/lib/Sidecar/RpcHandler.js.map +0 -1
  177. package/lib/Sidecar/RpcPaths.d.ts +0 -19
  178. package/lib/Sidecar/RpcPaths.d.ts.map +0 -1
  179. package/lib/Sidecar/RpcPaths.js +0 -31
  180. package/lib/Sidecar/RpcPaths.js.map +0 -1
  181. package/lib/Sidecar/RpcServer.d.ts +0 -30
  182. package/lib/Sidecar/RpcServer.d.ts.map +0 -1
  183. package/lib/Sidecar/RpcServer.js +0 -105
  184. package/lib/Sidecar/RpcServer.js.map +0 -1
  185. package/lib/Sidecar/RpcServerBun.d.ts +0 -4
  186. package/lib/Sidecar/RpcServerBun.d.ts.map +0 -1
  187. package/lib/Sidecar/RpcServerBun.js +0 -28
  188. package/lib/Sidecar/RpcServerBun.js.map +0 -1
  189. package/lib/Sidecar/RpcServerNode.d.ts +0 -4
  190. package/lib/Sidecar/RpcServerNode.d.ts.map +0 -1
  191. package/lib/Sidecar/RpcServerNode.js +0 -39
  192. package/lib/Sidecar/RpcServerNode.js.map +0 -1
  193. package/lib/Sidecar/RpcServices.d.ts +0 -6
  194. package/lib/Sidecar/RpcServices.d.ts.map +0 -1
  195. package/lib/Sidecar/RpcServices.js +0 -29
  196. package/lib/Sidecar/RpcServices.js.map +0 -1
  197. package/src/Cloudflare/Local/Sidecar.ts +0 -81
  198. package/src/Cloudflare/Local/SidecarHandlers.ts +0 -491
  199. package/src/Cloudflare/Local/SidecarServer.ts +0 -70
  200. package/src/Sidecar/Lock.ts +0 -66
  201. package/src/Sidecar/RpcClient.ts +0 -142
  202. package/src/Sidecar/RpcHandler.ts +0 -205
  203. package/src/Sidecar/RpcPaths.ts +0 -39
  204. package/src/Sidecar/RpcServer.ts +0 -153
  205. package/src/Sidecar/RpcServerBun.ts +0 -41
  206. package/src/Sidecar/RpcServerNode.ts +0 -51
  207. package/src/Sidecar/RpcServices.ts +0 -48
@@ -6,14 +6,12 @@ import type * as Path from "effect/Path";
6
6
  import * as Stream from "effect/Stream";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import type * as rolldown from "rolldown";
9
- import Sonda from "sonda/rolldown";
10
9
  import * as Bundle from "../../Bundle/Bundle.ts";
11
10
  import { findCwdForBundle } from "../../Bundle/TempRoot.ts";
12
11
  import {
13
12
  isDurableObjectExport,
14
13
  type DurableObjectExport,
15
14
  } from "./DurableObjectNamespace.ts";
16
- import type { WorkerProps } from "./Worker.ts";
17
15
  import { isWorkflowExport, type WorkflowExport } from "./Workflow.ts";
18
16
 
19
17
  export interface WorkerBundleOptions {
@@ -32,7 +30,7 @@ export interface WorkerBundleOptions {
32
30
  exports: Record<string, DurableObjectExport | WorkflowExport>;
33
31
  };
34
32
  stack: { name: string; stage: string };
35
- userOptions: WorkerProps["build"] | undefined;
33
+ extraOptions: Bundle.BundleExtraOptions | undefined;
36
34
  }
37
35
 
38
36
  export const WorkerBundle = Effect.gen(function* () {
@@ -74,11 +72,12 @@ export const WorkerBundle = Effect.gen(function* () {
74
72
  ),
75
73
  ]
76
74
  : undefined,
77
- ...(options.userOptions?.metafile ? [Sonda({ open: false })] : []),
78
75
  ],
79
76
  checks: {
80
77
  // Suppress unresolved import warnings for unrelated AWS packages
81
78
  unresolvedImport: false,
79
+ // Suppress warning caused by static import of `@effect/platform-node/NodeServices` in `WorkerBridge.ts`
80
+ ineffectiveDynamicImport: false,
82
81
  },
83
82
  };
84
83
  const outputOptions: rolldown.OutputOptions = {
@@ -88,10 +87,7 @@ export const WorkerBundle = Effect.gen(function* () {
88
87
  keepNames: true,
89
88
  dir: `.alchemy/bundles/${options.id}`,
90
89
  };
91
- const extraOptions: Bundle.BundleExtraOptions = {
92
- pure: options.userOptions?.pure,
93
- };
94
- return { inputOptions, outputOptions, extraOptions };
90
+ return { inputOptions, outputOptions, extraOptions: options.extraOptions };
95
91
  });
96
92
 
97
93
  const sanitizeMain = (main: string) =>
@@ -0,0 +1,403 @@
1
+ import * as zaraz from "@distilled.cloud/cloudflare/zaraz";
2
+ import * as Effect from "effect/Effect";
3
+ import { deepEqual, isResolved } from "../../Diff.ts";
4
+ import * as Provider from "../../Provider.ts";
5
+ import { Resource } from "../../Resource.ts";
6
+ import {
7
+ stripNullFields,
8
+ stripUndefinedFields,
9
+ unwrapRedacted,
10
+ } from "../../Util/data.ts";
11
+ import { CloudflareEnvironment } from "../CloudflareEnvironment.ts";
12
+ import type { Providers } from "../Providers.ts";
13
+ import { resolveZoneId, type ZoneReference } from "../Zone.ts";
14
+ import { defineZarazEvents } from "./ZarazEventTypes.ts";
15
+
16
+ export type ZarazWorkflow = zaraz.GetWorkflowResponse;
17
+ export type ZarazSettings = zaraz.PutConfigRequest["settings"];
18
+ export type ZarazAnalytics = NonNullable<zaraz.PutConfigRequest["analytics"]>;
19
+ export type ZarazConsent = NonNullable<zaraz.PutConfigRequest["consent"]>;
20
+
21
+ export type ZarazConfigProps = {
22
+ /**
23
+ * Zone whose Zaraz config should be managed. Accepts a zone id, a zone name
24
+ * (`example.com`), or a `{ zoneId, name? }` object.
25
+ */
26
+ zone: ZoneReference;
27
+ /**
28
+ * Data layer compatibility mode.
29
+ */
30
+ dataLayer?: boolean;
31
+ /**
32
+ * Key used for Zaraz debug mode. Defaults to the current zone config value.
33
+ */
34
+ debugKey?: string;
35
+ /**
36
+ * Zaraz settings to merge into the current zone config.
37
+ */
38
+ settings?: Partial<ZarazSettings>;
39
+ /**
40
+ * Zaraz tools keyed by tool id. When omitted, existing tools are retained.
41
+ */
42
+ tools?: Record<string, unknown>;
43
+ /**
44
+ * Zaraz triggers keyed by trigger id. When omitted, existing triggers are
45
+ * retained.
46
+ */
47
+ triggers?: Record<string, unknown>;
48
+ /**
49
+ * Zaraz variables keyed by variable id. When omitted, existing variables are
50
+ * retained. Secret variable values are not returned by Cloudflare reads.
51
+ */
52
+ variables?: Record<string, unknown>;
53
+ /**
54
+ * Cloudflare Monitoring settings.
55
+ */
56
+ analytics?: ZarazAnalytics;
57
+ /**
58
+ * Zaraz consent management configuration.
59
+ */
60
+ consent?: ZarazConsent;
61
+ /**
62
+ * Single Page Application support.
63
+ */
64
+ historyChange?: boolean;
65
+ /**
66
+ * Zaraz workflow mode. When omitted, the current workflow is retained.
67
+ */
68
+ workflow?: ZarazWorkflow;
69
+ /**
70
+ * Whether destroy should restore Cloudflare's default Zaraz config.
71
+ *
72
+ * By default, destroy only removes this resource from Alchemy state and keeps
73
+ * the current zone-level Zaraz config intact.
74
+ * If `workflow` is set, `delete: true` restores the workflow to real-time
75
+ * mode, Cloudflare's default.
76
+ * @default false
77
+ */
78
+ delete?: boolean;
79
+ };
80
+
81
+ export type ZarazConfig = Resource<
82
+ "Cloudflare.ZarazConfig",
83
+ ZarazConfigProps,
84
+ ZarazConfigAttributes,
85
+ never,
86
+ Providers
87
+ >;
88
+
89
+ export type ZarazConfigAttributes = {
90
+ /**
91
+ * Cloudflare zone id.
92
+ */
93
+ zoneId: string;
94
+ /**
95
+ * Data layer compatibility mode.
96
+ */
97
+ dataLayer: boolean;
98
+ /**
99
+ * Key used for Zaraz debug mode.
100
+ */
101
+ debugKey: string;
102
+ /**
103
+ * General Zaraz settings.
104
+ */
105
+ settings: zaraz.GetConfigResponse["settings"];
106
+ /**
107
+ * Zaraz tools keyed by tool id.
108
+ */
109
+ tools: Record<string, unknown>;
110
+ /**
111
+ * Zaraz triggers keyed by trigger id.
112
+ */
113
+ triggers: Record<string, unknown>;
114
+ /**
115
+ * Zaraz variables keyed by variable id.
116
+ */
117
+ variables: Record<string, unknown>;
118
+ /**
119
+ * Zaraz internal version of the config.
120
+ */
121
+ zarazVersion: number;
122
+ /**
123
+ * Cloudflare Monitoring settings.
124
+ */
125
+ analytics?: zaraz.GetConfigResponse["analytics"];
126
+ /**
127
+ * Consent management configuration.
128
+ */
129
+ consent?: zaraz.GetConfigResponse["consent"];
130
+ /**
131
+ * Single Page Application support.
132
+ */
133
+ historyChange?: boolean | null;
134
+ /**
135
+ * Current Zaraz workflow mode.
136
+ */
137
+ workflow: ZarazWorkflow;
138
+ };
139
+
140
+ /**
141
+ * A Cloudflare Zaraz zone configuration.
142
+ *
143
+ * Cloudflare Zaraz is an edge-managed third-party tool manager and analytics
144
+ * event pipeline. See the
145
+ * {@link https://developers.cloudflare.com/zaraz/ | Cloudflare Zaraz docs} and
146
+ * {@link https://developers.cloudflare.com/zaraz/web-api/ | Web API docs}.
147
+ *
148
+ * Zaraz is a zone-level singleton. This resource reconciles the current zone
149
+ * config and workflow to the desired values while retaining existing settings
150
+ * for fields omitted from props.
151
+ *
152
+ * Destroy keeps the current Zaraz config by default to avoid wiping unrelated
153
+ * zone-level analytics setup. Set `delete: true` to restore Cloudflare's
154
+ * default Zaraz config on destroy.
155
+ *
156
+ * @section Managing Zaraz
157
+ * @example Enable data layer compatibility
158
+ * ```typescript
159
+ * const zaraz = yield* Cloudflare.ZarazConfig("Analytics", {
160
+ * zone: "example.com",
161
+ * dataLayer: true,
162
+ * });
163
+ * ```
164
+ *
165
+ * @example Update Zaraz settings
166
+ * ```typescript
167
+ * const zaraz = yield* Cloudflare.ZarazConfig("Analytics", {
168
+ * zone: "example.com",
169
+ * settings: {
170
+ * autoInjectScript: true,
171
+ * hideIPAddress: true,
172
+ * },
173
+ * });
174
+ * ```
175
+ *
176
+ * @example Enable preview workflow
177
+ * ```typescript
178
+ * const zaraz = yield* Cloudflare.ZarazConfig("Analytics", {
179
+ * zone: "example.com",
180
+ * workflow: "preview",
181
+ * });
182
+ * ```
183
+ */
184
+ export const ZarazConfig = Object.assign(
185
+ Resource<ZarazConfig>("Cloudflare.ZarazConfig"),
186
+ {
187
+ /**
188
+ * Define a type-only contract for the events sent through this Zaraz config.
189
+ *
190
+ * The returned value carries only types. Browser code should use
191
+ * Cloudflare's injected `window.zaraz` API at runtime.
192
+ */
193
+ events: defineZarazEvents,
194
+ },
195
+ );
196
+
197
+ type ConfigResponse =
198
+ | zaraz.GetConfigResponse
199
+ | zaraz.PutConfigResponse
200
+ | zaraz.GetDefaultResponse;
201
+
202
+ const toAttributes = (
203
+ zoneId: string,
204
+ config: ConfigResponse,
205
+ workflow: ZarazWorkflow,
206
+ ): ZarazConfig["Attributes"] => ({
207
+ zoneId,
208
+ dataLayer: config.dataLayer,
209
+ debugKey: config.debugKey,
210
+ settings: config.settings,
211
+ tools: config.tools,
212
+ triggers: config.triggers,
213
+ variables: config.variables,
214
+ zarazVersion: config.zarazVersion,
215
+ analytics: config.analytics,
216
+ consent: config.consent,
217
+ historyChange: config.historyChange,
218
+ workflow,
219
+ });
220
+
221
+ const fromAttributes = (attrs: ZarazConfigAttributes): ConfigResponse => ({
222
+ dataLayer: attrs.dataLayer,
223
+ debugKey: attrs.debugKey,
224
+ settings: attrs.settings,
225
+ tools: attrs.tools,
226
+ triggers: attrs.triggers,
227
+ variables: attrs.variables,
228
+ zarazVersion: attrs.zarazVersion,
229
+ analytics: attrs.analytics,
230
+ consent: attrs.consent,
231
+ historyChange: attrs.historyChange,
232
+ });
233
+
234
+ const desiredConfig = (
235
+ observed: ConfigResponse,
236
+ props: ZarazConfigProps,
237
+ ): ConfigResponse => ({
238
+ dataLayer: props.dataLayer ?? observed.dataLayer,
239
+ debugKey: props.debugKey ?? observed.debugKey,
240
+ settings: {
241
+ ...stripNullFields(observed.settings),
242
+ ...props.settings,
243
+ },
244
+ tools: props.tools ?? observed.tools,
245
+ triggers: props.triggers ?? observed.triggers,
246
+ variables: props.variables ?? observed.variables,
247
+ zarazVersion: observed.zarazVersion,
248
+ analytics:
249
+ props.analytics ??
250
+ (observed.analytics ? stripNullFields(observed.analytics) : undefined),
251
+ consent:
252
+ props.consent ??
253
+ (observed.consent ? stripNullFields(observed.consent) : undefined),
254
+ historyChange: props.historyChange ?? observed.historyChange,
255
+ });
256
+
257
+ const toPutConfig = (
258
+ zoneId: string,
259
+ config: ConfigResponse,
260
+ ): zaraz.PutConfigRequest =>
261
+ stripUndefinedFields({
262
+ zoneId,
263
+ dataLayer: config.dataLayer,
264
+ debugKey: config.debugKey,
265
+ settings: stripNullFields(config.settings) as ZarazSettings,
266
+ tools: unwrapRedacted(config.tools) as Record<string, unknown>,
267
+ triggers: unwrapRedacted(config.triggers) as Record<string, unknown>,
268
+ variables: unwrapRedacted(config.variables) as Record<string, unknown>,
269
+ zarazVersion: config.zarazVersion,
270
+ analytics: config.analytics
271
+ ? (stripNullFields(config.analytics) as ZarazAnalytics)
272
+ : undefined,
273
+ consent: config.consent
274
+ ? (stripNullFields(config.consent) as ZarazConsent)
275
+ : undefined,
276
+ historyChange: config.historyChange ?? undefined,
277
+ }) as zaraz.PutConfigRequest;
278
+
279
+ const comparableConfig = (config: ConfigResponse) => {
280
+ const { zoneId: _, ...request } = toPutConfig("", config);
281
+ return request;
282
+ };
283
+
284
+ const configForCompare = (
285
+ observed: ConfigResponse,
286
+ oldProps: ZarazConfigProps | undefined,
287
+ props: ZarazConfigProps,
288
+ ): ConfigResponse => {
289
+ if (
290
+ props.variables === undefined ||
291
+ oldProps?.variables === undefined ||
292
+ !deepEqual(oldProps.variables, props.variables)
293
+ ) {
294
+ return observed;
295
+ }
296
+
297
+ // If props already requested these variables in the previous state, compare
298
+ // against props instead of Cloudflare's readback because secret variable
299
+ // values are intentionally write-only in the Zaraz API.
300
+ return {
301
+ ...observed,
302
+ variables: props.variables,
303
+ };
304
+ };
305
+
306
+ export const ZarazConfigProvider = () =>
307
+ Provider.effect(
308
+ ZarazConfig,
309
+ Effect.gen(function* () {
310
+ const { accountId } = yield* CloudflareEnvironment;
311
+ const getConfig = yield* zaraz.getConfig;
312
+ const putConfig = yield* zaraz.putConfig;
313
+ const getDefault = yield* zaraz.getDefault;
314
+ const getWorkflow = yield* zaraz.getWorkflow;
315
+ // Cloudflare's OpenAPI operation name is `putZaraz`, but the endpoint
316
+ // updates the zone's Zaraz workflow mode.
317
+ const putWorkflow = yield* zaraz.putZaraz;
318
+
319
+ const resolve = (zone: ZoneReference) =>
320
+ resolveZoneId({
321
+ accountId,
322
+ zone,
323
+ hostname: typeof zone === "string" ? zone : (zone.name ?? ""),
324
+ });
325
+
326
+ const observe = (zoneId: string) =>
327
+ Effect.all({
328
+ config: getConfig({ zoneId }),
329
+ workflow: getWorkflow({ zoneId }),
330
+ }).pipe(
331
+ Effect.map(({ config, workflow }) =>
332
+ toAttributes(zoneId, config, workflow),
333
+ ),
334
+ );
335
+
336
+ return {
337
+ stables: ["zoneId"],
338
+ diff: Effect.fn(function* ({ olds, news, output }) {
339
+ if (!output) return undefined;
340
+ if (!isResolved(news)) return undefined;
341
+
342
+ const zoneId = yield* resolve(news.zone);
343
+ if (zoneId !== output.zoneId) {
344
+ return { action: "replace" } as const;
345
+ }
346
+
347
+ const outputConfig = fromAttributes(output);
348
+ const comparableOutput = configForCompare(outputConfig, olds, news);
349
+ const desired = desiredConfig(outputConfig, news);
350
+ const desiredWorkflow = news.workflow ?? output.workflow;
351
+ if (
352
+ desiredWorkflow !== output.workflow ||
353
+ !deepEqual(
354
+ comparableConfig(comparableOutput),
355
+ comparableConfig(desired),
356
+ )
357
+ ) {
358
+ return { action: "update" } as const;
359
+ }
360
+ }),
361
+ read: Effect.fn(function* ({ olds, output }) {
362
+ const zoneId =
363
+ output?.zoneId ?? (olds ? yield* resolve(olds.zone) : undefined);
364
+ if (!zoneId) return undefined;
365
+ return yield* observe(zoneId);
366
+ }),
367
+ reconcile: Effect.fn(function* ({ news, output }) {
368
+ const zoneId = output?.zoneId ?? (yield* resolve(news.zone));
369
+ const observed = yield* observe(zoneId);
370
+ const observedConfig = fromAttributes(observed);
371
+ const desired = desiredConfig(observedConfig, news);
372
+ const desiredWorkflow = news.workflow ?? observed.workflow;
373
+
374
+ const updatedConfig = deepEqual(
375
+ comparableConfig(observedConfig),
376
+ comparableConfig(desired),
377
+ )
378
+ ? observedConfig
379
+ : yield* putConfig(toPutConfig(zoneId, desired));
380
+ const updatedWorkflow =
381
+ desiredWorkflow === observed.workflow
382
+ ? observed.workflow
383
+ : yield* putWorkflow({
384
+ zoneId,
385
+ workflow: desiredWorkflow,
386
+ });
387
+
388
+ return toAttributes(zoneId, updatedConfig, updatedWorkflow);
389
+ }),
390
+ delete: Effect.fn(function* ({ output, olds }) {
391
+ if (olds.delete !== true) return;
392
+ const defaults = yield* getDefault({ zoneId: output.zoneId });
393
+ yield* putConfig(toPutConfig(output.zoneId, defaults));
394
+ if (olds.workflow !== undefined) {
395
+ yield* putWorkflow({
396
+ zoneId: output.zoneId,
397
+ workflow: "realtime",
398
+ });
399
+ }
400
+ }),
401
+ };
402
+ }),
403
+ );
@@ -0,0 +1,257 @@
1
+ import type { Simplify } from "effect/Types";
2
+
3
+ /**
4
+ * Type-only helpers for Cloudflare Zaraz events.
5
+ *
6
+ * These declarations are not an Alchemy EventSource resource and do not send
7
+ * events at runtime. They only model the browser-side `window.zaraz` API and
8
+ * Zaraz HTTP event payloads so application code can share an event contract
9
+ * with infrastructure code.
10
+ */
11
+
12
+ /**
13
+ * A map of Zaraz event names to the properties accepted by each event.
14
+ *
15
+ * Use `undefined` for events that do not accept custom properties.
16
+ */
17
+ export type ZarazEventMap = Record<string, object | undefined>;
18
+
19
+ type RequireAtLeastOne<T extends object> = {
20
+ readonly [K in keyof T]-?: Required<Pick<T, K>> & Partial<Omit<T, K>>;
21
+ }[keyof T];
22
+
23
+ /**
24
+ * Product properties supported by Cloudflare Zaraz ecommerce events.
25
+ */
26
+ export type ZarazEcommerceProduct = {
27
+ readonly product_id?: string;
28
+ readonly sku?: string;
29
+ readonly category?: string;
30
+ readonly name?: string;
31
+ readonly brand?: string;
32
+ readonly variant?: string;
33
+ readonly price?: number;
34
+ readonly currency?: string;
35
+ readonly value?: number;
36
+ readonly quantity?: number;
37
+ readonly coupon?: string;
38
+ readonly position?: number;
39
+ };
40
+
41
+ /**
42
+ * Order and checkout properties supported by Cloudflare Zaraz ecommerce events.
43
+ */
44
+ export type ZarazEcommerceOrder = {
45
+ readonly checkout_id?: string;
46
+ readonly order_id?: string;
47
+ readonly affiliation?: string;
48
+ readonly total?: number;
49
+ readonly revenue?: number;
50
+ readonly shipping?: number;
51
+ readonly tax?: number;
52
+ readonly discount?: number;
53
+ readonly coupon?: string;
54
+ readonly currency?: string;
55
+ readonly products?: readonly ZarazEcommerceProduct[];
56
+ };
57
+
58
+ export type ZarazEcommercePromotion = {
59
+ readonly creative?: string;
60
+ };
61
+
62
+ export type ZarazEcommerceSearch = {
63
+ readonly query?: string;
64
+ };
65
+
66
+ export type ZarazEcommerceCheckoutStep = {
67
+ readonly step?: number;
68
+ };
69
+
70
+ export type ZarazEcommercePayment = {
71
+ readonly payment_type?: string;
72
+ };
73
+
74
+ export type ZarazEcommerceRefund = {
75
+ readonly amount?: number;
76
+ readonly amount_refunded?: number;
77
+ readonly currency?: string;
78
+ readonly refund_reason?: string;
79
+ };
80
+
81
+ /**
82
+ * Standard ecommerce events supported by Cloudflare's `zaraz.ecommerce()` API.
83
+ */
84
+ export type ZarazEcommerceEvents = {
85
+ readonly "Product List Viewed": RequireAtLeastOne<
86
+ Pick<ZarazEcommerceOrder, "products">
87
+ >;
88
+ readonly "Products Searched": RequireAtLeastOne<ZarazEcommerceSearch>;
89
+ readonly "Product Clicked": RequireAtLeastOne<ZarazEcommerceProduct>;
90
+ readonly "Product Added": RequireAtLeastOne<ZarazEcommerceProduct>;
91
+ readonly "Product Added to Wishlist": RequireAtLeastOne<ZarazEcommerceProduct>;
92
+ readonly "Product Removed": RequireAtLeastOne<ZarazEcommerceProduct>;
93
+ readonly "Product Viewed": RequireAtLeastOne<ZarazEcommerceProduct>;
94
+ readonly "Cart Viewed": RequireAtLeastOne<ZarazEcommerceOrder>;
95
+ readonly "Checkout Started": RequireAtLeastOne<ZarazEcommerceOrder>;
96
+ readonly "Checkout Step Viewed": RequireAtLeastOne<ZarazEcommerceCheckoutStep>;
97
+ readonly "Checkout Step Completed": RequireAtLeastOne<ZarazEcommerceCheckoutStep>;
98
+ readonly "Payment Info Entered": RequireAtLeastOne<ZarazEcommercePayment>;
99
+ readonly "Order Completed": RequireAtLeastOne<ZarazEcommerceOrder>;
100
+ readonly "Order Updated": RequireAtLeastOne<ZarazEcommerceOrder>;
101
+ readonly "Order Refunded": RequireAtLeastOne<
102
+ ZarazEcommerceOrder & ZarazEcommerceRefund
103
+ >;
104
+ readonly "Order Cancelled": RequireAtLeastOne<ZarazEcommerceOrder>;
105
+ readonly "Clicked Promotion": RequireAtLeastOne<ZarazEcommercePromotion>;
106
+ readonly "Viewed Promotion": RequireAtLeastOne<ZarazEcommercePromotion>;
107
+ readonly "Shipping Info Entered": RequireAtLeastOne<ZarazEcommerceOrder>;
108
+ };
109
+
110
+ declare const ZarazEventContractTypeId: unique symbol;
111
+
112
+ /**
113
+ * Type-only contract for an application's Zaraz events.
114
+ *
115
+ * The runtime value is intentionally empty. Application browser code should
116
+ * import derived types and call Cloudflare's injected `window.zaraz` API rather
117
+ * than importing Alchemy runtime code into the client bundle.
118
+ */
119
+ export type ZarazEventContract<
120
+ Events extends ZarazEventMap,
121
+ EcommerceEvents extends ZarazEventMap = {},
122
+ > = {
123
+ readonly [ZarazEventContractTypeId]: {
124
+ readonly events: Events;
125
+ readonly ecommerceEvents: EcommerceEvents;
126
+ };
127
+ };
128
+
129
+ /**
130
+ * Define an application's Zaraz event contract.
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * const zaraz = Cloudflare.ZarazConfig.events<{
135
+ * Login: { method: "google" | "email" | "email-link" };
136
+ * "Button Clicked": { button_label: string; context?: string };
137
+ * }>();
138
+ *
139
+ * const zarazWithEcommerce = Cloudflare.ZarazConfig.events<{
140
+ * Login: { method: "google" | "email" | "email-link" };
141
+ * }>({ ecommerce: true });
142
+ *
143
+ * export type AppZarazEvents = Cloudflare.InferZarazEvents<typeof zaraz>;
144
+ * ```
145
+ */
146
+ export function defineZarazEvents<const Events extends ZarazEventMap>(options: {
147
+ readonly ecommerce: true;
148
+ }): ZarazEventContract<Events, ZarazEcommerceEvents>;
149
+ export function defineZarazEvents<
150
+ const Events extends ZarazEventMap,
151
+ >(options?: { readonly ecommerce?: false }): ZarazEventContract<Events, {}>;
152
+ export function defineZarazEvents<
153
+ const Events extends ZarazEventMap,
154
+ >(_options?: {
155
+ readonly ecommerce?: boolean;
156
+ }): ZarazEventContract<Events, ZarazEventMap> {
157
+ return {} as ZarazEventContract<Events, {}>;
158
+ }
159
+
160
+ export type InferZarazEvents<Contract> =
161
+ Contract extends ZarazEventContract<infer Events, ZarazEventMap>
162
+ ? Events
163
+ : never;
164
+
165
+ export type InferZarazEcommerceEvents<Contract> =
166
+ Contract extends ZarazEventContract<ZarazEventMap, infer EcommerceEvents>
167
+ ? EcommerceEvents
168
+ : never;
169
+
170
+ export type ZarazEventName<Events extends ZarazEventMap> = Extract<
171
+ keyof Events,
172
+ string
173
+ >;
174
+
175
+ export type ZarazEventProperties<
176
+ Events extends ZarazEventMap,
177
+ Name extends ZarazEventName<Events>,
178
+ > = Events[Name];
179
+
180
+ type ZarazEventPropertiesArgs<Properties> = [Properties] extends [undefined]
181
+ ? [properties?: undefined]
182
+ : undefined extends Properties
183
+ ? [properties?: Exclude<Properties, undefined>]
184
+ : [properties: Properties];
185
+
186
+ /**
187
+ * Type for Cloudflare's injected `window.zaraz.track` function.
188
+ */
189
+ export type ZarazTrack<Events extends ZarazEventMap> = <
190
+ const Name extends ZarazEventName<Events>,
191
+ >(
192
+ eventName: Name,
193
+ ...args: ZarazEventPropertiesArgs<Events[Name]>
194
+ ) => Promise<void>;
195
+
196
+ /**
197
+ * Type for Cloudflare's injected `window.zaraz.ecommerce` function.
198
+ */
199
+ export type ZarazEcommerce<Events extends ZarazEventMap> = ZarazTrack<Events>;
200
+
201
+ export type ZarazSetScope = "page" | "session" | "persist";
202
+
203
+ /**
204
+ * Type for Cloudflare's injected browser-side Zaraz API.
205
+ */
206
+ export type ZarazWebApi<
207
+ Events extends ZarazEventMap,
208
+ EcommerceEvents extends ZarazEventMap = {},
209
+ > = {
210
+ set: (
211
+ name: string,
212
+ value: unknown,
213
+ options?: { scope?: ZarazSetScope },
214
+ ) => void;
215
+ track: ZarazTrack<Events>;
216
+ ecommerce: ZarazEcommerce<EcommerceEvents>;
217
+ preview: (debugKey: string) => Promise<void>;
218
+ debug: (debugKey: string) => Promise<void>;
219
+ showConsentModal: () => Promise<void>;
220
+ pageVariables: Record<string, unknown>;
221
+ };
222
+
223
+ export type ZarazSystem = {
224
+ cookies?: Record<string, unknown>;
225
+ device?: {
226
+ ip?: string;
227
+ resolution?: string;
228
+ viewport?: string;
229
+ language?: string;
230
+ "user-agent"?: string;
231
+ };
232
+ page?: {
233
+ title?: string;
234
+ url?: string;
235
+ referrer?: string;
236
+ encoding?: string;
237
+ };
238
+ };
239
+
240
+ export type ZarazClientProperties<
241
+ Events extends ZarazEventMap,
242
+ Name extends ZarazEventName<Events> = ZarazEventName<Events>,
243
+ > =
244
+ Name extends ZarazEventName<Events>
245
+ ? Events[Name] extends undefined
246
+ ? { readonly __zarazTrack: Name }
247
+ : Simplify<Events[Name] & { readonly __zarazTrack: Name }>
248
+ : never;
249
+
250
+ export type ZarazHttpEvent<Events extends ZarazEventMap> = {
251
+ readonly client: ZarazClientProperties<Events>;
252
+ readonly system?: ZarazSystem;
253
+ };
254
+
255
+ export type ZarazHttpEventsPayload<Events extends ZarazEventMap> = {
256
+ readonly events: readonly ZarazHttpEvent<Events>[];
257
+ };