@plurnk/plurnk-service 1.4.0 → 1.5.0

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 (227) hide show
  1. package/.env.defaults +20 -23
  2. package/INSTALL.md +6 -5
  3. package/README.md +1 -0
  4. package/SPEC.md +250 -149
  5. package/dist/Paths.d.ts.map +1 -1
  6. package/dist/Paths.js +2 -3
  7. package/dist/Paths.js.map +1 -1
  8. package/dist/build-info.json +1 -1
  9. package/dist/content/index.d.ts +1 -0
  10. package/dist/content/index.d.ts.map +1 -1
  11. package/dist/content/index.js +1 -0
  12. package/dist/content/index.js.map +1 -1
  13. package/dist/content/line-marker.d.ts +7 -3
  14. package/dist/content/line-marker.d.ts.map +1 -1
  15. package/dist/content/line-marker.js +2 -1
  16. package/dist/content/line-marker.js.map +1 -1
  17. package/dist/content/matcher.d.ts +1 -1
  18. package/dist/content/matcher.d.ts.map +1 -1
  19. package/dist/content/matcher.js +2 -2
  20. package/dist/content/matcher.js.map +1 -1
  21. package/dist/content/read-projector.d.ts +14 -0
  22. package/dist/content/read-projector.d.ts.map +1 -0
  23. package/dist/content/read-projector.js +86 -0
  24. package/dist/content/read-projector.js.map +1 -0
  25. package/dist/content/read-resolve.d.ts +2 -7
  26. package/dist/content/read-resolve.d.ts.map +1 -1
  27. package/dist/content/read-resolve.js +32 -107
  28. package/dist/content/read-resolve.js.map +1 -1
  29. package/dist/core/BranchReceipt.d.ts +0 -1
  30. package/dist/core/BranchReceipt.d.ts.map +1 -1
  31. package/dist/core/BranchReceipt.js +0 -3
  32. package/dist/core/BranchReceipt.js.map +1 -1
  33. package/dist/core/BudgetOverflow.d.ts +0 -3
  34. package/dist/core/BudgetOverflow.d.ts.map +1 -1
  35. package/dist/core/BudgetOverflow.js +3 -15
  36. package/dist/core/BudgetOverflow.js.map +1 -1
  37. package/dist/core/ChannelWrite.d.ts +3 -0
  38. package/dist/core/ChannelWrite.d.ts.map +1 -1
  39. package/dist/core/ChannelWrite.js +2 -2
  40. package/dist/core/ChannelWrite.js.map +1 -1
  41. package/dist/core/ChannelWrite.sql +4 -3
  42. package/dist/core/CoreSchemeServices.d.ts +10 -2
  43. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  44. package/dist/core/CoreSchemeServices.js.map +1 -1
  45. package/dist/core/Dispatcher.d.ts +3 -7
  46. package/dist/core/Dispatcher.d.ts.map +1 -1
  47. package/dist/core/Dispatcher.js +283 -217
  48. package/dist/core/Dispatcher.js.map +1 -1
  49. package/dist/core/Engine.d.ts +19 -12
  50. package/dist/core/Engine.d.ts.map +1 -1
  51. package/dist/core/Engine.js +261 -167
  52. package/dist/core/Engine.js.map +1 -1
  53. package/dist/core/Engine.sql +66 -63
  54. package/dist/core/LogBody.d.ts +2 -1
  55. package/dist/core/LogBody.d.ts.map +1 -1
  56. package/dist/core/LogBody.js +33 -1
  57. package/dist/core/LogBody.js.map +1 -1
  58. package/dist/core/LoopLifecycle.d.ts +1 -2
  59. package/dist/core/LoopLifecycle.d.ts.map +1 -1
  60. package/dist/core/LoopLifecycle.js +1 -4
  61. package/dist/core/LoopLifecycle.js.map +1 -1
  62. package/dist/core/LoopLifecycle.sql +1 -4
  63. package/dist/core/PacketBuilder.d.ts +3 -2
  64. package/dist/core/PacketBuilder.d.ts.map +1 -1
  65. package/dist/core/PacketBuilder.js +16 -16
  66. package/dist/core/PacketBuilder.js.map +1 -1
  67. package/dist/core/StrikeRail.d.ts +5 -1
  68. package/dist/core/StrikeRail.d.ts.map +1 -1
  69. package/dist/core/StrikeRail.js +4 -3
  70. package/dist/core/StrikeRail.js.map +1 -1
  71. package/dist/core/TerminalResult.d.ts +16 -0
  72. package/dist/core/TerminalResult.d.ts.map +1 -0
  73. package/dist/core/TerminalResult.js +49 -0
  74. package/dist/core/TerminalResult.js.map +1 -0
  75. package/dist/core/caps/CapsResolve.d.ts +5 -1
  76. package/dist/core/caps/CapsResolve.d.ts.map +1 -1
  77. package/dist/core/caps/CapsResolve.js +8 -4
  78. package/dist/core/caps/CapsResolve.js.map +1 -1
  79. package/dist/core/caps/DbChannelCaps.d.ts +1 -1
  80. package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
  81. package/dist/core/caps/DbChannelCaps.js +6 -4
  82. package/dist/core/caps/DbChannelCaps.js.map +1 -1
  83. package/dist/core/caps/DbEntryCaps.d.ts +1 -1
  84. package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
  85. package/dist/core/caps/DbEntryCaps.js +14 -4
  86. package/dist/core/caps/DbEntryCaps.js.map +1 -1
  87. package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
  88. package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
  89. package/dist/core/caps/DbNotifyCaps.js +6 -4
  90. package/dist/core/caps/DbNotifyCaps.js.map +1 -1
  91. package/dist/core/caps/DbSubscriptionCaps.d.ts +4 -6
  92. package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
  93. package/dist/core/caps/DbSubscriptionCaps.js +27 -13
  94. package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
  95. package/dist/core/caps/DbTagCaps.d.ts +1 -1
  96. package/dist/core/caps/DbTagCaps.d.ts.map +1 -1
  97. package/dist/core/caps/DbTagCaps.js +6 -4
  98. package/dist/core/caps/DbTagCaps.js.map +1 -1
  99. package/dist/core/caps/SchemeCtxImpl.d.ts +6 -1
  100. package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
  101. package/dist/core/caps/SchemeCtxImpl.js +6 -6
  102. package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
  103. package/dist/core/fork.sql +2 -2
  104. package/dist/core/git-iso.d.ts +3 -2
  105. package/dist/core/git-iso.d.ts.map +1 -1
  106. package/dist/core/git-iso.js +27 -12
  107. package/dist/core/git-iso.js.map +1 -1
  108. package/dist/core/git-membership.d.ts +1 -0
  109. package/dist/core/git-membership.d.ts.map +1 -1
  110. package/dist/core/git-membership.js +86 -24
  111. package/dist/core/git-membership.js.map +1 -1
  112. package/dist/core/git-state.d.ts +8 -1
  113. package/dist/core/git-state.d.ts.map +1 -1
  114. package/dist/core/git-state.js +35 -16
  115. package/dist/core/git-state.js.map +1 -1
  116. package/dist/core/namespace.d.ts +1 -0
  117. package/dist/core/namespace.d.ts.map +1 -1
  118. package/dist/core/namespace.js +20 -0
  119. package/dist/core/namespace.js.map +1 -1
  120. package/dist/core/optimistic-settlement.d.ts +2 -0
  121. package/dist/core/optimistic-settlement.d.ts.map +1 -0
  122. package/dist/core/optimistic-settlement.js +14 -0
  123. package/dist/core/optimistic-settlement.js.map +1 -0
  124. package/dist/core/packet-wire.d.ts +1 -1
  125. package/dist/core/packet-wire.d.ts.map +1 -1
  126. package/dist/core/packet-wire.js +81 -35
  127. package/dist/core/packet-wire.js.map +1 -1
  128. package/dist/core/results.d.ts +4 -2
  129. package/dist/core/results.d.ts.map +1 -1
  130. package/dist/core/results.js +6 -0
  131. package/dist/core/results.js.map +1 -1
  132. package/dist/core/scheme-types.d.ts +0 -12
  133. package/dist/core/scheme-types.d.ts.map +1 -1
  134. package/dist/core/scheme-types.js.map +1 -1
  135. package/dist/core/worker-ops.sql +4 -4
  136. package/dist/core/workspace-settings.js +1 -1
  137. package/dist/core/workspace-settings.js.map +1 -1
  138. package/dist/digest/Digest.d.ts.map +1 -1
  139. package/dist/digest/Digest.js +236 -74
  140. package/dist/digest/Digest.js.map +1 -1
  141. package/dist/digest/digest.sql +9 -5
  142. package/dist/schemes/Exec.d.ts +4 -4
  143. package/dist/schemes/Exec.d.ts.map +1 -1
  144. package/dist/schemes/Exec.js +56 -33
  145. package/dist/schemes/Exec.js.map +1 -1
  146. package/dist/schemes/ExecOutputScheme.d.ts +4 -5
  147. package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
  148. package/dist/schemes/ExecOutputScheme.js +12 -20
  149. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  150. package/dist/schemes/File.d.ts +1 -3
  151. package/dist/schemes/File.d.ts.map +1 -1
  152. package/dist/schemes/File.js +11 -19
  153. package/dist/schemes/File.js.map +1 -1
  154. package/dist/schemes/Log.d.ts +5 -5
  155. package/dist/schemes/Log.d.ts.map +1 -1
  156. package/dist/schemes/Log.js +74 -172
  157. package/dist/schemes/Log.js.map +1 -1
  158. package/dist/schemes/Log.sql +20 -13
  159. package/dist/schemes/Prompt.d.ts +2 -3
  160. package/dist/schemes/Prompt.d.ts.map +1 -1
  161. package/dist/schemes/Prompt.js +1 -4
  162. package/dist/schemes/Prompt.js.map +1 -1
  163. package/dist/schemes/Skill.d.ts +2 -3
  164. package/dist/schemes/Skill.d.ts.map +1 -1
  165. package/dist/schemes/Skill.js +0 -3
  166. package/dist/schemes/Skill.js.map +1 -1
  167. package/dist/schemes/Worker.d.ts +5 -5
  168. package/dist/schemes/Worker.d.ts.map +1 -1
  169. package/dist/schemes/Worker.js +92 -81
  170. package/dist/schemes/Worker.js.map +1 -1
  171. package/dist/schemes/_entry-crud.d.ts +2 -1
  172. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  173. package/dist/schemes/_entry-crud.js +14 -1
  174. package/dist/schemes/_entry-crud.js.map +1 -1
  175. package/dist/schemes/_entry-crud.sql +9 -3
  176. package/dist/schemes/_entry-find.d.ts +37 -11
  177. package/dist/schemes/_entry-find.d.ts.map +1 -1
  178. package/dist/schemes/_entry-find.js +141 -153
  179. package/dist/schemes/_entry-find.js.map +1 -1
  180. package/dist/schemes/_entry-ops.d.ts +9 -5
  181. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  182. package/dist/schemes/_entry-ops.js +12 -73
  183. package/dist/schemes/_entry-ops.js.map +1 -1
  184. package/dist/schemes/_entry-ops.sql +1 -2
  185. package/dist/schemes/_entry-semantic.d.ts +5 -7
  186. package/dist/schemes/_entry-semantic.d.ts.map +1 -1
  187. package/dist/schemes/_entry-semantic.js +10 -31
  188. package/dist/schemes/_entry-semantic.js.map +1 -1
  189. package/dist/schemes/_entry-semantic.sql +2 -2
  190. package/dist/schemes/_search-index.d.ts.map +1 -1
  191. package/dist/schemes/_search-index.js +6 -4
  192. package/dist/schemes/_search-index.js.map +1 -1
  193. package/dist/server/BranchBatches.d.ts +1 -0
  194. package/dist/server/BranchBatches.d.ts.map +1 -1
  195. package/dist/server/BranchBatches.js +1 -0
  196. package/dist/server/BranchBatches.js.map +1 -1
  197. package/dist/server/Daemon.d.ts +5 -7
  198. package/dist/server/Daemon.d.ts.map +1 -1
  199. package/dist/server/Daemon.js +222 -54
  200. package/dist/server/Daemon.js.map +1 -1
  201. package/dist/server/DaemonModule.d.ts +3 -3
  202. package/dist/server/DaemonModule.d.ts.map +1 -1
  203. package/dist/server/branch-batch.sql +3 -1
  204. package/dist/server/client-input.d.ts +2 -1
  205. package/dist/server/client-input.d.ts.map +1 -1
  206. package/dist/server/client-input.js +7 -1
  207. package/dist/server/client-input.js.map +1 -1
  208. package/dist/server/drain.sql +6 -5
  209. package/dist/server/envelope.d.ts +2 -2
  210. package/dist/server/envelope.d.ts.map +1 -1
  211. package/dist/server/envelope.js +0 -1
  212. package/dist/server/envelope.js.map +1 -1
  213. package/dist/server/envelope.sql +1 -2
  214. package/dist/server/lifecycle-recovery.sql +34 -1
  215. package/dist/server/logEntry.d.ts +1 -1
  216. package/dist/server/logEntry.d.ts.map +1 -1
  217. package/dist/server/logEntry.js +2 -1
  218. package/dist/server/logEntry.js.map +1 -1
  219. package/dist/service.d.ts.map +1 -1
  220. package/dist/service.js +13 -1
  221. package/dist/service.js.map +1 -1
  222. package/migrations/001_schema.sql +238 -125
  223. package/package.json +48 -46
  224. package/dist/matrix/live-specimen.d.ts +0 -9
  225. package/dist/matrix/live-specimen.d.ts.map +0 -1
  226. package/dist/matrix/live-specimen.js +0 -37
  227. package/dist/matrix/live-specimen.js.map +0 -1
