@lunora/bindings 1.0.0-alpha.2 → 1.0.0-alpha.3

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.
@@ -174,4 +174,98 @@ interface Kv {
174
174
  */
175
175
  declare const scopeKey: (prefix: string, key: string) => string;
176
176
  declare const createKv: (options: LunoraKvOptions) => Kv;
177
- export { type KVNamespaceLike, type Kv, type KvGetOptions, type KvListKey, type KvListOptions, type KvListResult, type KvNamespaceListResult, type KvNamespacePutOptions, type KvPutOptions, type KvValue, type KvValueType, type KvValueWithMetadata, type LunoraKvOptions, createKv, scopeKey };
177
+ /** One KV namespace as the studio's KV browser surfaces it (mirrors the runtime's KvNamespaceSummary). */
178
+ interface KvNamespaceSummaryLike {
179
+ binding: string;
180
+ }
181
+ /** One key entry as surfaced by the KV admin browser (mirrors the runtime's KvKeyEntry). */
182
+ interface KvKeyEntryLike {
183
+ expiration?: number;
184
+ metadata?: unknown;
185
+ name: string;
186
+ }
187
+ /** A paginated page of KV keys (mirrors the runtime's KvKeyListResult). */
188
+ interface KvKeyListResultLike {
189
+ cursor?: string;
190
+ keys: KvKeyEntryLike[];
191
+ listComplete: boolean;
192
+ }
193
+ /** A value together with its metadata (mirrors the runtime's KvValueResult). */
194
+ interface KvValueResultLike {
195
+ metadata: unknown;
196
+ value: null | string;
197
+ }
198
+ /**
199
+ * The structural shape of a KV introspector — mirrors `KvIntrospector` from
200
+ * `@lunora/runtime` without importing it, keeping the dependency graph clean.
201
+ */
202
+ interface KvIntrospectorLike {
203
+ deleteKey: (options: {
204
+ key: string;
205
+ namespace: string;
206
+ }) => Promise<void>;
207
+ getValue: (options: {
208
+ key: string;
209
+ namespace: string;
210
+ }) => Promise<KvValueResultLike>;
211
+ listKeys: (options: {
212
+ cursor?: string;
213
+ limit?: number;
214
+ namespace: string;
215
+ prefix?: string;
216
+ }) => Promise<KvKeyListResultLike>;
217
+ listNamespaces: () => Promise<KvNamespaceSummaryLike[]>;
218
+ putValue: (options: {
219
+ expiration?: number;
220
+ expirationTtl?: number;
221
+ key: string;
222
+ metadata?: unknown;
223
+ namespace: string;
224
+ value: string;
225
+ }) => Promise<void>;
226
+ }
227
+ /** Construction options for {@link createKvIntrospector}. */
228
+ interface CreateKvIntrospectorOptions {
229
+ /**
230
+ * Map of binding name → bound KV namespace. Each entry becomes one
231
+ * namespace the studio can browse. Example:
232
+ * ```ts
233
+ * createKvIntrospector({ namespaces: { MY_KV: env.MY_KV } })
234
+ * ```
235
+ */
236
+ namespaces: Record<string, KVNamespaceLike>;
237
+ }
238
+ /**
239
+ * Build a `KvIntrospector`-compatible object from a map of bound KV namespaces.
240
+ * Pass the result as `kvIntrospector` on `WorkerOptions` to enable the studio's
241
+ * KV browser (`/_lunora/admin/kv/*` endpoints).
242
+ * @example
243
+ * ```ts
244
+ * createWorker({
245
+ * // …
246
+ * kvIntrospector: createKvIntrospector({ namespaces: { MY_KV: env.MY_KV } }),
247
+ * });
248
+ * ```
249
+ */
250
+ declare const createKvIntrospector: (options: CreateKvIntrospectorOptions) => KvIntrospectorLike;
251
+ /**
252
+ * Zero-config KV introspector: scan a worker `env` for every bound Workers KV
253
+ * namespace and register each under its binding name. Every `kv_namespaces` entry
254
+ * in `wrangler.jsonc` then appears in the studio's KV browser automatically — no
255
+ * hand-written `createKvIntrospector({ namespaces: … })` call, and any binding
256
+ * name (not just `KV`) and any number of namespaces light up. Non-KV bindings
257
+ * (R2, Durable Objects, queues, secrets, …) are skipped via {@link isKvNamespace}.
258
+ *
259
+ * Returns an introspector even when `env` holds no KV namespaces — its
260
+ * `listNamespaces()` resolves to `[]`, so the studio renders an empty state
261
+ * rather than a "not configured" error.
262
+ * @example
263
+ * ```ts
264
+ * createWorker({
265
+ * // …
266
+ * kvIntrospector: createKvIntrospectorFromEnv(env),
267
+ * });
268
+ * ```
269
+ */
270
+ declare const createKvIntrospectorFromEnv: (env: unknown) => KvIntrospectorLike;
271
+ export { type CreateKvIntrospectorOptions, type KVNamespaceLike, type Kv, type KvGetOptions, type KvIntrospectorLike, type KvListKey, type KvListOptions, type KvListResult, type KvNamespaceListResult, type KvNamespacePutOptions, type KvPutOptions, type KvValue, type KvValueType, type KvValueWithMetadata, type LunoraKvOptions, createKv, createKvIntrospector, createKvIntrospectorFromEnv, scopeKey };
@@ -174,4 +174,98 @@ interface Kv {
174
174
  */
175
175
  declare const scopeKey: (prefix: string, key: string) => string;
176
176
  declare const createKv: (options: LunoraKvOptions) => Kv;
177
- export { type KVNamespaceLike, type Kv, type KvGetOptions, type KvListKey, type KvListOptions, type KvListResult, type KvNamespaceListResult, type KvNamespacePutOptions, type KvPutOptions, type KvValue, type KvValueType, type KvValueWithMetadata, type LunoraKvOptions, createKv, scopeKey };
177
+ /** One KV namespace as the studio's KV browser surfaces it (mirrors the runtime's KvNamespaceSummary). */
178
+ interface KvNamespaceSummaryLike {
179
+ binding: string;
180
+ }
181
+ /** One key entry as surfaced by the KV admin browser (mirrors the runtime's KvKeyEntry). */
182
+ interface KvKeyEntryLike {
183
+ expiration?: number;
184
+ metadata?: unknown;
185
+ name: string;
186
+ }
187
+ /** A paginated page of KV keys (mirrors the runtime's KvKeyListResult). */
188
+ interface KvKeyListResultLike {
189
+ cursor?: string;
190
+ keys: KvKeyEntryLike[];
191
+ listComplete: boolean;
192
+ }
193
+ /** A value together with its metadata (mirrors the runtime's KvValueResult). */
194
+ interface KvValueResultLike {
195
+ metadata: unknown;
196
+ value: null | string;
197
+ }
198
+ /**
199
+ * The structural shape of a KV introspector — mirrors `KvIntrospector` from
200
+ * `@lunora/runtime` without importing it, keeping the dependency graph clean.
201
+ */
202
+ interface KvIntrospectorLike {
203
+ deleteKey: (options: {
204
+ key: string;
205
+ namespace: string;
206
+ }) => Promise<void>;
207
+ getValue: (options: {
208
+ key: string;
209
+ namespace: string;
210
+ }) => Promise<KvValueResultLike>;
211
+ listKeys: (options: {
212
+ cursor?: string;
213
+ limit?: number;
214
+ namespace: string;
215
+ prefix?: string;
216
+ }) => Promise<KvKeyListResultLike>;
217
+ listNamespaces: () => Promise<KvNamespaceSummaryLike[]>;
218
+ putValue: (options: {
219
+ expiration?: number;
220
+ expirationTtl?: number;
221
+ key: string;
222
+ metadata?: unknown;
223
+ namespace: string;
224
+ value: string;
225
+ }) => Promise<void>;
226
+ }
227
+ /** Construction options for {@link createKvIntrospector}. */
228
+ interface CreateKvIntrospectorOptions {
229
+ /**
230
+ * Map of binding name → bound KV namespace. Each entry becomes one
231
+ * namespace the studio can browse. Example:
232
+ * ```ts
233
+ * createKvIntrospector({ namespaces: { MY_KV: env.MY_KV } })
234
+ * ```
235
+ */
236
+ namespaces: Record<string, KVNamespaceLike>;
237
+ }
238
+ /**
239
+ * Build a `KvIntrospector`-compatible object from a map of bound KV namespaces.
240
+ * Pass the result as `kvIntrospector` on `WorkerOptions` to enable the studio's
241
+ * KV browser (`/_lunora/admin/kv/*` endpoints).
242
+ * @example
243
+ * ```ts
244
+ * createWorker({
245
+ * // …
246
+ * kvIntrospector: createKvIntrospector({ namespaces: { MY_KV: env.MY_KV } }),
247
+ * });
248
+ * ```
249
+ */
250
+ declare const createKvIntrospector: (options: CreateKvIntrospectorOptions) => KvIntrospectorLike;
251
+ /**
252
+ * Zero-config KV introspector: scan a worker `env` for every bound Workers KV
253
+ * namespace and register each under its binding name. Every `kv_namespaces` entry
254
+ * in `wrangler.jsonc` then appears in the studio's KV browser automatically — no
255
+ * hand-written `createKvIntrospector({ namespaces: … })` call, and any binding
256
+ * name (not just `KV`) and any number of namespaces light up. Non-KV bindings
257
+ * (R2, Durable Objects, queues, secrets, …) are skipped via {@link isKvNamespace}.
258
+ *
259
+ * Returns an introspector even when `env` holds no KV namespaces — its
260
+ * `listNamespaces()` resolves to `[]`, so the studio renders an empty state
261
+ * rather than a "not configured" error.
262
+ * @example
263
+ * ```ts
264
+ * createWorker({
265
+ * // …
266
+ * kvIntrospector: createKvIntrospectorFromEnv(env),
267
+ * });
268
+ * ```
269
+ */
270
+ declare const createKvIntrospectorFromEnv: (env: unknown) => KvIntrospectorLike;
271
+ export { type CreateKvIntrospectorOptions, type KVNamespaceLike, type Kv, type KvGetOptions, type KvIntrospectorLike, type KvListKey, type KvListOptions, type KvListResult, type KvNamespaceListResult, type KvNamespacePutOptions, type KvPutOptions, type KvValue, type KvValueType, type KvValueWithMetadata, type LunoraKvOptions, createKv, createKvIntrospector, createKvIntrospectorFromEnv, scopeKey };
package/dist/kv/index.mjs CHANGED
@@ -1 +1,2 @@
1
1
  export { createKv, scopeKey } from '../packem_shared/createKv-DTiSt216.mjs';
2
+ export { createKvIntrospector, createKvIntrospectorFromEnv } from '../packem_shared/createKvIntrospector-BThrJVhP.mjs';
@@ -0,0 +1,75 @@
1
+ const createKvIntrospector = (options) => {
2
+ const { namespaces } = options;
3
+ const resolveNamespace = (binding) => {
4
+ const ns = namespaces[binding];
5
+ if (ns === void 0) {
6
+ throw new Error(`@lunora/bindings/kv: no namespace registered under binding "${binding}"`);
7
+ }
8
+ return ns;
9
+ };
10
+ const listNamespaces = () => Promise.resolve(
11
+ Object.keys(namespaces).map((binding) => {
12
+ return { binding };
13
+ })
14
+ );
15
+ const listKeys = async (listOptions) => {
16
+ const ns = resolveNamespace(listOptions.namespace);
17
+ const result = await ns.list({ cursor: listOptions.cursor, limit: listOptions.limit, prefix: listOptions.prefix });
18
+ const keys = result.keys.map((entry) => {
19
+ return {
20
+ expiration: entry.expiration,
21
+ metadata: entry.metadata,
22
+ name: entry.name
23
+ };
24
+ });
25
+ return {
26
+ cursor: result.list_complete ? void 0 : result.cursor,
27
+ keys,
28
+ listComplete: result.list_complete
29
+ };
30
+ };
31
+ const getValue = async (getOptions) => {
32
+ const ns = resolveNamespace(getOptions.namespace);
33
+ const result = await ns.getWithMetadata(getOptions.key, "text");
34
+ return { metadata: result.metadata ?? null, value: result.value ?? null };
35
+ };
36
+ const putValue = async (putOptions) => {
37
+ const ns = resolveNamespace(putOptions.namespace);
38
+ await ns.put(putOptions.key, putOptions.value, {
39
+ expiration: putOptions.expiration,
40
+ expirationTtl: putOptions.expirationTtl,
41
+ metadata: putOptions.metadata
42
+ });
43
+ };
44
+ const deleteKey = async (deleteOptions) => {
45
+ const ns = resolveNamespace(deleteOptions.namespace);
46
+ await ns.delete(deleteOptions.key);
47
+ };
48
+ return {
49
+ deleteKey,
50
+ getValue,
51
+ listKeys,
52
+ listNamespaces,
53
+ putValue
54
+ };
55
+ };
56
+ const isKvNamespace = (value) => {
57
+ if (typeof value !== "object" || value === null) {
58
+ return false;
59
+ }
60
+ const candidate = value;
61
+ return typeof candidate.getWithMetadata === "function" && typeof candidate.list === "function" && typeof candidate.put === "function" && typeof candidate.delete === "function";
62
+ };
63
+ const createKvIntrospectorFromEnv = (env) => {
64
+ const namespaces = {};
65
+ if (typeof env === "object" && env !== null) {
66
+ for (const [binding, value] of Object.entries(env)) {
67
+ if (isKvNamespace(value)) {
68
+ namespaces[binding] = value;
69
+ }
70
+ }
71
+ }
72
+ return createKvIntrospector({ namespaces });
73
+ };
74
+
75
+ export { createKvIntrospector, createKvIntrospectorFromEnv };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/bindings",
3
- "version": "1.0.0-alpha.2",
3
+ "version": "1.0.0-alpha.3",
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",