@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
package/dist/kv/index.d.mts
CHANGED
|
@@ -1,92 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* `KVNamespace` `get`/`put` body unions; declared here so the package stays
|
|
4
|
-
* runtime-agnostic and the `*Like` interfaces don't pull in
|
|
5
|
-
* `@cloudflare/workers-types` at runtime.
|
|
6
|
-
*/
|
|
7
|
-
type KvValue = ReadableStream | ArrayBuffer | ArrayBufferView | string;
|
|
8
|
-
/** How a raw KV read should decode the stored value. Mirrors KV's `type` option. */
|
|
9
|
-
type KvValueType = "text" | "json" | "arrayBuffer" | "stream";
|
|
10
|
-
/**
|
|
11
|
-
* Per-read options forwarded to the binding. `cacheTtl` is KV's edge-cache TTL
|
|
12
|
-
* (seconds, min 60); `type` selects the decode mode for {@link Kv.getRaw}.
|
|
13
|
-
*/
|
|
14
|
-
interface KvGetOptions {
|
|
15
|
-
/** KV edge-cache TTL in seconds (minimum 60). Forwarded verbatim. */
|
|
16
|
-
cacheTtl?: number;
|
|
17
|
-
/** Decode mode for a raw read. {@link Kv.get} always uses `"json"`. */
|
|
18
|
-
type?: KvValueType;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Minimal projection of Cloudflare's `KVNamespace`. Declared structurally so
|
|
22
|
-
* unit tests can pass a plain `Map`-backed double; the real binding satisfies
|
|
23
|
-
* the same shape. Mirrors `R2BucketLike` in `@lunora/storage`.
|
|
24
|
-
*/
|
|
25
|
-
interface KVNamespaceLike {
|
|
26
|
-
/** Delete a key. No-op if the key is absent. */
|
|
27
|
-
delete: (key: string) => Promise<void>;
|
|
28
|
-
/**
|
|
29
|
-
* Read a value. The real binding overloads on `options.type`; declared here
|
|
30
|
-
* as the broad union so a structural double need only return the value (or
|
|
31
|
-
* `null` when absent).
|
|
32
|
-
*/
|
|
33
|
-
get: (key: string, options?: KvGetOptions | KvValueType) => Promise<unknown>;
|
|
34
|
-
/**
|
|
35
|
-
* Read a value together with its associated metadata. Returns
|
|
36
|
-
* `{ value: null, metadata: null }` when the key is absent.
|
|
37
|
-
*/
|
|
38
|
-
getWithMetadata: (key: string, options?: KvGetOptions | KvValueType) => Promise<{
|
|
39
|
-
metadata: unknown;
|
|
40
|
-
value: unknown;
|
|
41
|
-
}>;
|
|
42
|
-
/** List keys, optionally filtered by `prefix` and paginated via `cursor`. */
|
|
43
|
-
list: (options?: {
|
|
44
|
-
cursor?: string;
|
|
45
|
-
limit?: number;
|
|
46
|
-
prefix?: string;
|
|
47
|
-
}) => Promise<KvNamespaceListResult>;
|
|
48
|
-
/** Write a value, optionally with TTL/expiration and metadata. */
|
|
49
|
-
put: (key: string, value: KvValue, options?: KvNamespacePutOptions) => Promise<void>;
|
|
50
|
-
}
|
|
51
|
-
/** The raw put options the KV binding accepts (mirrors `KVNamespacePutOptions`). */
|
|
52
|
-
interface KvNamespacePutOptions {
|
|
53
|
-
/** Absolute expiration as a Unix timestamp (seconds). Mutually exclusive with `expirationTtl`. */
|
|
54
|
-
expiration?: number;
|
|
55
|
-
/** Relative expiration in seconds from now (minimum 60). Mutually exclusive with `expiration`. */
|
|
56
|
-
expirationTtl?: number;
|
|
57
|
-
/** Arbitrary JSON metadata stored alongside the value, returned by `getWithMetadata`/`list`. */
|
|
58
|
-
metadata?: unknown;
|
|
59
|
-
}
|
|
60
|
-
/** One key entry as returned by the KV binding's `list`. */
|
|
61
|
-
interface KvListKey<Metadata = unknown> {
|
|
62
|
-
/** Absolute expiration (Unix seconds), when the key has one. */
|
|
63
|
-
expiration?: number;
|
|
64
|
-
/** The key's metadata, when set at write time. */
|
|
65
|
-
metadata?: Metadata;
|
|
66
|
-
/** The key name. */
|
|
67
|
-
name: string;
|
|
68
|
-
}
|
|
69
|
-
/** The raw `list` result shape returned by the KV binding. */
|
|
70
|
-
type KvNamespaceListResult<Metadata = unknown> = {
|
|
71
|
-
cacheStatus?: string | null;
|
|
72
|
-
cursor: string;
|
|
73
|
-
keys: KvListKey<Metadata>[];
|
|
74
|
-
list_complete: false;
|
|
75
|
-
} | {
|
|
76
|
-
cacheStatus?: string | null;
|
|
77
|
-
keys: KvListKey<Metadata>[];
|
|
78
|
-
list_complete: true;
|
|
79
|
-
};
|
|
1
|
+
import { KvGetOptions, KvListKey, KVNamespaceLike } from '@lunora/platform';
|
|
2
|
+
export type { KVNamespaceLike, KvGetOptions, KvListKey, KvNamespaceListResult, KvNamespacePutOptions, KvValue, KvValueType } from '@lunora/platform';
|
|
80
3
|
/** Construction options for the `createKv` factory. */
|
|
81
4
|
interface LunoraKvOptions {
|
|
82
5
|
/**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
6
|
+
* Optional per-instance key prefix applied to every operation (get/put/
|
|
7
|
+
* delete/list). Use for multi-tenant key namespacing — equivalent to
|
|
8
|
+
* calling the `scopeKey` helper on every key. Combined via `scopeKey`, so a
|
|
9
|
+
* `..` or NUL in the prefix is rejected.
|
|
10
|
+
*/
|
|
88
11
|
keyPrefix?: string;
|
|
89
|
-
/** The bound KV namespace (`env
|
|
12
|
+
/** The bound KV namespace (`env.<BINDING>`). */
|
|
90
13
|
namespace: KVNamespaceLike;
|
|
91
14
|
}
|
|
92
15
|
/** Options for {@link Kv.put}. JSON-stringifies the value unless `raw` is set. */
|
|
@@ -98,9 +21,9 @@ interface KvPutOptions {
|
|
|
98
21
|
/** Arbitrary metadata stored alongside the value (returned by `getWithMetadata`/`list`). */
|
|
99
22
|
metadata?: unknown;
|
|
100
23
|
/**
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
24
|
+
* When true, write `value` to KV verbatim (no `JSON.stringify`). `value`
|
|
25
|
+
* must already be a KV-writable type (string/ArrayBuffer/stream).
|
|
26
|
+
*/
|
|
104
27
|
raw?: boolean;
|
|
105
28
|
}
|
|
106
29
|
/** Options for {@link Kv.list}. */
|
|
@@ -117,9 +40,9 @@ interface KvListResult<Metadata = unknown> {
|
|
|
117
40
|
/** Cursor for the next page; `undefined` when the listing is complete. */
|
|
118
41
|
cursor?: string;
|
|
119
42
|
/**
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
43
|
+
* The key names, with any instance `keyPrefix` stripped back off so callers
|
|
44
|
+
* see the same keys they wrote.
|
|
45
|
+
*/
|
|
123
46
|
keys: KvListKey<Metadata>[];
|
|
124
47
|
/** True when this is the final page (no further `cursor`). */
|
|
125
48
|
listComplete: boolean;
|
|
@@ -132,46 +55,140 @@ interface KvValueWithMetadata<Value, Metadata> {
|
|
|
132
55
|
value: Value | null;
|
|
133
56
|
}
|
|
134
57
|
/**
|
|
135
|
-
* The typed Workers KV client bound to `ctx.kv`. JSON-decodes/encodes by
|
|
136
|
-
* default; a raw escape hatch ({@link Kv.getRaw} / `put(..., { raw: true })`)
|
|
137
|
-
* handles text/binary/stream values.
|
|
138
|
-
*/
|
|
58
|
+
* The typed Workers KV client bound to `ctx.kv`. JSON-decodes/encodes by
|
|
59
|
+
* default; a raw escape hatch ({@link Kv.getRaw} / `put(..., { raw: true })`)
|
|
60
|
+
* handles text/binary/stream values.
|
|
61
|
+
*/
|
|
139
62
|
interface Kv {
|
|
140
63
|
/** Delete a key. No-op if absent. */
|
|
141
64
|
delete: (key: string) => Promise<void>;
|
|
142
65
|
/**
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
66
|
+
* Read a key and `JSON.parse` it into `T`. Returns `null` when the key is
|
|
67
|
+
* absent. Throws if the stored value isn't valid JSON — use
|
|
68
|
+
* {@link Kv.getRaw} for non-JSON values.
|
|
69
|
+
*/
|
|
147
70
|
get: <T = unknown>(key: string, options?: {
|
|
148
71
|
cacheTtl?: number;
|
|
149
72
|
}) => Promise<T | null>;
|
|
150
73
|
/** Read a raw value with an explicit decode `type` (default `"text"`). Returns `null` when absent. */
|
|
151
74
|
getRaw: <T = string>(key: string, options?: KvGetOptions) => Promise<T | null>;
|
|
152
75
|
/**
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
76
|
+
* Read a key's JSON value together with its metadata. Returns
|
|
77
|
+
* `{ value: null, metadata: null }` when the key is absent.
|
|
78
|
+
*/
|
|
156
79
|
getWithMetadata: <T = unknown, M = unknown>(key: string, options?: {
|
|
157
80
|
cacheTtl?: number;
|
|
158
81
|
}) => Promise<KvValueWithMetadata<T, M>>;
|
|
159
82
|
/** List keys (optionally `prefix`-filtered, paginated via `cursor`). */
|
|
160
83
|
list: <M = unknown>(options?: KvListOptions) => Promise<KvListResult<M>>;
|
|
161
84
|
/**
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
85
|
+
* Write `value` to `key`. JSON-stringifies `value` unless `options.raw` is
|
|
86
|
+
* set (in which case `value` must be a KV-writable type). Forwards
|
|
87
|
+
* `expirationTtl`/`expiration`/`metadata` to the binding.
|
|
88
|
+
*/
|
|
166
89
|
put: <T = unknown>(key: string, value: T, options?: KvPutOptions) => Promise<void>;
|
|
167
90
|
}
|
|
168
91
|
/**
|
|
169
|
-
* Compose a per-tenant key from a scope prefix and a caller-supplied key. Both
|
|
170
|
-
* halves are validated — the prefix may not contain `..` or NUL either, and the
|
|
171
|
-
* resulting key must stay under KV's length ceiling. Recommended for any
|
|
172
|
-
* multi-tenant deployment so client-supplied keys can't address peer data.
|
|
173
|
-
* Mirrors `scopeKey` from `@lunora/storage`.
|
|
174
|
-
*/
|
|
92
|
+
* Compose a per-tenant key from a scope prefix and a caller-supplied key. Both
|
|
93
|
+
* halves are validated — the prefix may not contain `..` or NUL either, and the
|
|
94
|
+
* resulting key must stay under KV's length ceiling. Recommended for any
|
|
95
|
+
* multi-tenant deployment so client-supplied keys can't address peer data.
|
|
96
|
+
* Mirrors `scopeKey` from `@lunora/storage`.
|
|
97
|
+
*/
|
|
175
98
|
declare const scopeKey: (prefix: string, key: string) => string;
|
|
176
99
|
declare const createKv: (options: LunoraKvOptions) => Kv;
|
|
177
|
-
|
|
100
|
+
/** One KV namespace as the studio's KV browser surfaces it (mirrors the runtime's KvNamespaceSummary). */
|
|
101
|
+
interface KvNamespaceSummaryLike {
|
|
102
|
+
binding: string;
|
|
103
|
+
}
|
|
104
|
+
/** One key entry as surfaced by the KV admin browser (mirrors the runtime's KvKeyEntry). */
|
|
105
|
+
interface KvKeyEntryLike {
|
|
106
|
+
expiration?: number;
|
|
107
|
+
metadata?: unknown;
|
|
108
|
+
name: string;
|
|
109
|
+
}
|
|
110
|
+
/** A paginated page of KV keys (mirrors the runtime's KvKeyListResult). */
|
|
111
|
+
interface KvKeyListResultLike {
|
|
112
|
+
cursor?: string;
|
|
113
|
+
keys: KvKeyEntryLike[];
|
|
114
|
+
listComplete: boolean;
|
|
115
|
+
}
|
|
116
|
+
/** A value together with its metadata (mirrors the runtime's KvValueResult). */
|
|
117
|
+
interface KvValueResultLike {
|
|
118
|
+
metadata: unknown;
|
|
119
|
+
value: null | string;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The structural shape of a KV introspector — mirrors `KvIntrospector` from
|
|
123
|
+
* `@lunora/runtime` without importing it, keeping the dependency graph clean.
|
|
124
|
+
*/
|
|
125
|
+
interface KvIntrospectorLike {
|
|
126
|
+
deleteKey: (options: {
|
|
127
|
+
key: string;
|
|
128
|
+
namespace: string;
|
|
129
|
+
}) => Promise<void>;
|
|
130
|
+
getValue: (options: {
|
|
131
|
+
key: string;
|
|
132
|
+
namespace: string;
|
|
133
|
+
}) => Promise<KvValueResultLike>;
|
|
134
|
+
listKeys: (options: {
|
|
135
|
+
cursor?: string;
|
|
136
|
+
limit?: number;
|
|
137
|
+
namespace: string;
|
|
138
|
+
prefix?: string;
|
|
139
|
+
}) => Promise<KvKeyListResultLike>;
|
|
140
|
+
listNamespaces: () => Promise<KvNamespaceSummaryLike[]>;
|
|
141
|
+
putValue: (options: {
|
|
142
|
+
expiration?: number;
|
|
143
|
+
expirationTtl?: number;
|
|
144
|
+
key: string;
|
|
145
|
+
metadata?: unknown;
|
|
146
|
+
namespace: string;
|
|
147
|
+
value: string;
|
|
148
|
+
}) => Promise<void>;
|
|
149
|
+
}
|
|
150
|
+
/** Construction options for {@link createKvIntrospector}. */
|
|
151
|
+
interface CreateKvIntrospectorOptions {
|
|
152
|
+
/**
|
|
153
|
+
* Map of binding name → bound KV namespace. Each entry becomes one
|
|
154
|
+
* namespace the studio can browse. Example:
|
|
155
|
+
* ```ts
|
|
156
|
+
* createKvIntrospector({ namespaces: { MY_KV: env.MY_KV } })
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
namespaces: Record<string, KVNamespaceLike>;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Build a `KvIntrospector`-compatible object from a map of bound KV namespaces.
|
|
163
|
+
* Pass the result as `kvIntrospector` on `WorkerOptions` to enable the studio's
|
|
164
|
+
* KV browser (`/_lunora/admin/kv/*` endpoints).
|
|
165
|
+
* @example
|
|
166
|
+
* ```ts
|
|
167
|
+
* createWorker({
|
|
168
|
+
* // …
|
|
169
|
+
* kvIntrospector: createKvIntrospector({ namespaces: { MY_KV: env.MY_KV } }),
|
|
170
|
+
* });
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
declare const createKvIntrospector: (options: CreateKvIntrospectorOptions) => KvIntrospectorLike;
|
|
174
|
+
/**
|
|
175
|
+
* Zero-config KV introspector: scan a worker `env` for every bound Workers KV
|
|
176
|
+
* namespace and register each under its binding name. Every `kv_namespaces` entry
|
|
177
|
+
* in `wrangler.jsonc` then appears in the studio's KV browser automatically — no
|
|
178
|
+
* hand-written `createKvIntrospector({ namespaces: … })` call, and any binding
|
|
179
|
+
* name (not just `KV`) and any number of namespaces light up. Non-KV bindings
|
|
180
|
+
* (R2, Durable Objects, queues, secrets, …) are skipped via {@link isKvNamespace}.
|
|
181
|
+
*
|
|
182
|
+
* Returns an introspector even when `env` holds no KV namespaces — its
|
|
183
|
+
* `listNamespaces()` resolves to `[]`, so the studio renders an empty state
|
|
184
|
+
* rather than a "not configured" error.
|
|
185
|
+
* @example
|
|
186
|
+
* ```ts
|
|
187
|
+
* createWorker({
|
|
188
|
+
* // …
|
|
189
|
+
* kvIntrospector: createKvIntrospectorFromEnv(env),
|
|
190
|
+
* });
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
declare const createKvIntrospectorFromEnv: (env: unknown) => KvIntrospectorLike;
|
|
194
|
+
export { type CreateKvIntrospectorOptions, type Kv, type KvIntrospectorLike, type KvListOptions, type KvListResult, type KvPutOptions, type KvValueWithMetadata, type LunoraKvOptions, createKv, createKvIntrospector, createKvIntrospectorFromEnv, scopeKey };
|
package/dist/kv/index.d.ts
CHANGED
|
@@ -1,92 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* `KVNamespace` `get`/`put` body unions; declared here so the package stays
|
|
4
|
-
* runtime-agnostic and the `*Like` interfaces don't pull in
|
|
5
|
-
* `@cloudflare/workers-types` at runtime.
|
|
6
|
-
*/
|
|
7
|
-
type KvValue = ReadableStream | ArrayBuffer | ArrayBufferView | string;
|
|
8
|
-
/** How a raw KV read should decode the stored value. Mirrors KV's `type` option. */
|
|
9
|
-
type KvValueType = "text" | "json" | "arrayBuffer" | "stream";
|
|
10
|
-
/**
|
|
11
|
-
* Per-read options forwarded to the binding. `cacheTtl` is KV's edge-cache TTL
|
|
12
|
-
* (seconds, min 60); `type` selects the decode mode for {@link Kv.getRaw}.
|
|
13
|
-
*/
|
|
14
|
-
interface KvGetOptions {
|
|
15
|
-
/** KV edge-cache TTL in seconds (minimum 60). Forwarded verbatim. */
|
|
16
|
-
cacheTtl?: number;
|
|
17
|
-
/** Decode mode for a raw read. {@link Kv.get} always uses `"json"`. */
|
|
18
|
-
type?: KvValueType;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Minimal projection of Cloudflare's `KVNamespace`. Declared structurally so
|
|
22
|
-
* unit tests can pass a plain `Map`-backed double; the real binding satisfies
|
|
23
|
-
* the same shape. Mirrors `R2BucketLike` in `@lunora/storage`.
|
|
24
|
-
*/
|
|
25
|
-
interface KVNamespaceLike {
|
|
26
|
-
/** Delete a key. No-op if the key is absent. */
|
|
27
|
-
delete: (key: string) => Promise<void>;
|
|
28
|
-
/**
|
|
29
|
-
* Read a value. The real binding overloads on `options.type`; declared here
|
|
30
|
-
* as the broad union so a structural double need only return the value (or
|
|
31
|
-
* `null` when absent).
|
|
32
|
-
*/
|
|
33
|
-
get: (key: string, options?: KvGetOptions | KvValueType) => Promise<unknown>;
|
|
34
|
-
/**
|
|
35
|
-
* Read a value together with its associated metadata. Returns
|
|
36
|
-
* `{ value: null, metadata: null }` when the key is absent.
|
|
37
|
-
*/
|
|
38
|
-
getWithMetadata: (key: string, options?: KvGetOptions | KvValueType) => Promise<{
|
|
39
|
-
metadata: unknown;
|
|
40
|
-
value: unknown;
|
|
41
|
-
}>;
|
|
42
|
-
/** List keys, optionally filtered by `prefix` and paginated via `cursor`. */
|
|
43
|
-
list: (options?: {
|
|
44
|
-
cursor?: string;
|
|
45
|
-
limit?: number;
|
|
46
|
-
prefix?: string;
|
|
47
|
-
}) => Promise<KvNamespaceListResult>;
|
|
48
|
-
/** Write a value, optionally with TTL/expiration and metadata. */
|
|
49
|
-
put: (key: string, value: KvValue, options?: KvNamespacePutOptions) => Promise<void>;
|
|
50
|
-
}
|
|
51
|
-
/** The raw put options the KV binding accepts (mirrors `KVNamespacePutOptions`). */
|
|
52
|
-
interface KvNamespacePutOptions {
|
|
53
|
-
/** Absolute expiration as a Unix timestamp (seconds). Mutually exclusive with `expirationTtl`. */
|
|
54
|
-
expiration?: number;
|
|
55
|
-
/** Relative expiration in seconds from now (minimum 60). Mutually exclusive with `expiration`. */
|
|
56
|
-
expirationTtl?: number;
|
|
57
|
-
/** Arbitrary JSON metadata stored alongside the value, returned by `getWithMetadata`/`list`. */
|
|
58
|
-
metadata?: unknown;
|
|
59
|
-
}
|
|
60
|
-
/** One key entry as returned by the KV binding's `list`. */
|
|
61
|
-
interface KvListKey<Metadata = unknown> {
|
|
62
|
-
/** Absolute expiration (Unix seconds), when the key has one. */
|
|
63
|
-
expiration?: number;
|
|
64
|
-
/** The key's metadata, when set at write time. */
|
|
65
|
-
metadata?: Metadata;
|
|
66
|
-
/** The key name. */
|
|
67
|
-
name: string;
|
|
68
|
-
}
|
|
69
|
-
/** The raw `list` result shape returned by the KV binding. */
|
|
70
|
-
type KvNamespaceListResult<Metadata = unknown> = {
|
|
71
|
-
cacheStatus?: string | null;
|
|
72
|
-
cursor: string;
|
|
73
|
-
keys: KvListKey<Metadata>[];
|
|
74
|
-
list_complete: false;
|
|
75
|
-
} | {
|
|
76
|
-
cacheStatus?: string | null;
|
|
77
|
-
keys: KvListKey<Metadata>[];
|
|
78
|
-
list_complete: true;
|
|
79
|
-
};
|
|
1
|
+
import { KvGetOptions, KvListKey, KVNamespaceLike } from '@lunora/platform';
|
|
2
|
+
export type { KVNamespaceLike, KvGetOptions, KvListKey, KvNamespaceListResult, KvNamespacePutOptions, KvValue, KvValueType } from '@lunora/platform';
|
|
80
3
|
/** Construction options for the `createKv` factory. */
|
|
81
4
|
interface LunoraKvOptions {
|
|
82
5
|
/**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
6
|
+
* Optional per-instance key prefix applied to every operation (get/put/
|
|
7
|
+
* delete/list). Use for multi-tenant key namespacing — equivalent to
|
|
8
|
+
* calling the `scopeKey` helper on every key. Combined via `scopeKey`, so a
|
|
9
|
+
* `..` or NUL in the prefix is rejected.
|
|
10
|
+
*/
|
|
88
11
|
keyPrefix?: string;
|
|
89
|
-
/** The bound KV namespace (`env
|
|
12
|
+
/** The bound KV namespace (`env.<BINDING>`). */
|
|
90
13
|
namespace: KVNamespaceLike;
|
|
91
14
|
}
|
|
92
15
|
/** Options for {@link Kv.put}. JSON-stringifies the value unless `raw` is set. */
|
|
@@ -98,9 +21,9 @@ interface KvPutOptions {
|
|
|
98
21
|
/** Arbitrary metadata stored alongside the value (returned by `getWithMetadata`/`list`). */
|
|
99
22
|
metadata?: unknown;
|
|
100
23
|
/**
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
24
|
+
* When true, write `value` to KV verbatim (no `JSON.stringify`). `value`
|
|
25
|
+
* must already be a KV-writable type (string/ArrayBuffer/stream).
|
|
26
|
+
*/
|
|
104
27
|
raw?: boolean;
|
|
105
28
|
}
|
|
106
29
|
/** Options for {@link Kv.list}. */
|
|
@@ -117,9 +40,9 @@ interface KvListResult<Metadata = unknown> {
|
|
|
117
40
|
/** Cursor for the next page; `undefined` when the listing is complete. */
|
|
118
41
|
cursor?: string;
|
|
119
42
|
/**
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
43
|
+
* The key names, with any instance `keyPrefix` stripped back off so callers
|
|
44
|
+
* see the same keys they wrote.
|
|
45
|
+
*/
|
|
123
46
|
keys: KvListKey<Metadata>[];
|
|
124
47
|
/** True when this is the final page (no further `cursor`). */
|
|
125
48
|
listComplete: boolean;
|
|
@@ -132,46 +55,140 @@ interface KvValueWithMetadata<Value, Metadata> {
|
|
|
132
55
|
value: Value | null;
|
|
133
56
|
}
|
|
134
57
|
/**
|
|
135
|
-
* The typed Workers KV client bound to `ctx.kv`. JSON-decodes/encodes by
|
|
136
|
-
* default; a raw escape hatch ({@link Kv.getRaw} / `put(..., { raw: true })`)
|
|
137
|
-
* handles text/binary/stream values.
|
|
138
|
-
*/
|
|
58
|
+
* The typed Workers KV client bound to `ctx.kv`. JSON-decodes/encodes by
|
|
59
|
+
* default; a raw escape hatch ({@link Kv.getRaw} / `put(..., { raw: true })`)
|
|
60
|
+
* handles text/binary/stream values.
|
|
61
|
+
*/
|
|
139
62
|
interface Kv {
|
|
140
63
|
/** Delete a key. No-op if absent. */
|
|
141
64
|
delete: (key: string) => Promise<void>;
|
|
142
65
|
/**
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
66
|
+
* Read a key and `JSON.parse` it into `T`. Returns `null` when the key is
|
|
67
|
+
* absent. Throws if the stored value isn't valid JSON — use
|
|
68
|
+
* {@link Kv.getRaw} for non-JSON values.
|
|
69
|
+
*/
|
|
147
70
|
get: <T = unknown>(key: string, options?: {
|
|
148
71
|
cacheTtl?: number;
|
|
149
72
|
}) => Promise<T | null>;
|
|
150
73
|
/** Read a raw value with an explicit decode `type` (default `"text"`). Returns `null` when absent. */
|
|
151
74
|
getRaw: <T = string>(key: string, options?: KvGetOptions) => Promise<T | null>;
|
|
152
75
|
/**
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
76
|
+
* Read a key's JSON value together with its metadata. Returns
|
|
77
|
+
* `{ value: null, metadata: null }` when the key is absent.
|
|
78
|
+
*/
|
|
156
79
|
getWithMetadata: <T = unknown, M = unknown>(key: string, options?: {
|
|
157
80
|
cacheTtl?: number;
|
|
158
81
|
}) => Promise<KvValueWithMetadata<T, M>>;
|
|
159
82
|
/** List keys (optionally `prefix`-filtered, paginated via `cursor`). */
|
|
160
83
|
list: <M = unknown>(options?: KvListOptions) => Promise<KvListResult<M>>;
|
|
161
84
|
/**
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
85
|
+
* Write `value` to `key`. JSON-stringifies `value` unless `options.raw` is
|
|
86
|
+
* set (in which case `value` must be a KV-writable type). Forwards
|
|
87
|
+
* `expirationTtl`/`expiration`/`metadata` to the binding.
|
|
88
|
+
*/
|
|
166
89
|
put: <T = unknown>(key: string, value: T, options?: KvPutOptions) => Promise<void>;
|
|
167
90
|
}
|
|
168
91
|
/**
|
|
169
|
-
* Compose a per-tenant key from a scope prefix and a caller-supplied key. Both
|
|
170
|
-
* halves are validated — the prefix may not contain `..` or NUL either, and the
|
|
171
|
-
* resulting key must stay under KV's length ceiling. Recommended for any
|
|
172
|
-
* multi-tenant deployment so client-supplied keys can't address peer data.
|
|
173
|
-
* Mirrors `scopeKey` from `@lunora/storage`.
|
|
174
|
-
*/
|
|
92
|
+
* Compose a per-tenant key from a scope prefix and a caller-supplied key. Both
|
|
93
|
+
* halves are validated — the prefix may not contain `..` or NUL either, and the
|
|
94
|
+
* resulting key must stay under KV's length ceiling. Recommended for any
|
|
95
|
+
* multi-tenant deployment so client-supplied keys can't address peer data.
|
|
96
|
+
* Mirrors `scopeKey` from `@lunora/storage`.
|
|
97
|
+
*/
|
|
175
98
|
declare const scopeKey: (prefix: string, key: string) => string;
|
|
176
99
|
declare const createKv: (options: LunoraKvOptions) => Kv;
|
|
177
|
-
|
|
100
|
+
/** One KV namespace as the studio's KV browser surfaces it (mirrors the runtime's KvNamespaceSummary). */
|
|
101
|
+
interface KvNamespaceSummaryLike {
|
|
102
|
+
binding: string;
|
|
103
|
+
}
|
|
104
|
+
/** One key entry as surfaced by the KV admin browser (mirrors the runtime's KvKeyEntry). */
|
|
105
|
+
interface KvKeyEntryLike {
|
|
106
|
+
expiration?: number;
|
|
107
|
+
metadata?: unknown;
|
|
108
|
+
name: string;
|
|
109
|
+
}
|
|
110
|
+
/** A paginated page of KV keys (mirrors the runtime's KvKeyListResult). */
|
|
111
|
+
interface KvKeyListResultLike {
|
|
112
|
+
cursor?: string;
|
|
113
|
+
keys: KvKeyEntryLike[];
|
|
114
|
+
listComplete: boolean;
|
|
115
|
+
}
|
|
116
|
+
/** A value together with its metadata (mirrors the runtime's KvValueResult). */
|
|
117
|
+
interface KvValueResultLike {
|
|
118
|
+
metadata: unknown;
|
|
119
|
+
value: null | string;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The structural shape of a KV introspector — mirrors `KvIntrospector` from
|
|
123
|
+
* `@lunora/runtime` without importing it, keeping the dependency graph clean.
|
|
124
|
+
*/
|
|
125
|
+
interface KvIntrospectorLike {
|
|
126
|
+
deleteKey: (options: {
|
|
127
|
+
key: string;
|
|
128
|
+
namespace: string;
|
|
129
|
+
}) => Promise<void>;
|
|
130
|
+
getValue: (options: {
|
|
131
|
+
key: string;
|
|
132
|
+
namespace: string;
|
|
133
|
+
}) => Promise<KvValueResultLike>;
|
|
134
|
+
listKeys: (options: {
|
|
135
|
+
cursor?: string;
|
|
136
|
+
limit?: number;
|
|
137
|
+
namespace: string;
|
|
138
|
+
prefix?: string;
|
|
139
|
+
}) => Promise<KvKeyListResultLike>;
|
|
140
|
+
listNamespaces: () => Promise<KvNamespaceSummaryLike[]>;
|
|
141
|
+
putValue: (options: {
|
|
142
|
+
expiration?: number;
|
|
143
|
+
expirationTtl?: number;
|
|
144
|
+
key: string;
|
|
145
|
+
metadata?: unknown;
|
|
146
|
+
namespace: string;
|
|
147
|
+
value: string;
|
|
148
|
+
}) => Promise<void>;
|
|
149
|
+
}
|
|
150
|
+
/** Construction options for {@link createKvIntrospector}. */
|
|
151
|
+
interface CreateKvIntrospectorOptions {
|
|
152
|
+
/**
|
|
153
|
+
* Map of binding name → bound KV namespace. Each entry becomes one
|
|
154
|
+
* namespace the studio can browse. Example:
|
|
155
|
+
* ```ts
|
|
156
|
+
* createKvIntrospector({ namespaces: { MY_KV: env.MY_KV } })
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
namespaces: Record<string, KVNamespaceLike>;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Build a `KvIntrospector`-compatible object from a map of bound KV namespaces.
|
|
163
|
+
* Pass the result as `kvIntrospector` on `WorkerOptions` to enable the studio's
|
|
164
|
+
* KV browser (`/_lunora/admin/kv/*` endpoints).
|
|
165
|
+
* @example
|
|
166
|
+
* ```ts
|
|
167
|
+
* createWorker({
|
|
168
|
+
* // …
|
|
169
|
+
* kvIntrospector: createKvIntrospector({ namespaces: { MY_KV: env.MY_KV } }),
|
|
170
|
+
* });
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
declare const createKvIntrospector: (options: CreateKvIntrospectorOptions) => KvIntrospectorLike;
|
|
174
|
+
/**
|
|
175
|
+
* Zero-config KV introspector: scan a worker `env` for every bound Workers KV
|
|
176
|
+
* namespace and register each under its binding name. Every `kv_namespaces` entry
|
|
177
|
+
* in `wrangler.jsonc` then appears in the studio's KV browser automatically — no
|
|
178
|
+
* hand-written `createKvIntrospector({ namespaces: … })` call, and any binding
|
|
179
|
+
* name (not just `KV`) and any number of namespaces light up. Non-KV bindings
|
|
180
|
+
* (R2, Durable Objects, queues, secrets, …) are skipped via {@link isKvNamespace}.
|
|
181
|
+
*
|
|
182
|
+
* Returns an introspector even when `env` holds no KV namespaces — its
|
|
183
|
+
* `listNamespaces()` resolves to `[]`, so the studio renders an empty state
|
|
184
|
+
* rather than a "not configured" error.
|
|
185
|
+
* @example
|
|
186
|
+
* ```ts
|
|
187
|
+
* createWorker({
|
|
188
|
+
* // …
|
|
189
|
+
* kvIntrospector: createKvIntrospectorFromEnv(env),
|
|
190
|
+
* });
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
declare const createKvIntrospectorFromEnv: (env: unknown) => KvIntrospectorLike;
|
|
194
|
+
export { type CreateKvIntrospectorOptions, type Kv, type KvIntrospectorLike, type KvListOptions, type KvListResult, type KvPutOptions, type KvValueWithMetadata, type LunoraKvOptions, createKv, createKvIntrospector, createKvIntrospectorFromEnv, scopeKey };
|
package/dist/kv/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import{createKv as o,scopeKey as t}from"../packem_shared/createKv-CKeFDFuG.mjs";import{createKvIntrospector as p,createKvIntrospectorFromEnv as v}from"../packem_shared/createKvIntrospector-Bz0nqUjD.mjs";export{o as createKv,p as createKvIntrospector,v as createKvIntrospectorFromEnv,t as scopeKey};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as l}from"@lunora/errors";const u="https://api.cloudflare.com/client/v4/accounts";class c extends l{constructor(e,o){super("ANALYTICS_SQL_ERROR",`Analytics Engine SQL API returned ${String(e)}: ${o}`,{name:"AnalyticsSqlError",status:e})}}const d=n=>{const e=n.fetch??globalThis.fetch,o=`${u}/${encodeURIComponent(n.accountId)}/analytics_engine/sql`;return{query:async i=>{const t=await e(o,{body:i,headers:{Authorization:`Bearer ${n.apiToken}`,"Content-Type":"text/plain"},method:"POST"});if(!t.ok)throw new c(t.status,await t.text());let a;try{a=await t.json()}catch{throw new c(t.status,"Analytics Engine SQL API returned a non-JSON body.")}const r=a,s=r.data??[];return{columns:r.meta??[],rowCount:r.rows??s.length,rows:s}}}};export{c as AnalyticsSqlError,d as createAnalyticsSqlClient};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as h}from"@lunora/errors";import m from"./SelectBuilder-WMBQ2b4q.mjs";import{ident as l,toText as S}from"./Sql-CwhZl1Q5.mjs";const p="https://api.sql.cloudflarestorage.com/api/v1/accounts",w=t=>t[0]===void 0?[]:Object.keys(t[0]).map(e=>({name:e}));class i extends h{constructor(e,a){super("R2_SQL_ERROR",`R2 SQL query failed (${String(e)}): ${a}`,{name:"R2SqlError",status:e})}}const $=t=>{const e=t.fetch??globalThis.fetch,a=`${t.endpoint??p}/${encodeURIComponent(t.accountId)}/r2-sql/query/${encodeURIComponent(t.bucket)}`,d=`${t.accountId}_${t.bucket}`,n=async o=>{const r=await e(a,{body:JSON.stringify({query:o,warehouse:d}),headers:{Authorization:`Bearer ${t.apiToken}`,"Content-Type":"application/json"},method:"POST"});if(!r.ok)throw new i(r.status,await r.text());let u;try{u=await r.json()}catch{throw new i(r.status,"R2 SQL returned a non-JSON body.")}const s=u;if(s.success===!1||s.errors!==void 0&&s.errors.length>0)throw new i(r.status,JSON.stringify(s.errors??s));const c=s.result?.rows??[];return{columns:s.result?.schema??w(c),rowCount:c.length,rows:c}};return{describe:async o=>n(`DESCRIBE ${l(o)}`),explain:async(o,r)=>n(`EXPLAIN ${r?.format==="json"?"FORMAT JSON ":""}${S(o)}`),from:o=>new m(n,o),query:async o=>n(S(o)),showDatabases:async()=>n("SHOW DATABASES"),showTables:async o=>n(`SHOW TABLES IN ${l(o)}`)}};export{i as R2SqlError,$ as createR2Sql};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{renderOrderTerm as o}from"./asc-BsbdtIQU.mjs";import h from"./SetOperation-BrzMB1Tb.mjs";import{tableRef as s,toText as e,assertLimit as u,lit as a}from"./Sql-CwhZl1Q5.mjs";const n={cross:"CROSS JOIN",full:"FULL OUTER JOIN",inner:"INNER JOIN",left:"LEFT JOIN",right:"RIGHT JOIN"};class I{exec;table;selectItems=[];distinctFlag=!1;distinctOnItems=[];joins=[];whereConditions=[];groupByItems=[];havingConditions=[];qualifyCondition;orderByItems=[];limitValue;constructor(t,i){this.exec=t,this.table=s(i)}select(...t){return this.selectItems.push(...t.map(i=>e(i))),this}distinct(){return this.distinctFlag=!0,this}distinctOn(...t){return this.distinctOnItems.push(...t.map(i=>e(i))),this}innerJoin(t,i){return this.addJoin("inner",t,i)}leftJoin(t,i){return this.addJoin("left",t,i)}rightJoin(t,i){return this.addJoin("right",t,i)}fullJoin(t,i){return this.addJoin("full",t,i)}crossJoin(t){return this.addJoin("cross",t)}where(...t){return this.whereConditions.push(...t.map(i=>e(i))),this}groupBy(...t){return this.groupByItems.push(...t.map(i=>e(i))),this}having(...t){return this.havingConditions.push(...t.map(i=>e(i))),this}qualify(t){return this.qualifyCondition=e(t),this}orderBy(...t){return this.orderByItems.push(...t.map(i=>o(i))),this}limit(t){return u(t),this.limitValue=t,this}returns(){return this}union(t){return this.setOperation("UNION",t)}unionAll(t){return this.setOperation("UNION ALL",t)}intersect(t){return this.setOperation("INTERSECT",t)}except(t){return this.setOperation("EXCEPT",t)}get needsWrapForSetOperation(){return this.orderByItems.length>0||this.limitValue!==void 0}toSQL(){const t=[`${this.renderHead()} ${this.selectItems.length>0?this.selectItems.join(", "):"*"}`,`FROM ${this.table}`];for(const i of this.joins)t.push(i.on===void 0?`${n[i.kind]} ${i.table}`:`${n[i.kind]} ${i.table} ON ${e(i.on)}`);return this.whereConditions.length>0&&t.push(`WHERE ${this.whereConditions.join(" AND ")}`),this.groupByItems.length>0&&t.push(`GROUP BY ${this.groupByItems.join(", ")}`),this.havingConditions.length>0&&t.push(`HAVING ${this.havingConditions.join(" AND ")}`),this.qualifyCondition!==void 0&&t.push(`QUALIFY ${this.qualifyCondition}`),this.orderByItems.length>0&&t.push(`ORDER BY ${this.orderByItems.join(", ")}`),this.limitValue!==void 0&&t.push(`LIMIT ${a(this.limitValue)}`),t.join(" ")}async run(){return this.exec(this.toSQL())}renderHead(){return this.distinctOnItems.length>0?`SELECT DISTINCT ON (${this.distinctOnItems.join(", ")})`:this.distinctFlag?"SELECT DISTINCT":"SELECT"}addJoin(t,i,r){return this.joins.push({kind:t,on:r,table:s(i)}),this}setOperation(t,i){return new h(this.exec,[{query:this},{operator:t,query:i}])}}export{I as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{renderOrderTerm as o}from"./asc-BsbdtIQU.mjs";import{assertLimit as n,lit as u}from"./Sql-CwhZl1Q5.mjs";const h=e=>{const t=e.toSQL();return e.needsWrapForSetOperation===!0?`(${t})`:t};class d{needsWrapForSetOperation=!0;exec;members;orderByItems=[];limitValue;constructor(t,r){this.exec=t,this.members=r}union(t){return this.add("UNION",t)}unionAll(t){return this.add("UNION ALL",t)}intersect(t){return this.add("INTERSECT",t)}except(t){return this.add("EXCEPT",t)}orderBy(...t){return this.orderByItems.push(...t.map(r=>o(r))),this}limit(t){return n(t),this.limitValue=t,this}returns(){return this}toSQL(){const t=this.members.map(s=>{const i=h(s.query);return s.operator===void 0?i:`${s.operator} ${i}`}).join(" "),r=[];return this.orderByItems.length>0&&r.push(`ORDER BY ${this.orderByItems.join(", ")}`),this.limitValue!==void 0&&r.push(`LIMIT ${u(this.limitValue)}`),r.length>0?`${t} ${r.join(" ")}`:t}async run(){return this.exec(this.toSQL())}add(t,r){return this.members.push({operator:t,query:r}),this}}export{d as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const a=e=>`'${e.replaceAll("'","''")}'`,f=/^\w+(?:\.\w+)*$/,p=/^\w+(?:\.\w+)*(?:\s+(?:as\s+)?\w+)?$/i;class t{text;constructor(r){this.text=r}toString(){return this.text}}const o=e=>e instanceof t,u=e=>new t(e),w=e=>o(e)?e.text:e,y=e=>{if(typeof e!="string"||!f.test(e))throw new TypeError(`r2sql: invalid identifier ${JSON.stringify(e)} — expected dotted [A-Za-z0-9_] segments (e.g. "namespace.table").`);return e},g=e=>{if(typeof e!="string"||!p.test(e))throw new TypeError(`r2sql: invalid table reference ${JSON.stringify(e)} — expected "namespace.table" with an optional "[AS] alias".`);return e},s=e=>{if(e==null)return"NULL";if(typeof e=="boolean")return e?"true":"false";if(typeof e=="bigint")return e.toString();if(typeof e=="number"){if(!Number.isFinite(e))throw new TypeError(`r2sql: cannot inline a non-finite number (${String(e)}) as a SQL literal.`);return String(e)}if(typeof e=="string")return a(e);if(e instanceof Date)return a(e.toISOString());if(Array.isArray(e)){if(e.length===0)throw new TypeError("r2sql: cannot inline an empty array — `IN ()` is not valid SQL. Guard the empty case before building the query.");return`(${e.map(r=>s(r)).join(", ")})`}throw new TypeError(`r2sql: cannot inline a value of type ${typeof e} as a SQL literal. Wrap trusted SQL with raw(), or pass a primitive/Date/array.`)},c=(e,...r)=>{let n=e[0]??"";for(const[l,i]of r.entries())n+=o(i)?i.text:s(i),n+=e[l+1]??"";return new t(n)},S=(e,r)=>new t(e.map(n=>w(n)).join(r)),m=e=>{if(!Number.isInteger(e)||e<1||e>1e4)throw new RangeError(`r2sql: limit must be an integer between 1 and ${String(1e4)} (got ${String(e)}).`)};export{t as Sql,m as assertLimit,y as ident,o as isSql,S as joinSql,s as lit,u as raw,c as sql,g as tableRef,w as toText};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Sql as r,lit as s}from"./Sql-CwhZl1Q5.mjs";const n=/^[A-Z_][\w$]*$/i,a=t=>{if(!n.test(t))throw new TypeError(`r2sql: invalid identifier ${JSON.stringify(t)} — expected a simple SQL name (letters, digits, underscore).`);return t};let o=class extends r{as(e){return new r(`${this.text} AS ${a(e)}`)}between(e,i){return new r(`${this.text} BETWEEN ${s(e)} AND ${s(i)}`)}eq(e){return this.compare("=",e)}gt(e){return this.compare(">",e)}gte(e){return this.compare(">=",e)}lt(e){return this.compare("<",e)}lte(e){return this.compare("<=",e)}compare(e,i){return new r(`${this.text} ${e} ${s(i)}`)}};export{o as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{renderOrderTerm as m}from"./asc-BsbdtIQU.mjs";import{lit as n,toText as o}from"./Sql-CwhZl1Q5.mjs";import u from"./WindowExpression-CxCuEy8V.mjs";const l=r=>r===void 0?[]:Array.isArray(r)?r:[r],c=r=>{const e=[],a=l(r.partitionBy).map(s=>o(s));a.length>0&&e.push(`PARTITION BY ${a.join(", ")}`);const i=l(r.orderBy).map(s=>m(s));return i.length>0&&e.push(`ORDER BY ${i.join(", ")}`),r.frame!==void 0&&r.frame.length>0&&e.push(r.frame),`OVER (${e.join(" ")})`},$=(r,e,a)=>{const i=[o(r)];return e!==void 0&&i.push(n(e)),a!==void 0&&i.push(n(a)),i.join(", ")};class p{callText;constructor(e){this.callText=e}over(e={}){return new u(`${this.callText} ${c(e)}`)}}const t=r=>new p(r),R={avg:r=>t(`AVG(${o(r)})`),count:r=>t(`COUNT(${r===void 0?"*":o(r)})`),cumeDist:()=>t("CUME_DIST()"),denseRank:()=>t("DENSE_RANK()"),firstValue:r=>t(`FIRST_VALUE(${o(r)})`),lag:(r,e,a)=>t(`LAG(${$(r,e,a)})`),lastValue:r=>t(`LAST_VALUE(${o(r)})`),lead:(r,e,a)=>t(`LEAD(${$(r,e,a)})`),max:r=>t(`MAX(${o(r)})`),min:r=>t(`MIN(${o(r)})`),nthValue:(r,e)=>t(`NTH_VALUE(${o(r)}, ${n(e)})`),ntile:r=>t(`NTILE(${n(r)})`),percentRank:()=>t("PERCENT_RANK()"),rank:()=>t("RANK()"),rowNumber:()=>t("ROW_NUMBER()"),sum:r=>t(`SUM(${o(r)})`)};export{p as WindowFunction,R as fn};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Sql as t,toText as r}from"./Sql-CwhZl1Q5.mjs";const d=e=>({dir:"ASC",expr:e}),i=e=>({dir:"DESC",expr:e}),p=e=>typeof e=="string"||e instanceof t?r(e):`${r(e.expr)} ${e.dir}`;export{d as asc,i as desc,p as renderOrderTerm};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as a}from"@lunora/errors";const s=/^[a-z][a-z\d+\-.]*:\/\//i,c=[",","=","#","?","/"],d=e=>e.endsWith("/")?e.slice(0,-1):e,m=e=>e.startsWith("/")?e.slice(1):e,g=e=>Object.entries(e).filter(([,r])=>r!==void 0&&(typeof r=="string"||typeof r=="number")).map(([r,n])=>{const i=String(n),o=c.find(t=>i.includes(t));if(o!==void 0)throw new a("INTERNAL",`@lunora/bindings/images: transform option \`${r}\` value \`${i}\` contains a \`${o}\`, which the /cdn-cgi/image/ option path cannot represent (\`,\`/\`=\` are the option/key-value separators; \`#\`/\`?\`/\`/\` are URL-structural). Percent-encode the value — for colors use \`%23RRGGBB\`, not \`#RRGGBB\` or \`rgb(r,g,b)\`.`);return`${r}=${i}`}).join(","),l=e=>e.split("/").map(r=>encodeURIComponent(r)).join("/"),p=e=>{const r=d(e.baseUrl);if(e.imageId!==void 0){const t=e.variant??"public";return`${r}/${encodeURIComponent(e.imageId)}/${encodeURIComponent(t)}`}if(e.key===void 0)throw new a("INTERNAL","@lunora/bindings/images: buildImageDeliveryUrl requires either `imageId` or `key`");const n=e.transform===void 0?"":g(e.transform),i=s.test(e.key)?e.key:l(m(e.key)),o=n===""?"/cdn-cgi/image":`/cdn-cgi/image/${n}`;return`${r}${o}/${i}`};export{p as buildImageDeliveryUrl};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
const v=(e,r)=>{if(e.size<r)return;const t=e.keys().next().value;t!==void 0&&e.delete(t)},p=new TextEncoder,u=10080*60,U=/^[a-z][a-z0-9+\-.]*:\/\//i,$=Array.from({length:32},(e,r)=>r),S=new RegExp(`[${$.map(e=>String.fromCodePoint(e)).join("")}]`,"u"),x=e=>{const r=String.fromCodePoint(...e);return btoa(r).replaceAll("+","-").replaceAll("/","_").replaceAll("=","")},A=e=>{const r=e.replaceAll("-","+").replaceAll("_","/")+"===".slice((e.length+3)%4),t=atob(r),n=new Uint8Array(t.length);for(let a=0;a<t.length;a+=1)n[a]=t.codePointAt(a)??0;return n},C=64,m=new Map,f=async e=>{const r=m.get(e);if(r)return r;v(m,C);const t=crypto.subtle.importKey("raw",p.encode(e),{hash:"SHA-256",name:"HMAC"},!1,["sign","verify"]);return m.set(e,t),t},g=e=>{try{return new URL(e).host}catch{return e.replace(U,"").split("/")[0]??""}},y=e=>{if(S.test(e))throw new TypeError("hmac-url: value must not contain control characters (including CR/LF)")},R=async(e,r)=>{const t=await f(e),n=await crypto.subtle.sign("HMAC",t,p.encode(r));return x(new Uint8Array(n))},I=async(e,r,t)=>{const n=await f(e);return crypto.subtle.verify("HMAC",n,t,p.encode(r))},h=/^\//,L=e=>e===void 0?"":Object.entries(e).filter(([,r])=>r!==void 0).toSorted(([r],[t])=>(r>t?1:0)-(r<t?1:0)).map(([r,t])=>`${r}=${typeof t=="object"?JSON.stringify(t):String(t)}`).join("&"),b=(e,r,t,n)=>`${e.toLowerCase()}
|
|
2
|
+
${r}
|
|
3
|
+
${String(t)}
|
|
4
|
+
${n}`,E=e=>e.split("/").map(r=>encodeURIComponent(r)).join("/"),N=async e=>{const r=e.expiresInSeconds??3600;if(!Number.isFinite(r)||r<=0)throw new TypeError("@lunora/bindings/images: expiresInSeconds must be a positive finite number");if(r>u)throw new TypeError(`@lunora/bindings/images: expiresInSeconds must not exceed ${String(u)} (7 days)`);let t="";try{t=new URL(e.baseUrl).pathname}catch{}if(t!==""&&t!=="/")throw new TypeError(`@lunora/bindings/images: baseUrl must not carry a path ("${t}") — the key is verified from the full URL pathname, so a subpath base would make every signed URL fail verification`);const n=Math.floor(Date.now()/1e3)+r,a=g(e.baseUrl),o=L(e.transform),s=e.key.replace(h,"");y(s);const l=await R(e.secret,b(a,s,n,o)),i=e.baseUrl.endsWith("/")?e.baseUrl.slice(0,-1):e.baseUrl,c=E(s),d=o===""?"":`&t=${encodeURIComponent(o)}`;return`${i}/${c}?exp=${String(n)}&sig=${l}${d}`},j=async(e,r,t)=>{let n;try{n=e instanceof URL?e:new URL(e)}catch{return{reason:"malformed",valid:!1}}const a=n.searchParams.get("exp"),o=a===null?Number.NaN:Number(a),s=n.searchParams.get("sig"),l=n.searchParams.get("t")??"";if(!s||!Number.isInteger(o))return{reason:"malformed",valid:!1};if(o<Math.floor(Date.now()/1e3))return{reason:"expired",valid:!1};let i,c;try{i=n.pathname.replace(h,"").split("/").map(w=>decodeURIComponent(w)).join("/"),y(i),c=A(s)}catch{return{reason:"malformed",valid:!1}}const d=t?.expectedHost===void 0?n.host:g(t.expectedHost);return await I(r,b(d,i,o,l),c)?{key:i,transform:l===""?void 0:l,valid:!0}:{reason:"bad_signature",valid:!1}};export{N as buildSignedImageUrl,j as verifySignedImageUrl};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const i=8,u=async(t,o,s)=>{if(t.length===0)return[];const h=Math.max(1,Math.min(o,t.length)),a=Array.from({length:t.length});let e=0,n=!1,c;const l=Array.from({length:h},async()=>{for(;;){if(n)return;const r=e;if(e+=1,r>=t.length)return;try{a[r]=await s(t[r],r)}catch(f){n||(n=!0,c=f);return}}});if(await Promise.all(l),n)throw c;return a};export{i as U,u as c};
|