@@ -5,27 +5,29 @@ import WorkerControlAddress from "./WorkerControlAddress.js";
5
5
  import { entryPathnameOf, foldAuthorityIntoPath, renderAddress, renderTarget, schemeNameOf } from "./plurnk-uri.js";
6
6
  import Fork from "./fork.js";
7
7
  import WorkerCap from "./worker-cap.js";
8
- import { parsePath, PathSyntax } from "@plurnk/plurnk-contracts";
8
+ import { PathSyntax } from "@plurnk/plurnk-contracts";
9
9
  import Namespace from "./namespace.js";
10
10
  import LoopFlagsReader from "./LoopFlagsReader.js";
11
11
  import ChannelWrite from "./ChannelWrite.js";
12
- import { assertEditBatchReceipt, assertEditReceipt, assertResourceEffects, editReceipt, LineMarkerOps, MimetypeBinary, PathMimetype, projectEditReceipt, } from "../content/index.js";
12
+ import { assertEditBatchReceipt, assertEditReceipt, assertResourceEffects, editReceipt, LineMarkerOps, MimetypeBinary, PathMimetype, ReadProjector, projectEditReceipt, } from "../content/index.js";
13
13
  import SchemeCtxImpl from "./caps/SchemeCtxImpl.js";
14
14
  import EntryCrud from "../schemes/_entry-crud.js";
