@north-light/crouter 0.3.354 → 0.3.356
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/dto/delivery.d.ts +13 -3
- package/dist/api/dto/docs.d.ts +2 -0
- package/dist/api/dto/objects.d.ts +25 -22
- package/dist/api/dto/objects.js +1 -1
- package/dist/api/dto/reviews.d.ts +6 -4
- package/dist/builtin-memory/02-turn-lifecycle/02-resident.md +1 -0
- package/dist/builtin-memory/04-orchestration-kernel.md +10 -31
- package/dist/builtin-memory/internal/memory-loading.md +1 -1
- package/dist/clients/attach/overlays/file-review.d.ts +1 -0
- package/dist/clients/attach/overlays/file-review.js +2 -2
- package/dist/clients/attach/render/chat-view.d.ts +6 -0
- package/dist/clients/attach/render/chat-view.js +2 -2
- package/dist/clients/attach/render/doc-links.d.ts +34 -0
- package/dist/clients/attach/render/doc-links.js +1 -0
- package/dist/clients/attach/render/markdown-source.js +5 -5
- package/dist/clients/attach/session/doc-materialize.d.ts +53 -0
- package/dist/clients/attach/session/doc-materialize.js +3 -0
- package/dist/clients/attach/session/document-links.d.ts +21 -0
- package/dist/clients/attach/session/document-links.js +1 -0
- package/dist/clients/attach/session/file-links.d.ts +2 -0
- package/dist/clients/attach/session/file-links.js +1 -1
- package/dist/clients/attach/viewer.js +837 -761
- package/dist/clients/inbox/review/review-client.d.ts +5 -1
- package/dist/clients/inbox/review/review-client.js +1 -1
- package/dist/commands/canvas/list.js +3 -3
- package/dist/commands/canvas/read.d.ts +2 -4
- package/dist/commands/canvas/read.js +3 -5
- package/dist/commands/canvas/search.js +1 -1
- package/dist/commands/doc/write.js +1 -1
- package/dist/commands/node/lifecycle.js +2 -2
- package/dist/commands/sys/context/admin/actions.js +1 -1
- package/dist/commands/sys/sync-deps.js +1 -1
- package/dist/commands/sys/sync-shared.js +1 -1
- package/dist/core/canvas/daemon-actions.d.ts +43 -0
- package/dist/core/canvas/daemon-actions.js +26 -0
- package/dist/core/canvas/deliveries.d.ts +1 -0
- package/dist/core/canvas/deliveries.js +1 -1
- package/dist/core/canvas/migrations.js +106 -46
- package/dist/core/feed/messages.d.ts +2 -20
- package/dist/core/feed/messages.js +6 -10
- package/dist/core/graph/deletion.d.ts +5 -4
- package/dist/core/graph/deletion.js +3 -3
- package/dist/core/graph/documents.d.ts +8 -1
- package/dist/core/graph/documents.js +9 -6
- package/dist/core/graph/edges.d.ts +3 -2
- package/dist/core/graph/edges.js +1 -1
- package/dist/core/graph/events.d.ts +11 -6
- package/dist/core/graph/events.js +5 -4
- package/dist/core/graph/jobs.js +2 -2
- package/dist/core/graph/kinds/document.d.ts +19 -0
- package/dist/core/graph/kinds/document.js +5 -0
- package/dist/core/graph/kinds/dto.d.ts +5 -0
- package/dist/core/graph/kinds/dto.js +1 -0
- package/dist/core/graph/kinds/job.d.ts +2 -0
- package/dist/core/graph/kinds/job.js +2 -0
- package/dist/core/graph/kinds/kind.d.ts +39 -0
- package/dist/core/graph/kinds/kind.js +1 -0
- package/dist/core/graph/kinds/node.d.ts +2 -0
- package/dist/core/graph/kinds/node.js +4 -0
- package/dist/core/graph/kinds/owner.d.ts +2 -0
- package/dist/core/graph/kinds/owner.js +2 -0
- package/dist/core/graph/kinds/registry.d.ts +8 -0
- package/dist/core/graph/kinds/registry.js +1 -0
- package/dist/core/graph/kinds/render.d.ts +9 -0
- package/dist/core/graph/kinds/render.js +1 -0
- package/dist/core/graph/names.d.ts +3 -1
- package/dist/core/graph/names.js +3 -3
- package/dist/core/graph/objects.d.ts +5 -4
- package/dist/core/graph/objects.js +5 -5
- package/dist/core/graph/repo-sync/exchange.js +2 -2
- package/dist/core/graph/types.d.ts +6 -5
- package/dist/core/runs/operations.js +5 -5
- package/dist/core/runtime/broker-persona-guidance.js +2 -2
- package/dist/core/runtime/broker-protocol.d.ts +2 -7
- package/dist/core/runtime/persona.js +2 -2
- package/dist/core/runtime/roadmap.d.ts +3 -0
- package/dist/core/runtime/roadmap.js +8 -4
- package/dist/core/substrate/delivery/deliver.js +1 -1
- package/dist/core/substrate/delivery/listings.d.ts +3 -0
- package/dist/core/substrate/delivery/listings.js +5 -2
- package/dist/core/substrate/delivery/render-boot.js +26 -26
- package/dist/core/substrate/delivery/render-event.d.ts +13 -6
- package/dist/core/substrate/delivery/render-event.js +6 -4
- package/dist/daemon/api/handlers/docs.js +1 -1
- package/dist/daemon/api/handlers/human-requests.js +1 -1
- package/dist/daemon/api/handlers/objects.js +2 -5
- package/dist/daemon/api/handlers/reviews.d.ts +4 -1
- package/dist/daemon/api/handlers/reviews.js +1 -1
- package/dist/daemon/cron/settle.js +1 -1
- package/dist/daemon/cron/sinks.d.ts +3 -3
- package/dist/daemon/cron/sinks.js +2 -2
- package/dist/daemon/crtrd.js +6 -6
- package/dist/daemon/human/finish.js +6 -6
- package/dist/daemon/human/settle.d.ts +2 -3
- package/dist/daemon/human/settle.js +1 -1
- package/dist/daemon/reconcilers/action-lane.d.ts +8 -0
- package/dist/daemon/reconcilers/action-lane.js +1 -0
- package/dist/daemon/reconcilers/delivery-lane.d.ts +1 -1
- package/dist/daemon/reconcilers/delivery-lane.js +1 -1
- package/dist/shared/inbox-entry-body.js +7 -7
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +11 -11
- package/scripts/lib/isolation-env.mjs +8 -2
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/** Deleting a node with what it owns (spec §6 "Deletion follows the owner", A9). Rows are kept
|
|
2
|
-
* with `deleted_at` so every pointer resolves to "deleted" with the last preview
|
|
3
|
-
*
|
|
2
|
+
* with `deleted_at` so every pointer resolves to "deleted" with the last preview. A document a
|
|
3
|
+
* live document outside the deletion links to is kept: it stays owned by the deleted node, under
|
|
4
|
+
* its name and rules, and is collected when its last link goes (`collectUnlinked`). */
|
|
4
5
|
import type { DatabaseSync } from 'node:sqlite';
|
|
5
6
|
import type { ObjectRow } from './types.js';
|
|
6
|
-
/** Mark the node and everything it owns deleted
|
|
7
|
-
* drop the node's watches (both ends) and its `nodes` row. One savepoint, so it runs as one
|
|
7
|
+
/** Mark the node and everything it owns deleted except kept documents, which stay owned by the
|
|
8
|
+
* deleted node; drop the node's watches (both ends) and its `nodes` row. One savepoint, so it runs as one
|
|
8
9
|
* transaction or inside the caller's. Refuses a node whose managed worktree is unreconciled. */
|
|
9
10
|
export declare function deleteNodeWithOwned(db: DatabaseSync, node: ObjectRow): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var
|
|
1
|
+
var p=Object.defineProperty;var d=(e,o)=>p(e,"name",{value:o,configurable:!0});import{hasUnreconciledWorktree as u}from"../canvas/worktrees.js";import{deleteDocument as R}from"./documents.js";import{levelFor as s,publish as c}from"./events.js";import{getByRowid as w}from"./objects.js";let O=0;function T(e,o){if(o.type!=="node")throw new Error(`deleteNodeWithOwned: ${o.id} is a ${o.type}, not a node`);if(u(o.id))throw new Error(`refusing to delete node ${o.id}: its managed worktree is not reconciled`);const r=`graph_delete_${++O}`;e.exec(`SAVEPOINT ${r}`);try{f(e,o),e.exec(`RELEASE ${r}`)}catch(i){throw e.exec(`ROLLBACK TO ${r}`),e.exec(`RELEASE ${r}`),i}}d(T,"deleteNodeWithOwned");function f(e,o){const r=e.prepare(`WITH RECURSIVE sub(rowid) AS (
|
|
2
2
|
SELECT rowid FROM objects WHERE owner = ?
|
|
3
3
|
UNION SELECT o.rowid FROM objects o JOIN sub ON o.owner = sub.rowid)
|
|
4
|
-
SELECT o.* FROM objects o JOIN sub ON sub.rowid = o.rowid WHERE o.deleted_at IS NULL`).all(o.rowid),
|
|
5
|
-
WHERE e.type = 'links-to' AND e.dst = ? AND s.deleted_at IS NULL`)
|
|
4
|
+
SELECT o.* FROM objects o JOIN sub ON sub.rowid = o.rowid WHERE o.deleted_at IS NULL`).all(o.rowid),i=new Set(r.map(t=>t.rowid)),l=e.prepare(`SELECT e.src FROM edges e JOIN objects s ON s.rowid = e.src
|
|
5
|
+
WHERE e.type = 'links-to' AND e.dst = ? AND s.deleted_at IS NULL`);for(let t=!0;t;){t=!1;for(const n of r)n.type!=="document"||!i.has(n.rowid)||l.all(n.rowid).some(({src:a})=>!i.has(a))&&(i.delete(n.rowid),t=!0)}const E=new Date().toISOString();for(const t of r)if(i.has(t.rowid)){if(t.type==="document"){R(e,null,t);continue}e.prepare("UPDATE objects SET deleted_at = ? WHERE rowid = ?").run(E,t.rowid),c(e,{event:"deleted",object:w(e,t.rowid),actor:null,level:s("deleted",void 0,t.type),attrs:{}}),e.prepare("DELETE FROM watches WHERE object = ? OR watcher = ?").run(t.rowid,t.rowid)}e.prepare("UPDATE objects SET deleted_at = ? WHERE rowid = ?").run(E,o.rowid),c(e,{event:"deleted",object:w(e,o.rowid),actor:null,level:s("deleted",void 0,"node"),attrs:{}}),e.prepare("DELETE FROM watches WHERE object = ? OR watcher = ?").run(o.rowid,o.rowid),e.prepare("DELETE FROM nodes WHERE node_id = ?").run(o.id)}d(f,"deleteInside");export{T as deleteNodeWithOwned};
|
|
@@ -123,8 +123,15 @@ export declare function moveDocument(db: DatabaseSync, reader: Reader | null, do
|
|
|
123
123
|
owner?: ObjectRow;
|
|
124
124
|
}): ObjectRow;
|
|
125
125
|
/** Mark the document deleted; the row stays with its last preview. Publishes `deleted`
|
|
126
|
-
* (level 8), then ends the watches on it and drops its rules. A null actor is repo sync
|
|
126
|
+
* (level 8), then ends the watches on it and drops its rules. A null actor is repo sync or
|
|
127
|
+
* node deletion. What it linked to is re-checked by {@link collectUnlinked}. */
|
|
127
128
|
export declare function deleteDocument(db: DatabaseSync, reader: Reader | null, doc: ObjectRow, reposFn?: ReposFn): ObjectRow;
|
|
129
|
+
/** Delete each of `targets` that is a kept document nothing links to any more: a live document
|
|
130
|
+
* whose owner is deleted (node deletion kept it because a live document linked to it) and that
|
|
131
|
+
* no other live document links to. Each one deleted re-checks what it linked to, so chains of
|
|
132
|
+
* kept documents collapse; a cycle of kept documents keeps itself. Call it after a document
|
|
133
|
+
* stops linking to `targets` — it was deleted, or an edit dropped the links. */
|
|
134
|
+
export declare function collectUnlinked(db: DatabaseSync, targets: Iterable<number>, reposFn?: ReposFn): void;
|
|
128
135
|
export interface ImportedRevision {
|
|
129
136
|
rev_id?: string;
|
|
130
137
|
body_hash: string;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
var
|
|
2
|
-
WHERE rp.child = ? ORDER BY p.rev_id`).all(n.rowid);return{rowid:n.rowid,rev_id:n.rev_id,number:n.number,body_hash:n.body_hash,author:n.author===null?null:
|
|
3
|
-
AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rowid) ORDER BY r.number DESC, r.rev_id`).all(n.rowid).map(r=>K(t,r))}a(H,"heads");function
|
|
4
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(n.rowid,e.rev_id??
|
|
1
|
+
var ee=Object.defineProperty;var a=(t,n)=>ee(t,"name",{value:n,configurable:!0});import{randomUUID as te}from"node:crypto";import{CrtrError as A,notFound as v,usage as c}from"../errors.js";import{ExitCode as j}from"../../types.js";import{canChange as L,canRead as U,ownerChain as ne}from"./access.js";import{bodyOf as re,get as oe,put as F}from"./bodies.js";import{recordActedOn as R,setLinks as ie}from"./edges.js";import{publish as S}from"./events.js";import{resolveName as se}from"./names.js";import{createObject as ae,getById as ue,getByRowid as w}from"./objects.js";import{replaceRules as J,setRulesOwner as ce}from"./rules.js";import{recordWatch as k}from"./watches.js";function E(t){return t.class==="node"?t.node:t.class==="app"?t.app:t.person}a(E,"actorOf");let pe=0;function h(t,n){const e=`graph_doc_${++pe}`;t.exec(`SAVEPOINT ${e}`);try{const r=n();return t.exec(`RELEASE ${e}`),r}catch(r){throw t.exec(`ROLLBACK TO ${e}`),t.exec(`RELEASE ${e}`),r}}a(h,"atomic");function le(t,n){return new A("conflict",t,j.GENERAL,n)}a(le,"conflict");function _(t){if(t.type!=="document")throw c(`${t.type} ${t.id} is not a document`)}a(_,"assertDocument");function B(t,n){const e=ne(t,n);return(e.length?e[e.length-1]:n).type}a(B,"spaceType");function I(t,n,e){if(e!=="public"&&B(t,n)==="repo")throw c(`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>}`)."})}a(I,"assertPublicInRepo");function P(t,n,e,r){if(e.trim()===""||e!==e.trim())throw c(`invalid document name ${JSON.stringify(e)}`);const o=t.prepare("SELECT id FROM objects WHERE type = 'document' AND owner = ? AND name = ? AND deleted_at IS NULL").get(n.rowid,e);if(o&&o.id!==r?.id)throw le(`${n.name} already has a document named ${e}`,{name:e,holder:o.id})}a(P,"assertNameFree");function $(t,n,e){if(!U(t,n,e))throw v(`no document ${e.id}`)}a($,"readable");function O(t,n,e){if($(t,n,e),!L(t,n,e))throw new A("forbidden",`you cannot change ${e.name}`,j.GENERAL,{id:e.id})}a(O,"changeable");function K(t,n){const e=t.prepare(`SELECT p.rev_id FROM revision_parents rp JOIN revisions p ON p.rowid = rp.parent
|
|
2
|
+
WHERE rp.child = ? ORDER BY p.rev_id`).all(n.rowid);return{rowid:n.rowid,rev_id:n.rev_id,number:n.number,body_hash:n.body_hash,author:n.author===null?null:w(t,n.author)?.id??null,rationale:n.rationale,verbatim:n.verbatim===1,created_at:n.created_at,parents:e.map(r=>r.rev_id)}}a(K,"toRevision");function H(t,n){return t.prepare(`SELECT * FROM revisions r WHERE r.document = ?
|
|
3
|
+
AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rowid) ORDER BY r.number DESC, r.rev_id`).all(n.rowid).map(r=>K(t,r))}a(H,"heads");function me(t){return t.map(n=>n.rev_id).sort().join(",")}a(me,"headsKeyOf");function D(t,n){const e=t.prepare("SELECT * FROM documents WHERE object = ?").get(n.rowid);if(!e)throw v(`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}}a(D,"documentRecord");function x(t,n,e){const r=t.prepare(`INSERT INTO revisions (document, rev_id, number, body_hash, author, rationale, verbatim, created_at)
|
|
4
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(n.rowid,e.rev_id??te(),e.number,e.body_hash,e.author?.rowid??null,e.rationale,e.verbatim?1:0,e.created_at??new Date().toISOString()),o=t.prepare("INSERT INTO revision_parents (child, parent) VALUES (?, ?)");for(const i of e.parents)o.run(r.rowid,i);return r}a(x,"insertRevision");function de(t){const n=new Set;for(const e of t.matchAll(/\[\[([^[\]\n]+?)\]\]/g))n.add(e[1].trim());return[...n]}a(de,"linksIn");function G(t,n,e,r){const o=[],i=[];for(const u of de(r)){const s=se(t,n,u);"object"in s?o.push({written_as:u,dst:s.object}):"deleted"in s?o.push({written_as:u,dst:s.deleted}):"remote"in s||i.push(u)}return Q(t,ie(t,e,o)),i}a(G,"setRevisionLinks");function we(t,n,e){return n.type==="node"?n.id:t.prepare(`SELECT 1 FROM objects o JOIN objects d ON d.owner = o.rowid
|
|
5
5
|
WHERE o.name = ? AND o.type <> ? AND o.type IN ('person','app','repo','profile')
|
|
6
|
-
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}a(
|
|
7
|
-
WHERE e.type = 'links-to' AND e.src = ? AND e.written_as IS NOT NULL`).all(n.rowid);if(r.length===0)return e;const o=new Map;for(const i of r){if(i.deleted_at||i.written_as===i.id)continue;const u=!i.written_as.includes("/");if(u&&i.name===i.written_as)continue;const s=i.owner===null?null:
|
|
6
|
+
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}a(we,"ownerPrefix");function V(t,n,e){const r=t.prepare(`SELECT e.written_as, o.* FROM edges e JOIN objects o ON o.rowid = e.dst
|
|
7
|
+
WHERE e.type = 'links-to' AND e.src = ? AND e.written_as IS NOT NULL`).all(n.rowid);if(r.length===0)return e;const o=new Map;for(const i of r){if(i.deleted_at||i.written_as===i.id)continue;const u=!i.written_as.includes("/");if(u&&i.name===i.written_as)continue;const s=i.owner===null?null:w(t,i.owner),p=s?`${we(t,s,i.name)}/${i.name}`:i.name,l=u&&i.owner===n.owner?i.name:p;l!==i.written_as&&o.set(i.written_as,l)}return o.size===0?e:e.replace(/\[\[([^[\]\n]+?)\]\]/g,(i,u)=>{const s=o.get(u.trim());return s===void 0?i:`[[${s}]]`})}a(V,"withCurrentNames");function C(t,n,e){if(e.preview!==void 0){if(e.preview.trim()==="")throw c("a document needs a preview",{field:"preview"});t.prepare("UPDATE objects SET preview = ? WHERE rowid = ?").run(e.preview,n.rowid)}e.privacy!==void 0&&(I(t,w(t,n.owner),e.privacy),t.prepare("UPDATE objects SET privacy = ? WHERE rowid = ?").run(e.privacy,n.rowid));const r=a((i,u)=>t.prepare(`UPDATE documents SET ${i} = ? WHERE object = ?`).run(u,n.rowid),"set");e.summary!==void 0&&r("summary",e.summary),e.whyItExists!==void 0&&r("why_it_exists",e.whyItExists),e.unlisted!==void 0&&r("unlisted",e.unlisted?1:0),e.lintIgnore!==void 0&&r("lint_ignore_json",e.lintIgnore===null?null:JSON.stringify(e.lintIgnore)),e.extensions!==void 0&&r("extensions_json",e.extensions===null?null:JSON.stringify(e.extensions));const o=w(t,n.rowid);return e.rules!==void 0&&J(t,o,e.rules),o}a(C,"applyDocumentFields");function Y(t,n){if(n.owner.deleted_at)throw c(`${n.owner.name} is deleted`);if(n.preview.trim()==="")throw c("a document needs a preview",{field:"preview"});I(t,n.owner,n.privacy),P(t,n.owner,n.name);const e=ae(t,{...n.id?{id:n.id}:{},type:"document",kind:n.kind,owner:n.owner.rowid,privacy:n.privacy,name:n.name,preview:n.preview});return t.prepare("INSERT INTO documents (object, heads_key, revision) VALUES (?, '', 0)").run(e.rowid),C(t,e,{...n,preview:void 0,privacy:void 0,rules:n.rules??[]})}a(Y,"createDocument");function W(t,n){const e=H(t,n),r=me(e),o=Math.max(0,...e.map(i=>i.number));return t.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(r,o,n.rowid),{heads_key:r,revision:o,headCount:e.length}}a(W,"setHeads");function Ae(t,n,e,r,o){const i=F(r);return h(t,()=>{const u=E(n),s=Y(t,e),p=x(t,s,{number:1,body_hash:i,author:u,rationale:null,parents:[]}),{heads_key:l}=W(t,s);if(!L(t,n,s))throw v(`no owner ${e.owner.name} you can write to`);const d=G(t,n,s,r);return R(t,u,s),k(t,u,s,"write",{readHeadsKey:l}),S(t,{event:"created",object:s,actor:u,level:4,attrs:{revision:1},pointer:p.rev_id},o),{object:s,revision:1,unresolved:d}})}a(Ae,"writeDocument");function je(t,n,e){return t.prepare("SELECT read_heads_key FROM watches WHERE watcher = ? AND object = ?").get(E(n).rowid,e.rowid)?.read_heads_key??null}a(je,"readBase");function fe(t,n){let e=t;for(const{old:r,new:o}of n){if(r==="")throw c("a replacement needs non-empty old text",{field:"replace"});const i=e.indexOf(r);if(i<0)throw c(`old text not found: ${JSON.stringify(r.slice(0,120))}`,{field:"replace",old:r});if(e.indexOf(r,i+1)>=0)throw c(`old text occurs more than once: ${JSON.stringify(r.slice(0,120))}`,{field:"replace",old:r});e=e.slice(0,i)+o+e.slice(i+r.length)}return e}a(fe,"applyReplacements");function Le(t,n,e,r,o,i,u={},s){if(_(e),O(t,n,e),e.deleted_at)throw c(`${e.name} is deleted`);if(i===null)throw new A("read_first",`read ${e.name} before editing it`,j.GENERAL,{id:e.id});if(o.trim()==="")throw c("an edit needs a rationale",{field:"rationale"});const p=D(t,e);if(p.heads_key!==i)return{stale:{heads_key:p.heads_key,revision:p.revision}};const l=H(t,e);let d;if("body"in r)d=r.body;else{if(l.length!==1)throw c(`${e.name} has ${l.length} heads and needs a merge; pass the whole merged body`,{heads:l.length});d=fe(V(t,e,oe(l[0].body_hash)),r.replace)}const m=F(d);try{return h(t,()=>{const f=E(n),N=1+Math.max(0,...l.map(y=>y.number)),M=x(t,e,{number:N,body_hash:m,author:f,rationale:o,verbatim:r.verbatim===!0,parents:l.map(y=>y.rowid)}),T=M.rev_id,Z=t.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ? AND heads_key = ?").run(T,N,e.rowid,i);if(Number(Z.changes)===0){const y=D(t,e);throw new X(y.heads_key,y.revision)}const g=C(t,e,u),b=G(t,n,g,d);return R(t,f,g),k(t,f,g,"write",{readHeadsKey:T}),S(t,{event:"edited",object:g,actor:f,level:8,pointer:M.rev_id,attrs:{revision:N,from_revision:p.revision,rationale:o}},s),{revision:N,heads_key:T,unresolved:b}})}catch(f){if(f instanceof X)return{stale:{heads_key:f.heads_key,revision:f.revision}};throw f}}a(Le,"editDocument");class X extends Error{static{a(this,"StaleEdit")}heads_key;revision;constructor(n,e){super("stale"),this.heads_key=n,this.revision=e}}function ke(t,n,e,r){return _(e),n&&O(t,n,e),h(t,()=>{const o=C(t,e,r);return n&&R(t,E(n),o),o})}a(ke,"setDocumentRecord");function he(t,n,e,r){const o=E(n),{heads_key:i}=D(t,e);k(t,o,e,"read",{readHeadsKey:i}),S(t,{event:"read",object:e,actor:o,level:1,attrs:{}},r)}a(he,"recordRead");function Ie(t,n,e,r){return _(e),$(t,n,e),h(t,()=>{const o=D(t,e),i=H(t,e).map(s=>({...s,body:V(t,e,re(s.body_hash,o.source_path))})),u=t.prepare("SELECT dst, written_as FROM edges WHERE type = 'links-to' AND src = ? ORDER BY written_as").all(e.rowid).map(s=>({written_as:s.written_as??"",object:w(t,s.dst)})).filter(s=>s.object&&U(t,n,s.object)).map(s=>({...s,deleted:s.object.deleted_at!==null}));return e.deleted_at||he(t,n,e,r),{object:e,record:o,heads:i,needsMerge:i.length>1,links:u}})}a(Ie,"readDocument");function $e(t,n,e){return _(e),$(t,n,e),t.prepare("SELECT * FROM revisions WHERE document = ? ORDER BY number DESC, created_at DESC, rowid DESC").all(e.rowid).map(o=>K(t,o))}a($e,"history");function He(t,n,e,r){if(_(e),e.deleted_at)throw c(`${e.name} is deleted`);return n&&O(t,n,e),h(t,()=>{const o=r.owner??w(t,e.owner),i=r.name??e.name;P(t,o,i,e),I(t,o,e.privacy),t.prepare("UPDATE objects SET name = ?, owner = ? WHERE rowid = ?").run(i,o.rowid,e.rowid);const u=w(t,e.rowid);if(o.rowid!==e.owner&&(ce(t,u),n&&!L(t,n,u)))throw v(`no owner ${o.name} you can move documents to`);return n&&R(t,E(n),u),u})}a(He,"moveDocument");function Ce(t,n,e,r){return _(e),e.deleted_at?e:(n&&O(t,n,e),h(t,()=>{const o=q(t,n,e,r);return Q(t,z(t,e.rowid),r),o}))}a(Ce,"deleteDocument");function q(t,n,e,r){t.prepare("UPDATE objects SET deleted_at = ? WHERE rowid = ?").run(new Date().toISOString(),e.rowid);const o=w(t,e.rowid),i=n?E(n):null;return i&&R(t,i,o),S(t,{event:"deleted",object:o,actor:i,level:8,attrs:{}},r),t.prepare("DELETE FROM watches WHERE object = ?").run(e.rowid),J(t,o,[]),o}a(q,"markDeleted");function z(t,n){return t.prepare("SELECT dst FROM edges WHERE type = 'links-to' AND src = ?").all(n).map(({dst:e})=>e)}a(z,"linkTargets");function Q(t,n,e){const r=t.prepare(`SELECT 1 FROM objects d JOIN objects w ON w.rowid = d.owner
|
|
8
|
+
WHERE d.rowid = ? AND d.type = 'document' AND d.deleted_at IS NULL AND w.deleted_at IS NOT NULL
|
|
9
|
+
AND NOT EXISTS (SELECT 1 FROM edges e JOIN objects s ON s.rowid = e.src
|
|
10
|
+
WHERE e.type = 'links-to' AND e.dst = d.rowid AND e.src != d.rowid AND s.deleted_at IS NULL)`),o=[...n];for(;o.length>0;){const i=o.pop();r.get(i)!==void 0&&(q(t,null,w(t,i),e),o.push(...z(t,i)))}}a(Q,"collectUnlinked");function We(t,n){if(n.revisions.length===0)throw c(`import of ${n.name}: no revisions`);return h(t,()=>{const e=Y(t,n);t.prepare("UPDATE objects SET created_at = ? WHERE rowid = ?").run(n.revisions[0].created_at,e.rowid);let r=null;return n.revisions.forEach((o,i)=>{r=x(t,e,{...o,number:i+1,parents:r===null?[]:[r]}).rowid}),W(t,e),w(t,e.rowid)})}a(We,"importDocument");function Me(t,n,e){if(_(n),n.privacy!=="public"&&B(t,n)==="repo")throw c(`a repo owns only public documents; refusing revisions of ${n.name}`);return h(t,()=>{const r=t.prepare("SELECT rowid, number, document FROM revisions WHERE rev_id = ?");let o=e.filter(s=>!r.get(s.rev_id));for(;o.length>0;){const s=[];for(const p of o){const l=p.parents.map(m=>r.get(m));if(l.some(m=>m===void 0)){s.push(p);continue}const d=l;if(d.some(m=>m.document!==n.rowid))throw c(`revision ${p.rev_id} names a parent of another document`);x(t,n,{...p,author:null,number:1+Math.max(0,...d.map(m=>m.number)),parents:d.map(m=>m.rowid)})}if(s.length===o.length)throw c(`revisions of ${n.name} name unknown parents: ${s.map(p=>p.rev_id).join(", ")}`);o=s}const{heads_key:i,headCount:u}=W(t,n);return{heads_key:i,headCount:u}})}a(Me,"insertImportedRevisions");function Ue(t,n){const e=ue(t,n);if(!e||e.type!=="document")throw v(`no document ${n}`);return e}a(Ue,"documentById");export{E as actorOf,C as applyDocumentFields,fe as applyReplacements,Q as collectUnlinked,Ce as deleteDocument,Ue as documentById,D as documentRecord,Le as editDocument,H as heads,$e as history,We as importDocument,Me as insertImportedRevisions,de as linksIn,He as moveDocument,je as readBase,Ie as readDocument,he as recordRead,ke as setDocumentRecord,G as setRevisionLinks,V as withCurrentNames,Ae as writeDocument};
|
|
@@ -11,11 +11,12 @@ export interface Edge {
|
|
|
11
11
|
}
|
|
12
12
|
/** One row per actor and object however many times the actor acts. */
|
|
13
13
|
export declare function recordActedOn(db: DatabaseSync, actor: ObjectRow, object: ObjectRow): void;
|
|
14
|
-
/** Replace the document's `links-to` edges with the links of its current revision.
|
|
14
|
+
/** Replace the document's `links-to` edges with the links of its current revision. Answers the
|
|
15
|
+
* rowids it no longer links to, so the caller can collect a kept document that lost its last link. */
|
|
15
16
|
export declare function setLinks(db: DatabaseSync, doc: ObjectRow, links: ReadonlyArray<{
|
|
16
17
|
written_as: string;
|
|
17
18
|
dst: ObjectRow;
|
|
18
|
-
}>):
|
|
19
|
+
}>): number[];
|
|
19
20
|
/** Documents whose body links to `object`. */
|
|
20
21
|
export declare function backlinks(db: DatabaseSync, reader: Reader, object: ObjectRow): Edge[];
|
|
21
22
|
export declare function edgesOf(db: DatabaseSync, reader: Reader, object: ObjectRow, opts?: {
|
package/dist/core/graph/edges.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var l=Object.defineProperty;var a=(t,n)=>l(t,"name",{value:n,configurable:!0});import{canRead as y}from"./access.js";import{getByRowid as R}from"./objects.js";function k(t,n,e){t.prepare("INSERT OR IGNORE INTO edges (type, src, dst, written_as, created_at) VALUES ('acted-on', ?, ?, NULL, ?)").run(n.rowid,e.rowid,new Date().toISOString())}a(k,"recordActedOn");function L(t,n,e){const o=t.prepare("SELECT dst FROM edges WHERE type = 'links-to' AND src = ?").all(n.rowid).map(({dst:s})=>s);t.prepare("DELETE FROM edges WHERE type = 'links-to' AND src = ?").run(n.rowid);const c=t.prepare("INSERT OR IGNORE INTO edges (type, src, dst, written_as, created_at) VALUES ('links-to', ?, ?, ?, ?)"),d=new Date().toISOString();for(const s of e)c.run(n.rowid,s.dst.rowid,s.written_as,d);const i=new Set(e.map(s=>s.dst.rowid));return o.filter(s=>!i.has(s))}a(L,"setLinks");function _(t,n,e){return O(t,n,e,{type:"links-to",direction:"in"})}a(_,"backlinks");function O(t,n,e,o={}){if(!y(t,n,e))return[];const c=o.direction??"both",d=o.type?[o.type]:["acted-on","links-to"],i=[];for(const r of d)c!=="in"&&i.push(...t.prepare("SELECT * FROM edges WHERE type = ? AND src = ?").all(r,e.rowid)),c!=="out"&&i.push(...t.prepare("SELECT * FROM edges WHERE type = ? AND dst = ?").all(r,e.rowid));const s=new Set,E=[];for(const r of i){const f=`${r.type}|${r.src}|${r.dst}`;if(s.has(f))continue;s.add(f);const p=r.src===e.rowid?e:R(t,r.src),w=r.dst===e.rowid?e:R(t,r.dst);if(!p||!w)continue;const u=p===e?w:p;u!==e&&!y(t,n,u)||E.push({type:r.type,src:p,dst:w,written_as:r.written_as,created_at:r.created_at})}return E}a(O,"edgesOf");export{_ as backlinks,O as edgesOf,k as recordActedOn,L as setLinks};
|
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
import type { DatabaseSync } from 'node:sqlite';
|
|
4
4
|
import { type ReposFn } from './reader.js';
|
|
5
5
|
import { type Subscription } from './watches.js';
|
|
6
|
-
import { type CanvasEvent, type
|
|
7
|
-
/** The event's default level, or the caller's. Notices have no default: they carry the caller's tier.
|
|
8
|
-
|
|
6
|
+
import { type CanvasEvent, type Level, type ListenConfig, type ListenDelivery, type ObjectRow } from './types.js';
|
|
7
|
+
/** The event's default level, or the caller's. Notices have no default: they carry the caller's tier.
|
|
8
|
+
* Jobs no longer use it: their events publish at the level their kind declares. */
|
|
9
|
+
export declare function levelFor(event: string, override?: Level, objectType?: string): Level;
|
|
10
|
+
/** The level `ev` publishes at: the declared level when none is supplied; a supplied level
|
|
11
|
+
* must equal it or be one of the event's declared `levels`. Throws `unknown_event` for an
|
|
12
|
+
* event the object's kind does not declare, `invalid_level` for any other level. */
|
|
13
|
+
export declare function resolveLevel(ev: Pick<CanvasEvent, 'event' | 'object' | 'level'>): Level;
|
|
9
14
|
/** NATS-style match over dot-separated event names: `*` is one part, `>` the rest (at least one part). */
|
|
10
15
|
export declare function matchSubject(pattern: string, event: string): boolean;
|
|
11
16
|
/** Per config, the delivery of the highest threshold the level reaches; across configs, the
|
|
@@ -14,13 +19,13 @@ export declare function resolveDelivery(configs: readonly ListenConfig[], level:
|
|
|
14
19
|
/** A subscription's own config, else the watcher node's default, else the global default. */
|
|
15
20
|
export declare function listenConfigFor(db: DatabaseSync, subscription: Pick<Subscription, 'config'>, watcher: ObjectRow): ListenConfig;
|
|
16
21
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
22
|
+
* Validate the event against its object's kind (`resolveLevel`), then push it to every node watching the object itself whose subscriptions receive it — a watch on
|
|
23
|
+
* an owner hears that owner's own events, not those of the objects it owns — inside the caller's write transaction. Document and job events reach
|
|
19
24
|
* only live (active|idle) watchers; node events also reach done ones without waking them. The
|
|
20
25
|
* actor is never pushed its own act, access is re-checked per watcher, and apps, persons and
|
|
21
26
|
* other non-node watchers get nothing. Writes a `messages` row only when a delivery results.
|
|
22
27
|
* `reposFn` resolves a watcher's repos when the object lives in a repo space.
|
|
23
28
|
*/
|
|
24
|
-
export declare function publish(db: DatabaseSync,
|
|
29
|
+
export declare function publish(db: DatabaseSync, input: CanvasEvent, reposFn?: ReposFn): {
|
|
25
30
|
delivered: number;
|
|
26
31
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
var
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
var F=Object.defineProperty;var i=(t,e)=>F(t,"name",{value:e,configurable:!0});import{evalCondition as $}from"../predicate.js";import{insertEventMail as b,listenDeliveryTier as M}from"../feed/messages.js";import{ownerChain as W,readCheck as k}from"./access.js";import{publishLookups as x,readerForNode as H}from"./reader.js";import{configFromJson as m}from"./watches.js";import{CrtrError as J}from"../errors.js";import{ExitCode as B}from"../../types.js";import{eventSpec as U}from"./kinds/registry.js";import{DEFAULT_LISTEN as N}from"./types.js";const G={created:4,read:1,watched:1,edited:8,messaged:2,sent:1,configured:1,"report.update":5,"report.final":7,"report.result":5};function ce(t,e,o){if(e!==void 0)return e;if(t==="deleted")return o==="document"?8:5;if(t.startsWith("notice."))throw new Error(`${t} has no default level; pass the notice's tier as a level`);const n=G[t];if(n===void 0)throw new Error(`${t} has no default level`);return n}i(ce,"levelFor");function V(t){const e=U(t.object.type,t.event);if(t.level===void 0||t.level===e.level||e.levels?.includes(t.level))return t.level??e.level;const o=[e.level,...e.levels??[]].filter((n,r,l)=>l.indexOf(n)===r).sort((n,r)=>n-r);throw new J("invalid_level",`${t.object.type} ${t.event} publishes at level ${o.join(", ")}, not ${t.level}`,B.USAGE,{kind:t.object.type,event:t.event,level:t.level,allowed:o})}i(V,"resolveLevel");function K(t,e){const o=t.split("."),n=e.split(".");for(let r=0;r<o.length;r++){if(o[r]===">")return r===o.length-1&&n.length>r;if(r>=n.length||o[r]!=="*"&&o[r]!==n[r])return!1}return o.length===n.length}i(K,"matchSubject");const v={"next-turn":1,"after-turn":2,steer:3,interrupt:4};function X(t,e){let o=null;for(const n of t){let r=null;for(const l of n)l.threshold<=e&&(!r||l.threshold>r.threshold)&&(r=l);r&&(!o||v[r.delivery]>v[o])&&(o=r.delivery)}return o}i(X,"resolveDelivery");function ue(t,e,o){if(e.config)return e.config;const n=t.prepare("SELECT listen_default_json FROM nodes WHERE node_id = ?").get(o.id);return n?.listen_default_json?m(n.listen_default_json):N}i(ue,"listenConfigFor");const q=new Set(["messaged","sent","configured","report.update","report.final","report.result","notice.crash","notice.deadline","notice.close"]);function z(t){return t.event==="report.final"?"final":t.event==="report.result"?"completed":t.level>=7?"urgent":"update"}i(z,"inboxKind");function P(t,e){return e.type==="node"?e.id:t.prepare(`SELECT 1 FROM objects WHERE type IN ('person','app','repo','profile')
|
|
2
|
+
AND name = ? AND rowid != ? LIMIT 1`).get(e.name,e.rowid)?`${e.type}:${e.name}`:e.name}i(P,"ownerHandleOf");function Q(t,e,o){const n=e.object.owner===null?null:t.prepare("SELECT * FROM objects WHERE rowid = ?").get(e.object.owner),r=n?.id??null,l=n?P(t,n):null;return{event:e.event,level:e.level,object:{id:e.object.id,type:e.object.type,kind:e.object.kind,owner:r,owner_handle:l,name:e.object.name},actor:e.actor?.id??null,attrs:o,...e.pointer===void 0?{}:{pointer:e.pointer}}}i(Q,"pushFields");function O(t,e,o){const n=Q(t,e,o),r=e.messageBody!==void 0&&(e.event.startsWith("report.")||e.event.startsWith("notice.")),l=r?typeof e.messageBody=="object"&&e.messageBody!==null&&!Array.isArray(e.messageBody)?{...e.messageBody,push:n}:{body:e.messageBody,push:n}:{push:n},a=r&&e.event.startsWith("report.")?e.messageBody:null;return{kind:z(e),label:a?.label??`${e.event} ${e.object.type} ${e.object.name}`,...a?.bodyRef===void 0?{}:{bodyRef:a.bodyRef},...a?.disposition===void 0?{}:{disposition:a.disposition},senderNodeId:e.actor?.type==="node"?e.actor.id:null,senderName:e.actor?.name??null,bodyJson:l,event:e.event,objectId:e.object.id,level:e.level,actorId:e.actor?.id??null}}i(O,"mailInput");function Y(t){const e=t.attrs.revision,o=t.attrs.from_revision??(typeof e=="number"?e-1:null),n=t.attrs.rationale;return{...t.attrs,from_revision:o,revision:e,authors:t.actor?[t.actor.id]:[],rationales:typeof n=="string"?[n]:[]}}i(Y,"editAttrs");function Z(t,e){const o=[...t.authors??[],...e.authors.filter(n=>!(t.authors??[]).includes(n))];return{...e,from_revision:t.from_revision??null,authors:o,rationales:[...t.rationales??[],...e.rationales]}}i(Z,"mergeEdits");function fe(t,e,o=()=>[]){const n={...e,level:V(e)},r=W(t,n.object),l=q.has(n.event),a="w.object = ? AND w.watcher != ?",g="w.watcher, o.id AS watcher_id, n.status, n.listen_default_json, s.pattern, s.filter_json, s.config_json",E=t.prepare(l?`SELECT ${g} FROM watches w JOIN watch_subscriptions s ON s.watch = w.rowid
|
|
3
|
+
JOIN objects o ON o.rowid = w.watcher AND o.type = 'node' JOIN nodes n ON n.node_id = o.id WHERE ${a}`:`SELECT ${g} FROM nodes n CROSS JOIN objects o CROSS JOIN watches w CROSS JOIN watch_subscriptions s
|
|
4
|
+
WHERE n.status IN ('active','idle') AND o.id = n.node_id AND w.watcher = o.rowid AND s.watch = w.rowid AND ${a}`).all(n.object.rowid,n.actor?.rowid??-1);if(E.length===0)return{delivered:0};const S={event:n.event,level:n.level,id:n.object.id,type:n.object.type,kind:n.object.kind,actor:n.actor?.id??null,...n.attrs},p=new Map;for(const s of E){if(!K(s.pattern,n.event)||s.filter_json!==null&&!$(JSON.parse(s.filter_json),S))continue;const c=s.config_json?m(s.config_json):s.listen_default_json?m(s.listen_default_json):N,d=p.get(s.watcher)??{id:s.watcher_id,status:s.status,configs:[]};d.configs.push(c),p.set(s.watcher,d)}const R=(r.length?r[r.length-1]:n.object).type==="repo",D=i(()=>[],"noRepos"),A=k(t,n.object),I=x(t),u=[];for(const s of p.values()){const c=X(s.configs,n.level);if(c===null||!A(H(t,s.id,R?o:D,I)))continue;const d=M(c),f=s.status==="active"||s.status==="idle";u.push({nodeId:s.id,urgency:d.urgency,wake:f?d.wake:0})}if(u.length===0)return{delivered:0};if(n.event!=="edited")return b(t,O(t,n,n.attrs),u),{delivered:u.length};const _=Y(n),L=t.prepare(`SELECT d.delivery_id, d.message_id, m.body_json FROM deliveries d
|
|
4
5
|
JOIN messages m ON m.message_id = d.message_id
|
|
5
6
|
WHERE d.destination_kind = 'node_inbox' AND d.destination_id = ? AND d.state = 'pending' AND d.canceled_at IS NULL
|
|
6
|
-
AND m.event = 'edited' AND m.object_id = ?`),h=new Map,w=new Set,
|
|
7
|
+
AND m.event = 'edited' AND m.object_id = ?`),h=new Map,w=new Set,T=t.prepare("DELETE FROM deliveries WHERE delivery_id = ?");for(const s of u){const c=L.all(s.nodeId,n.object.id),d=c.at(-1);for(const j of c)T.run(j.delivery_id),w.add(j.message_id);const f=d?.message_id??null,y=h.get(f)??{prior:d?.body_json?JSON.parse(d.body_json).push?.attrs??null:null,destinations:[]};y.destinations.push(s),h.set(f,y)}for(const s of h.values())b(t,O(t,n,s.prior?Z(s.prior,_):_),s.destinations);const C=t.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)");for(const s of w)C.run(s,s);return{delivered:u.length}}i(fe,"publish");export{ce as levelFor,ue as listenConfigFor,K as matchSubject,fe as publish,X as resolveDelivery,V as resolveLevel};
|
package/dist/core/graph/jobs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
2
|
-
`,1)[0].trim()}n(
|
|
1
|
+
var f=Object.defineProperty;var n=(t,r)=>f(t,"name",{value:r,configurable:!0});import{recordActedOn as s}from"./edges.js";import{publish as u}from"./events.js";import{createObject as w,getByRowid as E}from"./objects.js";import{recordWatch as h}from"./watches.js";function d(t){return t.split(`
|
|
2
|
+
`,1)[0].trim()}n(d,"firstLine");function p(t,r){return`${d(t)} (${r})`}n(p,"previewOf");function b(t,r){return t.prepare("SELECT * FROM jobs WHERE object = ?").get(r.rowid)??null}n(b,"jobRow");function R(t,r,o){if(r.type!=="node")throw new Error(`startJob: ${r.id} is a ${r.type}, not a node`);const e=w(t,{...o.id===void 0?{}:{id:o.id},type:"job",kind:"bash",owner:r.rowid,privacy:"public",name:d(o.command)||"bash",preview:p(o.command,"running")});return t.prepare("INSERT INTO jobs (object, command, status, exit_code, job_dir, log_path) VALUES (?, ?, 'running', NULL, ?, ?)").run(e.rowid,o.command,o.jobDir,o.logPath),s(t,r,e),h(t,r,e,"job-start"),u(t,{event:"created",object:e,actor:r,attrs:{}}),e}n(R,"startJob");function T(t,r,o){const e=b(t,r);if(e===null)throw new Error(`finishJob: ${r.id} has no jobs row`);if(e.status!=="running")return r;const i="killedBy"in o,c=i?"killed":"exited",a=i?null:o.exitCode;t.prepare("UPDATE jobs SET status = ?, exit_code = ? WHERE object = ?").run(c,a,r.rowid);const m=i?"killed":`exited ${a}`;t.prepare("UPDATE objects SET preview = ? WHERE rowid = ?").run(p(e.command,m),r.rowid);const l=E(t,r.rowid);return i&&o.killedBy!==null&&s(t,o.killedBy,l),u(t,{event:c,object:l,actor:i?o.killedBy:null,attrs:i?{"log-path":e.log_path,...o.reason===void 0?{}:{reason:o.reason}}:{"exit-code":a,"log-path":e.log_path}}),l}n(T,"finishJob");export{T as finishJob,b as jobRow,R as startJob};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DocFieldsDTO, ObjectDTO } from '../../../api/dto/objects.js';
|
|
2
|
+
import { type ObjectKind } from './kind.js';
|
|
3
|
+
export interface DocumentReadData {
|
|
4
|
+
summary: string | null;
|
|
5
|
+
revision: number;
|
|
6
|
+
heads: Array<{
|
|
7
|
+
rev_id: string;
|
|
8
|
+
number: number;
|
|
9
|
+
author_id: string | null;
|
|
10
|
+
created_at: string;
|
|
11
|
+
}>;
|
|
12
|
+
/** One body per head, in `heads` order. */
|
|
13
|
+
bodies: string[];
|
|
14
|
+
needs_merge: boolean;
|
|
15
|
+
listing?: ObjectDTO[];
|
|
16
|
+
attachments?: string;
|
|
17
|
+
fields?: DocFieldsDTO;
|
|
18
|
+
}
|
|
19
|
+
export declare const documentKind: ObjectKind;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
var y=Object.defineProperty;var m=(n,e)=>y(n,"name",{value:e,configurable:!0});import{recordMemoryReadsIn as v}from"../../canvas/memory-reads.js";import{deliveryDocOf as _,nodeSubject as x,ownerHandle as $}from"../../substrate/delivery/corpus.js";import{recordContentDelivery as R}from"../../substrate/delivery/deliver.js";import{renderExplicitRead as D}from"../../substrate/delivery/render-event.js";import{canRead as S}from"../access.js";import{documentRecord as j,readDocument as k}from"../documents.js";import{loadLedger as E,saveLedger as N}from"../exposures.js";import{getByRowid as O}from"../objects.js";import{rulesOf as f}from"../rules.js";import{objectDTO as d}from"./dto.js";import{withBase as M}from"./kind.js";import{attr as l,header as I,section as L}from"./render.js";function A(n,e,t){const i=`${t.name}/`;return n.prepare(`SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND owner IS ?
|
|
2
|
+
AND substr(name, 1, ?) = ? ORDER BY name`).all(t.owner,i.length,i).filter(s=>!s.name.slice(i.length).includes("/")&&S(n,e,s)).map(s=>d(n,s))}m(A,"listingOf");function b(n,e,t){const i=e.class==="node",o=i?E(n,e.node.rowid):{node:0,preferenceSnapshot:null,objects:new Map,listed:new Map},s={db:n,reader:e,subject:i?x(n,e):null,ledger:o,channel:"transcript"},a=D(s,_(n,e,t));return i&&(R(n,e,a.delivered),N(n,o)),{attachments:a.attachments,linked:a.linked.map(u=>u.object)}}m(b,"explicitReadAttachments");function B(n,e,t){if(e.class!=="node"||t.kind!=="knowledge"&&t.kind!=="preference"||t.owner===null)return;const i=O(n,t.owner);i!==null&&v(n,e.node.id,[{name:t.name,scope:$(i),at:new Date().toISOString(),type:"read"}])}m(B,"recordExplicitMemoryRead");function T(n,e){const t=[];if(e.attachments&&t.push(e.attachments,""),t.push(I("object",n,l("privacy",n.privacy)+l("revision",e.revision))),t.push(`Preview: ${n.preview}`),e.summary&&t.push(`Summary: ${e.summary}`),e.fields!==void 0&&t.push(`<fields>
|
|
3
|
+
${JSON.stringify(e.fields,null,2)}
|
|
4
|
+
</fields>`),e.needs_merge?(e.heads.forEach((i,o)=>{t.push(`<head${l("rev",i.rev_id)}${l("number",i.number)}${l("author",i.author_id)}>`,e.bodies[o]??"","</head>")}),t.push(`This document has ${e.heads.length} heads. Your next \`crtr doc edit\` writes the merged body and joins them into one revision.`)):t.push("",e.bodies[0]??""),e.listing&&e.listing.length>0){const i=e.listing.some(a=>a.owner?.id===n.owner?.id&&a.name.startsWith(`${n.name}/`)),o=e.listing.some(a=>a.owner?.id!==n.owner?.id||!a.name.startsWith(`${n.name}/`)),s=i&&o?`Names under ${n.name}/ and documents it links to`:i?`Names under ${n.name}/`:"Documents it links to";t.push("",...L(s,e.listing))}return t.join(`
|
|
5
|
+
`)}m(T,"render");const V={name:"document",addressable:"name",events:M({edited:{level:8},deleted:{level:8}}),listItem(n,e){return{summary:j(n,e).summary??null,rules:f(n,e).length}},read(n,e,t,i){const o=k(n,e,t,i.reposFn);B(n,e,t);const s=o.record,a=A(n,e,t),{attachments:u,linked:h}=b(n,e,t),g=new Set(a.map(r=>r.id)),p=[...a,...h.filter(r=>!g.has(r.id)).map(r=>d(n,r))],c={summary:s.summary??null,revision:s.revision,heads:o.heads.map(r=>({rev_id:r.rev_id,number:r.number,author_id:r.author,created_at:r.created_at})),bodies:o.heads.map(r=>r.body),needs_merge:o.needsMerge,...p.length>0?{listing:p}:{},...u===""?{}:{attachments:u}};return i.fields&&(c.fields={why_it_exists:s.why_it_exists??null,unlisted:s.unlisted,lint_ignore:s.lint_ignore??[],extensions:s.extensions??{},rules:f(n,t).map(({matchFrontmatter:r,...w})=>({...w,...r===void 0?{}:{match_frontmatter:r}}))}),{data:c,text:T(d(n,t),c)}}};export{V as documentKind};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** An object row as the `/v1/objects` wire shape. */
|
|
2
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
3
|
+
import type { ObjectDTO } from '../../../api/dto/objects.js';
|
|
4
|
+
import type { ObjectRow } from '../types.js';
|
|
5
|
+
export declare function objectDTO(db: DatabaseSync, o: ObjectRow): ObjectDTO;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var i=Object.defineProperty;var d=(n,e)=>i(n,"name",{value:e,configurable:!0});import{getByRowid as a}from"../objects.js";function c(n,e){const t=e.owner===null?null:a(n,e.owner),r=t?{id:t.id,type:t.type,handle:t.type==="node"?t.id:t.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:r,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at}}d(c,"objectDTO");export{c as objectDTO};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{jobRow as m}from"../jobs.js";import{objectDTO as c}from"./dto.js";import{recordLiveRead as l,withBase as p}from"./kind.js";import{attr as a,header as h}from"./render.js";const f={name:"job",addressable:"id",events:p({exited:{level:7},killed:{level:7}}),read(r,s,e,d){const o=m(r,e);if(!o)throw new Error(`job object ${e.id} has no jobs row`);l(r,s,e,d.reposFn);const n=c(r,e),t={command:o.command,status:o.status,exit_code:o.exit_code,log_path:o.log_path},i=[h("object",n,a("status",t.status)+a("exit_code",t.exit_code)),`Command: ${t.command}`,`Log: ${t.log_path}`].join(`
|
|
2
|
+
`);return{data:t,text:i}}};export{f as jobKind};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** The one interface every canvas object kind implements: its declared events, how it is
|
|
2
|
+
* addressed, and how it reads. The kind name is what `objects.type` stores. */
|
|
3
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
4
|
+
import type { ReposFn } from '../reader.js';
|
|
5
|
+
import type { Level, ObjectRow, Reader } from '../types.js';
|
|
6
|
+
/** One declared event: its default level and, only where a sender may choose, the levels it may pick. */
|
|
7
|
+
export interface EventSpec {
|
|
8
|
+
level: Level;
|
|
9
|
+
levels?: readonly Level[];
|
|
10
|
+
}
|
|
11
|
+
export type EventCatalog = Readonly<Record<string, EventSpec>>;
|
|
12
|
+
/** What a read answers: the kind's structured fields and exactly the text `crtr canvas read` prints. */
|
|
13
|
+
export interface KindRead {
|
|
14
|
+
data: Record<string, unknown>;
|
|
15
|
+
text: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ReadOpts {
|
|
18
|
+
/** A document read also returns its optional fields. */
|
|
19
|
+
fields: boolean;
|
|
20
|
+
reposFn: ReposFn;
|
|
21
|
+
}
|
|
22
|
+
export interface ObjectKind {
|
|
23
|
+
/** Stored in `objects.type`. */
|
|
24
|
+
name: string;
|
|
25
|
+
/** The base events merged with the kind's own. */
|
|
26
|
+
events: EventCatalog;
|
|
27
|
+
/** `name`: found by `<owner>/<name>`; `id`: by id only; `handle`: an owner's handle. */
|
|
28
|
+
addressable: 'name' | 'id' | 'handle';
|
|
29
|
+
/** Records the read (a watch and a `read` event) and renders the object. */
|
|
30
|
+
read(db: DatabaseSync, reader: Reader, object: ObjectRow, opts: ReadOpts): KindRead;
|
|
31
|
+
/** Extra fields a list or search row carries. */
|
|
32
|
+
listItem?(db: DatabaseSync, object: ObjectRow): Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
/** Events every kind inherits. */
|
|
35
|
+
export declare const BASE_EVENTS: EventCatalog;
|
|
36
|
+
/** The base events with a kind's own merged over them. */
|
|
37
|
+
export declare function withBase(own?: EventCatalog): EventCatalog;
|
|
38
|
+
/** A live read of a non-document object: the reader watches it and its other watchers hear `read`. */
|
|
39
|
+
export declare function recordLiveRead(db: DatabaseSync, reader: Reader, object: ObjectRow, reposFn: ReposFn): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var l=Object.defineProperty;var d=(e,r)=>l(e,"name",{value:r,configurable:!0});import{actorOf as a}from"../documents.js";import{publish as c}from"../events.js";import{recordWatch as n}from"../watches.js";const f={created:{level:4},read:{level:1},watched:{level:1},deleted:{level:5},configured:{level:1}};function s(e={}){return{...f,...e}}d(s,"withBase");function h(e,r,t,i){const o=a(r);o.rowid!==t.rowid&&(n(e,o,t,"read"),c(e,{event:"read",object:t,actor:o,attrs:{}},i))}d(h,"recordLiveRead");export{f as BASE_EVENTS,h as recordLiveRead,s as withBase};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
var R=Object.defineProperty;var a=(e,t)=>R(e,"name",{value:t,configurable:!0});import{readGoal as u}from"../../runtime/kickoff.js";import{canRead as w}from"../access.js";import{objectDTO as p}from"./dto.js";import{recordLiveRead as j,withBase as L}from"./kind.js";import{attr as O,header as _,section as d}from"./render.js";const n=[3,5,7];function h(e){return e?.split(`
|
|
2
|
+
`).map(t=>t.trim()).find(t=>t!=="")??""}a(h,"firstLine");const F={name:"node",addressable:"id",events:L({messaged:{level:2},sent:{level:1},"report.update":{level:5,levels:n},"report.final":{level:7},"report.result":{level:5},"notice.crash":{level:5,levels:n},"notice.deadline":{level:5,levels:n},"notice.close":{level:5,levels:n}}),read(e,t,o,m){const E=e.prepare("SELECT status FROM nodes WHERE node_id = ?").get(o.id),s=a(l=>l.filter(i=>w(e,t,i)).map(i=>p(e,i)),"visible"),v=e.prepare(`SELECT o.* FROM nodes n JOIN objects o ON o.id = n.node_id
|
|
3
|
+
WHERE n.parent = ? AND o.deleted_at IS NULL ORDER BY o.rowid`).all(o.id),c=e.prepare("SELECT * FROM objects WHERE owner = ? AND deleted_at IS NULL ORDER BY rowid").all(o.rowid);j(e,t,o,m.reposFn);const r={status:E?.status??"deleted",mandate_first_line:h(u(o.id)),children:s(v),owned:s(c.filter(l=>l.type!=="job")),jobs:s(c.filter(l=>l.type==="job"))},f=[_("object",p(e,o),O("status",r.status)),`Mandate: ${r.mandate_first_line}`,...d("Children",r.children),...d("Owns",r.owned),...d("Jobs",r.jobs)].join(`
|
|
4
|
+
`);return{data:r,text:f}}};export{F as nodeKind};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var m=Object.defineProperty;var d=(a,t)=>m(a,"name",{value:t,configurable:!0});import{objectDTO as c}from"./dto.js";import{recordLiveRead as l,withBase as h}from"./kind.js";import{header as y}from"./render.js";function n(a){return{name:a,addressable:"handle",events:h(),read(t,o,e,s){const r=t.prepare("SELECT scheme, external_id FROM identities WHERE object = ?").get(e.rowid);l(t,o,e,s.reposFn);const i={handle:e.name,display_name:e.preview||e.name,identity:{scheme:r?.scheme??"",external_id:r?.external_id??""}},p=[y("object",c(t,e)),`Handle: ${i.handle}`,`Display name: ${i.display_name}`,`Identity: ${i.identity.scheme}:${i.identity.external_id}`].join(`
|
|
2
|
+
`);return{data:i,text:p}}}}d(n,"ownerKind");const E=[n("person"),n("app"),n("repo"),n("profile")];export{E as ownerKinds};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { EventSpec, ObjectKind } from './kind.js';
|
|
2
|
+
/** The names of every declared kind. */
|
|
3
|
+
export declare function kindNames(): string[];
|
|
4
|
+
export declare function isKind(name: string): boolean;
|
|
5
|
+
/** The kind named `name`, or throws `unknown_kind`. */
|
|
6
|
+
export declare function kindOf(name: string): ObjectKind;
|
|
7
|
+
/** The declared spec of `event` on `kind`, or throws `unknown_event` naming the declared events. */
|
|
8
|
+
export declare function eventSpec(kind: string, event: string): EventSpec;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var s=Object.defineProperty;var t=(n,e)=>s(n,"name",{value:e,configurable:!0});import{CrtrError as d}from"../../errors.js";import{ExitCode as c}from"../../../types.js";import{documentKind as u}from"./document.js";import{jobKind as m}from"./job.js";import{nodeKind as p}from"./node.js";import{ownerKinds as a}from"./owner.js";let f=null;function r(){return f??=new Map([u,p,m,...a].map(n=>[n.name,n])),f}t(r,"kinds");function k(){return[...r().keys()]}t(k,"kindNames");function K(n){return r().has(n)}t(K,"isKind");function w(n){const e=r().get(n);if(e===void 0)throw new d("unknown_kind",`no object kind named ${JSON.stringify(n)}; the kinds are ${k().join(", ")}`,c.USAGE,{kind:n});return e}t(w,"kindOf");function S(n,e){const o=w(n).events,i=Object.hasOwn(o,e)?o[e]:void 0;if(i===void 0)throw new d("unknown_event",`${n} declares no event ${JSON.stringify(e)}; it declares ${Object.keys(o).join(", ")}`,c.USAGE,{kind:n,event:e,declared:Object.keys(o)});return i}t(S,"eventSpec");export{S as eventSpec,K as isKind,k as kindNames,w as kindOf};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Text helpers every kind's `read` renders with, and the CLI renders deleted answers with.
|
|
2
|
+
* Pure: imports only DTO types, so the CLI can load it without the canvas. */
|
|
3
|
+
import type { ObjectDTO } from '../../../api/dto/objects.js';
|
|
4
|
+
export declare function attr(name: string, value: string | number | null | undefined): string;
|
|
5
|
+
/** One line per object: id, name, type/kind, owner handle and preview. */
|
|
6
|
+
export declare function objectRow(o: ObjectDTO): string;
|
|
7
|
+
/** `<tag type kind id name owner …extra>` */
|
|
8
|
+
export declare function header(tag: string, o: ObjectDTO, extra?: string): string;
|
|
9
|
+
export declare function section(title: string, rows: ObjectDTO[]): string[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var i=Object.defineProperty;var t=(e,n)=>i(e,"name",{value:n,configurable:!0});function r(e,n){return n==null||n===""?"":` ${e}="${String(n).replace(/"/g,""")}"`}t(r,"attr");function p(e){const n=e.kind?`${e.type}/${e.kind}`:e.type,d=e.owner?` owner=${e.owner.handle}`:"",$=e.deleted_at?" (deleted)":"";return`- ${e.id} [[${e.name}]] ${n}${d}${$} \u2014 ${e.preview}`}t(p,"objectRow");function u(e,n,d=""){return`<${e}${r("type",n.type)}${r("kind",n.kind)}${r("id",n.id)}${r("name",n.name)}${r("owner",n.owner?.handle)}${d}>`}t(u,"header");function a(e,n){return n.length===0?[]:[`${e}:`,...n.map(p)]}t(a,"section");export{r as attr,u as header,p as objectRow,a as section};
|
|
@@ -18,7 +18,9 @@ export declare function viewOf(db: DatabaseSync, reader: Reader): ObjectRow[];
|
|
|
18
18
|
/** The owner types that carry a handle; `<type>:<handle>/<name>` names one exactly. */
|
|
19
19
|
export declare const HANDLE_TYPES: readonly ["person", "app", "repo", "profile"];
|
|
20
20
|
/** Every owner a handle names: a node by id, and each handle-carrying owner type
|
|
21
|
-
* holding that handle (handles are unique per type, not across types).
|
|
21
|
+
* holding that handle (handles are unique per type, not across types). Any owner
|
|
22
|
+
* also answers to its object id, so `<owner object id>/<name>` names a document
|
|
23
|
+
* whatever its owner's type. */
|
|
22
24
|
export declare function ownersByHandle(db: DatabaseSync, handle: string, type?: string): ObjectRow[];
|
|
23
25
|
/** `name` may be wrapped in `[[ ]]`, a raw object id, a bare document name,
|
|
24
26
|
* `<owner handle>/<name>`, or `<type>:<owner handle>/<name>`. Under a handle
|
package/dist/core/graph/names.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
AND deleted_at IS ${o?"NOT NULL ORDER BY deleted_at DESC":"NULL"}`).all(
|
|
3
|
-
OR (type IN ('person','app','repo','profile') AND name = ?)`).all(
|
|
1
|
+
var A=Object.defineProperty;var a=(t,e)=>A(t,"name",{value:e,configurable:!0});import{isSafeNodeId as L}from"../canvas/paths.js";import{canRead as S,toolApps as j}from"./access.js";import{getById as x}from"./objects.js";function I(t,e){const r=t.prepare("SELECT * FROM objects WHERE type = 'person' AND name = 'user'").get(),o=j(t,e),u=e.class==="node"?[e.node,...e.repos,e.profile,e.app]:e.class==="app"?[e.app]:[],s=new Set;return[...u,r,...o].filter(i=>!!i&&!s.has(i.rowid)&&!!s.add(i.rowid))}a(I,"viewOf");function d(t,e,r,o){return t.prepare(`SELECT * FROM objects WHERE type = 'document' AND owner = ? AND name = ?
|
|
2
|
+
AND deleted_at IS ${o?"NOT NULL ORDER BY deleted_at DESC":"NULL"}`).all(e.rowid,r)}a(d,"documentsNamed");const h=["person","app","repo","profile"],O=/^(person|app|repo|profile|node):([^/]+)\/(.+)$/;function T(t,e,r){return r==="node"?t.prepare("SELECT * FROM objects WHERE type = 'node' AND id = ?").all(e):r?t.prepare("SELECT * FROM objects WHERE type = ? AND (name = ? OR id = ?)").all(r,e,e):t.prepare(`SELECT * FROM objects WHERE (type = 'node' AND id = ?)
|
|
3
|
+
OR (type IN ('person','app','repo','profile') AND (name = ? OR id = ?))`).all(e,e,e)}a(T,"ownersByHandle");function F(t,e,r){let o=r.trim().replace(/^\[\[(.*)\]\]$/,"$1").trim();const u=/^([^:/]+):reports\/(.+?)(?:\.md)?$/.exec(o);u&&!O.test(o)&&(o=`${u[1]}/reports/${u[2]}`);const s=a(c=>S(t,e,c),"readable"),i=x(t,o);if(i&&s(i))return i.deleted_at?{deleted:i}:{object:i};const R=I(t,e);for(const c of R){const p=d(t,c,o,!1).find(s);if(p)return{object:p}}const f=O.exec(o),N=o.indexOf("/");if(f||N>0){const c=f?f[2]:o.slice(0,N),p=f?f[3]:o.slice(N+1),E=T(t,c,f?.[1]),w=a(n=>{const l=R.findIndex(y=>y.rowid===n.rowid);return l<0?1/0:l},"rank"),g=E.filter(n=>w(n)<1/0).sort((n,l)=>w(n)-w(l));for(const n of g){const l=d(t,n,p,!1).find(s);if(l)return{object:l}}const m=E.flatMap(n=>d(t,n,p,!1)).filter(s);if(m.length===1)return{object:m[0]};if(m.length>1)return{ambiguous:m};const D=E.flatMap(n=>d(t,n,p,!0)).find(s);return D?{deleted:D}:!f&&E.length===0&&L(c)?{remote:o}:{missing:!0}}for(const c of R){const p=d(t,c,o,!0).find(s);if(p)return{deleted:p}}return{missing:!0}}a(F,"resolveName");export{h as HANDLE_TYPES,T as ownersByHandle,F as resolveName,I as viewOf};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
-
import type { ObjectRow,
|
|
2
|
+
import type { ObjectRow, Privacy, Reader } from './types.js';
|
|
3
|
+
export type OwnerType = 'person' | 'app' | 'repo' | 'profile';
|
|
3
4
|
export declare function getById(db: DatabaseSync, id: string): ObjectRow | null;
|
|
4
5
|
export declare function getByRowid(db: DatabaseSync, rowid: number): ObjectRow | null;
|
|
5
6
|
export declare function createObject(db: DatabaseSync, spec: {
|
|
6
7
|
id?: string;
|
|
7
|
-
type:
|
|
8
|
+
type: string;
|
|
8
9
|
kind: string | null;
|
|
9
10
|
owner: number | null;
|
|
10
11
|
privacy: Privacy;
|
|
@@ -15,7 +16,7 @@ export declare function createObject(db: DatabaseSync, spec: {
|
|
|
15
16
|
* (scheme, externalId). Handles are unique per owner type: a handle already held
|
|
16
17
|
* by a different identity of the same type throws `conflict` naming both; it is
|
|
17
18
|
* never renamed silently. */
|
|
18
|
-
export declare function ensureOwner(db: DatabaseSync, type:
|
|
19
|
+
export declare function ensureOwner(db: DatabaseSync, type: OwnerType, scheme: string, externalId: string, handle: string, owner?: ObjectRow): ObjectRow;
|
|
19
20
|
/** The owner object of a node grantee: `user:local` → person `user`; `app:<x>` → app `<x>`. */
|
|
20
21
|
export declare function ownerForGrantee(db: DatabaseSync, grantee: string): ObjectRow;
|
|
21
22
|
/** The owner's live document named `name`, or null. */
|
|
@@ -24,7 +25,7 @@ export declare function nodeObject(db: DatabaseSync, nodeId: string): ObjectRow;
|
|
|
24
25
|
export declare function setObjectName(db: DatabaseSync, object: ObjectRow, name: string): void;
|
|
25
26
|
export declare function setObjectKind(db: DatabaseSync, object: ObjectRow, kind: string | null): void;
|
|
26
27
|
export interface ObjectFilter {
|
|
27
|
-
type?:
|
|
28
|
+
type?: string;
|
|
28
29
|
kind?: string;
|
|
29
30
|
owner?: ObjectRow;
|
|
30
31
|
privacy?: Privacy;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
var S=Object.defineProperty;var s=(o,e)=>S(o,"name",{value:e,configurable:!0});import{CrtrError as L,notFound as j}from"../errors.js";import{ExitCode as b}from"../../types.js";import{newNodeId as T}from"../runtime/nodes.js";import{canRead as l}from"./access.js";import{bodyOf as
|
|
2
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(e.id??T(),e.type,e.kind,e.owner,e.privacy,e.name,e.preview,new Date().toISOString())}s(
|
|
1
|
+
var S=Object.defineProperty;var s=(o,e)=>S(o,"name",{value:e,configurable:!0});import{CrtrError as L,notFound as j}from"../errors.js";import{ExitCode as b}from"../../types.js";import{newNodeId as T}from"../runtime/nodes.js";import{kindOf as x}from"./kinds/registry.js";import{canRead as l}from"./access.js";import{bodyOf as y}from"./bodies.js";const I=new Set(["person","app","repo","profile"]);function h(o){return o||null}s(h,"rowFrom");function _(o,e){return h(o.prepare("SELECT * FROM objects WHERE id = ?").get(e))}s(_,"getById");function M(o,e){return h(o.prepare("SELECT * FROM objects WHERE rowid = ?").get(e))}s(M,"getByRowid");function g(o,e){return x(e.type),o.prepare(`INSERT INTO objects (id, type, kind, owner, privacy, name, preview, created_at)
|
|
2
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(e.id??T(),e.type,e.kind,e.owner,e.privacy,e.name,e.preview,new Date().toISOString())}s(g,"createObject");function R(o,e,t,p,r,n){if(!I.has(e))throw new Error(`ensureOwner: ${e} is not an owner type`);const a=o.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object
|
|
3
3
|
WHERE i.scheme = ? AND i.external_id = ?`).get(t,p);if(a)return a;const c=o.prepare(`SELECT o.id, o.type, i.scheme, i.external_id FROM objects o
|
|
4
4
|
LEFT JOIN identities i ON i.object = o.rowid
|
|
5
|
-
WHERE o.name = ? AND o.type = ?`).get(r,e);if(c)throw new L("conflict",`handle "${r}" is held by ${c.type} ${c.id} (${c.scheme}:${c.external_id}); cannot give it to ${e} ${t}:${p}`,b.GENERAL,{handle:r,holder:c.id});const d=
|
|
5
|
+
WHERE o.name = ? AND o.type = ?`).get(r,e);if(c)throw new L("conflict",`handle "${r}" is held by ${c.type} ${c.id} (${c.scheme}:${c.external_id}); cannot give it to ${e} ${t}:${p}`,b.GENERAL,{handle:r,holder:c.id});const d=g(o,{type:e,kind:null,owner:e==="profile"?n?.rowid??null:null,privacy:"public",name:r,preview:""});return o.prepare("INSERT INTO identities (object, scheme, external_id) VALUES (?, ?, ?)").run(d.rowid,t,p),d}s(R,"ensureOwner");function k(o,e){if(e==="user:local")return R(o,"person","local-user",e,"user");if(/^app:[a-z0-9][a-z0-9._-]*$/.test(e))return R(o,"app","app",e,e.slice(4));throw new Error(`node grantee ${JSON.stringify(e)} is neither app:<name> nor user:local`)}s(k,"ownerForGrantee");function U(o,e,t){return h(o.prepare("SELECT * FROM objects WHERE type = 'document' AND owner = ? AND name = ? AND deleted_at IS NULL").get(e,t))}s(U,"liveDocumentNamed");function P(o,e){const t=_(o,e);if(!t||t.type!=="node")throw j(`no node object ${e}`);return t}s(P,"nodeObject");function B(o,e,t){o.prepare("UPDATE objects SET name = ? WHERE rowid = ?").run(t,e.rowid)}s(B,"setObjectName");function J(o,e,t){o.prepare("UPDATE objects SET kind = ? WHERE rowid = ?").run(t,e.rowid)}s(J,"setObjectKind");function K(o,e,t,p={}){const r=["rowid > ?"],n=[p.after_rowid??0];return t.type!==void 0&&(r.push("type = ?"),n.push(t.type)),t.kind!==void 0&&(r.push("kind = ?"),n.push(t.kind)),t.owner!==void 0&&(r.push("owner = ?"),n.push(t.owner.rowid)),t.privacy!==void 0&&(r.push("privacy = ?"),n.push(t.privacy)),t.name!==void 0&&(r.push("name = ?"),n.push(t.name)),r.push(t.deleted?"deleted_at IS NOT NULL":"deleted_at IS NULL"),A(o,e,`SELECT * FROM objects WHERE ${r.join(" AND ")}`,n,Math.min(p.limit??100,100))}s(K,"listObjects");function Y(o,e,t){const r=[],n=`%${t.text.replace(/[\\%_]/g,i=>`\\${i}`)}%`;if(!t.grep){const i=o.prepare(`SELECT o.*, d.summary AS _summary FROM objects o LEFT JOIN documents d ON d.object = o.rowid
|
|
6
6
|
WHERE o.deleted_at IS NULL AND (o.name LIKE ?1 ESCAPE '\\' OR o.preview LIKE ?1 ESCAPE '\\' OR d.summary LIKE ?1 ESCAPE '\\')
|
|
7
7
|
ORDER BY o.rowid`).all(n),u=t.text.toLowerCase();for(const m of i){const{_summary:w,...E}=m,f=E;if(!l(o,e,f))continue;const O=f.name.toLowerCase().includes(u)?"name":f.preview.toLowerCase().includes(u)?"preview":"summary";if(r.push({object:f,field:O,...O==="summary"?{text:String(w)}:{}}),r.length>=100)return r}}if(!t.body&&!t.grep)return r;const a=new Set(r.map(i=>i.object.rowid)),c=t.grep?(i=>u=>i.test(u))(new RegExp(t.text)):i=>i.includes(t.text),d=o.prepare("SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL ORDER BY rowid").all(),N=o.prepare(`SELECT r.body_hash, d.source_path FROM revisions r JOIN documents d ON d.object = r.document WHERE r.document = ?
|
|
8
|
-
AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rowid)`);for(const i of d)if(!(a.has(i.rowid)||!l(o,e,i)))for(const{body_hash:u,source_path:m}of N.all(i.rowid)){const w=
|
|
9
|
-
`),E=w.findIndex(c);if(!(E<0)){if(r.push({object:i,field:"body",line:E+1,text:w[E]}),r.length>=100)return r;break}}return r}s(
|
|
8
|
+
AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rowid)`);for(const i of d)if(!(a.has(i.rowid)||!l(o,e,i)))for(const{body_hash:u,source_path:m}of N.all(i.rowid)){const w=y(u,m).split(`
|
|
9
|
+
`),E=w.findIndex(c);if(!(E<0)){if(r.push({object:i,field:"body",line:E+1,text:w[E]}),r.length>=100)return r;break}}return r}s(Y,"searchObjects");function A(o,e,t,p,r){const n=[],a=o.prepare(`${t} ORDER BY rowid LIMIT ${r*4}`);for(;;){const c=a.all(...p);for(const d of c)if(l(o,e,d)&&n.push(d),n.length===r)return n;if(c.length<r*4)return n;p[0]=c[c.length-1].rowid}}s(A,"readablePages");export{g as createObject,R as ensureOwner,_ as getById,M as getByRowid,K as listObjects,U as liveDocumentNamed,P as nodeObject,k as ownerForGrantee,Y as searchObjects,J as setObjectKind,B as setObjectName};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var A=Object.defineProperty;var f=(e,t)=>A(e,"name",{value:t,configurable:!0});import{get as B,put as T}from"../bodies.js";import{setLinks as j}from"../edges.js";import{deleteDocument as S,documentRecord as D,heads as M,importDocument as
|
|
2
|
-
WHERE e.type = 'links-to' AND e.src = ? ORDER BY e.written_as, o.id`),d=new Map;for(const n of l){const m=D(e,n),y=new Map((i.get(n.id)?.revisions??[]).map(s=>[s.rev_id,s])),h=new Set(M(e,n).map(s=>s.rev_id)),g=o.all(n.rowid).map(s=>({written_as:s.written_as??"",dst:s.id})),v=u.all(n.rowid).map(s=>y.get(s.rev_id)??{rev_id:s.rev_id,number:s.number,parents:p.all(s.rowid).map(O=>O.rev_id),body:B(s.body_hash),body_hash:s.body_hash,author:s.author===null?null:
|
|
1
|
+
var A=Object.defineProperty;var f=(e,t)=>A(e,"name",{value:t,configurable:!0});import{get as B,put as T}from"../bodies.js";import{setLinks as j}from"../edges.js";import{collectUnlinked as H,deleteDocument as S,documentRecord as D,heads as M,importDocument as W,insertImportedRevisions as F,moveDocument as J,setDocumentRecord as P}from"../documents.js";import{publish as $}from"../events.js";import{getById as C,getByRowid as Y,setObjectKind as K}from"../objects.js";import{rulesOf as z}from"../rules.js";import{SPACES as U}from"../spaces/index.js";import{docPath as V,parseDoc as E,serializeDoc as q}from"./ref-format.js";function I(e){const t=U[e.type];if(e.owner!==null||!t||t.sync!=="repo-ref")throw new Error(`${e.type} ${e.id} is not a space that syncs through a repo ref`)}f(I,"assertRepoRefSpace");function G(e){const t=new Map;for(const[r,i]of e??[]){const l=E(r,i);l.ok&&t.set(l.doc.id,l.doc)}return t}f(G,"parseMap");function de(e,t,r){I(t);const i=G(r),l=e.prepare("SELECT * FROM objects WHERE type = 'document' AND owner = ? AND privacy = 'public' ORDER BY rowid").all(t.rowid),u=e.prepare("SELECT rowid, rev_id, number, body_hash, author, rationale, created_at FROM revisions WHERE document = ? ORDER BY number, rev_id"),p=e.prepare("SELECT p.rev_id FROM revision_parents rp JOIN revisions p ON p.rowid = rp.parent WHERE rp.child = ?"),o=e.prepare(`SELECT e.written_as, o.id FROM edges e JOIN objects o ON o.rowid = e.dst
|
|
2
|
+
WHERE e.type = 'links-to' AND e.src = ? ORDER BY e.written_as, o.id`),d=new Map;for(const n of l){const m=D(e,n),y=new Map((i.get(n.id)?.revisions??[]).map(s=>[s.rev_id,s])),h=new Set(M(e,n).map(s=>s.rev_id)),g=o.all(n.rowid).map(s=>({written_as:s.written_as??"",dst:s.id})),v=u.all(n.rowid).map(s=>y.get(s.rev_id)??{rev_id:s.rev_id,number:s.number,parents:p.all(s.rowid).map(O=>O.rev_id),body:B(s.body_hash),body_hash:s.body_hash,author:s.author===null?null:Y(e,s.author)?.id??null,rationale:s.rationale,created_at:s.created_at,links:h.has(s.rev_id)?g:[]});if(v.length===0)continue;const R=z(e,n).map(s=>({position:s.position,on:s.on,match:s.match??null,match_frontmatter:s.matchFrontmatter??null,if:s.if??null,deliver:s.deliver})),_={id:n.id,name:n.name,kind:n.kind??"document",privacy:"public",preview:n.preview,summary:m.summary,why_it_exists:m.why_it_exists,unlisted:m.unlisted,lint_ignore:m.lint_ignore,extensions:m.extensions,created_at:n.created_at,deleted_at:n.deleted_at,rules:R,revisions:v};d.set(V(n.id),q(_))}return d}f(de,"exportRepo");function Q(e){return[...e].sort((t,r)=>t.position-r.position).map(t=>({on:t.on,deliver:t.deliver,...t.match!==null&&t.match!==void 0?{match:t.match}:{},...t.match_frontmatter!==null&&t.match_frontmatter!==void 0?{matchFrontmatter:t.match_frontmatter}:{},...t.if!==null&&t.if!==void 0?{if:t.if}:{}}))}f(Q,"toRuleSpecs");const X=f((e,t)=>JSON.stringify(e??null)===JSON.stringify(t??null),"same");function N(e,t){const r={},i=f(l=>!e||!X(e[l],t[l]),"differs");return i("preview")&&(r.preview=t.preview),i("summary")&&(r.summary=t.summary),i("why_it_exists")&&(r.whyItExists=t.why_it_exists),i("unlisted")&&(r.unlisted=t.unlisted),i("lint_ignore")&&(r.lintIgnore=t.lint_ignore),i("extensions")&&(r.extensions=t.extensions),i("rules")&&(r.rules=Q(t.rules)),r}f(N,"changedFields");let Z=0;function L(e,t){const r=`repo_sync_import_${++Z}`;e.exec(`SAVEPOINT ${r}`);try{const i=t();return e.exec(`RELEASE ${r}`),i}catch(i){throw e.exec(`ROLLBACK TO ${r}`),e.exec(`RELEASE ${r}`),i}}f(L,"atomic");function le(e,t,r,i,l){I(t);const u={created:0,revisions:0,deleted:0,needsMerge:[],refused:[]},p=[];for(const[o,d]of i){if(r.get(o)===d)continue;const n=E(o,d);if(!n.ok){u.refused.push({path:o,reason:n.reason});continue}const m=r.has(o)?E(o,r.get(o)):null;p.push({path:o,prior:m?.ok?m.doc:null,doc:n.doc})}for(const[o,d]of r){if(i.has(o))continue;const n=E(o,d);n.ok&&p.push({path:o,prior:n.doc,doc:null,priorId:n.doc.id})}for(const o of p)for(const d of o.doc?.revisions??[])T(d.body);return L(e,()=>{for(const o of p)try{L(e,()=>b(e,t,o.prior,o.doc,o.priorId??o.doc.id,u,l))}catch(d){u.refused.push({path:o.path,reason:d.message})}}),u}f(le,"importTree");function b(e,t,r,i,l,u,p){const o=C(e,l);if(o&&(o.type!=="document"||o.owner!==t.rowid))throw new Error(`id ${l} is held by ${o.type} ${o.name} outside ${t.name}`);if(!i){o&&!o.deleted_at&&(S(e,null,o,p),u.deleted++);return}const d=e.prepare("SELECT count(*) AS n FROM revisions WHERE document = ?");let n,m=!1,y=0,h=0;if(o){n=o,y=D(e,n).revision;const a=i.kind;(r?r.kind!==a:n.kind!==a)&&K(e,n,a);const c=N(r,i);Object.keys(c).length>0&&(n=P(e,null,n,c)),i.name!==n.name&&!n.deleted_at&&(n=J(e,null,n,{name:i.name}))}else{const c=i.revisions.filter(w=>w.parents.length===0).sort((w,x)=>w.number-x.number||w.rev_id.localeCompare(x.rev_id))[0];if(!c)throw new Error(`${i.name}: no root revision`);n=W(e,{id:i.id,owner:t,name:i.name,kind:i.kind,preview:i.preview,privacy:"public",...N(null,i),revisions:[{rev_id:c.rev_id,body_hash:c.body_hash,author:null,rationale:c.rationale,created_at:c.created_at}]}),m=!0,h=1}const g=d.get(n.rowid).n,{headCount:v}=F(e,n,i.revisions.map(a=>({rev_id:a.rev_id,parents:a.parents,body_hash:a.body_hash,rationale:a.rationale,created_at:a.created_at})));h+=d.get(n.rowid).n-g,u.revisions+=h;const _=M(e,n)[0],s=new Map(i.revisions.map(a=>[a.rev_id,a]));if(h>0){const a=(s.get(_.rev_id)?.links??[]).map(c=>({written_as:c.written_as,dst:C(e,c.dst)})).filter(c=>c.dst!==null);H(e,j(e,n,a),p)}if(v>1&&u.needsMerge.push(n.id),i.deleted_at&&!n.deleted_at){S(e,null,n,p),u.deleted++;return}if(m){u.created++,$(e,{event:"created",object:n,actor:null,level:4,attrs:{revision:_.number},pointer:_.rev_id},p);return}if(h===0)return;const k=i.revisions.filter(a=>!r?.revisions.some(c=>c.rev_id===a.rev_id)).sort((a,c)=>c.number-a.number||c.created_at.localeCompare(a.created_at))[0]??s.get(_.rev_id);$(e,{event:"edited",object:n,actor:null,level:8,pointer:_.rev_id,attrs:{revision:_.number,from_revision:y,rationale:k?.rationale??null,author:k?.author??null,heads:v,needs_merge:v>1}},p)}f(b,"importOne");export{de as exportRepo,le as importTree};
|