@north-light/cli 0.3.549 → 0.3.551

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.
Files changed (161) hide show
  1. package/dist/api/contract.d.ts +1 -1
  2. package/dist/api/contract.js +1 -1
  3. package/dist/api/dto/custom-objects.d.ts +1 -1
  4. package/dist/api/dto/docs.d.ts +12 -0
  5. package/dist/api/dto/objects.d.ts +17 -1
  6. package/dist/api/dto/objects.js +1 -1
  7. package/dist/api/dto/permissions.d.ts +76 -0
  8. package/dist/api/dto/permissions.js +1 -0
  9. package/dist/api/index.d.ts +3 -2
  10. package/dist/api/index.js +1 -1
  11. package/dist/api/routes.d.ts +4 -0
  12. package/dist/api/routes.js +1 -1
  13. package/dist/builtin-memory/crouter-concepts/documents.md +1 -1
  14. package/dist/builtin-memory/crouter-concepts/scopes-and-trust.md +2 -2
  15. package/dist/builtin-memory/crouter-sdk/release-notes.md +11 -0
  16. package/dist/builtin-memory/internal/INDEX.md +2 -1
  17. package/dist/builtin-memory/internal/agent-shaping.md +1 -1
  18. package/dist/builtin-memory/internal/storage-tiers.md +1 -1
  19. package/dist/builtin-memory/northlight-apps/guides/files-and-documents.md +3 -3
  20. package/dist/builtin-memory/northlight-apps/guides/many-users.md +1 -1
  21. package/dist/builtin-memory/northlight-apps/guides/test-your-app.md +1 -1
  22. package/dist/clients/attach/viewer.js +9 -9
  23. package/dist/commands/canvas/list.js +2 -2
  24. package/dist/commands/canvas/share.d.ts +2 -0
  25. package/dist/commands/canvas/share.js +2 -0
  26. package/dist/commands/canvas.js +1 -1
  27. package/dist/commands/doc/edit.js +1 -1
  28. package/dist/commands/doc/shared.js +1 -1
  29. package/dist/commands/doc/write.js +1 -1
  30. package/dist/commands/sys/sync-skills.js +2 -2
  31. package/dist/core/canvas/canvas.js +20 -20
  32. package/dist/core/canvas/db.js +4 -4
  33. package/dist/core/canvas/export-sqlite.js +2 -2
  34. package/dist/core/canvas/import-sqlite.js +5 -5
  35. package/dist/core/canvas/outcome-actions.js +3 -3
  36. package/dist/core/canvas/person.js +11 -9
  37. package/dist/core/canvas/schema-pg.sql +49 -5
  38. package/dist/core/environments.js +1 -1
  39. package/dist/core/feed/feed.js +1 -1
  40. package/dist/core/grants/mirror.d.ts +9 -1
  41. package/dist/core/grants/mirror.js +2 -2
  42. package/dist/core/grants/sync.js +1 -1
  43. package/dist/core/graph/access.d.ts +8 -49
  44. package/dist/core/graph/access.js +4 -7
  45. package/dist/core/graph/custom.d.ts +5 -2
  46. package/dist/core/graph/custom.js +9 -9
  47. package/dist/core/graph/deletion.js +5 -4
  48. package/dist/core/graph/documents.d.ts +12 -3
  49. package/dist/core/graph/documents.js +13 -7
  50. package/dist/core/graph/edges.js +3 -3
  51. package/dist/core/graph/events.js +12 -8
  52. package/dist/core/graph/kinds/document.js +4 -4
  53. package/dist/core/graph/kinds/kind.js +1 -1
  54. package/dist/core/graph/kinds/node.js +5 -5
  55. package/dist/core/graph/names.d.ts +16 -1
  56. package/dist/core/graph/names.js +11 -3
  57. package/dist/core/graph/objects.d.ts +19 -2
  58. package/dist/core/graph/objects.js +16 -10
  59. package/dist/core/graph/package-docs.js +5 -5
  60. package/dist/core/graph/package-files.js +2 -2
  61. package/dist/core/graph/reader.js +3 -3
  62. package/dist/core/graph/repo-sync/exchange.js +2 -2
  63. package/dist/core/graph/rules.d.ts +7 -5
  64. package/dist/core/graph/rules.js +3 -3
  65. package/dist/core/graph/types.d.ts +12 -1
  66. package/dist/core/graph/types.js +1 -1
  67. package/dist/core/graph/watches.d.ts +1 -1
  68. package/dist/core/graph/watches.js +9 -8
  69. package/dist/core/peer/envelope.d.ts +1 -1
  70. package/dist/core/peer/envelope.js +1 -1
  71. package/dist/core/peer/stubs.d.ts +1 -1
  72. package/dist/core/peer/stubs.js +1 -1
  73. package/dist/core/permissions/boot.d.ts +17 -0
  74. package/dist/core/permissions/boot.js +18 -0
  75. package/dist/core/permissions/builtin.d.ts +8 -0
  76. package/dist/core/permissions/builtin.js +20 -0
  77. package/dist/core/permissions/ceiling.d.ts +57 -0
  78. package/dist/core/permissions/ceiling.js +22 -0
  79. package/dist/core/permissions/compile.d.ts +14 -0
  80. package/dist/core/permissions/compile.js +1 -0
  81. package/dist/core/permissions/errors.d.ts +7 -0
  82. package/dist/core/permissions/errors.js +1 -0
  83. package/dist/core/permissions/evaluator.d.ts +28 -0
  84. package/dist/core/permissions/evaluator.js +6 -0
  85. package/dist/core/permissions/facts.d.ts +56 -0
  86. package/dist/core/permissions/facts.js +2 -0
  87. package/dist/core/permissions/schema-parser.d.ts +29 -0
  88. package/dist/core/permissions/schema-parser.js +2 -0
  89. package/dist/core/permissions/schemas.d.ts +21 -0
  90. package/dist/core/permissions/schemas.js +2 -0
  91. package/dist/core/permissions/sql.d.ts +16 -0
  92. package/dist/core/permissions/sql.js +1 -0
  93. package/dist/core/runtime/broker/frame-client.d.ts +1 -1
  94. package/dist/core/runtime/broker/frame-client.js +1 -1
  95. package/dist/core/runtime/broker/frame-dispatch.d.ts +1 -1
  96. package/dist/core/runtime/broker/frame-dispatch.js +1 -1
  97. package/dist/core/runtime/broker/frame-read.d.ts +4 -3
  98. package/dist/core/runtime/broker/frame-read.js +1 -1
  99. package/dist/core/runtime/broker-protocol.d.ts +1 -1
  100. package/dist/core/runtime/host.js +3 -3
  101. package/dist/core/runtime/placement.js +1 -1
  102. package/dist/core/runtime/roadmap.js +1 -1
  103. package/dist/core/substrate/delivery/corpus.d.ts +2 -2
  104. package/dist/core/substrate/delivery/corpus.js +3 -3
  105. package/dist/core/substrate/delivery/deliver.js +2 -2
  106. package/dist/core/substrate/delivery/render-event.js +6 -6
  107. package/dist/core/substrate/subject-fields.d.ts +2 -0
  108. package/dist/core/substrate/subject.js +1 -1
  109. package/dist/daemon/api/app-listener.js +12 -12
  110. package/dist/daemon/api/bridge.d.ts +1 -1
  111. package/dist/daemon/api/bridge.js +4 -4
  112. package/dist/daemon/api/handlers/attach.d.ts +4 -1
  113. package/dist/daemon/api/handlers/attach.js +1 -1
  114. package/dist/daemon/api/handlers/canvas.js +5 -5
  115. package/dist/daemon/api/handlers/check.d.ts +2 -0
  116. package/dist/daemon/api/handlers/check.js +1 -0
  117. package/dist/daemon/api/handlers/custom-objects.js +2 -2
  118. package/dist/daemon/api/handlers/docs.js +1 -1
  119. package/dist/daemon/api/handlers/files.js +1 -1
  120. package/dist/daemon/api/handlers/memory.js +7 -6
  121. package/dist/daemon/api/handlers/nodes.js +1 -1
  122. package/dist/daemon/api/handlers/objects.js +3 -3
  123. package/dist/daemon/api/handlers/relations.d.ts +2 -0
  124. package/dist/daemon/api/handlers/relations.js +1 -0
  125. package/dist/daemon/api/handlers/schemas.d.ts +12 -0
  126. package/dist/daemon/api/handlers/schemas.js +1 -0
  127. package/dist/daemon/api/handlers/shares.d.ts +2 -0
  128. package/dist/daemon/api/handlers/shares.js +1 -0
  129. package/dist/daemon/api/operations.js +1 -1
  130. package/dist/daemon/api/router.d.ts +10 -6
  131. package/dist/daemon/api/router.js +1 -1
  132. package/dist/daemon/api/server.js +1 -1
  133. package/dist/daemon/crtrd.js +12 -12
  134. package/dist/daemon/reconcilers/delivery-lane.js +2 -2
  135. package/dist/doc-editor/editor.css +1 -1
  136. package/dist/doc-editor/editor.js +16 -16
  137. package/dist/migrations/004-canvas-documents/fields.d.ts +1 -1
  138. package/dist/migrations/004-canvas-documents/fields.js +2 -2
  139. package/docs/apps/guides/files-and-documents.md +3 -3
  140. package/docs/apps/guides/many-users.md +1 -1
  141. package/docs/apps/guides/test-your-app.md +1 -1
  142. package/docs/cli/documents-and-preferences.md +1 -1
  143. package/docs/concepts/documents.md +1 -1
  144. package/docs/concepts/scopes-and-trust.md +2 -2
  145. package/docs/sdk/release-notes.md +11 -0
  146. package/package.json +2 -2
  147. package/packages/crouter-identity/dist/index.js +14 -1
  148. package/packages/crouter-identity/package.json +1 -1
  149. package/runtime.lock.json +13 -13
  150. package/dist/core/graph/spaces/app.d.ts +0 -3
  151. package/dist/core/graph/spaces/app.js +0 -1
  152. package/dist/core/graph/spaces/index.d.ts +0 -18
  153. package/dist/core/graph/spaces/index.js +0 -1
  154. package/dist/core/graph/spaces/own-memory.d.ts +0 -18
  155. package/dist/core/graph/spaces/own-memory.js +0 -1
  156. package/dist/core/graph/spaces/person.d.ts +0 -5
  157. package/dist/core/graph/spaces/person.js +0 -3
  158. package/dist/core/graph/spaces/repo.d.ts +0 -14
  159. package/dist/core/graph/spaces/repo.js +0 -1
  160. package/dist/core/runs/confinement.d.ts +0 -25
  161. package/dist/core/runs/confinement.js +0 -2
@@ -1,3 +1,11 @@
1
- var $=Object.defineProperty;var s=(e,t)=>$(e,"name",{value:t,configurable:!0});import{isSafeNodeId as H}from"../canvas/paths.js";import{canRead as D,toolApps as C}from"./access.js";import{getById as M}from"./objects.js";import{filterAsync as O,findAsync as d,flatMapAsync as h}from"../async-array.js";async function S(e,t){const n=await e.prepare("SELECT * FROM objects WHERE type = 'person' AND name = 'user' AND home_machine IS NULL").get(),r=await C(e,t),o=t.class==="node"?[t.node,...t.repos,...t.projectRepos,t.profile,t.app]:t.class==="app"?[t.app]:t.class==="object"?[t.owner,...t.repos,t.app]:[],c=new Set;return[...o,n,...r].filter(a=>!!a&&!c.has(a.id)&&!!c.add(a.id))}s(S,"viewOf");async function m(e,t,n,r,o=!1){return await e.prepareUncached(`SELECT * FROM objects WHERE (type = 'document' OR strpos(type, '.') > 0) AND owner = ? AND name = ? AND home_machine IS ${o?"NOT NULL":"NULL"}
2
- AND deleted_at IS ${r?"NOT NULL ORDER BY deleted_at DESC":"NULL"}`).all(t.id,n)}s(m,"namedObjects");const b=["person","app","repo","profile"],j=/^(person|app|repo|profile|node):([^/]+)\/(.+)$/,I=s(e=>e.trim().replace(/^\[\[(.*)\]\]$/,"$1").trim(),"unwrapRef");function g(e){const t=j.exec(e);if(t)return{type:t[1],handle:t[2],rest:t[3]};const n=e.indexOf("/");return n>0?{handle:e.slice(0,n),rest:e.slice(n+1)}:null}s(g,"splitOwnerRef");async function x(e,t,n){return n==="node"?await e.prepare("SELECT * FROM objects WHERE type = 'node' AND id = ?").all(t):n?await e.prepare("SELECT * FROM objects WHERE type = ? AND (name = ? OR id = ?) AND home_machine IS NULL").all(n,t,t):await e.prepare(`SELECT * FROM objects WHERE (type = 'node' AND id = ?)
3
- OR (type IN ('person','app','repo','profile') AND (name = ? OR id = ?) AND home_machine IS NULL)`).all(t,t,t)}s(x,"ownersByHandle");async function P(e,t,n,r){let o=I(n);const c=/^([^:/]+):reports\/(.+?)(?:\.md)?$/.exec(o);c&&!j.test(o)&&(o=`${c[1]}/reports/${c[2]}`);const a=r?.readable??(async p=>await D(e,t,p)),l=await M(e,o);if(l&&await a(l))return l.deleted_at?{deleted:l}:{object:l};const y=r?.view??await S(e,t);for(const p of y){const f=await d(await m(e,p,o,!1),a);if(f)return{object:f}}const u=g(o);if(u){const{type:p,handle:f,rest:R}=u,T=p!==void 0,N=await x(e,f,p),L=s(i=>{const w=y.findIndex(_=>_.id===i.id);return w<0?1/0:w},"rank"),U=N.filter(i=>L(i)<1/0).sort((i,w)=>L(i)-L(w));for(const i of U){const w=await d(await m(e,i,R,!1),a);if(w)return{object:w}}const E=await O(await h(N,async i=>await m(e,i,R,!1)),a);if(E.length===1)return{object:E[0]};if(E.length>1)return{ambiguous:E};const A=await d(await h(N,async i=>await m(e,i,R,!0)),a);return A?{deleted:A}:!T&&N.length===0&&H(f)?{remote:o}:{missing:!0}}for(const p of y){const f=await d(await m(e,p,o,!0),a);if(f)return{deleted:f}}return{missing:!0}}s(P,"resolveName");async function V(e,t,n){const r=I(n),o=s(async(l,y)=>await O(await h(l,async u=>await m(e,u,y,!1,!0)),async u=>await D(e,t,u)),"found"),c=await o(await S(e,t),r);if(c.length)return c;const a=g(r);return a?await o(await x(e,a.handle,a.type),a.rest):[]}s(V,"stubsNamed");export{b as HANDLE_TYPES,x as ownersByHandle,P as resolveName,V as stubsNamed,I as unwrapRef,S as viewOf};
1
+ var F=Object.defineProperty;var p=(t,e)=>F(t,"name",{value:e,configurable:!0});import{isSafeNodeId as H}from"../canvas/paths.js";import{OBJECT_TYPE as x}from"../permissions/builtin.js";import{ceilingFrag as W,resolveCeiling as v}from"../permissions/ceiling.js";import{compilePermission as Y}from"../permissions/compile.js";import{permits as k,resolveReader as A,userOf as q}from"../permissions/evaluator.js";import{loadDefinitions as B}from"../permissions/schemas.js";import{and as P,or as b,raw as I}from"../permissions/sql.js";import{getById as J}from"./objects.js";import{flatMapAsync as O}from"../async-array.js";async function g(t,e){const o=await t.prepare(`SELECT o.* FROM objects o JOIN identities i ON i.object = o.id AND i.scheme = 'user'
2
+ WHERE o.type = 'person' AND o.home_machine IS NULL AND regexp_replace(i.external_id, '^user:', '') = ?`).get(await q(t,e)),i=await t.prepare(`SELECT a.* FROM objects a WHERE a.type = 'app' AND a.deleted_at IS NULL AND EXISTS (
3
+ SELECT 1 FROM objects o JOIN documents d ON d.object = o.id
4
+ WHERE o.owner = a.id AND o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL)
5
+ ORDER BY a.name = 'crtr', a.name`).all(),a=e.class==="node"?[e.node,...e.repos,...e.projectRepos,e.profile,e.app]:e.class==="app"?[e.app]:e.class==="object"?[e.owner,...e.repos,e.app]:[],s=new Set;return[...a,o,...i].filter(r=>!!r&&!s.has(r.id)&&!!s.add(r.id))}p(g,"viewOf");async function V(t,e,o){const i=await k(t,e,"read",o),a=await v(t,e.reader,"read"),s=Y(await B(t),e.subjects,x,"read",I("gr.id")),r={sql:`EXISTS (WITH RECURSIVE up(id, depth) AS (
6
+ SELECT ${o}.id, 0
7
+ UNION ALL SELECT p.owner, up.depth + 1 FROM up JOIN objects p ON p.id = up.id WHERE p.owner IS NOT NULL AND up.depth < 12)
8
+ SELECT 1 FROM up gr WHERE ${s.sql})`,params:s.params},c=P([I(`${o}.deleted_at IS NOT NULL AND ${o}.schema_type IS NULL`),...a===null?[]:[W(a,o)],r]);return b([i,c])}p(V,"readableOrDeleted");const S=new WeakMap;function D(t,e){let o=S.get(e);return o===void 0&&(o=V(t,e,"o"),o.catch(()=>S.delete(e)),S.set(e,o)),o}p(D,"readableFrag");async function X(t,e,o){const i=await D(t,e);return(await t.prepareUncached(`SELECT ${i.sql} AS ok FROM objects o WHERE o.id = ?`).get(...i.params,o))?.ok===!0}p(X,"canReadOrDeleted");async function E(t,e,o,i,a,s=!1){const r=await D(t,e);return await t.prepareUncached(`SELECT o.* FROM objects o WHERE (o.type = 'document' OR strpos(o.type, '.') > 0) AND o.owner = ? AND o.name = ? AND o.home_machine IS ${s?"NOT NULL":"NULL"}
9
+ AND o.deleted_at IS ${a?"NOT NULL":"NULL"} AND ${r.sql}${a?" ORDER BY o.deleted_at DESC":""}`).all(o.id,i,...r.params)}p(E,"namedObjects");const re=["person","app","repo","profile"],T=/^(person|app|repo|profile|node):([^/]+)\/(.+)$/,h=p(t=>t.trim().replace(/^\[\[(.*)\]\]$/,"$1").trim(),"unwrapRef");function j(t){const e=T.exec(t);if(e)return{type:e[1],handle:e[2],rest:e[3]};const o=t.indexOf("/");return o>0?{handle:t.slice(0,o),rest:t.slice(o+1)}:null}p(j,"splitOwnerRef");async function U(t,e,o){return o==="node"?await t.prepare("SELECT * FROM objects WHERE type = 'node' AND id = ?").all(e):o?await t.prepare("SELECT * FROM objects WHERE type = ? AND (name = ? OR id = ?) AND home_machine IS NULL").all(o,e,e):await t.prepare(`SELECT * FROM objects WHERE (type = 'node' AND id = ?)
10
+ OR (type IN ('person','app','repo','profile') AND (name = ? OR id = ?) AND home_machine IS NULL)`).all(e,e,e)}p(U,"ownersByHandle");const _=/^([^:/]+):reports\/(.+?)(?:\.md)?$/;async function ie(t,e,o){const i=[...new Set(o.filter(n=>h(n)===n&&!_.test(n)))],a=new Map;if(i.length===0||e.view.length===0)return a;const s=new Set((await t.prepare("SELECT id FROM objects WHERE id = ANY(?::text[])").all(i)).map(n=>n.id)),r=i.filter(n=>!s.has(n));for(const n of r)a.set(n,null);const c=await D(t,e.ev),w=await t.prepareUncached(`SELECT o.id, o.owner, o.name FROM objects o WHERE (o.type = 'document' OR strpos(o.type, '.') > 0)
11
+ AND o.owner = ANY(?::text[]) AND o.name = ANY(?::text[]) AND o.home_machine IS NULL AND o.deleted_at IS NULL AND ${c.sql}`).all(e.view.map(n=>n.id),r,...c.params),m=new Map(e.view.map((n,d)=>[n.id,d])),l=new Map;for(const n of w){const d=m.get(n.owner);d<(l.get(n.name)??1/0)&&(l.set(n.name,d),a.set(n.name,n.id))}return a}p(ie,"viewResolutions");async function se(t,e,o,i){let a=h(o);const s=_.exec(a);s&&!T.test(a)&&(a=`${s[1]}/reports/${s[2]}`);const r=i?.ev??await A(t,e),c=await J(t,a);if(c&&await X(t,r,c.id))return c.deleted_at?{deleted:c}:{object:c};const w=i?.view??await g(t,e);for(const l of w){const n=(await E(t,r,l,a,!1))[0];if(n)return{object:n}}const m=j(a);if(m){const{type:l,handle:n,rest:d}=m,C=l!==void 0,N=await U(t,n,l),R=p(f=>{const u=w.findIndex($=>$.id===f.id);return u<0?1/0:u},"rank"),M=N.filter(f=>R(f)<1/0).sort((f,u)=>R(f)-R(u));for(const f of M){const u=(await E(t,r,f,d,!1))[0];if(u)return{object:u}}const L=await O(N,async f=>await E(t,r,f,d,!1));if(L.length===1)return{object:L[0]};if(L.length>1)return{ambiguous:L};const y=(await O(N,async f=>await E(t,r,f,d,!0)))[0];return y?{deleted:y}:!C&&N.length===0&&H(n)?{remote:a}:{missing:!0}}for(const l of w){const n=(await E(t,r,l,a,!0))[0];if(n)return{deleted:n}}return{missing:!0}}p(se,"resolveName");async function ce(t,e,o){const i=h(o),a=await A(t,e),s=p(async(w,m)=>await O(w,async l=>await E(t,a,l,m,!1,!0)),"found"),r=await s(await g(t,e),i);if(r.length)return r;const c=j(i);return c?await s(await U(t,c.handle,c.type),c.rest):[]}p(ce,"stubsNamed");export{re as HANDLE_TYPES,X as canReadOrDeleted,U as ownersByHandle,V as readableOrDeleted,se as resolveName,ce as stubsNamed,h as unwrapRef,g as viewOf,ie as viewResolutions};
@@ -12,6 +12,8 @@ export declare function createObject(db: CanvasDb, spec: {
12
12
  preview: string;
13
13
  /** Set for a stub, in the insert itself: the name indexes cover only rows without it. */
14
14
  home_machine?: string;
15
+ /** The permission schema type the object is evaluated under (`firstlight/memory`); omitted = `crtr/object`. */
16
+ schema_type?: string;
15
17
  }): Promise<ObjectRow>;