15
15
  import EntryOps from "../schemes/_entry-ops.js";
16
+ import EntryFind from "../schemes/_entry-find.js";
16
17
  import WorkspaceSettings from "./workspace-settings.js";
17
18
  import LoopLifecycle from "./LoopLifecycle.js";
19
+ import TerminalResult from "./TerminalResult.js";
18
20
  import Results from "./results.js";
19
21
  import { OperationFailureError } from "./results.js";
20
22
  import EffectPolicy from "../schemes/EffectPolicy.js";
21
- import { CoreSchemeAdapterBase } from "./CoreSchemeServices.js";
23
+ import { CoreSchemeAdapterBase, } from "./CoreSchemeServices.js";
22
24
  import { InvalidOperationResultError, NetworkAddress, } from "@plurnk/plurnk-schemes";
23
25
  import DurableStatement from "./DurableStatement.js";
24
26
  // SPEC {§scheme-surface}: writer must be in target scheme's manifest.writableBy.
25
27
  // OPEN/FOLD/READ/FIND are not gated — they curate the log or read, never mutating an entry.
26
28
  const MUTATING_OPS = new Set(["EDIT", "SEND", "COPY", "MOVE", "EXEC", "KILL", "FORK", "WORK"]);
27
29
  // Op dispatch ({§op-methods-op-dispatch}): gates (writableBy, loop flags), the
28
- // engine-owned op orchestrations (COPY/MOVE/KILL/SEND/READ-fanout), scheme
30
+ // engine-owned op orchestrations (COPY/MOVE/KILL/SEND), scheme
29
31
  // routing, the durable log write, and the proposal pause.
30
32
  class Dispatcher {
31
33
  static #failure(code, status, detail, fields = {}, extensions = {}) {
@@ -95,8 +97,14 @@ class Dispatcher {
95
97
  const handlerCtx = this.#handlerContext(scheme, ctx);
96
98
  return this.#schemes.manifestFor(scheme)?.category === "data" ? handlerCtx : null;
97
99
  }
98
- async #readEntry(scheme, pathname, ctx) {
100
+ #coreCrud(scheme) {
99
101
  const handler = this.#schemes.get(scheme);
102
+ return handler instanceof CoreSchemeAdapterBase
103
+ ? handler
104
+ : undefined;
105
+ }
106
+ async #readEntry(scheme, pathname, ctx) {
107
+ const handler = this.#coreCrud(scheme);
100
108
  const handlerCtx = this.#entryContext(scheme, ctx);
101
109
  if (typeof handler?.readEntry === "function" && handlerCtx !== null) {
102
110
  return Results.assert(await handler.readEntry(pathname, handlerCtx));
@@ -120,7 +128,7 @@ class Dispatcher {
120
128
  });
121
129
  }
122
130
  async #writeEntry(scheme, pathname, entry, ctx) {
123
- const handler = this.#schemes.get(scheme);
131
+ const handler = this.#coreCrud(scheme);
124
132
  const handlerCtx = this.#entryContext(scheme, ctx);
