bunderstack 0.25.1 → 1.0.0-beta.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 (167) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/auth-password.d.ts +27 -0
  3. package/dist/auth-password.d.ts.map +1 -0
  4. package/dist/auth-password.js +60 -0
  5. package/dist/auth-password.js.map +1 -0
  6. package/dist/auth.d.ts +1 -1
  7. package/dist/auth.d.ts.map +1 -1
  8. package/dist/auth.js +1 -0
  9. package/dist/auth.js.map +1 -1
  10. package/dist/backend.d.ts +3 -0
  11. package/dist/backend.d.ts.map +1 -1
  12. package/dist/backend.js +3 -1
  13. package/dist/backend.js.map +1 -1
  14. package/dist/cli.d.ts +11 -1
  15. package/dist/cli.d.ts.map +1 -1
  16. package/dist/cli.js +97 -2
  17. package/dist/cli.js.map +1 -1
  18. package/dist/config.d.ts +0 -8
  19. package/dist/config.d.ts.map +1 -1
  20. package/dist/config.js +0 -18
  21. package/dist/config.js.map +1 -1
  22. package/dist/crud-operations.d.ts.map +1 -1
  23. package/dist/crud-operations.js +19 -3
  24. package/dist/crud-operations.js.map +1 -1
  25. package/dist/dev/binaries.d.ts +22 -0
  26. package/dist/dev/binaries.d.ts.map +1 -0
  27. package/dist/dev/binaries.js +119 -0
  28. package/dist/dev/binaries.js.map +1 -0
  29. package/dist/dev/env.d.ts +11 -0
  30. package/dist/dev/env.d.ts.map +1 -0
  31. package/dist/dev/env.js +71 -0
  32. package/dist/dev/env.js.map +1 -0
  33. package/dist/dev/index.d.ts +45 -0
  34. package/dist/dev/index.d.ts.map +1 -0
  35. package/dist/dev/index.js +287 -0
  36. package/dist/dev/index.js.map +1 -0
  37. package/dist/dev/processes.d.ts +37 -0
  38. package/dist/dev/processes.d.ts.map +1 -0
  39. package/dist/dev/processes.js +104 -0
  40. package/dist/dev/processes.js.map +1 -0
  41. package/dist/dev/push.d.ts +2 -0
  42. package/dist/dev/push.d.ts.map +1 -0
  43. package/dist/dev/push.js +25 -0
  44. package/dist/dev/push.js.map +1 -0
  45. package/dist/env-probe.d.ts.map +1 -1
  46. package/dist/env-probe.js +0 -1
  47. package/dist/env-probe.js.map +1 -1
  48. package/dist/env.d.ts +0 -3
  49. package/dist/env.d.ts.map +1 -1
  50. package/dist/env.js +0 -7
  51. package/dist/env.js.map +1 -1
  52. package/dist/handler.d.ts +2 -0
  53. package/dist/handler.d.ts.map +1 -1
  54. package/dist/handler.js +1 -2
  55. package/dist/handler.js.map +1 -1
  56. package/dist/hosted-contract.d.ts.map +1 -1
  57. package/dist/hosted-contract.js +7 -3
  58. package/dist/hosted-contract.js.map +1 -1
  59. package/dist/index.d.ts +4 -2
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +1 -0
  62. package/dist/index.js.map +1 -1
  63. package/dist/jobs/define.d.ts +5 -0
  64. package/dist/jobs/define.d.ts.map +1 -1
  65. package/dist/jobs/define.js.map +1 -1
  66. package/dist/jobs/index.d.ts +2 -4
  67. package/dist/jobs/index.d.ts.map +1 -1
  68. package/dist/jobs/index.js +2 -3
  69. package/dist/jobs/index.js.map +1 -1
  70. package/dist/jobs/queue.d.ts +1 -0
  71. package/dist/jobs/queue.d.ts.map +1 -1
  72. package/dist/jobs/queue.js +6 -3
  73. package/dist/jobs/queue.js.map +1 -1
  74. package/dist/jobs/slots.d.ts +5 -0
  75. package/dist/jobs/slots.d.ts.map +1 -1
  76. package/dist/jobs/slots.js +12 -0
  77. package/dist/jobs/slots.js.map +1 -1
  78. package/dist/jobs/worker.d.ts +6 -0
  79. package/dist/jobs/worker.d.ts.map +1 -1
  80. package/dist/jobs/worker.js +32 -1
  81. package/dist/jobs/worker.js.map +1 -1
  82. package/dist/platform.d.ts +25 -0
  83. package/dist/platform.d.ts.map +1 -0
  84. package/dist/platform.js +24 -0
  85. package/dist/platform.js.map +1 -0
  86. package/dist/query/client.js +1 -1
  87. package/dist/query/client.js.map +1 -1
  88. package/dist/rate-limit.d.ts +2 -1
  89. package/dist/rate-limit.d.ts.map +1 -1
  90. package/dist/rate-limit.js +19 -22
  91. package/dist/rate-limit.js.map +1 -1
  92. package/dist/realtime/facade.d.ts +1 -1
  93. package/dist/realtime/facade.d.ts.map +1 -1
  94. package/dist/realtime/facade.js.map +1 -1
  95. package/dist/realtime/publisher.d.ts +0 -4
  96. package/dist/realtime/publisher.d.ts.map +1 -1
  97. package/dist/realtime/publisher.js +0 -9
  98. package/dist/realtime/publisher.js.map +1 -1
  99. package/dist/runtime.d.ts +5 -19
  100. package/dist/runtime.d.ts.map +1 -1
  101. package/dist/runtime.js +28 -102
  102. package/dist/runtime.js.map +1 -1
  103. package/dist/storage/registry.d.ts +2 -1
  104. package/dist/storage/registry.d.ts.map +1 -1
  105. package/dist/storage/registry.js +2 -2
  106. package/dist/storage/registry.js.map +1 -1
  107. package/dist/storage/s3.d.ts +11 -4
  108. package/dist/storage/s3.d.ts.map +1 -1
  109. package/dist/storage/s3.js +88 -34
  110. package/dist/storage/s3.js.map +1 -1
  111. package/dist/storage/sigv4.d.ts +24 -0
  112. package/dist/storage/sigv4.d.ts.map +1 -0
  113. package/dist/storage/sigv4.js +107 -0
  114. package/dist/storage/sigv4.js.map +1 -0
  115. package/dist/testing/fixture.d.ts.map +1 -1
  116. package/dist/testing/fixture.js +0 -3
  117. package/dist/testing/fixture.js.map +1 -1
  118. package/dist/testing/workers-fakes.d.ts +17 -0
  119. package/dist/testing/workers-fakes.d.ts.map +1 -0
  120. package/dist/testing/workers-fakes.js +98 -0
  121. package/dist/testing/workers-fakes.js.map +1 -0
  122. package/dist/typeid.d.ts +1 -0
  123. package/dist/typeid.d.ts.map +1 -1
  124. package/dist/typeid.js +34 -4
  125. package/dist/typeid.js.map +1 -1
  126. package/dist/vite.d.ts +30 -0
  127. package/dist/vite.d.ts.map +1 -0
  128. package/dist/vite.js +37 -0
  129. package/dist/vite.js.map +1 -0
  130. package/dist/workers/app.d.ts +19 -0
  131. package/dist/workers/app.d.ts.map +1 -0
  132. package/dist/workers/app.js +56 -0
  133. package/dist/workers/app.js.map +1 -0
  134. package/dist/workers/index.d.ts +21 -0
  135. package/dist/workers/index.d.ts.map +1 -0
  136. package/dist/workers/index.js +33 -0
  137. package/dist/workers/index.js.map +1 -0
  138. package/dist/workers/r2.d.ts +23 -0
  139. package/dist/workers/r2.d.ts.map +1 -0
  140. package/dist/workers/r2.js +77 -0
  141. package/dist/workers/r2.js.map +1 -0
  142. package/dist/workers/rate-limiter.d.ts +9 -0
  143. package/dist/workers/rate-limiter.d.ts.map +1 -0
  144. package/dist/workers/rate-limiter.js +22 -0
  145. package/dist/workers/rate-limiter.js.map +1 -0
  146. package/dist/workers/realtime-hub.d.ts +20 -0
  147. package/dist/workers/realtime-hub.d.ts.map +1 -0
  148. package/dist/workers/realtime-hub.js +117 -0
  149. package/dist/workers/realtime-hub.js.map +1 -0
  150. package/dist/workers/scheduler.d.ts +13 -0
  151. package/dist/workers/scheduler.d.ts.map +1 -0
  152. package/dist/workers/scheduler.js +71 -0
  153. package/dist/workers/scheduler.js.map +1 -0
  154. package/dist/workers/types.d.ts +55 -0
  155. package/dist/workers/types.d.ts.map +1 -0
  156. package/dist/workers/types.js +4 -0
  157. package/dist/workers/types.js.map +1 -0
  158. package/dist/workers/wrangler.d.ts +64 -0
  159. package/dist/workers/wrangler.d.ts.map +1 -0
  160. package/dist/workers/wrangler.js +115 -0
  161. package/dist/workers/wrangler.js.map +1 -0
  162. package/llms-full.txt +10 -1
  163. package/package.json +11 -3
  164. package/dist/jobs/runtime.d.ts +0 -17
  165. package/dist/jobs/runtime.d.ts.map +0 -1
  166. package/dist/jobs/runtime.js +0 -56
  167. package/dist/jobs/runtime.js.map +0 -1
