@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.
Files changed (57) hide show
  1. package/LICENSE.md +6 -0
  2. package/dist/analytics/index.d.mts +66 -87
  3. package/dist/analytics/index.d.ts +66 -87
  4. package/dist/analytics/index.mjs +1 -2
  5. package/dist/images/index.d.mts +148 -148
  6. package/dist/images/index.d.ts +148 -148
  7. package/dist/images/index.mjs +1 -3
  8. package/dist/kv/index.d.mts +130 -113
  9. package/dist/kv/index.d.ts +130 -113
  10. package/dist/kv/index.mjs +1 -1
  11. package/dist/packem_shared/AnalyticsSqlError-B-JcsUhX.mjs +1 -0
  12. package/dist/packem_shared/R2SqlError-Uaz0RJTR.mjs +1 -0
  13. package/dist/packem_shared/SelectBuilder-WMBQ2b4q.mjs +1 -0
  14. package/dist/packem_shared/SetOperation-BrzMB1Tb.mjs +1 -0
  15. package/dist/packem_shared/Sql-CwhZl1Q5.mjs +1 -0
  16. package/dist/packem_shared/WindowExpression-CxCuEy8V.mjs +1 -0
  17. package/dist/packem_shared/WindowFunction-rzl4CzYE.mjs +1 -0
  18. package/dist/packem_shared/asc-BsbdtIQU.mjs +1 -0
  19. package/dist/packem_shared/buildImageDeliveryUrl-CMUkVJfB.mjs +1 -0
  20. package/dist/packem_shared/buildSignedImageUrl-BzgCHIu6.mjs +4 -0
  21. package/dist/packem_shared/concurrent-DMFQCILU.mjs +1 -0
  22. package/dist/packem_shared/createAnalytics-HRPCzT1M.mjs +1 -0
  23. package/dist/packem_shared/createContextVectors-D-ae1wWO.mjs +6 -0
  24. package/dist/packem_shared/createImages-TW6ircG2.mjs +1 -0
  25. package/dist/packem_shared/createKv-CKeFDFuG.mjs +1 -0
  26. package/dist/packem_shared/createKvIntrospector-Bz0nqUjD.mjs +1 -0
  27. package/dist/packem_shared/createPipelines-n0Bf_p8Y.mjs +1 -0
  28. package/dist/packem_shared/createVectorAdminIntrospector-B9Yt09yj.mjs +1 -0
  29. package/dist/packem_shared/createVectors-jb8fAY6R.mjs +1 -0
  30. package/dist/pipelines/index.d.mts +24 -24
  31. package/dist/pipelines/index.d.ts +24 -24
  32. package/dist/pipelines/index.mjs +1 -1
  33. package/dist/r2sql/index.d.mts +124 -124
  34. package/dist/r2sql/index.d.ts +124 -124
  35. package/dist/r2sql/index.mjs +1 -7
  36. package/dist/vectors/index.d.mts +185 -134
  37. package/dist/vectors/index.d.ts +185 -134
  38. package/dist/vectors/index.mjs +1 -3
  39. package/package.json +5 -1
  40. package/dist/packem_shared/AnalyticsSqlError-CGTdsi4H.mjs +0 -42
  41. package/dist/packem_shared/R2SqlError-DlDd_SrE.mjs +0 -67
  42. package/dist/packem_shared/SelectBuilder-DHaXZwn_.mjs +0 -167
  43. package/dist/packem_shared/SetOperation-RDHcxccj.mjs +0 -80
  44. package/dist/packem_shared/Sql-DceGtcUd.mjs +0 -68
  45. package/dist/packem_shared/WindowExpression-Cg9s2xcr.mjs +0 -44
  46. package/dist/packem_shared/WindowFunction-DA3pGC3N.mjs +0 -82
  47. package/dist/packem_shared/asc-Cur-xO8v.mjs +0 -16
  48. package/dist/packem_shared/buildImageDeliveryUrl-D1sVfIOP.mjs +0 -30
  49. package/dist/packem_shared/buildSignedImageUrl-Otdgc_jO.mjs +0 -113
  50. package/dist/packem_shared/concurrent-Dj5sOibv.mjs +0 -23
  51. package/dist/packem_shared/createAnalytics-CEEI69o9.mjs +0 -57
  52. package/dist/packem_shared/createContextVectors-BSizpmu5.mjs +0 -140
  53. package/dist/packem_shared/createImages-CJrvqX0u.mjs +0 -80
  54. package/dist/packem_shared/createKv-DTiSt216.mjs +0 -141
  55. package/dist/packem_shared/createPipelines-CfyJ6VGu.mjs +0 -10
  56. package/dist/packem_shared/createVectorAdminIntrospector-BJUOM6VW.mjs +0 -51
  57. package/dist/packem_shared/createVectors-LSpGoKCd.mjs +0 -91
