@base44/functions-compiler 0.1.0 → 0.1.4

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.
@@ -3,6 +3,7 @@ import nodeNet from "node:net";
3
3
  import { Duplex } from "node:stream";
4
4
 
5
5
  import { isCloudflareTcpSocket } from "./tcp";
6
+ import type { CloudflareTcpSocket } from "./types";
6
7
 
7
8
  interface NodeNetSocket extends Duplex {
8
9
  connecting: boolean;
@@ -95,10 +96,7 @@ export function buildNodeNetSocket(
95
96
  label: string,
96
97
  options: { deferConnectEvent?: boolean } = {},
97
98
  ): NodeNetSocket {
98
- if (!isCloudflareTcpSocket(socket)) {
99
- throw new Error(`${label} did not return a readable/writable TCP socket`);
100
- }
101
- const cloudflareSocket = socket;
99
+ let cloudflareSocket: CloudflareTcpSocket | undefined;
102
100
 
103
101
  let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;
104
102
  let writer: WritableStreamDefaultWriter<Uint8Array> | null = null;
@@ -107,6 +105,14 @@ export function buildNodeNetSocket(
107
105
  let readableEnded = false;
108
106
  let timeout: ReturnType<typeof setTimeout> | null = null;
109
107
  let writeChain = Promise.resolve();
108
+ const socketReady = Promise.resolve(socket).then((resolved) => {
109
+ if (!isCloudflareTcpSocket(resolved)) {
110
+ throw new Error(`${label} did not return a readable/writable TCP socket`);
111
+ }
112
+ cloudflareSocket = resolved;
113
+ if (closed) resolved.close?.();
114
+ return resolved;
115
+ });
110
116
 
111
117
  function clearTimer() {
112
118
  if (!timeout) return;
@@ -143,8 +149,9 @@ export function buildNodeNetSocket(
143
149
  const bytes = chunk instanceof Uint8Array ? chunk : Buffer.from(chunk);
144
150
  writeChain = writeChain
145
151
  .then(async () => {
152
+ const connectedSocket = await socketReady;
146
153
  if (closed) throw new Error(`${label} connection is closed`);
147
- const activeWriter = writer ?? cloudflareSocket.writable.getWriter();
154
+ const activeWriter = writer ?? connectedSocket.writable.getWriter();
148
155
  writer = activeWriter;
149
156
  await activeWriter.write(bytes);
150
157
  })
@@ -157,7 +164,7 @@ export function buildNodeNetSocket(
157
164
  writeChain
158
165
  .then(() => {
159
166
  try {
160
- cloudflareSocket.close?.();
167
+ cloudflareSocket?.close?.();
161
168
  } finally {
162
169
  callback();
163
170
  }
@@ -168,7 +175,7 @@ export function buildNodeNetSocket(
168
175
  closed = true;
169
176
  clearTimer();
170
177
  try {
171
- cloudflareSocket.close?.();
178
+ cloudflareSocket?.close?.();
172
179
  } catch {
173
180
  // The stream is already being destroyed; surface the original error below.
174
181
  }
@@ -192,7 +199,7 @@ export function buildNodeNetSocket(
192
199
  };
193
200
 
194
201
  async function readLoop() {
195
- const activeReader = reader ?? cloudflareSocket.readable.getReader();
202
+ const activeReader = reader ?? cloudflareSocket!.readable.getReader();
196
203
  reader = activeReader;
197
204
  try {
198
205
  while (!closed) {
@@ -226,19 +233,15 @@ export function buildNodeNetSocket(
226
233
  }
227
234
  }
228
235
 
229
- if (cloudflareSocket.opened) {
230
- cloudflareSocket.opened.then(
231
- (info) => scheduleConnected(info),
232
- (error) => stream.destroy(error instanceof Error ? error : new Error(String(error))),
233
- );
234
- } else {
235
- queueMicrotask(() => scheduleConnected());
236
- }
237
- cloudflareSocket.closed?.finally(() => {
238
- closed = true;
239
- clearTimer();
240
- endReadable();
241
- releaseLocks();
236
+ socketReady.then(async (connectedSocket) => {
237
+ connectedSocket.closed?.finally(() => {
238
+ closed = true;
239
+ clearTimer();
240
+ endReadable();
241
+ releaseLocks();
242
+ }).catch((error) => stream.destroy(error instanceof Error ? error : new Error(String(error))));
243
+ const info = await connectedSocket.opened;
244
+ scheduleConnected(info);
242
245
  }).catch((error) => stream.destroy(error instanceof Error ? error : new Error(String(error))));
243
246
  return stream;
244
247
  }
@@ -0,0 +1,34 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ import { privateDataSourceReference } from "./manifest";
4
+ import { installPrivateNodeNetAdapter, registerPrivateNodeNetRoute } from "./node-net-adapter";
5
+ import { buildTcpPrivateDataSource } from "./tcp";
6
+
7
+ export function oracle(name: unknown) {
8
+ installPrivateNodeNetAdapter();
9
+ const entry = privateDataSourceReference(name, "oracle");
10
+ const source = buildTcpPrivateDataSource(entry);
11
+ // A numeric alias avoids Oracle's client-side DNS lookup of private hostnames.
12
+ const hash = createHash("sha256").update(String(entry.name)).digest("hex").slice(0, 28);
13
+ const driverHost = `fd44:${hash.match(/.{4}/g)!.join(":")}`;
14
+
15
+ const extension = {
16
+ oracledbOptions() {
17
+ const serviceName = source.database;
18
+ if (!serviceName || !/^[a-zA-Z0-9_.$#-]+$/.test(serviceName)) {
19
+ throw new Error("Enter an Oracle service name using letters, numbers, underscores, dots, $, #, or hyphens.");
20
+ }
21
+ const port = source.port!;
22
+ registerPrivateNodeNetRoute(driverHost, port, {
23
+ label: `Oracle private data source "${entry.name}"`,
24
+ connect: () => source.connect(),
25
+ });
26
+ return Object.freeze({
27
+ user: source.username,
28
+ password: source.password,
29
+ connectString: `(DESCRIPTION=(TRANSPORT_CONNECT_TIMEOUT=10)(CONNECT_TIMEOUT=15)(RETRY_COUNT=0)(ADDRESS=(PROTOCOL=TCP)(HOST=${driverHost})(PORT=${port}))(CONNECT_DATA=(SERVICE_NAME=${serviceName})(SERVER=DEDICATED)))`,
30
+ });
31
+ },
32
+ };
33
+ return Object.freeze(Object.defineProperties(extension, Object.getOwnPropertyDescriptors(source)) as typeof source & typeof extension);
34
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Compile an app's functions into the final Cloudflare Workers shards.
3
+ *
4
+ * The loop is: plan the partition, compile each shard, measure it, and halve any
5
+ * shard whose module is over a ceiling before anything is uploaded. Ported from
6
+ * `_build_shard_with_split` in apper's `cloudflare_wfp_runtime.py`.
7
+ *
8
+ * That function also splits on two exceptions, and neither is a trigger here.
9
+ * The bundler's 413 is its HTTP request-body cap — a property of the shard's
10
+ * packed SOURCE going over the wire, not of the module coming out, and there is
11
+ * no request to reject locally. Cloudflare's 10027 is a real Worker limit, but
12
+ * it is the same 64 MiB `workerRawSizeBreach` already computes from the bytes in
13
+ * hand, so reaching it from a rejected upload is a failsafe, not the design.
14
+ * Measuring is the design, and it happens here.
15
+ *
16
+ * Unlike the compiler's own `bundleApp`, this refuses a partial result. A module
17
+ * missing a handler is not a smaller success, it is a broken app.
18
+ */
19
+ import type { AppFunctionInput } from "../contracts.js";
20
+ import type { BundleErrorItem } from "../errors.js";
21
+ import { type ShardPolicy } from "./plan.js";
22
+ export interface CompiledShard {
23
+ /** Position in the emitted list. Not a deployment identity — the caller owns
24
+ * placement, and a split makes more shards than the plan had. */
25
+ index: number;
26
+ /** Function names in this shard, in the order they were compiled. */
27
+ functions: string[];
28
+ module: string;
29
+ mainModule: string;
30
+ rawBytes: number;
31
+ gzipBytes: number;
32
+ }
33
+ export interface ShardBuildFailure {
34
+ /** The function this is about, when it belongs to one. */
35
+ function?: string;
36
+ message: string;
37
+ errors?: BundleErrorItem[];
38
+ }
39
+ export type ShardBuildResult = {
40
+ ok: true;
41
+ shards: CompiledShard[];
42
+ } | {
43
+ ok: false;
44
+ failures: ShardBuildFailure[];
45
+ };
46
+ /**
47
+ * Compile every function into shards, or fail the whole build.
48
+ *
49
+ * Succeeds only when each declared function appears exactly once across the
50
+ * shards and every one of them compiled.
51
+ */
52
+ export declare function compileFunctionShards(functions: AppFunctionInput[], policy: ShardPolicy, options?: {
53
+ postResponseTelemetry?: boolean;
54
+ runtimeSecrets?: boolean;
55
+ }): Promise<ShardBuildResult>;
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Compile an app's functions into the final Cloudflare Workers shards.
3
+ *
4
+ * The loop is: plan the partition, compile each shard, measure it, and halve any
5
+ * shard whose module is over a ceiling before anything is uploaded. Ported from
6
+ * `_build_shard_with_split` in apper's `cloudflare_wfp_runtime.py`.
7
+ *
8
+ * That function also splits on two exceptions, and neither is a trigger here.
9
+ * The bundler's 413 is its HTTP request-body cap — a property of the shard's
10
+ * packed SOURCE going over the wire, not of the module coming out, and there is
11
+ * no request to reject locally. Cloudflare's 10027 is a real Worker limit, but
12
+ * it is the same 64 MiB `workerRawSizeBreach` already computes from the bytes in
13
+ * hand, so reaching it from a rejected upload is a failsafe, not the design.
14
+ * Measuring is the design, and it happens here.
15
+ *
16
+ * Unlike the compiler's own `bundleApp`, this refuses a partial result. A module
17
+ * missing a handler is not a smaller success, it is a broken app.
18
+ */
19
+ import { bundleApp } from "../bundler.js";
20
+ import { planFreshShards } from "./plan.js";
21
+ import { judgeBundleSize } from "./size.js";
22
+ /** Shards compile independently; bound how many esbuild runs are in flight so
23
+ * peak memory does not scale with the app's function count. */
24
+ const MAX_PARALLEL_SHARDS = 4;
25
+ /**
26
+ * Compile every function into shards, or fail the whole build.
27
+ *
28
+ * Succeeds only when each declared function appears exactly once across the
29
+ * shards and every one of them compiled.
30
+ */
31
+ export async function compileFunctionShards(functions, policy, options = {}) {
32
+ const duplicate = firstDuplicate(functions.map((fn) => fn.name));
33
+ if (duplicate) {
34
+ return fail([{ message: `Duplicate function name "${duplicate}".` }]);
35
+ }
36
+ const byName = new Map(functions.map((fn) => [fn.name, fn]));
37
+ const plan = planFreshShards(functions.map((fn) => fn.name), policy);
38
+ const groups = plan.map((names) => names.map((name) => byName.get(name)));
39
+ const outcomes = await mapWithConcurrency(groups, MAX_PARALLEL_SHARDS, (group) => buildWithSplit(group, policy, options));
40
+ const failures = outcomes.flatMap((o) => (o.ok ? [] : o.failures));
41
+ if (failures.length > 0)
42
+ return fail(failures);
43
+ const shards = outcomes
44
+ .flatMap((o) => (o.ok ? o.shards : []))
45
+ .map((shard, index) => ({ ...shard, index }));
46
+ const missing = assertEveryFunctionPlacedOnce(functions, shards);
47
+ if (missing.length > 0)
48
+ return fail(missing);
49
+ return { ok: true, shards };
50
+ }
51
+ /** Compile one planned group, halving it when its module is over a ceiling. The
52
+ * verdict comes from the bytes, so nothing is uploaded to discover it. */
53
+ async function buildWithSplit(group, policy, options) {
54
+ const response = await bundleApp({ functions: group, ...options });
55
+ const compileFailures = response.functions
56
+ .filter((fn) => !fn.ok)
57
+ .map((fn) => ({
58
+ function: fn.name,
59
+ message: `Function "${fn.name}" failed to compile.`,
60
+ errors: fn.ok ? undefined : fn.errors,
61
+ }));
62
+ // A partial module is what the service ships and a whole-app build must not:
63
+ // the peers compiled, but the app is missing a handler.
64
+ if (compileFailures.length > 0)
65
+ return { ok: false, failures: compileFailures };
66
+ if (!response.ok) {
67
+ return fail([{ message: "The combined build produced no module." }]);
68
+ }
69
+ const verdict = await judgeBundleSize(response.module, policy.gzipCapBytes);
70
+ if (!verdict.breach) {
71
+ return {
72
+ ok: true,
73
+ shards: [shardOf(group, response.module, response.main_module, verdict)],
74
+ };
75
+ }
76
+ if (group.length === 1) {
77
+ // Splitting is exhausted: one function's module alone is over the ceiling.
78
+ return {
79
+ ok: false,
80
+ failures: [{ function: group[0].name, message: verdict.breach }],
81
+ };
82
+ }
83
+ const mid = Math.floor(group.length / 2);
84
+ const halves = await Promise.all([
85
+ buildWithSplit(group.slice(0, mid), policy, options),
86
+ buildWithSplit(group.slice(mid), policy, options),
87
+ ]);
88
+ const halfFailures = halves.flatMap((h) => (h.ok ? [] : h.failures));
89
+ if (halfFailures.length > 0)
90
+ return { ok: false, failures: halfFailures };
91
+ return { ok: true, shards: halves.flatMap((h) => (h.ok ? h.shards : [])) };
92
+ }
93
+ function shardOf(group, module, mainModule, verdict) {
94
+ return {
95
+ functions: group.map((fn) => fn.name),
96
+ module,
97
+ mainModule,
98
+ rawBytes: verdict.rawBytes,
99
+ gzipBytes: verdict.gzipBytes,
100
+ };
101
+ }
102
+ /** Every declared function must land in exactly one emitted shard. A name that
103
+ * vanished, or turned up twice after a split, would deploy a broken app. */
104
+ function assertEveryFunctionPlacedOnce(functions, shards) {
105
+ const placed = new Map();
106
+ for (const shard of shards) {
107
+ for (const name of shard.functions) {
108
+ placed.set(name, (placed.get(name) ?? 0) + 1);
109
+ }
110
+ }
111
+ const failures = [];
112
+ for (const fn of functions) {
113
+ const count = placed.get(fn.name) ?? 0;
114
+ if (count !== 1) {
115
+ failures.push({
116
+ function: fn.name,
117
+ message: `Function "${fn.name}" appears in ${count} shards; expected exactly 1.`,
118
+ });
119
+ }
120
+ }
121
+ for (const name of placed.keys()) {
122
+ if (!functions.some((fn) => fn.name === name)) {
123
+ failures.push({
124
+ function: name,
125
+ message: `Shard holds unknown function "${name}".`,
126
+ });
127
+ }
128
+ }
129
+ return failures;
130
+ }
131
+ function firstDuplicate(names) {
132
+ const seen = new Set();
133
+ for (const name of names) {
134
+ if (seen.has(name))
135
+ return name;
136
+ seen.add(name);
137
+ }
138
+ return null;
139
+ }
140
+ function fail(failures) {
141
+ return { ok: false, failures };
142
+ }
143
+ /** Bounded concurrency, input order preserved. */
144
+ async function mapWithConcurrency(items, limit, fn) {
145
+ const results = new Array(items.length);
146
+ let cursor = 0;
147
+ const worker = async () => {
148
+ while (cursor < items.length) {
149
+ const index = cursor++;
150
+ results[index] = await fn(items[index]);
151
+ }
152
+ };
153
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => worker()));
154
+ return results;
155
+ }
156
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../../src/shards/build.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Which functions go in which shard, for a build with no previous deploy to
3
+ * reuse. Ported from apper's `shard_planning.py` (`target_shard_count`,
4
+ * `full_repartition`) and the capacity refusal in `deploy_app`.
5
+ *
6
+ * Only the fresh path crosses over. Incremental reuse needs the previous
7
+ * deployment map, and the per-app shard-size ratchet is state written across
8
+ * deploys; both stay with the service.
9
+ */
10
+ /** Everything the planner is allowed to know. It reads no settings and no
11
+ * entitlements — the caller resolves these and passes them in. */
12
+ export interface ShardPolicy {
13
+ /** Functions packed into one shard. May be lower than `globalShardSize` when
14
+ * the caller carries a ratcheted override. */
15
+ shardSize: number;
16
+ /** The size capacity is judged at. A lowered `shardSize` changes packing only
17
+ * and must never lock an app out of deploying. */
18
+ globalShardSize: number;
19
+ maxShards: number;
20
+ /** Compressed ceiling for one shard's module, in bytes. */
21
+ gzipCapBytes: number;
22
+ }
23
+ export declare class ShardCapacityError extends Error {
24
+ constructor(message: string);
25
+ }
26
+ /**
27
+ * How many shards the desired set needs. `maxShards === 1` — sharding off — is
28
+ * always one shard whatever the function count: the whole app bundles into a
29
+ * single Worker, which is the legacy per-app behaviour. Going through the
30
+ * division would trip the capacity check for a flag-off app.
31
+ */
32
+ export declare function targetShardCount(functionCount: number, shardSize: number, maxShards: number): number;
33
+ /**
34
+ * Refuse a set the product cannot hold. Judged at the GLOBAL shard size: a
35
+ * ratcheted-down `shardSize` affects packing only, so judging at it would lock
36
+ * a recovered app out of deploys. The resulting plan may therefore hold more
37
+ * shards than `maxShards` — bounded by the function count, which is no more
38
+ * Workers than the legacy per-function topology used. Do not "tidy" this into a
39
+ * check on the final shard count.
40
+ */
41
+ export declare function assertWithinCapacity(functionCount: number, policy: ShardPolicy): void;
42
+ /**
43
+ * The fresh partition, as a list of shards holding function names.
44
+ *
45
+ * A single planned shard keeps the caller's order; a multi-shard plan sorts by
46
+ * name before chunking. The two differ in apper and the difference is load
47
+ * bearing — function order inside a combined module changes the emitted bytes,
48
+ * so normalising them would be a byte change wearing a cleanup's clothes.
49
+ */
50
+ export declare function planFreshShards(names: string[], policy: ShardPolicy): string[][];
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Which functions go in which shard, for a build with no previous deploy to
3
+ * reuse. Ported from apper's `shard_planning.py` (`target_shard_count`,
4
+ * `full_repartition`) and the capacity refusal in `deploy_app`.
5
+ *
6
+ * Only the fresh path crosses over. Incremental reuse needs the previous
7
+ * deployment map, and the per-app shard-size ratchet is state written across
8
+ * deploys; both stay with the service.
9
+ */
10
+ export class ShardCapacityError extends Error {
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = "ShardCapacityError";
14
+ }
15
+ }
16
+ /** The three counts have to be whole numbers of at least one. A zero or
17
+ * negative `shardSize` made the chunking loop below never advance — it hung
18
+ * rather than refusing, where the Python raises on the same input. */
19
+ function assertUsablePolicy(policy) {
20
+ for (const field of ["shardSize", "globalShardSize", "maxShards"]) {
21
+ const value = policy[field];
22
+ if (!Number.isInteger(value) || value < 1) {
23
+ throw new Error(`ShardPolicy.${field} must be an integer of at least 1, got ${value}`);
24
+ }
25
+ }
26
+ }
27
+ function ceilDiv(a, b) {
28
+ return Math.ceil(a / b);
29
+ }
30
+ /**
31
+ * How many shards the desired set needs. `maxShards === 1` — sharding off — is
32
+ * always one shard whatever the function count: the whole app bundles into a
33
+ * single Worker, which is the legacy per-app behaviour. Going through the
34
+ * division would trip the capacity check for a flag-off app.
35
+ */
36
+ export function targetShardCount(functionCount, shardSize, maxShards) {
37
+ if (maxShards === 1)
38
+ return 1;
39
+ return ceilDiv(functionCount, shardSize);
40
+ }
41
+ /**
42
+ * Refuse a set the product cannot hold. Judged at the GLOBAL shard size: a
43
+ * ratcheted-down `shardSize` affects packing only, so judging at it would lock
44
+ * a recovered app out of deploys. The resulting plan may therefore hold more
45
+ * shards than `maxShards` — bounded by the function count, which is no more
46
+ * Workers than the legacy per-function topology used. Do not "tidy" this into a
47
+ * check on the final shard count.
48
+ */
49
+ export function assertWithinCapacity(functionCount, policy) {
50
+ const needed = targetShardCount(functionCount, policy.globalShardSize, policy.maxShards);
51
+ if (needed > policy.maxShards) {
52
+ throw new ShardCapacityError(`App has ${functionCount} functions — over the per-app Worker capacity of ` +
53
+ `${policy.globalShardSize * policy.maxShards} ` +
54
+ `(${policy.maxShards} shards × ${policy.globalShardSize}).`);
55
+ }
56
+ }
57
+ /**
58
+ * The fresh partition, as a list of shards holding function names.
59
+ *
60
+ * A single planned shard keeps the caller's order; a multi-shard plan sorts by
61
+ * name before chunking. The two differ in apper and the difference is load
62
+ * bearing — function order inside a combined module changes the emitted bytes,
63
+ * so normalising them would be a byte change wearing a cleanup's clothes.
64
+ */
65
+ export function planFreshShards(names, policy) {
66
+ assertUsablePolicy(policy);
67
+ assertWithinCapacity(names.length, policy);
68
+ const target = targetShardCount(names.length, policy.shardSize, policy.maxShards);
69
+ if (target <= 1)
70
+ return [[...names]];
71
+ const sorted = [...names].sort();
72
+ const shards = [];
73
+ for (let i = 0; i < sorted.length; i += policy.shardSize) {
74
+ shards.push(sorted.slice(i, i + policy.shardSize));
75
+ }
76
+ return shards;
77
+ }
78
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/shards/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgBH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAED;;uEAEuE;AACvE,SAAS,kBAAkB,CAAC,MAAmB;IAC7C,KAAK,MAAM,KAAK,IAAI,CAAC,WAAW,EAAE,iBAAiB,EAAE,WAAW,CAAU,EAAE,CAAC;QAC3E,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CACb,eAAe,KAAK,0CAA0C,KAAK,EAAE,CACtE,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,OAAO,CAAC,CAAS,EAAE,CAAS;IACnC,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,aAAqB,EACrB,SAAiB,EACjB,SAAiB;IAEjB,IAAI,SAAS,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,OAAO,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAClC,aAAqB,EACrB,MAAmB;IAEnB,MAAM,MAAM,GAAG,gBAAgB,CAC7B,aAAa,EACb,MAAM,CAAC,eAAe,EACtB,MAAM,CAAC,SAAS,CACjB,CAAC;IACF,IAAI,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;QAC9B,MAAM,IAAI,kBAAkB,CAC1B,WAAW,aAAa,mDAAmD;YACzE,GAAG,MAAM,CAAC,eAAe,GAAG,MAAM,CAAC,SAAS,GAAG;YAC/C,IAAI,MAAM,CAAC,SAAS,aAAa,MAAM,CAAC,eAAe,IAAI,CAC9D,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAe,EACf,MAAmB;IAEnB,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC3B,oBAAoB,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE3C,MAAM,MAAM,GAAG,gBAAgB,CAC7B,KAAK,CAAC,MAAM,EACZ,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,SAAS,CACjB,CAAC;IACF,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;IAErC,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACjC,MAAM,MAAM,GAAe,EAAE,CAAC;IAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * How large a compiled Worker module is, and whether Cloudflare will take it.
3
+ *
4
+ * Ported from apper's `cloudflare_wfp_runtime.py` (`measure_bundle_bytes`,
5
+ * `worker_raw_size_breach`, `worker_gzip_cap_breach`, `judge_bundle_size`).
6
+ * Deciding this before an upload is the whole point: the one real production
7
+ * rejection spent 30 seconds uploading 97 MB to be told the same thing this
8
+ * computes for free.
9
+ */
10
+ /** zlib's default, and the level `wrangler --dry-run` reports — so the number
11
+ * here means the same thing as the one in Cloudflare's docs, in a support
12
+ * thread and on a developer's terminal. It is not a claim about Cloudflare's
13
+ * own server-side compressor, which is unpublished; the safety margin lives in
14
+ * the cap instead. */
15
+ export declare const BUNDLE_GZIP_LEVEL = 6;
16
+ /** Cloudflare's uncompressed per-Worker ceiling, in the units Cloudflare states
17
+ * it in. Taken from a real 10027 in production, which reads verbatim: "Your
18
+ * Worker exceeded the uncompressed size limit of 64 MiB." Decimal 64_000_000
19
+ * would refuse 3 MiB early for no reason. */
20
+ export declare const WORKER_RAW_SIZE_CEILING_BYTES: number;
21
+ export interface BundleSize {
22
+ rawBytes: number;
23
+ gzipBytes: number;
24
+ }
25
+ export interface SizeVerdict extends BundleSize {
26
+ /** Why the module is over a ceiling, or `null` when it is within both. */
27
+ breach: string | null;
28
+ }
29
+ /** `(raw, gzip)` for a compiled module. Compression runs off the event loop:
30
+ * a multi-megabyte gzip is long enough to matter when several shards are in
31
+ * flight. */
32
+ export declare function measureBundleBytes(module: string): Promise<BundleSize>;
33
+ /** Why Cloudflare will reject this module outright, or `null`. The only verdict
34
+ * taken on a number Cloudflare states itself — no estimate, no compression, no
35
+ * headroom to argue about. */
36
+ export declare function workerRawSizeBreach(rawBytes: number): string | null;
37
+ /** Why the module is over the compressed ceiling, or `null`. Our side of the
38
+ * comparison is the estimate, not Cloudflare's number: it compresses
39
+ * server-side with an unpublished algorithm, so level-6 gzip is a proxy for
40
+ * the figure it measures. The cap carries the margin. */
41
+ export declare function workerGzipCapBreach(gzipBytes: number, gzipCapBytes: number): string | null;
42
+ /** Measure a module and say whether it breaches either ceiling. Never throws:
43
+ * what a breach costs depends on whether the caller can re-partition, and only
44
+ * the caller knows that. */
45
+ export declare function judgeBundleSize(module: string, gzipCapBytes: number): Promise<SizeVerdict>;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * How large a compiled Worker module is, and whether Cloudflare will take it.
3
+ *
4
+ * Ported from apper's `cloudflare_wfp_runtime.py` (`measure_bundle_bytes`,
5
+ * `worker_raw_size_breach`, `worker_gzip_cap_breach`, `judge_bundle_size`).
6
+ * Deciding this before an upload is the whole point: the one real production
7
+ * rejection spent 30 seconds uploading 97 MB to be told the same thing this
8
+ * computes for free.
9
+ */
10
+ import { promisify } from "node:util";
11
+ import { gzip } from "node:zlib";
12
+ const gzipAsync = promisify(gzip);
13
+ /** zlib's default, and the level `wrangler --dry-run` reports — so the number
14
+ * here means the same thing as the one in Cloudflare's docs, in a support
15
+ * thread and on a developer's terminal. It is not a claim about Cloudflare's
16
+ * own server-side compressor, which is unpublished; the safety margin lives in
17
+ * the cap instead. */
18
+ export const BUNDLE_GZIP_LEVEL = 6;
19
+ /** Cloudflare's uncompressed per-Worker ceiling, in the units Cloudflare states
20
+ * it in. Taken from a real 10027 in production, which reads verbatim: "Your
21
+ * Worker exceeded the uncompressed size limit of 64 MiB." Decimal 64_000_000
22
+ * would refuse 3 MiB early for no reason. */
23
+ export const WORKER_RAW_SIZE_CEILING_BYTES = 64 * 1024 * 1024;
24
+ /** `(raw, gzip)` for a compiled module. Compression runs off the event loop:
25
+ * a multi-megabyte gzip is long enough to matter when several shards are in
26
+ * flight. */
27
+ export async function measureBundleBytes(module) {
28
+ const raw = Buffer.from(module, "utf8");
29
+ const compressed = await gzipAsync(raw, { level: BUNDLE_GZIP_LEVEL });
30
+ return { rawBytes: raw.byteLength, gzipBytes: compressed.byteLength };
31
+ }
32
+ /** Why Cloudflare will reject this module outright, or `null`. The only verdict
33
+ * taken on a number Cloudflare states itself — no estimate, no compression, no
34
+ * headroom to argue about. */
35
+ export function workerRawSizeBreach(rawBytes) {
36
+ if (rawBytes > WORKER_RAW_SIZE_CEILING_BYTES) {
37
+ return (`bundled module is ${rawBytes} bytes uncompressed, over Cloudflare's ` +
38
+ `${WORKER_RAW_SIZE_CEILING_BYTES}-byte (64 MiB) per-Worker ceiling`);
39
+ }
40
+ return null;
41
+ }
42
+ /** Why the module is over the compressed ceiling, or `null`. Our side of the
43
+ * comparison is the estimate, not Cloudflare's number: it compresses
44
+ * server-side with an unpublished algorithm, so level-6 gzip is a proxy for
45
+ * the figure it measures. The cap carries the margin. */
46
+ export function workerGzipCapBreach(gzipBytes, gzipCapBytes) {
47
+ if (gzipBytes > gzipCapBytes) {
48
+ return (`bundled module is ${gzipBytes} bytes gzipped (level ${BUNDLE_GZIP_LEVEL}), ` +
49
+ `over the ${gzipCapBytes}-byte cap for a Cloudflare Worker script`);
50
+ }
51
+ return null;
52
+ }
53
+ /** Measure a module and say whether it breaches either ceiling. Never throws:
54
+ * what a breach costs depends on whether the caller can re-partition, and only
55
+ * the caller knows that. */
56
+ export async function judgeBundleSize(module, gzipCapBytes) {
57
+ const { rawBytes, gzipBytes } = await measureBundleBytes(module);
58
+ // Uncompressed first: it is the exact one, so when both are over it is the
59
+ // verdict worth reporting.
60
+ const breach = workerRawSizeBreach(rawBytes) ??
61
+ workerGzipCapBreach(gzipBytes, gzipCapBytes);
62
+ return { rawBytes, gzipBytes, breach };
63
+ }
64
+ //# sourceMappingURL=size.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"size.js","sourceRoot":"","sources":["../../../src/shards/size.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAElC;;;;uBAIuB;AACvB,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAEnC;;;8CAG8C;AAC9C,MAAM,CAAC,MAAM,6BAA6B,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAY9D;;cAEc;AACd,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAc;IACrD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC,CAAC;IACtE,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,UAAU,CAAC,UAAU,EAAE,CAAC;AACxE,CAAC;AAED;;+BAE+B;AAC/B,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,IAAI,QAAQ,GAAG,6BAA6B,EAAE,CAAC;QAC7C,OAAO,CACL,qBAAqB,QAAQ,yCAAyC;YACtE,GAAG,6BAA6B,mCAAmC,CACpE,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;0DAG0D;AAC1D,MAAM,UAAU,mBAAmB,CACjC,SAAiB,EACjB,YAAoB;IAEpB,IAAI,SAAS,GAAG,YAAY,EAAE,CAAC;QAC7B,OAAO,CACL,qBAAqB,SAAS,yBAAyB,iBAAiB,KAAK;YAC7E,YAAY,YAAY,0CAA0C,CACnE,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;6BAE6B;AAC7B,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAc,EACd,YAAoB;IAEpB,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACjE,2EAA2E;IAC3E,2BAA2B;IAC3B,MAAM,MAAM,GACV,mBAAmB,CAAC,QAAQ,CAAC;QAC7B,mBAAmB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AACzC,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * This package's version, as a literal.
3
+ *
4
+ * It goes into every compiled shard's banner, which means it is part of the
5
+ * emitted bytes and therefore part of a deployed version's identity. Reading it
6
+ * from `package.json` at runtime cannot work: a host that bundles this module
7
+ * into an artifact of its own — which the CLI does — ships no `package.json`
8
+ * beside it, and the read silently degraded to "unknown", so the same functions
9
+ * compiled to different bytes in the CLI and in the service.
10
+ *
11
+ * A literal resolves in every host. `version.test.ts` fails the build if it
12
+ * drifts from `package.json`, so bumping the package still means editing two
13
+ * files but cannot mean forgetting one.
14
+ */
15
+ export declare const COMPILER_VERSION = "0.1.4";
@@ -0,0 +1,16 @@
1
+ /**
2
+ * This package's version, as a literal.
3
+ *
4
+ * It goes into every compiled shard's banner, which means it is part of the
5
+ * emitted bytes and therefore part of a deployed version's identity. Reading it
6
+ * from `package.json` at runtime cannot work: a host that bundles this module
7
+ * into an artifact of its own — which the CLI does — ships no `package.json`
8
+ * beside it, and the read silently degraded to "unknown", so the same functions
9
+ * compiled to different bytes in the CLI and in the service.
10
+ *
11
+ * A literal resolves in every host. `version.test.ts` fails the build if it
12
+ * drifts from `package.json`, so bumping the package still means editing two
13
+ * files but cannot mean forgetting one.
14
+ */
15
+ export const COMPILER_VERSION = "0.1.4";
16
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC"}