@@ -0,0 +1,98 @@
1
+ export function createFakeState() {
2
+ let alarm = null;
3
+ const state = {
4
+ storage: {
5
+ async getAlarm() {
6
+ return alarm;
7
+ },
8
+ async setAlarm(time) {
9
+ alarm = typeof time === 'number' ? time : time.getTime();
10
+ },
11
+ async deleteAlarm() {
12
+ alarm = null;
13
+ },
14
+ },
15
+ alarmAt: () => alarm,
16
+ };
17
+ return state;
18
+ }
19
+ export function createFakeNamespace(make) {
20
+ const instances = new Map();
21
+ const entry = (name) => {
22
+ let found = instances.get(name);
23
+ if (!found) {
24
+ const state = createFakeState();
25
+ found = { object: make(state, name), state };
26
+ instances.set(name, found);
27
+ }
28
+ return found;
29
+ };
30
+ const namespace = {
31
+ idFromName: (name) => name,
32
+ get: (id) => ({
33
+ fetch: (input, init) => entry(String(id)).object.fetch(new Request(input, init)),
34
+ }),
35
+ instance: (name) => entry(name).object,
36
+ state: (name) => entry(name).state,
37
+ };
38
+ return namespace;
39
+ }
40
+ export function createFakeR2() {
41
+ const objects = new Map();
42
+ const meta = (key) => {
43
+ const found = objects.get(key);
44
+ return found
45
+ ? {
46
+ key,
47
+ size: found.bytes.byteLength,
48
+ httpMetadata: { contentType: found.contentType },
49
+ }
50
+ : null;
51
+ };
52
+ // Pages of two keys, so a cursor loop is exercised.
53
+ const PAGE = 2;
54
+ const bucket = {
55
+ objects,
56
+ async put(key, value, options) {
57
+ const bytes = typeof value === 'string'
58
+ ? new TextEncoder().encode(value)
59
+ : value instanceof ArrayBuffer
60
+ ? new Uint8Array(value)
61
+ : new Uint8Array(await new Response(value).arrayBuffer());
62
+ objects.set(key, {
63
+ bytes,
64
+ contentType: options?.httpMetadata?.contentType,
65
+ });
66
+ return meta(key);
67
+ },
68
+ async get(key) {
69
+ const found = objects.get(key);
70
+ if (!found)
71
+ return null;
72
+ return {
73
+ ...meta(key),
74
+ body: new Response(found.bytes).body,
75
+ };
76
+ },
77
+ async head(key) {
78
+ return meta(key);
79
+ },
80
+ async delete(key) {
81
+ objects.delete(key);
82
+ },
83
+ async list(options) {
84
+ const keys = [...objects.keys()]
85
+ .filter((key) => key.startsWith(options?.prefix ?? ''))
86
+ .sort();
87
+ const start = options?.cursor ? Number(options.cursor) : 0;
88
+ const truncated = start + PAGE < keys.length;
89
+ return {
90
+ objects: keys.slice(start, start + PAGE).map((key) => meta(key)),
91
+ truncated,
92
+ cursor: truncated ? String(start + PAGE) : undefined,
93
+ };
94
+ },
95
+ };
96
+ return bucket;
97
+ }
98
+ //# sourceMappingURL=workers-fakes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workers-fakes.js","sourceRoot":"","sources":["../../src/testing/workers-fakes.ts"],"names":[],"mappings":"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","sourcesContent":["// In-memory stand-ins for Worker bindings, so Durable Object classes and the\n// Worker entry run under `bun test`. Not a full emulation: alarms fire only\n// when a test calls `alarm()`.\nimport type {\n DurableObjectNamespaceLike,\n DurableObjectStateLike,\n R2BucketLike,\n R2ObjectLike,\n} from '../workers/types'\n\nexport function createFakeState() {\n let alarm: number | null = null\n const state: DurableObjectStateLike & { alarmAt(): number | null } = {\n storage: {\n async getAlarm() {\n return alarm\n },\n async setAlarm(time) {\n alarm = typeof time === 'number' ? time : time.getTime()\n },\n async deleteAlarm() {\n alarm = null\n },\n },\n alarmAt: () => alarm,\n }\n return state\n}\n\nexport function createFakeNamespace<\n T extends { fetch(request: Request): Promise<Response> },\n>(make: (state: DurableObjectStateLike, name: string) => T) {\n const instances = new Map<\n string,\n { object: T; state: ReturnType<typeof createFakeState> }\n >()\n const entry = (name: string) => {\n let found = instances.get(name)\n if (!found) {\n const state = createFakeState()\n found = { object: make(state, name), state }\n instances.set(name, found)\n }\n return found\n }\n const namespace: DurableObjectNamespaceLike & {\n instance(name: string): T\n state(name: string): ReturnType<typeof createFakeState>\n } = {\n idFromName: (name) => name,\n get: (id) => ({\n fetch: (input, init) =>\n entry(String(id)).object.fetch(new Request(input, init)),\n }),\n instance: (name) => entry(name).object,\n state: (name) => entry(name).state,\n }\n return namespace\n}\n\nexport function createFakeR2() {\n const objects = new Map<string, { bytes: Uint8Array; contentType?: string }>()\n const meta = (key: string): R2ObjectLike | null => {\n const found = objects.get(key)\n return found\n ? {\n key,\n size: found.bytes.byteLength,\n httpMetadata: { contentType: found.contentType },\n }\n : null\n }\n // Pages of two keys, so a cursor loop is exercised.\n const PAGE = 2\n const bucket: R2BucketLike & { objects: typeof objects } = {\n objects,\n async put(key, value, options) {\n const bytes =\n typeof value === 'string'\n ? new TextEncoder().encode(value)\n : value instanceof ArrayBuffer\n ? new Uint8Array(value)\n : new Uint8Array(await new Response(value).arrayBuffer())\n objects.set(key, {\n bytes,\n contentType: options?.httpMetadata?.contentType,\n })\n return meta(key)\n },\n async get(key) {\n const found = objects.get(key)\n if (!found) return null\n return {\n ...meta(key)!,\n body: new Response(found.bytes as unknown as BodyInit).body!,\n }\n },\n async head(key) {\n return meta(key)\n },\n async delete(key) {\n objects.delete(key)\n },\n async list(options) {\n const keys = [...objects.keys()]\n .filter((key) => key.startsWith(options?.prefix ?? ''))\n .sort()\n const start = options?.cursor ? Number(options.cursor) : 0\n const truncated = start + PAGE < keys.length\n return {\n objects: keys.slice(start, start + PAGE).map((key) => meta(key)!),\n truncated,\n cursor: truncated ? String(start + PAGE) : undefined,\n }\n },\n }\n return bucket\n}\n"]}
package/dist/typeid.d.ts CHANGED
@@ -9,6 +9,7 @@ export declare function encode(bytes: Uint8Array): string;
9
9
  export declare function decode(suffix: string): Uint8Array;
