@plurnk/plurnk-service 1.4.0 → 1.6.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.
- package/.env.defaults +37 -31
- package/INSTALL.md +16 -11
- package/README.md +1 -0
- package/SPEC.md +631 -397
- package/dist/Paths.d.ts.map +1 -1
- package/dist/Paths.js +2 -3
- package/dist/Paths.js.map +1 -1
- package/dist/build-info.json +1 -1
- package/dist/content/edit-collision.d.ts +5 -0
- package/dist/content/edit-collision.d.ts.map +1 -0
- package/dist/content/edit-collision.js +14 -0
- package/dist/content/edit-collision.js.map +1 -0
- package/dist/content/edit-receipt.d.ts +3 -2
- package/dist/content/edit-receipt.d.ts.map +1 -1
- package/dist/content/edit-receipt.js +58 -13
- package/dist/content/edit-receipt.js.map +1 -1
- package/dist/content/index.d.ts +5 -1
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +4 -1
- package/dist/content/index.js.map +1 -1
- package/dist/content/line-anchors.d.ts +41 -0
- package/dist/content/line-anchors.d.ts.map +1 -0
- package/dist/content/line-anchors.js +191 -0
- package/dist/content/line-anchors.js.map +1 -0
- package/dist/content/line-marker.d.ts +7 -3
- package/dist/content/line-marker.d.ts.map +1 -1
- package/dist/content/line-marker.js +2 -1
- package/dist/content/line-marker.js.map +1 -1
- package/dist/content/matcher.d.ts +1 -1
- package/dist/content/matcher.d.ts.map +1 -1
- package/dist/content/matcher.js +2 -2
- package/dist/content/matcher.js.map +1 -1
- package/dist/content/read-projector.d.ts +20 -0
- package/dist/content/read-projector.d.ts.map +1 -0
- package/dist/content/read-projector.js +122 -0
- package/dist/content/read-projector.js.map +1 -0
- package/dist/content/read-resolve.d.ts +2 -7
- package/dist/content/read-resolve.d.ts.map +1 -1
- package/dist/content/read-resolve.js +32 -107
- package/dist/content/read-resolve.js.map +1 -1
- package/dist/core/BranchReceipt.d.ts +0 -1
- package/dist/core/BranchReceipt.d.ts.map +1 -1
- package/dist/core/BranchReceipt.js +0 -3
- package/dist/core/BranchReceipt.js.map +1 -1
- package/dist/core/BudgetReadout.d.ts.map +1 -1
- package/dist/core/BudgetReadout.js +9 -2
- package/dist/core/BudgetReadout.js.map +1 -1
- package/dist/core/ChannelWrite.d.ts +3 -0
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js +3 -3
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/ChannelWrite.sql +4 -3
- package/dist/core/CoreSchemeServices.d.ts +10 -2
- package/dist/core/CoreSchemeServices.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +14 -8
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +656 -312
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/DurableStatement.d.ts.map +1 -1
- package/dist/core/DurableStatement.js +2 -0
- package/dist/core/DurableStatement.js.map +1 -1
- package/dist/core/Engine.d.ts +24 -18
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +620 -392
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +188 -119
- package/dist/core/ExecutableTools.d.ts +10 -0
- package/dist/core/ExecutableTools.d.ts.map +1 -0
- package/dist/core/ExecutableTools.js +41 -0
- package/dist/core/ExecutableTools.js.map +1 -0
- package/dist/core/ExecutorRegistry.d.ts +3 -3
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +2 -2
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/LogBody.d.ts +4 -1
- package/dist/core/LogBody.d.ts.map +1 -1
- package/dist/core/LogBody.js +46 -2
- package/dist/core/LogBody.js.map +1 -1
- package/dist/core/LogEntryProjection.d.ts +14 -0
- package/dist/core/LogEntryProjection.d.ts.map +1 -0
- package/dist/core/LogEntryProjection.js +38 -0
- package/dist/core/LogEntryProjection.js.map +1 -0
- package/dist/core/LoopLifecycle.d.ts +1 -2
- package/dist/core/LoopLifecycle.d.ts.map +1 -1
- package/dist/core/LoopLifecycle.js +1 -4
- package/dist/core/LoopLifecycle.js.map +1 -1
- package/dist/core/LoopLifecycle.sql +1 -4
- package/dist/core/ModelCall.d.ts +27 -0
- package/dist/core/ModelCall.d.ts.map +1 -0
- package/dist/core/ModelCall.js +125 -0
- package/dist/core/ModelCall.js.map +1 -0
- package/dist/core/PacketBuilder.d.ts +13 -12
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +146 -118
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/ProblemLog.d.ts.map +1 -1
- package/dist/core/ProblemLog.js +1 -0
- package/dist/core/ProblemLog.js.map +1 -1
- package/dist/core/ProposalLifecycle.d.ts.map +1 -1
- package/dist/core/ProposalLifecycle.js +3 -4
- package/dist/core/ProposalLifecycle.js.map +1 -1
- package/dist/core/ProviderInstantiate.d.ts.map +1 -1
- package/dist/core/ProviderInstantiate.js +23 -6
- package/dist/core/ProviderInstantiate.js.map +1 -1
- package/dist/core/SchemeRegistry.js +7 -7
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/StrikeRail.d.ts +5 -2
- package/dist/core/StrikeRail.d.ts.map +1 -1
- package/dist/core/StrikeRail.js +10 -7
- package/dist/core/StrikeRail.js.map +1 -1
- package/dist/core/TerminalResult.d.ts +16 -0
- package/dist/core/TerminalResult.d.ts.map +1 -0
- package/dist/core/TerminalResult.js +49 -0
- package/dist/core/TerminalResult.js.map +1 -0
- package/dist/core/caps/CapsResolve.d.ts +5 -1
- package/dist/core/caps/CapsResolve.d.ts.map +1 -1
- package/dist/core/caps/CapsResolve.js +9 -5
- package/dist/core/caps/CapsResolve.js.map +1 -1
- package/dist/core/caps/DbChannelCaps.d.ts +1 -1
- package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
- package/dist/core/caps/DbChannelCaps.js +6 -4
- package/dist/core/caps/DbChannelCaps.js.map +1 -1
- package/dist/core/caps/DbEntryCaps.d.ts +2 -1
- package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
- package/dist/core/caps/DbEntryCaps.js +21 -8
- package/dist/core/caps/DbEntryCaps.js.map +1 -1
- package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
- package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
- package/dist/core/caps/DbNotifyCaps.js +6 -4
- package/dist/core/caps/DbNotifyCaps.js.map +1 -1
- package/dist/core/caps/DbProjectionCaps.d.ts +1 -0
- package/dist/core/caps/DbProjectionCaps.d.ts.map +1 -1
- package/dist/core/caps/DbProjectionCaps.js +29 -0
- package/dist/core/caps/DbProjectionCaps.js.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.d.ts +4 -6
- package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.js +27 -13
- package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.d.ts +11 -3
- package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.js +10 -8
- package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
- package/dist/core/fork.d.ts.map +1 -1
- package/dist/core/fork.js +23 -7
- package/dist/core/fork.js.map +1 -1
- package/dist/core/fork.sql +35 -14
- package/dist/core/git-iso.d.ts +3 -2
- package/dist/core/git-iso.d.ts.map +1 -1
- package/dist/core/git-iso.js +27 -12
- package/dist/core/git-iso.js.map +1 -1
- package/dist/core/git-membership.d.ts +1 -0
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +87 -26
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/git-state.d.ts +8 -1
- package/dist/core/git-state.d.ts.map +1 -1
- package/dist/core/git-state.js +36 -17
- package/dist/core/git-state.js.map +1 -1
- package/dist/core/namespace.d.ts +1 -0
- package/dist/core/namespace.d.ts.map +1 -1
- package/dist/core/namespace.js +20 -0
- package/dist/core/namespace.js.map +1 -1
- package/dist/core/optimistic-settlement.d.ts +2 -0
- package/dist/core/optimistic-settlement.d.ts.map +1 -0
- package/dist/core/optimistic-settlement.js +14 -0
- package/dist/core/optimistic-settlement.js.map +1 -0
- package/dist/core/packet-wire.d.ts +5 -2
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +318 -114
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/provider-accounting.d.ts +24 -0
- package/dist/core/provider-accounting.d.ts.map +1 -0
- package/dist/core/provider-accounting.js +76 -0
- package/dist/core/provider-accounting.js.map +1 -0
- package/dist/core/results.d.ts +4 -2
- package/dist/core/results.d.ts.map +1 -1
- package/dist/core/results.js +6 -0
- package/dist/core/results.js.map +1 -1
- package/dist/core/scheme-types.d.ts +0 -12
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/core/scheme-types.js.map +1 -1
- package/dist/core/search-gate.d.ts +2 -2
- package/dist/core/search-gate.d.ts.map +1 -1
- package/dist/core/search-gate.js +8 -8
- package/dist/core/search-gate.js.map +1 -1
- package/dist/core/token-ruler.js +1 -1
- package/dist/core/token-ruler.js.map +1 -1
- package/dist/core/turn-scheduler.js +1 -1
- package/dist/core/turn-scheduler.js.map +1 -1
- package/dist/core/worker-ops.sql +4 -4
- package/dist/core/workspace-settings.js +1 -1
- package/dist/core/workspace-settings.js.map +1 -1
- package/dist/digest/Digest.d.ts.map +1 -1
- package/dist/digest/Digest.js +349 -76
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/digest.sql +42 -16
- package/dist/schemes/Exec.d.ts +4 -4
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +167 -134
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecOutputScheme.d.ts +4 -5
- package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
- package/dist/schemes/ExecOutputScheme.js +12 -20
- package/dist/schemes/ExecOutputScheme.js.map +1 -1
- package/dist/schemes/File.d.ts +5 -6
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +35 -41
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts +16 -5
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +229 -262
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/Log.sql +50 -27
- package/dist/schemes/Prompt.d.ts +3 -4
- package/dist/schemes/Prompt.d.ts.map +1 -1
- package/dist/schemes/Prompt.js +5 -5
- package/dist/schemes/Prompt.js.map +1 -1
- package/dist/schemes/Skill.d.ts +4 -5
- package/dist/schemes/Skill.d.ts.map +1 -1
- package/dist/schemes/Skill.js +3 -3
- package/dist/schemes/Skill.js.map +1 -1
- package/dist/schemes/Worker.d.ts +7 -7
- package/dist/schemes/Worker.d.ts.map +1 -1
- package/dist/schemes/Worker.js +99 -84
- package/dist/schemes/Worker.js.map +1 -1
- package/dist/schemes/_entry-crud.d.ts +2 -2
- package/dist/schemes/_entry-crud.d.ts.map +1 -1
- package/dist/schemes/_entry-crud.js +29 -10
- package/dist/schemes/_entry-crud.js.map +1 -1
- package/dist/schemes/_entry-crud.sql +11 -17
- package/dist/schemes/_entry-find.d.ts +41 -14
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +159 -166
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-find.sql +2 -21
- package/dist/schemes/_entry-graph.sql +1 -1
- package/dist/schemes/_entry-manifest.d.ts +5 -17
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +28 -24
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.d.ts +13 -8
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +98 -119
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-ops.sql +31 -6
- package/dist/schemes/_entry-semantic.d.ts +5 -7
- package/dist/schemes/_entry-semantic.d.ts.map +1 -1
- package/dist/schemes/_entry-semantic.js +10 -31
- package/dist/schemes/_entry-semantic.js.map +1 -1
- package/dist/schemes/_entry-semantic.sql +2 -2
- package/dist/schemes/_entry-send.js +4 -4
- package/dist/schemes/_entry-send.js.map +1 -1
- package/dist/schemes/_search-index.d.ts.map +1 -1
- package/dist/schemes/_search-index.js +16 -6
- package/dist/schemes/_search-index.js.map +1 -1
- package/dist/schemes/exec-abort.js +2 -2
- package/dist/schemes/exec-abort.js.map +1 -1
- package/dist/server/BranchBatches.d.ts +1 -0
- package/dist/server/BranchBatches.d.ts.map +1 -1
- package/dist/server/BranchBatches.js +1 -0
- package/dist/server/BranchBatches.js.map +1 -1
- package/dist/server/Daemon.d.ts +6 -8
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +238 -68
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/DaemonModule.d.ts +3 -3
- package/dist/server/DaemonModule.d.ts.map +1 -1
- package/dist/server/branch-batch.sql +3 -1
- package/dist/server/client-input.d.ts +2 -1
- package/dist/server/client-input.d.ts.map +1 -1
- package/dist/server/client-input.js +7 -1
- package/dist/server/client-input.js.map +1 -1
- package/dist/server/drain.sql +8 -7
- package/dist/server/envelope.d.ts +0 -2
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js +0 -1
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/envelope.sql +3 -4
- package/dist/server/lifecycle-recovery.sql +44 -1
- package/dist/server/logEntry.d.ts +2 -1
- package/dist/server/logEntry.d.ts.map +1 -1
- package/dist/server/logEntry.js +15 -2
- package/dist/server/logEntry.js.map +1 -1
- package/dist/server/logEntry.sql +5 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +13 -1
- package/dist/service.js.map +1 -1
- package/migrations/001_schema.sql +730 -221
- package/package.json +53 -46
- package/dist/core/BudgetOverflow.d.ts +0 -23
- package/dist/core/BudgetOverflow.d.ts.map +0 -1
- package/dist/core/BudgetOverflow.js +0 -61
- package/dist/core/BudgetOverflow.js.map +0 -1
- package/dist/core/caps/DbTagCaps.d.ts +0 -10
- package/dist/core/caps/DbTagCaps.d.ts.map +0 -1
- package/dist/core/caps/DbTagCaps.js +0 -44
- package/dist/core/caps/DbTagCaps.js.map +0 -1
- package/dist/matrix/live-specimen.d.ts +0 -9
- package/dist/matrix/live-specimen.d.ts.map +0 -1
- package/dist/matrix/live-specimen.js +0 -37
- package/dist/matrix/live-specimen.js.map +0 -1
package/dist/schemes/Log.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { PathSyntax, } from "@plurnk/plurnk-contracts";
|
|
2
|
-
import { LineMarkerOps } from "../content/index.js";
|
|
3
|
-
import { ReadResolve } from "../content/index.js";
|
|
1
|
+
import { InvalidTagSignalError, PathSyntax, TagSignal, } from "@plurnk/plurnk-contracts";
|
|
4
2
|
import Matcher from "../content/matcher.js";
|
|
5
|
-
import {
|
|
3
|
+
import { emptyFindFields, projectFindResult } from "./_entry-find.js";
|
|
6
4
|
import { CoreSchemeAdapterBase } from "../core/CoreSchemeServices.js";
|
|
7
5
|
import Results from "../core/results.js";
|
|
8
6
|
import LogBody from "../core/LogBody.js";
|
|
7
|
+
import LogEntryProjection from "../core/LogEntryProjection.js";
|
|
9
8
|
import EntrySemantic from "./_entry-semantic.js";
|
|
10
9
|
import EntryGraph from "./_entry-graph.js";
|
|
11
10
|
import { resolveSearchCandidates } from "./_search-candidate.js";
|
|
12
11
|
import { pathFolderSummaries, pathScope, pathScopeMatches } from "./_path-scope.js";
|
|
13
12
|
// log:///<loop_seq>/<turn_seq>/<sequence>[/<op>] — the trailing /op segment
|
|
14
|
-
// is
|
|
15
|
-
// parsing accepts it (or omits it)
|
|
16
|
-
// suffix is case-INSENSITIVE: model ops render UPPERCASE (READ/EDIT/FIND)
|
|
17
|
-
// engine-minted
|
|
13
|
+
// is canonical model-facing identity derived from the row's projected `op`;
|
|
14
|
+
// parsing accepts it (or omits it), but a supplied suffix must agree. The op
|
|
15
|
+
// suffix is case-INSENSITIVE: model ops render UPPERCASE (READ/EDIT/FIND) while
|
|
16
|
+
// engine-minted selectors such as `error` are lowercase. An actionless model
|
|
17
|
+
// emission has no suffix at all.
|
|
18
18
|
const COORDINATE = /^(\d+)\/(\d+)\/(\d+)(?:\/([A-Za-z]+))?$/;
|
|
19
19
|
// {§log-coordinate-hierarchy} — a log coordinate is a HIERARCHICAL PREFIX: `1` selects loop 1's rows,
|
|
20
20
|
// `1/2` turn 1/2's rows, `1/2/3` the one row. A full coordinate is always 3 parts, so a 1- or 2-part
|
|
@@ -42,18 +42,25 @@ const parseCoordinate = (pathname) => {
|
|
|
42
42
|
loopSeq: Number(match[1]),
|
|
43
43
|
turnSeq: Number(match[2]),
|
|
44
44
|
sequence: Number(match[3]),
|
|
45
|
+
op: match[4] ?? null,
|
|
45
46
|
};
|
|
46
47
|
};
|
|
47
|
-
const canonicalCoordinate = (coordinate) => coordinate.replace(/\/[A-Za-z]+$/, "");
|
|
48
48
|
// A rendered log path appends `/OP` to the canonical loop/turn/item coordinate
|
|
49
|
-
// as
|
|
49
|
+
// as identity. Selection honors both views: ordinary path globs map
|
|
50
50
|
// the three-level resource tree, while an explicit OP segment can still filter
|
|
51
51
|
// rows (`log:///**/READ`).
|
|
52
|
-
const coordinateScopeMatches = (scope, coordinate) => pathScopeMatches(scope, coordinate) || pathScopeMatches(scope,
|
|
52
|
+
const coordinateScopeMatches = (scope, coordinate) => pathScopeMatches(scope, coordinate) || pathScopeMatches(scope, LogEntryProjection.base(coordinate));
|
|
53
|
+
const projectedCoordinateRows = (rows) => rows.map((row) => ({
|
|
54
|
+
...row,
|
|
55
|
+
coordinate: LogEntryProjection.coordinate(row.coordinate, row),
|
|
56
|
+
}));
|
|
57
|
+
const coordinateCandidatePrefix = (prefix) => prefix === null
|
|
58
|
+
? null
|
|
59
|
+
: LogEntryProjection.base(prefix);
|
|
53
60
|
export default class Log extends CoreSchemeAdapterBase {
|
|
54
61
|
static manifest = {
|
|
55
62
|
name: "log",
|
|
56
|
-
channels: {},
|
|
63
|
+
channels: {},
|
|
57
64
|
defaultChannel: "",
|
|
58
65
|
category: "logging",
|
|
59
66
|
// Log KILL is model-authorized curation. Other mutations remain unavailable
|
|
@@ -62,27 +69,21 @@ export default class Log extends CoreSchemeAdapterBase {
|
|
|
62
69
|
volatile: false,
|
|
63
70
|
modelVisible: true,
|
|
64
71
|
folderScopes: true,
|
|
65
|
-
example: "
|
|
72
|
+
example: "## READ0 (log:///1/2/3)",
|
|
66
73
|
};
|
|
67
|
-
async
|
|
74
|
+
async resolveCoreRepresentation(target, ctx) {
|
|
68
75
|
const core = this.coreContext(ctx);
|
|
69
76
|
const { db, workerId } = core;
|
|
70
|
-
const failure = (code, status, detail, extensions = {}) =>
|
|
71
|
-
|
|
77
|
+
const failure = (code, status, detail, extensions = {}) => ({
|
|
78
|
+
result: Results.failure("scheme:log", code, status, detail, { content: null, mimetype: null, channel: null }, extensions),
|
|
79
|
+
});
|
|
80
|
+
if (target === null) {
|
|
72
81
|
return failure("read-target-required", 400, "READ requires a log coordinate.", {
|
|
73
82
|
recovery: "Provide one exact log coordinate.",
|
|
74
83
|
retryable: false,
|
|
75
84
|
});
|
|
76
85
|
}
|
|
77
|
-
|
|
78
|
-
// not a filter on a single-row read.
|
|
79
|
-
if (Array.isArray(statement.signal) && statement.signal.length > 0) {
|
|
80
|
-
return failure("tagged-coordinate-not-found", 404, "The exact log row does not carry every requested tag.", {
|
|
81
|
-
recovery: "Remove the tag filter or use a log row carrying those tags.",
|
|
82
|
-
retryable: false,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
const pathname = (statement.target.kind === "url" ? statement.target.pathname : statement.target.raw).replace(/^\//, "");
|
|
86
|
+
const pathname = (target.kind === "url" ? target.pathname : target.raw).replace(/^\//, "");
|
|
86
87
|
const coord = parseCoordinate(pathname);
|
|
87
88
|
if (coord === null) {
|
|
88
89
|
return failure("coordinate-malformed", 400, `The log coordinate '${pathname}' is malformed.`, {
|
|
@@ -94,67 +95,45 @@ export default class Log extends CoreSchemeAdapterBase {
|
|
|
94
95
|
const row = await db.log_read_by_coordinate.get({ worker_id: workerId, loop_seq: coord.loopSeq, turn_seq: coord.turnSeq, sequence: coord.sequence });
|
|
95
96
|
if (row === undefined)
|
|
96
97
|
return failure("entry-not-found", 404, `No log entry exists at log:///${pathname}.`);
|
|
98
|
+
if (!LogEntryProjection.accepts(coord.op, row)) {
|
|
99
|
+
return failure("entry-not-found", 404, `No log entry exists at log:///${pathname}.`);
|
|
100
|
+
}
|
|
97
101
|
const { content: underlyingContent, mimetype: underlyingMimetype } = LogBody.resolve({
|
|
98
102
|
op: row.op,
|
|
103
|
+
attrs: row.attrs,
|
|
99
104
|
tx: row.tx,
|
|
100
105
|
rx: row.rx,
|
|
101
106
|
mimetypeTx: row.mimetype_tx,
|
|
102
107
|
mimetypeRx: row.mimetype_rx,
|
|
103
108
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
content: null,
|
|
116
|
-
mimetype: null,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
if (resolved.reason === undefined) {
|
|
120
|
-
throw new Error(`Log.read: ReadResolve returned status ${resolved.status} without Problem Details or a diagnostic`);
|
|
121
|
-
}
|
|
122
|
-
return failure(resolved.status === 416
|
|
123
|
-
? "range-not-satisfiable"
|
|
124
|
-
: resolved.status === 501
|
|
125
|
-
? "matcher-unavailable"
|
|
126
|
-
: "read-resolution-failed", resolved.status, resolved.reason, {
|
|
127
|
-
...(resolved.range === undefined ? {} : { range: resolved.range, stage: "projection" }),
|
|
128
|
-
...(statement.body === null || resolved.status === 416
|
|
129
|
-
? {}
|
|
130
|
-
: {
|
|
131
|
-
stage: "matcher",
|
|
132
|
-
dialect: statement.body.dialect,
|
|
133
|
-
recovery: resolved.status === 501
|
|
134
|
-
? "Retry the READ without a content matcher."
|
|
135
|
-
: "Correct or remove the matcher.",
|
|
136
|
-
retryable: false,
|
|
137
|
-
}),
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
return { ...resolved };
|
|
109
|
+
return {
|
|
110
|
+
representation: {
|
|
111
|
+
channels: {
|
|
112
|
+
"": {
|
|
113
|
+
content: underlyingContent,
|
|
114
|
+
mimetype: underlyingMimetype,
|
|
115
|
+
state: "static",
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
};
|
|
141
120
|
}
|
|
142
121
|
// {§log-uniform-query} — FIND over the worker's log rows, on the SAME source-agnostic primitive
|
|
143
122
|
// every entry scheme runs (Matcher.matchCandidates, {§find-source-agnostic}): candidates are the
|
|
144
123
|
// coordinate-scoped rows resolved by LogBody exactly as READ shows them, so every content
|
|
145
|
-
// dialect works on log BY CONSTRUCTION and FIND
|
|
124
|
+
// dialect works on log BY CONSTRUCTION and log FIND -> coordinate READ composes like any scheme.
|
|
146
125
|
async find(statement, ctx) {
|
|
147
|
-
return this.#find(statement, this.coreContext(ctx)
|
|
126
|
+
return this.#find(statement, this.coreContext(ctx));
|
|
148
127
|
}
|
|
149
|
-
async #find(statement, core,
|
|
128
|
+
async #find(statement, core, allowTargetless = false, filterTags = []) {
|
|
150
129
|
const { db, workerId, mimetypes } = core;
|
|
151
130
|
const empty = (status, detail, extensions = {}) => {
|
|
152
|
-
const fields =
|
|
131
|
+
const fields = emptyFindFields();
|
|
153
132
|
if (status >= 400 && detail === undefined) {
|
|
154
133
|
throw new Error(`Log.find: selection returned status ${status} without Problem Details or a diagnostic`);
|
|
155
134
|
}
|
|
156
135
|
return status >= 400
|
|
157
|
-
? Results.failure("scheme:log", status === 416 ? "range-not-satisfiable" : status === 501 ? "matcher-not-implemented" : "find-failed", status, detail, fields, extensions)
|
|
136
|
+
? Results.failure("scheme:log", status === 404 ? "entry-not-found" : status === 416 ? "range-not-satisfiable" : status === 501 ? "matcher-not-implemented" : "find-failed", status, detail, fields, extensions)
|
|
158
137
|
: { status, ...fields };
|
|
159
138
|
};
|
|
160
139
|
if (statement.target === null && !allowTargetless) {
|
|
@@ -175,12 +154,18 @@ export default class Log extends CoreSchemeAdapterBase {
|
|
|
175
154
|
});
|
|
176
155
|
}
|
|
177
156
|
const scope = pathScope(glob, false);
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
157
|
+
if (scope.kind === "exact") {
|
|
158
|
+
const coordinate = parseCoordinate(scope.pathname);
|
|
159
|
+
if (coordinate === null)
|
|
160
|
+
throw new Error(`Exact log scope has a malformed coordinate ${JSON.stringify(scope.pathname)}`);
|
|
161
|
+
if (await this.#resolveExactId(coordinate, core) === null) {
|
|
162
|
+
return empty(404, `No log entry exists at ${statement.target?.raw ?? `log:///${scope.pathname}`}.`, { target: statement.target?.raw ?? `log:///${scope.pathname}` });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const storedCandidateRows = filterTags.length > 0
|
|
166
|
+
? await db.log_curation_find_candidates_tagged.all({ worker_id: workerId, scope_prefix: coordinateCandidatePrefix(scope.candidatePrefix), tags: JSON.stringify(filterTags) })
|
|
167
|
+
: await db.log_find_candidates.all({ worker_id: workerId, scope_prefix: coordinateCandidatePrefix(scope.candidatePrefix) });
|
|
168
|
+
const candidateRows = projectedCoordinateRows(storedCandidateRows);
|
|
184
169
|
let rows;
|
|
185
170
|
try {
|
|
186
171
|
rows = candidateRows.filter((row) => coordinateScopeMatches(scope, row.coordinate));
|
|
@@ -192,12 +177,13 @@ export default class Log extends CoreSchemeAdapterBase {
|
|
|
192
177
|
retryable: false,
|
|
193
178
|
});
|
|
194
179
|
}
|
|
195
|
-
const candidateByCanonicalCoord = new Map(candidateRows.map((row) => [
|
|
180
|
+
const candidateByCanonicalCoord = new Map(candidateRows.map((row) => [LogEntryProjection.base(row.coordinate), row]));
|
|
196
181
|
const byCoord = new Map(rows.map((r) => [r.coordinate, r]));
|
|
197
182
|
const projected = rows.map((r) => ({
|
|
198
183
|
key: r.coordinate,
|
|
199
184
|
...LogBody.resolve({
|
|
200
185
|
op: r.op,
|
|
186
|
+
attrs: r.attrs,
|
|
201
187
|
tx: r.tx,
|
|
202
188
|
rx: r.rx,
|
|
203
189
|
mimetypeTx: r.mimetype_tx,
|
|
@@ -234,26 +220,7 @@ export default class Log extends CoreSchemeAdapterBase {
|
|
|
234
220
|
retryable: false,
|
|
235
221
|
});
|
|
236
222
|
}
|
|
237
|
-
|
|
238
|
-
if (selection.page !== null) {
|
|
239
|
-
const page = LineMarkerOps.page(selected, selection.page);
|
|
240
|
-
if (page.status !== 200) {
|
|
241
|
-
if (page.problem === undefined)
|
|
242
|
-
throw new Error("Log semantic FIND pagination failed without Problem Details");
|
|
243
|
-
return Results.assert({
|
|
244
|
-
status: page.status,
|
|
245
|
-
problem: page.problem,
|
|
246
|
-
content: null,
|
|
247
|
-
mimetype: null,
|
|
248
|
-
results: [],
|
|
249
|
-
itemsTokenTotal: 0,
|
|
250
|
-
pathnames: [],
|
|
251
|
-
matches: [],
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
selected = page.items ?? [];
|
|
255
|
-
}
|
|
256
|
-
const sourceMatches = selected.map(({ key, lineStart, lineEnd }) => ({
|
|
223
|
+
const sourceMatches = ranked.results.map(({ key, lineStart, lineEnd }) => ({
|
|
257
224
|
key,
|
|
258
225
|
span: { lineStart, lineEnd },
|
|
259
226
|
}));
|
|
@@ -269,7 +236,7 @@ export default class Log extends CoreSchemeAdapterBase {
|
|
|
269
236
|
recovery: "Wait for search indexing to complete before repeating the search.",
|
|
270
237
|
retryable: false,
|
|
271
238
|
});
|
|
272
|
-
const universeRows = await db.log_find_candidates.all({ worker_id: workerId, scope_prefix: null });
|
|
239
|
+
const universeRows = projectedCoordinateRows(await db.log_find_candidates.all({ worker_id: workerId, scope_prefix: null }));
|
|
273
240
|
const universe = resolveSearchCandidates(universeRows.map(({ coordinate, deep_hash }) => ({ key: coordinate, deepHash: deep_hash })));
|
|
274
241
|
if (universe.state === "incomplete")
|
|
275
242
|
return empty(503, `The persistent search index covers ${universe.indexed} of ${universe.total} log results in the relationship universe.`, {
|
|
@@ -313,43 +280,15 @@ export default class Log extends CoreSchemeAdapterBase {
|
|
|
313
280
|
return Results.assert({
|
|
314
281
|
status: r.status,
|
|
315
282
|
problem: r.problem,
|
|
316
|
-
|
|
317
|
-
mimetype: null,
|
|
318
|
-
results: [],
|
|
319
|
-
itemsTokenTotal: 0,
|
|
320
|
-
pathnames: [],
|
|
321
|
-
matches: [],
|
|
283
|
+
...emptyFindFields(),
|
|
322
284
|
});
|
|
323
285
|
}
|
|
324
286
|
matches = r.matches.map(({ key, matches: ranges }) => ({ pathname: key, matches: ranges }));
|
|
325
287
|
}
|
|
326
288
|
const folderSummaries = statement.body === null
|
|
327
|
-
? pathFolderSummaries(scope, candidateRows.map((row) =>
|
|
289
|
+
? pathFolderSummaries(scope, candidateRows.map((row) => LogEntryProjection.base(row.coordinate)))
|
|
328
290
|
: [];
|
|
329
|
-
|
|
330
|
-
const page = LineMarkerOps.page(matches, statement.lineMarker);
|
|
331
|
-
if (page.status !== 200) {
|
|
332
|
-
if (page.problem === undefined)
|
|
333
|
-
throw new Error("Log FIND pagination failed without Problem Details");
|
|
334
|
-
return Results.assert({
|
|
335
|
-
status: page.status,
|
|
336
|
-
problem: page.problem,
|
|
337
|
-
content: null,
|
|
338
|
-
mimetype: null,
|
|
339
|
-
results: [],
|
|
340
|
-
itemsTokenTotal: 0,
|
|
341
|
-
pathnames: [],
|
|
342
|
-
matches: [],
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
matches = page.items ?? [];
|
|
346
|
-
}
|
|
347
|
-
// The result rows mirror the catalog-row shape: one item per selected
|
|
348
|
-
// resource, with optional match coordinates for a surgical READ.
|
|
349
|
-
let results = [];
|
|
350
|
-
const seenPath = [];
|
|
351
|
-
const seen = new Set();
|
|
352
|
-
let itemsTokenTotal = 0;
|
|
291
|
+
const resources = [];
|
|
353
292
|
for (const m of matches) {
|
|
354
293
|
const row = byCoord.get(m.pathname);
|
|
355
294
|
if (row === undefined)
|
|
@@ -357,22 +296,28 @@ export default class Log extends CoreSchemeAdapterBase {
|
|
|
357
296
|
const path = `log:///${m.pathname}`;
|
|
358
297
|
const proj = LogBody.resolve({
|
|
359
298
|
op: row.op,
|
|
299
|
+
attrs: row.attrs,
|
|
360
300
|
tx: row.tx,
|
|
361
301
|
rx: row.rx,
|
|
362
302
|
mimetypeTx: row.mimetype_tx,
|
|
363
303
|
mimetypeRx: row.mimetype_rx,
|
|
364
304
|
});
|
|
365
|
-
const
|
|
305
|
+
const channel = {
|
|
366
306
|
path,
|
|
367
|
-
|
|
307
|
+
mimetype: proj.mimetype,
|
|
308
|
+
tokens: row.tokens,
|
|
309
|
+
lines: proj.content.length === 0 ? 0 : proj.content.split("\n").length,
|
|
368
310
|
};
|
|
369
|
-
|
|
370
|
-
if (!
|
|
371
|
-
|
|
372
|
-
seenPath.push(m.pathname);
|
|
373
|
-
itemsTokenTotal += row.tokens;
|
|
311
|
+
const storedTags = JSON.parse(row.tags);
|
|
312
|
+
if (!Array.isArray(storedTags) || !storedTags.every((tag) => typeof tag === "string" && tag.length > 0)) {
|
|
313
|
+
throw new TypeError(`Log row ${m.pathname} contains invalid tag storage.`);
|
|
374
314
|
}
|
|
315
|
+
if (storedTags.length > 0)
|
|
316
|
+
channel.tags = storedTags;
|
|
317
|
+
const item = [channel];
|
|
318
|
+
resources.push({ item, match: m });
|
|
375
319
|
}
|
|
320
|
+
const scopes = [];
|
|
376
321
|
for (const folder of folderSummaries) {
|
|
377
322
|
const members = folder.pathnames.map((coordinate) => candidateByCanonicalCoord.get(coordinate)).filter((row) => row !== undefined);
|
|
378
323
|
const item = {
|
|
@@ -380,83 +325,56 @@ export default class Log extends CoreSchemeAdapterBase {
|
|
|
380
325
|
items: members.length,
|
|
381
326
|
tokens: members.reduce((sum, row) => sum + row.tokens, 0),
|
|
382
327
|
};
|
|
383
|
-
|
|
384
|
-
itemsTokenTotal += item.tokens;
|
|
328
|
+
scopes.push(item);
|
|
385
329
|
}
|
|
386
|
-
|
|
387
|
-
results.sort((a, b) => a.path.localeCompare(b.path));
|
|
388
|
-
if (statement.lineMarker !== null) {
|
|
389
|
-
const page = LineMarkerOps.page(results, statement.lineMarker);
|
|
390
|
-
if (page.status !== 200) {
|
|
391
|
-
if (page.problem === undefined)
|
|
392
|
-
throw new Error("Log FIND pagination failed without Problem Details");
|
|
393
|
-
return Results.assert({
|
|
394
|
-
status: page.status,
|
|
395
|
-
problem: page.problem,
|
|
396
|
-
content: null,
|
|
397
|
-
mimetype: null,
|
|
398
|
-
results: [],
|
|
399
|
-
itemsTokenTotal: 0,
|
|
400
|
-
pathnames: [],
|
|
401
|
-
matches: [],
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
results = page.items ?? [];
|
|
405
|
-
const retained = new Set(results.filter((item) => item.items === undefined).map((item) => item.path.replace(/^log:\/\/\//, "").replace(/^\//, "")));
|
|
406
|
-
matches = matches.filter((match) => retained.has(match.pathname));
|
|
407
|
-
seenPath.splice(0, seenPath.length, ...matches.map((match) => match.pathname).filter((coordinate, i, all) => all.indexOf(coordinate) === i));
|
|
408
|
-
itemsTokenTotal = results.reduce((sum, item) => sum + (item.items === undefined
|
|
409
|
-
? Object.values(item.channels).reduce((channelSum, channel) => channelSum + channel.tokens, 0)
|
|
410
|
-
: item.tokens ?? 0), 0);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
if (results.length === 0)
|
|
414
|
-
return empty(204);
|
|
415
|
-
// {§find-count-not-contents} is source-agnostic. Count the rendered
|
|
416
|
-
// catalog items; a shallow map's folder summaries replace, rather than
|
|
417
|
-
// conceal, their descendants.
|
|
418
|
-
const budget = Number.parseInt(process.env.PLURNK_SERVICE_FIND_MAX_MATCHES ?? "0", 10);
|
|
419
|
-
if (enforceRenderBudget && budget > 0 && results.length > budget) {
|
|
420
|
-
const noun = results.some((item) => item.items !== undefined) ? "catalog items" : "entries";
|
|
421
|
-
return {
|
|
422
|
-
status: 200,
|
|
423
|
-
content: `${results.length} ${noun} match, exceeding the render budget (${budget}) — not enumerated.`,
|
|
424
|
-
mimetype: "text/markdown",
|
|
425
|
-
results: [],
|
|
426
|
-
itemsTokenTotal,
|
|
427
|
-
pathnames: [],
|
|
428
|
-
matches: [],
|
|
429
|
-
omittedItems: results.length,
|
|
430
|
-
maximumItems: budget,
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
// matches[].pathname is the fan-out's retarget key — `/loop/turn/seq/OP` re-parses as a
|
|
434
|
-
// coordinate (the /OP suffix is accepted), so READ(log://)<matcher> fan-out delivers rows.
|
|
435
|
-
const fanMatches = matches.map((m) => ({ ...m, pathname: `/${m.pathname}` }));
|
|
436
|
-
return { status: 200, content: renderFindContent(results), mimetype: "application/json", results, itemsTokenTotal, pathnames: seenPath.map((p) => `/${p}`), matches: fanMatches };
|
|
330
|
+
return projectFindResult(statement, scope, resources, scopes);
|
|
437
331
|
}
|
|
438
332
|
async open(statement, ctx) {
|
|
439
|
-
|
|
333
|
+
const core = this.coreContext(ctx);
|
|
334
|
+
const outcome = await this.#planCuration(statement, core, 1);
|
|
335
|
+
if (outcome.plan !== null)
|
|
336
|
+
await this.#applyDirect(outcome.plan, core);
|
|
337
|
+
return outcome.result;
|
|
440
338
|
}
|
|
441
339
|
// FOLD toggles the expanded bit only — an active subscription stays alive. {§subscriptions-fold-keeps-subscription}
|
|
442
340
|
async fold(statement, ctx) {
|
|
443
|
-
|
|
341
|
+
const core = this.coreContext(ctx);
|
|
342
|
+
const outcome = await this.#planCuration(statement, core, 0);
|
|
343
|
+
if (outcome.plan !== null)
|
|
344
|
+
await this.#applyDirect(outcome.plan, core);
|
|
345
|
+
return outcome.result;
|
|
346
|
+
}
|
|
347
|
+
// Core dispatch retains the exact landed effect beside the suppressed
|
|
348
|
+
// OPEN/FOLD event. Direct scheme callers receive the ordinary result above.
|
|
349
|
+
async curate(statement, ctx) {
|
|
350
|
+
return this.#planCuration(statement, this.coreContext(ctx), statement.op === "OPEN" ? 1 : 0);
|
|
444
351
|
}
|
|
445
352
|
// Resolve a log:/// target — a concrete coordinate or path-glob — to the matched row ids.
|
|
446
353
|
// OPEN/FOLD/KILL share this one path selection; log curation has no positional pagination.
|
|
354
|
+
async #resolveExactId(coordinate, ctx) {
|
|
355
|
+
const row = await ctx.db.log_id_by_coordinate.get({
|
|
356
|
+
worker_id: ctx.workerId,
|
|
357
|
+
loop_seq: coordinate.loopSeq,
|
|
358
|
+
turn_seq: coordinate.turnSeq,
|
|
359
|
+
sequence: coordinate.sequence,
|
|
360
|
+
});
|
|
361
|
+
return row !== undefined && LogEntryProjection.accepts(coordinate.op, row) ? row.id : null;
|
|
362
|
+
}
|
|
447
363
|
async #resolveIds(pathname, ctx) {
|
|
448
364
|
const { db, workerId } = ctx;
|
|
449
365
|
const coord = parseCoordinate(pathname);
|
|
450
366
|
if (coord !== null) {
|
|
451
|
-
const
|
|
452
|
-
return
|
|
367
|
+
const id = await this.#resolveExactId(coord, ctx);
|
|
368
|
+
return id === null
|
|
369
|
+
? { status: 404, ids: [] }
|
|
370
|
+
: { status: 200, ids: [id] };
|
|
453
371
|
}
|
|
454
372
|
// {§log-coordinate-hierarchy} — one resolution for every consumer (curation here, find below).
|
|
455
373
|
const glob = coordinateGlob(pathname);
|
|
456
374
|
if (glob === null)
|
|
457
375
|
return { status: 400, ids: [], error: `The log target '${pathname}' is malformed.` };
|
|
458
376
|
const scope = pathScope(glob, false);
|
|
459
|
-
const candidates = await db.log_match_coordinates.all({ worker_id: workerId, scope_prefix: scope.candidatePrefix });
|
|
377
|
+
const candidates = projectedCoordinateRows(await db.log_match_coordinates.all({ worker_id: workerId, scope_prefix: coordinateCandidatePrefix(scope.candidatePrefix) }));
|
|
460
378
|
let matched;
|
|
461
379
|
try {
|
|
462
380
|
matched = candidates.filter((row) => coordinateScopeMatches(scope, row.coordinate));
|
|
@@ -470,8 +388,8 @@ export default class Log extends CoreSchemeAdapterBase {
|
|
|
470
388
|
return { status: 204, ids: [] };
|
|
471
389
|
return { status: 200, ids: matched.map((row) => row.id) };
|
|
472
390
|
}
|
|
473
|
-
// {§log-
|
|
474
|
-
// whole worker log when targetless
|
|
391
|
+
// {§log-item-tags} — resolve tagged OPEN/FOLD to ids: candidates are the target's glob scope (the
|
|
392
|
+
// whole worker log when targetless),
|
|
475
393
|
// AND-filtered to rows carrying EVERY listed tag. Zero matches is a no-op success (204), mirroring
|
|
476
394
|
// #resolveIds — recalling a name that tags nothing steers nothing.
|
|
477
395
|
async #resolveByTags(statement, tags, ctx) {
|
|
@@ -480,12 +398,16 @@ export default class Log extends CoreSchemeAdapterBase {
|
|
|
480
398
|
const glob = coordinateGlob(pathname);
|
|
481
399
|
if (glob === null)
|
|
482
400
|
return { status: 400, ids: [], error: `The log target '${pathname}' is malformed.` };
|
|
401
|
+
const coordinate = parseCoordinate(pathname);
|
|
402
|
+
if (coordinate !== null && await this.#resolveExactId(coordinate, ctx) === null) {
|
|
403
|
+
return { status: 404, ids: [] };
|
|
404
|
+
}
|
|
483
405
|
const scope = pathScope(glob, false);
|
|
484
|
-
const candidates = await db.log_match_coordinates_tagged.all({
|
|
406
|
+
const candidates = projectedCoordinateRows(await db.log_match_coordinates_tagged.all({
|
|
485
407
|
worker_id: workerId,
|
|
486
|
-
scope_prefix: scope.candidatePrefix,
|
|
408
|
+
scope_prefix: coordinateCandidatePrefix(scope.candidatePrefix),
|
|
487
409
|
tags: JSON.stringify(tags),
|
|
488
|
-
});
|
|
410
|
+
}));
|
|
489
411
|
let matched;
|
|
490
412
|
try {
|
|
491
413
|
matched = candidates.filter((row) => coordinateScopeMatches(scope, row.coordinate));
|
|
@@ -498,100 +420,145 @@ export default class Log extends CoreSchemeAdapterBase {
|
|
|
498
420
|
return { status: 200, ids: matched.map((row) => row.id) };
|
|
499
421
|
}
|
|
500
422
|
// A matcher-bearing curation op reuses FIND's complete source-agnostic selector with
|
|
501
|
-
//
|
|
502
|
-
//
|
|
423
|
+
// explicit all-results pagination. Curation tags are an independent selector;
|
|
424
|
+
// an authored FIND signal classifies its result row and never enters candidate selection.
|
|
503
425
|
async #resolveByMatcher(statement, filterTags, ctx) {
|
|
504
426
|
const selected = await this.#find({
|
|
505
427
|
...statement,
|
|
506
428
|
op: "FIND",
|
|
507
|
-
signal:
|
|
508
|
-
lineMarker:
|
|
509
|
-
}, ctx,
|
|
429
|
+
signal: null,
|
|
430
|
+
lineMarker: { marks: [1, -1] },
|
|
431
|
+
}, ctx, true, filterTags);
|
|
510
432
|
if (selected.status !== 200)
|
|
511
433
|
return { status: selected.status, ids: [], problem: selected.problem };
|
|
434
|
+
let selectedPaths = selected.results.flatMap((item) => Array.isArray(item) && typeof item[0]?.path === "string"
|
|
435
|
+
? [item[0].path]
|
|
436
|
+
: []);
|
|
437
|
+
if (selectedPaths.length === 0) {
|
|
438
|
+
if (statement.target === null) {
|
|
439
|
+
throw new Error("A successful broad log matcher selection returned no resource paths");
|
|
440
|
+
}
|
|
441
|
+
const pathname = (statement.target.kind === "url" ? statement.target.pathname : statement.target.raw).replace(/^\//, "");
|
|
442
|
+
const glob = coordinateGlob(pathname);
|
|
443
|
+
if (glob === null)
|
|
444
|
+
throw new Error(`Log matcher selected malformed target ${JSON.stringify(pathname)}`);
|
|
445
|
+
const scope = pathScope(glob, false);
|
|
446
|
+
if (scope.kind !== "exact") {
|
|
447
|
+
throw new Error("A successful broad log matcher selection returned no resource paths");
|
|
448
|
+
}
|
|
449
|
+
selectedPaths = [scope.pathname];
|
|
450
|
+
}
|
|
512
451
|
const ids = [];
|
|
513
|
-
for (const
|
|
514
|
-
const coordinate = parseCoordinate(pathname.replace(/^\//, ""));
|
|
452
|
+
for (const pathname of selectedPaths) {
|
|
453
|
+
const coordinate = parseCoordinate(pathname.replace(/^log:\/\/\//, "").replace(/^\//, ""));
|
|
515
454
|
if (coordinate === null)
|
|
516
455
|
throw new Error(`Log matcher selected malformed coordinate '${pathname}'`);
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
loop_seq: coordinate.loopSeq,
|
|
520
|
-
turn_seq: coordinate.turnSeq,
|
|
521
|
-
sequence: coordinate.sequence,
|
|
522
|
-
});
|
|
523
|
-
if (row === undefined)
|
|
456
|
+
const id = await this.#resolveExactId(coordinate, ctx);
|
|
457
|
+
if (id === null)
|
|
524
458
|
return { status: 404, ids: [] };
|
|
525
|
-
ids.push(
|
|
459
|
+
ids.push(id);
|
|
526
460
|
}
|
|
527
461
|
return ids.length === 0 ? { status: 204, ids: [] } : { status: 200, ids };
|
|
528
462
|
}
|
|
529
|
-
async #
|
|
530
|
-
for (const id of
|
|
531
|
-
await ctx.db.log_set_expanded_by_id.run({ id, expanded });
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
463
|
+
async #applyDirect(plan, ctx) {
|
|
464
|
+
for (const id of plan.targetLogEntryIds) {
|
|
465
|
+
await ctx.db.log_set_expanded_by_id.run({ id, expanded: plan.expanded });
|
|
466
|
+
for (const tag of plan.remove)
|
|
467
|
+
await ctx.db.log_remove_tag.run({ log_entry_id: id, tag });
|
|
468
|
+
for (const tag of plan.add)
|
|
469
|
+
await ctx.db.log_write_tag.run({ log_entry_id: id, tag });
|
|
536
470
|
}
|
|
537
|
-
return { status: 200, matched: ids.length };
|
|
538
471
|
}
|
|
539
|
-
async #
|
|
540
|
-
|
|
472
|
+
async #planCuration(statement, ctx, expanded) {
|
|
473
|
+
let tags;
|
|
474
|
+
try {
|
|
475
|
+
tags = TagSignal.curation(statement.signal);
|
|
476
|
+
}
|
|
477
|
+
catch (cause) {
|
|
478
|
+
if (!(cause instanceof InvalidTagSignalError))
|
|
479
|
+
throw cause;
|
|
480
|
+
return {
|
|
481
|
+
result: Results.failure("scheme:log", "tag-signal-invalid", 400, cause.message, {}, { retryable: false }),
|
|
482
|
+
plan: null,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
const planned = (ids) => ({
|
|
486
|
+
result: { status: 200, matched: ids.length },
|
|
487
|
+
plan: {
|
|
488
|
+
targetLogEntryIds: ids,
|
|
489
|
+
expanded,
|
|
490
|
+
add: tags.add,
|
|
491
|
+
remove: tags.remove,
|
|
492
|
+
},
|
|
493
|
+
});
|
|
541
494
|
if (statement.body !== null) {
|
|
542
|
-
const matched = await this.#resolveByMatcher(statement,
|
|
495
|
+
const matched = await this.#resolveByMatcher(statement, tags.filter, ctx);
|
|
543
496
|
if (matched.status === 204)
|
|
544
|
-
return { status: 204, matched: 0 };
|
|
497
|
+
return { result: { status: 204, matched: 0 }, plan: null };
|
|
545
498
|
if (matched.status !== 200) {
|
|
546
499
|
if (matched.problem !== undefined) {
|
|
547
|
-
return
|
|
500
|
+
return {
|
|
501
|
+
result: Results.assert({ status: matched.status, problem: matched.problem }),
|
|
502
|
+
plan: null,
|
|
503
|
+
};
|
|
548
504
|
}
|
|
549
|
-
return
|
|
505
|
+
return {
|
|
506
|
+
result: Results.failure("scheme:log", matched.status === 404 ? "entry-not-found" : "curation-failed", matched.status, "No log entry matches the requested selection."),
|
|
507
|
+
plan: null,
|
|
508
|
+
};
|
|
550
509
|
}
|
|
551
|
-
return
|
|
510
|
+
return planned(matched.ids);
|
|
552
511
|
}
|
|
553
|
-
// {§log-
|
|
554
|
-
//
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
512
|
+
// {§log-item-tags} — unsigned terms are the symmetric ALL-tags selector;
|
|
513
|
+
// signed terms are changes applied only after the exact set is resolved.
|
|
514
|
+
if (tags.filter.length > 0) {
|
|
515
|
+
const selected = await this.#resolveByTags(statement, [...tags.filter], ctx);
|
|
516
|
+
if (selected.status === 204)
|
|
517
|
+
return { result: { status: 204, matched: 0 }, plan: null };
|
|
518
|
+
if (selected.status !== 200) {
|
|
519
|
+
return {
|
|
520
|
+
result: Results.failure("scheme:log", "curation-failed", selected.status, selected.error ?? "No log entry matches the requested selection.", {}, {
|
|
521
|
+
target: statement.target?.raw ?? null,
|
|
522
|
+
...(selected.status === 400
|
|
523
|
+
? {
|
|
524
|
+
recovery: "Use a log coordinate, prefix, or glob.",
|
|
525
|
+
retryable: false,
|
|
526
|
+
}
|
|
527
|
+
: {}),
|
|
528
|
+
}),
|
|
529
|
+
plan: null,
|
|
530
|
+
};
|
|
570
531
|
}
|
|
571
|
-
return
|
|
532
|
+
return planned(selected.ids);
|
|
572
533
|
}
|
|
573
534
|
if (statement.target === null) {
|
|
574
|
-
return
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
535
|
+
return {
|
|
536
|
+
result: Results.failure("scheme:log", "target-required", 400, `${expanded === 1 ? "OPEN" : "FOLD"} requires a path, body pattern, or unsigned tag; signed tags do not select log items.`, {}, {
|
|
537
|
+
recovery: "Provide a log coordinate, prefix, glob, body pattern, or unsigned tag.",
|
|
538
|
+
retryable: false,
|
|
539
|
+
}),
|
|
540
|
+
plan: null,
|
|
541
|
+
};
|
|
578
542
|
}
|
|
579
543
|
const pathname = (statement.target.kind === "url" ? statement.target.pathname : statement.target.raw).replace(/^\//, "");
|
|
580
|
-
const
|
|
581
|
-
if (
|
|
582
|
-
return { status: 204, matched: 0 };
|
|
583
|
-
if (
|
|
584
|
-
return
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
544
|
+
const selected = await this.#resolveIds(pathname, ctx);
|
|
545
|
+
if (selected.status === 204)
|
|
546
|
+
return { result: { status: 204, matched: 0 }, plan: null };
|
|
547
|
+
if (selected.status !== 200) {
|
|
548
|
+
return {
|
|
549
|
+
result: Results.failure("scheme:log", selected.status === 404 ? "entry-not-found" : "curation-failed", selected.status, selected.error ?? `No log entry matches '${pathname}'.`, {}, {
|
|
550
|
+
target: pathname,
|
|
551
|
+
...(selected.status === 400
|
|
552
|
+
? {
|
|
553
|
+
recovery: "Use a log coordinate, prefix, or glob.",
|
|
554
|
+
retryable: false,
|
|
555
|
+
}
|
|
556
|
+
: {}),
|
|
557
|
+
}),
|
|
558
|
+
plan: null,
|
|
559
|
+
};
|
|
593
560
|
}
|
|
594
|
-
return
|
|
561
|
+
return planned(selected.ids);
|
|
595
562
|
}
|
|
596
563
|
// KILL shares OPEN/FOLD's address resolution, deletes instead of flipping visibility,
|
|
597
564
|
// and carries no positional scope. {§model-entry-log-curation} {§log-curation-set-selection}
|