@north-light/cli 0.3.549 → 0.3.550
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/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/custom-objects.d.ts +1 -1
- package/dist/api/dto/docs.d.ts +12 -0
- package/dist/api/dto/objects.d.ts +17 -1
- package/dist/api/dto/objects.js +1 -1
- package/dist/api/dto/permissions.d.ts +76 -0
- package/dist/api/dto/permissions.js +1 -0
- package/dist/api/index.d.ts +3 -2
- package/dist/api/index.js +1 -1
- package/dist/api/routes.d.ts +4 -0
- package/dist/api/routes.js +1 -1
- package/dist/builtin-memory/crouter-concepts/documents.md +1 -1
- package/dist/builtin-memory/crouter-concepts/scopes-and-trust.md +2 -2
- package/dist/builtin-memory/crouter-sdk/release-notes.md +11 -0
- package/dist/builtin-memory/internal/INDEX.md +2 -1
- package/dist/builtin-memory/internal/agent-shaping.md +1 -1
- package/dist/builtin-memory/internal/storage-tiers.md +1 -1
- package/dist/builtin-memory/northlight-apps/guides/files-and-documents.md +3 -3
- package/dist/builtin-memory/northlight-apps/guides/many-users.md +1 -1
- package/dist/builtin-memory/northlight-apps/guides/test-your-app.md +1 -1
- package/dist/clients/attach/viewer.js +9 -9
- package/dist/commands/canvas/list.js +2 -2
- package/dist/commands/canvas/share.d.ts +2 -0
- package/dist/commands/canvas/share.js +2 -0
- package/dist/commands/canvas.js +1 -1
- package/dist/commands/doc/edit.js +1 -1
- package/dist/commands/doc/shared.js +1 -1
- package/dist/commands/doc/write.js +1 -1
- package/dist/commands/sys/sync-skills.js +2 -2
- package/dist/core/canvas/canvas.js +20 -20
- package/dist/core/canvas/db.js +4 -4
- package/dist/core/canvas/export-sqlite.js +2 -2
- package/dist/core/canvas/import-sqlite.js +5 -5
- package/dist/core/canvas/outcome-actions.js +3 -3
- package/dist/core/canvas/person.js +11 -9
- package/dist/core/canvas/schema-pg.sql +49 -5
- package/dist/core/environments.js +1 -1
- package/dist/core/feed/feed.js +1 -1
- package/dist/core/grants/mirror.d.ts +9 -1
- package/dist/core/grants/mirror.js +2 -2
- package/dist/core/grants/sync.js +1 -1
- package/dist/core/graph/access.d.ts +8 -49
- package/dist/core/graph/access.js +4 -7
- package/dist/core/graph/custom.d.ts +5 -2
- package/dist/core/graph/custom.js +9 -9
- package/dist/core/graph/deletion.js +5 -4
- package/dist/core/graph/documents.d.ts +12 -3
- package/dist/core/graph/documents.js +13 -7
- package/dist/core/graph/edges.js +3 -3
- package/dist/core/graph/events.js +12 -8
- package/dist/core/graph/kinds/document.js +4 -4
- package/dist/core/graph/kinds/kind.js +1 -1
- package/dist/core/graph/kinds/node.js +5 -5
- package/dist/core/graph/names.d.ts +16 -1
- package/dist/core/graph/names.js +11 -3
- package/dist/core/graph/objects.d.ts +19 -2
- package/dist/core/graph/objects.js +16 -10
- package/dist/core/graph/package-docs.js +5 -5
- package/dist/core/graph/package-files.js +2 -2
- package/dist/core/graph/reader.js +3 -3
- package/dist/core/graph/repo-sync/exchange.js +2 -2
- package/dist/core/graph/rules.d.ts +7 -5
- package/dist/core/graph/rules.js +3 -3
- package/dist/core/graph/types.d.ts +12 -1
- package/dist/core/graph/types.js +1 -1
- package/dist/core/graph/watches.d.ts +1 -1
- package/dist/core/graph/watches.js +9 -8
- package/dist/core/peer/envelope.d.ts +1 -1
- package/dist/core/peer/envelope.js +1 -1
- package/dist/core/peer/stubs.d.ts +1 -1
- package/dist/core/peer/stubs.js +1 -1
- package/dist/core/permissions/boot.d.ts +17 -0
- package/dist/core/permissions/boot.js +18 -0
- package/dist/core/permissions/builtin.d.ts +8 -0
- package/dist/core/permissions/builtin.js +20 -0
- package/dist/core/permissions/ceiling.d.ts +57 -0
- package/dist/core/permissions/ceiling.js +22 -0
- package/dist/core/permissions/compile.d.ts +14 -0
- package/dist/core/permissions/compile.js +1 -0
- package/dist/core/permissions/errors.d.ts +7 -0
- package/dist/core/permissions/errors.js +1 -0
- package/dist/core/permissions/evaluator.d.ts +28 -0
- package/dist/core/permissions/evaluator.js +6 -0
- package/dist/core/permissions/facts.d.ts +56 -0
- package/dist/core/permissions/facts.js +2 -0
- package/dist/core/permissions/schema-parser.d.ts +29 -0
- package/dist/core/permissions/schema-parser.js +2 -0
- package/dist/core/permissions/schemas.d.ts +21 -0
- package/dist/core/permissions/schemas.js +2 -0
- package/dist/core/permissions/sql.d.ts +16 -0
- package/dist/core/permissions/sql.js +1 -0
- package/dist/core/runtime/broker/frame-client.d.ts +1 -1
- package/dist/core/runtime/broker/frame-client.js +1 -1
- package/dist/core/runtime/broker/frame-dispatch.d.ts +1 -1
- package/dist/core/runtime/broker/frame-dispatch.js +1 -1
- package/dist/core/runtime/broker/frame-read.d.ts +4 -3
- package/dist/core/runtime/broker/frame-read.js +1 -1
- package/dist/core/runtime/broker-protocol.d.ts +1 -1
- package/dist/core/runtime/host.js +3 -3
- package/dist/core/runtime/placement.js +1 -1
- package/dist/core/runtime/roadmap.js +1 -1
- package/dist/core/substrate/delivery/corpus.d.ts +2 -2
- package/dist/core/substrate/delivery/corpus.js +3 -3
- package/dist/core/substrate/delivery/deliver.js +2 -2
- package/dist/core/substrate/delivery/render-event.js +6 -6
- package/dist/core/substrate/subject-fields.d.ts +2 -0
- package/dist/core/substrate/subject.js +1 -1
- package/dist/daemon/api/app-listener.js +12 -12
- package/dist/daemon/api/bridge.d.ts +1 -1
- package/dist/daemon/api/bridge.js +4 -4
- package/dist/daemon/api/handlers/attach.d.ts +4 -1
- package/dist/daemon/api/handlers/attach.js +1 -1
- package/dist/daemon/api/handlers/canvas.js +5 -5
- package/dist/daemon/api/handlers/check.d.ts +2 -0
- package/dist/daemon/api/handlers/check.js +1 -0
- package/dist/daemon/api/handlers/custom-objects.js +2 -2
- package/dist/daemon/api/handlers/docs.js +1 -1
- package/dist/daemon/api/handlers/files.js +1 -1
- package/dist/daemon/api/handlers/memory.js +7 -6
- package/dist/daemon/api/handlers/nodes.js +1 -1
- package/dist/daemon/api/handlers/objects.js +3 -3
- package/dist/daemon/api/handlers/relations.d.ts +2 -0
- package/dist/daemon/api/handlers/relations.js +1 -0
- package/dist/daemon/api/handlers/schemas.d.ts +12 -0
- package/dist/daemon/api/handlers/schemas.js +1 -0
- package/dist/daemon/api/handlers/shares.d.ts +2 -0
- package/dist/daemon/api/handlers/shares.js +1 -0
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/router.d.ts +10 -6
- package/dist/daemon/api/router.js +1 -1
- package/dist/daemon/api/server.js +1 -1
- package/dist/daemon/crtrd.js +12 -12
- package/dist/daemon/reconcilers/delivery-lane.js +2 -2
- package/dist/migrations/004-canvas-documents/fields.d.ts +1 -1
- package/dist/migrations/004-canvas-documents/fields.js +2 -2
- package/docs/apps/guides/files-and-documents.md +3 -3
- package/docs/apps/guides/many-users.md +1 -1
- package/docs/apps/guides/test-your-app.md +1 -1
- package/docs/cli/documents-and-preferences.md +1 -1
- package/docs/concepts/documents.md +1 -1
- package/docs/concepts/scopes-and-trust.md +2 -2
- package/docs/sdk/release-notes.md +11 -0
- package/package.json +2 -2
- package/packages/crouter-identity/dist/index.js +14 -1
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
- package/dist/core/graph/spaces/app.d.ts +0 -3
- package/dist/core/graph/spaces/app.js +0 -1
- package/dist/core/graph/spaces/index.d.ts +0 -18
- package/dist/core/graph/spaces/index.js +0 -1
- package/dist/core/graph/spaces/own-memory.d.ts +0 -18
- package/dist/core/graph/spaces/own-memory.js +0 -1
- package/dist/core/graph/spaces/person.d.ts +0 -5
- package/dist/core/graph/spaces/person.js +0 -3
- package/dist/core/graph/spaces/repo.d.ts +0 -14
- package/dist/core/graph/spaces/repo.js +0 -1
- package/dist/core/runs/confinement.d.ts +0 -25
- package/dist/core/runs/confinement.js +0 -2
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
var
|
|
2
|
-
SELECT owner,
|
|
1
|
+
var c=Object.defineProperty;var n=(e,t)=>c(e,"name",{value:t,configurable:!0});import{check as r,resolveReader as p}from"../permissions/evaluator.js";async function s(e,t){return await e.prepare(`WITH RECURSIVE chain(id, owner, depth) AS (
|
|
2
|
+
SELECT id, owner, 0 FROM objects WHERE id = ?
|
|
3
3
|
UNION ALL
|
|
4
|
-
SELECT o.owner, c.depth + 1 FROM objects o JOIN chain c ON o.id = c.
|
|
5
|
-
) SELECT o.* FROM chain c
|
|
6
|
-
SELECT 1 FROM objects o JOIN documents d ON d.object = o.id
|
|
7
|
-
WHERE o.owner = a.id AND o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL)
|
|
8
|
-
ORDER BY a.name = 'crtr', a.name`).all()).filter(i=>f(n,t,i))}s(v,"toolApps");function f(n,t,a){return a.type==="app"}s(f,"mayUseTools");const T=s(n=>n.class!=="app"||n.place!==void 0,"isRun");async function A(n,t,a){const i=`${(await l(n,a)).external_id.replace(/^app:/,"")}:`;return t.some(e=>e.startsWith(i)&&!e.slice(i.length).includes(":"))}s(A,"holdsProviderGroup");async function I(n,t,a){return t.class==="person"?!0:t.class==="object"?t.owner.id===a.id:t.class==="app"?t.app.id===a.id||a.type==="profile"&&a.owner===t.app.id:t.app.id===a.id||a.type==="profile"&&t.profile?.id===a.id||a.type==="repo"&&await L(n,t,a,"rw")}s(I,"mayActAs");async function _(n,t,a){if(t.class!=="node"&&!(t.class==="app"&&t.place!==void 0))return!0;const i=t.class==="app"?t.place:await x(t.node.id,n);if(i===null)return!0;const e=await O(a.id,n);return e===null?!i.isolated:N(i,e)}s(_,"customReachable");const h=s(n=>n.type.includes("."),"isCustom");async function J(n,t,a){return await E(n,await o(n,t),t,a)}s(J,"objectsTargets");async function E(n,{space:t,path:a},i,e){const p=[];if(t.type==="person")p.push("user");else if(t.type==="app"&&a.length>0){const c=a[0];if(c.type==="profile")a.length>1&&p.push(`profile:${(await l(n,c)).external_id}`);else if(c.type==="node"||h(c)){const r=(await l(n,t)).external_id;r!==e&&p.push(r)}}return i.type==="node"&&p.push("node"),p}s(E,"targetsOf");async function u(n,t,a,i,e){if(t.class==="person"||t.scopes===null)return!1;const p=t.scopes;if(p.length===0||a!=="link"&&i.space.type==="app"&&t.app.id===i.space.id)return!1;const c=(await l(n,t.app)).external_id;return(await E(n,i,e,c)).some(r=>{const m=`crtr:objects:${a}:${r}`;return p.some(d=>S(d,m))})}s(u,"holdsInLocated");async function M(n,t,a,i){return t.class==="person"||t.scopes===null?!1:await u(n,t,a,await o(n,i),i)}s(M,"holdsObjectsScope");async function B(n,t,a,i,e){const p=i.owner===null?null:await n.prepareUncached("SELECT * FROM objects WHERE id = ?").get(i.owner)??null;if(p===null)return!1;const c={class:"app",app:t,scopes:a??[]};return await I(n,c,p)?a===null||await M(n,c,"link",e):!1}s(B,"linkAuthorizes");async function Y(n,t,a){return(await U(n,a))(t)}s(Y,"canRead");async function U(n,t){let a=null,i=null;return async e=>{if(e.class==="person")return!0;if(a??=await o(n,t),a.space.type==="app"&&a.path.length>0){if(i??=await y(n,t),i&&f(n,e,a.space))return!0;if(h(t)){if(e.app.id===a.space.id&&!await _(n,e,t))return!1;if(e.app.id!==a.space.id&&e.scopes!==null&&f(n,e,a.space)&&(t.privacy==="public"||T(e)&&await A(n,e.scopes,a.space)))return!0}}return await u(n,e,"read",a,t)||e.class==="object"&&h(e.object)&&await u(n,e,"watch",a,t)?!0:await a.impl.canRead(n,e,a.space,a.path,t)}}s(U,"readCheck");async function z(n,t,a){if(await y(n,a))return!1;const i=await o(n,a);if(await u(n,t,"write",i,a))return!0;const{space:e,path:p,impl:c}=i;return await c.canChange(n,t,e,p,a)}s(z,"canChange");async function G(n,t,a,i){if(t.class==="person")return null;const{space:e,path:p}=await o(n,a);return e.type==="app"&&t.app.id===e.id?await w(n,t,e,p,a,i):null}s(G,"missingMemoryScope");async function V(n,t,a,i){if(t.class==="person")return null;const e=await o(n,a),{space:p}=e;return p.type!=="app"||t.app.id!==p.id?null:await w(n,t,p,e.path.length===0?[a]:[...e.path],null,i)}s(V,"missingMemoryScopeUnder");export{z as canChange,Y as canRead,_ as customReachable,M as holdsObjectsScope,A as holdsProviderGroup,y as isShipped,B as linkAuthorizes,I as mayActAs,f as mayUseTools,G as missingMemoryScope,V as missingMemoryScopeUnder,J as objectsTargets,C as ownerChain,U as readCheck,P as spaceOf,v as toolApps};
|
|
4
|
+
SELECT o.id, o.owner, c.depth + 1 FROM objects o JOIN chain c ON o.id = c.owner WHERE c.depth < 12
|
|
5
|
+
) SELECT o.* FROM chain c JOIN objects o ON o.id = c.id ORDER BY c.depth DESC LIMIT 1`).get(t.id)??t}n(s,"spaceOf");async function d(e,t){return t.type!=="document"?!1:(await e.prepare("SELECT source_path FROM documents WHERE object = ?").get(t.id))?.source_path!=null}n(d,"isShipped");async function w(e,t,a){return await r(e,t,"read",a.id)}n(w,"canRead");async function f(e,t,a){return await d(e,a)||a.privacy!=="public"&&(await s(e,a)).type==="repo"&&a.owner!==null?!1:await r(e,t,"edit",a.id)}n(f,"canChange");async function h(e,t,a,i){if(t.class==="person")return!1;const o=await p(e,t);return a==="write"?await r(e,o,"edit",i.id):await r(e,o,"read",i.id,a==="read"?{}:{verb:a})}n(h,"holdsObjectsScope");export{f as canChange,w as canRead,h as holdsObjectsScope,d as isShipped,s as spaceOf};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { Level, ObjectRow, Privacy, Reader } from './types.js';
|
|
2
|
-
import { customReachable, mayActAs } from './access.js';
|
|
3
2
|
import { onQueueSignal } from './receivers.js';
|
|
4
3
|
import type { CanvasDb } from '../canvas/db.js';
|
|
5
|
-
|
|
4
|
+
/** Whether this reader may register, publish or watch as an object owned by `owner`. */
|
|
5
|
+
export declare function mayActAs(db: CanvasDb, reader: Reader, owner: ObjectRow): Promise<boolean>;
|
|
6
|
+
/** Whether the reader reaches the custom object: it may read it. */
|
|
7
|
+
export declare function customReachable(db: CanvasDb, reader: Reader, object: ObjectRow): Promise<boolean>;
|
|
8
|
+
export { onQueueSignal };
|
|
6
9
|
export type CustomReceiver = 'queue' | {
|
|
7
10
|
url: string;
|
|
8
11
|
} | {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
var
|
|
2
|
-
VALUES (?, ?, ?, ?)`).run(
|
|
3
|
-
WHERE destination_id = ? AND state = 'pending' AND canceled_at IS NULL`).run(
|
|
4
|
-
WHERE destination_id = ? AND state = 'pending' AND canceled_at IS NULL`).run(m==='"queue"'?null:
|
|
5
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1)`).run(i,n.id,a,w,m,m,p,u),A(e,i),{outcome:"inserted",changed:[]};const r=
|
|
6
|
-
UNION ALL SELECT depth FROM message_causes WHERE message_id = ? LIMIT 1`).get(u,u);if(u!==null&&
|
|
7
|
-
`)[0],t.id),await O(e,{event:`${t.type}.${n.event}`,object:t,actor:i,attrs:{},messageBody:{summary:n.summary,...n.state===void 0?{}:{state:n.state}},...n.cause===void 0?{}:{cause:n.cause}},async p=>await N(e,p))}l(
|
|
1
|
+
var C=Object.defineProperty;var l=(e,t)=>C(e,"name",{value:t,configurable:!0});import{createHash as x,randomBytes as P,timingSafeEqual as $}from"node:crypto";import{CrtrError as j,usage as f}from"../errors.js";import{ExitCode as S}from"../../types.js";import{createObject as J,liveDocumentNamed as q}from"./objects.js";import{invalidateCustomKind as A}from"./kinds/registry.js";import{publish as O,HOP_LIMIT as F,HopLimitError as M}from"./events.js";import{checkoutsForCwd as N}from"./repo-sync/repos.js";import{onQueueSignal as B,signalQueue as b}from"./receivers.js";import{receiverEndpoint as K,isPublicReceiverAddress as V}from"./receiver-url.js";import{isIP as G}from"node:net";import{KIND as k,LOCAL as D,MAX_KIND_EVENTS as Q,RESERVED_KIND_ROOTS as Y,eventCatalogReasons as z}from"../plugin-objects-manifest.js";import{check as L}from"../permissions/evaluator.js";import{userOf as X}from"../permissions/evaluator.js";import{nodePlace as Z}from"../permissions/ceiling.js";import{PermissionError as ee}from"../permissions/errors.js";import{writeActsFor as te}from"../permissions/facts.js";async function ne(e,t,i){return t.class!=="person"&&t.app.id===i.id?!0:await L(e,t,"edit",i.id)}l(ne,"mayActAs");async function ie(e,t,i){return await L(e,t,"read",i.id)}l(ie,"customReachable");const se=["public","private"];function h(e,t){throw new j(e,t,S.GENERAL)}l(h,"conflict");function R(e){return x("sha256").update(e).digest("hex")}l(R,"hash");function H(){return`crtr_pub_${P(32).toString("base64url")}`}l(H,"issueToken");function ae(e){if(e==null)return null;if(e==="queue")return JSON.stringify(e);if(typeof e!="object"||Array.isArray(e))throw f("invalid custom receiver; expected queue, an https url, or argv and absolute cwd");if("url"in e&&typeof e.url=="string")try{const i=K(e.url).hostname.replace(/^\[|\]$/g,"");if(G(i)&&!V(i))throw new Error("non-public IP address");return JSON.stringify({url:e.url})}catch(t){throw f(`invalid custom receiver: ${String(t)}`)}if("argv"in e&&Array.isArray(e.argv)&&e.argv.length>0&&e.argv.every(t=>typeof t=="string")&&typeof e.cwd=="string"&&e.cwd.startsWith("/"))return JSON.stringify(e);throw f("invalid custom receiver; url must be https and argv requires a command and absolute cwd")}l(ae,"receiverJson");async function re(e,t){return t.class==="node"?(await Z(e,t.node.id))?.isolated===!0:t.class==="app"||t.class==="object"?t.place?.isolated===!0:!1}l(re,"inIsolatedRun");async function Ne(e,t,i){const{kind:n,name:a,owner:s,description:o,events:u,receiver:c,rotateToken:w,privacy:p}=i;if(p!==void 0&&!se.includes(p))throw f(`invalid privacy ${JSON.stringify(p)}`);if(await ne(e,t,s)||h("forbidden",`cannot act as ${s.id}`),await re(e,t))throw new ee(403,"isolated_run","a custom object cannot be registered from an isolated run");if(!k.test(n)||Y.includes(n.split(".")[0]))throw f(`invalid or reserved kind ${n}`);if(!a.trim()||a!==a.trim()||a.includes("\0"))throw f(`invalid object name ${JSON.stringify(a)}`);const m=ae(c);if(c!=="queue"&&c!==null&&c!==void 0&&"argv"in c&&t.class!=="person"&&t.class!=="node"&&h("argv_forbidden","argv receiver requires a person or node principal"),u===void 0){const d=await e.prepare("SELECT source FROM object_kinds WHERE name = ?").get(n);if(d?.source!=="manifest"&&d?.source!=="peer")throw f("events is required unless the kind is declared by an installed plugin")}else if(Object.keys(u).length>Q||Object.entries(u).some(([d,_])=>!D.test(d)||!Number.isInteger(_?.level)||_.level<1||_.level>10||_.supersedes!==void 0&&typeof _.supersedes!="boolean"||_.ends_wait!==void 0&&typeof _.ends_wait!="boolean"))throw f("invalid custom event catalog");const r=await q(e,s.id,a);if(r&&!await ie(e,t,r)&&h("forbidden",`cannot act as ${s.id}`),r&&t.class!=="person"&&t.class!=="node"){const d=(await e.prepare("SELECT receiver_json FROM custom_objects WHERE object = ?").get(r.id))?.receiver_json;d&&typeof JSON.parse(d)=="object"&&"argv"in JSON.parse(d)&&h("argv_forbidden","argv receiver requires a person or node principal")}r&&r.type!==n&&h("name_taken",`${s.name}/${a} is already held by ${r.id}`);const g=u===void 0?null:await oe(e,{name:n,owner:s,description:o,events:u,read:null,source:"register"});if(!r){const d=await J(e,{type:n,kind:null,owner:s.id,privacy:p??"public",name:a,preview:""});s.type==="app"&&await te(e,d.id,await X(e,t));const _=H();return await e.prepare(`INSERT INTO custom_objects (object, description, receiver_json, token_hash)
|
|
2
|
+
VALUES (?, ?, ?, ?)`).run(d.id,o,m,R(_)),t.class==="node"&&await e.prepare("UPDATE custom_objects SET registered_by = ? WHERE object = ?").run(t.node.id,d.id),m!==null&&await e.prepare("UPDATE objects SET reception = 2 WHERE id = ?").run(d.id),await O(e,{event:"created",object:d,actor:t.class==="object"?t.object:t.class==="person"?t.person:t.class==="app"?t.app:t.node,attrs:{}},async U=>await N(e,U)),{object:d,token:_,created:!0,changed:[]}}const y=await e.prepare("SELECT description, receiver_json FROM custom_objects WHERE object = ?").get(r.id);y||h("name_taken",`${s.name}/${a} is not a custom object`);const E=[];if(y.description!==o&&E.push("description"),y.receiver_json!==m&&E.push("receiver"),g?.changed.includes("events")&&E.push("events"),g?.changed.includes("description")&&!E.includes("description")&&E.push("description"),p!==void 0&&r.privacy!==p&&E.push("privacy"),w&&E.push("token"),E.length===0)return{object:r,created:!1,changed:E};const v=w?H():void 0,T=E.includes("privacy")?{...r,privacy:p}:r;if(T!==r&&await e.prepare("UPDATE objects SET privacy = ? WHERE id = ?").run(p,r.id),await e.prepare("UPDATE custom_objects SET description = ?, receiver_json = ?, token_hash = COALESCE(?, token_hash) WHERE object = ?").run(o,m,v===void 0?null:R(v),r.id),await e.prepare("UPDATE objects SET reception = ? WHERE id = ?").run(m===null?0:2,r.id),y.receiver_json!==m){const d=Date.now();if(m===null){const _=await e.prepare(`UPDATE deliveries SET canceled_at = ?, updated_at = ?
|
|
3
|
+
WHERE destination_id = ? AND state = 'pending' AND canceled_at IS NULL`).run(d,d,r.id);await e.prepare("UPDATE custom_objects SET dropped = dropped + ? WHERE object = ?").run(_.changes,r.id)}else await e.prepare(`UPDATE deliveries SET next_attempt_at = ?, updated_at = ?
|
|
4
|
+
WHERE destination_id = ? AND state = 'pending' AND canceled_at IS NULL`).run(m==='"queue"'?null:d,d,r.id);b(r.id)}return w&&b(r.id),await O(e,{event:"configured",object:T,actor:t.class==="object"?t.object:t.class==="person"?t.person:t.class==="app"?t.app:t.node,attrs:{changed:E.join(",")}},async d=>await N(e,d)),{object:T,...v===void 0?{}:{token:v},created:!1,changed:E}}l(Ne,"registerCustomObject");async function oe(e,t){const{name:i,owner:n,description:a,events:s,read:o,source:u}=t;if(!k.test(i))throw f(`invalid kind ${i}`);if(z(s,i).length>0)throw f("invalid custom event catalog");const c=await e.prepare("SELECT owner, description, events_json, read_json, source, declared FROM object_kinds WHERE name = ?").get(i),w=I(s),p=u==="manifest"&&o!==null?JSON.stringify({command:o.command}):null,m=new Date().toISOString();if(!c)return await e.prepare(`INSERT INTO object_kinds (name, owner, description, events_json, created_at, updated_at, read_json, source, declared)
|
|
5
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1)`).run(i,n.id,a,w,m,m,p,u),A(e,i),{outcome:"inserted",changed:[]};const r=c.owner===n.id;if(u==="peer"){if(c.source!=="peer")return{outcome:"skipped",changed:[]}}else if(c.source!=="peer"&&(r||h("kind_owned",`${i} belongs to another owner`),u==="register"&&c.source==="manifest"))return{outcome:"skipped",changed:[]};const g=[];return I(JSON.parse(c.events_json))!==w&&g.push("events"),c.description!==a&&g.push("description"),(c.read_json??null)!==p&&g.push("read"),g.length===0&&r&&c.source===u&&c.declared===1?{outcome:"unchanged",changed:g}:(await e.prepare("UPDATE object_kinds SET owner = ?, description = ?, events_json = ?, read_json = ?, source = ?, declared = 1, updated_at = ? WHERE name = ?").run(n.id,a,w,p,u,m,i),A(e,i),{outcome:"updated",changed:g})}l(oe,"upsertKind");async function Te(e,t,i,n){const a=await e.prepare("SELECT events_json FROM object_kinds WHERE name = ?").get(t.type);if(!a||t.deleted_at!==null)throw f(`${t.id} is not a live custom object`);const s=JSON.parse(a.events_json);if(!D.test(n.event)||!Object.hasOwn(s,n.event))throw new j("unknown_event",`${t.type} declares ${Object.keys(s).join(", ")}, not ${n.event}`,S.USAGE,{declared:Object.keys(s),event:n.event});if(typeof n.summary!="string")throw f("summary must be text");const o=n.state===void 0?null:JSON.stringify(n.state);if(n.state!==void 0&&o===void 0)throw f("state must be JSON-serializable");if(Buffer.byteLength(JSON.stringify({summary:n.summary,...n.state===void 0?{}:{state:n.state}}))>64*1024)throw new j("body_too_large","custom event summary and state exceed 64 KiB",S.USAGE);if(o!==null&&Buffer.byteLength(o)>64*1024)throw f("state exceeds 64 KiB");const u=n.cause??null,c=u===null?null:await e.prepare(`SELECT depth FROM messages WHERE message_id = ?
|
|
6
|
+
UNION ALL SELECT depth FROM message_causes WHERE message_id = ? LIMIT 1`).get(u,u);if(u!==null&&c===void 0)throw new j("unknown_cause",`no message ${u}`,S.USAGE);if(c&&c.depth>=F)throw new M(c.depth+1);const w=new Date().toISOString();return await e.prepare("UPDATE custom_objects SET summary = ?, state_json = ?, published_at = ? WHERE object = ?").run(n.summary,o,w,t.id),await e.prepare("UPDATE objects SET preview = ? WHERE id = ?").run(n.summary.split(`
|
|
7
|
+
`)[0],t.id),await O(e,{event:`${t.type}.${n.event}`,object:t,actor:i,attrs:{},messageBody:{summary:n.summary,...n.state===void 0?{}:{state:n.state}},...n.cause===void 0?{}:{cause:n.cause}},async p=>await N(e,p))}l(Te,"publishCustom");async function be(e,t,i){if(t.deleted_at!==null)return;await O(e,{event:"deleted",object:t,actor:i,attrs:{}},async a=>await N(e,a));const n=new Date().toISOString();await e.prepare("UPDATE objects SET deleted_at = ?, reception = 0 WHERE id = ?").run(n,t.id),await e.prepare("UPDATE deliveries SET canceled_at = ?, updated_at = ? WHERE destination_id = ? AND state = 'pending' AND canceled_at IS NULL").run(Date.now(),Date.now(),t.id),b(t.id)}l(be,"unregisterCustomObject");function I(e){return JSON.stringify(Object.fromEntries(Object.entries(e).sort(([t],[i])=>t.localeCompare(i)).map(([t,i])=>[t,{level:i.level,...i.supersedes===void 0?{}:{supersedes:i.supersedes},...i.ends_wait===void 0?{}:{ends_wait:i.ends_wait}}])))}l(I,"canonicalEvents");async function Re(e,t,i=0){return await W(e,t.id,i,"pending")}l(Re,"pendingFor");async function Ae(e,t){const i=await e.prepare("SELECT destination_id FROM deliveries WHERE delivery_id = ?").get(t);return i?(await W(e,i.destination_id,-1,"handed_off")).find(n=>n.delivery_id===t)??null:null}l(Ae,"pushLineFor");async function W(e,t,i,n){return(await e.prepare(`SELECT d.delivery_id, m.message_id, m.seq, m.event, m.level, m.object_id, m.actor_id, m.body_json, m.body_ref, m.depth, m.created_at,
|
|
8
8
|
o.type, o.name, owner.id AS owner_id
|
|
9
9
|
FROM deliveries d JOIN messages m ON m.message_id = d.message_id JOIN objects o ON o.id = m.object_id
|
|
10
10
|
LEFT JOIN objects owner ON owner.id = o.owner
|
|
11
|
-
WHERE d.destination_id = ? AND d.state = ? AND d.canceled_at IS NULL AND m.seq > ? ORDER BY m.seq`).all(t,n,i)).map(s=>{const o=s.body_json?JSON.parse(s.body_json):{};return{delivery_id:s.delivery_id,message_id:s.message_id,seq:s.seq,event:s.event,level:s.level,object:{id:s.object_id,type:s.type,name:s.name,owner:s.owner_id},actor:s.actor_id,attrs:o.push?.attrs??{},...o.summary===void 0?{}:{summary:o.summary},...o.state===void 0?{}:{state:o.state},...o.push?.pointer===void 0?{}:{pointer:o.push.pointer},depth:s.depth,created_at:s.created_at}})}l(
|
|
12
|
-
WHERE delivery_id = ? AND destination_id = ? AND state = 'pending' AND canceled_at IS NULL`).run(n,n,o,t.id);Number(u.changes)?s++:a.push(o)}return{acknowledged:s,...a.length?{unknown:a}:{}}}l(
|
|
11
|
+
WHERE d.destination_id = ? AND d.state = ? AND d.canceled_at IS NULL AND m.seq > ? ORDER BY m.seq`).all(t,n,i)).map(s=>{const o=s.body_json?JSON.parse(s.body_json):{};return{delivery_id:s.delivery_id,message_id:s.message_id,seq:s.seq,event:s.event,level:s.level,object:{id:s.object_id,type:s.type,name:s.name,owner:s.owner_id},actor:s.actor_id,attrs:o.push?.attrs??{},...o.summary===void 0?{}:{summary:o.summary},...o.state===void 0?{}:{state:o.state},...o.push?.pointer===void 0?{}:{pointer:o.push.pointer},depth:s.depth,created_at:s.created_at}})}l(W,"queueLines");async function ke(e,t,i){const n=Date.now(),a=[];let s=0;for(const o of i){const u=await e.prepare(`UPDATE deliveries SET state = 'acknowledged', acknowledged_at = ?, updated_at = ?
|
|
12
|
+
WHERE delivery_id = ? AND destination_id = ? AND state = 'pending' AND canceled_at IS NULL`).run(n,n,o,t.id);Number(u.changes)?s++:a.push(o)}return{acknowledged:s,...a.length?{unknown:a}:{}}}l(ke,"acknowledge");async function De(e,t,i){const n=await e.prepare("SELECT token_hash FROM custom_objects WHERE object = ?").get(t.id),a=Buffer.from(n?.token_hash??"".padEnd(64,"0"),"hex"),s=Buffer.from(R(i),"hex");return i.startsWith("crtr_pub_")&&t.deleted_at===null&&a.length===s.length&&$(a,s)&&n?.token_hash!=null}l(De,"verifyPublishToken");export{ke as acknowledge,ie as customReachable,ne as mayActAs,B as onQueueSignal,Re as pendingFor,Te as publishCustom,Ae as pushLineFor,Ne as registerCustomObject,be as unregisterCustomObject,oe as upsertKind,De as verifyPublishToken};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
var u=Object.defineProperty;var
|
|
2
|
-
WHERE i.scheme = 'profile' AND i.external_id = ? AND o.type = 'profile' AND o.deleted_at IS NULL`).get(t);return i?(await
|
|
1
|
+
var u=Object.defineProperty;var n=(e,t)=>u(e,"name",{value:t,configurable:!0});import{hasUnreconciledWorktree as f}from"../canvas/worktrees.js";import{deleteDocument as R}from"./documents.js";import{publish as E}from"./events.js";import{getById as c}from"./objects.js";import{writeActsFor as N}from"../permissions/facts.js";import{rememberDeletedIsolated as O}from"../permissions/ceiling.js";import{withCanvasWrite as w}from"../canvas/db.js";async function A(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 m(e,t),{savepoint:!0})}n(A,"deleteNodeWithOwned");async function H(e,t){const i=await e.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.id = i.object
|
|
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:r}of o)r&&await N(e,s,r);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 E(e,{event:"deleted",object:await c(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}n(H,"deleteProfileWithOwned");async function m(e,t){const i=await p(e,t);await e.prepare("UPDATE objects SET deleted_at = ? WHERE id = ?").run(i,t.id),await E(e,{event:"deleted",object:await c(e,t.id),actor:null,attrs:{}}),await e.prepare("DELETE FROM watches WHERE object = ? OR watcher = ?").run(t.id,t.id),await O(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)}n(m,"deleteInside");async function p(e,t){const i=await e.prepare(`WITH RECURSIVE sub(id) AS (
|
|
3
4
|
SELECT id FROM objects WHERE owner = ?
|
|
4
5
|
UNION SELECT o.id FROM objects o JOIN sub ON o.owner = sub.id)
|
|
5
|
-
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)),
|
|
6
|
-
WHERE e.type = 'links-to' AND e.dst = ? AND s.deleted_at IS NULL`);for(let a=!0;a;){a=!1;for(const
|
|
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 r=new Date().toISOString();for(const a of i)if(o.has(a.id)){if(a.type==="document"){await R(e,null,a);continue}await e.prepare("UPDATE objects SET deleted_at = ? WHERE id = ?").run(r,a.id),await E(e,{event:"deleted",object:await c(e,a.id),actor:null,attrs:{}}),await e.prepare("DELETE FROM watches WHERE object = ? OR watcher = ?").run(a.id,a.id)}return r}n(p,"deleteOwned");export{A as deleteNodeWithOwned,H as deleteProfileWithOwned};
|
|
@@ -17,6 +17,13 @@ export interface WriteSpec extends DocumentFields {
|
|
|
17
17
|
kind: string;
|
|
18
18
|
preview: string;
|
|
19
19
|
privacy: Privacy;
|
|
20
|
+
/** The permission schema type the document is evaluated under (`firstlight/memory`); omitted = `crtr/object`. */
|
|
21
|
+
schemaType?: string;
|
|
22
|
+
/** Facts written on the new document in the same transaction (`{ relation: 'editor', subject: 'user:abc' }`); they need a `schemaType`. */
|
|
23
|
+
relations?: Array<{
|
|
24
|
+
relation: string;
|
|
25
|
+
subject: string;
|
|
26
|
+
}>;
|
|
20
27
|
}
|
|
21
28
|
export type DocumentChange = {
|
|
22
29
|
body: string;
|
|
@@ -103,9 +110,11 @@ export declare function editDocument(db: CanvasDb, reader: Reader, doc: ObjectRo
|
|
|
103
110
|
revision: number;
|
|
104
111
|
};
|
|
105
112
|
}>;
|
|
106
|
-
/** Change record fields (preview, summary, privacy, rules, …)
|
|
107
|
-
*
|
|
108
|
-
|
|
113
|
+
/** Change record fields (preview, summary, privacy, rules, …). A null reader is repo sync, which carries no reader, checks no access and
|
|
114
|
+
* records no history. Any other caller's change is recorded as a revision with the current body, so `history` attributes it (author,
|
|
115
|
+
* `acting_app`, `acting_for`) with the rationale. It descends from the highest head only, so a document that needs a merge still does. A caller
|
|
116
|
+
* that held the heads before keeps holding them; one that had not read the body does not gain a read base it never earned. */
|
|
117
|
+
export declare function setDocumentRecord(db: CanvasDb, reader: Reader | null, doc: ObjectRow, fields: DocumentFields, rationale?: string): Promise<ObjectRow>;
|
|
109
118
|
/** Record that the reader holds the document's current heads: a `read` watch with that read
|
|
110
119
|
* base, and a `read` push. Delivery's content load calls this without reading the body. */
|
|
111
120
|
export declare function recordRead(db: CanvasDb, reader: Reader, doc: ObjectRow, reposFn?: ReposFn): Promise<void>;
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
var
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
var se=Object.defineProperty;var s=(e,n)=>se(e,"name",{value:n,configurable:!0});import{randomUUID as ce}from"node:crypto";import{CrtrError as N,notFound as R,usage as l}from"../errors.js";import{ExitCode as g}from"../../types.js";import{bodyOf as ue,get as we,put as B}from"./bodies.js";import{dropUnresolved as pe,linksOf as le,recordActedOn as O,setLinks as q,setUnresolved as de,unresolvedLinksCouldName as me}from"./edges.js";import{publish as k}from"./events.js";import{readableOrDeleted as fe,resolveName as ye}from"./names.js";import{createObject as he,getById as f}from"./objects.js";import{liveLookups as Ee,readerForNode as _e,readerForObject as ve}from"./reader.js";import{replaceRules as G,setRulesOwner as Ne}from"./rules.js";import{recordWatch as j}from"./watches.js";import{withCanvasWrite as y}from"../canvas/db.js";import{filterAsync as Re,forEachAsync as ge,mapAsync as x}from"../async-array.js";import{check as A,resolveReader as Oe,userOf as K}from"../permissions/evaluator.js";import{missingMemoryScope as Y}from"../permissions/ceiling.js";import{deleteFact as Se,parseSubject as Te,writeActsFor as De,writeFact as ke}from"../permissions/facts.js";import{PermissionError as je}from"../permissions/errors.js";function E(e){return e.class==="node"?e.node:e.class==="app"?e.app:e.class==="object"?e.object:e.person}s(E,"actorOf");async function C(e,n){const t=`user:${await K(e,n)}`;return n.class==="person"?{app:null,for:t}:{app:(await e.prepare("SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(n.app.id))?.external_id??null,for:t}}s(C,"actingOf");function xe(e,n){return new N("conflict",e,g.GENERAL,n)}s(xe,"conflict");function h(e){if(e.type!=="document")throw l(`${e.type} ${e.id} is not a document`)}s(h,"assertDocument");async function V(e,n){return(await e.prepare(`WITH RECURSIVE chain(id, owner, type, depth) AS (
|
|
2
|
+
SELECT id, owner, type, 0 FROM objects WHERE id = ?
|
|
3
|
+
UNION ALL
|
|
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 W(e,n,t){if(t!=="public"&&await V(e,n)==="repo")throw l(`a repo owns only public documents; ${n.name} cannot own a ${t} one`,{rule:"repo-public-only",next:"Write it as public, or give it another owner (your own documents can carry `if: {repo: <handle>}`)."})}s(W,"assertPublicInRepo");async function M(e,n,t,a){if(t.trim()===""||t!==t.trim())throw l(`invalid document name ${JSON.stringify(t)}`);const i=await e.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,t);if(i&&i.id!==a?.id)throw xe(`${n.name} already has a document named ${t}`,{name:t,holder:i.id})}s(M,"assertNameFree");async function U(e,n,t){if(!await A(e,n,"read",t.id))throw R(`no document ${t.id}`)}s(U,"readable");function X(e){return new N("scope_missing",`missing scope ${e}`,g.GENERAL,{scope:e})}s(X,"scopeMissing");async function L(e,n,t){await U(e,n,t);const{source_path:a}=await v(e,t);if(a!==null)throw new N("forbidden",`${t.name} is a built-in document shipped by a package (served from ${a}); \`crtr doc\` cannot change it`,g.GENERAL,{id:t.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(e,n,t.id,"rw");if(i!==null)throw X(i);if(!await A(e,n,"edit",t.id))throw new N("forbidden",`you cannot change ${t.name}`,g.GENERAL,{id:t.id})}s(L,"changeable");async function z(e,n){const t=await e.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:t.map(a=>a.rev_id)}}s(z,"toRevision");async function I(e,n){const t=await e.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 x(t,async a=>await z(e,a))}s(I,"heads");function Ae(e){return e.map(n=>n.rev_id).sort().join(",")}s(Ae,"headsKeyOf");async function v(e,n){const t=await e.prepare("SELECT * FROM documents WHERE object = ?").get(n.id);if(!t)throw R(`no document record for ${n.id}`);return{summary:t.summary,heads_key:t.heads_key,revision:t.revision,why_it_exists:t.why_it_exists,unlisted:t.unlisted===1,lint_ignore:t.lint_ignore_json===null?null:JSON.parse(t.lint_ignore_json),extensions:t.extensions_json===null?null:JSON.parse(t.extensions_json),source_path:t.source_path}}s(v,"documentRecord");async function S(e,n,t){const a=await e.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,t.rev_id??ce(),t.number,t.body_hash,t.author?.id??null,t.rationale,t.verbatim?1:0,t.created_at??new Date().toISOString(),t.acting?.app??null,t.acting?.for??null),i=e.prepare("INSERT INTO revision_parents (child, parent) VALUES (?, ?)");for(const r of t.parents)await i.run(a.rev_id,r);return a}s(S,"insertRevision");function Le(e){const n=new Set;for(const t of e.matchAll(/\[\[([^[\]\n]+?)\]\]/g))n.add(t[1].trim());return[...n]}s(Le,"linksIn");async function Q(e,n,t){const a=[],i=[];for(const r of t){const o=await ye(e,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(e,n,t,a){const{links:i,unresolved:r}=await Q(e,n,Le(a));return await ie(e,await q(e,t,i)),await de(e,t,r),r}s(Z,"setRevisionLinks");async function F(e,n,t){const a=new Map;for(const{src:i,name:r}of await me(e,n.name,n.id))a.set(i,[...a.get(i)??[],r]);for(const[i,r]of a){const o=await f(e,i),u=o?.owner==null?null:await f(e,o.owner);if(!o||o.deleted_at||o.home_machine!==null||!u||u.deleted_at)continue;const w=u.type==="node"?await _e(e,u.id,t??(async()=>[]),Ee(e),!0):await ve(e,o,t),c=(await Q(e,w,r)).links.filter(p=>p.dst.id===n.id);c.length!==0&&(await q(e,o,[...await le(e,o),...c]),await pe(e,o,c.map(p=>p.written_as)))}}s(F,"linkNewlyNamed");async function Ie(e,n,t){return n.type==="node"?n.id:await e.prepare(`SELECT 1 FROM objects o JOIN objects d ON d.owner = o.id
|
|
5
9
|
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,
|
|
7
|
-
WHERE e.type = 'links-to' AND e.src = ? AND e.written_as IS NOT NULL`).all(n.id);if(
|
|
10
|
+
AND d.type = 'document' AND d.name = ? AND d.deleted_at IS NULL LIMIT 1`).get(n.name,n.type,t)?`${n.type}:${n.name}`:n.name}s(Ie,"ownerPrefix");async function b(e,n,t){const a=await e.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 t;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 u=r.owner===null?null:await f(e,r.owner),w=u?`${await Ie(e,u,r.name)}/${r.name}`:r.name,c=o&&r.owner===n.owner?r.name:w;c!==r.written_as&&i.set(r.written_as,c)}return i.size===0?t:t.replace(/\[\[([^[\]\n]+?)\]\]/g,(r,o)=>{const u=i.get(o.trim());return u===void 0?r:`[[${u}]]`})}s(b,"withCurrentNames");async function J(e,n,t){if(t.preview!==void 0){if(t.preview.trim()==="")throw l("a document needs a preview",{field:"preview"});await e.prepare("UPDATE objects SET preview = ? WHERE id = ?").run(t.preview,n.id)}t.privacy!==void 0&&(await W(e,await f(e,n.owner),t.privacy),await e.prepare("UPDATE objects SET privacy = ? WHERE id = ?").run(t.privacy,n.id));const a=s(async(r,o)=>await e.prepareUncached(`UPDATE documents SET ${r} = ? WHERE object = ?`).run(o,n.id),"set");t.summary!==void 0&&await a("summary",t.summary),t.whyItExists!==void 0&&await a("why_it_exists",t.whyItExists),t.unlisted!==void 0&&await a("unlisted",t.unlisted?1:0),t.lintIgnore!==void 0&&await a("lint_ignore_json",t.lintIgnore===null?null:JSON.stringify(t.lintIgnore)),t.extensions!==void 0&&await a("extensions_json",t.extensions===null?null:JSON.stringify(t.extensions));const i=await f(e,n.id);return t.rules!==void 0&&await G(e,i,t.rules),i}s(J,"applyDocumentFields");async function ee(e,n,t,a="crtr"){if(n.owner.deleted_at)throw l(`${n.owner.name} is deleted`);if(n.relations?.length&&n.schemaType===void 0)throw new je(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 W(e,n.owner,n.privacy),await M(e,n.owner,n.name);const i=await he(e,{...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:u}of n.relations??[])await ke(e,{resourceType:n.schemaType,resourceId:i.id,relation:o,subject:Te(u),writtenBy:a});await e.prepare("INSERT INTO documents (object, heads_key, revision) VALUES (?, '', 0)").run(i.id);const r=await J(e,i,{...n,preview:void 0,privacy:void 0,rules:n.rules??[]});return await F(e,r,t),r}s(ee,"createDocument");async function $(e,n){const t=await I(e,n),a=Ae(t),i=Math.max(0,...t.map(r=>r.number));return await e.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(a,i,n.id),{heads_key:a,revision:i,headCount:t.length}}s($,"setHeads");async function nt(e,n,t,a,i){const r=B(a);return await y(async()=>{const o=E(n),u=await Y(e,n,t.owner.id,"rw",!0);if(u!==null)throw X(u);const w=await C(e,n),c=await ee(e,t,i,w.app??w.for);t.owner.type==="app"&&t.schemaType===void 0&&await De(e,c.id,await K(e,n));const p=await S(e,c,{number:1,body_hash:r,author:o,rationale:null,parents:[],acting:w});if(!await A(e,n,"edit",c.id))throw R(`no owner ${t.owner.name} you can write to`);const{heads_key:d}=await $(e,c),m=await Z(e,n,c,a);return await O(e,o,c),await j(e,o,c,"write",{readHeadsKey:d}),await k(e,{event:"created",object:c,actor:o,attrs:{revision:1},pointer:p.rev_id},i),{object:c,revision:1,unresolved:m}},{savepoint:!0})}s(nt,"writeDocument");async function $e(e,n,t){return(await e.prepare("SELECT read_heads_key FROM watches WHERE watcher = ? AND object = ?").get(E(n).id,t.id))?.read_heads_key??null}s($e,"readBase");function He(e,n){let t=e;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=t.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(t.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});t=t.slice(0,o)+r+t.slice(o+i.length)}return t}s(He,"applyReplacements");async function at(e,n,t,a,i,r,o={},u){if(h(t),await L(e,n,t),t.deleted_at)throw l(`${t.name} is deleted`);if(r===null)throw new N("read_first",`read ${t.name} before editing it`,g.GENERAL,{id:t.id});if(i.trim()==="")throw l("an edit needs a rationale",{field:"rationale"});const w=await v(e,t);if(w.heads_key!==r)return{stale:{heads_key:w.heads_key,revision:w.revision}};const c=await I(e,t);let p;if("body"in a)p=a.body;else{if(c.length!==1)throw l(`${t.name} has ${c.length} heads and needs a merge; pass the whole merged body`,{heads:c.length});p=He(await b(e,t,we(c[0].body_hash)),a.replace)}const d=B(p);try{return await y(async()=>{const m=E(n),T=1+Math.max(0,...c.map(_=>_.number)),P=await S(e,t,{number:T,body_hash:d,author:m,rationale:i,verbatim:a.verbatim===!0,parents:c.map(_=>_.rev_id),acting:await C(e,n)}),H=P.rev_id,re=await e.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ? AND heads_key = ?").run(H,T,t.id,r);if(Number(re.changes)===0){const _=await v(e,t);throw new te(_.heads_key,_.revision)}const D=await J(e,t,o),oe=await Z(e,n,D,p);return await O(e,m,D),await j(e,m,D,"write",{readHeadsKey:H}),await k(e,{event:"edited",object:D,actor:m,pointer:P.rev_id,attrs:{revision:T,from_revision:w.revision,rationale:i}},u),{revision:T,heads_key:H,unresolved:oe}},{savepoint:!0})}catch(m){if(m instanceof te)return{stale:{heads_key:m.heads_key,revision:m.revision}};throw m}}s(at,"editDocument");class te extends Error{static{s(this,"StaleEdit")}heads_key;revision;constructor(n,t){super("stale"),this.heads_key=n,this.revision=t}}async function it(e,n,t,a,i){if(h(t),n&&(await L(e,n,t),i===void 0||i.trim()===""))throw l("an edit needs a rationale",{field:"rationale"});return await y(async()=>{const r=await J(e,t,a);if(n){const o=E(n),u=await v(e,t),w=await $e(e,n,t),c=await I(e,t),p=c[0];await S(e,t,{number:1+Math.max(...c.map(m=>m.number)),body_hash:p.body_hash,author:o,rationale:i,parents:[p.rev_id],acting:await C(e,n)});const{heads_key:d}=await $(e,t);await O(e,o,r),await j(e,o,t,"write",w===u.heads_key?{readHeadsKey:d}:{})}return r},{savepoint:!0})}s(it,"setDocumentRecord");async function Ce(e,n,t,a){const i=E(n),{heads_key:r}=await v(e,t);await j(e,i,t,"read",{readHeadsKey:r}),await k(e,{event:"read",object:t,actor:i,attrs:{}},a)}s(Ce,"recordRead");async function rt(e,n,t,a){return h(t),await U(e,n,t),await y(async()=>{const i=await v(e,t),r=await x(await I(e,t),async c=>({...c,body:await b(e,t,ue(c.body_hash,i.source_path))})),o=await fe(e,await Oe(e,n),"o"),w=(await e.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 (${o.sql}) ORDER BY e.written_as`).all(t.id,...o.params)).map(({_written_as:c,...p})=>({written_as:c??"",object:p,deleted:p.deleted_at!==null}));return t.deleted_at||await Ce(e,n,t,a),{object:t,record:i,heads:r,needsMerge:r.length>1,links:w}},{savepoint:!0})}s(rt,"readDocument");async function ot(e,n,t){h(t),await U(e,n,t);const a=await e.prepare("SELECT * FROM revisions WHERE document = ? ORDER BY number DESC, created_at DESC, seq DESC").all(t.id);return await x(a,async i=>await z(e,i))}s(ot,"history");async function st(e,n,t,a,i){if(h(t),t.deleted_at)throw l(`${t.name} is deleted`);return n&&await L(e,n,t),await y(async()=>{const r=a.owner??await f(e,t.owner),o=a.name??t.name;await M(e,r,o,t),await W(e,r,t.privacy),await e.prepare("UPDATE objects SET name = ?, owner = ? WHERE id = ?").run(o,r.id,t.id);const u=await f(e,t.id);if(r.id!==t.owner&&(await Ne(e,u),n&&!await A(e,n,"edit",u.id)))throw R(`no owner ${r.name} you can move documents to`);return n&&await O(e,E(n),u),(o!==t.name||r.id!==t.owner)&&await F(e,u,i),u},{savepoint:!0})}s(st,"moveDocument");async function ct(e,n,t,a,i){return h(t),t.deleted_at?t:(n&&await L(e,n,t),await y(async()=>{const r=await ne(e,n,t,a,i);return await We(e,t),await ie(e,await ae(e,t.id),a),r},{savepoint:!0}))}s(ct,"deleteDocument");async function We(e,n){if(n.schema_type==null)return;const t=await e.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 t)await Se(e,{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(We,"deleteFactsOf");async function ut(e,n,t,a){return h(n),n.deleted_at?await y(async()=>{await M(e,await f(e,n.owner),t,n),await e.prepare("UPDATE objects SET deleted_at = NULL, name = ? WHERE id = ?").run(t,n.id);const i=await f(e,n.id);return await F(e,i,a),i},{savepoint:!0}):n}s(ut,"restoreDocument");async function ne(e,n,t,a,i){await e.prepare("UPDATE objects SET deleted_at = ? WHERE id = ?").run(i??new Date().toISOString(),t.id);const r=await f(e,t.id),o=n?E(n):null;return o&&await O(e,o,r),await k(e,{event:"deleted",object:r,actor:o,attrs:{}},a),await e.prepare("DELETE FROM watches WHERE object = ?").run(t.id),await G(e,r,[]),r}s(ne,"markDeleted");async function ae(e,n){return(await e.prepare("SELECT dst FROM edges WHERE type = 'links-to' AND src = ?").all(n)).map(({dst:t})=>t)}s(ae,"linkTargets");async function ie(e,n,t){const a=e.prepare(`SELECT 1 FROM objects d JOIN objects w ON w.id = d.owner
|
|
8
14
|
WHERE d.id = ? AND d.type = 'document' AND d.deleted_at IS NULL AND w.deleted_at IS NOT NULL
|
|
9
15
|
AND NOT EXISTS (SELECT 1 FROM edges e JOIN objects s ON s.id = e.src
|
|
10
|
-
WHERE e.type = 'links-to' AND e.dst = d.id AND e.src != d.id AND s.deleted_at IS NULL)`),
|
|
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(e,null,await f(e,r),t),i.push(...await ae(e,r)))}}s(ie,"collectUnlinked");async function wt(e,n,t){if(n.revisions.length===0)throw l(`import of ${n.name}: no revisions`);return await y(async()=>{const a=await ee(e,n,t);await e.prepare("UPDATE objects SET created_at = ? WHERE id = ?").run(n.revisions[0].created_at,a.id);let i=null;return await ge(n.revisions,async(r,o)=>{i=(await S(e,a,{...r,number:o+1,parents:i===null?[]:[i]})).rev_id}),await $(e,a),await f(e,a.id)},{savepoint:!0})}s(wt,"importDocument");async function pt(e,n,t){if(h(n),n.privacy!=="public"&&await V(e,n)==="repo")throw l(`a repo owns only public documents; refusing revisions of ${n.name}`);return await y(async()=>{const a=e.prepare("SELECT rev_id, number, document FROM revisions WHERE rev_id = ?");let i=await Re(t,async u=>!await a.get(u.rev_id));for(;i.length>0;){const u=[];for(const w of i){const c=await x(w.parents,async d=>await a.get(d));if(c.some(d=>d===void 0)){u.push(w);continue}const p=c;if(p.some(d=>d.document!==n.id))throw l(`revision ${w.rev_id} names a parent of another document`);await S(e,n,{...w,author:null,number:1+Math.max(0,...p.map(d=>d.number)),parents:p.map(d=>d.rev_id)})}if(u.length===i.length)throw l(`revisions of ${n.name} name unknown parents: ${u.map(w=>w.rev_id).join(", ")}`);i=u}const{heads_key:r,headCount:o}=await $(e,n);return{heads_key:r,headCount:o}},{savepoint:!0})}s(pt,"insertImportedRevisions");async function lt(e,n){const t=await f(e,n);if(!t||t.type!=="document")throw R(`no document ${n}`);return t}s(lt,"documentById");export{E as actorOf,J as applyDocumentFields,He as applyReplacements,ie as collectUnlinked,ct as deleteDocument,lt as documentById,v as documentRecord,at as editDocument,I as heads,ot as history,wt as importDocument,pt as insertImportedRevisions,Le as linksIn,st as moveDocument,$e as readBase,rt as readDocument,Ce as recordRead,ut as restoreDocument,it as setDocumentRecord,Z as setRevisionLinks,b as withCurrentNames,nt as writeDocument};
|
package/dist/core/graph/edges.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
ON CONFLICT DO NOTHING`).run(
|
|
3
|
-
ON CONFLICT DO NOTHING`),
|
|
1
|
+
var f=Object.defineProperty;var i=(e,t)=>f(e,"name",{value:t,configurable:!0});import{check as y,permits as R,resolveReader as T}from"../permissions/evaluator.js";import{getById as E}from"./objects.js";import{mapAsync as S}from"../async-array.js";async function I(e,t,n){await e.prepare(`INSERT INTO edges (type, src, dst, written_as, created_at) VALUES ('acted-on', ?, ?, NULL, ?)
|
|
2
|
+
ON CONFLICT DO NOTHING`).run(t.id,n.id,new Date().toISOString())}i(I,"recordActedOn");async function _(e,t,n){const a=(await e.prepare("SELECT dst FROM edges WHERE type = 'links-to' AND src = ?").all(t.id)).map(({dst:r})=>r);await e.prepare("DELETE FROM edges WHERE type = 'links-to' AND src = ?").run(t.id);const o=e.prepare(`INSERT INTO edges (type, src, dst, written_as, created_at) VALUES ('links-to', ?, ?, ?, ?)
|
|
3
|
+
ON CONFLICT DO NOTHING`),c=new Date().toISOString();for(const r of n)await o.run(t.id,r.dst.id,r.written_as,c);const p=new Set(n.map(r=>r.dst.id));return a.filter(r=>!p.has(r))}i(_,"setLinks");async function x(e,t,n){await e.prepare("DELETE FROM unresolved_links WHERE src = ?").run(t.id);const a=e.prepare("INSERT INTO unresolved_links (src, name) VALUES (?, ?) ON CONFLICT DO NOTHING");for(const o of n)await a.run(t.id,o)}i(x,"setUnresolved");async function C(e,t,n){const a=e.prepare("DELETE FROM unresolved_links WHERE src = ? AND name = ?");for(const o of n)await a.run(t.id,o)}i(C,"dropUnresolved");async function g(e,t,n){return await e.prepare("SELECT src, name FROM unresolved_links WHERE name = ? OR name = ? OR right(name, length(CAST(? AS text)) + 1) = '/' || CAST(? AS text) ORDER BY src, name").all(t,n,t,t)}i(g,"unresolvedLinksCouldName");async function h(e,t){const n=await e.prepare("SELECT e.dst, e.written_as FROM edges e WHERE e.type = 'links-to' AND e.src = ? ORDER BY e.written_as").all(t.id);return await S(n,async a=>({written_as:a.written_as??"",dst:await E(e,a.dst)}))}i(h,"linksOf");async function F(e,t,n){return await m(e,t,n,{type:"links-to",direction:"in"})}i(F,"backlinks");async function m(e,t,n,a={}){const o=await T(e,t);if(!await y(e,o,"read",n.id))return[];const c=a.direction??"both",p=a.type?[a.type]:["acted-on","links-to"],r=await R(e,o,"read","x"),d=[];for(const s of p)c!=="in"&&d.push(...await e.prepareUncached(`SELECT e.* FROM edges e JOIN objects x ON x.id = e.dst WHERE e.type = ? AND e.src = ? AND ${r.sql}`).all(s,n.id,...r.params)),c!=="out"&&d.push(...await e.prepareUncached(`SELECT e.* FROM edges e JOIN objects x ON x.id = e.src WHERE e.type = ? AND e.dst = ? AND ${r.sql}`).all(s,n.id,...r.params));const l=new Set,w=[];for(const s of d){const O=`${s.type}|${s.src}|${s.dst}`;if(l.has(O))continue;l.add(O);const u=s.src===n.id?n:await E(e,s.src),N=s.dst===n.id?n:await E(e,s.dst);!u||!N||w.push({type:s.type,src:u,dst:N,written_as:s.written_as,created_at:s.created_at})}return w}i(m,"edgesOf");export{F as backlinks,C as dropUnresolved,m as edgesOf,h as linksOf,I as recordActedOn,_ as setLinks,x as setUnresolved,g as unresolvedLinksCouldName};
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
var
|
|
2
|
-
AND name = ? AND id != ? LIMIT 1`).get(e.name,e.id)?`${e.type}:${e.name}`:e.name}d(
|
|
3
|
-
`)[0]:`${e.event} ${e.object.type} ${e.object.name}`),...l?.bodyRef===void 0?{}:{bodyRef:l.bodyRef},...l?.disposition===void 0?{}:{disposition:l.disposition},senderNodeId:e.actor?.type==="node"?e.actor.id:null,senderName:e.actor===null||e.actor===void 0?null:e.actor.home_machine?`${e.actor.name} on ${
|
|
1
|
+
var W=Object.defineProperty;var d=(t,e)=>W(t,"name",{value:e,configurable:!0});import{EventEmitter as H}from"node:events";const O=new H;O.setMaxListeners(0);async function de(t,e){const o=d(i=>e(i),"listener");return O.on(t,o),()=>O.off(t,o)}d(de,"subscribeDocumentEvents");import{evalCondition as x}from"../predicate.js";import{finalizeNodeMail as $,insertMessage as B}from"../feed/messages.js";import{check as S,resolveReader as J}from"../permissions/evaluator.js";import{getById as U}from"./objects.js";import{appScopesNow as K,publishLookups as G,readerForObject as q}from"./reader.js";import{configFromJson as M}from"./watches.js";import{CrtrError as L}from"../errors.js";import{ExitCode as v}from"../../types.js";import{eventSpec as V,kindOf as C}from"./kinds/registry.js";import{machineNameOf as T}from"../peer/names.js";import{settleObjectWaits as Y}from"../canvas/waits.js";import{RECEIVERS as F}from"./receivers.js";import{DEFAULT_LISTEN as k}from"./types.js";import{afterCommit as z}from"../canvas/db.js";function P(t,e){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((i,n,r)=>r.indexOf(i)===n).sort((i,n)=>i-n);throw new L("invalid_level",`${t.object.type} ${t.event} publishes at level ${o.join(", ")}, not ${t.level}`,v.USAGE,{kind:t.object.type,event:t.event,level:t.level,allowed:o})}d(P,"resolveLevel");function X(t,e){const o=t.split("."),i=e.split(".");for(let n=0;n<o.length;n++){if(o[n]===">")return n===o.length-1&&i.length>n;if(n>=i.length||o[n]!=="*"&&o[n]!==i[n])return!1}return o.length===i.length}d(X,"matchSubject");const D={"next-turn":1,"after-turn":2,steer:3,interrupt:4};function Q(t,e){let o=null;for(const i of t){let n=null;for(const r of i)r.threshold<=e&&(!n||r.threshold>n.threshold)&&(n=r);n&&(!o||D[n.delivery]>D[o])&&(o=n.delivery)}return o}d(Q,"resolveDelivery");async function Ne(t,e,o){if(e.config)return e.config;const i=await t.prepare("SELECT listen_json FROM objects WHERE id = ?").get(o.id);return i?.listen_json?M(i.listen_json):k}d(Ne,"listenConfigFor");function Z(t,e){return e.inboxKind??(t.level>=7?"urgent":"update")}d(Z,"inboxKind");async function ee(t,e){return e.type==="node"?e.id:await t.prepare(`SELECT 1 FROM objects WHERE type IN ('person','app','repo','profile')
|
|
2
|
+
AND name = ? AND id != ? LIMIT 1`).get(e.name,e.id)?`${e.type}:${e.name}`:e.name}d(ee,"ownerHandleOf");async function te(t,e,o){const i=e.object.owner===null?null:await t.prepare("SELECT * FROM objects WHERE id = ?").get(e.object.owner),n=i?.id??null,r=i?await ee(t,i):null,f=e.object.type==="node"?await t.prepare("SELECT i.external_id FROM nodes n JOIN identities i ON i.object = n.app AND i.scheme = 'app' WHERE n.node_id = ?").get(e.object.id):void 0;return{event:e.event,level:e.level,object:{id:e.object.id,type:e.object.type,kind:e.object.kind,owner:n,owner_handle:r,...f?{app:f.external_id}:{},name:e.object.name,...e.object.home_machine?{machine:T(e.object.home_machine),machine_id:e.object.home_machine}:{}},actor:e.actor?.id??null,...e.actor?.home_machine?{actor_machine:T(e.actor.home_machine),actor_machine_id:e.actor.home_machine}:{},attrs:o,...e.pointer===void 0?{}:{pointer:e.pointer}}}d(te,"pushFields");async function ne(t,e,o,i){const n=await te(t,e,i),r=e.messageBody!==void 0&&o.carriesBody===!0,f=r?typeof e.messageBody=="object"&&e.messageBody!==null&&!Array.isArray(e.messageBody)?{...e.messageBody,push:n}:{body:e.messageBody,push:n}:{push:n},c=r?e.messageBody:null,l=c?.bodyRef===void 0?null:c;return{kind:Z(e,o),label:l?.label??(typeof c?.label=="string"?c.label:e.object.type.includes(".")&&typeof e.messageBody?.summary=="string"?e.messageBody.summary.split(`
|
|
3
|
+
`)[0]:`${e.event} ${e.object.type} ${e.object.name}`),...l?.bodyRef===void 0?{}:{bodyRef:l.bodyRef},...l?.disposition===void 0?{}:{disposition:l.disposition},senderNodeId:e.actor?.type==="node"?e.actor.id:null,senderName:e.actor===null||e.actor===void 0?null:e.actor.home_machine?`${e.actor.name} on ${T(e.actor.home_machine)}`:e.actor.name,bodyJson:f}}d(ne,"messageInput");async function oe(t,e,o,i,n){if(!o.supersedes)return[{attrs:e.attrs,destinations:[...i]}];const r=t.prepare(`SELECT d.delivery_id, d.message_id, d.state, m.body_json FROM deliveries d
|
|
4
4
|
JOIN messages m ON m.message_id = d.message_id
|
|
5
5
|
WHERE d.destination_id = ? AND d.state IN ('pending', 'handed_off')
|
|
6
6
|
AND d.canceled_at IS NULL AND m.event = ? AND m.object_id = ?
|
|
7
|
-
ORDER BY m.seq`),f=
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
ORDER BY m.seq`),f=t.prepare("DELETE FROM deliveries WHERE delivery_id = ?"),c=t.prepare("SELECT * FROM objects WHERE id = ?"),l=new Map,_=new Set;for(const m of i){const b=n(m),R=await c.get(b),N=R===void 0?void 0:await C(t,R.type),y=await r.all(b,e.event,e.object.id),j=new Set;for(const u of y)u.state==="handed_off"?await N?.supersedeClaimed?.(t,u.delivery_id)&&j.add(u.delivery_id):(await f.run(u.delivery_id),_.add(u.message_id));const s=y.filter(u=>u.state==="pending"||j.has(u.delivery_id)).at(-1),a=s?.message_id??null,p=l.get(a)??{prior:s?.body_json?JSON.parse(s.body_json).push?.attrs??null:null,destinations:[]};p.destinations.push(m),l.set(a,p)}const I=t.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)");for(const m of _)await I.run(m,m);return[...l.values()].map(m=>({attrs:o.merge?o.merge(m.prior,e):e.attrs,destinations:m.destinations}))}d(oe,"supersede");async function ie(t,e,o,i,n){const r=await U(t,i);if(r===null||r.type!=="app")return!1;const f=await t.prepare("SELECT 1 FROM peer_stubs WHERE object = ?").get(e.id)!==void 0,c=f?null:await K(t,r,n);if(!f&&c===null||e.owner===null)return!1;const l=await J(t,{class:"app",app:r,scopes:c??[]});return(e.owner===r.id||await S(t,l,"edit",e.owner))&&(c===null||await S(t,l,"read",o.id,{verb:"link"}))}d(ie,"linkDelivers");async function se(t,e){return((await t.prepare(`WITH RECURSIVE chain(id, depth) AS (
|
|
8
|
+
SELECT owner, 1 FROM objects WHERE id = ? AND owner IS NOT NULL
|
|
9
|
+
UNION ALL
|
|
10
|
+
SELECT o.owner, c.depth + 1 FROM objects o JOIN chain c ON o.id = c.id WHERE o.owner IS NOT NULL
|
|
11
|
+
) SELECT o.type FROM chain c JOIN objects o ON o.id = c.id ORDER BY c.depth DESC LIMIT 1`).get(e.id))?.type??e.type)==="repo"}d(se,"inRepoSpace");const A=8;class re extends L{static{d(this,"HopLimitError")}constructor(e){super("hop_limit",`event depth ${e} exceeds ${A}`,v.GENERAL,{depth:e,limit:A})}}async function Oe(t,e,o=async()=>[]){const i=await V(t,e.object.type,e.event),n={...e,level:P(e,i)};n.object.type==="document"&&(n.event==="edited"||n.event==="deleted")&&z(()=>O.emit(n.object.id,n.event));const r=e.cause??null,f=r===null?null:await t.prepare(`SELECT depth FROM messages WHERE message_id = ?
|
|
12
|
+
UNION ALL SELECT depth FROM message_causes WHERE message_id = ? LIMIT 1`).get(r,r);if(r!==null&&f===void 0)throw new L("unknown_cause",`no message ${r}`,v.USAGE,{cause:r});const c=f===null?0:f.depth+1;if(c>A)throw new re(c);(i.endsWait===!0||n.event==="deleted")&&await Y(t,n.object.id);const l=`w.watcher, o.id AS watcher_id, o.type AS watcher_type, o.reception, s.pattern, s.filter_json,
|
|
13
|
+
COALESCE(s.config_json, o.listen_json) AS config_json, w.link_app`,_=await t.prepareUncached(i.durable?`SELECT ${l} FROM watches w JOIN watch_subscriptions s ON s.watch = w.watch_id
|
|
14
|
+
JOIN objects o ON o.id = w.watcher WHERE w.object = ? AND w.watcher != ? AND o.reception >= 1`:`SELECT ${l} FROM objects o JOIN watches w ON w.watcher = o.id JOIN watch_subscriptions s ON s.watch = w.watch_id
|
|
15
|
+
WHERE o.reception = 2 AND w.object = ? AND w.watcher != ?`).all(n.object.id,n.actor?.id??"");if(_.length===0)return{delivered:0,message_id:null,depth:c};const I={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},m=new Map;for(const s of _){if(n.onlyWatchers!==void 0&&!n.onlyWatchers.includes(s.watcher_id)||!X(s.pattern,n.event)||s.filter_json!==null&&!x(JSON.parse(s.filter_json),I))continue;const a=m.get(s.watcher)??{id:s.watcher_id,type:s.watcher_type,reception:s.reception,configs:[],configured:!1,linkApp:s.link_app};a.configs.push(s.config_json?M(s.config_json):k),a.configured||=s.config_json!==null,m.set(s.watcher,a)}const b=await se(t,n.object),R=d(async()=>[],"noRepos"),N=G(t),y=[];for(const s of m.values()){const a=await t.prepare("SELECT * FROM objects WHERE id = ?").get(s.id),p=await C(t,a.type),u=p.receiverFor?await p.receiverFor(t,a):p.receiver;if(u===void 0)continue;const E=u==="peer"&&!s.configured?"next-turn":Q(s.configs,n.level);if(E===null)continue;const h=await q(t,a,b?o:R,N);if(h.scopes===null&&(h.class==="node"||h.owner.type!=="person"))continue;const w=await S(t,h,"read",n.object.id),g=s.linkApp!==null&&!w&&await ie(t,n.object,a,s.linkApp,N);!g&&!w||y.push({id:s.id,reception:s.reception,delivery:E,receiver:u,...g?{linked:g}:{}})}if(y.length===0)return{delivered:0,message_id:null,depth:c};let j=null;for(const s of await oe(t,n,i,y,a=>a.id)){const a=new Map;for(const w of s.destinations)a.set(w.receiver,[...a.get(w.receiver)??[],w]);const p=await ne(t,n,i,s.attrs),u=[...a].map(([w,g])=>F[w].bodyOwner(g)).find(w=>w!==null)??null,E=$({...p,urgency:"normal"},u),h=await B(t,{operationId:E.operationId,createdAt:E.createdAt,senderNodeId:p.senderNodeId,senderName:p.senderName,kind:p.kind,label:p.label,bodyRef:E.bodyRef,bodyJson:E.bodyJson,disposition:p.disposition??null,event:{event:n.event,objectId:n.object.id,level:n.level,actorId:n.actor?.id??null},causeMessageId:r,depth:c});j??=h;for(const[w,g]of a)await F[w].enqueue(t,{messageId:h,event:n.event,level:n.level,senderNodeId:p.senderNodeId,elevatesTier:i.elevatesTier===!0,...n.idempotencyKey===void 0?{}:{idempotencyKey:n.idempotencyKey}},g)}return{delivered:y.length,message_id:j,depth:c}}d(Oe,"publish");export{A as HOP_LIMIT,re as HopLimitError,Ne as listenConfigFor,X as matchSubject,Oe as publish,Q as resolveDelivery,P as resolveLevel,de as subscribeDocumentEvents,oe as supersede};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var
|
|
2
|
-
AND substr(name, 1, ?) = ? ORDER BY name`).all(n.owner,i.length,i
|
|
1
|
+
var v=Object.defineProperty;var m=(e,t)=>v(e,"name",{value:t,configurable:!0});import{recordMemoryReadsIn as _}from"../../canvas/memory-reads.js";import{deliveryDocOf as D,nodeSubject as N,ownerHandle as $}from"../../substrate/delivery/corpus.js";import{recordContentDelivery as x}from"../../substrate/delivery/deliver.js";import{renderExplicitRead as S}from"../../substrate/delivery/render-event.js";import{permits as R,resolveReader as A}from"../../permissions/evaluator.js";import{documentRecord as O,readDocument as j}from"../documents.js";import{loadLedger as k,saveLedger as E}from"../exposures.js";import{getById as I}from"../objects.js";import{rulesOf as p}from"../rules.js";import{objectDTO as d}from"./dto.js";import{withBase as L}from"./kind.js";import{attr as l,header as M,section as T}from"./render.js";import{mapAsync as h}from"../../async-array.js";async function b(e,t,n){const i=`${n.name}/`,s=await R(e,await A(e,t),"read","o"),o=await e.prepareUncached(`SELECT o.* FROM objects o WHERE o.type = 'document' AND o.deleted_at IS NULL AND o.home_machine IS NULL AND o.owner IS NOT DISTINCT FROM ?
|
|
2
|
+
AND substr(o.name, 1, ?) = ? AND strpos(substr(o.name, ? + 1), '/') = 0 AND ${s.sql} ORDER BY o.name`).all(n.owner,i.length,i,i.length,...s.params);return await h(o,async a=>await d(e,a))}m(b,"listingOf");async function B(e,t,n){const i=t.class==="node",s=i?await k(e,t.node.id):{node:"",preferenceSnapshot:null,objects:new Map,listed:new Map},o={db:e,reader:t,subject:i?await N(e,t):null,ledger:s,channel:"transcript"},a=await S(o,await D(e,t,n));return i&&(await x(e,t,a.delivered),await E(e,s)),{attachments:a.attachments,linked:a.linked.map(u=>u.object)}}m(B,"explicitReadAttachments");async function C(e,t,n){if(t.class!=="node"||n.kind!=="knowledge"&&n.kind!=="preference"||n.owner===null)return;const i=await I(e,n.owner);i!==null&&await _(e,t.node.id,[{name:n.name,scope:$(i),at:new Date().toISOString(),type:"read"}])}m(C,"recordExplicitMemoryRead");function U(e,t){const n=[];if(t.attachments&&n.push(t.attachments,""),n.push(M("object",e,l("privacy",e.privacy)+l("revision",t.revision))),n.push(`Preview: ${e.preview}`),t.summary&&n.push(`Summary: ${t.summary}`),t.fields!==void 0&&n.push(`<fields>
|
|
3
3
|
${JSON.stringify(t.fields,null,2)}
|
|
4
|
-
</fields>`),t.needs_merge?(t.heads.forEach((i,s)=>{n.push(`<head${l("rev",i.rev_id)}${l("number",i.number)}${l("author",i.author_id)}>`,t.bodies[s]??"","</head>")}),n.push(`This document has ${t.heads.length} heads. Your next \`crtr doc edit\` writes the merged body and joins them into one revision.`)):n.push("",t.bodies[0]??""),t.listing&&t.listing.length>0){const i=t.listing.some(
|
|
5
|
-
`)}m(
|
|
4
|
+
</fields>`),t.needs_merge?(t.heads.forEach((i,s)=>{n.push(`<head${l("rev",i.rev_id)}${l("number",i.number)}${l("author",i.author_id)}>`,t.bodies[s]??"","</head>")}),n.push(`This document has ${t.heads.length} heads. Your next \`crtr doc edit\` writes the merged body and joins them into one revision.`)):n.push("",t.bodies[0]??""),t.listing&&t.listing.length>0){const i=t.listing.some(a=>a.owner?.id===e.owner?.id&&a.name.startsWith(`${e.name}/`)),s=t.listing.some(a=>a.owner?.id!==e.owner?.id||!a.name.startsWith(`${e.name}/`)),o=i&&s?`Names under ${e.name}/ and documents it links to`:i?`Names under ${e.name}/`:"Documents it links to";n.push("",...T(o,t.listing))}return n.join(`
|
|
5
|
+
`)}m(U,"render");function W(e){const t=e.attrs.revision,n=e.attrs.from_revision??(typeof t=="number"?t-1:null),i=e.attrs.rationale;return{...e.attrs,from_revision:n,revision:t,authors:e.actor?[e.actor.id]:[],rationales:typeof i=="string"?[i]:[]}}m(W,"editAttrs");const F=m((e,t)=>{const n=W(t);if(e===null)return n;const i=e.authors??[];return{...n,from_revision:e.from_revision??null,authors:[...i,...n.authors.filter(s=>!i.includes(s))],rationales:[...e.rationales??[],...n.rationales]}},"mergeEdits"),ne={name:"document",addressable:"name",events:L({edited:{level:8,supersedes:!0,merge:F},deleted:{level:8}}),async listItem(e,t){return{summary:(await O(e,t)).summary??null,rules:(await p(e,t)).length}},async read(e,t,n,i){const s=await j(e,t,n,i.reposFn);await C(e,t,n);const o=s.record,a=await b(e,t,n),{attachments:u,linked:w}=await B(e,t,n),y=new Set(a.map(r=>r.id)),f=[...a,...await h(w.filter(r=>!y.has(r.id)),async r=>await d(e,r))],c={summary:o.summary??null,revision:o.revision,heads:s.heads.map(r=>({rev_id:r.rev_id,number:r.number,author_id:r.author,created_at:r.created_at})),bodies:s.heads.map(r=>r.body),needs_merge:s.needsMerge,...f.length>0?{listing:f}:{},...u===""?{}:{attachments:u}};return i.fields&&(c.fields={why_it_exists:o.why_it_exists??null,unlisted:o.unlisted,lint_ignore:o.lint_ignore??[],extensions:o.extensions??{},rules:(await p(e,n)).map(({matchFrontmatter:r,...g})=>({...g,...r===void 0?{}:{match_frontmatter:r}}))}),{data:c,text:U(await d(e,n),c)}}};export{ne as documentKind};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var r=Object.defineProperty;var l=(e,t)=>r(e,"name",{value:t,configurable:!0});const d={created:{level:4},read:{level:1},watched:{level:1},deleted:{level:5},configured:{level:1}};function
|
|
1
|
+
var r=Object.defineProperty;var l=(e,t)=>r(e,"name",{value:t,configurable:!0});const d={created:{level:4},read:{level:1},watched:{level:1},deleted:{level:5},configured:{level:1},"relation.written":{level:1},"relation.deleted":{level:1}};function o(e={}){return{...d,...e}}l(o,"withBase");export{d as BASE_EVENTS,o as withBase};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var
|
|
2
|
-
`).map(r=>r.trim()).find(r=>r!=="")??""}
|
|
3
|
-
VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, ?)`);for(const
|
|
4
|
-
WHERE n.parent = ? AND o.deleted_at IS NULL ORDER BY o.seq`).all(a.id),u=await
|
|
5
|
-
`);return{data:
|
|
1
|
+
var y=Object.defineProperty;var c=(e,r)=>y(e,"name",{value:r,configurable:!0});import{settleMatchingControllerWait as _}from"../../canvas/waits.js";import{consumeDeadlineOnWake as T,effectiveTier as N,listenDeliveryTier as R,newId as b}from"../../feed/messages.js";import{readGoal as h}from"../../runtime/kickoff.js";import{permits as I,resolveReader as D}from"../../permissions/evaluator.js";import{objectDTO as f}from"./dto.js";import{withBase as S}from"./kind.js";import{recordLiveRead as L}from"./live-read.js";import{attr as O,header as j,section as v}from"./render.js";import{mapAsync as A}from"../../async-array.js";const w=[3,5,7];function g(e){return e?.split(`
|
|
2
|
+
`).map(r=>r.trim()).find(r=>r!=="")??""}c(g,"firstLine");const q={bodyOwner:c(e=>e[0]?.id??null,"bodyOwner"),async enqueue(e,r,a){const s=Date.now(),l=r.senderNodeId,n=a.map(t=>t.id),d=`(${n.map(()=>"?").join(",")})`,m=l===null?new Set:new Set((await e.prepareUncached(`SELECT DISTINCT node_id FROM waits WHERE kind = 'controller' AND target_id = ? AND satisfied_at IS NULL AND node_id IN ${d}`).all(l,...n)).map(t=>t.node_id)),u=new Set((await e.prepareUncached(`SELECT DISTINCT anchor_node FROM crons WHERE cancel_on_wake = 1 AND anchor_node IN ${d}`).all(...n)).map(t=>t.anchor_node)),o=e.prepare(`INSERT INTO deliveries (delivery_id, message_id, destination_id, urgency, wake, state, created_at, updated_at, idempotency_key)
|
|
3
|
+
VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, ?)`);for(const t of a){const i=R(t.delivery),p=t.reception===2?i.wake:0,E=r.elevatesTier?await N(t.id,i.urgency):i.urgency;await o.run(b(),r.messageId,t.id,E,p,s,s,r.idempotencyKey===void 0?null:`${r.idempotencyKey}:${t.id}`),m.has(t.id)&&await _(t.id,l,s,e),p===1&&E!=="deferred"&&u.has(t.id)&&await T(t.id)}}},K={name:"node",addressable:"id",receiver:"inbox",receiverFor:c(async(e,r)=>await e.prepare("SELECT 1 FROM peer_stubs WHERE object = ?").get(r.id)===void 0?"inbox":"peer","receiverFor"),events:S({configured:{level:1,durable:!0},messaged:{level:2,durable:!0},sent:{level:1,durable:!0},"report.update":{level:5,levels:w,durable:!0,carriesBody:!0,elevatesTier:!0},"report.final":{level:7,durable:!0,carriesBody:!0,elevatesTier:!0,inboxKind:"final"},outcome:{level:1,durable:!0,supersedes:!0,carriesBody:!0},"report.result":{level:5,durable:!0,carriesBody:!0,elevatesTier:!0,inboxKind:"completed"},"notice.crash":{level:5,levels:w,durable:!0,carriesBody:!0,elevatesTier:!0},"notice.deadline":{level:5,levels:w,durable:!0,carriesBody:!0,elevatesTier:!0},"notice.close":{level:5,levels:w,durable:!0,carriesBody:!0,elevatesTier:!0}}),async read(e,r,a,s){const l=await e.prepare("SELECT status FROM nodes WHERE node_id = ?").get(a.id),n=await I(e,await D(e,r),"read","o"),d=c(async i=>await A(i,async p=>await f(e,p)),"visible"),m=await e.prepareUncached(`SELECT o.* FROM nodes n JOIN objects o ON o.id = n.node_id
|
|
4
|
+
WHERE n.parent = ? AND o.deleted_at IS NULL AND ${n.sql} ORDER BY o.seq`).all(a.id,...n.params),u=await e.prepareUncached(`SELECT o.* FROM objects o WHERE o.owner = ? AND o.deleted_at IS NULL AND ${n.sql} ORDER BY o.seq`).all(a.id,...n.params);await L(e,r,a,s.reposFn);const o={status:l?.status??"deleted",mandate_first_line:g(await h(a.id)),children:await d(m),owned:await d(u.filter(i=>i.type!=="job")),jobs:await d(u.filter(i=>i.type==="job"))},t=[j("object",await f(e,a),O("status",o.status)),`Mandate: ${o.mandate_first_line}`,...v("Children",o.children),...v("Owns",o.owned),...v("Jobs",o.jobs)].join(`
|
|
5
|
+
`);return{data:o,text:t}}};export{q as nodeInbox,K as nodeKind};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type EvalReader } from '../permissions/evaluator.js';
|
|
2
|
+
import { type Frag } from '../permissions/sql.js';
|
|
1
3
|
import type { ObjectRow, Reader } from './types.js';
|
|
2
4
|
import type { CanvasDb } from '../canvas/db.js';
|
|
3
5
|
export type Resolution = {
|
|
@@ -15,6 +17,13 @@ export type Resolution = {
|
|
|
15
17
|
* first, the profile's project repos, profile, app, you, then the apps whose tools the run may use (installed
|
|
16
18
|
* plugins) with `crtr` last. */
|
|
17
19
|
export declare function viewOf(db: CanvasDb, reader: Reader): Promise<ObjectRow[]>;
|
|
20
|
+
/** SQL true when the reader may read the candidate `alias`, live or deleted. A live object is `permits(read)`. A deleted object other than a node
|
|
21
|
+
* has lost its parent in the evaluator's `relation_source` (a reaped node keeps it, so its live documents stay readable), so a deleted one without a schema type is read through the facts on itself and on each owner up its
|
|
22
|
+
* chain (the authority it held before deletion), within the reader's ceiling. The "every app is visible" rule is not such a grant. A deleted
|
|
23
|
+
* schema-typed object has had its facts removed, so only the `permits` branch answers it: its own namespace's app. */
|
|
24
|
+
export declare function readableOrDeleted(db: CanvasDb, ev: EvalReader, alias: string): Promise<Frag>;
|
|
25
|
+
/** Whether the reader may read the object `id`, live or deleted. */
|
|
26
|
+
export declare function canReadOrDeleted(db: CanvasDb, ev: EvalReader, id: string): Promise<boolean>;
|
|
18
27
|
/** The owner types that carry a handle; `<type>:<handle>/<name>` names one exactly. */
|
|
19
28
|
export declare const HANDLE_TYPES: readonly ["person", "app", "repo", "profile"];
|
|
20
29
|
/** A ref without its `[[ ]]` wrapper. */
|
|
@@ -32,8 +41,14 @@ export declare function ownersByHandle(db: CanvasDb, handle: string, type?: stri
|
|
|
32
41
|
/** Precomputed for one reader across many resolutions (a corpus names every document). */
|
|
33
42
|
export interface ResolveContext {
|
|
34
43
|
view: ObjectRow[];
|
|
35
|
-
|
|
44
|
+
ev: EvalReader;
|
|
36
45
|
}
|
|
46
|
+
/** For each of `names`, the id of the live object `resolveName(name, context)` answers, or null when it answers none (a deleted, ambiguous or
|
|
47
|
+
* missing name, or a live object under the name's own split, which is named otherwise), computed for all of them in two queries. No entry
|
|
48
|
+
* when only `resolveName` itself can tell: a wrapped or old-report name, or a name that is an object id. A corpus resolves every document's
|
|
49
|
+
* name to choose its display name; one `resolveName` each walks the whole view, twice for a name it misses, with a permission-checked
|
|
50
|
+
* query per owner. */
|
|
51
|
+
export declare function viewResolutions(db: CanvasDb, context: ResolveContext, names: readonly string[]): Promise<Map<string, string | null>>;
|
|
37
52
|
export declare function resolveName(db: CanvasDb, reader: Reader, raw: string, context?: ResolveContext): Promise<Resolution>;
|
|
38
53
|
/** Every readable stub of another machine's object that `raw` (`<owner handle>/<name>`, `<type>:<owner handle>/<name>` or a bare name) names:
|
|
39
54
|
* empty when none does, more than one when the name is ambiguous. `resolveName` never answers a stub, so a stub cannot shadow a local
|