@lunora/bindings 1.0.0-alpha.9 → 1.0.0-alpha.91

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 (67) hide show
  1. package/README.md +11 -9
  2. package/dist/ai-search/index.d.mts +741 -0
  3. package/dist/ai-search/index.d.ts +741 -0
  4. package/dist/ai-search/index.mjs +0 -0
  5. package/dist/analytics/index.d.mts +21 -25
  6. package/dist/analytics/index.d.ts +21 -25
  7. package/dist/analytics/index.mjs +1 -2
  8. package/dist/artifacts/index.d.mts +426 -0
  9. package/dist/artifacts/index.d.ts +426 -0
  10. package/dist/artifacts/index.mjs +1 -0
  11. package/dist/images/index.d.mts +37 -52
  12. package/dist/images/index.d.ts +37 -52
  13. package/dist/images/index.mjs +1 -3
  14. package/dist/kv/index.d.mts +4 -81
  15. package/dist/kv/index.d.ts +4 -81
  16. package/dist/kv/index.mjs +1 -2
  17. package/dist/packem_shared/AnalyticsSqlError-CeQ3A5Eb.mjs +1 -0
  18. package/dist/packem_shared/R2SqlError-ZHgOWClB.mjs +1 -0
  19. package/dist/packem_shared/SelectBuilder-DJXNSdqC.mjs +1 -0
  20. package/dist/packem_shared/SetOperation-kiI5Wnlm.mjs +1 -0
  21. package/dist/packem_shared/Sql-BfnxRway.mjs +1 -0
  22. package/dist/packem_shared/WindowExpression-VX7EEV3h.mjs +1 -0
  23. package/dist/packem_shared/WindowFunction-CL4jYy2l.mjs +1 -0
  24. package/dist/packem_shared/asc-DP_WFiAE.mjs +1 -0
  25. package/dist/packem_shared/authenticatedRemote-CPm87dLq.mjs +1 -0
  26. package/dist/packem_shared/buildImageDeliveryUrl-Brqs-dcZ.mjs +1 -0
  27. package/dist/packem_shared/buildSignedImageUrl-BV-iSJKA.mjs +4 -0
  28. package/dist/packem_shared/cap-error-body-YBKO32BF.mjs +1 -0
  29. package/dist/packem_shared/concurrent-vRmSvRpF.mjs +1 -0
  30. package/dist/packem_shared/createAnalytics-BXTNc57d.mjs +1 -0
  31. package/dist/packem_shared/createArtifacts-cXCusYbi.mjs +1 -0
  32. package/dist/packem_shared/createContextVectors-DiyO3pZU.mjs +6 -0
  33. package/dist/packem_shared/createImages-D7JExfqF.mjs +1 -0
  34. package/dist/packem_shared/createKv-mAHanD5g.mjs +1 -0
  35. package/dist/packem_shared/createKvIntrospector-BpRiFRFQ.mjs +1 -0
  36. package/dist/packem_shared/createPipelines-CIvqrc7E.mjs +1 -0
  37. package/dist/packem_shared/createVectorAdminIntrospector-Ct8v6PxJ.mjs +1 -0
  38. package/dist/packem_shared/createVectors-Dzv0ilKE.mjs +1 -0
  39. package/dist/pipelines/index.d.mts +1 -1
  40. package/dist/pipelines/index.d.ts +1 -1
  41. package/dist/pipelines/index.mjs +1 -1
  42. package/dist/r2sql/index.d.mts +54 -10
  43. package/dist/r2sql/index.d.ts +54 -10
  44. package/dist/r2sql/index.mjs +1 -7
  45. package/dist/vectors/index.d.mts +236 -71
  46. package/dist/vectors/index.d.ts +236 -71
  47. package/dist/vectors/index.mjs +1 -3
  48. package/package.json +14 -3
  49. package/dist/packem_shared/AnalyticsSqlError-C2nz3jpH.mjs +0 -41
  50. package/dist/packem_shared/R2SqlError-drPKSCZ3.mjs +0 -65
  51. package/dist/packem_shared/SelectBuilder-BOqJQHEv.mjs +0 -168
  52. package/dist/packem_shared/SetOperation-DmPgUL8W.mjs +0 -81
  53. package/dist/packem_shared/Sql-B3zq2YGx.mjs +0 -74
  54. package/dist/packem_shared/WindowExpression-BT_uA6g1.mjs +0 -44
  55. package/dist/packem_shared/WindowFunction-DrnuZUF6.mjs +0 -82
  56. package/dist/packem_shared/asc-DZbQCxh1.mjs +0 -16
  57. package/dist/packem_shared/buildImageDeliveryUrl-qZ7XbqTL.mjs +0 -35
  58. package/dist/packem_shared/buildSignedImageUrl-DNUFfyGP.mjs +0 -130
  59. package/dist/packem_shared/concurrent-CkCEVwqP.mjs +0 -39
  60. package/dist/packem_shared/createAnalytics-CEEI69o9.mjs +0 -57
  61. package/dist/packem_shared/createContextVectors-DwZtnPeC.mjs +0 -140
  62. package/dist/packem_shared/createImages-BzRnsz3H.mjs +0 -85
  63. package/dist/packem_shared/createKv-C8Iyu5hD.mjs +0 -145
  64. package/dist/packem_shared/createKvIntrospector-Byk4GfsY.mjs +0 -77
  65. package/dist/packem_shared/createPipelines-CfyJ6VGu.mjs +0 -10
  66. package/dist/packem_shared/createVectorAdminIntrospector-DuSvcBa5.mjs +0 -53
  67. package/dist/packem_shared/createVectors-CTSrctiK.mjs +0 -95
