@north-light/cli 0.3.579 → 0.3.581
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/api/client.d.ts +6 -1
- package/dist/api/client.js +3 -3
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/execution-start.d.ts +14 -0
- package/dist/api/dto/health.d.ts +20 -0
- package/dist/api/dto/imports.d.ts +142 -0
- package/dist/api/dto/imports.js +0 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -1
- package/dist/api/routes.d.ts +2 -0
- package/dist/api/routes.js +1 -1
- package/dist/builtin-memory/internal/storage-tiers.md +2 -2
- package/dist/clients/attach/session/connection.d.ts +3 -1
- package/dist/clients/attach/session/connection.js +1 -1
- package/dist/clients/attach/viewer.js +510 -510
- package/dist/commands/api-client.d.ts +7 -4
- package/dist/commands/api-client.js +3 -3
- package/dist/commands/sys/daemon.js +1 -1
- package/dist/commands/sys/import/resume.d.ts +1 -0
- package/dist/commands/sys/import/resume.js +1 -0
- package/dist/commands/sys/import/start.d.ts +5 -0
- package/dist/commands/sys/import/start.js +4 -0
- package/dist/commands/sys/join.d.ts +2 -0
- package/dist/commands/sys/join.js +1 -1
- package/dist/commands/sys/machine/rotate.d.ts +1 -0
- package/dist/commands/sys/machine/rotate.js +1 -0
- package/dist/commands/sys/signin.d.ts +7 -0
- package/dist/commands/sys/signin.js +2 -0
- package/dist/commands/sys.js +1 -1
- package/dist/core/blobs/r2.d.ts +4 -0
- package/dist/core/blobs/r2.js +7 -4
- package/dist/core/canvas/daemon-owner.d.ts +1 -1
- package/dist/core/canvas/executions.d.ts +1 -1
- package/dist/core/canvas/export-sqlite.js +1 -1
- package/dist/core/canvas/node-tokens.d.ts +2 -0
- package/dist/core/canvas/node-tokens.js +3 -3
- package/dist/core/canvas/schema-pg/009-bodies.sql +10 -0
- package/dist/core/canvas/schema-version.d.ts +1 -1
- package/dist/core/canvas/schema-version.js +1 -1
- package/dist/core/directory/person-credential.d.ts +36 -0
- package/dist/core/directory/person-credential.js +1 -0
- package/dist/core/feed/feed.js +3 -3
- package/dist/core/feed/inbox.js +1 -1
- package/dist/core/feed/reports.js +2 -2
- package/dist/core/graph/access.d.ts +1 -1
- package/dist/core/graph/bodies-boot-copy.d.ts +14 -0
- package/dist/core/graph/bodies-boot-copy.js +1 -0
- package/dist/core/graph/bodies.d.ts +13 -6
- package/dist/core/graph/bodies.js +1 -1
- package/dist/core/graph/deletion.d.ts +4 -1
- package/dist/core/graph/deletion.js +3 -3
- package/dist/core/graph/documents.d.ts +4 -0
- package/dist/core/graph/documents.js +10 -10
- package/dist/core/graph/objects.js +11 -11
- package/dist/core/graph/package-docs.js +5 -5
- package/dist/core/graph/reader.js +2 -2
- package/dist/core/graph/repo-sync/exchange.js +2 -2
- package/dist/core/graph/repo-sync/ref-format.d.ts +0 -1
- package/dist/core/graph/repo-sync/ref-format.js +2 -2
- package/dist/core/import/bundle/blob-keys.d.ts +10 -0
- package/dist/core/import/bundle/blob-keys.js +1 -0
- package/dist/core/import/bundle/layout.d.ts +89 -0
- package/dist/core/import/bundle/layout.js +5 -0
- package/dist/core/import/export/archive.d.ts +11 -0
- package/dist/core/import/export/archive.js +1 -0
- package/dist/core/import/export/cloud.d.ts +21 -0
- package/dist/core/import/export/cloud.js +1 -0
- package/dist/core/import/export/dump.d.ts +34 -0
- package/dist/core/import/export/dump.js +3 -0
- package/dist/core/import/export/files.d.ts +22 -0
- package/dist/core/import/export/files.js +1 -0
- package/dist/core/import/export/inline.d.ts +3 -0
- package/dist/core/import/export/inline.js +1 -0
- package/dist/core/import/export/source.d.ts +2 -0
- package/dist/core/import/export/source.js +1 -0
- package/dist/core/import/export/stores.d.ts +9 -0
- package/dist/core/import/export/stores.js +1 -0
- package/dist/core/import/load/bind.d.ts +19 -0
- package/dist/core/import/load/bind.js +15 -0
- package/dist/core/import/load/blobs.d.ts +10 -0
- package/dist/core/import/load/blobs.js +1 -0
- package/dist/core/import/load/catalog.d.ts +32 -0
- package/dist/core/import/load/catalog.js +9 -0
- package/dist/core/import/load/copy.d.ts +26 -0
- package/dist/core/import/load/copy.js +6 -0
- package/dist/core/import/load/path-map.d.ts +7 -0
- package/dist/core/import/load/path-map.js +1 -0
- package/dist/core/import/load/prepare.d.ts +37 -0
- package/dist/core/import/load/prepare.js +10 -0
- package/dist/core/import/load/rewrite.d.ts +39 -0
- package/dist/core/import/load/rewrite.js +9 -0
- package/dist/core/import/load/rollback.d.ts +3 -0
- package/dist/core/import/load/rollback.js +6 -0
- package/dist/core/import/load/run.d.ts +22 -0
- package/dist/core/import/load/run.js +3 -0
- package/dist/core/import/load/stage.d.ts +15 -0
- package/dist/core/import/load/stage.js +1 -0
- package/dist/core/import/load/stores.d.ts +24 -0
- package/dist/core/import/load/stores.js +2 -0
- package/dist/core/import/load/verify.d.ts +4 -0
- package/dist/core/import/load/verify.js +11 -0
- package/dist/core/peer/receiver.js +1 -1
- package/dist/core/runtime/broker.js +1 -1
- package/dist/core/runtime/revive.d.ts +5 -2
- package/dist/core/runtime/revive.js +2 -2
- package/dist/core/runtime/roadmap.js +3 -3
- package/dist/core/runtime/runner/blaxel/runner.js +3 -3
- package/dist/core/runtime/runner/index.d.ts +1 -0
- package/dist/core/runtime/runner/index.js +1 -1
- package/dist/core/runtime/runner/machine/call.d.ts +8 -0
- package/dist/core/runtime/runner/machine/call.js +3 -0
- package/dist/core/runtime/runner/machine/environment.d.ts +1 -0
- package/dist/core/runtime/runner/machine/environment.js +1 -0
- package/dist/core/runtime/runner/machine/executor.d.ts +23 -0
- package/dist/core/runtime/runner/machine/executor.js +1 -0
- package/dist/core/runtime/runner/machine/machine-executor.test.d.ts +1 -0
- package/dist/core/runtime/runner/machine/machine-executor.test.js +4 -0
- package/dist/core/runtime/runner/machine/ops.d.ts +127 -0
- package/dist/core/runtime/runner/machine/ops.js +1 -0
- package/dist/core/runtime/runner/machine/runner.d.ts +12 -0
- package/dist/core/runtime/runner/machine/runner.js +1 -0
- package/dist/core/runtime/runner/types.d.ts +7 -1
- package/dist/core/runtime/warm-pool.d.ts +2 -0
- package/dist/core/runtime/warm-pool.js +1 -1
- package/dist/core/secrets/env-rows.d.ts +1 -0
- package/dist/core/secrets/env-rows.js +1 -1
- package/dist/core/storage/purge.d.ts +6 -0
- package/dist/core/storage/purge.js +4 -0
- package/dist/core/subscription-state.d.ts +1 -0
- package/dist/core/subscription-state.js +1 -1
- package/dist/core/substrate/delivery/corpus.d.ts +2 -0
- package/dist/core/substrate/delivery/corpus.js +3 -3
- package/dist/core/substrate/delivery/deliver.js +3 -3
- package/dist/core/substrate/delivery/render-boot.js +25 -25
- package/dist/core/substrate/delivery/render-event.d.ts +1 -1
- package/dist/core/substrate/delivery/render-event.js +4 -4
- package/dist/core/substrate/delivery/types.d.ts +2 -2
- package/dist/daemon/api/bridge.js +3 -3
- package/dist/daemon/api/document-listener.js +4 -4
- package/dist/daemon/api/handlers/attach.js +1 -1
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/api/handlers/daemon.js +1 -1
- package/dist/daemon/api/handlers/docs.js +1 -1
- package/dist/daemon/api/handlers/execution-start.js +2 -2
- package/dist/daemon/api/handlers/files.js +1 -1
- package/dist/daemon/api/handlers/imports.d.ts +2 -0
- package/dist/daemon/api/handlers/imports.js +1 -0
- package/dist/daemon/api/handlers/machine-credential.d.ts +2 -0
- package/dist/daemon/api/handlers/machine-credential.js +1 -0
- package/dist/daemon/api/handlers/memory.js +4 -4
- package/dist/daemon/api/handlers/profiles.js +1 -1
- package/dist/daemon/api/handlers/providers.js +1 -1
- package/dist/daemon/api/handlers/reports.js +4 -4
- package/dist/daemon/api/handlers/runs.js +1 -1
- package/dist/daemon/api/import-auth.d.ts +6 -0
- package/dist/daemon/api/import-auth.js +1 -0
- package/dist/daemon/api/operations.d.ts +2 -2
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/router.d.ts +7 -1
- package/dist/daemon/api/router.js +1 -1
- package/dist/daemon/api/server.js +1 -1
- package/dist/daemon/boot-migrations.js +1 -1
- package/dist/daemon/broker-signals/bus.d.ts +3 -2
- package/dist/daemon/broker-signals/bus.js +1 -1
- package/dist/daemon/cron/armed-context.d.ts +1 -0
- package/dist/daemon/cron/armed-context.js +1 -1
- package/dist/daemon/cron/closed.d.ts +31 -0
- package/dist/daemon/cron/closed.js +1 -0
- package/dist/daemon/cron/ended.js +2 -2
- package/dist/daemon/cron/lease-recovery.js +1 -1
- package/dist/daemon/cron/machine-exec.d.ts +5 -0
- package/dist/daemon/cron/machine-exec.js +1 -0
- package/dist/daemon/cron/machine-fire.d.ts +26 -0
- package/dist/daemon/cron/machine-fire.js +1 -0
- package/dist/daemon/cron/recurrence.d.ts +14 -0
- package/dist/daemon/cron/recurrence.js +1 -0
- package/dist/daemon/cron/run-token.d.ts +7 -0
- package/dist/daemon/cron/run-token.js +1 -0
- package/dist/daemon/cron-run.js +2 -2
- package/dist/daemon/crtrd-cli.js +1 -1
- package/dist/daemon/crtrd.d.ts +2 -2
- package/dist/daemon/crtrd.js +10 -10
- package/dist/daemon/drain.d.ts +25 -0
- package/dist/daemon/drain.js +9 -0
- package/dist/daemon/due-sources.d.ts +1 -1
- package/dist/daemon/due-sources.js +1 -1
- package/dist/daemon/due.js +8 -8
- package/dist/daemon/fleet.js +5 -5
- package/dist/daemon/import-dump-run.d.ts +1 -0
- package/dist/daemon/import-dump-run.js +5 -0
- package/dist/daemon/machine-daemon.d.ts +7 -0
- package/dist/daemon/machine-daemon.js +1 -0
- package/dist/daemon/machine-link/client.d.ts +29 -0
- package/dist/daemon/machine-link/client.js +1 -0
- package/dist/daemon/machine-link/config.d.ts +14 -0
- package/dist/daemon/machine-link/config.js +3 -0
- package/dist/daemon/machine-link/credential.d.ts +7 -0
- package/dist/daemon/machine-link/credential.js +2 -0
- package/dist/daemon/machine-link/forward.d.ts +16 -0
- package/dist/daemon/machine-link/forward.js +1 -0
- package/dist/daemon/machine-link/frames.d.ts +49 -0
- package/dist/daemon/machine-link/frames.js +1 -0
- package/dist/daemon/machine-link/holder.d.ts +73 -0
- package/dist/daemon/machine-link/holder.js +9 -0
- package/dist/daemon/manage.d.ts +19 -3
- package/dist/daemon/manage.js +3 -3
- package/dist/daemon/reconcilers/cron-lane.js +1 -1
- package/dist/daemon/reconcilers/delivery-lane.js +2 -2
- package/dist/daemon/reconcilers/import-lane.d.ts +9 -0
- package/dist/daemon/reconcilers/import-lane.js +1 -0
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/remote-wake.d.ts +3 -0
- package/dist/daemon/reconcilers/node-lifecycle/remote-wake.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
- package/dist/daemon/reconcilers/purge-due-work.d.ts +13 -0
- package/dist/daemon/reconcilers/purge-due-work.js +1 -0
- package/dist/daemon/reconcilers/row-claim.d.ts +1 -0
- package/dist/daemon/reconcilers/row-claim.js +2 -2
- package/dist/daemon/review/finish.js +1 -1
- package/dist/daemon/runs/observer.js +3 -3
- package/dist/migrations/004-canvas-documents/apply.d.ts +2 -4
- package/dist/migrations/004-canvas-documents/apply.js +1 -1
- package/dist/migrations/004-canvas-documents/index.js +2 -2
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
- package/scripts/sandbox-start.mjs +185 -2
package/dist/core/feed/inbox.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var c=Object.defineProperty;var i=(n,e)=>c(n,"name",{value:e,configurable:!0});import{openDb as u}from"../canvas/db.js";import{heads as m}from"../graph/documents.js";import{getById as l}from"../graph/objects.js";import{get as p}from"../graph/bodies.js";import{validateOperationId as y}from"../events/operation-id.js";import{formatInboxCard as h,machineOfEntries as b}from"../../shared/generated-context.js";import{inlineBody as g,renderEntryBody as I,reportRefNodeId as w}from"../../shared/inbox-entry-body.js";import{mapAsync as x}from"../async-array.js";const _=/^(?!0{32}$)[0-9a-f]{32}$/;function k(n){if(typeof n!="string"||!_.test(n))throw new TypeError("entry_id must be 32 lowercase non-zero hexadecimal characters");return n}i(k,"validateInboxEntryId");function z(n){const e=n.message;return{entry_id:k(e.messageId),operation_id:y(e.operationId),ts:e.createdAt,from:e.senderNodeId,...e.senderName===null?{}:{from_name:e.senderName},tier:n.urgency,kind:e.kind,...e.bodyRef===null?{}:{ref:e.bodyRef},label:e.label,...e.bodyJson===null?{}:{data:e.bodyJson},...e.disposition===null?{}:{disposition:e.disposition}}}i(z,"nodeMailEntry");async function B(n){const e=await u(),t=await l(e,n);if(t===null||t.type!=="document"||t.deleted_at!==null)return null;const d=(await m(e,t))[0];return d===void 0?null:await p(e,d.body_hash)}i(B,"reportDocumentBody");async function E(n){const e=n.data?.push?.object?.id,t=w(n.ref)!==void 0&&e!==void 0?await B(e):null;return I(n,t)}i(E,"entryBody");async function N(n){return{kind:n.kind,body:await E(n),...n.ref===void 0?{}:{ref:n.ref},...n.disposition===void 0?{}:{disposition:n.disposition}}}i(N,"cardEntry");async function C(n){if(n.length===0)return[];const e=new Map;for(const o of n){const r=o.from??"system";e.has(r)||e.set(r,[]),e.get(r).push(o)}const t=[],d=[];let s=-1;for(const[o,r]of e){if(o==="human"){for(const a of r){const f=g(a);t.push({kind:"human",text:f===""?a.label:f,entries:[a]})}continue}s<0&&(s=t.length,t.push({kind:"card",text:"",entries:[]})),d.push({id:o,...b(r),entries:await x(r,N)})}return s>=0&&(t[s]={kind:"card",text:h(d),entries:n.filter(o=>(o.from??"system")!=="human")}),t}i(C,"coalesce");export{C as coalesce,z as nodeMailEntry,B as reportDocumentBody};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var f=Object.defineProperty;var
|
|
2
|
-
ORDER BY created_at DESC, name DESC LIMIT 1`).get(n.id)}
|
|
1
|
+
var f=Object.defineProperty;var a=(t,e)=>f(t,"name",{value:e,configurable:!0});import{openDb as r}from"../canvas/db.js";import{findMany as u}from"../graph/bodies.js";import{heads as c}from"../graph/documents.js";import{getById as i}from"../graph/objects.js";async function h(t){const e=await r(),n=await i(e,t);return n===null||n.type!=="node"?!1:await e.prepare("SELECT 1 FROM objects WHERE owner = ? AND type = 'document' AND kind = 'report' AND deleted_at IS NULL LIMIT 1").get(n.id)!==void 0}a(h,"hasReportDocuments");async function p(t){const e=await r(),n=await i(e,t);if(!(n===null||n.type!=="node"))return await e.prepare(`SELECT * FROM objects WHERE owner = ? AND type = 'document' AND kind = 'report' AND deleted_at IS NULL
|
|
2
|
+
ORDER BY created_at DESC, name DESC LIMIT 1`).get(n.id)}a(p,"newestReport");async function D(t){const e=await p(t);return e===void 0?null:`${t}/${e.name}`}a(D,"newestReportName");async function N(t){const e=await r(),n=await p(t);if(n===void 0)return"";const o=(await c(e,n))[0];return o===void 0?"":((await u(e,[o.body_hash])).get(o.body_hash)??"").trim()}a(N,"newestReportBody");async function b(t){const e=t.indexOf("/");if(e<=0||!t.startsWith("reports/",e+1))return null;const n=await r(),o=await i(n,t.slice(0,e));if(o===null||o.type!=="node")return null;const s=await n.prepare("SELECT * FROM objects WHERE owner = ? AND type = 'document' AND name = ? AND deleted_at IS NULL").get(o.id,t.slice(e+1)),d=s===void 0?void 0:(await c(n,s))[0];return d===void 0?null:(await u(n,[d.body_hash])).get(d.body_hash)??null}a(b,"reportBodyByName");export{h as hasReportDocuments,N as newestReportBody,D as newestReportName,b as reportBodyByName};
|
|
@@ -2,7 +2,7 @@ import type { CanvasDb } from '../canvas/db.js';
|
|
|
2
2
|
import type { ObjectRow, Reader } from './types.js';
|
|
3
3
|
/** The top of the object's owner chain (the object itself when it has no owner): its app, person or repo. */
|
|
4
4
|
export declare function spaceOf(db: CanvasDb, object: ObjectRow): Promise<ObjectRow>;
|
|
5
|
-
/** A shipped document: one a package reconciler owns
|
|
5
|
+
/** A shipped document: one a package reconciler owns (`documents.source_path` is set, as provenance and the shipped marker). Only the reconciler writes it. */
|
|
6
6
|
export declare function isShipped(db: CanvasDb, object: ObjectRow): Promise<boolean>;
|
|
7
7
|
/** Callers turn `false` into `not_found`. */
|
|
8
8
|
export declare function canRead(db: CanvasDb, reader: Reader, object: ObjectRow): Promise<boolean>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** D106 first boot on a Mac: copy every document text still in the old `<canvas home>/bodies/<hh>/<hash>` files into the `bodies` table.
|
|
2
|
+
* A file is copied only when the sha256 of its text equals its name; any other is skipped and reported. Rows go in with
|
|
3
|
+
* `ON CONFLICT DO NOTHING` in batches of one transaction each, so an interrupted run resumes and a finished one changes nothing.
|
|
4
|
+
* The files stay in place for a rollback to the previous build. Cloud roles never call this: their text was never on a disk that survives. */
|
|
5
|
+
export interface BodiesBootCopyReport {
|
|
6
|
+
scanned: number;
|
|
7
|
+
copied: number;
|
|
8
|
+
mismatched: string[];
|
|
9
|
+
nulReplaced: Array<{
|
|
10
|
+
hash: string;
|
|
11
|
+
replaced: number;
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
14
|
+
export declare function copyBodyFilesToTable(root?: string): Promise<BodiesBootCopyReport>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var m=Object.defineProperty;var r=(n,a)=>m(n,"name",{value:a,configurable:!0});import{existsSync as l,readdirSync as f,readFileSync as d}from"node:fs";import{join as c}from"node:path";import{openDb as u,withCanvasWrite as w}from"../canvas/db.js";import{crtrHome as y}from"../canvas/paths.js";import{hashOf as T,storable as b}from"./bodies.js";const O=200;async function E(n=c(y(),"bodies")){const a={scanned:0,copied:0,mismatched:[],nulReplaced:[]};if(!l(n))return a;const p=new Set((await(await u()).prepare("SELECT hash FROM bodies").all()).map(e=>e.hash));let i=[];const h=r(async()=>{if(i.length===0)return;const e=i;i=[],await w(async t=>{const s=t.prepare("INSERT INTO bodies (hash, text) VALUES (?, ?) ON CONFLICT (hash) DO NOTHING");for(const o of e)a.copied+=(await s.run(o.hash,o.text)).changes})},"flush");for(const e of f(n,{withFileTypes:!0}))if(!(!e.isDirectory()||!/^[0-9a-f]{2}$/.test(e.name)))for(const t of f(c(n,e.name))){if(!/^[0-9a-f]{64}$/.test(t)||(a.scanned+=1,p.has(t)))continue;const s=d(c(n,e.name,t),"utf8");if(T(s)!==t||!t.startsWith(e.name)){a.mismatched.push(t);continue}const o=b(s);o!==s&&a.nulReplaced.push({hash:t,replaced:s.split("\0").length-1}),i.push({hash:t,text:o}),i.length>=O&&await h()}return await h(),a}r(E,"copyBodyFilesToTable");export{E as copyBodyFilesToTable};
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
export declare function
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import type { CanvasDb } from '../canvas/db.js';
|
|
2
|
+
export declare function hashOf(body: string): string;
|
|
3
|
+
/** Postgres `text` cannot hold U+0000, so each NUL is stored as U+FFFD. The one rule for a new write and for the Mac boot copy. */
|
|
4
|
+
export declare function storable(text: string): string;
|
|
5
|
+
/** Store a body and return its hash, which is the hash of the text as stored (see `storable`). Call inside the transaction that writes the
|
|
6
|
+
* revision naming it, so a rolled-back write leaves neither.
|
|
7
|
+
* `as` is for text that arrives with the hash its revision already names (repo sync): the row is stored under that hash even when `storable` changed the text. */
|
|
8
|
+
export declare function put(db: CanvasDb, body: string, as?: string): Promise<string>;
|
|
9
|
+
export declare function get(db: CanvasDb, hash: string): Promise<string>;
|
|
10
|
+
/** The bodies that exist among `hashes`, in one query. A hash with no row is absent from the map. */
|
|
11
|
+
export declare function findMany(db: CanvasDb, hashes: readonly string[]): Promise<Map<string, string>>;
|
|
12
|
+
/** Several bodies in one query. A hash with no row is `no body <hash>`, as for `get`. */
|
|
13
|
+
export declare function getMany(db: CanvasDb, hashes: readonly string[]): Promise<Map<string, string>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var c=Object.defineProperty;var r=(t,e)=>c(t,"name",{value:e,configurable:!0});import{createHash as f}from"node:crypto";import{notFound as u}from"../errors.js";const h=/^[0-9a-f]{64}$/;function s(t){if(!h.test(t))throw new Error(`invalid body hash: ${t}`);return t}r(s,"checked");function p(t){return f("sha256").update(t,"utf8").digest("hex")}r(p,"hashOf");function d(t){return t.replaceAll("\0","\uFFFD")}r(d,"storable");async function l(t,e,n){const o=d(e),a=n===void 0?p(o):s(n);return await t.prepare("INSERT INTO bodies (hash, text) VALUES (?, ?) ON CONFLICT (hash) DO NOTHING").run(a,o),a}r(l,"put");async function O(t,e){const n=await t.prepare("SELECT text FROM bodies WHERE hash = ?").get(s(e));if(n===void 0)throw u(`no body ${e}`);return n.text}r(O,"get");async function w(t,e){const n=[...new Set(e)].map(s),o=new Map;if(n.length===0)return o;const a=await t.prepare("SELECT hash, text FROM bodies WHERE hash = ANY(?::text[])").all(n);for(const i of a)o.set(i.hash,i.text);return o}r(w,"findMany");async function b(t,e){const n=await w(t,e);for(const o of e)if(!n.has(o))throw u(`no body ${o}`);return n}r(b,"getMany");export{w as findMany,O as get,b as getMany,p as hashOf,l as put,d as storable};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ObjectRow } from './types.js';
|
|
1
|
+
import type { ObjectRow, Reader } from './types.js';
|
|
2
2
|
import { type CanvasDb } from '../canvas/db.js';
|
|
3
3
|
/** Mark the node and everything it owns deleted except kept documents, which stay owned by the
|
|
4
4
|
* deleted node; drop the node's watches (both ends) and its `nodes` row. One savepoint, so it runs as one
|
|
@@ -11,3 +11,6 @@ export declare function deleteNodeWithOwned(db: CanvasDb, node: ObjectRow): Prom
|
|
|
11
11
|
* a new profile can take the name, and its identity row stays so a kept document's permission
|
|
12
12
|
* string still names the profile id. Returns whether a profile object existed for `profileId`. */
|
|
13
13
|
export declare function deleteProfileWithOwned(db: CanvasDb, profileId: string): Promise<boolean>;
|
|
14
|
+
/** The owner's undelete, inside the 30 days before the purge pass may take the row (`core/storage/purge.ts`). Only a document comes back:
|
|
15
|
+
* a deleted node has lost its `nodes` row and everything that cascaded from it, and a deleted profile has lost its handle. */
|
|
16
|
+
export declare function undeleteObject(db: CanvasDb, reader: Reader | null, object: ObjectRow): Promise<ObjectRow>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
var u=Object.defineProperty;var
|
|
1
|
+
var u=Object.defineProperty;var r=(e,t)=>u(e,"name",{value:t,configurable:!0});import{hasUnreconciledWorktree as f}from"../canvas/worktrees.js";import{usage as m}from"../errors.js";import{deleteDocument as O,undeleteDocument as R}from"./documents.js";import{publish as c}from"./events.js";import{getById as E}from"./objects.js";import{writeActsFor as N}from"../permissions/facts.js";import{rememberDeletedIsolated as D}from"../permissions/ceiling.js";import{withCanvasWrite as w}from"../canvas/db.js";async function j(e,t){if(t.type!=="node")throw new Error(`deleteNodeWithOwned: ${t.id} is a ${t.type}, not a node`);if(await f(t.id))throw new Error(`refusing to delete node ${t.id}: its managed worktree is not reconciled`);await w(async()=>await T(e,t),{savepoint:!0})}r(j,"deleteNodeWithOwned");async function _(e,t){const i=await e.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.id = i.object
|
|
2
2
|
WHERE i.scheme = 'profile' AND i.external_id = ? AND o.type = 'profile' AND o.deleted_at IS NULL`).get(t);return i?(await w(async()=>{const o=await e.prepare(`UPDATE objects o SET owner = n.app FROM nodes n WHERE n.node_id = o.id AND o.owner = ? AND n.app IS NOT NULL
|
|
3
|
-
RETURNING o.id, n.sub`).all(i.id);for(const{id:s,sub:
|
|
3
|
+
RETURNING o.id, n.sub`).all(i.id);for(const{id:s,sub:n}of o)n&&await N(e,s,n);await p(e,i),await e.prepare("UPDATE objects SET deleted_at = ?, name = ? WHERE id = ?").run(new Date().toISOString(),`${i.name}.deleted-${i.id}`,i.id),await c(e,{event:"deleted",object:await E(e,i.id),actor:null,attrs:{}}),await e.prepare("DELETE FROM watches WHERE object = ? OR watcher = ?").run(i.id,i.id)},{savepoint:!0}),!0):!1}r(_,"deleteProfileWithOwned");async function F(e,t,i){if(i.type!=="document")throw m(`${i.type} ${i.name} cannot be undeleted; only documents can`,{id:i.id});return await R(e,t,i)}r(F,"undeleteObject");async function T(e,t){const i=await p(e,t);await e.prepare("UPDATE objects SET deleted_at = ? WHERE id = ?").run(i,t.id),await c(e,{event:"deleted",object:await E(e,t.id),actor:null,attrs:{}}),await e.prepare("DELETE FROM watches WHERE object = ? OR watcher = ?").run(t.id,t.id),await D(t.id,e),await e.prepare("UPDATE nodes SET run_id = NULL WHERE run_id = ? AND node_id != ? AND isolated = 0").run(t.id,t.id),await e.prepare("DELETE FROM nodes WHERE node_id = ?").run(t.id)}r(T,"deleteInside");async function p(e,t){const i=await e.prepare(`WITH RECURSIVE sub(id) AS (
|
|
4
4
|
SELECT id FROM objects WHERE owner = ?
|
|
5
5
|
UNION SELECT o.id FROM objects o JOIN sub ON o.owner = sub.id)
|
|
6
6
|
SELECT o.* FROM objects o JOIN sub ON sub.id = o.id WHERE o.deleted_at IS NULL`).all(t.id),o=new Set(i.map(a=>a.id)),s=e.prepare(`SELECT e.src FROM edges e JOIN objects s ON s.id = e.src
|
|
7
|
-
WHERE e.type = 'links-to' AND e.dst = ? AND s.deleted_at IS NULL`);for(let a=!0;a;){a=!1;for(const d of i)d.type!=="document"||!o.has(d.id)||(await s.all(d.id)).some(({src:l})=>!o.has(l))&&(o.delete(d.id),a=!0)}const
|
|
7
|
+
WHERE e.type = 'links-to' AND e.dst = ? AND s.deleted_at IS NULL`);for(let a=!0;a;){a=!1;for(const d of i)d.type!=="document"||!o.has(d.id)||(await s.all(d.id)).some(({src:l})=>!o.has(l))&&(o.delete(d.id),a=!0)}const n=new Date().toISOString();for(const a of i)if(o.has(a.id)){if(a.type==="document"){await O(e,null,a);continue}await e.prepare("UPDATE objects SET deleted_at = ? WHERE id = ?").run(n,a.id),await c(e,{event:"deleted",object:await E(e,a.id),actor:null,attrs:{}}),await e.prepare("DELETE FROM watches WHERE object = ? OR watcher = ?").run(a.id,a.id)}return n}r(p,"deleteOwned");export{j as deleteNodeWithOwned,_ as deleteProfileWithOwned,F as undeleteObject};
|
|
@@ -136,6 +136,10 @@ export declare function deleteDocument(db: CanvasDb, reader: Reader | null, doc:
|
|
|
136
136
|
/** Repo sync: bring a deleted document back under `name` (checked free first). Revisions, record
|
|
137
137
|
* and links are untouched; the watches and rules deletion dropped are not recreated here. */
|
|
138
138
|
export declare function restoreDocument(db: CanvasDb, doc: ObjectRow, name: string, reposFn?: ReposFn): Promise<ObjectRow>;
|
|
139
|
+
/** The owner's undelete: bring a deleted document back under its own name inside the 30-day window (past it the purge pass may take the
|
|
140
|
+
* row). Its owner must be live. Revisions, record and links are untouched; the rules, watches and facts deletion dropped are not
|
|
141
|
+
* recreated (as {@link restoreDocument}). */
|
|
142
|
+
export declare function undeleteDocument(db: CanvasDb, reader: Reader | null, doc: ObjectRow, now?: number): Promise<ObjectRow>;
|
|
139
143
|
/** Delete each of `targets` that is a kept document nothing links to any more: a live document
|
|
140
144
|
* whose owner is deleted (node deletion kept it because a live document linked to it) and that
|
|
141
145
|
* no other live document links to. Each one deleted re-checks what it linked to, so chains of
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
var se=Object.defineProperty;var s=(
|
|
1
|
+
var se=Object.defineProperty;var s=(t,n)=>se(t,"name",{value:n,configurable:!0});import{randomUUID as ce}from"node:crypto";import{CrtrError as R,notFound as N,usage as l}from"../errors.js";import{ExitCode as g}from"../../types.js";import{get as ue,getMany as we,put as B}from"./bodies.js";import{dropUnresolved as pe,linksOf as le,recordActedOn as O,setLinks as G,setUnresolved as de,unresolvedLinksCouldName as me}from"./edges.js";import{publish as j}from"./events.js";import{readableOrDeleted as fe,resolveName as ye}from"./names.js";import{createObject as he,getById as m}from"./objects.js";import{liveLookups as _e,readerForNode as Ee,readerForObject as ve}from"./reader.js";import{replaceRules as q,setRulesOwner as Re}from"./rules.js";import{recordWatch as x}from"./watches.js";import{withCanvasWrite as y}from"../canvas/db.js";import{PURGE_RETENTION_MS as Ne}from"../storage/purge.js";import{filterAsync as ge,forEachAsync as Oe,mapAsync as A}from"../async-array.js";import{check as L,resolveReader as Se,userOf as K}from"../permissions/evaluator.js";import{missingMemoryScope as Y}from"../permissions/ceiling.js";import{deleteFact as De,parseSubject as Te,writeActsFor as ke,writeFact as je}from"../permissions/facts.js";import{PermissionError as xe}from"../permissions/errors.js";function _(t){return t.class==="node"?t.node:t.class==="app"?t.app:t.class==="object"?t.object:t.person}s(_,"actorOf");async function M(t,n){const e=`user:${await K(t,n)}`;return n.class==="person"?{app:null,for:e}:{app:(await t.prepare("SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(n.app.id))?.external_id??null,for:e}}s(M,"actingOf");function Ae(t,n){return new R("conflict",t,g.GENERAL,n)}s(Ae,"conflict");function h(t){if(t.type!=="document")throw l(`${t.type} ${t.id} is not a document`)}s(h,"assertDocument");async function V(t,n){return(await t.prepare(`WITH RECURSIVE chain(id, owner, type, depth) AS (
|
|
2
2
|
SELECT id, owner, type, 0 FROM objects WHERE id = ?
|
|
3
3
|
UNION ALL
|
|
4
4
|
SELECT o.id, o.owner, o.type, c.depth + 1 FROM objects o JOIN chain c ON o.id = c.owner WHERE c.depth < 12
|
|
5
|
-
) SELECT type FROM chain ORDER BY depth DESC LIMIT 1`).get(n.id))?.type??n.type}s(V,"spaceType");async function
|
|
6
|
-
WHERE rp.child = ? ORDER BY p.rev_id`).all(n.rev_id);return{rev_id:n.rev_id,seq:n.seq,number:n.number,body_hash:n.body_hash,author:n.author,rationale:n.rationale,verbatim:n.verbatim===1,created_at:n.created_at,acting_app:n.acting_app,acting_for:n.acting_for,parents:
|
|
7
|
-
AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rev_id) ORDER BY r.number DESC, r.rev_id`).all(n.id);return await
|
|
8
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(n.id,
|
|
5
|
+
) SELECT type FROM chain ORDER BY depth DESC LIMIT 1`).get(n.id))?.type??n.type}s(V,"spaceType");async function C(t,n,e){if(e!=="public"&&await V(t,n)==="repo")throw l(`a repo owns only public documents; ${n.name} cannot own a ${e} one`,{rule:"repo-public-only",next:"Write it as public, or give it another owner (your own documents can carry `if: {repo: <handle>}`)."})}s(C,"assertPublicInRepo");async function U(t,n,e,a){if(e.trim()===""||e!==e.trim())throw l(`invalid document name ${JSON.stringify(e)}`);const i=await t.prepare("SELECT id FROM objects WHERE (type = 'document' OR strpos(type, '.') > 0) AND owner = ? AND name = ? AND deleted_at IS NULL AND home_machine IS NULL").get(n.id,e);if(i&&i.id!==a?.id)throw Ae(`${n.name} already has a document named ${e}`,{name:e,holder:i.id})}s(U,"assertNameFree");async function W(t,n,e){if(!await L(t,n,"read",e.id))throw N(`no document ${e.id}`)}s(W,"readable");function X(t){return new R("scope_missing",`missing scope ${t}`,g.GENERAL,{scope:t})}s(X,"scopeMissing");async function S(t,n,e){await W(t,n,e);const{source_path:a}=await v(t,e);if(a!==null)throw new R("forbidden",`${e.name} is a built-in document shipped by a package; \`crtr doc\` cannot change it`,g.GENERAL,{id:e.id,next:"Edit it in the source repository of the package that ships it (for crtr's own documents, the crouter repo), not with `crtr doc`."});const i=await Y(t,n,e.id,"rw");if(i!==null)throw X(i);if(!await L(t,n,"edit",e.id))throw new R("forbidden",`you cannot change ${e.name}`,g.GENERAL,{id:e.id})}s(S,"changeable");async function z(t,n){const e=await t.prepare(`SELECT p.rev_id FROM revision_parents rp JOIN revisions p ON p.rev_id = rp.parent
|
|
6
|
+
WHERE rp.child = ? ORDER BY p.rev_id`).all(n.rev_id);return{rev_id:n.rev_id,seq:n.seq,number:n.number,body_hash:n.body_hash,author:n.author,rationale:n.rationale,verbatim:n.verbatim===1,created_at:n.created_at,acting_app:n.acting_app,acting_for:n.acting_for,parents:e.map(a=>a.rev_id)}}s(z,"toRevision");async function I(t,n){const e=await t.prepare(`SELECT * FROM revisions r WHERE r.document = ?
|
|
7
|
+
AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rev_id) ORDER BY r.number DESC, r.rev_id`).all(n.id);return await A(e,async a=>await z(t,a))}s(I,"heads");function Le(t){return t.map(n=>n.rev_id).sort().join(",")}s(Le,"headsKeyOf");async function v(t,n){const e=await t.prepare("SELECT * FROM documents WHERE object = ?").get(n.id);if(!e)throw N(`no document record for ${n.id}`);return{summary:e.summary,heads_key:e.heads_key,revision:e.revision,why_it_exists:e.why_it_exists,unlisted:e.unlisted===1,lint_ignore:e.lint_ignore_json===null?null:JSON.parse(e.lint_ignore_json),extensions:e.extensions_json===null?null:JSON.parse(e.extensions_json),source_path:e.source_path}}s(v,"documentRecord");async function D(t,n,e){const a=await t.prepare(`INSERT INTO revisions (document, rev_id, number, body_hash, author, rationale, verbatim, created_at, acting_app, acting_for)
|
|
8
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(n.id,e.rev_id??ce(),e.number,e.body_hash,e.author?.id??null,e.rationale,e.verbatim?1:0,e.created_at??new Date().toISOString(),e.acting?.app??null,e.acting?.for??null),i=t.prepare("INSERT INTO revision_parents (child, parent) VALUES (?, ?)");for(const r of e.parents)await i.run(a.rev_id,r);return a}s(D,"insertRevision");function Ie(t){const n=new Set;for(const e of t.matchAll(/\[\[([^[\]\n]+?)\]\]/g))n.add(e[1].trim());return[...n]}s(Ie,"linksIn");async function Q(t,n,e){const a=[],i=[];for(const r of e){const o=await ye(t,n,r);"object"in o?a.push({written_as:r,dst:o.object}):"deleted"in o?a.push({written_as:r,dst:o.deleted}):"remote"in o||i.push(r)}return{links:a,unresolved:i}}s(Q,"resolveLinks");async function Z(t,n,e,a){const{links:i,unresolved:r}=await Q(t,n,Ie(a));return await ie(t,await G(t,e,i)),await de(t,e,r),r}s(Z,"setRevisionLinks");async function F(t,n,e){const a=new Map;for(const{src:i,name:r}of await me(t,n.name,n.id))a.set(i,[...a.get(i)??[],r]);for(const[i,r]of a){const o=await m(t,i),c=o?.owner==null?null:await m(t,o.owner);if(!o||o.deleted_at||o.home_machine!==null||!c||c.deleted_at)continue;const p=c.type==="node"?await Ee(t,c.id,e??(async()=>[]),_e(t),!0):await ve(t,o,e),u=(await Q(t,p,r)).links.filter(d=>d.dst.id===n.id);u.length!==0&&(await G(t,o,[...await le(t,o),...u]),await pe(t,o,u.map(d=>d.written_as)))}}s(F,"linkNewlyNamed");async function $e(t,n,e){return n.type==="node"?n.id:await t.prepare(`SELECT 1 FROM objects o JOIN objects d ON d.owner = o.id
|
|
9
9
|
WHERE o.name = ? AND o.type <> ? AND o.type IN ('person','app','repo','profile')
|
|
10
|
-
AND d.type = 'document' AND d.name = ? AND d.deleted_at IS NULL LIMIT 1`).get(n.name,n.type,
|
|
11
|
-
WHERE e.type = 'links-to' AND e.src = ? AND e.written_as IS NOT NULL`).all(n.id);if(a.length===0)return
|
|
12
|
-
WHERE e.type = 'links-to' AND e.src = ? AND (${
|
|
13
|
-
WHERE (resource_type = ? AND resource_id = ?) OR (subject_type = ? AND subject_id = ?)`).all(n.schema_type,n.id,n.schema_type,n.id);for(const a of
|
|
10
|
+
AND d.type = 'document' AND d.name = ? AND d.deleted_at IS NULL LIMIT 1`).get(n.name,n.type,e)?`${n.type}:${n.name}`:n.name}s($e,"ownerPrefix");async function b(t,n,e){const a=await t.prepare(`SELECT e.written_as, o.* FROM edges e JOIN objects o ON o.id = e.dst
|
|
11
|
+
WHERE e.type = 'links-to' AND e.src = ? AND e.written_as IS NOT NULL`).all(n.id);if(a.length===0)return e;const i=new Map;for(const r of a){if(r.deleted_at||r.written_as===r.id)continue;const o=!r.written_as.includes("/");if(o&&r.name===r.written_as)continue;const c=r.owner===null?null:await m(t,r.owner),p=c?`${await $e(t,c,r.name)}/${r.name}`:r.name,u=o&&r.owner===n.owner?r.name:p;u!==r.written_as&&i.set(r.written_as,u)}return i.size===0?e:e.replace(/\[\[([^[\]\n]+?)\]\]/g,(r,o)=>{const c=i.get(o.trim());return c===void 0?r:`[[${c}]]`})}s(b,"withCurrentNames");async function J(t,n,e){if(e.preview!==void 0){if(e.preview.trim()==="")throw l("a document needs a preview",{field:"preview"});await t.prepare("UPDATE objects SET preview = ? WHERE id = ?").run(e.preview,n.id)}e.privacy!==void 0&&(await C(t,await m(t,n.owner),e.privacy),await t.prepare("UPDATE objects SET privacy = ? WHERE id = ?").run(e.privacy,n.id));const a=s(async(r,o)=>await t.prepareUncached(`UPDATE documents SET ${r} = ? WHERE object = ?`).run(o,n.id),"set");e.summary!==void 0&&await a("summary",e.summary),e.whyItExists!==void 0&&await a("why_it_exists",e.whyItExists),e.unlisted!==void 0&&await a("unlisted",e.unlisted?1:0),e.lintIgnore!==void 0&&await a("lint_ignore_json",e.lintIgnore===null?null:JSON.stringify(e.lintIgnore)),e.extensions!==void 0&&await a("extensions_json",e.extensions===null?null:JSON.stringify(e.extensions));const i=await m(t,n.id);return e.rules!==void 0&&await q(t,i,e.rules),i}s(J,"applyDocumentFields");async function ee(t,n,e,a="crtr"){if(n.owner.deleted_at)throw l(`${n.owner.name} is deleted`);if(n.relations?.length&&n.schemaType===void 0)throw new xe(400,"schema_type_required","relations need a schemaType: they are facts about a typed resource",{field:"relations"});if(n.preview.trim()==="")throw l("a document needs a preview",{field:"preview"});await C(t,n.owner,n.privacy),await U(t,n.owner,n.name);const i=await he(t,{...n.id?{id:n.id}:{},type:"document",kind:n.kind,owner:n.owner.id,privacy:n.privacy,name:n.name,preview:n.preview,...n.schemaType!==void 0?{schema_type:n.schemaType}:{}});for(const{relation:o,subject:c}of n.relations??[])await je(t,{resourceType:n.schemaType,resourceId:i.id,relation:o,subject:Te(c),writtenBy:a});await t.prepare("INSERT INTO documents (object, heads_key, revision) VALUES (?, '', 0)").run(i.id);const r=await J(t,i,{...n,preview:void 0,privacy:void 0,rules:n.rules??[]});return await F(t,r,e),r}s(ee,"createDocument");async function $(t,n){const e=await I(t,n),a=Le(e),i=Math.max(0,...e.map(r=>r.number));return await t.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(a,i,n.id),{heads_key:a,revision:i,headCount:e.length}}s($,"setHeads");async function rt(t,n,e,a,i){return await y(async()=>{const r=await B(t,a),o=_(n),c=await Y(t,n,e.owner.id,"rw",!0);if(c!==null)throw X(c);const p=await M(t,n),u=await ee(t,e,i,p.app??p.for);e.owner.type==="app"&&e.schemaType===void 0&&await ke(t,u.id,await K(t,n));const d=await D(t,u,{number:1,body_hash:r,author:o,rationale:null,parents:[],acting:p});if(!await L(t,n,"edit",u.id))throw N(`no owner ${e.owner.name} you can write to`);const{heads_key:w}=await $(t,u),f=await Z(t,n,u,a);return await O(t,o,u),await x(t,o,u,"write",{readHeadsKey:w}),await j(t,{event:"created",object:u,actor:o,attrs:{revision:1},pointer:d.rev_id},i),{object:u,revision:1,unresolved:f}},{savepoint:!0})}s(rt,"writeDocument");async function He(t,n,e){return(await t.prepare("SELECT read_heads_key FROM watches WHERE watcher = ? AND object = ?").get(_(n).id,e.id))?.read_heads_key??null}s(He,"readBase");function Me(t,n){let e=t;for(const[a,{old:i,new:r}]of n.entries()){if(i==="")throw l(`block ${a+1}: a replacement needs non-empty old text`,{field:"replace"});const o=e.indexOf(i);if(o<0)throw l(`block ${a+1}: old text not found: ${JSON.stringify(i.slice(0,120))}`,{field:"replace",old:i});if(e.indexOf(i,o+1)>=0)throw l(`block ${a+1}: old text occurs more than once: ${JSON.stringify(i.slice(0,120))}`,{field:"replace",old:i});e=e.slice(0,o)+r+e.slice(o+i.length)}return e}s(Me,"applyReplacements");async function ot(t,n,e,a,i,r,o={},c){if(h(e),await S(t,n,e),e.deleted_at)throw l(`${e.name} is deleted`);if(r===null)throw new R("read_first",`read ${e.name} before editing it`,g.GENERAL,{id:e.id});if(i.trim()==="")throw l("an edit needs a rationale",{field:"rationale"});const p=await v(t,e);if(p.heads_key!==r)return{stale:{heads_key:p.heads_key,revision:p.revision}};const u=await I(t,e);let d;if("body"in a)d=a.body;else{if(u.length!==1)throw l(`${e.name} has ${u.length} heads and needs a merge; pass the whole merged body`,{heads:u.length});d=Me(await b(t,e,await ue(t,u[0].body_hash)),a.replace)}try{return await y(async()=>{const w=await B(t,d),f=_(n),T=1+Math.max(0,...u.map(E=>E.number)),P=await D(t,e,{number:T,body_hash:w,author:f,rationale:i,verbatim:a.verbatim===!0,parents:u.map(E=>E.rev_id),acting:await M(t,n)}),H=P.rev_id,re=await t.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ? AND heads_key = ?").run(H,T,e.id,r);if(Number(re.changes)===0){const E=await v(t,e);throw new te(E.heads_key,E.revision)}const k=await J(t,e,o),oe=await Z(t,n,k,d);return await O(t,f,k),await x(t,f,k,"write",{readHeadsKey:H}),await j(t,{event:"edited",object:k,actor:f,pointer:P.rev_id,attrs:{revision:T,from_revision:p.revision,rationale:i}},c),{revision:T,heads_key:H,unresolved:oe}},{savepoint:!0})}catch(w){if(w instanceof te)return{stale:{heads_key:w.heads_key,revision:w.revision}};throw w}}s(ot,"editDocument");class te extends Error{static{s(this,"StaleEdit")}heads_key;revision;constructor(n,e){super("stale"),this.heads_key=n,this.revision=e}}async function st(t,n,e,a,i){if(h(e),n&&(await S(t,n,e),i===void 0||i.trim()===""))throw l("an edit needs a rationale",{field:"rationale"});return await y(async()=>{const r=await J(t,e,a);if(n){const o=_(n),c=await v(t,e),p=await He(t,n,e),u=await I(t,e),d=u[0];await D(t,e,{number:1+Math.max(...u.map(f=>f.number)),body_hash:d.body_hash,author:o,rationale:i,parents:[d.rev_id],acting:await M(t,n)});const{heads_key:w}=await $(t,e);await O(t,o,r),await x(t,o,e,"write",p===c.heads_key?{readHeadsKey:w}:{})}return r},{savepoint:!0})}s(st,"setDocumentRecord");async function Ce(t,n,e,a){const i=_(n),{heads_key:r}=await v(t,e);await x(t,i,e,"read",{readHeadsKey:r}),await j(t,{event:"read",object:e,actor:i,attrs:{}},a)}s(Ce,"recordRead");async function ct(t,n,e,a){return h(e),await W(t,n,e),await y(async()=>{const i=await v(t,e),r=await I(t,e),o=await we(t,r.map(w=>w.body_hash)),c=await A(r,async w=>({...w,body:await b(t,e,o.get(w.body_hash))})),p=await fe(t,await Se(t,n),"o"),d=(await t.prepareUncached(`SELECT e.written_as AS _written_as, o.* FROM edges e JOIN objects o ON o.id = e.dst
|
|
12
|
+
WHERE e.type = 'links-to' AND e.src = ? AND (${p.sql}) ORDER BY e.written_as`).all(e.id,...p.params)).map(({_written_as:w,...f})=>({written_as:w??"",object:f,deleted:f.deleted_at!==null}));return e.deleted_at||await Ce(t,n,e,a),{object:e,record:i,heads:c,needsMerge:c.length>1,links:d}},{savepoint:!0})}s(ct,"readDocument");async function ut(t,n,e){h(e),await W(t,n,e);const a=await t.prepare("SELECT * FROM revisions WHERE document = ? ORDER BY number DESC, created_at DESC, seq DESC").all(e.id);return await A(a,async i=>await z(t,i))}s(ut,"history");async function wt(t,n,e,a,i){if(h(e),e.deleted_at)throw l(`${e.name} is deleted`);return n&&await S(t,n,e),await y(async()=>{const r=a.owner??await m(t,e.owner),o=a.name??e.name;await U(t,r,o,e),await C(t,r,e.privacy),await t.prepare("UPDATE objects SET name = ?, owner = ? WHERE id = ?").run(o,r.id,e.id);const c=await m(t,e.id);if(r.id!==e.owner&&(await Re(t,c),n&&!await L(t,n,"edit",c.id)))throw N(`no owner ${r.name} you can move documents to`);return n&&await O(t,_(n),c),(o!==e.name||r.id!==e.owner)&&await F(t,c,i),c},{savepoint:!0})}s(wt,"moveDocument");async function pt(t,n,e,a,i){return h(e),e.deleted_at?e:(n&&await S(t,n,e),await y(async()=>{const r=await ne(t,n,e,a,i);return await Ue(t,e),await ie(t,await ae(t,e.id),a),r},{savepoint:!0}))}s(pt,"deleteDocument");async function Ue(t,n){if(n.schema_type==null)return;const e=await t.prepare(`SELECT resource_type, resource_id, relation, subject_type, subject_id, subject_relation FROM relations
|
|
13
|
+
WHERE (resource_type = ? AND resource_id = ?) OR (subject_type = ? AND subject_id = ?)`).all(n.schema_type,n.id,n.schema_type,n.id);for(const a of e)await De(t,{resourceType:a.resource_type,resourceId:a.resource_id,relation:a.relation,subject:{type:a.subject_type,id:a.subject_id,relation:a.subject_relation}})}s(Ue,"deleteFactsOf");async function We(t,n,e,a){return h(n),n.deleted_at?await y(async()=>{await U(t,await m(t,n.owner),e,n),await t.prepare("UPDATE objects SET deleted_at = NULL, name = ? WHERE id = ?").run(e,n.id);const i=await m(t,n.id);return await F(t,i,a),i},{savepoint:!0}):n}s(We,"restoreDocument");async function lt(t,n,e,a=Date.now()){if(h(e),!e.deleted_at)return e;if(Date.parse(e.deleted_at)<a-Ne)throw l(`${e.name} was deleted over 30 days ago and can no longer be undeleted`,{id:e.id,deleted_at:e.deleted_at});const i=e.owner===null?null:await m(t,e.owner);if(i===null||i.deleted_at)throw l(`${e.name} cannot be undeleted while its owner is deleted`,{id:e.id});return await y(async()=>{const r=await We(t,e,e.name);return n&&await S(t,n,r),r},{savepoint:!0})}s(lt,"undeleteDocument");async function ne(t,n,e,a,i){await t.prepare("UPDATE objects SET deleted_at = ? WHERE id = ?").run(i??new Date().toISOString(),e.id);const r=await m(t,e.id),o=n?_(n):null;return o&&await O(t,o,r),await j(t,{event:"deleted",object:r,actor:o,attrs:{}},a),await t.prepare("DELETE FROM watches WHERE object = ?").run(e.id),await q(t,r,[]),r}s(ne,"markDeleted");async function ae(t,n){return(await t.prepare("SELECT dst FROM edges WHERE type = 'links-to' AND src = ?").all(n)).map(({dst:e})=>e)}s(ae,"linkTargets");async function ie(t,n,e){const a=t.prepare(`SELECT 1 FROM objects d JOIN objects w ON w.id = d.owner
|
|
14
14
|
WHERE d.id = ? AND d.type = 'document' AND d.deleted_at IS NULL AND w.deleted_at IS NOT NULL
|
|
15
15
|
AND NOT EXISTS (SELECT 1 FROM edges e JOIN objects s ON s.id = e.src
|
|
16
|
-
WHERE e.type = 'links-to' AND e.dst = d.id AND e.src != d.id AND s.deleted_at IS NULL)`),i=[...n];for(;i.length>0;){const r=i.pop();await a.get(r)!==void 0&&(await ne(
|
|
16
|
+
WHERE e.type = 'links-to' AND e.dst = d.id AND e.src != d.id AND s.deleted_at IS NULL)`),i=[...n];for(;i.length>0;){const r=i.pop();await a.get(r)!==void 0&&(await ne(t,null,await m(t,r),e),i.push(...await ae(t,r)))}}s(ie,"collectUnlinked");async function dt(t,n,e){if(n.revisions.length===0)throw l(`import of ${n.name}: no revisions`);return await y(async()=>{const a=await ee(t,n,e);await t.prepare("UPDATE objects SET created_at = ? WHERE id = ?").run(n.revisions[0].created_at,a.id);let i=null;return await Oe(n.revisions,async(r,o)=>{i=(await D(t,a,{...r,number:o+1,parents:i===null?[]:[i]})).rev_id}),await $(t,a),await m(t,a.id)},{savepoint:!0})}s(dt,"importDocument");async function mt(t,n,e){if(h(n),n.privacy!=="public"&&await V(t,n)==="repo")throw l(`a repo owns only public documents; refusing revisions of ${n.name}`);return await y(async()=>{const a=t.prepare("SELECT rev_id, number, document FROM revisions WHERE rev_id = ?");let i=await ge(e,async c=>!await a.get(c.rev_id));for(;i.length>0;){const c=[];for(const p of i){const u=await A(p.parents,async w=>await a.get(w));if(u.some(w=>w===void 0)){c.push(p);continue}const d=u;if(d.some(w=>w.document!==n.id))throw l(`revision ${p.rev_id} names a parent of another document`);await D(t,n,{...p,author:null,number:1+Math.max(0,...d.map(w=>w.number)),parents:d.map(w=>w.rev_id)})}if(c.length===i.length)throw l(`revisions of ${n.name} name unknown parents: ${c.map(p=>p.rev_id).join(", ")}`);i=c}const{heads_key:r,headCount:o}=await $(t,n);return{heads_key:r,headCount:o}},{savepoint:!0})}s(mt,"insertImportedRevisions");async function ft(t,n){const e=await m(t,n);if(!e||e.type!=="document")throw N(`no document ${n}`);return e}s(ft,"documentById");export{_ as actorOf,J as applyDocumentFields,Me as applyReplacements,ie as collectUnlinked,pt as deleteDocument,ft as documentById,v as documentRecord,ot as editDocument,I as heads,ut as history,dt as importDocument,mt as insertImportedRevisions,Ie as linksIn,wt as moveDocument,He as readBase,ct as readDocument,Ce as recordRead,We as restoreDocument,st as setDocumentRecord,Z as setRevisionLinks,lt as undeleteDocument,b as withCurrentNames,rt as writeDocument};
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
var
|
|
2
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(t.id??await
|
|
3
|
-
WHERE i.scheme = ? AND i.external_id = ?`).get(o,i),"find"),
|
|
1
|
+
var W=Object.defineProperty;var s=(e,t)=>W(e,"name",{value:t,configurable:!0});import{CrtrError as D,notFound as T}from"../errors.js";import{ExitCode as x}from"../../types.js";import{newNodeId as v}from"../runtime/nodes.js";import{kindOf as k}from"./kinds/registry.js";import{findMany as J}from"./bodies.js";import{isCloudRole as O,personId as P,personIdAsync as Y}from"../canvas/person.js";import{check as B,permits as A,resolveReader as $}from"../permissions/evaluator.js";import{userSubject as C,writeActsFor as g}from"../permissions/facts.js";const K=new Set(["person","app","repo","profile"]);function U(e){return e||null}s(U,"rowFrom");async function X(e,t){return U(await e.prepare("SELECT * FROM objects WHERE id = ?").get(t))}s(X,"getById");async function z(e,t){return await k(e,t.type),await e.prepare(`INSERT INTO objects (id, type, kind, owner, privacy, name, preview, created_at, home_machine, schema_type)
|
|
2
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(t.id??await v(),t.type,t.kind,t.owner,t.privacy,t.name,t.preview,new Date().toISOString(),t.home_machine??null,t.schema_type??null)}s(z,"createObject");const G=s((e,t)=>`owner:${e}:${t}`,"ownerLockKey");async function F(e,t,o,i,n,d){if(!K.has(t))throw new Error(`ensureOwner: ${t} is not an owner type`);const p=s(async()=>await e.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.id = i.object
|
|
3
|
+
WHERE i.scheme = ? AND i.external_id = ?`).get(o,i),"find"),u=await p();if(u)return u;await e.prepare("SELECT pg_advisory_xact_lock(hashtext(?))").get(G(o,i));const f=await p();if(f)return f;const E=await e.prepare(`SELECT o.id, o.type, i.scheme, i.external_id FROM objects o
|
|
4
4
|
LEFT JOIN identities i ON i.object = o.id
|
|
5
|
-
WHERE o.name = ? AND o.type = ? AND o.home_machine IS NULL`).get(n,t);if(
|
|
5
|
+
WHERE o.name = ? AND o.type = ? AND o.home_machine IS NULL`).get(n,t);if(E)throw new D("conflict",`handle "${n}" is held by ${E.type} ${E.id} (${E.scheme}:${E.external_id}); cannot give it to ${t} ${o}:${i}`,x.GENERAL,{handle:n,holder:E.id});const h=await z(e,{type:t,kind:null,owner:t==="profile"?d?.id??null:null,privacy:"public",name:n,preview:""});return await e.prepare("INSERT INTO identities (object, scheme, external_id) VALUES (?, ?, ?)").run(h.id,o,i),t==="person"&&o==="user"?await g(e,h.id,C(i).id):(t==="profile"||t==="repo")&&await g(e,h.id,C(await Y()).id),h}s(F,"ensureOwner");async function ae(e,t,o){if(O())return!1;const i=await e.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.id = i.object
|
|
6
6
|
WHERE i.scheme = 'profile' AND i.external_id = ? AND o.type = 'profile' AND o.deleted_at IS NULL`).get(t),n=await e.prepare(`SELECT o.id FROM objects o
|
|
7
|
-
WHERE o.name = ? AND o.type = 'profile' AND o.deleted_at IS NULL AND o.home_machine IS NULL AND o.id <> ?`).get(o,i?i.id:"");if(n)throw new
|
|
7
|
+
WHERE o.name = ? AND o.type = 'profile' AND o.deleted_at IS NULL AND o.home_machine IS NULL AND o.id <> ?`).get(o,i?i.id:"");if(n)throw new D("conflict",`handle "${o}" is already held by profile object ${n.id}; cannot rename profile ${t} to it`,x.GENERAL,{handle:o,holder:n.id});if(!i)return!1;const d=i;return d.name===o?!1:(await V(e,d,o),!0)}s(ae,"renameProfileObject");function ce(e,t){return O()?e:t}s(ce,"profileHandle");async function de(e,t){if(/^app:[a-z0-9][a-z0-9._-]*$/.test(t))return await F(e,"app","app",t,t.slice(4));if(/^user:[a-z0-9][a-z0-9._-]*$/.test(t))return await F(e,"person","user",t,!O()&&t===P()?"user":t.slice(5));throw new Error(`principal ${JSON.stringify(t)} is neither app:<name> nor user:<id>`)}s(de,"ownerForGrantee");async function pe(e,t,o){return U(await e.prepare("SELECT * FROM objects WHERE (type = 'document' OR strpos(type, '.') > 0) AND owner = ? AND name = ? AND deleted_at IS NULL AND home_machine IS NULL").get(t,o))}s(pe,"liveDocumentNamed");async function ue(e,t){const o=await X(e,t);if(!o||o.type!=="node")throw T(`no node object ${t}`);return o}s(ue,"nodeObject");async function V(e,t,o){await e.prepare("UPDATE objects SET name = ? WHERE id = ?").run(o,t.id)}s(V,"setObjectName");async function Ee(e,t,o){await e.prepare("UPDATE objects SET kind = ? WHERE id = ?").run(o,t.id)}s(Ee,"setObjectKind");function _(e,t){const o=[];return e.type!==void 0&&(o.push("o.type = ?"),t.push(e.type)),e.kind!==void 0&&(o.push("o.kind = ?"),t.push(e.kind)),e.owner!==void 0&&(o.push("o.owner = ?"),t.push(e.owner.id)),e.namePrefix!==void 0&&(o.push("o.name LIKE ? ESCAPE '\\'"),t.push(`${e.namePrefix.replace(/[\\%_]/g,i=>`\\${i}`)}%`)),e.schemaType!==void 0&&(o.push("o.schema_type = ?"),t.push(e.schemaType)),e.extensions!==void 0&&(o.push("EXISTS (SELECT 1 FROM documents x WHERE x.object = o.id AND x.extensions_json::jsonb @> ?::jsonb)"),t.push(JSON.stringify(e.extensions))),o}s(_,"narrowing");function Q(e,t){const o="WITH RECURSIVE sa(id, owner, depth) AS (SELECT o.id, o.owner, 0 UNION ALL SELECT p.id, p.owner, sa.depth + 1 FROM sa JOIN objects p ON p.id = sa.owner WHERE sa.depth < 12)";t.push(e.subjects.userIds,e.subjects.groupIds);const i=["o.schema_type IS NULL",`EXISTS (${o} SELECT 1 FROM sa JOIN relations r ON r.resource_type = 'crtr/object' AND r.resource_id = sa.id
|
|
8
8
|
AND r.relation IN ('reader', 'editor', 'messenger', 'user_of') AND r.written_by <> 'package'
|
|
9
9
|
AND ((r.subject_type = 'user' AND r.subject_id = ANY(?::text[])) OR (r.subject_type = 'crtr/group' AND r.subject_relation = 'member' AND r.subject_id = ANY(?::text[]))))`];return e.userId===null?i:(t.push(e.userId),[...i,`NOT EXISTS (${o} SELECT 1 FROM sa JOIN relations r ON r.resource_type = 'crtr/object' AND r.resource_id = sa.id
|
|
10
|
-
AND r.relation = 'acts_for' AND r.subject_type = 'user' AND r.subject_id = ?)`])}s(
|
|
10
|
+
AND r.relation = 'acts_for' AND r.subject_type = 'user' AND r.subject_id = ?)`])}s(Q,"sharedOnly");async function me(e,t,o,i={}){const n=["o.seq > ?"],d=[i.after_seq??0];o.privacy!==void 0&&(n.push("o.privacy = ?"),d.push(o.privacy)),o.name!==void 0&&(n.push("o.name = ?"),d.push(o.name)),n.push("o.home_machine IS NULL"),n.push(o.deleted?"o.deleted_at IS NOT NULL":"o.deleted_at IS NULL"),n.push(..._(o,d));const p=await $(e,t);o.shared&&n.push(...Q(p,d));const u=await A(e,p,"read","o"),f=Math.min(i.limit??100,100);return await e.prepareUncached(`SELECT o.* FROM objects o WHERE ${n.join(" AND ")} AND (${u.sql}) ORDER BY o.seq LIMIT ${f}`).all(...d,...u.params)}s(me,"listObjects");async function fe(e,t,o){const n=[],d=`%${o.text.replace(/[\\%_]/g,r=>`\\${r}`)}%`,p=await $(e,t);if(!o.grep){const r=[d],m=_(o,r).map(l=>` AND ${l}`).join(""),c=await A(e,p,"read","o"),a=await e.prepareUncached(`SELECT o.*, (SELECT d.summary FROM documents d WHERE d.object = o.id) AS _summary FROM objects o
|
|
11
11
|
WHERE o.deleted_at IS NULL AND o.home_machine IS NULL AND (o.name ILIKE ?1 ESCAPE '\\' OR o.preview ILIKE ?1 ESCAPE '\\'
|
|
12
|
-
OR EXISTS (SELECT 1 FROM documents d WHERE d.object = o.id AND d.summary ILIKE ?1 ESCAPE '\\'))${
|
|
13
|
-
AND (${
|
|
12
|
+
OR EXISTS (SELECT 1 FROM documents d WHERE d.object = o.id AND d.summary ILIKE ?1 ESCAPE '\\'))${m}
|
|
13
|
+
AND (${c.sql}) ORDER BY o.seq LIMIT 100`).all(...r,...c.params),N=o.text.toLowerCase();for(const l of a){const{_summary:S,...H}=l,L=H,b=L.name.toLowerCase().includes(N)?"name":L.preview.toLowerCase().includes(N)?"preview":"summary";n.push({object:L,field:b,...b==="summary"?{text:String(S)}:{}})}if(n.length>=100)return n}if(!o.body&&!o.grep)return n;const u=new Set(n.map(r=>r.object.id)),f=o.grep?(r=>m=>r.test(m))(new RegExp(o.text)):r=>r.includes(o.text),E=[],h=_(o,E).map(r=>` AND ${r}`).join(""),R=await e.prepareUncached(`SELECT o.id, r.body_hash AS body_hash FROM objects o
|
|
14
14
|
JOIN documents d ON d.object = o.id JOIN revisions r ON r.document = o.id
|
|
15
|
-
WHERE o.type = 'document' AND o.deleted_at IS NULL AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rev_id)${
|
|
16
|
-
ORDER BY o.seq, r.number`).all(...
|
|
17
|
-
`),
|
|
15
|
+
WHERE o.type = 'document' AND o.deleted_at IS NULL AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rev_id)${h}
|
|
16
|
+
ORDER BY o.seq, r.number`).all(...E),y=await A(e,p,"read","o"),M=200;let w=[];const j=s(async()=>{if(w.length===0)return!1;const r=await e.prepareUncached(`SELECT o.* FROM objects o WHERE o.id = ANY(?::text[]) AND (${y.sql})`).all(w.map(c=>c.id),...y.params),m=new Map(r.map(c=>[c.id,c]));for(const c of w){const a=m.get(c.id);if(a!==void 0&&(n.push({object:a,field:"body",line:c.line,text:c.text}),n.length>=100))return!0}return w=[],!1},"flush"),I=100;for(let r=0;r<R.length;r+=I){const m=R.slice(r,r+I).filter(a=>!u.has(a.id)),c=await J(e,m.map(a=>a.body_hash));for(const a of m){if(u.has(a.id))continue;const N=c.get(a.body_hash);if(N===void 0){if(await B(e,p,"read",a.id))throw T(`no body ${a.body_hash}`);continue}if(!o.grep&&!N.includes(o.text))continue;const l=N.split(`
|
|
17
|
+
`),S=l.findIndex(f);if(!(S<0)&&(u.add(a.id),w.push({id:a.id,line:S+1,text:l[S]}),w.length>=M&&await j()))return n}}return await j(),n}s(fe,"searchObjects");export{z as createObject,F as ensureOwner,X as getById,me as listObjects,pe as liveDocumentNamed,ue as nodeObject,de as ownerForGrantee,G as ownerLockKey,ce as profileHandle,ae as renameProfileObject,fe as searchObjects,Ee as setObjectKind,V as setObjectName};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var S=Object.defineProperty;var u=(e,a)=>S(e,"name",{value:a,configurable:!0});import{
|
|
2
|
-
WHERE o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all();for(const{id:n}of t)o.has(n)||await v(e,await
|
|
1
|
+
var S=Object.defineProperty;var u=(e,a)=>S(e,"name",{value:a,configurable:!0});import{randomUUID as _}from"node:crypto";import{CrtrError as g}from"../errors.js";import{emitEvent as O}from"../events/emit.js";import{quarantineBrokenPlugins as I}from"../plugin-quarantine.js";import{listInstalledPlugins as D}from"../resolver.js";import{builtinMemoryRoot as L,pluginMemoryDir as R}from"../scope.js";import{documentFields as T}from"../../migrations/004-canvas-documents/fields.js";import{put as A}from"./bodies.js";import{applyDocumentFields as k,deleteDocument as j,documentRecord as F,heads as x,importDocument as C,linksIn as U,moveDocument as H}from"./documents.js";import{deleteShippedReader as M,writeShippedReader as W}from"../permissions/facts.js";import{setLinks as J}from"./edges.js";import{publish as b}from"./events.js";import{resolveName as P}from"./names.js";import{packageFiles as q}from"./package-files.js";import{ensureOwner as B,getById as h,setObjectKind as K}from"./objects.js";import{checkoutsForCwd as $}from"./repo-sync/repos.js";import{rulesOf as z}from"./rules.js";function G(){return[{plugin:"crtr",dir:L()},...D("user").filter(e=>e.enabled).map(e=>({plugin:e.name,dir:R(e)}))]}u(G,"packageSources");async function Ee(e,a){const i={created:0,updated:0,deleted:0,conflicts:[],invalid:[],quarantined:[]};if(a===void 0){i.quarantined=I();for(const n of i.quarantined)O({level:"error",event:"daemon.plugins.quarantined",fields:{plugin:n.name,file:n.file,reason:n.reason,...n.movedTo!==void 0?{moved_to:n.movedTo}:{}}})}const l=a??G(),m=u(async n=>await $(e,n),"reposFn"),o=new Set,r=[],f=new Set;for(const n of l){let s;try{if(f.has(n.plugin))throw new g("conflict",`plugin "${n.plugin}" is already provided by another package`);f.add(n.plugin),s=await B(e,"app","app",`app:${n.plugin}`,n.plugin)}catch(p){if(!(p instanceof g)||p.code!=="conflict")throw p;i.conflicts.push({plugin:n.plugin,message:p.message});continue}o.add(s.id);const{files:c,invalid:w}=q(n.dir);i.invalid.push(...w);const E=new Set;for(const p of c){const d=await X(e,s,p,i,m);E.add(d),r.push({owner:s,id:d,body:p.body})}await v(e,s,E,i,m)}const t=await e.prepare(`SELECT DISTINCT o.owner AS id FROM objects o JOIN documents d ON d.object = o.id
|
|
2
|
+
WHERE o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all();for(const{id:n}of t)o.has(n)||await v(e,await h(e,n),new Set,i,m);for(const{owner:n,id:s,body:c}of r)await Q(e,n,await h(e,s),c);return i}u(Ee,"reconcilePackageDocuments");async function Q(e,a,i,l){if(i.deleted_at!==null)return;const m={class:"app",app:a,scopes:[]},o=[];for(const t of U(l)){const n=await P(e,m,t);"object"in n&&n.object.id!==i.id&&await V(e,n.object.id)&&o.push({written_as:t,dst:n.object})}const r=u(t=>JSON.stringify(t.map(n=>[n.dst,n.written_as]).sort()),"key"),f=await e.prepare("SELECT dst, written_as FROM edges WHERE type = 'links-to' AND src = ?").all(i.id);r(f)!==r(o.map(t=>({dst:t.dst.id,written_as:t.written_as})))&&await J(e,i,o)}u(Q,"deriveLinks");async function V(e,a){return await e.prepare("SELECT 1 FROM documents WHERE object = ? AND source_path IS NOT NULL").get(a)!==void 0}u(V,"shippedDocument");async function X(e,a,i,l,m){const o=T(i.frontmatter,i.body,{missingPrivacy:"public"}),r={preview:o.preview.trim()===""?i.name:o.preview,summary:o.summary,whyItExists:o.whyItExists,unlisted:o.unlisted,rules:o.rules,lintIgnore:Array.isArray(o.lintIgnore)?o.lintIgnore.filter(d=>typeof d=="string"):null,extensions:o.extensions!==null&&typeof o.extensions=="object"&&!Array.isArray(o.extensions)?o.extensions:null},f=await A(e,i.body);let t=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
|
|
3
3
|
WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path = ?`).get(a.id,i.path);if(t||(t=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
|
|
4
|
-
WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND o.name = ? AND d.source_path IS NOT NULL`).get(a.id,i.name),t&&await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(i.path,t.id)),!t){const d=await C(e,{owner:a,name:i.name,kind:o.kind,privacy:"public",...r,revisions:[{body_hash:f,author:a,rationale:null,created_at:new Date().toISOString()}]});return await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(i.path,d.id),await W(e,d.id),l.created+=1,d.id}const n=t.name!==i.name||t.kind!==o.kind;t.name!==i.name&&(t=await
|
|
5
|
-
SELECT ?, document, ?, ?, author, rationale, verbatim, ? FROM revisions WHERE rev_id = ?`).run(d,
|
|
6
|
-
WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all(a.id);for(const r of o)i.has(r.id)||(await j(e,null,r,m),await M(e,r.id),l.deleted+=1)}u(v,"deleteMissing");export{G as packageSources,
|
|
4
|
+
WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND o.name = ? AND d.source_path IS NOT NULL`).get(a.id,i.name),t&&await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(i.path,t.id)),!t){const d=await C(e,{owner:a,name:i.name,kind:o.kind,privacy:"public",...r,revisions:[{body_hash:f,author:a,rationale:null,created_at:new Date().toISOString()}]});return await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(i.path,d.id),await W(e,d.id),l.created+=1,d.id}const n=t.name!==i.name||t.kind!==o.kind;t.name!==i.name&&(t=await H(e,null,t,{name:i.name})),t.kind!==o.kind&&(await K(e,t,o.kind),t=await h(e,t.id));const s=await F(e,t),[c]=await x(e,t),w=c?.body_hash!==f,E=t.preview!==r.preview||s.summary!==r.summary||s.why_it_exists!==r.whyItExists||s.unlisted!==r.unlisted||JSON.stringify(s.lint_ignore)!==JSON.stringify(r.lintIgnore)||JSON.stringify(s.extensions)!==JSON.stringify(r.extensions)||JSON.stringify((await z(e,t)).map(N))!==JSON.stringify(r.rules.map(N));if(!w&&!E&&!n)return t.id;const p=await k(e,t,r);if(w&&c){const d=_(),y=c.number+1;await e.prepare(`INSERT INTO revisions (rev_id, document, number, body_hash, author, rationale, verbatim, created_at)
|
|
5
|
+
SELECT ?, document, ?, ?, author, rationale, verbatim, ? FROM revisions WHERE rev_id = ?`).run(d,y,f,new Date().toISOString(),c.rev_id),await e.prepare("INSERT INTO revision_parents (child, parent) SELECT ?, parent FROM revision_parents WHERE child = ?").run(d,c.rev_id),await e.prepare("DELETE FROM revisions WHERE rev_id = ?").run(c.rev_id),await e.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(d,y,p.id),await b(e,{event:"edited",object:p,actor:a,pointer:d,attrs:{revision:y,from_revision:c.number,rationale:"package file changed"}},m)}return l.updated+=1,p.id}u(X,"convergeFile");function N(e){return[e.on,e.deliver,e.match??null,e.matchFrontmatter??null,e.if??null]}u(N,"ruleKey");async function v(e,a,i,l,m){const o=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
|
|
6
|
+
WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all(a.id);for(const r of o)i.has(r.id)||(await j(e,null,r,m),await M(e,r.id),l.deleted+=1)}u(v,"deleteMissing");export{G as packageSources,Ee as reconcilePackageDocuments};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var b=Object.defineProperty;var p=(o,e)=>b(o,"name",{value:e,configurable:!0});import{coveredByAny as d,isSandboxedNode as C}from"../../shared/env.js";import{existsSync as S}from"node:fs";import{CrtrError as L}from"../errors.js";import{getCachedProfile as
|
|
2
|
-
sub, grant_id, scopes_json, cwd, profile_id, app FROM nodes WHERE node_id = ?`).get(e);if(!a||!l)throw new Error(`no node ${e}`);const{grantee:c,sub:s,grant_id:i,scopes_json:u,cwd:f,profile_id:g,app:h}=l,m=h===null?null:await r.object(h);if(!m)throw new Error(`node ${e} has no app`);const _=u===null?null:JSON.parse(u);return{class:"node",node:a,app:m,scopes:await x(c,s??"",i,_,r,t),launched:_,cwd:f,...await N(f,g,n,r),profile:g===null?null:await r.profile(g,m)}}p(J,"readerForNode");async function re(o,e,n=async()=>[],r=w(o)){const t=await o.prepare("SELECT envelope_json FROM peer_stubs WHERE object = ?").get(e.id);if(t!==void 0)return await k(o,e,t.envelope_json===null?null:JSON.parse(t.envelope_json),r);if(e.type==="node")return await J(o,e.id,n,r,!0);const a=e.owner===null?null:await r.object(e.owner);if(!a)throw new Error(`watcher ${e.id} has no owner`);const c=(a.type==="app"?a:a.type==="profile"&&a.owner!==null?await r.object(a.owner):null)??await o.prepare("SELECT o.* FROM identities i JOIN objects o ON o.id = i.object WHERE i.external_id = 'app:terminal' AND i.scheme = 'app'").get()??await o.prepare("SELECT * FROM objects WHERE type = 'person' AND name = 'user' AND home_machine IS NULL").get();if(!c)throw new Error("no terminal app or person object");const s=(await o.prepare("SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(c.id))?.external_id??"app:terminal",i=await o.prepare("SELECT resolved_scopes_json FROM grants WHERE grantee = ? AND status = 'active'").all(s),u=a.type==="person"?null:i.length===0?r.pluginScopes(s):i.length===1?JSON.parse(i[0].resolved_scopes_json).filter(f=>f.mode==="always").map(f=>R(f.scope)):null;return{class:"object",object:e,node:e,owner:a,app:c,scopes:u,repos:a.type==="repo"?[a]:[]}}p(re,"readerForObject");async function se(o,e,n){const r=(await o.prepare("SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(e.id))?.external_id;if(r===void 0)return null;const t=await o.prepare("SELECT resolved_scopes_json FROM grants WHERE grantee = ? AND status = 'active'").all(r);return t.length===0?n.pluginScopes(r):t.length>1?null:JSON.parse(t[0].resolved_scopes_json).filter(a=>a.mode==="always").map(a=>R(a.scope))}p(se,"appScopesNow");async function k(o,e,n,r){const t=await j(o);if(n!==null&&n.app===null)return{class:"object",object:e,node:e,owner:t,app:t,scopes:null,repos:[]};const a=n?.app==null?null:await E(o,n.app),l=n?.app==null?null:await r.grantFor(n.app,n.sub),c=n===null||l===null?null:n.node_scopes===null?l.resolved:n.node_scopes.filter(i=>d(l.resolved,i)),s=a??(e.owner===null?t:await r.object(e.owner)??t);return{class:"object",object:e,node:e,owner:s,app:s,scopes:c,launched:n?.node_scopes??null,repos:[],...n?.place==null?{}:{place:n.place}}}p(k,"readerForStub");function w(o){return{grantById:O,grantFor:v,pluginScopes:p(e=>A(e.replace(/^app:/,""))?.scopes??null,"pluginScopes"),object:p(async e=>await T(o,e),"object"),profile:p(async(e,n)=>await G(o,e,n),"profile"),projectPaths:p(e=>C()?[]
|
|
1
|
+
var b=Object.defineProperty;var p=(o,e)=>b(o,"name",{value:e,configurable:!0});import{coveredByAny as d,isSandboxedNode as C}from"../../shared/env.js";import{existsSync as S}from"node:fs";import{CrtrError as L}from"../errors.js";import{getCachedProfile as $}from"../scoped-state/person-store.js";import{parseScope as R}from"@north-light/identity";import{activeGrant as O,grantFor as v}from"../grants/mirror.js";import{ApiError as M}from"../../api/errors.js";import{pluginAppToken as A}from"../secrets.js";import{loadExactProfileManifest as H}from"../profiles/manifest.js";import{ensureOwner as y,getById as T,nodeObject as W,ownerForGrantee as D,profileHandle as P}from"./objects.js";import{personId as B}from"../canvas/person.js";import{warmCheckoutIdentities as F}from"./repo-sync/repos.js";async function N(o,e,n,r){const t=e===null?[]:r.projectPaths(e),a=await n(o),l=new Set(a.map(s=>s.repo.id)),c=[];for(const s of t){if(!S(s))continue;let i;try{i=await n(s)}catch(u){if(u instanceof L&&u.code==="git-lookup-failed")continue;throw u}for(const u of i)l.has(u.repo.id)||(l.add(u.repo.id),c.push(u))}return{repos:a.map(s=>s.repo),checkouts:a,projectRepos:c.map(s=>s.repo),projectCheckouts:c}}p(N,"viewRepos");async function ne(o,e){const n=await o.prepare("SELECT cwd, profile_id FROM nodes WHERE node_id = ?").get(e);if(n!==void 0&&(await F(n.cwd),n.profile_id))for(const r of w(o).projectPaths(n.profile_id))S(r)&&await F(r)}p(ne,"warmNodeRepos");async function ae(o,e,n){if(e.class==="anonymous")throw new Error("a publish token cannot read as a principal");if(e.class==="person")return{class:"person",person:await j(o)};if(e.class==="app")return{class:"app",app:await E(o,e.app),scopes:e.scopes,...e.sub===void 0?{}:{sub:e.sub}};if(e.class==="machine"||e.class==="import")throw new Error(`a ${e.class} token cannot read as a principal`);if(e.class==="forwarded"){if(e.actor.kind==="person")return{class:"person",person:await j(o)};const c=await v(e.actor.app,e.sub);if(!c)throw new M(403,"app_not_granted",`${e.actor.app} holds no active grant for ${e.sub} on this machine`);const s=e.actor.node_scopes;return{class:"app",app:await E(o,e.actor.app),place:e.actor.place,launched:s,scopes:s===null?c.resolved:s.filter(i=>d(c.resolved,i))}}if(e.class==="cron"){const c=e.grant?(await O(e.grant))?.resolved??[]:[],s=e.scopes;return{class:"app",app:await E(o,e.app),scopes:s===null?c:s.filter(i=>d(c,i)),launched:s,place:{run:e.origin_run??"",isolated:e.isolated}}}const r=w(o),t=await W(o,e.node),a=await o.prepare("SELECT cwd, profile_id, app FROM nodes WHERE node_id = ?").get(e.node),l=a.app===null?null:await r.object(a.app);if(!l)throw new Error(`node ${e.node} has no app`);return{class:"node",node:t,app:l,scopes:await x(e.app,e.sub,e.grant,e.scopes,r),launched:e.scopes,cwd:a.cwd,...await N(a.cwd,a.profile_id,n,r),profile:a.profile_id===null?null:await r.profile(a.profile_id,l)}}p(ae,"readerFor");async function J(o,e,n,r=w(o),t=!1){const a=await o.prepare("SELECT o.* FROM objects o WHERE o.id = ? AND o.type = 'node'").get(e),l=await o.prepare(`SELECT (SELECT external_id FROM identities WHERE object = nodes.app) AS grantee,
|
|
2
|
+
sub, grant_id, scopes_json, cwd, profile_id, app FROM nodes WHERE node_id = ?`).get(e);if(!a||!l)throw new Error(`no node ${e}`);const{grantee:c,sub:s,grant_id:i,scopes_json:u,cwd:f,profile_id:g,app:h}=l,m=h===null?null:await r.object(h);if(!m)throw new Error(`node ${e} has no app`);const _=u===null?null:JSON.parse(u);return{class:"node",node:a,app:m,scopes:await x(c,s??"",i,_,r,t),launched:_,cwd:f,...await N(f,g,n,r),profile:g===null?null:await r.profile(g,m)}}p(J,"readerForNode");async function re(o,e,n=async()=>[],r=w(o)){const t=await o.prepare("SELECT envelope_json FROM peer_stubs WHERE object = ?").get(e.id);if(t!==void 0)return await k(o,e,t.envelope_json===null?null:JSON.parse(t.envelope_json),r);if(e.type==="node")return await J(o,e.id,n,r,!0);const a=e.owner===null?null:await r.object(e.owner);if(!a)throw new Error(`watcher ${e.id} has no owner`);const c=(a.type==="app"?a:a.type==="profile"&&a.owner!==null?await r.object(a.owner):null)??await o.prepare("SELECT o.* FROM identities i JOIN objects o ON o.id = i.object WHERE i.external_id = 'app:terminal' AND i.scheme = 'app'").get()??await o.prepare("SELECT * FROM objects WHERE type = 'person' AND name = 'user' AND home_machine IS NULL").get();if(!c)throw new Error("no terminal app or person object");const s=(await o.prepare("SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(c.id))?.external_id??"app:terminal",i=await o.prepare("SELECT resolved_scopes_json FROM grants WHERE grantee = ? AND status = 'active'").all(s),u=a.type==="person"?null:i.length===0?r.pluginScopes(s):i.length===1?JSON.parse(i[0].resolved_scopes_json).filter(f=>f.mode==="always").map(f=>R(f.scope)):null;return{class:"object",object:e,node:e,owner:a,app:c,scopes:u,repos:a.type==="repo"?[a]:[]}}p(re,"readerForObject");async function se(o,e,n){const r=(await o.prepare("SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(e.id))?.external_id;if(r===void 0)return null;const t=await o.prepare("SELECT resolved_scopes_json FROM grants WHERE grantee = ? AND status = 'active'").all(r);return t.length===0?n.pluginScopes(r):t.length>1?null:JSON.parse(t[0].resolved_scopes_json).filter(a=>a.mode==="always").map(a=>R(a.scope))}p(se,"appScopesNow");async function k(o,e,n,r){const t=await j(o);if(n!==null&&n.app===null)return{class:"object",object:e,node:e,owner:t,app:t,scopes:null,repos:[]};const a=n?.app==null?null:await E(o,n.app),l=n?.app==null?null:await r.grantFor(n.app,n.sub),c=n===null||l===null?null:n.node_scopes===null?l.resolved:n.node_scopes.filter(i=>d(l.resolved,i)),s=a??(e.owner===null?t:await r.object(e.owner)??t);return{class:"object",object:e,node:e,owner:s,app:s,scopes:c,launched:n?.node_scopes??null,repos:[],...n?.place==null?{}:{place:n.place}}}p(k,"readerForStub");function w(o){return{grantById:O,grantFor:v,pluginScopes:p(e=>A(e.replace(/^app:/,""))?.scopes??null,"pluginScopes"),object:p(async e=>await T(o,e),"object"),profile:p(async(e,n)=>await G(o,e,n),"profile"),projectPaths:p(e=>C()?[]:$(e)?.manifest.projects.map(n=>n.path)??[],"projectPaths")}}p(w,"liveLookups");function pe(o){const e=w(o),n=new Map,r=p((t,a)=>(n.has(t)||n.set(t,a()),n.get(t)),"memo");return{grantById:p(t=>r(`g\0${t}`,()=>e.grantById(t)),"grantById"),grantFor:p((t,a)=>r(`a\0${t}\0${a}`,()=>e.grantFor(t,a)),"grantFor"),pluginScopes:p(t=>r(`t\0${t}`,()=>e.pluginScopes(t)),"pluginScopes"),object:p(async t=>await r(`o\0${t}`,async()=>await e.object(t)),"object"),profile:p(async(t,a)=>await r(`p\0${t}\0${a.id}`,async()=>await e.profile(t,a)),"profile"),projectPaths:p(t=>r(`j\0${t}`,()=>e.projectPaths(t)),"projectPaths")}}p(pe,"publishLookups");async function x(o,e,n,r,t,a=!1){const l=n?await t.grantById(n):await t.grantFor(o,e),c=l&&l.grantee===o?l.resolved:null;if(c===null){if(a)return null;throw new Error(`the grant of ${o} is not active`)}return r===null?c:r.filter(s=>d(c,s))}p(x,"nodeScopes");async function j(o){return await D(o,B())}p(j,"personObject");async function E(o,e){return await y(o,"app","app",e,e.replace(/^app:/,""))}p(E,"appObject");async function G(o,e,n){return await o.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.id = i.object
|
|
3
3
|
WHERE i.scheme = 'profile' AND i.external_id = ?`).get(e)??await y(o,"profile","profile",e,P(e,H(e).manifest.name),n)}p(G,"profileObject");export{se as appScopesNow,w as liveLookups,G as profileObject,pe as publishLookups,ae as readerFor,J as readerForNode,re as readerForObject,N as viewRepos,ne as warmNodeRepos};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
2
|
-
WHERE e.type = 'links-to' AND e.src = ? ORDER BY e.written_as, o.id`),s=new Map;for(const e of p){const d=await
|
|
1
|
+
var $=Object.defineProperty;var _=(t,i)=>$(t,"name",{value:i,configurable:!0});import{getMany as B,put as J}from"../bodies.js";import{setLinks as U,setUnresolved as P}from"../edges.js";import{collectUnlinked as Y,deleteDocument as I,documentRecord as C,heads as T,importDocument as z,insertImportedRevisions as b,moveDocument as q,restoreDocument as F,setDocumentRecord as G}from"../documents.js";import{CrtrError as K}from"../../errors.js";import{publish as M}from"../events.js";import{getById as O,setObjectKind as V}from"../objects.js";import{rulesOf as Q}from"../rules.js";import{docPath as X,mergeDoc as Z,parseDoc as E,serializeDoc as A}from"./ref-format.js";import{withCanvasWrite as H}from"../../canvas/db.js";import{mapAsync as L}from"../../async-array.js";function W(t){if(t.owner!==null||t.type!=="repo")throw new Error(`${t.type} ${t.id} is not a space that syncs through a repo ref`)}_(W,"assertRepoRefSpace");function ee(t){const i=new Map;for(const[a,n]of t??[]){const m=E(a,n);m.ok&&i.set(m.doc.id,m.doc)}return i}_(ee,"parseMap");async function Ee(t,i,a,n=null){W(i);const m=ee(a),p=await t.prepare("SELECT * FROM objects WHERE type = 'document' AND owner = ? AND privacy = 'public' ORDER BY seq").all(i.id),l=t.prepare("SELECT rev_id, number, body_hash, author, rationale, created_at FROM revisions WHERE document = ? ORDER BY number, rev_id"),w=t.prepare("SELECT p.rev_id FROM revision_parents rp JOIN revisions p ON p.rev_id = rp.parent WHERE rp.child = ?"),r=t.prepare(`SELECT e.written_as, o.id FROM edges e JOIN objects o ON o.id = e.dst
|
|
2
|
+
WHERE e.type = 'links-to' AND e.src = ? ORDER BY e.written_as, o.id`),s=new Map;for(const e of p){const d=await C(t,e),c=new Map((m.get(e.id)?.revisions??[]).map(o=>[o.rev_id,o])),h=new Set((await T(t,e)).map(o=>o.rev_id)),v=(await r.all(e.id)).map(o=>({written_as:o.written_as??"",dst:o.id})),k=await l.all(e.id),g=await B(t,k.filter(o=>!c.has(o.rev_id)).map(o=>o.body_hash)),N=await L(k,async o=>c.get(o.rev_id)??{rev_id:o.rev_id,number:o.number,parents:(await w.all(o.rev_id)).map(x=>x.rev_id),body:g.get(o.body_hash),body_hash:o.body_hash,author:o.author,rationale:o.rationale,created_at:o.created_at,links:h.has(o.rev_id)?v:[]});if(N.length===0)continue;const y=(await Q(t,e)).map(o=>({position:o.position,on:o.on,match:o.match??null,match_frontmatter:o.matchFrontmatter??null,if:o.if??null,deliver:o.deliver})),S={id:e.id,name:e.name,kind:e.kind??"document",privacy:"public",preview:e.preview,summary:d.summary,why_it_exists:d.why_it_exists,unlisted:d.unlisted,lint_ignore:d.lint_ignore,extensions:d.extensions,created_at:e.created_at,deleted_at:e.deleted_at,rules:y,revisions:N};s.set(X(e.id),A(S))}for(const e of te(a,n)){const d=a.get(e),c=s.get(e);if(c===void 0){s.set(e,d);continue}const h=E(e,d),v=E(e,c);if(!h.ok){s.set(e,d);continue}if(!v.ok)continue;const k=n?.has(e)?E(e,n.get(e)):null,g=Z(k?.ok?k.doc:null,v.doc,h.doc);g&&s.set(e,A(g))}return s}_(Ee,"exportRepo");function te(t,i){return[...t??[]].filter(([a,n])=>i?.get(a)!==n).map(([a])=>a)}_(te,"pendingPaths");function ne(t){return[...t].sort((i,a)=>i.position-a.position).map(i=>({on:i.on,deliver:i.deliver,...i.match!==null&&i.match!==void 0?{match:i.match}:{},...i.match_frontmatter!==null&&i.match_frontmatter!==void 0?{matchFrontmatter:i.match_frontmatter}:{},...i.if!==null&&i.if!==void 0?{if:i.if}:{}}))}_(ne,"toRuleSpecs");const ie=_((t,i)=>JSON.stringify(t??null)===JSON.stringify(i??null),"same");function j(t,i){const a={},n=_(m=>!t||!ie(t[m],i[m]),"differs");return n("preview")&&(a.preview=i.preview),n("summary")&&(a.summary=i.summary),n("why_it_exists")&&(a.whyItExists=i.why_it_exists),n("unlisted")&&(a.unlisted=i.unlisted),n("lint_ignore")&&(a.lintIgnore=i.lint_ignore),n("extensions")&&(a.extensions=i.extensions),n("rules")&&(a.rules=ne(i.rules)),a}_(j,"changedFields");async function ke(t,i,a,n,m){W(i);const p={created:0,revisions:0,deleted:0,needsMerge:[],refused:[]},l=[];for(const[r,s]of n){if(a.get(r)===s)continue;const e=E(r,s);if(!e.ok){p.refused.push({path:r,reason:e.reason});continue}const d=a.has(r)?E(r,a.get(r)):null;l.push({path:r,prior:d?.ok?d.doc:null,doc:e.doc})}for(const[r,s]of a){if(n.has(r))continue;const e=E(r,s);e.ok&&l.push({path:r,prior:e.doc,doc:null,priorId:e.doc.id})}const w=[];return await H(async()=>{for(const s of l)for(const e of s.doc?.revisions??[])await J(t,e.body,e.body_hash);let r=l;for(;;){const s=[];for(const e of r){const d={created:0,revisions:0,deleted:0,needsMerge:[],refused:[]};try{const c=[];await H(async()=>await ce(t,i,e.prior,e.doc,e.priorId??e.doc.id,d,m,c),{savepoint:!0}),w.push(...c),p.created+=d.created,p.revisions+=d.revisions,p.deleted+=d.deleted,p.needsMerge.push(...d.needsMerge)}catch(c){de(c)?s.push({w:e,error:c}):p.refused.push({path:e.path,reason:c.message})}}if(s.length===0)break;if(s.length===r.length){for(const e of s)p.refused.push({path:e.w.path,reason:e.error.message});break}r=s.map(e=>e.w)}await ae(t,n,new Set(w),new Set(l.map(s=>s.path)))},{savepoint:!0}),p}_(ke,"importTree");async function ae(t,i,a,n){if(a.size===0)return;const m=t.prepare("INSERT INTO edges (type, src, dst, written_as, created_at) VALUES ('links-to', ?, ?, ?, ?) ON CONFLICT DO NOTHING"),p=new Date().toISOString();for(const[l,w]of i){if(!n.has(l)&&!se(w,a))continue;const r=E(l,w);if(!r.ok||r.doc.deleted_at)continue;const s=await O(t,r.doc.id);if(!s||s.type!=="document"||s.deleted_at)continue;const e=(await T(t,s))[0],d=r.doc.revisions.find(c=>c.rev_id===e?.rev_id)?.links??[];for(const c of d){const h=await O(t,c.dst);h&&h.id!==s.id&&await m.run(s.id,h.id,c.written_as,p)}}}_(ae,"completeLinks");const re=/"dst":\s*"((?:[^"\\]|\\.)*)"/g;function se(t,i){for(const a of t.matchAll(re))if(i.has(oe(a[1])))return!0;return!1}_(se,"namesArrived");function oe(t){if(!t.includes("\\"))return t;try{return JSON.parse(`"${t}"`)}catch{return t}}_(oe,"decodeJsonString");function de(t){return t instanceof K&&t.code==="conflict"&&t.details?.holder!==void 0}_(de,"isNameConflict");async function ce(t,i,a,n,m,p,l,w){const r=await O(t,m);if(r&&(r.type!=="document"||r.owner!==i.id))throw new Error(`id ${m} is held by ${r.type} ${r.name} outside ${i.name}`);if(!n){r&&!r.deleted_at&&(await I(t,null,r,l),p.deleted++);return}const s=t.prepare("SELECT count(*) AS n FROM revisions WHERE document = ?");let e,d=!1,c=!1,h=0,v=0;if(r){e=r,h=(await C(t,e)).revision,e.created_at!==n.created_at&&(await t.prepare("UPDATE objects SET created_at = ? WHERE id = ?").run(n.created_at,e.id),e=await O(t,e.id));const u=n.kind;(a?a.kind!==u:e.kind!==u)&&await V(t,e,u),c=!!e.deleted_at&&n.deleted_at===null&&(a===null||a.deleted_at!=null);let f=j(c?null:a,n);if(n.deleted_at&&e.deleted_at){const{rules:R,...D}=f;f=D}c&&(e=await F(t,e,n.name,l),w?.push(e.id)),Object.keys(f).length>0&&(e=await G(t,null,e,f)),n.name!==e.name&&!e.deleted_at&&(e=await q(t,null,e,{name:n.name},l))}else{const f=n.revisions.filter(R=>R.parents.length===0).sort((R,D)=>R.number-D.number||R.rev_id.localeCompare(D.rev_id))[0];if(!f)throw new Error(`${n.name}: no root revision`);e=await z(t,{id:n.id,owner:i,name:n.name,kind:n.kind,preview:n.preview,privacy:"public",...j(null,n),revisions:[{rev_id:f.rev_id,body_hash:f.body_hash,author:null,rationale:f.rationale,created_at:f.created_at}]},l),e.created_at!==n.created_at&&(await t.prepare("UPDATE objects SET created_at = ? WHERE id = ?").run(n.created_at,e.id),e=await O(t,e.id)),d=!0,w?.push(e.id),v=1}const k=(await s.get(e.id)).n,{headCount:g}=await b(t,e,n.revisions.map(u=>({rev_id:u.rev_id,parents:u.parents,body_hash:u.body_hash,rationale:u.rationale,created_at:u.created_at})));v+=(await s.get(e.id)).n-k,p.revisions+=v;const y=(await T(t,e))[0],S=new Map(n.revisions.map(u=>[u.rev_id,u]));if(v>0||c){const u=(await L(S.get(y.rev_id)?.links??[],async f=>({written_as:f.written_as,dst:await O(t,f.dst)}))).filter(f=>f.dst!==null);await Y(t,await U(t,e,u),l),await P(t,e,[])}if(g>1&&p.needsMerge.push(e.id),n.deleted_at&&!e.deleted_at){await I(t,null,e,l,n.deleted_at),p.deleted++;return}if(n.deleted_at&&e.deleted_at&&e.deleted_at!==n.deleted_at&&await t.prepare("UPDATE objects SET deleted_at = ? WHERE id = ?").run(n.deleted_at,e.id),d){p.created++,await M(t,{event:"created",object:e,actor:null,attrs:{revision:y.number},pointer:y.rev_id},l);return}if(c){p.created++,await M(t,{event:"created",object:e,actor:null,attrs:{revision:y.number},pointer:y.rev_id},l);return}if(v===0)return;const x=n.revisions.filter(u=>!a?.revisions.some(f=>f.rev_id===u.rev_id)).sort((u,f)=>f.number-u.number||f.created_at.localeCompare(u.created_at))[0]??S.get(y.rev_id);await M(t,{event:"edited",object:e,actor:null,pointer:y.rev_id,attrs:{revision:y.number,from_revision:h,rationale:x?.rationale??null,author:x?.author??null,heads:g,needs_merge:g>1}},l)}_(ce,"importOne");export{Ee as exportRepo,ke as importTree,se as namesArrived,te as pendingPaths};
|
|
@@ -41,7 +41,6 @@ export interface RefDocument {
|
|
|
41
41
|
export declare const docPath: (id: string) => string;
|
|
42
42
|
/** Every field merged three-way: all but id, created_at and revisions. */
|
|
43
43
|
export declare const RECORD_FIELDS: readonly ["name", "kind", "privacy", "preview", "summary", "why_it_exists", "unlisted", "lint_ignore", "extensions", "deleted_at", "rules"];
|
|
44
|
-
export declare function sha256(body: string): string;
|
|
45
44
|
export declare function serializeDoc(doc: RefDocument): string;
|
|
46
45
|
/** Parse one ref file. Refuses anything not public, a body whose hash disagrees, or an id that is not the file name. */
|
|
47
46
|
export declare function parseDoc(path: string, text: string): {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var $=Object.defineProperty;var a=(n,t)=>$(n,"name",{value:t,configurable:!0});import{
|
|
2
|
-
`}a(
|
|
1
|
+
var $=Object.defineProperty;var a=(n,t)=>$(n,"name",{value:t,configurable:!0});import{hashOf as x}from"../bodies.js";const D=a(n=>`docs/${n}.json`,"docPath"),N=["name","kind","privacy","preview","summary","why_it_exists","unlisted","lint_ignore","extensions","deleted_at","rules"];function _(n){if(Array.isArray(n))return n.map(_);if(n&&typeof n=="object"){const t={};for(const r of Object.keys(n).sort())t[r]=_(n[r]);return t}return n}a(_,"sortKeys");const g=a(n=>JSON.stringify(_(n??null)),"stable");function b(n,t){return n.number-t.number||(n.rev_id<t.rev_id?-1:n.rev_id>t.rev_id?1:0)}a(b,"byRevision");function R(n){const t={...n,rules:[...n.rules].sort((r,o)=>r.position-o.position),revisions:[...n.revisions].map(r=>({...r,parents:[...r.parents].sort()})).sort(b)};return`${JSON.stringify(_(t),null,2)}
|
|
2
|
+
`}a(R,"serializeDoc");const j=new Set(["boot","workspace-open","file-read","document-read","command","pre-command","slash-command"]),E=new Set(["name","preview","content"]),p=a(n=>typeof n=="string","isStr"),w=a(n=>n===null||typeof n=="string","isStrOrNull");function A(n,t){let r;try{r=JSON.parse(t)}catch(e){return{ok:!1,reason:`not JSON: ${e.message}`}}if(!r||typeof r!="object"||Array.isArray(r))return{ok:!1,reason:"not an object"};const o=r;if(o.privacy!=="public")return{ok:!1,reason:`privacy is ${String(o.privacy)}; a repo holds only public documents`};if(!p(o.id)||D(o.id)!==n)return{ok:!1,reason:`id ${String(o.id)} does not match file ${n}`};for(const e of["name","kind","preview","created_at"])if(!p(o[e]))return{ok:!1,reason:`${e} is not a string`};for(const e of["summary","why_it_exists","deleted_at"])if(!w(o[e]))return{ok:!1,reason:`${e} is not a string or null`};if(typeof o.unlisted!="boolean")return{ok:!1,reason:"unlisted is not a boolean"};if(!Array.isArray(o.rules))return{ok:!1,reason:"rules is not an array"};for(const e of o.rules)if(!e||typeof e.position!="number"||!j.has(e.on)||!E.has(e.deliver))return{ok:!1,reason:"malformed rule"};if(!Array.isArray(o.revisions)||o.revisions.length===0)return{ok:!1,reason:"no revisions"};const f=new Set;for(const e of o.revisions){if(!e||!p(e.rev_id)||typeof e.number!="number"||!p(e.body)||!p(e.body_hash)||!p(e.created_at)||!w(e.author)||!w(e.rationale)||!Array.isArray(e.parents)||!e.parents.every(p)||!Array.isArray(e.links))return{ok:!1,reason:"malformed revision"};if(f.has(e.rev_id))return{ok:!1,reason:`revision ${e.rev_id} appears twice`};if(f.add(e.rev_id),x(e.body)!==e.body_hash)return{ok:!1,reason:`revision ${e.rev_id} body does not match body_hash`}}return{ok:!0,doc:o}}a(A,"parseDoc");function O(...n){const t=new Map;for(const r of n)for(const o of r?.revisions??[])t.has(o.rev_id)||t.set(o.rev_id,o);return[...t.values()].sort(b)}a(O,"unionRevisions");function I(n,t,r,o=new Date().toISOString()){if(!t&&!r)return null;if(!t||!r){const e=t??r;return n?{...e,deleted_at:e.deleted_at??o,revisions:O(r,t,n)}:e}const f={...r,revisions:O(r,t,n)};for(const e of N){const c=g(t[e]),m=g(r[e]);if(c===m)continue;const h=n?g(n[e]):void 0;if(e==="deleted_at"&&n?.deleted_at!=null&&t.deleted_at===null!=(r.deleted_at===null)){f.deleted_at=null,f.rules=(t.deleted_at===null?t:r).rules;continue}h===m&&(f[e]=t[e])}return f}a(I,"mergeDoc");function C(n,t,r,o=new Date().toISOString()){const f=new Map,e=[],c=new Map,m=[...new Set([...n.keys(),...t.keys(),...r.keys()])].sort();for(const i of m){const s=[];let l=null;for(const u of[n,t,r]){const y=u.get(i);if(y===void 0){s.push(null);continue}const v=A(i,y);v.ok?s.push(v.doc):(l??=v.reason,s.push(null))}if(l!==null){e.push({path:i,reason:l});const u=t.get(i)??r.get(i);u!==void 0&&f.set(i,u);continue}const d=I(s[0],s[1],s[2],o);d&&c.set(i,d)}const h=a(i=>{const s=r.get(i);if(s===void 0)return null;const l=A(i,s);return l.ok&&l.doc.deleted_at===null?l.doc.name:null},"remoteNameOf"),k=new Map;for(const[i,s]of c)s.deleted_at===null&&k.set(s.name,[...k.get(s.name)??[],i]);const S=[];for(const[i,s]of k){if(s.length<2)continue;const l=s.find(d=>h(d)===i)??[...s].sort((d,u)=>c.get(d).id.localeCompare(c.get(u).id))[0];for(const d of s){if(d===l)continue;const u=c.get(d),y=`${i}-${u.id.slice(0,8)}`;c.set(d,{...u,name:y}),S.push({id:u.id,from:i,to:y})}}for(const[i,s]of c)f.set(i,R(s));return{tree:new Map([...f].sort(([i],[s])=>i.localeCompare(s))),renamed:S,refused:e}}a(C,"mergeTrees");export{N as RECORD_FIELDS,D as docPath,I as mergeDoc,C as mergeTrees,A as parseDoc,R as serializeDoc};
|