@fragno-dev/cloudflare-fragment 0.0.1

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 (64) hide show
  1. package/LICENSE.md +16 -0
  2. package/README.md +92 -0
  3. package/dist/browser/client/react.d.ts +290 -0
  4. package/dist/browser/client/react.d.ts.map +1 -0
  5. package/dist/browser/client/react.js +166 -0
  6. package/dist/browser/client/react.js.map +1 -0
  7. package/dist/browser/client/solid.d.ts +292 -0
  8. package/dist/browser/client/solid.d.ts.map +1 -0
  9. package/dist/browser/client/solid.js +136 -0
  10. package/dist/browser/client/solid.js.map +1 -0
  11. package/dist/browser/client/svelte.d.ts +287 -0
  12. package/dist/browser/client/svelte.d.ts.map +1 -0
  13. package/dist/browser/client/svelte.js +134 -0
  14. package/dist/browser/client/svelte.js.map +1 -0
  15. package/dist/browser/client/vanilla.d.ts +316 -0
  16. package/dist/browser/client/vanilla.d.ts.map +1 -0
  17. package/dist/browser/client/vanilla.js +160 -0
  18. package/dist/browser/client/vanilla.js.map +1 -0
  19. package/dist/browser/client/vue.d.ts +290 -0
  20. package/dist/browser/client/vue.d.ts.map +1 -0
  21. package/dist/browser/client/vue.js +133 -0
  22. package/dist/browser/client/vue.js.map +1 -0
  23. package/dist/browser/client-Bk-J98pf.d.ts +679 -0
  24. package/dist/browser/client-Bk-J98pf.d.ts.map +1 -0
  25. package/dist/browser/index.d.ts +2027 -0
  26. package/dist/browser/index.d.ts.map +1 -0
  27. package/dist/browser/index.js +27 -0
  28. package/dist/browser/index.js.map +1 -0
  29. package/dist/browser/schema-Bt-h9kGf.js +2348 -0
  30. package/dist/browser/schema-Bt-h9kGf.js.map +1 -0
  31. package/dist/node/cloudflare-api.d.ts +106 -0
  32. package/dist/node/cloudflare-api.d.ts.map +1 -0
  33. package/dist/node/cloudflare-api.js +146 -0
  34. package/dist/node/cloudflare-api.js.map +1 -0
  35. package/dist/node/contracts.d.ts +288 -0
  36. package/dist/node/contracts.d.ts.map +1 -0
  37. package/dist/node/contracts.js +66 -0
  38. package/dist/node/contracts.js.map +1 -0
  39. package/dist/node/definition.d.ts +339 -0
  40. package/dist/node/definition.d.ts.map +1 -0
  41. package/dist/node/definition.js +417 -0
  42. package/dist/node/definition.js.map +1 -0
  43. package/dist/node/deployment-tag.d.ts +13 -0
  44. package/dist/node/deployment-tag.d.ts.map +1 -0
  45. package/dist/node/deployment-tag.js +73 -0
  46. package/dist/node/deployment-tag.js.map +1 -0
  47. package/dist/node/index.d.ts +786 -0
  48. package/dist/node/index.d.ts.map +1 -0
  49. package/dist/node/index.js +35 -0
  50. package/dist/node/index.js.map +1 -0
  51. package/dist/node/routes.d.ts +520 -0
  52. package/dist/node/routes.d.ts.map +1 -0
  53. package/dist/node/routes.js +100 -0
  54. package/dist/node/routes.js.map +1 -0
  55. package/dist/node/schema.d.ts +11 -0
  56. package/dist/node/schema.d.ts.map +1 -0
  57. package/dist/node/schema.js +24 -0
  58. package/dist/node/schema.js.map +1 -0
  59. package/dist/node/script-name.d.ts +13 -0
  60. package/dist/node/script-name.d.ts.map +1 -0
  61. package/dist/node/script-name.js +35 -0
  62. package/dist/node/script-name.js.map +1 -0
  63. package/dist/tsconfig.tsbuildinfo +1 -0
  64. package/package.json +98 -0