@@ -1,55 +1,5 @@
1
- /**
2
- * Minimal structural projection of `VectorizeIndex` so unit tests can pass a
3
- * plain-object double and the real Cloudflare binding satisfies the same shape.
4
- * Mirrors the surface documented at
5
- * https://developers.cloudflare.com/vectorize/reference/client-api/.
6
- */
7
- interface VectorizeIndexLike {
8
- deleteByIds: (ids: ReadonlyArray<string>) => Promise<VectorizeDeleteMutation>;
9
- describe?: () => Promise<VectorizeIndexDetails>;
10
- getByIds: (ids: ReadonlyArray<string>) => Promise<ReadonlyArray<VectorizeVector>>;
11
- insert: (vectors: ReadonlyArray<VectorizeVector>) => Promise<VectorizeUpsertMutation>;
12
- query: (vector: ReadonlyArray<number>, options?: VectorizeQueryOptions) => Promise<VectorizeMatches>;
13
- upsert: (vectors: ReadonlyArray<VectorizeVector>) => Promise<VectorizeUpsertMutation>;
14
- }
15
- type VectorMetric = "cosine" | "euclidean" | "dot-product";
16
- interface VectorizeVector {
17
- id: string;
18
- metadata?: Record<string, unknown>;
19
- namespace?: string;
20
- values: ReadonlyArray<number>;
21
- }
22
- interface VectorizeQueryOptions {
23
- filter?: Record<string, unknown>;
24
- namespace?: string;
25
- returnMetadata?: "none" | "indexed" | "all";
26
- returnValues?: boolean;
27
- topK?: number;
28
- }
29
- interface VectorizeMatch {
30
- id: string;
31
- metadata?: Record<string, unknown>;
32
- namespace?: string;
33
- score: number;
34
- values?: ReadonlyArray<number>;
35
- }
36
- interface VectorizeMatches {
37
- count: number;
38
- matches: ReadonlyArray<VectorizeMatch>;
39
- }
40
- interface VectorizeUpsertMutation {
41
- mutationId: string;
42
- }
43
- interface VectorizeDeleteMutation {
44
- count?: number;
45
- mutationId: string;
46
- }
47
- interface VectorizeIndexDetails {
48
- dimensions: number;
49
- processedUpToDatetime?: string;
50
- processedUpToMutation?: string;
51
- vectorsCount: number;
52
- }
1
+ import { VectorizeDeleteMutation, VectorizeIndexDetails, VectorizeVector, VectorizeMatches, VectorizeUpsertMutation, VectorizeIndexLike, VectorMetric } from '@lunora/platform';
2
+ export type { VectorMetric, VectorizeDeleteMutation, VectorizeIndexDetails, VectorizeIndexLike, VectorizeMatch, VectorizeMatches, VectorizeQueryOptions, VectorizeUpsertMutation, VectorizeVector } from '@lunora/platform';
53
3
  /**
54
4
  * Bring-your-own-embedder: a user-supplied async fn that converts a single
55
5
  * source value (a row, a chunk, an arbitrary string) into a numeric vector.
@@ -107,6 +57,7 @@ interface VectorMatchesLike {
107
57
  interface VectorRecordLike {
108
58
  id: string;
109
59
  metadata?: Record<string, unknown>;
60
+ namespace?: string;
110
61
  values: ReadonlyArray<number>;
111
62
  }
112
63
  interface VectorQueryInputLike {
@@ -134,22 +85,125 @@ interface VectorUpsertInputLike {
134
85
  /**
135
86
  * Structural mirror of `@lunora/server`'s `VectorSearch`. Declared here so the
136
87
  * adapter never imports `@lunora/server` (keeps the dependency edge one-way:
137
- * the generated DO depends on both, neither depends on the other).
88
+ * the generated DO depends on both, neither depends on the other). `getByIds`/
89
+ * `deleteByIds` carry an optional trailing `namespace` — a pure addition (more
90
+ * general, not narrower) that stays assignable to `@lunora/server`'s
91
+ * `VectorSearchReader`/`VectorSearch`, whose own two-argument signatures are
92
+ * unchanged: a function accepting an extra OPTIONAL parameter is assignable
93
+ * wherever a function taking fewer parameters is expected.
138
94
  */
