mcpscraper-memory-db 1.0.0 → 1.0.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.
- package/dist/db/chat-link.js +3 -3
- package/dist/db/chat-link.js.map +1 -1
- package/dist/db/facts.js +1 -1
- package/dist/db/facts.js.map +1 -1
- package/dist/db/interlink-state.js +1 -1
- package/dist/db/interlink-state.js.map +1 -1
- package/dist/db/keys.js +2 -2
- package/dist/db/keys.js.map +1 -1
- package/dist/db/messages.js +2 -2
- package/dist/db/messages.js.map +1 -1
- package/dist/db/notes.js +4 -4
- package/dist/db/notes.js.map +1 -1
- package/dist/db/presence.js +1 -1
- package/dist/db/presence.js.map +1 -1
- package/dist/db/props-snapshots.js +1 -1
- package/dist/db/props-snapshots.js.map +1 -1
- package/dist/db/provenance.js +1 -1
- package/dist/db/provenance.js.map +1 -1
- package/dist/db/schedule-entitlements.js +2 -2
- package/dist/db/schedule-entitlements.js.map +1 -1
- package/dist/db/schedule-link.js +3 -3
- package/dist/db/schedule-link.js.map +1 -1
- package/dist/db/scheduled-actions.js +1 -1
- package/dist/db/scheduled-actions.js.map +1 -1
- package/dist/db/secure-notes.js +2 -2
- package/dist/db/secure-notes.js.map +1 -1
- package/dist/db/shares.js +1 -1
- package/dist/db/shares.js.map +1 -1
- package/dist/db/tables.js +1 -1
- package/dist/db/tables.js.map +1 -1
- package/dist/db/trust.js +2 -2
- package/dist/db/trust.js.map +1 -1
- package/dist/db/usage.js +3 -3
- package/dist/db/usage.js.map +1 -1
- package/dist/db/vaults.js +3 -3
- package/dist/db/vaults.js.map +1 -1
- package/dist/db/video-runs.js +2 -2
- package/dist/db/video-runs.js.map +1 -1
- package/dist/db/webhooks.js +2 -2
- package/dist/db/webhooks.js.map +1 -1
- package/dist/lib/auth.js +3 -3
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/video-reaper.js.map +1 -1
- package/dist/rag/index-pipeline.js +4 -4
- package/dist/rag/index-pipeline.js.map +1 -1
- package/dist/rag/query-tool.d.ts +5 -1
- package/dist/rag/query-tool.js +2 -2
- package/dist/rag/query-tool.js.map +1 -1
- package/dist/rag/vector-store.js +1 -1
- package/dist/rag/vector-store.js.map +1 -1
- package/package.json +1 -1
package/dist/db/chat-link.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ensureSchema, query } from "./pool";
|
|
2
|
-
import { issueKeyRow, revokeKeyRow, getKeyById } from "./keys";
|
|
3
|
-
import { fullScope } from "../lib/auth";
|
|
1
|
+
import { ensureSchema, query } from "./pool.js";
|
|
2
|
+
import { issueKeyRow, revokeKeyRow, getKeyById } from "./keys.js";
|
|
3
|
+
import { fullScope } from "../lib/auth.js";
|
|
4
4
|
async function getChatLinkKeyId(identity) {
|
|
5
5
|
await ensureSchema();
|
|
6
6
|
const res = await query(
|
package/dist/db/chat-link.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/chat-link.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\nimport { issueKeyRow, revokeKeyRow, getKeyById } from './keys'\nimport { fullScope } from '../lib/auth'\n\nasync function getChatLinkKeyId(identity: string): Promise<string | null> {\n await ensureSchema()\n const res = await query<{ chat_link_key_id: string | null }>(\n `SELECT chat_link_key_id FROM mem_settings WHERE identity = $1 LIMIT 1`,\n [identity],\n )\n return res.rows[0]?.chat_link_key_id ?? null\n}\n\nasync function setChatLinkKeyId(identity: string, keyId: string | null): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_settings (identity, chat_link_key_id, updated_at)\n VALUES ($1, $2, now())\n ON CONFLICT (identity) DO UPDATE SET chat_link_key_id = EXCLUDED.chat_link_key_id, updated_at = now()`,\n [identity, keyId],\n )\n}\n\nexport async function getOrCreateChatLinkSecret(identity: string): Promise<string> {\n const existingId = await getChatLinkKeyId(identity)\n if (existingId) {\n const existing = await getKeyById(existingId)\n if (existing && !existing.revoked) {\n throw new ChatLinkSecretNotRetrievable()\n }\n }\n const issued = await issueKeyRow({ identity, vaults: [], scope: fullScope(), plan: 'free', expiresAt: null })\n await setChatLinkKeyId(identity, issued.keyId)\n return issued.secret\n}\n\nexport class ChatLinkSecretNotRetrievable extends Error {\n constructor() {\n super('A chat link already exists for this identity; its secret cannot be re-displayed (only revoked and reissued).')\n }\n}\n\nexport async function revokeChatLink(identity: string): Promise<boolean> {\n const existingId = await getChatLinkKeyId(identity)\n if (!existingId) return false\n await revokeKeyRow(existingId)\n await setChatLinkKeyId(identity, null)\n return true\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AACpC,SAAS,aAAa,cAAc,kBAAkB;AACtD,SAAS,iBAAiB;AAE1B,eAAe,iBAAiB,UAA0C;AACxE,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AACA,SAAO,IAAI,KAAK,CAAC,GAAG,oBAAoB;AAC1C;AAEA,eAAe,iBAAiB,UAAkB,OAAqC;AACrF,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA,IAGA,CAAC,UAAU,KAAK;AAAA,EAClB;AACF;AAEA,eAAsB,0BAA0B,UAAmC;AACjF,QAAM,aAAa,MAAM,iBAAiB,QAAQ;AAClD,MAAI,YAAY;AACd,UAAM,WAAW,MAAM,WAAW,UAAU;AAC5C,QAAI,YAAY,CAAC,SAAS,SAAS;AACjC,YAAM,IAAI,6BAA6B;AAAA,IACzC;AAAA,EACF;AACA,QAAM,SAAS,MAAM,YAAY,EAAE,UAAU,QAAQ,CAAC,GAAG,OAAO,UAAU,GAAG,MAAM,QAAQ,WAAW,KAAK,CAAC;AAC5G,QAAM,iBAAiB,UAAU,OAAO,KAAK;AAC7C,SAAO,OAAO;AAChB;AAEO,MAAM,qCAAqC,MAAM;AAAA,EACtD,cAAc;AACZ,UAAM,8GAA8G;AAAA,EACtH;AACF;AAEA,eAAsB,eAAe,UAAoC;AACvE,QAAM,aAAa,MAAM,iBAAiB,QAAQ;AAClD,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,aAAa,UAAU;AAC7B,QAAM,iBAAiB,UAAU,IAAI;AACrC,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/db/chat-link.ts"],"sourcesContent":["import { ensureSchema, query } from './pool.js'\nimport { issueKeyRow, revokeKeyRow, getKeyById } from './keys.js'\nimport { fullScope } from '../lib/auth.js'\n\nasync function getChatLinkKeyId(identity: string): Promise<string | null> {\n await ensureSchema()\n const res = await query<{ chat_link_key_id: string | null }>(\n `SELECT chat_link_key_id FROM mem_settings WHERE identity = $1 LIMIT 1`,\n [identity],\n )\n return res.rows[0]?.chat_link_key_id ?? null\n}\n\nasync function setChatLinkKeyId(identity: string, keyId: string | null): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_settings (identity, chat_link_key_id, updated_at)\n VALUES ($1, $2, now())\n ON CONFLICT (identity) DO UPDATE SET chat_link_key_id = EXCLUDED.chat_link_key_id, updated_at = now()`,\n [identity, keyId],\n )\n}\n\nexport async function getOrCreateChatLinkSecret(identity: string): Promise<string> {\n const existingId = await getChatLinkKeyId(identity)\n if (existingId) {\n const existing = await getKeyById(existingId)\n if (existing && !existing.revoked) {\n throw new ChatLinkSecretNotRetrievable()\n }\n }\n const issued = await issueKeyRow({ identity, vaults: [], scope: fullScope(), plan: 'free', expiresAt: null })\n await setChatLinkKeyId(identity, issued.keyId)\n return issued.secret\n}\n\nexport class ChatLinkSecretNotRetrievable extends Error {\n constructor() {\n super('A chat link already exists for this identity; its secret cannot be re-displayed (only revoked and reissued).')\n }\n}\n\nexport async function revokeChatLink(identity: string): Promise<boolean> {\n const existingId = await getChatLinkKeyId(identity)\n if (!existingId) return false\n await revokeKeyRow(existingId)\n await setChatLinkKeyId(identity, null)\n return true\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AACpC,SAAS,aAAa,cAAc,kBAAkB;AACtD,SAAS,iBAAiB;AAE1B,eAAe,iBAAiB,UAA0C;AACxE,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AACA,SAAO,IAAI,KAAK,CAAC,GAAG,oBAAoB;AAC1C;AAEA,eAAe,iBAAiB,UAAkB,OAAqC;AACrF,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA,IAGA,CAAC,UAAU,KAAK;AAAA,EAClB;AACF;AAEA,eAAsB,0BAA0B,UAAmC;AACjF,QAAM,aAAa,MAAM,iBAAiB,QAAQ;AAClD,MAAI,YAAY;AACd,UAAM,WAAW,MAAM,WAAW,UAAU;AAC5C,QAAI,YAAY,CAAC,SAAS,SAAS;AACjC,YAAM,IAAI,6BAA6B;AAAA,IACzC;AAAA,EACF;AACA,QAAM,SAAS,MAAM,YAAY,EAAE,UAAU,QAAQ,CAAC,GAAG,OAAO,UAAU,GAAG,MAAM,QAAQ,WAAW,KAAK,CAAC;AAC5G,QAAM,iBAAiB,UAAU,OAAO,KAAK;AAC7C,SAAO,OAAO;AAChB;AAEO,MAAM,qCAAqC,MAAM;AAAA,EACtD,cAAc;AACZ,UAAM,8GAA8G;AAAA,EACtH;AACF;AAEA,eAAsB,eAAe,UAAoC;AACvE,QAAM,aAAa,MAAM,iBAAiB,QAAQ;AAClD,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,aAAa,UAAU;AAC7B,QAAM,iBAAiB,UAAU,IAAI;AACrC,SAAO;AACT;","names":[]}
|
package/dist/db/facts.js
CHANGED
package/dist/db/facts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/facts.ts"],"sourcesContent":["import { createHash } from 'node:crypto'\nimport { ensureSchema, query } from './pool'\n\nexport type FactStatus = 'active' | 'superseded'\nexport type PrecedencePolicy = 'recency' | 'source-priority' | 'intent-aware'\n\nexport interface Fact {\n id: string\n vault: string\n subject: string\n value: string\n source: string\n confidence: number\n status: FactStatus\n supersededBy: string | null\n resolution: string | null\n precedence: string | null\n identity: string\n createdAt: string\n updatedAt: string\n}\n\ninterface RawFactRow {\n id: string\n vault: string\n subject: string\n value: string\n source: string\n confidence: number\n status: string\n superseded_by: string | null\n resolution: string | null\n precedence: string | null\n identity: string\n created_at: string\n updated_at: string\n}\n\nfunction mapFact(r: RawFactRow): Fact {\n return {\n id: r.id,\n vault: r.vault,\n subject: r.subject,\n value: r.value,\n source: r.source,\n confidence: Number(r.confidence),\n status: r.status as FactStatus,\n supersededBy: r.superseded_by,\n resolution: r.resolution,\n precedence: r.precedence,\n identity: r.identity,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n }\n}\n\nconst FACT_COLS =\n 'id, vault, subject, value, source, confidence, status, superseded_by, resolution, precedence, identity, created_at, updated_at'\n\nexport function canonicalSubject(subject: string): string {\n return subject.trim().toLowerCase().replace(/\\s+/g, ' ')\n}\n\nexport function factId(vault: string, subject: string, createdAtIso: string): string {\n return `fact_${createHash('sha256').update(`${vault}::${canonicalSubject(subject)}::${createdAtIso}`).digest('hex').slice(0, 24)}`\n}\n\nexport function sourceRank(source: string): number {\n const s = (source || '').toLowerCase()\n if (s.startsWith('user')) return 4\n if (s.startsWith('tool')) return 3\n if (s.startsWith('chat')) return 2\n if (s.startsWith('library')) return 1\n return 0\n}\n\nexport interface PrecedenceResult {\n winner: Fact\n losers: Fact[]\n reason: string\n ambiguous: boolean\n}\n\nexport function applyPrecedence(facts: Fact[], policy: PrecedencePolicy): PrecedenceResult {\n if (facts.length === 0) {\n throw new Error('applyPrecedence requires at least one fact')\n }\n const byRecency = (a: Fact, b: Fact): number => Date.parse(b.createdAt) - Date.parse(a.createdAt)\n const sorted = [...facts]\n let winner: Fact\n let reason: string\n let ambiguous = false\n\n if (policy === 'source-priority') {\n sorted.sort((a, b) => {\n const r = sourceRank(b.source) - sourceRank(a.source)\n return r !== 0 ? r : byRecency(a, b)\n })\n winner = sorted[0]\n reason = `source-priority: kept \"${winner.source || 'unknown'}\" (rank ${sourceRank(winner.source)}), most recent on tie`\n if (\n sorted.length > 1 &&\n sourceRank(sorted[1].source) === sourceRank(winner.source) &&\n Date.parse(sorted[1].createdAt) === Date.parse(winner.createdAt)\n ) {\n ambiguous = true\n }\n } else {\n sorted.sort(byRecency)\n winner = sorted[0]\n reason =\n policy === 'intent-aware'\n ? 'intent-aware: newest assertion kept (v2: recency; flagged for review when history may be referenced intentionally)'\n : 'recency: newest assertion kept'\n if (policy === 'intent-aware' && sorted.length > 1) {\n ambiguous = true\n }\n if (sorted.length > 1 && Date.parse(sorted[1].createdAt) === Date.parse(winner.createdAt)) {\n ambiguous = true\n }\n }\n\n const losers = sorted.filter(f => f.id !== winner.id)\n return { winner, losers, reason, ambiguous }\n}\n\nexport async function vaultPrecedenceDefault(vault: string): Promise<PrecedencePolicy> {\n await ensureSchema()\n const res = await query<{ precedence_default: string | null }>(\n `SELECT precedence_default FROM mem_vault_registry WHERE vault = $1 AND precedence_default IS NOT NULL LIMIT 1`,\n [vault],\n )\n const p = res.rows[0]?.precedence_default\n if (p === 'recency' || p === 'source-priority' || p === 'intent-aware') return p\n return 'recency'\n}\n\nexport interface RecordFactArgs {\n vault: string\n subject: string\n value: string\n source?: string\n confidence?: number\n identity: string\n reason?: string\n precedence?: PrecedencePolicy\n}\n\nexport async function recordFact(args: RecordFactArgs): Promise<{ fact: Fact; superseded?: Fact }> {\n await ensureSchema()\n const subject = canonicalSubject(args.subject)\n const value = args.value\n const source = args.source ?? ''\n const confidence = typeof args.confidence === 'number' ? args.confidence : 0.5\n const precedence = args.precedence ?? 'recency'\n\n const existingRes = await query<RawFactRow>(\n `SELECT ${FACT_COLS} FROM mem_facts\n WHERE vault = $1 AND subject = $2 AND status = 'active' AND identity = $3\n ORDER BY created_at DESC LIMIT 1`,\n [args.vault, subject, args.identity],\n )\n const existing = existingRes.rows[0] ? mapFact(existingRes.rows[0]) : null\n\n if (existing && existing.value === value) {\n const bumped = await query<RawFactRow>(\n `UPDATE mem_facts SET updated_at = now(), confidence = $2 WHERE id = $1 RETURNING ${FACT_COLS}`,\n [existing.id, confidence],\n )\n return { fact: mapFact(bumped.rows[0]) }\n }\n\n const createdAtIso = new Date().toISOString()\n let id = factId(args.vault, subject, createdAtIso)\n const clash = await query(`SELECT 1 FROM mem_facts WHERE id = $1 LIMIT 1`, [id])\n if ((clash.rowCount ?? 0) > 0) {\n id = `${id}_${Math.random().toString(36).slice(2, 8)}`\n }\n\n let superseded: Fact | undefined\n if (existing) {\n const sup = await query<RawFactRow>(\n `UPDATE mem_facts\n SET status = 'superseded', superseded_by = $2, resolution = $3, precedence = $4, updated_at = now()\n WHERE id = $1 RETURNING ${FACT_COLS}`,\n [existing.id, id, args.reason ?? 'replaced by newer assertion', precedence],\n )\n superseded = mapFact(sup.rows[0])\n }\n\n const inserted = await query<RawFactRow>(\n `INSERT INTO mem_facts (id, vault, subject, value, source, confidence, status, identity, created_at, updated_at)\n VALUES ($1, $2, $3, $4, $5, $6, 'active', $7, $8::timestamptz, $8::timestamptz)\n RETURNING ${FACT_COLS}`,\n [id, args.vault, subject, value, source, confidence, args.identity, createdAtIso],\n )\n\n return { fact: mapFact(inserted.rows[0]), superseded }\n}\n\nexport async function factHistory(\n vault: string,\n subject: string,\n): Promise<{ current: Fact | null; history: Fact[] }> {\n await ensureSchema()\n const subj = canonicalSubject(subject)\n const res = await query<RawFactRow>(\n `SELECT ${FACT_COLS} FROM mem_facts WHERE vault = $1 AND subject = $2 ORDER BY created_at DESC`,\n [vault, subj],\n )\n const all = res.rows.map(mapFact)\n const current = all.find(f => f.status === 'active') ?? null\n const history = all.filter(f => f.status === 'superseded')\n return { current, history }\n}\n\nexport async function activeFacts(vault: string): Promise<Fact[]> {\n await ensureSchema()\n const res = await query<RawFactRow>(\n `SELECT ${FACT_COLS} FROM mem_facts WHERE vault = $1 AND status = 'active' ORDER BY subject ASC, created_at DESC`,\n [vault],\n )\n return res.rows.map(mapFact)\n}\n\nexport async function contradictions(vault: string): Promise<{ subject: string; facts: Fact[] }[]> {\n const active = await activeFacts(vault)\n const bySubject = new Map<string, Fact[]>()\n for (const f of active) {\n const arr = bySubject.get(f.subject) ?? []\n arr.push(f)\n bySubject.set(f.subject, arr)\n }\n const out: { subject: string; facts: Fact[] }[] = []\n for (const [subject, facts] of bySubject) {\n if (facts.length > 1) out.push({ subject, facts })\n }\n return out\n}\n\nexport async function supersedeFact(args: {\n loserId: string\n winnerId: string\n reason: string\n precedence: PrecedencePolicy\n}): Promise<Fact | null> {\n await ensureSchema()\n const res = await query<RawFactRow>(\n `UPDATE mem_facts\n SET status = 'superseded', superseded_by = $2, resolution = $3, precedence = $4, updated_at = now()\n WHERE id = $1 AND status = 'active'\n RETURNING ${FACT_COLS}`,\n [args.loserId, args.winnerId, args.reason, args.precedence],\n )\n return res.rows[0] ? mapFact(res.rows[0]) : null\n}\n\nexport async function deleteFactsForVault(vault: string): Promise<number> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_facts WHERE vault = $1`, [vault])\n return res.rowCount ?? 0\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/db/facts.ts"],"sourcesContent":["import { createHash } from 'node:crypto'\nimport { ensureSchema, query } from './pool.js'\n\nexport type FactStatus = 'active' | 'superseded'\nexport type PrecedencePolicy = 'recency' | 'source-priority' | 'intent-aware'\n\nexport interface Fact {\n id: string\n vault: string\n subject: string\n value: string\n source: string\n confidence: number\n status: FactStatus\n supersededBy: string | null\n resolution: string | null\n precedence: string | null\n identity: string\n createdAt: string\n updatedAt: string\n}\n\ninterface RawFactRow {\n id: string\n vault: string\n subject: string\n value: string\n source: string\n confidence: number\n status: string\n superseded_by: string | null\n resolution: string | null\n precedence: string | null\n identity: string\n created_at: string\n updated_at: string\n}\n\nfunction mapFact(r: RawFactRow): Fact {\n return {\n id: r.id,\n vault: r.vault,\n subject: r.subject,\n value: r.value,\n source: r.source,\n confidence: Number(r.confidence),\n status: r.status as FactStatus,\n supersededBy: r.superseded_by,\n resolution: r.resolution,\n precedence: r.precedence,\n identity: r.identity,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n }\n}\n\nconst FACT_COLS =\n 'id, vault, subject, value, source, confidence, status, superseded_by, resolution, precedence, identity, created_at, updated_at'\n\nexport function canonicalSubject(subject: string): string {\n return subject.trim().toLowerCase().replace(/\\s+/g, ' ')\n}\n\nexport function factId(vault: string, subject: string, createdAtIso: string): string {\n return `fact_${createHash('sha256').update(`${vault}::${canonicalSubject(subject)}::${createdAtIso}`).digest('hex').slice(0, 24)}`\n}\n\nexport function sourceRank(source: string): number {\n const s = (source || '').toLowerCase()\n if (s.startsWith('user')) return 4\n if (s.startsWith('tool')) return 3\n if (s.startsWith('chat')) return 2\n if (s.startsWith('library')) return 1\n return 0\n}\n\nexport interface PrecedenceResult {\n winner: Fact\n losers: Fact[]\n reason: string\n ambiguous: boolean\n}\n\nexport function applyPrecedence(facts: Fact[], policy: PrecedencePolicy): PrecedenceResult {\n if (facts.length === 0) {\n throw new Error('applyPrecedence requires at least one fact')\n }\n const byRecency = (a: Fact, b: Fact): number => Date.parse(b.createdAt) - Date.parse(a.createdAt)\n const sorted = [...facts]\n let winner: Fact\n let reason: string\n let ambiguous = false\n\n if (policy === 'source-priority') {\n sorted.sort((a, b) => {\n const r = sourceRank(b.source) - sourceRank(a.source)\n return r !== 0 ? r : byRecency(a, b)\n })\n winner = sorted[0]\n reason = `source-priority: kept \"${winner.source || 'unknown'}\" (rank ${sourceRank(winner.source)}), most recent on tie`\n if (\n sorted.length > 1 &&\n sourceRank(sorted[1].source) === sourceRank(winner.source) &&\n Date.parse(sorted[1].createdAt) === Date.parse(winner.createdAt)\n ) {\n ambiguous = true\n }\n } else {\n sorted.sort(byRecency)\n winner = sorted[0]\n reason =\n policy === 'intent-aware'\n ? 'intent-aware: newest assertion kept (v2: recency; flagged for review when history may be referenced intentionally)'\n : 'recency: newest assertion kept'\n if (policy === 'intent-aware' && sorted.length > 1) {\n ambiguous = true\n }\n if (sorted.length > 1 && Date.parse(sorted[1].createdAt) === Date.parse(winner.createdAt)) {\n ambiguous = true\n }\n }\n\n const losers = sorted.filter(f => f.id !== winner.id)\n return { winner, losers, reason, ambiguous }\n}\n\nexport async function vaultPrecedenceDefault(vault: string): Promise<PrecedencePolicy> {\n await ensureSchema()\n const res = await query<{ precedence_default: string | null }>(\n `SELECT precedence_default FROM mem_vault_registry WHERE vault = $1 AND precedence_default IS NOT NULL LIMIT 1`,\n [vault],\n )\n const p = res.rows[0]?.precedence_default\n if (p === 'recency' || p === 'source-priority' || p === 'intent-aware') return p\n return 'recency'\n}\n\nexport interface RecordFactArgs {\n vault: string\n subject: string\n value: string\n source?: string\n confidence?: number\n identity: string\n reason?: string\n precedence?: PrecedencePolicy\n}\n\nexport async function recordFact(args: RecordFactArgs): Promise<{ fact: Fact; superseded?: Fact }> {\n await ensureSchema()\n const subject = canonicalSubject(args.subject)\n const value = args.value\n const source = args.source ?? ''\n const confidence = typeof args.confidence === 'number' ? args.confidence : 0.5\n const precedence = args.precedence ?? 'recency'\n\n const existingRes = await query<RawFactRow>(\n `SELECT ${FACT_COLS} FROM mem_facts\n WHERE vault = $1 AND subject = $2 AND status = 'active' AND identity = $3\n ORDER BY created_at DESC LIMIT 1`,\n [args.vault, subject, args.identity],\n )\n const existing = existingRes.rows[0] ? mapFact(existingRes.rows[0]) : null\n\n if (existing && existing.value === value) {\n const bumped = await query<RawFactRow>(\n `UPDATE mem_facts SET updated_at = now(), confidence = $2 WHERE id = $1 RETURNING ${FACT_COLS}`,\n [existing.id, confidence],\n )\n return { fact: mapFact(bumped.rows[0]) }\n }\n\n const createdAtIso = new Date().toISOString()\n let id = factId(args.vault, subject, createdAtIso)\n const clash = await query(`SELECT 1 FROM mem_facts WHERE id = $1 LIMIT 1`, [id])\n if ((clash.rowCount ?? 0) > 0) {\n id = `${id}_${Math.random().toString(36).slice(2, 8)}`\n }\n\n let superseded: Fact | undefined\n if (existing) {\n const sup = await query<RawFactRow>(\n `UPDATE mem_facts\n SET status = 'superseded', superseded_by = $2, resolution = $3, precedence = $4, updated_at = now()\n WHERE id = $1 RETURNING ${FACT_COLS}`,\n [existing.id, id, args.reason ?? 'replaced by newer assertion', precedence],\n )\n superseded = mapFact(sup.rows[0])\n }\n\n const inserted = await query<RawFactRow>(\n `INSERT INTO mem_facts (id, vault, subject, value, source, confidence, status, identity, created_at, updated_at)\n VALUES ($1, $2, $3, $4, $5, $6, 'active', $7, $8::timestamptz, $8::timestamptz)\n RETURNING ${FACT_COLS}`,\n [id, args.vault, subject, value, source, confidence, args.identity, createdAtIso],\n )\n\n return { fact: mapFact(inserted.rows[0]), superseded }\n}\n\nexport async function factHistory(\n vault: string,\n subject: string,\n): Promise<{ current: Fact | null; history: Fact[] }> {\n await ensureSchema()\n const subj = canonicalSubject(subject)\n const res = await query<RawFactRow>(\n `SELECT ${FACT_COLS} FROM mem_facts WHERE vault = $1 AND subject = $2 ORDER BY created_at DESC`,\n [vault, subj],\n )\n const all = res.rows.map(mapFact)\n const current = all.find(f => f.status === 'active') ?? null\n const history = all.filter(f => f.status === 'superseded')\n return { current, history }\n}\n\nexport async function activeFacts(vault: string): Promise<Fact[]> {\n await ensureSchema()\n const res = await query<RawFactRow>(\n `SELECT ${FACT_COLS} FROM mem_facts WHERE vault = $1 AND status = 'active' ORDER BY subject ASC, created_at DESC`,\n [vault],\n )\n return res.rows.map(mapFact)\n}\n\nexport async function contradictions(vault: string): Promise<{ subject: string; facts: Fact[] }[]> {\n const active = await activeFacts(vault)\n const bySubject = new Map<string, Fact[]>()\n for (const f of active) {\n const arr = bySubject.get(f.subject) ?? []\n arr.push(f)\n bySubject.set(f.subject, arr)\n }\n const out: { subject: string; facts: Fact[] }[] = []\n for (const [subject, facts] of bySubject) {\n if (facts.length > 1) out.push({ subject, facts })\n }\n return out\n}\n\nexport async function supersedeFact(args: {\n loserId: string\n winnerId: string\n reason: string\n precedence: PrecedencePolicy\n}): Promise<Fact | null> {\n await ensureSchema()\n const res = await query<RawFactRow>(\n `UPDATE mem_facts\n SET status = 'superseded', superseded_by = $2, resolution = $3, precedence = $4, updated_at = now()\n WHERE id = $1 AND status = 'active'\n RETURNING ${FACT_COLS}`,\n [args.loserId, args.winnerId, args.reason, args.precedence],\n )\n return res.rows[0] ? mapFact(res.rows[0]) : null\n}\n\nexport async function deleteFactsForVault(vault: string): Promise<number> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_facts WHERE vault = $1`, [vault])\n return res.rowCount ?? 0\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/interlink-state.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\n\nexport type InterlinkPhase = 'orphans' | 'springboard' | 'walkthrough'\n\nexport interface InterlinkCounts {\n orphansFound: number\n orphansLinked: number\n springboardLinks: number\n contractLinks: number\n hubsCreated: number\n removals: number\n budgetRemaining: number\n}\n\nexport interface InterlinkCursor {\n phase: InterlinkPhase\n vaultIndex: number\n noteOffset: number\n edgeIndex?: number\n runDate: string\n counts?: InterlinkCounts\n}\n\nexport interface InterlinkStateRecord {\n identity: string\n cursor: InterlinkCursor | null\n lastRunAt: string | null\n lastSummary: string | null\n}\n\ninterface RawRow {\n identity: string\n cursor: unknown\n last_run_at: string | null\n last_summary: string | null\n}\n\nfunction mapRow(r: RawRow): InterlinkStateRecord {\n return {\n identity: r.identity,\n cursor: (r.cursor && typeof r.cursor === 'object' ? (r.cursor as InterlinkCursor) : null),\n lastRunAt: r.last_run_at,\n lastSummary: r.last_summary,\n }\n}\n\nexport async function getInterlinkState(identity: string): Promise<InterlinkStateRecord | null> {\n await ensureSchema()\n const res = await query<RawRow>(\n `SELECT identity, cursor, last_run_at, last_summary FROM mem_interlink_state WHERE identity = $1 LIMIT 1`,\n [identity],\n )\n return res.rows[0] ? mapRow(res.rows[0]) : null\n}\n\nexport async function upsertInterlinkState(args: {\n identity: string\n cursor?: InterlinkCursor | null\n lastRunAt?: string\n lastSummary?: string\n}): Promise<void> {\n await ensureSchema()\n const touchesCursor = 'cursor' in args\n const cursorJson = touchesCursor && args.cursor ? JSON.stringify(args.cursor) : null\n await query(\n `INSERT INTO mem_interlink_state (identity, cursor, last_run_at, last_summary, updated_at)\n VALUES ($1, $2, $3, $4, now())\n ON CONFLICT (identity) DO UPDATE SET\n cursor = CASE WHEN $5 THEN $2::jsonb ELSE mem_interlink_state.cursor END,\n last_run_at = COALESCE($3, mem_interlink_state.last_run_at),\n last_summary = COALESCE($4, mem_interlink_state.last_summary),\n updated_at = now()`,\n [args.identity, cursorJson, args.lastRunAt ?? null, args.lastSummary ?? null, touchesCursor],\n )\n}\n\nexport async function clearInterlinkCursor(identity: string): Promise<void> {\n await ensureSchema()\n await query(`UPDATE mem_interlink_state SET cursor = NULL, updated_at = now() WHERE identity = $1`, [identity])\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AAqCpC,SAAS,OAAO,GAAiC;AAC/C,SAAO;AAAA,IACL,UAAU,EAAE;AAAA,IACZ,QAAS,EAAE,UAAU,OAAO,EAAE,WAAW,WAAY,EAAE,SAA6B;AAAA,IACpF,WAAW,EAAE;AAAA,IACb,aAAa,EAAE;AAAA,EACjB;AACF;AAEA,eAAsB,kBAAkB,UAAwD;AAC9F,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AACA,SAAO,IAAI,KAAK,CAAC,IAAI,OAAO,IAAI,KAAK,CAAC,CAAC,IAAI;AAC7C;AAEA,eAAsB,qBAAqB,MAKzB;AAChB,QAAM,aAAa;AACnB,QAAM,gBAAgB,YAAY;AAClC,QAAM,aAAa,iBAAiB,KAAK,SAAS,KAAK,UAAU,KAAK,MAAM,IAAI;AAChF,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,CAAC,KAAK,UAAU,YAAY,KAAK,aAAa,MAAM,KAAK,eAAe,MAAM,aAAa;AAAA,EAC7F;AACF;AAEA,eAAsB,qBAAqB,UAAiC;AAC1E,QAAM,aAAa;AACnB,QAAM,MAAM,wFAAwF,CAAC,QAAQ,CAAC;AAChH;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/db/interlink-state.ts"],"sourcesContent":["import { ensureSchema, query } from './pool.js'\n\nexport type InterlinkPhase = 'orphans' | 'springboard' | 'walkthrough'\n\nexport interface InterlinkCounts {\n orphansFound: number\n orphansLinked: number\n springboardLinks: number\n contractLinks: number\n hubsCreated: number\n removals: number\n budgetRemaining: number\n}\n\nexport interface InterlinkCursor {\n phase: InterlinkPhase\n vaultIndex: number\n noteOffset: number\n edgeIndex?: number\n runDate: string\n counts?: InterlinkCounts\n}\n\nexport interface InterlinkStateRecord {\n identity: string\n cursor: InterlinkCursor | null\n lastRunAt: string | null\n lastSummary: string | null\n}\n\ninterface RawRow {\n identity: string\n cursor: unknown\n last_run_at: string | null\n last_summary: string | null\n}\n\nfunction mapRow(r: RawRow): InterlinkStateRecord {\n return {\n identity: r.identity,\n cursor: (r.cursor && typeof r.cursor === 'object' ? (r.cursor as InterlinkCursor) : null),\n lastRunAt: r.last_run_at,\n lastSummary: r.last_summary,\n }\n}\n\nexport async function getInterlinkState(identity: string): Promise<InterlinkStateRecord | null> {\n await ensureSchema()\n const res = await query<RawRow>(\n `SELECT identity, cursor, last_run_at, last_summary FROM mem_interlink_state WHERE identity = $1 LIMIT 1`,\n [identity],\n )\n return res.rows[0] ? mapRow(res.rows[0]) : null\n}\n\nexport async function upsertInterlinkState(args: {\n identity: string\n cursor?: InterlinkCursor | null\n lastRunAt?: string\n lastSummary?: string\n}): Promise<void> {\n await ensureSchema()\n const touchesCursor = 'cursor' in args\n const cursorJson = touchesCursor && args.cursor ? JSON.stringify(args.cursor) : null\n await query(\n `INSERT INTO mem_interlink_state (identity, cursor, last_run_at, last_summary, updated_at)\n VALUES ($1, $2, $3, $4, now())\n ON CONFLICT (identity) DO UPDATE SET\n cursor = CASE WHEN $5 THEN $2::jsonb ELSE mem_interlink_state.cursor END,\n last_run_at = COALESCE($3, mem_interlink_state.last_run_at),\n last_summary = COALESCE($4, mem_interlink_state.last_summary),\n updated_at = now()`,\n [args.identity, cursorJson, args.lastRunAt ?? null, args.lastSummary ?? null, touchesCursor],\n )\n}\n\nexport async function clearInterlinkCursor(identity: string): Promise<void> {\n await ensureSchema()\n await query(`UPDATE mem_interlink_state SET cursor = NULL, updated_at = now() WHERE identity = $1`, [identity])\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AAqCpC,SAAS,OAAO,GAAiC;AAC/C,SAAO;AAAA,IACL,UAAU,EAAE;AAAA,IACZ,QAAS,EAAE,UAAU,OAAO,EAAE,WAAW,WAAY,EAAE,SAA6B;AAAA,IACpF,WAAW,EAAE;AAAA,IACb,aAAa,EAAE;AAAA,EACjB;AACF;AAEA,eAAsB,kBAAkB,UAAwD;AAC9F,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AACA,SAAO,IAAI,KAAK,CAAC,IAAI,OAAO,IAAI,KAAK,CAAC,CAAC,IAAI;AAC7C;AAEA,eAAsB,qBAAqB,MAKzB;AAChB,QAAM,aAAa;AACnB,QAAM,gBAAgB,YAAY;AAClC,QAAM,aAAa,iBAAiB,KAAK,SAAS,KAAK,UAAU,KAAK,MAAM,IAAI;AAChF,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,CAAC,KAAK,UAAU,YAAY,KAAK,aAAa,MAAM,KAAK,eAAe,MAAM,aAAa;AAAA,EAC7F;AACF;AAEA,eAAsB,qBAAqB,UAAiC;AAC1E,QAAM,aAAa;AACnB,QAAM,MAAM,wFAAwF,CAAC,QAAQ,CAAC;AAChH;","names":[]}
|
package/dist/db/keys.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { ensureSchema, query } from "./pool";
|
|
3
|
-
import { generateSecret, hashSecret, normalizeScope } from "../lib/auth";
|
|
2
|
+
import { ensureSchema, query } from "./pool.js";
|
|
3
|
+
import { generateSecret, hashSecret, normalizeScope } from "../lib/auth.js";
|
|
4
4
|
const PLAN_QUOTA_BYTES = {
|
|
5
5
|
free: 1e7,
|
|
6
6
|
pro: 5e9,
|
package/dist/db/keys.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/keys.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto'\nimport { ensureSchema, query } from './pool'\nimport { generateSecret, hashSecret, normalizeScope } from '../lib/auth'\nimport type { Scope } from '../lib/auth'\n\nexport const PLAN_QUOTA_BYTES: Record<string, number> = {\n free: 10_000_000,\n pro: 5_000_000_000,\n team: 50_000_000_000,\n enterprise: 500_000_000_000,\n unlimited: Number.MAX_SAFE_INTEGER,\n}\n\nexport function planQuotaBytes(plan: string): number {\n return PLAN_QUOTA_BYTES[plan] ?? PLAN_QUOTA_BYTES.free\n}\n\nexport function isUnlimitedPlan(plan: string): boolean {\n return plan === 'unlimited'\n}\n\nexport interface IssueKeyArgs {\n identity: string\n vaults: string[]\n scope: Scope\n plan: string\n expiresAt: string | null\n}\n\nexport interface IssuedKey {\n keyId: string\n secret: string\n identity: string\n vaults: string[]\n scope: Scope\n plan: string\n expiresAt: string | null\n}\n\nexport async function issueKeyRow(args: IssueKeyArgs): Promise<IssuedKey> {\n await ensureSchema()\n const keyId = `key_${randomBytes(8).toString('hex')}`\n const secret = generateSecret()\n await query(\n `INSERT INTO mem_keys (key_id, secret_hash, identity, vaults, scope, plan, expires_at)\n VALUES ($1, $2, $3, $4::jsonb, $5::jsonb, $6, $7)`,\n [keyId, hashSecret(secret), args.identity, JSON.stringify(args.vaults), JSON.stringify(args.scope), args.plan, args.expiresAt],\n )\n return { keyId, secret, identity: args.identity, vaults: args.vaults, scope: args.scope, plan: args.plan, expiresAt: args.expiresAt }\n}\n\nexport interface KeySummary {\n keyId: string\n identity: string\n vaults: string[]\n scope: Scope\n plan: string\n expiresAt: string | null\n revoked: boolean\n usageCount: number\n lastUsedAt: string | null\n}\n\ninterface KeyRow {\n key_id: string\n identity: string\n vaults: unknown\n scope: unknown\n plan: string\n expires_at: string | null\n revoked: boolean\n usage_count: number\n last_used_at: string | null\n}\n\nfunction toArr(v: unknown): string[] {\n return Array.isArray(v) ? v.filter((x): x is string => typeof x === 'string') : []\n}\n\nfunction mapKey(row: KeyRow): KeySummary {\n return {\n keyId: row.key_id,\n identity: row.identity,\n vaults: toArr(row.vaults),\n scope: normalizeScope(row.scope as Record<string, boolean>),\n plan: row.plan,\n expiresAt: row.expires_at,\n revoked: row.revoked,\n usageCount: row.usage_count,\n lastUsedAt: row.last_used_at,\n }\n}\n\nexport async function listKeyRows(filter: { identity?: string; vault?: string; plan?: string }): Promise<KeySummary[]> {\n await ensureSchema()\n const clauses: string[] = []\n const params: unknown[] = []\n if (filter.identity) {\n params.push(filter.identity)\n clauses.push(`identity = $${params.length}`)\n }\n if (filter.plan) {\n params.push(filter.plan)\n clauses.push(`plan = $${params.length}`)\n }\n if (filter.vault) {\n params.push(JSON.stringify([filter.vault]))\n clauses.push(`vaults @> $${params.length}::jsonb`)\n }\n const where = clauses.length ? `WHERE ${clauses.join(' AND ')}` : ''\n const res = await query<KeyRow>(\n `SELECT key_id, identity, vaults, scope, plan, expires_at, revoked, usage_count, last_used_at\n FROM mem_keys ${where} ORDER BY created_at DESC`,\n params,\n )\n return res.rows.map(mapKey)\n}\n\nexport async function revokeKeyRow(keyId: string): Promise<boolean> {\n await ensureSchema()\n const res = await query('UPDATE mem_keys SET revoked = TRUE WHERE key_id = $1', [keyId])\n return (res.rowCount ?? 0) > 0\n}\n\nexport async function setScopeRow(keyId: string, scope: Scope | undefined, plan: string | undefined): Promise<boolean> {\n await ensureSchema()\n const sets: string[] = []\n const params: unknown[] = []\n if (scope) {\n params.push(JSON.stringify(scope))\n sets.push(`scope = $${params.length}::jsonb`)\n }\n if (plan) {\n params.push(plan)\n sets.push(`plan = $${params.length}`)\n }\n if (!sets.length) return false\n params.push(keyId)\n const res = await query(`UPDATE mem_keys SET ${sets.join(', ')} WHERE key_id = $${params.length}`, params)\n return (res.rowCount ?? 0) > 0\n}\n\nexport async function planForIdentity(identity: string): Promise<string> {\n await ensureSchema()\n const res = await query<{ plan: string }>(\n `SELECT plan FROM mem_keys WHERE identity = $1 ORDER BY created_at DESC LIMIT 1`,\n [identity],\n )\n return res.rows[0]?.plan ?? 'free'\n}\n\nexport async function getKeyById(keyId: string): Promise<KeySummary | null> {\n await ensureSchema()\n const res = await query<KeyRow>(\n `SELECT key_id, identity, vaults, scope, plan, expires_at, revoked, usage_count, last_used_at\n FROM mem_keys WHERE key_id = $1 LIMIT 1`,\n [keyId],\n )\n return res.rows[0] ? mapKey(res.rows[0]) : null\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/db/keys.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto'\nimport { ensureSchema, query } from './pool.js'\nimport { generateSecret, hashSecret, normalizeScope } from '../lib/auth.js'\nimport type { Scope } from '../lib/auth.js'\n\nexport const PLAN_QUOTA_BYTES: Record<string, number> = {\n free: 10_000_000,\n pro: 5_000_000_000,\n team: 50_000_000_000,\n enterprise: 500_000_000_000,\n unlimited: Number.MAX_SAFE_INTEGER,\n}\n\nexport function planQuotaBytes(plan: string): number {\n return PLAN_QUOTA_BYTES[plan] ?? PLAN_QUOTA_BYTES.free\n}\n\nexport function isUnlimitedPlan(plan: string): boolean {\n return plan === 'unlimited'\n}\n\nexport interface IssueKeyArgs {\n identity: string\n vaults: string[]\n scope: Scope\n plan: string\n expiresAt: string | null\n}\n\nexport interface IssuedKey {\n keyId: string\n secret: string\n identity: string\n vaults: string[]\n scope: Scope\n plan: string\n expiresAt: string | null\n}\n\nexport async function issueKeyRow(args: IssueKeyArgs): Promise<IssuedKey> {\n await ensureSchema()\n const keyId = `key_${randomBytes(8).toString('hex')}`\n const secret = generateSecret()\n await query(\n `INSERT INTO mem_keys (key_id, secret_hash, identity, vaults, scope, plan, expires_at)\n VALUES ($1, $2, $3, $4::jsonb, $5::jsonb, $6, $7)`,\n [keyId, hashSecret(secret), args.identity, JSON.stringify(args.vaults), JSON.stringify(args.scope), args.plan, args.expiresAt],\n )\n return { keyId, secret, identity: args.identity, vaults: args.vaults, scope: args.scope, plan: args.plan, expiresAt: args.expiresAt }\n}\n\nexport interface KeySummary {\n keyId: string\n identity: string\n vaults: string[]\n scope: Scope\n plan: string\n expiresAt: string | null\n revoked: boolean\n usageCount: number\n lastUsedAt: string | null\n}\n\ninterface KeyRow {\n key_id: string\n identity: string\n vaults: unknown\n scope: unknown\n plan: string\n expires_at: string | null\n revoked: boolean\n usage_count: number\n last_used_at: string | null\n}\n\nfunction toArr(v: unknown): string[] {\n return Array.isArray(v) ? v.filter((x): x is string => typeof x === 'string') : []\n}\n\nfunction mapKey(row: KeyRow): KeySummary {\n return {\n keyId: row.key_id,\n identity: row.identity,\n vaults: toArr(row.vaults),\n scope: normalizeScope(row.scope as Record<string, boolean>),\n plan: row.plan,\n expiresAt: row.expires_at,\n revoked: row.revoked,\n usageCount: row.usage_count,\n lastUsedAt: row.last_used_at,\n }\n}\n\nexport async function listKeyRows(filter: { identity?: string; vault?: string; plan?: string }): Promise<KeySummary[]> {\n await ensureSchema()\n const clauses: string[] = []\n const params: unknown[] = []\n if (filter.identity) {\n params.push(filter.identity)\n clauses.push(`identity = $${params.length}`)\n }\n if (filter.plan) {\n params.push(filter.plan)\n clauses.push(`plan = $${params.length}`)\n }\n if (filter.vault) {\n params.push(JSON.stringify([filter.vault]))\n clauses.push(`vaults @> $${params.length}::jsonb`)\n }\n const where = clauses.length ? `WHERE ${clauses.join(' AND ')}` : ''\n const res = await query<KeyRow>(\n `SELECT key_id, identity, vaults, scope, plan, expires_at, revoked, usage_count, last_used_at\n FROM mem_keys ${where} ORDER BY created_at DESC`,\n params,\n )\n return res.rows.map(mapKey)\n}\n\nexport async function revokeKeyRow(keyId: string): Promise<boolean> {\n await ensureSchema()\n const res = await query('UPDATE mem_keys SET revoked = TRUE WHERE key_id = $1', [keyId])\n return (res.rowCount ?? 0) > 0\n}\n\nexport async function setScopeRow(keyId: string, scope: Scope | undefined, plan: string | undefined): Promise<boolean> {\n await ensureSchema()\n const sets: string[] = []\n const params: unknown[] = []\n if (scope) {\n params.push(JSON.stringify(scope))\n sets.push(`scope = $${params.length}::jsonb`)\n }\n if (plan) {\n params.push(plan)\n sets.push(`plan = $${params.length}`)\n }\n if (!sets.length) return false\n params.push(keyId)\n const res = await query(`UPDATE mem_keys SET ${sets.join(', ')} WHERE key_id = $${params.length}`, params)\n return (res.rowCount ?? 0) > 0\n}\n\nexport async function planForIdentity(identity: string): Promise<string> {\n await ensureSchema()\n const res = await query<{ plan: string }>(\n `SELECT plan FROM mem_keys WHERE identity = $1 ORDER BY created_at DESC LIMIT 1`,\n [identity],\n )\n return res.rows[0]?.plan ?? 'free'\n}\n\nexport async function getKeyById(keyId: string): Promise<KeySummary | null> {\n await ensureSchema()\n const res = await query<KeyRow>(\n `SELECT key_id, identity, vaults, scope, plan, expires_at, revoked, usage_count, last_used_at\n FROM mem_keys WHERE key_id = $1 LIMIT 1`,\n [keyId],\n )\n return res.rows[0] ? mapKey(res.rows[0]) : null\n}\n"],"mappings":"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","names":[]}
|
package/dist/db/messages.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ensureSchema, query } from "./pool";
|
|
2
|
-
import { putNoteRow, noteId, mapNote, NOTE_COLUMNS } from "./notes";
|
|
1
|
+
import { ensureSchema, query } from "./pool.js";
|
|
2
|
+
import { putNoteRow, noteId, mapNote, NOTE_COLUMNS } from "./notes.js";
|
|
3
3
|
function messagePath() {
|
|
4
4
|
return `msg/${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
5
5
|
}
|
package/dist/db/messages.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/messages.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\nimport { putNoteRow, noteId, mapNote, NOTE_COLUMNS, type NoteRecord, type RawNoteRow } from './notes'\n\nfunction messagePath(): string {\n return `msg/${new Date().toISOString().replace(/[:.]/g, '-')}`\n}\n\nexport interface AttachedNoteRef {\n noteId: string\n srcVault: string\n srcPath: string\n title: string\n ownerIdentity: string\n}\n\nexport async function postMessageRow(args: {\n vault: string\n content: string\n authorIdentity: string\n attachedNote?: AttachedNoteRef\n}): Promise<NoteRecord> {\n return putNoteRow({\n vault: args.vault,\n path: messagePath(),\n content: args.content,\n kind: 'message',\n source: 'message',\n props: args.attachedNote ? { attachedNote: args.attachedNote } : undefined,\n updatedBy: args.authorIdentity,\n })\n}\n\nexport async function replyMessageRow(args: {\n vault: string\n parentMessageId: string\n content: string\n authorIdentity: string\n attachedNote?: AttachedNoteRef\n}): Promise<NoteRecord> {\n return putNoteRow({\n vault: args.vault,\n path: messagePath(),\n content: args.content,\n kind: 'message',\n source: 'message',\n props: { parentMessageId: args.parentMessageId, ...(args.attachedNote ? { attachedNote: args.attachedNote } : {}) },\n updatedBy: args.authorIdentity,\n })\n}\n\nexport interface ThreadSummary {\n replyCount: number\n lastReplyAt: string | null\n}\n\nexport async function listTopLevelMessages(vault: string): Promise<NoteRecord[]> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS}\n FROM mem_notes WHERE vault = $1 AND kind = 'message' AND (props->>'parentMessageId') IS NULL\n ORDER BY captured_at ASC`,\n [vault],\n )\n return res.rows.map(mapNote)\n}\n\nexport async function listThreadReplies(vault: string, parentMessageId: string): Promise<NoteRecord[]> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS}\n FROM mem_notes WHERE vault = $1 AND kind = 'message' AND props->>'parentMessageId' = $2\n ORDER BY captured_at ASC`,\n [vault, parentMessageId],\n )\n return res.rows.map(mapNote)\n}\n\nexport async function threadSummaries(vault: string): Promise<Map<string, ThreadSummary>> {\n await ensureSchema()\n const res = await query<{ parent: string; reply_count: string; last_reply_at: string }>(\n `SELECT props->>'parentMessageId' AS parent, COUNT(*)::int AS reply_count, MAX(captured_at) AS last_reply_at\n FROM mem_notes WHERE vault = $1 AND kind = 'message' AND props->>'parentMessageId' IS NOT NULL\n GROUP BY props->>'parentMessageId'`,\n [vault],\n )\n const out = new Map<string, ThreadSummary>()\n for (const r of res.rows) out.set(r.parent, { replyCount: Number(r.reply_count), lastReplyAt: r.last_reply_at })\n return out\n}\n\nexport async function markRead(messageId: string, identity: string): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_message_reads (message_id, identity, read_at) VALUES ($1, $2, now())\n ON CONFLICT (message_id, identity) DO NOTHING`,\n [messageId, identity],\n )\n}\n\nexport interface ReadReceipt {\n identity: string\n readAt: string\n}\n\nexport async function readReceiptsFor(messageIds: string[]): Promise<Map<string, ReadReceipt[]>> {\n await ensureSchema()\n if (messageIds.length === 0) return new Map()\n const res = await query<{ message_id: string; identity: string; read_at: string }>(\n `SELECT message_id, identity, read_at FROM mem_message_reads WHERE message_id = ANY($1) ORDER BY read_at ASC`,\n [messageIds],\n )\n const out = new Map<string, ReadReceipt[]>()\n for (const r of res.rows) {\n const list = out.get(r.message_id) ?? []\n list.push({ identity: r.identity, readAt: r.read_at })\n out.set(r.message_id, list)\n }\n return out\n}\n\nexport async function recordMentions(args: { messageId: string; ownerIdentity: string; vault: string; mentions: string[] }): Promise<void> {\n if (args.mentions.length === 0) return\n await ensureSchema()\n await Promise.all(\n args.mentions.map(mentioned =>\n query(\n `INSERT INTO mem_mentions (message_id, owner_identity, vault, mentioned) VALUES ($1, $2, $3, $4)`,\n [args.messageId, args.ownerIdentity, args.vault, mentioned],\n ),\n ),\n )\n}\n\nexport interface MentionRow {\n messageId: string\n ownerIdentity: string\n vault: string\n createdAt: string\n}\n\nexport async function mentionsFor(identity: string, limit: number): Promise<MentionRow[]> {\n await ensureSchema()\n const res = await query<{ message_id: string; owner_identity: string; vault: string; created_at: string }>(\n `SELECT message_id, owner_identity, vault, created_at FROM mem_mentions WHERE mentioned = $1 ORDER BY created_at DESC LIMIT $2`,\n [identity, limit],\n )\n return res.rows.map(r => ({ messageId: r.message_id, ownerIdentity: r.owner_identity, vault: r.vault, createdAt: r.created_at }))\n}\n\nexport async function toggleReaction(args: { messageId: string; identity: string; emoji: string; remove?: boolean }): Promise<void> {\n await ensureSchema()\n if (args.remove) {\n await query(`DELETE FROM mem_message_reactions WHERE message_id = $1 AND identity = $2 AND emoji = $3`, [args.messageId, args.identity, args.emoji])\n } else {\n await query(\n `INSERT INTO mem_message_reactions (message_id, identity, emoji) VALUES ($1, $2, $3) ON CONFLICT (message_id, identity, emoji) DO NOTHING`,\n [args.messageId, args.identity, args.emoji],\n )\n }\n}\n\nexport interface Reaction {\n identity: string\n emoji: string\n}\n\nexport async function reactionsFor(messageIds: string[]): Promise<Map<string, Reaction[]>> {\n await ensureSchema()\n if (messageIds.length === 0) return new Map()\n const res = await query<{ message_id: string; identity: string; emoji: string }>(\n `SELECT message_id, identity, emoji FROM mem_message_reactions WHERE message_id = ANY($1) ORDER BY created_at ASC`,\n [messageIds],\n )\n const out = new Map<string, Reaction[]>()\n for (const r of res.rows) {\n const list = out.get(r.message_id) ?? []\n list.push({ identity: r.identity, emoji: r.emoji })\n out.set(r.message_id, list)\n }\n return out\n}\n\nconst EPOCH = '1970-01-01T00:00:00.000Z'\n\nexport async function getCursor(identity: string, vault: string): Promise<string> {\n await ensureSchema()\n const res = await query<{ last_polled_at: string }>(\n `SELECT last_polled_at FROM mem_channel_cursor WHERE identity = $1 AND vault = $2 LIMIT 1`,\n [identity, vault],\n )\n return res.rows[0]?.last_polled_at ?? EPOCH\n}\n\nexport async function advanceCursor(identity: string, vault: string): Promise<string> {\n await ensureSchema()\n const res = await query<{ last_polled_at: string }>(\n `INSERT INTO mem_channel_cursor (identity, vault, last_polled_at) VALUES ($1, $2, now())\n ON CONFLICT (identity, vault) DO UPDATE SET last_polled_at = now()\n RETURNING last_polled_at`,\n [identity, vault],\n )\n return res.rows[0].last_polled_at\n}\n\nexport async function topLevelMessagesSince(vault: string, since: string): Promise<NoteRecord[]> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS}\n FROM mem_notes WHERE vault = $1 AND kind = 'message' AND (props->>'parentMessageId') IS NULL AND captured_at > $2::timestamptz\n ORDER BY captured_at ASC`,\n [vault, since],\n )\n return res.rows.map(mapNote)\n}\n\nexport interface ReplySince {\n note: NoteRecord\n parentMessageId: string\n}\n\nexport async function repliesSince(vault: string, since: string): Promise<ReplySince[]> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS}\n FROM mem_notes WHERE vault = $1 AND kind = 'message' AND (props->>'parentMessageId') IS NOT NULL AND captured_at > $2::timestamptz\n ORDER BY captured_at ASC`,\n [vault, since],\n )\n return res.rows.map(r => ({ note: mapNote(r), parentMessageId: String((r.props as Record<string, unknown> | null)?.parentMessageId ?? '') }))\n}\n\nexport interface ReactionSince {\n messageId: string\n identity: string\n emoji: string\n createdAt: string\n}\n\nexport async function reactionsSince(vault: string, since: string): Promise<ReactionSince[]> {\n await ensureSchema()\n const res = await query<{ path: string; identity: string; emoji: string; created_at: string }>(\n `SELECT n.path, r.identity, r.emoji, r.created_at\n FROM mem_message_reactions r JOIN mem_notes n ON n.id = r.message_id\n WHERE n.vault = $1 AND r.created_at > $2::timestamptz\n ORDER BY r.created_at ASC`,\n [vault, since],\n )\n return res.rows.map(r => ({ messageId: r.path, identity: r.identity, emoji: r.emoji, createdAt: r.created_at }))\n}\n\nexport { noteId as messageNoteId }\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/db/messages.ts"],"sourcesContent":["import { ensureSchema, query } from './pool.js'\nimport { putNoteRow, noteId, mapNote, NOTE_COLUMNS, type NoteRecord, type RawNoteRow } from './notes.js'\n\nfunction messagePath(): string {\n return `msg/${new Date().toISOString().replace(/[:.]/g, '-')}`\n}\n\nexport interface AttachedNoteRef {\n noteId: string\n srcVault: string\n srcPath: string\n title: string\n ownerIdentity: string\n}\n\nexport async function postMessageRow(args: {\n vault: string\n content: string\n authorIdentity: string\n attachedNote?: AttachedNoteRef\n}): Promise<NoteRecord> {\n return putNoteRow({\n vault: args.vault,\n path: messagePath(),\n content: args.content,\n kind: 'message',\n source: 'message',\n props: args.attachedNote ? { attachedNote: args.attachedNote } : undefined,\n updatedBy: args.authorIdentity,\n })\n}\n\nexport async function replyMessageRow(args: {\n vault: string\n parentMessageId: string\n content: string\n authorIdentity: string\n attachedNote?: AttachedNoteRef\n}): Promise<NoteRecord> {\n return putNoteRow({\n vault: args.vault,\n path: messagePath(),\n content: args.content,\n kind: 'message',\n source: 'message',\n props: { parentMessageId: args.parentMessageId, ...(args.attachedNote ? { attachedNote: args.attachedNote } : {}) },\n updatedBy: args.authorIdentity,\n })\n}\n\nexport interface ThreadSummary {\n replyCount: number\n lastReplyAt: string | null\n}\n\nexport async function listTopLevelMessages(vault: string): Promise<NoteRecord[]> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS}\n FROM mem_notes WHERE vault = $1 AND kind = 'message' AND (props->>'parentMessageId') IS NULL\n ORDER BY captured_at ASC`,\n [vault],\n )\n return res.rows.map(mapNote)\n}\n\nexport async function listThreadReplies(vault: string, parentMessageId: string): Promise<NoteRecord[]> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS}\n FROM mem_notes WHERE vault = $1 AND kind = 'message' AND props->>'parentMessageId' = $2\n ORDER BY captured_at ASC`,\n [vault, parentMessageId],\n )\n return res.rows.map(mapNote)\n}\n\nexport async function threadSummaries(vault: string): Promise<Map<string, ThreadSummary>> {\n await ensureSchema()\n const res = await query<{ parent: string; reply_count: string; last_reply_at: string }>(\n `SELECT props->>'parentMessageId' AS parent, COUNT(*)::int AS reply_count, MAX(captured_at) AS last_reply_at\n FROM mem_notes WHERE vault = $1 AND kind = 'message' AND props->>'parentMessageId' IS NOT NULL\n GROUP BY props->>'parentMessageId'`,\n [vault],\n )\n const out = new Map<string, ThreadSummary>()\n for (const r of res.rows) out.set(r.parent, { replyCount: Number(r.reply_count), lastReplyAt: r.last_reply_at })\n return out\n}\n\nexport async function markRead(messageId: string, identity: string): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_message_reads (message_id, identity, read_at) VALUES ($1, $2, now())\n ON CONFLICT (message_id, identity) DO NOTHING`,\n [messageId, identity],\n )\n}\n\nexport interface ReadReceipt {\n identity: string\n readAt: string\n}\n\nexport async function readReceiptsFor(messageIds: string[]): Promise<Map<string, ReadReceipt[]>> {\n await ensureSchema()\n if (messageIds.length === 0) return new Map()\n const res = await query<{ message_id: string; identity: string; read_at: string }>(\n `SELECT message_id, identity, read_at FROM mem_message_reads WHERE message_id = ANY($1) ORDER BY read_at ASC`,\n [messageIds],\n )\n const out = new Map<string, ReadReceipt[]>()\n for (const r of res.rows) {\n const list = out.get(r.message_id) ?? []\n list.push({ identity: r.identity, readAt: r.read_at })\n out.set(r.message_id, list)\n }\n return out\n}\n\nexport async function recordMentions(args: { messageId: string; ownerIdentity: string; vault: string; mentions: string[] }): Promise<void> {\n if (args.mentions.length === 0) return\n await ensureSchema()\n await Promise.all(\n args.mentions.map(mentioned =>\n query(\n `INSERT INTO mem_mentions (message_id, owner_identity, vault, mentioned) VALUES ($1, $2, $3, $4)`,\n [args.messageId, args.ownerIdentity, args.vault, mentioned],\n ),\n ),\n )\n}\n\nexport interface MentionRow {\n messageId: string\n ownerIdentity: string\n vault: string\n createdAt: string\n}\n\nexport async function mentionsFor(identity: string, limit: number): Promise<MentionRow[]> {\n await ensureSchema()\n const res = await query<{ message_id: string; owner_identity: string; vault: string; created_at: string }>(\n `SELECT message_id, owner_identity, vault, created_at FROM mem_mentions WHERE mentioned = $1 ORDER BY created_at DESC LIMIT $2`,\n [identity, limit],\n )\n return res.rows.map(r => ({ messageId: r.message_id, ownerIdentity: r.owner_identity, vault: r.vault, createdAt: r.created_at }))\n}\n\nexport async function toggleReaction(args: { messageId: string; identity: string; emoji: string; remove?: boolean }): Promise<void> {\n await ensureSchema()\n if (args.remove) {\n await query(`DELETE FROM mem_message_reactions WHERE message_id = $1 AND identity = $2 AND emoji = $3`, [args.messageId, args.identity, args.emoji])\n } else {\n await query(\n `INSERT INTO mem_message_reactions (message_id, identity, emoji) VALUES ($1, $2, $3) ON CONFLICT (message_id, identity, emoji) DO NOTHING`,\n [args.messageId, args.identity, args.emoji],\n )\n }\n}\n\nexport interface Reaction {\n identity: string\n emoji: string\n}\n\nexport async function reactionsFor(messageIds: string[]): Promise<Map<string, Reaction[]>> {\n await ensureSchema()\n if (messageIds.length === 0) return new Map()\n const res = await query<{ message_id: string; identity: string; emoji: string }>(\n `SELECT message_id, identity, emoji FROM mem_message_reactions WHERE message_id = ANY($1) ORDER BY created_at ASC`,\n [messageIds],\n )\n const out = new Map<string, Reaction[]>()\n for (const r of res.rows) {\n const list = out.get(r.message_id) ?? []\n list.push({ identity: r.identity, emoji: r.emoji })\n out.set(r.message_id, list)\n }\n return out\n}\n\nconst EPOCH = '1970-01-01T00:00:00.000Z'\n\nexport async function getCursor(identity: string, vault: string): Promise<string> {\n await ensureSchema()\n const res = await query<{ last_polled_at: string }>(\n `SELECT last_polled_at FROM mem_channel_cursor WHERE identity = $1 AND vault = $2 LIMIT 1`,\n [identity, vault],\n )\n return res.rows[0]?.last_polled_at ?? EPOCH\n}\n\nexport async function advanceCursor(identity: string, vault: string): Promise<string> {\n await ensureSchema()\n const res = await query<{ last_polled_at: string }>(\n `INSERT INTO mem_channel_cursor (identity, vault, last_polled_at) VALUES ($1, $2, now())\n ON CONFLICT (identity, vault) DO UPDATE SET last_polled_at = now()\n RETURNING last_polled_at`,\n [identity, vault],\n )\n return res.rows[0].last_polled_at\n}\n\nexport async function topLevelMessagesSince(vault: string, since: string): Promise<NoteRecord[]> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS}\n FROM mem_notes WHERE vault = $1 AND kind = 'message' AND (props->>'parentMessageId') IS NULL AND captured_at > $2::timestamptz\n ORDER BY captured_at ASC`,\n [vault, since],\n )\n return res.rows.map(mapNote)\n}\n\nexport interface ReplySince {\n note: NoteRecord\n parentMessageId: string\n}\n\nexport async function repliesSince(vault: string, since: string): Promise<ReplySince[]> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS}\n FROM mem_notes WHERE vault = $1 AND kind = 'message' AND (props->>'parentMessageId') IS NOT NULL AND captured_at > $2::timestamptz\n ORDER BY captured_at ASC`,\n [vault, since],\n )\n return res.rows.map(r => ({ note: mapNote(r), parentMessageId: String((r.props as Record<string, unknown> | null)?.parentMessageId ?? '') }))\n}\n\nexport interface ReactionSince {\n messageId: string\n identity: string\n emoji: string\n createdAt: string\n}\n\nexport async function reactionsSince(vault: string, since: string): Promise<ReactionSince[]> {\n await ensureSchema()\n const res = await query<{ path: string; identity: string; emoji: string; created_at: string }>(\n `SELECT n.path, r.identity, r.emoji, r.created_at\n FROM mem_message_reactions r JOIN mem_notes n ON n.id = r.message_id\n WHERE n.vault = $1 AND r.created_at > $2::timestamptz\n ORDER BY r.created_at ASC`,\n [vault, since],\n )\n return res.rows.map(r => ({ messageId: r.path, identity: r.identity, emoji: r.emoji, createdAt: r.created_at }))\n}\n\nexport { noteId as messageNoteId }\n"],"mappings":"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","names":[]}
|
package/dist/db/notes.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ensureSchema, query } from "./pool";
|
|
2
|
-
import { removeDocument } from "../rag/index-pipeline";
|
|
3
|
-
import { EMBED_DIM } from "../rag/embedder";
|
|
4
|
-
import { INDEX_NAME } from "../rag/vector-store";
|
|
1
|
+
import { ensureSchema, query } from "./pool.js";
|
|
2
|
+
import { removeDocument } from "../rag/index-pipeline.js";
|
|
3
|
+
import { EMBED_DIM } from "../rag/embedder.js";
|
|
4
|
+
import { INDEX_NAME } from "../rag/vector-store.js";
|
|
5
5
|
const EMBED_BYTES_PER_CHUNK = EMBED_DIM * 4;
|
|
6
6
|
function noteId(vault, path) {
|
|
7
7
|
return `${vault}::${path}`;
|
package/dist/db/notes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/notes.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\nimport { removeDocument } from '../rag/index-pipeline'\nimport { EMBED_DIM } from '../rag/embedder'\nimport { INDEX_NAME } from '../rag/vector-store'\n\nconst EMBED_BYTES_PER_CHUNK = EMBED_DIM * 4\n\nexport type NoteKind = 'note' | 'library' | 'capture' | 'decision' | 'message'\n\nexport type NoteProps = Record<string, unknown>\n\nexport interface NoteRecord {\n id: string\n vault: string\n path: string\n title: string\n content: string\n kind: NoteKind\n source: string\n props: NoteProps\n capturedAt: string\n createdAt: string\n updatedAt: string\n revision: number\n updatedBy: string | null\n}\n\nexport interface RawNoteRow {\n id: string\n vault: string\n path: string\n title: string\n content: string\n kind: string\n source: string\n props: unknown\n captured_at: string\n created_at: string\n updated_at: string\n revision: number\n updated_by: string | null\n}\n\nexport function noteId(vault: string, path: string): string {\n return `${vault}::${path}`\n}\n\nexport function mapNote(r: RawNoteRow): NoteRecord {\n return {\n id: r.id,\n vault: r.vault,\n path: r.path,\n title: r.title,\n content: r.content,\n kind: r.kind as NoteKind,\n source: r.source,\n props: (r.props && typeof r.props === 'object' ? (r.props as NoteProps) : {}),\n capturedAt: r.captured_at,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n revision: r.revision,\n updatedBy: r.updated_by,\n }\n}\n\nexport const NOTE_COLUMNS = 'id, vault, path, title, content, kind, source, props, captured_at, created_at, updated_at, revision, updated_by'\n\nexport interface PutNoteArgs {\n vault: string\n path: string\n title?: string\n content: string\n kind?: NoteKind\n source?: string\n props?: NoteProps\n capturedAt?: string\n updatedBy?: string\n}\n\nexport async function putNoteRow(args: PutNoteArgs): Promise<NoteRecord> {\n await ensureSchema()\n const id = noteId(args.vault, args.path)\n const res = await query<RawNoteRow>(\n `INSERT INTO mem_notes (id, vault, path, title, content, kind, source, props, captured_at, updated_at, revision, updated_by)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, COALESCE($9::timestamptz, now()), now(), 1, $10)\n ON CONFLICT (vault, path) DO UPDATE SET\n title = EXCLUDED.title, content = EXCLUDED.content, kind = EXCLUDED.kind,\n source = EXCLUDED.source, props = EXCLUDED.props, captured_at = EXCLUDED.captured_at,\n updated_at = now(), revision = mem_notes.revision + 1, updated_by = EXCLUDED.updated_by\n RETURNING ${NOTE_COLUMNS}`,\n [id, args.vault, args.path, args.title ?? args.path, args.content, args.kind ?? 'note', args.source ?? '', JSON.stringify(args.props ?? {}), args.capturedAt ?? null, args.updatedBy ?? null],\n )\n return mapNote(res.rows[0])\n}\n\nexport type PutNoteCheckedResult = { ok: true; note: NoteRecord } | { ok: false; conflict: true; current: NoteRecord }\n\nexport async function putNoteRowChecked(args: PutNoteArgs & { baseRevision: number }): Promise<PutNoteCheckedResult> {\n await ensureSchema()\n const id = noteId(args.vault, args.path)\n const res = await query<RawNoteRow>(\n `INSERT INTO mem_notes (id, vault, path, title, content, kind, source, props, captured_at, updated_at, revision, updated_by)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, COALESCE($9::timestamptz, now()), now(), 1, $10)\n ON CONFLICT (vault, path) DO UPDATE SET\n title = EXCLUDED.title, content = EXCLUDED.content, kind = EXCLUDED.kind,\n source = EXCLUDED.source, props = EXCLUDED.props, captured_at = EXCLUDED.captured_at,\n updated_at = now(), revision = mem_notes.revision + 1, updated_by = EXCLUDED.updated_by\n WHERE mem_notes.revision = $11\n RETURNING ${NOTE_COLUMNS}`,\n [id, args.vault, args.path, args.title ?? args.path, args.content, args.kind ?? 'note', args.source ?? '', JSON.stringify(args.props ?? {}), args.capturedAt ?? null, args.updatedBy ?? null, args.baseRevision],\n )\n if (res.rows[0]) return { ok: true, note: mapNote(res.rows[0]) }\n const current = await getNoteRow(args.vault, args.path)\n if (!current) return { ok: true, note: await putNoteRow(args) }\n return { ok: false, conflict: true, current }\n}\n\nexport async function getNoteRow(vault: string, path: string): Promise<NoteRecord | null> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS} FROM mem_notes WHERE vault = $1 AND path = $2 LIMIT 1`,\n [vault, path],\n )\n return res.rows[0] ? mapNote(res.rows[0]) : null\n}\n\nexport async function getNoteById(id: string): Promise<NoteRecord | null> {\n await ensureSchema()\n const res = await query<RawNoteRow>(`SELECT ${NOTE_COLUMNS} FROM mem_notes WHERE id = $1 LIMIT 1`, [id])\n return res.rows[0] ? mapNote(res.rows[0]) : null\n}\n\nexport async function listNoteRows(vault: string, kind?: NoteKind, tags?: string[]): Promise<NoteRecord[]> {\n await ensureSchema()\n const params: unknown[] = [vault]\n let where = 'vault = $1'\n if (kind) {\n params.push(kind)\n where += ` AND kind = $${params.length}`\n }\n if (tags && tags.length > 0) {\n params.push(JSON.stringify(tags))\n where += ` AND (props->'tags')::jsonb ?| ARRAY(SELECT jsonb_array_elements_text($${params.length}::jsonb))`\n }\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS}\n FROM mem_notes WHERE ${where} ORDER BY updated_at DESC`,\n params,\n )\n return res.rows.map(mapNote)\n}\n\nexport async function listAllNoteRows(): Promise<NoteRecord[]> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS} FROM mem_notes ORDER BY vault ASC, updated_at DESC`,\n )\n return res.rows.map(mapNote)\n}\n\nexport async function resolveNoteRefs(vault: string, refs: string[]): Promise<Map<string, NoteRecord>> {\n const notes = await listNoteRows(vault)\n const byPath = new Map(notes.map(n => [n.path, n]))\n const byTitle = new Map(notes.map(n => [n.title.toLowerCase(), n]))\n const resolved = new Map<string, NoteRecord>()\n for (const ref of refs) {\n const note = byPath.get(ref) ?? byTitle.get(ref.toLowerCase())\n if (note) resolved.set(ref, note)\n }\n return resolved\n}\n\nexport async function exportVaultRows(vault: string): Promise<NoteRecord[]> {\n return listNoteRows(vault)\n}\n\nexport async function captureRow(args: { vault: string; content: string; source: string; capturedAt?: string; title?: string; folder?: string; props?: NoteProps }): Promise<NoteRecord> {\n const stamp = args.capturedAt ?? new Date().toISOString()\n const base = `captures/${stamp.replace(/[:.]/g, '-')}`\n const path = args.folder ? `${args.folder}/${base}` : base\n return putNoteRow({\n vault: args.vault,\n path,\n title: args.title ?? `Capture ${stamp}`,\n content: args.content,\n kind: 'capture',\n source: args.source,\n props: args.props,\n capturedAt: stamp,\n })\n}\n\nexport async function deleteNoteRow(vault: string, path: string): Promise<{ deleted: boolean }> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_notes WHERE vault = $1 AND path = $2`, [vault, path])\n const deleted = (res.rowCount ?? 0) > 0\n if (deleted) {\n try {\n await removeDocument({ vault, id: noteId(vault, path) })\n } catch {\n void 0\n }\n }\n return { deleted }\n}\n\nexport type DeleteNoteCheckedResult = { ok: true; deleted: boolean } | { ok: false; conflict: true; current: NoteRecord }\n\nexport async function deleteNoteRowChecked(vault: string, path: string, baseRevision: number): Promise<DeleteNoteCheckedResult> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_notes WHERE vault = $1 AND path = $2 AND revision = $3`, [vault, path, baseRevision])\n if ((res.rowCount ?? 0) > 0) {\n try {\n await removeDocument({ vault, id: noteId(vault, path) })\n } catch {\n void 0\n }\n return { ok: true, deleted: true }\n }\n const current = await getNoteRow(vault, path)\n if (!current) return { ok: true, deleted: false }\n return { ok: false, conflict: true, current }\n}\n\nexport async function deleteNotesForVault(vault: string): Promise<number> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_notes WHERE vault = $1`, [vault])\n return res.rowCount ?? 0\n}\n\nexport interface VaultStorageResult {\n perVault: { vault: string; bytes: number; notes: number }[]\n totalBytes: number\n}\n\nexport async function vaultStorage(vaults?: string[]): Promise<VaultStorageResult> {\n if (vaults && vaults.length === 0) return { perVault: [], totalBytes: 0 }\n await ensureSchema()\n const params: unknown[] = []\n let where = ''\n if (vaults && vaults.length > 0) {\n params.push(vaults)\n where = `WHERE vault = ANY($1)`\n }\n const res = await query<{ vault: string; notes: string; bytes: string }>(\n `SELECT vault, COUNT(*)::int AS notes, COALESCE(SUM(octet_length(content)), 0)::bigint AS bytes\n FROM mem_notes ${where} GROUP BY vault ORDER BY vault ASC`,\n params,\n )\n const embedChunks = await embedChunksByVault(vaults)\n const perVault = res.rows.map(r => {\n const contentBytes = Number(r.bytes)\n const embedBytes = (embedChunks.get(r.vault) ?? 0) * EMBED_BYTES_PER_CHUNK\n return { vault: r.vault, bytes: contentBytes + embedBytes, notes: Number(r.notes) }\n })\n const totalBytes = perVault.reduce((sum, v) => sum + v.bytes, 0)\n return { perVault, totalBytes }\n}\n\nasync function embedChunksByVault(vaults?: string[]): Promise<Map<string, number>> {\n const out = new Map<string, number>()\n if (vaults && vaults.length === 0) return out\n const params: unknown[] = []\n let where = ''\n if (vaults && vaults.length > 0) {\n params.push(vaults)\n where = `WHERE metadata->>'vault' = ANY($1)`\n }\n try {\n const res = await query<{ vault: string; chunks: string }>(\n `SELECT metadata->>'vault' AS vault, COUNT(*)::bigint AS chunks\n FROM \"${INDEX_NAME}\" ${where} GROUP BY 1`,\n params,\n )\n for (const r of res.rows) if (r.vault) out.set(r.vault, Number(r.chunks))\n } catch {\n return out\n }\n return out\n}\n\nexport async function listCapturesInRange(args: { vault: string; from: string; to: string }): Promise<NoteRecord[]> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS}\n FROM mem_notes\n WHERE vault = $1 AND captured_at >= $2::timestamptz AND captured_at <= $3::timestamptz\n ORDER BY captured_at DESC`,\n [args.vault, args.from, args.to],\n )\n return res.rows.map(mapNote)\n}\n\nexport interface SuggestHit {\n vault: string\n path: string\n title: string\n kind: NoteKind\n}\n\nexport async function suggestNotesAcrossVaults(vaults: string[], prefix: string, limit: number): Promise<SuggestHit[]> {\n await ensureSchema()\n if (vaults.length === 0 || !prefix.trim()) return []\n const res = await query<{ vault: string; path: string; title: string; kind: string; rank: number }>(\n `SELECT vault, path, title, kind,\n CASE WHEN title ILIKE $2 || '%' THEN 0 ELSE 1 END AS rank\n FROM mem_notes\n WHERE vault = ANY($1) AND kind != 'message' AND title ILIKE '%' || $2 || '%'\n ORDER BY rank ASC, updated_at DESC\n LIMIT $3`,\n [vaults, prefix.trim(), limit],\n )\n return res.rows.map(r => ({ vault: r.vault, path: r.path, title: r.title, kind: r.kind as NoteKind }))\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/db/notes.ts"],"sourcesContent":["import { ensureSchema, query } from './pool.js'\nimport { removeDocument } from '../rag/index-pipeline.js'\nimport { EMBED_DIM } from '../rag/embedder.js'\nimport { INDEX_NAME } from '../rag/vector-store.js'\n\nconst EMBED_BYTES_PER_CHUNK = EMBED_DIM * 4\n\nexport type NoteKind = 'note' | 'library' | 'capture' | 'decision' | 'message'\n\nexport type NoteProps = Record<string, unknown>\n\nexport interface NoteRecord {\n id: string\n vault: string\n path: string\n title: string\n content: string\n kind: NoteKind\n source: string\n props: NoteProps\n capturedAt: string\n createdAt: string\n updatedAt: string\n revision: number\n updatedBy: string | null\n}\n\nexport interface RawNoteRow {\n id: string\n vault: string\n path: string\n title: string\n content: string\n kind: string\n source: string\n props: unknown\n captured_at: string\n created_at: string\n updated_at: string\n revision: number\n updated_by: string | null\n}\n\nexport function noteId(vault: string, path: string): string {\n return `${vault}::${path}`\n}\n\nexport function mapNote(r: RawNoteRow): NoteRecord {\n return {\n id: r.id,\n vault: r.vault,\n path: r.path,\n title: r.title,\n content: r.content,\n kind: r.kind as NoteKind,\n source: r.source,\n props: (r.props && typeof r.props === 'object' ? (r.props as NoteProps) : {}),\n capturedAt: r.captured_at,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n revision: r.revision,\n updatedBy: r.updated_by,\n }\n}\n\nexport const NOTE_COLUMNS = 'id, vault, path, title, content, kind, source, props, captured_at, created_at, updated_at, revision, updated_by'\n\nexport interface PutNoteArgs {\n vault: string\n path: string\n title?: string\n content: string\n kind?: NoteKind\n source?: string\n props?: NoteProps\n capturedAt?: string\n updatedBy?: string\n}\n\nexport async function putNoteRow(args: PutNoteArgs): Promise<NoteRecord> {\n await ensureSchema()\n const id = noteId(args.vault, args.path)\n const res = await query<RawNoteRow>(\n `INSERT INTO mem_notes (id, vault, path, title, content, kind, source, props, captured_at, updated_at, revision, updated_by)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, COALESCE($9::timestamptz, now()), now(), 1, $10)\n ON CONFLICT (vault, path) DO UPDATE SET\n title = EXCLUDED.title, content = EXCLUDED.content, kind = EXCLUDED.kind,\n source = EXCLUDED.source, props = EXCLUDED.props, captured_at = EXCLUDED.captured_at,\n updated_at = now(), revision = mem_notes.revision + 1, updated_by = EXCLUDED.updated_by\n RETURNING ${NOTE_COLUMNS}`,\n [id, args.vault, args.path, args.title ?? args.path, args.content, args.kind ?? 'note', args.source ?? '', JSON.stringify(args.props ?? {}), args.capturedAt ?? null, args.updatedBy ?? null],\n )\n return mapNote(res.rows[0])\n}\n\nexport type PutNoteCheckedResult = { ok: true; note: NoteRecord } | { ok: false; conflict: true; current: NoteRecord }\n\nexport async function putNoteRowChecked(args: PutNoteArgs & { baseRevision: number }): Promise<PutNoteCheckedResult> {\n await ensureSchema()\n const id = noteId(args.vault, args.path)\n const res = await query<RawNoteRow>(\n `INSERT INTO mem_notes (id, vault, path, title, content, kind, source, props, captured_at, updated_at, revision, updated_by)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, COALESCE($9::timestamptz, now()), now(), 1, $10)\n ON CONFLICT (vault, path) DO UPDATE SET\n title = EXCLUDED.title, content = EXCLUDED.content, kind = EXCLUDED.kind,\n source = EXCLUDED.source, props = EXCLUDED.props, captured_at = EXCLUDED.captured_at,\n updated_at = now(), revision = mem_notes.revision + 1, updated_by = EXCLUDED.updated_by\n WHERE mem_notes.revision = $11\n RETURNING ${NOTE_COLUMNS}`,\n [id, args.vault, args.path, args.title ?? args.path, args.content, args.kind ?? 'note', args.source ?? '', JSON.stringify(args.props ?? {}), args.capturedAt ?? null, args.updatedBy ?? null, args.baseRevision],\n )\n if (res.rows[0]) return { ok: true, note: mapNote(res.rows[0]) }\n const current = await getNoteRow(args.vault, args.path)\n if (!current) return { ok: true, note: await putNoteRow(args) }\n return { ok: false, conflict: true, current }\n}\n\nexport async function getNoteRow(vault: string, path: string): Promise<NoteRecord | null> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS} FROM mem_notes WHERE vault = $1 AND path = $2 LIMIT 1`,\n [vault, path],\n )\n return res.rows[0] ? mapNote(res.rows[0]) : null\n}\n\nexport async function getNoteById(id: string): Promise<NoteRecord | null> {\n await ensureSchema()\n const res = await query<RawNoteRow>(`SELECT ${NOTE_COLUMNS} FROM mem_notes WHERE id = $1 LIMIT 1`, [id])\n return res.rows[0] ? mapNote(res.rows[0]) : null\n}\n\nexport async function listNoteRows(vault: string, kind?: NoteKind, tags?: string[]): Promise<NoteRecord[]> {\n await ensureSchema()\n const params: unknown[] = [vault]\n let where = 'vault = $1'\n if (kind) {\n params.push(kind)\n where += ` AND kind = $${params.length}`\n }\n if (tags && tags.length > 0) {\n params.push(JSON.stringify(tags))\n where += ` AND (props->'tags')::jsonb ?| ARRAY(SELECT jsonb_array_elements_text($${params.length}::jsonb))`\n }\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS}\n FROM mem_notes WHERE ${where} ORDER BY updated_at DESC`,\n params,\n )\n return res.rows.map(mapNote)\n}\n\nexport async function listAllNoteRows(): Promise<NoteRecord[]> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS} FROM mem_notes ORDER BY vault ASC, updated_at DESC`,\n )\n return res.rows.map(mapNote)\n}\n\nexport async function resolveNoteRefs(vault: string, refs: string[]): Promise<Map<string, NoteRecord>> {\n const notes = await listNoteRows(vault)\n const byPath = new Map(notes.map(n => [n.path, n]))\n const byTitle = new Map(notes.map(n => [n.title.toLowerCase(), n]))\n const resolved = new Map<string, NoteRecord>()\n for (const ref of refs) {\n const note = byPath.get(ref) ?? byTitle.get(ref.toLowerCase())\n if (note) resolved.set(ref, note)\n }\n return resolved\n}\n\nexport async function exportVaultRows(vault: string): Promise<NoteRecord[]> {\n return listNoteRows(vault)\n}\n\nexport async function captureRow(args: { vault: string; content: string; source: string; capturedAt?: string; title?: string; folder?: string; props?: NoteProps }): Promise<NoteRecord> {\n const stamp = args.capturedAt ?? new Date().toISOString()\n const base = `captures/${stamp.replace(/[:.]/g, '-')}`\n const path = args.folder ? `${args.folder}/${base}` : base\n return putNoteRow({\n vault: args.vault,\n path,\n title: args.title ?? `Capture ${stamp}`,\n content: args.content,\n kind: 'capture',\n source: args.source,\n props: args.props,\n capturedAt: stamp,\n })\n}\n\nexport async function deleteNoteRow(vault: string, path: string): Promise<{ deleted: boolean }> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_notes WHERE vault = $1 AND path = $2`, [vault, path])\n const deleted = (res.rowCount ?? 0) > 0\n if (deleted) {\n try {\n await removeDocument({ vault, id: noteId(vault, path) })\n } catch {\n void 0\n }\n }\n return { deleted }\n}\n\nexport type DeleteNoteCheckedResult = { ok: true; deleted: boolean } | { ok: false; conflict: true; current: NoteRecord }\n\nexport async function deleteNoteRowChecked(vault: string, path: string, baseRevision: number): Promise<DeleteNoteCheckedResult> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_notes WHERE vault = $1 AND path = $2 AND revision = $3`, [vault, path, baseRevision])\n if ((res.rowCount ?? 0) > 0) {\n try {\n await removeDocument({ vault, id: noteId(vault, path) })\n } catch {\n void 0\n }\n return { ok: true, deleted: true }\n }\n const current = await getNoteRow(vault, path)\n if (!current) return { ok: true, deleted: false }\n return { ok: false, conflict: true, current }\n}\n\nexport async function deleteNotesForVault(vault: string): Promise<number> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_notes WHERE vault = $1`, [vault])\n return res.rowCount ?? 0\n}\n\nexport interface VaultStorageResult {\n perVault: { vault: string; bytes: number; notes: number }[]\n totalBytes: number\n}\n\nexport async function vaultStorage(vaults?: string[]): Promise<VaultStorageResult> {\n if (vaults && vaults.length === 0) return { perVault: [], totalBytes: 0 }\n await ensureSchema()\n const params: unknown[] = []\n let where = ''\n if (vaults && vaults.length > 0) {\n params.push(vaults)\n where = `WHERE vault = ANY($1)`\n }\n const res = await query<{ vault: string; notes: string; bytes: string }>(\n `SELECT vault, COUNT(*)::int AS notes, COALESCE(SUM(octet_length(content)), 0)::bigint AS bytes\n FROM mem_notes ${where} GROUP BY vault ORDER BY vault ASC`,\n params,\n )\n const embedChunks = await embedChunksByVault(vaults)\n const perVault = res.rows.map(r => {\n const contentBytes = Number(r.bytes)\n const embedBytes = (embedChunks.get(r.vault) ?? 0) * EMBED_BYTES_PER_CHUNK\n return { vault: r.vault, bytes: contentBytes + embedBytes, notes: Number(r.notes) }\n })\n const totalBytes = perVault.reduce((sum, v) => sum + v.bytes, 0)\n return { perVault, totalBytes }\n}\n\nasync function embedChunksByVault(vaults?: string[]): Promise<Map<string, number>> {\n const out = new Map<string, number>()\n if (vaults && vaults.length === 0) return out\n const params: unknown[] = []\n let where = ''\n if (vaults && vaults.length > 0) {\n params.push(vaults)\n where = `WHERE metadata->>'vault' = ANY($1)`\n }\n try {\n const res = await query<{ vault: string; chunks: string }>(\n `SELECT metadata->>'vault' AS vault, COUNT(*)::bigint AS chunks\n FROM \"${INDEX_NAME}\" ${where} GROUP BY 1`,\n params,\n )\n for (const r of res.rows) if (r.vault) out.set(r.vault, Number(r.chunks))\n } catch {\n return out\n }\n return out\n}\n\nexport async function listCapturesInRange(args: { vault: string; from: string; to: string }): Promise<NoteRecord[]> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS}\n FROM mem_notes\n WHERE vault = $1 AND captured_at >= $2::timestamptz AND captured_at <= $3::timestamptz\n ORDER BY captured_at DESC`,\n [args.vault, args.from, args.to],\n )\n return res.rows.map(mapNote)\n}\n\nexport interface SuggestHit {\n vault: string\n path: string\n title: string\n kind: NoteKind\n}\n\nexport async function suggestNotesAcrossVaults(vaults: string[], prefix: string, limit: number): Promise<SuggestHit[]> {\n await ensureSchema()\n if (vaults.length === 0 || !prefix.trim()) return []\n const res = await query<{ vault: string; path: string; title: string; kind: string; rank: number }>(\n `SELECT vault, path, title, kind,\n CASE WHEN title ILIKE $2 || '%' THEN 0 ELSE 1 END AS rank\n FROM mem_notes\n WHERE vault = ANY($1) AND kind != 'message' AND title ILIKE '%' || $2 || '%'\n ORDER BY rank ASC, updated_at DESC\n LIMIT $3`,\n [vaults, prefix.trim(), limit],\n )\n return res.rows.map(r => ({ vault: r.vault, path: r.path, title: r.title, kind: r.kind as NoteKind }))\n}\n"],"mappings":"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","names":[]}
|
package/dist/db/presence.js
CHANGED
package/dist/db/presence.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/presence.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\n\nconst PRESENCE_TTL_SECONDS = 120\n\nexport async function touchPresence(noteId: string, identity: string): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_note_presence (note_id, identity, heartbeat_at)\n VALUES ($1, $2, now())\n ON CONFLICT (note_id, identity) DO UPDATE SET heartbeat_at = now()`,\n [noteId, identity],\n )\n}\n\nexport async function othersPresent(noteId: string, identity: string): Promise<string[]> {\n await ensureSchema()\n const res = await query<{ identity: string }>(\n `SELECT identity FROM mem_note_presence\n WHERE note_id = $1 AND identity != $2 AND heartbeat_at > now() - interval '${PRESENCE_TTL_SECONDS} seconds'`,\n [noteId, identity],\n )\n return res.rows.map(r => r.identity)\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AAEpC,MAAM,uBAAuB;AAE7B,eAAsB,cAAc,QAAgB,UAAiC;AACnF,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA,IAGA,CAAC,QAAQ,QAAQ;AAAA,EACnB;AACF;AAEA,eAAsB,cAAc,QAAgB,UAAqC;AACvF,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,kFAC8E,oBAAoB;AAAA,IAClG,CAAC,QAAQ,QAAQ;AAAA,EACnB;AACA,SAAO,IAAI,KAAK,IAAI,OAAK,EAAE,QAAQ;AACrC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/db/presence.ts"],"sourcesContent":["import { ensureSchema, query } from './pool.js'\n\nconst PRESENCE_TTL_SECONDS = 120\n\nexport async function touchPresence(noteId: string, identity: string): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_note_presence (note_id, identity, heartbeat_at)\n VALUES ($1, $2, now())\n ON CONFLICT (note_id, identity) DO UPDATE SET heartbeat_at = now()`,\n [noteId, identity],\n )\n}\n\nexport async function othersPresent(noteId: string, identity: string): Promise<string[]> {\n await ensureSchema()\n const res = await query<{ identity: string }>(\n `SELECT identity FROM mem_note_presence\n WHERE note_id = $1 AND identity != $2 AND heartbeat_at > now() - interval '${PRESENCE_TTL_SECONDS} seconds'`,\n [noteId, identity],\n )\n return res.rows.map(r => r.identity)\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AAEpC,MAAM,uBAAuB;AAE7B,eAAsB,cAAc,QAAgB,UAAiC;AACnF,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA,IAGA,CAAC,QAAQ,QAAQ;AAAA,EACnB;AACF;AAEA,eAAsB,cAAc,QAAgB,UAAqC;AACvF,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,kFAC8E,oBAAoB;AAAA,IAClG,CAAC,QAAQ,QAAQ;AAAA,EACnB;AACA,SAAO,IAAI,KAAK,IAAI,OAAK,EAAE,QAAQ;AACrC;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/props-snapshots.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\n\nexport interface PropsSnapshotRecord {\n id: number\n vault: string\n path: string\n prevProps: Record<string, unknown>\n runId: string | null\n createdAt: string\n}\n\ninterface RawRow {\n id: number\n vault: string\n path: string\n prev_props: unknown\n run_id: string | null\n created_at: string\n}\n\nfunction mapRow(r: RawRow): PropsSnapshotRecord {\n return {\n id: r.id,\n vault: r.vault,\n path: r.path,\n prevProps: r.prev_props && typeof r.prev_props === 'object' ? (r.prev_props as Record<string, unknown>) : {},\n runId: r.run_id,\n createdAt: r.created_at,\n }\n}\n\nexport async function snapshotProps(args: {\n vault: string\n path: string\n prevProps: Record<string, unknown>\n runId?: string\n}): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_props_snapshots (vault, path, prev_props, run_id) VALUES ($1, $2, $3, $4)`,\n [args.vault, args.path, JSON.stringify(args.prevProps ?? {}), args.runId ?? null],\n )\n}\n\nexport async function listPropsSnapshots(vault: string, path: string, limit = 20): Promise<PropsSnapshotRecord[]> {\n await ensureSchema()\n const res = await query<RawRow>(\n `SELECT id, vault, path, prev_props, run_id, created_at FROM mem_props_snapshots WHERE vault = $1 AND path = $2 ORDER BY created_at DESC LIMIT $3`,\n [vault, path, limit],\n )\n return res.rows.map(mapRow)\n}\n\nexport async function pruneOldPropsSnapshots(retentionDays = 30): Promise<number> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_props_snapshots WHERE created_at < now() - ($1 || ' days')::interval`, [retentionDays])\n return res.rowCount ?? 0\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AAoBpC,SAAS,OAAO,GAAgC;AAC9C,SAAO;AAAA,IACL,IAAI,EAAE;AAAA,IACN,OAAO,EAAE;AAAA,IACT,MAAM,EAAE;AAAA,IACR,WAAW,EAAE,cAAc,OAAO,EAAE,eAAe,WAAY,EAAE,aAAyC,CAAC;AAAA,IAC3G,OAAO,EAAE;AAAA,IACT,WAAW,EAAE;AAAA,EACf;AACF;AAEA,eAAsB,cAAc,MAKlB;AAChB,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA,IACA,CAAC,KAAK,OAAO,KAAK,MAAM,KAAK,UAAU,KAAK,aAAa,CAAC,CAAC,GAAG,KAAK,SAAS,IAAI;AAAA,EAClF;AACF;AAEA,eAAsB,mBAAmB,OAAe,MAAc,QAAQ,IAAoC;AAChH,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,OAAO,MAAM,KAAK;AAAA,EACrB;AACA,SAAO,IAAI,KAAK,IAAI,MAAM;AAC5B;AAEA,eAAsB,uBAAuB,gBAAgB,IAAqB;AAChF,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM,MAAM,wFAAwF,CAAC,aAAa,CAAC;AAC/H,SAAO,IAAI,YAAY;AACzB;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/db/props-snapshots.ts"],"sourcesContent":["import { ensureSchema, query } from './pool.js'\n\nexport interface PropsSnapshotRecord {\n id: number\n vault: string\n path: string\n prevProps: Record<string, unknown>\n runId: string | null\n createdAt: string\n}\n\ninterface RawRow {\n id: number\n vault: string\n path: string\n prev_props: unknown\n run_id: string | null\n created_at: string\n}\n\nfunction mapRow(r: RawRow): PropsSnapshotRecord {\n return {\n id: r.id,\n vault: r.vault,\n path: r.path,\n prevProps: r.prev_props && typeof r.prev_props === 'object' ? (r.prev_props as Record<string, unknown>) : {},\n runId: r.run_id,\n createdAt: r.created_at,\n }\n}\n\nexport async function snapshotProps(args: {\n vault: string\n path: string\n prevProps: Record<string, unknown>\n runId?: string\n}): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_props_snapshots (vault, path, prev_props, run_id) VALUES ($1, $2, $3, $4)`,\n [args.vault, args.path, JSON.stringify(args.prevProps ?? {}), args.runId ?? null],\n )\n}\n\nexport async function listPropsSnapshots(vault: string, path: string, limit = 20): Promise<PropsSnapshotRecord[]> {\n await ensureSchema()\n const res = await query<RawRow>(\n `SELECT id, vault, path, prev_props, run_id, created_at FROM mem_props_snapshots WHERE vault = $1 AND path = $2 ORDER BY created_at DESC LIMIT $3`,\n [vault, path, limit],\n )\n return res.rows.map(mapRow)\n}\n\nexport async function pruneOldPropsSnapshots(retentionDays = 30): Promise<number> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_props_snapshots WHERE created_at < now() - ($1 || ' days')::interval`, [retentionDays])\n return res.rowCount ?? 0\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AAoBpC,SAAS,OAAO,GAAgC;AAC9C,SAAO;AAAA,IACL,IAAI,EAAE;AAAA,IACN,OAAO,EAAE;AAAA,IACT,MAAM,EAAE;AAAA,IACR,WAAW,EAAE,cAAc,OAAO,EAAE,eAAe,WAAY,EAAE,aAAyC,CAAC;AAAA,IAC3G,OAAO,EAAE;AAAA,IACT,WAAW,EAAE;AAAA,EACf;AACF;AAEA,eAAsB,cAAc,MAKlB;AAChB,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA,IACA,CAAC,KAAK,OAAO,KAAK,MAAM,KAAK,UAAU,KAAK,aAAa,CAAC,CAAC,GAAG,KAAK,SAAS,IAAI;AAAA,EAClF;AACF;AAEA,eAAsB,mBAAmB,OAAe,MAAc,QAAQ,IAAoC;AAChH,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,OAAO,MAAM,KAAK;AAAA,EACrB;AACA,SAAO,IAAI,KAAK,IAAI,MAAM;AAC5B;AAEA,eAAsB,uBAAuB,gBAAgB,IAAqB;AAChF,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM,MAAM,wFAAwF,CAAC,aAAa,CAAC;AAC/H,SAAO,IAAI,YAAY;AACzB;","names":[]}
|
package/dist/db/provenance.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/provenance.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\n\nexport interface ProvenanceEntry {\n keyId: string | null\n identity: string\n session: string | null\n op: string\n target?: string\n detail?: Record<string, unknown>\n}\n\nexport async function logProvenance(entry: ProvenanceEntry): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_provenance (key_id, identity, session, op, target, detail)\n VALUES ($1, $2, $3, $4, $5, $6::jsonb)`,\n [entry.keyId, entry.identity, entry.session ?? null, entry.op, entry.target ?? null, JSON.stringify(entry.detail ?? {})],\n )\n}\n\nexport interface ProvenanceRow {\n id: string\n keyId: string | null\n identity: string\n session: string | null\n op: string\n target: string | null\n detail: Record<string, unknown>\n ts: string\n}\n\ninterface RawProvRow {\n id: string\n key_id: string | null\n identity: string\n session: string | null\n op: string\n target: string | null\n detail: Record<string, unknown>\n ts: string\n}\n\nexport async function listProvenance(filter: { identity?: string; op?: string; limit?: number }): Promise<ProvenanceRow[]> {\n await ensureSchema()\n const clauses: string[] = []\n const params: unknown[] = []\n if (filter.identity) {\n params.push(filter.identity)\n clauses.push(`identity = $${params.length}`)\n }\n if (filter.op) {\n params.push(filter.op)\n clauses.push(`op = $${params.length}`)\n }\n const where = clauses.length ? `WHERE ${clauses.join(' AND ')}` : ''\n params.push(Math.min(filter.limit ?? 100, 1000))\n const res = await query<RawProvRow>(\n `SELECT id, key_id, identity, session, op, target, detail, ts FROM mem_provenance ${where} ORDER BY ts DESC LIMIT $${params.length}`,\n params,\n )\n return res.rows.map(r => ({\n id: String(r.id),\n keyId: r.key_id,\n identity: r.identity,\n session: r.session,\n op: r.op,\n target: r.target,\n detail: r.detail,\n ts: r.ts,\n }))\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AAWpC,eAAsB,cAAc,OAAuC;AACzE,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA;AAAA,IAEA,CAAC,MAAM,OAAO,MAAM,UAAU,MAAM,WAAW,MAAM,MAAM,IAAI,MAAM,UAAU,MAAM,KAAK,UAAU,MAAM,UAAU,CAAC,CAAC,CAAC;AAAA,EACzH;AACF;AAwBA,eAAsB,eAAe,QAAsF;AACzH,QAAM,aAAa;AACnB,QAAM,UAAoB,CAAC;AAC3B,QAAM,SAAoB,CAAC;AAC3B,MAAI,OAAO,UAAU;AACnB,WAAO,KAAK,OAAO,QAAQ;AAC3B,YAAQ,KAAK,eAAe,OAAO,MAAM,EAAE;AAAA,EAC7C;AACA,MAAI,OAAO,IAAI;AACb,WAAO,KAAK,OAAO,EAAE;AACrB,YAAQ,KAAK,SAAS,OAAO,MAAM,EAAE;AAAA,EACvC;AACA,QAAM,QAAQ,QAAQ,SAAS,SAAS,QAAQ,KAAK,OAAO,CAAC,KAAK;AAClE,SAAO,KAAK,KAAK,IAAI,OAAO,SAAS,KAAK,GAAI,CAAC;AAC/C,QAAM,MAAM,MAAM;AAAA,IAChB,oFAAoF,KAAK,4BAA4B,OAAO,MAAM;AAAA,IAClI;AAAA,EACF;AACA,SAAO,IAAI,KAAK,IAAI,QAAM;AAAA,IACxB,IAAI,OAAO,EAAE,EAAE;AAAA,IACf,OAAO,EAAE;AAAA,IACT,UAAU,EAAE;AAAA,IACZ,SAAS,EAAE;AAAA,IACX,IAAI,EAAE;AAAA,IACN,QAAQ,EAAE;AAAA,IACV,QAAQ,EAAE;AAAA,IACV,IAAI,EAAE;AAAA,EACR,EAAE;AACJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/db/provenance.ts"],"sourcesContent":["import { ensureSchema, query } from './pool.js'\n\nexport interface ProvenanceEntry {\n keyId: string | null\n identity: string\n session: string | null\n op: string\n target?: string\n detail?: Record<string, unknown>\n}\n\nexport async function logProvenance(entry: ProvenanceEntry): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_provenance (key_id, identity, session, op, target, detail)\n VALUES ($1, $2, $3, $4, $5, $6::jsonb)`,\n [entry.keyId, entry.identity, entry.session ?? null, entry.op, entry.target ?? null, JSON.stringify(entry.detail ?? {})],\n )\n}\n\nexport interface ProvenanceRow {\n id: string\n keyId: string | null\n identity: string\n session: string | null\n op: string\n target: string | null\n detail: Record<string, unknown>\n ts: string\n}\n\ninterface RawProvRow {\n id: string\n key_id: string | null\n identity: string\n session: string | null\n op: string\n target: string | null\n detail: Record<string, unknown>\n ts: string\n}\n\nexport async function listProvenance(filter: { identity?: string; op?: string; limit?: number }): Promise<ProvenanceRow[]> {\n await ensureSchema()\n const clauses: string[] = []\n const params: unknown[] = []\n if (filter.identity) {\n params.push(filter.identity)\n clauses.push(`identity = $${params.length}`)\n }\n if (filter.op) {\n params.push(filter.op)\n clauses.push(`op = $${params.length}`)\n }\n const where = clauses.length ? `WHERE ${clauses.join(' AND ')}` : ''\n params.push(Math.min(filter.limit ?? 100, 1000))\n const res = await query<RawProvRow>(\n `SELECT id, key_id, identity, session, op, target, detail, ts FROM mem_provenance ${where} ORDER BY ts DESC LIMIT $${params.length}`,\n params,\n )\n return res.rows.map(r => ({\n id: String(r.id),\n keyId: r.key_id,\n identity: r.identity,\n session: r.session,\n op: r.op,\n target: r.target,\n detail: r.detail,\n ts: r.ts,\n }))\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AAWpC,eAAsB,cAAc,OAAuC;AACzE,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA;AAAA,IAEA,CAAC,MAAM,OAAO,MAAM,UAAU,MAAM,WAAW,MAAM,MAAM,IAAI,MAAM,UAAU,MAAM,KAAK,UAAU,MAAM,UAAU,CAAC,CAAC,CAAC;AAAA,EACzH;AACF;AAwBA,eAAsB,eAAe,QAAsF;AACzH,QAAM,aAAa;AACnB,QAAM,UAAoB,CAAC;AAC3B,QAAM,SAAoB,CAAC;AAC3B,MAAI,OAAO,UAAU;AACnB,WAAO,KAAK,OAAO,QAAQ;AAC3B,YAAQ,KAAK,eAAe,OAAO,MAAM,EAAE;AAAA,EAC7C;AACA,MAAI,OAAO,IAAI;AACb,WAAO,KAAK,OAAO,EAAE;AACrB,YAAQ,KAAK,SAAS,OAAO,MAAM,EAAE;AAAA,EACvC;AACA,QAAM,QAAQ,QAAQ,SAAS,SAAS,QAAQ,KAAK,OAAO,CAAC,KAAK;AAClE,SAAO,KAAK,KAAK,IAAI,OAAO,SAAS,KAAK,GAAI,CAAC;AAC/C,QAAM,MAAM,MAAM;AAAA,IAChB,oFAAoF,KAAK,4BAA4B,OAAO,MAAM;AAAA,IAClI;AAAA,EACF;AACA,SAAO,IAAI,KAAK,IAAI,QAAM;AAAA,IACxB,IAAI,OAAO,EAAE,EAAE;AAAA,IACf,OAAO,EAAE;AAAA,IACT,UAAU,EAAE;AAAA,IACZ,SAAS,EAAE;AAAA,IACX,IAAI,EAAE;AAAA,IACN,QAAQ,EAAE;AAAA,IACV,QAAQ,EAAE;AAAA,IACV,IAAI,EAAE;AAAA,EACR,EAAE;AACJ;","names":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ensureSchema, query } from "./pool";
|
|
2
|
-
import { encryptSecret, decryptSecret } from "../lib/secret-crypto";
|
|
1
|
+
import { ensureSchema, query } from "./pool.js";
|
|
2
|
+
import { encryptSecret, decryptSecret } from "../lib/secret-crypto.js";
|
|
3
3
|
function mapRow(r) {
|
|
4
4
|
return {
|
|
5
5
|
identity: r.identity,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/schedule-entitlements.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\nimport { encryptSecret, decryptSecret } from '../lib/secret-crypto'\n\nexport interface ScheduleEntitlement {\n identity: string\n enabled: boolean\n quotaPerPeriod: number\n usedThisPeriod: number\n periodStart: string\n}\n\ninterface RawRow {\n identity: string\n enabled: boolean\n quota_per_period: number\n used_this_period: number\n period_start: string\n scraper_api_key_enc: string | null\n}\n\nfunction mapRow(r: RawRow): ScheduleEntitlement {\n return {\n identity: r.identity,\n enabled: r.enabled,\n quotaPerPeriod: r.quota_per_period,\n usedThisPeriod: r.used_this_period,\n periodStart: r.period_start,\n }\n}\n\nasync function rollPeriodIfNeeded(identity: string): Promise<void> {\n await query(\n `UPDATE mem_schedule_entitlements\n SET used_this_period = 0, period_start = date_trunc('month', now())::date, updated_at = now()\n WHERE identity = $1 AND period_start < date_trunc('month', now())::date`,\n [identity],\n )\n}\n\nexport async function getEntitlement(identity: string): Promise<ScheduleEntitlement | null> {\n await ensureSchema()\n await rollPeriodIfNeeded(identity)\n const res = await query<RawRow>(`SELECT * FROM mem_schedule_entitlements WHERE identity = $1 LIMIT 1`, [identity])\n return res.rows[0] ? mapRow(res.rows[0]) : null\n}\n\nexport async function setEntitlement(args: {\n identity: string\n enabled: boolean\n quotaPerPeriod?: number\n scraperApiKey?: string\n}): Promise<void> {\n await ensureSchema()\n const scraperApiKeyEnc = args.scraperApiKey ? encryptSecret(args.scraperApiKey) : null\n await query(\n `INSERT INTO mem_schedule_entitlements (identity, enabled, quota_per_period, used_this_period, period_start, scraper_api_key_enc)\n VALUES ($1, $2, $3, 0, date_trunc('month', now())::date, $4)\n ON CONFLICT (identity) DO UPDATE SET\n enabled = EXCLUDED.enabled,\n quota_per_period = COALESCE($3, mem_schedule_entitlements.quota_per_period),\n scraper_api_key_enc = COALESCE($4, mem_schedule_entitlements.scraper_api_key_enc),\n updated_at = now()`,\n [args.identity, args.enabled, args.quotaPerPeriod ?? 1000, scraperApiKeyEnc],\n )\n}\n\nexport async function getScraperApiKey(identity: string): Promise<string | null> {\n await ensureSchema()\n const res = await query<{ scraper_api_key_enc: string | null }>(\n `SELECT scraper_api_key_enc FROM mem_schedule_entitlements WHERE identity = $1 LIMIT 1`,\n [identity],\n )\n const enc = res.rows[0]?.scraper_api_key_enc\n return enc ? decryptSecret(enc) : null\n}\n\nexport type ConsumeQuotaResult = { ok: true } | { ok: false; reason: 'not_enabled' | 'quota_exceeded' }\n\nexport async function tryConsumeQuota(identity: string): Promise<ConsumeQuotaResult> {\n await ensureSchema()\n await rollPeriodIfNeeded(identity)\n const res = await query<{ used_this_period: number }>(\n `UPDATE mem_schedule_entitlements\n SET used_this_period = used_this_period + 1, updated_at = now()\n WHERE identity = $1 AND enabled = true AND used_this_period < quota_per_period\n RETURNING used_this_period`,\n [identity],\n )\n if ((res.rowCount ?? 0) > 0) return { ok: true }\n const current = await getEntitlement(identity)\n if (!current || !current.enabled) return { ok: false, reason: 'not_enabled' }\n return { ok: false, reason: 'quota_exceeded' }\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AACpC,SAAS,eAAe,qBAAqB;AAmB7C,SAAS,OAAO,GAAgC;AAC9C,SAAO;AAAA,IACL,UAAU,EAAE;AAAA,IACZ,SAAS,EAAE;AAAA,IACX,gBAAgB,EAAE;AAAA,IAClB,gBAAgB,EAAE;AAAA,IAClB,aAAa,EAAE;AAAA,EACjB;AACF;AAEA,eAAe,mBAAmB,UAAiC;AACjE,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA,IAGA,CAAC,QAAQ;AAAA,EACX;AACF;AAEA,eAAsB,eAAe,UAAuD;AAC1F,QAAM,aAAa;AACnB,QAAM,mBAAmB,QAAQ;AACjC,QAAM,MAAM,MAAM,MAAc,uEAAuE,CAAC,QAAQ,CAAC;AACjH,SAAO,IAAI,KAAK,CAAC,IAAI,OAAO,IAAI,KAAK,CAAC,CAAC,IAAI;AAC7C;AAEA,eAAsB,eAAe,MAKnB;AAChB,QAAM,aAAa;AACnB,QAAM,mBAAmB,KAAK,gBAAgB,cAAc,KAAK,aAAa,IAAI;AAClF,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,CAAC,KAAK,UAAU,KAAK,SAAS,KAAK,kBAAkB,KAAM,gBAAgB;AAAA,EAC7E;AACF;AAEA,eAAsB,iBAAiB,UAA0C;AAC/E,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AACA,QAAM,MAAM,IAAI,KAAK,CAAC,GAAG;AACzB,SAAO,MAAM,cAAc,GAAG,IAAI;AACpC;AAIA,eAAsB,gBAAgB,UAA+C;AACnF,QAAM,aAAa;AACnB,QAAM,mBAAmB,QAAQ;AACjC,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA;AAAA;AAAA;AAAA,IAIA,CAAC,QAAQ;AAAA,EACX;AACA,OAAK,IAAI,YAAY,KAAK,EAAG,QAAO,EAAE,IAAI,KAAK;AAC/C,QAAM,UAAU,MAAM,eAAe,QAAQ;AAC7C,MAAI,CAAC,WAAW,CAAC,QAAQ,QAAS,QAAO,EAAE,IAAI,OAAO,QAAQ,cAAc;AAC5E,SAAO,EAAE,IAAI,OAAO,QAAQ,iBAAiB;AAC/C;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/db/schedule-entitlements.ts"],"sourcesContent":["import { ensureSchema, query } from './pool.js'\nimport { encryptSecret, decryptSecret } from '../lib/secret-crypto.js'\n\nexport interface ScheduleEntitlement {\n identity: string\n enabled: boolean\n quotaPerPeriod: number\n usedThisPeriod: number\n periodStart: string\n}\n\ninterface RawRow {\n identity: string\n enabled: boolean\n quota_per_period: number\n used_this_period: number\n period_start: string\n scraper_api_key_enc: string | null\n}\n\nfunction mapRow(r: RawRow): ScheduleEntitlement {\n return {\n identity: r.identity,\n enabled: r.enabled,\n quotaPerPeriod: r.quota_per_period,\n usedThisPeriod: r.used_this_period,\n periodStart: r.period_start,\n }\n}\n\nasync function rollPeriodIfNeeded(identity: string): Promise<void> {\n await query(\n `UPDATE mem_schedule_entitlements\n SET used_this_period = 0, period_start = date_trunc('month', now())::date, updated_at = now()\n WHERE identity = $1 AND period_start < date_trunc('month', now())::date`,\n [identity],\n )\n}\n\nexport async function getEntitlement(identity: string): Promise<ScheduleEntitlement | null> {\n await ensureSchema()\n await rollPeriodIfNeeded(identity)\n const res = await query<RawRow>(`SELECT * FROM mem_schedule_entitlements WHERE identity = $1 LIMIT 1`, [identity])\n return res.rows[0] ? mapRow(res.rows[0]) : null\n}\n\nexport async function setEntitlement(args: {\n identity: string\n enabled: boolean\n quotaPerPeriod?: number\n scraperApiKey?: string\n}): Promise<void> {\n await ensureSchema()\n const scraperApiKeyEnc = args.scraperApiKey ? encryptSecret(args.scraperApiKey) : null\n await query(\n `INSERT INTO mem_schedule_entitlements (identity, enabled, quota_per_period, used_this_period, period_start, scraper_api_key_enc)\n VALUES ($1, $2, $3, 0, date_trunc('month', now())::date, $4)\n ON CONFLICT (identity) DO UPDATE SET\n enabled = EXCLUDED.enabled,\n quota_per_period = COALESCE($3, mem_schedule_entitlements.quota_per_period),\n scraper_api_key_enc = COALESCE($4, mem_schedule_entitlements.scraper_api_key_enc),\n updated_at = now()`,\n [args.identity, args.enabled, args.quotaPerPeriod ?? 1000, scraperApiKeyEnc],\n )\n}\n\nexport async function getScraperApiKey(identity: string): Promise<string | null> {\n await ensureSchema()\n const res = await query<{ scraper_api_key_enc: string | null }>(\n `SELECT scraper_api_key_enc FROM mem_schedule_entitlements WHERE identity = $1 LIMIT 1`,\n [identity],\n )\n const enc = res.rows[0]?.scraper_api_key_enc\n return enc ? decryptSecret(enc) : null\n}\n\nexport type ConsumeQuotaResult = { ok: true } | { ok: false; reason: 'not_enabled' | 'quota_exceeded' }\n\nexport async function tryConsumeQuota(identity: string): Promise<ConsumeQuotaResult> {\n await ensureSchema()\n await rollPeriodIfNeeded(identity)\n const res = await query<{ used_this_period: number }>(\n `UPDATE mem_schedule_entitlements\n SET used_this_period = used_this_period + 1, updated_at = now()\n WHERE identity = $1 AND enabled = true AND used_this_period < quota_per_period\n RETURNING used_this_period`,\n [identity],\n )\n if ((res.rowCount ?? 0) > 0) return { ok: true }\n const current = await getEntitlement(identity)\n if (!current || !current.enabled) return { ok: false, reason: 'not_enabled' }\n return { ok: false, reason: 'quota_exceeded' }\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AACpC,SAAS,eAAe,qBAAqB;AAmB7C,SAAS,OAAO,GAAgC;AAC9C,SAAO;AAAA,IACL,UAAU,EAAE;AAAA,IACZ,SAAS,EAAE;AAAA,IACX,gBAAgB,EAAE;AAAA,IAClB,gBAAgB,EAAE;AAAA,IAClB,aAAa,EAAE;AAAA,EACjB;AACF;AAEA,eAAe,mBAAmB,UAAiC;AACjE,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA,IAGA,CAAC,QAAQ;AAAA,EACX;AACF;AAEA,eAAsB,eAAe,UAAuD;AAC1F,QAAM,aAAa;AACnB,QAAM,mBAAmB,QAAQ;AACjC,QAAM,MAAM,MAAM,MAAc,uEAAuE,CAAC,QAAQ,CAAC;AACjH,SAAO,IAAI,KAAK,CAAC,IAAI,OAAO,IAAI,KAAK,CAAC,CAAC,IAAI;AAC7C;AAEA,eAAsB,eAAe,MAKnB;AAChB,QAAM,aAAa;AACnB,QAAM,mBAAmB,KAAK,gBAAgB,cAAc,KAAK,aAAa,IAAI;AAClF,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,CAAC,KAAK,UAAU,KAAK,SAAS,KAAK,kBAAkB,KAAM,gBAAgB;AAAA,EAC7E;AACF;AAEA,eAAsB,iBAAiB,UAA0C;AAC/E,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AACA,QAAM,MAAM,IAAI,KAAK,CAAC,GAAG;AACzB,SAAO,MAAM,cAAc,GAAG,IAAI;AACpC;AAIA,eAAsB,gBAAgB,UAA+C;AACnF,QAAM,aAAa;AACnB,QAAM,mBAAmB,QAAQ;AACjC,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA;AAAA;AAAA;AAAA,IAIA,CAAC,QAAQ;AAAA,EACX;AACA,OAAK,IAAI,YAAY,KAAK,EAAG,QAAO,EAAE,IAAI,KAAK;AAC/C,QAAM,UAAU,MAAM,eAAe,QAAQ;AAC7C,MAAI,CAAC,WAAW,CAAC,QAAQ,QAAS,QAAO,EAAE,IAAI,OAAO,QAAQ,cAAc;AAC5E,SAAO,EAAE,IAAI,OAAO,QAAQ,iBAAiB;AAC/C;","names":[]}
|
package/dist/db/schedule-link.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ensureSchema, query } from "./pool";
|
|
2
|
-
import { issueKeyRow, revokeKeyRow, getKeyById } from "./keys";
|
|
3
|
-
import { fullScope } from "../lib/auth";
|
|
1
|
+
import { ensureSchema, query } from "./pool.js";
|
|
2
|
+
import { issueKeyRow, revokeKeyRow, getKeyById } from "./keys.js";
|
|
3
|
+
import { fullScope } from "../lib/auth.js";
|
|
4
4
|
async function getScheduleLinkKeyId(identity) {
|
|
5
5
|
await ensureSchema();
|
|
6
6
|
const res = await query(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/schedule-link.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\nimport { issueKeyRow, revokeKeyRow, getKeyById } from './keys'\nimport { fullScope } from '../lib/auth'\n\nasync function getScheduleLinkKeyId(identity: string): Promise<string | null> {\n await ensureSchema()\n const res = await query<{ schedule_link_key_id: string | null }>(\n `SELECT schedule_link_key_id FROM mem_settings WHERE identity = $1 LIMIT 1`,\n [identity],\n )\n return res.rows[0]?.schedule_link_key_id ?? null\n}\n\nasync function setScheduleLinkKeyId(identity: string, keyId: string | null): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_settings (identity, schedule_link_key_id, updated_at)\n VALUES ($1, $2, now())\n ON CONFLICT (identity) DO UPDATE SET schedule_link_key_id = EXCLUDED.schedule_link_key_id, updated_at = now()`,\n [identity, keyId],\n )\n}\n\nexport async function getOrCreateScheduleLinkSecret(identity: string): Promise<string> {\n const existingId = await getScheduleLinkKeyId(identity)\n if (existingId) {\n const existing = await getKeyById(existingId)\n if (existing && !existing.revoked) {\n throw new ScheduleLinkSecretNotRetrievable()\n }\n }\n const issued = await issueKeyRow({ identity, vaults: [], scope: fullScope(), plan: 'free', expiresAt: null })\n await setScheduleLinkKeyId(identity, issued.keyId)\n return issued.secret\n}\n\nexport class ScheduleLinkSecretNotRetrievable extends Error {\n constructor() {\n super('A schedule link already exists for this identity; its secret cannot be re-displayed (only revoked and reissued).')\n }\n}\n\nexport async function revokeScheduleLink(identity: string): Promise<boolean> {\n const existingId = await getScheduleLinkKeyId(identity)\n if (!existingId) return false\n await revokeKeyRow(existingId)\n await setScheduleLinkKeyId(identity, null)\n return true\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AACpC,SAAS,aAAa,cAAc,kBAAkB;AACtD,SAAS,iBAAiB;AAE1B,eAAe,qBAAqB,UAA0C;AAC5E,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AACA,SAAO,IAAI,KAAK,CAAC,GAAG,wBAAwB;AAC9C;AAEA,eAAe,qBAAqB,UAAkB,OAAqC;AACzF,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA,IAGA,CAAC,UAAU,KAAK;AAAA,EAClB;AACF;AAEA,eAAsB,8BAA8B,UAAmC;AACrF,QAAM,aAAa,MAAM,qBAAqB,QAAQ;AACtD,MAAI,YAAY;AACd,UAAM,WAAW,MAAM,WAAW,UAAU;AAC5C,QAAI,YAAY,CAAC,SAAS,SAAS;AACjC,YAAM,IAAI,iCAAiC;AAAA,IAC7C;AAAA,EACF;AACA,QAAM,SAAS,MAAM,YAAY,EAAE,UAAU,QAAQ,CAAC,GAAG,OAAO,UAAU,GAAG,MAAM,QAAQ,WAAW,KAAK,CAAC;AAC5G,QAAM,qBAAqB,UAAU,OAAO,KAAK;AACjD,SAAO,OAAO;AAChB;AAEO,MAAM,yCAAyC,MAAM;AAAA,EAC1D,cAAc;AACZ,UAAM,kHAAkH;AAAA,EAC1H;AACF;AAEA,eAAsB,mBAAmB,UAAoC;AAC3E,QAAM,aAAa,MAAM,qBAAqB,QAAQ;AACtD,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,aAAa,UAAU;AAC7B,QAAM,qBAAqB,UAAU,IAAI;AACzC,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/db/schedule-link.ts"],"sourcesContent":["import { ensureSchema, query } from './pool.js'\nimport { issueKeyRow, revokeKeyRow, getKeyById } from './keys.js'\nimport { fullScope } from '../lib/auth.js'\n\nasync function getScheduleLinkKeyId(identity: string): Promise<string | null> {\n await ensureSchema()\n const res = await query<{ schedule_link_key_id: string | null }>(\n `SELECT schedule_link_key_id FROM mem_settings WHERE identity = $1 LIMIT 1`,\n [identity],\n )\n return res.rows[0]?.schedule_link_key_id ?? null\n}\n\nasync function setScheduleLinkKeyId(identity: string, keyId: string | null): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_settings (identity, schedule_link_key_id, updated_at)\n VALUES ($1, $2, now())\n ON CONFLICT (identity) DO UPDATE SET schedule_link_key_id = EXCLUDED.schedule_link_key_id, updated_at = now()`,\n [identity, keyId],\n )\n}\n\nexport async function getOrCreateScheduleLinkSecret(identity: string): Promise<string> {\n const existingId = await getScheduleLinkKeyId(identity)\n if (existingId) {\n const existing = await getKeyById(existingId)\n if (existing && !existing.revoked) {\n throw new ScheduleLinkSecretNotRetrievable()\n }\n }\n const issued = await issueKeyRow({ identity, vaults: [], scope: fullScope(), plan: 'free', expiresAt: null })\n await setScheduleLinkKeyId(identity, issued.keyId)\n return issued.secret\n}\n\nexport class ScheduleLinkSecretNotRetrievable extends Error {\n constructor() {\n super('A schedule link already exists for this identity; its secret cannot be re-displayed (only revoked and reissued).')\n }\n}\n\nexport async function revokeScheduleLink(identity: string): Promise<boolean> {\n const existingId = await getScheduleLinkKeyId(identity)\n if (!existingId) return false\n await revokeKeyRow(existingId)\n await setScheduleLinkKeyId(identity, null)\n return true\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AACpC,SAAS,aAAa,cAAc,kBAAkB;AACtD,SAAS,iBAAiB;AAE1B,eAAe,qBAAqB,UAA0C;AAC5E,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AACA,SAAO,IAAI,KAAK,CAAC,GAAG,wBAAwB;AAC9C;AAEA,eAAe,qBAAqB,UAAkB,OAAqC;AACzF,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA,IAGA,CAAC,UAAU,KAAK;AAAA,EAClB;AACF;AAEA,eAAsB,8BAA8B,UAAmC;AACrF,QAAM,aAAa,MAAM,qBAAqB,QAAQ;AACtD,MAAI,YAAY;AACd,UAAM,WAAW,MAAM,WAAW,UAAU;AAC5C,QAAI,YAAY,CAAC,SAAS,SAAS;AACjC,YAAM,IAAI,iCAAiC;AAAA,IAC7C;AAAA,EACF;AACA,QAAM,SAAS,MAAM,YAAY,EAAE,UAAU,QAAQ,CAAC,GAAG,OAAO,UAAU,GAAG,MAAM,QAAQ,WAAW,KAAK,CAAC;AAC5G,QAAM,qBAAqB,UAAU,OAAO,KAAK;AACjD,SAAO,OAAO;AAChB;AAEO,MAAM,yCAAyC,MAAM;AAAA,EAC1D,cAAc;AACZ,UAAM,kHAAkH;AAAA,EAC1H;AACF;AAEA,eAAsB,mBAAmB,UAAoC;AAC3E,QAAM,aAAa,MAAM,qBAAqB,QAAQ;AACtD,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,aAAa,UAAU;AAC7B,QAAM,qBAAqB,UAAU,IAAI;AACzC,SAAO;AACT;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/scheduled-actions.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { ensureSchema, query } from './pool'\nimport type { ScheduleCadence } from '../lib/schedule-cadence'\n\nexport type ScheduledActionStatus = 'active' | 'paused' | 'deleted' | 'completed'\n\nexport interface ScheduledActionRecord {\n id: string\n identity: string\n description: string\n vault: string\n cadence: ScheduleCadence\n timeOfDay: string | null\n timezone: string\n status: ScheduledActionStatus\n nextRunAt: string\n lastRunAt: string | null\n lastRunStatus: string | null\n createdAt: string\n updatedAt: string\n system: string | null\n}\n\ninterface RawRow {\n id: string\n identity: string\n description: string\n vault: string\n cadence: string\n time_of_day: string | null\n timezone: string\n status: string\n next_run_at: string\n last_run_at: string | null\n last_run_status: string | null\n created_at: string\n updated_at: string\n system: string | null\n}\n\nfunction mapRow(r: RawRow): ScheduledActionRecord {\n return {\n id: r.id,\n identity: r.identity,\n description: r.description,\n vault: r.vault,\n cadence: r.cadence as ScheduleCadence,\n timeOfDay: r.time_of_day,\n timezone: r.timezone,\n status: r.status as ScheduledActionStatus,\n nextRunAt: r.next_run_at,\n lastRunAt: r.last_run_at,\n lastRunStatus: r.last_run_status,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n system: r.system,\n }\n}\n\nconst COLUMNS = 'id, identity, description, vault, cadence, time_of_day, timezone, status, next_run_at, last_run_at, last_run_status, created_at, updated_at, system'\n\nexport async function createScheduledAction(args: {\n identity: string\n description: string\n vault: string\n cadence: ScheduleCadence\n timeOfDay?: string | null\n timezone?: string | null\n nextRunAt: string\n system?: string | null\n}): Promise<ScheduledActionRecord> {\n await ensureSchema()\n const id = randomUUID()\n const res = await query<RawRow>(\n `INSERT INTO mem_scheduled_actions (id, identity, description, vault, cadence, time_of_day, timezone, status, next_run_at, system)\n VALUES ($1, $2, $3, $4, $5, $6, $7, 'active', $8, $9)\n RETURNING ${COLUMNS}`,\n [id, args.identity, args.description, args.vault, args.cadence, args.timeOfDay ?? null, args.timezone ?? 'UTC', args.nextRunAt, args.system ?? null],\n )\n return mapRow(res.rows[0])\n}\n\nexport async function getSystemScheduledAction(identity: string, system: string): Promise<ScheduledActionRecord | null> {\n await ensureSchema()\n const res = await query<RawRow>(\n `SELECT ${COLUMNS} FROM mem_scheduled_actions WHERE identity = $1 AND system = $2 AND status != 'deleted' LIMIT 1`,\n [identity, system],\n )\n return res.rows[0] ? mapRow(res.rows[0]) : null\n}\n\nexport async function getScheduledAction(id: string, identity: string): Promise<ScheduledActionRecord | null> {\n await ensureSchema()\n const res = await query<RawRow>(\n `SELECT ${COLUMNS} FROM mem_scheduled_actions WHERE id = $1 AND identity = $2 AND status != 'deleted' LIMIT 1`,\n [id, identity],\n )\n return res.rows[0] ? mapRow(res.rows[0]) : null\n}\n\nexport async function listScheduledActionsForIdentity(identity: string): Promise<ScheduledActionRecord[]> {\n await ensureSchema()\n const res = await query<RawRow>(\n `SELECT ${COLUMNS} FROM mem_scheduled_actions WHERE identity = $1 AND status != 'deleted' ORDER BY created_at DESC`,\n [identity],\n )\n return res.rows.map(mapRow)\n}\n\nexport async function patchScheduledAction(args: {\n id: string\n identity: string\n status?: 'active' | 'paused'\n description?: string\n cadence?: ScheduleCadence\n timeOfDay?: string | null\n timezone?: string | null\n nextRunAt?: string\n}): Promise<ScheduledActionRecord | null> {\n await ensureSchema()\n const sets: string[] = []\n const params: unknown[] = []\n if (args.status !== undefined) { params.push(args.status); sets.push(`status = $${params.length}`) }\n if (args.description !== undefined) { params.push(args.description); sets.push(`description = $${params.length}`) }\n if (args.cadence !== undefined) { params.push(args.cadence); sets.push(`cadence = $${params.length}`) }\n if (args.timeOfDay !== undefined) { params.push(args.timeOfDay); sets.push(`time_of_day = $${params.length}`) }\n if (args.timezone !== undefined) { params.push(args.timezone); sets.push(`timezone = $${params.length}`) }\n if (args.nextRunAt !== undefined) { params.push(args.nextRunAt); sets.push(`next_run_at = $${params.length}`) }\n if (sets.length === 0) return getScheduledAction(args.id, args.identity)\n sets.push('updated_at = now()')\n params.push(args.id, args.identity)\n const res = await query<RawRow>(\n `UPDATE mem_scheduled_actions SET ${sets.join(', ')}\n WHERE id = $${params.length - 1} AND identity = $${params.length} AND status != 'deleted'\n RETURNING ${COLUMNS}`,\n params,\n )\n return res.rows[0] ? mapRow(res.rows[0]) : null\n}\n\nexport type DeleteScheduledActionResult =\n | { deleted: true }\n | { deleted: false; reason: 'not_found' | 'system' }\n\nexport async function deleteScheduledAction(id: string, identity: string): Promise<DeleteScheduledActionResult> {\n await ensureSchema()\n const existing = await getScheduledAction(id, identity)\n if (!existing) return { deleted: false, reason: 'not_found' }\n if (existing.system) return { deleted: false, reason: 'system' }\n const res = await query(\n `UPDATE mem_scheduled_actions SET status = 'deleted', updated_at = now() WHERE id = $1 AND identity = $2 AND status != 'deleted'`,\n [id, identity],\n )\n return (res.rowCount ?? 0) > 0 ? { deleted: true } : { deleted: false, reason: 'not_found' }\n}\n\nexport async function listDueScheduledActions(nowIso: string, limit: number): Promise<ScheduledActionRecord[]> {\n await ensureSchema()\n const res = await query<RawRow>(\n `SELECT ${COLUMNS} FROM mem_scheduled_actions WHERE status = 'active' AND next_run_at <= $1 ORDER BY next_run_at ASC LIMIT $2`,\n [nowIso, limit],\n )\n return res.rows.map(mapRow)\n}\n\nexport async function markScheduledActionRan(id: string, args: { lastRunAt: string; nextRunAt: string; lastRunStatus: string; status?: ScheduledActionStatus }): Promise<void> {\n await ensureSchema()\n if (args.status !== undefined) {\n await query(\n `UPDATE mem_scheduled_actions SET last_run_at = $2, next_run_at = $3, last_run_status = $4, status = $5, updated_at = now() WHERE id = $1`,\n [id, args.lastRunAt, args.nextRunAt, args.lastRunStatus, args.status],\n )\n return\n }\n await query(\n `UPDATE mem_scheduled_actions SET last_run_at = $2, next_run_at = $3, last_run_status = $4, updated_at = now() WHERE id = $1`,\n [id, args.lastRunAt, args.nextRunAt, args.lastRunStatus],\n )\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/db/scheduled-actions.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { ensureSchema, query } from './pool.js'\nimport type { ScheduleCadence } from '../lib/schedule-cadence.js'\n\nexport type ScheduledActionStatus = 'active' | 'paused' | 'deleted' | 'completed'\n\nexport interface ScheduledActionRecord {\n id: string\n identity: string\n description: string\n vault: string\n cadence: ScheduleCadence\n timeOfDay: string | null\n timezone: string\n status: ScheduledActionStatus\n nextRunAt: string\n lastRunAt: string | null\n lastRunStatus: string | null\n createdAt: string\n updatedAt: string\n system: string | null\n}\n\ninterface RawRow {\n id: string\n identity: string\n description: string\n vault: string\n cadence: string\n time_of_day: string | null\n timezone: string\n status: string\n next_run_at: string\n last_run_at: string | null\n last_run_status: string | null\n created_at: string\n updated_at: string\n system: string | null\n}\n\nfunction mapRow(r: RawRow): ScheduledActionRecord {\n return {\n id: r.id,\n identity: r.identity,\n description: r.description,\n vault: r.vault,\n cadence: r.cadence as ScheduleCadence,\n timeOfDay: r.time_of_day,\n timezone: r.timezone,\n status: r.status as ScheduledActionStatus,\n nextRunAt: r.next_run_at,\n lastRunAt: r.last_run_at,\n lastRunStatus: r.last_run_status,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n system: r.system,\n }\n}\n\nconst COLUMNS = 'id, identity, description, vault, cadence, time_of_day, timezone, status, next_run_at, last_run_at, last_run_status, created_at, updated_at, system'\n\nexport async function createScheduledAction(args: {\n identity: string\n description: string\n vault: string\n cadence: ScheduleCadence\n timeOfDay?: string | null\n timezone?: string | null\n nextRunAt: string\n system?: string | null\n}): Promise<ScheduledActionRecord> {\n await ensureSchema()\n const id = randomUUID()\n const res = await query<RawRow>(\n `INSERT INTO mem_scheduled_actions (id, identity, description, vault, cadence, time_of_day, timezone, status, next_run_at, system)\n VALUES ($1, $2, $3, $4, $5, $6, $7, 'active', $8, $9)\n RETURNING ${COLUMNS}`,\n [id, args.identity, args.description, args.vault, args.cadence, args.timeOfDay ?? null, args.timezone ?? 'UTC', args.nextRunAt, args.system ?? null],\n )\n return mapRow(res.rows[0])\n}\n\nexport async function getSystemScheduledAction(identity: string, system: string): Promise<ScheduledActionRecord | null> {\n await ensureSchema()\n const res = await query<RawRow>(\n `SELECT ${COLUMNS} FROM mem_scheduled_actions WHERE identity = $1 AND system = $2 AND status != 'deleted' LIMIT 1`,\n [identity, system],\n )\n return res.rows[0] ? mapRow(res.rows[0]) : null\n}\n\nexport async function getScheduledAction(id: string, identity: string): Promise<ScheduledActionRecord | null> {\n await ensureSchema()\n const res = await query<RawRow>(\n `SELECT ${COLUMNS} FROM mem_scheduled_actions WHERE id = $1 AND identity = $2 AND status != 'deleted' LIMIT 1`,\n [id, identity],\n )\n return res.rows[0] ? mapRow(res.rows[0]) : null\n}\n\nexport async function listScheduledActionsForIdentity(identity: string): Promise<ScheduledActionRecord[]> {\n await ensureSchema()\n const res = await query<RawRow>(\n `SELECT ${COLUMNS} FROM mem_scheduled_actions WHERE identity = $1 AND status != 'deleted' ORDER BY created_at DESC`,\n [identity],\n )\n return res.rows.map(mapRow)\n}\n\nexport async function patchScheduledAction(args: {\n id: string\n identity: string\n status?: 'active' | 'paused'\n description?: string\n cadence?: ScheduleCadence\n timeOfDay?: string | null\n timezone?: string | null\n nextRunAt?: string\n}): Promise<ScheduledActionRecord | null> {\n await ensureSchema()\n const sets: string[] = []\n const params: unknown[] = []\n if (args.status !== undefined) { params.push(args.status); sets.push(`status = $${params.length}`) }\n if (args.description !== undefined) { params.push(args.description); sets.push(`description = $${params.length}`) }\n if (args.cadence !== undefined) { params.push(args.cadence); sets.push(`cadence = $${params.length}`) }\n if (args.timeOfDay !== undefined) { params.push(args.timeOfDay); sets.push(`time_of_day = $${params.length}`) }\n if (args.timezone !== undefined) { params.push(args.timezone); sets.push(`timezone = $${params.length}`) }\n if (args.nextRunAt !== undefined) { params.push(args.nextRunAt); sets.push(`next_run_at = $${params.length}`) }\n if (sets.length === 0) return getScheduledAction(args.id, args.identity)\n sets.push('updated_at = now()')\n params.push(args.id, args.identity)\n const res = await query<RawRow>(\n `UPDATE mem_scheduled_actions SET ${sets.join(', ')}\n WHERE id = $${params.length - 1} AND identity = $${params.length} AND status != 'deleted'\n RETURNING ${COLUMNS}`,\n params,\n )\n return res.rows[0] ? mapRow(res.rows[0]) : null\n}\n\nexport type DeleteScheduledActionResult =\n | { deleted: true }\n | { deleted: false; reason: 'not_found' | 'system' }\n\nexport async function deleteScheduledAction(id: string, identity: string): Promise<DeleteScheduledActionResult> {\n await ensureSchema()\n const existing = await getScheduledAction(id, identity)\n if (!existing) return { deleted: false, reason: 'not_found' }\n if (existing.system) return { deleted: false, reason: 'system' }\n const res = await query(\n `UPDATE mem_scheduled_actions SET status = 'deleted', updated_at = now() WHERE id = $1 AND identity = $2 AND status != 'deleted'`,\n [id, identity],\n )\n return (res.rowCount ?? 0) > 0 ? { deleted: true } : { deleted: false, reason: 'not_found' }\n}\n\nexport async function listDueScheduledActions(nowIso: string, limit: number): Promise<ScheduledActionRecord[]> {\n await ensureSchema()\n const res = await query<RawRow>(\n `SELECT ${COLUMNS} FROM mem_scheduled_actions WHERE status = 'active' AND next_run_at <= $1 ORDER BY next_run_at ASC LIMIT $2`,\n [nowIso, limit],\n )\n return res.rows.map(mapRow)\n}\n\nexport async function markScheduledActionRan(id: string, args: { lastRunAt: string; nextRunAt: string; lastRunStatus: string; status?: ScheduledActionStatus }): Promise<void> {\n await ensureSchema()\n if (args.status !== undefined) {\n await query(\n `UPDATE mem_scheduled_actions SET last_run_at = $2, next_run_at = $3, last_run_status = $4, status = $5, updated_at = now() WHERE id = $1`,\n [id, args.lastRunAt, args.nextRunAt, args.lastRunStatus, args.status],\n )\n return\n }\n await query(\n `UPDATE mem_scheduled_actions SET last_run_at = $2, next_run_at = $3, last_run_status = $4, updated_at = now() WHERE id = $1`,\n [id, args.lastRunAt, args.nextRunAt, args.lastRunStatus],\n )\n}\n"],"mappings":"AAAA,SAAS,kBAAkB;AAC3B,SAAS,cAAc,aAAa;AAuCpC,SAAS,OAAO,GAAkC;AAChD,SAAO;AAAA,IACL,IAAI,EAAE;AAAA,IACN,UAAU,EAAE;AAAA,IACZ,aAAa,EAAE;AAAA,IACf,OAAO,EAAE;AAAA,IACT,SAAS,EAAE;AAAA,IACX,WAAW,EAAE;AAAA,IACb,UAAU,EAAE;AAAA,IACZ,QAAQ,EAAE;AAAA,IACV,WAAW,EAAE;AAAA,IACb,WAAW,EAAE;AAAA,IACb,eAAe,EAAE;AAAA,IACjB,WAAW,EAAE;AAAA,IACb,WAAW,EAAE;AAAA,IACb,QAAQ,EAAE;AAAA,EACZ;AACF;AAEA,MAAM,UAAU;AAEhB,eAAsB,sBAAsB,MAST;AACjC,QAAM,aAAa;AACnB,QAAM,KAAK,WAAW;AACtB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA;AAAA,iBAEa,OAAO;AAAA,IACpB,CAAC,IAAI,KAAK,UAAU,KAAK,aAAa,KAAK,OAAO,KAAK,SAAS,KAAK,aAAa,MAAM,KAAK,YAAY,OAAO,KAAK,WAAW,KAAK,UAAU,IAAI;AAAA,EACrJ;AACA,SAAO,OAAO,IAAI,KAAK,CAAC,CAAC;AAC3B;AAEA,eAAsB,yBAAyB,UAAkB,QAAuD;AACtH,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB,UAAU,OAAO;AAAA,IACjB,CAAC,UAAU,MAAM;AAAA,EACnB;AACA,SAAO,IAAI,KAAK,CAAC,IAAI,OAAO,IAAI,KAAK,CAAC,CAAC,IAAI;AAC7C;AAEA,eAAsB,mBAAmB,IAAY,UAAyD;AAC5G,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB,UAAU,OAAO;AAAA,IACjB,CAAC,IAAI,QAAQ;AAAA,EACf;AACA,SAAO,IAAI,KAAK,CAAC,IAAI,OAAO,IAAI,KAAK,CAAC,CAAC,IAAI;AAC7C;AAEA,eAAsB,gCAAgC,UAAoD;AACxG,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB,UAAU,OAAO;AAAA,IACjB,CAAC,QAAQ;AAAA,EACX;AACA,SAAO,IAAI,KAAK,IAAI,MAAM;AAC5B;AAEA,eAAsB,qBAAqB,MASD;AACxC,QAAM,aAAa;AACnB,QAAM,OAAiB,CAAC;AACxB,QAAM,SAAoB,CAAC;AAC3B,MAAI,KAAK,WAAW,QAAW;AAAE,WAAO,KAAK,KAAK,MAAM;AAAG,SAAK,KAAK,aAAa,OAAO,MAAM,EAAE;AAAA,EAAE;AACnG,MAAI,KAAK,gBAAgB,QAAW;AAAE,WAAO,KAAK,KAAK,WAAW;AAAG,SAAK,KAAK,kBAAkB,OAAO,MAAM,EAAE;AAAA,EAAE;AAClH,MAAI,KAAK,YAAY,QAAW;AAAE,WAAO,KAAK,KAAK,OAAO;AAAG,SAAK,KAAK,cAAc,OAAO,MAAM,EAAE;AAAA,EAAE;AACtG,MAAI,KAAK,cAAc,QAAW;AAAE,WAAO,KAAK,KAAK,SAAS;AAAG,SAAK,KAAK,kBAAkB,OAAO,MAAM,EAAE;AAAA,EAAE;AAC9G,MAAI,KAAK,aAAa,QAAW;AAAE,WAAO,KAAK,KAAK,QAAQ;AAAG,SAAK,KAAK,eAAe,OAAO,MAAM,EAAE;AAAA,EAAE;AACzG,MAAI,KAAK,cAAc,QAAW;AAAE,WAAO,KAAK,KAAK,SAAS;AAAG,SAAK,KAAK,kBAAkB,OAAO,MAAM,EAAE;AAAA,EAAE;AAC9G,MAAI,KAAK,WAAW,EAAG,QAAO,mBAAmB,KAAK,IAAI,KAAK,QAAQ;AACvE,OAAK,KAAK,oBAAoB;AAC9B,SAAO,KAAK,KAAK,IAAI,KAAK,QAAQ;AAClC,QAAM,MAAM,MAAM;AAAA,IAChB,oCAAoC,KAAK,KAAK,IAAI,CAAC;AAAA,mBACpC,OAAO,SAAS,CAAC,oBAAoB,OAAO,MAAM;AAAA,iBACpD,OAAO;AAAA,IACpB;AAAA,EACF;AACA,SAAO,IAAI,KAAK,CAAC,IAAI,OAAO,IAAI,KAAK,CAAC,CAAC,IAAI;AAC7C;AAMA,eAAsB,sBAAsB,IAAY,UAAwD;AAC9G,QAAM,aAAa;AACnB,QAAM,WAAW,MAAM,mBAAmB,IAAI,QAAQ;AACtD,MAAI,CAAC,SAAU,QAAO,EAAE,SAAS,OAAO,QAAQ,YAAY;AAC5D,MAAI,SAAS,OAAQ,QAAO,EAAE,SAAS,OAAO,QAAQ,SAAS;AAC/D,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,IAAI,QAAQ;AAAA,EACf;AACA,UAAQ,IAAI,YAAY,KAAK,IAAI,EAAE,SAAS,KAAK,IAAI,EAAE,SAAS,OAAO,QAAQ,YAAY;AAC7F;AAEA,eAAsB,wBAAwB,QAAgB,OAAiD;AAC7G,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB,UAAU,OAAO;AAAA,IACjB,CAAC,QAAQ,KAAK;AAAA,EAChB;AACA,SAAO,IAAI,KAAK,IAAI,MAAM;AAC5B;AAEA,eAAsB,uBAAuB,IAAY,MAAsH;AAC7K,QAAM,aAAa;AACnB,MAAI,KAAK,WAAW,QAAW;AAC7B,UAAM;AAAA,MACJ;AAAA,MACA,CAAC,IAAI,KAAK,WAAW,KAAK,WAAW,KAAK,eAAe,KAAK,MAAM;AAAA,IACtE;AACA;AAAA,EACF;AACA,QAAM;AAAA,IACJ;AAAA,IACA,CAAC,IAAI,KAAK,WAAW,KAAK,WAAW,KAAK,aAAa;AAAA,EACzD;AACF;","names":[]}
|
package/dist/db/secure-notes.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ensureSchema, query } from "./pool";
|
|
2
|
-
import { encryptSecret, decryptSecret } from "../lib/secret-crypto";
|
|
1
|
+
import { ensureSchema, query } from "./pool.js";
|
|
2
|
+
import { encryptSecret, decryptSecret } from "../lib/secret-crypto.js";
|
|
3
3
|
const COLUMNS = "id, vault, path, title, content_enc, created_at, updated_at, updated_by";
|
|
4
4
|
function secureNoteId(vault, path) {
|
|
5
5
|
return `secure::${vault}::${path}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/secure-notes.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\nimport { encryptSecret, decryptSecret } from '../lib/secret-crypto'\n\nexport interface SecureNoteRecord {\n id: string\n vault: string\n path: string\n title: string\n content: string\n createdAt: string\n updatedAt: string\n updatedBy: string | null\n}\n\ninterface RawSecureNoteRow {\n id: string\n vault: string\n path: string\n title: string\n content_enc: string\n created_at: string\n updated_at: string\n updated_by: string | null\n}\n\nconst COLUMNS = 'id, vault, path, title, content_enc, created_at, updated_at, updated_by'\n\nfunction secureNoteId(vault: string, path: string): string {\n return `secure::${vault}::${path}`\n}\n\nfunction mapRow(r: RawSecureNoteRow): SecureNoteRecord {\n return {\n id: r.id,\n vault: r.vault,\n path: r.path,\n title: r.title,\n content: decryptSecret(r.content_enc),\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n updatedBy: r.updated_by,\n }\n}\n\nexport async function putSecureNote(args: { vault: string; path: string; title?: string; content: string; updatedBy?: string }): Promise<SecureNoteRecord> {\n await ensureSchema()\n const id = secureNoteId(args.vault, args.path)\n const res = await query<RawSecureNoteRow>(\n `INSERT INTO mem_secure_notes (id, vault, path, title, content_enc, updated_at, updated_by)\n VALUES ($1, $2, $3, $4, $5, now(), $6)\n ON CONFLICT (vault, path) DO UPDATE SET\n title = EXCLUDED.title, content_enc = EXCLUDED.content_enc, updated_at = now(), updated_by = EXCLUDED.updated_by\n RETURNING ${COLUMNS}`,\n [id, args.vault, args.path, args.title ?? args.path, encryptSecret(args.content), args.updatedBy ?? null],\n )\n return mapRow(res.rows[0])\n}\n\nexport async function getSecureNote(vault: string, path: string): Promise<SecureNoteRecord | null> {\n await ensureSchema()\n const res = await query<RawSecureNoteRow>(\n `SELECT ${COLUMNS} FROM mem_secure_notes WHERE vault = $1 AND path = $2 LIMIT 1`,\n [vault, path],\n )\n return res.rows[0] ? mapRow(res.rows[0]) : null\n}\n\nexport async function listSecureNotes(vault: string): Promise<Array<{ path: string; title: string; updatedAt: string }>> {\n await ensureSchema()\n const res = await query<{ path: string; title: string; updated_at: string }>(\n `SELECT path, title, updated_at FROM mem_secure_notes WHERE vault = $1 ORDER BY updated_at DESC`,\n [vault],\n )\n return res.rows.map(r => ({ path: r.path, title: r.title, updatedAt: r.updated_at }))\n}\n\nexport async function deleteSecureNote(vault: string, path: string): Promise<boolean> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_secure_notes WHERE vault = $1 AND path = $2`, [vault, path])\n return (res.rowCount ?? 0) > 0\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AACpC,SAAS,eAAe,qBAAqB;AAwB7C,MAAM,UAAU;AAEhB,SAAS,aAAa,OAAe,MAAsB;AACzD,SAAO,WAAW,KAAK,KAAK,IAAI;AAClC;AAEA,SAAS,OAAO,GAAuC;AACrD,SAAO;AAAA,IACL,IAAI,EAAE;AAAA,IACN,OAAO,EAAE;AAAA,IACT,MAAM,EAAE;AAAA,IACR,OAAO,EAAE;AAAA,IACT,SAAS,cAAc,EAAE,WAAW;AAAA,IACpC,WAAW,EAAE;AAAA,IACb,WAAW,EAAE;AAAA,IACb,WAAW,EAAE;AAAA,EACf;AACF;AAEA,eAAsB,cAAc,MAAuH;AACzJ,QAAM,aAAa;AACnB,QAAM,KAAK,aAAa,KAAK,OAAO,KAAK,IAAI;AAC7C,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA;AAAA;AAAA;AAAA,iBAIa,OAAO;AAAA,IACpB,CAAC,IAAI,KAAK,OAAO,KAAK,MAAM,KAAK,SAAS,KAAK,MAAM,cAAc,KAAK,OAAO,GAAG,KAAK,aAAa,IAAI;AAAA,EAC1G;AACA,SAAO,OAAO,IAAI,KAAK,CAAC,CAAC;AAC3B;AAEA,eAAsB,cAAc,OAAe,MAAgD;AACjG,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB,UAAU,OAAO;AAAA,IACjB,CAAC,OAAO,IAAI;AAAA,EACd;AACA,SAAO,IAAI,KAAK,CAAC,IAAI,OAAO,IAAI,KAAK,CAAC,CAAC,IAAI;AAC7C;AAEA,eAAsB,gBAAgB,OAAmF;AACvH,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,KAAK;AAAA,EACR;AACA,SAAO,IAAI,KAAK,IAAI,QAAM,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,OAAO,WAAW,EAAE,WAAW,EAAE;AACtF;AAEA,eAAsB,iBAAiB,OAAe,MAAgC;AACpF,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM,MAAM,+DAA+D,CAAC,OAAO,IAAI,CAAC;AACpG,UAAQ,IAAI,YAAY,KAAK;AAC/B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/db/secure-notes.ts"],"sourcesContent":["import { ensureSchema, query } from './pool.js'\nimport { encryptSecret, decryptSecret } from '../lib/secret-crypto.js'\n\nexport interface SecureNoteRecord {\n id: string\n vault: string\n path: string\n title: string\n content: string\n createdAt: string\n updatedAt: string\n updatedBy: string | null\n}\n\ninterface RawSecureNoteRow {\n id: string\n vault: string\n path: string\n title: string\n content_enc: string\n created_at: string\n updated_at: string\n updated_by: string | null\n}\n\nconst COLUMNS = 'id, vault, path, title, content_enc, created_at, updated_at, updated_by'\n\nfunction secureNoteId(vault: string, path: string): string {\n return `secure::${vault}::${path}`\n}\n\nfunction mapRow(r: RawSecureNoteRow): SecureNoteRecord {\n return {\n id: r.id,\n vault: r.vault,\n path: r.path,\n title: r.title,\n content: decryptSecret(r.content_enc),\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n updatedBy: r.updated_by,\n }\n}\n\nexport async function putSecureNote(args: { vault: string; path: string; title?: string; content: string; updatedBy?: string }): Promise<SecureNoteRecord> {\n await ensureSchema()\n const id = secureNoteId(args.vault, args.path)\n const res = await query<RawSecureNoteRow>(\n `INSERT INTO mem_secure_notes (id, vault, path, title, content_enc, updated_at, updated_by)\n VALUES ($1, $2, $3, $4, $5, now(), $6)\n ON CONFLICT (vault, path) DO UPDATE SET\n title = EXCLUDED.title, content_enc = EXCLUDED.content_enc, updated_at = now(), updated_by = EXCLUDED.updated_by\n RETURNING ${COLUMNS}`,\n [id, args.vault, args.path, args.title ?? args.path, encryptSecret(args.content), args.updatedBy ?? null],\n )\n return mapRow(res.rows[0])\n}\n\nexport async function getSecureNote(vault: string, path: string): Promise<SecureNoteRecord | null> {\n await ensureSchema()\n const res = await query<RawSecureNoteRow>(\n `SELECT ${COLUMNS} FROM mem_secure_notes WHERE vault = $1 AND path = $2 LIMIT 1`,\n [vault, path],\n )\n return res.rows[0] ? mapRow(res.rows[0]) : null\n}\n\nexport async function listSecureNotes(vault: string): Promise<Array<{ path: string; title: string; updatedAt: string }>> {\n await ensureSchema()\n const res = await query<{ path: string; title: string; updated_at: string }>(\n `SELECT path, title, updated_at FROM mem_secure_notes WHERE vault = $1 ORDER BY updated_at DESC`,\n [vault],\n )\n return res.rows.map(r => ({ path: r.path, title: r.title, updatedAt: r.updated_at }))\n}\n\nexport async function deleteSecureNote(vault: string, path: string): Promise<boolean> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_secure_notes WHERE vault = $1 AND path = $2`, [vault, path])\n return (res.rowCount ?? 0) > 0\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AACpC,SAAS,eAAe,qBAAqB;AAwB7C,MAAM,UAAU;AAEhB,SAAS,aAAa,OAAe,MAAsB;AACzD,SAAO,WAAW,KAAK,KAAK,IAAI;AAClC;AAEA,SAAS,OAAO,GAAuC;AACrD,SAAO;AAAA,IACL,IAAI,EAAE;AAAA,IACN,OAAO,EAAE;AAAA,IACT,MAAM,EAAE;AAAA,IACR,OAAO,EAAE;AAAA,IACT,SAAS,cAAc,EAAE,WAAW;AAAA,IACpC,WAAW,EAAE;AAAA,IACb,WAAW,EAAE;AAAA,IACb,WAAW,EAAE;AAAA,EACf;AACF;AAEA,eAAsB,cAAc,MAAuH;AACzJ,QAAM,aAAa;AACnB,QAAM,KAAK,aAAa,KAAK,OAAO,KAAK,IAAI;AAC7C,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA;AAAA;AAAA;AAAA,iBAIa,OAAO;AAAA,IACpB,CAAC,IAAI,KAAK,OAAO,KAAK,MAAM,KAAK,SAAS,KAAK,MAAM,cAAc,KAAK,OAAO,GAAG,KAAK,aAAa,IAAI;AAAA,EAC1G;AACA,SAAO,OAAO,IAAI,KAAK,CAAC,CAAC;AAC3B;AAEA,eAAsB,cAAc,OAAe,MAAgD;AACjG,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB,UAAU,OAAO;AAAA,IACjB,CAAC,OAAO,IAAI;AAAA,EACd;AACA,SAAO,IAAI,KAAK,CAAC,IAAI,OAAO,IAAI,KAAK,CAAC,CAAC,IAAI;AAC7C;AAEA,eAAsB,gBAAgB,OAAmF;AACvH,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,KAAK;AAAA,EACR;AACA,SAAO,IAAI,KAAK,IAAI,QAAM,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,OAAO,WAAW,EAAE,WAAW,EAAE;AACtF;AAEA,eAAsB,iBAAiB,OAAe,MAAgC;AACpF,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM,MAAM,+DAA+D,CAAC,OAAO,IAAI,CAAC;AACpG,UAAQ,IAAI,YAAY,KAAK;AAC/B;","names":[]}
|
package/dist/db/shares.js
CHANGED