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

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.
@@ -1,3 +1,4 @@
1
+ import { LunoraError } from '@lunora/errors';
1
2
  /**
2
3
  * Structural types for the Analytics Engine write path.
3
4
  *
@@ -130,9 +131,8 @@ interface AnalyticsSqlResult {
130
131
  rowCount: number;
131
132
  rows: Record<string, unknown>[];
132
133
  }
133
- /** Thrown when the SQL API responds with a non-2xx status; carries the status + body for the caller to surface. */
134
- declare class AnalyticsSqlError extends Error {
135
- readonly status: number;
134
+ /** Thrown when the SQL API responds with a non-2xx status; a `LunoraError` subclass carrying the HTTP `status` + body for the caller to surface. */
135
+ declare class AnalyticsSqlError extends LunoraError {
136
136
  constructor(status: number, body: string);
137
137
  }
138
138
  /** The read client: a single `query(sql)` over the AE SQL API. */
@@ -1,3 +1,4 @@
1
+ import { LunoraError } from '@lunora/errors';
1
2
  /**
2
3
  * Structural types for the Analytics Engine write path.
3
4
  *
@@ -130,9 +131,8 @@ interface AnalyticsSqlResult {
130
131
  rowCount: number;
131
132
  rows: Record<string, unknown>[];
132
133
  }
133
- /** Thrown when the SQL API responds with a non-2xx status; carries the status + body for the caller to surface. */
134
- declare class AnalyticsSqlError extends Error {
135
- readonly status: number;
134
+ /** Thrown when the SQL API responds with a non-2xx status; a `LunoraError` subclass carrying the HTTP `status` + body for the caller to surface. */
135
+ declare class AnalyticsSqlError extends LunoraError {
136
136
  constructor(status: number, body: string);
137
137
  }
138
138
  /** The read client: a single `query(sql)` over the AE SQL API. */
@@ -1,2 +1,2 @@
1
1
  export { createAnalytics } from '../packem_shared/createAnalytics-CEEI69o9.mjs';
2
- export { AnalyticsSqlError, createAnalyticsSqlClient } from '../packem_shared/AnalyticsSqlError-CGTdsi4H.mjs';
2
+ export { AnalyticsSqlError, createAnalyticsSqlClient } from '../packem_shared/AnalyticsSqlError-C2nz3jpH.mjs';
@@ -1,3 +1,3 @@
1
- export { createImages } from '../packem_shared/createImages-CJrvqX0u.mjs';
2
- export { buildImageDeliveryUrl } from '../packem_shared/buildImageDeliveryUrl-D1sVfIOP.mjs';
3
- export { buildSignedImageUrl, verifySignedImageUrl } from '../packem_shared/buildSignedImageUrl-Otdgc_jO.mjs';
1
+ export { createImages } from '../packem_shared/createImages-BzRnsz3H.mjs';
2
+ export { buildImageDeliveryUrl } from '../packem_shared/buildImageDeliveryUrl-B6_QU91n.mjs';
3
+ export { buildSignedImageUrl, verifySignedImageUrl } from '../packem_shared/buildSignedImageUrl-6iibA9v1.mjs';
package/dist/kv/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- export { createKv, scopeKey } from '../packem_shared/createKv-DTiSt216.mjs';
2
- export { createKvIntrospector, createKvIntrospectorFromEnv } from '../packem_shared/createKvIntrospector-BThrJVhP.mjs';
1
+ export { createKv, scopeKey } from '../packem_shared/createKv-7lNrS1W2.mjs';
2
+ export { createKvIntrospector, createKvIntrospectorFromEnv } from '../packem_shared/createKvIntrospector-CrAgQp1w.mjs';
@@ -1,10 +1,9 @@
1
+ import { LunoraError } from '@lunora/errors';
2
+
1
3
  const SQL_API_BASE = "https://api.cloudflare.com/client/v4/accounts";
2
- class AnalyticsSqlError extends Error {
3
- status;
4
+ class AnalyticsSqlError extends LunoraError {
4
5
  constructor(status, body) {
5
- super(`Analytics Engine SQL API returned ${String(status)}: ${body}`);
6
- this.name = "AnalyticsSqlError";
7
- this.status = status;
6
+ super("ANALYTICS_SQL_ERROR", `Analytics Engine SQL API returned ${String(status)}: ${body}`, { name: "AnalyticsSqlError", status });
8
7
  }
9
8
  }
10
9
  const createAnalyticsSqlClient = (config) => {
@@ -1,3 +1,4 @@
1
+ import { LunoraError } from '@lunora/errors';
1
2
  import SelectBuilder from './SelectBuilder-DHaXZwn_.mjs';
2
3
  import { ident, toText } from './Sql-DceGtcUd.mjs';
3
4
 
@@ -10,12 +11,9 @@ const inferColumns = (rows) => {
10
11
  return { name };
11
12
  });
12
13
  };
13
- class R2SqlError extends Error {
14
- status;
14
+ class R2SqlError extends LunoraError {
15
15
  constructor(status, body) {
16
- super(`R2 SQL query failed (${String(status)}): ${body}`);
17
- this.name = "R2SqlError";
18
- this.status = status;
16
+ super("R2_SQL_ERROR", `R2 SQL query failed (${String(status)}): ${body}`, { name: "R2SqlError", status });
19
17
  }
20
18
  }
21
19
  const createR2Sql = (config) => {
@@ -1,10 +1,13 @@
1
+ import { LunoraError } from '@lunora/errors';
2
+
1
3
  const ABSOLUTE_URL_RE = /^[a-z][a-z\d+\-.]*:\/\//i;
2
4
  const stripTrailingSlash = (value) => value.endsWith("/") ? value.slice(0, -1) : value;
3
5
  const stripLeadingSlash = (value) => value.startsWith("/") ? value.slice(1) : value;
4
6
  const serializeTransform = (transform) => Object.entries(transform).filter(([, value]) => value !== void 0 && (typeof value === "string" || typeof value === "number")).map(([key, value]) => {
5
7
  const serialized = String(value);
6
8
  if (serialized.includes(",") || serialized.includes("=")) {
7
- throw new Error(
9
+ throw new LunoraError(
10
+ "INTERNAL",
8
11
  `@lunora/bindings/images: transform option \`${key}\` value \`${serialized}\` contains a \`,\` or \`=\`, which the /cdn-cgi/image/ option list cannot represent (these are the option/key-value separators). For colors, use the hex form (e.g. \`#RRGGBB\`/\`%23RRGGBB\`) instead of \`rgb(r,g,b)\`.`
9
12
  );
10
13
  }
@@ -18,7 +21,7 @@ const buildImageDeliveryUrl = (options) => {
18
21
  return `${base}/${encodeURIComponent(options.imageId)}/${encodeURIComponent(variant)}`;
19
22
  }
20
23
  if (options.key === void 0) {
21
- throw new Error("@lunora/bindings/images: buildImageDeliveryUrl requires either `imageId` or `key`");
24
+ throw new LunoraError("INTERNAL", "@lunora/bindings/images: buildImageDeliveryUrl requires either `imageId` or `key`");
22
25
  }
23
26
  const optionString = options.transform === void 0 ? "" : serializeTransform(options.transform);
24
27
  const isAbsolute = ABSOLUTE_URL_RE.test(options.key);
@@ -54,10 +54,10 @@ const encodeKey = (key) => key.split("/").map((segment) => encodeURIComponent(se
54
54
  const buildSignedImageUrl = async (options) => {
55
55
  const expiresInSeconds = options.expiresInSeconds ?? 60 * 60;
56
56
  if (!Number.isFinite(expiresInSeconds) || expiresInSeconds <= 0) {
57
- throw new Error("@lunora/bindings/images: expiresInSeconds must be a positive finite number");
57
+ throw new TypeError("@lunora/bindings/images: expiresInSeconds must be a positive finite number");
58
58
  }
59
59
  if (expiresInSeconds > MAX_EXPIRES_IN_SECONDS) {
60
- throw new Error(`@lunora/bindings/images: expiresInSeconds must not exceed ${String(MAX_EXPIRES_IN_SECONDS)} (7 days)`);
60
+ throw new TypeError(`@lunora/bindings/images: expiresInSeconds must not exceed ${String(MAX_EXPIRES_IN_SECONDS)} (7 days)`);
61
61
  }
62
62
  const exp = Math.floor(Date.now() / 1e3) + expiresInSeconds;
63
63
  const host = extractHost(options.baseUrl);
@@ -1,3 +1,5 @@
1
+ import { LunoraError } from '@lunora/errors';
2
+
1
3
  const ALLOWED_OUTPUT_FORMATS = /* @__PURE__ */ new Set(["image/avif", "image/gif", "image/jpeg", "image/png", "image/webp"]);
2
4
  const DEFAULT_MAX_DIMENSION = 1e4;
3
5
  const DEFAULT_OUTPUT_FORMAT = "image/webp";
@@ -14,7 +16,7 @@ const toStream = (input) => {
14
16
  }
15
17
  if (isR2ObjectBody(input)) {
16
18
  if (input.body === null) {
17
- throw new Error("@lunora/bindings/images: R2 object body is null (object missing or already consumed)");
19
+ throw new LunoraError("INTERNAL", "@lunora/bindings/images: R2 object body is null (object missing or already consumed)");
18
20
  }
19
21
  return input.body;
20
22
  }
@@ -35,7 +37,7 @@ const sanitizeTransform = (transform, maxDimension) => {
35
37
  }
36
38
  const clampDimension = (value) => {
37
39
  if (!Number.isFinite(value) || value <= 0) {
38
- throw new Error("@lunora/bindings/images: width/height must be a positive finite number");
40
+ throw new TypeError("@lunora/bindings/images: width/height must be a positive finite number");
39
41
  }
40
42
  return Math.min(Math.floor(value), maxDimension);
41
43
  };
@@ -54,7 +56,10 @@ const splitOverlay = (overlay) => {
54
56
  const resolveOutput = (output) => {
55
57
  const format = output?.format ?? DEFAULT_OUTPUT_FORMAT;
56
58
  if (!ALLOWED_OUTPUT_FORMATS.has(format)) {
57
- throw new Error(`@lunora/bindings/images: unsupported output format "${format}" (allowed: ${[...ALLOWED_OUTPUT_FORMATS].join(", ")})`);
59
+ throw new LunoraError(
60
+ "INTERNAL",
61
+ `@lunora/bindings/images: unsupported output format "${format}" (allowed: ${[...ALLOWED_OUTPUT_FORMATS].join(", ")})`
62
+ );
58
63
  }
59
64
  return { ...output, format };
60
65
  };
@@ -1,19 +1,21 @@
1
+ import { LunoraError } from '@lunora/errors';
2
+
1
3
  const MAX_KEY_LENGTH = 512;
2
4
  const MAX_LIST_LIMIT = 1e3;
3
5
  const validateKey = (key) => {
4
6
  if (typeof key !== "string" || key.length === 0) {
5
- throw new Error("@lunora/bindings/kv: key must be a non-empty string");
7
+ throw new TypeError("@lunora/bindings/kv: key must be a non-empty string");
6
8
  }
7
9
  if (key.length > MAX_KEY_LENGTH) {
8
- throw new Error(`@lunora/bindings/kv: key exceeds ${String(MAX_KEY_LENGTH)}-byte limit`);
10
+ throw new LunoraError("INTERNAL", `@lunora/bindings/kv: key exceeds ${String(MAX_KEY_LENGTH)}-byte limit`);
9
11
  }
10
12
  if (key.includes("\0")) {
11
- throw new Error("@lunora/bindings/kv: key contains NUL byte");
13
+ throw new LunoraError("INTERNAL", "@lunora/bindings/kv: key contains NUL byte");
12
14
  }
13
15
  const segments = key.split("/");
14
16
  for (const segment of segments) {
15
17
  if (segment === "." || segment === "..") {
16
- throw new Error("@lunora/bindings/kv: key contains a `.`/`..` path component");
18
+ throw new LunoraError("INTERNAL", "@lunora/bindings/kv: key contains a `.`/`..` path component");
17
19
  }
18
20
  }
19
21
  };
@@ -22,15 +24,15 @@ const validatePrefix = (prefix) => {
22
24
  return;
23
25
  }
24
26
  if (prefix.length > MAX_KEY_LENGTH) {
25
- throw new Error(`@lunora/bindings/kv: prefix exceeds ${String(MAX_KEY_LENGTH)}-byte limit`);
27
+ throw new LunoraError("INTERNAL", `@lunora/bindings/kv: prefix exceeds ${String(MAX_KEY_LENGTH)}-byte limit`);
26
28
  }
27
29
  if (prefix.includes("\0")) {
28
- throw new Error("@lunora/bindings/kv: prefix contains NUL byte");
30
+ throw new LunoraError("INTERNAL", "@lunora/bindings/kv: prefix contains NUL byte");
29
31
  }
30
32
  const segments = prefix.split("/");
31
33
  for (const segment of segments) {
32
34
  if (segment === "." || segment === "..") {
33
- throw new Error("@lunora/bindings/kv: prefix contains a `.`/`..` path component");
35
+ throw new LunoraError("INTERNAL", "@lunora/bindings/kv: prefix contains a `.`/`..` path component");
34
36
  }
35
37
  }
36
38
  };
@@ -40,14 +42,14 @@ const scopeKey = (prefix, key) => {
40
42
  const trimmedPrefix = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
41
43
  const composed = `${trimmedPrefix}/${key}`;
42
44
  if (composed.length > MAX_KEY_LENGTH) {
43
- throw new Error(`@lunora/bindings/kv: scoped key exceeds ${String(MAX_KEY_LENGTH)}-byte limit`);
45
+ throw new LunoraError("INTERNAL", `@lunora/bindings/kv: scoped key exceeds ${String(MAX_KEY_LENGTH)}-byte limit`);
44
46
  }
45
47
  return composed;
46
48
  };
47
49
  const toPutOptions = (options) => {
48
50
  const out = {};
49
51
  if (options.expiration !== void 0 && options.expirationTtl !== void 0) {
50
- throw new Error("@lunora/bindings/kv: `expiration` and `expirationTtl` are mutually exclusive");
52
+ throw new LunoraError("INTERNAL", "@lunora/bindings/kv: `expiration` and `expirationTtl` are mutually exclusive");
51
53
  }
52
54
  if (options.expiration !== void 0) {
53
55
  out.expiration = options.expiration;
@@ -62,7 +64,7 @@ const toPutOptions = (options) => {
62
64
  };
63
65
  const createKv = (options) => {
64
66
  if (!options.namespace) {
65
- throw new Error("@lunora/bindings/kv: `namespace` is required");
67
+ throw new TypeError("@lunora/bindings/kv: `namespace` is required");
66
68
  }
67
69
  const { keyPrefix, namespace } = options;
68
70
  if (keyPrefix !== void 0) {
@@ -103,7 +105,7 @@ const createKv = (options) => {
103
105
  };
104
106
  const list = async (listOptions = {}) => {
105
107
  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");
108
+ throw new TypeError("@lunora/bindings/kv: `limit` must be a positive integer");
107
109
  }
108
110
  let { prefix } = listOptions;
109
111
  if (prefix !== void 0) {
@@ -1,9 +1,11 @@
1
+ import { LunoraError } from '@lunora/errors';
2
+
1
3
  const createKvIntrospector = (options) => {
2
4
  const { namespaces } = options;
3
5
  const resolveNamespace = (binding) => {
4
6
  const ns = namespaces[binding];
5
7
  if (ns === void 0) {
6
- throw new Error(`@lunora/bindings/kv: no namespace registered under binding "${binding}"`);
8
+ throw new LunoraError("BAD_REQUEST", `@lunora/bindings/kv: no namespace registered under binding "${binding}"`);
7
9
  }
8
10
  return ns;
9
11
  };
@@ -1,3 +1,5 @@
1
+ import { LunoraError } from '@lunora/errors';
2
+
1
3
  const MAX_TOP_K = 20;
2
4
  const DEFAULT_TOP_K = 10;
3
5
  const createVectorAdminIntrospector = (options) => {
@@ -28,11 +30,11 @@ const createVectorAdminIntrospector = (options) => {
28
30
  const queryIndex = async ({ name, text, topK }) => {
29
31
  const binding = indexes[name];
30
32
  if (binding === void 0) {
31
- throw new Error(`@lunora/bindings/vectors: no Vectorize binding registered for index "${name}"`);
33
+ throw new LunoraError("INTERNAL", `@lunora/bindings/vectors: no Vectorize binding registered for index "${name}"`);
32
34
  }
33
35
  const embed = embedders[name];
34
36
  if (embed === void 0) {
35
- throw new Error(`@lunora/bindings/vectors: no embedder registered for index "${name}" — it lists read-only`);
37
+ throw new LunoraError("INTERNAL", `@lunora/bindings/vectors: no embedder registered for index "${name}" — it lists read-only`);
36
38
  }
37
39
  const vector = await embed(text);
38
40
  const result = await binding.query(vector, {
@@ -1,9 +1,13 @@
1
+ import { LunoraError } from '@lunora/errors';
1
2
  import { c as concurrentMap, U as UPSERT_EMBED_CONCURRENCY } from './concurrent-Dj5sOibv.mjs';
2
3
 
3
4
  const resolveIndex = (indexes, name) => {
4
5
  const index = indexes[name];
5
6
  if (!index) {
6
- throw new Error(`@lunora/bindings/vectors: no index registered for "${name}". Known indexes: ${Object.keys(indexes).join(", ") || "(none)"}`);
7
+ throw new LunoraError(
8
+ "INTERNAL",
9
+ `@lunora/bindings/vectors: no index registered for "${name}". Known indexes: ${Object.keys(indexes).join(", ") || "(none)"}`
10
+ );
7
11
  }
8
12
  return index;
9
13
  };
@@ -22,7 +26,7 @@ const MAX_ID_BATCH = 1e3;
22
26
  const MAX_UPSERT_BATCH = 1e3;
23
27
  const createVectors = (options) => {
24
28
  if (Object.keys(options.indexes).length === 0) {
25
- throw new Error("@lunora/bindings/vectors: at least one index binding is required");
29
+ throw new TypeError("@lunora/bindings/vectors: at least one index binding is required");
26
30
  }
27
31
  const upsert = async (indexName, input) => {
28
32
  const index = resolveIndex(options.indexes, indexName);
@@ -52,7 +56,7 @@ const createVectors = (options) => {
52
56
  values = input.vector;
53
57
  } else {
54
58
  if (!input.embed || input.input === void 0) {
55
- throw new Error("@lunora/bindings/vectors: query requires either `vector` or both `input` and `embed`");
59
+ throw new TypeError("@lunora/bindings/vectors: query requires either `vector` or both `input` and `embed`");
56
60
  }
57
61
  values = await input.embed(input.input);
58
62
  }
@@ -81,7 +85,7 @@ const createVectors = (options) => {
81
85
  const describe = async (indexName) => {
82
86
  const index = resolveIndex(options.indexes, indexName);
83
87
  if (!index.describe) {
84
- throw new Error(`@lunora/bindings/vectors: binding for "${indexName}" does not implement describe()`);
88
+ throw new LunoraError("INTERNAL", `@lunora/bindings/vectors: binding for "${indexName}" does not implement describe()`);
85
89
  }
86
90
  return index.describe();
87
91
  };
@@ -1,3 +1,4 @@
1
+ import { LunoraError } from '@lunora/errors';
1
2
  /**
2
3
  * A composed SQL fragment. Carries the finished `text`; `toString()` returns it
3
4
  * so a fragment can be dropped straight into a template or `String(...)`.
@@ -265,8 +266,7 @@ declare class SelectBuilder<Row = Record<string, unknown>> implements Queryable<
265
266
  * envelope, or an unparseable body; carries the HTTP `status` and the raw body
266
267
  * for the caller to surface.
267
268
  */
268
- declare class R2SqlError extends Error {
269
- readonly status: number;
269
+ declare class R2SqlError extends LunoraError {
270
270
  constructor(status: number, body: string);
271
271
  }
272
272
  /**
@@ -1,3 +1,4 @@
1
+ import { LunoraError } from '@lunora/errors';
1
2
  /**
2
3
  * A composed SQL fragment. Carries the finished `text`; `toString()` returns it
3
4
  * so a fragment can be dropped straight into a template or `String(...)`.
@@ -265,8 +266,7 @@ declare class SelectBuilder<Row = Record<string, unknown>> implements Queryable<
265
266
  * envelope, or an unparseable body; carries the HTTP `status` and the raw body
266
267
  * for the caller to surface.
267
268
  */
268
- declare class R2SqlError extends Error {
269
- readonly status: number;
269
+ declare class R2SqlError extends LunoraError {
270
270
  constructor(status: number, body: string);
271
271
  }
272
272
  /**
@@ -1,5 +1,5 @@
1
1
  export { default as SelectBuilder } from '../packem_shared/SelectBuilder-DHaXZwn_.mjs';
2
- export { R2SqlError, createR2Sql } from '../packem_shared/R2SqlError-DlDd_SrE.mjs';
2
+ export { R2SqlError, createR2Sql } from '../packem_shared/R2SqlError-Boygg3S0.mjs';
3
3
  export { asc, desc, renderOrderTerm } from '../packem_shared/asc-Cur-xO8v.mjs';
4
4
  export { default as SetOperation } from '../packem_shared/SetOperation-RDHcxccj.mjs';
5
5
  export { Sql, isSql, joinSql, lit, raw, sql, toText } from '../packem_shared/Sql-DceGtcUd.mjs';
@@ -1,3 +1,3 @@
1
1
  export { createContextVectors, createVectorSyncHook } from '../packem_shared/createContextVectors-BSizpmu5.mjs';
2
- export { createVectorAdminIntrospector } from '../packem_shared/createVectorAdminIntrospector-BJUOM6VW.mjs';
3
- export { default as createVectors } from '../packem_shared/createVectors-LSpGoKCd.mjs';
2
+ export { createVectorAdminIntrospector } from '../packem_shared/createVectorAdminIntrospector-C-qTDWA1.mjs';
3
+ export { default as createVectors } from '../packem_shared/createVectors-DqxgEPQb.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/bindings",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.0-alpha.4",
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",
@@ -63,6 +63,9 @@
63
63
  "publishConfig": {
64
64
  "access": "public"
65
65
  },
66
+ "dependencies": {
67
+ "@lunora/errors": "1.0.0-alpha.1"
68
+ },
66
69
  "engines": {
67
70
  "node": "^22.15.0 || >=24.11.0"
68
71
  }