10
10
  /** Whether a string is a valid, non-empty TypeID prefix. */
11
11
  export declare function isValidPrefix(prefix: string): boolean;
12
+ export declare function uuidv7Bytes(now?: number): Uint8Array;
12
13
  /** Generate a new, branded TypeID for the given prefix. */
13
14
  export declare function generate<P extends string>(prefix: P): TypeId<P>;
14
15
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"typeid.d.ts","sourceRoot":"","sources":["../src/typeid.ts"],"names":[],"mappings":"AAkBA,OAAO,CAAC,MAAM,WAAW,EAAE,OAAO,MAAM,CAAA;AAExC,uFAAuF;AACvF,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS,MAAM,IAAI,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAA;CAAE,CAAA;AAE9E,iEAAiE;AACjE,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAWhD;AAED,gFAAgF;AAChF,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAUjD;AAED,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAErD;AAOD,2DAA2D;AAC3D,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAK/D;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,EACpC,EAAE,EAAE,MAAM,EACV,cAAc,CAAC,EAAE,CAAC,GACjB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAelD;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAG5E;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC;;;;;;;GAMjD"}
1
+ {"version":3,"file":"typeid.d.ts","sourceRoot":"","sources":["../src/typeid.ts"],"names":[],"mappings":"AAkBA,OAAO,CAAC,MAAM,WAAW,EAAE,OAAO,MAAM,CAAA;AAExC,uFAAuF;AACvF,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS,MAAM,IAAI,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAA;CAAE,CAAA;AAE9E,iEAAiE;AACjE,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAWhD;AAED,gFAAgF;AAChF,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAUjD;AAED,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAErD;AAYD,wBAAgB,WAAW,CAAC,GAAG,GAAE,MAAmB,GAAG,UAAU,CAwBhE;AAED,2DAA2D;AAC3D,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAK/D;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,EACpC,EAAE,EAAE,MAAM,EACV,cAAc,CAAC,EAAE,CAAC,GACjB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAelD;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAG5E;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC;;;;;;;GAMjD"}
package/dist/typeid.js CHANGED
@@ -2,9 +2,9 @@
2
2
  //
3
3
  // Zero-dependency TypeID implementation (https://github.com/jetify-com/typeid).
4
4
  // Stripe-style prefixed, k-sortable identifiers: `prefix_<26-char base32 UUIDv7>`.
5
- // The only runtime primitive we need is the raw UUIDv7 bytes, which Bun gives us
6
- // natively via `Bun.randomUUIDv7("buffer")` — so we don't pull in the `uuid` dep
7
- // that the reference `typeid-js` package relies on.
5
+ // The only runtime primitive we need is UUIDv7 bytes. `uuidv7Bytes` builds them
6
+ // from WebCrypto, so the same code runs in Bun, workerd, and celld without the
7
+ // `uuid` dependency that the reference `typeid-js` package relies on.
8
8
  import { customType } from 'drizzle-orm/sqlite-core';
9
9
  // Crockford base32 alphabet (lowercase, excludes i/l/o/u), as used by the TypeID spec.
10
10
  const ALPHABET = '0123456789abcdefghjkmnpqrstvwxyz';
@@ -48,11 +48,41 @@ function bytesToUuid(bytes) {
48
48
  const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
49
49
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
50
50
  }
51
+ // Monotonic state: RFC 9562 method 1, a 12-bit counter in rand_a. Seeded with
52
+ // random bits below its top bit on each new millisecond, so it can grow.
53
+ let lastMs = -1;
54
+ let counter = 0;
55
+ export function uuidv7Bytes(now = Date.now()) {
56
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
57
+ if (now > lastMs) {
58
+ lastMs = now;
59
+ counter = ((bytes[6] & 0x07) << 8) | bytes[7];
60
+ }
61
+ else {
62
+ counter += 1;
63
+ if (counter > 0xfff) {
64
+ // Counter overflow: borrow the next millisecond, keep the order.
65
+ lastMs += 1;
66
+ counter = 0;
67
+ }
68
+ }
69
+ const ms = lastMs;
70
+ bytes[0] = Math.floor(ms / 2 ** 40) & 0xff;
71
+ bytes[1] = Math.floor(ms / 2 ** 32) & 0xff;
72
+ bytes[2] = Math.floor(ms / 2 ** 24) & 0xff;
73
+ bytes[3] = Math.floor(ms / 2 ** 16) & 0xff;
74
+ bytes[4] = Math.floor(ms / 2 ** 8) & 0xff;
75
+ bytes[5] = ms & 0xff;
76
+ bytes[6] = 0x70 | ((counter >> 8) & 0x0f);
77
+ bytes[7] = counter & 0xff;
78
+ bytes[8] = 0x80 | (bytes[8] & 0x3f);
79
+ return bytes;
80
+ }
51
81
  /** Generate a new, branded TypeID for the given prefix. */
52
82
  export function generate(prefix) {
53
83
  if (!isValidPrefix(prefix))
54
84
  throw new Error(`Invalid typeid prefix: "${prefix}"`);
55
- const bytes = Bun.randomUUIDv7('buffer');
85
+ const bytes = uuidv7Bytes();
56
86
  return `${prefix}_${encode(bytes)}`;
57
87
  }
