@lunora/bindings 1.0.0-alpha.2 → 1.0.0-alpha.21
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/LICENSE.md +6 -0
- package/dist/analytics/index.d.mts +66 -87
- package/dist/analytics/index.d.ts +66 -87
- package/dist/analytics/index.mjs +1 -2
- package/dist/images/index.d.mts +148 -148
- package/dist/images/index.d.ts +148 -148
- package/dist/images/index.mjs +1 -3
- package/dist/kv/index.d.mts +130 -113
- package/dist/kv/index.d.ts +130 -113
- package/dist/kv/index.mjs +1 -1
- package/dist/packem_shared/AnalyticsSqlError-B-JcsUhX.mjs +1 -0
- package/dist/packem_shared/R2SqlError-Uaz0RJTR.mjs +1 -0
- package/dist/packem_shared/SelectBuilder-WMBQ2b4q.mjs +1 -0
- package/dist/packem_shared/SetOperation-BrzMB1Tb.mjs +1 -0
- package/dist/packem_shared/Sql-CwhZl1Q5.mjs +1 -0
- package/dist/packem_shared/WindowExpression-CxCuEy8V.mjs +1 -0
- package/dist/packem_shared/WindowFunction-rzl4CzYE.mjs +1 -0
- package/dist/packem_shared/asc-BsbdtIQU.mjs +1 -0
- package/dist/packem_shared/buildImageDeliveryUrl-CMUkVJfB.mjs +1 -0
- package/dist/packem_shared/buildSignedImageUrl-BzgCHIu6.mjs +4 -0
- package/dist/packem_shared/concurrent-DMFQCILU.mjs +1 -0
- package/dist/packem_shared/createAnalytics-HRPCzT1M.mjs +1 -0
- package/dist/packem_shared/createContextVectors-D-ae1wWO.mjs +6 -0
- package/dist/packem_shared/createImages-TW6ircG2.mjs +1 -0
- package/dist/packem_shared/createKv-CKeFDFuG.mjs +1 -0
- package/dist/packem_shared/createKvIntrospector-Bz0nqUjD.mjs +1 -0
- package/dist/packem_shared/createPipelines-n0Bf_p8Y.mjs +1 -0
- package/dist/packem_shared/createVectorAdminIntrospector-B9Yt09yj.mjs +1 -0
- package/dist/packem_shared/createVectors-jb8fAY6R.mjs +1 -0
- package/dist/pipelines/index.d.mts +24 -24
- package/dist/pipelines/index.d.ts +24 -24
- package/dist/pipelines/index.mjs +1 -1
- package/dist/r2sql/index.d.mts +124 -124
- package/dist/r2sql/index.d.ts +124 -124
- package/dist/r2sql/index.mjs +1 -7
- package/dist/vectors/index.d.mts +185 -134
- package/dist/vectors/index.d.ts +185 -134
- package/dist/vectors/index.mjs +1 -3
- package/package.json +5 -1
- package/dist/packem_shared/AnalyticsSqlError-CGTdsi4H.mjs +0 -42
- package/dist/packem_shared/R2SqlError-DlDd_SrE.mjs +0 -67
- package/dist/packem_shared/SelectBuilder-DHaXZwn_.mjs +0 -167
- package/dist/packem_shared/SetOperation-RDHcxccj.mjs +0 -80
- package/dist/packem_shared/Sql-DceGtcUd.mjs +0 -68
- package/dist/packem_shared/WindowExpression-Cg9s2xcr.mjs +0 -44
- package/dist/packem_shared/WindowFunction-DA3pGC3N.mjs +0 -82
- package/dist/packem_shared/asc-Cur-xO8v.mjs +0 -16
- package/dist/packem_shared/buildImageDeliveryUrl-D1sVfIOP.mjs +0 -30
- package/dist/packem_shared/buildSignedImageUrl-Otdgc_jO.mjs +0 -113
- package/dist/packem_shared/concurrent-Dj5sOibv.mjs +0 -23
- package/dist/packem_shared/createAnalytics-CEEI69o9.mjs +0 -57
- package/dist/packem_shared/createContextVectors-BSizpmu5.mjs +0 -140
- package/dist/packem_shared/createImages-CJrvqX0u.mjs +0 -80
- package/dist/packem_shared/createKv-DTiSt216.mjs +0 -141
- package/dist/packem_shared/createPipelines-CfyJ6VGu.mjs +0 -10
- package/dist/packem_shared/createVectorAdminIntrospector-BJUOM6VW.mjs +0 -51
- package/dist/packem_shared/createVectors-LSpGoKCd.mjs +0 -91
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { c as concurrentMap, U as UPSERT_EMBED_CONCURRENCY } from './concurrent-Dj5sOibv.mjs';
|
|
2
|
-
|
|
3
|
-
const createContextVectors = (lunora) => {
|
|
4
|
-
const upsert = async (indexName, input) => {
|
|
5
|
-
await lunora.upsert(indexName, {
|
|
6
|
-
embed: input.embed,
|
|
7
|
-
id: input.id,
|
|
8
|
-
input: input.input,
|
|
9
|
-
metadata: input.metadata,
|
|
10
|
-
namespace: input.namespace
|
|
11
|
-
});
|
|
12
|
-
};
|
|
13
|
-
return {
|
|
14
|
-
deleteByIds: async (indexName, ids) => {
|
|
15
|
-
await lunora.deleteByIds(indexName, ids);
|
|
16
|
-
},
|
|
17
|
-
getByIds: async (indexName, ids) => {
|
|
18
|
-
const records = await lunora.getByIds(indexName, ids);
|
|
19
|
-
return records.map((record) => {
|
|
20
|
-
return { id: record.id, metadata: record.metadata, values: record.values };
|
|
21
|
-
});
|
|
22
|
-
},
|
|
23
|
-
query: async (indexName, input) => {
|
|
24
|
-
const result = await lunora.query(indexName, {
|
|
25
|
-
embed: input.embed,
|
|
26
|
-
filter: input.filter,
|
|
27
|
-
input: input.input,
|
|
28
|
-
namespace: input.namespace,
|
|
29
|
-
// Default to "indexed" rather than "all": returning every
|
|
30
|
-
// metadata field by default leaks whatever was stored on the
|
|
31
|
-
// vector (potentially cross-tenant if namespaces aren't wired).
|
|
32
|
-
// Callers that need full metadata opt in explicitly via input.
|
|
33
|
-
returnMetadata: input.returnMetadata ?? "indexed",
|
|
34
|
-
topK: input.topK,
|
|
35
|
-
vector: input.vector
|
|
36
|
-
});
|
|
37
|
-
return {
|
|
38
|
-
count: result.count,
|
|
39
|
-
matches: result.matches.map((match) => {
|
|
40
|
-
return { id: match.id, metadata: match.metadata, score: match.score };
|
|
41
|
-
})
|
|
42
|
-
};
|
|
43
|
-
},
|
|
44
|
-
upsert,
|
|
45
|
-
upsertNow: upsert
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
const sharedNamespaceWarned = /* @__PURE__ */ new Set();
|
|
49
|
-
const warnSharedNamespace = (indexName) => {
|
|
50
|
-
if (sharedNamespaceWarned.has(indexName)) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
sharedNamespaceWarned.add(indexName);
|
|
54
|
-
console.warn(
|
|
55
|
-
`[@lunora/bindings/vectors] index "${indexName}" syncs vectors without a namespace — in a
|
|
56
|
-
multi-tenant/sharded app this exposes one tenant's vectors (and any captured
|
|
57
|
-
metadata) to every other tenant, since Vectorize indexes are account-global.
|
|
58
|
-
Pass \`namespace\` (the shard/tenant key) on both write and query — query-side
|
|
59
|
-
namespace filtering is mandatory for multi-tenant apps. Single-tenant apps that
|
|
60
|
-
legitimately have no tenant key suppress this via { allowSharedNamespace: true }.`
|
|
61
|
-
);
|
|
62
|
-
};
|
|
63
|
-
const pickMetadata = (row, fields) => {
|
|
64
|
-
const result = {};
|
|
65
|
-
for (const field of fields) {
|
|
66
|
-
if (field in row) {
|
|
67
|
-
result[field] = row[field];
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return result;
|
|
71
|
-
};
|
|
72
|
-
const createVectorSyncHook = (options) => {
|
|
73
|
-
const { allowSharedNamespace, namespace, schema, vectors } = options;
|
|
74
|
-
return async (event) => {
|
|
75
|
-
const tableDefinition = schema.tables[event.table];
|
|
76
|
-
const inlineIndexes = tableDefinition?.vectorIndexes ?? [];
|
|
77
|
-
const standaloneIndexes = Object.entries(schema.vectorIndexes).filter(([, definition]) => definition.table === event.table);
|
|
78
|
-
if (inlineIndexes.length === 0 && standaloneIndexes.length === 0) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
const allIndexNames = [...inlineIndexes.map((index) => index.name), ...standaloneIndexes.map(([name]) => name)];
|
|
82
|
-
if (event.op === "delete") {
|
|
83
|
-
await Promise.all(allIndexNames.map((name) => vectors.deleteByIds(name, [event.id])));
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
const row = event.doc;
|
|
87
|
-
if (!row) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
const inlineWithValue = inlineIndexes.map((index) => {
|
|
91
|
-
return { index, value: row[index.field] };
|
|
92
|
-
});
|
|
93
|
-
const inlineToUpsert = inlineWithValue.filter((entry) => entry.value !== void 0 && entry.value !== null);
|
|
94
|
-
const inlineToClear = inlineWithValue.filter((entry) => entry.value === void 0 || entry.value === null);
|
|
95
|
-
for (const { index, value } of inlineToUpsert) {
|
|
96
|
-
if (typeof value !== "string") {
|
|
97
|
-
throw new TypeError(
|
|
98
|
-
`@lunora/bindings/vectors: inline index "${index.name}" expects a string source at "${index.field}" on table "${event.table}" (got ${typeof value}); use a standalone defineVectorIndex with a select() to derive text from non-string columns`
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
const operations = [
|
|
103
|
-
...inlineToClear.map((entry) => async () => {
|
|
104
|
-
await vectors.deleteByIds(entry.index.name, [event.id]);
|
|
105
|
-
}),
|
|
106
|
-
...inlineToUpsert.map((entry) => async () => {
|
|
107
|
-
if (!allowSharedNamespace && namespace === void 0) {
|
|
108
|
-
warnSharedNamespace(entry.index.name);
|
|
109
|
-
}
|
|
110
|
-
await vectors.upsert(entry.index.name, {
|
|
111
|
-
embed: entry.index.embed,
|
|
112
|
-
id: event.id,
|
|
113
|
-
input: entry.value,
|
|
114
|
-
metadata: entry.index.metadata ? pickMetadata(row, entry.index.metadata) : void 0,
|
|
115
|
-
namespace
|
|
116
|
-
});
|
|
117
|
-
}),
|
|
118
|
-
...standaloneIndexes.map(([name, definition]) => async () => {
|
|
119
|
-
if (!allowSharedNamespace && namespace === void 0) {
|
|
120
|
-
warnSharedNamespace(name);
|
|
121
|
-
}
|
|
122
|
-
await vectors.upsert(name, {
|
|
123
|
-
embed: definition.embed,
|
|
124
|
-
id: event.id,
|
|
125
|
-
input: definition.select(row),
|
|
126
|
-
metadata: definition.metadata?.(row),
|
|
127
|
-
namespace
|
|
128
|
-
});
|
|
129
|
-
})
|
|
130
|
-
];
|
|
131
|
-
try {
|
|
132
|
-
await concurrentMap(operations, UPSERT_EMBED_CONCURRENCY, async (operation) => operation());
|
|
133
|
-
} catch (error) {
|
|
134
|
-
await Promise.allSettled(allIndexNames.map((name) => vectors.deleteByIds(name, [event.id])));
|
|
135
|
-
throw error;
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
export { createContextVectors, createVectorSyncHook };
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
const ALLOWED_OUTPUT_FORMATS = /* @__PURE__ */ new Set(["image/avif", "image/gif", "image/jpeg", "image/png", "image/webp"]);
|
|
2
|
-
const DEFAULT_MAX_DIMENSION = 1e4;
|
|
3
|
-
const DEFAULT_OUTPUT_FORMAT = "image/webp";
|
|
4
|
-
const isR2ObjectBody = (input) => {
|
|
5
|
-
if (typeof input !== "object") {
|
|
6
|
-
return false;
|
|
7
|
-
}
|
|
8
|
-
const object = input;
|
|
9
|
-
return "body" in object && !(input instanceof ArrayBuffer) && !(input instanceof Uint8Array);
|
|
10
|
-
};
|
|
11
|
-
const toStream = (input) => {
|
|
12
|
-
if (input instanceof ReadableStream) {
|
|
13
|
-
return input;
|
|
14
|
-
}
|
|
15
|
-
if (isR2ObjectBody(input)) {
|
|
16
|
-
if (input.body === null) {
|
|
17
|
-
throw new Error("@lunora/bindings/images: R2 object body is null (object missing or already consumed)");
|
|
18
|
-
}
|
|
19
|
-
return input.body;
|
|
20
|
-
}
|
|
21
|
-
if (input instanceof Blob) {
|
|
22
|
-
return input.stream();
|
|
23
|
-
}
|
|
24
|
-
const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
25
|
-
return new ReadableStream({
|
|
26
|
-
start(controller) {
|
|
27
|
-
controller.enqueue(bytes);
|
|
28
|
-
controller.close();
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
};
|
|
32
|
-
const sanitizeTransform = (transform, maxDimension) => {
|
|
33
|
-
if (transform === void 0) {
|
|
34
|
-
return {};
|
|
35
|
-
}
|
|
36
|
-
const clampDimension = (value) => {
|
|
37
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
38
|
-
throw new Error("@lunora/bindings/images: width/height must be a positive finite number");
|
|
39
|
-
}
|
|
40
|
-
return Math.min(Math.floor(value), maxDimension);
|
|
41
|
-
};
|
|
42
|
-
const rest = { ...transform };
|
|
43
|
-
delete rest.draw;
|
|
44
|
-
return {
|
|
45
|
-
...rest,
|
|
46
|
-
...transform.width === void 0 ? {} : { width: clampDimension(transform.width) },
|
|
47
|
-
...transform.height === void 0 ? {} : { height: clampDimension(transform.height) }
|
|
48
|
-
};
|
|
49
|
-
};
|
|
50
|
-
const splitOverlay = (overlay) => {
|
|
51
|
-
const { image, transform, ...drawOptions } = overlay;
|
|
52
|
-
return { drawOptions, image, transform };
|
|
53
|
-
};
|
|
54
|
-
const resolveOutput = (output) => {
|
|
55
|
-
const format = output?.format ?? DEFAULT_OUTPUT_FORMAT;
|
|
56
|
-
if (!ALLOWED_OUTPUT_FORMATS.has(format)) {
|
|
57
|
-
throw new Error(`@lunora/bindings/images: unsupported output format "${format}" (allowed: ${[...ALLOWED_OUTPUT_FORMATS].join(", ")})`);
|
|
58
|
-
}
|
|
59
|
-
return { ...output, format };
|
|
60
|
-
};
|
|
61
|
-
const createImages = (options) => {
|
|
62
|
-
const { binding } = options;
|
|
63
|
-
const maxDimension = options.maxDimension ?? DEFAULT_MAX_DIMENSION;
|
|
64
|
-
return {
|
|
65
|
-
info: async (input) => binding.info(toStream(input)),
|
|
66
|
-
transform: async (input, transform, output, overlays) => {
|
|
67
|
-
const safeTransform = sanitizeTransform(transform, maxDimension);
|
|
68
|
-
const outputOptions = resolveOutput(output);
|
|
69
|
-
let transformer = binding.input(toStream(input)).transform(safeTransform);
|
|
70
|
-
for (const overlay of overlays ?? []) {
|
|
71
|
-
const { drawOptions, image, transform: overlayTransform } = splitOverlay(overlay);
|
|
72
|
-
const overlayImage = overlayTransform === void 0 ? toStream(image) : binding.input(toStream(image)).transform(sanitizeTransform(overlayTransform, maxDimension));
|
|
73
|
-
transformer = transformer.draw(overlayImage, drawOptions);
|
|
74
|
-
}
|
|
75
|
-
return transformer.output(outputOptions);
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export { createImages };
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
const MAX_KEY_LENGTH = 512;
|
|
2
|
-
const MAX_LIST_LIMIT = 1e3;
|
|
3
|
-
const validateKey = (key) => {
|
|
4
|
-
if (typeof key !== "string" || key.length === 0) {
|
|
5
|
-
throw new Error("@lunora/bindings/kv: key must be a non-empty string");
|
|
6
|
-
}
|
|
7
|
-
if (key.length > MAX_KEY_LENGTH) {
|
|
8
|
-
throw new Error(`@lunora/bindings/kv: key exceeds ${String(MAX_KEY_LENGTH)}-byte limit`);
|
|
9
|
-
}
|
|
10
|
-
if (key.includes("\0")) {
|
|
11
|
-
throw new Error("@lunora/bindings/kv: key contains NUL byte");
|
|
12
|
-
}
|
|
13
|
-
const segments = key.split("/");
|
|
14
|
-
for (const segment of segments) {
|
|
15
|
-
if (segment === "." || segment === "..") {
|
|
16
|
-
throw new Error("@lunora/bindings/kv: key contains a `.`/`..` path component");
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
const validatePrefix = (prefix) => {
|
|
21
|
-
if (prefix.length === 0) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (prefix.length > MAX_KEY_LENGTH) {
|
|
25
|
-
throw new Error(`@lunora/bindings/kv: prefix exceeds ${String(MAX_KEY_LENGTH)}-byte limit`);
|
|
26
|
-
}
|
|
27
|
-
if (prefix.includes("\0")) {
|
|
28
|
-
throw new Error("@lunora/bindings/kv: prefix contains NUL byte");
|
|
29
|
-
}
|
|
30
|
-
const segments = prefix.split("/");
|
|
31
|
-
for (const segment of segments) {
|
|
32
|
-
if (segment === "." || segment === "..") {
|
|
33
|
-
throw new Error("@lunora/bindings/kv: prefix contains a `.`/`..` path component");
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
const scopeKey = (prefix, key) => {
|
|
38
|
-
validateKey(prefix);
|
|
39
|
-
validateKey(key);
|
|
40
|
-
const trimmedPrefix = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
41
|
-
const composed = `${trimmedPrefix}/${key}`;
|
|
42
|
-
if (composed.length > MAX_KEY_LENGTH) {
|
|
43
|
-
throw new Error(`@lunora/bindings/kv: scoped key exceeds ${String(MAX_KEY_LENGTH)}-byte limit`);
|
|
44
|
-
}
|
|
45
|
-
return composed;
|
|
46
|
-
};
|
|
47
|
-
const toPutOptions = (options) => {
|
|
48
|
-
const out = {};
|
|
49
|
-
if (options.expiration !== void 0 && options.expirationTtl !== void 0) {
|
|
50
|
-
throw new Error("@lunora/bindings/kv: `expiration` and `expirationTtl` are mutually exclusive");
|
|
51
|
-
}
|
|
52
|
-
if (options.expiration !== void 0) {
|
|
53
|
-
out.expiration = options.expiration;
|
|
54
|
-
}
|
|
55
|
-
if (options.expirationTtl !== void 0) {
|
|
56
|
-
out.expirationTtl = options.expirationTtl;
|
|
57
|
-
}
|
|
58
|
-
if (options.metadata !== void 0) {
|
|
59
|
-
out.metadata = options.metadata;
|
|
60
|
-
}
|
|
61
|
-
return Object.keys(out).length > 0 ? out : void 0;
|
|
62
|
-
};
|
|
63
|
-
const createKv = (options) => {
|
|
64
|
-
if (!options.namespace) {
|
|
65
|
-
throw new Error("@lunora/bindings/kv: `namespace` is required");
|
|
66
|
-
}
|
|
67
|
-
const { keyPrefix, namespace } = options;
|
|
68
|
-
if (keyPrefix !== void 0) {
|
|
69
|
-
validateKey(keyPrefix);
|
|
70
|
-
}
|
|
71
|
-
const resolve = (key) => {
|
|
72
|
-
if (keyPrefix === void 0) {
|
|
73
|
-
validateKey(key);
|
|
74
|
-
return key;
|
|
75
|
-
}
|
|
76
|
-
return scopeKey(keyPrefix, key);
|
|
77
|
-
};
|
|
78
|
-
const stripPrefix = (name) => {
|
|
79
|
-
if (keyPrefix === void 0) {
|
|
80
|
-
return name;
|
|
81
|
-
}
|
|
82
|
-
const trimmed = keyPrefix.endsWith("/") ? keyPrefix : `${keyPrefix}/`;
|
|
83
|
-
return name.startsWith(trimmed) ? name.slice(trimmed.length) : name;
|
|
84
|
-
};
|
|
85
|
-
const get = async (key, getOptions = {}) => {
|
|
86
|
-
const value = await namespace.get(resolve(key), { cacheTtl: getOptions.cacheTtl, type: "json" });
|
|
87
|
-
return value ?? null;
|
|
88
|
-
};
|
|
89
|
-
const getRaw = async (key, getOptions = {}) => {
|
|
90
|
-
const value = await namespace.get(resolve(key), { cacheTtl: getOptions.cacheTtl, type: getOptions.type ?? "text" });
|
|
91
|
-
return value ?? null;
|
|
92
|
-
};
|
|
93
|
-
const getWithMetadata = async (key, getOptions = {}) => {
|
|
94
|
-
const result = await namespace.getWithMetadata(resolve(key), { cacheTtl: getOptions.cacheTtl, type: "json" });
|
|
95
|
-
return { metadata: result.metadata ?? null, value: result.value ?? null };
|
|
96
|
-
};
|
|
97
|
-
const put = async (key, value, putOptions = {}) => {
|
|
98
|
-
const body = putOptions.raw ? value : JSON.stringify(value);
|
|
99
|
-
await namespace.put(resolve(key), body, toPutOptions(putOptions));
|
|
100
|
-
};
|
|
101
|
-
const deleteKey = async (key) => {
|
|
102
|
-
await namespace.delete(resolve(key));
|
|
103
|
-
};
|
|
104
|
-
const list = async (listOptions = {}) => {
|
|
105
|
-
if (listOptions.limit !== void 0 && (!Number.isInteger(listOptions.limit) || listOptions.limit <= 0)) {
|
|
106
|
-
throw new Error("@lunora/bindings/kv: `limit` must be a positive integer");
|
|
107
|
-
}
|
|
108
|
-
let { prefix } = listOptions;
|
|
109
|
-
if (prefix !== void 0) {
|
|
110
|
-
validatePrefix(prefix);
|
|
111
|
-
}
|
|
112
|
-
if (keyPrefix !== void 0) {
|
|
113
|
-
const base = keyPrefix.endsWith("/") ? keyPrefix : `${keyPrefix}/`;
|
|
114
|
-
prefix = listOptions.prefix === void 0 ? base : `${base}${listOptions.prefix}`;
|
|
115
|
-
validatePrefix(prefix);
|
|
116
|
-
}
|
|
117
|
-
const limit = listOptions.limit === void 0 ? void 0 : Math.min(listOptions.limit, MAX_LIST_LIMIT);
|
|
118
|
-
const result = await namespace.list({ cursor: listOptions.cursor, limit, prefix });
|
|
119
|
-
const keys = result.keys.map((entry) => {
|
|
120
|
-
return {
|
|
121
|
-
...entry,
|
|
122
|
-
name: stripPrefix(entry.name)
|
|
123
|
-
};
|
|
124
|
-
});
|
|
125
|
-
return {
|
|
126
|
-
cursor: result.list_complete ? void 0 : result.cursor,
|
|
127
|
-
keys,
|
|
128
|
-
listComplete: result.list_complete
|
|
129
|
-
};
|
|
130
|
-
};
|
|
131
|
-
return {
|
|
132
|
-
delete: deleteKey,
|
|
133
|
-
get,
|
|
134
|
-
getRaw,
|
|
135
|
-
getWithMetadata,
|
|
136
|
-
list,
|
|
137
|
-
put
|
|
138
|
-
};
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
export { createKv, scopeKey };
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
const MAX_TOP_K = 20;
|
|
2
|
-
const DEFAULT_TOP_K = 10;
|
|
3
|
-
const createVectorAdminIntrospector = (options) => {
|
|
4
|
-
const { embedders, indexes, registry } = options;
|
|
5
|
-
const listIndexes = async () => Promise.all(
|
|
6
|
-
registry.map(async (entry) => {
|
|
7
|
-
const binding = indexes[entry.name];
|
|
8
|
-
if (binding?.describe === void 0) {
|
|
9
|
-
return { ...entry };
|
|
10
|
-
}
|
|
11
|
-
try {
|
|
12
|
-
const details = await binding.describe();
|
|
13
|
-
return {
|
|
14
|
-
...entry,
|
|
15
|
-
dimensions: entry.dimensions ?? details.dimensions,
|
|
16
|
-
processedUpToMutation: details.processedUpToMutation,
|
|
17
|
-
vectorsCount: details.vectorsCount
|
|
18
|
-
};
|
|
19
|
-
} catch {
|
|
20
|
-
return { ...entry };
|
|
21
|
-
}
|
|
22
|
-
})
|
|
23
|
-
);
|
|
24
|
-
const hasEmbedders = embedders !== void 0 && Object.keys(embedders).length > 0;
|
|
25
|
-
if (!hasEmbedders) {
|
|
26
|
-
return { listIndexes };
|
|
27
|
-
}
|
|
28
|
-
const queryIndex = async ({ name, text, topK }) => {
|
|
29
|
-
const binding = indexes[name];
|
|
30
|
-
if (binding === void 0) {
|
|
31
|
-
throw new Error(`@lunora/bindings/vectors: no Vectorize binding registered for index "${name}"`);
|
|
32
|
-
}
|
|
33
|
-
const embed = embedders[name];
|
|
34
|
-
if (embed === void 0) {
|
|
35
|
-
throw new Error(`@lunora/bindings/vectors: no embedder registered for index "${name}" — it lists read-only`);
|
|
36
|
-
}
|
|
37
|
-
const vector = await embed(text);
|
|
38
|
-
const result = await binding.query(vector, {
|
|
39
|
-
returnMetadata: "all",
|
|
40
|
-
topK: Math.min(topK ?? DEFAULT_TOP_K, MAX_TOP_K)
|
|
41
|
-
});
|
|
42
|
-
return {
|
|
43
|
-
matches: result.matches.map((match) => {
|
|
44
|
-
return { id: match.id, metadata: match.metadata, score: match.score };
|
|
45
|
-
})
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
return { listIndexes, queryIndex };
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export { createVectorAdminIntrospector };
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { c as concurrentMap, U as UPSERT_EMBED_CONCURRENCY } from './concurrent-Dj5sOibv.mjs';
|
|
2
|
-
|
|
3
|
-
const resolveIndex = (indexes, name) => {
|
|
4
|
-
const index = indexes[name];
|
|
5
|
-
if (!index) {
|
|
6
|
-
throw new Error(`@lunora/bindings/vectors: no index registered for "${name}". Known indexes: ${Object.keys(indexes).join(", ") || "(none)"}`);
|
|
7
|
-
}
|
|
8
|
-
return index;
|
|
9
|
-
};
|
|
10
|
-
const toVector = async (input) => {
|
|
11
|
-
const values = await input.embed(input.input);
|
|
12
|
-
return {
|
|
13
|
-
id: input.id,
|
|
14
|
-
metadata: input.metadata,
|
|
15
|
-
namespace: input.namespace,
|
|
16
|
-
values
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
const MAX_TOP_K = 100;
|
|
20
|
-
const MAX_TOP_K_WITH_VALUES = 20;
|
|
21
|
-
const MAX_ID_BATCH = 1e3;
|
|
22
|
-
const MAX_UPSERT_BATCH = 1e3;
|
|
23
|
-
const createVectors = (options) => {
|
|
24
|
-
if (Object.keys(options.indexes).length === 0) {
|
|
25
|
-
throw new Error("@lunora/bindings/vectors: at least one index binding is required");
|
|
26
|
-
}
|
|
27
|
-
const upsert = async (indexName, input) => {
|
|
28
|
-
const index = resolveIndex(options.indexes, indexName);
|
|
29
|
-
const vector = await toVector(input);
|
|
30
|
-
return index.upsert([vector]);
|
|
31
|
-
};
|
|
32
|
-
const upsertMany = async (indexName, inputs) => {
|
|
33
|
-
const index = resolveIndex(options.indexes, indexName);
|
|
34
|
-
if (inputs.length > MAX_UPSERT_BATCH) {
|
|
35
|
-
throw new RangeError(
|
|
36
|
-
`@lunora/bindings/vectors: upsertMany batch exceeds ${String(MAX_UPSERT_BATCH)} (got ${String(inputs.length)}) — split across calls`
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
const vectors = await concurrentMap(inputs, UPSERT_EMBED_CONCURRENCY, toVector);
|
|
40
|
-
return index.upsert(vectors);
|
|
41
|
-
};
|
|
42
|
-
const query = async (indexName, input) => {
|
|
43
|
-
const index = resolveIndex(options.indexes, indexName);
|
|
44
|
-
const wantsHeavyPayload = input.returnValues === true || input.returnMetadata === "all";
|
|
45
|
-
const topKCeiling = wantsHeavyPayload ? MAX_TOP_K_WITH_VALUES : MAX_TOP_K;
|
|
46
|
-
if (input.topK !== void 0 && (!Number.isInteger(input.topK) || input.topK < 1 || input.topK > topKCeiling)) {
|
|
47
|
-
const reason = wantsHeavyPayload ? ' (lowered to 20 because returnValues/returnMetadata:"all" is set)' : "";
|
|
48
|
-
throw new RangeError(`@lunora/bindings/vectors: topK must be an integer in [1, ${String(topKCeiling)}]${reason} (got ${String(input.topK)})`);
|
|
49
|
-
}
|
|
50
|
-
let values;
|
|
51
|
-
if (input.vector && input.vector.length > 0) {
|
|
52
|
-
values = input.vector;
|
|
53
|
-
} else {
|
|
54
|
-
if (!input.embed || input.input === void 0) {
|
|
55
|
-
throw new Error("@lunora/bindings/vectors: query requires either `vector` or both `input` and `embed`");
|
|
56
|
-
}
|
|
57
|
-
values = await input.embed(input.input);
|
|
58
|
-
}
|
|
59
|
-
return index.query(values, {
|
|
60
|
-
filter: input.filter,
|
|
61
|
-
namespace: input.namespace,
|
|
62
|
-
returnMetadata: input.returnMetadata,
|
|
63
|
-
returnValues: input.returnValues,
|
|
64
|
-
topK: input.topK
|
|
65
|
-
});
|
|
66
|
-
};
|
|
67
|
-
const getByIds = async (indexName, ids) => {
|
|
68
|
-
const index = resolveIndex(options.indexes, indexName);
|
|
69
|
-
if (ids.length > MAX_ID_BATCH) {
|
|
70
|
-
throw new RangeError(`@lunora/bindings/vectors: getByIds accepts at most ${String(MAX_ID_BATCH)} ids (got ${String(ids.length)})`);
|
|
71
|
-
}
|
|
72
|
-
return index.getByIds(ids);
|
|
73
|
-
};
|
|
74
|
-
const deleteByIds = async (indexName, ids) => {
|
|
75
|
-
const index = resolveIndex(options.indexes, indexName);
|
|
76
|
-
if (ids.length > MAX_ID_BATCH) {
|
|
77
|
-
throw new RangeError(`@lunora/bindings/vectors: deleteByIds accepts at most ${String(MAX_ID_BATCH)} ids (got ${String(ids.length)})`);
|
|
78
|
-
}
|
|
79
|
-
return index.deleteByIds(ids);
|
|
80
|
-
};
|
|
81
|
-
const describe = async (indexName) => {
|
|
82
|
-
const index = resolveIndex(options.indexes, indexName);
|
|
83
|
-
if (!index.describe) {
|
|
84
|
-
throw new Error(`@lunora/bindings/vectors: binding for "${indexName}" does not implement describe()`);
|
|
85
|
-
}
|
|
86
|
-
return index.describe();
|
|
87
|
-
};
|
|
88
|
-
return { deleteByIds, describe, getByIds, query, upsert, upsertMany };
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
export { createVectors as default };
|