125
133
  if (typeof handler?.writeEntry === "function" && handlerCtx !== null) {
126
134
  return Results.assert(await handler.writeEntry(pathname, entry, handlerCtx));
@@ -137,7 +145,7 @@ class Dispatcher {
137
145
  return Results.assert(await caps.write(pathname, entry, scheme === "prompt" ? "worker" : "commons"));
138
146
  }
139
147
  async #deleteEntry(scheme, pathname, ctx) {
140
- const handler = this.#schemes.get(scheme);
148
+ const handler = this.#coreCrud(scheme);
141
149
  const handlerCtx = this.#entryContext(scheme, ctx);
142
150
  if (typeof handler?.deleteEntry === "function" && handlerCtx !== null) {
143
151
  return Results.assert(await handler.deleteEntry(pathname, handlerCtx));
@@ -154,7 +162,7 @@ class Dispatcher {
154
162
  return Results.assert(await caps.delete(pathname, scheme === "prompt" ? "worker" : "commons"));
155
163
  }
156
164
  async #deleteChannel(scheme, pathname, channel, ctx) {
157
- const handler = this.#schemes.get(scheme);
165
+ const handler = this.#coreCrud(scheme);
158
166
  const handlerCtx = this.#entryContext(scheme, ctx);
159
167
  if (typeof handler?.deleteChannel === "function" && handlerCtx !== null) {
160
168
  return Results.assert(await handler.deleteChannel(pathname, channel, handlerCtx));
@@ -241,7 +249,10 @@ class Dispatcher {
241
249
  else {
242
250
  try {
243
251
  const addressedScheme = first.target?.kind === "url" ? first.target.scheme : schemeName;
244
- initial = Results.assert(await method.call(handler, group, new SchemeCtxImpl(schemeCtx, addressedScheme ?? schemeName, manifest, this.#liveSubscriptions)));
252
+ const publishedChannel = first.target?.kind === "url"
253
+ ? first.target.fragment ?? manifest.defaultChannel
254
+ : manifest.defaultChannel;
255
+ initial = Results.assert(await method.call(handler, group, new SchemeCtxImpl(schemeCtx, addressedScheme ?? schemeName, manifest, this.#liveSubscriptions, { publishedChannel })));
245
256
  }
246
257
  catch (err) {
247
258
  if (err instanceof InvalidOperationResultError)
@@ -297,31 +308,15 @@ class Dispatcher {
297
308
  return result;
298
309
  }
299
310
  async #dispatchOne(context) {
300
- const { statement, workspaceId, workerId, loopId, turnId, sequence, origin, onDispatch, operationConstraint, } = context;
311
+ const { statement, workspaceId, workerId, loopId, turnId, sequence, origin, onDispatch, } = context;
301
312
  const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, loopId, turnId, origin });
302
313
  let result;
303
- let denial = operationConstraint !== undefined
304
- && !operationConstraint.allowedOperations.includes(statement.op)
305
- ? _a.#failure("operation-not-allowed", 409, `${statement.op} was not executed because ${operationConstraint.detail}.`, {}, {
306
- constraint: operationConstraint.code,
307
- allowedOperations: [...operationConstraint.allowedOperations],
308
- retryable: false,
309
- })
310
- : null;
311
- if (denial === null)
312
- denial = this.#checkWritable(statement, origin);
314
+ let denial = this.#checkWritable(statement, origin);
313
315
  if (denial === null)
314
316
  denial = await this.#checkFlagsGate(statement, loopId);
315
317
  if (denial !== null) {
316
318
  result = denial;
317
319
  }
318
- else if (this.#readFansOut(statement)) {
319
- // Multi-resource scopes, relation matchers, and acquisition-scheme
320
- // matchers use FIND for selection and write their own rows. An exact
321
- // stored-resource content matcher remains a direct READ, preserving
322
- // content-level outcomes such as 203.
323
- return await this.#handleReadFanout(statement, schemeCtx, { workerId, loopId, turnId, sequence, origin, onDispatch });
324
- }
325
320
  else {
326
321
  // {§scheme-surface-exception-500} Scheme-handler
327
322
  // exceptions become the action-entry's outcome (status 500), not a
@@ -531,11 +526,49 @@ class Dispatcher {
531
526
  origin: "client",
532
527
  });
533
528
  const schemeCtx = new SchemeCtxImpl(coreCtx, addressedScheme, manifest, this.#liveSubscriptions);
529
+ const resolved = await this.#resolveDataEntryAddress({
530
+ target,
531
+ routedScheme,
532
+ handler,
533
+ manifest,
534
+ ctx: coreCtx,
535
+ schemeCtx,
536
+ });
537
+ if (resolved.address === null)
538
+ return null;
539
+ const rendered = target.kind === "url"
540
+ ? renderTarget({ ...target, fragment: null })
541
+ : renderTarget({ scheme: null, pathname: target.raw, fragment: null });
542
+ if (rendered === null)
543
+ throw new TypeError("Resolved entry target did not render.");
544
+ return {
545
+ ownerId: resolved.address.ownerId,
546
+ scheme: resolved.address.scheme,
547
+ pathname: resolved.address.pathname,
548
+ target: rendered,
549
+ };
550
+ }
551
+ async #resolveDataEntryAddress({ target, routedScheme, handler, manifest, ctx, schemeCtx, }) {
552
+ const addressedScheme = target.kind === "url" ? target.scheme : routedScheme;
553
+ const identityTarget = target.kind === "url"
554
+ ? {
555
+ ...target,
556
+ pathname: PathSyntax.decodeParens(target.pathname),
557
+ fragment: null,
558
+ }
559
+ : { ...target, raw: PathSyntax.decodeParens(target.raw) };
534
560
  const resolved = handler.resolveEntryAddress === undefined
535
- ? { pathname: entryPathnameOf(target), owner: "commons" }
536
- : await handler.resolveEntryAddress(target, schemeCtx);
561
+ ? { pathname: entryPathnameOf(identityTarget), owner: "commons" }
562
+ : await handler.resolveEntryAddress(identityTarget, schemeCtx);
537
563
  if (resolved === null)
538
- return null;
564
+ return { address: null, result: null };
565
+ if ("status" in resolved) {
566
+ const result = Results.assert(resolved);
567
+ if (result.status < 300) {
568
+ throw new InvalidOperationResultError(`Scheme '${routedScheme}' returned a successful operation result instead of an entry address.`);
569
+ }
570
+ return { address: null, result };
571
+ }
539
572
  if (typeof resolved.pathname !== "string") {
540
573
  throw new TypeError(`Scheme '${routedScheme}' returned an invalid entry pathname.`);
541
574
  }
@@ -547,10 +580,10 @@ class Dispatcher {
547
580
  ownerId = resolved.ownerId;
548
581
  }
549
582
  else if (resolved.owner === "worker") {
550
- ownerId = workerId;
583
+ ownerId = ctx.workerId;
551
584
  }
552
585
  else if (resolved.owner === "commons") {
553
- ownerId = await Owner.commonsId(this.#db, workspaceId);
586
+ ownerId = await Owner.commonsId(this.#db, ctx.workspaceId);
554
587
  }
555
588
  else {
556
589
  throw new TypeError(`Scheme '${routedScheme}' returned an invalid entry owner.`);
@@ -558,16 +591,48 @@ class Dispatcher {
558
591
  if (!Number.isSafeInteger(ownerId) || ownerId < 1) {
559
592
  throw new TypeError(`Scheme '${routedScheme}' returned an invalid entry owner id.`);
560
593
  }
561
- const rendered = target.kind === "url"
562
- ? renderTarget({ ...target, fragment: null })
563
- : renderTarget({ scheme: null, pathname: target.raw, fragment: null });
564
- if (rendered === null)
565
- throw new TypeError("Resolved entry target did not render.");
566
594
  return {
567
- ownerId,
568
- scheme: manifest.storedScheme ?? addressedScheme,
569
- pathname: resolved.pathname,
570
- target: rendered,
595
+ address: {
596
+ ownerId,
597
+ scheme: manifest.storedScheme ?? addressedScheme,
598
+ pathname: resolved.pathname,
599
+ },
600
+ result: null,
601
+ };
602
+ }
603
+ async #prepareDataRepresentation({ target, routedScheme, handler, manifest, ctx, schemeCtx, publishedChannel, }) {
604
+ const resolved = await this.#resolveDataEntryAddress({
605
+ target,
606
+ routedScheme,
607
+ handler,
608
+ manifest,
609
+ ctx,
610
+ schemeCtx,
611
+ });
612
+ if (resolved.address === null
613
+ || resolved.result !== null
614
+ || typeof handler.prepareRepresentation !== "function") {
615
+ return resolved;
616
+ }
617
+ const address = resolved.address;
618
+ const selectionNeutralTarget = target.kind === "url"
619
+ ? {
620
+ ...target,
621
+ raw: renderTarget({ ...target, fragment: null }) ?? target.raw,
622
+ fragment: null,
623
+ }
624
+ : target;
625
+ const preparationCtx = new SchemeCtxImpl(ctx, target.kind === "url" ? target.scheme : routedScheme, manifest, this.#liveSubscriptions, {
626
+ ownerId: address.ownerId,
627
+ publishedChannel,
628
+ });
629
+ const prepared = Results.assertRepresentationPreparation(await handler.prepareRepresentation({
630
+ target: selectionNeutralTarget,
631
+ pathname: address.pathname,
632
+ }, preparationCtx));
633
+ return {
634
+ address,
635
+ result: prepared.status === 200 ? null : prepared,
571
636
  };
572
637
  }
573
638
  // An accepted EXEC reads a non-file source through the same registered
@@ -829,7 +894,7 @@ class Dispatcher {
829
894
  if (statement.op === "FORK") {
830
895
  // Branch the current worker's log into a named sister.
831
896
  const branchWorkerId = await Fork.fork(this.#db, ctx.workerId, name);
832
- await ctx.injectWorker({ workspaceId: ctx.workspaceId, workerId: branchWorkerId, prompt, flags });
897
+ await ctx.injectWorker({ workspaceId: ctx.workspaceId, workerId: branchWorkerId, prompt, flags, parentLoopId: ctx.loopId });
833
898
  return { status: 200, body: name };
834
899
  }
835
900
  // WORK — a fresh worker sister named <name>.
@@ -838,7 +903,7 @@ class Dispatcher {
838
903
  });
839
904
  if (row === undefined)
840
905
  throw new Error("worker spawn: worker insert returned no row");
841
- await ctx.injectWorker({ workspaceId: ctx.workspaceId, workerId: row.id, prompt, flags });
906
+ await ctx.injectWorker({ workspaceId: ctx.workspaceId, workerId: row.id, prompt, flags, parentLoopId: ctx.loopId });
842
907
  return { status: 200, body: name };
843
908
  }
844
909
  async #handleCopy(statement, ctx) {
@@ -949,178 +1014,28 @@ class Dispatcher {
949
1014
  // carries the delete target to review (#isProposal fires on 202). Plurnk-internal deletes execute inline.
950
1015
  return this.#deleteEntry(schemeName, entryPathnameOf(path), ctx);
951
1016
  }
952
- // A multi-resource READ uses FIND as its resource selector, then writes one
953
- // READ row per selected resource. Match coordinates remain metadata on that
954
- // resource; they never create extra rows or replace its content.
955
- #readFansOut(statement) {
956
- if (statement.op !== "READ")
957
- return false;
958
- const body = statement.body;
959
- if (body?.dialect === "semantic" || body?.dialect === "graph")
960
- return true;
961
- const schemeName = schemeNameOf(statement.target);
962
- const handler = schemeName === null
963
- ? undefined
964
- : this.#schemes.get(schemeName);
965
- if (body !== null && typeof handler?.prepareFind === "function")
966
- return true;
967
- const t = statement.target;
968
- const p = t === null ? "" : (t.kind === "url" ? t.pathname : t.raw);
969
- if (PathSyntax.hasGlob(p))
970
- return true;
971
- return p.endsWith("/") && schemeName !== null && this.#schemes.manifestFor(schemeName)?.folderScopes === true;
972
- }
973
- static #retargetRead(statement, pathname, lineMarker, body) {
974
- const t = statement.target;
975
- const target = t !== null && t.kind === "url"
976
- ? { ...t, pathname, raw: `${t.scheme}://${pathname}` }
977
- : { ...t, raw: pathname };
978
- return { ...statement, target: target, lineMarker, body };
979
- }
980
- // READ scope always projects text. Semantic READ reserves a leading decimal
981
- // for similarity selection; any remaining integers are the text projection.
982
- // FIND retains its own public result-range interpretation.
983
- static #readMarkers(statement) {
984
- const marker = statement.lineMarker;
985
- if (statement.body?.dialect !== "semantic") {
986
- return { selection: null, projection: marker };
987
- }
988
- if (marker === null)
989
- return { selection: null, projection: null };
990
- const [first, ...rest] = marker.marks;
991
- if (Number.isInteger(first)) {
992
- return { selection: null, projection: marker };
993
- }
994
- return {
995
- selection: { marks: [first] },
996
- projection: rest.length === 0 ? null : { marks: rest },
997
- };
998
- }
999
- #standardEntryManifest(schemeName, statement) {
1000
- const handler = this.#schemes.get(schemeName);
1001
- if (typeof handler?.find === "function")
1002
- return null;
1003
- const target = statement.target;
1004
- if (target === null || target.kind !== "url" || target.scheme === null)
1005
- return null;
1006
- const manifest = this.#schemes.manifestFor(schemeName);
1007
- return manifest === undefined ? null : { ...manifest, name: target.scheme };
1008
- }
1009
- static #storedEntryRead(statement, pathname, lineMarker, body, storage) {
1010
- const target = statement.target;
1011
- if (target === null || target.kind !== "url")
1012
- return _a.#retargetRead(statement, pathname, lineMarker, body);
1013
- if (NetworkAddress.supports(target.scheme)) {
1014
- const parsed = parsePath(renderAddress(target.scheme, pathname));
1015
- if (parsed === null || parsed.kind !== "url") {
1016
- throw new Error(`failed to reconstruct network entry target for ${target.scheme}:${pathname}`);
1017
- }
1018
- const reconstructed = { ...parsed, fragment: target.fragment };
1019
- const raw = renderTarget(reconstructed);
1020
- if (raw === null)
1021
- throw new Error(`failed to render network entry target for ${target.scheme}:${pathname}`);
1022
- return {
1023
- ...statement,
1024
- target: { ...reconstructed, raw },
1025
- lineMarker,
1026
- body,
1027
- };
1028
- }
1029
- const hostPrefix = target.hostname === null ? null : `/${target.hostname}`;
1030
- const displayPath = hostPrefix !== null && pathname.startsWith(`${hostPrefix}/`)
1031
- ? pathname.slice(hostPrefix.length)
1032
- : pathname;
1033
- return {
1034
- ...statement,
1035
- target: storage
1036
- ? { ...target, hostname: null, pathname, raw: `${target.scheme}://${pathname}` }
1037
- : { ...target, pathname: displayPath, raw: `${target.scheme}://${target.hostname ?? ""}${displayPath}` },
1038
- lineMarker,
1039
- body,
1040
- };
1041
- }
1042
- async #handleReadFanout(statement, ctx, ids) {
1043
- const { workerId, loopId, turnId, sequence, origin, onDispatch } = ids;
1044
- const schemeName = schemeNameOf(statement.target);
1045
- const read = statement;
1046
- const markers = _a.#readMarkers(read);
1047
- const findStatement = {
1048
- ...statement,
1049
- op: "FIND",
1050
- lineMarker: markers.selection,
1051
- };
1052
- const standardEntryManifest = schemeName === null ? null : this.#standardEntryManifest(schemeName, statement);
1053
- const found = await this.#run(schemeName, findStatement, ctx);
1054
- const matches = found.matches ?? [];
1055
- const omittedItems = typeof found.omittedItems === "number" ? found.omittedItems : null;
1056
- if (omittedItems !== null) {
1057
- const maximumItems = typeof found.maximumItems === "number" ? found.maximumItems : null;
1058
- const result = _a.#failure("read-materialization-too-large", 413, `READ selected ${omittedItems} resources, exceeding the materialization limit${maximumItems === null ? "" : ` of ${maximumItems}`}.`, {}, {
1059
- resources: omittedItems,
1060
- ...(maximumItems === null ? {} : { maximumResources: maximumItems }),
1061
- stage: "materialization",
1062
- recovery: "Narrow the target or matcher.",
1063
- retryable: false,
1064
- });
1065
- const readRowId = await this.#writeLog({ statement, result, workspaceId: ctx.workspaceId, workerId, loopId, turnId, sequence, origin });
1066
- onDispatch?.(readRowId);
1067
- return { ...result, rowsWritten: 1 };
1068
- }
1069
- // Find-less scheme, a matcher/scope error, or zero matches → a single row carrying the
1070
- // status, exactly like a non-fanned READ. The model sees the empty/failed result, not silence.
1071
- if (found.status !== 200 || matches.length === 0) {
1072
- const result = found.status === 200
1073
- ? { status: 204, ...(read.body === null ? {} : { matches: [] }) }
1074
- : found;
1075
- const id = await this.#writeLog({ statement, result, workspaceId: ctx.workspaceId, workerId, loopId, turnId, sequence, origin });
1076
- onDispatch?.(id);
1077
- return { ...result, rowsWritten: 1 };
1078
- }
1079
- const fannedStatuses = [];
1080
- const scopeNormalizations = [];
1081
- let written = 0;
1082
- for (const match of matches) {
1083
- ctx.signal?.throwIfAborted();
1084
- const executionStatement = standardEntryManifest === null
1085
- ? _a.#retargetRead(statement, match.pathname, markers.projection, null)
1086
- : _a.#storedEntryRead(statement, match.pathname, markers.projection, null, true);
1087
- const readResult = standardEntryManifest === null
1088
- ? await this.#run(schemeName, executionStatement, ctx)
1089
- : await EntryOps.readWorkspaceEntry(executionStatement, ctx, standardEntryManifest);
1090
- const result = read.body === null
1091
- ? readResult
1092
- : { ...readResult, matches: match.matches };
1093
- const loggedStatement = standardEntryManifest === null
1094
- ? _a.#retargetRead(statement, match.pathname, statement.lineMarker, read.body)
1095
- : _a.#storedEntryRead(statement, match.pathname, statement.lineMarker, read.body, false);
1096
- const id = await this.#writeLog({ statement: loggedStatement, result, workspaceId: ctx.workspaceId, workerId, loopId, turnId, sequence: sequence + written, origin });
1097
- onDispatch?.(id);
1098
- fannedStatuses.push(result.status);
1099
- scopeNormalizations.push(...(result.scopeNormalizations ?? []));
1100
- written++;
1101
- }
1102
- return {
1103
- status: 200,
1104
- rowsWritten: written,
1105
- fannedStatuses,
1106
- ...(scopeNormalizations.length === 0 ? {} : { scopeNormalizations }),
1107
- };
1108
- }
1109
- // {§model-entry} — mirror an admitted model emission back as an actionless `model` log row, so
1110
- // the model can inspect and curate its own prior behavior.
1017
+ // {§model-entry} — mirror a model emission back as an actionless log row, so
1018
+ // the model can inspect and curate its own prior behavior. Ordinary admitted
1019
+ // emissions are folded; the bounded invalid-emission lifeline is the one
1020
+ // born-open rejected item under {§invalid-emission-attempts} and labels
1021
+ // that admission fact structurally.
1111
1022
  // Born FOLDED by default (budget-neutral until OPENed); the turn-0 exemplar passes folded:false
1112
1023
  // (born open — the one worked example the model orients on, thinning the grammar). text/vnd.plurnk.
1113
- async writeModelEntry({ verbatim, workerId, loopId, turnId, sequence, folded, origin = "model", reasoningItems }) {
1024
+ async writeModelEntry({ verbatim, workerId, loopId, turnId, sequence, folded, origin = "model", admission = "accepted", reasoningItems }) {
1114
1025
  const row = await this.#db.engine_insert_log_entry.get({
1115
1026
  worker_id: workerId, loop_id: loopId, turn_id: turnId, sequence,
1116
- origin, source: null, op: "model", suffix: "", signal: null,
1027
+ origin, source: null, op: null, suffix: "", signal: null,
1117
1028
  scheme: null, username: null, password: null, hostname: null, port: null,
1118
1029
  pathname: null, query: null, fragment: null, lineMarker: null,
1119
1030
  tx: "", mimetype_tx: "text/vnd.plurnk",
1120
1031
  rx: JSON.stringify({ content: verbatim, mimetype: "text/vnd.plurnk" }),
1121
1032
  mimetype_rx: "application/json",
1122
1033
  status_rx: 200, tokens: this.#tokenize(verbatim), state: "resolved", outcome: null,
1123
- attrs: reasoningItems !== undefined && reasoningItems.length > 0 ? JSON.stringify({ reasoning: reasoningItems }) : "{}",
1034
+ attrs: JSON.stringify({
1035
+ kind: "model_emission",
1036
+ ...(admission === "rejected" ? { admission } : {}),
1037
+ ...(reasoningItems !== undefined && reasoningItems.length > 0 ? { reasoning: reasoningItems } : {}),
1038
+ }),
1124
1039
  });
1125
1040
  if (row === undefined)
1126
1041
  throw new Error("Dispatcher.writeModelEntry: insert returned no row");
@@ -1202,7 +1117,30 @@ class Dispatcher {
1202
1117
  };
1203
1118
  }
1204
1119
  async #selectSource(selection, ctx) {
1205
- const read = await this.#readEntry(selection.scheme, selection.pathname, ctx);
1120
+ const handler = this.#schemes.get(selection.scheme);
1121
+ if (handler === undefined) {
1122
+ throw new InvalidOperationResultError(`Resolved COPY/MOVE source scheme '${selection.scheme}' is no longer registered.`);
1123
+ }
1124
+ const addressedScheme = selection.target.kind === "url"
1125
+ ? selection.target.scheme
1126
+ : selection.scheme;
1127
+ const schemeCtx = new SchemeCtxImpl(ctx, addressedScheme, selection.manifest, this.#liveSubscriptions, { publishedChannel: selection.channel });
1128
+ const prepared = await this.#prepareDataRepresentation({
1129
+ target: selection.target,
1130
+ routedScheme: selection.scheme,
1131
+ handler,
1132
+ manifest: selection.manifest,
1133
+ ctx,
1134
+ schemeCtx,
1135
+ publishedChannel: selection.channel,
1136
+ });
1137
+ if (prepared.result !== null)
1138
+ return prepared.result;
1139
+ if (prepared.address === null) {
1140
+ return _a.#failure("entry-not-found", 404, `No entry exists at ${this.#resourceAddress(selection)}.`, {}, { target: this.#resourceAddress(selection) });
1141
+ }
1142
+ const storageAddress = prepared.address;
1143
+ const read = await EntryCrud.readEntry(storageAddress.pathname, ctx, storageAddress.scheme, storageAddress.ownerId);
1206
1144
  if (read.status >= 400)
