@loomwork/ui-hydration 0.0.0-stage → 0.1.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/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +398 -2
- package/dist/cache.d.ts +71 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +157 -0
- package/dist/cache.js.map +1 -0
- package/dist/chatSource.d.ts +105 -0
- package/dist/chatSource.d.ts.map +1 -0
- package/dist/chatSource.js +259 -0
- package/dist/chatSource.js.map +1 -0
- package/dist/client.d.ts +17 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +36 -0
- package/dist/client.js.map +1 -0
- package/dist/contentRefs.d.ts +78 -0
- package/dist/contentRefs.d.ts.map +1 -0
- package/dist/contentRefs.js +129 -0
- package/dist/contentRefs.js.map +1 -0
- package/dist/controller.d.ts +228 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +454 -0
- package/dist/controller.js.map +1 -0
- package/dist/dataProvider.d.ts +53 -0
- package/dist/dataProvider.d.ts.map +1 -0
- package/dist/dataProvider.js +77 -0
- package/dist/dataProvider.js.map +1 -0
- package/dist/esm/cache.js +151 -0
- package/dist/esm/cache.js.map +1 -0
- package/dist/esm/chatSource.js +254 -0
- package/dist/esm/chatSource.js.map +1 -0
- package/dist/esm/client.js +15 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/esm/contentRefs.js +119 -0
- package/dist/esm/contentRefs.js.map +1 -0
- package/dist/esm/controller.js +449 -0
- package/dist/esm/controller.js.map +1 -0
- package/dist/esm/dataProvider.js +72 -0
- package/dist/esm/dataProvider.js.map +1 -0
- package/dist/esm/index.js +15 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/invoker.js +2 -0
- package/dist/esm/invoker.js.map +1 -0
- package/dist/esm/invokers.js +310 -0
- package/dist/esm/invokers.js.map +1 -0
- package/dist/esm/loomEffectProvider.js +96 -0
- package/dist/esm/loomEffectProvider.js.map +1 -0
- package/dist/esm/params.js +207 -0
- package/dist/esm/params.js.map +1 -0
- package/dist/esm/path.js +24 -0
- package/dist/esm/path.js.map +1 -0
- package/dist/esm/plan.js +190 -0
- package/dist/esm/plan.js.map +1 -0
- package/dist/esm/projection.js +230 -0
- package/dist/esm/projection.js.map +1 -0
- package/dist/esm/provider.js +416 -0
- package/dist/esm/provider.js.map +1 -0
- package/dist/esm/repair.js +53 -0
- package/dist/esm/repair.js.map +1 -0
- package/dist/esm/reshape.js +156 -0
- package/dist/esm/reshape.js.map +1 -0
- package/dist/esm/restricted.js +54 -0
- package/dist/esm/restricted.js.map +1 -0
- package/dist/esm/scheduler.js +247 -0
- package/dist/esm/scheduler.js.map +1 -0
- package/dist/esm/select.js +18 -0
- package/dist/esm/select.js.map +1 -0
- package/dist/esm/shape.js +34 -0
- package/dist/esm/shape.js.map +1 -0
- package/dist/esm/split/client.js +309 -0
- package/dist/esm/split/client.js.map +1 -0
- package/dist/esm/split/index.js +3 -0
- package/dist/esm/split/index.js.map +1 -0
- package/dist/esm/split/server.js +488 -0
- package/dist/esm/split/server.js.map +1 -0
- package/dist/esm/split/shared.js +13 -0
- package/dist/esm/split/shared.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/invoker.d.ts +10 -0
- package/dist/invoker.d.ts.map +1 -0
- package/dist/invoker.js +3 -0
- package/dist/invoker.js.map +1 -0
- package/dist/invokers.d.ts +36 -0
- package/dist/invokers.d.ts.map +1 -0
- package/dist/invokers.js +315 -0
- package/dist/invokers.js.map +1 -0
- package/dist/loomEffectProvider.d.ts +67 -0
- package/dist/loomEffectProvider.d.ts.map +1 -0
- package/dist/loomEffectProvider.js +101 -0
- package/dist/loomEffectProvider.js.map +1 -0
- package/dist/params.d.ts +103 -0
- package/dist/params.d.ts.map +1 -0
- package/dist/params.js +221 -0
- package/dist/params.js.map +1 -0
- package/dist/path.d.ts +12 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +28 -0
- package/dist/path.js.map +1 -0
- package/dist/plan.d.ts +56 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +199 -0
- package/dist/plan.js.map +1 -0
- package/dist/projection.d.ts +122 -0
- package/dist/projection.d.ts.map +1 -0
- package/dist/projection.js +238 -0
- package/dist/projection.js.map +1 -0
- package/dist/provider.d.ts +152 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +421 -0
- package/dist/provider.js.map +1 -0
- package/dist/repair.d.ts +18 -0
- package/dist/repair.d.ts.map +1 -0
- package/dist/repair.js +57 -0
- package/dist/repair.js.map +1 -0
- package/dist/reshape.d.ts +32 -0
- package/dist/reshape.d.ts.map +1 -0
- package/dist/reshape.js +164 -0
- package/dist/reshape.js.map +1 -0
- package/dist/restricted.d.ts +21 -0
- package/dist/restricted.d.ts.map +1 -0
- package/dist/restricted.js +61 -0
- package/dist/restricted.js.map +1 -0
- package/dist/scheduler.d.ts +82 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +254 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/select.d.ts +34 -0
- package/dist/select.d.ts.map +1 -0
- package/dist/select.js +21 -0
- package/dist/select.js.map +1 -0
- package/dist/shape.d.ts +3 -0
- package/dist/shape.d.ts.map +1 -0
- package/dist/shape.js +37 -0
- package/dist/shape.js.map +1 -0
- package/dist/split/client.d.ts +132 -0
- package/dist/split/client.d.ts.map +1 -0
- package/dist/split/client.js +313 -0
- package/dist/split/client.js.map +1 -0
- package/dist/split/index.d.ts +5 -0
- package/dist/split/index.d.ts.map +1 -0
- package/dist/split/index.js +14 -0
- package/dist/split/index.js.map +1 -0
- package/dist/split/server.d.ts +160 -0
- package/dist/split/server.d.ts.map +1 -0
- package/dist/split/server.js +496 -0
- package/dist/split/server.js.map +1 -0
- package/dist/split/shared.d.ts +6 -0
- package/dist/split/shared.d.ts.map +1 -0
- package/dist/split/shared.js +19 -0
- package/dist/split/shared.js.map +1 -0
- package/package.json +77 -3
- package/src/cache.ts +211 -0
- package/src/chatSource.ts +374 -0
- package/src/client.ts +46 -0
- package/src/contentRefs.ts +195 -0
- package/src/controller.ts +708 -0
- package/src/dataProvider.ts +141 -0
- package/src/index.ts +86 -0
- package/src/invoker.ts +14 -0
- package/src/invokers.ts +348 -0
- package/src/loomEffectProvider.ts +136 -0
- package/src/params.ts +314 -0
- package/src/path.ts +26 -0
- package/src/plan.ts +197 -0
- package/src/projection.ts +297 -0
- package/src/provider.ts +657 -0
- package/src/repair.ts +70 -0
- package/src/reshape.ts +193 -0
- package/src/restricted.ts +69 -0
- package/src/scheduler.ts +284 -0
- package/src/select.ts +59 -0
- package/src/shape.ts +39 -0
- package/src/split/client.ts +449 -0
- package/src/split/index.ts +18 -0
- package/src/split/server.ts +746 -0
- package/src/split/shared.ts +18 -0
package/dist/reshape.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.jsonPathSelect = exports.outputShapeHashOf = void 0;
|
|
4
|
+
exports.defineReshapers = defineReshapers;
|
|
5
|
+
exports.safeStringify = safeStringify;
|
|
6
|
+
exports.reshape = reshape;
|
|
7
|
+
const core_1 = require("@loomwork/core");
|
|
8
|
+
const plugin_sandbox_1 = require("@loomwork/plugin-sandbox");
|
|
9
|
+
const select_1 = require("./select");
|
|
10
|
+
/**
|
|
11
|
+
* The channel loom's sandbox completes on when the SCRIPT itself threw on its input (its own
|
|
12
|
+
* exception, a SyntaxError, a ReferenceError for a global the VM lacks). Recognised by channel
|
|
13
|
+
* only: its payload (`{ name?, message }`) is script-authored, UNTRUSTED text — it can echo the
|
|
14
|
+
* raw output — so it is never read, and never copied into a finding message.
|
|
15
|
+
*/
|
|
16
|
+
const SCRIPT_ERROR = "scriptError";
|
|
17
|
+
const LIMIT_EXCEEDED = "limitExceeded";
|
|
18
|
+
/** A sandbox `TypedFailure` → the output-caused kind it proves, if any. Channel + payload only. */
|
|
19
|
+
function sandboxFailureKind(e) {
|
|
20
|
+
if (e.channel === SCRIPT_ERROR)
|
|
21
|
+
return "script-error";
|
|
22
|
+
if (e.channel !== LIMIT_EXCEEDED)
|
|
23
|
+
return undefined;
|
|
24
|
+
const kind = e.payload?.kind;
|
|
25
|
+
if (kind === "cpu")
|
|
26
|
+
return "limit-cpu";
|
|
27
|
+
if (kind === "memory")
|
|
28
|
+
return "limit-memory";
|
|
29
|
+
if (kind === "output")
|
|
30
|
+
return "limit-output";
|
|
31
|
+
return undefined; // wallClock (host-load dependent) or an unknown kind
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A registry over a plain record. Map-backed, so only the record's OWN entries are registered —
|
|
35
|
+
* `"constructor"` or `"__proto__"` never resolve to something on a prototype.
|
|
36
|
+
*/
|
|
37
|
+
function defineReshapers(entries) {
|
|
38
|
+
const byName = new Map(Object.entries(entries));
|
|
39
|
+
return { get: (name) => byName.get(name) };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A Named reshaper runs in-process, trusted like host code — but the RAW terminal output is also
|
|
43
|
+
* read by callers after `reshape` returns (output-shape hashing, drift sampling): a reshaper that
|
|
44
|
+
* mutates its input in place must never corrupt what those later readers see, so it gets a copy,
|
|
45
|
+
* never the original reference. A raw value `structuredClone` cannot represent anywhere in its
|
|
46
|
+
* tree (a function; `structuredClone` throws) is REFUSED rather than handed to the reshaper live
|
|
47
|
+
* — that would let the reshaper corrupt what later readers see, exactly what the copy exists to
|
|
48
|
+
* prevent. (A class instance is not this case: `structuredClone` clones it fine, it just drops
|
|
49
|
+
* the prototype, so the copy is a plain object with the same own properties.) Throws; `runNamed`
|
|
50
|
+
* turns that into a `reshape-failed` finding before the reshaper ever runs.
|
|
51
|
+
*/
|
|
52
|
+
function cloneForReshaper(raw) {
|
|
53
|
+
try {
|
|
54
|
+
return structuredClone(raw);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
throw new Error("input is not cloneable");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function runNamed(name, raw, registry) {
|
|
61
|
+
const fn = registry?.get(name);
|
|
62
|
+
if (fn === undefined)
|
|
63
|
+
return {
|
|
64
|
+
ok: false,
|
|
65
|
+
finding: {
|
|
66
|
+
code: "reshape-unregistered",
|
|
67
|
+
severity: "error",
|
|
68
|
+
message: registry === undefined
|
|
69
|
+
? `reshaper "${name}" is Named, but no reshaper registry was supplied`
|
|
70
|
+
: `no reshaper is registered under "${name}"`,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
let cloned;
|
|
74
|
+
try {
|
|
75
|
+
cloned = cloneForReshaper(raw);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
// The OUTPUT is the cause: it holds a value that cannot be copied, so the reshaper never runs.
|
|
79
|
+
return {
|
|
80
|
+
ok: false,
|
|
81
|
+
failureKind: "uncloneable-output",
|
|
82
|
+
finding: {
|
|
83
|
+
code: "reshape-failed",
|
|
84
|
+
severity: "error",
|
|
85
|
+
message: `reshaper "${name}": ${err instanceof Error ? err.message : String(err)}`,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
return { ok: true, value: await fn(cloned) };
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
// Host code threw: not proven output-caused, so no kind — a plain error, not drift.
|
|
94
|
+
return {
|
|
95
|
+
ok: false,
|
|
96
|
+
finding: {
|
|
97
|
+
code: "reshape-failed",
|
|
98
|
+
severity: "error",
|
|
99
|
+
message: `reshaper "${name}" failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* A finding MESSAGE must never be the thing that throws. A `TypedFailure` payload is authored by
|
|
106
|
+
* a tool server or a sandbox script — it can be cyclic, or carry a bigint, and `JSON.stringify`
|
|
107
|
+
* throws on both. Losing the payload text is a far better outcome than losing the finding.
|
|
108
|
+
* (Not exported from the package index: it exists for this package's message building.)
|
|
109
|
+
*/
|
|
110
|
+
function safeStringify(v) {
|
|
111
|
+
try {
|
|
112
|
+
return JSON.stringify(v) ?? String(v);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return "[unserializable payload]";
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async function reshape(reshaping, raw, ctx) {
|
|
119
|
+
if (reshaping.engine === "JSONPath")
|
|
120
|
+
return (0, select_1.jsonPathSelect)(raw, reshaping.scriptRef);
|
|
121
|
+
if (reshaping.engine === "Named")
|
|
122
|
+
return runNamed(reshaping.scriptRef, raw, ctx.reshapers);
|
|
123
|
+
try {
|
|
124
|
+
const value = await ctx.invoker.invokeTransient(plugin_sandbox_1.SANDBOX_TOOL_ID, { scriptRef: { __loomContent: reshaping.scriptRef }, input: raw }, {
|
|
125
|
+
label: "loom-ui.reshape",
|
|
126
|
+
...(ctx.signal && { signal: ctx.signal }),
|
|
127
|
+
...(ctx.principal && { principal: ctx.principal }),
|
|
128
|
+
});
|
|
129
|
+
return { ok: true, value };
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
if (err instanceof core_1.TypedFailure) {
|
|
133
|
+
const failureKind = sandboxFailureKind(err);
|
|
134
|
+
return {
|
|
135
|
+
ok: false,
|
|
136
|
+
...(failureKind !== undefined && { failureKind }),
|
|
137
|
+
finding: {
|
|
138
|
+
code: `reshape-${err.channel}`,
|
|
139
|
+
severity: "error",
|
|
140
|
+
// A `scriptError` payload is script-authored text that can echo the raw output
|
|
141
|
+
// (possibly sensitive, untrusted): fixed text only, never its `name` or `message` —
|
|
142
|
+
// no finding, handle status or log hook ever carries it. The raw output travels only
|
|
143
|
+
// as the tainted drift sample. Other channels' payloads are loom-authored.
|
|
144
|
+
message: failureKind === "script-error"
|
|
145
|
+
? `reshape script ${reshaping.scriptRef} failed on the output (script-error)`
|
|
146
|
+
: `sandbox ${err.channel}: ${safeStringify(err.payload)}`,
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
ok: false,
|
|
152
|
+
finding: {
|
|
153
|
+
code: "reshape-failed",
|
|
154
|
+
severity: "error",
|
|
155
|
+
message: err instanceof Error ? err.message : String(err),
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
var shape_1 = require("./shape");
|
|
161
|
+
Object.defineProperty(exports, "outputShapeHashOf", { enumerable: true, get: function () { return shape_1.outputShapeHashOf; } });
|
|
162
|
+
var select_2 = require("./select");
|
|
163
|
+
Object.defineProperty(exports, "jsonPathSelect", { enumerable: true, get: function () { return select_2.jsonPathSelect; } });
|
|
164
|
+
//# sourceMappingURL=reshape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reshape.js","sourceRoot":"","sources":["../src/reshape.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Audience, type Finding, type HandleUse, type Viewer } from "@loomwork/ui-core";
|
|
2
|
+
/**
|
|
3
|
+
* What the store (and so the viewer) is told about a refused handle. A fixed text: it names no
|
|
4
|
+
* handle, class, tool or argument, so refusing leaks nothing beyond "this is restricted".
|
|
5
|
+
*/
|
|
6
|
+
export declare const restrictedFinding: () => Finding;
|
|
7
|
+
/** The advisory for classified handles with no viewer supplied (they are all refused). */
|
|
8
|
+
export declare const viewerNotSuppliedFinding: () => Finding;
|
|
9
|
+
/** Split an inventory into the handles a viewer may load and the ones it may not. */
|
|
10
|
+
export declare function partitionByViewer(uses: readonly HandleUse[], viewer: Viewer | undefined): {
|
|
11
|
+
allowed: HandleUse[];
|
|
12
|
+
restricted: HandleUse[];
|
|
13
|
+
advisory: Finding[];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* A copy of the viewer holding only a clearance read once; a throwing getter reads as none. For a
|
|
17
|
+
* broadcast `audience` the result is the audience's viewer (no clearance, whoever asked: the
|
|
18
|
+
* viewer is not even read), so a handle compile withheld for the audience is refused here too.
|
|
19
|
+
*/
|
|
20
|
+
export declare function snapshotViewer(viewer: Viewer | undefined, audience?: Audience): Viewer | undefined;
|
|
21
|
+
//# sourceMappingURL=restricted.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restricted.d.ts","sourceRoot":"","sources":["../src/restricted.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,MAAM,EAIZ,MAAM,mBAAmB,CAAC;AAS3B;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAAO,OAInC,CAAC;AAEH,0FAA0F;AAC1F,eAAO,MAAM,wBAAwB,QAAO,OAK1C,CAAC;AAEH,qFAAqF;AACrF,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,SAAS,SAAS,EAAE,EAC1B,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB;IAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IAAC,UAAU,EAAE,SAAS,EAAE,CAAC;IAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;CAAE,CAUxE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,QAAQ,CAAC,EAAE,QAAQ,GAClB,MAAM,GAAG,SAAS,CASpB"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.viewerNotSuppliedFinding = exports.restrictedFinding = void 0;
|
|
4
|
+
exports.partitionByViewer = partitionByViewer;
|
|
5
|
+
exports.snapshotViewer = snapshotViewer;
|
|
6
|
+
const ui_core_1 = require("@loomwork/ui-core");
|
|
7
|
+
// ── Viewer enforcement at the data boundary ───────────────────────────────────
|
|
8
|
+
// Hydration is where data actually flows, so it is the second place a binding's `sensitivity`
|
|
9
|
+
// is enforced (the first is `compile`, which redacts the node). A handle whose strictest class the
|
|
10
|
+
// viewer may not see is never planned, never invoked, never cached, never served: its provider
|
|
11
|
+
// is not called at all. FAIL-CLOSED: no viewer is a viewer with no clearance; unclassified handles
|
|
12
|
+
// are unaffected either way. See `@loomwork/ui-core`'s `viewer.ts`.
|
|
13
|
+
/**
|
|
14
|
+
* What the store (and so the viewer) is told about a refused handle. A fixed text: it names no
|
|
15
|
+
* handle, class, tool or argument, so refusing leaks nothing beyond "this is restricted".
|
|
16
|
+
*/
|
|
17
|
+
const restrictedFinding = () => ({
|
|
18
|
+
code: "restricted",
|
|
19
|
+
severity: "error",
|
|
20
|
+
message: "this data is restricted for the current viewer",
|
|
21
|
+
});
|
|
22
|
+
exports.restrictedFinding = restrictedFinding;
|
|
23
|
+
/** The advisory for classified handles with no viewer supplied (they are all refused). */
|
|
24
|
+
const viewerNotSuppliedFinding = () => ({
|
|
25
|
+
code: "viewer-not-supplied",
|
|
26
|
+
severity: "warn",
|
|
27
|
+
message: "the experience binds classified handles and no viewer was supplied: they are refused (no viewer is the most restrictive viewer)",
|
|
28
|
+
});
|
|
29
|
+
exports.viewerNotSuppliedFinding = viewerNotSuppliedFinding;
|
|
30
|
+
/** Split an inventory into the handles a viewer may load and the ones it may not. */
|
|
31
|
+
function partitionByViewer(uses, viewer) {
|
|
32
|
+
const allowed = [];
|
|
33
|
+
const restricted = [];
|
|
34
|
+
for (const use of uses)
|
|
35
|
+
((0, ui_core_1.mayView)(viewer, use.sensitivity) ? allowed : restricted).push(use);
|
|
36
|
+
const classified = uses.some((u) => u.sensitivity !== undefined);
|
|
37
|
+
return {
|
|
38
|
+
allowed,
|
|
39
|
+
restricted,
|
|
40
|
+
advisory: classified && viewer === undefined ? [(0, exports.viewerNotSuppliedFinding)()] : [],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A copy of the viewer holding only a clearance read once; a throwing getter reads as none. For a
|
|
45
|
+
* broadcast `audience` the result is the audience's viewer (no clearance, whoever asked: the
|
|
46
|
+
* viewer is not even read), so a handle compile withheld for the audience is refused here too.
|
|
47
|
+
*/
|
|
48
|
+
function snapshotViewer(viewer, audience) {
|
|
49
|
+
if ((0, ui_core_1.isBroadcast)(audience))
|
|
50
|
+
return (0, ui_core_1.effectiveViewer)(undefined, audience);
|
|
51
|
+
if (viewer === undefined)
|
|
52
|
+
return undefined;
|
|
53
|
+
try {
|
|
54
|
+
const clearance = viewer.clearance;
|
|
55
|
+
return clearance === undefined ? {} : { clearance };
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return {};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=restricted.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restricted.js","sourceRoot":"","sources":["../src/restricted.ts"],"names":[],"mappings":";;;AAoCA,8CAaC;AAOD,wCAYC;AApED,+CAQ2B;AAE3B,iFAAiF;AACjF,8FAA8F;AAC9F,mGAAmG;AACnG,+FAA+F;AAC/F,mGAAmG;AACnG,oEAAoE;AAEpE;;;GAGG;AACI,MAAM,iBAAiB,GAAG,GAAY,EAAE,CAAC,CAAC;IAC/C,IAAI,EAAE,YAAY;IAClB,QAAQ,EAAE,OAAO;IACjB,OAAO,EAAE,gDAAgD;CAC1D,CAAC,CAAC;AAJU,QAAA,iBAAiB,qBAI3B;AAEH,0FAA0F;AACnF,MAAM,wBAAwB,GAAG,GAAY,EAAE,CAAC,CAAC;IACtD,IAAI,EAAE,qBAAqB;IAC3B,QAAQ,EAAE,MAAM;IAChB,OAAO,EACL,iIAAiI;CACpI,CAAC,CAAC;AALU,QAAA,wBAAwB,4BAKlC;AAEH,qFAAqF;AACrF,SAAgB,iBAAiB,CAC/B,IAA0B,EAC1B,MAA0B;IAE1B,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,MAAM,UAAU,GAAgB,EAAE,CAAC;IACnC,KAAK,MAAM,GAAG,IAAI,IAAI;QAAE,CAAC,IAAA,iBAAO,EAAC,MAAM,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5F,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC;IACjE,OAAO;QACL,OAAO;QACP,UAAU;QACV,QAAQ,EAAE,UAAU,IAAI,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,IAAA,gCAAwB,GAAE,CAAC,CAAC,CAAC,CAAC,EAAE;KACjF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAgB,cAAc,CAC5B,MAA0B,EAC1B,QAAmB;IAEnB,IAAI,IAAA,qBAAW,EAAC,QAAQ,CAAC;QAAE,OAAO,IAAA,yBAAe,EAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACvE,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QACnC,OAAO,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { Finding, Priority } from "@loomwork/ui-core";
|
|
2
|
+
import type { HydrationStore } from "./projection";
|
|
3
|
+
export interface ScheduledJob {
|
|
4
|
+
readonly handle: string;
|
|
5
|
+
readonly priority: Priority;
|
|
6
|
+
/** The identity of the question. Same fingerprint + a live status = already asked; no re-run. */
|
|
7
|
+
readonly fingerprint: string;
|
|
8
|
+
}
|
|
9
|
+
/** What a load publishes. Serializable: it is also what a `HandleLoader` returns across a boundary. */
|
|
10
|
+
export type HandleLoadResult = {
|
|
11
|
+
readonly state: "ready";
|
|
12
|
+
readonly value: unknown;
|
|
13
|
+
readonly finding?: Finding;
|
|
14
|
+
} | {
|
|
15
|
+
readonly state: "stale" | "error";
|
|
16
|
+
readonly finding: Finding;
|
|
17
|
+
}
|
|
18
|
+
/** Delivered, and the upstream has no such row (`upstream-empty`): no value (loom ADR 0100). */
|
|
19
|
+
| {
|
|
20
|
+
readonly state: "empty";
|
|
21
|
+
readonly finding: Finding;
|
|
22
|
+
};
|
|
23
|
+
export interface SchedulerHooks<J extends ScheduledJob, G, R extends HandleLoadResult> {
|
|
24
|
+
newGroup(): G;
|
|
25
|
+
run(job: J, signal: AbortSignal, group: G): Promise<R>;
|
|
26
|
+
/** A value that needs no load (a cache hit): published `ready` at once. */
|
|
27
|
+
cached?(job: J): {
|
|
28
|
+
readonly value: unknown;
|
|
29
|
+
} | undefined;
|
|
30
|
+
/** The result is current (not superseded, not aborted) and about to be published. */
|
|
31
|
+
commit?(job: J, result: R): void;
|
|
32
|
+
/**
|
|
33
|
+
* The result was published. A throw here is swallowed: the handle keeps the status just
|
|
34
|
+
* published (the load is no longer in flight, so it is not moved to an error status) and
|
|
35
|
+
* nothing rejects.
|
|
36
|
+
*/
|
|
37
|
+
published?(job: J, result: R): void;
|
|
38
|
+
}
|
|
39
|
+
export type TimerFn = (fn: () => void, ms: number) => () => void;
|
|
40
|
+
export declare const defaultTimer: TimerFn;
|
|
41
|
+
/** NUL: cannot occur in a handle or a hash, so joined fingerprints never collide. */
|
|
42
|
+
export declare const KEY_SEP: string;
|
|
43
|
+
export declare const disposedFinding: (what: string) => Finding;
|
|
44
|
+
export declare const tooDeep: (experienceId: string, max: number) => Finding;
|
|
45
|
+
export declare class HandleScheduler<J extends ScheduledJob, G, R extends HandleLoadResult> {
|
|
46
|
+
private readonly store;
|
|
47
|
+
private readonly hooks;
|
|
48
|
+
private readonly setTimer;
|
|
49
|
+
private readonly batchWindowMs;
|
|
50
|
+
private readonly inflight;
|
|
51
|
+
/**
|
|
52
|
+
* P1 jobs waiting for the batch timer, ONE per handle: a question changed inside the window
|
|
53
|
+
* replaces the queued one (the old one would otherwise start first and wedge the new one).
|
|
54
|
+
*/
|
|
55
|
+
private pending;
|
|
56
|
+
private cancelBatch;
|
|
57
|
+
private readonly lazy;
|
|
58
|
+
/** handle → fingerprint of the job it was last STARTED with. Pruned with the handle. */
|
|
59
|
+
private readonly loaded;
|
|
60
|
+
private disposed;
|
|
61
|
+
constructor(store: HydrationStore, hooks: SchedulerHooks<J, G, R>, setTimer?: TimerFn, batchWindowMs?: number);
|
|
62
|
+
get isDisposed(): boolean;
|
|
63
|
+
/** Schedule by priority; a job whose question is already answered (or being answered) is skipped. */
|
|
64
|
+
schedule(jobs: readonly J[]): void;
|
|
65
|
+
/** Keep only these handles: abort, dequeue, forget and unpublish every other one. */
|
|
66
|
+
retain(keep: ReadonlySet<string>): void;
|
|
67
|
+
/**
|
|
68
|
+
* Ask again even if already asked: each in-flight load is superseded and a fresh one starts at
|
|
69
|
+
* once (one group). A lazy job stays lazy — its status untouched, its last value on screen.
|
|
70
|
+
*/
|
|
71
|
+
reload(jobs: readonly J[]): void;
|
|
72
|
+
/** The surface reports a lazy (P2) widget entered the viewport. */
|
|
73
|
+
markVisible(handle: string): void;
|
|
74
|
+
inFlight(): string[];
|
|
75
|
+
/** Terminal: every in-flight or queued load ends on an error status, and nothing schedules again. */
|
|
76
|
+
dispose(): void;
|
|
77
|
+
/** A group made on first use, then shared: a call that starts nothing makes none. */
|
|
78
|
+
private lazyGroup;
|
|
79
|
+
private scheduleOne;
|
|
80
|
+
private start;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HandleScheduler = exports.tooDeep = exports.disposedFinding = exports.KEY_SEP = exports.defaultTimer = void 0;
|
|
4
|
+
const defaultTimer = (fn, ms) => {
|
|
5
|
+
const t = setTimeout(fn, ms);
|
|
6
|
+
return () => clearTimeout(t);
|
|
7
|
+
};
|
|
8
|
+
exports.defaultTimer = defaultTimer;
|
|
9
|
+
/** NUL: cannot occur in a handle or a hash, so joined fingerprints never collide. */
|
|
10
|
+
exports.KEY_SEP = String.fromCharCode(0);
|
|
11
|
+
const disposedFinding = (what) => ({
|
|
12
|
+
code: "disposed",
|
|
13
|
+
severity: "error",
|
|
14
|
+
message: `the hydration controller is disposed; ${what} does nothing`,
|
|
15
|
+
});
|
|
16
|
+
exports.disposedFinding = disposedFinding;
|
|
17
|
+
const tooDeep = (experienceId, max) => ({
|
|
18
|
+
code: "tree-too-deep",
|
|
19
|
+
severity: "error",
|
|
20
|
+
message: `experience "${experienceId}" exceeds MAX_TREE_DEPTH (${max})`,
|
|
21
|
+
});
|
|
22
|
+
exports.tooDeep = tooDeep;
|
|
23
|
+
class HandleScheduler {
|
|
24
|
+
store;
|
|
25
|
+
hooks;
|
|
26
|
+
setTimer;
|
|
27
|
+
batchWindowMs;
|
|
28
|
+
inflight = new Map();
|
|
29
|
+
/**
|
|
30
|
+
* P1 jobs waiting for the batch timer, ONE per handle: a question changed inside the window
|
|
31
|
+
* replaces the queued one (the old one would otherwise start first and wedge the new one).
|
|
32
|
+
*/
|
|
33
|
+
pending = new Map();
|
|
34
|
+
cancelBatch;
|
|
35
|
+
lazy = new Map();
|
|
36
|
+
/** handle → fingerprint of the job it was last STARTED with. Pruned with the handle. */
|
|
37
|
+
loaded = new Map();
|
|
38
|
+
disposed = false;
|
|
39
|
+
constructor(store, hooks, setTimer = exports.defaultTimer, batchWindowMs = 50) {
|
|
40
|
+
this.store = store;
|
|
41
|
+
this.hooks = hooks;
|
|
42
|
+
this.setTimer = setTimer;
|
|
43
|
+
this.batchWindowMs = batchWindowMs;
|
|
44
|
+
}
|
|
45
|
+
get isDisposed() {
|
|
46
|
+
return this.disposed;
|
|
47
|
+
}
|
|
48
|
+
/** Schedule by priority; a job whose question is already answered (or being answered) is skipped. */
|
|
49
|
+
schedule(jobs) {
|
|
50
|
+
if (this.disposed)
|
|
51
|
+
return;
|
|
52
|
+
// One group for every job this call starts now (a P1 batch and a markVisible get their own).
|
|
53
|
+
const now = this.lazyGroup();
|
|
54
|
+
for (const job of jobs)
|
|
55
|
+
this.scheduleOne(job, now);
|
|
56
|
+
}
|
|
57
|
+
/** Keep only these handles: abort, dequeue, forget and unpublish every other one. */
|
|
58
|
+
retain(keep) {
|
|
59
|
+
const drop = new Set();
|
|
60
|
+
for (const handle of [
|
|
61
|
+
...this.inflight.keys(),
|
|
62
|
+
...this.lazy.keys(),
|
|
63
|
+
...this.pending.keys(),
|
|
64
|
+
...this.store.handles(),
|
|
65
|
+
])
|
|
66
|
+
if (!keep.has(handle))
|
|
67
|
+
drop.add(handle);
|
|
68
|
+
if (drop.size === 0)
|
|
69
|
+
return;
|
|
70
|
+
for (const handle of drop) {
|
|
71
|
+
this.inflight.get(handle)?.abort();
|
|
72
|
+
this.inflight.delete(handle);
|
|
73
|
+
this.lazy.delete(handle);
|
|
74
|
+
this.pending.delete(handle);
|
|
75
|
+
this.loaded.delete(handle);
|
|
76
|
+
if (this.store.status(handle) !== undefined)
|
|
77
|
+
this.store.remove(handle);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Ask again even if already asked: each in-flight load is superseded and a fresh one starts at
|
|
82
|
+
* once (one group). A lazy job stays lazy — its status untouched, its last value on screen.
|
|
83
|
+
*/
|
|
84
|
+
reload(jobs) {
|
|
85
|
+
if (this.disposed)
|
|
86
|
+
return;
|
|
87
|
+
const group = this.lazyGroup();
|
|
88
|
+
for (const job of jobs) {
|
|
89
|
+
this.inflight.get(job.handle)?.abort();
|
|
90
|
+
this.inflight.delete(job.handle);
|
|
91
|
+
this.pending.delete(job.handle); // started below (or re-queued lazy): not a second time
|
|
92
|
+
if (job.priority === "P2") {
|
|
93
|
+
this.lazy.set(job.handle, job);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
this.start(job, group()).catch(() => { });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** The surface reports a lazy (P2) widget entered the viewport. */
|
|
100
|
+
markVisible(handle) {
|
|
101
|
+
if (this.disposed)
|
|
102
|
+
return;
|
|
103
|
+
const job = this.lazy.get(handle);
|
|
104
|
+
if (!job)
|
|
105
|
+
return;
|
|
106
|
+
this.lazy.delete(handle);
|
|
107
|
+
this.start(job, this.hooks.newGroup()).catch(() => { });
|
|
108
|
+
}
|
|
109
|
+
inFlight() {
|
|
110
|
+
return [...this.inflight.keys()].sort();
|
|
111
|
+
}
|
|
112
|
+
/** Terminal: every in-flight or queued load ends on an error status, and nothing schedules again. */
|
|
113
|
+
dispose() {
|
|
114
|
+
if (this.disposed)
|
|
115
|
+
return;
|
|
116
|
+
this.disposed = true;
|
|
117
|
+
for (const [handle, ac] of this.inflight) {
|
|
118
|
+
ac.abort();
|
|
119
|
+
// The widget must not sit on a spinner forever because its controller went away.
|
|
120
|
+
this.store.setStatus(handle, {
|
|
121
|
+
state: "error",
|
|
122
|
+
finding: {
|
|
123
|
+
code: "disposed",
|
|
124
|
+
severity: "error",
|
|
125
|
+
message: `hydration for "${handle}" was disposed while in flight`,
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
this.inflight.clear();
|
|
130
|
+
this.cancelBatch?.();
|
|
131
|
+
this.cancelBatch = undefined;
|
|
132
|
+
for (const job of this.pending.values()) {
|
|
133
|
+
// Already published `pending`; leaving it there would spin the widget forever waiting on a
|
|
134
|
+
// batch timer that just got cancelled. Lazy jobs were never published: nothing to terminate.
|
|
135
|
+
this.store.setStatus(job.handle, {
|
|
136
|
+
state: "error",
|
|
137
|
+
finding: {
|
|
138
|
+
code: "disposed",
|
|
139
|
+
severity: "error",
|
|
140
|
+
message: `hydration for "${job.handle}" was disposed while queued`,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
this.pending.clear();
|
|
145
|
+
this.lazy.clear();
|
|
146
|
+
this.loaded.clear();
|
|
147
|
+
}
|
|
148
|
+
/** A group made on first use, then shared: a call that starts nothing makes none. */
|
|
149
|
+
lazyGroup() {
|
|
150
|
+
let made;
|
|
151
|
+
return () => {
|
|
152
|
+
if (made === undefined)
|
|
153
|
+
made = { group: this.hooks.newGroup() };
|
|
154
|
+
return made.group;
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
scheduleOne(job, group) {
|
|
158
|
+
const st = this.store.status(job.handle)?.state;
|
|
159
|
+
if (this.loaded.get(job.handle) === job.fingerprint) {
|
|
160
|
+
// Same question, already asked: loading, loaded (an `empty` answer is an answer), or
|
|
161
|
+
// drifted-and-awaiting-repair.
|
|
162
|
+
if (st === "ready" || st === "pending" || st === "stale" || st === "empty")
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
// A different question for this handle: whatever is in flight answers the OLD one.
|
|
167
|
+
this.inflight.get(job.handle)?.abort();
|
|
168
|
+
this.inflight.delete(job.handle);
|
|
169
|
+
}
|
|
170
|
+
// A job queued for the batch answers the old question (or the old priority): this one owns it.
|
|
171
|
+
this.pending.delete(job.handle);
|
|
172
|
+
if (job.priority === "P2") {
|
|
173
|
+
this.lazy.set(job.handle, job);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
this.lazy.delete(job.handle); // promoted out of P2: the lazy entry has a new owner
|
|
177
|
+
if (job.priority === "P0") {
|
|
178
|
+
this.start(job, group()).catch(() => { });
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
this.store.setStatus(job.handle, { state: "pending" });
|
|
182
|
+
// Keyed by handle: a changed question REPLACES the queued one rather than queueing behind it.
|
|
183
|
+
this.pending.set(job.handle, job);
|
|
184
|
+
this.cancelBatch ??= this.setTimer(() => {
|
|
185
|
+
this.cancelBatch = undefined;
|
|
186
|
+
const batch = [...this.pending.values()];
|
|
187
|
+
this.pending.clear();
|
|
188
|
+
if (batch.length === 0)
|
|
189
|
+
return;
|
|
190
|
+
const g = this.hooks.newGroup();
|
|
191
|
+
for (const j of batch)
|
|
192
|
+
this.start(j, g).catch(() => { });
|
|
193
|
+
}, this.batchWindowMs);
|
|
194
|
+
}
|
|
195
|
+
async start(job, group) {
|
|
196
|
+
const { handle } = job;
|
|
197
|
+
if (this.disposed)
|
|
198
|
+
return;
|
|
199
|
+
this.loaded.set(handle, job.fingerprint);
|
|
200
|
+
const cached = this.hooks.cached?.(job);
|
|
201
|
+
if (cached) {
|
|
202
|
+
this.inflight.delete(handle);
|
|
203
|
+
this.store.setValue(handle, cached.value, { state: "ready" });
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (this.inflight.has(handle))
|
|
207
|
+
return;
|
|
208
|
+
const ac = new AbortController();
|
|
209
|
+
this.inflight.set(handle, ac);
|
|
210
|
+
try {
|
|
211
|
+
this.store.setStatus(handle, { state: "pending" });
|
|
212
|
+
const result = await this.hooks.run(job, ac.signal, group);
|
|
213
|
+
if (this.inflight.get(handle) !== ac || ac.signal.aborted)
|
|
214
|
+
return; // superseded or aborted
|
|
215
|
+
this.inflight.delete(handle);
|
|
216
|
+
this.hooks.commit?.(job, result);
|
|
217
|
+
if (result.state === "ready")
|
|
218
|
+
this.store.setValue(handle, result.value, {
|
|
219
|
+
state: "ready",
|
|
220
|
+
...(result.finding && { finding: result.finding }),
|
|
221
|
+
});
|
|
222
|
+
else
|
|
223
|
+
this.store.setStatus(handle, { state: result.state, finding: result.finding });
|
|
224
|
+
this.hooks.published?.(job, result);
|
|
225
|
+
}
|
|
226
|
+
catch (e) {
|
|
227
|
+
// `run` is total by construction — but a loader or invoker that rejects with a value whose
|
|
228
|
+
// message getter throws, a store subscriber that throws synchronously on ANY publish here
|
|
229
|
+
// (including the first `pending`), or any future defect must land on the widget as an error
|
|
230
|
+
// status, never as an unhandled rejection that takes the host process down.
|
|
231
|
+
if (this.inflight.get(handle) !== ac || ac.signal.aborted)
|
|
232
|
+
return;
|
|
233
|
+
this.inflight.delete(handle);
|
|
234
|
+
try {
|
|
235
|
+
// The publish below can re-trigger the very subscriber whose throw got us here. A second
|
|
236
|
+
// throw must not escape: `inflight` is already cleaned up, which is the invariant
|
|
237
|
+
// `dispose`/`retain`/`inFlight` depend on.
|
|
238
|
+
this.store.setStatus(handle, {
|
|
239
|
+
state: "error",
|
|
240
|
+
finding: {
|
|
241
|
+
code: "hydration-internal",
|
|
242
|
+
severity: "error",
|
|
243
|
+
message: e instanceof Error ? e.message : String(e),
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
// Ignored — see comment above.
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
exports.HandleScheduler = HandleScheduler;
|
|
254
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"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"}
|
package/dist/select.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Finding } from "@loomwork/ui-core";
|
|
2
|
+
/**
|
|
3
|
+
* Why a reshaping failed, when the tool OUTPUT demonstrably caused it — the failures a re-authored
|
|
4
|
+
* binding can repair. Typed from the failure's channel and payload, never from message text:
|
|
5
|
+
*
|
|
6
|
+
* - `script-error`: the sandbox reports the script threw on this input (a `TypedFailure` on loom's
|
|
7
|
+
* `scriptError` channel, `SANDBOX_ERRORS.scriptError`).
|
|
8
|
+
* - `selects-nothing`: a JSONPath selects nothing from the output.
|
|
9
|
+
* - `uncloneable-output`: the output holds a value `structuredClone` cannot copy (a function, say),
|
|
10
|
+
* so a Named reshaper is never handed it. Recorded WITHOUT a sample: such a value is not a data
|
|
11
|
+
* observation a planner can author against.
|
|
12
|
+
* - `limit-cpu`, `limit-memory`, `limit-output`: a deterministic resource limit the script hit on
|
|
13
|
+
* this input. `limit-cpu` is measured against the host's instruction-count `interruptBudget`,
|
|
14
|
+
* not time — deterministic for a given budget, so LOWERING the budget surfaces as output drift.
|
|
15
|
+
*
|
|
16
|
+
* Anything else has no kind and is not drift. That includes a `wallClock` limit (host-load
|
|
17
|
+
* dependent — on a loaded host a script can trip `wallClock` before `cpu`, and then no drift is
|
|
18
|
+
* reported that time), a Named reshaper's own throw (host code: a host bug or its own I/O is not
|
|
19
|
+
* proven output-caused, and must never get the host's reshaper replaced by a model-authored
|
|
20
|
+
* binding), a missing script, an unregistered reshaper, an untyped throw (transport, worker pool,
|
|
21
|
+
* integrity), an authorization or quota denial, and an abort.
|
|
22
|
+
*/
|
|
23
|
+
export type ReshapeFailureKind = "script-error" | "selects-nothing" | "uncloneable-output" | "limit-cpu" | "limit-memory" | "limit-output";
|
|
24
|
+
export type ReshapeResult = {
|
|
25
|
+
ok: true;
|
|
26
|
+
value: unknown;
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
finding: Finding;
|
|
30
|
+
/** Set only when the output demonstrably caused the failure (see `ReshapeFailureKind`). */
|
|
31
|
+
failureKind?: ReshapeFailureKind;
|
|
32
|
+
};
|
|
33
|
+
export declare function jsonPathSelect(value: unknown, path: string): ReshapeResult;
|
|
34
|
+
//# sourceMappingURL=select.d.ts.map
|