16
18
  /** The owner object for an identity, created on first sight. Idempotent on
17
19
  * (scheme, externalId). Handles are unique per owner type: a handle already held
@@ -31,13 +33,25 @@ export declare function liveDocumentNamed(db: CanvasDb, owner: string, name: str
31
33
  export declare function nodeObject(db: CanvasDb, nodeId: string): Promise<ObjectRow>;
32
34
  export declare function setObjectName(db: CanvasDb, object: ObjectRow, name: string): Promise<void>;
33
35
  export declare function setObjectKind(db: CanvasDb, object: ObjectRow, kind: string | null): Promise<void>;
34
- export interface ObjectFilter {
36
+ export interface ExtensionsFilter {
37
+ /** Documents whose `extensions` contain this (JSON containment: `{ 'firstlight': { workspace: 'w1' } }`). */
38
+ extensions?: Record<string, Record<string, unknown>>;
39
+ /** Objects whose permission schema type is exactly this. */
40
+ schemaType?: string;
41
+ }
42
+ export interface ObjectFilter extends ExtensionsFilter {
35
43
  type?: string;
36
44
  kind?: string;
37
45
  owner?: ObjectRow;
38
46
  privacy?: Privacy;
39
47
  name?: string;
40
48
  deleted?: boolean;
49
+ /** Objects whose name starts with this text (matched literally). */
50
+ namePrefix?: string;
51
+ /** Only objects the reader reaches through a share and not through its own tree: a `reader`/`editor`/`messenger`/`user_of` fact (not a package's) names the
52
+ * reader's user, `user:*` or a team it belongs to, on the object or an ancestor, and no `acts_for` fact names the reader's user on the object or an ancestor.
53
+ * Applies to objects governed by `crtr/object` (no `schema_type`). */
54
+ shared?: boolean;
41
55
  }
42
56
  /** `after_seq` is the `seq` of the last object on the previous page (the `/v1` cursor, a decimal number). */
