@lunora/platform-cloudflare 1.0.0-alpha.101 → 1.0.0-alpha.103

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/README.md CHANGED
@@ -8,7 +8,7 @@ The Cloudflare implementation of the [`@lunora/platform`](../platform) host cont
8
8
  import { createShardPlatform } from "@lunora/platform-cloudflare";
9
9
 
10
10
  // Inside a Durable Object:
11
- const platform = createShardPlatform(state);
11
+ const platform = createShardPlatform(state, env);
12
12
  ```
13
13
 
14
14
  ## Why it exists
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DurableObjectNamespace, DurableObjectState } from '@cloudflare/workers-types';
2
- import { ShardAlarms, ShardDirectory, ShardHost, ShardKvStore, SocketHost, PlatformCapabilities, SchedulerHost } from '@lunora/platform';
2
+ import { SecretValueHost, SecretValueStore, ShardAlarms, ShardDirectory, ShardHost, ShardKvStore, SocketHost, PlatformCapabilities, SchedulerHost } from '@lunora/platform';
3
3
  /** The alarm subset of `state.storage`, structurally (shared by shard + session hosts). */
4
4
  interface AlarmStorageLike {
5
5
  deleteAlarm?: () => Promise<void>;
@@ -24,18 +24,58 @@ interface KvStorageLike {
24
24
  delete: (key: string) => Promise<boolean | number>;
25
25
  get: <T = unknown>(key: string) => Promise<T | undefined>;
26
26
  list?: <T = unknown>(options?: {
27
+ limit?: number;
27
28
  prefix?: string;
28
29
  }) => Promise<Map<string, T>>;
29
30
  put: (key: string, value: unknown) => Promise<void>;
30
31
  }
32
+ /**
33
+ * {@link KvStorageLike} plus `transaction`, which {@link createSecretValueHost}
34
+ * needs for its read-check-bind in `take`. Method syntax on purpose, so the real
35
+ * `DurableObjectStorage` (whose transaction closure takes its own transaction
36
+ * type) is accepted without a cast.
37
+ */
38
+ interface SecretStorageLike extends KvStorageLike {
39
+ list: <T = unknown>(options?: {
40
+ limit?: number;
41
+ prefix?: string;
42
+ }) => Promise<Map<string, T>>;
43
+ transaction: <T>(closure: (txn: KvStorageLike) => Promise<T>) => Promise<T>;
44
+ }
31
45
  /**
32
46
  * Turn a Durable Object's `state.storage` into the provider-neutral
33
47
  * {@link ShardKvStore}. The DO's KV surface already matches the contract
34
- * method-for-method; this adapter only normalizes the return of `delete`
35
- * (workerd resolves a boolean, some doubles a number) and fails closed on a
36
- * `list` the underlying storage does not provide.
48
+ * method-for-method; this adapter normalizes the return of `delete` (workerd
49
+ * resolves a boolean, some doubles a number), fails closed on a `list` the
50
+ * underlying storage does not provide, and keeps secret records invisible: `get`
51
+ * and `list` skip a reserved key whose stored value is secret-shaped, and
52
+ * `put`/`delete` refuse to touch one.
37
53
  */
38
54
  declare const createShardKvStore: (storage: KvStorageLike) => ShardKvStore;
55
+ /**
56
+ * Turn a Durable Object's `state.storage` into a {@link SecretValueHost}.
57
+ *
58
+ * Records live under {@link SECRET_VALUE_KEY_PREFIX} in the shard's own
59
+ * key-value storage. Keys sort by expiry (a zero-padded `expiresAt` leads the
60
+ * ref), so the sweep in `put` reads the earliest-expiring records first and stops
61
+ * at the first live one in a page.
62
+ *
63
+ * `take` runs its read, claimant bind and write inside `storage.transaction`, so
64
+ * two takes cannot both observe the ref unbound. `options.now` exists for the
65
+ * conformance suite's clock and defaults to `Date.now`.
66
+ */
67
+ declare const createSecretValueStore: (storage: SecretStorageLike, options?: {
68
+ now?: () => number;
69
+ }) => SecretValueStore;
70
+ /**
71
+ * Turn a Durable Object's `state.storage` and Worker `env` into a
72
+ * {@link SecretValueHost}: the reserved-namespace store wrapped in AES-256-GCM.
73
+ * With no key bound, every `put` and `take` refuses with `SecretValueKeyError`;
74
+ * nothing falls back to storing plaintext.
75
+ */
76
+ declare const createSecretValueHost: (storage: SecretStorageLike, env: unknown, options?: {
77
+ now?: () => number;
78
+ }) => SecretValueHost;
39
79
  declare const createSocketHost: (state: DurableObjectState) => SocketHost;
40
80
  /**
41
81
  * Turn a Durable Object namespace binding into a {@link ShardDirectory}.
@@ -57,6 +97,8 @@ declare const createShardDirectory: (namespace: DurableObjectNamespace) => Shard
57
97
  interface ShardPlatform {
58
98
  /** Durable key-value storage for this shard. */
59
99
  kv: ShardKvStore;
100
+ /** Human-supplied secret values held for one durable step, kept out of `kv`'s reach. */
101
+ secretValues: SecretValueHost;
60
102
  /** Single-writer execution, local SQL, transactions, alarms, identity. */
61
103
  shard: ShardHost;
62
104
  /** Hibernated WebSocket subscriptions for this shard. */
@@ -106,8 +148,12 @@ interface WorkerPlatformOptions {
106
148
  * `state` is typed `unknown` so this package does not force
107
149
  * `@cloudflare/workers-types` onto callers that are already holding the runtime
108
150
  * object; the adapters narrow it internally.
151
+ *
152
+ * `env` carries the Worker secrets the secret-value host seals under. Leave it out
153
+ * for a shard that holds no key: the host then refuses every put and take rather
154
+ * than store plaintext.
109
155
  */
110
- declare const createShardPlatform: (state: unknown) => ShardPlatform;
156
+ declare const createShardPlatform: (state: unknown, env?: unknown) => ShardPlatform;
111
157
  /**
112
158
  * Compose every Worker-scoped contract from the Worker's `env`.
113
159
  *
@@ -117,6 +163,50 @@ declare const createShardPlatform: (state: unknown) => ShardPlatform;
117
163
  */
118
164
  declare const createWorkerPlatform: (env: unknown, options?: WorkerPlatformOptions) => WorkerPlatform;
119
165
  export { type ShardPlatform, type WorkerPlatform, type WorkerPlatformOptions,
166
+ /**
167
+ * `@lunora/platform-cloudflare` — the Cloudflare implementation of the
168
+ * `@lunora/platform` contracts.
169
+ *
170
+ * `@lunora/platform` says what a host must provide; this says how Cloudflare
171
+ * provides it, over `DurableObjectState`, hibernatable WebSockets, and a
172
+ * `DurableObjectNamespace`. A second target ships the same surface from its own
173
+ * `@lunora/platform-<target>` package, so adding one is a sibling rather than a
174
+ * refactor of `@lunora/do`.
175
+ *
176
+ * # Why this depends on nothing but the contracts
177
+ *
178
+ * An earlier version of this package sat *above* `@lunora/do` — it composed
179
+ * adapters that lived there, and so depended on the very package it was meant
180
+ * to make replaceable. That inverts the end-state, and with one host it also
181
+ * had zero consumers, so it was dissolved. This version is the other way up:
182
+ * the adapters live here, `@lunora/do` depends on this, and the only imports
183
+ * are `@lunora/platform` plus Cloudflare's own types. That is what makes the
184
+ * seam real rather than aspirational — `@lunora/do` can no longer reach a
185
+ * provider API except through a contract.
186
+ */
187
+ createSecretValueHost,
188
+ /**
189
+ * `@lunora/platform-cloudflare` — the Cloudflare implementation of the
190
+ * `@lunora/platform` contracts.
191
+ *
192
+ * `@lunora/platform` says what a host must provide; this says how Cloudflare
193
+ * provides it, over `DurableObjectState`, hibernatable WebSockets, and a
194
+ * `DurableObjectNamespace`. A second target ships the same surface from its own
195
+ * `@lunora/platform-<target>` package, so adding one is a sibling rather than a
196
+ * refactor of `@lunora/do`.
197
+ *
198
+ * # Why this depends on nothing but the contracts
199
+ *
200
+ * An earlier version of this package sat *above* `@lunora/do` — it composed
201
+ * adapters that lived there, and so depended on the very package it was meant
202
+ * to make replaceable. That inverts the end-state, and with one host it also
203
+ * had zero consumers, so it was dissolved. This version is the other way up:
204
+ * the adapters live here, `@lunora/do` depends on this, and the only imports
205
+ * are `@lunora/platform` plus Cloudflare's own types. That is what makes the
206
+ * seam real rather than aspirational — `@lunora/do` can no longer reach a
207
+ * provider API except through a contract.
208
+ */
209
+ createSecretValueStore,
120
210
  /**
121
211
  * `@lunora/platform-cloudflare` — the Cloudflare implementation of the
122
212
  * `@lunora/platform` contracts.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DurableObjectNamespace, DurableObjectState } from '@cloudflare/workers-types';
2
- import { ShardAlarms, ShardDirectory, ShardHost, ShardKvStore, SocketHost, PlatformCapabilities, SchedulerHost } from '@lunora/platform';
2
+ import { SecretValueHost, SecretValueStore, ShardAlarms, ShardDirectory, ShardHost, ShardKvStore, SocketHost, PlatformCapabilities, SchedulerHost } from '@lunora/platform';
3
3
  /** The alarm subset of `state.storage`, structurally (shared by shard + session hosts). */
4
4
  interface AlarmStorageLike {
5
5
  deleteAlarm?: () => Promise<void>;
@@ -24,18 +24,58 @@ interface KvStorageLike {
24
24
  delete: (key: string) => Promise<boolean | number>;
25
25
  get: <T = unknown>(key: string) => Promise<T | undefined>;
26
26
  list?: <T = unknown>(options?: {
27
+ limit?: number;
27
28
  prefix?: string;
28
29
  }) => Promise<Map<string, T>>;
29
30
  put: (key: string, value: unknown) => Promise<void>;
30
31
  }
32
+ /**
33
+ * {@link KvStorageLike} plus `transaction`, which {@link createSecretValueHost}
34
+ * needs for its read-check-bind in `take`. Method syntax on purpose, so the real
35
+ * `DurableObjectStorage` (whose transaction closure takes its own transaction
36
+ * type) is accepted without a cast.
37
+ */
38
+ interface SecretStorageLike extends KvStorageLike {
39
+ list: <T = unknown>(options?: {
40
+ limit?: number;
41
+ prefix?: string;
42
+ }) => Promise<Map<string, T>>;
43
+ transaction: <T>(closure: (txn: KvStorageLike) => Promise<T>) => Promise<T>;
44
+ }
31
45
  /**
32
46
  * Turn a Durable Object's `state.storage` into the provider-neutral
33
47
  * {@link ShardKvStore}. The DO's KV surface already matches the contract
34
- * method-for-method; this adapter only normalizes the return of `delete`
35
- * (workerd resolves a boolean, some doubles a number) and fails closed on a
36
- * `list` the underlying storage does not provide.
48
+ * method-for-method; this adapter normalizes the return of `delete` (workerd
49
+ * resolves a boolean, some doubles a number), fails closed on a `list` the
50
+ * underlying storage does not provide, and keeps secret records invisible: `get`
51
+ * and `list` skip a reserved key whose stored value is secret-shaped, and
52
+ * `put`/`delete` refuse to touch one.
37
53
  */
38
54
  declare const createShardKvStore: (storage: KvStorageLike) => ShardKvStore;
55
+ /**
56
+ * Turn a Durable Object's `state.storage` into a {@link SecretValueHost}.
57
+ *
58
+ * Records live under {@link SECRET_VALUE_KEY_PREFIX} in the shard's own
59
+ * key-value storage. Keys sort by expiry (a zero-padded `expiresAt` leads the
60
+ * ref), so the sweep in `put` reads the earliest-expiring records first and stops
61
+ * at the first live one in a page.
62
+ *
63
+ * `take` runs its read, claimant bind and write inside `storage.transaction`, so
64
+ * two takes cannot both observe the ref unbound. `options.now` exists for the
65
+ * conformance suite's clock and defaults to `Date.now`.
66
+ */
67
+ declare const createSecretValueStore: (storage: SecretStorageLike, options?: {
68
+ now?: () => number;
69
+ }) => SecretValueStore;
70
+ /**
71
+ * Turn a Durable Object's `state.storage` and Worker `env` into a
72
+ * {@link SecretValueHost}: the reserved-namespace store wrapped in AES-256-GCM.
73
+ * With no key bound, every `put` and `take` refuses with `SecretValueKeyError`;
74
+ * nothing falls back to storing plaintext.
75
+ */
76
+ declare const createSecretValueHost: (storage: SecretStorageLike, env: unknown, options?: {
77
+ now?: () => number;
78
+ }) => SecretValueHost;
39
79
  declare const createSocketHost: (state: DurableObjectState) => SocketHost;
40
80
  /**
41
81
  * Turn a Durable Object namespace binding into a {@link ShardDirectory}.
@@ -57,6 +97,8 @@ declare const createShardDirectory: (namespace: DurableObjectNamespace) => Shard
57
97
  interface ShardPlatform {
58
98
  /** Durable key-value storage for this shard. */
59
99
  kv: ShardKvStore;
100
+ /** Human-supplied secret values held for one durable step, kept out of `kv`'s reach. */
101
+ secretValues: SecretValueHost;
60
102
  /** Single-writer execution, local SQL, transactions, alarms, identity. */
61
103
  shard: ShardHost;
62
104
  /** Hibernated WebSocket subscriptions for this shard. */
@@ -106,8 +148,12 @@ interface WorkerPlatformOptions {
106
148
  * `state` is typed `unknown` so this package does not force
107
149
  * `@cloudflare/workers-types` onto callers that are already holding the runtime
108
150
  * object; the adapters narrow it internally.
151
+ *
152
+ * `env` carries the Worker secrets the secret-value host seals under. Leave it out
153
+ * for a shard that holds no key: the host then refuses every put and take rather
154
+ * than store plaintext.
109
155
  */
110
- declare const createShardPlatform: (state: unknown) => ShardPlatform;
156
+ declare const createShardPlatform: (state: unknown, env?: unknown) => ShardPlatform;
111
157
  /**
112
158
  * Compose every Worker-scoped contract from the Worker's `env`.
113
159
  *
@@ -117,6 +163,50 @@ declare const createShardPlatform: (state: unknown) => ShardPlatform;
117
163
  */
118
164
  declare const createWorkerPlatform: (env: unknown, options?: WorkerPlatformOptions) => WorkerPlatform;
119
165
  export { type ShardPlatform, type WorkerPlatform, type WorkerPlatformOptions,
166
+ /**
167
+ * `@lunora/platform-cloudflare` — the Cloudflare implementation of the
168
+ * `@lunora/platform` contracts.
169
+ *
170
+ * `@lunora/platform` says what a host must provide; this says how Cloudflare
171
+ * provides it, over `DurableObjectState`, hibernatable WebSockets, and a
172
+ * `DurableObjectNamespace`. A second target ships the same surface from its own
173
+ * `@lunora/platform-<target>` package, so adding one is a sibling rather than a
174
+ * refactor of `@lunora/do`.
175
+ *
176
+ * # Why this depends on nothing but the contracts
177
+ *
178
+ * An earlier version of this package sat *above* `@lunora/do` — it composed
179
+ * adapters that lived there, and so depended on the very package it was meant
180
+ * to make replaceable. That inverts the end-state, and with one host it also
181
+ * had zero consumers, so it was dissolved. This version is the other way up:
182
+ * the adapters live here, `@lunora/do` depends on this, and the only imports
183
+ * are `@lunora/platform` plus Cloudflare's own types. That is what makes the
184
+ * seam real rather than aspirational — `@lunora/do` can no longer reach a
185
+ * provider API except through a contract.
186
+ */
187
+ createSecretValueHost,
188
+ /**
189
+ * `@lunora/platform-cloudflare` — the Cloudflare implementation of the
190
+ * `@lunora/platform` contracts.
191
+ *
192
+ * `@lunora/platform` says what a host must provide; this says how Cloudflare
193
+ * provides it, over `DurableObjectState`, hibernatable WebSockets, and a
194
+ * `DurableObjectNamespace`. A second target ships the same surface from its own
195
+ * `@lunora/platform-<target>` package, so adding one is a sibling rather than a
196
+ * refactor of `@lunora/do`.
197
+ *
198
+ * # Why this depends on nothing but the contracts
199
+ *
200
+ * An earlier version of this package sat *above* `@lunora/do` — it composed
201
+ * adapters that lived there, and so depended on the very package it was meant
202
+ * to make replaceable. That inverts the end-state, and with one host it also
203
+ * had zero consumers, so it was dissolved. This version is the other way up:
204
+ * the adapters live here, `@lunora/do` depends on this, and the only imports
205
+ * are `@lunora/platform` plus Cloudflare's own types. That is what makes the
206
+ * seam real rather than aspirational — `@lunora/do` can no longer reach a
207
+ * provider API except through a contract.
208
+ */
209
+ createSecretValueStore,
120
210
  /**
121
211
  * `@lunora/platform-cloudflare` — the Cloudflare implementation of the
122
212
  * `@lunora/platform` contracts.
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{createShardAlarms as t,createShardDirectory as a,createShardHost as o,createShardKvStore as c,createSocketHost as S}from"./packem_shared/createShardAlarms-CWMouR-D.mjs";import{createShardPlatform as h,createWorkerPlatform as m}from"./packem_shared/createShardPlatform-DuE7x2MZ.mjs";export{t as createShardAlarms,a as createShardDirectory,o as createShardHost,c as createShardKvStore,h as createShardPlatform,S as createSocketHost,m as createWorkerPlatform};
1
+ import{createSecretValueHost as t,createSecretValueStore as a,createShardAlarms as o,createShardDirectory as c,createShardHost as S,createShardKvStore as d,createSocketHost as h}from"./packem_shared/createSecretValueHost-BaYQQbQ9.mjs";import{createShardPlatform as m,createWorkerPlatform as f}from"./packem_shared/createShardPlatform-ZR4yt0P8.mjs";export{t as createSecretValueHost,a as createSecretValueStore,o as createShardAlarms,c as createShardDirectory,S as createShardHost,d as createShardKvStore,m as createShardPlatform,h as createSocketHost,f as createWorkerPlatform};
@@ -0,0 +1 @@
1
+ import{LunoraError as b}from"@lunora/errors";import{clampSecretValueTtl as k,encryptingSecretValueHost as A,findSocketTagBudgetViolation as _}from"@lunora/platform";const T=(e,t,o)=>{const c=e.storage.sql;if(typeof c?.exec!="function")throw new TypeError("storage.sql.exec is not available on this DurableObjectState");return c.exec(t,...o)},U=e=>({delete:()=>typeof e.deleteAlarm=="function"?e.deleteAlarm():Promise.resolve(),get:()=>typeof e.getAlarm=="function"?e.getAlarm():null,set:t=>typeof e.setAlarm=="function"?e.setAlarm(t):Promise.resolve()}),R=(e,t)=>{if(!(!(e instanceof Error)||e===t||e.cause!==void 0))try{Object.defineProperty(e,"cause",{configurable:!0,value:t,writable:!0})}catch{}},C=e=>{const{storage:t}=e,o={get databaseSize(){return e.storage.sql?.databaseSize},exec:(n,...a)=>T(e,n,a)},c=U(t),r=async n=>{if(typeof e.blockConcurrencyWhile=="function"){const a=await e.blockConcurrencyWhile(async()=>{try{return{ok:!0,value:await n()}}catch(u){return{error:u,ok:!1}}});if(!a.ok)throw a.error;return a.value}return n()},i=async n=>{const a=t;if(typeof a?.transaction=="function"){let u;try{return await a.transaction(async()=>{try{return await n()}catch(s){throw u={value:s},s}})}catch(s){throw u?(R(u.value,s),u.value):s}}return n()};return{alarms:c,runSerialized:r,shardKey:e.id?.name,sql:o,transaction:i,waitUntil:typeof e.waitUntil=="function"?n=>{e.waitUntil(n)}:void 0}},w="__lunora_secret_value:",x=32,p=e=>e.startsWith(w),d=e=>{if(typeof e!="object"||e===null)return!1;const t=e;return typeof t.expiresAt=="number"&&Number.isFinite(t.expiresAt)&&typeof t.scope=="string"&&typeof t.value=="string"&&(t.claimant===void 0||typeof t.claimant=="string")},D=e=>({delete:async t=>{if(p(t)&&d(await e.get(t)))throw new TypeError(`key "${t}" holds a secret value and cannot be deleted through the KV surface`);return!!await e.delete(t)},get:async t=>{const o=await e.get(t);return p(t)&&d(o)?void 0:o},list:async t=>{if(typeof e.list!="function")throw new TypeError("storage.list is not available on this DurableObjectStorage");const o=t?.prefix,c=await e.list(o===void 0?{}:{prefix:o});for(const[r,i]of c)p(r)&&d(i)&&c.delete(r);return c},put:async(t,o)=>{if(p(t)&&(d(o)||d(await e.get(t))))throw new TypeError(`key "${t}" is reserved for secret values and cannot be written through the KV surface`);await e.put(t,o)}}),O=(e,t={})=>{const o=t.now??Date.now,c=r=>`${w}${r}`;return{discard:async r=>{const i=c(r);return d(await e.get(i))?!!await e.delete(i):!1},put:async(r,i)=>{const n=o(),a=n+k(i.ttlMs),u=`${String(a).padStart(15,"0")}.${crypto.randomUUID()}`,s=c(u),l=await r(u);if(await e.get(s)!==void 0)throw new Error(`secret value ref "${u}" is already in use`);await e.put(s,{expiresAt:a,scope:i.scope,value:l});const E=[...await e.list({limit:x,prefix:w})].filter(([,f])=>d(f)&&f.expiresAt<=n).map(([f])=>f);return await Promise.all(E.map(async f=>e.delete(f))),u},take:(r,i,n)=>e.transaction(async a=>{const u=c(r),s=await a.get(u);if(!d(s))return;if(s.expiresAt<=o()){await a.delete(u);return}if(s.scope!==i.scope||s.claimant!==void 0&&s.claimant!==i.claimant)return;const l=await n(s.value);if(l!==void 0){if(s.expiresAt<=o()){await a.delete(u);return}return s.claimant===void 0&&await a.put(u,{...s,claimant:i.claimant}),l}})}},K=(e,t,o={})=>{const c=t??{},r=i=>typeof i=="string"?i:void 0;return A(O(e,o),{key:r(c.LUNORA_SECRET_VALUE_KEY),previousKey:r(c.LUNORA_SECRET_VALUE_KEY_PREVIOUS)})},h="lunora-socket:",y=new WeakMap,S=new WeakMap;let g=0;const m=(e,t)=>{const{getTags:o}=e;if(typeof o=="function")try{return o.call(e,t).find(c=>c.startsWith(h))}catch{return}},W=(e,t)=>{const o=m(e,t);if(o!==void 0)return o.slice(h.length);const c=y.get(t);if(c!==void 0)return c;const r=S.get(t);if(r!==void 0)return r;g+=1;const i=`cf-socket-${String(g)}`;return S.set(t,i),i},$=e=>{const t=e,o=new WeakSet;let c=0,r;const i=()=>{const n=e.getWebSockets();return(r?.generation!==c||r.length!==n.length)&&(r={generation:c,length:n.length,set:new WeakSet(n)}),r.set};return{newPair:()=>{if(t.openSocketPair!==void 0)return t.openSocketPair();const n=new WebSocketPair;return{client:n[0],server:n[1]}},upgradeResponse:n=>t.upgradeResponse!==void 0?t.upgradeResponse(n):new Response(null,{status:101,webSocket:n}),accept:(n,a,u)=>{const s=_(u);if(s!==void 0)throw new b("SOCKET_TAG_BUDGET_EXCEEDED",s);const l=n,v=crypto.randomUUID();return e.acceptWebSocket(l,[`${h}${v}`,...u??[]]),y.set(l,v),c+=1,a!==void 0&&l.serializeAttachment?.(a),l},getSockets:n=>e.getWebSockets(n),handleFor:n=>{const a=n;if(y.has(a)||m(e,a)!==void 0)return a;if(!o.has(a)){if(i().has(a))return a;o.add(a)}},idFor:n=>W(e,n)}},P=e=>{const t=r=>({fetch:i=>r.fetch(i)}),o=r=>r===void 0?void 0:{locationHint:r};return{get:(r,i)=>t(e.get(r,o(i))),getByName:(r,i)=>t(e.get(e.idFromName(r),o(i))),idForName:r=>e.idFromName(r),jurisdiction:r=>P(e.jurisdiction(r))}};export{K as createSecretValueHost,O as createSecretValueStore,U as createShardAlarms,P as createShardDirectory,C as createShardHost,D as createShardKvStore,$ as createSocketHost};
@@ -0,0 +1 @@
1
+ import{createSocketHost as n,createSecretValueHost as l,createShardHost as d,createShardKvStore as i,createShardDirectory as u}from"./createSecretValueHost-BaYQQbQ9.mjs";import{CLOUDFLARE_CAPABILITIES as s}from"@lunora/platform";const m=(t,r={})=>{const e=t;return{kv:i(e.storage),shard:d(e),secretValues:l(e.storage,r),sockets:n(e)}},S=(t,r={})=>{const e=t??{},o=c=>{const a=e[c];if(a==null)throw new Error(`@lunora/platform-cloudflare: no Durable Object namespace bound as "${c}" — add it to wrangler.jsonc's durable_objects.bindings`);return u(a)};return r.scheduler===void 0?{capabilities:s,directory:o}:{capabilities:s,directory:o,scheduler:r.scheduler}};export{m as createShardPlatform,S as createWorkerPlatform};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/platform-cloudflare",
3
- "version": "1.0.0-alpha.101",
3
+ "version": "1.0.0-alpha.103",
4
4
  "description": "Cloudflare implementation of the Lunora platform contracts: ShardHost, SocketHost, ShardDirectory, ShardKvStore over Durable Objects",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@lunora/errors": ">=1.0.0-alpha.51 <2.0.0-0",
