quario 0.1.0 → 0.2.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/CHANGELOG.md +138 -0
- package/README.md +19 -4
- package/lib/index.d.ts +154 -10
- package/lib/index.js +109 -67
- package/lib/license.js +1 -1
- package/lib/locate.js +43 -56
- package/lib/plan.js +328 -136
- package/lib/scope.js +4 -10
- package/lib/stream.js +6 -6
- package/lib/style.js +23 -8
- package/package.json +5 -5
package/lib/index.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* definition lives in `./plan.js`; what the two of them share lives in the
|
|
13
13
|
* siblings beside it, one seam each. See SCHEMA.md for the document shape.
|
|
14
14
|
*/
|
|
15
|
+
import { relocate as relocateQuery } from "padvinder";
|
|
15
16
|
import { MARKING, verify } from "./license.js";
|
|
16
17
|
import { err, locate } from "./locate.js";
|
|
17
18
|
import { BLOCKED, NAME, record } from "./names.js";
|
|
@@ -22,30 +23,24 @@ import { opt } from "./stream.js";
|
|
|
22
23
|
// The event stream's public seam, single-sourced in ./stream.js, and the
|
|
23
24
|
// diagnostic predicate in ./locate.js. Re-exported here because a consumer
|
|
24
25
|
// imports them from the package, not from a file inside it.
|
|
25
|
-
export { breathe, isReportBand, text, typed, walk } from "./stream.js";
|
|
26
|
+
export { breathe, display, isReportBand, text, typed, walk } from "./stream.js";
|
|
26
27
|
export { isDiagnostic } from "./locate.js";
|
|
27
28
|
|
|
28
29
|
/** @typedef {import("./scope.js").Scope} Scope */
|
|
29
30
|
|
|
30
31
|
/** @type {(data: any) => any} */
|
|
31
32
|
let inputOf = (data) => data ?? {};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
let takeRows = (rows, n) => (n == null ? rows : rows.slice(0, n));
|
|
39
|
-
/** @type {(aggs: any, rows: any[], base: any) => Scope} */
|
|
40
|
-
let foldAggs = (aggs, rows, base) => {
|
|
33
|
+
// Every aggregate is computed before any is injected, so results never depend
|
|
34
|
+
// on declaration order; a sibling read inside an aggregate expression is always
|
|
35
|
+
// the pre-injection null (see SCHEMA.md, deliberate asymmetries). Hence the two
|
|
36
|
+
// passes: one to fold, one to write the values onto the report root.
|
|
37
|
+
/** @type {(aggs: any, rows: any[], base: any, root: any) => Scope} */
|
|
38
|
+
let foldAggs = (aggs, rows, base, root) => {
|
|
41
39
|
/** @type {Scope} */
|
|
42
40
|
let aggregates = {};
|
|
43
41
|
for (let [name, fold] of aggs) aggregates[name] = aggregateValue(fold, rows, base);
|
|
44
|
-
return aggregates;
|
|
45
|
-
};
|
|
46
|
-
/** @type {(root: any, aggregates: Scope, aggs: any) => void} */
|
|
47
|
-
let injectAggs = (root, aggregates, aggs) => {
|
|
48
42
|
for (let [name] of aggs) root[name] = aggregates[name];
|
|
43
|
+
return aggregates;
|
|
49
44
|
};
|
|
50
45
|
/** @type {(band: any, perPage: (items: any) => any) => any} */
|
|
51
46
|
let maybePage = (band, perPage) => band && perPage(band);
|
|
@@ -58,23 +53,6 @@ let pageOf = (pageHeader, pageFooter, perPage) => {
|
|
|
58
53
|
);
|
|
59
54
|
};
|
|
60
55
|
|
|
61
|
-
/**
|
|
62
|
-
* @type {(parts: any, ctx: any) => Generator<object, void, undefined>}
|
|
63
|
-
*/
|
|
64
|
-
function* reportEvents(parts, ctx) {
|
|
65
|
-
let { header, empty, band, footer, marking, columns } = parts;
|
|
66
|
-
let { root, aggregates, page, rows, base, runners } = ctx;
|
|
67
|
-
yield opt(
|
|
68
|
-
{ type: "report-start", params: root.params, aggregates },
|
|
69
|
-
{ page, columns, marking: marking() },
|
|
70
|
-
);
|
|
71
|
-
yield* header(base);
|
|
72
|
-
if (!rows.length && empty) yield* empty(base);
|
|
73
|
-
else yield* band(rows, base, runners);
|
|
74
|
-
yield* footer(base);
|
|
75
|
-
yield { type: "report-end" };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
56
|
/** @type {(name: any) => void} */
|
|
79
57
|
let checkTargetName = (name) => {
|
|
80
58
|
if (typeof name !== "string" || !NAME.test(name) || BLOCKED.has(name))
|
|
@@ -105,9 +83,23 @@ let runTarget = (compile, stream, data) => {
|
|
|
105
83
|
* @returns {string[]} All definition problems, in document order.
|
|
106
84
|
*/
|
|
107
85
|
export function validate(schema, funcs) {
|
|
108
|
-
return plan(schema, funcs).problems;
|
|
86
|
+
return plan(schema, funcs).problems.map((problem) => problem.message);
|
|
109
87
|
}
|
|
110
88
|
|
|
89
|
+
// The structured problems, frozen for handing out: the traversal's own objects
|
|
90
|
+
// go to exactly one caller, so freezing here cannot disturb a second reader.
|
|
91
|
+
// The diagnostic stays an engine-minted error and is deliberately not frozen.
|
|
92
|
+
/** @type {(problems: any[]) => readonly any[]} */
|
|
93
|
+
let freezeProblems = (problems) => Object.freeze(problems.map((problem) => Object.freeze(problem)));
|
|
94
|
+
|
|
95
|
+
// The per-node anchor sets, as frozen host data (CONTEXT.md, "Freeze"): which
|
|
96
|
+
// anchors and group handles each compiled source reads, keyed by schema path.
|
|
97
|
+
/** @type {(anchors: Map<string, Set<string>>) => any} */
|
|
98
|
+
let freezeAnchors = (anchors) =>
|
|
99
|
+
Object.freeze(
|
|
100
|
+
Object.fromEntries(Array.from(anchors, ([path, set]) => [path, Object.freeze([...set])])),
|
|
101
|
+
);
|
|
102
|
+
|
|
111
103
|
/**
|
|
112
104
|
* Compile a report once, stream structured render events against data many times.
|
|
113
105
|
*
|
|
@@ -125,13 +117,31 @@ export function validate(schema, funcs) {
|
|
|
125
117
|
* wording, or false when the render is covered. A thunk, so what it answers
|
|
126
118
|
* is this render's answer and not the compile's.
|
|
127
119
|
* @returns {{(data?: any): Generator<object, void, undefined>, names: string[],
|
|
128
|
-
* functions: string
|
|
120
|
+
* functions: { name: string, arity: number, doc?: string }[],
|
|
121
|
+
* paths: readonly any[]}} Event stream factory.
|
|
129
122
|
*/
|
|
130
123
|
function events(schema, funcs, options, marking) {
|
|
131
124
|
// The traversal collects every problem, so a thrown error is the first of the
|
|
132
125
|
// same list `validate()` would return.
|
|
133
126
|
let planned = plan(schema, funcs, options);
|
|
134
|
-
if (planned.problems.length)
|
|
127
|
+
if (planned.problems.length)
|
|
128
|
+
throw planned.diagnostic() || SyntaxError(planned.problems[0].message);
|
|
129
|
+
return assemble(planned, schema, marking);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Build the event stream factory from a settled traversal — the half of the
|
|
134
|
+
* one compile that runs only when the plan carried no problems, shared by
|
|
135
|
+
* `report()`'s throwing path and the instance's `plan()`.
|
|
136
|
+
*
|
|
137
|
+
* @param {ReturnType<typeof plan>} planned The finished traversal.
|
|
138
|
+
* @param {any} schema The report document, for locating `data` faults.
|
|
139
|
+
* @param {() => string | false} marking This render's unlicensed marking.
|
|
140
|
+
* @returns {{(data?: any): Generator<object, void, undefined>, names: string[],
|
|
141
|
+
* functions: { name: string, arity: number, doc?: string }[],
|
|
142
|
+
* paths: readonly any[]}} Event stream factory.
|
|
143
|
+
*/
|
|
144
|
+
function assemble(planned, schema, marking) {
|
|
135
145
|
// Null only for a non-object schema, which is itself a collected problem.
|
|
136
146
|
// `select` and `params` come from the traversal too: it checked them, so
|
|
137
147
|
// nothing here compiles or clones a second time.
|
|
@@ -143,6 +153,7 @@ function events(schema, funcs, options, marking) {
|
|
|
143
153
|
take,
|
|
144
154
|
aggs,
|
|
145
155
|
running,
|
|
156
|
+
style,
|
|
146
157
|
header,
|
|
147
158
|
empty,
|
|
148
159
|
columns,
|
|
@@ -159,7 +170,7 @@ function events(schema, funcs, options, marking) {
|
|
|
159
170
|
try {
|
|
160
171
|
return select(data);
|
|
161
172
|
} catch (error) {
|
|
162
|
-
locate("data", schema.data, error,
|
|
173
|
+
locate("data", schema.data, error, select.isDiagnostic(error) && relocateQuery);
|
|
163
174
|
}
|
|
164
175
|
};
|
|
165
176
|
|
|
@@ -174,12 +185,11 @@ function events(schema, funcs, options, marking) {
|
|
|
174
185
|
// aggregates (over the rows that remain). It cannot be lazy because the
|
|
175
186
|
// report header may interpolate report aggregates; only the banded walk
|
|
176
187
|
// itself streams.
|
|
177
|
-
let rows =
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
let aggregates = foldAggs(aggs, rows, base);
|
|
182
|
-
injectAggs(root, aggregates, aggs);
|
|
188
|
+
let rows = runSelect(root.input);
|
|
189
|
+
if (where) rows = rows.filter((row) => where(withRow(base, row)));
|
|
190
|
+
if (sort) rows = sort(rows, base);
|
|
191
|
+
if (take != null) rows = rows.slice(0, take);
|
|
192
|
+
let aggregates = foldAggs(aggs, rows, base, root);
|
|
183
193
|
// The report's runners, started per render like everything else here.
|
|
184
194
|
let runners = startRunners(running);
|
|
185
195
|
// Per-render page band closures: a paginated target calls them once per
|
|
@@ -188,18 +198,30 @@ function events(schema, funcs, options, marking) {
|
|
|
188
198
|
/** @type {(items: any) => (page: any) => any[]} */
|
|
189
199
|
let perPage = (items) => (page) => [...items(withPage(base, page))];
|
|
190
200
|
let page = pageOf(pageHeader, pageFooter, perPage);
|
|
191
|
-
// The
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
// (SCHEMA.md, "License
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
201
|
+
// The banded walk itself, over everything the pre-pass settled above. The
|
|
202
|
+
// marking rides on the opening event — its wording, so that a target owns
|
|
203
|
+
// only where it goes. Read as the event is yielded, so a render drained
|
|
204
|
+
// before verification settles counts as unlicensed (SCHEMA.md, "License
|
|
205
|
+
// keys").
|
|
206
|
+
/** @type {() => Generator<object, void, undefined>} */
|
|
207
|
+
function* walked() {
|
|
208
|
+
yield opt(
|
|
209
|
+
{ type: "report-start", params: root.params, aggregates },
|
|
210
|
+
{ page, columns, style: style(base), marking: marking() },
|
|
211
|
+
);
|
|
212
|
+
yield* header(base);
|
|
213
|
+
if (!rows.length && empty) yield* empty(base);
|
|
214
|
+
else yield* band(rows, base, runners);
|
|
215
|
+
yield* footer(base);
|
|
216
|
+
yield { type: "report-end" };
|
|
217
|
+
}
|
|
218
|
+
return walked();
|
|
199
219
|
};
|
|
200
220
|
return Object.assign(emit, {
|
|
201
221
|
names: Array.from(planned.names),
|
|
202
|
-
|
|
222
|
+
// Already signature objects — plan() paired each called name with its
|
|
223
|
+
// registry metadata through xprsn's signatures().
|
|
224
|
+
functions: planned.functions,
|
|
203
225
|
paths: select.paths,
|
|
204
226
|
});
|
|
205
227
|
}
|
|
@@ -217,31 +239,51 @@ function events(schema, funcs, options, marking) {
|
|
|
217
239
|
* license?: string, trust?: {publicKey?: string, release?: string}}} [options]
|
|
218
240
|
* Host configuration. `trust` is internal, not API — see ./license.js.
|
|
219
241
|
* @returns {{license: Promise<LicenseInfo>,
|
|
220
|
-
* report: (schema: any, funcs?: Record<string, Function>) => any
|
|
242
|
+
* report: (schema: any, funcs?: Record<string, Function>) => any,
|
|
243
|
+
* plan: (schema: any, funcs?: Record<string, Function>) => any}} The instance.
|
|
221
244
|
*/
|
|
222
245
|
export function quario(options) {
|
|
223
246
|
let { licensed, settled } = verify(options?.license, options?.trust);
|
|
247
|
+
// The wording is the instance's, where the key is; the stream is what
|
|
248
|
+
// states it, so nothing wraps a compiled stream to reach its first event.
|
|
249
|
+
/** @type {() => string | false} */
|
|
250
|
+
let marking = () => !licensed() && MARKING;
|
|
251
|
+
// The compiled report around a stream: what `report()` returns, and what
|
|
252
|
+
// `plan()` carries when the document had no problems.
|
|
253
|
+
/** @type {(stream: ReturnType<typeof events>) => any} */
|
|
254
|
+
let wrap = (stream) => ({
|
|
255
|
+
stream,
|
|
256
|
+
names: stream.names,
|
|
257
|
+
functions: stream.functions,
|
|
258
|
+
paths: stream.paths,
|
|
259
|
+
// Every render awaits verification before the target pulls its first
|
|
260
|
+
// event, so no render is marked merely for racing the check and no
|
|
261
|
+
// target carries that concern itself. Target shape problems throw
|
|
262
|
+
// synchronously — definition errors, like everything else — while a
|
|
263
|
+
// render is always a promise, even over a synchronous renderer.
|
|
264
|
+
render: (/** @type {any} */ target, /** @type {any} */ data) => {
|
|
265
|
+
checkTarget(target);
|
|
266
|
+
return settled.then(() => runTarget(target.compile, stream, data));
|
|
267
|
+
},
|
|
268
|
+
});
|
|
224
269
|
return {
|
|
225
270
|
license: settled,
|
|
226
271
|
/** @type {(schema: any, funcs?: Record<string, Function>) => any} */
|
|
227
272
|
report(schema, funcs) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
273
|
+
return wrap(events(schema, funcs, options, marking));
|
|
274
|
+
},
|
|
275
|
+
// The plan (CONTEXT.md): both readings of the one descent, kept.
|
|
276
|
+
// `report()` and `validate()` name what a caller wanted from it; this
|
|
277
|
+
// hands over the whole thing — the compiled report (null while the
|
|
278
|
+
// document has problems), every problem structurally, and the per-node
|
|
279
|
+
// anchor sets — so an editing host pays one traversal per edit, not two.
|
|
280
|
+
/** @type {(schema: any, funcs?: Record<string, Function>) => any} */
|
|
281
|
+
plan(schema, funcs) {
|
|
282
|
+
let planned = plan(schema, funcs, options);
|
|
231
283
|
return {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
paths: stream.paths,
|
|
236
|
-
// Every render awaits verification before the target pulls its first
|
|
237
|
-
// event, so no render is marked merely for racing the check and no
|
|
238
|
-
// target carries that concern itself. Target shape problems throw
|
|
239
|
-
// synchronously — definition errors, like everything else — while a
|
|
240
|
-
// render is always a promise, even over a synchronous renderer.
|
|
241
|
-
render: (/** @type {any} */ target, /** @type {any} */ data) => {
|
|
242
|
-
checkTarget(target);
|
|
243
|
-
return settled.then(() => runTarget(target.compile, stream, data));
|
|
244
|
-
},
|
|
284
|
+
report: planned.problems.length ? null : wrap(assemble(planned, schema, marking)),
|
|
285
|
+
problems: freezeProblems(planned.problems),
|
|
286
|
+
anchors: freezeAnchors(planned.anchors),
|
|
245
287
|
};
|
|
246
288
|
},
|
|
247
289
|
};
|
package/lib/license.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// reassigns them. A key is valid for every version released inside its window
|
|
12
12
|
// (LICENSE section 7), so validity compares ISO date strings and never reads
|
|
13
13
|
// a clock — accepted output stays accepted.
|
|
14
|
-
let RELEASE = "2026-
|
|
14
|
+
let RELEASE = "2026-09-01"; // x-release-please-date
|
|
15
15
|
// The verifying half of the signing pair: the 65-byte uncompressed P-256
|
|
16
16
|
// point, base64url. The private half never enters the repo;
|
|
17
17
|
// scripts/license/sign.mjs mints keys against it.
|
package/lib/locate.js
CHANGED
|
@@ -1,36 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Located diagnostics: where quario re-throws an engine's error with the
|
|
3
|
-
* band/item path and offending source attached
|
|
4
|
-
* the copy authenticated.
|
|
3
|
+
* band/item path and offending source attached.
|
|
5
4
|
*/
|
|
6
5
|
import { isDiagnostic as isQueryDiagnostic } from "padvinder";
|
|
7
6
|
import { isDiagnostic as isXprsnDiagnostic } from "xprsn";
|
|
8
7
|
import { isDiagnostic as isTemplateDiagnostic } from "sjabloon";
|
|
9
8
|
|
|
10
|
-
// The engines authenticate their diagnostics by identity (WeakMap/WeakSet), so
|
|
11
|
-
// the located copies `locate()` re-throws can't pass the upstream guards.
|
|
12
|
-
// quario registers each copy it makes from an authenticated original here,
|
|
13
|
-
// giving located diagnostics the same identity-based authentication.
|
|
14
|
-
/** @type {WeakSet<any>} */
|
|
15
|
-
let DIAG = new WeakSet();
|
|
16
|
-
|
|
17
9
|
/**
|
|
18
|
-
* True when an error is one of the stack's authenticated
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* `code`/`start`/`end`/`limit`/`actual`/`blocks` metadata.
|
|
22
|
-
* by identity: an error merely shaped like a diagnostic does
|
|
10
|
+
* True when an error is one of the stack's authenticated diagnostics — thrown
|
|
11
|
+
* by an engine (xprsn, sjabloon, or padvinder) directly, or re-thrown by
|
|
12
|
+
* quario through that engine's `relocate` — i.e. it safely carries the
|
|
13
|
+
* optional `code`/`start`/`end`/`limit`/`actual`/`blocks` metadata.
|
|
14
|
+
* Authentication is by identity: an error merely shaped like a diagnostic does
|
|
15
|
+
* not pass, and a located copy passes because the engine that minted the
|
|
16
|
+
* original made the copy.
|
|
23
17
|
*
|
|
24
18
|
* @param {unknown} error The caught value.
|
|
25
19
|
* @returns {boolean} Whether `error` is a located diagnostic.
|
|
26
20
|
*/
|
|
27
21
|
export function isDiagnostic(error) {
|
|
28
|
-
return (
|
|
29
|
-
DIAG.has(/** @type {any} */ (error)) ||
|
|
30
|
-
isXprsnDiagnostic(error) ||
|
|
31
|
-
isTemplateDiagnostic(error) ||
|
|
32
|
-
isQueryDiagnostic(error)
|
|
33
|
-
);
|
|
22
|
+
return isXprsnDiagnostic(error) || isTemplateDiagnostic(error) || isQueryDiagnostic(error);
|
|
34
23
|
}
|
|
35
24
|
|
|
36
25
|
/** @type {(msg: string) => never} */
|
|
@@ -38,43 +27,41 @@ export let err = (msg) => {
|
|
|
38
27
|
throw Error(msg);
|
|
39
28
|
};
|
|
40
29
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
let locationOf = (path, src) => (src === undefined ? path : path + " [" + String(src) + "]");
|
|
46
|
-
|
|
47
|
-
/** @type {(path: string, src: any, error: any) => any} */
|
|
48
|
-
let wrap = (path, src, error) => {
|
|
49
|
-
let location = locationOf(path, src);
|
|
50
|
-
if (error instanceof Error)
|
|
51
|
-
return new /** @type {any} */ (error.constructor)(location + ": " + error.message);
|
|
52
|
-
return new Error(location + ": " + String(error));
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/** @type {(located: any, error: any, keys: string[], extra: (value: any) => any) => void} */
|
|
56
|
-
let copyOwn = (located, error, keys, extra) => {
|
|
57
|
-
for (let key of keys) if (Object.hasOwn(error, key)) located[key] = extra(error[key]);
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/** @type {(located: any, error: any) => void} */
|
|
61
|
-
let copyBlocks = (located, error) => {
|
|
62
|
-
if (Object.hasOwn(error, "blocks"))
|
|
63
|
-
Object.defineProperty(located, "blocks", { value: error.blocks, enumerable: true });
|
|
64
|
-
};
|
|
30
|
+
// How a located error remembers where it happened as data: the traversal reads
|
|
31
|
+
// this to build a structured problem, so nothing parses a message back apart.
|
|
32
|
+
// A symbol, so the located copy's public shape stays exactly the engine's.
|
|
33
|
+
export let LOCATION = Symbol("quario.location");
|
|
65
34
|
|
|
66
|
-
/** @type {(
|
|
67
|
-
let
|
|
68
|
-
|
|
69
|
-
copyOwn(located, error, ["start", "end"], (value) => value + shift);
|
|
70
|
-
copyOwn(located, error, ["limit", "actual"], (value) => value);
|
|
71
|
-
copyBlocks(located, error);
|
|
72
|
-
DIAG.add(located);
|
|
35
|
+
/** @type {(path: string, msg: string) => never} */
|
|
36
|
+
export let fault = (path, msg) => {
|
|
37
|
+
throw Object.assign(Error(path + ": " + msg), { [LOCATION]: { path } });
|
|
73
38
|
};
|
|
74
39
|
|
|
75
|
-
/**
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Re-throw `error` located at `path`, with `src` — the offending author
|
|
42
|
+
* source — in the message.
|
|
43
|
+
*
|
|
44
|
+
* A diagnostic is relocated by the engine that authenticated it, so the copy
|
|
45
|
+
* carries every field that engine puts on it and passes that engine's own
|
|
46
|
+
* guard (ADR 0024). `relocate` is that engine's relocation — and a verdict as
|
|
47
|
+
* much as a callback: the call site holds the guard, because two of them are
|
|
48
|
+
* scoped to one evaluator rather than to the module, and `false` is its way of
|
|
49
|
+
* saying the guard rejected this error. Anything else — a host function's
|
|
50
|
+
* throw, quario's own verdict on image bytes — is wrapped as a plain error
|
|
51
|
+
* with no metadata at all.
|
|
52
|
+
*
|
|
53
|
+
* @type {(path: string, src: any, error: any, relocate?: false | ((e: any, o: { prefix: string, offset: number }) => any), offset?: number) => never}
|
|
54
|
+
*/
|
|
55
|
+
export let locate = (path, src, error, relocate, offset = 0) => {
|
|
56
|
+
let prefix = (src === undefined ? path : path + " [" + String(src) + "]") + ": ";
|
|
57
|
+
let where = { [LOCATION]: { path, source: src } };
|
|
58
|
+
if (relocate) throw Object.assign(relocate(error, { prefix, offset }), where);
|
|
59
|
+
// A host throw keeps its own class — the located error is the same kind of
|
|
60
|
+
// failure, named where it happened — and gains no diagnostic metadata.
|
|
61
|
+
throw Object.assign(
|
|
62
|
+
error instanceof Error
|
|
63
|
+
? new /** @type {any} */ (error.constructor)(prefix + error.message)
|
|
64
|
+
: Error(prefix + String(error)),
|
|
65
|
+
where,
|
|
66
|
+
);
|
|
80
67
|
};
|