@@ -1,167 +0,0 @@
1
- import { renderOrderTerm } from './asc-Cur-xO8v.mjs';
2
- import SetOperation from './SetOperation-RDHcxccj.mjs';
3
- import { tableRef, toText, lit } from './Sql-DceGtcUd.mjs';
4
-
5
- const JOIN_KEYWORDS = {
6
- cross: "CROSS JOIN",
7
- full: "FULL OUTER JOIN",
8
- inner: "INNER JOIN",
9
- left: "LEFT JOIN",
10
- right: "RIGHT JOIN"
11
- };
12
- class SelectBuilder {
13
- exec;
14
- table;
15
- selectItems = [];
16
- distinctFlag = false;
17
- distinctOnItems = [];
18
- joins = [];
19
- whereConditions = [];
20
- groupByItems = [];
21
- havingConditions = [];
22
- qualifyCondition;
23
- orderByItems = [];
24
- limitValue;
25
- constructor(exec, table) {
26
- this.exec = exec;
27
- this.table = tableRef(table);
28
- }
29
- /** The `SELECT` list. Omit/empty for `SELECT *`. Items are columns, expressions, or aliased window fragments (`fn.rowNumber().over(...).as("rk")`). */
30
- select(...items) {
31
- this.selectItems.push(...items.map((item) => toText(item)));
32
- return this;
33
- }
34
- /** `SELECT DISTINCT` — unique rows. */
35
- distinct() {
36
- this.distinctFlag = true;
37
- return this;
38
- }
39
- /** `DISTINCT ON (cols)` — the first row per distinct combination, ordered by {@link orderBy}. */
40
- distinctOn(...columns) {
41
- this.distinctOnItems.push(...columns.map((column) => toText(column)));
42
- return this;
43
- }
44
- /** `INNER JOIN table ON condition`. */
45
- innerJoin(table, on) {
46
- return this.addJoin("inner", table, on);
47
- }
48
- /** `LEFT JOIN table ON condition`. */
49
- leftJoin(table, on) {
50
- return this.addJoin("left", table, on);
51
- }
52
- /** `RIGHT JOIN table ON condition`. */
53
- rightJoin(table, on) {
54
- return this.addJoin("right", table, on);
55
- }
56
- /** `FULL OUTER JOIN table ON condition`. */
57
- fullJoin(table, on) {
58
- return this.addJoin("full", table, on);
59
- }
60
- /** `CROSS JOIN table` (no `ON`). */
61
- crossJoin(table) {
62
- return this.addJoin("cross", table);
63
- }
64
- /** Add `WHERE` condition(s). Multiple calls (and multiple args) are `AND`-ed. Bind values with the `sql` tag. */
65
- where(...conditions) {
66
- this.whereConditions.push(...conditions.map((condition) => toText(condition)));
67
- return this;
68
- }
69
- /** `GROUP BY` column(s)/expression(s). */
70
- groupBy(...columns) {
71
- this.groupByItems.push(...columns.map((column) => toText(column)));
72
- return this;
73
- }
74
- /** Add `HAVING` condition(s) over aggregates; multiple are `AND`-ed. */
75
- having(...conditions) {
76
- this.havingConditions.push(...conditions.map((condition) => toText(condition)));
77
- return this;
78
- }
79
- /**
80
- * `QUALIFY` — filter on a window function without a subquery, e.g.
81
- * `.qualify(fn.rowNumber().over({ partitionBy: "region", orderBy: desc("total") }).lte(3))`.
82
- */
83
- qualify(condition) {
84
- this.qualifyCondition = toText(condition);
85
- return this;
86
- }
87
- /** `ORDER BY` term(s) — bare strings (ASC) or {@link import("./order").asc | asc}/{@link import("./order").desc | desc} tags. */
88
- orderBy(...terms) {
89
- this.orderByItems.push(...terms.map((term) => renderOrderTerm(term)));
90
- return this;
91
- }
92
- /** `LIMIT n` (R2 SQL: 1–10,000, default 500). */
93
- limit(n) {
94
- this.limitValue = n;
95
- return this;
96
- }
97
- /** Re-type the result rows without changing the query (the builder carries no schema of its own). */
98
- returns() {
99
- return this;
100
- }
101
- /** `this UNION other` — all rows from both, duplicates removed. */
102
- union(other) {
103
- return this.setOperation("UNION", other);
104
- }
105
- /** `this UNION ALL other` — all rows from both, duplicates kept. */
106
- unionAll(other) {
107
- return this.setOperation("UNION ALL", other);
108
- }
109
- /** `this INTERSECT other` — rows present in both. */
110
- intersect(other) {
111
- return this.setOperation("INTERSECT", other);
112
- }
113
- /** `this EXCEPT other` — rows in `this` but not `other`. */
114
- except(other) {
115
- return this.setOperation("EXCEPT", other);
116
- }
117
- /** True when this query carries its own `ORDER BY`/`LIMIT` — so a set operation must parenthesise it. */
118
- get needsWrapForSetOperation() {
119
- return this.orderByItems.length > 0 || this.limitValue !== void 0;
120
- }
121
- /** Render the `SELECT` statement (no trailing semicolon). */
122
- toSQL() {
123
- const parts = [`${this.renderHead()} ${this.selectItems.length > 0 ? this.selectItems.join(", ") : "*"}`, `FROM ${this.table}`];
124
- for (const join of this.joins) {
125
- parts.push(join.on === void 0 ? `${JOIN_KEYWORDS[join.kind]} ${join.table}` : `${JOIN_KEYWORDS[join.kind]} ${join.table} ON ${toText(join.on)}`);
126
- }
127
- if (this.whereConditions.length > 0) {
128
- parts.push(`WHERE ${this.whereConditions.join(" AND ")}`);
129
- }
130
- if (this.groupByItems.length > 0) {
131
- parts.push(`GROUP BY ${this.groupByItems.join(", ")}`);
132
- }
133
- if (this.havingConditions.length > 0) {
134
- parts.push(`HAVING ${this.havingConditions.join(" AND ")}`);
135
- }
136
- if (this.qualifyCondition !== void 0) {
137
- parts.push(`QUALIFY ${this.qualifyCondition}`);
138
- }
139
- if (this.orderByItems.length > 0) {
140
- parts.push(`ORDER BY ${this.orderByItems.join(", ")}`);
141
- }
142
- if (this.limitValue !== void 0) {
143
- parts.push(`LIMIT ${lit(this.limitValue)}`);
144
- }
145
- return parts.join(" ");
146
- }
147
- /** Execute the query and return the typed result. */
148
- async run() {
149
- return this.exec(this.toSQL());
150
- }
151
- /** The `SELECT [DISTINCT [ON (...)]]` head. */
152
- renderHead() {
153
- if (this.distinctOnItems.length > 0) {
154
- return `SELECT DISTINCT ON (${this.distinctOnItems.join(", ")})`;
155
- }
156
- return this.distinctFlag ? "SELECT DISTINCT" : "SELECT";
157
- }
158
- addJoin(kind, table, on) {
159
- this.joins.push({ kind, on, table: tableRef(table) });
160
- return this;
161
- }
162
- setOperation(operator, other) {
163
- return new SetOperation(this.exec, [{ query: this }, { operator, query: other }]);
164
- }
165
- }
166
-
167
- export { SelectBuilder as default };
@@ -1,80 +0,0 @@
1
- import { renderOrderTerm } from './asc-Cur-xO8v.mjs';
2
- import { lit } from './Sql-DceGtcUd.mjs';
3
-
4
- const renderMember = (query) => {
5
- const text = query.toSQL();
6
- return query.needsWrapForSetOperation === true ? `(${text})` : text;
7
- };
8
- class SetOperation {
9
- /**
10
- * Always `true`: a nested set operation must be parenthesised when it is a
11
- * member of another set operation, or mixed operators mis-associate — e.g.
12
- * `a.union(b.except(c))` must render `a UNION (b EXCEPT c)`, not the flat
13
- * `a UNION b EXCEPT c`.
14
- */
15
- needsWrapForSetOperation = true;
16
- exec;
17
- members;
18
- orderByItems = [];
19
- limitValue;
20
- constructor(exec, members) {
21
- this.exec = exec;
22
- this.members = members;
23
- }
24
- /** Append `UNION other`. */
25
- union(other) {
26
- return this.add("UNION", other);
27
- }
28
- /** Append `UNION ALL other`. */
29
- unionAll(other) {
30
- return this.add("UNION ALL", other);
31
- }
32
- /** Append `INTERSECT other`. */
33
- intersect(other) {
34
- return this.add("INTERSECT", other);
35
- }
36
- /** Append `EXCEPT other`. */
37
- except(other) {
38
- return this.add("EXCEPT", other);
39
- }
40
- /** `ORDER BY` applied to the combined result. */
41
- orderBy(...terms) {
42
- this.orderByItems.push(...terms.map((term) => renderOrderTerm(term)));
43
- return this;
44
- }
45
- /** `LIMIT` applied to the combined result. */
46
- limit(n) {
47
- this.limitValue = n;
48
- return this;
49
- }
50
- /** Re-type the combined result rows. */
51
- returns() {
52
- return this;
53
- }
54
- /** Render the combined statement. */
55
- toSQL() {
56
- const parts = this.members.map((member) => {
57
- const text = renderMember(member.query);
58
- return member.operator === void 0 ? text : `${member.operator} ${text}`;
59
- });
60
- const combined = parts.join(" ");
61
- const tail = [];
62
- if (this.orderByItems.length > 0) {
63
- tail.push(`ORDER BY ${this.orderByItems.join(", ")}`);
64
- }
65
- if (this.limitValue !== void 0) {
66
- tail.push(`LIMIT ${lit(this.limitValue)}`);
67
- }
68
- return tail.length > 0 ? `${combined} ${tail.join(" ")}` : combined;
69
- }
70
- /** Execute the combined query and return the typed result. */
71
- async run() {
72
- return this.exec(this.toSQL());
73
- }
74
- add(operator, other) {
75
- this.members.push({ operator, query: other });
76
- return this;
77
- }
78
- }
79
-
80
- export { SetOperation as default };
@@ -1,68 +0,0 @@
1
- const quoteString = (value) => `'${value.replaceAll("'", "''")}'`;
2
- const IDENTIFIER_RE = /^\w+(?:\.\w+)*$/;
3
- const TABLE_REF_RE = /^\w+(?:\.\w+)*(?:\s+(?:as\s+)?\w+)?$/i;
4
- class Sql {
5
- text;
6
- constructor(text) {
7
- this.text = text;
8
- }
9
- toString() {
10
- return this.text;
11
- }
12
- }
13
- const isSql = (value) => value instanceof Sql;
14
- const raw = (text) => new Sql(text);
15
- const toText = (value) => isSql(value) ? value.text : value;
16
- const ident = (name) => {
17
- if (typeof name !== "string" || !IDENTIFIER_RE.test(name)) {
18
- throw new TypeError(`r2sql: invalid identifier ${JSON.stringify(name)} — expected dotted [A-Za-z0-9_] segments (e.g. "namespace.table").`);
19
- }
20
- return name;
21
- };
22
- const tableRef = (ref) => {
23
- if (typeof ref !== "string" || !TABLE_REF_RE.test(ref)) {
24
- throw new TypeError(`r2sql: invalid table reference ${JSON.stringify(ref)} — expected "namespace.table" with an optional "[AS] alias".`);
25
- }
26
- return ref;
27
- };
28
- const lit = (value) => {
29
- if (value === null || value === void 0) {
30
- return "NULL";
31
- }
32
- if (typeof value === "boolean") {
33
- return value ? "true" : "false";
34
- }
35
- if (typeof value === "bigint") {
36
- return value.toString();
37
- }
38
- if (typeof value === "number") {
39
- if (!Number.isFinite(value)) {
40
- throw new TypeError(`r2sql: cannot inline a non-finite number (${String(value)}) as a SQL literal.`);
41
- }
42
- return String(value);
43
- }
44
- if (typeof value === "string") {
45
- return quoteString(value);
46
- }
47
- if (value instanceof Date) {
48
- return quoteString(value.toISOString());
49
- }
50
- if (Array.isArray(value)) {
51
- if (value.length === 0) {
52
- throw new TypeError("r2sql: cannot inline an empty array — `IN ()` is not valid SQL. Guard the empty case before building the query.");
53
- }
54
- return `(${value.map((element) => lit(element)).join(", ")})`;
55
- }
56
- throw new TypeError(`r2sql: cannot inline a value of type ${typeof value} as a SQL literal. Wrap trusted SQL with raw(), or pass a primitive/Date/array.`);
57
- };
58
- const sql = (strings, ...values) => {
59
- let out = strings[0] ?? "";
60
- for (const [index, value] of values.entries()) {
61
- out += isSql(value) ? value.text : lit(value);
62
- out += strings[index + 1] ?? "";
63
- }
64
- return new Sql(out);
65
- };
66
- const joinSql = (parts, separator) => new Sql(parts.map((part) => toText(part)).join(separator));
67
-
68
- export { Sql, ident, isSql, joinSql, lit, raw, sql, tableRef, toText };
@@ -1,44 +0,0 @@
1
- import { Sql, lit } from './Sql-DceGtcUd.mjs';
2
-
3
- const IDENTIFIER = /^[A-Z_][\w$]*$/i;
4
- const assertIdent = (name) => {
5
- if (!IDENTIFIER.test(name)) {
6
- throw new TypeError(`r2sql: invalid identifier ${JSON.stringify(name)} — expected a simple SQL name (letters, digits, underscore).`);
7
- }
8
- return name;
9
- };
10
- class WindowExpression extends Sql {
11
- /** Alias the expression — `... AS alias` — for use in a `SELECT` list. */
12
- as(alias) {
13
- return new Sql(`${this.text} AS ${assertIdent(alias)}`);
14
- }
15
- /** `expr BETWEEN low AND high`. */
16
- between(low, high) {
17
- return new Sql(`${this.text} BETWEEN ${lit(low)} AND ${lit(high)}`);
18
- }
19
- /** `expr = value`. */
20
- eq(value) {
21
- return this.compare("=", value);
22
- }
23
- /** `expr > value`. */
24
- gt(value) {
25
- return this.compare(">", value);
26
- }
27
- /** `expr >= value`. */
28
- gte(value) {
29
- return this.compare(">=", value);
30
- }
31
- /** `expr < value`. */
32
- lt(value) {
33
- return this.compare("<", value);
34
- }
35
- /** `expr &lt;= value`. */
36
- lte(value) {
37
- return this.compare("<=", value);
38
- }
39
- compare(operator, value) {
40
- return new Sql(`${this.text} ${operator} ${lit(value)}`);
41
- }
42
- }
43
-
44
- export { WindowExpression as default };
@@ -1,82 +0,0 @@
1
- import { renderOrderTerm } from './asc-Cur-xO8v.mjs';
2
- import { lit, toText } from './Sql-DceGtcUd.mjs';
3
- import WindowExpression from './WindowExpression-Cg9s2xcr.mjs';
4
-
5
- const toArray = (value) => {
6
- if (value === void 0) {
7
- return [];
8
- }
9
- return Array.isArray(value) ? value : [value];
10
- };
11
- const renderOver = (spec) => {
12
- const clauses = [];
13
- const partitions = toArray(spec.partitionBy).map((part) => toText(part));
14
- if (partitions.length > 0) {
15
- clauses.push(`PARTITION BY ${partitions.join(", ")}`);
16
- }
17
- const orders = toArray(spec.orderBy).map((order) => renderOrderTerm(order));
18
- if (orders.length > 0) {
19
- clauses.push(`ORDER BY ${orders.join(", ")}`);
20
- }
21
- if (spec.frame !== void 0 && spec.frame.length > 0) {
22
- clauses.push(spec.frame);
23
- }
24
- return `OVER (${clauses.join(" ")})`;
25
- };
26
- const offsetArguments = (column, offset, fallback) => {
27
- const parts = [toText(column)];
28
- if (offset !== void 0) {
29
- parts.push(lit(offset));
30
- }
31
- if (fallback !== void 0) {
32
- parts.push(lit(fallback));
33
- }
34
- return parts.join(", ");
35
- };
36
- class WindowFunction {
37
- callText;
38
- constructor(callText) {
39
- this.callText = callText;
40
- }
41
- /** Attach the window frame, yielding a {@link WindowExpression}. */
42
- over(spec = {}) {
43
- return new WindowExpression(`${this.callText} ${renderOver(spec)}`);
44
- }
45
- }
46
- const windowFunction = (callText) => new WindowFunction(callText);
47
- const fn = {
48
- /** `AVG(column) OVER (...)`. */
49
- avg: (column) => windowFunction(`AVG(${toText(column)})`),
50
- /** `COUNT(column) OVER (...)` — omit the column for `COUNT(*)`. */
51
- count: (column) => windowFunction(`COUNT(${column === void 0 ? "*" : toText(column)})`),
52
- /** `CUME_DIST() OVER (...)`. */
53
- cumeDist: () => windowFunction("CUME_DIST()"),
54
- /** `DENSE_RANK() OVER (...)`. */
55
- denseRank: () => windowFunction("DENSE_RANK()"),
56
- /** `FIRST_VALUE(column) OVER (...)`. */
57
- firstValue: (column) => windowFunction(`FIRST_VALUE(${toText(column)})`),
58
- /** `LAG(column[, offset[, default]]) OVER (...)`. */
59
- lag: (column, offset, fallback) => windowFunction(`LAG(${offsetArguments(column, offset, fallback)})`),
60
- /** `LAST_VALUE(column) OVER (...)`. */
61
- lastValue: (column) => windowFunction(`LAST_VALUE(${toText(column)})`),
62
- /** `LEAD(column[, offset[, default]]) OVER (...)`. */
63
- lead: (column, offset, fallback) => windowFunction(`LEAD(${offsetArguments(column, offset, fallback)})`),
64
- /** `MAX(column) OVER (...)`. */
65
- max: (column) => windowFunction(`MAX(${toText(column)})`),
66
- /** `MIN(column) OVER (...)`. */
67
- min: (column) => windowFunction(`MIN(${toText(column)})`),
68
- /** `NTH_VALUE(column, n) OVER (...)`. */
69
- nthValue: (column, n) => windowFunction(`NTH_VALUE(${toText(column)}, ${lit(n)})`),
70
- /** `NTILE(buckets) OVER (...)`. */
71
- ntile: (buckets) => windowFunction(`NTILE(${lit(buckets)})`),
72
- /** `PERCENT_RANK() OVER (...)`. */
73
- percentRank: () => windowFunction("PERCENT_RANK()"),
74
- /** `RANK() OVER (...)`. */
75
- rank: () => windowFunction("RANK()"),
76
- /** `ROW_NUMBER() OVER (...)`. */
77
- rowNumber: () => windowFunction("ROW_NUMBER()"),
78
- /** `SUM(column) OVER (...)`. */
79
- sum: (column) => windowFunction(`SUM(${toText(column)})`)
80
- };
81
-
82
- export { WindowFunction, fn };
@@ -1,16 +0,0 @@
1
- import { Sql, toText } from './Sql-DceGtcUd.mjs';
2
-
3
- const asc = (expr) => {
4
- return { dir: "ASC", expr };
5
- };
6
- const desc = (expr) => {
7
- return { dir: "DESC", expr };
8
- };
9
- const renderOrderTerm = (term) => {
10
- if (typeof term === "string" || term instanceof Sql) {
11
- return toText(term);
12
- }
13
- return `${toText(term.expr)} ${term.dir}`;
14
- };
15
-
16
- export { asc, desc, renderOrderTerm };
@@ -1,30 +0,0 @@
1
- const ABSOLUTE_URL_RE = /^[a-z][a-z\d+\-.]*:\/\//i;
2
- const stripTrailingSlash = (value) => value.endsWith("/") ? value.slice(0, -1) : value;
3
- const stripLeadingSlash = (value) => value.startsWith("/") ? value.slice(1) : value;
4
- const serializeTransform = (transform) => Object.entries(transform).filter(([, value]) => value !== void 0 && (typeof value === "string" || typeof value === "number")).map(([key, value]) => {
5
- const serialized = String(value);
6
- if (serialized.includes(",") || serialized.includes("=")) {
7
- throw new Error(
8
- `@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
- );
10
- }
11
- return `${key}=${serialized}`;
12
- }).join(",");
13
- const encodeKey = (key) => key.split("/").map((segment) => encodeURIComponent(segment)).join("/");
14
- const buildImageDeliveryUrl = (options) => {
15
- const base = stripTrailingSlash(options.baseUrl);
16
- if (options.imageId !== void 0) {
17
- const variant = options.variant ?? "public";
18
- return `${base}/${encodeURIComponent(options.imageId)}/${encodeURIComponent(variant)}`;
19
- }
20
- if (options.key === void 0) {
21
- throw new Error("@lunora/bindings/images: buildImageDeliveryUrl requires either `imageId` or `key`");
22
- }
23
- const optionString = options.transform === void 0 ? "" : serializeTransform(options.transform);
24
- const isAbsolute = ABSOLUTE_URL_RE.test(options.key);
25
- const source = isAbsolute ? options.key : `/${encodeKey(stripLeadingSlash(options.key))}`;
26
- const prefix = optionString === "" ? "/cdn-cgi/image" : `/cdn-cgi/image/${optionString}`;
27
- return `${base}${prefix}${source.startsWith("/") ? source : `/${source}`}`;
28
- };
29
-
30
- export { buildImageDeliveryUrl };
@@ -1,113 +0,0 @@
1
- const textEncoder = new TextEncoder();
2
- const MAX_EXPIRES_IN_SECONDS = 7 * 24 * 60 * 60;
3
- const SCHEME_PREFIX_RE = /^[a-z][a-z\d+\-.]*:\/\//i;
4
- const LEADING_SLASH_RE = /^\//;
5
- const toBase64Url = (bytes) => {
6
- const binary = String.fromCodePoint(...bytes);
7
- return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
8
- };
9
- const fromBase64Url = (input) => {
10
- const padded = input.replaceAll("-", "+").replaceAll("_", "/") + "===".slice((input.length + 3) % 4);
11
- const binary = atob(padded);
12
- const bytes = new Uint8Array(binary.length);
13
- for (let index = 0; index < binary.length; index += 1) {
14
- bytes[index] = binary.codePointAt(index) ?? 0;
15
- }
16
- return bytes;
17
- };
18
- const KEY_CACHE_MAX = 64;
19
- const keyCache = /* @__PURE__ */ new Map();
20
- const importHmacKey = async (secret) => {
21
- const cached = keyCache.get(secret);
22
- if (cached) {
23
- return cached;
24
- }
25
- if (keyCache.size >= KEY_CACHE_MAX) {
26
- const oldest = keyCache.keys().next().value;
27
- if (oldest !== void 0) {
28
- keyCache.delete(oldest);
29
- }
30
- }
31
- const keyPromise = crypto.subtle.importKey("raw", textEncoder.encode(secret), { hash: "SHA-256", name: "HMAC" }, false, ["sign", "verify"]);
32
- keyCache.set(secret, keyPromise);
33
- return keyPromise;
34
- };
35
- const serializeTransform = (transform) => {
36
- if (transform === void 0) {
37
- return "";
38
- }
39
- return Object.entries(transform).filter(([, value]) => value !== void 0).toSorted(([a], [b]) => (a > b ? 1 : 0) - (a < b ? 1 : 0)).map(([key, value]) => `${key}=${typeof value === "object" ? JSON.stringify(value) : String(value)}`).join("&");
40
- };
41
- const canonicalize = (host, key, exp, transform) => `${host.toLowerCase()}
42
- ${key}
43
- ${String(exp)}
44
- ${transform}`;
45
- const extractHost = (input) => {
46
- try {
47
- return new URL(input).host;
48
- } catch {
49
- const noScheme = input.replace(SCHEME_PREFIX_RE, "");
50
- return noScheme.split("/")[0] ?? "";
51
- }
52
- };
53
- const encodeKey = (key) => key.split("/").map((segment) => encodeURIComponent(segment)).join("/");
54
- const buildSignedImageUrl = async (options) => {
55
- const expiresInSeconds = options.expiresInSeconds ?? 60 * 60;
56
- if (!Number.isFinite(expiresInSeconds) || expiresInSeconds <= 0) {
57
- throw new Error("@lunora/bindings/images: expiresInSeconds must be a positive finite number");
58
- }
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)`);
61
- }
62
- const exp = Math.floor(Date.now() / 1e3) + expiresInSeconds;
63
- const host = extractHost(options.baseUrl);
64
- const transform = serializeTransform(options.transform);
65
- const normalizedKey = options.key.replace(LEADING_SLASH_RE, "");
66
- const cryptoKey = await importHmacKey(options.secret);
67
- const signature = await crypto.subtle.sign("HMAC", cryptoKey, textEncoder.encode(canonicalize(host, normalizedKey, exp, transform)));
68
- const sig = toBase64Url(new Uint8Array(signature));
69
- const base = options.baseUrl.endsWith("/") ? options.baseUrl.slice(0, -1) : options.baseUrl;
70
- const safeKey = encodeKey(normalizedKey);
71
- const tParameter = transform === "" ? "" : `&t=${encodeURIComponent(transform)}`;
72
- return `${base}/${safeKey}?exp=${String(exp)}&sig=${sig}${tParameter}`;
73
- };
74
- const verifySignedImageUrl = async (input, secret, options) => {
75
- let url;
76
- try {
77
- url = input instanceof URL ? input : new URL(input);
78
- } catch {
79
- return { reason: "malformed", valid: false };
80
- }
81
- const expRaw = url.searchParams.get("exp");
82
- const exp = expRaw === null ? Number.NaN : Number(expRaw);
83
- const sig = url.searchParams.get("sig");
84
- const transform = url.searchParams.get("t") ?? "";
85
- if (!sig || !Number.isInteger(exp)) {
86
- return { reason: "malformed", valid: false };
87
- }
88
- if (exp < Math.floor(Date.now() / 1e3)) {
89
- return { reason: "expired", valid: false };
90
- }
91
- let key;
92
- let sigBytes;
93
- try {
94
- key = url.pathname.replace(LEADING_SLASH_RE, "").split("/").map((segment) => decodeURIComponent(segment)).join("/");
95
- sigBytes = fromBase64Url(sig);
96
- } catch {
97
- return { reason: "malformed", valid: false };
98
- }
99
- const host = options?.expectedHost === void 0 ? url.host : extractHost(options.expectedHost);
100
- const cryptoKey = await importHmacKey(secret);
101
- const valid = await crypto.subtle.verify(
102
- "HMAC",
103
- cryptoKey,
104
- sigBytes,
105
- textEncoder.encode(canonicalize(host, key, exp, transform))
106
- );
107
- if (!valid) {
108
- return { reason: "bad_signature", valid: false };
109
- }
110
- return { key, transform: transform === "" ? void 0 : transform, valid: true };
111
- };
112
-
113
- export { buildSignedImageUrl, verifySignedImageUrl };
@@ -1,23 +0,0 @@
1
- const UPSERT_EMBED_CONCURRENCY = 8;
2
- const concurrentMap = async (items, limit, function_) => {
3
- if (items.length === 0) {
4
- return [];
5
- }
6
- const effectiveLimit = Math.max(1, Math.min(limit, items.length));
7
- const results = Array.from({ length: items.length });
8
- let cursor = 0;
9
- const workers = Array.from({ length: effectiveLimit }, async () => {
10
- for (; ; ) {
11
- const index = cursor;
12
- cursor += 1;
13
- if (index >= items.length) {
14
- return;
15
- }
16
- results[index] = await function_(items[index], index);
17
- }
18
- });
19
- await Promise.all(workers);
20
- return results;
21
- };
22
-
23
- export { UPSERT_EMBED_CONCURRENCY as U, concurrentMap as c };
@@ -1,57 +0,0 @@
1
- const MAX_BLOBS = 20;
2
- const MAX_DOUBLES = 20;
3
- const MAX_INDEXES = 1;
4
- const MAX_BLOB_BYTES = 16 * 1024;
5
- const MAX_INDEX_BYTES = 96;
6
- const TEXT_ENCODER = new TextEncoder();
7
- const byteLengthOf = (value) => {
8
- if (value === null) {
9
- return 0;
10
- }
11
- if (typeof value === "string") {
12
- return TEXT_ENCODER.encode(value).length;
13
- }
14
- return value.byteLength;
15
- };
16
- const assertWithin = (kind, length, max) => {
17
- if (length > max) {
18
- throw new RangeError(`@lunora/bindings/analytics: a data point may carry at most ${String(max)} ${kind} (got ${String(length)}).`);
19
- }
20
- };
21
- const assertByteBudget = (kind, bytes, max) => {
22
- if (bytes > max) {
23
- throw new RangeError(`@lunora/bindings/analytics: a data point's ${kind} may total at most ${String(max)} bytes (got ${String(bytes)}).`);
24
- }
25
- };
26
- const createAnalytics = (binding) => {
27
- const writeDataPoint = (event) => {
28
- assertWithin("blobs", event.blobs?.length ?? 0, MAX_BLOBS);
29
- assertWithin("doubles", event.doubles?.length ?? 0, MAX_DOUBLES);
30
- assertWithin("indexes", event.indexes?.length ?? 0, MAX_INDEXES);
31
- const blobBytes = (event.blobs ?? []).reduce((total, blob) => total + byteLengthOf(blob), 0);
32
- assertByteBudget("blobs", blobBytes, MAX_BLOB_BYTES);
33
- for (const index of event.indexes ?? []) {
34
- assertByteBudget("index", byteLengthOf(index), MAX_INDEX_BYTES);
35
- }
36
- binding.writeDataPoint(event);
37
- };
38
- const track = (name, event = {}) => {
39
- const dimensionEntries = Object.entries(event.dimensions ?? {});
40
- const metricEntries = Object.entries(event.metrics ?? {});
41
- const blobs = [name, ...dimensionEntries.map(([, value]) => value)];
42
- const doubles = metricEntries.map(([, value]) => value);
43
- const indexes = event.index === void 0 ? [] : [event.index];
44
- writeDataPoint({ blobs, doubles, indexes });
45
- const dimensions = dimensionEntries.map(([field], offset) => {
46
- return { column: `blob${String(offset + 2)}`, field };
47
- });
48
- const metrics = metricEntries.map(([field], offset) => {
49
- return { column: `double${String(offset + 1)}`, field };
50
- });
51
- const index = event.index === void 0 ? null : { column: "index1", field: "index" };
52
- return { dimensions, index, metrics, name };
53
- };
54
- return { track, writeDataPoint };
55
- };
56
-
57
- export { createAnalytics };