47
- "@lunora/platform": "1.0.0-alpha.71"
47
+ "@lunora/platform": "1.0.0-alpha.73"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@cloudflare/workers-types": ">=4.0.0"
@@ -1 +0,0 @@
1
- import{LunoraError as v}from"@lunora/errors";import{findSocketTagBudgetViolation as S}from"@lunora/platform";const w=(e,t,i)=>{const c=e.storage.sql;if(typeof c?.exec!="function")throw new TypeError("storage.sql.exec is not available on this DurableObjectState");return c.exec(t,...i)},k=e=>({delete:()=>typeof e.deleteAlarm=="function"?e.deleteAlarm():Promise.resolve(),get:()=>typeof e.getAlarm=="function"?e.getAlarm():null,set:t=>typeof e.setAlarm=="function"?e.setAlarm(t):Promise.resolve()}),b=(e,t)=>{if(!(!(e instanceof Error)||e===t||e.cause!==void 0))try{Object.defineProperty(e,"cause",{configurable:!0,value:t,writable:!0})}catch{}},T=e=>{const{storage:t}=e,i={get databaseSize(){return e.storage.sql?.databaseSize},exec:(r,...o)=>w(e,r,o)},c=k(t),n=async r=>{if(typeof e.blockConcurrencyWhile=="function"){const o=await e.blockConcurrencyWhile(async()=>{try{return{ok:!0,value:await r()}}catch(s){return{error:s,ok:!1}}});if(!o.ok)throw o.error;return o.value}return r()},a=async r=>{const o=t;if(typeof o?.transaction=="function"){let s;try{return await o.transaction(async()=>{try{return await r()}catch(u){throw s={value:u},u}})}catch(u){throw s?(b(s.value,u),s.value):u}}return r()};return{alarms:c,runSerialized:n,shardKey:e.id?.name,sql:i,transaction:a,waitUntil:typeof e.waitUntil=="function"?r=>{e.waitUntil(r)}:void 0}},D=e=>({delete:async t=>!!await e.delete(t),get:t=>e.get(t),list:t=>{if(typeof e.list!="function")throw new TypeError("storage.list is not available on this DurableObjectStorage");return e.list(t)},put:(t,i)=>e.put(t,i)}),f="lunora-socket:",d=new WeakMap,y=new WeakMap;let g=0;const p=(e,t)=>{const{getTags:i}=e;if(typeof i=="function")try{return i.call(e,t).find(c=>c.startsWith(f))}catch{return}},m=(e,t)=>{const i=p(e,t);if(i!==void 0)return i.slice(f.length);const c=d.get(t);if(c!==void 0)return c;const n=y.get(t);if(n!==void 0)return n;g+=1;const a=`cf-socket-${String(g)}`;return y.set(t,a),a},O=e=>{const t=e,i=new WeakSet;let c=0,n;const a=()=>{const r=e.getWebSockets();return(n?.generation!==c||n.length!==r.length)&&(n={generation:c,length:r.length,set:new WeakSet(r)}),n.set};return{newPair:()=>{if(t.openSocketPair!==void 0)return t.openSocketPair();const r=new WebSocketPair;return{client:r[0],server:r[1]}},upgradeResponse:r=>t.upgradeResponse!==void 0?t.upgradeResponse(r):new Response(null,{status:101,webSocket:r}),accept:(r,o,s)=>{const u=S(s);if(u!==void 0)throw new v("SOCKET_TAG_BUDGET_EXCEEDED",u);const l=r,h=crypto.randomUUID();return e.acceptWebSocket(l,[`${f}${h}`,...s??[]]),d.set(l,h),c+=1,o!==void 0&&l.serializeAttachment?.(o),l},getSockets:r=>e.getWebSockets(r),handleFor:r=>{const o=r;if(d.has(o)||p(e,o)!==void 0)return o;if(!i.has(o)){if(a().has(o))return o;i.add(o)}},idFor:r=>m(e,r)}},W=e=>{const t=n=>({fetch:a=>n.fetch(a)}),i=n=>n===void 0?void 0:{locationHint:n};return{get:(n,a)=>t(e.get(n,i(a))),getByName:(n,a)=>t(e.get(e.idFromName(n),i(a))),idForName:n=>e.idFromName(n),jurisdiction:n=>W(e.jurisdiction(n))}};export{k as createShardAlarms,W as createShardDirectory,T as createShardHost,D as createShardKvStore,O as createSocketHost};
@@ -1 +0,0 @@
1
- import{createSocketHost as n,createShardHost as d,createShardKvStore as l,createShardDirectory as i}from"./createShardAlarms-CWMouR-D.mjs";import{CLOUDFLARE_CAPABILITIES as c}from"@lunora/platform";const h=e=>{const r=e;return{kv:l(r.storage),shard:d(r),sockets:n(r)}},m=(e,r={})=>{const s=e??{},a=o=>{const t=s[o];if(t==null)throw new Error(`@lunora/platform-cloudflare: no Durable Object namespace bound as "${o}" — add it to wrangler.jsonc's durable_objects.bindings`);return i(t)};return r.scheduler===void 0?{capabilities:c,directory:a}:{capabilities:c,directory:a,scheduler:r.scheduler}};export{h as createShardPlatform,m as createWorkerPlatform};