@lunora/do 0.0.0 → 1.0.0-alpha.1

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 (47) hide show
  1. package/LICENSE.md +105 -0
  2. package/README.md +115 -9
  3. package/__assets__/package-og.svg +14 -0
  4. package/dist/index.d.mts +5599 -0
  5. package/dist/index.d.ts +5599 -0
  6. package/dist/index.mjs +35 -0
  7. package/dist/packem_shared/ADMIN_FUNCTION_PREFIX-Dzdqq5J2.mjs +313 -0
  8. package/dist/packem_shared/AUTH_METRICS_BUCKET_MS-CiHHYeJi.mjs +84 -0
  9. package/dist/packem_shared/ConflictError-C0STs6bU.mjs +13 -0
  10. package/dist/packem_shared/CountRlsUnsupportedError-28ZvvwKS.mjs +133 -0
  11. package/dist/packem_shared/DATA_MIGRATION_STATE_TABLE-PTtTiQ7U.mjs +237 -0
  12. package/dist/packem_shared/LogBuffer-B_Ezju_N.mjs +37 -0
  13. package/dist/packem_shared/NotFoundError-CMuMZt81.mjs +10 -0
  14. package/dist/packem_shared/ROOT_DO_SIZE_WARN_BYTES-DQkmGiCS.mjs +4009 -0
  15. package/dist/packem_shared/ReactiveCache-ByVzgH3d.mjs +259 -0
  16. package/dist/packem_shared/SCAN_DEP-DLJF8dsj.mjs +19 -0
  17. package/dist/packem_shared/SESSION_DO_TTL_DEFAULT-ilPZsVwu.mjs +180 -0
  18. package/dist/packem_shared/SHARD_REGISTRY_DO_NAME-BsAbi5Mn.mjs +146 -0
  19. package/dist/packem_shared/aggregateTableName-CxNqY1Sl.mjs +64 -0
  20. package/dist/packem_shared/applyCdcChanges-Ctdmxmrv.mjs +103 -0
  21. package/dist/packem_shared/applyOnDelete-CMif2RKw.mjs +165 -0
  22. package/dist/packem_shared/armRestore-BJk53Ro8.mjs +55 -0
  23. package/dist/packem_shared/assertFlatPredicate-DyVYReuT.mjs +160 -0
  24. package/dist/packem_shared/assertReadonly-dDcFE1YZ.mjs +29 -0
  25. package/dist/packem_shared/assertValidClientId-CBZ1zC96.mjs +1745 -0
  26. package/dist/packem_shared/backfillAggregateIndexes-BF5eL7kW.mjs +80 -0
  27. package/dist/packem_shared/buildSecurityAudit-CCAvoFlr.mjs +1 -0
  28. package/dist/packem_shared/buildSeekWhere-lVsNXSLy.mjs +84 -0
  29. package/dist/packem_shared/clearCapturedMail-CPpgl-dX.mjs +104 -0
  30. package/dist/packem_shared/compileWhereSql-CXrhFA3G.mjs +127 -0
  31. package/dist/packem_shared/createSystemReader-8CzSZP9V.mjs +80 -0
  32. package/dist/packem_shared/ctx-db-idempotency-DkC9rP91.mjs +35 -0
  33. package/dist/packem_shared/do-exec-5eQy5cEi.mjs +12 -0
  34. package/dist/packem_shared/do-sql-BCHCWtrD.mjs +87 -0
  35. package/dist/packem_shared/encodePartitionKey-C6blLR5K.mjs +1 -0
  36. package/dist/packem_shared/ensureFunctionMetricsTables-UDNVD7FS.mjs +248 -0
  37. package/dist/packem_shared/exportShardRows-DZEhUeyI.mjs +156 -0
  38. package/dist/packem_shared/ftsTableName-BLEMawrp.mjs +38 -0
  39. package/dist/packem_shared/guardWriter-u3UlnCH5.mjs +128 -0
  40. package/dist/packem_shared/matchesStaticWhere-CFk6adSu.mjs +54 -0
  41. package/dist/packem_shared/rank-CrkEIpF4.mjs +102 -0
  42. package/dist/packem_shared/renderSql-D6eUcn2N.mjs +16 -0
  43. package/dist/packem_shared/runShardMigrations-C3bn5r93.mjs +103 -0
  44. package/dist/packem_shared/runTriggers-5N6_Fx0A.mjs +20 -0
  45. package/dist/packem_shared/security-audit-CucgBice.mjs +158 -0
  46. package/dist/packem_shared/serveRelationFanout-Clr1a05L.mjs +24 -0
  47. package/package.json +41 -17