139
95
  interface VectorSearchLike {
140
- deleteByIds: (indexName: string, ids: ReadonlyArray<string>) => Promise<void>;
141
- getByIds: (indexName: string, ids: ReadonlyArray<string>) => Promise<ReadonlyArray<VectorRecordLike>>;
96
+ deleteByIds: (indexName: string, ids: ReadonlyArray<string>, namespace?: string) => Promise<void>;
97
+ getByIds: (indexName: string, ids: ReadonlyArray<string>, namespace?: string) => Promise<ReadonlyArray<VectorRecordLike>>;
142
98
  query: (indexName: string, input: VectorQueryInputLike) => Promise<VectorMatchesLike>;
143
99
  upsert: (indexName: string, input: VectorUpsertInputLike) => Promise<void>;
144
100
  upsertNow: (indexName: string, input: VectorUpsertInputLike) => Promise<void>;
145
101
  }
102
+ /** Options for {@link createContextVectors}. */
103
+ interface CreateContextVectorsOptions {
104
+ /**
105
+ * Hold `upsert`'s remote write until the caller's storage transaction has
106
+ * COMMITTED, running it at once when none is open. The shard host supplies
107
+ * `ShardDO.deferAfterCommit`; codegen wires it.
108
+ *
109
+ * This is what separates `upsert` from `upsertNow`. Vectorize is outside the
110
+ * shard's SQLite and cannot roll back, so an inline `ctx.vectors.upsert` in a
111
+ * mutation that later throws leaves a vector pointing at a row that does not
112
+ * exist — and a search surfaces it. Omitted, both methods write inline, which
113
+ * is correct for a caller that has no transaction to wait for (an action, a
114
+ * test, the `@lunora/ai` RAG helpers).
115
+ */
116
+ deferAfterCommit?: (work: () => Promise<void>) => Promise<void>;
117
+ /**
118
+ * The DO's own shard/tenant key, applied as the default `namespace` for
119
+ * an operation against an index in `shardedIndexNames` that doesn't pass
120
+ * one explicitly. `undefined` means this instance HAS no shard key —
121
+ * always true for the root/default DO instance, since only a per-tenant
122
+ * instance owns one. See `shardedIndexNames` for what that implies per
123
+ * index, and {@link createContextVectors}'s docblock for the full
124
+ * root-instance rule.
125
+ */
126
+ namespace?: string;
127
+ /**
128
+ * Vector index names sourced from a `.shardBy()`'d table — the ones
129
+ * `namespace` is a meaningful tenant scope for. `ctx.vectors` is a single
130
+ * flat facade over EVERY declared index (root-scoped and sharded tables
131
+ * alike — Vectorize indexes are account-global and `config.vectors(env)`
132
+ * registers them all in one flat map), reachable from ANY DO instance —
133
+ * so `namespace` can only be a safe default for the indexes actually
134
+ * listed here.
135
+ *
136
+ * An index NOT in this set (sourced from a root-scoped table) always
137
+ * stays namespace-less, regardless of `namespace` or which DO instance
138
+ * calls it — it has no tenant identity to begin with, so scoping it would
139
+ * silently return nothing for legitimate, intentionally shared data (and,
140
+ * called from a per-tenant instance, would wrongly search under that
141
+ * tenant's namespace even though nothing was ever written there under
142
+ * it). An index IN this set, called from a per-tenant DO instance
143
+ * (`namespace` is set), defaults to `namespace`, scoping correctly. An
144
+ * index IN this set, called from the root/default DO instance
145
+ * (`namespace` is `undefined`) with no explicit override, is unsafe to
146
+ * default at all — see {@link createContextVectors}'s docblock.
147
+ *
148
+ * Omitted (or empty) → no index is ever treated as sharded, i.e.
149
+ * `namespace` never applies as a default on any call — the unsharded-app,
150
+ * byte-identical-to-today case.
151
+ */
152
+ shardedIndexNames?: ReadonlyArray<string>;
153
+ }
146
154
  /**
147
155
  * Bridge `LunoraVectors` (returns Vectorize mutation receipts) to the server's
148
- * `VectorSearch` contract (void mutations, server match/record shapes). Both
149
- * `upsert` and `upsertNow` write inline — this design has no post-commit queue,
150
- * so "now" and "deferred" collapse to the same synchronous call.
156
+ * `VectorSearch` contract (void mutations, server match/record shapes).
157
+ *
158
+ * `upsert` vs `upsertNow` — IMPORTANT: with `options.deferAfterCommit` supplied
159
+ * (codegen wires the shard host's), `upsert` holds the remote write until the
160
+ * caller's transaction has committed and `upsertNow` writes inline, which is
161
+ * what `MutationCtx`'s contract documents. Without it both write inline: a
162
+ * caller with no transaction open has nothing to wait for. The NAMESPACE is
163
+ * resolved eagerly either way — before the deferral, not inside it — so a
164
+ * misconfiguration (the root-instance throw below) still reaches the handler
165
+ * that made the call instead of a post-commit log line nobody is holding.
166
+ *
167
+ * Tenant isolation (read side) — IMPORTANT: an explicit `namespace` argument
168
+ * on any call (`input.namespace` for `query`/`upsert`/`upsertNow`, the
169
+ * trailing `namespace` parameter for `getByIds`/`deleteByIds`) ALWAYS wins —
170
+ * this is a deliberate soft default, not a hard boundary: `ctx.vectors` is
171
+ * trusted server-side app code (the same trust level that lets `ctx.db` read
172
+ * any table), so a caller that explicitly names a namespace is trusted to
173
+ * mean it, including a legitimate cross-tenant admin read/write. Absent an
174
+ * explicit namespace, `options.namespace` (this DO instance's own shard key)
175
+ * is the DEFAULT for any index in `options.shardedIndexNames` — see that
176
+ * option's docblock for why the default is index-scoped rather than global.
177
+ *
178
+ * Root-instance rule — IMPORTANT: when an operation targets a sharded index
179
+ * (one in `shardedIndexNames`) and BOTH the explicit argument and
180
+ * `options.namespace` are absent (this is the root/default DO instance, which
181
+ * owns no shard key), there is no safe default and no override — this THROWS
182
+ * rather than silently resolving to "no namespace". A namespace-less
183
+ * query/getByIds/deleteByIds/upsert against a sharded index would reach or
184
+ * mutate EVERY tenant's vectors (Vectorize indexes are account-global), which
185
+ * is the exact cross-tenant leak this file exists to close; returning an
186
+ * empty result set instead would masquerade that same configuration problem
187
+ * as "no data", which is worse — a caller debugging it sees nothing rather
188
+ * than a directed error. This case is reachable in a MIXED schema (some
189
+ * vectorized tables `.shardBy()`'d, others root-scoped) whenever application
190
+ * code queries a sharded index's name from the root DO instance without an
191
+ * explicit namespace; it is not reachable from `createVectorSyncHook`'s own
192
+ * internal calls, which only ever process a table this DO instance owns (so
193
+ * a sharded table's write never reaches a root instance in the first place).
194
+ *
195
+ * Id path, unrelated axis — IMPORTANT: independent of the override/root rules
196
+ * above, `getByIds`/`deleteByIds` can't ask Vectorize to filter by namespace
197
+ * remotely at all (its id-based operations take no `namespace` option), so
198
+ * once a namespace IS resolved (explicit or defaulted) for these two methods,
199
+ * isolation is enforced client-side: `getByIds` drops any returned record
200
+ * whose `namespace` doesn't match (fail closed: a record with no `namespace`
201
+ * field is treated as a mismatch, never as "belongs to everyone"), and
202
+ * `deleteByIds` resolves ids via `getByIds` first and only deletes the subset
203
+ * that belongs to the resolved namespace — silently, by design (see the
204
+ * `deleteByIds` implementation for the no-signal tradeoff this makes).
151
205
  */
