@lunora/do 1.0.0-alpha.44 → 1.0.0-alpha.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +55 -1
- package/dist/index.d.ts +55 -1
- package/dist/index.mjs +1 -44
- package/dist/packem_shared/ADMIN_FUNCTIONS-PxZ8Lr9e.mjs +1 -0
- package/dist/packem_shared/AGGREGATE_SQL_FUNCTION-QYaiuty4.mjs +1 -0
- package/dist/packem_shared/AUTH_METRICS_BUCKETS_TABLE-D0wNaez7.mjs +22 -0
- package/dist/packem_shared/CDC_LOG_TABLE-E_J5LPoK.mjs +8 -0
- package/dist/packem_shared/ConflictError-C8GtJmjS.mjs +1 -0
- package/dist/packem_shared/CountRlsUnsupportedError-Cl8XpYDL.mjs +1 -0
- package/dist/packem_shared/DATA_MIGRATION_STATE_TABLE-CaO6L0Ee.mjs +31 -0
- package/dist/packem_shared/DEFAULT_MAX_RELATION_KEYS-BRdmX7WW.mjs +1 -0
- package/dist/packem_shared/FUNCTION_METRICS_BUCKETS_TABLE-BPPeqM11.mjs +56 -0
- package/dist/packem_shared/GEO_DEFAULT_PRECISION-CWAm_oYY.mjs +1 -0
- package/dist/packem_shared/LogBuffer-bIvCelI-.mjs +1 -0
- package/dist/packem_shared/MAIL_RETENTION-KmozO2NQ.mjs +18 -0
- package/dist/packem_shared/MAX_SQL_ROWS-Bdu25ASB.mjs +2 -0
- package/dist/packem_shared/MIN_ADMIN_TOKEN_LENGTH-Ch6OW6_3.mjs +1 -0
- package/dist/packem_shared/NotFoundError-J3tjf4Uo.mjs +1 -0
- package/dist/packem_shared/NotUniqueError-BcP6GOem.mjs +1 -0
- package/dist/packem_shared/RANK_TIEBREAK-DtX8zQyc.mjs +1 -0
- package/dist/packem_shared/RLS_UNWRAP_SYMBOL-C6_WX3dG.mjs +1 -0
- package/dist/packem_shared/ROOT_DO_SIZE_WARN_BYTES-CP5wvJOW.mjs +101 -0
- package/dist/packem_shared/ReactiveCache-1_9Rs7J_.mjs +1 -0
- package/dist/packem_shared/SCAN_DEP-D_yR9EeV.mjs +1 -0
- package/dist/packem_shared/SESSION_DO_TTL_DEFAULT-GvBy_DBz.mjs +1 -0
- package/dist/packem_shared/SHARD_REGISTRY_DO_NAME-Caa0fR4N.mjs +1 -0
- package/dist/packem_shared/aggregateTableName-G-eXyjcz.mjs +1 -0
- package/dist/packem_shared/applyOnDelete-BvQN7pDL.mjs +1 -0
- package/dist/packem_shared/applySelect-Bq2KOrkL.mjs +1 -0
- package/dist/packem_shared/armRestore-BNzdvQ_o.mjs +1 -0
- package/dist/packem_shared/backfillAggregateIndexes-BAQ3Fwwh.mjs +1 -0
- package/dist/packem_shared/buildFtsMatch-CV0Z7PWv.mjs +1 -0
- package/dist/packem_shared/compileWhereSql-BLcfs4QW.mjs +1 -0
- package/dist/packem_shared/constant-time-equal-BVG05Guz.mjs +1 -0
- package/dist/packem_shared/context-telemetry-8eigmiNQ.mjs +1 -0
- package/dist/packem_shared/createMetrics-CyjLFXtA.mjs +1 -0
- package/dist/packem_shared/createSystemReader-DcDcrtM3.mjs +1 -0
- package/dist/packem_shared/ctx-db-idempotency-wiVoGnpQ.mjs +19 -0
- package/dist/packem_shared/ctx-db-shapes-DzX_H5q8.mjs +1 -0
- package/dist/packem_shared/diffExternalSource-DMpkJta1.mjs +1 -0
- package/dist/packem_shared/do-exec-BLe9lLrN.mjs +1 -0
- package/dist/packem_shared/do-sql-BYIQTG3z.mjs +1 -0
- package/dist/packem_shared/exportShardRows-kt42wijd.mjs +1 -0
- package/dist/packem_shared/hasTrigger-_rexbWMO.mjs +1 -0
- package/dist/packem_shared/isSoftDeleted-BvhQov04.mjs +9 -0
- package/dist/packem_shared/json-response-wrh9TBPw.mjs +1 -0
- package/dist/packem_shared/materializeExternalRows-BFmT9gsw.mjs +1 -0
- package/dist/packem_shared/renderSql-B5lF5Jd9.mjs +1 -0
- package/dist/packem_shared/runShardMigrations-bxOHpfID.mjs +5 -0
- package/dist/packem_shared/security-audit-BKUOgE0x.mjs +1 -0
- package/dist/packem_shared/selectExpiredIds-BGVP3d8-.mjs +1 -0
- package/dist/packem_shared/serialize-sql-DiRzL7A4.mjs +1 -0
- package/dist/packem_shared/serveRelationFanout-Ct5D2Tbk.mjs +1 -0
- package/dist/packem_shared/stableStringify-BjLh4gvA.mjs +1 -0
- package/dist/packem_shared/stableWireKey-YEHLaX6X.mjs +1 -0
- package/dist/packem_shared/subscriptionListDeltas-Bs69JbA8.mjs +1 -0
- package/dist/packem_shared/wire-codec-Ctnni0h6.mjs +1 -0
- package/package.json +1 -1
- package/dist/packem_shared/ADMIN_FUNCTIONS-CnZkbXH_.mjs +0 -374
- package/dist/packem_shared/AGGREGATE_SQL_FUNCTION-CQsu2Xga.mjs +0 -56
- package/dist/packem_shared/AUTH_METRICS_BUCKETS_TABLE-CiHHYeJi.mjs +0 -84
- package/dist/packem_shared/CDC_LOG_TABLE-uwOJxJJZ.mjs +0 -107
- package/dist/packem_shared/ConflictError-CLoq37xH.mjs +0 -12
- package/dist/packem_shared/CountRlsUnsupportedError-BGxj0pgS.mjs +0 -133
- package/dist/packem_shared/DATA_MIGRATION_STATE_TABLE-CYwBpyTr.mjs +0 -245
- package/dist/packem_shared/DEFAULT_MAX_RELATION_KEYS-BEan1CRD.mjs +0 -211
- package/dist/packem_shared/FUNCTION_METRICS_BUCKETS_TABLE-UDNVD7FS.mjs +0 -248
- package/dist/packem_shared/GEO_DEFAULT_PRECISION-BWnsNmpP.mjs +0 -114
- package/dist/packem_shared/LogBuffer-B_Ezju_N.mjs +0 -37
- package/dist/packem_shared/MAIL_RETENTION-CPpgl-dX.mjs +0 -104
- package/dist/packem_shared/MAX_SQL_ROWS-iFAA8FbD.mjs +0 -61
- package/dist/packem_shared/MIN_ADMIN_TOKEN_LENGTH-CCAvoFlr.mjs +0 -1
- package/dist/packem_shared/NotFoundError-C70b9hLw.mjs +0 -9
- package/dist/packem_shared/NotUniqueError-DbtlWwcG.mjs +0 -2031
- package/dist/packem_shared/RANK_TIEBREAK-CXhdcA1o.mjs +0 -91
- package/dist/packem_shared/RLS_UNWRAP_SYMBOL-DTvHvRzY.mjs +0 -141
- package/dist/packem_shared/ROOT_DO_SIZE_WARN_BYTES-B_zt4RQE.mjs +0 -7676
- package/dist/packem_shared/ReactiveCache-DnSvbjil.mjs +0 -233
- package/dist/packem_shared/SCAN_DEP-DLJF8dsj.mjs +0 -19
- package/dist/packem_shared/SESSION_DO_TTL_DEFAULT-BnSKgVO4.mjs +0 -169
- package/dist/packem_shared/SHARD_REGISTRY_DO_NAME-D99roc-r.mjs +0 -144
- package/dist/packem_shared/aggregateTableName-CxNqY1Sl.mjs +0 -64
- package/dist/packem_shared/applyOnDelete-BXSq3S70.mjs +0 -176
- package/dist/packem_shared/applySelect-WQY8m62C.mjs +0 -103
- package/dist/packem_shared/armRestore-4Px61hHS.mjs +0 -49
- package/dist/packem_shared/backfillAggregateIndexes-BGTwynwh.mjs +0 -81
- package/dist/packem_shared/buildFtsMatch-BLEMawrp.mjs +0 -38
- package/dist/packem_shared/compileWhereSql-DE6yfRcQ.mjs +0 -129
- package/dist/packem_shared/constant-time-equal-BVRWZgES.mjs +0 -12
- package/dist/packem_shared/context-telemetry-CDpyil58.mjs +0 -223
- package/dist/packem_shared/createMetrics-C4I2Yanj.mjs +0 -1
- package/dist/packem_shared/createSystemReader-D12eNH13.mjs +0 -82
- package/dist/packem_shared/ctx-db-idempotency-BdcNpvY4.mjs +0 -108
- package/dist/packem_shared/ctx-db-shapes-DTeFiHYS.mjs +0 -53
- package/dist/packem_shared/diffExternalSource-CovHfdyo.mjs +0 -44
- package/dist/packem_shared/do-exec-5eQy5cEi.mjs +0 -12
- package/dist/packem_shared/do-sql-CGAgiQUz.mjs +0 -88
- package/dist/packem_shared/exportShardRows-Dy3oFZ26.mjs +0 -157
- package/dist/packem_shared/hasTrigger-5N6_Fx0A.mjs +0 -20
- package/dist/packem_shared/isSoftDeleted-CFJmhjFP.mjs +0 -217
- package/dist/packem_shared/json-response-BdbtpOhm.mjs +0 -3
- package/dist/packem_shared/materializeExternalRows-BtEGs1Fv.mjs +0 -45
- package/dist/packem_shared/renderSql-D6eUcn2N.mjs +0 -16
- package/dist/packem_shared/runShardMigrations-BVax6rYu.mjs +0 -120
- package/dist/packem_shared/security-audit-CucgBice.mjs +0 -158
- package/dist/packem_shared/selectExpiredIds-C5W29Upb.mjs +0 -18
- package/dist/packem_shared/serialize-sql-BlRUoiQe.mjs +0 -14
- package/dist/packem_shared/serveRelationFanout-k7HTh3CC.mjs +0 -21
- package/dist/packem_shared/stableStringify-mC40mZts.mjs +0 -40
- package/dist/packem_shared/stableWireKey-DKuXO7T5.mjs +0 -6
- package/dist/packem_shared/subscriptionListDeltas-CT76bYny.mjs +0 -126
- package/dist/packem_shared/wire-codec-CzQc1pvf.mjs +0 -225
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{sql as e}from"drizzle-orm";import{r as $}from"./do-exec-BLe9lLrN.mjs";import{T as o}from"./do-sql-BYIQTG3z.mjs";const E=(s,i,d,r)=>{const a=d-(i.after??0),t=[e`${o(i.field)} IS NOT NULL`,e`${o(i.field)} < ${a}`];i.softDeleteField!==void 0&&t.push(e`${o(i.softDeleteField)} IS NULL`);const f=e`SELECT id FROM ${e.identifier(i.table)} WHERE ${e.join(t,e` AND `)} LIMIT ${e.raw(String(Math.max(0,Math.floor(r))+1))}`,l=$(s,f).toArray(),n=l.length>r,m=l.slice(0,r).map(p=>p.id);return{hasMore:n,ids:m}};export{E as selectExpiredIds};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const o=t=>typeof t=="boolean"?t?1:0:t===null||typeof t=="string"||typeof t=="number"?t:typeof t=="bigint"?t.toString():JSON.stringify(t);export{o as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as i}from"@lunora/errors";import{RELATION_FUNCTION_PREFIX as r}from"./ADMIN_FUNCTIONS-PxZ8Lr9e.mjs";const h=async(s,n,l,t)=>{const o=typeof t.table=="string"?t.table:"",a=s.tables[o];if(!a)throw new i("UNKNOWN_TABLE",`${r} unknown table "${o}"`,{status:404});if(a.shardMode?.kind==="global")throw new i("BAD_REQUEST",`${r} table "${o}" is global, not shard-local`);const e=t.where??void 0;return l===`${r}count`?n.count(o,e):(await n.findMany(o,{orderBy:t.orderBy,where:e,with:t.with})).page};export{h as serveRelationFanout};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const y=(e,t)=>e<t?-1:e>t?1:0,s=e=>{if(e===void 0)return"null";if(typeof e=="bigint")throw new TypeError("stableStringify: cannot use a bigint in a stable JSON cache key — pass it as a string, or use stableWireKey");if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(r=>s(r)).join(",")}]`;const t=Object.getPrototypeOf(e);if(t!==null&&t!==Object.prototype){const r=e.constructor?.name??"value";throw new TypeError(`stableStringify: cannot use a ${r} in a stable JSON cache key — only plain objects, arrays, and JSON primitives are supported (wire-typed values key via stableWireKey)`)}const n=e,a=Object.keys(n).toSorted(y),i=[];for(const r of a){const o=n[r];o!==void 0&&i.push(`${JSON.stringify(r)}:${s(o)}`)}return`{${i.join(",")}}`};export{s as stableStringify};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{stableStringify as r}from"./stableStringify-BjLh4gvA.mjs";import{f as e}from"./wire-codec-Ctnni0h6.mjs";const s=t=>r(e(t));export{s as stableWireKey};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{f as l}from"./wire-codec-Ctnni0h6.mjs";const c="_id",p="__lunora__",b=t=>{if(typeof t!="object"||t===null||Array.isArray(t))return;const r=t[c];return typeof r=="string"?r:void 0},y=t=>{const r=new Map,n=[];for(const s of t){const e=b(s);if(e===void 0||r.has(e))return;r.set(e,s),n.push(e)}return{byId:r,order:n}},h=(t,r)=>{const n=t.order.filter(e=>r.byId.has(e)),s=r.order.filter(e=>t.byId.has(e));return n.length!==s.length?!1:n.every((e,o)=>s[o]===e)},g=(t,r,n,s)=>{const e=[];for(const o of t.order)r.byId.has(o)||e.push({delta:{key:o,op:"delete",table:n},frame:`{"key":${JSON.stringify(o)},"op":"delete","table":${s}}`});return e},m=(t,r,n,s)=>{const e=[];for(const o of r.order){const i=r.byId.get(o),a=t.byId.get(o),u=JSON.stringify(l(i)),f=a===void 0?void 0:JSON.stringify(a);if(f===u)continue;const d=f===void 0?"insert":"update";e.push({delta:{key:o,op:d,row:i,table:n},frame:`{"key":${JSON.stringify(o)},"op":"${d}","row":${u},"table":${s}}`})}return e},S=(t,r,n,s)=>{let e;try{e=JSON.parse(t)}catch{return}if(!Array.isArray(e)||!Array.isArray(r))return;const o=y(e),i=y(r);if(o===void 0||i===void 0||!h(o,i))return;const a=n===""?p:n,u=JSON.stringify(a),f=[...g(o,i,a,u),...m(o,i,a,u)];if(!(f.length>i.order.length)){if(s!==void 0)for(const{frame:d}of f)s.push(d);return f.map(({delta:d})=>d)}},$=(t,r)=>{try{return t.send(r),!0}catch{return!1}},w=(t,r,n,s)=>{const e=JSON.stringify(r);let o=!0;for(const i of n)$(t,`{"type":"delta","id":${e},"delta":${i}${s}}`)||(o=!1);return o},A=async t=>{let r=0;for(;r<100;){r+=1;const n=t.bufferedAmount;if(typeof n!="number"||n<1048576)return;await new Promise(s=>{setTimeout(s,20)})}};export{A as awaitWsDrain,w as sendDeltaFrames,S as subscriptionListDeltas,$ as trySendFrame};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const b=e=>{let t="";for(let i=0;i<e.length;i+=32768)t+=String.fromCharCode(...e.subarray(i,i+32768));return btoa(t)},w=e=>{const t=atob(e),i=new Uint8Array(t.length);for(let c=0;c<t.length;c+=1)i[c]=t.codePointAt(c)??0;return i},o="$lunora.wire$";const g="__proto__",d={BigInt64Array,BigUint64Array,Float32Array,Float64Array,Int8Array,Int16Array,Int32Array,Uint8Array,Uint8ClampedArray,Uint16Array,Uint32Array},p={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},l=(e,t=0)=>{if(t>64)throw new RangeError("wire-codec: value nesting exceeds the 64-level limit");if(e===void 0)return[o,"undefined"];if(e===null)return null;const i=typeof e;if(i==="bigint")return[o,"bigint",e.toString()];if(i==="number"){const r=e;return Number.isNaN(r)?[o,"nan"]:r===1/0?[o,"inf"]:r===-1/0?[o,"-inf"]:r}if(i!=="object")return e;if(e instanceof Date)return[o,"date",l(e.getTime(),t+1)];if(e instanceof Error){const r=e,a={};for(const y of Object.keys(r))r[y]!==void 0&&(a[y]=l(r[y],t+1));const f=[o,"error",r.name,r.message,a];return r.cause!==void 0&&f.push(l(r.cause,t+1)),f}if(e instanceof URL)return[o,"url",e.href];if(e instanceof Map)return[o,"map",[...e.entries()].map(([r,a])=>[l(r,t+1),l(a,t+1)])];if(e instanceof Set)return[o,"set",[...e].map(r=>l(r,t+1))];if(e instanceof ArrayBuffer)return[o,"bytes",b(new Uint8Array(e)),"ArrayBuffer"];if(ArrayBuffer.isView(e)){const r=e,a=r.constructor.name,f=new Uint8Array(r.buffer,r.byteOffset,r.byteLength);return a==="Uint8Array"?[o,"bytes",b(f)]:[o,"bytes",b(f),a]}if(Array.isArray(e)){const r=e.map(a=>l(a,t+1));return r.length>0&&r[0]===o?[o,"arr",r]:r}const c=Object.getPrototypeOf(e);if(c!==null&&c!==Object.prototype){const r=e.constructor?.name??"value";throw new TypeError(`wire-codec: cannot encode a ${r} over the Lunora wire — only plain objects, arrays, and the supported built-ins (Date, Error, URL, Map, Set, ArrayBuffer/typed arrays, bigint) round-trip`)}const n=e,u={};for(const r of Object.keys(n)){const a=n[r];a!==void 0&&(u[r]=l(a,t+1))}return u},s=(e,t=0)=>{if(t>64)throw new RangeError("wire-codec: value nesting exceeds the 64-level limit");if(e===null||typeof e!="object")return e;if(Array.isArray(e)){if(e[0]===o)switch(e[1]){case"-inf":return-1/0;case"arr":return e[2].map(n=>s(n,t+1));case"bigint":{const n=e[2];if(typeof n!="string"||n.length>1024||!/^-?\d+$/.test(n))throw new RangeError("wire-codec: invalid or over-long bigint (max 1024 digits)");return BigInt(n)}case"date":return new Date(s(e[2],t+1));case"map":return new Map(e[2].map(([n,u])=>[s(n,t+1),s(u,t+1)]));case"set":return new Set(e[2].map(n=>s(n,t+1)));case"url":return new URL(e[2]);case"error":{const n=e[2],u=e[3],r=(Object.hasOwn(p,n)?p[n]:void 0)??Error,a=new r(u);a.name!==n&&Object.defineProperty(a,"name",{configurable:!0,value:n,writable:!0});const f=s(e[4],t+1);for(const y of Object.keys(f))y===g?Object.defineProperty(a,y,{configurable:!0,enumerable:!0,value:f[y],writable:!0}):a[y]=f[y];return e.length>5&&Object.defineProperty(a,"cause",{configurable:!0,value:s(e[5],t+1),writable:!0}),a}case"bytes":{const n=w(e[2]),u=e[3]??"Uint8Array";if(u==="ArrayBuffer")return n.buffer.byteLength===n.byteLength?n.buffer:n.slice().buffer;const r=Object.hasOwn(d,u)?d[u]:void 0;return r?new r(n.slice().buffer):n}case"inf":return 1/0;case"nan":return Number.NaN;case"undefined":return;default:return e.map(n=>s(n,t+1))}return e.map(n=>s(n,t+1))}const i=e,c={};for(const n of Object.keys(i)){const u=s(i[n],t+1);n===g?Object.defineProperty(c,n,{configurable:!0,enumerable:!0,value:u,writable:!0}):c[n]=u}return c};export{s as c,l as f};
|
package/package.json
CHANGED
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
import { LunoraError } from '@lunora/errors';
|
|
2
|
-
|
|
3
|
-
const quoteIdentifier = (name) => `"${name.replaceAll('"', '""')}"`;
|
|
4
|
-
|
|
5
|
-
const ADMIN_FUNCTION_PREFIX = "__lunora_admin__:";
|
|
6
|
-
const RELATION_FUNCTION_PREFIX = "__lunora_relation__:";
|
|
7
|
-
const FLAGS_FUNCTION_PREFIX = "__lunora_flags__:";
|
|
8
|
-
const ADMIN_FUNCTIONS = {
|
|
9
|
-
applyCdc: "__lunora_admin__:applyCdc",
|
|
10
|
-
assignIssue: "__lunora_admin__:assignIssue",
|
|
11
|
-
cdcSync: "__lunora_admin__:cdcSync",
|
|
12
|
-
clearCapturedMail: "__lunora_admin__:clearCapturedMail",
|
|
13
|
-
clearQueueMessages: "__lunora_admin__:clearQueueMessages",
|
|
14
|
-
clearTable: "__lunora_admin__:clearTable",
|
|
15
|
-
createWorkflowInstance: "__lunora_admin__:createWorkflowInstance",
|
|
16
|
-
deleteRows: "__lunora_admin__:deleteRows",
|
|
17
|
-
describeTable: "__lunora_admin__:describeTable",
|
|
18
|
-
describeTables: "__lunora_admin__:describeTables",
|
|
19
|
-
exportShard: "__lunora_admin__:exportShard",
|
|
20
|
-
facetColumn: "__lunora_admin__:facetColumn",
|
|
21
|
-
getAdvisories: "__lunora_admin__:getAdvisories",
|
|
22
|
-
getAuditLog: "__lunora_admin__:getAuditLog",
|
|
23
|
-
getAuthMetrics: "__lunora_admin__:getAuthMetrics",
|
|
24
|
-
getCapturedMail: "__lunora_admin__:getCapturedMail",
|
|
25
|
-
getFanoutMetrics: "__lunora_admin__:getFanoutMetrics",
|
|
26
|
-
getFunctionStats: "__lunora_admin__:getFunctionStats",
|
|
27
|
-
getIssues: "__lunora_admin__:getIssues",
|
|
28
|
-
getMetricHistory: "__lunora_admin__:getMetricHistory",
|
|
29
|
-
getMetricSeries: "__lunora_admin__:getMetricSeries",
|
|
30
|
-
listSubscriptions: "__lunora_admin__:listSubscriptions",
|
|
31
|
-
listTableIndexes: "__lunora_admin__:listTableIndexes",
|
|
32
|
-
getLogs: "__lunora_admin__:getLogs",
|
|
33
|
-
getMetrics: "__lunora_admin__:getMetrics",
|
|
34
|
-
getPitrBookmark: "__lunora_admin__:getPitrBookmark",
|
|
35
|
-
getQueueMessages: "__lunora_admin__:getQueueMessages",
|
|
36
|
-
getRequestLog: "__lunora_admin__:getRequestLog",
|
|
37
|
-
getSecurityAudit: "__lunora_admin__:getSecurityAudit",
|
|
38
|
-
getSettings: "__lunora_admin__:getSettings",
|
|
39
|
-
getTraces: "__lunora_admin__:getTraces",
|
|
40
|
-
// eslint-disable-next-line no-secrets/no-secrets -- reserved admin RPC path constant, not a credential
|
|
41
|
-
getWorkflowInstanceStatus: "__lunora_admin__:getWorkflowInstanceStatus",
|
|
42
|
-
ignoreIssue: "__lunora_admin__:ignoreIssue",
|
|
43
|
-
importShard: "__lunora_admin__:importShard",
|
|
44
|
-
listFlags: "__lunora_admin__:listFlags",
|
|
45
|
-
listQueues: "__lunora_admin__:listQueues",
|
|
46
|
-
listTables: "__lunora_admin__:listTables",
|
|
47
|
-
listWorkflows: "__lunora_admin__:listWorkflows",
|
|
48
|
-
maskPolicies: "__lunora_admin__:maskPolicies",
|
|
49
|
-
migrationStatus: "__lunora_admin__:migrationStatus",
|
|
50
|
-
pitrRestore: "__lunora_admin__:pitrRestore",
|
|
51
|
-
rankBefore: "__lunora_admin__:rankBefore",
|
|
52
|
-
rankPage: "__lunora_admin__:rankPage",
|
|
53
|
-
readTablePage: "__lunora_admin__:readTablePage",
|
|
54
|
-
recordAuthEvent: "__lunora_admin__:recordAuthEvent",
|
|
55
|
-
recordContainerEvent: "__lunora_admin__:recordContainerEvent",
|
|
56
|
-
recordMail: "__lunora_admin__:recordMail",
|
|
57
|
-
recordQueueMessage: "__lunora_admin__:recordQueueMessage",
|
|
58
|
-
replayQueueMessage: "__lunora_admin__:replayQueueMessage",
|
|
59
|
-
resolveIssue: "__lunora_admin__:resolveIssue",
|
|
60
|
-
rlsPolicies: "__lunora_admin__:rlsPolicies",
|
|
61
|
-
runAs: "__lunora_admin__:runAs",
|
|
62
|
-
runMigration: "__lunora_admin__:runMigration",
|
|
63
|
-
runSql: "__lunora_admin__:runSql",
|
|
64
|
-
sendQueueMessage: "__lunora_admin__:sendQueueMessage",
|
|
65
|
-
sendTestMail: "__lunora_admin__:sendTestMail",
|
|
66
|
-
setIssueSeverity: "__lunora_admin__:setIssueSeverity",
|
|
67
|
-
storageOrphans: "__lunora_admin__:storageOrphans",
|
|
68
|
-
storageReferences: "__lunora_admin__:storageReferences",
|
|
69
|
-
storageRules: "__lunora_admin__:storageRules",
|
|
70
|
-
studioFeatures: "__lunora_admin__:studioFeatures",
|
|
71
|
-
writeRow: "__lunora_admin__:writeRow"
|
|
72
|
-
};
|
|
73
|
-
const DEFAULT_PAGE_SIZE = 50;
|
|
74
|
-
const MAX_PAGE_SIZE = 500;
|
|
75
|
-
const DEFAULT_FACET_LIMIT = 30;
|
|
76
|
-
const MAX_FACET_LIMIT = 200;
|
|
77
|
-
const DOC_COLUMN = "__doc__";
|
|
78
|
-
const safeParseObject = (text) => {
|
|
79
|
-
try {
|
|
80
|
-
const value = JSON.parse(text);
|
|
81
|
-
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
82
|
-
} catch {
|
|
83
|
-
return void 0;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
const expandDocumentRows = (columns, rows) => {
|
|
87
|
-
if (!columns.includes(DOC_COLUMN)) {
|
|
88
|
-
return { columns, rows };
|
|
89
|
-
}
|
|
90
|
-
const parsed = [];
|
|
91
|
-
for (const row of rows) {
|
|
92
|
-
const raw = row[DOC_COLUMN];
|
|
93
|
-
const documentData = typeof raw === "string" ? safeParseObject(raw) : void 0;
|
|
94
|
-
if (documentData === void 0) {
|
|
95
|
-
return { columns, rows };
|
|
96
|
-
}
|
|
97
|
-
const meta = Object.fromEntries(Object.entries(row).filter(([column]) => column !== DOC_COLUMN));
|
|
98
|
-
parsed.push({ ...meta, ...documentData });
|
|
99
|
-
}
|
|
100
|
-
const metaColumns = columns.filter((name) => name !== DOC_COLUMN);
|
|
101
|
-
const documentKeys = [];
|
|
102
|
-
const seen = new Set(metaColumns);
|
|
103
|
-
for (const documentData of parsed) {
|
|
104
|
-
for (const key of Object.keys(documentData)) {
|
|
105
|
-
if (!seen.has(key)) {
|
|
106
|
-
seen.add(key);
|
|
107
|
-
documentKeys.push(key);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return { columns: [...metaColumns, ...documentKeys], rows: parsed };
|
|
112
|
-
};
|
|
113
|
-
const escapeLike = (value) => value.replaceAll(/[\\%_]/g, (character) => `\\${character}`);
|
|
114
|
-
const isInternalTable = (name) => name.startsWith("sqlite_") || name.startsWith("_cf_") || name.startsWith("__miniflare") || name.startsWith("__lunora") || name.includes("__fts_");
|
|
115
|
-
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
116
|
-
const countRows = (sql, quotedTable) => {
|
|
117
|
-
const row = sql.exec(`SELECT COUNT(*) AS c FROM ${quotedTable}`).one();
|
|
118
|
-
return Number(row.c);
|
|
119
|
-
};
|
|
120
|
-
const listTables = (sql) => {
|
|
121
|
-
const names = sql.exec("SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name").toArray();
|
|
122
|
-
const tables = [];
|
|
123
|
-
for (const { name } of names) {
|
|
124
|
-
if (isInternalTable(name)) {
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
tables.push({ name, rowCount: countRows(sql, quoteIdentifier(name)) });
|
|
128
|
-
}
|
|
129
|
-
return tables;
|
|
130
|
-
};
|
|
131
|
-
const tableExists = (sql, table) => sql.exec("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1", table).toArray().length > 0;
|
|
132
|
-
const FILTER_SQL_OPERATOR = { eq: "=", gt: ">", gte: ">=", lt: "<", lte: "<=", ne: "<>" };
|
|
133
|
-
const filterValueText = (value) => {
|
|
134
|
-
if (typeof value === "string") {
|
|
135
|
-
return value;
|
|
136
|
-
}
|
|
137
|
-
return typeof value === "number" || typeof value === "boolean" ? String(value) : "";
|
|
138
|
-
};
|
|
139
|
-
const resolveColumnExpression = (column, physicalColumns) => {
|
|
140
|
-
const isPhysical = physicalColumns.includes(column);
|
|
141
|
-
const isDocumentStored = physicalColumns.includes(DOC_COLUMN);
|
|
142
|
-
if (!isPhysical && !isDocumentStored) {
|
|
143
|
-
return void 0;
|
|
144
|
-
}
|
|
145
|
-
return isPhysical ? { expression: quoteIdentifier(column), params: [] } : { expression: `json_extract(${quoteIdentifier(DOC_COLUMN)}, ?)`, params: [`$."${column.replaceAll('"', '""')}"`] };
|
|
146
|
-
};
|
|
147
|
-
const buildFilterClause = (clause, physicalColumns) => {
|
|
148
|
-
const resolved = resolveColumnExpression(clause.column, physicalColumns);
|
|
149
|
-
if (resolved === void 0) {
|
|
150
|
-
return void 0;
|
|
151
|
-
}
|
|
152
|
-
const { expression, params: pathParameters } = resolved;
|
|
153
|
-
if (clause.operator === "contains") {
|
|
154
|
-
return {
|
|
155
|
-
params: [...pathParameters, `%${escapeLike(filterValueText(clause.value))}%`],
|
|
156
|
-
sql: String.raw`CAST(${expression} AS TEXT) LIKE ? ESCAPE '\'`
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
return { params: [...pathParameters, clause.value], sql: `${expression} ${FILTER_SQL_OPERATOR[clause.operator]} ?` };
|
|
160
|
-
};
|
|
161
|
-
const buildTablePredicate = (columns, needle, filters) => {
|
|
162
|
-
const conjuncts = [];
|
|
163
|
-
const parameters = [];
|
|
164
|
-
if (needle !== "" && columns.length > 0) {
|
|
165
|
-
const pattern = `%${escapeLike(needle)}%`;
|
|
166
|
-
conjuncts.push(`(${columns.map((name) => String.raw`CAST(${quoteIdentifier(name)} AS TEXT) LIKE ? ESCAPE '\'`).join(" OR ")})`);
|
|
167
|
-
parameters.push(...columns.map(() => pattern));
|
|
168
|
-
}
|
|
169
|
-
for (const clause of filters ?? []) {
|
|
170
|
-
const built = buildFilterClause(clause, columns);
|
|
171
|
-
if (built !== void 0) {
|
|
172
|
-
conjuncts.push(`(${built.sql})`);
|
|
173
|
-
parameters.push(...built.params);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
return conjuncts.length === 0 ? void 0 : { parameters, where: conjuncts.join(" AND ") };
|
|
177
|
-
};
|
|
178
|
-
const buildOrderBy = (orderBy, physicalColumns) => {
|
|
179
|
-
if (orderBy === void 0) {
|
|
180
|
-
return void 0;
|
|
181
|
-
}
|
|
182
|
-
const resolved = resolveColumnExpression(orderBy.column, physicalColumns);
|
|
183
|
-
if (resolved === void 0) {
|
|
184
|
-
return void 0;
|
|
185
|
-
}
|
|
186
|
-
const keyword = orderBy.direction === "desc" ? "DESC" : "ASC";
|
|
187
|
-
return { params: resolved.params, sql: `${resolved.expression} ${keyword}` };
|
|
188
|
-
};
|
|
189
|
-
const readTablePage = (sql, options) => {
|
|
190
|
-
const { table } = options;
|
|
191
|
-
if (isInternalTable(table) || !tableExists(sql, table)) {
|
|
192
|
-
throw new LunoraError("UNKNOWN_TABLE", `unknown table: ${table}`, { status: 404 });
|
|
193
|
-
}
|
|
194
|
-
const limit = clamp(Math.trunc(options.limit ?? DEFAULT_PAGE_SIZE), 1, MAX_PAGE_SIZE);
|
|
195
|
-
const offset = Math.max(0, Math.trunc(options.offset ?? 0));
|
|
196
|
-
const quoted = quoteIdentifier(table);
|
|
197
|
-
const columns = sql.exec(`PRAGMA table_info(${quoted})`).toArray().map((column) => column.name);
|
|
198
|
-
const needle = options.search?.trim() ?? "";
|
|
199
|
-
const withReferences = (page) => {
|
|
200
|
-
if (options.refs === void 0) {
|
|
201
|
-
return page;
|
|
202
|
-
}
|
|
203
|
-
const references = {};
|
|
204
|
-
for (const column of page.columns) {
|
|
205
|
-
const target = options.refs[column];
|
|
206
|
-
if (target !== void 0) {
|
|
207
|
-
references[column] = target;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
return Object.keys(references).length > 0 ? { ...page, refs: references } : page;
|
|
211
|
-
};
|
|
212
|
-
const predicate = buildTablePredicate(columns, needle, options.filters);
|
|
213
|
-
const order = buildOrderBy(options.orderBy, columns);
|
|
214
|
-
const whereSql = predicate === void 0 ? "" : ` WHERE ${predicate.where}`;
|
|
215
|
-
const orderSql = order === void 0 ? "" : ` ORDER BY ${order.sql}`;
|
|
216
|
-
const whereParams = predicate?.parameters ?? [];
|
|
217
|
-
const orderParams = order?.params ?? [];
|
|
218
|
-
let total;
|
|
219
|
-
if (!options.skipCount) {
|
|
220
|
-
total = predicate === void 0 ? countRows(sql, quoted) : Number(sql.exec(`SELECT COUNT(*) AS c FROM ${quoted}${whereSql}`, ...whereParams).one().c);
|
|
221
|
-
}
|
|
222
|
-
const rawRows = sql.exec(`SELECT * FROM ${quoted}${whereSql}${orderSql} LIMIT ? OFFSET ?`, ...whereParams, ...orderParams, limit, offset).toArray();
|
|
223
|
-
return withReferences({ ...expandDocumentRows(columns, rawRows), total });
|
|
224
|
-
};
|
|
225
|
-
const selectMatchingIds = (sql, options) => {
|
|
226
|
-
const { table } = options;
|
|
227
|
-
if (isInternalTable(table) || !tableExists(sql, table)) {
|
|
228
|
-
throw new LunoraError("UNKNOWN_TABLE", `unknown table: ${table}`, { status: 404 });
|
|
229
|
-
}
|
|
230
|
-
const limit = clamp(Math.trunc(options.limit ?? MAX_PAGE_SIZE), 1, MAX_PAGE_SIZE);
|
|
231
|
-
const quoted = quoteIdentifier(table);
|
|
232
|
-
const columns = sql.exec(`PRAGMA table_info(${quoted})`).toArray().map((column) => column.name);
|
|
233
|
-
const needle = options.search?.trim() ?? "";
|
|
234
|
-
const predicate = buildTablePredicate(columns, needle, options.filters);
|
|
235
|
-
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();
|
|
236
|
-
const hasMore = fetched.length > limit;
|
|
237
|
-
const ids = (hasMore ? fetched.slice(0, limit) : fetched).map((row) => row.id);
|
|
238
|
-
return { hasMore, ids };
|
|
239
|
-
};
|
|
240
|
-
const knownDisplayColumns = (sql, quotedTable, physicalColumns) => {
|
|
241
|
-
const known = new Set(physicalColumns.filter((name) => name !== DOC_COLUMN));
|
|
242
|
-
if (!physicalColumns.includes(DOC_COLUMN)) {
|
|
243
|
-
return known;
|
|
244
|
-
}
|
|
245
|
-
const sample = sql.exec(`SELECT ${quoteIdentifier(DOC_COLUMN)} AS doc FROM ${quotedTable} LIMIT ?`, MAX_PAGE_SIZE).toArray();
|
|
246
|
-
for (const { doc } of sample) {
|
|
247
|
-
const documentData = typeof doc === "string" ? safeParseObject(doc) : void 0;
|
|
248
|
-
if (documentData !== void 0) {
|
|
249
|
-
for (const key of Object.keys(documentData)) {
|
|
250
|
-
known.add(key);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
return known;
|
|
255
|
-
};
|
|
256
|
-
const facetColumn = (sql, options) => {
|
|
257
|
-
const { column, table } = options;
|
|
258
|
-
if (isInternalTable(table) || !tableExists(sql, table)) {
|
|
259
|
-
throw new LunoraError("UNKNOWN_TABLE", `unknown table: ${table}`, { status: 404 });
|
|
260
|
-
}
|
|
261
|
-
const quoted = quoteIdentifier(table);
|
|
262
|
-
const physicalColumns = sql.exec(`PRAGMA table_info(${quoted})`).toArray().map((info) => info.name);
|
|
263
|
-
if (!knownDisplayColumns(sql, quoted, physicalColumns).has(column)) {
|
|
264
|
-
throw new LunoraError("UNKNOWN_COLUMN", `unknown column: ${column}`, { status: 404 });
|
|
265
|
-
}
|
|
266
|
-
const resolved = resolveColumnExpression(column, physicalColumns);
|
|
267
|
-
if (resolved === void 0) {
|
|
268
|
-
throw new LunoraError("UNKNOWN_COLUMN", `unknown column: ${column}`, { status: 404 });
|
|
269
|
-
}
|
|
270
|
-
const limit = clamp(Math.trunc(options.limit ?? DEFAULT_FACET_LIMIT), 1, MAX_FACET_LIMIT);
|
|
271
|
-
const needle = options.search?.trim() ?? "";
|
|
272
|
-
const predicate = buildTablePredicate(physicalColumns, needle, options.filters);
|
|
273
|
-
const whereSql = predicate === void 0 ? "" : ` WHERE ${predicate.where}`;
|
|
274
|
-
const whereParams = predicate?.parameters ?? [];
|
|
275
|
-
const rows = sql.exec(
|
|
276
|
-
`SELECT ${resolved.expression} AS value, COUNT(*) AS count FROM ${quoted}${whereSql} GROUP BY ${resolved.expression} ORDER BY count DESC LIMIT ?`,
|
|
277
|
-
...resolved.params,
|
|
278
|
-
...whereParams,
|
|
279
|
-
...resolved.params,
|
|
280
|
-
limit + 1
|
|
281
|
-
).toArray();
|
|
282
|
-
const truncated = rows.length > limit;
|
|
283
|
-
const kept = truncated ? rows.slice(0, limit) : rows;
|
|
284
|
-
return {
|
|
285
|
-
truncated,
|
|
286
|
-
values: kept.map((row) => {
|
|
287
|
-
return { count: Number(row.count), value: row.value };
|
|
288
|
-
})
|
|
289
|
-
};
|
|
290
|
-
};
|
|
291
|
-
const findStorageReferences = (sql, storageColumns, keys) => {
|
|
292
|
-
const references = {};
|
|
293
|
-
const scanned = keys.slice(0, MAX_PAGE_SIZE);
|
|
294
|
-
for (const key of scanned) {
|
|
295
|
-
references[key] = [];
|
|
296
|
-
}
|
|
297
|
-
if (scanned.length === 0) {
|
|
298
|
-
return { references, storageColumns };
|
|
299
|
-
}
|
|
300
|
-
const placeholders = scanned.map(() => "?").join(", ");
|
|
301
|
-
for (const [table, columns] of Object.entries(storageColumns)) {
|
|
302
|
-
if (isInternalTable(table) || !tableExists(sql, table)) {
|
|
303
|
-
continue;
|
|
304
|
-
}
|
|
305
|
-
const quoted = quoteIdentifier(table);
|
|
306
|
-
const physicalColumns = sql.exec(`PRAGMA table_info(${quoted})`).toArray().map((column) => column.name);
|
|
307
|
-
for (const column of columns) {
|
|
308
|
-
const resolved = resolveColumnExpression(column, physicalColumns);
|
|
309
|
-
if (resolved === void 0) {
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
const rows = sql.exec(
|
|
313
|
-
`SELECT id, ${resolved.expression} AS ref FROM ${quoted} WHERE ${resolved.expression} IN (${placeholders})`,
|
|
314
|
-
...resolved.params,
|
|
315
|
-
...resolved.params,
|
|
316
|
-
...scanned
|
|
317
|
-
).toArray();
|
|
318
|
-
for (const row of rows) {
|
|
319
|
-
references[row.ref]?.push({ column, id: row.id, table });
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
return { references, storageColumns };
|
|
324
|
-
};
|
|
325
|
-
const summarizeSubscriptions = (attachments) => {
|
|
326
|
-
const connections = attachments.map((attachment, id) => {
|
|
327
|
-
const subscriptions = Object.values(attachment.subs ?? {}).map((sub) => {
|
|
328
|
-
return { args: sub.args, functionPath: sub.functionPath, table: sub.table };
|
|
329
|
-
});
|
|
330
|
-
return { admin: attachment.admin === true, id, subscriptions };
|
|
331
|
-
});
|
|
332
|
-
const totalSubscriptions = connections.reduce((sum, connection) => sum + connection.subscriptions.length, 0);
|
|
333
|
-
return { connections, totalConnections: connections.length, totalSubscriptions };
|
|
334
|
-
};
|
|
335
|
-
const DEFAULT_FANOUT_TOPIC_LIMIT = 20;
|
|
336
|
-
const createFanoutCounters = () => {
|
|
337
|
-
return { maxMs: 0, passes: 0, peakSocketsIterated: 0, socketsDelivered: 0, socketsIterated: 0, totalMs: 0 };
|
|
338
|
-
};
|
|
339
|
-
const recordFanoutPass = (counters, iterated, delivered, ms) => {
|
|
340
|
-
return {
|
|
341
|
-
maxMs: Math.max(counters.maxMs, ms),
|
|
342
|
-
passes: counters.passes + 1,
|
|
343
|
-
peakSocketsIterated: Math.max(counters.peakSocketsIterated, iterated),
|
|
344
|
-
socketsDelivered: counters.socketsDelivered + delivered,
|
|
345
|
-
socketsIterated: counters.socketsIterated + iterated,
|
|
346
|
-
totalMs: counters.totalMs + ms
|
|
347
|
-
};
|
|
348
|
-
};
|
|
349
|
-
const summarizeFanoutTopics = (attachments, limit = DEFAULT_FANOUT_TOPIC_LIMIT) => {
|
|
350
|
-
const shapeCounts = /* @__PURE__ */ new Map();
|
|
351
|
-
const whisperCounts = /* @__PURE__ */ new Map();
|
|
352
|
-
for (const attachment of attachments) {
|
|
353
|
-
for (const shape of Object.values(attachment.shapes ?? {})) {
|
|
354
|
-
const key = shape.name ?? "(unknown shape)";
|
|
355
|
-
shapeCounts.set(key, (shapeCounts.get(key) ?? 0) + 1);
|
|
356
|
-
}
|
|
357
|
-
for (const topic of attachment.whispers ?? []) {
|
|
358
|
-
whisperCounts.set(topic, (whisperCounts.get(topic) ?? 0) + 1);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
const topics = [
|
|
362
|
-
...[...shapeCounts].map(([topic, subscribers]) => {
|
|
363
|
-
return { kind: "shape", subscribers, topic };
|
|
364
|
-
}),
|
|
365
|
-
...[...whisperCounts].map(([topic, subscribers]) => {
|
|
366
|
-
return { kind: "whisper", subscribers, topic };
|
|
367
|
-
})
|
|
368
|
-
];
|
|
369
|
-
topics.sort((a, b) => b.subscribers - a.subscribers || a.topic.localeCompare(b.topic));
|
|
370
|
-
const peakSubscribers = topics[0]?.subscribers ?? 0;
|
|
371
|
-
return { peakSubscribers, topics: topics.slice(0, limit), totalConnections: attachments.length };
|
|
372
|
-
};
|
|
373
|
-
|
|
374
|
-
export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, DEFAULT_FANOUT_TOPIC_LIMIT, FLAGS_FUNCTION_PREFIX, MAX_PAGE_SIZE, RELATION_FUNCTION_PREFIX, createFanoutCounters, facetColumn, findStorageReferences, listTables, readTablePage, recordFanoutPass, selectMatchingIds, summarizeFanoutTopics, summarizeSubscriptions };
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { LunoraError } from '@lunora/errors';
|
|
2
|
-
|
|
3
|
-
const COUNT_OPTION_KEYS = /* @__PURE__ */ new Set(["baseWhere", "relationBaseWhere", "restrictsCounts", "where"]);
|
|
4
|
-
const matchesStaticWhere = (document, predicate) => {
|
|
5
|
-
for (const [field, expected] of Object.entries(predicate)) {
|
|
6
|
-
const actual = document[field];
|
|
7
|
-
if (expected !== null && typeof expected === "object" && !Array.isArray(expected)) {
|
|
8
|
-
const operatorKeys = Object.keys(expected);
|
|
9
|
-
if (operatorKeys.length === 1 && operatorKeys[0] === "eq") {
|
|
10
|
-
if (actual !== expected.eq) {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
continue;
|
|
14
|
-
}
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
if (actual !== expected) {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return true;
|
|
22
|
-
};
|
|
23
|
-
const normalizeCountArgument = (argument) => {
|
|
24
|
-
if (argument === void 0) {
|
|
25
|
-
return {};
|
|
26
|
-
}
|
|
27
|
-
if (typeof argument !== "object" || Array.isArray(argument)) {
|
|
28
|
-
return { where: argument };
|
|
29
|
-
}
|
|
30
|
-
const keys = Object.keys(argument);
|
|
31
|
-
if (keys.length === 0) {
|
|
32
|
-
return {};
|
|
33
|
-
}
|
|
34
|
-
if (keys.every((key) => COUNT_OPTION_KEYS.has(key))) {
|
|
35
|
-
return argument;
|
|
36
|
-
}
|
|
37
|
-
return { where: argument };
|
|
38
|
-
};
|
|
39
|
-
const AGGREGATE_SQL_FUNCTION = { avg: "AVG", count: "COUNT", max: "MAX", min: "MIN", sum: "SUM" };
|
|
40
|
-
const aggregateSqlFunction = (op) => {
|
|
41
|
-
const sqlFunction = AGGREGATE_SQL_FUNCTION[op];
|
|
42
|
-
if (sqlFunction === void 0) {
|
|
43
|
-
throw new LunoraError("INTERNAL", `unknown aggregate op "${op}": expected one of ${Object.keys(AGGREGATE_SQL_FUNCTION).join(", ")}`);
|
|
44
|
-
}
|
|
45
|
-
return sqlFunction;
|
|
46
|
-
};
|
|
47
|
-
const throwingScheduler = {
|
|
48
|
-
runAfter: () => {
|
|
49
|
-
throw new LunoraError("INTERNAL", "ctx.scheduler: no scheduler configured for triggers. Pass `scheduler` to the ctx-db factory.");
|
|
50
|
-
},
|
|
51
|
-
runAt: () => {
|
|
52
|
-
throw new LunoraError("INTERNAL", "ctx.scheduler: no scheduler configured for triggers. Pass `scheduler` to the ctx-db factory.");
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export { AGGREGATE_SQL_FUNCTION, aggregateSqlFunction, matchesStaticWhere, normalizeCountArgument, throwingScheduler };
|
|
@@ -1,84 +0,0 @@
|
|
|
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 };
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { sql } from 'drizzle-orm';
|
|
2
|
-
import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
|
|
3
|
-
import { ConflictError } from './ConflictError-CLoq37xH.mjs';
|
|
4
|
-
|
|
5
|
-
const CDC_LOG_TABLE = "__cdc_log";
|
|
6
|
-
const migrateCdcLog = (sql$1) => {
|
|
7
|
-
runDrizzle(
|
|
8
|
-
sql$1,
|
|
9
|
-
sql`CREATE TABLE IF NOT EXISTS ${sql.identifier(CDC_LOG_TABLE)} (
|
|
10
|
-
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
11
|
-
ts REAL NOT NULL,
|
|
12
|
-
${sql.identifier("table")} TEXT NOT NULL,
|
|
13
|
-
id TEXT NOT NULL,
|
|
14
|
-
op TEXT NOT NULL,
|
|
15
|
-
doc TEXT
|
|
16
|
-
)`
|
|
17
|
-
);
|
|
18
|
-
};
|
|
19
|
-
const appendCdcChange = (sql$1, ts, table, id, op, doc) => {
|
|
20
|
-
const docValue = doc === void 0 ? null : JSON.stringify(doc);
|
|
21
|
-
runDrizzle(
|
|
22
|
-
sql$1,
|
|
23
|
-
sql`INSERT INTO ${sql.identifier(CDC_LOG_TABLE)} (ts, ${sql.identifier("table")}, id, op, doc) VALUES (${ts}, ${table}, ${id}, ${op}, ${docValue})`
|
|
24
|
-
);
|
|
25
|
-
};
|
|
26
|
-
const readCdcChanges = (sql$1, options = {}) => {
|
|
27
|
-
const sinceSeq = options.sinceSeq ?? 0;
|
|
28
|
-
const limit = Math.max(1, Math.min(options.limit ?? 1e3, 1e4));
|
|
29
|
-
const tableFilter = options.tables && options.tables.size > 0 ? sql` AND ${sql.identifier("table")} IN (${sql.join(
|
|
30
|
-
[...options.tables].map((table) => sql`${table}`),
|
|
31
|
-
sql`, `
|
|
32
|
-
)})` : sql``;
|
|
33
|
-
const rows = runDrizzle(
|
|
34
|
-
sql$1,
|
|
35
|
-
sql`SELECT seq, ts, ${sql.identifier("table")}, id, op, doc FROM ${sql.identifier(CDC_LOG_TABLE)} WHERE seq > ${sinceSeq}${tableFilter} ORDER BY seq ASC LIMIT ${limit}`
|
|
36
|
-
).toArray();
|
|
37
|
-
const changes = rows.map((row) => {
|
|
38
|
-
const base = { id: row.id, op: row.op, seq: row.seq, table: row.table, ts: row.ts };
|
|
39
|
-
return row.doc === null ? base : { ...base, doc: JSON.parse(row.doc) };
|
|
40
|
-
});
|
|
41
|
-
return { changes, cursor: changes.at(-1)?.seq ?? sinceSeq };
|
|
42
|
-
};
|
|
43
|
-
const trimCdcChanges = (sql$1, throughSeq) => {
|
|
44
|
-
runDrizzle(sql$1, sql`DELETE FROM ${sql.identifier(CDC_LOG_TABLE)} WHERE seq <= ${throughSeq}`);
|
|
45
|
-
};
|
|
46
|
-
const readCdcCursor = (sql$1) => {
|
|
47
|
-
const seqRow = runDrizzle(sql$1, sql`SELECT seq FROM sqlite_sequence WHERE name = ${CDC_LOG_TABLE}`).toArray();
|
|
48
|
-
const fromSequence = seqRow[0]?.seq;
|
|
49
|
-
if (typeof fromSequence === "number") {
|
|
50
|
-
return fromSequence;
|
|
51
|
-
}
|
|
52
|
-
const rows = runDrizzle(sql$1, sql`SELECT MAX(seq) AS seq FROM ${sql.identifier(CDC_LOG_TABLE)}`).toArray();
|
|
53
|
-
return rows[0]?.seq ?? 0;
|
|
54
|
-
};
|
|
55
|
-
const minCdcSeq = (sql$1) => {
|
|
56
|
-
const rows = runDrizzle(sql$1, sql`SELECT MIN(seq) AS seq FROM ${sql.identifier(CDC_LOG_TABLE)}`).toArray();
|
|
57
|
-
return rows[0]?.seq ?? void 0;
|
|
58
|
-
};
|
|
59
|
-
const CDC_META_TABLE = "__cdc_meta";
|
|
60
|
-
const migrateCdcMeta = (sql$1) => {
|
|
61
|
-
runDrizzle(sql$1, sql`CREATE TABLE IF NOT EXISTS ${sql.identifier(CDC_META_TABLE)} (id INTEGER PRIMARY KEY CHECK (id = 1), epoch TEXT NOT NULL)`);
|
|
62
|
-
};
|
|
63
|
-
const readCdcEpoch = (sql$1) => {
|
|
64
|
-
migrateCdcMeta(sql$1);
|
|
65
|
-
const rows = runDrizzle(sql$1, sql`SELECT epoch FROM ${sql.identifier(CDC_META_TABLE)} WHERE id = 1`).toArray();
|
|
66
|
-
const existing = rows[0]?.epoch;
|
|
67
|
-
if (typeof existing === "string" && existing.length > 0) {
|
|
68
|
-
return existing;
|
|
69
|
-
}
|
|
70
|
-
const minted = crypto.randomUUID();
|
|
71
|
-
runDrizzle(sql$1, sql`INSERT INTO ${sql.identifier(CDC_META_TABLE)} (id, epoch) VALUES (1, ${minted})`);
|
|
72
|
-
return minted;
|
|
73
|
-
};
|
|
74
|
-
const bumpCdcEpoch = (sql$1) => {
|
|
75
|
-
migrateCdcMeta(sql$1);
|
|
76
|
-
const next = crypto.randomUUID();
|
|
77
|
-
runDrizzle(sql$1, sql`INSERT INTO ${sql.identifier(CDC_META_TABLE)} (id, epoch) VALUES (1, ${next}) ON CONFLICT(id) DO UPDATE SET epoch = excluded.epoch`);
|
|
78
|
-
return next;
|
|
79
|
-
};
|
|
80
|
-
const applyCdcChange = async (writer, change) => {
|
|
81
|
-
if (change.op === "delete") {
|
|
82
|
-
await writer.delete(change.id, change.table);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
const document = change.doc ?? {};
|
|
86
|
-
try {
|
|
87
|
-
await writer.insert(change.table, document, { allowExplicitId: true });
|
|
88
|
-
} catch (error) {
|
|
89
|
-
if (!(error instanceof ConflictError)) {
|
|
90
|
-
throw error;
|
|
91
|
-
}
|
|
92
|
-
const fields = {};
|
|
93
|
-
for (const [key, value] of Object.entries(document)) {
|
|
94
|
-
if (key !== "_id") {
|
|
95
|
-
fields[key] = value;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
await writer.replace(change.id, fields, void 0, { allowExplicitId: true });
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
const applyCdcChanges = async (writer, changes) => {
|
|
102
|
-
for (const change of changes) {
|
|
103
|
-
await applyCdcChange(writer, change);
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
export { CDC_LOG_TABLE, CDC_META_TABLE, appendCdcChange, applyCdcChanges, bumpCdcEpoch, migrateCdcLog, migrateCdcMeta, minCdcSeq, readCdcChanges, readCdcCursor, readCdcEpoch, trimCdcChanges };
|