58
88
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"typeid.js","sourceRoot":"","sources":["../src/typeid.ts"],"names":[],"mappings":"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","sourcesContent":["// src/typeid.ts\n//\n// Zero-dependency TypeID implementation (https://github.com/jetify-com/typeid).\n// Stripe-style prefixed, k-sortable identifiers: `prefix_<26-char base32 UUIDv7>`.\n// The only runtime primitive we need is the raw UUIDv7 bytes, which Bun gives us\n// natively via `Bun.randomUUIDv7(\"buffer\")` — so we don't pull in the `uuid` dep\n// that the reference `typeid-js` package relies on.\n\nimport { customType } from 'drizzle-orm/sqlite-core'\n\n// Crockford base32 alphabet (lowercase, excludes i/l/o/u), as used by the TypeID spec.\nconst ALPHABET = '0123456789abcdefghjkmnpqrstvwxyz'\n\n// Spec prefix rules: lowercase ascii, underscores allowed only internally, 1–63 chars.\nconst PREFIX_RE = /^[a-z]([a-z_]{0,61}[a-z])?$/\n// 26 base32 chars from the alphabet above.\nconst SUFFIX_RE = /^[0-9a-hjkmnp-tv-z]{26}$/\n\ndeclare const typeIdBrand: unique symbol\n\n/** A branded TypeID string. `TypeId<'post'>` is incompatible with `TypeId<'user'>`. */\nexport type TypeId<P extends string> = string & { readonly [typeIdBrand]?: P }\n\n/** Encode a 16-byte UUID into the 26-character base32 suffix. */\nexport function encode(bytes: Uint8Array): string {\n // First two chars cover byte 0: top 3 bits, then low 5 bits.\n let out = ALPHABET[bytes[0]! >> 5]! + ALPHABET[bytes[0]! & 31]!\n // Remaining 15 bytes (120 bits) → 24 groups of 5 bits.\n let buf = 0n\n for (let i = 1; i < 16; i++) buf = (buf << 8n) | BigInt(bytes[i]!)\n const chars: string[] = []\n for (let i = 0; i < 24; i++) {\n chars.push(ALPHABET[Number((buf >> (BigInt(23 - i) * 5n)) & 31n)]!)\n }\n return out + chars.join('')\n}\n\n/** Decode a 26-character base32 suffix back into the original 16 UUID bytes. */\nexport function decode(suffix: string): Uint8Array {\n const map: Record<string, number> = {}\n for (let i = 0; i < ALPHABET.length; i++) map[ALPHABET[i]!] = i\n const bytes = new Uint8Array(16)\n bytes[0] = (map[suffix[0]!]! << 5) | map[suffix[1]!]!\n let buf = 0n\n for (let i = 2; i < 26; i++) buf = (buf << 5n) | BigInt(map[suffix[i]!]!)\n for (let i = 0; i < 15; i++)\n bytes[15 - i] = Number((buf >> BigInt(i * 8)) & 0xffn)\n return bytes\n}\n\n/** Whether a string is a valid, non-empty TypeID prefix. */\nexport function isValidPrefix(prefix: string): boolean {\n return PREFIX_RE.test(prefix)\n}\n\nfunction bytesToUuid(bytes: Uint8Array): string {\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`\n}\n\n/** Generate a new, branded TypeID for the given prefix. */\nexport function generate<P extends string>(prefix: P): TypeId<P> {\n if (!isValidPrefix(prefix))\n throw new Error(`Invalid typeid prefix: \"${prefix}\"`)\n const bytes = Bun.randomUUIDv7('buffer')\n return `${prefix}_${encode(bytes)}` as TypeId<P>\n}\n\n/**\n * Parse a TypeID into its parts and the decoded UUID. Throws if the id is\n * malformed or — when `expectedPrefix` is given — if the prefix does not match.\n */\nexport function parse<P extends string>(\n id: string,\n expectedPrefix?: P,\n): { prefix: string; suffix: string; uuid: string } {\n const sep = id.lastIndexOf('_')\n if (sep <= 0) throw new Error(`Malformed typeid: \"${id}\"`)\n const prefix = id.slice(0, sep)\n const suffix = id.slice(sep + 1)\n if (!isValidPrefix(prefix))\n throw new Error(`Malformed typeid prefix: \"${id}\"`)\n if (!SUFFIX_RE.test(suffix))\n throw new Error(`Malformed typeid suffix: \"${id}\"`)\n if (expectedPrefix !== undefined && prefix !== expectedPrefix) {\n throw new Error(\n `Expected typeid prefix \"${expectedPrefix}\" but got \"${prefix}\"`,\n )\n }\n return { prefix, suffix, uuid: bytesToUuid(decode(suffix)) }\n}\n\n/**\n * Escape hatch: validate a raw string against a prefix and brand it as a TypeID.\n * Use at trust boundaries (e.g. a raw URL param) where the string isn't yet typed.\n */\nexport function asTypeId<P extends string>(prefix: P, raw: string): TypeId<P> {\n parse(raw, prefix)\n return raw as TypeId<P>\n}\n\n/**\n * Drizzle column builder for a branded TypeID text value. Stores a plain `text`\n * column so drizzle-kit migrations and `$inferSelect` work unchanged.\n *\n * Use Drizzle's `$defaultFn` when a column should generate IDs on insert:\n *\n * id: typeid('post').primaryKey().$defaultFn(() => generate('post'))\n */\nexport function typeid<P extends string>(prefix: P) {\n if (!isValidPrefix(prefix))\n throw new Error(`Invalid typeid prefix: \"${prefix}\"`)\n return customType<{ data: TypeId<P>; driverData: string }>({\n dataType: () => 'text',\n })()\n}\n"]}
1
+ {"version":3,"file":"typeid.js","sourceRoot":"","sources":["../src/typeid.ts"],"names":[],"mappings":"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","sourcesContent":["// src/typeid.ts\n//\n// Zero-dependency TypeID implementation (https://github.com/jetify-com/typeid).\n// Stripe-style prefixed, k-sortable identifiers: `prefix_<26-char base32 UUIDv7>`.\n// The only runtime primitive we need is UUIDv7 bytes. `uuidv7Bytes` builds them\n// from WebCrypto, so the same code runs in Bun, workerd, and celld without the\n// `uuid` dependency that the reference `typeid-js` package relies on.\n\nimport { customType } from 'drizzle-orm/sqlite-core'\n\n// Crockford base32 alphabet (lowercase, excludes i/l/o/u), as used by the TypeID spec.\nconst ALPHABET = '0123456789abcdefghjkmnpqrstvwxyz'\n\n// Spec prefix rules: lowercase ascii, underscores allowed only internally, 1–63 chars.\nconst PREFIX_RE = /^[a-z]([a-z_]{0,61}[a-z])?$/\n// 26 base32 chars from the alphabet above.\nconst SUFFIX_RE = /^[0-9a-hjkmnp-tv-z]{26}$/\n\ndeclare const typeIdBrand: unique symbol\n\n/** A branded TypeID string. `TypeId<'post'>` is incompatible with `TypeId<'user'>`. */\nexport type TypeId<P extends string> = string & { readonly [typeIdBrand]?: P }\n\n/** Encode a 16-byte UUID into the 26-character base32 suffix. */\nexport function encode(bytes: Uint8Array): string {\n // First two chars cover byte 0: top 3 bits, then low 5 bits.\n let out = ALPHABET[bytes[0]! >> 5]! + ALPHABET[bytes[0]! & 31]!\n // Remaining 15 bytes (120 bits) → 24 groups of 5 bits.\n let buf = 0n\n for (let i = 1; i < 16; i++) buf = (buf << 8n) | BigInt(bytes[i]!)\n const chars: string[] = []\n for (let i = 0; i < 24; i++) {\n chars.push(ALPHABET[Number((buf >> (BigInt(23 - i) * 5n)) & 31n)]!)\n }\n return out + chars.join('')\n}\n\n/** Decode a 26-character base32 suffix back into the original 16 UUID bytes. */\nexport function decode(suffix: string): Uint8Array {\n const map: Record<string, number> = {}\n for (let i = 0; i < ALPHABET.length; i++) map[ALPHABET[i]!] = i\n const bytes = new Uint8Array(16)\n bytes[0] = (map[suffix[0]!]! << 5) | map[suffix[1]!]!\n let buf = 0n\n for (let i = 2; i < 26; i++) buf = (buf << 5n) | BigInt(map[suffix[i]!]!)\n for (let i = 0; i < 15; i++)\n bytes[15 - i] = Number((buf >> BigInt(i * 8)) & 0xffn)\n return bytes\n}\n\n/** Whether a string is a valid, non-empty TypeID prefix. */\nexport function isValidPrefix(prefix: string): boolean {\n return PREFIX_RE.test(prefix)\n}\n\nfunction bytesToUuid(bytes: Uint8Array): string {\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`\n}\n\n// Monotonic state: RFC 9562 method 1, a 12-bit counter in rand_a. Seeded with\n// random bits below its top bit on each new millisecond, so it can grow.\nlet lastMs = -1\nlet counter = 0\n\nexport function uuidv7Bytes(now: number = Date.now()): Uint8Array {\n const bytes = crypto.getRandomValues(new Uint8Array(16))\n if (now > lastMs) {\n lastMs = now\n counter = ((bytes[6]! & 0x07) << 8) | bytes[7]!\n } else {\n counter += 1\n if (counter > 0xfff) {\n // Counter overflow: borrow the next millisecond, keep the order.\n lastMs += 1\n counter = 0\n }\n }\n const ms = lastMs\n bytes[0] = Math.floor(ms / 2 ** 40) & 0xff\n bytes[1] = Math.floor(ms / 2 ** 32) & 0xff\n bytes[2] = Math.floor(ms / 2 ** 24) & 0xff\n bytes[3] = Math.floor(ms / 2 ** 16) & 0xff\n bytes[4] = Math.floor(ms / 2 ** 8) & 0xff\n bytes[5] = ms & 0xff\n bytes[6] = 0x70 | ((counter >> 8) & 0x0f)\n bytes[7] = counter & 0xff\n bytes[8] = 0x80 | (bytes[8]! & 0x3f)\n return bytes\n}\n\n/** Generate a new, branded TypeID for the given prefix. */\nexport function generate<P extends string>(prefix: P): TypeId<P> {\n if (!isValidPrefix(prefix))\n throw new Error(`Invalid typeid prefix: \"${prefix}\"`)\n const bytes = uuidv7Bytes()\n return `${prefix}_${encode(bytes)}` as TypeId<P>\n}\n\n/**\n * Parse a TypeID into its parts and the decoded UUID. Throws if the id is\n * malformed or — when `expectedPrefix` is given — if the prefix does not match.\n */\nexport function parse<P extends string>(\n id: string,\n expectedPrefix?: P,\n): { prefix: string; suffix: string; uuid: string } {\n const sep = id.lastIndexOf('_')\n if (sep <= 0) throw new Error(`Malformed typeid: \"${id}\"`)\n const prefix = id.slice(0, sep)\n const suffix = id.slice(sep + 1)\n if (!isValidPrefix(prefix))\n throw new Error(`Malformed typeid prefix: \"${id}\"`)\n if (!SUFFIX_RE.test(suffix))\n throw new Error(`Malformed typeid suffix: \"${id}\"`)\n if (expectedPrefix !== undefined && prefix !== expectedPrefix) {\n throw new Error(\n `Expected typeid prefix \"${expectedPrefix}\" but got \"${prefix}\"`,\n )\n }\n return { prefix, suffix, uuid: bytesToUuid(decode(suffix)) }\n}\n\n/**\n * Escape hatch: validate a raw string against a prefix and brand it as a TypeID.\n * Use at trust boundaries (e.g. a raw URL param) where the string isn't yet typed.\n */\nexport function asTypeId<P extends string>(prefix: P, raw: string): TypeId<P> {\n parse(raw, prefix)\n return raw as TypeId<P>\n}\n\n/**\n * Drizzle column builder for a branded TypeID text value. Stores a plain `text`\n * column so drizzle-kit migrations and `$inferSelect` work unchanged.\n *\n * Use Drizzle's `$defaultFn` when a column should generate IDs on insert:\n *\n * id: typeid('post').primaryKey().$defaultFn(() => generate('post'))\n */\nexport function typeid<P extends string>(prefix: P) {\n if (!isValidPrefix(prefix))\n throw new Error(`Invalid typeid prefix: \"${prefix}\"`)\n return customType<{ data: TypeId<P>; driverData: string }>({\n dataType: () => 'text',\n })()\n}\n"]}
package/dist/vite.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Vite plugin for a bunderstack SPA. It builds into `dist/client`, which
3
+ * `wrangler.json` serves as static assets, and under `bunderstack dev` it
4
+ * proxies the API to the dev Worker.
5
+ *
6
+ * ```ts
7
+ * import { bunderstack } from 'bunderstack/vite'
8
+ * export default defineConfig({ plugins: [react(), bunderstack()] })
9
+ * ```
10
+ *
11
+ * No import of `vite`: the plugin is a plain object, so the package does not
12
+ * depend on a Vite version.
13
+ */
14
+ export declare function bunderstack(options?: {
15
+ apiPrefix?: string;
16
+ }): {
17
+ name: "bunderstack";
18
+ config(): {
19
+ server?: {
20
+ proxy: Record<string, {
21
+ target: string;
22
+ changeOrigin: boolean;
23
+ }>;
24
+ } | undefined;
25
+ build: {
26
+ outDir: string;
27
+ };
28
+ };
29
+ };
30
+ //# sourceMappingURL=vite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO;;;;mBAe3C,MAAM,CAAC,MAAM,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,YAAY,EAAE,OAAO,CAAA;aAAE,CAAC;;;;;;EAO7E"}
package/dist/vite.js ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Vite plugin for a bunderstack SPA. It builds into `dist/client`, which
3
+ * `wrangler.json` serves as static assets, and under `bunderstack dev` it
4
+ * proxies the API to the dev Worker.
5
+ *
6
+ * ```ts
7
+ * import { bunderstack } from 'bunderstack/vite'
8
+ * export default defineConfig({ plugins: [react(), bunderstack()] })
9
+ * ```
10
+ *
11
+ * No import of `vite`: the plugin is a plain object, so the package does not
12
+ * depend on a Vite version.
13
+ */
14
+ export function bunderstack(options = {}) {
15
+ const prefix = options.apiPrefix ?? '/api';
16
+ return {
17
+ name: 'bunderstack',
18
+ config() {
19
+ // `bunderstack dev` sets this to the celld URL.
20
+ const target = process.env.BUNDERSTACK_DEV_API_URL;
21
+ return {
22
+ build: { outDir: 'dist/client' },
23
+ ...(target
24
+ ? {
25
+ server: {
26
+ proxy: {
27
+ // Auth checks the Origin against APP_URL, the Vite URL.
28
+ [prefix]: { target, changeOrigin: false },
29
+ },
30
+ },
31
+ }
32
+ : {}),
33
+ };
34
+ },
35
+ };
36
+ }
37
+ //# sourceMappingURL=vite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.js","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,WAAW,CAAC,UAAkC,EAAE;IAC9D,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,CAAA;IAC1C,OAAO;QACL,IAAI,EAAE,aAAsB;QAC5B,MAAM;YACJ,gDAAgD;YAChD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAA;YAClD,OAAO;gBACL,KAAK,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE;gBAChC,GAAG,CAAC,MAAM;oBACR,CAAC,CAAC;wBACE,MAAM,EAAE;4BACN,KAAK,EAAE;gCACL,wDAAwD;gCACxD,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE;6BACmB;yBAC/D;qBACF;oBACH,CAAC,CAAC,EAAE,CAAC;aACR,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["/**\n * Vite plugin for a bunderstack SPA. It builds into `dist/client`, which\n * `wrangler.json` serves as static assets, and under `bunderstack dev` it\n * proxies the API to the dev Worker.\n *\n * ```ts\n * import { bunderstack } from 'bunderstack/vite'\n * export default defineConfig({ plugins: [react(), bunderstack()] })\n * ```\n *\n * No import of `vite`: the plugin is a plain object, so the package does not\n * depend on a Vite version.\n */\nexport function bunderstack(options: { apiPrefix?: string } = {}) {\n const prefix = options.apiPrefix ?? '/api'\n return {\n name: 'bunderstack' as const,\n config() {\n // `bunderstack dev` sets this to the celld URL.\n const target = process.env.BUNDERSTACK_DEV_API_URL\n return {\n build: { outDir: 'dist/client' },\n ...(target\n ? {\n server: {\n proxy: {\n // Auth checks the Origin against APP_URL, the Vite URL.\n [prefix]: { target, changeOrigin: false },\n } as Record<string, { target: string; changeOrigin: boolean }>,\n },\n }\n : {}),\n }\n },\n }\n}\n"]}
@@ -0,0 +1,19 @@
1
+ import type { BunderstackBackend } from '../backend.js';
2
+ import type { Platform } from '../platform.js';
3
+ import type { DurableObjectNamespaceLike, WorkerEnv } from './types.js';
4
+ export type WorkerApp = {
5
+ handler(request: Request): Promise<Response>;
6
+ jobs: {
7
+ tick(now?: number): Promise<{
8
+ claimed: number;
9
+ }>;
10
+ nextDueAt(now?: number, until?: number): Promise<number | null>;
11
+ };
12
+ };
13
+ export declare function notifyScheduler(namespace: DurableObjectNamespaceLike, runAt: number): Promise<void>;
14
+ export declare function envStrings(env: WorkerEnv): Record<string, string>;
15
+ export declare function workerPlatform(env: WorkerEnv): Partial<Platform>;
16
+ type AnyBackend = BunderstackBackend<any>;
17
+ export declare function appFor(backend: AnyBackend, env: WorkerEnv, role: 'fetch' | 'scheduler', platform?: Partial<Platform>): Promise<WorkerApp>;
18
+ export {};
19
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/workers/app.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,KAAK,EAAE,0BAA0B,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAOpE,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC5C,IAAI,EAAE;QACJ,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAChD,SAAS,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;KAChE,CAAA;CACF,CAAA;AAED,wBAAsB,eAAe,CACnC,SAAS,EAAE,0BAA0B,EACrC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMjE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgBhE;AAED,KAAK,UAAU,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAA;AAGzC,wBAAgB,MAAM,CACpB,OAAO,EAAE,UAAU,EACnB,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,OAAO,GAAG,WAAW,EAC3B,QAAQ,GAAE,OAAO,CAAC,QAAQ,CAAM,GAC/B,OAAO,CAAC,SAAS,CAAC,CAapB"}
@@ -0,0 +1,56 @@
1
+ import { workerStorageFactory } from './r2.js';
2
+ import { durableRateLimitStore } from './rate-limiter.js';
3
+ import { HubPublisher } from './realtime-hub.js';
4
+ import { stub } from './types.js';
5
+ export async function notifyScheduler(namespace, runAt) {
6
+ const res = await stub(namespace, 'main').fetch('https://scheduler/notify', {
7
+ method: 'POST',
8
+ body: JSON.stringify({ runAt }),
9
+ });
10
+ if (!res.ok) {
11
+ throw new Error(`[bunderstack] scheduler notify failed (${res.status})`);
12
+ }
13
+ }
14
+ export function envStrings(env) {
15
+ const out = {};
16
+ for (const [key, value] of Object.entries(env)) {
17
+ if (typeof value === 'string')
18
+ out[key] = value;
19
+ }
20
+ return out;
21
+ }
22
+ export function workerPlatform(env) {
23
+ const { SCHEDULER, REALTIME, RATE_LIMITER } = env;
24
+ return {
25
+ ...(SCHEDULER
26
+ ? {
27
+ jobs: {
28
+ notify: (runAt) => notifyScheduler(SCHEDULER, runAt),
29
+ },
30
+ }
31
+ : {}),
32
+ ...(REALTIME
33
+ ? { realtime: new HubPublisher(() => stub(REALTIME, 'main')) }
34
+ : {}),
35
+ ...(RATE_LIMITER ? { rateLimit: durableRateLimitStore(RATE_LIMITER) } : {}),
36
+ storage: workerStorageFactory(env),
37
+ };
38
+ }
39
+ const apps = new WeakMap();
40
+ export function appFor(backend, env, role, platform = {}) {
41
+ let byRole = apps.get(backend);
42
+ if (!byRole)
43
+ apps.set(backend, (byRole = new Map()));
44
+ const cached = byRole.get(role);
45
+ if (cached)
46
+ return cached;
47
+ const started = backend.start({
48
+ env: envStrings(env),
49
+ platform: { ...workerPlatform(env), ...platform },
50
+ });
51
+ byRole.set(role, started);
52
+ // A failed start must not poison the isolate: the next request retries.
53
+ started.catch(() => byRole.delete(role));
54
+ return started;
55
+ }
56
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/workers/app.ts"],"names":[],"mappings":"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","sourcesContent":["// src/workers/app.ts — one app per isolate and role, built from bindings.\nimport type { BunderstackBackend } from '../backend'\nimport type { Platform } from '../platform'\nimport type { DurableObjectNamespaceLike, WorkerEnv } from './types'\n\nimport { workerStorageFactory } from './r2'\nimport { durableRateLimitStore } from './rate-limiter'\nimport { HubPublisher } from './realtime-hub'\nimport { stub } from './types'\n\nexport type WorkerApp = {\n handler(request: Request): Promise<Response>\n jobs: {\n tick(now?: number): Promise<{ claimed: number }>\n nextDueAt(now?: number, until?: number): Promise<number | null>\n }\n}\n\nexport async function notifyScheduler(\n namespace: DurableObjectNamespaceLike,\n runAt: number,\n): Promise<void> {\n const res = await stub(namespace, 'main').fetch('https://scheduler/notify', {\n method: 'POST',\n body: JSON.stringify({ runAt }),\n })\n if (!res.ok) {\n throw new Error(`[bunderstack] scheduler notify failed (${res.status})`)\n }\n}\n\nexport function envStrings(env: WorkerEnv): Record<string, string> {\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(env)) {\n if (typeof value === 'string') out[key] = value\n }\n return out\n}\n\nexport function workerPlatform(env: WorkerEnv): Partial<Platform> {\n const { SCHEDULER, REALTIME, RATE_LIMITER } = env\n return {\n ...(SCHEDULER\n ? {\n jobs: {\n notify: (runAt: number) => notifyScheduler(SCHEDULER, runAt),\n },\n }\n : {}),\n ...(REALTIME\n ? { realtime: new HubPublisher(() => stub(REALTIME, 'main')) }\n : {}),\n ...(RATE_LIMITER ? { rateLimit: durableRateLimitStore(RATE_LIMITER) } : {}),\n storage: workerStorageFactory(env),\n }\n}\n\ntype AnyBackend = BunderstackBackend<any>\nconst apps = new WeakMap<AnyBackend, Map<string, Promise<WorkerApp>>>()\n\nexport function appFor(\n backend: AnyBackend,\n env: WorkerEnv,\n role: 'fetch' | 'scheduler',\n platform: Partial<Platform> = {},\n): Promise<WorkerApp> {\n let byRole = apps.get(backend)\n if (!byRole) apps.set(backend, (byRole = new Map()))\n const cached = byRole.get(role)\n if (cached) return cached\n const started = backend.start({\n env: envStrings(env),\n platform: { ...workerPlatform(env), ...platform },\n }) as Promise<WorkerApp>\n byRole.set(role, started)\n // A failed start must not poison the isolate: the next request retries.\n started.catch(() => byRole.delete(role))\n return started\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import type { BunderstackBackend } from '../backend.js';
2
+ import type { ExecutionContextLike, WorkerEnv } from './types.js';
3
+ import { RateLimiter } from './rate-limiter.js';
4
+ import { RealtimeHub } from './realtime-hub.js';
5
+ export declare function createWorker(backend: BunderstackBackend<any>): {
6
+ handler: {
7
+ fetch(request: Request, env: WorkerEnv, _ctx: ExecutionContextLike): Promise<Response>;
8
+ scheduled(_controller: unknown, env: WorkerEnv, ctx: ExecutionContextLike): Promise<void>;
9
+ };
10
+ durableObjects: {
11
+ Scheduler: import("./scheduler.js").SchedulerClass;
12
+ RealtimeHub: typeof RealtimeHub;
13
+ RateLimiter: typeof RateLimiter;
14
+ };
15
+ };
16
+ export { createSchedulerClass, type SchedulerClass, type SchedulerObject, } from './scheduler.js';
17
+ export { HubPublisher, RealtimeHub } from './realtime-hub.js';
18
+ export { durableRateLimitStore, RateLimiter } from './rate-limiter.js';
19
+ export { bucketBindingName, R2StorageAdapter } from './r2.js';
20
+ export type * from './types.js';
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/workers/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAG9D,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAG5C,wBAAgB,YAAY,CAAC,OAAO,EAAE,kBAAkB,CAAC,GAAG,CAAC;;uBAG9C,OAAO,OACX,SAAS,QACR,oBAAoB,GACzB,OAAO,CAAC,QAAQ,CAAC;+BASL,OAAO,OACf,SAAS,OACT,oBAAoB,GACxB,OAAO,CAAC,IAAI,CAAC;;;;;;;EAanB;AAED,OAAO,EACL,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC1D,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,MAAM,CAAA;AAC1D,mBAAmB,SAAS,CAAA"}
@@ -0,0 +1,33 @@
1
+ import { appFor, notifyScheduler } from './app.js';
2
+ import { RateLimiter } from './rate-limiter.js';
3
+ import { RealtimeHub } from './realtime-hub.js';
4
+ import { createSchedulerClass } from './scheduler.js';
5
+ export function createWorker(backend) {
6
+ const handler = {
7
+ async fetch(request, env, _ctx) {
8
+ const app = await appFor(backend, env, 'fetch');
9
+ const response = await app.handler(request);
10
+ // A path that reached the Worker but has no route: let the SPA answer.
11
+ if (response.status === 404 && env.ASSETS)
12
+ return env.ASSETS.fetch(request);
13
+ return response;
14
+ },
15
+ async scheduled(_controller, env, ctx) {
16
+ if (env.SCHEDULER)
17
+ ctx.waitUntil(notifyScheduler(env.SCHEDULER, Date.now()));
18
+ },
19
+ };
20
+ return {
21
+ handler,
22
+ durableObjects: {
23
+ Scheduler: createSchedulerClass(backend),
24
+ RealtimeHub,
25
+ RateLimiter,
26
+ },
27
+ };
28
+ }
29
+ export { createSchedulerClass, } from './scheduler.js';
30
+ export { HubPublisher, RealtimeHub } from './realtime-hub.js';
31
+ export { durableRateLimitStore, RateLimiter } from './rate-limiter.js';
32
+ export { bucketBindingName, R2StorageAdapter } from './r2.js';
33
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workers/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,OAAO,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAElD,MAAM,UAAU,YAAY,CAAC,OAAgC;IAC3D,MAAM,OAAO,GAAG;QACd,KAAK,CAAC,KAAK,CACT,OAAgB,EAChB,GAAc,EACd,IAA0B;YAE1B,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;YAC/C,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;YAC3C,uEAAuE;YACvE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM;gBACvC,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YAClC,OAAO,QAAQ,CAAA;QACjB,CAAC;QACD,KAAK,CAAC,SAAS,CACb,WAAoB,EACpB,GAAc,EACd,GAAyB;YAEzB,IAAI,GAAG,CAAC,SAAS;gBACf,GAAG,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;QAC7D,CAAC;KACF,CAAA;IACD,OAAO;QACL,OAAO;QACP,cAAc,EAAE;YACd,SAAS,EAAE,oBAAoB,CAAC,OAAO,CAAC;YACxC,WAAW;YACX,WAAW;SACZ;KACF,CAAA;AACH,CAAC;AAED,OAAO,EACL,oBAAoB,GAGrB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC1D,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,MAAM,CAAA","sourcesContent":["// src/workers/index.ts — `bunderstack/workers`: run an app as a Worker on\n// Cloudflare or celld. See the Workers runtime spec for the binding names.\nimport type { BunderstackBackend } from '../backend'\nimport type { ExecutionContextLike, WorkerEnv } from './types'\n\nimport { appFor, notifyScheduler } from './app'\nimport { RateLimiter } from './rate-limiter'\nimport { RealtimeHub } from './realtime-hub'\nimport { createSchedulerClass } from './scheduler'\n\nexport function createWorker(backend: BunderstackBackend<any>) {\n const handler = {\n async fetch(\n request: Request,\n env: WorkerEnv,\n _ctx: ExecutionContextLike,\n ): Promise<Response> {\n const app = await appFor(backend, env, 'fetch')\n const response = await app.handler(request)\n // A path that reached the Worker but has no route: let the SPA answer.\n if (response.status === 404 && env.ASSETS)\n return env.ASSETS.fetch(request)\n return response\n },\n async scheduled(\n _controller: unknown,\n env: WorkerEnv,\n ctx: ExecutionContextLike,\n ): Promise<void> {\n if (env.SCHEDULER)\n ctx.waitUntil(notifyScheduler(env.SCHEDULER, Date.now()))\n },\n }\n return {\n handler,\n durableObjects: {\n Scheduler: createSchedulerClass(backend),\n RealtimeHub,\n RateLimiter,\n },\n }\n}\n\nexport {\n createSchedulerClass,\n type SchedulerClass,\n type SchedulerObject,\n} from './scheduler'\nexport { HubPublisher, RealtimeHub } from './realtime-hub'\nexport { durableRateLimitStore, RateLimiter } from './rate-limiter'\nexport { bucketBindingName, R2StorageAdapter } from './r2'\nexport type * from './types'\n"]}
@@ -0,0 +1,23 @@
1
+ import type { StorageAdapterFactory } from '../platform.js';
2
+ import type { PresignGetOptions, PresignPutOptions, StorageAdapter } from '../storage/index.js';
3
+ import type { R2BucketLike, WorkerEnv } from './types.js';
4
+ import { S3StorageAdapter } from '../storage/s3.js';
5
+ export declare function bucketBindingName(bucketName: string): string;
6
+ export declare class R2StorageAdapter implements StorageAdapter {
7
+ private readonly bucket;
8
+ presignPut?: (key: string, opts: PresignPutOptions) => Promise<string>;
9
+ presignGet?: (key: string, opts: PresignGetOptions) => Promise<string>;
10
+ publicUrlFor?: (key: string) => string | undefined;
11
+ constructor(bucket: R2BucketLike, presigner?: S3StorageAdapter);
12
+ upload(fileId: string, data: Blob | ArrayBuffer, contentType: string): Promise<void>;
13
+ get(fileId: string): Promise<Response>;
14
+ delete(fileId: string): Promise<void>;
15
+ exists(fileId: string): Promise<boolean>;
16
+ stat(key: string): Promise<{
17
+ size: number;
18
+ contentType: string;
19
+ } | null>;
20
+ list(prefix: string): Promise<string[]>;
21
+ }
22
+ export declare function workerStorageFactory(env: WorkerEnv): StorageAdapterFactory;
23
+ //# sourceMappingURL=r2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2.d.ts","sourceRoot":"","sources":["../../src/workers/r2.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACf,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAEtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAEhD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,qBAAa,gBAAiB,YAAW,cAAc;IAMnD,OAAO,CAAC,QAAQ,CAAC,MAAM;IALzB,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IACtE,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IACtE,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;gBAG/B,MAAM,EAAE,YAAY,EACrC,SAAS,CAAC,EAAE,gBAAgB;IASxB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,WAAW,EAAE,WAAW,EAAE,MAAM;IAKpE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAWtC,MAAM,CAAC,MAAM,EAAE,MAAM;IAIrB,MAAM,CAAC,MAAM,EAAE,MAAM;IAIrB,IAAI,CAAC,GAAG,EAAE,MAAM;;;;IAUhB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAU9C;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,SAAS,GAAG,qBAAqB,CAY1E"}
@@ -0,0 +1,77 @@
1
+ import { S3StorageAdapter } from '../storage/s3.js';
2
+ export function bucketBindingName(bucketName) {
3
+ return `BUCKET_${bucketName.toUpperCase().replace(/[^A-Z0-9]/g, '_')}`;
4
+ }
5
+ export class R2StorageAdapter {
6
+ bucket;
7
+ presignPut;
8
+ presignGet;
9
+ publicUrlFor;
10
+ constructor(bucket, presigner) {
11
+ this.bucket = bucket;
12
+ if (presigner) {
13
+ this.presignPut = (key, opts) => presigner.presignPut(key, opts);
14
+ this.presignGet = (key, opts) => presigner.presignGet(key, opts);
15
+ this.publicUrlFor = (key) => presigner.publicUrlFor(key);
16
+ }
17
+ }
18
+ async upload(fileId, data, contentType) {
19
+ const bytes = data instanceof Blob ? await data.arrayBuffer() : data;
20
+ await this.bucket.put(fileId, bytes, { httpMetadata: { contentType } });
21
+ }
22
+ async get(fileId) {
23
+ const object = await this.bucket.get(fileId);
24
+ if (!object)
25
+ return new Response('Not found', { status: 404 });
26
+ return new Response(object.body, {
27
+ headers: {
28
+ 'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
29
+ },
30
+ });
31
+ }
32
+ async delete(fileId) {
33
+ await this.bucket.delete(fileId);
34
+ }
35
+ async exists(fileId) {
36
+ return (await this.bucket.head(fileId)) !== null;
37
+ }
38
+ async stat(key) {
39
+ const object = await this.bucket.head(key);
40
+ return object
41
+ ? {
42
+ size: object.size,
43
+ contentType: object.httpMetadata?.contentType ?? '',
44
+ }
45
+ : null;
46
+ }
47
+ async list(prefix) {
48
+ const keys = [];
49
+ let cursor;
50
+ do {
51
+ const page = await this.bucket.list({ prefix, cursor });
52
+ keys.push(...page.objects.map((object) => object.key));
53
+ cursor = page.truncated ? page.cursor : undefined;
54
+ } while (cursor);
55
+ return keys;
56
+ }
57
+ }
58
+ export function workerStorageFactory(env) {
59
+ return (backend, bucket) => {
60
+ const binding = env[bucketBindingName(bucket.name)];
61
+ const s3 = backend.type === 's3' ? new S3StorageAdapter(backend) : undefined;
62
+ if (binding)
63
+ return new R2StorageAdapter(binding, s3);
64
+ if (s3)
65
+ return s3;
66
+ // The core builds an adapter for every resolved bucket at start, even the
67
+ // implicit default one; fail on use, not on start.
68
+ return missingBinding(bucket.name);
69
+ };
70
+ }
71
+ function missingBinding(bucketName) {
72
+ const fail = async () => {
73
+ throw new Error(`[bunderstack] storage bucket "${bucketName}" needs the R2 binding ${bucketBindingName(bucketName)}`);
74
+ };
75
+ return { upload: fail, get: fail, delete: fail, exists: fail };
76
+ }
77
+ //# sourceMappingURL=r2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2.js","sourceRoot":"","sources":["../../src/workers/r2.ts"],"names":[],"mappings":"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","sourcesContent":["// src/workers/r2.ts — storage on an R2 binding. Presign needs S3 keys to the\n// same bucket; without them uploads go through the Worker (proxy mode).\nimport type { StorageAdapterFactory } from '../platform'\nimport type {\n PresignGetOptions,\n PresignPutOptions,\n StorageAdapter,\n} from '../storage/index'\nimport type { R2BucketLike, WorkerEnv } from './types'\n\nimport { S3StorageAdapter } from '../storage/s3'\n\nexport function bucketBindingName(bucketName: string): string {\n return `BUCKET_${bucketName.toUpperCase().replace(/[^A-Z0-9]/g, '_')}`\n}\n\nexport class R2StorageAdapter implements StorageAdapter {\n presignPut?: (key: string, opts: PresignPutOptions) => Promise<string>\n presignGet?: (key: string, opts: PresignGetOptions) => Promise<string>\n publicUrlFor?: (key: string) => string | undefined\n\n constructor(\n private readonly bucket: R2BucketLike,\n presigner?: S3StorageAdapter,\n ) {\n if (presigner) {\n this.presignPut = (key, opts) => presigner.presignPut(key, opts)\n this.presignGet = (key, opts) => presigner.presignGet(key, opts)\n this.publicUrlFor = (key) => presigner.publicUrlFor(key)\n }\n }\n\n async upload(fileId: string, data: Blob | ArrayBuffer, contentType: string) {\n const bytes = data instanceof Blob ? await data.arrayBuffer() : data\n await this.bucket.put(fileId, bytes, { httpMetadata: { contentType } })\n }\n\n async get(fileId: string): Promise<Response> {\n const object = await this.bucket.get(fileId)\n if (!object) return new Response('Not found', { status: 404 })\n return new Response(object.body, {\n headers: {\n 'Content-Type':\n object.httpMetadata?.contentType || 'application/octet-stream',\n },\n })\n }\n\n async delete(fileId: string) {\n await this.bucket.delete(fileId)\n }\n\n async exists(fileId: string) {\n return (await this.bucket.head(fileId)) !== null\n }\n\n async stat(key: string) {\n const object = await this.bucket.head(key)\n return object\n ? {\n size: object.size,\n contentType: object.httpMetadata?.contentType ?? '',\n }\n : null\n }\n\n async list(prefix: string): Promise<string[]> {\n const keys: string[] = []\n let cursor: string | undefined\n do {\n const page = await this.bucket.list({ prefix, cursor })\n keys.push(...page.objects.map((object) => object.key))\n cursor = page.truncated ? page.cursor : undefined\n } while (cursor)\n return keys\n }\n}\n\nexport function workerStorageFactory(env: WorkerEnv): StorageAdapterFactory {\n return (backend, bucket) => {\n const binding = env[bucketBindingName(bucket.name)] as\n | R2BucketLike\n | undefined\n const s3 = backend.type === 's3' ? new S3StorageAdapter(backend) : undefined\n if (binding) return new R2StorageAdapter(binding, s3)\n if (s3) return s3\n // The core builds an adapter for every resolved bucket at start, even the\n // implicit default one; fail on use, not on start.\n return missingBinding(bucket.name)\n }\n}\n\nfunction missingBinding(bucketName: string): StorageAdapter {\n const fail = async (): Promise<never> => {\n throw new Error(\n `[bunderstack] storage bucket \"${bucketName}\" needs the R2 binding ${bucketBindingName(bucketName)}`,\n )\n }\n return { upload: fail, get: fail, delete: fail, exists: fail }\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import type { RateLimitStore } from '../platform.js';
2
+ import type { DurableObjectNamespaceLike, DurableObjectStateLike } from './types.js';
3
+ export declare class RateLimiter {
4
+ private readonly store;
5
+ constructor(_state: DurableObjectStateLike, _env: unknown);
6
+ fetch(request: Request): Promise<Response>;
7
+ }
8
+ export declare function durableRateLimitStore(namespace: DurableObjectNamespaceLike): RateLimitStore;
9
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/workers/rate-limiter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,KAAK,EACV,0BAA0B,EAC1B,sBAAsB,EACvB,MAAM,SAAS,CAAA;AAOhB,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;gBAEzC,MAAM,EAAE,sBAAsB,EAAE,IAAI,EAAE,OAAO;IAEnD,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;CAIjD;AAED,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,0BAA0B,GACpC,cAAc,CAUhB"}