@@ -0,0 +1,417 @@
1
+ import { createCloudflareApiClient, getCloudflareApiError, reconcileCloudflareWorkerDeployment, resolveCloudflareDispatchNamespaceName } from "./cloudflare-api.js";
2
+ import { SUPPORTED_DEPLOYMENT_FORMAT } from "./contracts.js";
3
+ import { cloudflareSchema } from "./schema.js";
4
+ import { resolveCloudflareScriptName } from "./script-name.js";
5
+ import { defineFragment } from "@fragno-dev/core";
6
+ import { ExponentialBackoffRetryPolicy, withDatabase } from "@fragno-dev/db";
7
+
8
+ //#region src/definition.ts
9
+ const textEncoder = new TextEncoder();
10
+ const hookWriteRetryPolicy = new ExponentialBackoffRetryPolicy({
11
+ maxRetries: 5,
12
+ initialDelayMs: 10,
13
+ maxDelayMs: 250
14
+ });
15
+ const resolveDispatcherConfig = (config) => {
16
+ if ("dispatcher" in config && config.dispatcher !== void 0) return config.dispatcher;
17
+ if ("dispatchNamespace" in config && config.dispatchNamespace !== void 0) return config.dispatchNamespace;
18
+ throw new Error("Cloudflare fragment requires `dispatcher` or `dispatchNamespace` to resolve the dispatch namespace name.");
19
+ };
20
+ const normalizeStringList = (value) => {
21
+ if (!Array.isArray(value)) return [];
22
+ return value.filter((entry) => typeof entry === "string" && entry.length > 0);
23
+ };
24
+ const toIsoDateTime = (value) => {
25
+ return value ? value.toISOString() : null;
26
+ };
27
+ const buildDeploymentSummary = (deployment, appId) => {
28
+ const cloudflare = deployment.cloudflareEtag || deployment.cloudflareModifiedOn ? {
29
+ etag: deployment.cloudflareEtag,
30
+ modifiedOn: deployment.cloudflareModifiedOn
31
+ } : null;
32
+ return {
33
+ id: deployment.id.valueOf(),
34
+ appId,
35
+ scriptName: deployment.scriptName,
36
+ status: deployment.status === "queued" || deployment.status === "deploying" || deployment.status === "succeeded" || deployment.status === "failed" ? deployment.status : "failed",
37
+ format: deployment.format === SUPPORTED_DEPLOYMENT_FORMAT ? SUPPORTED_DEPLOYMENT_FORMAT : "esmodule",
38
+ entrypoint: deployment.entrypoint,
39
+ sourceByteLength: deployment.sourceByteLength,
40
+ compatibilityDate: deployment.compatibilityDate,
41
+ compatibilityFlags: normalizeStringList(deployment.compatibilityFlags),
42
+ attemptCount: deployment.attemptCount,
43
+ queuedAt: deployment.createdAt.toISOString(),
44
+ startedAt: toIsoDateTime(deployment.startedAt),
45
+ completedAt: toIsoDateTime(deployment.completedAt),
46
+ errorCode: deployment.errorCode,
47
+ errorMessage: deployment.errorMessage,
48
+ cloudflare,
49
+ createdAt: deployment.createdAt.toISOString(),
50
+ updatedAt: deployment.updatedAt.toISOString()
51
+ };
52
+ };
53
+ const buildDeploymentDetail = (deployment, appId) => {
54
+ return {
55
+ ...buildDeploymentSummary(deployment, appId),
56
+ sourceCode: deployment.sourceCode ?? ""
57
+ };
58
+ };
59
+ const buildAppSummary = (app, latestDeployment) => {
60
+ return {
61
+ id: app.id.valueOf(),
62
+ scriptName: app.scriptName,
63
+ latestDeployment: latestDeployment ? buildDeploymentSummary(latestDeployment, app.id.valueOf()) : null,
64
+ createdAt: app.createdAt.toISOString(),
65
+ updatedAt: app.updatedAt.toISOString()
66
+ };
67
+ };
68
+ const formatDeployError = (error) => {
69
+ const cloudflareError = getCloudflareApiError(error);
70
+ if (cloudflareError) return {
71
+ code: cloudflareError.code,
72
+ message: cloudflareError.message
73
+ };
74
+ if (error instanceof Error) return {
75
+ code: error.name || "Error",
76
+ message: error.message
77
+ };
78
+ return {
79
+ code: "unknown",
80
+ message: String(error)
81
+ };
82
+ };
83
+ const readCloudflareSummary = (response) => {
84
+ return {
85
+ etag: response.etag ?? null,
86
+ modifiedOn: response.modified_on ?? null,
87
+ raw: response
88
+ };
89
+ };
90
+ const createLivePointerGuard = (input) => {
91
+ if (input.expectedLiveEtag !== null) return {
92
+ kind: "etag",
93
+ expectedLiveEtag: input.expectedLiveEtag
94
+ };
95
+ return {
96
+ kind: "first-deploy",
97
+ leaseDeploymentId: input.deploymentId
98
+ };
99
+ };
100
+ const canPromoteDeploymentToLive = (app, deploymentId, livePointerGuard) => {
101
+ if (livePointerGuard.kind === "first-deploy") return app.liveCloudflareEtag === null && app.firstDeploymentLeaseId === livePointerGuard.leaseDeploymentId;
102
+ return app.liveCloudflareEtag === livePointerGuard.expectedLiveEtag || app.liveDeploymentId === deploymentId;
103
+ };
104
+ const cloudflareFragmentDefinition = defineFragment("cloudflare-fragment").extend(withDatabase(cloudflareSchema)).withDependencies(({ config }) => {
105
+ const dispatchNamespace = resolveCloudflareDispatchNamespaceName(resolveDispatcherConfig(config));
106
+ return {
107
+ cloudflare: "cloudflare" in config && config.cloudflare ? config.cloudflare : createCloudflareApiClient({
108
+ apiToken: config.apiToken,
109
+ fetchImplementation: config.fetchImplementation
110
+ }),
111
+ dispatchNamespace
112
+ };
113
+ }).providesService("cloudflare", ({ deps }) => ({ getClient: () => deps.cloudflare })).provideHooks(({ defineHook, deps, config }) => ({ deployWorker: defineHook(async function(input) {
114
+ const livePointerGuard = createLivePointerGuard(input);
115
+ let remoteResult = null;
116
+ let remoteError = null;
117
+ let remoteUploadAttempted = false;
118
+ const prepareFirstDeployment = async () => {
119
+ if (livePointerGuard.kind !== "first-deploy") return null;
120
+ return await this.handlerTx({ retryPolicy: hookWriteRetryPolicy }).retrieve(({ forSchema }) => forSchema(cloudflareSchema).findFirst("deployment", (b) => b.whereIndex("primary", (eb) => eb("id", "=", input.deploymentId)).join((j) => j.app()))).mutate(({ forSchema, retrieveResult: [deployment] }) => {
121
+ if (!deployment?.app) return { action: "missing" };
122
+ const uow = forSchema(cloudflareSchema);
123
+ const now = uow.now();
124
+ const deploymentId = deployment.id.valueOf();
125
+ const app = deployment.app;
126
+ if (app.liveCloudflareEtag !== null) {
127
+ if (app.firstDeploymentLeaseId === deploymentId) uow.update("app", app.id, (b) => b.set({
128
+ updatedAt: now,
129
+ firstDeploymentLeaseId: null
130
+ }).check());
131
+ return {
132
+ action: "superseded",
133
+ currentDeploymentId: app.liveDeploymentId,
134
+ currentDeploymentTag: app.liveDeploymentId ?? "current-live-deployment",
135
+ currentEtag: app.liveCloudflareEtag,
136
+ currentModifiedOn: null
137
+ };
138
+ }
139
+ if (app.firstDeploymentLeaseId !== null && app.firstDeploymentLeaseId !== deploymentId) return { action: "retry" };
140
+ if (app.firstDeploymentLeaseId !== deploymentId) uow.update("app", app.id, (b) => b.set({
141
+ updatedAt: now,
142
+ firstDeploymentLeaseId: deploymentId
143
+ }).check());
144
+ return { action: "continue" };
145
+ }).execute();
146
+ };
147
+ const firstDeploymentPreparation = await prepareFirstDeployment();
148
+ if (firstDeploymentPreparation?.action === "missing") return;
149
+ if (firstDeploymentPreparation?.action === "retry") throw new Error(`Initial Cloudflare deploy for '${input.scriptName}' is already in progress.`);
150
+ if (firstDeploymentPreparation?.action === "superseded") remoteResult = firstDeploymentPreparation;
151
+ if (!remoteResult) {
152
+ remoteUploadAttempted = true;
153
+ try {
154
+ remoteResult = await reconcileCloudflareWorkerDeployment(deps.cloudflare, {
155
+ accountId: config.accountId,
156
+ dispatchNamespace: deps.dispatchNamespace,
157
+ appId: input.appId,
158
+ deploymentId: input.deploymentId,
159
+ expectedLiveEtag: input.expectedLiveEtag,
160
+ deploymentTagPrefix: config.deploymentTagPrefix,
161
+ scriptName: input.scriptName,
162
+ entrypoint: input.entrypoint,
163
+ moduleContent: input.moduleContent,
164
+ compatibilityDate: input.compatibilityDate,
165
+ compatibilityFlags: input.compatibilityFlags,
166
+ scriptTags: config.scriptTags
167
+ });
168
+ } catch (error) {
169
+ remoteError = error;
170
+ }
171
+ }
172
+ const remoteDeploymentId = remoteResult?.action === "superseded" || remoteResult?.action === "already-deployed" ? remoteResult.currentDeploymentId : null;
173
+ await (remoteDeploymentId && remoteDeploymentId !== input.deploymentId ? this.handlerTx({ retryPolicy: hookWriteRetryPolicy }).retrieve(({ forSchema }) => forSchema(cloudflareSchema).findFirst("deployment", (b) => b.whereIndex("primary", (eb) => eb("id", "=", input.deploymentId)).join((j) => j.app())).findFirst("deployment", (b) => b.whereIndex("primary", (eb) => eb("id", "=", remoteDeploymentId)))).transformRetrieve(([deployment, remoteDeployment]) => ({
174
+ deployment,
175
+ remoteDeployment: remoteDeployment ?? null
176
+ })) : this.handlerTx({ retryPolicy: hookWriteRetryPolicy }).retrieve(({ forSchema }) => forSchema(cloudflareSchema).findFirst("deployment", (b) => b.whereIndex("primary", (eb) => eb("id", "=", input.deploymentId)).join((j) => j.app()))).transformRetrieve(([deployment]) => ({
177
+ deployment,
178
+ remoteDeployment: null
179
+ }))).mutate(({ forSchema, retrieveResult: { deployment, remoteDeployment } }) => {
180
+ if (!deployment || !deployment.app) return;
181
+ const uow = forSchema(cloudflareSchema);
182
+ const now = uow.now();
183
+ const appId = deployment.app.id;
184
+ const deploymentId = deployment.id.valueOf();
185
+ const ownsFirstDeploymentLease = deployment.app.firstDeploymentLeaseId === deploymentId;
186
+ const updateApp = (changes = {}) => {
187
+ uow.update("app", appId, (b) => b.set({
188
+ updatedAt: now,
189
+ ...changes,
190
+ ...ownsFirstDeploymentLease ? { firstDeploymentLeaseId: null } : {}
191
+ }).check());
192
+ };
193
+ if (remoteResult?.action === "already-deployed") {
194
+ const etag = remoteResult.currentEtag ?? deployment.cloudflareEtag ?? (deployment.app.liveDeploymentId === deploymentId ? deployment.app.liveCloudflareEtag : null);
195
+ const modifiedOn = remoteResult.currentModifiedOn ?? deployment.cloudflareModifiedOn;
196
+ uow.update("deployment", deployment.id, (b) => b.set({
197
+ status: "succeeded",
198
+ updatedAt: now,
199
+ startedAt: deployment.startedAt ?? now,
200
+ completedAt: deployment.completedAt ?? now,
201
+ attemptCount: Math.max(deployment.attemptCount, 1),
202
+ errorCode: null,
203
+ errorMessage: null,
204
+ cloudflareEtag: etag,
205
+ cloudflareModifiedOn: modifiedOn
206
+ }).check());
207
+ updateApp({
208
+ liveDeploymentId: deploymentId,
209
+ liveCloudflareEtag: etag
210
+ });
211
+ return;
212
+ }
213
+ if (remoteResult?.action === "uploaded") {
214
+ const cloudflare = readCloudflareSummary(remoteResult.response);
215
+ const canMoveLivePointer = canPromoteDeploymentToLive(deployment.app, deploymentId, livePointerGuard);
216
+ uow.update("deployment", deployment.id, (b) => b.set({
217
+ status: "succeeded",
218
+ updatedAt: now,
219
+ startedAt: deployment.startedAt ?? now,
220
+ completedAt: now,
221
+ attemptCount: deployment.attemptCount + 1,
222
+ errorCode: null,
223
+ errorMessage: null,
224
+ cloudflareEtag: cloudflare.etag,
225
+ cloudflareModifiedOn: cloudflare.modifiedOn,
226
+ cloudflareResponse: cloudflare.raw
227
+ }).check());
228
+ updateApp(canMoveLivePointer ? {
229
+ liveDeploymentId: deploymentId,
230
+ liveCloudflareEtag: cloudflare.etag
231
+ } : {});
232
+ return;
233
+ }
234
+ if (remoteResult?.action === "superseded") {
235
+ const supersededBy = remoteDeployment?.id.valueOf() ?? remoteResult.currentDeploymentId ?? remoteResult.currentDeploymentTag;
236
+ const winnerEtag = remoteResult.currentEtag ?? remoteDeployment?.cloudflareEtag ?? (deployment.app.liveDeploymentId === remoteResult.currentDeploymentId ? deployment.app.liveCloudflareEtag : null);
237
+ const winnerModifiedOn = remoteResult.currentModifiedOn ?? remoteDeployment?.cloudflareModifiedOn ?? null;
238
+ if (remoteDeployment) uow.update("deployment", remoteDeployment.id, (b) => b.set({
239
+ status: "succeeded",
240
+ updatedAt: now,
241
+ startedAt: remoteDeployment.startedAt ?? now,
242
+ completedAt: remoteDeployment.completedAt ?? now,
243
+ attemptCount: Math.max(remoteDeployment.attemptCount, 1),
244
+ errorCode: null,
245
+ errorMessage: null,
246
+ cloudflareEtag: winnerEtag,
247
+ cloudflareModifiedOn: winnerModifiedOn
248
+ }).check());
249
+ updateApp({
250
+ liveDeploymentId: remoteResult.currentDeploymentId,
251
+ liveCloudflareEtag: winnerEtag
252
+ });
253
+ if (deployment.status === "succeeded") return;
254
+ uow.update("deployment", deployment.id, (b) => b.set({
255
+ status: "failed",
256
+ updatedAt: now,
257
+ startedAt: deployment.startedAt ?? now,
258
+ completedAt: now,
259
+ attemptCount: deployment.attemptCount + (remoteUploadAttempted ? 1 : 0),
260
+ errorCode: "DEPLOYMENT_SUPERSEDED",
261
+ errorMessage: `Deployment was superseded by '${supersededBy}'.`
262
+ }).check());
263
+ return;
264
+ }
265
+ if (deployment.status === "succeeded") {
266
+ updateApp();
267
+ return;
268
+ }
269
+ const formatted = formatDeployError(remoteError);
270
+ uow.update("deployment", deployment.id, (b) => b.set({
271
+ status: "failed",
272
+ updatedAt: now,
273
+ startedAt: deployment.startedAt ?? now,
274
+ completedAt: now,
275
+ attemptCount: deployment.attemptCount + (remoteUploadAttempted ? 1 : 0),
276
+ errorCode: formatted.code,
277
+ errorMessage: formatted.message
278
+ }).check());
279
+ updateApp();
280
+ }).execute();
281
+ }) })).providesBaseService(({ defineService, config }) => {
282
+ return defineService({
283
+ upsertApp: function(appId) {
284
+ return this.serviceTx(cloudflareSchema).retrieve((uow) => uow.findFirst("app", (b) => b.whereIndex("primary", (eb) => eb("id", "=", appId)))).mutate(({ uow, retrieveResult: [existingApp] }) => {
285
+ if (existingApp) return buildAppSummary(existingApp, null);
286
+ const createdAt = /* @__PURE__ */ new Date();
287
+ const scriptName = resolveCloudflareScriptName(appId, config);
288
+ return {
289
+ id: uow.create("app", {
290
+ id: appId,
291
+ scriptName,
292
+ liveDeploymentId: null,
293
+ liveCloudflareEtag: null,
294
+ firstDeploymentLeaseId: null,
295
+ createdAt,
296
+ updatedAt: createdAt
297
+ }).valueOf(),
298
+ scriptName,
299
+ latestDeployment: null,
300
+ createdAt: createdAt.toISOString(),
301
+ updatedAt: createdAt.toISOString()
302
+ };
303
+ }).build();
304
+ },
305
+ queueDeployment: function(appId, request) {
306
+ return this.serviceTx(cloudflareSchema).retrieve((uow) => uow.findFirst("app", (b) => b.whereIndex("primary", (eb) => eb("id", "=", appId)))).mutate(({ uow, retrieveResult: [existingApp] }) => {
307
+ const now = /* @__PURE__ */ new Date();
308
+ const compatibilityDate = request.compatibilityDate ?? config.compatibilityDate;
309
+ const compatibilityFlags = normalizeStringList(request.compatibilityFlags ?? config.compatibilityFlags);
310
+ const scriptName = existingApp?.scriptName ?? resolveCloudflareScriptName(appId, config);
311
+ const sourceByteLength = textEncoder.encode(request.script.content).byteLength;
312
+ const ensuredAppId = existingApp?.id ?? uow.create("app", {
313
+ id: appId,
314
+ scriptName,
315
+ liveDeploymentId: null,
316
+ liveCloudflareEtag: null,
317
+ firstDeploymentLeaseId: null,
318
+ createdAt: now,
319
+ updatedAt: now
320
+ });
321
+ if (existingApp) uow.update("app", existingApp.id, (b) => b.set({ updatedAt: now }).check());
322
+ const deploymentIdValue = uow.create("deployment", {
323
+ appId: ensuredAppId,
324
+ status: "queued",
325
+ format: SUPPORTED_DEPLOYMENT_FORMAT,
326
+ entrypoint: request.script.entrypoint,
327
+ scriptName,
328
+ sourceCode: request.script.content,
329
+ sourceByteLength,
330
+ compatibilityDate,
331
+ compatibilityFlags,
332
+ attemptCount: 0,
333
+ startedAt: null,
334
+ completedAt: null,
335
+ errorCode: null,
336
+ errorMessage: null,
337
+ cloudflareEtag: null,
338
+ cloudflareModifiedOn: null,
339
+ cloudflareResponse: null,
340
+ createdAt: now,
341
+ updatedAt: now
342
+ }).valueOf();
343
+ const expectedLiveEtag = existingApp?.liveCloudflareEtag ?? null;
344
+ uow.triggerHook("deployWorker", {
345
+ deploymentId: deploymentIdValue,
346
+ appId: ensuredAppId.valueOf(),
347
+ expectedLiveEtag,
348
+ scriptName,
349
+ entrypoint: request.script.entrypoint,
350
+ moduleContent: request.script.content,
351
+ compatibilityDate,
352
+ compatibilityFlags
353
+ });
354
+ return {
355
+ id: deploymentIdValue,
356
+ appId: ensuredAppId.valueOf(),
357
+ scriptName,
358
+ status: "queued",
359
+ format: SUPPORTED_DEPLOYMENT_FORMAT,
360
+ entrypoint: request.script.entrypoint,
361
+ sourceByteLength,
362
+ compatibilityDate,
363
+ compatibilityFlags,
364
+ attemptCount: 0,
365
+ queuedAt: now.toISOString(),
366
+ startedAt: null,
367
+ completedAt: null,
368
+ errorCode: null,
369
+ errorMessage: null,
370
+ cloudflare: null,
371
+ createdAt: now.toISOString(),
372
+ updatedAt: now.toISOString()
373
+ };
374
+ }).build();
375
+ },
376
+ getDeployment: function(deploymentId) {
377
+ return this.serviceTx(cloudflareSchema).retrieve((uow) => uow.findFirst("deployment", (b) => b.whereIndex("primary", (eb) => eb("id", "=", deploymentId)).join((j) => j.app()))).transformRetrieve(([deployment]) => {
378
+ return deployment?.app ? buildDeploymentDetail(deployment, deployment.app.id.valueOf()) : null;
379
+ }).build();
380
+ },
381
+ getAppState: function(appId) {
382
+ return this.serviceTx(cloudflareSchema).retrieve((uow) => uow.findFirst("app", (b) => b.whereIndex("primary", (eb) => eb("id", "=", appId))).findFirst("deployment", (b) => b.whereIndex("idx_deployment_app_createdAt", (eb) => eb("appId", "=", appId)).orderByIndex("idx_deployment_app_createdAt", "desc"))).transformRetrieve(([app, latestDeployment]) => {
383
+ return app ? {
384
+ ...buildAppSummary(app, latestDeployment),
385
+ liveDeploymentId: app.liveDeploymentId,
386
+ liveCloudflareEtag: app.liveCloudflareEtag
387
+ } : null;
388
+ }).build();
389
+ },
390
+ listApps: function() {
391
+ return this.serviceTx(cloudflareSchema).retrieve((uow) => uow.find("app", (b) => b.whereIndex("primary")).find("deployment", (b) => b.whereIndex("primary").join((j) => j.app()))).transformRetrieve(([apps, deployments]) => {
392
+ const latestDeploymentsByAppId = /* @__PURE__ */ new Map();
393
+ for (const deployment of deployments) {
394
+ if (!deployment.app) continue;
395
+ const deploymentAppId = deployment.app.id.valueOf();
396
+ const existingDeployment = latestDeploymentsByAppId.get(deploymentAppId);
397
+ if (!existingDeployment || deployment.createdAt.getTime() > existingDeployment.createdAt.getTime()) latestDeploymentsByAppId.set(deploymentAppId, deployment);
398
+ }
399
+ return apps.map((app) => buildAppSummary(app, latestDeploymentsByAppId.get(app.id.valueOf()) ?? null)).sort((left, right) => {
400
+ const updatedAtCompare = right.updatedAt.localeCompare(left.updatedAt);
401
+ if (updatedAtCompare !== 0) return updatedAtCompare;
402
+ return left.id.localeCompare(right.id);
403
+ });
404
+ }).build();
405
+ },
406
+ listAppDeployments: function(appId) {
407
+ return this.serviceTx(cloudflareSchema).retrieve((uow) => uow.findFirst("app", (b) => b.whereIndex("primary", (eb) => eb("id", "=", appId))).find("deployment", (b) => b.whereIndex("idx_deployment_app_createdAt", (eb) => eb("appId", "=", appId)).orderByIndex("idx_deployment_app_createdAt", "desc"))).transformRetrieve(([app, deployments]) => {
408
+ if (!app) return null;
409
+ return deployments.map((deployment) => buildDeploymentSummary(deployment, app.id.valueOf()));
410
+ }).build();
411
+ }
412
+ });
413
+ }).build();
414
+
415
+ //#endregion
416
+ export { cloudflareFragmentDefinition };
417
+ //# sourceMappingURL=definition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition.js","names":[],"sources":["../../src/definition.ts"],"sourcesContent":["import type { ClientOptions as CloudflareClientOptions } from \"cloudflare\";\n\nimport { defineFragment } from \"@fragno-dev/core\";\nimport { ExponentialBackoffRetryPolicy, withDatabase, type HookFn } from \"@fragno-dev/db\";\n\nimport {\n createCloudflareApiClient,\n getCloudflareApiError,\n reconcileCloudflareWorkerDeployment,\n resolveCloudflareDispatchNamespaceName,\n type CloudflareApiClient,\n type CloudflareDispatcherConfig,\n type CloudflareScriptUpdateResponse,\n} from \"./cloudflare-api\";\nimport {\n SUPPORTED_DEPLOYMENT_FORMAT,\n type CloudflareAppSummary,\n type CloudflareDeployRequest,\n type CloudflareDeploymentDetail,\n type CloudflareDeploymentSummary,\n} from \"./contracts\";\nimport { cloudflareSchema } from \"./schema\";\nimport { resolveCloudflareScriptName, type CloudflareScriptNameConfig } from \"./script-name\";\n\ntype DeploymentRow = {\n id: { valueOf(): string };\n status: string;\n format: string;\n entrypoint: string;\n scriptName: string;\n sourceByteLength: number;\n compatibilityDate: string;\n compatibilityFlags: unknown;\n attemptCount: number;\n startedAt: Date | null;\n completedAt: Date | null;\n errorCode: string | null;\n errorMessage: string | null;\n cloudflareEtag: string | null;\n cloudflareModifiedOn: string | null;\n createdAt: Date;\n updatedAt: Date;\n};\n\ntype AppRow = {\n id: { valueOf(): string };\n scriptName: string;\n liveDeploymentId: string | null;\n liveCloudflareEtag: string | null;\n firstDeploymentLeaseId: string | null;\n createdAt: Date;\n updatedAt: Date;\n};\n\ntype CloudflareLiveDeploymentSnapshot = {\n currentDeploymentTag: string;\n currentDeploymentId: string | null;\n currentEtag: string | null;\n currentModifiedOn: string | null;\n};\n\ntype DeployWorkerRemoteResult =\n | Awaited<ReturnType<typeof reconcileCloudflareWorkerDeployment>>\n | ({ action: \"superseded\" } & CloudflareLiveDeploymentSnapshot);\n\ntype FirstDeploymentPreparation =\n | { action: \"missing\" }\n | { action: \"retry\" }\n | { action: \"continue\" }\n | ({ action: \"superseded\" } & CloudflareLiveDeploymentSnapshot);\n\ntype LivePointerGuard =\n | {\n kind: \"etag\";\n expectedLiveEtag: string;\n }\n | {\n kind: \"first-deploy\";\n leaseDeploymentId: string;\n };\n\nconst textEncoder = new TextEncoder();\nconst hookWriteRetryPolicy = new ExponentialBackoffRetryPolicy({\n maxRetries: 5,\n initialDelayMs: 10,\n maxDelayMs: 250,\n});\n\ntype CloudflareFragmentSharedConfig = CloudflareScriptNameConfig & {\n accountId: string;\n compatibilityDate: string;\n compatibilityFlags?: string[];\n scriptTags?: string[];\n deploymentTagPrefix?: string;\n};\n\ntype CloudflareFragmentApiTokenConfig = {\n apiToken: string;\n cloudflare?: never;\n fetchImplementation?: CloudflareClientOptions[\"fetch\"];\n};\n\ntype CloudflareFragmentClientConfig = {\n cloudflare: CloudflareApiClient;\n apiToken?: never;\n fetchImplementation?: never;\n};\n\nexport type CloudflareFragmentConfig = CloudflareFragmentSharedConfig &\n (CloudflareFragmentApiTokenConfig | CloudflareFragmentClientConfig) &\n (\n | {\n dispatcher: CloudflareDispatcherConfig;\n dispatchNamespace?: never;\n }\n | {\n dispatchNamespace: string;\n dispatcher?: never;\n }\n );\n\nexport type DeployWorkerHookInput = {\n deploymentId: string;\n appId: string;\n expectedLiveEtag: string | null;\n scriptName: string;\n entrypoint: string;\n moduleContent: string;\n compatibilityDate: string;\n compatibilityFlags: string[];\n};\n\nexport type CloudflareHooksMap = {\n deployWorker: HookFn<DeployWorkerHookInput>;\n};\n\nconst resolveDispatcherConfig = (config: CloudflareFragmentConfig): CloudflareDispatcherConfig => {\n if (\"dispatcher\" in config && config.dispatcher !== undefined) {\n return config.dispatcher;\n }\n\n if (\"dispatchNamespace\" in config && config.dispatchNamespace !== undefined) {\n return config.dispatchNamespace;\n }\n\n throw new Error(\n \"Cloudflare fragment requires `dispatcher` or `dispatchNamespace` to resolve the dispatch namespace name.\",\n );\n};\n\nconst normalizeStringList = (value: unknown) => {\n if (!Array.isArray(value)) {\n return [];\n }\n\n return value.filter((entry): entry is string => typeof entry === \"string\" && entry.length > 0);\n};\n\nconst toIsoDateTime = (value: Date | null) => {\n return value ? value.toISOString() : null;\n};\n\nconst buildDeploymentSummary = (\n deployment: DeploymentRow,\n appId: string,\n): CloudflareDeploymentSummary => {\n const cloudflare =\n deployment.cloudflareEtag || deployment.cloudflareModifiedOn\n ? {\n etag: deployment.cloudflareEtag,\n modifiedOn: deployment.cloudflareModifiedOn,\n }\n : null;\n\n return {\n id: deployment.id.valueOf(),\n appId,\n scriptName: deployment.scriptName,\n status:\n deployment.status === \"queued\" ||\n deployment.status === \"deploying\" ||\n deployment.status === \"succeeded\" ||\n deployment.status === \"failed\"\n ? deployment.status\n : \"failed\",\n format:\n deployment.format === SUPPORTED_DEPLOYMENT_FORMAT ? SUPPORTED_DEPLOYMENT_FORMAT : \"esmodule\",\n entrypoint: deployment.entrypoint,\n sourceByteLength: deployment.sourceByteLength,\n compatibilityDate: deployment.compatibilityDate,\n compatibilityFlags: normalizeStringList(deployment.compatibilityFlags),\n attemptCount: deployment.attemptCount,\n queuedAt: deployment.createdAt.toISOString(),\n startedAt: toIsoDateTime(deployment.startedAt),\n completedAt: toIsoDateTime(deployment.completedAt),\n errorCode: deployment.errorCode,\n errorMessage: deployment.errorMessage,\n cloudflare,\n createdAt: deployment.createdAt.toISOString(),\n updatedAt: deployment.updatedAt.toISOString(),\n };\n};\n\nconst buildDeploymentDetail = (\n deployment: DeploymentRow & { sourceCode?: string },\n appId: string,\n): CloudflareDeploymentDetail => {\n return {\n ...buildDeploymentSummary(deployment, appId),\n sourceCode: deployment.sourceCode ?? \"\",\n };\n};\n\nconst buildAppSummary = (\n app: AppRow,\n latestDeployment: DeploymentRow | null,\n): CloudflareAppSummary => {\n return {\n id: app.id.valueOf(),\n scriptName: app.scriptName,\n latestDeployment: latestDeployment\n ? buildDeploymentSummary(latestDeployment, app.id.valueOf())\n : null,\n createdAt: app.createdAt.toISOString(),\n updatedAt: app.updatedAt.toISOString(),\n };\n};\n\nconst formatDeployError = (error: unknown) => {\n const cloudflareError = getCloudflareApiError(error);\n if (cloudflareError) {\n return {\n code: cloudflareError.code,\n message: cloudflareError.message,\n };\n }\n\n if (error instanceof Error) {\n return {\n code: error.name || \"Error\",\n message: error.message,\n };\n }\n\n return {\n code: \"unknown\",\n message: String(error),\n };\n};\n\nconst readCloudflareSummary = (response: CloudflareScriptUpdateResponse) => {\n return {\n etag: response.etag ?? null,\n modifiedOn: response.modified_on ?? null,\n raw: response,\n };\n};\n\nconst createLivePointerGuard = (input: DeployWorkerHookInput): LivePointerGuard => {\n if (input.expectedLiveEtag !== null) {\n return {\n kind: \"etag\",\n expectedLiveEtag: input.expectedLiveEtag,\n };\n }\n\n return {\n kind: \"first-deploy\",\n leaseDeploymentId: input.deploymentId,\n };\n};\n\nconst canPromoteDeploymentToLive = (\n app: AppRow,\n deploymentId: string,\n livePointerGuard: LivePointerGuard,\n) => {\n if (livePointerGuard.kind === \"first-deploy\") {\n return (\n app.liveCloudflareEtag === null &&\n app.firstDeploymentLeaseId === livePointerGuard.leaseDeploymentId\n );\n }\n\n return (\n app.liveCloudflareEtag === livePointerGuard.expectedLiveEtag ||\n app.liveDeploymentId === deploymentId\n );\n};\n\nexport const cloudflareFragmentDefinition = defineFragment<CloudflareFragmentConfig>(\n \"cloudflare-fragment\",\n)\n .extend(withDatabase(cloudflareSchema))\n .withDependencies(({ config }) => {\n const dispatchNamespace = resolveCloudflareDispatchNamespaceName(\n resolveDispatcherConfig(config),\n );\n const cloudflare =\n \"cloudflare\" in config && config.cloudflare\n ? config.cloudflare\n : createCloudflareApiClient({\n apiToken: config.apiToken,\n fetchImplementation: config.fetchImplementation,\n });\n\n return {\n cloudflare,\n dispatchNamespace,\n };\n })\n .providesService(\"cloudflare\", ({ deps }) => ({\n getClient: () => deps.cloudflare,\n }))\n .provideHooks<CloudflareHooksMap>(({ defineHook, deps, config }) => ({\n deployWorker: defineHook(async function (input) {\n // Cloudflare CAS starts once the app has a live etag. The first deploy uses a local\n // lease so we still get compare-and-swap semantics before that point.\n const livePointerGuard = createLivePointerGuard(input);\n let remoteResult: DeployWorkerRemoteResult | null = null;\n let remoteError: unknown = null;\n let remoteUploadAttempted = false;\n\n const prepareFirstDeployment = async (): Promise<FirstDeploymentPreparation | null> => {\n if (livePointerGuard.kind !== \"first-deploy\") {\n return null;\n }\n\n return await this.handlerTx({ retryPolicy: hookWriteRetryPolicy })\n .retrieve(({ forSchema }) =>\n forSchema(cloudflareSchema).findFirst(\"deployment\", (b) =>\n b\n .whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", input.deploymentId))\n .join((j) => j.app()),\n ),\n )\n .mutate(({ forSchema, retrieveResult: [deployment] }) => {\n if (!deployment?.app) {\n return { action: \"missing\" as const };\n }\n\n const uow = forSchema(cloudflareSchema);\n const now = uow.now();\n const deploymentId = deployment.id.valueOf();\n const app = deployment.app;\n\n if (app.liveCloudflareEtag !== null) {\n if (app.firstDeploymentLeaseId === deploymentId) {\n uow.update(\"app\", app.id, (b) =>\n b\n .set({\n updatedAt: now,\n firstDeploymentLeaseId: null,\n })\n .check(),\n );\n }\n\n return {\n action: \"superseded\" as const,\n currentDeploymentId: app.liveDeploymentId,\n currentDeploymentTag: app.liveDeploymentId ?? \"current-live-deployment\",\n currentEtag: app.liveCloudflareEtag,\n currentModifiedOn: null,\n };\n }\n\n if (\n app.firstDeploymentLeaseId !== null &&\n app.firstDeploymentLeaseId !== deploymentId\n ) {\n return { action: \"retry\" as const };\n }\n\n if (app.firstDeploymentLeaseId !== deploymentId) {\n uow.update(\"app\", app.id, (b) =>\n b\n .set({\n updatedAt: now,\n firstDeploymentLeaseId: deploymentId,\n })\n .check(),\n );\n }\n\n return { action: \"continue\" as const };\n })\n .execute();\n };\n\n const firstDeploymentPreparation = await prepareFirstDeployment();\n if (firstDeploymentPreparation?.action === \"missing\") {\n return;\n }\n\n if (firstDeploymentPreparation?.action === \"retry\") {\n throw new Error(\n `Initial Cloudflare deploy for '${input.scriptName}' is already in progress.`,\n );\n }\n\n if (firstDeploymentPreparation?.action === \"superseded\") {\n remoteResult = firstDeploymentPreparation;\n }\n\n if (!remoteResult) {\n remoteUploadAttempted = true;\n\n try {\n remoteResult = await reconcileCloudflareWorkerDeployment(deps.cloudflare, {\n accountId: config.accountId,\n dispatchNamespace: deps.dispatchNamespace,\n appId: input.appId,\n deploymentId: input.deploymentId,\n expectedLiveEtag: input.expectedLiveEtag,\n deploymentTagPrefix: config.deploymentTagPrefix,\n scriptName: input.scriptName,\n entrypoint: input.entrypoint,\n moduleContent: input.moduleContent,\n compatibilityDate: input.compatibilityDate,\n compatibilityFlags: input.compatibilityFlags,\n scriptTags: config.scriptTags,\n });\n } catch (error) {\n remoteError = error;\n }\n }\n\n const remoteDeploymentId =\n remoteResult?.action === \"superseded\" || remoteResult?.action === \"already-deployed\"\n ? remoteResult.currentDeploymentId\n : null;\n const finalizeDeploymentStateTx =\n remoteDeploymentId && remoteDeploymentId !== input.deploymentId\n ? this.handlerTx({ retryPolicy: hookWriteRetryPolicy })\n .retrieve(({ forSchema }) =>\n forSchema(cloudflareSchema)\n .findFirst(\"deployment\", (b) =>\n b\n .whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", input.deploymentId))\n .join((j) => j.app()),\n )\n .findFirst(\"deployment\", (b) =>\n b.whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", remoteDeploymentId)),\n ),\n )\n .transformRetrieve(([deployment, remoteDeployment]) => ({\n deployment,\n remoteDeployment: remoteDeployment ?? null,\n }))\n : this.handlerTx({ retryPolicy: hookWriteRetryPolicy })\n .retrieve(({ forSchema }) =>\n forSchema(cloudflareSchema).findFirst(\"deployment\", (b) =>\n b\n .whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", input.deploymentId))\n .join((j) => j.app()),\n ),\n )\n .transformRetrieve(([deployment]) => ({\n deployment,\n remoteDeployment: null,\n }));\n\n await finalizeDeploymentStateTx\n .mutate(({ forSchema, retrieveResult: { deployment, remoteDeployment } }) => {\n if (!deployment || !deployment.app) {\n return;\n }\n\n const uow = forSchema(cloudflareSchema);\n const now = uow.now();\n const appId = deployment.app.id;\n const deploymentId = deployment.id.valueOf();\n const ownsFirstDeploymentLease = deployment.app.firstDeploymentLeaseId === deploymentId;\n const updateApp = (\n changes: Partial<{\n liveDeploymentId: string | null;\n liveCloudflareEtag: string | null;\n }> = {},\n ) => {\n uow.update(\"app\", appId, (b) =>\n b\n .set({\n updatedAt: now,\n ...changes,\n ...(ownsFirstDeploymentLease ? { firstDeploymentLeaseId: null } : {}),\n })\n .check(),\n );\n };\n\n if (remoteResult?.action === \"already-deployed\") {\n const etag =\n remoteResult.currentEtag ??\n deployment.cloudflareEtag ??\n (deployment.app.liveDeploymentId === deploymentId\n ? deployment.app.liveCloudflareEtag\n : null);\n const modifiedOn = remoteResult.currentModifiedOn ?? deployment.cloudflareModifiedOn;\n\n uow.update(\"deployment\", deployment.id, (b) =>\n b\n .set({\n status: \"succeeded\",\n updatedAt: now,\n startedAt: deployment.startedAt ?? now,\n completedAt: deployment.completedAt ?? now,\n attemptCount: Math.max(deployment.attemptCount, 1),\n errorCode: null,\n errorMessage: null,\n cloudflareEtag: etag,\n cloudflareModifiedOn: modifiedOn,\n })\n .check(),\n );\n updateApp({\n liveDeploymentId: deploymentId,\n liveCloudflareEtag: etag,\n });\n return;\n }\n\n if (remoteResult?.action === \"uploaded\") {\n const cloudflare = readCloudflareSummary(remoteResult.response);\n const canMoveLivePointer = canPromoteDeploymentToLive(\n deployment.app,\n deploymentId,\n livePointerGuard,\n );\n\n uow.update(\"deployment\", deployment.id, (b) =>\n b\n .set({\n status: \"succeeded\",\n updatedAt: now,\n startedAt: deployment.startedAt ?? now,\n completedAt: now,\n attemptCount: deployment.attemptCount + 1,\n errorCode: null,\n errorMessage: null,\n cloudflareEtag: cloudflare.etag,\n cloudflareModifiedOn: cloudflare.modifiedOn,\n cloudflareResponse: cloudflare.raw,\n })\n .check(),\n );\n updateApp(\n canMoveLivePointer\n ? {\n liveDeploymentId: deploymentId,\n liveCloudflareEtag: cloudflare.etag,\n }\n : {},\n );\n return;\n }\n\n if (remoteResult?.action === \"superseded\") {\n const supersededBy =\n remoteDeployment?.id.valueOf() ??\n remoteResult.currentDeploymentId ??\n remoteResult.currentDeploymentTag;\n const winnerEtag =\n remoteResult.currentEtag ??\n remoteDeployment?.cloudflareEtag ??\n (deployment.app.liveDeploymentId === remoteResult.currentDeploymentId\n ? deployment.app.liveCloudflareEtag\n : null);\n const winnerModifiedOn =\n remoteResult.currentModifiedOn ?? remoteDeployment?.cloudflareModifiedOn ?? null;\n\n if (remoteDeployment) {\n uow.update(\"deployment\", remoteDeployment.id, (b) =>\n b\n .set({\n status: \"succeeded\",\n updatedAt: now,\n startedAt: remoteDeployment.startedAt ?? now,\n completedAt: remoteDeployment.completedAt ?? now,\n attemptCount: Math.max(remoteDeployment.attemptCount, 1),\n errorCode: null,\n errorMessage: null,\n cloudflareEtag: winnerEtag,\n cloudflareModifiedOn: winnerModifiedOn,\n })\n .check(),\n );\n }\n\n updateApp({\n liveDeploymentId: remoteResult.currentDeploymentId,\n liveCloudflareEtag: winnerEtag,\n });\n\n if (deployment.status === \"succeeded\") {\n return;\n }\n\n uow.update(\"deployment\", deployment.id, (b) =>\n b\n .set({\n status: \"failed\",\n updatedAt: now,\n startedAt: deployment.startedAt ?? now,\n completedAt: now,\n attemptCount: deployment.attemptCount + (remoteUploadAttempted ? 1 : 0),\n errorCode: \"DEPLOYMENT_SUPERSEDED\",\n errorMessage: `Deployment was superseded by '${supersededBy}'.`,\n })\n .check(),\n );\n return;\n }\n\n if (deployment.status === \"succeeded\") {\n updateApp();\n return;\n }\n\n const formatted = formatDeployError(remoteError);\n\n uow.update(\"deployment\", deployment.id, (b) =>\n b\n .set({\n status: \"failed\",\n updatedAt: now,\n startedAt: deployment.startedAt ?? now,\n completedAt: now,\n attemptCount: deployment.attemptCount + (remoteUploadAttempted ? 1 : 0),\n errorCode: formatted.code,\n errorMessage: formatted.message,\n })\n .check(),\n );\n updateApp();\n })\n .execute();\n }),\n }))\n .providesBaseService(({ defineService, config }) => {\n return defineService({\n upsertApp: function (appId: string) {\n return this.serviceTx(cloudflareSchema)\n .retrieve((uow) =>\n uow.findFirst(\"app\", (b) => b.whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", appId))),\n )\n .mutate(({ uow, retrieveResult: [existingApp] }) => {\n if (existingApp) {\n return buildAppSummary(existingApp, null);\n }\n\n const createdAt = new Date();\n const scriptName = resolveCloudflareScriptName(appId, config);\n const createdId = uow.create(\"app\", {\n id: appId,\n scriptName,\n liveDeploymentId: null,\n liveCloudflareEtag: null,\n firstDeploymentLeaseId: null,\n createdAt,\n updatedAt: createdAt,\n });\n\n return {\n id: createdId.valueOf(),\n scriptName,\n latestDeployment: null,\n createdAt: createdAt.toISOString(),\n updatedAt: createdAt.toISOString(),\n };\n })\n .build();\n },\n queueDeployment: function (appId: string, request: CloudflareDeployRequest) {\n return this.serviceTx(cloudflareSchema)\n .retrieve((uow) =>\n uow.findFirst(\"app\", (b) => b.whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", appId))),\n )\n .mutate(({ uow, retrieveResult: [existingApp] }) => {\n const now = new Date();\n const compatibilityDate = request.compatibilityDate ?? config.compatibilityDate;\n const compatibilityFlags = normalizeStringList(\n request.compatibilityFlags ?? config.compatibilityFlags,\n );\n const scriptName =\n existingApp?.scriptName ?? resolveCloudflareScriptName(appId, config);\n const sourceByteLength = textEncoder.encode(request.script.content).byteLength;\n const ensuredAppId =\n existingApp?.id ??\n uow.create(\"app\", {\n id: appId,\n scriptName,\n liveDeploymentId: null,\n liveCloudflareEtag: null,\n firstDeploymentLeaseId: null,\n createdAt: now,\n updatedAt: now,\n });\n\n if (existingApp) {\n uow.update(\"app\", existingApp.id, (b) => b.set({ updatedAt: now }).check());\n }\n\n const deploymentId = uow.create(\"deployment\", {\n appId: ensuredAppId,\n status: \"queued\",\n format: SUPPORTED_DEPLOYMENT_FORMAT,\n entrypoint: request.script.entrypoint,\n scriptName,\n sourceCode: request.script.content,\n sourceByteLength,\n compatibilityDate,\n compatibilityFlags,\n attemptCount: 0,\n startedAt: null,\n completedAt: null,\n errorCode: null,\n errorMessage: null,\n cloudflareEtag: null,\n cloudflareModifiedOn: null,\n cloudflareResponse: null,\n createdAt: now,\n updatedAt: now,\n });\n const deploymentIdValue = deploymentId.valueOf();\n const expectedLiveEtag = existingApp?.liveCloudflareEtag ?? null;\n\n // The hook payload carries the immutable deploy snapshot plus the live etag it was\n // queued against so Cloudflare can enforce compare-and-swap updates for us.\n uow.triggerHook(\"deployWorker\", {\n deploymentId: deploymentIdValue,\n appId: ensuredAppId.valueOf(),\n expectedLiveEtag,\n scriptName,\n entrypoint: request.script.entrypoint,\n moduleContent: request.script.content,\n compatibilityDate,\n compatibilityFlags,\n });\n\n return {\n id: deploymentIdValue,\n appId: ensuredAppId.valueOf(),\n scriptName,\n status: \"queued\" as const,\n format: SUPPORTED_DEPLOYMENT_FORMAT as \"esmodule\",\n entrypoint: request.script.entrypoint,\n sourceByteLength,\n compatibilityDate,\n compatibilityFlags,\n attemptCount: 0,\n queuedAt: now.toISOString(),\n startedAt: null,\n completedAt: null,\n errorCode: null,\n errorMessage: null,\n cloudflare: null,\n createdAt: now.toISOString(),\n updatedAt: now.toISOString(),\n };\n })\n .build();\n },\n getDeployment: function (deploymentId: string) {\n return this.serviceTx(cloudflareSchema)\n .retrieve((uow) =>\n uow.findFirst(\"deployment\", (b) =>\n b.whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", deploymentId)).join((j) => j.app()),\n ),\n )\n .transformRetrieve(([deployment]) => {\n return deployment?.app\n ? buildDeploymentDetail(deployment, deployment.app.id.valueOf())\n : null;\n })\n .build();\n },\n getAppState: function (appId: string) {\n return this.serviceTx(cloudflareSchema)\n .retrieve((uow) =>\n uow\n .findFirst(\"app\", (b) => b.whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", appId)))\n .findFirst(\"deployment\", (b) =>\n b\n .whereIndex(\"idx_deployment_app_createdAt\", (eb) => eb(\"appId\", \"=\", appId))\n .orderByIndex(\"idx_deployment_app_createdAt\", \"desc\"),\n ),\n )\n .transformRetrieve(([app, latestDeployment]) => {\n return app\n ? {\n ...buildAppSummary(app, latestDeployment),\n liveDeploymentId: app.liveDeploymentId,\n liveCloudflareEtag: app.liveCloudflareEtag,\n }\n : null;\n })\n .build();\n },\n listApps: function () {\n return this.serviceTx(cloudflareSchema)\n .retrieve((uow) =>\n uow\n .find(\"app\", (b) => b.whereIndex(\"primary\"))\n .find(\"deployment\", (b) => b.whereIndex(\"primary\").join((j) => j.app())),\n )\n .transformRetrieve(([apps, deployments]) => {\n const latestDeploymentsByAppId = new Map<string, (typeof deployments)[number]>();\n\n for (const deployment of deployments) {\n if (!deployment.app) {\n continue;\n }\n\n const deploymentAppId = deployment.app.id.valueOf();\n const existingDeployment = latestDeploymentsByAppId.get(deploymentAppId);\n\n if (\n !existingDeployment ||\n deployment.createdAt.getTime() > existingDeployment.createdAt.getTime()\n ) {\n latestDeploymentsByAppId.set(deploymentAppId, deployment);\n }\n }\n\n return apps\n .map((app) =>\n buildAppSummary(app, latestDeploymentsByAppId.get(app.id.valueOf()) ?? null),\n )\n .sort((left, right) => {\n const updatedAtCompare = right.updatedAt.localeCompare(left.updatedAt);\n if (updatedAtCompare !== 0) {\n return updatedAtCompare;\n }\n\n return left.id.localeCompare(right.id);\n });\n })\n .build();\n },\n listAppDeployments: function (appId: string) {\n return this.serviceTx(cloudflareSchema)\n .retrieve((uow) =>\n uow\n .findFirst(\"app\", (b) => b.whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", appId)))\n .find(\"deployment\", (b) =>\n b\n .whereIndex(\"idx_deployment_app_createdAt\", (eb) => eb(\"appId\", \"=\", appId))\n .orderByIndex(\"idx_deployment_app_createdAt\", \"desc\"),\n ),\n )\n .transformRetrieve(([app, deployments]) => {\n if (!app) {\n return null;\n }\n\n return deployments.map((deployment) =>\n buildDeploymentSummary(deployment, app.id.valueOf()),\n );\n })\n .build();\n },\n });\n })\n .build();\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ //#region src/deployment-tag.d.ts
2
+ declare const DEFAULT_DEPLOYMENT_TAG_PREFIX = "fragno";
3
+ declare const sanitizeCloudflareTag: (value: string) => string;
4
+ declare const normalizeCloudflareDeploymentTagPrefix: (prefix?: string) => string;
5
+ declare const buildCloudflareAppTag: (appId: string, prefix?: string) => string;
6
+ declare const buildCloudflareDeploymentTag: (deploymentId: string, prefix?: string) => string;
7
+ declare const getCloudflareAppIdFromTag: (tag: string, prefix?: string) => string | null;
8
+ declare const getCloudflareDeploymentIdFromTag: (tag: string, prefix?: string) => string | null;
9
+ declare const findCloudflareAppTag: (tags: string[], prefix?: string) => string | null;
10
+ declare const findCloudflareDeploymentTag: (tags: string[], prefix?: string) => string | null;
11
+ //#endregion
12
+ export { DEFAULT_DEPLOYMENT_TAG_PREFIX, buildCloudflareAppTag, buildCloudflareDeploymentTag, findCloudflareAppTag, findCloudflareDeploymentTag, getCloudflareAppIdFromTag, getCloudflareDeploymentIdFromTag, normalizeCloudflareDeploymentTagPrefix, sanitizeCloudflareTag };
13
+ //# sourceMappingURL=deployment-tag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment-tag.d.ts","names":[],"sources":["../../src/deployment-tag.ts"],"mappings":";cAAa,6BAAA;AAAA,cAMA,qBAAA,GAAyB,KAAA;AAAA,cASzB,sCAAA,GAA0C,MAAA;AAAA,cA2G1C,qBAAA,GAAyB,KAAA,UAAe,MAAA;AAAA,cAIxC,4BAAA,GAAgC,YAAA,UAAsB,MAAA;AAAA,cAItD,yBAAA,GAA6B,GAAA,UAAa,MAAA;AAAA,cAI1C,gCAAA,GAAoC,GAAA,UAAa,MAAA;AAAA,cAIjD,oBAAA,GAAwB,IAAA,YAAgB,MAAA;AAAA,cAIxC,2BAAA,GAA+B,IAAA,YAAgB,MAAA"}
@@ -0,0 +1,73 @@
1
+ //#region src/deployment-tag.ts
2
+ const DEFAULT_DEPLOYMENT_TAG_PREFIX = "fragno";
3
+ const MAX_TAG_LENGTH = 63;
4
+ const APP_TAG_SEGMENT = "app";
5
+ const DEPLOYMENT_TAG_SEGMENT = "dep";
6
+ const sanitizeCloudflareTag = (value) => {
7
+ return value.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+/, "").replace(/-+$/, "").replace(/--+/g, "-");
8
+ };
9
+ const normalizeCloudflareDeploymentTagPrefix = (prefix) => {
10
+ const normalized = sanitizeCloudflareTag(prefix ?? DEFAULT_DEPLOYMENT_TAG_PREFIX);
11
+ if (!normalized) throw new Error("Cloudflare deployment tag prefix must contain at least one alphanumeric character.");
12
+ return normalized;
13
+ };
14
+ const trimTrailingHyphens = (value) => value.replace(/-+$/, "");
15
+ const capCloudflareTagPrefix = (prefix, kind, normalizedId, label) => {
16
+ const maxPrefixLength = MAX_TAG_LENGTH - `-${kind}-`.length - normalizedId.length;
17
+ if (maxPrefixLength < 1) throw new Error(`Cloudflare ${label} tag '${normalizedId}' exceeds the ${MAX_TAG_LENGTH} character limit even with the shortest possible prefix.`);
18
+ const cappedPrefix = trimTrailingHyphens(prefix.slice(0, maxPrefixLength));
19
+ if (!cappedPrefix) throw new Error(`Cloudflare ${label} tag prefix must retain at least one alphanumeric character after capping.`);
20
+ return cappedPrefix;
21
+ };
22
+ const buildCloudflareScopedTag = (id, prefix, kind, label) => {
23
+ const normalizedPrefix = normalizeCloudflareDeploymentTagPrefix(prefix);
24
+ const normalizedId = sanitizeCloudflareTag(id);
25
+ if (!normalizedId) throw new Error(`Cloudflare ${label} id must contain at least one alphanumeric character.`);
26
+ return `${capCloudflareTagPrefix(normalizedPrefix, kind, normalizedId, label)}-${kind}-${normalizedId}`;
27
+ };
28
+ const readCloudflareScopedTagId = (tag, prefix, kind, label) => {
29
+ const normalizedTag = sanitizeCloudflareTag(tag);
30
+ const marker = `-${kind}-`;
31
+ const normalizedPrefix = normalizeCloudflareDeploymentTagPrefix(prefix);
32
+ let markerIndex = normalizedTag.indexOf(marker);
33
+ while (markerIndex >= 1) {
34
+ const normalizedId = normalizedTag.slice(markerIndex + marker.length);
35
+ if (normalizedId) try {
36
+ const expectedPrefix = capCloudflareTagPrefix(normalizedPrefix, kind, normalizedId, label);
37
+ if (normalizedTag.slice(0, markerIndex) === expectedPrefix) return normalizedId;
38
+ } catch {}
39
+ markerIndex = normalizedTag.indexOf(marker, markerIndex + 1);
40
+ }
41
+ return null;
42
+ };
43
+ const findCloudflareScopedTag = (tags, prefix, kind, label) => {
44
+ const marker = `-${kind}-`;
45
+ for (const tag of tags) {
46
+ const normalizedTag = sanitizeCloudflareTag(tag);
47
+ if (!normalizedTag.includes(marker)) continue;
48
+ if (readCloudflareScopedTagId(normalizedTag, prefix, kind, label) !== null) return normalizedTag;
49
+ }
50
+ return null;
51
+ };
52
+ const buildCloudflareAppTag = (appId, prefix) => {
53
+ return buildCloudflareScopedTag(appId, prefix, APP_TAG_SEGMENT, "app");
54
+ };
55
+ const buildCloudflareDeploymentTag = (deploymentId, prefix) => {
56
+ return buildCloudflareScopedTag(deploymentId, prefix, DEPLOYMENT_TAG_SEGMENT, "deployment");
57
+ };
58
+ const getCloudflareAppIdFromTag = (tag, prefix) => {
59
+ return readCloudflareScopedTagId(tag, prefix, APP_TAG_SEGMENT, "app");
60
+ };
61
+ const getCloudflareDeploymentIdFromTag = (tag, prefix) => {
62
+ return readCloudflareScopedTagId(tag, prefix, DEPLOYMENT_TAG_SEGMENT, "deployment");
63
+ };
64
+ const findCloudflareAppTag = (tags, prefix) => {
65
+ return findCloudflareScopedTag(tags, prefix, APP_TAG_SEGMENT, "app");
66
+ };
67
+ const findCloudflareDeploymentTag = (tags, prefix) => {
68
+ return findCloudflareScopedTag(tags, prefix, DEPLOYMENT_TAG_SEGMENT, "deployment");
69
+ };
70
+
71
+ //#endregion
72
+ export { DEFAULT_DEPLOYMENT_TAG_PREFIX, buildCloudflareAppTag, buildCloudflareDeploymentTag, findCloudflareAppTag, findCloudflareDeploymentTag, getCloudflareAppIdFromTag, getCloudflareDeploymentIdFromTag, normalizeCloudflareDeploymentTagPrefix, sanitizeCloudflareTag };
73
+ //# sourceMappingURL=deployment-tag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment-tag.js","names":[],"sources":["../../src/deployment-tag.ts"],"sourcesContent":["export const DEFAULT_DEPLOYMENT_TAG_PREFIX = \"fragno\";\n\nconst MAX_TAG_LENGTH = 63;\nconst APP_TAG_SEGMENT = \"app\";\nconst DEPLOYMENT_TAG_SEGMENT = \"dep\";\n\nexport const sanitizeCloudflareTag = (value: string) => {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, \"-\")\n .replace(/^-+/, \"\")\n .replace(/-+$/, \"\")\n .replace(/--+/g, \"-\");\n};\n\nexport const normalizeCloudflareDeploymentTagPrefix = (prefix?: string) => {\n const normalized = sanitizeCloudflareTag(prefix ?? DEFAULT_DEPLOYMENT_TAG_PREFIX);\n if (!normalized) {\n throw new Error(\n \"Cloudflare deployment tag prefix must contain at least one alphanumeric character.\",\n );\n }\n\n return normalized;\n};\n\nconst trimTrailingHyphens = (value: string) => value.replace(/-+$/, \"\");\n\nconst capCloudflareTagPrefix = (\n prefix: string,\n kind: string,\n normalizedId: string,\n label: string,\n) => {\n const maxPrefixLength = MAX_TAG_LENGTH - `-${kind}-`.length - normalizedId.length;\n if (maxPrefixLength < 1) {\n throw new Error(\n `Cloudflare ${label} tag '${normalizedId}' exceeds the ${MAX_TAG_LENGTH} character limit even with the shortest possible prefix.`,\n );\n }\n\n const cappedPrefix = trimTrailingHyphens(prefix.slice(0, maxPrefixLength));\n if (!cappedPrefix) {\n throw new Error(\n `Cloudflare ${label} tag prefix must retain at least one alphanumeric character after capping.`,\n );\n }\n\n return cappedPrefix;\n};\n\nconst buildCloudflareScopedTag = (\n id: string,\n prefix: string | undefined,\n kind: string,\n label: string,\n) => {\n const normalizedPrefix = normalizeCloudflareDeploymentTagPrefix(prefix);\n const normalizedId = sanitizeCloudflareTag(id);\n\n if (!normalizedId) {\n throw new Error(`Cloudflare ${label} id must contain at least one alphanumeric character.`);\n }\n\n const cappedPrefix = capCloudflareTagPrefix(normalizedPrefix, kind, normalizedId, label);\n return `${cappedPrefix}-${kind}-${normalizedId}`;\n};\n\nconst readCloudflareScopedTagId = (\n tag: string,\n prefix: string | undefined,\n kind: string,\n label: string,\n) => {\n const normalizedTag = sanitizeCloudflareTag(tag);\n const marker = `-${kind}-`;\n const normalizedPrefix = normalizeCloudflareDeploymentTagPrefix(prefix);\n let markerIndex = normalizedTag.indexOf(marker);\n\n while (markerIndex >= 1) {\n const normalizedId = normalizedTag.slice(markerIndex + marker.length);\n if (normalizedId) {\n try {\n const expectedPrefix = capCloudflareTagPrefix(normalizedPrefix, kind, normalizedId, label);\n const tagPrefix = normalizedTag.slice(0, markerIndex);\n if (tagPrefix === expectedPrefix) {\n return normalizedId;\n }\n } catch {\n // Ignore unrelated tags that happen to contain the same marker.\n }\n }\n\n markerIndex = normalizedTag.indexOf(marker, markerIndex + 1);\n }\n\n return null;\n};\n\nconst findCloudflareScopedTag = (\n tags: string[],\n prefix: string | undefined,\n kind: string,\n label: string,\n) => {\n const marker = `-${kind}-`;\n\n for (const tag of tags) {\n const normalizedTag = sanitizeCloudflareTag(tag);\n if (!normalizedTag.includes(marker)) {\n continue;\n }\n\n const id = readCloudflareScopedTagId(normalizedTag, prefix, kind, label);\n if (id !== null) {\n return normalizedTag;\n }\n }\n\n return null;\n};\n\nexport const buildCloudflareAppTag = (appId: string, prefix?: string) => {\n return buildCloudflareScopedTag(appId, prefix, APP_TAG_SEGMENT, \"app\");\n};\n\nexport const buildCloudflareDeploymentTag = (deploymentId: string, prefix?: string) => {\n return buildCloudflareScopedTag(deploymentId, prefix, DEPLOYMENT_TAG_SEGMENT, \"deployment\");\n};\n\nexport const getCloudflareAppIdFromTag = (tag: string, prefix?: string) => {\n return readCloudflareScopedTagId(tag, prefix, APP_TAG_SEGMENT, \"app\");\n};\n\nexport const getCloudflareDeploymentIdFromTag = (tag: string, prefix?: string) => {\n return readCloudflareScopedTagId(tag, prefix, DEPLOYMENT_TAG_SEGMENT, \"deployment\");\n};\n\nexport const findCloudflareAppTag = (tags: string[], prefix?: string) => {\n return findCloudflareScopedTag(tags, prefix, APP_TAG_SEGMENT, \"app\");\n};\n\nexport const findCloudflareDeploymentTag = (tags: string[], prefix?: string) => {\n return findCloudflareScopedTag(tags, prefix, DEPLOYMENT_TAG_SEGMENT, \"deployment\");\n};\n"],"mappings":";AAAA,MAAa,gCAAgC;AAE7C,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AACxB,MAAM,yBAAyB;AAE/B,MAAa,yBAAyB,UAAkB;AACtD,QAAO,MACJ,aAAa,CACb,QAAQ,gBAAgB,IAAI,CAC5B,QAAQ,OAAO,GAAG,CAClB,QAAQ,OAAO,GAAG,CAClB,QAAQ,QAAQ,IAAI;;AAGzB,MAAa,0CAA0C,WAAoB;CACzE,MAAM,aAAa,sBAAsB,UAAU,8BAA8B;AACjF,KAAI,CAAC,WACH,OAAM,IAAI,MACR,qFACD;AAGH,QAAO;;AAGT,MAAM,uBAAuB,UAAkB,MAAM,QAAQ,OAAO,GAAG;AAEvE,MAAM,0BACJ,QACA,MACA,cACA,UACG;CACH,MAAM,kBAAkB,iBAAiB,IAAI,KAAK,GAAG,SAAS,aAAa;AAC3E,KAAI,kBAAkB,EACpB,OAAM,IAAI,MACR,cAAc,MAAM,QAAQ,aAAa,gBAAgB,eAAe,0DACzE;CAGH,MAAM,eAAe,oBAAoB,OAAO,MAAM,GAAG,gBAAgB,CAAC;AAC1E,KAAI,CAAC,aACH,OAAM,IAAI,MACR,cAAc,MAAM,4EACrB;AAGH,QAAO;;AAGT,MAAM,4BACJ,IACA,QACA,MACA,UACG;CACH,MAAM,mBAAmB,uCAAuC,OAAO;CACvE,MAAM,eAAe,sBAAsB,GAAG;AAE9C,KAAI,CAAC,aACH,OAAM,IAAI,MAAM,cAAc,MAAM,uDAAuD;AAI7F,QAAO,GADc,uBAAuB,kBAAkB,MAAM,cAAc,MAAM,CACjE,GAAG,KAAK,GAAG;;AAGpC,MAAM,6BACJ,KACA,QACA,MACA,UACG;CACH,MAAM,gBAAgB,sBAAsB,IAAI;CAChD,MAAM,SAAS,IAAI,KAAK;CACxB,MAAM,mBAAmB,uCAAuC,OAAO;CACvE,IAAI,cAAc,cAAc,QAAQ,OAAO;AAE/C,QAAO,eAAe,GAAG;EACvB,MAAM,eAAe,cAAc,MAAM,cAAc,OAAO,OAAO;AACrE,MAAI,aACF,KAAI;GACF,MAAM,iBAAiB,uBAAuB,kBAAkB,MAAM,cAAc,MAAM;AAE1F,OADkB,cAAc,MAAM,GAAG,YAAY,KACnC,eAChB,QAAO;UAEH;AAKV,gBAAc,cAAc,QAAQ,QAAQ,cAAc,EAAE;;AAG9D,QAAO;;AAGT,MAAM,2BACJ,MACA,QACA,MACA,UACG;CACH,MAAM,SAAS,IAAI,KAAK;AAExB,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,gBAAgB,sBAAsB,IAAI;AAChD,MAAI,CAAC,cAAc,SAAS,OAAO,CACjC;AAIF,MADW,0BAA0B,eAAe,QAAQ,MAAM,MAAM,KAC7D,KACT,QAAO;;AAIX,QAAO;;AAGT,MAAa,yBAAyB,OAAe,WAAoB;AACvE,QAAO,yBAAyB,OAAO,QAAQ,iBAAiB,MAAM;;AAGxE,MAAa,gCAAgC,cAAsB,WAAoB;AACrF,QAAO,yBAAyB,cAAc,QAAQ,wBAAwB,aAAa;;AAG7F,MAAa,6BAA6B,KAAa,WAAoB;AACzE,QAAO,0BAA0B,KAAK,QAAQ,iBAAiB,MAAM;;AAGvE,MAAa,oCAAoC,KAAa,WAAoB;AAChF,QAAO,0BAA0B,KAAK,QAAQ,wBAAwB,aAAa;;AAGrF,MAAa,wBAAwB,MAAgB,WAAoB;AACvE,QAAO,wBAAwB,MAAM,QAAQ,iBAAiB,MAAM;;AAGtE,MAAa,+BAA+B,MAAgB,WAAoB;AAC9E,QAAO,wBAAwB,MAAM,QAAQ,wBAAwB,aAAa"}