43
57
  export interface Page {
@@ -46,10 +60,13 @@ export interface Page {
46
60
  }
47
61
  /** Objects the reader can read, in `seq` (insertion) order. Records nothing. `limit` ≤ 100. */
48
62
  export declare function listObjects(db: CanvasDb, reader: Reader, filter: ObjectFilter, page?: Page): Promise<ObjectRow[]>;
49
- export interface SearchQuery {
63
+ export interface SearchQuery extends ExtensionsFilter {
50
64
  text: string;
51
65
  body?: boolean;
52
66
  grep?: boolean;
67
+ type?: string;
68
+ kind?: string;
69
+ owner?: ObjectRow;
53
70
  }
54
71
  export interface SearchHit {
55
72
  object: ObjectRow;
@@ -1,11 +1,17 @@
1
- var y=Object.defineProperty;var s=(o,e)=>y(o,"name",{value:e,configurable:!0});import{CrtrError as N,notFound as _}from"../errors.js";import{ExitCode as L}from"../../types.js";import{newNodeId as I}from"../runtime/nodes.js";import{kindOf as j}from"./kinds/registry.js";import{canRead as h}from"./access.js";import{bodyOf as A}from"./bodies.js";import{personId as b}from"../canvas/person.js";const T=new Set(["person","app","repo","profile"]);function O(o){return o||null}s(O,"rowFrom");async function D(o,e){return O(await o.prepare("SELECT * FROM objects WHERE id = ?").get(e))}s(D,"getById");async function x(o,e){return await j(o,e.type),await o.prepare(`INSERT INTO objects (id, type, kind, owner, privacy, name, preview, created_at, home_machine)
2
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(e.id??await I(),e.type,e.kind,e.owner,e.privacy,e.name,e.preview,new Date().toISOString(),e.home_machine??null)}s(x,"createObject");async function R(o,e,t,r,n,i){if(!T.has(e))throw new Error(`ensureOwner: ${e} is not an owner type`);const d=await o.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.id = i.object
3
- WHERE i.scheme = ? AND i.external_id = ?`).get(t,r);if(d)return d;const c=await o.prepare(`SELECT o.id, o.type, i.scheme, i.external_id FROM objects o
1
+ var F=Object.defineProperty;var i=(e,t)=>F(e,"name",{value:t,configurable:!0});import{CrtrError as _,notFound as M}from"../errors.js";import{ExitCode as j}from"../../types.js";import{newNodeId as H}from"../runtime/nodes.js";import{kindOf as W}from"./kinds/registry.js";import{bodyOf as v}from"./bodies.js";import{personId as k,personIdAsync as J}from"../canvas/person.js";import{check as P,permits as l,resolveReader as I}from"../permissions/evaluator.js";import{userSubject as y,writeActsFor as b}from"../permissions/facts.js";const Y=new Set(["person","app","repo","profile"]);function D(e){return e||null}i(D,"rowFrom");async function K(e,t){return D(await e.prepare("SELECT * FROM objects WHERE id = ?").get(t))}i(K,"getById");async function X(e,t){return await W(e,t.type),await e.prepare(`INSERT INTO objects (id, type, kind, owner, privacy, name, preview, created_at, home_machine, schema_type)
2
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(t.id??await H(),t.type,t.kind,t.owner,t.privacy,t.name,t.preview,new Date().toISOString(),t.home_machine??null,t.schema_type??null)}i(X,"createObject");async function T(e,t,o,s,n,c){if(!Y.has(t))throw new Error(`ensureOwner: ${t} is not an owner type`);const p=await e.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.id = i.object
3
+ WHERE i.scheme = ? AND i.external_id = ?`).get(o,s);if(p)return p;const d=await e.prepare(`SELECT o.id, o.type, i.scheme, i.external_id FROM objects o
4
4
  LEFT JOIN identities i ON i.object = o.id
5
- WHERE o.name = ? AND o.type = ? AND o.home_machine IS NULL`).get(n,e);if(c)throw new N("conflict",`handle "${n}" is held by ${c.type} ${c.id} (${c.scheme}:${c.external_id}); cannot give it to ${e} ${t}:${r}`,L.GENERAL,{handle:n,holder:c.id});const p=await x(o,{type:e,kind:null,owner:e==="profile"?i?.id??null:null,privacy:"public",name:n,preview:""});return await o.prepare("INSERT INTO identities (object, scheme, external_id) VALUES (?, ?, ?)").run(p.id,t,r),p}s(R,"ensureOwner");async function P(o,e,t){const r=await o.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.id = i.object
6
- WHERE i.scheme = 'profile' AND i.external_id = ? AND o.type = 'profile' AND o.deleted_at IS NULL`).get(e),n=await o.prepare(`SELECT o.id FROM objects o
7
- WHERE o.name = ? AND o.type = 'profile' AND o.deleted_at IS NULL AND o.home_machine IS NULL AND o.id <> ?`).get(t,r?r.id:"");if(n)throw new N("conflict",`handle "${t}" is already held by profile object ${n.id}; cannot rename profile ${e} to it`,L.GENERAL,{handle:t,holder:n.id});if(!r)return!1;const i=r;return i.name===t?!1:(await $(o,i,t),!0)}s(P,"renameProfileObject");async function J(o,e){if(/^app:[a-z0-9][a-z0-9._-]*$/.test(e))return await R(o,"app","app",e,e.slice(4));if(/^user:[a-z0-9][a-z0-9._-]*$/.test(e))return await R(o,"person","user",e,e===b()?"user":e.slice(5));throw new Error(`principal ${JSON.stringify(e)} is neither app:<name> nor user:<id>`)}s(J,"ownerForGrantee");async function z(o,e,t){return O(await o.prepare("SELECT * FROM objects WHERE (type = 'document' OR strpos(type, '.') > 0) AND owner = ? AND name = ? AND deleted_at IS NULL AND home_machine IS NULL").get(e,t))}s(z,"liveDocumentNamed");async function B(o,e){const t=await D(o,e);if(!t||t.type!=="node")throw _(`no node object ${e}`);return t}s(B,"nodeObject");async function $(o,e,t){await o.prepare("UPDATE objects SET name = ? WHERE id = ?").run(t,e.id)}s($,"setObjectName");async function G(o,e,t){await o.prepare("UPDATE objects SET kind = ? WHERE id = ?").run(t,e.id)}s(G,"setObjectKind");async function K(o,e,t,r={}){const n=["seq > ?"],i=[r.after_seq??0];return t.type!==void 0&&(n.push("type = ?"),i.push(t.type)),t.kind!==void 0&&(n.push("kind = ?"),i.push(t.kind)),t.owner!==void 0&&(n.push("owner = ?"),i.push(t.owner.id)),t.privacy!==void 0&&(n.push("privacy = ?"),i.push(t.privacy)),t.name!==void 0&&(n.push("name = ?"),i.push(t.name)),n.push("home_machine IS NULL"),n.push(t.deleted?"deleted_at IS NOT NULL":"deleted_at IS NULL"),await C(o,e,`SELECT * FROM objects WHERE ${n.join(" AND ")}`,i,Math.min(r.limit??100,100))}s(K,"listObjects");async function Y(o,e,t){const n=[],i=`%${t.text.replace(/[\\%_]/g,a=>`\\${a}`)}%`;if(!t.grep){const a=await o.prepare(`SELECT o.*, d.summary AS _summary FROM objects o LEFT JOIN documents d ON d.object = o.id
8
- WHERE o.deleted_at IS NULL AND o.home_machine IS NULL AND (o.name ILIKE ?1 ESCAPE '\\' OR o.preview ILIKE ?1 ESCAPE '\\' OR d.summary ILIKE ?1 ESCAPE '\\')
9
- ORDER BY o.seq`).all(i),u=t.text.toLowerCase();for(const f of a){const{_summary:m,...E}=f,w=E;if(!await h(o,e,w))continue;const l=w.name.toLowerCase().includes(u)?"name":w.preview.toLowerCase().includes(u)?"preview":"summary";if(n.push({object:w,field:l,...l==="summary"?{text:String(m)}:{}}),n.length>=100)return n}}if(!t.body&&!t.grep)return n;const d=new Set(n.map(a=>a.object.id)),c=t.grep?(a=>u=>a.test(u))(new RegExp(t.text)):a=>a.includes(t.text),p=await o.prepare("SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL ORDER BY seq").all(),S=o.prepare(`SELECT r.body_hash, d.source_path FROM revisions r JOIN documents d ON d.object = r.document WHERE r.document = ?
10
- AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rev_id)`);for(const a of p)if(!(d.has(a.id)||!await h(o,e,a)))for(const{body_hash:u,source_path:f}of await S.all(a.id)){const m=A(u,f).split(`
11
- `),E=m.findIndex(c);if(!(E<0)){if(n.push({object:a,field:"body",line:E+1,text:m[E]}),n.length>=100)return n;break}}return n}s(Y,"searchObjects");async function C(o,e,t,r,n){const i=[],d=o.prepareUncached(`${t} ORDER BY seq LIMIT ${n*4}`);for(;;){const c=await d.all(...r);for(const p of c)if(await h(o,e,p)&&i.push(p),i.length===n)return i;if(c.length<n*4)return i;r[0]=c[c.length-1].seq}}s(C,"readablePages");export{x as createObject,R as ensureOwner,D as getById,K as listObjects,z as liveDocumentNamed,B as nodeObject,J as ownerForGrantee,P as renameProfileObject,Y as searchObjects,G as setObjectKind,$ as setObjectName};
5
+ WHERE o.name = ? AND o.type = ? AND o.home_machine IS NULL`).get(n,t);if(d)throw new _("conflict",`handle "${n}" is held by ${d.type} ${d.id} (${d.scheme}:${d.external_id}); cannot give it to ${t} ${o}:${s}`,j.GENERAL,{handle:n,holder:d.id});const E=await X(e,{type:t,kind:null,owner:t==="profile"?c?.id??null:null,privacy:"public",name:n,preview:""});return await e.prepare("INSERT INTO identities (object, scheme, external_id) VALUES (?, ?, ?)").run(E.id,o,s),t==="person"&&o==="user"?await b(e,E.id,y(s).id):(t==="profile"||t==="repo")&&await b(e,E.id,y(await J()).id),E}i(T,"ensureOwner");async function re(e,t,o){const s=await e.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.id = i.object
6
+ WHERE i.scheme = 'profile' AND i.external_id = ? AND o.type = 'profile' AND o.deleted_at IS NULL`).get(t),n=await e.prepare(`SELECT o.id FROM objects o
7
+ WHERE o.name = ? AND o.type = 'profile' AND o.deleted_at IS NULL AND o.home_machine IS NULL AND o.id <> ?`).get(o,s?s.id:"");if(n)throw new _("conflict",`handle "${o}" is already held by profile object ${n.id}; cannot rename profile ${t} to it`,j.GENERAL,{handle:o,holder:n.id});if(!s)return!1;const c=s;return c.name===o?!1:(await z(e,c,o),!0)}i(re,"renameProfileObject");async function ie(e,t){if(/^app:[a-z0-9][a-z0-9._-]*$/.test(t))return await T(e,"app","app",t,t.slice(4));if(/^user:[a-z0-9][a-z0-9._-]*$/.test(t))return await T(e,"person","user",t,t===k()?"user":t.slice(5));throw new Error(`principal ${JSON.stringify(t)} is neither app:<name> nor user:<id>`)}i(ie,"ownerForGrantee");async function se(e,t,o){return D(await e.prepare("SELECT * FROM objects WHERE (type = 'document' OR strpos(type, '.') > 0) AND owner = ? AND name = ? AND deleted_at IS NULL AND home_machine IS NULL").get(t,o))}i(se,"liveDocumentNamed");async function ae(e,t){const o=await K(e,t);if(!o||o.type!=="node")throw M(`no node object ${t}`);return o}i(ae,"nodeObject");async function z(e,t,o){await e.prepare("UPDATE objects SET name = ? WHERE id = ?").run(o,t.id)}i(z,"setObjectName");async function ce(e,t,o){await e.prepare("UPDATE objects SET kind = ? WHERE id = ?").run(o,t.id)}i(ce,"setObjectKind");function S(e,t){const o=[];return e.type!==void 0&&(o.push("o.type = ?"),t.push(e.type)),e.kind!==void 0&&(o.push("o.kind = ?"),t.push(e.kind)),e.owner!==void 0&&(o.push("o.owner = ?"),t.push(e.owner.id)),e.namePrefix!==void 0&&(o.push("o.name LIKE ? ESCAPE '\\'"),t.push(`${e.namePrefix.replace(/[\\%_]/g,s=>`\\${s}`)}%`)),e.schemaType!==void 0&&(o.push("o.schema_type = ?"),t.push(e.schemaType)),e.extensions!==void 0&&(o.push("EXISTS (SELECT 1 FROM documents x WHERE x.object = o.id AND x.extensions_json::jsonb @> ?::jsonb)"),t.push(JSON.stringify(e.extensions))),o}i(S,"narrowing");function B(e,t){const o="WITH RECURSIVE sa(id, owner, depth) AS (SELECT o.id, o.owner, 0 UNION ALL SELECT p.id, p.owner, sa.depth + 1 FROM sa JOIN objects p ON p.id = sa.owner WHERE sa.depth < 12)";return t.push(e.subjects.userIds,e.subjects.groupIds,e.userId),["o.schema_type IS NULL",`EXISTS (${o} SELECT 1 FROM sa JOIN relations r ON r.resource_type = 'crtr/object' AND r.resource_id = sa.id
8
+ AND r.relation IN ('reader', 'editor', 'messenger', 'user_of') AND r.written_by <> 'package'
9
+ AND ((r.subject_type = 'user' AND r.subject_id = ANY(?::text[])) OR (r.subject_type = 'crtr/group' AND r.subject_relation = 'member' AND r.subject_id = ANY(?::text[]))))`,`NOT EXISTS (${o} SELECT 1 FROM sa JOIN relations r ON r.resource_type = 'crtr/object' AND r.resource_id = sa.id
10
+ AND r.relation = 'acts_for' AND r.subject_type = 'user' AND r.subject_id = ?)`]}i(B,"sharedOnly");async function de(e,t,o,s={}){const n=["o.seq > ?"],c=[s.after_seq??0];o.privacy!==void 0&&(n.push("o.privacy = ?"),c.push(o.privacy)),o.name!==void 0&&(n.push("o.name = ?"),c.push(o.name)),n.push("o.home_machine IS NULL"),n.push(o.deleted?"o.deleted_at IS NOT NULL":"o.deleted_at IS NULL"),n.push(...S(o,c));const p=await I(e,t);o.shared&&n.push(...B(p,c));const d=await l(e,p,"read","o"),E=Math.min(s.limit??100,100);return await e.prepareUncached(`SELECT o.* FROM objects o WHERE ${n.join(" AND ")} AND (${d.sql}) ORDER BY o.seq LIMIT ${E}`).all(...c,...d.params)}i(de,"listObjects");async function pe(e,t,o){const n=[],c=`%${o.text.replace(/[\\%_]/g,r=>`\\${r}`)}%`,p=await I(e,t);if(!o.grep){const r=[c],u=S(o,r).map(w=>` AND ${w}`).join(""),a=await l(e,p,"read","o"),m=await e.prepareUncached(`SELECT o.*, (SELECT d.summary FROM documents d WHERE d.object = o.id) AS _summary FROM objects o
11
+ WHERE o.deleted_at IS NULL AND o.home_machine IS NULL AND (o.name ILIKE ?1 ESCAPE '\\' OR o.preview ILIKE ?1 ESCAPE '\\'
12
+ OR EXISTS (SELECT 1 FROM documents d WHERE d.object = o.id AND d.summary ILIKE ?1 ESCAPE '\\'))${u}
13
+ AND (${a.sql}) ORDER BY o.seq LIMIT 100`).all(...r,...a.params),h=o.text.toLowerCase();for(const w of m){const{_summary:U,...g}=w,f=g,R=f.name.toLowerCase().includes(h)?"name":f.preview.toLowerCase().includes(h)?"preview":"summary";n.push({object:f,field:R,...R==="summary"?{text:String(U)}:{}})}if(n.length>=100)return n}if(!o.body&&!o.grep)return n;const d=new Set(n.map(r=>r.object.id)),E=o.grep?(r=>u=>r.test(u))(new RegExp(o.text)):r=>r.includes(o.text),O=[],x=S(o,O).map(r=>` AND ${r}`).join(""),$=await e.prepareUncached(`SELECT o.id, r.body_hash AS body_hash, d.source_path AS source_path FROM objects o
14
+ JOIN documents d ON d.object = o.id JOIN revisions r ON r.document = o.id
15
+ WHERE o.type = 'document' AND o.deleted_at IS NULL AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rev_id)${x}
16
+ ORDER BY o.seq, r.number`).all(...O),L=await l(e,p,"read","o"),C=200;let N=[];const A=i(async()=>{if(N.length===0)return!1;const r=await e.prepareUncached(`SELECT o.* FROM objects o WHERE o.id = ANY(?::text[]) AND (${L.sql})`).all(N.map(a=>a.id),...L.params),u=new Map(r.map(a=>[a.id,a]));for(const a of N){const m=u.get(a.id);if(m!==void 0&&(n.push({object:m,field:"body",line:a.line,text:a.text}),n.length>=100))return!0}return N=[],!1},"flush");for(const r of $){if(d.has(r.id))continue;let u;try{u=v(r.body_hash,r.source_path)}catch(h){if(await P(e,p,"read",r.id))throw h;continue}if(!o.grep&&!u.includes(o.text))continue;const a=u.split(`
17
+ `),m=a.findIndex(E);if(!(m<0)&&(d.add(r.id),N.push({id:r.id,line:m+1,text:a[m]}),N.length>=C&&await A()))return n}return await A(),n}i(pe,"searchObjects");export{X as createObject,T as ensureOwner,K as getById,de as listObjects,se as liveDocumentNamed,ae as nodeObject,ie as ownerForGrantee,re as renameProfileObject,pe as searchObjects,ce as setObjectKind,z as setObjectName};
@@ -1,6 +1,6 @@
1
- var S=Object.defineProperty;var u=(e,a)=>S(e,"name",{value:a,configurable:!0});import{createHash as _,randomUUID as O}from"node:crypto";import{CrtrError as g}from"../errors.js";import{emitEvent as I}from"../events/emit.js";import{quarantineBrokenPlugins as D}from"../plugin-quarantine.js";import{listInstalledPlugins as T}from"../resolver.js";import{builtinMemoryRoot as L,pluginMemoryDir as R}from"../scope.js";import{documentFields as k}from"../../migrations/004-canvas-documents/fields.js";import{applyDocumentFields as A,deleteDocument as j,documentRecord as F,heads as x,importDocument as C,linksIn as H,moveDocument as U}from"./documents.js";import{isShipped as b}from"./access.js";import{setLinks as J}from"./edges.js";import{publish as M}from"./events.js";import{resolveName as W}from"./names.js";import{packageFiles as P}from"./package-files.js";import{ensureOwner as q,getById as E,setObjectKind as B}from"./objects.js";import{checkoutsForCwd as K}from"./repo-sync/repos.js";import{rulesOf as $}from"./rules.js";function z(){return[{plugin:"crtr",dir:L()},...T("user").filter(e=>e.enabled).map(e=>({plugin:e.name,dir:R(e)}))]}u(z,"packageSources");const G=u(e=>_("sha256").update(e,"utf8").digest("hex"),"sha256");async function fe(e,a){const i={created:0,updated:0,deleted:0,conflicts:[],invalid:[],quarantined:[]};if(a===void 0){i.quarantined=D();for(const n of i.quarantined)I({level:"error",event:"daemon.plugins.quarantined",fields:{plugin:n.name,file:n.file,reason:n.reason,...n.movedTo!==void 0?{moved_to:n.movedTo}:{}}})}const l=a??z(),m=u(async n=>await K(e,n),"reposFn"),o=new Set,r=[],f=new Set;for(const n of l){let s;try{if(f.has(n.plugin))throw new g("conflict",`plugin "${n.plugin}" is already provided by another package`);f.add(n.plugin),s=await q(e,"app","app",`app:${n.plugin}`,n.plugin)}catch(p){if(!(p instanceof g)||p.code!=="conflict")throw p;i.conflicts.push({plugin:n.plugin,message:p.message});continue}o.add(s.id);const{files:d,invalid:w}=P(n.dir);i.invalid.push(...w);const h=new Set;for(const p of d){const c=await V(e,s,p,i,m);h.add(c),r.push({owner:s,id:c,body:p.body})}await v(e,s,h,i,m)}const t=await e.prepare(`SELECT DISTINCT o.owner AS id FROM objects o JOIN documents d ON d.object = o.id
2
- WHERE o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all();for(const{id:n}of t)o.has(n)||await v(e,await E(e,n),new Set,i,m);for(const{owner:n,id:s,body:d}of r)await Q(e,n,await E(e,s),d);return i}u(fe,"reconcilePackageDocuments");async function Q(e,a,i,l){if(i.deleted_at!==null)return;const m={class:"app",app:a,scopes:[]},o=[];for(const t of H(l)){const n=await W(e,m,t);"object"in n&&n.object.id!==i.id&&await b(e,n.object)&&o.push({written_as:t,dst:n.object})}const r=u(t=>JSON.stringify(t.map(n=>[n.dst,n.written_as]).sort()),"key"),f=await e.prepare("SELECT dst, written_as FROM edges WHERE type = 'links-to' AND src = ?").all(i.id);r(f)!==r(o.map(t=>({dst:t.dst.id,written_as:t.written_as})))&&await J(e,i,o)}u(Q,"deriveLinks");async function V(e,a,i,l,m){const o=k(i.frontmatter,i.body,{missingPrivacy:"public"}),r={preview:o.preview.trim()===""?i.name:o.preview,summary:o.summary,whyItExists:o.whyItExists,unlisted:o.unlisted,rules:o.rules,lintIgnore:Array.isArray(o.lintIgnore)?o.lintIgnore.filter(c=>typeof c=="string"):null,extensions:o.extensions!==null&&typeof o.extensions=="object"&&!Array.isArray(o.extensions)?o.extensions:null},f=G(i.body);let t=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
1
+ var S=Object.defineProperty;var u=(e,a)=>S(e,"name",{value:a,configurable:!0});import{createHash as _,randomUUID as O}from"node:crypto";import{CrtrError as g}from"../errors.js";import{emitEvent as I}from"../events/emit.js";import{quarantineBrokenPlugins as D}from"../plugin-quarantine.js";import{listInstalledPlugins as L}from"../resolver.js";import{builtinMemoryRoot as R,pluginMemoryDir as T}from"../scope.js";import{documentFields as A}from"../../migrations/004-canvas-documents/fields.js";import{applyDocumentFields as k,deleteDocument as j,documentRecord as F,heads as x,importDocument as C,linksIn as H,moveDocument as U}from"./documents.js";import{deleteShippedReader as M,writeShippedReader as W}from"../permissions/facts.js";import{setLinks as J}from"./edges.js";import{publish as b}from"./events.js";import{resolveName as P}from"./names.js";import{packageFiles as q}from"./package-files.js";import{ensureOwner as B,getById as y,setObjectKind as K}from"./objects.js";import{checkoutsForCwd as $}from"./repo-sync/repos.js";import{rulesOf as z}from"./rules.js";function G(){return[{plugin:"crtr",dir:R()},...L("user").filter(e=>e.enabled).map(e=>({plugin:e.name,dir:T(e)}))]}u(G,"packageSources");const Q=u(e=>_("sha256").update(e,"utf8").digest("hex"),"sha256");async function he(e,a){const i={created:0,updated:0,deleted:0,conflicts:[],invalid:[],quarantined:[]};if(a===void 0){i.quarantined=D();for(const n of i.quarantined)I({level:"error",event:"daemon.plugins.quarantined",fields:{plugin:n.name,file:n.file,reason:n.reason,...n.movedTo!==void 0?{moved_to:n.movedTo}:{}}})}const l=a??G(),m=u(async n=>await $(e,n),"reposFn"),o=new Set,r=[],f=new Set;for(const n of l){let s;try{if(f.has(n.plugin))throw new g("conflict",`plugin "${n.plugin}" is already provided by another package`);f.add(n.plugin),s=await B(e,"app","app",`app:${n.plugin}`,n.plugin)}catch(p){if(!(p instanceof g)||p.code!=="conflict")throw p;i.conflicts.push({plugin:n.plugin,message:p.message});continue}o.add(s.id);const{files:c,invalid:w}=q(n.dir);i.invalid.push(...w);const h=new Set;for(const p of c){const d=await Y(e,s,p,i,m);h.add(d),r.push({owner:s,id:d,body:p.body})}await v(e,s,h,i,m)}const t=await e.prepare(`SELECT DISTINCT o.owner AS id FROM objects o JOIN documents d ON d.object = o.id
2
+ WHERE o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all();for(const{id:n}of t)o.has(n)||await v(e,await y(e,n),new Set,i,m);for(const{owner:n,id:s,body:c}of r)await V(e,n,await y(e,s),c);return i}u(he,"reconcilePackageDocuments");async function V(e,a,i,l){if(i.deleted_at!==null)return;const m={class:"app",app:a,scopes:[]},o=[];for(const t of H(l)){const n=await P(e,m,t);"object"in n&&n.object.id!==i.id&&await X(e,n.object.id)&&o.push({written_as:t,dst:n.object})}const r=u(t=>JSON.stringify(t.map(n=>[n.dst,n.written_as]).sort()),"key"),f=await e.prepare("SELECT dst, written_as FROM edges WHERE type = 'links-to' AND src = ?").all(i.id);r(f)!==r(o.map(t=>({dst:t.dst.id,written_as:t.written_as})))&&await J(e,i,o)}u(V,"deriveLinks");async function X(e,a){return await e.prepare("SELECT 1 FROM documents WHERE object = ? AND source_path IS NOT NULL").get(a)!==void 0}u(X,"shippedDocument");async function Y(e,a,i,l,m){const o=A(i.frontmatter,i.body,{missingPrivacy:"public"}),r={preview:o.preview.trim()===""?i.name:o.preview,summary:o.summary,whyItExists:o.whyItExists,unlisted:o.unlisted,rules:o.rules,lintIgnore:Array.isArray(o.lintIgnore)?o.lintIgnore.filter(d=>typeof d=="string"):null,extensions:o.extensions!==null&&typeof o.extensions=="object"&&!Array.isArray(o.extensions)?o.extensions:null},f=Q(i.body);let t=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
3
3
  WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path = ?`).get(a.id,i.path);if(t||(t=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
4
- WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND o.name = ? AND d.source_path IS NOT NULL`).get(a.id,i.name),t&&await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(i.path,t.id)),!t){const c=await C(e,{owner:a,name:i.name,kind:o.kind,privacy:"public",...r,revisions:[{body_hash:f,author:a,rationale:null,created_at:new Date().toISOString()}]});return await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(i.path,c.id),l.created+=1,c.id}const n=t.name!==i.name||t.kind!==o.kind;t.name!==i.name&&(t=await U(e,null,t,{name:i.name})),t.kind!==o.kind&&(await B(e,t,o.kind),t=await E(e,t.id));const s=await F(e,t),[d]=await x(e,t),w=d?.body_hash!==f,h=t.preview!==r.preview||s.summary!==r.summary||s.why_it_exists!==r.whyItExists||s.unlisted!==r.unlisted||JSON.stringify(s.lint_ignore)!==JSON.stringify(r.lintIgnore)||JSON.stringify(s.extensions)!==JSON.stringify(r.extensions)||JSON.stringify((await $(e,t)).map(N))!==JSON.stringify(r.rules.map(N));if(!w&&!h&&!n)return t.id;const p=await A(e,t,r);if(w&&d){const c=O(),y=d.number+1;await e.prepare(`INSERT INTO revisions (rev_id, document, number, body_hash, author, rationale, verbatim, created_at)
5
- SELECT ?, document, ?, ?, author, rationale, verbatim, ? FROM revisions WHERE rev_id = ?`).run(c,y,f,new Date().toISOString(),d.rev_id),await e.prepare("INSERT INTO revision_parents (child, parent) SELECT ?, parent FROM revision_parents WHERE child = ?").run(c,d.rev_id),await e.prepare("DELETE FROM revisions WHERE rev_id = ?").run(d.rev_id),await e.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(c,y,p.id),await M(e,{event:"edited",object:p,actor:a,pointer:c,attrs:{revision:y,from_revision:d.number,rationale:"package file changed"}},m)}return l.updated+=1,p.id}u(V,"convergeFile");function N(e){return[e.on,e.deliver,e.match??null,e.matchFrontmatter??null,e.if??null]}u(N,"ruleKey");async function v(e,a,i,l,m){const o=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
6
- WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all(a.id);for(const r of o)i.has(r.id)||(await j(e,null,r,m),l.deleted+=1)}u(v,"deleteMissing");export{z as packageSources,fe as reconcilePackageDocuments};
4
+ WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND o.name = ? AND d.source_path IS NOT NULL`).get(a.id,i.name),t&&await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(i.path,t.id)),!t){const d=await C(e,{owner:a,name:i.name,kind:o.kind,privacy:"public",...r,revisions:[{body_hash:f,author:a,rationale:null,created_at:new Date().toISOString()}]});return await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(i.path,d.id),await W(e,d.id),l.created+=1,d.id}const n=t.name!==i.name||t.kind!==o.kind;t.name!==i.name&&(t=await U(e,null,t,{name:i.name})),t.kind!==o.kind&&(await K(e,t,o.kind),t=await y(e,t.id));const s=await F(e,t),[c]=await x(e,t),w=c?.body_hash!==f,h=t.preview!==r.preview||s.summary!==r.summary||s.why_it_exists!==r.whyItExists||s.unlisted!==r.unlisted||JSON.stringify(s.lint_ignore)!==JSON.stringify(r.lintIgnore)||JSON.stringify(s.extensions)!==JSON.stringify(r.extensions)||JSON.stringify((await z(e,t)).map(N))!==JSON.stringify(r.rules.map(N));if(!w&&!h&&!n)return t.id;const p=await k(e,t,r);if(w&&c){const d=O(),E=c.number+1;await e.prepare(`INSERT INTO revisions (rev_id, document, number, body_hash, author, rationale, verbatim, created_at)
5
+ SELECT ?, document, ?, ?, author, rationale, verbatim, ? FROM revisions WHERE rev_id = ?`).run(d,E,f,new Date().toISOString(),c.rev_id),await e.prepare("INSERT INTO revision_parents (child, parent) SELECT ?, parent FROM revision_parents WHERE child = ?").run(d,c.rev_id),await e.prepare("DELETE FROM revisions WHERE rev_id = ?").run(c.rev_id),await e.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(d,E,p.id),await b(e,{event:"edited",object:p,actor:a,pointer:d,attrs:{revision:E,from_revision:c.number,rationale:"package file changed"}},m)}return l.updated+=1,p.id}u(Y,"convergeFile");function N(e){return[e.on,e.deliver,e.match??null,e.matchFrontmatter??null,e.if??null]}u(N,"ruleKey");async function v(e,a,i,l,m){const o=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
6
+ WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all(a.id);for(const r of o)i.has(r.id)||(await j(e,null,r,m),await M(e,r.id),l.deleted+=1)}u(v,"deleteMissing");export{G as packageSources,he as reconcilePackageDocuments};
@@ -1,2 +1,2 @@
1
- var b=Object.defineProperty;var u=(e,r)=>b(e,"name",{value:r,configurable:!0});import{readFileSync as v}from"node:fs";import{relative as $,sep as S}from"node:path";import{parseFrontmatterGeneric as w}from"../frontmatter.js";import{pathExists as k,walkFiles as x}from"../fs-utils.js";import{explicitLocalName as N,isDocKind as A,localNameFromPath as h,representationForPath as E}from"../substrate/schema.js";function W(e){const r=[];if(!k(e))return{files:[],invalid:r};const n=new Map;for(const o of x(e,i=>i.endsWith(".md")&&i!=="SKILL.md",i=>i===".crouter"||i.startsWith(".")).sort()){const i=$(e,o).split(S).join("/");let s;try{s=w(v(o,"utf8"))}catch(f){r.push({path:o,error:`invalid frontmatter: ${(f instanceof Error?f.message:String(f)).split(`
2
- `)[0]}`});continue}const d=h(i)===""?"":N(s.data)??h(i),a=d===""?"index":d,l={name:a,path:o,frontmatter:s.data,body:s.body,index:E(i)==="directory-index"},c=n.get(a);if(c&&(c.index||!l.index)){r.push({path:o,error:`name ${a} is already held by ${c.path}`});continue}c&&r.push({path:c.path,error:`name ${a} is already held by ${o}`}),n.set(a,l)}return{files:[...n.values()].map(({index:o,...i})=>i).sort((o,i)=>o.name.localeCompare(i.name)),invalid:r}}u(W,"packageFiles");const m=["public","friends","private"],y=["boot","workspace-open","read","memory-read","command","pre-command"],p=["name","preview","content"],j=["system-prompt-visibility","file-read-visibility","applies-to","read-when"],O=["on","match","match-frontmatter","gate","at"],g=["length","broad-memory-read","orphan"];function J(e){if(e===void 0)return null;if(!Array.isArray(e))return`invalid surfaces: ${JSON.stringify(e)} (expected a list of {on, at, match?, match-frontmatter?, gate?} entries)`;for(const r of e){if(r===null||typeof r!="object"||Array.isArray(r))return`invalid surfaces entry: ${JSON.stringify(r)} (expected an {on, at, match?, match-frontmatter?, gate?} object)`;const n=r;for(const a of Object.keys(n))if(!O.includes(a))return`invalid surfaces entry: unknown key \`${a}\` (an entry carries only on, match, match-frontmatter, gate, at)`;const t=n.on;if(typeof t!="string"||!y.includes(t))return`invalid surfaces entry \`on\`: ${JSON.stringify(t)} (expected ${y.join("|")})`;const o=n.at;if(typeof o!="string"||!p.includes(o))return`invalid surfaces entry \`at\`: ${JSON.stringify(o)} (expected ${p.join("|")})`;let i=!1;if(n.match!==void 0){if(t==="boot"||t==="workspace-open")return`invalid surfaces entry: \`match\` is meaningless on \`${t}\` \u2014 the entry's presence is the match; drop it`;const a=n.match,l=typeof a=="string"?[a]:Array.isArray(a)&&a.every(c=>typeof c=="string")?a:null;if(l===null||l.length===0||l.some(c=>c.trim()===""))return`invalid surfaces entry \`match\`: ${JSON.stringify(a)} (expected a non-empty glob or non-empty glob list)`;i=!0}const s=n["match-frontmatter"];if(s!==void 0){if(t!=="read")return"invalid surfaces entry: `match-frontmatter` is a `read`-event predicate only";if(s===null||typeof s!="object"||Array.isArray(s))return`invalid surfaces entry \`match-frontmatter\`: ${JSON.stringify(s)} (expected a field\u2192matcher object)`}if((t==="read"||t==="memory-read"||t==="command"||t==="pre-command")&&!i&&s===void 0)return`invalid surfaces entry: a \`${t}\` entry requires \`match\`${t==="read"?" or `match-frontmatter`":""}`;const d=n.gate;if(d!==void 0&&(d===null||typeof d!="object"||Array.isArray(d)))return`invalid surfaces entry \`gate\`: ${JSON.stringify(d)} (expected a field\u2192matcher object)`}return null}u(J,"lintPackageSurfaces");function C(e){if(e===null)return"missing frontmatter: a package document requires `kind: knowledge|preference`";if(!A(e.kind))return`invalid kind: ${JSON.stringify(e.kind)} (expected knowledge|preference)`;if("when"in e||"why"in e)return'retired `when`/`why` keys: merge them into one `when-and-why-to-read` line \u2014 "When <circumstance>, this <kind> should be read because <broader downstream payoff>." WHY is the benefit unlocked by reading, not the document summary, rule, or obedience rationale.';if(typeof e["when-and-why-to-read"]!="string"||e["when-and-why-to-read"].trim()==="")return'missing `when-and-why-to-read`: one routing line \u2014 "When <circumstance>, this <kind> should be read because <broader downstream payoff>." WHY is the benefit unlocked by reading, not the document summary, rule, or obedience rationale.';for(const n of j)if(n in e)return`retired \`${n}\` key: a package document no longer carries it; delete it and, to load the document on its own, route it with a \`surfaces\` list of {on: ${y.join("|")}, at: ${p.join("|")}, match?, match-frontmatter?, gate?} entries (omit \`surfaces\` for a package document delivered by no rule \u2014 read only when pointed at, listed, or searched)`;if("privacy"in e&&!m.includes(e.privacy))return`invalid privacy: ${JSON.stringify(e.privacy)} (expected ${m.join("|")})`;if(e.unlisted!==void 0&&typeof e.unlisted!="boolean")return`invalid unlisted: ${JSON.stringify(e.unlisted)} (expected a boolean)`;const r=J(e.surfaces);if(r!==null)return r;if(e.gate!==void 0&&(e.gate===null||typeof e.gate!="object"||Array.isArray(e.gate)))return`invalid gate: ${JSON.stringify(e.gate)} (expected a field\u2192matcher object)`;if(e.slash!==void 0&&typeof e.slash!="boolean")return`invalid slash: ${JSON.stringify(e.slash)} (expected a boolean)`;if(e.rationale!==void 0&&typeof e.rationale!="string")return`invalid rationale: ${JSON.stringify(e.rationale)} (expected a string)`;if(e["lint-ignore"]!==void 0){const n=Array.isArray(e["lint-ignore"])?e["lint-ignore"]:[e["lint-ignore"]];if(n.length===0||!n.every(t=>typeof t=="string"&&g.includes(t)))return`invalid lint-ignore: ${JSON.stringify(e["lint-ignore"])} (suppressible rules: ${g.map(t=>`\`${t}\``).join(", ")})`}return null}u(C,"lintPackageFrontmatter");export{C as lintPackageFrontmatter,W as packageFiles};
1
+ var b=Object.defineProperty;var u=(e,r)=>b(e,"name",{value:r,configurable:!0});import{readFileSync as v}from"node:fs";import{relative as $,sep as S}from"node:path";import{parseFrontmatterGeneric as w}from"../frontmatter.js";import{pathExists as k,walkFiles as x}from"../fs-utils.js";import{readLegacyPrivacy as N}from"./types.js";import{explicitLocalName as A,isDocKind as E,localNameFromPath as h,representationForPath as j}from"../substrate/schema.js";function I(e){const r=[];if(!k(e))return{files:[],invalid:r};const n=new Map;for(const o of x(e,i=>i.endsWith(".md")&&i!=="SKILL.md",i=>i===".crouter"||i.startsWith(".")).sort()){const i=$(e,o).split(S).join("/");let s;try{s=w(v(o,"utf8"))}catch(f){r.push({path:o,error:`invalid frontmatter: ${(f instanceof Error?f.message:String(f)).split(`
2
+ `)[0]}`});continue}const d=h(i)===""?"":A(s.data)??h(i),a=d===""?"index":d,l={name:a,path:o,frontmatter:s.data,body:s.body,index:j(i)==="directory-index"},c=n.get(a);if(c&&(c.index||!l.index)){r.push({path:o,error:`name ${a} is already held by ${c.path}`});continue}c&&r.push({path:c.path,error:`name ${a} is already held by ${o}`}),n.set(a,l)}return{files:[...n.values()].map(({index:o,...i})=>i).sort((o,i)=>o.name.localeCompare(i.name)),invalid:r}}u(I,"packageFiles");const m=["public","private"],y=["boot","workspace-open","read","memory-read","command","pre-command"],p=["name","preview","content"],O=["system-prompt-visibility","file-read-visibility","applies-to","read-when"],J=["on","match","match-frontmatter","gate","at"],g=["length","broad-memory-read","orphan"];function F(e){if(e===void 0)return null;if(!Array.isArray(e))return`invalid surfaces: ${JSON.stringify(e)} (expected a list of {on, at, match?, match-frontmatter?, gate?} entries)`;for(const r of e){if(r===null||typeof r!="object"||Array.isArray(r))return`invalid surfaces entry: ${JSON.stringify(r)} (expected an {on, at, match?, match-frontmatter?, gate?} object)`;const n=r;for(const a of Object.keys(n))if(!J.includes(a))return`invalid surfaces entry: unknown key \`${a}\` (an entry carries only on, match, match-frontmatter, gate, at)`;const t=n.on;if(typeof t!="string"||!y.includes(t))return`invalid surfaces entry \`on\`: ${JSON.stringify(t)} (expected ${y.join("|")})`;const o=n.at;if(typeof o!="string"||!p.includes(o))return`invalid surfaces entry \`at\`: ${JSON.stringify(o)} (expected ${p.join("|")})`;let i=!1;if(n.match!==void 0){if(t==="boot"||t==="workspace-open")return`invalid surfaces entry: \`match\` is meaningless on \`${t}\` \u2014 the entry's presence is the match; drop it`;const a=n.match,l=typeof a=="string"?[a]:Array.isArray(a)&&a.every(c=>typeof c=="string")?a:null;if(l===null||l.length===0||l.some(c=>c.trim()===""))return`invalid surfaces entry \`match\`: ${JSON.stringify(a)} (expected a non-empty glob or non-empty glob list)`;i=!0}const s=n["match-frontmatter"];if(s!==void 0){if(t!=="read")return"invalid surfaces entry: `match-frontmatter` is a `read`-event predicate only";if(s===null||typeof s!="object"||Array.isArray(s))return`invalid surfaces entry \`match-frontmatter\`: ${JSON.stringify(s)} (expected a field\u2192matcher object)`}if((t==="read"||t==="memory-read"||t==="command"||t==="pre-command")&&!i&&s===void 0)return`invalid surfaces entry: a \`${t}\` entry requires \`match\`${t==="read"?" or `match-frontmatter`":""}`;const d=n.gate;if(d!==void 0&&(d===null||typeof d!="object"||Array.isArray(d)))return`invalid surfaces entry \`gate\`: ${JSON.stringify(d)} (expected a field\u2192matcher object)`}return null}u(F,"lintPackageSurfaces");function Y(e){if(e===null)return"missing frontmatter: a package document requires `kind: knowledge|preference`";if(!E(e.kind))return`invalid kind: ${JSON.stringify(e.kind)} (expected knowledge|preference)`;if("when"in e||"why"in e)return'retired `when`/`why` keys: merge them into one `when-and-why-to-read` line \u2014 "When <circumstance>, this <kind> should be read because <broader downstream payoff>." WHY is the benefit unlocked by reading, not the document summary, rule, or obedience rationale.';if(typeof e["when-and-why-to-read"]!="string"||e["when-and-why-to-read"].trim()==="")return'missing `when-and-why-to-read`: one routing line \u2014 "When <circumstance>, this <kind> should be read because <broader downstream payoff>." WHY is the benefit unlocked by reading, not the document summary, rule, or obedience rationale.';for(const n of O)if(n in e)return`retired \`${n}\` key: a package document no longer carries it; delete it and, to load the document on its own, route it with a \`surfaces\` list of {on: ${y.join("|")}, at: ${p.join("|")}, match?, match-frontmatter?, gate?} entries (omit \`surfaces\` for a package document delivered by no rule \u2014 read only when pointed at, listed, or searched)`;if("privacy"in e&&!m.includes(N(e.privacy)))return`invalid privacy: ${JSON.stringify(e.privacy)} (expected ${m.join("|")})`;if(e.unlisted!==void 0&&typeof e.unlisted!="boolean")return`invalid unlisted: ${JSON.stringify(e.unlisted)} (expected a boolean)`;const r=F(e.surfaces);if(r!==null)return r;if(e.gate!==void 0&&(e.gate===null||typeof e.gate!="object"||Array.isArray(e.gate)))return`invalid gate: ${JSON.stringify(e.gate)} (expected a field\u2192matcher object)`;if(e.slash!==void 0&&typeof e.slash!="boolean")return`invalid slash: ${JSON.stringify(e.slash)} (expected a boolean)`;if(e.rationale!==void 0&&typeof e.rationale!="string")return`invalid rationale: ${JSON.stringify(e.rationale)} (expected a string)`;if(e["lint-ignore"]!==void 0){const n=Array.isArray(e["lint-ignore"])?e["lint-ignore"]:[e["lint-ignore"]];if(n.length===0||!n.every(t=>typeof t=="string"&&g.includes(t)))return`invalid lint-ignore: ${JSON.stringify(e["lint-ignore"])} (suppressible rules: ${g.map(t=>`\`${t}\``).join(", ")})`}return null}u(Y,"lintPackageFrontmatter");export{Y as lintPackageFrontmatter,I as packageFiles};
@@ -1,3 +1,3 @@
1
- var F=Object.defineProperty;var p=(o,e)=>F(o,"name",{value:e,configurable:!0});import{coveredByAny as f,isSandboxedNode as N}from"../../shared/env.js";import{existsSync as x}from"node:fs";import{CrtrError as b}from"../errors.js";import{getStoredProfile as L}from"../scoped-state/profiles.js";import{userScopeRoot as $}from"../scope.js";import{parseScope as _}from"@north-light/identity";import{activeGrant as S,grantFor as R}from"../grants/mirror.js";import{ApiError as A}from"../../api/errors.js";import{pluginAppToken as C}from"../secrets.js";import{loadExactProfileManifest as M}from"../profiles/manifest.js";import{ensureOwner as O,getById as T,nodeObject as H,ownerForGrantee as W}from"./objects.js";import{personId as D}from"../canvas/person.js";async function v(o,e,r,a){const n=e===null?[]:a.projectPaths(e),t=await r(o),l=new Set(t.map(s=>s.repo.id)),c=[];for(const s of n){if(!x(s))continue;let i;try{i=await r(s)}catch(u){if(u instanceof b&&u.code==="git-lookup-failed")continue;throw u}for(const u of i)l.has(u.repo.id)||(l.add(u.repo.id),c.push(u))}return{repos:t.map(s=>s.repo),checkouts:t,projectRepos:c.map(s=>s.repo),projectCheckouts:c}}p(v,"viewRepos");async function oe(o,e,r){if(e.class==="anonymous")throw new Error("a publish token cannot read as a principal");if(e.class==="person")return{class:"person",person:await j(o)};if(e.class==="app")return{class:"app",app:await E(o,e.app),scopes:e.scopes};if(e.class==="machine")throw new Error("a machine token cannot read as a principal");if(e.class==="forwarded"){if(e.actor.kind==="person")return{class:"person",person:await j(o)};const c=await R(e.actor.app,e.sub);if(!c)throw new A(403,"app_not_granted",`${e.actor.app} holds no active grant for ${e.sub} on this machine`);const s=e.actor.node_scopes;return{class:"app",app:await E(o,e.actor.app),place:e.actor.place,launched:s,scopes:s===null?c.resolved:s.filter(i=>f(c.resolved,i))}}if(e.class==="cron"){const c=e.grant?(await S(e.grant))?.resolved??[]:[],s=e.scopes;return{class:"app",app:await E(o,e.app),scopes:s===null?c:s.filter(i=>f(c,i)),launched:s,place:{run:e.origin_run??"",isolated:e.isolated}}}const a=d(o),n=await H(o,e.node),t=n.owner===null?null:await a.object(n.owner);if(!t)throw new Error(`node ${e.node} has no owner`);const l=await o.prepare("SELECT cwd, profile_id FROM nodes WHERE node_id = ?").get(e.node);return{class:"node",node:n,app:t,scopes:await y(e.app,e.sub,e.grant,e.scopes,a),launched:e.scopes,cwd:l.cwd,...await v(l.cwd,l.profile_id,r,a),profile:l.profile_id===null?null:await a.profile(l.profile_id,t)}}p(oe,"readerFor");async function P(o,e,r,a=d(o),n=!1){const t=await o.prepare("SELECT o.* FROM objects o WHERE o.id = ? AND o.type = 'node'").get(e),l=await o.prepare(`SELECT (SELECT external_id FROM identities WHERE object = ?) AS grantee,
2
- sub, grant_id, scopes_json, cwd, profile_id FROM nodes WHERE node_id = ?`).get(t?.owner??null,e);if(!t||!l)throw new Error(`no node ${e}`);const{grantee:c,sub:s,grant_id:i,scopes_json:u,cwd:w,profile_id:g}=l,m=t.owner===null?null:await a.object(t.owner);if(!m)throw new Error(`node ${e} has no owner`);const h=u===null?null:JSON.parse(u);return{class:"node",node:t,app:m,scopes:await y(c,s??"",i,h,a,n),launched:h,cwd:w,...await v(w,g,r,a),profile:g===null?null:await a.profile(g,m)}}p(P,"readerForNode");async function ne(o,e,r=async()=>[],a=d(o)){const n=await o.prepare("SELECT envelope_json FROM peer_stubs WHERE object = ?").get(e.id);if(n!==void 0)return await B(o,e,n.envelope_json===null?null:JSON.parse(n.envelope_json),a);if(e.type==="node")return await P(o,e.id,r,a,!0);const t=e.owner===null?null:await a.object(e.owner);if(!t)throw new Error(`watcher ${e.id} has no owner`);const c=(t.type==="app"?t:t.type==="profile"&&t.owner!==null?await a.object(t.owner):null)??await o.prepare("SELECT o.* FROM identities i JOIN objects o ON o.id = i.object WHERE i.external_id = 'app:terminal' AND i.scheme = 'app'").get()??await o.prepare("SELECT * FROM objects WHERE type = 'person' AND name = 'user' AND home_machine IS NULL").get();if(!c)throw new Error("no terminal app or person object");const s=(await o.prepare("SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(c.id))?.external_id??"app:terminal",i=await o.prepare("SELECT resolved_scopes_json FROM grants WHERE grantee = ? AND status = 'active'").all(s),u=t.type==="person"?null:i.length===0?a.pluginScopes(s):i.length===1?JSON.parse(i[0].resolved_scopes_json).filter(w=>w.mode==="always").map(w=>_(w.scope)):null;return{class:"object",object:e,node:e,owner:t,app:c,scopes:u,repos:t.type==="repo"?[t]:[]}}p(ne,"readerForObject");async function te(o,e,r){const a=(await o.prepare("SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(e.id))?.external_id;if(a===void 0)return null;const n=await o.prepare("SELECT resolved_scopes_json FROM grants WHERE grantee = ? AND status = 'active'").all(a);return n.length===0?r.pluginScopes(a):n.length>1?null:JSON.parse(n[0].resolved_scopes_json).filter(t=>t.mode==="always").map(t=>_(t.scope))}p(te,"appScopesNow");async function B(o,e,r,a){const n=await j(o);if(r!==null&&r.app===null)return{class:"object",object:e,node:e,owner:n,app:n,scopes:null,repos:[]};const t=r?.app==null?null:await E(o,r.app),l=r?.app==null?null:await a.grantFor(r.app,r.sub),c=r===null||l===null?null:r.node_scopes===null?l.resolved:r.node_scopes.filter(i=>f(l.resolved,i)),s=t??(e.owner===null?n:await a.object(e.owner)??n);return{class:"object",object:e,node:e,owner:s,app:s,scopes:c,launched:r?.node_scopes??null,repos:[],...r?.place==null?{}:{place:r.place}}}p(B,"readerForStub");function d(o){return{grantById:S,grantFor:R,pluginScopes:p(e=>C(e.replace(/^app:/,""))?.scopes??null,"pluginScopes"),object:p(async e=>await T(o,e),"object"),profile:p(async(e,r)=>await J(o,e,r),"profile"),projectPaths:p(e=>N()?[]:L($(),e)?.manifest.projects.map(r=>r.path)??[],"projectPaths")}}p(d,"liveLookups");function re(o){const e=d(o),r=new Map,a=p((n,t)=>(r.has(n)||r.set(n,t()),r.get(n)),"memo");return{grantById:p(n=>a(`g\0${n}`,()=>e.grantById(n)),"grantById"),grantFor:p((n,t)=>a(`a\0${n}\0${t}`,()=>e.grantFor(n,t)),"grantFor"),pluginScopes:p(n=>a(`t\0${n}`,()=>e.pluginScopes(n)),"pluginScopes"),object:p(async n=>await a(`o\0${n}`,async()=>await e.object(n)),"object"),profile:p(async(n,t)=>await a(`p\0${n}\0${t.id}`,async()=>await e.profile(n,t)),"profile"),projectPaths:p(n=>a(`j\0${n}`,()=>e.projectPaths(n)),"projectPaths")}}p(re,"publishLookups");async function y(o,e,r,a,n,t=!1){const l=r?await n.grantById(r):await n.grantFor(o,e),c=l&&l.grantee===o?l.resolved:null;if(c===null){if(t)return null;throw new Error(`the grant of ${o} is not active`)}return a===null?c:a.filter(s=>f(c,s))}p(y,"nodeScopes");async function j(o){return await W(o,D())}p(j,"personObject");async function E(o,e){return await O(o,"app","app",e,e.replace(/^app:/,""))}p(E,"appObject");async function J(o,e,r){return await o.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.id = i.object
3
- WHERE i.scheme = 'profile' AND i.external_id = ?`).get(e)??await O(o,"profile","profile",e,M(e).manifest.name,r)}p(J,"profileObject");export{te as appScopesNow,d as liveLookups,J as profileObject,re as publishLookups,oe as readerFor,P as readerForNode,ne as readerForObject,v as viewRepos};
1
+ var N=Object.defineProperty;var p=(o,e)=>N(o,"name",{value:e,configurable:!0});import{coveredByAny as f,isSandboxedNode as b}from"../../shared/env.js";import{existsSync as x}from"node:fs";import{CrtrError as L}from"../errors.js";import{getStoredProfile as $}from"../scoped-state/profiles.js";import{userScopeRoot as A}from"../scope.js";import{parseScope as S}from"@north-light/identity";import{activeGrant as R,grantFor as O}from"../grants/mirror.js";import{ApiError as C}from"../../api/errors.js";import{pluginAppToken as M}from"../secrets.js";import{loadExactProfileManifest as T}from"../profiles/manifest.js";import{ensureOwner as v,getById as H,nodeObject as W,ownerForGrantee as D}from"./objects.js";import{personId as P}from"../canvas/person.js";async function y(o,e,r,a){const t=e===null?[]:a.projectPaths(e),n=await r(o),l=new Set(n.map(s=>s.repo.id)),c=[];for(const s of t){if(!x(s))continue;let i;try{i=await r(s)}catch(u){if(u instanceof L&&u.code==="git-lookup-failed")continue;throw u}for(const u of i)l.has(u.repo.id)||(l.add(u.repo.id),c.push(u))}return{repos:n.map(s=>s.repo),checkouts:n,projectRepos:c.map(s=>s.repo),projectCheckouts:c}}p(y,"viewRepos");async function te(o,e,r){if(e.class==="anonymous")throw new Error("a publish token cannot read as a principal");if(e.class==="person")return{class:"person",person:await j(o)};if(e.class==="app")return{class:"app",app:await E(o,e.app),scopes:e.scopes,...e.sub===void 0?{}:{sub:e.sub}};if(e.class==="machine")throw new Error("a machine token cannot read as a principal");if(e.class==="forwarded"){if(e.actor.kind==="person")return{class:"person",person:await j(o)};const c=await O(e.actor.app,e.sub);if(!c)throw new C(403,"app_not_granted",`${e.actor.app} holds no active grant for ${e.sub} on this machine`);const s=e.actor.node_scopes;return{class:"app",app:await E(o,e.actor.app),place:e.actor.place,launched:s,scopes:s===null?c.resolved:s.filter(i=>f(c.resolved,i))}}if(e.class==="cron"){const c=e.grant?(await R(e.grant))?.resolved??[]:[],s=e.scopes;return{class:"app",app:await E(o,e.app),scopes:s===null?c:s.filter(i=>f(c,i)),launched:s,place:{run:e.origin_run??"",isolated:e.isolated}}}const a=d(o),t=await W(o,e.node),n=await o.prepare("SELECT cwd, profile_id, app FROM nodes WHERE node_id = ?").get(e.node),l=n.app===null?null:await a.object(n.app);if(!l)throw new Error(`node ${e.node} has no app`);return{class:"node",node:t,app:l,scopes:await F(e.app,e.sub,e.grant,e.scopes,a),launched:e.scopes,cwd:n.cwd,...await y(n.cwd,n.profile_id,r,a),profile:n.profile_id===null?null:await a.profile(n.profile_id,l)}}p(te,"readerFor");async function B(o,e,r,a=d(o),t=!1){const n=await o.prepare("SELECT o.* FROM objects o WHERE o.id = ? AND o.type = 'node'").get(e),l=await o.prepare(`SELECT (SELECT external_id FROM identities WHERE object = nodes.app) AS grantee,
2
+ sub, grant_id, scopes_json, cwd, profile_id, app FROM nodes WHERE node_id = ?`).get(e);if(!n||!l)throw new Error(`no node ${e}`);const{grantee:c,sub:s,grant_id:i,scopes_json:u,cwd:w,profile_id:g,app:h}=l,m=h===null?null:await a.object(h);if(!m)throw new Error(`node ${e} has no app`);const _=u===null?null:JSON.parse(u);return{class:"node",node:n,app:m,scopes:await F(c,s??"",i,_,a,t),launched:_,cwd:w,...await y(w,g,r,a),profile:g===null?null:await a.profile(g,m)}}p(B,"readerForNode");async function ne(o,e,r=async()=>[],a=d(o)){const t=await o.prepare("SELECT envelope_json FROM peer_stubs WHERE object = ?").get(e.id);if(t!==void 0)return await J(o,e,t.envelope_json===null?null:JSON.parse(t.envelope_json),a);if(e.type==="node")return await B(o,e.id,r,a,!0);const n=e.owner===null?null:await a.object(e.owner);if(!n)throw new Error(`watcher ${e.id} has no owner`);const c=(n.type==="app"?n:n.type==="profile"&&n.owner!==null?await a.object(n.owner):null)??await o.prepare("SELECT o.* FROM identities i JOIN objects o ON o.id = i.object WHERE i.external_id = 'app:terminal' AND i.scheme = 'app'").get()??await o.prepare("SELECT * FROM objects WHERE type = 'person' AND name = 'user' AND home_machine IS NULL").get();if(!c)throw new Error("no terminal app or person object");const s=(await o.prepare("SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(c.id))?.external_id??"app:terminal",i=await o.prepare("SELECT resolved_scopes_json FROM grants WHERE grantee = ? AND status = 'active'").all(s),u=n.type==="person"?null:i.length===0?a.pluginScopes(s):i.length===1?JSON.parse(i[0].resolved_scopes_json).filter(w=>w.mode==="always").map(w=>S(w.scope)):null;return{class:"object",object:e,node:e,owner:n,app:c,scopes:u,repos:n.type==="repo"?[n]:[]}}p(ne,"readerForObject");async function re(o,e,r){const a=(await o.prepare("SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(e.id))?.external_id;if(a===void 0)return null;const t=await o.prepare("SELECT resolved_scopes_json FROM grants WHERE grantee = ? AND status = 'active'").all(a);return t.length===0?r.pluginScopes(a):t.length>1?null:JSON.parse(t[0].resolved_scopes_json).filter(n=>n.mode==="always").map(n=>S(n.scope))}p(re,"appScopesNow");async function J(o,e,r,a){const t=await j(o);if(r!==null&&r.app===null)return{class:"object",object:e,node:e,owner:t,app:t,scopes:null,repos:[]};const n=r?.app==null?null:await E(o,r.app),l=r?.app==null?null:await a.grantFor(r.app,r.sub),c=r===null||l===null?null:r.node_scopes===null?l.resolved:r.node_scopes.filter(i=>f(l.resolved,i)),s=n??(e.owner===null?t:await a.object(e.owner)??t);return{class:"object",object:e,node:e,owner:s,app:s,scopes:c,launched:r?.node_scopes??null,repos:[],...r?.place==null?{}:{place:r.place}}}p(J,"readerForStub");function d(o){return{grantById:R,grantFor:O,pluginScopes:p(e=>M(e.replace(/^app:/,""))?.scopes??null,"pluginScopes"),object:p(async e=>await H(o,e),"object"),profile:p(async(e,r)=>await G(o,e,r),"profile"),projectPaths:p(e=>b()?[]:$(A(),e)?.manifest.projects.map(r=>r.path)??[],"projectPaths")}}p(d,"liveLookups");function ae(o){const e=d(o),r=new Map,a=p((t,n)=>(r.has(t)||r.set(t,n()),r.get(t)),"memo");return{grantById:p(t=>a(`g\0${t}`,()=>e.grantById(t)),"grantById"),grantFor:p((t,n)=>a(`a\0${t}\0${n}`,()=>e.grantFor(t,n)),"grantFor"),pluginScopes:p(t=>a(`t\0${t}`,()=>e.pluginScopes(t)),"pluginScopes"),object:p(async t=>await a(`o\0${t}`,async()=>await e.object(t)),"object"),profile:p(async(t,n)=>await a(`p\0${t}\0${n.id}`,async()=>await e.profile(t,n)),"profile"),projectPaths:p(t=>a(`j\0${t}`,()=>e.projectPaths(t)),"projectPaths")}}p(ae,"publishLookups");async function F(o,e,r,a,t,n=!1){const l=r?await t.grantById(r):await t.grantFor(o,e),c=l&&l.grantee===o?l.resolved:null;if(c===null){if(n)return null;throw new Error(`the grant of ${o} is not active`)}return a===null?c:a.filter(s=>f(c,s))}p(F,"nodeScopes");async function j(o){return await D(o,P())}p(j,"personObject");async function E(o,e){return await v(o,"app","app",e,e.replace(/^app:/,""))}p(E,"appObject");async function G(o,e,r){return await o.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.id = i.object
3
+ WHERE i.scheme = 'profile' AND i.external_id = ?`).get(e)??await v(o,"profile","profile",e,T(e).manifest.name,r)}p(G,"profileObject");export{re as appScopesNow,d as liveLookups,G as profileObject,ae as publishLookups,te as readerFor,B as readerForNode,ne as readerForObject,y as viewRepos};
@@ -1,2 +1,2 @@
1
- var j=Object.defineProperty;var _=(t,i)=>j(t,"name",{value:i,configurable:!0});import{get as $,put as B}from"../bodies.js";import{setLinks as J,setUnresolved as P}from"../edges.js";import{collectUnlinked as U,deleteDocument as C,documentRecord as I,heads as x,importDocument as Y,insertImportedRevisions as z,moveDocument as b,restoreDocument as q,setDocumentRecord as F}from"../documents.js";import{CrtrError as G}from"../../errors.js";import{publish as D}from"../events.js";import{getById as k,setObjectKind as K}from"../objects.js";import{rulesOf as V}from"../rules.js";import{SPACES as Q}from"../spaces/index.js";import{docPath as X,mergeDoc as Z,parseDoc as g,serializeDoc as M}from"./ref-format.js";import{withCanvasWrite as A}from"../../canvas/db.js";import{mapAsync as H}from"../../async-array.js";function L(t){const i=Q[t.type];if(t.owner!==null||!i||i.sync!=="repo-ref")throw new Error(`${t.type} ${t.id} is not a space that syncs through a repo ref`)}_(L,"assertRepoRefSpace");function ee(t){const i=new Map;for(const[r,n]of t??[]){const m=g(r,n);m.ok&&i.set(m.doc.id,m.doc)}return i}_(ee,"parseMap");async function Oe(t,i,r,n=null){L(i);const m=ee(r),p=await t.prepare("SELECT * FROM objects WHERE type = 'document' AND owner = ? AND privacy = 'public' ORDER BY seq").all(i.id),l=t.prepare("SELECT rev_id, number, body_hash, author, rationale, created_at FROM revisions WHERE document = ? ORDER BY number, rev_id"),w=t.prepare("SELECT p.rev_id FROM revision_parents rp JOIN revisions p ON p.rev_id = rp.parent WHERE rp.child = ?"),a=t.prepare(`SELECT e.written_as, o.id FROM edges e JOIN objects o ON o.id = e.dst
2
- WHERE e.type = 'links-to' AND e.src = ? ORDER BY e.written_as, o.id`),s=new Map;for(const e of p){const d=await I(t,e),c=new Map((m.get(e.id)?.revisions??[]).map(o=>[o.rev_id,o])),h=new Set((await x(t,e)).map(o=>o.rev_id)),v=(await a.all(e.id)).map(o=>({written_as:o.written_as??"",dst:o.id})),E=await H(await l.all(e.id),async o=>c.get(o.rev_id)??{rev_id:o.rev_id,number:o.number,parents:(await w.all(o.rev_id)).map(R=>R.rev_id),body:$(o.body_hash),body_hash:o.body_hash,author:o.author,rationale:o.rationale,created_at:o.created_at,links:h.has(o.rev_id)?v:[]});if(E.length===0)continue;const y=(await V(t,e)).map(o=>({position:o.position,on:o.on,match:o.match??null,match_frontmatter:o.matchFrontmatter??null,if:o.if??null,deliver:o.deliver})),N={id:e.id,name:e.name,kind:e.kind??"document",privacy:"public",preview:e.preview,summary:d.summary,why_it_exists:d.why_it_exists,unlisted:d.unlisted,lint_ignore:d.lint_ignore,extensions:d.extensions,created_at:e.created_at,deleted_at:e.deleted_at,rules:y,revisions:E};s.set(X(e.id),M(N))}for(const e of te(r,n)){const d=r.get(e),c=s.get(e);if(c===void 0){s.set(e,d);continue}const h=g(e,d),v=g(e,c);if(!h.ok){s.set(e,d);continue}if(!v.ok)continue;const E=n?.has(e)?g(e,n.get(e)):null,y=Z(E?.ok?E.doc:null,v.doc,h.doc);y&&s.set(e,M(y))}return s}_(Oe,"exportRepo");function te(t,i){return[...t??[]].filter(([r,n])=>i?.get(r)!==n).map(([r])=>r)}_(te,"pendingPaths");function ne(t){return[...t].sort((i,r)=>i.position-r.position).map(i=>({on:i.on,deliver:i.deliver,...i.match!==null&&i.match!==void 0?{match:i.match}:{},...i.match_frontmatter!==null&&i.match_frontmatter!==void 0?{matchFrontmatter:i.match_frontmatter}:{},...i.if!==null&&i.if!==void 0?{if:i.if}:{}}))}_(ne,"toRuleSpecs");const ie=_((t,i)=>JSON.stringify(t??null)===JSON.stringify(i??null),"same");function W(t,i){const r={},n=_(m=>!t||!ie(t[m],i[m]),"differs");return n("preview")&&(r.preview=i.preview),n("summary")&&(r.summary=i.summary),n("why_it_exists")&&(r.whyItExists=i.why_it_exists),n("unlisted")&&(r.unlisted=i.unlisted),n("lint_ignore")&&(r.lintIgnore=i.lint_ignore),n("extensions")&&(r.extensions=i.extensions),n("rules")&&(r.rules=ne(i.rules)),r}_(W,"changedFields");async function Re(t,i,r,n,m){L(i);const p={created:0,revisions:0,deleted:0,needsMerge:[],refused:[]},l=[];for(const[a,s]of n){if(r.get(a)===s)continue;const e=g(a,s);if(!e.ok){p.refused.push({path:a,reason:e.reason});continue}const d=r.has(a)?g(a,r.get(a)):null;l.push({path:a,prior:d?.ok?d.doc:null,doc:e.doc})}for(const[a,s]of r){if(n.has(a))continue;const e=g(a,s);e.ok&&l.push({path:a,prior:e.doc,doc:null,priorId:e.doc.id})}for(const a of l)for(const s of a.doc?.revisions??[])B(s.body);const w=[];return await A(async()=>{let a=l;for(;;){const s=[];for(const e of a){const d={created:0,revisions:0,deleted:0,needsMerge:[],refused:[]};try{const c=[];await A(async()=>await ce(t,i,e.prior,e.doc,e.priorId??e.doc.id,d,m,c),{savepoint:!0}),w.push(...c),p.created+=d.created,p.revisions+=d.revisions,p.deleted+=d.deleted,p.needsMerge.push(...d.needsMerge)}catch(c){de(c)?s.push({w:e,error:c}):p.refused.push({path:e.path,reason:c.message})}}if(s.length===0)break;if(s.length===a.length){for(const e of s)p.refused.push({path:e.w.path,reason:e.error.message});break}a=s.map(e=>e.w)}await re(t,n,new Set(w),new Set(l.map(s=>s.path)))},{savepoint:!0}),p}_(Re,"importTree");async function re(t,i,r,n){if(r.size===0)return;const m=t.prepare("INSERT INTO edges (type, src, dst, written_as, created_at) VALUES ('links-to', ?, ?, ?, ?) ON CONFLICT DO NOTHING"),p=new Date().toISOString();for(const[l,w]of i){if(!n.has(l)&&!oe(w,r))continue;const a=g(l,w);if(!a.ok||a.doc.deleted_at)continue;const s=await k(t,a.doc.id);if(!s||s.type!=="document"||s.deleted_at)continue;const e=(await x(t,s))[0],d=a.doc.revisions.find(c=>c.rev_id===e?.rev_id)?.links??[];for(const c of d){const h=await k(t,c.dst);h&&h.id!==s.id&&await m.run(s.id,h.id,c.written_as,p)}}}_(re,"completeLinks");const ae=/"dst":\s*"((?:[^"\\]|\\.)*)"/g;function oe(t,i){for(const r of t.matchAll(ae))if(i.has(se(r[1])))return!0;return!1}_(oe,"namesArrived");function se(t){if(!t.includes("\\"))return t;try{return JSON.parse(`"${t}"`)}catch{return t}}_(se,"decodeJsonString");function de(t){return t instanceof G&&t.code==="conflict"&&t.details?.holder!==void 0}_(de,"isNameConflict");async function ce(t,i,r,n,m,p,l,w){const a=await k(t,m);if(a&&(a.type!=="document"||a.owner!==i.id))throw new Error(`id ${m} is held by ${a.type} ${a.name} outside ${i.name}`);if(!n){a&&!a.deleted_at&&(await C(t,null,a,l),p.deleted++);return}const s=t.prepare("SELECT count(*) AS n FROM revisions WHERE document = ?");let e,d=!1,c=!1,h=0,v=0;if(a){e=a,h=(await I(t,e)).revision,e.created_at!==n.created_at&&(await t.prepare("UPDATE objects SET created_at = ? WHERE id = ?").run(n.created_at,e.id),e=await k(t,e.id));const u=n.kind;(r?r.kind!==u:e.kind!==u)&&await K(t,e,u),c=!!e.deleted_at&&n.deleted_at===null&&(r===null||r.deleted_at!=null);let f=W(c?null:r,n);if(n.deleted_at&&e.deleted_at){const{rules:O,...S}=f;f=S}c&&(e=await q(t,e,n.name,l),w?.push(e.id)),Object.keys(f).length>0&&(e=await F(t,null,e,f)),n.name!==e.name&&!e.deleted_at&&(e=await b(t,null,e,{name:n.name},l))}else{const f=n.revisions.filter(O=>O.parents.length===0).sort((O,S)=>O.number-S.number||O.rev_id.localeCompare(S.rev_id))[0];if(!f)throw new Error(`${n.name}: no root revision`);e=await Y(t,{id:n.id,owner:i,name:n.name,kind:n.kind,preview:n.preview,privacy:"public",...W(null,n),revisions:[{rev_id:f.rev_id,body_hash:f.body_hash,author:null,rationale:f.rationale,created_at:f.created_at}]},l),e.created_at!==n.created_at&&(await t.prepare("UPDATE objects SET created_at = ? WHERE id = ?").run(n.created_at,e.id),e=await k(t,e.id)),d=!0,w?.push(e.id),v=1}const E=(await s.get(e.id)).n,{headCount:y}=await z(t,e,n.revisions.map(u=>({rev_id:u.rev_id,parents:u.parents,body_hash:u.body_hash,rationale:u.rationale,created_at:u.created_at})));v+=(await s.get(e.id)).n-E,p.revisions+=v;const o=(await x(t,e))[0],R=new Map(n.revisions.map(u=>[u.rev_id,u]));if(v>0||c){const u=(await H(R.get(o.rev_id)?.links??[],async f=>({written_as:f.written_as,dst:await k(t,f.dst)}))).filter(f=>f.dst!==null);await U(t,await J(t,e,u),l),await P(t,e,[])}if(y>1&&p.needsMerge.push(e.id),n.deleted_at&&!e.deleted_at){await C(t,null,e,l,n.deleted_at),p.deleted++;return}if(n.deleted_at&&e.deleted_at&&e.deleted_at!==n.deleted_at&&await t.prepare("UPDATE objects SET deleted_at = ? WHERE id = ?").run(n.deleted_at,e.id),d){p.created++,await D(t,{event:"created",object:e,actor:null,attrs:{revision:o.number},pointer:o.rev_id},l);return}if(c){p.created++,await D(t,{event:"created",object:e,actor:null,attrs:{revision:o.number},pointer:o.rev_id},l);return}if(v===0)return;const T=n.revisions.filter(u=>!r?.revisions.some(f=>f.rev_id===u.rev_id)).sort((u,f)=>f.number-u.number||f.created_at.localeCompare(u.created_at))[0]??R.get(o.rev_id);await D(t,{event:"edited",object:e,actor:null,pointer:o.rev_id,attrs:{revision:o.number,from_revision:h,rationale:T?.rationale??null,author:T?.author??null,heads:y,needs_merge:y>1}},l)}_(ce,"importOne");export{Oe as exportRepo,Re as importTree,oe as namesArrived,te as pendingPaths};
1
+ var j=Object.defineProperty;var _=(t,i)=>j(t,"name",{value:i,configurable:!0});import{get as $,put as B}from"../bodies.js";import{setLinks as J,setUnresolved as U}from"../edges.js";import{collectUnlinked as P,deleteDocument as I,documentRecord as M,heads as x,importDocument as Y,insertImportedRevisions as z,moveDocument as b,restoreDocument as q,setDocumentRecord as F}from"../documents.js";import{CrtrError as G}from"../../errors.js";import{publish as D}from"../events.js";import{getById as k,setObjectKind as K}from"../objects.js";import{rulesOf as V}from"../rules.js";import{docPath as Q,mergeDoc as X,parseDoc as g,serializeDoc as C}from"./ref-format.js";import{withCanvasWrite as A}from"../../canvas/db.js";import{mapAsync as H}from"../../async-array.js";function L(t){if(t.owner!==null||t.type!=="repo")throw new Error(`${t.type} ${t.id} is not a space that syncs through a repo ref`)}_(L,"assertRepoRefSpace");function Z(t){const i=new Map;for(const[r,n]of t??[]){const m=g(r,n);m.ok&&i.set(m.doc.id,m.doc)}return i}_(Z,"parseMap");async function Ee(t,i,r,n=null){L(i);const m=Z(r),p=await t.prepare("SELECT * FROM objects WHERE type = 'document' AND owner = ? AND privacy = 'public' ORDER BY seq").all(i.id),l=t.prepare("SELECT rev_id, number, body_hash, author, rationale, created_at FROM revisions WHERE document = ? ORDER BY number, rev_id"),w=t.prepare("SELECT p.rev_id FROM revision_parents rp JOIN revisions p ON p.rev_id = rp.parent WHERE rp.child = ?"),a=t.prepare(`SELECT e.written_as, o.id FROM edges e JOIN objects o ON o.id = e.dst
2
+ WHERE e.type = 'links-to' AND e.src = ? ORDER BY e.written_as, o.id`),s=new Map;for(const e of p){const d=await M(t,e),c=new Map((m.get(e.id)?.revisions??[]).map(o=>[o.rev_id,o])),h=new Set((await x(t,e)).map(o=>o.rev_id)),v=(await a.all(e.id)).map(o=>({written_as:o.written_as??"",dst:o.id})),E=await H(await l.all(e.id),async o=>c.get(o.rev_id)??{rev_id:o.rev_id,number:o.number,parents:(await w.all(o.rev_id)).map(R=>R.rev_id),body:$(o.body_hash),body_hash:o.body_hash,author:o.author,rationale:o.rationale,created_at:o.created_at,links:h.has(o.rev_id)?v:[]});if(E.length===0)continue;const y=(await V(t,e)).map(o=>({position:o.position,on:o.on,match:o.match??null,match_frontmatter:o.matchFrontmatter??null,if:o.if??null,deliver:o.deliver})),N={id:e.id,name:e.name,kind:e.kind??"document",privacy:"public",preview:e.preview,summary:d.summary,why_it_exists:d.why_it_exists,unlisted:d.unlisted,lint_ignore:d.lint_ignore,extensions:d.extensions,created_at:e.created_at,deleted_at:e.deleted_at,rules:y,revisions:E};s.set(Q(e.id),C(N))}for(const e of ee(r,n)){const d=r.get(e),c=s.get(e);if(c===void 0){s.set(e,d);continue}const h=g(e,d),v=g(e,c);if(!h.ok){s.set(e,d);continue}if(!v.ok)continue;const E=n?.has(e)?g(e,n.get(e)):null,y=X(E?.ok?E.doc:null,v.doc,h.doc);y&&s.set(e,C(y))}return s}_(Ee,"exportRepo");function ee(t,i){return[...t??[]].filter(([r,n])=>i?.get(r)!==n).map(([r])=>r)}_(ee,"pendingPaths");function te(t){return[...t].sort((i,r)=>i.position-r.position).map(i=>({on:i.on,deliver:i.deliver,...i.match!==null&&i.match!==void 0?{match:i.match}:{},...i.match_frontmatter!==null&&i.match_frontmatter!==void 0?{matchFrontmatter:i.match_frontmatter}:{},...i.if!==null&&i.if!==void 0?{if:i.if}:{}}))}_(te,"toRuleSpecs");const ne=_((t,i)=>JSON.stringify(t??null)===JSON.stringify(i??null),"same");function W(t,i){const r={},n=_(m=>!t||!ne(t[m],i[m]),"differs");return n("preview")&&(r.preview=i.preview),n("summary")&&(r.summary=i.summary),n("why_it_exists")&&(r.whyItExists=i.why_it_exists),n("unlisted")&&(r.unlisted=i.unlisted),n("lint_ignore")&&(r.lintIgnore=i.lint_ignore),n("extensions")&&(r.extensions=i.extensions),n("rules")&&(r.rules=te(i.rules)),r}_(W,"changedFields");async function ke(t,i,r,n,m){L(i);const p={created:0,revisions:0,deleted:0,needsMerge:[],refused:[]},l=[];for(const[a,s]of n){if(r.get(a)===s)continue;const e=g(a,s);if(!e.ok){p.refused.push({path:a,reason:e.reason});continue}const d=r.has(a)?g(a,r.get(a)):null;l.push({path:a,prior:d?.ok?d.doc:null,doc:e.doc})}for(const[a,s]of r){if(n.has(a))continue;const e=g(a,s);e.ok&&l.push({path:a,prior:e.doc,doc:null,priorId:e.doc.id})}for(const a of l)for(const s of a.doc?.revisions??[])B(s.body);const w=[];return await A(async()=>{let a=l;for(;;){const s=[];for(const e of a){const d={created:0,revisions:0,deleted:0,needsMerge:[],refused:[]};try{const c=[];await A(async()=>await de(t,i,e.prior,e.doc,e.priorId??e.doc.id,d,m,c),{savepoint:!0}),w.push(...c),p.created+=d.created,p.revisions+=d.revisions,p.deleted+=d.deleted,p.needsMerge.push(...d.needsMerge)}catch(c){se(c)?s.push({w:e,error:c}):p.refused.push({path:e.path,reason:c.message})}}if(s.length===0)break;if(s.length===a.length){for(const e of s)p.refused.push({path:e.w.path,reason:e.error.message});break}a=s.map(e=>e.w)}await ie(t,n,new Set(w),new Set(l.map(s=>s.path)))},{savepoint:!0}),p}_(ke,"importTree");async function ie(t,i,r,n){if(r.size===0)return;const m=t.prepare("INSERT INTO edges (type, src, dst, written_as, created_at) VALUES ('links-to', ?, ?, ?, ?) ON CONFLICT DO NOTHING"),p=new Date().toISOString();for(const[l,w]of i){if(!n.has(l)&&!ae(w,r))continue;const a=g(l,w);if(!a.ok||a.doc.deleted_at)continue;const s=await k(t,a.doc.id);if(!s||s.type!=="document"||s.deleted_at)continue;const e=(await x(t,s))[0],d=a.doc.revisions.find(c=>c.rev_id===e?.rev_id)?.links??[];for(const c of d){const h=await k(t,c.dst);h&&h.id!==s.id&&await m.run(s.id,h.id,c.written_as,p)}}}_(ie,"completeLinks");const re=/"dst":\s*"((?:[^"\\]|\\.)*)"/g;function ae(t,i){for(const r of t.matchAll(re))if(i.has(oe(r[1])))return!0;return!1}_(ae,"namesArrived");function oe(t){if(!t.includes("\\"))return t;try{return JSON.parse(`"${t}"`)}catch{return t}}_(oe,"decodeJsonString");function se(t){return t instanceof G&&t.code==="conflict"&&t.details?.holder!==void 0}_(se,"isNameConflict");async function de(t,i,r,n,m,p,l,w){const a=await k(t,m);if(a&&(a.type!=="document"||a.owner!==i.id))throw new Error(`id ${m} is held by ${a.type} ${a.name} outside ${i.name}`);if(!n){a&&!a.deleted_at&&(await I(t,null,a,l),p.deleted++);return}const s=t.prepare("SELECT count(*) AS n FROM revisions WHERE document = ?");let e,d=!1,c=!1,h=0,v=0;if(a){e=a,h=(await M(t,e)).revision,e.created_at!==n.created_at&&(await t.prepare("UPDATE objects SET created_at = ? WHERE id = ?").run(n.created_at,e.id),e=await k(t,e.id));const u=n.kind;(r?r.kind!==u:e.kind!==u)&&await K(t,e,u),c=!!e.deleted_at&&n.deleted_at===null&&(r===null||r.deleted_at!=null);let f=W(c?null:r,n);if(n.deleted_at&&e.deleted_at){const{rules:O,...S}=f;f=S}c&&(e=await q(t,e,n.name,l),w?.push(e.id)),Object.keys(f).length>0&&(e=await F(t,null,e,f)),n.name!==e.name&&!e.deleted_at&&(e=await b(t,null,e,{name:n.name},l))}else{const f=n.revisions.filter(O=>O.parents.length===0).sort((O,S)=>O.number-S.number||O.rev_id.localeCompare(S.rev_id))[0];if(!f)throw new Error(`${n.name}: no root revision`);e=await Y(t,{id:n.id,owner:i,name:n.name,kind:n.kind,preview:n.preview,privacy:"public",...W(null,n),revisions:[{rev_id:f.rev_id,body_hash:f.body_hash,author:null,rationale:f.rationale,created_at:f.created_at}]},l),e.created_at!==n.created_at&&(await t.prepare("UPDATE objects SET created_at = ? WHERE id = ?").run(n.created_at,e.id),e=await k(t,e.id)),d=!0,w?.push(e.id),v=1}const E=(await s.get(e.id)).n,{headCount:y}=await z(t,e,n.revisions.map(u=>({rev_id:u.rev_id,parents:u.parents,body_hash:u.body_hash,rationale:u.rationale,created_at:u.created_at})));v+=(await s.get(e.id)).n-E,p.revisions+=v;const o=(await x(t,e))[0],R=new Map(n.revisions.map(u=>[u.rev_id,u]));if(v>0||c){const u=(await H(R.get(o.rev_id)?.links??[],async f=>({written_as:f.written_as,dst:await k(t,f.dst)}))).filter(f=>f.dst!==null);await P(t,await J(t,e,u),l),await U(t,e,[])}if(y>1&&p.needsMerge.push(e.id),n.deleted_at&&!e.deleted_at){await I(t,null,e,l,n.deleted_at),p.deleted++;return}if(n.deleted_at&&e.deleted_at&&e.deleted_at!==n.deleted_at&&await t.prepare("UPDATE objects SET deleted_at = ? WHERE id = ?").run(n.deleted_at,e.id),d){p.created++,await D(t,{event:"created",object:e,actor:null,attrs:{revision:o.number},pointer:o.rev_id},l);return}if(c){p.created++,await D(t,{event:"created",object:e,actor:null,attrs:{revision:o.number},pointer:o.rev_id},l);return}if(v===0)return;const T=n.revisions.filter(u=>!r?.revisions.some(f=>f.rev_id===u.rev_id)).sort((u,f)=>f.number-u.number||f.created_at.localeCompare(u.created_at))[0]??R.get(o.rev_id);await D(t,{event:"edited",object:e,actor:null,pointer:o.rev_id,attrs:{revision:o.number,from_revision:h,rationale:T?.rationale??null,author:T?.author??null,heads:y,needs_merge:y>1}},l)}_(de,"importOne");export{Ee as exportRepo,ke as importTree,ae as namesArrived,ee as pendingPaths};
@@ -1,8 +1,7 @@
1
1
  /** A document's delivery rules as `rules` rows. `rules.owner` mirrors the
2
- * document's `objects.owner` so delivery can select rules by owner through
3
- * `rules_on_owner`; `replaceRules` and `setRulesOwner` are the only writers. */
2
+ * document's `objects.owner` so a rule is found by its document's owner; `replaceRules` and `setRulesOwner` are the only writers. */
4
3
  import type { ObjectRow, RuleOn, RuleSpec } from './types.js';
5
- import type { CanvasDb } from '../canvas/db.js';
4
+ import type { CanvasDb, CanvasValue } from '../canvas/db.js';
6
5
  export declare const RULE_ONS: readonly RuleOn[];
7
6
  export interface RuleRow extends RuleSpec {
8
7
  object: string;
@@ -14,8 +13,11 @@ export declare function replaceRules(db: CanvasDb, object: ObjectRow, specs: rea
14
13
  /** Re-point the document's rule rows at its current owner (after a move). */
15
14
  export declare function setRulesOwner(db: CanvasDb, object: ObjectRow): Promise<void>;
16
15
  export declare function rulesOf(db: CanvasDb, object: ObjectRow): Promise<RuleRow[]>;
17
- /** Every rule on one of `on` whose document belongs to one of `owners`. */
18
- export declare function rulesForOwners(db: CanvasDb, on: readonly RuleOn[], owners: readonly string[]): Promise<RuleRow[]>;
16
+ /** Every rule on one of `on` whose document satisfies `permitted`: SQL over the document's `objects` row aliased `o` (the evaluator's `permits`). */
17
+ export declare function rulesOn(db: CanvasDb, on: readonly RuleOn[], permitted: {
18
+ sql: string;
19
+ params: CanvasValue[];
20
+ }): Promise<RuleRow[]>;
19
21
  /** The only converter from package/legacy frontmatter (`surfaces`, `gate`,
20
22
  * `slash`) to rules. Tolerant like the runtime parser it replaces: invalid
21
23
  * entries are dropped; `lintRules` is the strict check. A document-level
@@ -1,3 +1,3 @@
1
- var g=Object.defineProperty;var d=(e,n)=>g(e,"name",{value:n,configurable:!0});import{forEachAsync as R}from"../async-array.js";const h=["boot","workspace-open","file-read","document-read","command","pre-command","slash-command"],m=["name","preview","content"],y=["file-read","document-read","command","pre-command"],E=["on","match","matchFrontmatter","if","deliver"];function O(e){return{object:e.object,owner:e.owner,position:e.position,on:e.on_event,deliver:e.deliver,...e.match_json!==null?{match:JSON.parse(e.match_json)}:{},...e.match_frontmatter_json!==null?{matchFrontmatter:JSON.parse(e.match_frontmatter_json)}:{},...e.if_json!==null?{if:JSON.parse(e.if_json)}:{}}}d(O,"fromRaw");async function _(e,n,t){const i=w(t);if(i!==null)throw new Error(`replaceRules(${n.id}): ${i}`);await e.prepare("DELETE FROM rules WHERE object = ?").run(n.id);const c=e.prepare(`INSERT INTO rules (object, owner, position, on_event, match_json, match_frontmatter_json, if_json, deliver)
2
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);await R(t,async(r,o)=>{await c.run(n.id,n.owner,o,r.on,r.match!==void 0?JSON.stringify(r.match):null,r.matchFrontmatter!==void 0?JSON.stringify(r.matchFrontmatter):null,r.if!==void 0?JSON.stringify(r.if):null,r.deliver)})}d(_,"replaceRules");async function v(e,n){const t=await e.prepare("SELECT owner FROM objects WHERE id = ?").get(n.id);if(t===void 0)throw new Error(`setRulesOwner: object ${n.id} does not exist`);await e.prepare("UPDATE rules SET owner = ? WHERE object = ?").run(t.owner,n.id)}d(v,"setRulesOwner");async function $(e,n){return(await e.prepare("SELECT * FROM rules WHERE object = ? ORDER BY position").all(n.id)).map(O)}d($,"rulesOf");async function A(e,n,t){if(n.length===0||t.length===0)return[];const i=`SELECT * FROM rules WHERE on_event IN (${n.map(()=>"?").join(",")})
3
- AND owner IN (${t.map(()=>"?").join(",")}) ORDER BY object, position`;return(await e.prepareUncached(i).all(...n,...t)).map(O)}d(A,"rulesForOwners");const j={boot:"boot","workspace-open":"workspace-open",read:"file-read","memory-read":"document-read",command:"command","pre-command":"pre-command"};function s(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)?e:void 0}d(s,"predicate");function S(e){if(typeof e=="string")return e.trim()===""?void 0:[e.trim()];if(!Array.isArray(e))return;const n=e.filter(t=>typeof t=="string"&&t.trim()!=="").map(t=>t.trim());return n.length>0?n:void 0}d(S,"globs");function b(e){const n=s(e.gate),t=d(r=>n!==void 0&&r!==void 0?{all:[n,r]}:n??r,"withGate"),i=[],c=Array.isArray(e.surfaces)?e.surfaces:[];for(const r of c){const o=s(r);if(o===void 0)continue;const a=typeof o.on=="string"?j[o.on]:void 0,u=o.at;if(a===void 0||typeof u!="string"||!m.includes(u))continue;const f=S(o.match),l=a==="file-read"?s(o["match-frontmatter"]):void 0;if(y.includes(a)&&f===void 0&&l===void 0)continue;const p=t(s(o.gate));i.push({on:a,deliver:u,...f!==void 0&&a!=="boot"&&a!=="workspace-open"?{match:f}:{},...l!==void 0?{matchFrontmatter:l}:{},...p!==void 0?{if:p}:{}})}return e.slash===!0&&i.push({on:"slash-command",deliver:"content",...n!==void 0?{if:n}:{}}),i}d(b,"rulesFromFrontmatter");function w(e){if(!Array.isArray(e))return`invalid rules: ${JSON.stringify(e)} (expected a list of {on, deliver, match?, matchFrontmatter?, if?} entries)`;for(const n of e){const t=s(n);if(t===void 0)return`invalid rule: ${JSON.stringify(n)} (expected an {on, deliver, match?, matchFrontmatter?, if?} object)`;for(const a of Object.keys(t))if(!E.includes(a))return`invalid rule: unknown key \`${a}\` (a rule carries only ${E.join(", ")})`;const i=t.on;if(typeof i!="string"||!h.includes(i))return`invalid rule \`on\`: ${JSON.stringify(i)} (expected ${h.join("|")})`;const c=t.deliver;if(typeof c!="string"||!m.includes(c))return`invalid rule \`deliver\`: ${JSON.stringify(c)} (expected ${m.join("|")})`;const r=t.match;if(r!==void 0){if(i==="boot"||i==="workspace-open"||i==="slash-command")return`invalid rule: \`match\` is meaningless on \`${i}\`; drop it`;if(!Array.isArray(r)||r.length===0||r.some(a=>typeof a!="string"||a.trim()===""))return`invalid rule \`match\`: ${JSON.stringify(r)} (expected a non-empty list of non-empty globs)`}const o=t.matchFrontmatter;if(o!==void 0){if(i!=="file-read")return"invalid rule: `matchFrontmatter` applies to `file-read` rules only";if(s(o)===void 0)return`invalid rule \`matchFrontmatter\`: ${JSON.stringify(o)} (expected a field\u2192matcher object)`}if(y.includes(i)&&r===void 0&&o===void 0)return`invalid rule: a \`${i}\` rule requires \`match\`${i==="file-read"?" or `matchFrontmatter`":""}`;if(t.if!==void 0&&s(t.if)===void 0)return`invalid rule \`if\`: ${JSON.stringify(t.if)} (expected a predicate object)`}return null}d(w,"lintRules");export{h as RULE_ONS,w as lintRules,_ as replaceRules,A as rulesForOwners,b as rulesFromFrontmatter,$ as rulesOf,v as setRulesOwner};
1
+ var N=Object.defineProperty;var d=(e,n)=>N(e,"name",{value:n,configurable:!0});import{forEachAsync as R}from"../async-array.js";const h=["boot","workspace-open","file-read","document-read","command","pre-command","slash-command"],m=["name","preview","content"],y=["file-read","document-read","command","pre-command"],E=["on","match","matchFrontmatter","if","deliver"];function O(e){return{object:e.object,owner:e.owner,position:e.position,on:e.on_event,deliver:e.deliver,...e.match_json!==null?{match:JSON.parse(e.match_json)}:{},...e.match_frontmatter_json!==null?{matchFrontmatter:JSON.parse(e.match_frontmatter_json)}:{},...e.if_json!==null?{if:JSON.parse(e.if_json)}:{}}}d(O,"fromRaw");async function v(e,n,t){const i=S(t);if(i!==null)throw new Error(`replaceRules(${n.id}): ${i}`);await e.prepare("DELETE FROM rules WHERE object = ?").run(n.id);const c=e.prepare(`INSERT INTO rules (object, owner, position, on_event, match_json, match_frontmatter_json, if_json, deliver)
2
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);await R(t,async(r,o)=>{await c.run(n.id,n.owner,o,r.on,r.match!==void 0?JSON.stringify(r.match):null,r.matchFrontmatter!==void 0?JSON.stringify(r.matchFrontmatter):null,r.if!==void 0?JSON.stringify(r.if):null,r.deliver)})}d(v,"replaceRules");async function F(e,n){const t=await e.prepare("SELECT owner FROM objects WHERE id = ?").get(n.id);if(t===void 0)throw new Error(`setRulesOwner: object ${n.id} does not exist`);await e.prepare("UPDATE rules SET owner = ? WHERE object = ?").run(t.owner,n.id)}d(F,"setRulesOwner");async function $(e,n){return(await e.prepare("SELECT * FROM rules WHERE object = ? ORDER BY position").all(n.id)).map(O)}d($,"rulesOf");async function b(e,n,t){if(n.length===0)return[];const i=`SELECT r.* FROM rules r JOIN objects o ON o.id = r.object WHERE r.on_event IN (${n.map(()=>"?").join(",")})
3
+ AND o.deleted_at IS NULL AND ${t.sql} ORDER BY r.object, r.position`;return(await e.prepareUncached(i).all(...n,...t.params)).map(O)}d(b,"rulesOn");const g={boot:"boot","workspace-open":"workspace-open",read:"file-read","memory-read":"document-read",command:"command","pre-command":"pre-command"};function s(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)?e:void 0}d(s,"predicate");function j(e){if(typeof e=="string")return e.trim()===""?void 0:[e.trim()];if(!Array.isArray(e))return;const n=e.filter(t=>typeof t=="string"&&t.trim()!=="").map(t=>t.trim());return n.length>0?n:void 0}d(j,"globs");function A(e){const n=s(e.gate),t=d(r=>n!==void 0&&r!==void 0?{all:[n,r]}:n??r,"withGate"),i=[],c=Array.isArray(e.surfaces)?e.surfaces:[];for(const r of c){const o=s(r);if(o===void 0)continue;const a=typeof o.on=="string"?g[o.on]:void 0,u=o.at;if(a===void 0||typeof u!="string"||!m.includes(u))continue;const f=j(o.match),l=a==="file-read"?s(o["match-frontmatter"]):void 0;if(y.includes(a)&&f===void 0&&l===void 0)continue;const p=t(s(o.gate));i.push({on:a,deliver:u,...f!==void 0&&a!=="boot"&&a!=="workspace-open"?{match:f}:{},...l!==void 0?{matchFrontmatter:l}:{},...p!==void 0?{if:p}:{}})}return e.slash===!0&&i.push({on:"slash-command",deliver:"content",...n!==void 0?{if:n}:{}}),i}d(A,"rulesFromFrontmatter");function S(e){if(!Array.isArray(e))return`invalid rules: ${JSON.stringify(e)} (expected a list of {on, deliver, match?, matchFrontmatter?, if?} entries)`;for(const n of e){const t=s(n);if(t===void 0)return`invalid rule: ${JSON.stringify(n)} (expected an {on, deliver, match?, matchFrontmatter?, if?} object)`;for(const a of Object.keys(t))if(!E.includes(a))return`invalid rule: unknown key \`${a}\` (a rule carries only ${E.join(", ")})`;const i=t.on;if(typeof i!="string"||!h.includes(i))return`invalid rule \`on\`: ${JSON.stringify(i)} (expected ${h.join("|")})`;const c=t.deliver;if(typeof c!="string"||!m.includes(c))return`invalid rule \`deliver\`: ${JSON.stringify(c)} (expected ${m.join("|")})`;const r=t.match;if(r!==void 0){if(i==="boot"||i==="workspace-open"||i==="slash-command")return`invalid rule: \`match\` is meaningless on \`${i}\`; drop it`;if(!Array.isArray(r)||r.length===0||r.some(a=>typeof a!="string"||a.trim()===""))return`invalid rule \`match\`: ${JSON.stringify(r)} (expected a non-empty list of non-empty globs)`}const o=t.matchFrontmatter;if(o!==void 0){if(i!=="file-read")return"invalid rule: `matchFrontmatter` applies to `file-read` rules only";if(s(o)===void 0)return`invalid rule \`matchFrontmatter\`: ${JSON.stringify(o)} (expected a field\u2192matcher object)`}if(y.includes(i)&&r===void 0&&o===void 0)return`invalid rule: a \`${i}\` rule requires \`match\`${i==="file-read"?" or `matchFrontmatter`":""}`;if(t.if!==void 0&&s(t.if)===void 0)return`invalid rule \`if\`: ${JSON.stringify(t.if)} (expected a predicate object)`}return null}d(S,"lintRules");export{h as RULE_ONS,S as lintRules,v as replaceRules,A as rulesFromFrontmatter,$ as rulesOf,b as rulesOn,F as setRulesOwner};
@@ -1,5 +1,8 @@
1
1
  /** Shared types of the canvas graph: documents, nodes, jobs and their owners as rows of `objects`. */
