@lunora/bindings 1.0.0-alpha.70 → 1.0.0-alpha.72
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.
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{c as w,U as v}from"./concurrent-vRmSvRpF.mjs";const M=(s,r)=>{if(!r.includes("."))return s[r];let d=s;for(const n of r.split(".")){if(d===null||typeof d!="object"||Array.isArray(d))return;d=d[n]}return d},A=(s,r)=>{const d=r?.namespace,n=new Set(r?.shardedIndexNames),c=(a,i)=>{if(i!==void 0)return i;if(n.has(a)){if(d!==void 0)return d;throw new Error(`@lunora/bindings/vectors: index "${a}" belongs to a sharded table, but this DO instance has no shard key (it is the root/default DO) and no explicit namespace was given. A namespace-less operation here would reach every tenant's vectors — Vectorize indexes are account-global. Pass an explicit namespace, or issue this call from the sharded DO instance that owns the tenant.`)}},e=r?.deferAfterCommit,l=async(a,i,m)=>{await s.upsert(a,{embed:i.embed,id:i.id,input:i.input,metadata:i.metadata,namespace:m})},f=async(a,i)=>{await l(a,i,c(a,i.namespace))},o=e===void 0?f:async(a,i)=>{const m=c(a,i.namespace);await e(async()=>l(a,i,m))},t=async(a,i,m)=>{const u=await s.getByIds(a,i);return m===void 0?u:u.filter(h=>h.namespace===m)};return{deleteByIds:async(a,i,m)=>{const u=c(a,m);if(u===void 0){await s.deleteByIds(a,i);return}const h=await t(a,i,u);h.length!==0&&await s.deleteByIds(a,h.map(p=>p.id))},getByIds:async(a,i,m)=>{const u=c(a,m);return(await t(a,i,u)).map(p=>({id:p.id,metadata:p.metadata,namespace:p.namespace,values:[...p.values]}))},query:async(a,i)=>{const m=await s.query(a,{embed:i.embed,filter:i.filter,input:i.input,namespace:c(a,i.namespace),returnMetadata:i.returnMetadata??"indexed",topK:i.topK,vector:i.vector});return{count:m.count,matches:m.matches.map(u=>({id:u.id,metadata:u.metadata,score:u.score}))}},upsert:o,upsertNow:f}},y=new Set,b=s=>{y.has(s)||(y.add(s),console.warn(`[@lunora/bindings/vectors] index "${s}" syncs vectors without a namespace — in a
|
|
2
|
+
multi-tenant/sharded app this exposes one tenant's vectors (and any captured
|
|
3
|
+
metadata) to every other tenant, since Vectorize indexes are account-global.
|
|
4
|
+
Pass \`namespace\` (the shard/tenant key) on both write and query — query-side
|
|
5
|
+
namespace filtering is mandatory for multi-tenant apps. Single-tenant apps that
|
|
6
|
+
legitimately have no tenant key suppress this via { allowSharedNamespace: true }.`))},S=(s,r)=>{const d={};for(const n of r)n in s&&(d[n]=s[n]);return d},g=(s,r)=>{const d=s.tables[r.table],n=d?.vectorIndexes??[],c=Object.entries(s.vectorIndexes).filter(([,t])=>t.table===r.table);if(n.length===0&&c.length===0)return;const e=d?.softDeleteMode?.field,l=e!==void 0&&r.doc?.[e]!==void 0&&r.doc[e]!==null;if(r.op==="delete"||l)return{deletes:[...n.map(t=>t.name),...c.map(([t])=>t)],upserts:[]};const f=r.doc;if(!f)return;const o={deletes:[],upserts:[]};for(const t of n){const a=M(f,t.field);if(a==null)o.deletes.push(t.name);else if(typeof a=="string")o.upserts.push({embed:t.embed,input:a,metadata:t.metadata?S(f,t.metadata):void 0,name:t.name});else throw new TypeError(`@lunora/bindings/vectors: inline index "${t.name}" expects a string source at "${t.field}" on table "${r.table}" (got ${typeof a}); use a standalone defineVectorIndex with a select() to derive text from non-string columns`)}for(const[t,a]of c)o.upserts.push({embed:a.embed,input:a.select(f),metadata:a.metadata?.(f),name:t});return o},D=s=>{const{allowSharedNamespace:r,namespace:d,schema:n,vectors:c}=s;return async e=>{const l=g(n,e);if(!l)return;const f=[...l.deletes.map(o=>async()=>{await c.deleteByIds(o,[e.id])}),...l.upserts.map(o=>async()=>{!r&&d===void 0&&b(o.name),await c.upsertNow(o.name,{embed:o.embed,id:e.id,input:o.input,metadata:o.metadata,namespace:d})})];await w(f,v,async o=>o())}},I=1e3,x=(s,r)=>{const d=[];for(let n=0;n<s.length;n+=r)d.push(s.slice(n,n+r));return d},k=async(s,r)=>{const d=await w(s,v,async e=>{try{return{item:e,ok:!0,value:await r(e)}}catch(l){return{error:l,item:e,ok:!1}}}),n=d.flatMap(e=>e.ok?[{item:e.item,value:e.value}]:[]),c=d.flatMap(e=>e.ok?[]:[{error:e.error,item:e.item}]);if(s.length>=2&&n.length===0)throw c[0]?.error;return{failed:c,ok:n}},B=(s,r,d,n)=>{const c=new Map,e=[];for(const{doc:l,id:f}of d){let o;try{o=g(s,{doc:l,id:f,op:"update",table:r})}catch(t){n.set(f,t);continue}for(const t of o?.deletes??[])c.set(t,[...c.get(t)??[],f]);for(const t of o?.upserts??[])e.push({id:f,upsert:t})}return{deletes:c,pending:e}},C=async(s,r,d,n)=>{const{allowSharedNamespace:c,namespace:e,upsertMany:l,vectors:f}=s;!c&&e===void 0&&b(r);const o=d.map(({id:t,upsert:a,values:i})=>({embed:()=>i,id:t,input:a.input,metadata:a.metadata,namespace:e}));for(const t of x(o,I))try{await l(r,t)}catch{const a=await k(t,async i=>f.upsertNow(r,i));for(const{error:i,item:m}of a.failed)n.set(m.id,i)}},E=s=>async(r,d)=>{const n=new Map,{deletes:c,pending:e}=B(s.schema,r,d,n),l=await k(e,async({upsert:o})=>o.embed(o.input)),f=new Map;for(const{error:o,item:t}of l.failed)n.set(t.id,o);for(const{item:o,value:t}of l.ok)f.set(o.upsert.name,[...f.get(o.upsert.name)??[],{...o,values:t}]);for(const[o,t]of c)for(const a of x(t,I))await s.vectors.deleteByIds(o,a);for(const[o,t]of f)await C(s,o,t,n);return[...n].map(([o,t])=>({error:t,id:o}))},V=s=>{const r=new Map,d=(n,c,e)=>{const l=e===void 0?c:[...c,e];r.set(n,[...r.get(n)??[],JSON.stringify(l)])};for(const[n,c]of Object.entries(s.tables))for(const e of c.vectorIndexes??[])d(n,[e.name,e.field,e.dimensions,e.metric,e.metadata??[]],e.model);for(const[n,c]of Object.entries(s.vectorIndexes))d(c.table,[n,"(select)",c.dimensions,c.metric],c.model);return[...r].map(([n,c])=>({profile:c.toSorted((e,l)=>e.localeCompare(l)).join("|"),table:n}))};export{A as createContextVectors,E as createVectorBackfillSync,D as createVectorSyncHook,V as vectorBackfillTargets};
|
package/dist/vectors/index.d.mts
CHANGED
|
@@ -214,18 +214,30 @@ interface WriteEvent {
|
|
|
214
214
|
type WriteHook = (event: WriteEvent) => Promise<void>;
|
|
215
215
|
/** Inline vector index declared via `.vectorize(field, ...)` (DSL Shape A). */
|
|
216
216
|
interface TableVectorIndexLike {
|
|
217
|
+
dimensions?: number;
|
|
217
218
|
embed: VectorEmbedderLike;
|
|
218
219
|
field: string;
|
|
219
220
|
metadata?: ReadonlyArray<string>;
|
|
221
|
+
metric?: string;
|
|
222
|
+
/** Declared identifier of what `embed` produces; part of the backfill fingerprint. */
|
|
223
|
+
model?: string;
|
|
220
224
|
name: string;
|
|
221
225
|
}
|
|
222
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
|
+
};
|
|
223
231
|
vectorIndexes?: ReadonlyArray<TableVectorIndexLike>;
|
|
224
232
|
}
|
|
225
233
|
/** Standalone vector index declared via `defineVectorIndex(...)` (DSL Shape B). */
|
|
226
234
|
interface VectorIndexDefinitionLike {
|
|
235
|
+
dimensions?: number;
|
|
227
236
|
embed: VectorEmbedderLike;
|
|
228
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;
|
|
229
241
|
select: (row: Record<string, unknown>) => string;
|
|
230
242
|
table: string;
|
|
231
243
|
}
|
|
@@ -242,7 +254,7 @@ interface SchemaLike {
|
|
|
242
254
|
* Build a {@link WriteHook} that keeps Vectorize in sync with row writes. On
|
|
243
255
|
* insert/update it embeds each matching index's source (Shape A `row[field]`,
|
|
244
256
|
* Shape B `select(row)`) and upserts; on delete it removes the row's id from
|
|
245
|
-
* every index sourced from the table.
|
|
257
|
+
* every index sourced from the table. {@link planRowSync} makes the decision.
|
|
246
258
|
*
|
|
247
259
|
* Tenant isolation — IMPORTANT: Vectorize indexes are account-global and shared
|
|
248
260
|
* by every shard DO. Without a `namespace`, a multi-tenant sharded app has NO
|
|
@@ -298,6 +310,71 @@ declare const createVectorSyncHook: (options: {
|
|
|
298
310
|
schema: SchemaLike;
|
|
299
311
|
vectors: VectorSearchLike;
|
|
300
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.
|
|
322
|
+
*/
|
|
323
|
+
type VectorBackfillSync = (table: string, rows: ReadonlyArray<{
|
|
324
|
+
doc: Record<string, unknown>;
|
|
325
|
+
id: string;
|
|
326
|
+
}>) => Promise<ReadonlyArray<VectorBackfillFailure>>;
|
|
327
|
+
/**
|
|
328
|
+
* The backfill's counterpart to {@link createVectorSyncHook}: the same
|
|
329
|
+
* {@link planRowSync} decision for a whole page of rows, with the remote calls
|
|
330
|
+
* batched — every row is embedded (bounded concurrency), then each index takes
|
|
331
|
+
* ONE `upsertMany` and ONE `deleteByIds` per 1000 rows instead of a call per row.
|
|
332
|
+
* That is what keeps a page short enough to hold the shard's write-hook chain.
|
|
333
|
+
*
|
|
334
|
+
* Failures are split in two, because they need opposite handling.
|
|
335
|
+
*
|
|
336
|
+
* A ROW failure is deterministic and would fail on every retry: a non-string
|
|
337
|
+
* source, a `select()` that throws, text the model rejects, metadata Vectorize
|
|
338
|
+
* refuses. The row is reported and the page moves on — the live hook only logs
|
|
339
|
+
* these too, and a backfill that stopped on one would never finish.
|
|
340
|
+
*
|
|
341
|
+
* A SERVICE failure is transient: the embedder or Vectorize is unreachable. The
|
|
342
|
+
* call rejects, so the page is retried. It is recognised as every attempt in a
|
|
343
|
+
* group of two or more failing, and as a failed `deleteByIds` (which has no row
|
|
344
|
+
* content to blame). A batch `upsertMany` that fails is retried one row at a
|
|
345
|
+
* time to tell the two apart.
|
|
346
|
+
*
|
|
347
|
+
* `upsertMany` is the raw binding call, so the namespace is passed explicitly —
|
|
348
|
+
* the same `namespace` the live hook scopes by.
|
|
349
|
+
*/
|
|
350
|
+
type BackfillSyncOptions = {
|
|
351
|
+
allowSharedNamespace?: boolean;
|
|
352
|
+
namespace?: string;
|
|
353
|
+
schema: SchemaLike;
|
|
354
|
+
upsertMany: LunoraVectors["upsertMany"];
|
|
355
|
+
vectors: VectorSearchLike;
|
|
356
|
+
};
|
|
357
|
+
declare const createVectorBackfillSync: (options: BackfillSyncOptions) => VectorBackfillSync;
|
|
358
|
+
/**
|
|
359
|
+
* Every table with a vector index sourced from it, each with a fingerprint of
|
|
360
|
+
* what its stored vectors were built from — the input to the shard's vector
|
|
361
|
+
* backfill, which re-walks a table whose fingerprint changed.
|
|
362
|
+
*
|
|
363
|
+
* Covers what the schema can see: index names, the inline source field,
|
|
364
|
+
* dimensions, metric, inline metadata fields and the declared `model`. A
|
|
365
|
+
* function (`embed`, a Shape B `select`/`metadata`) has no stable identity to
|
|
366
|
+
* fingerprint — its source text changes with unrelated rebuilds of the bundle,
|
|
367
|
+
* which would re-embed whole tables for nothing — so the declared `model` string
|
|
368
|
+
* stands in for `embed`, and any other change is announced by calling the
|
|
369
|
+
* backfill with `restart: true`.
|
|
370
|
+
*
|
|
371
|
+
* `model` joins a descriptor only when declared, so an index without one keeps
|
|
372
|
+
* the fingerprint it was recorded under and is not re-embedded for it.
|
|
373
|
+
*/
|
|
374
|
+
declare const vectorBackfillTargets: (schema: SchemaLike) => {
|
|
375
|
+
profile: string;
|
|
376
|
+
table: string;
|
|
377
|
+
}[];
|
|
301
378
|
/**
|
|
302
379
|
* One vector index as the generated `LUNORA_VECTOR_INDEXES` registry describes
|
|
303
380
|
* it — the static schema shape, independent of any live binding. Structurally
|
|
@@ -361,4 +438,4 @@ interface VectorAdminIntrospectorOptions {
|
|
|
361
438
|
*/
|
|
362
439
|
declare const createVectorAdminIntrospector: (options: VectorAdminIntrospectorOptions) => VectorAdminIntrospector;
|
|
363
440
|
declare const createVectors: (options: LunoraVectorsOptions) => LunoraVectors;
|
|
364
|
-
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 VectorQueryInputLike, type VectorRecordLike, type VectorSearchLike, type VectorUpsertInputLike, type WriteEvent, type WriteHook, createContextVectors, createVectorAdminIntrospector, createVectorSyncHook, createVectors };
|
|
441
|
+
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 };
|
package/dist/vectors/index.d.ts
CHANGED
|
@@ -214,18 +214,30 @@ interface WriteEvent {
|
|
|
214
214
|
type WriteHook = (event: WriteEvent) => Promise<void>;
|
|
215
215
|
/** Inline vector index declared via `.vectorize(field, ...)` (DSL Shape A). */
|
|
216
216
|
interface TableVectorIndexLike {
|
|
217
|
+
dimensions?: number;
|
|
217
218
|
embed: VectorEmbedderLike;
|
|
218
219
|
field: string;
|
|
219
220
|
metadata?: ReadonlyArray<string>;
|
|
221
|
+
metric?: string;
|
|
222
|
+
/** Declared identifier of what `embed` produces; part of the backfill fingerprint. */
|
|
223
|
+
model?: string;
|
|
220
224
|
name: string;
|
|
221
225
|
}
|
|
222
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
|
+
};
|
|
223
231
|
vectorIndexes?: ReadonlyArray<TableVectorIndexLike>;
|
|
224
232
|
}
|
|
225
233
|
/** Standalone vector index declared via `defineVectorIndex(...)` (DSL Shape B). */
|
|
226
234
|
interface VectorIndexDefinitionLike {
|
|
235
|
+
dimensions?: number;
|
|
227
236
|
embed: VectorEmbedderLike;
|
|
228
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;
|
|
229
241
|
select: (row: Record<string, unknown>) => string;
|
|
230
242
|
table: string;
|
|
231
243
|
}
|
|
@@ -242,7 +254,7 @@ interface SchemaLike {
|
|
|
242
254
|
* Build a {@link WriteHook} that keeps Vectorize in sync with row writes. On
|
|
243
255
|
* insert/update it embeds each matching index's source (Shape A `row[field]`,
|
|
244
256
|
* Shape B `select(row)`) and upserts; on delete it removes the row's id from
|
|
245
|
-
* every index sourced from the table.
|
|
257
|
+
* every index sourced from the table. {@link planRowSync} makes the decision.
|
|
246
258
|
*
|
|
247
259
|
* Tenant isolation — IMPORTANT: Vectorize indexes are account-global and shared
|
|
248
260
|
* by every shard DO. Without a `namespace`, a multi-tenant sharded app has NO
|
|
@@ -298,6 +310,71 @@ declare const createVectorSyncHook: (options: {
|
|
|
298
310
|
schema: SchemaLike;
|
|
299
311
|
vectors: VectorSearchLike;
|
|
300
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.
|
|
322
|
+
*/
|
|
323
|
+
type VectorBackfillSync = (table: string, rows: ReadonlyArray<{
|
|
324
|
+
doc: Record<string, unknown>;
|
|
325
|
+
id: string;
|
|
326
|
+
}>) => Promise<ReadonlyArray<VectorBackfillFailure>>;
|
|
327
|
+
/**
|
|
328
|
+
* The backfill's counterpart to {@link createVectorSyncHook}: the same
|
|
329
|
+
* {@link planRowSync} decision for a whole page of rows, with the remote calls
|
|
330
|
+
* batched — every row is embedded (bounded concurrency), then each index takes
|
|
331
|
+
* ONE `upsertMany` and ONE `deleteByIds` per 1000 rows instead of a call per row.
|
|
332
|
+
* That is what keeps a page short enough to hold the shard's write-hook chain.
|
|
333
|
+
*
|
|
334
|
+
* Failures are split in two, because they need opposite handling.
|
|
335
|
+
*
|
|
336
|
+
* A ROW failure is deterministic and would fail on every retry: a non-string
|
|
337
|
+
* source, a `select()` that throws, text the model rejects, metadata Vectorize
|
|
338
|
+
* refuses. The row is reported and the page moves on — the live hook only logs
|
|
339
|
+
* these too, and a backfill that stopped on one would never finish.
|
|
340
|
+
*
|
|
341
|
+
* A SERVICE failure is transient: the embedder or Vectorize is unreachable. The
|
|
342
|
+
* call rejects, so the page is retried. It is recognised as every attempt in a
|
|
343
|
+
* group of two or more failing, and as a failed `deleteByIds` (which has no row
|
|
344
|
+
* content to blame). A batch `upsertMany` that fails is retried one row at a
|
|
345
|
+
* time to tell the two apart.
|
|
346
|
+
*
|
|
347
|
+
* `upsertMany` is the raw binding call, so the namespace is passed explicitly —
|
|
348
|
+
* the same `namespace` the live hook scopes by.
|
|
349
|
+
*/
|
|
350
|
+
type BackfillSyncOptions = {
|
|
351
|
+
allowSharedNamespace?: boolean;
|
|
352
|
+
namespace?: string;
|
|
353
|
+
schema: SchemaLike;
|
|
354
|
+
upsertMany: LunoraVectors["upsertMany"];
|
|
355
|
+
vectors: VectorSearchLike;
|
|
356
|
+
};
|
|
357
|
+
declare const createVectorBackfillSync: (options: BackfillSyncOptions) => VectorBackfillSync;
|
|
358
|
+
/**
|
|
359
|
+
* Every table with a vector index sourced from it, each with a fingerprint of
|
|
360
|
+
* what its stored vectors were built from — the input to the shard's vector
|
|
361
|
+
* backfill, which re-walks a table whose fingerprint changed.
|
|
362
|
+
*
|
|
363
|
+
* Covers what the schema can see: index names, the inline source field,
|
|
364
|
+
* dimensions, metric, inline metadata fields and the declared `model`. A
|
|
365
|
+
* function (`embed`, a Shape B `select`/`metadata`) has no stable identity to
|
|
366
|
+
* fingerprint — its source text changes with unrelated rebuilds of the bundle,
|
|
367
|
+
* which would re-embed whole tables for nothing — so the declared `model` string
|
|
368
|
+
* stands in for `embed`, and any other change is announced by calling the
|
|
369
|
+
* backfill with `restart: true`.
|
|
370
|
+
*
|
|
371
|
+
* `model` joins a descriptor only when declared, so an index without one keeps
|
|
372
|
+
* the fingerprint it was recorded under and is not re-embedded for it.
|
|
373
|
+
*/
|
|
374
|
+
declare const vectorBackfillTargets: (schema: SchemaLike) => {
|
|
375
|
+
profile: string;
|
|
376
|
+
table: string;
|
|
377
|
+
}[];
|
|
301
378
|
/**
|
|
302
379
|
* One vector index as the generated `LUNORA_VECTOR_INDEXES` registry describes
|
|
303
380
|
* it — the static schema shape, independent of any live binding. Structurally
|
|
@@ -361,4 +438,4 @@ interface VectorAdminIntrospectorOptions {
|
|
|
361
438
|
*/
|
|
362
439
|
declare const createVectorAdminIntrospector: (options: VectorAdminIntrospectorOptions) => VectorAdminIntrospector;
|
|
363
440
|
declare const createVectors: (options: LunoraVectorsOptions) => LunoraVectors;
|
|
364
|
-
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 VectorQueryInputLike, type VectorRecordLike, type VectorSearchLike, type VectorUpsertInputLike, type WriteEvent, type WriteHook, createContextVectors, createVectorAdminIntrospector, createVectorSyncHook, createVectors };
|
|
441
|
+
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 };
|
package/dist/vectors/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createContextVectors as t,createVectorSyncHook as
|
|
1
|
+
import{createContextVectors as t,createVectorBackfillSync as o,createVectorSyncHook as c,vectorBackfillTargets as a}from"../packem_shared/createContextVectors-fqM-VsVd.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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/bindings",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.72",
|
|
4
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",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"analytics",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@lunora/errors": "1.0.0-alpha.41",
|
|
68
|
-
"@lunora/platform": "1.0.0-alpha.
|
|
68
|
+
"@lunora/platform": "1.0.0-alpha.37"
|
|
69
69
|
},
|
|
70
70
|
"engines": {
|
|
71
71
|
"node": "^22.15.0 || >=24.11.0"
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import{c as b,U as g}from"./concurrent-vRmSvRpF.mjs";const x=(s,c)=>{if(!c.includes("."))return s[c];let n=s;for(const d of c.split(".")){if(n===null||typeof n!="object"||Array.isArray(n))return;n=n[d]}return n},S=(s,c)=>{const n=c?.namespace,d=new Set(c?.shardedIndexNames),l=(t,a)=>{if(a!==void 0)return a;if(d.has(t)){if(n!==void 0)return n;throw new Error(`@lunora/bindings/vectors: index "${t}" belongs to a sharded table, but this DO instance has no shard key (it is the root/default DO) and no explicit namespace was given. A namespace-less operation here would reach every tenant's vectors — Vectorize indexes are account-global. Pass an explicit namespace, or issue this call from the sharded DO instance that owns the tenant.`)}},i=c?.deferAfterCommit,v=async(t,a,r)=>{await s.upsert(t,{embed:a.embed,id:a.id,input:a.input,metadata:a.metadata,namespace:r})},u=async(t,a)=>{await v(t,a,l(t,a.namespace))},f=i===void 0?u:async(t,a)=>{const r=l(t,a.namespace);await i(async()=>v(t,a,r))},h=async(t,a,r)=>{const o=await s.getByIds(t,a);return r===void 0?o:o.filter(m=>m.namespace===r)};return{deleteByIds:async(t,a,r)=>{const o=l(t,r);if(o===void 0){await s.deleteByIds(t,a);return}const m=await h(t,a,o);m.length!==0&&await s.deleteByIds(t,m.map(e=>e.id))},getByIds:async(t,a,r)=>{const o=l(t,r);return(await h(t,a,o)).map(e=>({id:e.id,metadata:e.metadata,namespace:e.namespace,values:[...e.values]}))},query:async(t,a)=>{const r=await s.query(t,{embed:a.embed,filter:a.filter,input:a.input,namespace:l(t,a.namespace),returnMetadata:a.returnMetadata??"indexed",topK:a.topK,vector:a.vector});return{count:r.count,matches:r.matches.map(o=>({id:o.id,metadata:o.metadata,score:o.score}))}},upsert:f,upsertNow:u}},w=new Set,y=s=>{w.has(s)||(w.add(s),console.warn(`[@lunora/bindings/vectors] index "${s}" syncs vectors without a namespace — in a
|
|
2
|
-
multi-tenant/sharded app this exposes one tenant's vectors (and any captured
|
|
3
|
-
metadata) to every other tenant, since Vectorize indexes are account-global.
|
|
4
|
-
Pass \`namespace\` (the shard/tenant key) on both write and query — query-side
|
|
5
|
-
namespace filtering is mandatory for multi-tenant apps. Single-tenant apps that
|
|
6
|
-
legitimately have no tenant key suppress this via { allowSharedNamespace: true }.`))},I=(s,c)=>{const n={};for(const d of c)d in s&&(n[d]=s[d]);return n},C=s=>{const{allowSharedNamespace:c,namespace:n,schema:d,vectors:l}=s;return async i=>{const u=d.tables[i.table]?.vectorIndexes??[],f=Object.entries(d.vectorIndexes).filter(([,e])=>e.table===i.table);if(u.length===0&&f.length===0)return;const h=[...u.map(e=>e.name),...f.map(([e])=>e)];if(i.op==="delete"){await Promise.all(h.map(e=>l.deleteByIds(e,[i.id])));return}const t=i.doc;if(!t)return;const a=u.map(e=>({index:e,value:x(t,e.field)})),r=a.filter(e=>e.value!==void 0&&e.value!==null),o=a.filter(e=>e.value===void 0||e.value===null);for(const{index:e,value:p}of r)if(typeof p!="string")throw new TypeError(`@lunora/bindings/vectors: inline index "${e.name}" expects a string source at "${e.field}" on table "${i.table}" (got ${typeof p}); use a standalone defineVectorIndex with a select() to derive text from non-string columns`);const m=[...o.map(e=>async()=>{await l.deleteByIds(e.index.name,[i.id])}),...r.map(e=>async()=>{!c&&n===void 0&&y(e.index.name),await l.upsertNow(e.index.name,{embed:e.index.embed,id:i.id,input:e.value,metadata:e.index.metadata?I(t,e.index.metadata):void 0,namespace:n})}),...f.map(([e,p])=>async()=>{!c&&n===void 0&&y(e),await l.upsertNow(e,{embed:p.embed,id:i.id,input:p.select(t),metadata:p.metadata?.(t),namespace:n})})];await b(m,g,async e=>e())}};export{S as createContextVectors,C as createVectorSyncHook};
|