@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
package/dist/index.mjs ADDED
@@ -0,0 +1,35 @@
1
+ export { exportShardRows, exportShardTable, importShardRows, parseExportShardArgs, parseImportShardArgs, selectExportTables, validateImportRow } from './packem_shared/exportShardRows-DZEhUeyI.mjs';
2
+ export { AGGREGATE_SQL_FUNCTION, aggregateSqlFunction, matchesStaticWhere, normalizeCountArgument, throwingScheduler } from './packem_shared/matchesStaticWhere-CFk6adSu.mjs';
3
+ export { aggregateTableName, coerceAggregateNumber, encodeAggregateKey, foldAggregateTally, readAggregateValue } from './packem_shared/aggregateTableName-CxNqY1Sl.mjs';
4
+ export { CountRlsUnsupportedError, mergeWhere, planAggregateLookup, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy } from './packem_shared/CountRlsUnsupportedError-28ZvvwKS.mjs';
5
+ export { AUTH_METRICS_BUCKETS_TABLE, AUTH_METRICS_BUCKET_MS, AUTH_METRICS_BUCKET_RETENTION, AUTH_METRICS_TABLE, ensureAuthMetricsTables, readAuthMetrics, recordAuthEvent } from './packem_shared/AUTH_METRICS_BUCKET_MS-CiHHYeJi.mjs';
6
+ export { NotUniqueError, assertValidClientId, createShardCtxDb, normalizeIdStructurally } from './packem_shared/assertValidClientId-CBZ1zC96.mjs';
7
+ export { DATA_MIGRATION_STATE_TABLE, readMigrationStatus, runDataMigration } from './packem_shared/DATA_MIGRATION_STATE_TABLE-PTtTiQ7U.mjs';
8
+ export { SCAN_DEP, createDependencyTracker, depKey } from './packem_shared/SCAN_DEP-DLJF8dsj.mjs';
9
+ export { renderSql } from './packem_shared/renderSql-D6eUcn2N.mjs';
10
+ export { FUNCTION_METRICS_BUCKETS_TABLE, FUNCTION_METRICS_BUCKET_MS, FUNCTION_METRICS_BUCKET_RETENTION, FUNCTION_METRICS_INDEX_TABLE, FUNCTION_METRICS_TABLE, ensureFunctionMetricsTables, readFunctionMetricBuckets, readFunctionMetricIndexHits, readFunctionMetrics, readFunctionMetricsTotals, recordFunctionMetric } from './packem_shared/ensureFunctionMetricsTables-UDNVD7FS.mjs';
11
+ export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, facetColumn, listTables, readTablePage, selectMatchingIds } from './packem_shared/ADMIN_FUNCTION_PREFIX-Dzdqq5J2.mjs';
12
+ export { LogBuffer } from './packem_shared/LogBuffer-B_Ezju_N.mjs';
13
+ export { MAIL_RETENTION, MAIL_TABLE, clearCapturedMail, ensureMailTable, readCapturedMail, recordCapturedMail } from './packem_shared/clearCapturedMail-CPpgl-dX.mjs';
14
+ export { default as NotFoundError } from './packem_shared/NotFoundError-CMuMZt81.mjs';
15
+ export { armRestore, readBookmark } from './packem_shared/armRestore-BJk53Ro8.mjs';
16
+ export { buildSeekWhere, decodeCursor, encodeCursor, normalizeOrderKeys } from './packem_shared/buildSeekWhere-lVsNXSLy.mjs';
17
+ export { R as RANK_TIEBREAK, e as encodePartitionKey, m as matchesRankStaticWhere, r as rankTableName, a as resolveRankPartition, s as sortColumnName } from './packem_shared/rank-CrkEIpF4.mjs';
18
+ export { ReactiveCache, reactiveCacheKey, stableStringify } from './packem_shared/ReactiveCache-ByVzgH3d.mjs';
19
+ export { serveRelationFanout } from './packem_shared/serveRelationFanout-Clr1a05L.mjs';
20
+ export { DEFAULT_MAX_RELATION_KEYS, assertFlatPredicate, containsRelationPredicate, isRelationPredicate, resolveRelationPredicates } from './packem_shared/assertFlatPredicate-DyVYReuT.mjs';
21
+ export { applyOnDelete, resolveWith, runRowValidators } from './packem_shared/applyOnDelete-CMif2RKw.mjs';
22
+ export { RLS_UNWRAP_SYMBOL, RlsRequiredError, guardWriter } from './packem_shared/guardWriter-u3UlnCH5.mjs';
23
+ export { buildFtsMatch, ftsTableName, scoreDocument, stringifySearchText, tokenizeSearch } from './packem_shared/ftsTableName-BLEMawrp.mjs';
24
+ export { M as MIN_ADMIN_TOKEN_LENGTH, a as MIN_AUTH_SECRET_LENGTH, b as buildSecurityAudit } from './packem_shared/security-audit-CucgBice.mjs';
25
+ export { SESSION_DO_TTL_DEFAULT, SessionDO } from './packem_shared/SESSION_DO_TTL_DEFAULT-ilPZsVwu.mjs';
26
+ export { ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, ShardDO, subscriptionListDeltas } from './packem_shared/ROOT_DO_SIZE_WARN_BYTES-DQkmGiCS.mjs';
27
+ export { SHARD_REGISTRY_DO_NAME, ShardRegistryDO } from './packem_shared/SHARD_REGISTRY_DO_NAME-BsAbi5Mn.mjs';
28
+ export { MAX_SQL_ROWS, assertReadonly, runReadonlySql } from './packem_shared/assertReadonly-dDcFE1YZ.mjs';
29
+ export { createSystemReader } from './packem_shared/createSystemReader-8CzSZP9V.mjs';
30
+ export { ConflictError } from './packem_shared/ConflictError-C0STs6bU.mjs';
31
+ export { hasTrigger, runTriggers } from './packem_shared/runTriggers-5N6_Fx0A.mjs';
32
+ export { compileWhereSql } from './packem_shared/compileWhereSql-CXrhFA3G.mjs';
33
+ export { CDC_LOG_TABLE, applyCdcChanges, readCdcChanges, trimCdcChanges } from './packem_shared/applyCdcChanges-Ctdmxmrv.mjs';
34
+ export { backfillAggregateIndexes, backfillRankIndexes } from './packem_shared/backfillAggregateIndexes-BF5eL7kW.mjs';
35
+ export { runShardMigrations } from './packem_shared/runShardMigrations-C3bn5r93.mjs';
@@ -0,0 +1,313 @@
1
+ const ADMIN_FUNCTION_PREFIX = "__lunora_admin__:";
2
+ const RELATION_FUNCTION_PREFIX = "__lunora_relation__:";
3
+ const ADMIN_FUNCTIONS = {
4
+ applyCdc: "__lunora_admin__:applyCdc",
5
+ cdcSync: "__lunora_admin__:cdcSync",
6
+ clearCapturedMail: "__lunora_admin__:clearCapturedMail",
7
+ clearTable: "__lunora_admin__:clearTable",
8
+ createWorkflowInstance: "__lunora_admin__:createWorkflowInstance",
9
+ deleteRows: "__lunora_admin__:deleteRows",
10
+ describeTable: "__lunora_admin__:describeTable",
11
+ describeTables: "__lunora_admin__:describeTables",
12
+ exportShard: "__lunora_admin__:exportShard",
13
+ facetColumn: "__lunora_admin__:facetColumn",
14
+ getAdvisories: "__lunora_admin__:getAdvisories",
15
+ getAuditLog: "__lunora_admin__:getAuditLog",
16
+ getAuthMetrics: "__lunora_admin__:getAuthMetrics",
17
+ getCapturedMail: "__lunora_admin__:getCapturedMail",
18
+ getFunctionStats: "__lunora_admin__:getFunctionStats",
19
+ listSubscriptions: "__lunora_admin__:listSubscriptions",
20
+ listTableIndexes: "__lunora_admin__:listTableIndexes",
21
+ getLogs: "__lunora_admin__:getLogs",
22
+ getMetrics: "__lunora_admin__:getMetrics",
23
+ getPitrBookmark: "__lunora_admin__:getPitrBookmark",
24
+ getRequestLog: "__lunora_admin__:getRequestLog",
25
+ getSecurityAudit: "__lunora_admin__:getSecurityAudit",
26
+ getSettings: "__lunora_admin__:getSettings",
27
+ // eslint-disable-next-line no-secrets/no-secrets -- reserved admin RPC path constant, not a credential
28
+ getWorkflowInstanceStatus: "__lunora_admin__:getWorkflowInstanceStatus",
29
+ importShard: "__lunora_admin__:importShard",
30
+ listTables: "__lunora_admin__:listTables",
31
+ listWorkflows: "__lunora_admin__:listWorkflows",
32
+ maskPolicies: "__lunora_admin__:maskPolicies",
33
+ migrationStatus: "__lunora_admin__:migrationStatus",
34
+ pitrRestore: "__lunora_admin__:pitrRestore",
35
+ rankBefore: "__lunora_admin__:rankBefore",
36
+ rankPage: "__lunora_admin__:rankPage",
37
+ readTablePage: "__lunora_admin__:readTablePage",
38
+ recordAuthEvent: "__lunora_admin__:recordAuthEvent",
39
+ recordContainerEvent: "__lunora_admin__:recordContainerEvent",
40
+ recordMail: "__lunora_admin__:recordMail",
41
+ rlsPolicies: "__lunora_admin__:rlsPolicies",
42
+ runAs: "__lunora_admin__:runAs",
43
+ runMigration: "__lunora_admin__:runMigration",
44
+ runSql: "__lunora_admin__:runSql",
45
+ sendTestMail: "__lunora_admin__:sendTestMail",
46
+ storageOrphans: "__lunora_admin__:storageOrphans",
47
+ storageReferences: "__lunora_admin__:storageReferences",
48
+ storageRules: "__lunora_admin__:storageRules",
49
+ studioFeatures: "__lunora_admin__:studioFeatures",
50
+ writeRow: "__lunora_admin__:writeRow"
51
+ };
52
+ const DEFAULT_PAGE_SIZE = 50;
53
+ const MAX_PAGE_SIZE = 500;
54
+ const DEFAULT_FACET_LIMIT = 30;
55
+ const MAX_FACET_LIMIT = 200;
56
+ const DOC_COLUMN = "__doc__";
57
+ const safeParseObject = (text) => {
58
+ try {
59
+ const value = JSON.parse(text);
60
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
61
+ } catch {
62
+ return void 0;
63
+ }
64
+ };
65
+ const expandDocumentRows = (columns, rows) => {
66
+ if (!columns.includes(DOC_COLUMN)) {
67
+ return { columns, rows };
68
+ }
69
+ const parsed = [];
70
+ for (const row of rows) {
71
+ const raw = row[DOC_COLUMN];
72
+ const documentData = typeof raw === "string" ? safeParseObject(raw) : void 0;
73
+ if (documentData === void 0) {
74
+ return { columns, rows };
75
+ }
76
+ const meta = Object.fromEntries(Object.entries(row).filter(([column]) => column !== DOC_COLUMN));
77
+ parsed.push({ ...meta, ...documentData });
78
+ }
79
+ const metaColumns = columns.filter((name) => name !== DOC_COLUMN);
80
+ const documentKeys = [];
81
+ const seen = new Set(metaColumns);
82
+ for (const documentData of parsed) {
83
+ for (const key of Object.keys(documentData)) {
84
+ if (!seen.has(key)) {
85
+ seen.add(key);
86
+ documentKeys.push(key);
87
+ }
88
+ }
89
+ }
90
+ return { columns: [...metaColumns, ...documentKeys], rows: parsed };
91
+ };
92
+ const escapeLike = (value) => value.replaceAll(/[\\%_]/g, (character) => `\\${character}`);
93
+ const isInternalTable = (name) => name.startsWith("sqlite_") || name.startsWith("_cf_") || name.startsWith("__miniflare") || name.startsWith("__lunora") || name.includes("__fts_");
94
+ const quoteIdentifier = (name) => `"${name.replaceAll('"', '""')}"`;
95
+ const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
96
+ const countRows = (sql, quotedTable) => {
97
+ const row = sql.exec(`SELECT COUNT(*) AS c FROM ${quotedTable}`).one();
98
+ return Number(row.c);
99
+ };
100
+ const listTables = (sql) => {
101
+ const names = sql.exec("SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name").toArray();
102
+ const tables = [];
103
+ for (const { name } of names) {
104
+ if (isInternalTable(name)) {
105
+ continue;
106
+ }
107
+ tables.push({ name, rowCount: countRows(sql, quoteIdentifier(name)) });
108
+ }
109
+ return tables;
110
+ };
111
+ const tableExists = (sql, table) => sql.exec("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1", table).toArray().length > 0;
112
+ const FILTER_SQL_OPERATOR = { eq: "=", gt: ">", gte: ">=", lt: "<", lte: "<=", ne: "<>" };
113
+ const filterValueText = (value) => {
114
+ if (typeof value === "string") {
115
+ return value;
116
+ }
117
+ return typeof value === "number" || typeof value === "boolean" ? String(value) : "";
118
+ };
119
+ const resolveColumnExpression = (column, physicalColumns) => {
120
+ const isPhysical = physicalColumns.includes(column);
121
+ const isDocumentStored = physicalColumns.includes(DOC_COLUMN);
122
+ if (!isPhysical && !isDocumentStored) {
123
+ return void 0;
124
+ }
125
+ return isPhysical ? { expression: quoteIdentifier(column), params: [] } : { expression: `json_extract(${quoteIdentifier(DOC_COLUMN)}, ?)`, params: [`$."${column.replaceAll('"', '""')}"`] };
126
+ };
127
+ const buildFilterClause = (clause, physicalColumns) => {
128
+ const resolved = resolveColumnExpression(clause.column, physicalColumns);
129
+ if (resolved === void 0) {
130
+ return void 0;
131
+ }
132
+ const { expression, params: pathParameters } = resolved;
133
+ if (clause.operator === "contains") {
134
+ return {
135
+ params: [...pathParameters, `%${escapeLike(filterValueText(clause.value))}%`],
136
+ sql: String.raw`CAST(${expression} AS TEXT) LIKE ? ESCAPE '\'`
137
+ };
138
+ }
139
+ return { params: [...pathParameters, clause.value], sql: `${expression} ${FILTER_SQL_OPERATOR[clause.operator]} ?` };
140
+ };
141
+ const buildTablePredicate = (columns, needle, filters) => {
142
+ const conjuncts = [];
143
+ const parameters = [];
144
+ if (needle !== "" && columns.length > 0) {
145
+ const pattern = `%${escapeLike(needle)}%`;
146
+ conjuncts.push(`(${columns.map((name) => String.raw`CAST(${quoteIdentifier(name)} AS TEXT) LIKE ? ESCAPE '\'`).join(" OR ")})`);
147
+ parameters.push(...columns.map(() => pattern));
148
+ }
149
+ for (const clause of filters ?? []) {
150
+ const built = buildFilterClause(clause, columns);
151
+ if (built !== void 0) {
152
+ conjuncts.push(`(${built.sql})`);
153
+ parameters.push(...built.params);
154
+ }
155
+ }
156
+ return conjuncts.length === 0 ? void 0 : { parameters, where: conjuncts.join(" AND ") };
157
+ };
158
+ const buildOrderBy = (orderBy, physicalColumns) => {
159
+ if (orderBy === void 0) {
160
+ return void 0;
161
+ }
162
+ const resolved = resolveColumnExpression(orderBy.column, physicalColumns);
163
+ if (resolved === void 0) {
164
+ return void 0;
165
+ }
166
+ const keyword = orderBy.direction === "desc" ? "DESC" : "ASC";
167
+ return { params: resolved.params, sql: `${resolved.expression} ${keyword}` };
168
+ };
169
+ const readTablePage = (sql, options) => {
170
+ const { table } = options;
171
+ if (isInternalTable(table) || !tableExists(sql, table)) {
172
+ throw Object.assign(new Error(`unknown table: ${table}`), { code: "UNKNOWN_TABLE", name: "LunoraError", status: 404 });
173
+ }
174
+ const limit = clamp(Math.trunc(options.limit ?? DEFAULT_PAGE_SIZE), 1, MAX_PAGE_SIZE);
175
+ const offset = Math.max(0, Math.trunc(options.offset ?? 0));
176
+ const quoted = quoteIdentifier(table);
177
+ const columns = sql.exec(`PRAGMA table_info(${quoted})`).toArray().map((column) => column.name);
178
+ const needle = options.search?.trim() ?? "";
179
+ const withReferences = (page) => {
180
+ if (options.refs === void 0) {
181
+ return page;
182
+ }
183
+ const references = {};
184
+ for (const column of page.columns) {
185
+ const target = options.refs[column];
186
+ if (target !== void 0) {
187
+ references[column] = target;
188
+ }
189
+ }
190
+ return Object.keys(references).length > 0 ? { ...page, refs: references } : page;
191
+ };
192
+ const predicate = buildTablePredicate(columns, needle, options.filters);
193
+ const order = buildOrderBy(options.orderBy, columns);
194
+ const whereSql = predicate === void 0 ? "" : ` WHERE ${predicate.where}`;
195
+ const orderSql = order === void 0 ? "" : ` ORDER BY ${order.sql}`;
196
+ const whereParams = predicate?.parameters ?? [];
197
+ const orderParams = order?.params ?? [];
198
+ const total = predicate === void 0 ? countRows(sql, quoted) : Number(sql.exec(`SELECT COUNT(*) AS c FROM ${quoted}${whereSql}`, ...whereParams).one().c);
199
+ const rawRows = sql.exec(`SELECT * FROM ${quoted}${whereSql}${orderSql} LIMIT ? OFFSET ?`, ...whereParams, ...orderParams, limit, offset).toArray();
200
+ return withReferences({ ...expandDocumentRows(columns, rawRows), total });
201
+ };
202
+ const selectMatchingIds = (sql, options) => {
203
+ const { table } = options;
204
+ if (isInternalTable(table) || !tableExists(sql, table)) {
205
+ throw Object.assign(new Error(`unknown table: ${table}`), { code: "UNKNOWN_TABLE", name: "LunoraError", status: 404 });
206
+ }
207
+ const limit = clamp(Math.trunc(options.limit ?? MAX_PAGE_SIZE), 1, MAX_PAGE_SIZE);
208
+ const quoted = quoteIdentifier(table);
209
+ const columns = sql.exec(`PRAGMA table_info(${quoted})`).toArray().map((column) => column.name);
210
+ const needle = options.search?.trim() ?? "";
211
+ const predicate = buildTablePredicate(columns, needle, options.filters);
212
+ const fetched = predicate === void 0 ? sql.exec(`SELECT id FROM ${quoted} LIMIT ?`, limit + 1).toArray() : sql.exec(`SELECT id FROM ${quoted} WHERE ${predicate.where} LIMIT ?`, ...predicate.parameters, limit + 1).toArray();
213
+ const hasMore = fetched.length > limit;
214
+ const ids = (hasMore ? fetched.slice(0, limit) : fetched).map((row) => row.id);
215
+ return { hasMore, ids };
216
+ };
217
+ const knownDisplayColumns = (sql, quotedTable, physicalColumns) => {
218
+ const known = new Set(physicalColumns.filter((name) => name !== DOC_COLUMN));
219
+ if (!physicalColumns.includes(DOC_COLUMN)) {
220
+ return known;
221
+ }
222
+ const sample = sql.exec(`SELECT ${quoteIdentifier(DOC_COLUMN)} AS doc FROM ${quotedTable} LIMIT ?`, MAX_PAGE_SIZE).toArray();
223
+ for (const { doc } of sample) {
224
+ const documentData = typeof doc === "string" ? safeParseObject(doc) : void 0;
225
+ if (documentData !== void 0) {
226
+ for (const key of Object.keys(documentData)) {
227
+ known.add(key);
228
+ }
229
+ }
230
+ }
231
+ return known;
232
+ };
233
+ const facetColumn = (sql, options) => {
234
+ const { column, table } = options;
235
+ if (isInternalTable(table) || !tableExists(sql, table)) {
236
+ throw Object.assign(new Error(`unknown table: ${table}`), { code: "UNKNOWN_TABLE", name: "LunoraError", status: 404 });
237
+ }
238
+ const quoted = quoteIdentifier(table);
239
+ const physicalColumns = sql.exec(`PRAGMA table_info(${quoted})`).toArray().map((info) => info.name);
240
+ if (!knownDisplayColumns(sql, quoted, physicalColumns).has(column)) {
241
+ throw Object.assign(new Error(`unknown column: ${column}`), { code: "UNKNOWN_COLUMN", name: "LunoraError", status: 404 });
242
+ }
243
+ const resolved = resolveColumnExpression(column, physicalColumns);
244
+ if (resolved === void 0) {
245
+ throw Object.assign(new Error(`unknown column: ${column}`), { code: "UNKNOWN_COLUMN", name: "LunoraError", status: 404 });
246
+ }
247
+ const limit = clamp(Math.trunc(options.limit ?? DEFAULT_FACET_LIMIT), 1, MAX_FACET_LIMIT);
248
+ const needle = options.search?.trim() ?? "";
249
+ const predicate = buildTablePredicate(physicalColumns, needle, options.filters);
250
+ const whereSql = predicate === void 0 ? "" : ` WHERE ${predicate.where}`;
251
+ const whereParams = predicate?.parameters ?? [];
252
+ const rows = sql.exec(
253
+ `SELECT ${resolved.expression} AS value, COUNT(*) AS count FROM ${quoted}${whereSql} GROUP BY ${resolved.expression} ORDER BY count DESC LIMIT ?`,
254
+ ...resolved.params,
255
+ ...whereParams,
256
+ ...resolved.params,
257
+ limit + 1
258
+ ).toArray();
259
+ const truncated = rows.length > limit;
260
+ const kept = truncated ? rows.slice(0, limit) : rows;
261
+ return {
262
+ truncated,
263
+ values: kept.map((row) => {
264
+ return { count: Number(row.count), value: row.value };
265
+ })
266
+ };
267
+ };
268
+ const findStorageReferences = (sql, storageColumns, keys) => {
269
+ const references = {};
270
+ const scanned = keys.slice(0, MAX_PAGE_SIZE);
271
+ for (const key of scanned) {
272
+ references[key] = [];
273
+ }
274
+ if (scanned.length === 0) {
275
+ return { references, storageColumns };
276
+ }
277
+ const placeholders = scanned.map(() => "?").join(", ");
278
+ for (const [table, columns] of Object.entries(storageColumns)) {
279
+ if (isInternalTable(table) || !tableExists(sql, table)) {
280
+ continue;
281
+ }
282
+ const quoted = quoteIdentifier(table);
283
+ const physicalColumns = sql.exec(`PRAGMA table_info(${quoted})`).toArray().map((column) => column.name);
284
+ for (const column of columns) {
285
+ const resolved = resolveColumnExpression(column, physicalColumns);
286
+ if (resolved === void 0) {
287
+ continue;
288
+ }
289
+ const rows = sql.exec(
290
+ `SELECT id, ${resolved.expression} AS ref FROM ${quoted} WHERE ${resolved.expression} IN (${placeholders})`,
291
+ ...resolved.params,
292
+ ...resolved.params,
293
+ ...scanned
294
+ ).toArray();
295
+ for (const row of rows) {
296
+ references[row.ref]?.push({ column, id: row.id, table });
297
+ }
298
+ }
299
+ }
300
+ return { references, storageColumns };
301
+ };
302
+ const summarizeSubscriptions = (attachments) => {
303
+ const connections = attachments.map((attachment, id) => {
304
+ const subscriptions = Object.values(attachment.subs ?? {}).map((sub) => {
305
+ return { args: sub.args, functionPath: sub.functionPath, table: sub.table };
306
+ });
307
+ return { admin: attachment.admin === true, id, subscriptions };
308
+ });
309
+ const totalSubscriptions = connections.reduce((sum, connection) => sum + connection.subscriptions.length, 0);
310
+ return { connections, totalConnections: connections.length, totalSubscriptions };
311
+ };
312
+
313
+ export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, MAX_PAGE_SIZE, RELATION_FUNCTION_PREFIX, facetColumn, findStorageReferences, listTables, readTablePage, selectMatchingIds, summarizeSubscriptions };
@@ -0,0 +1,84 @@
1
+ const AUTH_METRICS_TABLE = "__lunora_auth_metrics";
2
+ const AUTH_METRICS_BUCKETS_TABLE = "__lunora_auth_metrics_buckets";
3
+ const AUTH_METRICS_ROW_KEY = "app";
4
+ const AUTH_METRICS_BUCKET_MS = 6e4;
5
+ const AUTH_METRICS_BUCKET_RETENTION = 1440;
6
+ const runSql = (sql, query, ...params) => {
7
+ const runner = sql.exec;
8
+ return runner.call(sql, query, ...params);
9
+ };
10
+ const bucketFloor = (ts) => Math.floor(ts / AUTH_METRICS_BUCKET_MS) * AUTH_METRICS_BUCKET_MS;
11
+ const ensureAuthMetricsTables = (sql) => {
12
+ runSql(
13
+ sql,
14
+ `CREATE TABLE IF NOT EXISTS "${AUTH_METRICS_TABLE}" (
15
+ key TEXT PRIMARY KEY,
16
+ attempts INTEGER NOT NULL DEFAULT 0,
17
+ failures INTEGER NOT NULL DEFAULT 0,
18
+ since_ms INTEGER NOT NULL DEFAULT 0
19
+ )`
20
+ );
21
+ runSql(
22
+ sql,
23
+ `CREATE TABLE IF NOT EXISTS "${AUTH_METRICS_BUCKETS_TABLE}" (
24
+ bucket_ms INTEGER PRIMARY KEY,
25
+ attempts INTEGER NOT NULL DEFAULT 0,
26
+ failures INTEGER NOT NULL DEFAULT 0
27
+ )`
28
+ );
29
+ };
30
+ const recordAuthEvent = (sql, input) => {
31
+ ensureAuthMetricsTables(sql);
32
+ const failureCount = input.outcome === "fail" ? 1 : 0;
33
+ runSql(
34
+ sql,
35
+ `INSERT INTO "${AUTH_METRICS_TABLE}" (key, attempts, failures, since_ms)
36
+ VALUES (?, 1, ?, ?)
37
+ ON CONFLICT(key) DO UPDATE SET
38
+ attempts = attempts + 1,
39
+ failures = failures + excluded.failures,
40
+ since_ms = CASE WHEN since_ms = 0 THEN excluded.since_ms ELSE since_ms END`,
41
+ AUTH_METRICS_ROW_KEY,
42
+ failureCount,
43
+ input.ts
44
+ );
45
+ const bucket = bucketFloor(input.ts);
46
+ runSql(
47
+ sql,
48
+ `INSERT INTO "${AUTH_METRICS_BUCKETS_TABLE}" (bucket_ms, attempts, failures)
49
+ VALUES (?, 1, ?)
50
+ ON CONFLICT(bucket_ms) DO UPDATE SET
51
+ attempts = attempts + 1,
52
+ failures = failures + excluded.failures`,
53
+ bucket,
54
+ failureCount
55
+ );
56
+ runSql(
57
+ sql,
58
+ `DELETE FROM "${AUTH_METRICS_BUCKETS_TABLE}"
59
+ WHERE bucket_ms <= (
60
+ SELECT MAX(bucket_ms) - ? FROM "${AUTH_METRICS_BUCKETS_TABLE}"
61
+ )`,
62
+ AUTH_METRICS_BUCKET_RETENTION * AUTH_METRICS_BUCKET_MS
63
+ );
64
+ };
65
+ const readAuthMetrics = (sql) => {
66
+ ensureAuthMetricsTables(sql);
67
+ const row = runSql(
68
+ sql,
69
+ `SELECT attempts, failures, since_ms FROM "${AUTH_METRICS_TABLE}" WHERE key = ?`,
70
+ AUTH_METRICS_ROW_KEY
71
+ ).toArray()[0];
72
+ const attempts = row?.attempts ?? 0;
73
+ const failures = row?.failures ?? 0;
74
+ const sinceMs = row?.since_ms ?? 0;
75
+ const history = runSql(
76
+ sql,
77
+ `SELECT bucket_ms, attempts, failures FROM "${AUTH_METRICS_BUCKETS_TABLE}" ORDER BY bucket_ms ASC`
78
+ ).toArray().map((bucket) => {
79
+ return { attempts: bucket.attempts, bucketMs: bucket.bucket_ms, failures: bucket.failures };
80
+ });
81
+ return { attempts, failureRate: attempts === 0 ? 0 : failures / attempts, failures, history, sinceMs };
82
+ };
83
+
84
+ export { AUTH_METRICS_BUCKETS_TABLE, AUTH_METRICS_BUCKET_MS, AUTH_METRICS_BUCKET_RETENTION, AUTH_METRICS_ROW_KEY, AUTH_METRICS_TABLE, ensureAuthMetricsTables, readAuthMetrics, recordAuthEvent };
@@ -0,0 +1,13 @@
1
+ class ConflictError extends Error {
2
+ code = "CONFLICT";
3
+ /** Why the conflict fired; `occ` is the contention signal the metrics layer counts. */
4
+ kind;
5
+ status = 409;
6
+ constructor(message = "Optimistic concurrency conflict", kind = "conflict") {
7
+ super(message);
8
+ this.name = "ConflictError";
9
+ this.kind = kind;
10
+ }
11
+ }
12
+
13
+ export { ConflictError };
@@ -0,0 +1,133 @@
1
+ class CountRlsUnsupportedError extends Error {
2
+ code = "COUNT_RLS_UNSUPPORTED";
3
+ name = "LunoraError";
4
+ status = 422;
5
+ constructor(table) {
6
+ super(
7
+ table === void 0 ? "count() is not supported in an RLS-restricted context" : `count() is not supported on table "${table}" inside an RLS-restricted context`
8
+ );
9
+ }
10
+ }
11
+ const mergeWhere = (left, right) => {
12
+ if (!left) {
13
+ return right;
14
+ }
15
+ if (!right) {
16
+ return left;
17
+ }
18
+ return { AND: [left, right] };
19
+ };
20
+ const BOOLEAN_COMBINATORS = /* @__PURE__ */ new Set(["AND", "NOT", "OR"]);
21
+ const NOT_EQ = /* @__PURE__ */ Symbol("not-eq");
22
+ const resolveEqValue = (value) => {
23
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
24
+ const operatorKeys = Object.keys(value);
25
+ if (operatorKeys.length === 1 && operatorKeys[0] === "eq") {
26
+ return value.eq;
27
+ }
28
+ return NOT_EQ;
29
+ }
30
+ return value;
31
+ };
32
+ const parseRequestedEqKeys = (requested, accept) => {
33
+ const resolved = {};
34
+ for (const [key, raw] of Object.entries(requested)) {
35
+ if (BOOLEAN_COMBINATORS.has(key) || !accept(key)) {
36
+ return void 0;
37
+ }
38
+ const value = resolveEqValue(raw);
39
+ if (value === NOT_EQ) {
40
+ return void 0;
41
+ }
42
+ resolved[key] = value;
43
+ }
44
+ return resolved;
45
+ };
46
+ const reconcileStaticWhere = (staticWhere, resolved, requested, crossCheckRequested) => {
47
+ const merged = { ...resolved };
48
+ if (!staticWhere) {
49
+ return merged;
50
+ }
51
+ for (const [key, value] of Object.entries(staticWhere)) {
52
+ if (key in merged) {
53
+ if (merged[key] !== value) {
54
+ return void 0;
55
+ }
56
+ } else if (crossCheckRequested && key in requested) {
57
+ if (requested[key] !== value) {
58
+ return void 0;
59
+ }
60
+ } else {
61
+ merged[key] = value;
62
+ }
63
+ }
64
+ return merged;
65
+ };
66
+ const planAggregateLookup = (index, requestedWhere) => {
67
+ const by = index.by ?? [];
68
+ const requested = requestedWhere ?? {};
69
+ const resolved = parseRequestedEqKeys(requested, (key) => by.includes(key));
70
+ if (resolved === void 0) {
71
+ return void 0;
72
+ }
73
+ for (const key of by) {
74
+ if (!(key in resolved)) {
75
+ return void 0;
76
+ }
77
+ }
78
+ return reconcileStaticWhere(index.where, resolved, requested, true);
79
+ };
80
+ const collectPartialKey = (index, requestedWhere, byFields) => {
81
+ const partial = parseRequestedEqKeys(requestedWhere ?? {}, (key) => byFields.has(key));
82
+ if (partial === void 0) {
83
+ return void 0;
84
+ }
85
+ return reconcileStaticWhere(index.where, partial, {}, false);
86
+ };
87
+ const selectIndexForReducer = (indexes, op, field, requestedWhere) => {
88
+ let best;
89
+ for (const index of indexes) {
90
+ if (index.op !== op) {
91
+ continue;
92
+ }
93
+ if (op !== "count" && index.field !== field) {
94
+ continue;
95
+ }
96
+ const key = planAggregateLookup(index, requestedWhere);
97
+ if (!key) {
98
+ continue;
99
+ }
100
+ if (!best || (index.by?.length ?? 0) > (best.index.by?.length ?? 0)) {
101
+ best = { index, key };
102
+ }
103
+ }
104
+ return best;
105
+ };
106
+ const selectIndexForCount = (indexes, requestedWhere) => selectIndexForReducer(indexes, "count", void 0, requestedWhere);
107
+ const selectIndexForAggregate = (indexes, op, field, requestedWhere) => selectIndexForReducer(indexes, op, field, requestedWhere);
108
+ const selectIndexForGroupBy = (indexes, op, field, by, requestedWhere) => {
109
+ const requestedFields = new Set(by);
110
+ for (const index of indexes) {
111
+ if (index.op !== op) {
112
+ continue;
113
+ }
114
+ if (op !== "count" && index.field !== field) {
115
+ continue;
116
+ }
117
+ const indexBy = index.by ?? [];
118
+ if (indexBy.length !== requestedFields.size) {
119
+ continue;
120
+ }
121
+ if (!indexBy.every((key) => requestedFields.has(key))) {
122
+ continue;
123
+ }
124
+ const partial = collectPartialKey(index, requestedWhere, requestedFields);
125
+ if (partial === void 0) {
126
+ continue;
127
+ }
128
+ return { index, partial };
129
+ }
130
+ return void 0;
131
+ };
132
+
133
+ export { CountRlsUnsupportedError, mergeWhere, planAggregateLookup, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy };