@loom-forge/check 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/README.md +444 -0
- package/dist/index.d.ts +478 -0
- package/dist/index.js +867 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,867 @@
|
|
|
1
|
+
import { s, format, subtype, isSerializable } from '@tslite/type-core';
|
|
2
|
+
import { toJsonSchema, fromJsonSchema } from '@tslite/jsonschema';
|
|
3
|
+
import { inferValue } from '@tslite/infer';
|
|
4
|
+
import { s as s$1, createAnchors, check, spanOf, annotate, createEnv, anchorDiagnostics, lowerValue } from '@tslite/checker';
|
|
5
|
+
import { ASSERTER, createBoundary } from '@tslite/validate';
|
|
6
|
+
import { vanilla } from '@tslite/operators';
|
|
7
|
+
import { addressOf, isLive, compileDefinition, mountScopesOf, mountingOf, compileComponent, EMPTY_MOUNT, settledRoute } from '@loom-forge/forge';
|
|
8
|
+
import * as b from '@tslite/core/builders';
|
|
9
|
+
import { isExprMarker, EXPR_MARKER } from '@tslite/core';
|
|
10
|
+
import { parseExpression } from '@tslite/parser';
|
|
11
|
+
import { compile, DEFAULT_PROFILE } from '@loom-forge/tslite/compile';
|
|
12
|
+
import { FIELDS, slotOf, TREE } from '@loom-forge/core';
|
|
13
|
+
import { PRESETS } from '@tslite/capability';
|
|
14
|
+
|
|
15
|
+
// src/checker.ts
|
|
16
|
+
function createTsliteChecker() {
|
|
17
|
+
const inferType = (value) => inferValue(value);
|
|
18
|
+
const fromJS = (jsonSchema) => fromJsonSchema(jsonSchema);
|
|
19
|
+
return {
|
|
20
|
+
subtype: (a, b2) => subtype(a, b2),
|
|
21
|
+
inferType,
|
|
22
|
+
fromJsonSchema: fromJS,
|
|
23
|
+
toJsonSchema: (schema) => toJsonSchema(schema),
|
|
24
|
+
assignable: (value, jsonSchema) => subtype(inferType(value), fromJS(jsonSchema)),
|
|
25
|
+
format: (schema) => format(schema)
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function checkTree(node, registry, checker) {
|
|
29
|
+
const issues = [];
|
|
30
|
+
const visit = (n) => {
|
|
31
|
+
if (n.kind !== "node") return;
|
|
32
|
+
const descriptor = registry.get(n.type);
|
|
33
|
+
if (descriptor?.schema && !checker.assignable(n.props, descriptor.schema)) {
|
|
34
|
+
issues.push({
|
|
35
|
+
nodeId: n.id,
|
|
36
|
+
type: n.type,
|
|
37
|
+
expected: checker.format(checker.fromJsonSchema(descriptor.schema)),
|
|
38
|
+
got: checker.format(checker.inferType(n.props))
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
n.children.forEach(visit);
|
|
42
|
+
};
|
|
43
|
+
visit(node);
|
|
44
|
+
return issues;
|
|
45
|
+
}
|
|
46
|
+
var SCALARS = {
|
|
47
|
+
any: s.any,
|
|
48
|
+
unknown: s.unknown,
|
|
49
|
+
string: s.string,
|
|
50
|
+
number: s.number,
|
|
51
|
+
boolean: s.boolean,
|
|
52
|
+
null: s.null
|
|
53
|
+
};
|
|
54
|
+
function resolveTypeRef(ref, types = {}) {
|
|
55
|
+
if (typeof ref === "string") return types[ref] ?? SCALARS[ref] ?? s.unknown;
|
|
56
|
+
const o = ref;
|
|
57
|
+
if (typeof o.t === "string") return ref;
|
|
58
|
+
return fromJsonSchema(ref);
|
|
59
|
+
}
|
|
60
|
+
var unresolvedName = (ref, types) => typeof ref === "string" && !(ref in types) && !(ref in SCALARS) ? ref : void 0;
|
|
61
|
+
function resolveProps(props, types = {}) {
|
|
62
|
+
const shape = {};
|
|
63
|
+
for (const [rawKey, ref] of Object.entries(props ?? {})) {
|
|
64
|
+
const optional = rawKey.endsWith("?");
|
|
65
|
+
const key = optional ? rawKey.slice(0, -1) : rawKey;
|
|
66
|
+
const type = resolveTypeRef(ref, types);
|
|
67
|
+
shape[key] = optional ? { type: s.union([type, s.undefined]), optional: true } : { type };
|
|
68
|
+
}
|
|
69
|
+
return s.object(shape);
|
|
70
|
+
}
|
|
71
|
+
function componentType(ir, types = {}) {
|
|
72
|
+
return {
|
|
73
|
+
props: resolveProps(ir.props, types),
|
|
74
|
+
...ir.category ? { category: ir.category } : {},
|
|
75
|
+
entry: ir.entry
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function componentAssignable(child, contract, types = {}) {
|
|
79
|
+
if (contract.props === void 0) return true;
|
|
80
|
+
return subtype(child.props, resolveProps(contract.props, types));
|
|
81
|
+
}
|
|
82
|
+
function checkDeclarations(def, types = {}) {
|
|
83
|
+
const issues = [];
|
|
84
|
+
const report = (path, name) => {
|
|
85
|
+
issues.push({
|
|
86
|
+
code: "unknown-type-name",
|
|
87
|
+
message: `Type '${name}' is not declared: it is neither a scalar (${Object.keys(SCALARS).join(", ")}) nor a name in the type catalog.`,
|
|
88
|
+
severity: "error",
|
|
89
|
+
path,
|
|
90
|
+
address: addressOf(path),
|
|
91
|
+
source: name
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
for (const [rawKey, ref] of Object.entries(def.props ?? {})) {
|
|
95
|
+
const name = unresolvedName(ref, types);
|
|
96
|
+
if (name !== void 0) report(["props", rawKey], name);
|
|
97
|
+
}
|
|
98
|
+
for (const [key, decl] of Object.entries(def.state ?? {})) {
|
|
99
|
+
const name = unresolvedName(decl.type, types);
|
|
100
|
+
if (name !== void 0) report(["state", key], name);
|
|
101
|
+
}
|
|
102
|
+
return issues;
|
|
103
|
+
}
|
|
104
|
+
function declaredContextsOf(catalog, types = {}) {
|
|
105
|
+
const cache = /* @__PURE__ */ new Map();
|
|
106
|
+
return (name) => {
|
|
107
|
+
if (cache.has(name)) return cache.get(name);
|
|
108
|
+
const ref = catalog?.[name];
|
|
109
|
+
const schema = ref ? resolveTypeRef(ref, types) : void 0;
|
|
110
|
+
const ok = schema && isSerializable(schema) ? schema : void 0;
|
|
111
|
+
cache.set(name, ok);
|
|
112
|
+
return ok;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function checkContextCatalog(catalog, types = {}) {
|
|
116
|
+
const issues = [];
|
|
117
|
+
for (const [name, ref] of Object.entries(catalog ?? {})) {
|
|
118
|
+
const unresolved = unresolvedName(ref, types);
|
|
119
|
+
if (unresolved !== void 0) {
|
|
120
|
+
issues.push({
|
|
121
|
+
code: "unknown-type-name",
|
|
122
|
+
message: `Type '${unresolved}' is not declared: it is neither a scalar (${Object.keys(SCALARS).join(", ")}) nor a name in the type catalog.`,
|
|
123
|
+
severity: "error",
|
|
124
|
+
path: ["contexts", name],
|
|
125
|
+
address: addressOf(["contexts", name]),
|
|
126
|
+
source: unresolved
|
|
127
|
+
});
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (!isSerializable(resolveTypeRef(ref, types)))
|
|
131
|
+
issues.push({
|
|
132
|
+
code: "unknown-type-name",
|
|
133
|
+
message: `Context '${name}' must be serializable DATA: a contract with functions (or \`unknown\`/\`any\`) is not a contract. Behavior goes through events \u2192 ActionPort, which is the door that creates an edge in the graph.`,
|
|
134
|
+
severity: "error",
|
|
135
|
+
path: ["contexts", name],
|
|
136
|
+
address: addressOf(["contexts", name]),
|
|
137
|
+
source: name
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return issues;
|
|
141
|
+
}
|
|
142
|
+
function declaredParamsOf(updates, types = {}) {
|
|
143
|
+
return (action) => {
|
|
144
|
+
const decl = updates?.[action]?.params;
|
|
145
|
+
return decl ? resolveProps(decl, types) : void 0;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function hostParamsOf(port, types = {}) {
|
|
149
|
+
return (action) => {
|
|
150
|
+
const schema = port?.paramsOf?.(action);
|
|
151
|
+
return schema === void 0 || schema === null ? void 0 : resolveTypeRef(schema, types);
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function paramsResolver(...resolvers) {
|
|
155
|
+
return (action) => {
|
|
156
|
+
for (const resolve of resolvers) {
|
|
157
|
+
const found = resolve(action);
|
|
158
|
+
if (found) return found;
|
|
159
|
+
}
|
|
160
|
+
return void 0;
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function partialStateType(state, types = {}) {
|
|
164
|
+
return s.object(
|
|
165
|
+
Object.fromEntries(
|
|
166
|
+
Object.entries(state ?? {}).map(([k, decl]) => [
|
|
167
|
+
k,
|
|
168
|
+
{ type: resolveTypeRef(decl.type, types), optional: true }
|
|
169
|
+
])
|
|
170
|
+
)
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
function partialOf(schema) {
|
|
174
|
+
const o = schema;
|
|
175
|
+
if (o?.t !== "object" || !o.props) return schema;
|
|
176
|
+
return {
|
|
177
|
+
...o,
|
|
178
|
+
props: Object.fromEntries(
|
|
179
|
+
Object.entries(o.props).map(([key, field]) => [
|
|
180
|
+
key,
|
|
181
|
+
{ ...field, optional: true }
|
|
182
|
+
])
|
|
183
|
+
)
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function declaredPropsOf(sys, types = {}) {
|
|
187
|
+
const cache = /* @__PURE__ */ new Map();
|
|
188
|
+
return (type) => {
|
|
189
|
+
if (cache.has(type)) return cache.get(type);
|
|
190
|
+
const ir = sys.components.get(type);
|
|
191
|
+
const schema = ir ? componentType(ir, types).props : void 0;
|
|
192
|
+
cache.set(type, schema);
|
|
193
|
+
return schema;
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function stateTypeOf(state, types = {}) {
|
|
197
|
+
return (key) => {
|
|
198
|
+
const decl = state?.[key];
|
|
199
|
+
return decl ? resolveTypeRef(decl.type, types) : void 0;
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function propTypeOf(schema, key) {
|
|
203
|
+
const o = schema;
|
|
204
|
+
return o?.t === "object" ? o.props?.[key]?.type : void 0;
|
|
205
|
+
}
|
|
206
|
+
function hostResultOf(port, types = {}) {
|
|
207
|
+
return (action) => {
|
|
208
|
+
const schema = port?.resultOf?.(action);
|
|
209
|
+
return schema === void 0 || schema === null ? void 0 : resolveTypeRef(schema, types);
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
var DEFAULT_ASSERTER = ASSERTER;
|
|
213
|
+
var ctxRoot = (def, contexts) => {
|
|
214
|
+
const uses = def.context?.uses ?? [];
|
|
215
|
+
if (!contexts || uses.length === 0) return void 0;
|
|
216
|
+
const shape = {};
|
|
217
|
+
for (const name of uses)
|
|
218
|
+
if (contexts[name]) shape[name] = { type: contexts[name] };
|
|
219
|
+
return { ctx: s.object(shape) };
|
|
220
|
+
};
|
|
221
|
+
var stateSchema = (state, types) => s.object(
|
|
222
|
+
Object.fromEntries(
|
|
223
|
+
Object.entries(state ?? {}).map(([k, decl]) => [
|
|
224
|
+
k,
|
|
225
|
+
{ type: resolveTypeRef(decl.type, types) }
|
|
226
|
+
])
|
|
227
|
+
)
|
|
228
|
+
);
|
|
229
|
+
var envOf = (vars, opts) => {
|
|
230
|
+
const operators = opts.operators ?? vanilla;
|
|
231
|
+
const boundary = createBoundary({
|
|
232
|
+
types: opts.types ?? {},
|
|
233
|
+
reserved: [...Object.keys(operators), ...Object.keys(vars)]
|
|
234
|
+
});
|
|
235
|
+
return createEnv(
|
|
236
|
+
{ ...operators, ...boundary.check.vars, ...vars },
|
|
237
|
+
{ types: boundary.check.types, asserters: boundary.check.asserters }
|
|
238
|
+
);
|
|
239
|
+
};
|
|
240
|
+
function buildEnv(def, opts = {}) {
|
|
241
|
+
const types = opts.types ?? {};
|
|
242
|
+
return envOf(
|
|
243
|
+
{
|
|
244
|
+
props: resolveProps(def.props, types),
|
|
245
|
+
state: stateSchema(def.state, types),
|
|
246
|
+
...opts.data ? { data: opts.data } : {},
|
|
247
|
+
...ctxRoot(def, opts.contexts) ?? {},
|
|
248
|
+
// o PENDENTE é fato de uma INSTÂNCIA, e instância é o boundary de um componente vivo: só
|
|
249
|
+
// ele tem a raiz. Num componente puro, `pending` é `unknown-name` — e o remédio é um update
|
|
250
|
+
// ou um efeito, que é o que o torna vivo (ADR-018).
|
|
251
|
+
...isLive(def) ? { pending: PENDING } : {}
|
|
252
|
+
},
|
|
253
|
+
opts
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
var PENDING = s.record(s.boolean);
|
|
257
|
+
function buildInitEnv(def, opts = {}) {
|
|
258
|
+
return envOf(
|
|
259
|
+
{
|
|
260
|
+
props: resolveProps(def.props, opts.types ?? {}),
|
|
261
|
+
...ctxRoot(def, opts.contexts) ?? {}
|
|
262
|
+
},
|
|
263
|
+
opts
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
var IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
267
|
+
var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && !isExprMarker(v);
|
|
268
|
+
var NO_SYSTEM = {
|
|
269
|
+
slotScopesOf: () => void 0,
|
|
270
|
+
vocabulary: /* @__PURE__ */ new Set()
|
|
271
|
+
};
|
|
272
|
+
var ACTION_ERROR = s$1.object({
|
|
273
|
+
code: { type: s$1.string },
|
|
274
|
+
message: { type: s$1.string, optional: true },
|
|
275
|
+
data: { type: s$1.unknown, optional: true }
|
|
276
|
+
});
|
|
277
|
+
function lowerComponent(authored, opts = {}) {
|
|
278
|
+
const shapeOf = opts.shapeOf ?? (() => "list");
|
|
279
|
+
const propsOf = opts.propsOf ?? (() => void 0);
|
|
280
|
+
const paramsOf = opts.paramsOf ?? (() => void 0);
|
|
281
|
+
const exhaustiveParams = opts.exhaustiveParams ?? (() => true);
|
|
282
|
+
const weak = /* @__PURE__ */ new Set();
|
|
283
|
+
const contextOf = opts.contextOf ?? (() => void 0);
|
|
284
|
+
const emitsOf = opts.emitsOf ?? (() => void 0);
|
|
285
|
+
const resultOf = opts.resultOf ?? (() => void 0);
|
|
286
|
+
const compiled = opts.compiled ?? compileDefinition(authored, compile);
|
|
287
|
+
const def = compiled.def;
|
|
288
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
289
|
+
const texts = /* @__PURE__ */ new Map();
|
|
290
|
+
const failed = /* @__PURE__ */ new Set();
|
|
291
|
+
const sugared = (address) => address.startsWith("provides.") || address.startsWith("commands.") ? `#0.${address}` : void 0;
|
|
292
|
+
for (const island of compiled.islands) {
|
|
293
|
+
nodes.set(island.address, island.node);
|
|
294
|
+
texts.set(island.address, island.text);
|
|
295
|
+
const alias = sugared(island.address);
|
|
296
|
+
if (alias !== void 0 && !nodes.has(alias)) {
|
|
297
|
+
nodes.set(alias, island.node);
|
|
298
|
+
texts.set(alias, island.text);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
for (const d of compiled.diagnostics) {
|
|
302
|
+
failed.add(d.address);
|
|
303
|
+
const alias = sugared(d.address);
|
|
304
|
+
if (alias !== void 0) failed.add(alias);
|
|
305
|
+
}
|
|
306
|
+
const anchors = createAnchors();
|
|
307
|
+
const islands = [];
|
|
308
|
+
const frames = /* @__PURE__ */ new Map();
|
|
309
|
+
const pendingFrames = [];
|
|
310
|
+
let temp = 0;
|
|
311
|
+
let call = 0;
|
|
312
|
+
let act = 0;
|
|
313
|
+
let evt = 0;
|
|
314
|
+
let ctxn = 0;
|
|
315
|
+
let ini = 0;
|
|
316
|
+
let aln = 0;
|
|
317
|
+
const root = {
|
|
318
|
+
...def.body,
|
|
319
|
+
...def.provides ? { provides: { ...def.provides, ...def.body.provides } } : {},
|
|
320
|
+
...def.commands ? { commands: { ...def.commands, ...def.body.commands } } : {}
|
|
321
|
+
};
|
|
322
|
+
const mounting = opts.mounting ?? NO_SYSTEM;
|
|
323
|
+
const scopesAt = mountScopesOf(root, mounting);
|
|
324
|
+
const routing = {
|
|
325
|
+
updates: new Set(Object.keys(def.updates ?? {})),
|
|
326
|
+
vocabulary: /* @__PURE__ */ new Set([
|
|
327
|
+
...mountingOf([
|
|
328
|
+
compileComponent(def, { components: /* @__PURE__ */ new Set([def.component]) })
|
|
329
|
+
]).vocabulary,
|
|
330
|
+
...mounting.vocabulary
|
|
331
|
+
])
|
|
332
|
+
};
|
|
333
|
+
const nameNode = (name, path) => {
|
|
334
|
+
const address = addressOf(path);
|
|
335
|
+
texts.set(address, name);
|
|
336
|
+
const node = b.identifier(name);
|
|
337
|
+
islands.push({ node, path, address, source: name });
|
|
338
|
+
return anchors.stamp(node, path);
|
|
339
|
+
};
|
|
340
|
+
const fromPath = (observed, path) => {
|
|
341
|
+
try {
|
|
342
|
+
const node = parseExpression(observed);
|
|
343
|
+
const address = addressOf(path);
|
|
344
|
+
texts.set(address, observed);
|
|
345
|
+
islands.push({ node, path, address, source: observed });
|
|
346
|
+
return anchors.stamp(node, path);
|
|
347
|
+
} catch {
|
|
348
|
+
return void 0;
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
const lowerAt = (value, path, extras = []) => {
|
|
352
|
+
const expr = lowerValue(prepared(value, path), path, anchors, {
|
|
353
|
+
child: (p, key) => [...p, key],
|
|
354
|
+
island: (marker, p) => {
|
|
355
|
+
const address = addressOf(p);
|
|
356
|
+
const node = nodes.get(address) ?? b.fromExprMarker(marker);
|
|
357
|
+
const source = texts.get(address);
|
|
358
|
+
islands.push({
|
|
359
|
+
node,
|
|
360
|
+
path: p,
|
|
361
|
+
address,
|
|
362
|
+
...source ? { source } : {}
|
|
363
|
+
});
|
|
364
|
+
return node;
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
if (extras.length > 0 && expr.type === "ObjectExpression")
|
|
368
|
+
for (const extra of extras)
|
|
369
|
+
expr.properties.push(
|
|
370
|
+
anchors.stamp(
|
|
371
|
+
b.property(
|
|
372
|
+
b.literal(extra.key),
|
|
373
|
+
extra.value
|
|
374
|
+
),
|
|
375
|
+
extra.path
|
|
376
|
+
)
|
|
377
|
+
);
|
|
378
|
+
return expr;
|
|
379
|
+
};
|
|
380
|
+
const ISLAND = { [EXPR_MARKER]: "Island" };
|
|
381
|
+
const prepared = (value, path) => {
|
|
382
|
+
const address = addressOf(path);
|
|
383
|
+
if (nodes.has(address)) return ISLAND;
|
|
384
|
+
if (typeof value === "string") return failed.has(address) ? null : value;
|
|
385
|
+
if (Array.isArray(value))
|
|
386
|
+
return value.map((x, i) => prepared(x, [...path, i]));
|
|
387
|
+
if (isRecord(value))
|
|
388
|
+
return Object.fromEntries(
|
|
389
|
+
Object.entries(value).map(([k, v]) => [k, prepared(v, [...path, k])])
|
|
390
|
+
);
|
|
391
|
+
return value;
|
|
392
|
+
};
|
|
393
|
+
const stmt = (e) => b.expressionStatement(e);
|
|
394
|
+
const body = [];
|
|
395
|
+
const initBody = [];
|
|
396
|
+
const push = (st) => {
|
|
397
|
+
if (st) body.push(st);
|
|
398
|
+
};
|
|
399
|
+
const hasIsland = (v) => {
|
|
400
|
+
if (isExprMarker(v)) return true;
|
|
401
|
+
if (Array.isArray(v)) return v.some(hasIsland);
|
|
402
|
+
if (v && typeof v === "object") return Object.values(v).some(hasIsland);
|
|
403
|
+
return false;
|
|
404
|
+
};
|
|
405
|
+
const emitValue = (v, path, expects) => {
|
|
406
|
+
if (!hasIsland(v)) return;
|
|
407
|
+
const e = lowerAt(v, path);
|
|
408
|
+
push(
|
|
409
|
+
expects === "boolean" ? b.variableDeclaration("const", [
|
|
410
|
+
b.variableDeclarator(
|
|
411
|
+
annotate(b.identifier(`__t${temp++}`), s$1.boolean),
|
|
412
|
+
e
|
|
413
|
+
)
|
|
414
|
+
]) : stmt(e)
|
|
415
|
+
);
|
|
416
|
+
};
|
|
417
|
+
const emitIntent = (intent, path, scopes, received) => {
|
|
418
|
+
const at = [...path, "params"];
|
|
419
|
+
const route = typeof intent.action === "string" ? settledRoute(intent.action, scopes, routing) : void 0;
|
|
420
|
+
const target = route === void 0 || route.kind === "command" ? void 0 : route.action;
|
|
421
|
+
const declared = target === void 0 ? void 0 : paramsOf(target);
|
|
422
|
+
if (declared && target !== void 0 && !exhaustiveParams(target))
|
|
423
|
+
weak.add(addressOf(at));
|
|
424
|
+
const supplied = [];
|
|
425
|
+
for (const key of intent.resolve ?? [])
|
|
426
|
+
if (typeof key === "string")
|
|
427
|
+
supplied.push({
|
|
428
|
+
key,
|
|
429
|
+
...declared && propTypeOf(declared, key) ? { type: propTypeOf(declared, key) } : {},
|
|
430
|
+
path: [...path, "resolve"]
|
|
431
|
+
});
|
|
432
|
+
if (declared && route && route.kind !== "command" && isRecord(route.bound)) {
|
|
433
|
+
const written = isRecord(intent.params) ? intent.params : {};
|
|
434
|
+
for (const key of Object.keys(route.bound))
|
|
435
|
+
if (!(key in written) && !supplied.some((s4) => s4.key === key) && propTypeOf(declared, key))
|
|
436
|
+
supplied.push({
|
|
437
|
+
key,
|
|
438
|
+
type: propTypeOf(declared, key),
|
|
439
|
+
path: [...path, "action"]
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
const named = supplied.map((s4, i) => ({
|
|
443
|
+
...s4,
|
|
444
|
+
local: IDENTIFIER.test(s4.key) && s4.key !== "event" ? s4.key : `__s${i}`
|
|
445
|
+
}));
|
|
446
|
+
const inner = [];
|
|
447
|
+
if (intent.params !== void 0) {
|
|
448
|
+
if (declared && isRecord(intent.params))
|
|
449
|
+
inner.push(
|
|
450
|
+
b.variableDeclaration("const", [
|
|
451
|
+
b.variableDeclarator(
|
|
452
|
+
annotate(b.identifier(`__e${evt++}`), declared),
|
|
453
|
+
lowerAt(
|
|
454
|
+
intent.params,
|
|
455
|
+
at,
|
|
456
|
+
named.map((s4) => ({
|
|
457
|
+
key: s4.key,
|
|
458
|
+
value: b.identifier(s4.local),
|
|
459
|
+
path: s4.path
|
|
460
|
+
}))
|
|
461
|
+
)
|
|
462
|
+
)
|
|
463
|
+
])
|
|
464
|
+
);
|
|
465
|
+
else if (hasIsland(intent.params))
|
|
466
|
+
inner.push(stmt(lowerAt(intent.params, at)));
|
|
467
|
+
} else if (declared && named.length > 0)
|
|
468
|
+
inner.push(
|
|
469
|
+
b.variableDeclaration("const", [
|
|
470
|
+
b.variableDeclarator(
|
|
471
|
+
annotate(b.identifier(`__e${evt++}`), declared),
|
|
472
|
+
lowerAt(
|
|
473
|
+
{},
|
|
474
|
+
at,
|
|
475
|
+
named.map((s4) => ({
|
|
476
|
+
key: s4.key,
|
|
477
|
+
value: b.identifier(s4.local),
|
|
478
|
+
path: s4.path
|
|
479
|
+
}))
|
|
480
|
+
)
|
|
481
|
+
)
|
|
482
|
+
])
|
|
483
|
+
);
|
|
484
|
+
const outer = body.length;
|
|
485
|
+
if (intent.then)
|
|
486
|
+
emitIntent(
|
|
487
|
+
intent.then,
|
|
488
|
+
[...path, "then"],
|
|
489
|
+
scopes,
|
|
490
|
+
route?.kind === "update" ? s$1.undefined : (target === void 0 ? void 0 : resultOf(target)) ?? s$1.unknown
|
|
491
|
+
);
|
|
492
|
+
if (intent.catch)
|
|
493
|
+
emitIntent(intent.catch, [...path, "catch"], scopes, ACTION_ERROR);
|
|
494
|
+
inner.push(...body.splice(outer));
|
|
495
|
+
if (inner.length === 0) return;
|
|
496
|
+
const event = annotate(b.identifier("event"), received);
|
|
497
|
+
anchors.stamp(event, path);
|
|
498
|
+
push(
|
|
499
|
+
stmt(
|
|
500
|
+
b.arrowFunctionExpression(
|
|
501
|
+
[
|
|
502
|
+
event,
|
|
503
|
+
...named.map((s4) => {
|
|
504
|
+
const param = nameNode(s4.local, s4.path);
|
|
505
|
+
return s4.type ? annotate(param, s4.type) : param;
|
|
506
|
+
})
|
|
507
|
+
],
|
|
508
|
+
b.blockStatement(inner)
|
|
509
|
+
)
|
|
510
|
+
)
|
|
511
|
+
);
|
|
512
|
+
};
|
|
513
|
+
const wholeOf = (v) => isExprMarker(v) ? v : void 0;
|
|
514
|
+
const emitInits = () => {
|
|
515
|
+
for (const [key, decl] of Object.entries(def.state ?? {})) {
|
|
516
|
+
if (decl.init === void 0) continue;
|
|
517
|
+
const at = ["state", key, "init"];
|
|
518
|
+
if (!hasIsland(decl.init) && failed.has(addressOf(at))) continue;
|
|
519
|
+
const expr = lowerAt(decl.init, at);
|
|
520
|
+
const type = opts.stateTypeOf?.(key);
|
|
521
|
+
const id = b.identifier(`__i${ini++}`);
|
|
522
|
+
initBody.push(
|
|
523
|
+
b.variableDeclaration("const", [
|
|
524
|
+
b.variableDeclarator(
|
|
525
|
+
type ? annotate(id, type) : id,
|
|
526
|
+
expr
|
|
527
|
+
)
|
|
528
|
+
])
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
const emitActions = () => {
|
|
533
|
+
const setType = opts.setType;
|
|
534
|
+
if (!setType) return;
|
|
535
|
+
for (const [name, action] of Object.entries(def.updates ?? {})) {
|
|
536
|
+
const set = action.set;
|
|
537
|
+
if (set === void 0) continue;
|
|
538
|
+
const frame = ["updates", name];
|
|
539
|
+
const decl = b.variableDeclaration("const", [
|
|
540
|
+
b.variableDeclarator(
|
|
541
|
+
annotate(b.identifier(`__s${act++}`), setType),
|
|
542
|
+
lowerAt(set, [...frame, "set"])
|
|
543
|
+
)
|
|
544
|
+
]);
|
|
545
|
+
const param = annotate(
|
|
546
|
+
b.identifier("params"),
|
|
547
|
+
paramsOf(name) ?? s$1.unknown
|
|
548
|
+
);
|
|
549
|
+
anchors.stamp(param, frame);
|
|
550
|
+
const block = b.blockStatement([decl]);
|
|
551
|
+
frames.set(addressOf(frame), block);
|
|
552
|
+
push(
|
|
553
|
+
stmt(
|
|
554
|
+
b.arrowFunctionExpression(
|
|
555
|
+
[param],
|
|
556
|
+
block
|
|
557
|
+
)
|
|
558
|
+
)
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
const emitEffects = () => {
|
|
563
|
+
for (const [name, effect] of Object.entries(def.effects ?? {})) {
|
|
564
|
+
const at = ["effects", name];
|
|
565
|
+
effect.on.forEach((observed, i) => {
|
|
566
|
+
const e = fromPath(observed, [...at, "on", i]);
|
|
567
|
+
if (e) push(stmt(e));
|
|
568
|
+
});
|
|
569
|
+
emitIntent(effect, at, [EMPTY_MOUNT], s$1.undefined);
|
|
570
|
+
if (effect.cleanup)
|
|
571
|
+
emitIntent(
|
|
572
|
+
effect.cleanup,
|
|
573
|
+
[...at, "cleanup"],
|
|
574
|
+
[EMPTY_MOUNT],
|
|
575
|
+
s$1.undefined
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
const walk = (node, path) => {
|
|
580
|
+
const base = path;
|
|
581
|
+
const data = wholeOf(node.each);
|
|
582
|
+
const binding = typeof node.as === "string" ? node.as : void 0;
|
|
583
|
+
const inner = [];
|
|
584
|
+
const outer = body.length;
|
|
585
|
+
const pending = pendingFrames.length;
|
|
586
|
+
pendingFrames.push(addressOf(base));
|
|
587
|
+
const callee = node.el == null && typeof node.type === "string" ? propsOf(node.type) : void 0;
|
|
588
|
+
for (const [field, role] of Object.entries(FIELDS)) {
|
|
589
|
+
if (role !== "slot" || field === "each") continue;
|
|
590
|
+
if (field === "props" && callee) continue;
|
|
591
|
+
if (field === "provides") continue;
|
|
592
|
+
const value = node[field];
|
|
593
|
+
if (value === void 0) continue;
|
|
594
|
+
const decl = slotOf(field);
|
|
595
|
+
if (field === "commands") {
|
|
596
|
+
for (const [name, alias] of Object.entries(
|
|
597
|
+
value
|
|
598
|
+
)) {
|
|
599
|
+
if (typeof alias === "string") continue;
|
|
600
|
+
const at = [...base, "commands", name, "params"];
|
|
601
|
+
const declared = paramsOf(alias.action);
|
|
602
|
+
if (!declared || !isRecord(alias.params)) {
|
|
603
|
+
emitValue(alias.params, at);
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
if (!exhaustiveParams(alias.action)) weak.add(addressOf(at));
|
|
607
|
+
push(
|
|
608
|
+
b.variableDeclaration("const", [
|
|
609
|
+
b.variableDeclarator(
|
|
610
|
+
annotate(
|
|
611
|
+
b.identifier(`__a${aln++}`),
|
|
612
|
+
partialOf(declared)
|
|
613
|
+
),
|
|
614
|
+
lowerAt(alias.params, at)
|
|
615
|
+
)
|
|
616
|
+
])
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
} else if (decl?.shape === "intentParams") {
|
|
620
|
+
const scopes = scopesAt.get(node) ?? [];
|
|
621
|
+
for (const [name, binding2] of Object.entries(
|
|
622
|
+
value
|
|
623
|
+
))
|
|
624
|
+
emitIntent(
|
|
625
|
+
binding2,
|
|
626
|
+
[...base, field, name],
|
|
627
|
+
scopes,
|
|
628
|
+
(node.el == null && node.type !== void 0 ? emitsOf(node.type, name) : void 0) ?? s$1.unknown
|
|
629
|
+
);
|
|
630
|
+
} else emitValue(value, [...base, field], decl?.expects);
|
|
631
|
+
}
|
|
632
|
+
if (node.provides)
|
|
633
|
+
for (const [name, value] of Object.entries(node.provides)) {
|
|
634
|
+
const declared = contextOf(name);
|
|
635
|
+
const at = [...base, "provides", name];
|
|
636
|
+
if (!declared) {
|
|
637
|
+
emitValue(value, at);
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
const e = lowerAt(value, at);
|
|
641
|
+
push(
|
|
642
|
+
b.variableDeclaration("const", [
|
|
643
|
+
b.variableDeclarator(
|
|
644
|
+
annotate(b.identifier(`__x${ctxn++}`), declared),
|
|
645
|
+
e
|
|
646
|
+
)
|
|
647
|
+
])
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
if (callee)
|
|
651
|
+
push(
|
|
652
|
+
b.variableDeclaration("const", [
|
|
653
|
+
b.variableDeclarator(
|
|
654
|
+
annotate(b.identifier(`__c${call++}`), callee),
|
|
655
|
+
lowerAt(node.props ?? {}, [...base, "props"])
|
|
656
|
+
)
|
|
657
|
+
])
|
|
658
|
+
);
|
|
659
|
+
for (const [field, shape] of Object.entries(TREE)) {
|
|
660
|
+
const value = node[field];
|
|
661
|
+
if (value === void 0) continue;
|
|
662
|
+
if (shape === "list" && Array.isArray(value))
|
|
663
|
+
value.forEach((child, i) => walk(child, [...path, i]));
|
|
664
|
+
else if (shape === "node" && value && typeof value === "object")
|
|
665
|
+
walk(value, [...path, field]);
|
|
666
|
+
}
|
|
667
|
+
if (!data || !binding) return;
|
|
668
|
+
inner.push(...body.splice(outer));
|
|
669
|
+
const coll = lowerAt(data, [...base, "each"]);
|
|
670
|
+
const param = nameNode(binding, [...base, "as"]);
|
|
671
|
+
const block = b.blockStatement(inner);
|
|
672
|
+
for (const address of pendingFrames.splice(pending))
|
|
673
|
+
frames.set(address, block);
|
|
674
|
+
const fn = b.arrowFunctionExpression(
|
|
675
|
+
[param],
|
|
676
|
+
block
|
|
677
|
+
);
|
|
678
|
+
push(
|
|
679
|
+
stmt(
|
|
680
|
+
shapeOf(coll) === "list" ? b.callExpression(b.identifier("map"), [
|
|
681
|
+
fn,
|
|
682
|
+
coll
|
|
683
|
+
]) : b.callExpression(fn, [
|
|
684
|
+
coll
|
|
685
|
+
])
|
|
686
|
+
)
|
|
687
|
+
);
|
|
688
|
+
};
|
|
689
|
+
walk(root, [0]);
|
|
690
|
+
emitInits();
|
|
691
|
+
emitActions();
|
|
692
|
+
emitEffects();
|
|
693
|
+
const rootBlock = b.blockStatement(body);
|
|
694
|
+
for (const address of pendingFrames.splice(0)) frames.set(address, rootBlock);
|
|
695
|
+
const program = b.arrowFunctionExpression(
|
|
696
|
+
[],
|
|
697
|
+
rootBlock
|
|
698
|
+
);
|
|
699
|
+
const inits = initBody.length ? b.arrowFunctionExpression(
|
|
700
|
+
[],
|
|
701
|
+
b.blockStatement(initBody)
|
|
702
|
+
) : void 0;
|
|
703
|
+
return {
|
|
704
|
+
program,
|
|
705
|
+
anchors,
|
|
706
|
+
texts,
|
|
707
|
+
frames,
|
|
708
|
+
islands,
|
|
709
|
+
compiled,
|
|
710
|
+
weak,
|
|
711
|
+
...inits ? { inits } : {}
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
var checkerProfileOf = (deploy) => ({
|
|
715
|
+
...PRESETS.complete,
|
|
716
|
+
name: `${deploy.name}:types`,
|
|
717
|
+
...deploy.types ? { types: deploy.types } : {}
|
|
718
|
+
});
|
|
719
|
+
var isWeak = (address, weak) => {
|
|
720
|
+
for (const prefix of weak)
|
|
721
|
+
if (address === prefix || address.startsWith(`${prefix}.`)) return true;
|
|
722
|
+
return false;
|
|
723
|
+
};
|
|
724
|
+
var exhaustiveHostParams = (port) => {
|
|
725
|
+
const level = port?.provenanceOf?.("params");
|
|
726
|
+
return level === "inferred" || level === "verified";
|
|
727
|
+
};
|
|
728
|
+
var issuesOf = (root, diagnostics, lowered, fallback) => anchorDiagnostics(root, diagnostics, lowered.anchors, {
|
|
729
|
+
fallback,
|
|
730
|
+
textOf: (path) => lowered.texts.get(addressOf(path))
|
|
731
|
+
}).map(({ diagnostic, anchor, span }) => {
|
|
732
|
+
const address = addressOf(anchor);
|
|
733
|
+
const source = lowered.texts.get(address);
|
|
734
|
+
return {
|
|
735
|
+
code: String(diagnostic.code ?? "error"),
|
|
736
|
+
message: String(diagnostic.message ?? ""),
|
|
737
|
+
severity: diagnostic.severity === "warning" ? "warning" : "error",
|
|
738
|
+
path: anchor,
|
|
739
|
+
address,
|
|
740
|
+
...source === void 0 ? {} : { source },
|
|
741
|
+
...span === void 0 ? {} : { span }
|
|
742
|
+
};
|
|
743
|
+
});
|
|
744
|
+
function checkComponent(def, env, opts = {}) {
|
|
745
|
+
return [...analyzeComponent(def, env, opts).issues];
|
|
746
|
+
}
|
|
747
|
+
function analyzeComponent(def, env, opts = {}) {
|
|
748
|
+
const profile = checkerProfileOf(opts.profile ?? DEFAULT_PROFILE);
|
|
749
|
+
const lowered = lowerComponent(def, {
|
|
750
|
+
...opts.compiled ? { compiled: opts.compiled } : {},
|
|
751
|
+
shapeOf: (expression) => dataShape(expression, env),
|
|
752
|
+
...opts.propsOf ? { propsOf: opts.propsOf } : {},
|
|
753
|
+
// o `set` de um update local é um call-site sobre o ESTADO: mesmo mecanismo, outra
|
|
754
|
+
// superfície. O tipo sai das declarações do próprio componente, então não há seam.
|
|
755
|
+
setType: partialStateType(def.state, opts.types),
|
|
756
|
+
// a MESMA ordem do runtime: update local primeiro, e o que não resolve aqui sai pela porta.
|
|
757
|
+
paramsOf: paramsResolver(
|
|
758
|
+
declaredParamsOf(def.updates, opts.types),
|
|
759
|
+
hostParamsOf(opts.actions, opts.types)
|
|
760
|
+
),
|
|
761
|
+
// um update local é sempre exaustivo (a declaração É a verdade); a ação do host vale o que
|
|
762
|
+
// a proveniência disser.
|
|
763
|
+
exhaustiveParams: (action) => def.updates?.[action] !== void 0 || exhaustiveHostParams(opts.actions),
|
|
764
|
+
...opts.emitsOf ? { emitsOf: opts.emitsOf } : {},
|
|
765
|
+
resultOf: hostResultOf(opts.actions, opts.types),
|
|
766
|
+
...opts.mounting ? { mounting: opts.mounting } : {},
|
|
767
|
+
stateTypeOf: stateTypeOf(def.state, opts.types),
|
|
768
|
+
contextOf: declaredContextsOf(opts.contexts, opts.types)
|
|
769
|
+
});
|
|
770
|
+
const { program, inits, weak, compiled } = lowered;
|
|
771
|
+
const issues = [
|
|
772
|
+
...checkDeclarations(def, opts.types),
|
|
773
|
+
...checkContextCatalog(opts.contexts, opts.types)
|
|
774
|
+
];
|
|
775
|
+
if (opts.compiled === void 0)
|
|
776
|
+
issues.push(
|
|
777
|
+
...compiled.diagnostics.map((d) => ({
|
|
778
|
+
code: d.code,
|
|
779
|
+
message: d.message,
|
|
780
|
+
severity: d.severity,
|
|
781
|
+
path: d.path,
|
|
782
|
+
address: d.address,
|
|
783
|
+
...d.source === void 0 ? {} : { source: d.source },
|
|
784
|
+
...d.span === void 0 ? {} : { span: d.span }
|
|
785
|
+
}))
|
|
786
|
+
);
|
|
787
|
+
const initEnv = buildInitEnv(def, {
|
|
788
|
+
...opts.types ? { types: opts.types } : {},
|
|
789
|
+
...opts.contexts ? {
|
|
790
|
+
contexts: Object.fromEntries(
|
|
791
|
+
Object.entries(opts.contexts).map(([name, ref]) => [
|
|
792
|
+
name,
|
|
793
|
+
resolveTypeRef(ref, opts.types ?? {})
|
|
794
|
+
])
|
|
795
|
+
)
|
|
796
|
+
} : {}
|
|
797
|
+
});
|
|
798
|
+
const seeded = inits ? check(inits, initEnv, {
|
|
799
|
+
profile,
|
|
800
|
+
...opts.limits ? { limits: opts.limits } : {}
|
|
801
|
+
}) : void 0;
|
|
802
|
+
if (inits && seeded)
|
|
803
|
+
issues.push(
|
|
804
|
+
...issuesOf(inits, seeded.diagnostics, lowered, ["state"])
|
|
805
|
+
);
|
|
806
|
+
const body = check(program, env, {
|
|
807
|
+
profile,
|
|
808
|
+
...opts.limits ? { limits: opts.limits } : {}
|
|
809
|
+
});
|
|
810
|
+
issues.push(...issuesOf(program, body.diagnostics, lowered, [0]));
|
|
811
|
+
return {
|
|
812
|
+
issues: issues.map(
|
|
813
|
+
(issue) => issue.code === "excess-property" && isWeak(issue.address, weak) ? { ...issue, severity: "warning" } : issue
|
|
814
|
+
),
|
|
815
|
+
lowered,
|
|
816
|
+
body,
|
|
817
|
+
initEnv,
|
|
818
|
+
...seeded ? { inits: seeded } : {}
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
function checkExpression(source, env, path, opts = {}) {
|
|
822
|
+
const address = addressOf(path);
|
|
823
|
+
let ast;
|
|
824
|
+
try {
|
|
825
|
+
ast = parseExpression(source);
|
|
826
|
+
} catch (error) {
|
|
827
|
+
return [
|
|
828
|
+
{
|
|
829
|
+
code: "parse-error",
|
|
830
|
+
message: String(error?.message ?? error),
|
|
831
|
+
severity: "error",
|
|
832
|
+
path,
|
|
833
|
+
address,
|
|
834
|
+
source
|
|
835
|
+
}
|
|
836
|
+
];
|
|
837
|
+
}
|
|
838
|
+
const { diagnostics } = check(ast, env, {
|
|
839
|
+
profile: checkerProfileOf(opts.profile ?? DEFAULT_PROFILE),
|
|
840
|
+
...opts.resultType ? { resultType: opts.resultType } : {},
|
|
841
|
+
...opts.limits ? { limits: opts.limits } : {}
|
|
842
|
+
});
|
|
843
|
+
return diagnostics.map((d) => {
|
|
844
|
+
const span = spanOf(source, d.node?.loc);
|
|
845
|
+
return {
|
|
846
|
+
code: String(d.code ?? "error"),
|
|
847
|
+
message: String(d.message ?? ""),
|
|
848
|
+
severity: d.severity === "warning" ? "warning" : "error",
|
|
849
|
+
path,
|
|
850
|
+
address,
|
|
851
|
+
source,
|
|
852
|
+
...span === void 0 ? {} : { span }
|
|
853
|
+
};
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
function dataShape(expression, env) {
|
|
857
|
+
try {
|
|
858
|
+
const t = check(expression, env).type;
|
|
859
|
+
return t?.t === "array" ? "list" : "value";
|
|
860
|
+
} catch {
|
|
861
|
+
return "list";
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
export { DEFAULT_ASSERTER, analyzeComponent, buildEnv, buildInitEnv, checkComponent, checkContextCatalog, checkDeclarations, checkExpression, checkTree, checkerProfileOf, componentAssignable, componentType, createTsliteChecker, declaredContextsOf, declaredParamsOf, declaredPropsOf, lowerComponent, partialStateType, resolveProps, resolveTypeRef, stateTypeOf };
|
|
866
|
+
//# sourceMappingURL=index.js.map
|
|
867
|
+
//# sourceMappingURL=index.js.map
|