@@ -0,0 +1,80 @@
1
+ import { sql } from 'drizzle-orm';
2
+ import { matchesStaticWhere } from './matchesStaticWhere-CFk6adSu.mjs';
3
+ import { encodeAggregateKey, foldAggregateTally, aggregateTableName } from './aggregateTableName-CxNqY1Sl.mjs';
4
+ import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
5
+ import { D as DOC_COLUMN, r as rowToDocument, A as AGG_KEY, a as AGG_VALUE, b as AGG_COUNT } from './do-sql-BCHCWtrD.mjs';
6
+ import { param } from './renderSql-D6eUcn2N.mjs';
7
+ import { s as sortColumnName, m as matchesRankStaticWhere, e as encodePartitionKey, b as serializeSqlValue, r as rankTableName } from './rank-CrkEIpF4.mjs';
8
+
9
+ const backfillAggregateIndex = (sql$1, tableName, index) => {
10
+ const aggTable = aggregateTableName(tableName, index.name);
11
+ const existing = runDrizzle(sql$1, sql`SELECT COUNT(*) AS count FROM ${sql.identifier(aggTable)}`).one();
12
+ if (existing.count > 0) {
13
+ return;
14
+ }
15
+ const by = index.by ?? [];
16
+ const tallies = /* @__PURE__ */ new Map();
17
+ const rows = runDrizzle(sql$1, sql`SELECT id, _creationTime, ${sql.identifier(DOC_COLUMN)} FROM ${sql.identifier(tableName)}`).toArray();
18
+ for (const row of rows) {
19
+ const record = rowToDocument(row);
20
+ if (!record || index.where && !matchesStaticWhere(record, index.where)) {
21
+ continue;
22
+ }
23
+ const encoded = encodeAggregateKey(by, record);
24
+ foldAggregateTally(tallies, encoded, index, record);
25
+ }
26
+ for (const [encoded, tally] of tallies) {
27
+ runDrizzle(
28
+ sql$1,
29
+ sql`INSERT INTO ${sql.identifier(aggTable)} (${AGG_KEY}, ${AGG_VALUE}, ${AGG_COUNT}) VALUES (${encoded}, ${tally.value}, ${tally.count})`
30
+ );
31
+ }
32
+ };
33
+ const backfillAggregateIndexes = (sql, schema) => {
34
+ for (const [tableName, definition] of Object.entries(schema.tables)) {
35
+ if (definition.shardMode?.kind === "global" || !definition.aggregateIndexes) {
36
+ continue;
37
+ }
38
+ for (const index of definition.aggregateIndexes) {
39
+ backfillAggregateIndex(sql, tableName, index);
40
+ }
41
+ }
42
+ };
43
+ const backfillRankIndex = (sql$1, tableName, index) => {
44
+ const rankTable = rankTableName(tableName, index.name);
45
+ const existing = runDrizzle(sql$1, sql`SELECT COUNT(*) AS count FROM ${sql.identifier(rankTable)}`).one();
46
+ if (existing.count > 0) {
47
+ return;
48
+ }
49
+ const sortColumns = index.sortBy.map((_, i) => sortColumnName(i));
50
+ const columnsSql = sql.join(
51
+ ["__id__", "__partition__", ...sortColumns].map((column) => sql.identifier(column)),
52
+ sql`, `
53
+ );
54
+ const rows = runDrizzle(sql$1, sql`SELECT id, _creationTime, ${sql.identifier(DOC_COLUMN)} FROM ${sql.identifier(tableName)}`).toArray();
55
+ for (const row of rows) {
56
+ const record = rowToDocument(row);
57
+ if (!record || index.where && !matchesRankStaticWhere(record, index.where)) {
58
+ continue;
59
+ }
60
+ const partitionKey = encodePartitionKey(index.partitionBy ?? [], record);
61
+ const sortValues = index.sortBy.map((key) => serializeSqlValue(record[key.field] ?? null));
62
+ const valuesSql = sql.join(
63
+ [record["_id"], partitionKey, ...sortValues].map((value) => param(value)),
64
+ sql`, `
65
+ );
66
+ runDrizzle(sql$1, sql`INSERT INTO ${sql.identifier(rankTable)} (${columnsSql}) VALUES (${valuesSql})`);
67
+ }
68
+ };
69
+ const backfillRankIndexes = (sql, schema) => {
70
+ for (const [tableName, definition] of Object.entries(schema.tables)) {
71
+ if (definition.shardMode?.kind === "global" || !definition.rankIndexes) {
72
+ continue;
73
+ }
74
+ for (const index of definition.rankIndexes) {
75
+ backfillRankIndex(sql, tableName, index);
76
+ }
77
+ }
78
+ };
79
+
80
+ export { backfillAggregateIndexes, backfillRankIndexes };
@@ -0,0 +1 @@
1
+ export { M as MIN_ADMIN_TOKEN_LENGTH, a as MIN_AUTH_SECRET_LENGTH, b as buildSecurityAudit } from './security-audit-CucgBice.mjs';
@@ -0,0 +1,84 @@
1
+ const TIEBREAK_FIELD = "id";
2
+ const ID_FIELDS = /* @__PURE__ */ new Set(["_id", "id"]);
3
+ const normalizeOrderKeys = (orderBy) => {
4
+ const keys = [];
5
+ for (const entry of orderBy ?? []) {
6
+ for (const [field, direction] of Object.entries(entry)) {
7
+ keys.push({ direction, field });
8
+ }
9
+ }
10
+ if (keys.length === 0) {
11
+ return [{ direction: "asc", field: "_creationTime" }];
12
+ }
13
+ return keys;
14
+ };
15
+ const toBase64 = (text) => {
16
+ const bytes = new TextEncoder().encode(text);
17
+ let binary = "";
18
+ for (const byte of bytes) {
19
+ binary += String.fromCodePoint(byte);
20
+ }
21
+ return btoa(binary);
22
+ };
23
+ const fromBase64 = (encoded) => {
24
+ const binary = atob(encoded);
25
+ const bytes = Uint8Array.from(binary, (character) => character.codePointAt(0) ?? 0);
26
+ return new TextDecoder().decode(bytes);
27
+ };
28
+ const encodeCursor = (record, keys) => {
29
+ const values = keys.map((key) => record[key.field]);
30
+ values.push(record["_id"]);
31
+ return toBase64(JSON.stringify(values));
32
+ };
33
+ const invalidCursor = () => Object.assign(new TypeError("invalid cursor"), { code: "BAD_REQUEST", name: "LunoraError", status: 400 });
34
+ const decodeCursor = (cursor) => {
35
+ let decoded;
36
+ try {
37
+ decoded = JSON.parse(fromBase64(cursor));
38
+ } catch {
39
+ throw invalidCursor();
40
+ }
41
+ if (!Array.isArray(decoded)) {
42
+ throw invalidCursor();
43
+ }
44
+ return decoded;
45
+ };
46
+ const buildSeekWhere = (keys, cursorValues) => {
47
+ const columns = keys.some((key) => ID_FIELDS.has(key.field)) ? keys : [...keys, { direction: "asc", field: TIEBREAK_FIELD }];
48
+ const branches = [];
49
+ for (const [pivot, pivotColumn] of columns.entries()) {
50
+ const conditions = [];
51
+ for (const [prefix, prefixColumn] of columns.slice(0, pivot).entries()) {
52
+ conditions.push({ [prefixColumn.field]: { eq: cursorValues[prefix] } });
53
+ }
54
+ const strictOperator = pivotColumn.direction === "desc" ? "lt" : "gt";
55
+ conditions.push({ [pivotColumn.field]: { [strictOperator]: cursorValues[pivot] } });
56
+ const [first] = conditions;
57
+ branches.push(conditions.length === 1 && first !== void 0 ? first : { AND: conditions });
58
+ }
59
+ return { OR: branches };
60
+ };
61
+ const seekBeforeOperator = (direction, isFinal) => {
62
+ if (direction === "desc") {
63
+ return isFinal ? "gte" : "gt";
64
+ }
65
+ return isFinal ? "lte" : "lt";
66
+ };
67
+ const buildSeekBeforeWhere = (keys, cursorValues) => {
68
+ const columns = keys.some((key) => ID_FIELDS.has(key.field)) ? keys : [...keys, { direction: "asc", field: TIEBREAK_FIELD }];
69
+ const branches = [];
70
+ for (const [pivot, pivotColumn] of columns.entries()) {
71
+ const conditions = [];
72
+ for (const [prefix, prefixColumn] of columns.slice(0, pivot).entries()) {
73
+ conditions.push({ [prefixColumn.field]: { eq: cursorValues[prefix] } });
74
+ }
75
+ const isFinal = pivot === columns.length - 1;
76
+ const operator = seekBeforeOperator(pivotColumn.direction, isFinal);
77
+ conditions.push({ [pivotColumn.field]: { [operator]: cursorValues[pivot] } });
78
+ const [first] = conditions;
79
+ branches.push(conditions.length === 1 && first !== void 0 ? first : { AND: conditions });
80
+ }
81
+ return { OR: branches };
82
+ };
83
+
84
+ export { buildSeekBeforeWhere, buildSeekWhere, decodeCursor, encodeCursor, normalizeOrderKeys };
@@ -0,0 +1,104 @@
1
+ const MAIL_TABLE = "__lunora_mail";
2
+ const MAIL_RETENTION = 500;
3
+ const MAX_BODY_CHARS = 256 * 1024;
4
+ const capBody = (value) => value !== void 0 && value.length > MAX_BODY_CHARS ? `${value.slice(0, MAX_BODY_CHARS)}
5
+ … [truncated by the dev mail catcher]` : value;
6
+ const runSql = (sql, query, ...params) => {
7
+ const runner = sql.exec;
8
+ return runner.call(sql, query, ...params);
9
+ };
10
+ const orNull = (value) => (
11
+ // eslint-disable-next-line unicorn/no-null -- SQL NULL is the correct value for an absent column.
12
+ value ?? null
13
+ );
14
+ const encode = (value) => {
15
+ if (value === void 0) {
16
+ return null;
17
+ }
18
+ return JSON.stringify(value);
19
+ };
20
+ const decode = (value) => {
21
+ if (value === null || value === void 0 || value === "") {
22
+ return void 0;
23
+ }
24
+ try {
25
+ return JSON.parse(value);
26
+ } catch {
27
+ return void 0;
28
+ }
29
+ };
30
+ const ensureMailTable = (sql) => {
31
+ runSql(
32
+ sql,
33
+ `CREATE TABLE IF NOT EXISTS "${MAIL_TABLE}" (
34
+ id TEXT PRIMARY KEY,
35
+ captured_at INTEGER NOT NULL,
36
+ from_addr TEXT,
37
+ to_addrs TEXT NOT NULL,
38
+ cc_addrs TEXT,
39
+ bcc_addrs TEXT,
40
+ reply_to TEXT,
41
+ subject TEXT NOT NULL,
42
+ html TEXT,
43
+ body_text TEXT,
44
+ headers TEXT
45
+ )`
46
+ );
47
+ };
48
+ const recordCapturedMail = (sql, input, capturedAt) => {
49
+ ensureMailTable(sql);
50
+ const id = crypto.randomUUID();
51
+ runSql(
52
+ sql,
53
+ `INSERT INTO "${MAIL_TABLE}" (id, captured_at, from_addr, to_addrs, cc_addrs, bcc_addrs, reply_to, subject, html, body_text, headers)
54
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
55
+ id,
56
+ capturedAt,
57
+ orNull(input.from),
58
+ JSON.stringify(input.to),
59
+ encode(input.cc),
60
+ encode(input.bcc),
61
+ orNull(input.replyTo),
62
+ input.subject,
63
+ orNull(capBody(input.html)),
64
+ orNull(capBody(input.text)),
65
+ encode(input.headers)
66
+ );
67
+ runSql(
68
+ sql,
69
+ `DELETE FROM "${MAIL_TABLE}"
70
+ WHERE id NOT IN (
71
+ SELECT id FROM "${MAIL_TABLE}" ORDER BY captured_at DESC, id DESC LIMIT ?
72
+ )`,
73
+ MAIL_RETENTION
74
+ );
75
+ return { id };
76
+ };
77
+ const readCapturedMail = (sql, options = {}) => {
78
+ ensureMailTable(sql);
79
+ const limit = Math.min(Math.max(options.limit ?? 50, 1), MAIL_RETENTION);
80
+ const rows = runSql(sql, `SELECT * FROM "${MAIL_TABLE}" ORDER BY captured_at DESC, id DESC LIMIT ?`, limit).toArray();
81
+ const entries = rows.map((row) => {
82
+ return {
83
+ bcc: decode(row.bcc_addrs),
84
+ capturedAt: row.captured_at,
85
+ cc: decode(row.cc_addrs),
86
+ from: row.from_addr ?? void 0,
87
+ headers: decode(row.headers),
88
+ html: row.html ?? void 0,
89
+ id: row.id,
90
+ replyTo: row.reply_to ?? void 0,
91
+ subject: row.subject,
92
+ text: row.body_text ?? void 0,
93
+ to: decode(row.to_addrs) ?? row.to_addrs
94
+ };
95
+ });
96
+ return { entries };
97
+ };
98
+ const clearCapturedMail = (sql) => {
99
+ ensureMailTable(sql);
100
+ runSql(sql, `DELETE FROM "${MAIL_TABLE}"`);
101
+ return { cleared: true };
102
+ };
103
+
104
+ export { MAIL_RETENTION, MAIL_TABLE, MAX_BODY_CHARS, clearCapturedMail, ensureMailTable, readCapturedMail, recordCapturedMail };
@@ -0,0 +1,127 @@
1
+ import { sql } from 'drizzle-orm';
2
+
3
+ const RELATION_EXISTS_KEY = "__relationExists";
4
+ const OPERATOR_KEYS = ["eq", "ne", "lt", "lte", "gt", "gte", "in", "notIn", "isNull", "contains"];
5
+ const OPERATOR_KEY_SET = new Set(OPERATOR_KEYS);
6
+ const BINARY_COMPARATORS = { eq: "=", gt: ">", gte: ">=", lt: "<", lte: "<=", ne: "<>" };
7
+ const isOperatorObject = (value) => {
8
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
9
+ return false;
10
+ }
11
+ const keys = Object.keys(value);
12
+ return keys.length > 0 && keys.every((key) => OPERATOR_KEY_SET.has(key));
13
+ };
14
+ const compileContains = (reference, value, strategy) => {
15
+ const term = sql`${strategy.serialize(value)}`;
16
+ return strategy.likeContains ? strategy.likeContains(reference, term) : sql`${reference} LIKE '%' || ${term} || '%'`;
17
+ };
18
+ const compileComparator = (reference, operator, comparator, value, strategy) => {
19
+ if (value === null) {
20
+ return operator === "ne" ? sql`${reference} IS NOT NULL` : sql`${reference} IS NULL`;
21
+ }
22
+ return sql`${reference} ${sql.raw(comparator)} ${strategy.serialize(value)}`;
23
+ };
24
+ const compileInList = (reference, keyword, value, strategy) => {
25
+ const items = Array.isArray(value) ? value : [];
26
+ if (items.length === 0) {
27
+ return keyword === "IN" ? sql`0 = 1` : sql`1 = 1`;
28
+ }
29
+ const list = sql.join(
30
+ items.map((item) => sql`${strategy.serialize(item)}`),
31
+ sql`, `
32
+ );
33
+ return keyword === "IN" ? sql`${reference} IN (${list})` : sql`${reference} NOT IN (${list})`;
34
+ };
35
+ const compileFieldOperators = (reference, operators, strategy) => {
36
+ const record = operators;
37
+ const clauses = [];
38
+ for (const operator of OPERATOR_KEYS) {
39
+ if (!(operator in record)) {
40
+ continue;
41
+ }
42
+ const value = record[operator];
43
+ const comparator = BINARY_COMPARATORS[operator];
44
+ if (comparator) {
45
+ clauses.push(compileComparator(reference, operator, comparator, value, strategy));
46
+ } else if (operator === "isNull") {
47
+ clauses.push(value ? sql`${reference} IS NULL` : sql`${reference} IS NOT NULL`);
48
+ } else if (operator === "contains") {
49
+ clauses.push(compileContains(reference, value, strategy));
50
+ } else {
51
+ clauses.push(compileInList(reference, operator === "in" ? "IN" : "NOT IN", value, strategy));
52
+ }
53
+ }
54
+ return clauses;
55
+ };
56
+ const compileField = (field, value, strategy) => {
57
+ const reference = strategy.fieldRef(field);
58
+ if (isOperatorObject(value)) {
59
+ return compileFieldOperators(reference, value, strategy);
60
+ }
61
+ if (value === null) {
62
+ return [sql`${reference} IS NULL`];
63
+ }
64
+ return [sql`${reference} = ${strategy.serialize(value)}`];
65
+ };
66
+ const joinClauses = (clauses, connector) => {
67
+ if (clauses.length === 0) {
68
+ return void 0;
69
+ }
70
+ if (clauses.length === 1) {
71
+ return clauses[0];
72
+ }
73
+ return sql.join(
74
+ clauses.map((clause) => sql`(${clause})`),
75
+ sql` ${sql.raw(connector)} `
76
+ );
77
+ };
78
+ const compileGroup = (value, connector, strategy) => {
79
+ const branches = Array.isArray(value) ? value : [];
80
+ const parts = [];
81
+ for (const branch of branches) {
82
+ const compiled = compileNode(branch ?? {}, strategy);
83
+ if (compiled) {
84
+ parts.push(compiled);
85
+ }
86
+ }
87
+ if (parts.length === 0) {
88
+ return connector === "OR" ? sql`0 = 1` : void 0;
89
+ }
90
+ return joinClauses(parts, connector);
91
+ };
92
+ const STRUCTURAL_KEYS = /* @__PURE__ */ new Set(["AND", "NOT", "OR", RELATION_EXISTS_KEY]);
93
+ const compileStructuralKey = (key, value, strategy) => {
94
+ if (key === RELATION_EXISTS_KEY) {
95
+ if (!strategy.relationExists) {
96
+ throw new Error("encountered a relation EXISTS marker without a relationExists strategy hook");
97
+ }
98
+ return strategy.relationExists(value);
99
+ }
100
+ if (key === "NOT") {
101
+ const inner = compileNode(value ?? {}, strategy);
102
+ return inner ? sql`NOT (${inner})` : void 0;
103
+ }
104
+ return compileGroup(value, key, strategy);
105
+ };
106
+ const compileNode = (where, strategy) => {
107
+ const clauses = [];
108
+ for (const [key, value] of Object.entries(where)) {
109
+ if (STRUCTURAL_KEYS.has(key)) {
110
+ const compiled = compileStructuralKey(key, value, strategy);
111
+ if (compiled) {
112
+ clauses.push(compiled);
113
+ }
114
+ } else {
115
+ clauses.push(...compileField(key, value, strategy));
116
+ }
117
+ }
118
+ return joinClauses(clauses, "AND");
119
+ };
120
+ const compileWhereSql = (where, strategy) => {
121
+ if (!where || Object.keys(where).length === 0) {
122
+ return void 0;
123
+ }
124
+ return compileNode(where, strategy);
125
+ };
126
+
127
+ export { compileWhereSql };
@@ -0,0 +1,80 @@
1
+ const toScheduledFunctionDoc = (record) => {
2
+ const doc = {
3
+ args: record["args"] ?? {},
4
+ enqueuedAt: typeof record["enqueuedAt"] === "number" ? record["enqueuedAt"] : 0,
5
+ functionPath: typeof record["functionPath"] === "string" ? record["functionPath"] : "",
6
+ id: typeof record["id"] === "string" ? record["id"] : "",
7
+ scheduledFor: typeof record["scheduledFor"] === "number" ? record["scheduledFor"] : 0
8
+ };
9
+ if (typeof record["attempts"] === "number") {
10
+ doc.attempts = record["attempts"];
11
+ }
12
+ if (typeof record["shardKey"] === "string") {
13
+ doc.shardKey = record["shardKey"];
14
+ }
15
+ return doc;
16
+ };
17
+ const toStorageMetadata = (object) => {
18
+ const meta = {
19
+ key: typeof object["key"] === "string" ? object["key"] : "",
20
+ size: typeof object["size"] === "number" ? object["size"] : 0
21
+ };
22
+ const contentType = object["httpMetadata"]?.contentType ?? object["contentType"];
23
+ if (typeof contentType === "string") {
24
+ meta.contentType = contentType;
25
+ }
26
+ if (object["customMetadata"] && typeof object["customMetadata"] === "object") {
27
+ meta.customMetadata = object["customMetadata"];
28
+ }
29
+ if (typeof object["sha256"] === "string") {
30
+ meta.sha256 = object["sha256"];
31
+ }
32
+ const { uploaded } = object;
33
+ if (typeof uploaded === "number") {
34
+ meta.uploaded = uploaded;
35
+ } else if (uploaded instanceof Date) {
36
+ meta.uploaded = uploaded.getTime();
37
+ }
38
+ return meta;
39
+ };
40
+ const createSystemReader = (options = {}) => {
41
+ const requireScheduler = () => {
42
+ if (!options.scheduler) {
43
+ throw new Error('ctx.db.system.query("_scheduled_functions"): no scheduler configured. Pass `scheduler` to createShardDO().');
44
+ }
45
+ return options.scheduler;
46
+ };
47
+ const requireStorage = () => {
48
+ if (!options.storage) {
49
+ throw new Error('ctx.db.system.query("_storage"): no storage configured. Pass `storage` to createShardDO().');
50
+ }
51
+ return options.storage;
52
+ };
53
+ const collectScheduled = async () => {
54
+ const records = await requireScheduler().list();
55
+ return records.map((record) => toScheduledFunctionDoc(record));
56
+ };
57
+ const collectStorage = async () => {
58
+ const { objects } = await requireStorage().list();
59
+ return objects.map((object) => toStorageMetadata(object));
60
+ };
61
+ const queryReader = ((table) => {
62
+ if (table === "_scheduled_functions") {
63
+ return { collect: collectScheduled };
64
+ }
65
+ return { collect: collectStorage };
66
+ });
67
+ return {
68
+ get: (async (table, id) => {
69
+ if (table === "_scheduled_functions") {
70
+ const record = await requireScheduler().get(id);
71
+ return record ? toScheduledFunctionDoc(record) : null;
72
+ }
73
+ const meta = await requireStorage().getMetadata(id);
74
+ return meta ? toStorageMetadata(meta) : null;
75
+ }),
76
+ query: queryReader
77
+ };
78
+ };
79
+
80
+ export { createSystemReader };
@@ -0,0 +1,35 @@
1
+ import { sql } from 'drizzle-orm';
2
+ import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
3
+
4
+ const IDEMPOTENCY_TABLE = "__idempotency";
5
+ const migrateIdempotency = (sql$1) => {
6
+ runDrizzle(
7
+ sql$1,
8
+ sql`CREATE TABLE IF NOT EXISTS ${sql.identifier(IDEMPOTENCY_TABLE)} (
9
+ identity TEXT NOT NULL,
10
+ mutation_id TEXT NOT NULL,
11
+ result_json TEXT NOT NULL,
12
+ ts REAL NOT NULL,
13
+ PRIMARY KEY (identity, mutation_id)
14
+ )`
15
+ );
16
+ };
17
+ const readIdempotent = (sql$1, identity, mutationId) => {
18
+ const rows = runDrizzle(
19
+ sql$1,
20
+ sql`SELECT result_json, ts FROM ${sql.identifier(IDEMPOTENCY_TABLE)} WHERE identity = ${identity} AND mutation_id = ${mutationId} LIMIT 1`
21
+ ).toArray();
22
+ const row = rows[0];
23
+ return row === void 0 ? void 0 : { resultJson: row.result_json, ts: row.ts };
24
+ };
25
+ const writeIdempotent = (sql$1, identity, mutationId, resultJson, ts) => {
26
+ runDrizzle(
27
+ sql$1,
28
+ sql`INSERT OR IGNORE INTO ${sql.identifier(IDEMPOTENCY_TABLE)} (identity, mutation_id, result_json, ts) VALUES (${identity}, ${mutationId}, ${resultJson}, ${ts})`
29
+ );
30
+ };
31
+ const trimIdempotent = (sql$1, olderThanTs) => {
32
+ runDrizzle(sql$1, sql`DELETE FROM ${sql.identifier(IDEMPOTENCY_TABLE)} WHERE ts < ${olderThanTs}`);
33
+ };
34
+
35
+ export { IDEMPOTENCY_TABLE as I, migrateIdempotency as m, readIdempotent as r, trimIdempotent as t, writeIdempotent as w };
@@ -0,0 +1,12 @@
1
+ import { renderSql } from './renderSql-D6eUcn2N.mjs';
2
+
3
+ const runSql = (sql, query, ...params) => {
4
+ const runner = sql.exec;
5
+ return runner.call(sql, query, ...params);
6
+ };
7
+ const runDrizzle = (exec, query) => {
8
+ const { params, sql: text } = renderSql("sqlite", query);
9
+ return runSql(exec, text, ...params);
10
+ };
11
+
12
+ export { runDrizzle as r };
@@ -0,0 +1,87 @@
1
+ import { sql } from 'drizzle-orm';
2
+ import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
3
+
4
+ const DOC_COLUMN = "__doc__";
5
+ const quoteIdentifier = (name) => `"${name.replaceAll('"', '""')}"`;
6
+ const jsonPath = (field) => {
7
+ if (field === "_id" || field === "id") {
8
+ return "id";
9
+ }
10
+ if (field === "_creationTime") {
11
+ return "_creationTime";
12
+ }
13
+ return `json_extract(${DOC_COLUMN}, '$.${field.replaceAll("'", "''")}')`;
14
+ };
15
+ const jsonPathSql = (field) => sql.raw(jsonPath(field));
16
+ const qualifiedJsonPath = (table, field) => {
17
+ const qualified = quoteIdentifier(table);
18
+ if (field === "_id" || field === "id") {
19
+ return `${qualified}.id`;
20
+ }
21
+ if (field === "_creationTime") {
22
+ return `${qualified}._creationTime`;
23
+ }
24
+ return `json_extract(${qualified}.${DOC_COLUMN}, '$.${field.replaceAll("'", "''")}')`;
25
+ };
26
+ const qualifiedJsonPathSql = (table, field) => sql.raw(qualifiedJsonPath(table, field));
27
+ const createIndexSql = (name, table, columns, unique) => sql`CREATE ${unique ? sql`UNIQUE ` : sql``}INDEX IF NOT EXISTS ${sql.identifier(name)} ON ${sql.identifier(table)} (${columns})`;
28
+ const AGG_KEY = sql.identifier("__key__");
29
+ const AGG_VALUE = sql.identifier("__value__");
30
+ const AGG_COUNT = sql.identifier("__count__");
31
+ const aggUpsertSql = (aggTable, key, value, count, set) => sql`INSERT INTO ${sql.identifier(aggTable)} (${AGG_KEY}, ${AGG_VALUE}, ${AGG_COUNT}) VALUES (${key}, ${value}, ${count}) ON CONFLICT(${AGG_KEY}) DO UPDATE SET ${set}`;
32
+ const tableColumns = (definition) => {
33
+ const columns = [];
34
+ for (const [field, validator] of Object.entries(definition.shape)) {
35
+ const column = validator._meta?.column;
36
+ if (column) {
37
+ columns.push([field, column]);
38
+ }
39
+ }
40
+ return columns;
41
+ };
42
+ const rowToDocument = (row) => {
43
+ if (!row) {
44
+ return void 0;
45
+ }
46
+ const raw = row[DOC_COLUMN];
47
+ let parsed;
48
+ if (typeof raw === "string") {
49
+ parsed = JSON.parse(raw);
50
+ } else if (raw && typeof raw === "object") {
51
+ parsed = raw;
52
+ } else {
53
+ parsed = {};
54
+ }
55
+ const { id } = row;
56
+ if (typeof id === "string") {
57
+ parsed["_id"] = id;
58
+ }
59
+ const creationTime = row["_creationTime"];
60
+ if (typeof creationTime === "number") {
61
+ parsed["_creationTime"] = creationTime;
62
+ }
63
+ return parsed;
64
+ };
65
+ const ftsAvailabilityCache = /* @__PURE__ */ new WeakMap();
66
+ const isFtsAvailable = (sql$1) => {
67
+ const cached = ftsAvailabilityCache.get(sql$1);
68
+ if (cached !== void 0) {
69
+ return cached;
70
+ }
71
+ let available;
72
+ try {
73
+ runDrizzle(sql$1, sql`CREATE VIRTUAL TABLE IF NOT EXISTS ${sql.identifier("__lunora_fts_probe")} USING fts5(x)`);
74
+ available = true;
75
+ } catch {
76
+ available = false;
77
+ } finally {
78
+ try {
79
+ runDrizzle(sql$1, sql`DROP TABLE IF EXISTS ${sql.identifier("__lunora_fts_probe")}`);
80
+ } catch {
81
+ }
82
+ }
83
+ ftsAvailabilityCache.set(sql$1, available);
84
+ return available;
85
+ };
86
+
87
+ export { AGG_KEY as A, DOC_COLUMN as D, AGG_VALUE as a, AGG_COUNT as b, createIndexSql as c, aggUpsertSql as d, qualifiedJsonPathSql as e, isFtsAvailable as i, jsonPathSql as j, quoteIdentifier as q, rowToDocument as r, tableColumns as t };
@@ -0,0 +1 @@
1
+ export { R as RANK_TIEBREAK, e as encodePartitionKey, m as matchesRankStaticWhere, c as rankKeyFromDoc, r as rankTableName, a as resolveRankPartition, s as sortColumnName } from './rank-CrkEIpF4.mjs';