152
- declare const createContextVectors: (lunora: LunoraVectors) => VectorSearchLike;
206
+ declare const createContextVectors: (lunora: LunoraVectors, options?: CreateContextVectorsOptions) => VectorSearchLike;
153
207
  /** A single row mutation observed by the ctx-db, fed to {@link createVectorSyncHook}. */
154
208
  interface WriteEvent {
155
209
  doc?: Record<string, unknown>;
@@ -160,18 +214,30 @@ interface WriteEvent {
160
214
  type WriteHook = (event: WriteEvent) => Promise<void>;
161
215
  /** Inline vector index declared via `.vectorize(field, ...)` (DSL Shape A). */
162
216
  interface TableVectorIndexLike {
217
+ dimensions?: number;
163
218
  embed: VectorEmbedderLike;
164
219
  field: string;
165
220
  metadata?: ReadonlyArray<string>;
221
+ metric?: string;
222
+ /** Declared identifier of what `embed` produces; part of the backfill fingerprint. */
223
+ model?: string;
166
224
  name: string;
167
225
  }
168
226
  interface TableDefinitionLike {
227
+ /** `.softDelete()` marker column. A row whose marker is set is hidden from `ctx.db`, so it must have no vector. */
228
+ softDeleteMode?: {
229
+ field: string;
230
+ };
169
231
  vectorIndexes?: ReadonlyArray<TableVectorIndexLike>;
170
232
  }
171
233
  /** Standalone vector index declared via `defineVectorIndex(...)` (DSL Shape B). */
172
234
  interface VectorIndexDefinitionLike {
235
+ dimensions?: number;
173
236
  embed: VectorEmbedderLike;
174
237
  metadata?: (row: Record<string, unknown>) => Record<string, unknown>;
238
+ metric?: string;
239
+ /** Declared identifier of what `embed` produces; part of the backfill fingerprint. */
240
+ model?: string;
175
241
  select: (row: Record<string, unknown>) => string;
176
242
  table: string;
177
243
  }
@@ -188,7 +254,7 @@ interface SchemaLike {
188
254
  * Build a {@link WriteHook} that keeps Vectorize in sync with row writes. On
189
255
  * insert/update it embeds each matching index's source (Shape A `row[field]`,
190
256
  * Shape B `select(row)`) and upserts; on delete it removes the row's id from
191
- * every index sourced from the table. Runs inline within the write path.
257
+ * every index sourced from the table. {@link planRowSync} makes the decision.
192
258
  *
193
259
  * Tenant isolation — IMPORTANT: Vectorize indexes are account-global and shared
194
260
  * by every shard DO. Without a `namespace`, a multi-tenant sharded app has NO
@@ -202,16 +268,41 @@ interface SchemaLike {
202
268
  * (regardless of whether metadata is present); a genuinely single-tenant app
203
269
  * suppresses it with `allowSharedNamespace: true`.
204
270
  *
205
- * Consistency — IMPORTANT: this hook runs inline within the mutation but talks
206
- * to Vectorize, which is external and non-transactional. The per-index calls
207
- * fan out; if one fails after others have already applied, the SQLite write may
208
- * roll back while the applied Vectorize mutations cannot — leaving SQLite and
209
- * Vectorize diverged. We mitigate, not eliminate: upserts/deletes are
210
- * idempotent (keyed by row id), so a retry of the same write converges; and on
211
- * a fan-out failure we attempt a best-effort compensating delete of the row's
212
- * id from every affected index before re-throwing. A delete after a failed
213
- * upsert can itself fail — this is best-effort, the authoritative recovery is
214
- * re-running the (idempotent) write.
271
+ * Since plan 255, codegen satisfies the query-side requirement automatically
272
+ * for a `.shardBy()`'d vectorized table: the `vectors` instance passed in
273
+ * `options` here is the SAME `createContextVectors(...)` instance exposed as
274
+ * `ctx.vectors`, constructed with the identical shard-key `namespace` default
275
+ * AND the identical `shardedIndexNames` — so `ctx.vectors.query`/`getByIds`/
276
+ * `deleteByIds` are scoped without any app code changes. One consequence of
277
+ * sharing that instance: this hook's own internal `deleteByIds` calls (on row
278
+ * delete and on a cleared inline field) now also go through the
279
+ * namespace-verifying path described on
280
+ * {@link createContextVectors} — an extra `getByIds` subrequest per
281
+ * delete-shaped write, not a behavior change (the row being deleted was
282
+ * written under this same shard's namespace, so the verification passes).
283
+ * This never hits {@link createContextVectors}'s root-instance throw: a write
284
+ * event only ever fires for a table THIS DO instance owns, so if this hook
285
+ * processes a write for a sharded index, this instance IS a real per-tenant
286
+ * shard (not root) — `namespace` here is never `undefined` for that index.
287
+ *
288
+ * Consistency — IMPORTANT: Vectorize is external and non-transactional, so this
289
+ * hook runs AFTER the mutation's transaction has committed, never inside it (the
290
+ * shard host holds it — `ShardDO.deferAfterCommit`). That ordering is what stops
291
+ * a rolled-back write from leaving a vector for a row that does not exist, and a
292
+ * rolled-back delete from leaving a live row with its vector already purged.
293
+ *
294
+ * Two commits to the same row do not race: the shard host drains one
295
+ * transaction's held work entirely before the next transaction's, so the hooks
296
+ * apply in COMMIT order even though each may take hundreds of milliseconds. Fan
297
+ * out within a single hook is still unordered — the indexes are independent.
298
+ *
299
+ * What remains is the opposite divergence, and it is the one worth having: the
300
+ * row is committed and this hook may still fail — fully, or partway through a
301
+ * fan-out that already applied to some indexes. The row is then indexed in some
302
+ * indexes and not others. Nothing is compensated, deliberately: the row SURVIVES
303
+ * a failure here, so purging the indexes that did apply would turn a partially
304
+ * indexed row into an unsearchable one. Upserts and deletes are idempotent
305
+ * (keyed by row id), so re-running the same write converges.
215
306
  */
216
307
  declare const createVectorSyncHook: (options: {
217
308
  allowSharedNamespace?: boolean;
@@ -219,6 +310,80 @@ declare const createVectorSyncHook: (options: {
219
310
  schema: SchemaLike;
220
311
  vectors: VectorSearchLike;
221
312
  }) => WriteHook;
313
+ /** A row the backfill could not index, and why. */
314
+ interface VectorBackfillFailure {
315
+ error: unknown;
316
+ id: string;
317
+ }
318
+ /**
319
+ * Index one page of rows for the shard's vector backfill. Resolves with the rows
320
+ * that failed on their own; REJECTS when the failure is the service's rather than
321
+ * a row's, so the caller holds its cursor and retries the page — with a
322
+ * `SERVICE_UNAVAILABLE` `LunoraError` when the error shows the failure to be
323
+ * transient, and with the raw error when only the whole page failing suggests it.
324
+ */
325
+ type VectorBackfillSync = (table: string, rows: ReadonlyArray<{
326
+ doc: Record<string, unknown>;
327
+ id: string;
328
+ }>) => Promise<ReadonlyArray<VectorBackfillFailure>>;
329
+ /**
330
+ * The backfill's counterpart to {@link createVectorSyncHook}: the same
331
+ * {@link planRowSync} decision for a whole page of rows, with the remote calls
332
+ * batched — every row is embedded (bounded concurrency), then each index takes
333
+ * ONE `upsertMany` and ONE `deleteByIds` per 1000 rows instead of a call per row.
334
+ * That is what keeps a page short enough to hold the shard's write-hook chain.
335
+ *
336
+ * Failures are split in two, because they need opposite handling.
337
+ *
338
+ * A ROW failure is deterministic and would fail on every retry: a non-string
339
+ * source, a `select()` that throws, text the model rejects, metadata Vectorize
340
+ * refuses. The row is reported and the page moves on — the live hook only logs
341
+ * these too, and a backfill that stopped on one would never finish.
342
+ *
343
+ * A SERVICE failure is transient: the embedder or Vectorize is unreachable. The
344
+ * call rejects, so the page is retried. It is recognised by the error where the
345
+ * error says (an HTTP 5xx/408/429 status, a timeout — see {@link classifyFailure};
346
+ * these reject as `SERVICE_UNAVAILABLE`), and otherwise as every attempt in a
347
+ * group of two or more failing, and as a failed `deleteByIds` (which has no row
348
+ * content to blame). A batch `upsertMany` that fails is retried one row at a
349
+ * time to tell the two apart. A group that fails whole on every retry — each
350
+ * row refused for the same reason, with no status to show it — rejects each
351
+ * time too; the backfill writes such a page off after a few consecutive tries.
352
+ *
353
+ * `upsertMany` is the raw binding call, so the namespace is passed explicitly —
354
+ * the same `namespace` the live hook scopes by.
355
+ */
356
+ type BackfillSyncOptions = {
357
+ allowSharedNamespace?: boolean;
358
+ namespace?: string;
359
+ schema: SchemaLike;
360
+ upsertMany: LunoraVectors["upsertMany"];
361
+ vectors: VectorSearchLike;
362
+ };
363
+ declare const createVectorBackfillSync: (options: BackfillSyncOptions) => VectorBackfillSync;
364
+ /**
365
+ * Every table with a vector index sourced from it, each with a fingerprint of
366
+ * what its stored vectors were built from — the input to the shard's vector
367
+ * backfill, which re-walks a table whose fingerprint changed.
368
+ *
369
+ * Covers what the schema can see: index names, the inline source field,
370
+ * dimensions, metric, inline metadata fields, the declared `model`, and the
371
+ * table's `.softDelete()` field — a row hidden by a newly chosen marker keeps
372
+ * its vector until the table is walked again. A
373
+ * function (`embed`, a Shape B `select`/`metadata`) has no stable identity to
374
+ * fingerprint — its source text changes with unrelated rebuilds of the bundle,
375
+ * which would re-embed whole tables for nothing — so the declared `model` string
376
+ * stands in for `embed`, and any other change is announced by calling the
377
+ * backfill with `restart: true`.
378
+ *
379
+ * `model` joins a descriptor only when declared, and the soft-delete field only
380
+ * when the table has one, so an index without either keeps the fingerprint it
381
+ * was recorded under and is not re-embedded for it.
382
+ */
383
+ declare const vectorBackfillTargets: (schema: SchemaLike) => {
384
+ profile: string;
385
+ table: string;
386
+ }[];
222
387
  /**
223
388
  * One vector index as the generated `LUNORA_VECTOR_INDEXES` registry describes
224
389
  * it — the static schema shape, independent of any live binding. Structurally
@@ -282,4 +447,4 @@ interface VectorAdminIntrospectorOptions {
282
447
  */
283
448
  declare const createVectorAdminIntrospector: (options: VectorAdminIntrospectorOptions) => VectorAdminIntrospector;
284
449
  declare const createVectors: (options: LunoraVectorsOptions) => LunoraVectors;
285
- export { type EmbedFunction, type LunoraVectors, type LunoraVectorsOptions, type QueryInput, type SchemaLike, type TableDefinitionLike, type TableVectorIndexLike, type UpsertInput, type VectorAdminIndexSummary, type VectorAdminIntrospector, type VectorAdminIntrospectorOptions, type VectorAdminQueryMatch, type VectorEmbedderLike, type VectorIndexDefinitionLike, type VectorIndexRegistryEntry, type VectorMatchLike, type VectorMatchesLike, type VectorMetric, type VectorQueryInputLike, type VectorRecordLike, type VectorSearchLike, type VectorUpsertInputLike, type VectorizeDeleteMutation, type VectorizeIndexDetails, type VectorizeIndexLike, type VectorizeMatch, type VectorizeMatches, type VectorizeQueryOptions, type VectorizeUpsertMutation, type VectorizeVector, type WriteEvent, type WriteHook, createContextVectors, createVectorAdminIntrospector, createVectorSyncHook, createVectors };
450
+ export { type EmbedFunction, type LunoraVectors, type LunoraVectorsOptions, type QueryInput, type SchemaLike, type TableDefinitionLike, type TableVectorIndexLike, type UpsertInput, type VectorAdminIndexSummary, type VectorAdminIntrospector, type VectorAdminIntrospectorOptions, type VectorAdminQueryMatch, type VectorBackfillFailure, type VectorBackfillSync, type VectorEmbedderLike, type VectorIndexDefinitionLike, type VectorIndexRegistryEntry, type VectorMatchLike, type VectorMatchesLike, type VectorQueryInputLike, type VectorRecordLike, type VectorSearchLike, type VectorUpsertInputLike, type WriteEvent, type WriteHook, createContextVectors, createVectorAdminIntrospector, createVectorBackfillSync, createVectorSyncHook, createVectors, vectorBackfillTargets };
@@ -1,3 +1 @@
1
- export { createContextVectors, createVectorSyncHook } from '../packem_shared/createContextVectors-DwZtnPeC.mjs';
2
- export { createVectorAdminIntrospector } from '../packem_shared/createVectorAdminIntrospector-DuSvcBa5.mjs';
3
- export { default as createVectors } from '../packem_shared/createVectors-CTSrctiK.mjs';
1
+ import{createContextVectors as t,createVectorBackfillSync as o,createVectorSyncHook as c,vectorBackfillTargets as a}from"../packem_shared/createContextVectors-DiyO3pZU.mjs";import{createVectorAdminIntrospector as l}from"../packem_shared/createVectorAdminIntrospector-Ct8v6PxJ.mjs";import{default as s}from"../packem_shared/createVectors-Dzv0ilKE.mjs";export{t as createContextVectors,l as createVectorAdminIntrospector,o as createVectorBackfillSync,c as createVectorSyncHook,s as createVectors,a as vectorBackfillTargets};
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@lunora/bindings",
3
- "version": "1.0.0-alpha.9",
4
- "description": "Lightweight Cloudflare binding helpers for Lunora — ctx.kv, ctx.images, ctx.analytics, ctx.pipelines, ctx.vectors, ctx.r2sql — one install, per-binding subpaths",
3
+ "version": "1.0.0-alpha.91",
4
+ "description": "Lightweight Cloudflare binding helpers for Lunora — ctx.kv, ctx.images, ctx.analytics, ctx.pipelines, ctx.vectors, ctx.r2sql, ctx.artifacts, ctx.aiSearch — one install, per-binding subpaths",
5
5
  "keywords": [
6
+ "ai-search",
6
7
  "analytics",
8
+ "artifacts",
7
9
  "cloudflare",
8
10
  "images",
9
11
  "kv",
@@ -58,13 +60,22 @@
58
60
  "types": "./dist/r2sql/index.d.ts",
59
61
  "import": "./dist/r2sql/index.mjs"
60
62
  },
63
+ "./artifacts": {
64
+ "types": "./dist/artifacts/index.d.ts",
65
+ "import": "./dist/artifacts/index.mjs"
66
+ },
67
+ "./ai-search": {
68
+ "types": "./dist/ai-search/index.d.ts",
69
+ "import": "./dist/ai-search/index.mjs"
70
+ },
61
71
  "./package.json": "./package.json"
62
72
  },
63
73
  "publishConfig": {
64
74
  "access": "public"
65
75
  },
66
76
  "dependencies": {
67
- "@lunora/errors": "1.0.0-alpha.6"
77
+ "@lunora/errors": "1.0.0-alpha.49",
78
+ "@lunora/platform": "1.0.0-alpha.50"
68
79
  },
69
80
  "engines": {
70
81
  "node": "^22.15.0 || >=24.11.0"
@@ -1,41 +0,0 @@
1
- import { LunoraError } from '@lunora/errors';
2
-
3
- const SQL_API_BASE = "https://api.cloudflare.com/client/v4/accounts";
4
- class AnalyticsSqlError extends LunoraError {
5
- constructor(status, body) {
6
- super("ANALYTICS_SQL_ERROR", `Analytics Engine SQL API returned ${String(status)}: ${body}`, { name: "AnalyticsSqlError", status });
7
- }
8
- }
9
- const createAnalyticsSqlClient = (config) => {
10
- const fetchImpl = config.fetch ?? globalThis.fetch;
11
- const endpoint = `${SQL_API_BASE}/${encodeURIComponent(config.accountId)}/analytics_engine/sql`;
12
- const query = async (sql) => {
13
- const response = await fetchImpl(endpoint, {
14
- body: sql,
15
- headers: {
16
- Authorization: `Bearer ${config.apiToken}`,
17
- "Content-Type": "text/plain"
18
- },
19
- method: "POST"
20
- });
21
- if (!response.ok) {
22
- throw new AnalyticsSqlError(response.status, await response.text());
23
- }
24
- let raw;
25
- try {
26
- raw = await response.json();
27
- } catch {
28
- throw new AnalyticsSqlError(response.status, "Analytics Engine SQL API returned a non-JSON body.");
29
- }
30
- const body = raw;
31
- const rows = body.data ?? [];
32
- return {
33
- columns: body.meta ?? [],
34
- rowCount: body.rows ?? rows.length,
35
- rows
36
- };
37
- };
38
- return { query };
39
- };
40
-
41
- export { AnalyticsSqlError, createAnalyticsSqlClient };
@@ -1,65 +0,0 @@
1
- import { LunoraError } from '@lunora/errors';
2
- import SelectBuilder from './SelectBuilder-BOqJQHEv.mjs';
3
- import { ident, toText } from './Sql-B3zq2YGx.mjs';
4
-
5
- const API_BASE = "https://api.sql.cloudflarestorage.com/api/v1/accounts";
6
- const inferColumns = (rows) => {
7
- if (rows[0] === void 0) {
8
- return [];
9
- }
10
- return Object.keys(rows[0]).map((name) => {
11
- return { name };
12
- });
13
- };
14
- class R2SqlError extends LunoraError {
15
- constructor(status, body) {
16
- super("R2_SQL_ERROR", `R2 SQL query failed (${String(status)}): ${body}`, { name: "R2SqlError", status });
17
- }
18
- }
19
- const createR2Sql = (config) => {
20
- const fetchImpl = config.fetch ?? globalThis.fetch;
21
- const base = config.endpoint ?? API_BASE;
22
- const endpoint = `${base}/${encodeURIComponent(config.accountId)}/r2-sql/query/${encodeURIComponent(config.bucket)}`;
23
- const warehouse = `${config.accountId}_${config.bucket}`;
24
- const exec = async (statement) => {
25
- const response = await fetchImpl(endpoint, {
26
- body: JSON.stringify({ query: statement, warehouse }),
27
- headers: {
28
- Authorization: `Bearer ${config.apiToken}`,
29
- "Content-Type": "application/json"
30
- },
31
- method: "POST"
32
- });
33
- if (!response.ok) {
34
- throw new R2SqlError(response.status, await response.text());
35
- }
36
- let raw;
37
- try {
38
- raw = await response.json();
39
- } catch {
40
- throw new R2SqlError(response.status, "R2 SQL returned a non-JSON body.");
41
- }
42
- const body = raw;
43
- if (body.success === false || body.errors !== void 0 && body.errors.length > 0) {
44
- throw new R2SqlError(response.status, JSON.stringify(body.errors ?? body));
45
- }
46
- const rows = body.result?.rows ?? [];
47
- return {
48
- columns: body.result?.schema ?? inferColumns(rows),
49
- rowCount: rows.length,
50
- rows
51
- };
52
- };
53
- return {
54
- describe: async (table) => exec(`DESCRIBE ${ident(table)}`),
55
- explain: async (statement, options) => exec(`EXPLAIN ${options?.format === "json" ? "FORMAT JSON " : ""}${toText(statement)}`),
56
- // `SelectBuilder`'s constructor validates the table reference (allowing an
57
- // optional `[AS] alias`), so no pre-validation here.
58
- from: (table) => new SelectBuilder(exec, table),
59
- query: async (statement) => exec(toText(statement)),
60
- showDatabases: async () => exec("SHOW DATABASES"),
61
- showTables: async (namespace) => exec(`SHOW TABLES IN ${ident(namespace)}`)
62
- };
63
- };
64
-
65
- export { R2SqlError, createR2Sql };