1207
1145
  return read;
1208
1146
  if (read.status !== 200 || read.entry === null) {
@@ -1210,8 +1148,8 @@ class Dispatcher {
1210
1148
  }
1211
1149
  const selected = read.entry.channels[selection.channel];
1212
1150
  if (selected === undefined) {
1213
- return _a.#failure("channel-not-found", 404, `No channel named #${selection.channel} exists at ${renderAddress(selection.scheme, selection.identityPathname)}.`, {}, {
1214
- target: renderAddress(selection.scheme, selection.identityPathname),
1151
+ return _a.#failure("channel-not-found", 404, `No channel named #${selection.channel} exists at ${renderAddress(storageAddress.scheme, storageAddress.pathname)}.`, {}, {
1152
+ target: renderAddress(storageAddress.scheme, storageAddress.pathname),
1215
1153
  requestedChannel: selection.channel,
1216
1154
  availableChannels: Object.keys(read.entry.channels),
1217
1155
  retryable: false,
@@ -1234,8 +1172,12 @@ class Dispatcher {
1234
1172
  content = sliced.text ?? "";
1235
1173
  scopeNormalizations = sliced.scopeNormalizations;
1236
1174
  }
1175
+ if (selected.producerResult !== undefined && selected.producerResult.status >= 400) {
1176
+ return Results.assert(selected.producerResult);
1177
+ }
1237
1178
  return {
1238
1179
  ...selection,
1180
+ storageAddress,
1239
1181
  content,
1240
1182
  mimetype: selected.mimetype,
1241
1183
  ...(scopeNormalizations === undefined ? {} : { scopeNormalizations }),
@@ -1911,7 +1853,7 @@ class Dispatcher {
1911
1853
  if (status === null) {
1912
1854
  return _a.#failure("send-status-required", 400, "SEND requires a numeric status.", {}, { retryable: false });
1913
1855
  }
1914
- const raw = statement.body === null ? "" : typeof statement.body === "string" ? statement.body : statement.body.raw;
1856
+ const raw = statement.body === null ? "" : statement.body.raw;
1915
1857
  // The park rides SEND[202] only ({§park-202-only}). A scoped SEND[102] is neither
1916
1858
  // a wait nor a meaningful continuation, so reject it instead of preserving the
1917
1859
  // retired dual spelling.
@@ -1927,7 +1869,7 @@ class Dispatcher {
1927
1869
  // indefinite park. {§join-blocking-collect}
1928
1870
  const joinArmed = this.#joinTargets.delete(loopId);
1929
1871
  if (status === 102 && statement.lineMarker === null && joinArmed) {
1930
- if (!await this.#lifecycle.park(loopId, raw.length > 0 ? raw : "parked — awaiting a worker's result (blocking collect)")) {
1872
+ if (!await this.#lifecycle.park(loopId)) {
1931
1873
  return _a.#statusResult(await this.#lifecycle.status(loopId), "loop-already-terminal", "The loop was already terminal when SEND attempted to park it.");
1932
1874
  }
1933
1875
  this.#parkDeadlines.set(loopId, -1); // indefinite: the bounded child's terminal is the wake edge
@@ -1942,7 +1884,7 @@ class Dispatcher {
1942
1884
  const marks = statement.lineMarker?.marks[0];
1943
1885
  const seconds = typeof marks === "number" ? marks : -1; // bare 202 / absent T = indefinite, bounded by the join
1944
1886
  if (await this.#hasLiveWork(workerId)) {
1945
- if (!await this.#lifecycle.park(loopId, raw.length > 0 ? raw : "waiting on live work")) {
1887
+ if (!await this.#lifecycle.park(loopId)) {
1946
1888
  return _a.#statusResult(await this.#lifecycle.status(loopId), "loop-already-terminal", "The loop was already terminal when SEND attempted to wait.");
1947
1889
  }
1948
1890
  this.#parkDeadlines.set(loopId, seconds);
@@ -1964,7 +1906,7 @@ class Dispatcher {
1964
1906
  return branchDenial;
1965
1907
  // The joined set is already drained. Awaiting an empty task group completes
1966
1908
  // immediately; it never parks and needs no corrective model turn.
1967
- const finished = await this.#lifecycle.finish(loopId, { status: 200 }, { message: raw === "" ? null : raw });
1909
+ const finished = await this.#lifecycle.finish(loopId, TerminalResult.success(raw));
1968
1910
  return {
1969
1911
  status: finished !== null ? 200 : await this.#lifecycle.status(loopId),
1970
1912
  attrs: { joined: true, pending: 0 },
@@ -2022,7 +1964,7 @@ class Dispatcher {
2022
1964
  const branchDenial = await this.#branchCompletionGate?.(workerId) ?? null;
2023
1965
  if (branchDenial !== null)
2024
1966
  return branchDenial;
2025
- const finished = await this.#lifecycle.finish(loopId, { status: 200 }, { message: raw === "" ? null : raw });
1967
+ const finished = await this.#lifecycle.finish(loopId, TerminalResult.success(raw));
2026
1968
  return _a.#statusResult(finished !== null ? 200 : await this.#lifecycle.status(loopId), "loop-already-terminal", "The loop was already terminal when SEND attempted to conclude it.");
2027
1969
  }
2028
1970
  if (status === 499) {
@@ -2050,7 +1992,20 @@ class Dispatcher {
2050
1992
  }
2051
1993
  async #run(schemeName, statement, ctx) {
2052
1994
  if (schemeName === null) {
2053
- return _a.#failure("target-scheme-required", 400, `${statement.op} requires a target scheme.`, {}, { operation: statement.op, retryable: false });
1995
+ const fields = statement.op === "READ"
1996
+ ? { content: null, mimetype: null, channel: null }
1997
+ : statement.op === "FIND"
1998
+ ? {
1999
+ content: null,
2000
+ mimetype: null,
2001
+ results: [],
2002
+ itemsTokenTotal: 0,
2003
+ returnedItemsTokenTotal: 0,
2004
+ matchingPathCount: 0,
2005
+ matchLocationCount: 0,
2006
+ }
2007
+ : {};
2008
+ return _a.#failure("target-scheme-required", 400, `${statement.op} requires a target scheme.`, fields, { operation: statement.op, retryable: false });
2054
2009
  }
2055
2010
  if (statement.op === "SEND" && statement.signal === 499 && statement.target?.kind === "url") {
2056
2011
  const addressedScheme = statement.target.scheme;
@@ -2084,16 +2039,127 @@ class Dispatcher {
2084
2039
  const manifest = this.#schemes.manifestFor(schemeName);
2085
2040
  if (manifest === undefined)
2086
2041
  throw new Error(`scheme '${schemeName}' has no manifest`);
2087
- const schemeCtx = new SchemeCtxImpl(ctx, addressedScheme ?? schemeName, manifest, this.#liveSubscriptions);
2088
- if (typeof method === "function")
2089
- return Results.assert(await method.call(handler, statement, schemeCtx));
2042
+ const publishedChannel = statement.target?.kind === "url"
2043
+ ? statement.target.fragment ?? manifest.defaultChannel
2044
+ : manifest.defaultChannel;
2045
+ const schemeCtx = new SchemeCtxImpl(ctx, addressedScheme ?? schemeName, manifest, this.#liveSubscriptions, { publishedChannel });
2046
+ if (statement.op === "READ"
2047
+ && handler instanceof CoreSchemeAdapterBase
2048
+ && typeof handler.resolveCoreRepresentation === "function") {
2049
+ const selectionNeutralTarget = statement.target?.kind === "url"
2050
+ ? {
2051
+ ...statement.target,
2052
+ raw: renderTarget({ ...statement.target, fragment: null }) ?? statement.target.raw,
2053
+ fragment: null,
2054
+ }
2055
+ : statement.target;
2056
+ const resolved = await handler
2057
+ .resolveCoreRepresentation(selectionNeutralTarget, schemeCtx);
2058
+ if ("result" in resolved)
2059
+ return Results.assertReadResult(resolved.result);
2060
+ if (selectionNeutralTarget === null) {
2061
+ throw new InvalidOperationResultError(`Core scheme '${schemeName}' resolved a targetless READ representation.`);
2062
+ }
2063
+ const target = renderTarget(selectionNeutralTarget.kind === "url"
2064
+ ? selectionNeutralTarget
2065
+ : { scheme: null, pathname: selectionNeutralTarget.raw });
2066
+ if (target === null) {
2067
+ throw new TypeError(`Core scheme '${schemeName}' resolved an unrenderable READ target.`);
2068
+ }
2069
+ return Results.assertReadResult(await ReadProjector.project({
2070
+ statement,
2071
+ manifest,
2072
+ target,
2073
+ representation: resolved.representation,
2074
+ mimetypes: ctx.mimetypes,
2075
+ }));
2076
+ }
2077
+ if (statement.op === "READ" && manifest.category === "data") {
2078
+ const prepared = statement.target === null
2079
+ ? { address: null, result: null }
2080
+ : await this.#prepareDataRepresentation({
2081
+ target: statement.target,
2082
+ routedScheme: schemeName,
2083
+ handler: handler,
2084
+ manifest,
2085
+ ctx,
2086
+ schemeCtx,
2087
+ publishedChannel,
2088
+ });
2089
+ if (prepared.result !== null)
2090
+ return prepared.result;
2091
+ const resolved = prepared.address;
2092
+ if (statement.target !== null && resolved === null) {
2093
+ const target = renderTarget(statement.target.kind === "url"
2094
+ ? statement.target
2095
+ : { scheme: null, pathname: statement.target.raw });
2096
+ return Results.failure(`scheme:${schemeName}`, "entry-not-found", 404, `No entry exists at ${target ?? "the requested address"}.`, { content: null, mimetype: null, channel: null }, { target });
2097
+ }
2098
+ const storageScheme = resolved?.scheme ?? manifest.storedScheme ?? addressedScheme ?? schemeName;
2099
+ const projected = Results.assertReadResult(await EntryOps.readWorkspaceEntry(statement, ctx, { ...manifest, name: storageScheme, storedScheme: storageScheme }, resolved === null
2100
+ ? {}
2101
+ : {
2102
+ ownerId: resolved.ownerId,
2103
+ pathname: resolved.pathname,
2104
+ }));
2105
+ return projected;
2106
+ }
2090
2107
  if (statement.op !== "FIND" || manifest.category !== "data") {
2108
+ if (typeof method === "function") {
2109
+ return Results.assert(await method.call(handler, statement, schemeCtx));
2110
+ }
2091
2111
  return _a.#failure("operation-not-implemented", 501, `Scheme '${schemeName}' does not implement ${statement.op}.`, {}, {
2092
2112
  scheme: schemeName,
2093
2113
  operation: statement.op,
2094
2114
  retryable: false,
2095
2115
  });
2096
2116
  }
2117
+ const targetPathname = statement.target?.kind === "url"
2118
+ ? statement.target.pathname
2119
+ : statement.target?.raw ?? "";
2120
+ const collectionTarget = manifest.folderScopes === true
2121
+ && (targetPathname === "" || targetPathname.endsWith("/"));
2122
+ const exactTarget = statement.target !== null
2123
+ && !collectionTarget
2124
+ && !PathSyntax.hasGlob(entryPathnameOf(statement.target));
2125
+ if (exactTarget && statement.target !== null) {
2126
+ const prepared = await this.#prepareDataRepresentation({
2127
+ target: statement.target,
2128
+ routedScheme: schemeName,
2129
+ handler: handler,
2130
+ manifest,
2131
+ ctx,
2132
+ schemeCtx,
2133
+ publishedChannel,
2134
+ });
2135
+ if (prepared.result !== null)
2136
+ return prepared.result;
2137
+ if (prepared.address === null) {
2138
+ const target = renderTarget(statement.target.kind === "url"
2139
+ ? statement.target
2140
+ : { scheme: null, pathname: statement.target.raw });
2141
+ return Results.failure(`scheme:${schemeName}`, "entry-not-found", 404, `No entry exists at ${target ?? "the requested address"}.`, {
2142
+ content: null,
2143
+ mimetype: null,
2144
+ results: [],
2145
+ itemsTokenTotal: 0,
2146
+ returnedItemsTokenTotal: 0,
2147
+ matchingPathCount: 0,
2148
+ matchLocationCount: 0,
2149
+ }, { target });
2150
+ }
2151
+ const storageScheme = prepared.address.scheme
2152
+ ?? manifest.storedScheme
2153
+ ?? addressedScheme
2154
+ ?? schemeName;
2155
+ return Results.assert(await EntryFind.findWorkspaceEntries(statement, ctx, { ...manifest, name: storageScheme, storedScheme: storageScheme }, {
2156
+ ownerId: prepared.address.ownerId,
2157
+ pathname: prepared.address.pathname,
2158
+ }));
2159
+ }
2160
+ if (typeof method === "function") {
2161
+ return Results.assert(await method.call(handler, statement, schemeCtx));
2162
+ }
2097
2163
  const prepareFind = handler.prepareFind;
2098
2164
  if (typeof prepareFind === "function") {
2099
2165
  const prepared = await prepareFind.call(handler, statement, schemeCtx);