2
- export type Privacy = 'public' | 'friends' | 'private';
2
+ export type Privacy = 'public' | 'private';
3
+ /** `friends` was a privacy level until cycle 3. Data from an old source (a peer on an older release, a plugin package, a
4
+ * legacy canvas or frontmatter) that still says `friends` reads as `private`; a `/v1` write or CLI flag naming it stays refused. */
5
+ export declare const readLegacyPrivacy: (value: unknown) => unknown;
3
6
  export interface ObjectRow {
4
7
  id: string;
5
8
  /** Insertion order: the identity column the object list pages by. */
@@ -15,6 +18,8 @@ export interface ObjectRow {
15
18
  deleted_at: string | null;
16
19
  /** Set on a stub (a remote object's local representative): the machine (`runtime:<id>`) that owns the real object. */
17
20
  home_machine?: string | null;
21
+ /** A document's permission schema type (`firstlight/memory`); null for an ordinary object, evaluated as `crtr/object`. */
22
+ schema_type?: string | null;
18
23
  }
19
24
  export type Level = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
20
25
  export type ListenDelivery = 'interrupt' | 'steer' | 'after-turn' | 'next-turn';
@@ -59,6 +64,8 @@ export type Reader = {
59
64
  class: 'app';
60
65
  app: ObjectRow;
61
66
  scopes: readonly string[];
67
+ /** The person this call acts for: the `sub` of the member's call token. Absent for a runtime-owned call (cron, forwarded). */
68
+ sub?: string;
62
69
  /** The launched `scopes` list exactly as given (null/absent = none was given). Unlike `scopes` it is not filtered through the grant, so it keeps the own-app memory strings a grant never holds; it alone narrows access to the app's own documents. */
63
70
  launched?: readonly string[] | null;
64
71
  /** A cron command's run place (its origin run, and whether that run is isolated): it reads its own app's nodes under run confinement, as a node of that run does. Absent for the app's server. */
@@ -115,6 +122,10 @@ export interface Revision {
115
122
  rationale: string | null;
116
123
  verbatim: boolean;
117
124
  created_at: string;
125
+ /** The app principal (`app:<x>`) that made the revision; null for a person's own revision and for imported ones. */
126
+ acting_app: string | null;
127
+ /** The person (`user:<id>`) the revision was made for; null for imported ones. */
128
+ acting_for: string | null;
118
129
  /** parent rev_ids */
119
130
  parents: string[];
120
131
  }
@@ -1 +1 @@
1
- const e=[{threshold:10,delivery:"interrupt"},{threshold:7,delivery:"steer"},{threshold:5,delivery:"after-turn"},{threshold:3,delivery:"next-turn"}];export{e as DEFAULT_LISTEN};
1
+ var d=Object.defineProperty;var r=(e,t)=>d(e,"name",{value:t,configurable:!0});const i=r(e=>e==="friends"?"private":e,"readLegacyPrivacy"),o=[{threshold:10,delivery:"interrupt"},{threshold:7,delivery:"steer"},{threshold:5,delivery:"after-turn"},{threshold:3,delivery:"next-turn"}];export{o as DEFAULT_LISTEN,i as readLegacyPrivacy};
@@ -28,7 +28,7 @@ export interface RecordWatchOpts {
28
28
  /** message subscriptions only: 'final' (the default) or 'result:<request message_id>' */
29
29
  endsOn?: string;
30
30
  readHeadsKey?: string;
31
- /** The app that made this watch through `link`: `publish` then delivers the watched object's events past the watcher's read gate (see `linkAuthorizes`). */
31
+ /** The app that made this watch through `link`: `publish` then delivers the watched object's events past the watcher's read gate (see `linkDelivers` in events.ts). */
32
32
  linkApp?: ObjectRow;
33
33
  }
34
34
  export declare function configToJson(config: ListenConfig): string;
@@ -1,14 +1,15 @@
1
- var S=Object.defineProperty;var i=(t,e)=>S(t,"name",{value:e,configurable:!0});import{canRead as O}from"./access.js";import{receiverForObject as j}from"./receivers.js";import{getById as y}from"./objects.js";import{randomUUID as h}from"node:crypto";import{filterAsync as D,mapAsync as m}from"../async-array.js";const f=["spawn","job-start","ask"],A=["report.final","report.result","notice.>"];function N(t){return JSON.stringify(Object.fromEntries(t.map(e=>[String(e.threshold),e.delivery])))}i(N,"configToJson");function b(t){const e=JSON.parse(t);return Object.entries(e).map(([n,r])=>({threshold:Number(n),delivery:r})).sort((n,r)=>r.threshold-n.threshold)}i(b,"configFromJson");async function _(t,e,n){return await t.prepare("SELECT * FROM watches WHERE watcher = ? AND object = ?").get(e.id,n.id)}i(_,"watchRow");async function E(t,e){return await t.prepare("SELECT * FROM watch_subscriptions WHERE watch = ? ORDER BY seq").all(e)}i(E,"subsOf");async function u(t,e){const n=await E(t,e);if(n.length===0){await t.prepare("DELETE FROM watches WHERE watch_id = ?").run(e);return}const r=n.some(a=>f.includes(a.started_by))?1:0;await t.prepare("UPDATE watches SET in_tree = ? WHERE watch_id = ? AND in_tree != ?").run(r,e,r)}i(u,"settle");function g(t,e){return t==="message"?A:e.patterns?.length?e.patterns:[">"]}i(g,"defaultPatterns");async function x(t,e,n,r,a={}){if(r==="fork")throw new Error("recordWatch: fork watches are made by copyWatchesForFork");if(e.type.includes(".")&&!await j(t,e))throw new Error(`custom object ${e.id} has no receiver`);const s=new Date().toISOString();await t.prepare(`INSERT INTO watches (watch_id, watcher, object, in_tree, read_heads_key, created_at, link_app) VALUES (?, ?, ?, 0, ?, ?, ?)
1
+ var S=Object.defineProperty;var i=(t,e)=>S(t,"name",{value:e,configurable:!0});import{permits as R,resolveReader as j}from"../permissions/evaluator.js";import{receiverForObject as b}from"./receivers.js";import{getById as N}from"./objects.js";import{randomUUID as h}from"node:crypto";import{mapAsync as m}from"../async-array.js";const f=["spawn","job-start","ask"],D=["report.final","report.result","notice.>"];function y(t){return JSON.stringify(Object.fromEntries(t.map(e=>[String(e.threshold),e.delivery])))}i(y,"configToJson");function A(t){const e=JSON.parse(t);return Object.entries(e).map(([a,r])=>({threshold:Number(a),delivery:r})).sort((a,r)=>r.threshold-a.threshold)}i(A,"configFromJson");async function u(t,e,a){return await t.prepare("SELECT * FROM watches WHERE watcher = ? AND object = ?").get(e.id,a.id)}i(u,"watchRow");async function E(t,e){return await t.prepare("SELECT * FROM watch_subscriptions WHERE watch = ? ORDER BY seq").all(e)}i(E,"subsOf");async function l(t,e){const a=await E(t,e);if(a.length===0){await t.prepare("DELETE FROM watches WHERE watch_id = ?").run(e);return}const r=a.some(n=>f.includes(n.started_by))?1:0;await t.prepare("UPDATE watches SET in_tree = ? WHERE watch_id = ? AND in_tree != ?").run(r,e,r)}i(l,"settle");function g(t,e){return t==="message"?D:e.patterns?.length?e.patterns:[">"]}i(g,"defaultPatterns");async function x(t,e,a,r,n={}){if(r==="fork")throw new Error("recordWatch: fork watches are made by copyWatchesForFork");if(e.type.includes(".")&&!await b(t,e))throw new Error(`custom object ${e.id} has no receiver`);const o=new Date().toISOString();await t.prepare(`INSERT INTO watches (watch_id, watcher, object, in_tree, read_heads_key, created_at, link_app) VALUES (?, ?, ?, 0, ?, ?, ?)
2
2
  ON CONFLICT (watcher, object) DO UPDATE SET read_heads_key = COALESCE(excluded.read_heads_key, watches.read_heads_key),
3
- link_app = COALESCE(excluded.link_app, watches.link_app)`).run(h(),e.id,n.id,a.readHeadsKey??null,s,a.linkApp?.id??null);const c=await _(t,e,n),w=r==="message"?a.endsOn??"final":null,l=f.includes(r),o=await E(t,c.watch_id),d=t.prepare(`INSERT INTO watch_subscriptions (subscription_id, watch, pattern, filter_json, config_json, started_by, ends_on, created_at)
4
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);for(const R of g(r,a))o.some(p=>p.pattern===R&&(p.ends_on===null||p.ends_on===w)&&(!l||f.includes(p.started_by)))||await d.run(h(),c.watch_id,R,a.filter===void 0?null:JSON.stringify(a.filter),a.config?N(a.config):null,r,w,s);await u(t,c.watch_id)}i(x,"recordWatch");async function U(t,e,n,r){await t.prepare("UPDATE watches SET link_app = ? WHERE watcher = ? AND object = ?").run(r.id,e.id,n.id)}i(U,"markLinkWatch");async function J(t,e,n,r){const a=await _(t,e,n);a&&(r===void 0?await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ?").run(a.watch_id):typeof r=="string"?await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ? AND pattern = ?").run(a.watch_id,r):await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ? AND started_by = ?").run(a.watch_id,r.startedBy),await u(t,a.watch_id))}i(J,"unwatch");async function v(t,e,n,r,a){const s=await _(t,e,n),c=s?(await t.prepare("UPDATE watch_subscriptions SET config_json = ? WHERE watch = ? AND pattern = ?").run(a?N(a):null,s.watch_id,r)).changes:0;if(Number(c)===0)throw new Error(`${e.id} has no subscription ${r} on ${n.id}`)}i(v,"setSubscriptionConfig");async function B(t,e,n){const r=typeof n=="string"?await t.prepare(`DELETE FROM watch_subscriptions WHERE started_by = 'message'
3
+ link_app = COALESCE(excluded.link_app, watches.link_app)`).run(h(),e.id,a.id,n.readHeadsKey??null,o,n.linkApp?.id??null);const c=await u(t,e,a),s=r==="message"?n.endsOn??"final":null,d=f.includes(r),w=await E(t,c.watch_id),p=t.prepare(`INSERT INTO watch_subscriptions (subscription_id, watch, pattern, filter_json, config_json, started_by, ends_on, created_at)
4
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);for(const O of g(r,n))w.some(_=>_.pattern===O&&(_.ends_on===null||_.ends_on===s)&&(!d||f.includes(_.started_by)))||await p.run(h(),c.watch_id,O,n.filter===void 0?null:JSON.stringify(n.filter),n.config?y(n.config):null,r,s,o);await l(t,c.watch_id)}i(x,"recordWatch");async function U(t,e,a,r){await t.prepare("UPDATE watches SET link_app = ? WHERE watcher = ? AND object = ?").run(r.id,e.id,a.id)}i(U,"markLinkWatch");async function J(t,e,a,r){const n=await u(t,e,a);n&&(r===void 0?await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ?").run(n.watch_id):typeof r=="string"?await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ? AND pattern = ?").run(n.watch_id,r):await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ? AND started_by = ?").run(n.watch_id,r.startedBy),await l(t,n.watch_id))}i(J,"unwatch");async function v(t,e,a,r,n){const o=await u(t,e,a),c=o?(await t.prepare("UPDATE watch_subscriptions SET config_json = ? WHERE watch = ? AND pattern = ?").run(n?y(n):null,o.watch_id,r)).changes:0;if(Number(c)===0)throw new Error(`${e.id} has no subscription ${r} on ${a.id}`)}i(v,"setSubscriptionConfig");async function $(t,e,a){const r=typeof a=="string"?await t.prepare(`DELETE FROM watch_subscriptions WHERE started_by = 'message'
5
5
  AND watch IN (SELECT watch_id FROM watches WHERE object = ?) RETURNING watch`).all(e.id):await t.prepare(`DELETE FROM watch_subscriptions WHERE started_by = 'message' AND ends_on = ?
6
- AND watch IN (SELECT watch_id FROM watches WHERE object = ?) RETURNING watch`).all(`result:${n.result}`,e.id);for(const a of new Set(r.map(s=>s.watch)))await u(t,a)}i(B,"endMessageSubscriptions");async function I(t,e){const n=await y(t,e.watcher),r=await y(t,e.object);return!n||!r?null:{watch_id:e.watch_id,watcher:n,object:r,in_tree:e.in_tree===1,read_heads_key:e.read_heads_key,created_at:e.created_at,subscriptions:(await E(t,e.watch_id)).map(a=>({subscription_id:a.subscription_id,pattern:a.pattern,started_by:a.started_by,ends_on:a.ends_on,created_at:a.created_at,filter:a.filter_json===null?null:JSON.parse(a.filter_json),config:a.config_json===null?null:b(a.config_json)}))}}i(I,"hydrate");async function T(t,e,n){const r=(await m(n,async a=>await I(t,a))).filter(a=>a!==null);return await D(r,async a=>await O(t,e,a.watcher)&&await O(t,e,a.object))}i(T,"visible");async function P(t,e,n){return await T(t,e,await t.prepare("SELECT * FROM watches WHERE watcher = ? ORDER BY seq").all(n.id))}i(P,"watchesOf");async function q(t,e,n){return await T(t,e,await t.prepare("SELECT * FROM watches WHERE object = ? ORDER BY seq").all(n.id))}i(q,"watchersOf");async function V(t,e){return(await t.prepare("SELECT o.id FROM watches w JOIN objects o ON o.id = w.watcher WHERE w.object = ? ORDER BY w.seq").all(e.id)).map(n=>n.id)}i(V,"watcherIdsOf");async function Y(t,e){return await t.prepare(`WITH RECURSIVE walk(id) AS (
6
+ AND watch IN (SELECT watch_id FROM watches WHERE object = ?) RETURNING watch`).all(`result:${a.result}`,e.id);for(const n of new Set(r.map(o=>o.watch)))await l(t,n)}i($,"endMessageSubscriptions");async function I(t,e){const a=await N(t,e.watcher),r=await N(t,e.object);return!a||!r?null:{watch_id:e.watch_id,watcher:a,object:r,in_tree:e.in_tree===1,read_heads_key:e.read_heads_key,created_at:e.created_at,subscriptions:(await E(t,e.watch_id)).map(n=>({subscription_id:n.subscription_id,pattern:n.pattern,started_by:n.started_by,ends_on:n.ends_on,created_at:n.created_at,filter:n.filter_json===null?null:JSON.parse(n.filter_json),config:n.config_json===null?null:A(n.config_json)}))}}i(I,"hydrate");async function T(t,e,a,r){const n=await j(t,e),o=await R(t,n,"read","wr"),c=await R(t,n,"read","ob"),s=await t.prepareUncached(`SELECT w.* FROM watches w JOIN objects wr ON wr.id = w.watcher JOIN objects ob ON ob.id = w.object
7
+ WHERE w.${a} = ? AND ${o.sql} AND ${c.sql} ORDER BY w.seq`).all(r,...o.params,...c.params);return(await m(s,async d=>await I(t,d))).filter(d=>d!==null)}i(T,"visible");async function q(t,e,a){return await T(t,e,"watcher",a.id)}i(q,"watchesOf");async function B(t,e,a){return await T(t,e,"object",a.id)}i(B,"watchersOf");async function P(t,e){return(await t.prepare("SELECT o.id FROM watches w JOIN objects o ON o.id = w.watcher WHERE w.object = ? ORDER BY w.seq").all(e.id)).map(a=>a.id)}i(P,"watcherIdsOf");async function V(t,e){return await t.prepare(`WITH RECURSIVE walk(id) AS (
7
8
  SELECT CAST(? AS text)
8
9
  UNION
9
10
  SELECT w.watcher FROM walk JOIN watches w ON w.object = walk.id AND w.in_tree = 1
10
11
  ) SELECT o.* FROM walk JOIN objects o ON o.id = walk.id
11
- WHERE o.type = 'node' AND o.id != ?`).all(e.id,e.id)}i(Y,"ancestors");async function $(t,e){return await t.prepare(`SELECT o.* FROM watches w JOIN objects o ON o.id = w.watcher
12
- WHERE w.object = ? AND w.in_tree = 1 ORDER BY w.seq`).all(e.id)}i($,"treeWatchersOf");async function G(t,e,n){const r=new Date().toISOString(),a=await t.prepare("SELECT * FROM watches WHERE watcher = ?").all(e.id),s=t.prepare(`INSERT INTO watch_subscriptions (subscription_id, watch, pattern, filter_json, config_json, started_by, ends_on, created_at)
13
- VALUES (?, ?, ?, ?, ?, 'fork', ?, ?)`);for(const c of a){if(c.object===n.id)continue;await t.prepare(`INSERT INTO watches (watch_id, watcher, object, in_tree, read_heads_key, created_at) VALUES (?, ?, ?, 0, ?, ?)
14
- ON CONFLICT (watcher, object) DO NOTHING`).run(h(),n.id,c.object,c.read_heads_key,r);const w=await _(t,n,{id:c.object}),l=await E(t,w.watch_id);for(const o of await E(t,c.watch_id))l.some(d=>d.pattern===o.pattern&&d.ends_on===o.ends_on)||await s.run(h(),w.watch_id,o.pattern,o.filter_json,o.config_json,o.ends_on,r);await u(t,w.watch_id)}}i(G,"copyWatchesForFork");export{Y as ancestors,b as configFromJson,N as configToJson,G as copyWatchesForFork,B as endMessageSubscriptions,U as markLinkWatch,x as recordWatch,v as setSubscriptionConfig,$ as treeWatchersOf,J as unwatch,V as watcherIdsOf,q as watchersOf,P as watchesOf};
12
+ WHERE o.type = 'node' AND o.id != ?`).all(e.id,e.id)}i(V,"ancestors");async function G(t,e){return await t.prepare(`SELECT o.* FROM watches w JOIN objects o ON o.id = w.watcher
13
+ WHERE w.object = ? AND w.in_tree = 1 ORDER BY w.seq`).all(e.id)}i(G,"treeWatchersOf");async function Y(t,e,a){const r=new Date().toISOString(),n=await t.prepare("SELECT * FROM watches WHERE watcher = ?").all(e.id),o=t.prepare(`INSERT INTO watch_subscriptions (subscription_id, watch, pattern, filter_json, config_json, started_by, ends_on, created_at)
14
+ VALUES (?, ?, ?, ?, ?, 'fork', ?, ?)`);for(const c of n){if(c.object===a.id)continue;await t.prepare(`INSERT INTO watches (watch_id, watcher, object, in_tree, read_heads_key, created_at) VALUES (?, ?, ?, 0, ?, ?)
15
+ ON CONFLICT (watcher, object) DO NOTHING`).run(h(),a.id,c.object,c.read_heads_key,r);const s=await u(t,a,{id:c.object}),d=await E(t,s.watch_id);for(const w of await E(t,c.watch_id))d.some(p=>p.pattern===w.pattern&&p.ends_on===w.ends_on)||await o.run(h(),s.watch_id,w.pattern,w.filter_json,w.config_json,w.ends_on,r);await l(t,s.watch_id)}}i(Y,"copyWatchesForFork");export{V as ancestors,A as configFromJson,y as configToJson,Y as copyWatchesForFork,$ as endMessageSubscriptions,U as markLinkWatch,x as recordWatch,v as setSubscriptionConfig,G as treeWatchersOf,J as unwatch,P as watcherIdsOf,B as watchersOf,q as watchesOf};
@@ -1,6 +1,6 @@
1
1
  import type { RequestPrincipal } from '../../daemon/api/router.js';
2
2
  import type { ProviderCaller } from '../../daemon/api/handlers/providers.js';
3
- import { type RunPlace } from '../runs/confinement.js';
3
+ import { type RunPlace } from '../permissions/ceiling.js';
4
4
  export declare const ENVELOPE_HEADER = "Crtr-Envelope";
5
5
  /** A header larger than this is refused unread. */
6
6
  export declare const MAX_ENVELOPE_BYTES: number;
@@ -1 +1 @@
1
- var p=Object.defineProperty;var c=(e,o)=>p(e,"name",{value:o,configurable:!0});import{joinedOwner as b}from"../directory/join.js";import{runPlace as d}from"../runs/confinement.js";import{ownMachineId as f}from"./directory.js";const y="Crtr-Envelope",h=16*1024;class r extends Error{static{c(this,"EnvelopeError")}constructor(o){super(o),this.name="EnvelopeError"}}async function _(e,o={}){const n=o.via??f(),s=e.class==="person"?"":e.sub,t=o.owner??(s.startsWith("user:")?s:b());if(t==null)throw new r("this machine has no account owner to make a peer call for");if(e.class==="person")return{sub:t,act:{sub:n}};if(e.class==="broker"){const a=await(o.placeOf??(async u=>await d(u)))(e.node);return{sub:t,act:{sub:n,act:{sub:e.app,node:e.node,node_scopes:e.scopes===null?null:[...e.scopes],place:a}}}}return{sub:t,act:{sub:n,act:{sub:e.app,node:e.anchor,node_scopes:e.scopes===null?null:[...e.scopes],place:{run:e.origin_run??"",isolated:e.isolated}}}}}c(_,"envelopeFor");function g(e){return Buffer.from(JSON.stringify(e),"utf8").toString("base64url")}c(g,"encodeEnvelope");const l=c(e=>typeof e=="string"&&e!=="","isString"),i=c(e=>typeof e=="object"&&e!==null&&!Array.isArray(e),"isRecord");function S(e){if(e.length>h)throw new r("the envelope is too large");if(!/^[A-Za-z0-9_-]+$/.test(e))throw new r("the envelope is not base64url");let o;try{o=JSON.parse(Buffer.from(e,"base64url").toString("utf8"))}catch{throw new r("the envelope is not JSON")}if(!i(o)||!l(o.sub)||!o.sub.startsWith("user:"))throw new r("the envelope names no user");const n=o.act;if(!i(n)||!l(n.sub)||!n.sub.startsWith("runtime:"))throw new r("the envelope names no calling machine");const s=n.act;if(s===void 0)return{sub:o.sub,act:{sub:n.sub}};if(!i(s)||!l(s.sub)||!s.sub.startsWith("app:"))throw new r("the envelope names no calling app");const{node:t,node_scopes:a,place:u}=s;if(t!==null&&!l(t))throw new r("the envelope node is not a node id");if(a!==null&&!(Array.isArray(a)&&a.every(l)))throw new r("the envelope node_scopes is not a list of scopes");if(u!==null&&!(i(u)&&typeof u.run=="string"&&typeof u.isolated=="boolean"))throw new r("the envelope place is malformed");return{sub:o.sub,act:{sub:n.sub,act:{sub:s.sub,node:t,node_scopes:a,place:u===null?null:{run:u.run,isolated:u.isolated}}}}}c(S,"decodeEnvelope");async function O(e,o,n={}){const s=n.via??f();if(e.class==="person"||e.grantee===null)return{sub:o,act:{sub:s}};const t=e.node_id===null?null:await(n.placeOf??(async a=>await d(a)))(e.node_id);return{sub:o,act:{sub:s,act:{sub:e.grantee,node:e.node_id,node_scopes:e.nodeScopes===null?null:[...e.nodeScopes],place:t}}}}c(O,"envelopeForProviderCaller");export{y as ENVELOPE_HEADER,r as EnvelopeError,h as MAX_ENVELOPE_BYTES,S as decodeEnvelope,g as encodeEnvelope,_ as envelopeFor,O as envelopeForProviderCaller};
1
+ var b=Object.defineProperty;var c=(e,o)=>b(e,"name",{value:o,configurable:!0});import{joinedOwner as w}from"../directory/join.js";import{nodePlace as d}from"../permissions/ceiling.js";import{openDb as f}from"../canvas/db.js";import{ownMachineId as p}from"./directory.js";const g="Crtr-Envelope",h=16*1024;class r extends Error{static{c(this,"EnvelopeError")}constructor(o){super(o),this.name="EnvelopeError"}}async function S(e,o={}){const n=o.via??p(),s=e.class==="person"?"":e.sub,t=o.owner??(s.startsWith("user:")?s:w());if(t==null)throw new r("this machine has no account owner to make a peer call for");if(e.class==="person")return{sub:t,act:{sub:n}};if(e.class==="broker"){const a=await(o.placeOf??(async u=>await d(await f(),u)))(e.node);return{sub:t,act:{sub:n,act:{sub:e.app,node:e.node,node_scopes:e.scopes===null?null:[...e.scopes],place:a}}}}return{sub:t,act:{sub:n,act:{sub:e.app,node:e.anchor,node_scopes:e.scopes===null?null:[...e.scopes],place:{run:e.origin_run??"",isolated:e.isolated}}}}}c(S,"envelopeFor");function O(e){return Buffer.from(JSON.stringify(e),"utf8").toString("base64url")}c(O,"encodeEnvelope");const l=c(e=>typeof e=="string"&&e!=="","isString"),i=c(e=>typeof e=="object"&&e!==null&&!Array.isArray(e),"isRecord");function A(e){if(e.length>h)throw new r("the envelope is too large");if(!/^[A-Za-z0-9_-]+$/.test(e))throw new r("the envelope is not base64url");let o;try{o=JSON.parse(Buffer.from(e,"base64url").toString("utf8"))}catch{throw new r("the envelope is not JSON")}if(!i(o)||!l(o.sub)||!o.sub.startsWith("user:"))throw new r("the envelope names no user");const n=o.act;if(!i(n)||!l(n.sub)||!n.sub.startsWith("runtime:"))throw new r("the envelope names no calling machine");const s=n.act;if(s===void 0)return{sub:o.sub,act:{sub:n.sub}};if(!i(s)||!l(s.sub)||!s.sub.startsWith("app:"))throw new r("the envelope names no calling app");const{node:t,node_scopes:a,place:u}=s;if(t!==null&&!l(t))throw new r("the envelope node is not a node id");if(a!==null&&!(Array.isArray(a)&&a.every(l)))throw new r("the envelope node_scopes is not a list of scopes");if(u!==null&&!(i(u)&&typeof u.run=="string"&&typeof u.isolated=="boolean"))throw new r("the envelope place is malformed");return{sub:o.sub,act:{sub:n.sub,act:{sub:s.sub,node:t,node_scopes:a,place:u===null?null:{run:u.run,isolated:u.isolated}}}}}c(A,"decodeEnvelope");async function x(e,o,n={}){const s=n.via??p();if(e.class==="person"||e.grantee===null)return{sub:o,act:{sub:s}};const t=e.node_id===null?null:await(n.placeOf??(async a=>await d(await f(),a)))(e.node_id);return{sub:o,act:{sub:s,act:{sub:e.grantee,node:e.node_id,node_scopes:e.nodeScopes===null?null:[...e.nodeScopes],place:t}}}}c(x,"envelopeForProviderCaller");export{g as ENVELOPE_HEADER,r as EnvelopeError,h as MAX_ENVELOPE_BYTES,A as decodeEnvelope,O as encodeEnvelope,S as envelopeFor,x as envelopeForProviderCaller};
@@ -1,4 +1,4 @@
1
- import type { ObjectRow, Privacy } from '../graph/types.js';
1
+ import { type ObjectRow, type Privacy } from '../graph/types.js';
2
2
  import { type CustomEvents } from '../graph/custom.js';
3
3
  import type { ObjectDTO } from '../../api/dto/objects.js';
4
4
  import type { Envelope } from './envelope.js';