@ontrails/cloudflare 1.0.0-beta.39 → 1.0.0-beta.42

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/src/env.ts CHANGED
@@ -10,8 +10,8 @@
10
10
  *
11
11
  * Overrides are re-resolved whenever a new `env` object arrives, and core
12
12
  * resolves overrides before its singleton resource cache, so no resource
13
- * instance can capture a stale env. Every Cloudflare subpath (`/kv` today,
14
- * `/d1`, `/queues`, `/r2` later) consumes this one seam.
13
+ * instance can capture a stale env. Every Cloudflare service subpath consumes
14
+ * this one seam.
15
15
  */
16
16
 
17
17
  import {
@@ -19,6 +19,7 @@ import {
19
19
  matchesTrailPattern,
20
20
  Result,
21
21
  filterSurfaceTrails,
22
+ isLiveTrailVersionEntry,
22
23
  } from '@ontrails/core';
23
24
  import type {
24
25
  AnyResource,
@@ -31,7 +32,8 @@ import type {
31
32
  /**
32
33
  * The ambient Worker environment: bindings keyed by their wrangler-configured
33
34
  * names. Values are runtime binding objects (KV namespaces, D1 databases,
34
- * queues), so they are typed as `unknown` and narrowed by each subpath.
35
+ * R2 buckets, queues), so they are typed as `unknown` and narrowed by each
36
+ * subpath.
35
37
  */
36
38
  export type WorkersEnv = Readonly<Record<string, unknown>>;
37
39
 
@@ -110,6 +112,10 @@ export interface BuildEnvResourceOverridesOptions extends Pick<
110
112
  > {
111
113
  /** Resource IDs already provided explicitly; env resolution skips them. */
112
114
  readonly except?: readonly string[] | undefined;
115
+ /** Worker entrypoint whose reachable resources should be materialized. */
116
+ readonly entrypoint?: 'fetch' | 'queue' | undefined;
117
+ /** Physical queue delivered to the current queue entrypoint. */
118
+ readonly queue?: string | undefined;
113
119
  }
114
120
 
115
121
  const isInternalTrail = (
@@ -134,18 +140,25 @@ const passesIncludeFilter = (
134
140
  matchesAnyPattern(trailId, include);
135
141
 
136
142
  /**
137
- * Mirror of the fetch kernel's webhook trail eligibility: webhook consumers
138
- * become HTTP routes even though `filterSurfaceTrails` skips
139
- * activation-driven trails, so the bridge applies the same rules here.
143
+ * Mirror of Worker activation eligibility for one entrypoint. Activation
144
+ * consumers are skipped by `filterSurfaceTrails`, so the bridge selects them
145
+ * explicitly without making fetch depend on queue-only resources.
140
146
  */
141
- const isEligibleWebhookTrail = (
147
+ const isEligibleWorkerActivationTrail = (
142
148
  graphTrail: Trail<unknown, unknown, unknown>,
143
149
  options: BuildEnvResourceOverridesOptions
144
150
  ): boolean => {
145
- const hasWebhookSource = graphTrail.activationSources.some(
146
- (activation) => activation.source.kind === 'webhook'
151
+ const activationKind =
152
+ (options.entrypoint ?? 'fetch') === 'queue' ? 'queue' : 'webhook';
153
+ const hasWorkerActivationSource = graphTrail.activationSources.some(
154
+ (activation) =>
155
+ activation.source.kind === activationKind &&
156
+ (activationKind !== 'queue' ||
157
+ options.queue === undefined ||
158
+ (typeof activation.source.queue === 'string' &&
159
+ activation.source.queue.trim() === options.queue))
147
160
  );
148
- if (!hasWebhookSource) {
161
+ if (!hasWorkerActivationSource) {
149
162
  return false;
150
163
  }
151
164
  if (
@@ -173,37 +186,58 @@ const collectSurfaceEligibleTrails = (
173
186
  ): readonly Trail<unknown, unknown, unknown>[] => {
174
187
  const trails = graph.list();
175
188
  const eligible = new Map<string, Trail<unknown, unknown, unknown>>();
176
- const filtered = filterSurfaceTrails(trails, {
177
- exclude: options.exclude,
178
- include: options.include,
179
- intent: options.intent,
180
- });
181
- for (const graphTrail of filtered) {
182
- eligible.set(graphTrail.id, graphTrail);
189
+ if ((options.entrypoint ?? 'fetch') === 'fetch') {
190
+ const filtered = filterSurfaceTrails(trails, {
191
+ exclude: options.exclude,
192
+ include: options.include,
193
+ intent: options.intent,
194
+ });
195
+ for (const graphTrail of filtered) {
196
+ eligible.set(graphTrail.id, graphTrail);
197
+ }
183
198
  }
184
199
  for (const graphTrail of trails) {
185
200
  if (
186
201
  !eligible.has(graphTrail.id) &&
187
- isEligibleWebhookTrail(graphTrail, options)
202
+ isEligibleWorkerActivationTrail(graphTrail, options)
188
203
  ) {
189
204
  eligible.set(graphTrail.id, graphTrail);
190
205
  }
191
206
  }
207
+ const byId = new Map(trails.map((graphTrail) => [graphTrail.id, graphTrail]));
208
+ const pending = [...eligible.values()];
209
+ for (const graphTrail of pending) {
210
+ const composedIds = [
211
+ ...graphTrail.composes,
212
+ ...Object.values(graphTrail.versions ?? {})
213
+ .filter(isLiveTrailVersionEntry)
214
+ .flatMap((entry) => entry.composes ?? []),
215
+ ];
216
+ for (const reference of composedIds) {
217
+ const composedId =
218
+ typeof reference === 'string' ? reference : reference.id;
219
+ const composed = byId.get(composedId);
220
+ if (composed !== undefined && !eligible.has(composed.id)) {
221
+ eligible.set(composed.id, composed);
222
+ pending.push(composed);
223
+ }
224
+ }
225
+ }
192
226
  return [...eligible.values()];
193
227
  };
194
228
 
195
229
  /**
196
230
  * All resources a trail can execute with: the current contract's declarations
197
- * plus any fork version entry's own `resources`, since core runs historical
198
- * forks with the entry's resource set.
231
+ * plus any live fork version entry's own `resources`, since core runs
232
+ * supported historical forks with the entry's resource set.
199
233
  */
200
234
  const declaredTrailResources = (
201
235
  graphTrail: Trail<unknown, unknown, unknown>
202
236
  ): readonly AnyResource[] => [
203
237
  ...graphTrail.resources,
204
- ...Object.values(graphTrail.versions ?? {}).flatMap(
205
- (entry) => entry.resources ?? []
206
- ),
238
+ ...Object.values(graphTrail.versions ?? {})
239
+ .filter(isLiveTrailVersionEntry)
240
+ .flatMap((entry) => entry.resources ?? []),
207
241
  ];
208
242
 
209
243
  const collectEnvBoundResources = (
@@ -228,8 +262,8 @@ const collectEnvBoundResources = (
228
262
 
229
263
  /**
230
264
  * Resolve every env-bound resource declared by the topo's surface-eligible
231
- * trails (including fork-version resources) into a resource override map for
232
- * one Worker env.
265
+ * trails (including live fork-version resources) into a resource override map
266
+ * for one Worker env.
233
267
  *
234
268
  * Returns `Result.err` when a required binding is missing from the env or a
235
269
  * binding value fails the resource's narrowing check. Trails filtered off the
@@ -260,7 +294,7 @@ export const buildEnvResourceOverrides = (
260
294
  if (value === undefined) {
261
295
  return Result.err(
262
296
  new InternalError(
263
- `Worker env is missing binding "${spec.binding}" required by resource "${declared.id}". Declare the binding in your wrangler configuration (for example a kv_namespaces entry) or provide an explicit resource override.`,
297
+ `Worker env is missing binding "${spec.binding}" required by resource "${declared.id}". Declare the binding in your wrangler configuration (for example kv_namespaces, d1_databases, r2_buckets, or queues) or provide an explicit resource override.`,
264
298
  { context: { binding: spec.binding, resourceId: declared.id } }
265
299
  )
266
300
  );
package/src/index.ts CHANGED
@@ -4,6 +4,10 @@
4
4
  * Service subpaths are the primary entry points:
5
5
  * - `@ontrails/cloudflare/workers` — HTTP surface materializer (fetch handler)
6
6
  * - `@ontrails/cloudflare/kv` — key-value resource
7
+ * - `@ontrails/cloudflare/d1` — D1-backed store resource
8
+ * - `@ontrails/cloudflare/r2` — R2 blob/object resource
9
+ * - `@ontrails/cloudflare/queues` — Queue producer resource and consumer
10
+ * materializer
7
11
  *
8
12
  * The root export re-exports the subpaths for convenience and adapter
9
13
  * tooling, and owns the adapter's `trails.lock` overlay overlay
@@ -12,6 +16,17 @@
12
16
 
13
17
  export { cloudflareOverlay } from './facts.js';
14
18
  export type { CloudflareLockFacts } from './facts.js';
19
+ export { cloudflareD1, connectD1 } from './d1/index.js';
20
+ export type {
21
+ CloudflareD1AllResult,
22
+ CloudflareD1Connection,
23
+ CloudflareD1Database,
24
+ CloudflareD1Options,
25
+ CloudflareD1PreparedStatement,
26
+ CloudflareD1Resource,
27
+ CloudflareD1RunResult,
28
+ ConnectD1Options,
29
+ } from './d1/index.js';
15
30
  export {
16
31
  buildEnvResourceOverrides,
17
32
  getEnvBinding,
@@ -32,6 +47,46 @@ export type {
32
47
  CloudflareKvPutOptions,
33
48
  CreateMemoryKvOptions,
34
49
  } from './kv/index.js';
50
+ export { cloudflareR2, createMemoryR2, r2ObjectToBlobRef } from './r2/index.js';
51
+ export type {
52
+ CloudflareR2Bucket,
53
+ CloudflareR2Conditional,
54
+ CloudflareR2GetOptions,
55
+ CloudflareR2HttpMetadata,
56
+ CloudflareR2ListOptions,
57
+ CloudflareR2Object,
58
+ CloudflareR2ObjectBody,
59
+ CloudflareR2Objects,
60
+ CloudflareR2Options,
61
+ CloudflareR2PutBody,
62
+ CloudflareR2PutOptions,
63
+ CloudflareR2Range,
64
+ CloudflareR2StorageClass,
65
+ MemoryCloudflareR2Bucket,
66
+ R2ObjectToBlobRefOptions,
67
+ } from './r2/index.js';
68
+ export {
69
+ cloudflareQueue,
70
+ createMemoryQueue,
71
+ createQueueHandler,
72
+ } from './queues/index.js';
73
+ export type {
74
+ CloudflareQueue,
75
+ CloudflareQueueBatch,
76
+ CloudflareQueueHandler,
77
+ CloudflareQueueMessage,
78
+ CloudflareQueueMetrics,
79
+ CloudflareQueueOptions,
80
+ CloudflareQueueRetryOptions,
81
+ CloudflareQueueSendBatchOptions,
82
+ CloudflareQueueSendOptions,
83
+ CloudflareQueueSendRequest,
84
+ CloudflareQueueSendResult,
85
+ CloudflareQueuesContentType,
86
+ CreateQueueHandlerOptions,
87
+ MemoryCloudflareQueue,
88
+ MemoryQueueMessage,
89
+ } from './queues/index.js';
35
90
  export { createWorkersHandler } from './workers/index.js';
36
91
  export type {
37
92
  CloudflareWorker,
package/src/kv/index.ts CHANGED
@@ -226,7 +226,7 @@ const isKvBinding = (value: unknown): value is CloudflareKv => {
226
226
  * const flags = cloudflareKv('flags', { binding: 'FLAGS' });
227
227
  *
228
228
  * const showFlag = trail('flag.show', {
229
- * blaze: async (input, ctx) => {
229
+ * implementation: async (input, ctx) => {
230
230
  * const value = await flags.from(ctx).get(input.key);
231
231
  * return Result.ok({ value });
232
232
  * },