alchemy 0.70.1 → 0.70.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/alchemy.js +1 -1
- package/lib/clickhouse/organization.d.ts +1 -1
- package/lib/clickhouse/organization.js +2 -2
- package/lib/clickhouse/organization.js.map +1 -1
- package/lib/clickhouse/service.d.ts +28 -18
- package/lib/clickhouse/service.d.ts.map +1 -1
- package/lib/clickhouse/service.js +61 -9
- package/lib/clickhouse/service.js.map +1 -1
- package/lib/cloudflare/account-api-token.js +1 -1
- package/lib/cloudflare/account-api-token.js.map +1 -1
- package/lib/cloudflare/bun-spa/bun-spa.d.ts.map +1 -1
- package/lib/cloudflare/bun-spa/bun-spa.js.map +1 -1
- package/lib/cloudflare/d1-database.d.ts +1 -1
- package/lib/cloudflare/d1-database.d.ts.map +1 -1
- package/lib/cloudflare/d1-database.js +1 -1
- package/lib/cloudflare/d1-database.js.map +1 -1
- package/lib/cloudflare/permission-groups.d.ts +1489 -26
- package/lib/cloudflare/permission-groups.d.ts.map +1 -1
- package/lib/cloudflare/permission-groups.js +1787 -6
- package/lib/cloudflare/permission-groups.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts +14 -2
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +4 -1
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker-stub.d.ts +3 -1
- package/lib/cloudflare/worker-stub.d.ts.map +1 -1
- package/lib/cloudflare/worker-stub.js +19 -7
- package/lib/cloudflare/worker-stub.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +74 -10
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +16 -7
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/util/camel-to-snake.d.ts +12 -0
- package/lib/util/camel-to-snake.d.ts.map +1 -0
- package/lib/util/camel-to-snake.js +16 -0
- package/lib/util/camel-to-snake.js.map +1 -0
- package/package.json +1 -1
- package/src/clickhouse/organization.ts +2 -2
- package/src/clickhouse/service.ts +124 -33
- package/src/cloudflare/account-api-token.ts +1 -1
- package/src/cloudflare/bun-spa/bun-spa.ts +0 -1
- package/src/cloudflare/d1-database.ts +2 -2
- package/src/cloudflare/permission-groups.ts +1832 -45
- package/src/cloudflare/worker-metadata.ts +20 -3
- package/src/cloudflare/worker-stub.ts +28 -11
- package/src/cloudflare/worker.ts +98 -17
- package/src/util/camel-to-snake.ts +88 -0
- package/workers/tunnel-proxy.js +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assertNever } from "../util/assert-never.ts";
|
|
2
|
+
import { camelToSnakeObjectDeep } from "../util/camel-to-snake.ts";
|
|
2
3
|
import { logger } from "../util/logger.ts";
|
|
3
4
|
import { memoize } from "../util/memoize.ts";
|
|
4
5
|
import { extractCloudflareResult } from "./api-response.ts";
|
|
@@ -180,8 +181,20 @@ export interface WorkerMetadata {
|
|
|
180
181
|
compatibility_date: string;
|
|
181
182
|
compatibility_flags?: string[];
|
|
182
183
|
bindings: WorkerBindingSpec[];
|
|
183
|
-
observability
|
|
184
|
-
enabled
|
|
184
|
+
observability?: {
|
|
185
|
+
enabled?: boolean;
|
|
186
|
+
head_sampling_rate?: number;
|
|
187
|
+
logs?: {
|
|
188
|
+
enabled?: boolean;
|
|
189
|
+
head_sampling_rate?: number;
|
|
190
|
+
invocation_logs?: boolean;
|
|
191
|
+
};
|
|
192
|
+
traces?: {
|
|
193
|
+
enabled?: boolean;
|
|
194
|
+
head_sampling_rate?: number;
|
|
195
|
+
persist?: boolean;
|
|
196
|
+
destinations?: string[];
|
|
197
|
+
};
|
|
185
198
|
};
|
|
186
199
|
logpush?: boolean;
|
|
187
200
|
migrations?: SingleStepMigration;
|
|
@@ -235,6 +248,7 @@ export async function prepareWorkerMetadata(
|
|
|
235
248
|
...(oldSettings?.tags ?? []),
|
|
236
249
|
]),
|
|
237
250
|
);
|
|
251
|
+
|
|
238
252
|
const oldBindings = oldSettings?.bindings;
|
|
239
253
|
// we use Cloudflare Worker tags to store a mapping between Alchemy's stable identifier and the binding name
|
|
240
254
|
// e.g.
|
|
@@ -315,6 +329,8 @@ export async function prepareWorkerMetadata(
|
|
|
315
329
|
return [];
|
|
316
330
|
});
|
|
317
331
|
|
|
332
|
+
const observability = camelToSnakeObjectDeep(props.observability);
|
|
333
|
+
|
|
318
334
|
// Prepare metadata with bindings
|
|
319
335
|
const meta: WorkerMetadata = {
|
|
320
336
|
compatibility_date: props.compatibilityDate,
|
|
@@ -324,7 +340,8 @@ export async function prepareWorkerMetadata(
|
|
|
324
340
|
),
|
|
325
341
|
bindings: [],
|
|
326
342
|
observability: {
|
|
327
|
-
|
|
343
|
+
...observability,
|
|
344
|
+
enabled: observability?.enabled !== false,
|
|
328
345
|
},
|
|
329
346
|
logpush: props.logpush ?? false,
|
|
330
347
|
// TODO(sam): base64 encode instead? 0 collision risk vs readability.
|
|
@@ -128,7 +128,7 @@ export const WorkerStub = Resource("cloudflare::WorkerStub", async function <
|
|
|
128
128
|
} as WorkerStub<RPC>;
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
-
async function exists(
|
|
131
|
+
export async function exists(
|
|
132
132
|
api: CloudflareApi,
|
|
133
133
|
workerName: string,
|
|
134
134
|
): Promise<boolean> {
|
|
@@ -145,9 +145,10 @@ async function exists(
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
async function createEmptyWorker(
|
|
148
|
+
export async function createEmptyWorker(
|
|
149
149
|
api: CloudflareApi,
|
|
150
150
|
workerName: string,
|
|
151
|
+
version?: string,
|
|
151
152
|
): Promise<void> {
|
|
152
153
|
// Minimal empty worker script
|
|
153
154
|
const emptyScript = `export default {
|
|
@@ -178,6 +179,12 @@ async function createEmptyWorker(
|
|
|
178
179
|
main_module: "worker.js",
|
|
179
180
|
compatibility_date: "2025-04-20",
|
|
180
181
|
bindings: [],
|
|
182
|
+
...(version != null && {
|
|
183
|
+
annotations: {
|
|
184
|
+
"workers/tag": version,
|
|
185
|
+
"workers/message": `Version ${version}`,
|
|
186
|
+
},
|
|
187
|
+
}),
|
|
181
188
|
}),
|
|
182
189
|
],
|
|
183
190
|
{
|
|
@@ -189,14 +196,24 @@ async function createEmptyWorker(
|
|
|
189
196
|
// Upload worker script
|
|
190
197
|
await extractCloudflareResult(
|
|
191
198
|
`create empty worker "${workerName}"`,
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
199
|
+
version != null
|
|
200
|
+
? api.post(
|
|
201
|
+
`/accounts/${api.accountId}/workers/scripts/${workerName}/versions`,
|
|
202
|
+
formData,
|
|
203
|
+
{
|
|
204
|
+
headers: {
|
|
205
|
+
"Content-Type": "multipart/form-data",
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
)
|
|
209
|
+
: api.put(
|
|
210
|
+
`/accounts/${api.accountId}/workers/scripts/${workerName}`,
|
|
211
|
+
formData,
|
|
212
|
+
{
|
|
213
|
+
headers: {
|
|
214
|
+
"Content-Type": "multipart/form-data",
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
),
|
|
201
218
|
);
|
|
202
219
|
}
|
package/src/cloudflare/worker.ts
CHANGED
|
@@ -61,6 +61,7 @@ import { Workflow, isWorkflow, upsertWorkflow } from "./workflow.ts";
|
|
|
61
61
|
import "../esbuild/bundle.ts";
|
|
62
62
|
import { Scope } from "../scope.ts";
|
|
63
63
|
import type { WorkerRef } from "./worker-ref.ts";
|
|
64
|
+
import { createEmptyWorker, exists } from "./worker-stub.ts";
|
|
64
65
|
|
|
65
66
|
/**
|
|
66
67
|
* Configuration options for static assets
|
|
@@ -166,18 +167,12 @@ export interface BaseWorkerProps<
|
|
|
166
167
|
url?: boolean;
|
|
167
168
|
|
|
168
169
|
/**
|
|
169
|
-
*
|
|
170
|
+
* Specify the observability behavior of the Worker.
|
|
170
171
|
*
|
|
171
|
-
*
|
|
172
|
-
* @default
|
|
172
|
+
* @see https://developers.cloudflare.com/workers/wrangler/configuration/#observability
|
|
173
|
+
* @default - `enabled: true`
|
|
173
174
|
*/
|
|
174
|
-
observability?:
|
|
175
|
-
/**
|
|
176
|
-
* Whether to enable worker logs
|
|
177
|
-
* @default true
|
|
178
|
-
*/
|
|
179
|
-
enabled?: boolean;
|
|
180
|
-
};
|
|
175
|
+
observability?: WorkerObservability;
|
|
181
176
|
|
|
182
177
|
/**
|
|
183
178
|
* Enable Workers Logpush to export trace events (request/response metadata,
|
|
@@ -393,6 +388,85 @@ export interface BaseWorkerProps<
|
|
|
393
388
|
tailConsumers?: Array<Worker | { service: string }>;
|
|
394
389
|
}
|
|
395
390
|
|
|
391
|
+
export interface WorkerObservability {
|
|
392
|
+
/**
|
|
393
|
+
* If observability is enabled for this Worker
|
|
394
|
+
*
|
|
395
|
+
* @default true
|
|
396
|
+
*/
|
|
397
|
+
enabled?: boolean;
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* A number between 0 and 1, where 0 indicates zero out of one hundred requests are logged, and 1 indicates every request is logged.
|
|
401
|
+
* If head_sampling_rate is unspecified, it is configured to a default value of 1 (100%).
|
|
402
|
+
* @see https://developers.cloudflare.com/workers/observability/logs/workers-logs/#head-based-sampling
|
|
403
|
+
* @default 1
|
|
404
|
+
*/
|
|
405
|
+
headSamplingRate?: number;
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Configuration for worker logs
|
|
409
|
+
*/
|
|
410
|
+
logs?: {
|
|
411
|
+
/**
|
|
412
|
+
* Whether logs are enabled
|
|
413
|
+
* @default true
|
|
414
|
+
*/
|
|
415
|
+
enabled?: boolean;
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* The sampling rate for logs
|
|
419
|
+
*/
|
|
420
|
+
headSamplingRate?: number;
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Set to false to disable invocation logs
|
|
424
|
+
* @default true
|
|
425
|
+
*/
|
|
426
|
+
invocationLogs?: boolean;
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* If logs should be persisted to the Cloudflare observability platform where they can be queried in the dashboard.
|
|
430
|
+
* @default true
|
|
431
|
+
*/
|
|
432
|
+
persist?: boolean;
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* What destinations logs emitted from the Worker should be sent to.
|
|
436
|
+
* @default []
|
|
437
|
+
*/
|
|
438
|
+
destinations?: string[];
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Configuration for worker traces
|
|
443
|
+
*/
|
|
444
|
+
traces?: {
|
|
445
|
+
/**
|
|
446
|
+
* Whether traces are enabled
|
|
447
|
+
* @default true
|
|
448
|
+
*/
|
|
449
|
+
enabled?: boolean;
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* The sampling rate for traces
|
|
453
|
+
*/
|
|
454
|
+
headSamplingRate?: number;
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* If traces should be persisted to the Cloudflare observability platform where they can be queried in the dashboard.
|
|
458
|
+
* @default true
|
|
459
|
+
*/
|
|
460
|
+
persist?: boolean;
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* What destinations traces emitted from the Worker should be sent to.
|
|
464
|
+
* @default []
|
|
465
|
+
*/
|
|
466
|
+
destinations?: string[];
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
396
470
|
export interface InlineWorkerProps<
|
|
397
471
|
B extends Bindings | undefined = Bindings,
|
|
398
472
|
RPC extends Rpc.WorkerEntrypointBranded = Rpc.WorkerEntrypointBranded,
|
|
@@ -863,13 +937,20 @@ const _Worker = Resource(
|
|
|
863
937
|
durableObjects: options.durableObjects,
|
|
864
938
|
workflows: options.workflows,
|
|
865
939
|
});
|
|
866
|
-
if (
|
|
940
|
+
if (this.output?.dev?.hasRemote !== false) {
|
|
867
941
|
const api = await createCloudflareApi(props);
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
942
|
+
if (props.version) {
|
|
943
|
+
//* if the worker exists we deploy an empty version so we can destroy
|
|
944
|
+
if (await exists(api, options.name)) {
|
|
945
|
+
await createEmptyWorker(api, options.name, props.version);
|
|
946
|
+
}
|
|
947
|
+
} else {
|
|
948
|
+
await deleteQueueConsumers(api, options.name);
|
|
949
|
+
await deleteWorker(api, {
|
|
950
|
+
scriptName: options.name,
|
|
951
|
+
dispatchNamespace: options.dispatchNamespace,
|
|
952
|
+
});
|
|
953
|
+
}
|
|
873
954
|
}
|
|
874
955
|
return this.destroy();
|
|
875
956
|
}
|
|
@@ -1607,7 +1688,7 @@ async function assertWorkerDoesNotExist(
|
|
|
1607
1688
|
);
|
|
1608
1689
|
}
|
|
1609
1690
|
|
|
1610
|
-
async function getScriptMetadata(
|
|
1691
|
+
export async function getScriptMetadata(
|
|
1611
1692
|
api: CloudflareApi,
|
|
1612
1693
|
scriptName: string,
|
|
1613
1694
|
): Promise<WorkerScriptMetadata | undefined> {
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export interface LogPushProps {
|
|
2
|
+
maxFooBar: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function camelToSnakeObjectDeep<T extends object | undefined>(
|
|
6
|
+
obj: T,
|
|
7
|
+
): T extends undefined ? undefined : CamelToSnake<T> {
|
|
8
|
+
return (
|
|
9
|
+
obj
|
|
10
|
+
? Object.fromEntries(
|
|
11
|
+
Object.entries(obj).map(([key, value]) => [
|
|
12
|
+
key
|
|
13
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2") // Handle consecutive capitals: "FOOBar" -> "FOO_Bar"
|
|
14
|
+
.replace(/([a-z])([A-Z])/g, "$1_$2") // Handle normal camelCase: "fooBar" -> "foo_Bar"
|
|
15
|
+
.toLowerCase(),
|
|
16
|
+
Array.isArray(value)
|
|
17
|
+
? value.map(camelToSnakeObjectDeep)
|
|
18
|
+
: typeof value === "object" && value !== null
|
|
19
|
+
? camelToSnakeObjectDeep(value)
|
|
20
|
+
: value,
|
|
21
|
+
]),
|
|
22
|
+
)
|
|
23
|
+
: undefined
|
|
24
|
+
) as T extends undefined ? undefined : CamelToSnake<T>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Helper to check if a character is uppercase
|
|
28
|
+
type IsUpper<C extends string> = C extends Uppercase<C>
|
|
29
|
+
? C extends Lowercase<C>
|
|
30
|
+
? false // Not a letter
|
|
31
|
+
: true // Uppercase letter
|
|
32
|
+
: false;
|
|
33
|
+
|
|
34
|
+
// Helper to check if a character is lowercase
|
|
35
|
+
type IsLower<C extends string> = C extends Lowercase<C>
|
|
36
|
+
? C extends Uppercase<C>
|
|
37
|
+
? false // Not a letter
|
|
38
|
+
: true // Lowercase letter
|
|
39
|
+
: false;
|
|
40
|
+
|
|
41
|
+
// Convert a camelCase string to snake_case at the type level
|
|
42
|
+
type CamelToSnakeString<
|
|
43
|
+
S extends string,
|
|
44
|
+
Acc extends string = "",
|
|
45
|
+
> = S extends `${infer First}${infer Second}${infer Rest}`
|
|
46
|
+
? IsUpper<First> extends true
|
|
47
|
+
? IsUpper<Second> extends true
|
|
48
|
+
? // FOO... pattern - check if next char is lowercase
|
|
49
|
+
Rest extends `${infer Third}${infer _}`
|
|
50
|
+
? IsLower<Third> extends true
|
|
51
|
+
? // FOOBar -> add First, add _, add Second lowercase, continue with Rest
|
|
52
|
+
CamelToSnakeString<
|
|
53
|
+
Rest,
|
|
54
|
+
`${Acc}${Lowercase<First>}_${Lowercase<Second>}`
|
|
55
|
+
>
|
|
56
|
+
: // FOO -> continue
|
|
57
|
+
CamelToSnakeString<`${Second}${Rest}`, `${Acc}${Lowercase<First>}`>
|
|
58
|
+
: // Only two chars left, both upper
|
|
59
|
+
CamelToSnakeString<`${Second}${Rest}`, `${Acc}${Lowercase<First>}`>
|
|
60
|
+
: // FooBar or Foo - add underscore before uppercase if not at start
|
|
61
|
+
CamelToSnakeString<
|
|
62
|
+
`${Second}${Rest}`,
|
|
63
|
+
Acc extends "" ? `${Lowercase<First>}` : `${Acc}_${Lowercase<First>}`
|
|
64
|
+
>
|
|
65
|
+
: IsLower<First> extends true
|
|
66
|
+
? IsUpper<Second> extends true
|
|
67
|
+
? // fooBar -> just add the lowercase char, underscore will be added when processing uppercase
|
|
68
|
+
CamelToSnakeString<`${Second}${Rest}`, `${Acc}${First}`>
|
|
69
|
+
: // foobar -> continue
|
|
70
|
+
CamelToSnakeString<`${Second}${Rest}`, `${Acc}${First}`>
|
|
71
|
+
: // Number or special char
|
|
72
|
+
CamelToSnakeString<`${Second}${Rest}`, `${Acc}${First}`>
|
|
73
|
+
: S extends `${infer Last}`
|
|
74
|
+
? `${Acc}${Lowercase<Last>}`
|
|
75
|
+
: Acc;
|
|
76
|
+
|
|
77
|
+
// Deep transformation for objects
|
|
78
|
+
type CamelToSnake<T> = T extends object
|
|
79
|
+
? T extends Array<infer U>
|
|
80
|
+
? Array<CamelToSnake<U>>
|
|
81
|
+
: T extends Date | RegExp | Function
|
|
82
|
+
? T // Don't transform special object types
|
|
83
|
+
: {
|
|
84
|
+
[K in keyof T as K extends string
|
|
85
|
+
? CamelToSnakeString<K>
|
|
86
|
+
: K]: CamelToSnake<T[K]>;
|
|
87
|
+
}
|
|
88
|
+
: T;
|
package/workers/tunnel-proxy.js
CHANGED
|
@@ -34,7 +34,7 @@ var h={async fetch(e,r){let n=new URL(e.url);n.host=r.TUNNEL_HOST;let l=new Head
|
|
|
34
34
|
Alchemy</a>.</p>
|
|
35
35
|
</div>
|
|
36
36
|
<div class="bg-slate-200 px-5 py-3 flex flex-col w-full max-w-lg">
|
|
37
|
-
<p class="text-sm text-slate-500">Alchemy 0.70.
|
|
37
|
+
<p class="text-sm text-slate-500">Alchemy 0.70.2</p>
|
|
38
38
|
</div>
|
|
39
39
|
</div>
|
|
40
40
|
</body>
|