@llui/compiler 0.3.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 +21 -0
- package/dist/accessor-resolver.d.ts +58 -0
- package/dist/accessor-resolver.d.ts.map +1 -0
- package/dist/accessor-resolver.js +119 -0
- package/dist/accessor-resolver.js.map +1 -0
- package/dist/binding-descriptors.d.ts +105 -0
- package/dist/binding-descriptors.d.ts.map +1 -0
- package/dist/binding-descriptors.js +340 -0
- package/dist/binding-descriptors.js.map +1 -0
- package/dist/collect-deps.d.ts +49 -0
- package/dist/collect-deps.d.ts.map +1 -0
- package/dist/collect-deps.js +444 -0
- package/dist/collect-deps.js.map +1 -0
- package/dist/compiler-cache.d.ts +20 -0
- package/dist/compiler-cache.d.ts.map +1 -0
- package/dist/compiler-cache.js +20 -0
- package/dist/compiler-cache.js.map +1 -0
- package/dist/cross-file-resolver.d.ts +109 -0
- package/dist/cross-file-resolver.d.ts.map +1 -0
- package/dist/cross-file-resolver.js +530 -0
- package/dist/cross-file-resolver.js.map +1 -0
- package/dist/cross-file-walker.d.ts +63 -0
- package/dist/cross-file-walker.d.ts.map +1 -0
- package/dist/cross-file-walker.js +516 -0
- package/dist/cross-file-walker.js.map +1 -0
- package/dist/diagnostic.d.ts +76 -0
- package/dist/diagnostic.d.ts.map +1 -0
- package/dist/diagnostic.js +59 -0
- package/dist/diagnostic.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/introspection-factory.d.ts +54 -0
- package/dist/introspection-factory.d.ts.map +1 -0
- package/dist/introspection-factory.js +46 -0
- package/dist/introspection-factory.js.map +1 -0
- package/dist/manifest.d.ts +144 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +209 -0
- package/dist/manifest.js.map +1 -0
- package/dist/module.d.ts +222 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +256 -0
- package/dist/module.js.map +1 -0
- package/dist/modules/_element-helpers.d.ts +4 -0
- package/dist/modules/_element-helpers.d.ts.map +1 -0
- package/dist/modules/_element-helpers.js +138 -0
- package/dist/modules/_element-helpers.js.map +1 -0
- package/dist/modules/_msg-variants.d.ts +10 -0
- package/dist/modules/_msg-variants.d.ts.map +1 -0
- package/dist/modules/_msg-variants.js +97 -0
- package/dist/modules/_msg-variants.js.map +1 -0
- package/dist/modules/_shared.d.ts +16 -0
- package/dist/modules/_shared.d.ts.map +1 -0
- package/dist/modules/_shared.js +30 -0
- package/dist/modules/_shared.js.map +1 -0
- package/dist/modules/accessibility.d.ts +3 -0
- package/dist/modules/accessibility.d.ts.map +1 -0
- package/dist/modules/accessibility.js +82 -0
- package/dist/modules/accessibility.js.map +1 -0
- package/dist/modules/accessor-side-effect.d.ts +3 -0
- package/dist/modules/accessor-side-effect.d.ts.map +1 -0
- package/dist/modules/accessor-side-effect.js +113 -0
- package/dist/modules/accessor-side-effect.js.map +1 -0
- package/dist/modules/agent-emits-drift.d.ts +3 -0
- package/dist/modules/agent-emits-drift.d.ts.map +1 -0
- package/dist/modules/agent-emits-drift.js +158 -0
- package/dist/modules/agent-emits-drift.js.map +1 -0
- package/dist/modules/agent-example-on-payload.d.ts +3 -0
- package/dist/modules/agent-example-on-payload.d.ts.map +1 -0
- package/dist/modules/agent-example-on-payload.js +53 -0
- package/dist/modules/agent-example-on-payload.js.map +1 -0
- package/dist/modules/agent-exclusive-annotations.d.ts +3 -0
- package/dist/modules/agent-exclusive-annotations.d.ts.map +1 -0
- package/dist/modules/agent-exclusive-annotations.js +68 -0
- package/dist/modules/agent-exclusive-annotations.js.map +1 -0
- package/dist/modules/agent-missing-intent.d.ts +3 -0
- package/dist/modules/agent-missing-intent.d.ts.map +1 -0
- package/dist/modules/agent-missing-intent.js +47 -0
- package/dist/modules/agent-missing-intent.js.map +1 -0
- package/dist/modules/agent-msg-resolvable.d.ts +3 -0
- package/dist/modules/agent-msg-resolvable.d.ts.map +1 -0
- package/dist/modules/agent-msg-resolvable.js +161 -0
- package/dist/modules/agent-msg-resolvable.js.map +1 -0
- package/dist/modules/agent-nonextractable-handler.d.ts +3 -0
- package/dist/modules/agent-nonextractable-handler.d.ts.map +1 -0
- package/dist/modules/agent-nonextractable-handler.js +127 -0
- package/dist/modules/agent-nonextractable-handler.js.map +1 -0
- package/dist/modules/agent-optional-field-undocumented.d.ts +3 -0
- package/dist/modules/agent-optional-field-undocumented.d.ts.map +1 -0
- package/dist/modules/agent-optional-field-undocumented.js +67 -0
- package/dist/modules/agent-optional-field-undocumented.js.map +1 -0
- package/dist/modules/agent-tagsend-translator-missing.d.ts +3 -0
- package/dist/modules/agent-tagsend-translator-missing.d.ts.map +1 -0
- package/dist/modules/agent-tagsend-translator-missing.js +58 -0
- package/dist/modules/agent-tagsend-translator-missing.js.map +1 -0
- package/dist/modules/agent-warning-on-confirm.d.ts +3 -0
- package/dist/modules/agent-warning-on-confirm.d.ts.map +1 -0
- package/dist/modules/agent-warning-on-confirm.js +46 -0
- package/dist/modules/agent-warning-on-confirm.js.map +1 -0
- package/dist/modules/async-update.d.ts +3 -0
- package/dist/modules/async-update.d.ts.map +1 -0
- package/dist/modules/async-update.js +86 -0
- package/dist/modules/async-update.js.map +1 -0
- package/dist/modules/binding-descriptors.d.ts +4 -0
- package/dist/modules/binding-descriptors.d.ts.map +1 -0
- package/dist/modules/binding-descriptors.js +48 -0
- package/dist/modules/binding-descriptors.js.map +1 -0
- package/dist/modules/bitmask-overflow.d.ts +3 -0
- package/dist/modules/bitmask-overflow.d.ts.map +1 -0
- package/dist/modules/bitmask-overflow.js +152 -0
- package/dist/modules/bitmask-overflow.js.map +1 -0
- package/dist/modules/compiler-stamp.d.ts +3 -0
- package/dist/modules/compiler-stamp.d.ts.map +1 -0
- package/dist/modules/compiler-stamp.js +44 -0
- package/dist/modules/compiler-stamp.js.map +1 -0
- package/dist/modules/component-meta.d.ts +3 -0
- package/dist/modules/component-meta.d.ts.map +1 -0
- package/dist/modules/component-meta.js +44 -0
- package/dist/modules/component-meta.js.map +1 -0
- package/dist/modules/controlled-input.d.ts +3 -0
- package/dist/modules/controlled-input.d.ts.map +1 -0
- package/dist/modules/controlled-input.js +68 -0
- package/dist/modules/controlled-input.js.map +1 -0
- package/dist/modules/core-synthesis.d.ts +18 -0
- package/dist/modules/core-synthesis.d.ts.map +1 -0
- package/dist/modules/core-synthesis.js +748 -0
- package/dist/modules/core-synthesis.js.map +1 -0
- package/dist/modules/direct-state-in-view.d.ts +3 -0
- package/dist/modules/direct-state-in-view.d.ts.map +1 -0
- package/dist/modules/direct-state-in-view.js +103 -0
- package/dist/modules/direct-state-in-view.js.map +1 -0
- package/dist/modules/each-closure-violation.d.ts +3 -0
- package/dist/modules/each-closure-violation.d.ts.map +1 -0
- package/dist/modules/each-closure-violation.js +255 -0
- package/dist/modules/each-closure-violation.js.map +1 -0
- package/dist/modules/each-memo.d.ts +15 -0
- package/dist/modules/each-memo.d.ts.map +1 -0
- package/dist/modules/each-memo.js +115 -0
- package/dist/modules/each-memo.js.map +1 -0
- package/dist/modules/effect-without-handler.d.ts +3 -0
- package/dist/modules/effect-without-handler.d.ts.map +1 -0
- package/dist/modules/effect-without-handler.js +92 -0
- package/dist/modules/effect-without-handler.js.map +1 -0
- package/dist/modules/element-rewrite.d.ts +22 -0
- package/dist/modules/element-rewrite.d.ts.map +1 -0
- package/dist/modules/element-rewrite.js +1017 -0
- package/dist/modules/element-rewrite.js.map +1 -0
- package/dist/modules/empty-props.d.ts +3 -0
- package/dist/modules/empty-props.d.ts.map +1 -0
- package/dist/modules/empty-props.js +50 -0
- package/dist/modules/empty-props.js.map +1 -0
- package/dist/modules/exhaustive-effect-handling.d.ts +3 -0
- package/dist/modules/exhaustive-effect-handling.d.ts.map +1 -0
- package/dist/modules/exhaustive-effect-handling.js +61 -0
- package/dist/modules/exhaustive-effect-handling.js.map +1 -0
- package/dist/modules/exhaustive-update.d.ts +3 -0
- package/dist/modules/exhaustive-update.d.ts.map +1 -0
- package/dist/modules/exhaustive-update.js +146 -0
- package/dist/modules/exhaustive-update.js.map +1 -0
- package/dist/modules/forgotten-spread.d.ts +3 -0
- package/dist/modules/forgotten-spread.d.ts.map +1 -0
- package/dist/modules/forgotten-spread.js +51 -0
- package/dist/modules/forgotten-spread.js.map +1 -0
- package/dist/modules/form-boilerplate.d.ts +3 -0
- package/dist/modules/form-boilerplate.d.ts.map +1 -0
- package/dist/modules/form-boilerplate.js +101 -0
- package/dist/modules/form-boilerplate.js.map +1 -0
- package/dist/modules/imperative-dom-in-view.d.ts +3 -0
- package/dist/modules/imperative-dom-in-view.d.ts.map +1 -0
- package/dist/modules/imperative-dom-in-view.js +123 -0
- package/dist/modules/imperative-dom-in-view.js.map +1 -0
- package/dist/modules/item-dedup.d.ts +7 -0
- package/dist/modules/item-dedup.d.ts.map +1 -0
- package/dist/modules/item-dedup.js +204 -0
- package/dist/modules/item-dedup.js.map +1 -0
- package/dist/modules/map-on-state-array.d.ts +3 -0
- package/dist/modules/map-on-state-array.d.ts.map +1 -0
- package/dist/modules/map-on-state-array.js +84 -0
- package/dist/modules/map-on-state-array.js.map +1 -0
- package/dist/modules/mask-legend.d.ts +10 -0
- package/dist/modules/mask-legend.d.ts.map +1 -0
- package/dist/modules/mask-legend.js +50 -0
- package/dist/modules/mask-legend.js.map +1 -0
- package/dist/modules/missing-memo.d.ts +3 -0
- package/dist/modules/missing-memo.d.ts.map +1 -0
- package/dist/modules/missing-memo.js +114 -0
- package/dist/modules/missing-memo.js.map +1 -0
- package/dist/modules/msg-annotations.d.ts +9 -0
- package/dist/modules/msg-annotations.d.ts.map +1 -0
- package/dist/modules/msg-annotations.js +54 -0
- package/dist/modules/msg-annotations.js.map +1 -0
- package/dist/modules/msg-schema.d.ts +10 -0
- package/dist/modules/msg-schema.d.ts.map +1 -0
- package/dist/modules/msg-schema.js +70 -0
- package/dist/modules/msg-schema.js.map +1 -0
- package/dist/modules/namespace-import.d.ts +3 -0
- package/dist/modules/namespace-import.d.ts.map +1 -0
- package/dist/modules/namespace-import.js +80 -0
- package/dist/modules/namespace-import.js.map +1 -0
- package/dist/modules/nested-send-in-update.d.ts +3 -0
- package/dist/modules/nested-send-in-update.d.ts.map +1 -0
- package/dist/modules/nested-send-in-update.js +77 -0
- package/dist/modules/nested-send-in-update.js.map +1 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.d.ts +3 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.d.ts.map +1 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.js +100 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.js.map +1 -0
- package/dist/modules/no-eager-item-accessor.d.ts +3 -0
- package/dist/modules/no-eager-item-accessor.d.ts.map +1 -0
- package/dist/modules/no-eager-item-accessor.js +74 -0
- package/dist/modules/no-eager-item-accessor.js.map +1 -0
- package/dist/modules/no-let-reactive-accessor.d.ts +3 -0
- package/dist/modules/no-let-reactive-accessor.d.ts.map +1 -0
- package/dist/modules/no-let-reactive-accessor.js +227 -0
- package/dist/modules/no-let-reactive-accessor.js.map +1 -0
- package/dist/modules/no-list-render-in-sample.d.ts +3 -0
- package/dist/modules/no-list-render-in-sample.d.ts.map +1 -0
- package/dist/modules/no-list-render-in-sample.js +89 -0
- package/dist/modules/no-list-render-in-sample.js.map +1 -0
- package/dist/modules/no-sample-in-accessor.d.ts +3 -0
- package/dist/modules/no-sample-in-accessor.d.ts.map +1 -0
- package/dist/modules/no-sample-in-accessor.js +141 -0
- package/dist/modules/no-sample-in-accessor.js.map +1 -0
- package/dist/modules/no-sample-in-reactive-position.d.ts +3 -0
- package/dist/modules/no-sample-in-reactive-position.d.ts.map +1 -0
- package/dist/modules/no-sample-in-reactive-position.js +72 -0
- package/dist/modules/no-sample-in-reactive-position.js.map +1 -0
- package/dist/modules/pure-update-function.d.ts +3 -0
- package/dist/modules/pure-update-function.d.ts.map +1 -0
- package/dist/modules/pure-update-function.js +127 -0
- package/dist/modules/pure-update-function.js.map +1 -0
- package/dist/modules/reactive-paths.d.ts +3 -0
- package/dist/modules/reactive-paths.d.ts.map +1 -0
- package/dist/modules/reactive-paths.js +77 -0
- package/dist/modules/reactive-paths.js.map +1 -0
- package/dist/modules/row-factory.d.ts +12 -0
- package/dist/modules/row-factory.d.ts.map +1 -0
- package/dist/modules/row-factory.js +385 -0
- package/dist/modules/row-factory.js.map +1 -0
- package/dist/modules/schema-hash.d.ts +15 -0
- package/dist/modules/schema-hash.d.ts.map +1 -0
- package/dist/modules/schema-hash.js +70 -0
- package/dist/modules/schema-hash.js.map +1 -0
- package/dist/modules/spread-in-children.d.ts +3 -0
- package/dist/modules/spread-in-children.d.ts.map +1 -0
- package/dist/modules/spread-in-children.js +144 -0
- package/dist/modules/spread-in-children.js.map +1 -0
- package/dist/modules/state-mutation.d.ts +3 -0
- package/dist/modules/state-mutation.d.ts.map +1 -0
- package/dist/modules/state-mutation.js +138 -0
- package/dist/modules/state-mutation.js.map +1 -0
- package/dist/modules/state-schema.d.ts +8 -0
- package/dist/modules/state-schema.d.ts.map +1 -0
- package/dist/modules/state-schema.js +55 -0
- package/dist/modules/state-schema.js.map +1 -0
- package/dist/modules/static-items.d.ts +3 -0
- package/dist/modules/static-items.d.ts.map +1 -0
- package/dist/modules/static-items.js +125 -0
- package/dist/modules/static-items.js.map +1 -0
- package/dist/modules/static-on.d.ts +3 -0
- package/dist/modules/static-on.d.ts.map +1 -0
- package/dist/modules/static-on.js +100 -0
- package/dist/modules/static-on.js.map +1 -0
- package/dist/modules/string-effect-callback.d.ts +3 -0
- package/dist/modules/string-effect-callback.d.ts.map +1 -0
- package/dist/modules/string-effect-callback.js +50 -0
- package/dist/modules/string-effect-callback.js.map +1 -0
- package/dist/modules/structural-mask.d.ts +8 -0
- package/dist/modules/structural-mask.d.ts.map +1 -0
- package/dist/modules/structural-mask.js +76 -0
- package/dist/modules/structural-mask.js.map +1 -0
- package/dist/modules/subapp-requires-reason.d.ts +3 -0
- package/dist/modules/subapp-requires-reason.d.ts.map +1 -0
- package/dist/modules/subapp-requires-reason.js +129 -0
- package/dist/modules/subapp-requires-reason.js.map +1 -0
- package/dist/modules/text-mask.d.ts +12 -0
- package/dist/modules/text-mask.d.ts.map +1 -0
- package/dist/modules/text-mask.js +63 -0
- package/dist/modules/text-mask.js.map +1 -0
- package/dist/modules/view-bag-import.d.ts +3 -0
- package/dist/modules/view-bag-import.d.ts.map +1 -0
- package/dist/modules/view-bag-import.js +80 -0
- package/dist/modules/view-bag-import.js.map +1 -0
- package/dist/msg-annotations.d.ts +104 -0
- package/dist/msg-annotations.d.ts.map +1 -0
- package/dist/msg-annotations.js +242 -0
- package/dist/msg-annotations.js.map +1 -0
- package/dist/msg-schema.d.ts +130 -0
- package/dist/msg-schema.d.ts.map +1 -0
- package/dist/msg-schema.js +770 -0
- package/dist/msg-schema.js.map +1 -0
- package/dist/schema-hash.d.ts +16 -0
- package/dist/schema-hash.d.ts.map +1 -0
- package/dist/schema-hash.js +31 -0
- package/dist/schema-hash.js.map +1 -0
- package/dist/state-schema.d.ts +41 -0
- package/dist/state-schema.d.ts.map +1 -0
- package/dist/state-schema.js +156 -0
- package/dist/state-schema.js.map +1 -0
- package/dist/transform.d.ts +109 -0
- package/dist/transform.d.ts.map +1 -0
- package/dist/transform.js +1390 -0
- package/dist/transform.js.map +1 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// `view-bag-import` — errors when a file that defines a component
|
|
2
|
+
// imports view-bag primitives (text, each, show, branch, memo,
|
|
3
|
+
// selector) directly from @llui/dom. The view bag (`view: ({ text,
|
|
4
|
+
// each, … }) => …`) is typed to the component's State; the direct
|
|
5
|
+
// import is generic. Migrated from
|
|
6
|
+
// `@llui/eslint-plugin/src/rules/view-bag-import.ts`.
|
|
7
|
+
import ts from 'typescript';
|
|
8
|
+
import { rangeFromOffsets } from '../diagnostic.js';
|
|
9
|
+
import { findComponentCalls } from './_shared.js';
|
|
10
|
+
const VIEW_BAG_NAMES = new Set(['text', 'each', 'show', 'branch', 'memo', 'selector']);
|
|
11
|
+
export function viewBagImportModule() {
|
|
12
|
+
return {
|
|
13
|
+
name: 'view-bag-import',
|
|
14
|
+
compilerVersion: '^0.3.0',
|
|
15
|
+
diagnostics: [
|
|
16
|
+
{
|
|
17
|
+
id: 'llui/view-bag-import',
|
|
18
|
+
description: 'View bag primitive imported from @llui/dom in a file that defines a component — use the view bag instead.',
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
visitors: {
|
|
22
|
+
[ts.SyntaxKind.SourceFile]: (ctx, node) => {
|
|
23
|
+
const visited = node;
|
|
24
|
+
const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true);
|
|
25
|
+
const componentCalls = findComponentCalls(sf);
|
|
26
|
+
if (componentCalls.length === 0)
|
|
27
|
+
return;
|
|
28
|
+
// At least one component() must have a `view:` property for the
|
|
29
|
+
// rule to be meaningful (matches the ESLint rule's semantics).
|
|
30
|
+
let definesView = false;
|
|
31
|
+
for (const call of componentCalls) {
|
|
32
|
+
const arg = call.arguments[0];
|
|
33
|
+
if (!arg || !ts.isObjectLiteralExpression(arg))
|
|
34
|
+
continue;
|
|
35
|
+
for (const prop of arg.properties) {
|
|
36
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
37
|
+
ts.isIdentifier(prop.name) &&
|
|
38
|
+
prop.name.text === 'view') {
|
|
39
|
+
definesView = true;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (definesView)
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
if (!definesView)
|
|
47
|
+
return;
|
|
48
|
+
for (const stmt of sf.statements) {
|
|
49
|
+
if (!ts.isImportDeclaration(stmt))
|
|
50
|
+
continue;
|
|
51
|
+
if (!ts.isStringLiteral(stmt.moduleSpecifier))
|
|
52
|
+
continue;
|
|
53
|
+
if (stmt.moduleSpecifier.text !== '@llui/dom')
|
|
54
|
+
continue;
|
|
55
|
+
const clause = stmt.importClause;
|
|
56
|
+
if (!clause || !clause.namedBindings || !ts.isNamedImports(clause.namedBindings))
|
|
57
|
+
continue;
|
|
58
|
+
for (const spec of clause.namedBindings.elements) {
|
|
59
|
+
const importedName = (spec.propertyName ?? spec.name).text;
|
|
60
|
+
if (VIEW_BAG_NAMES.has(importedName)) {
|
|
61
|
+
ctx.reportDiagnostic({
|
|
62
|
+
id: 'llui/view-bag-import',
|
|
63
|
+
severity: 'error',
|
|
64
|
+
category: 'style',
|
|
65
|
+
message: `Don't import \`${importedName}\` from '@llui/dom' in a file that defines a ` +
|
|
66
|
+
`component. Use the view bag: \`view: ({ ${importedName}, … }) => [...]\`. ` +
|
|
67
|
+
`The view-bag version is typed to your component's State.`,
|
|
68
|
+
location: {
|
|
69
|
+
file: sf.fileName,
|
|
70
|
+
range: rangeFromOffsets(sf.text, spec.getStart(sf), spec.getEnd()),
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=view-bag-import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view-bag-import.js","sourceRoot":"","sources":["../../src/modules/view-bag-import.ts"],"names":[],"mappings":"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","sourcesContent":["// `view-bag-import` — errors when a file that defines a component\n// imports view-bag primitives (text, each, show, branch, memo,\n// selector) directly from @llui/dom. The view bag (`view: ({ text,\n// each, … }) => …`) is typed to the component's State; the direct\n// import is generic. Migrated from\n// `@llui/eslint-plugin/src/rules/view-bag-import.ts`.\n\nimport ts from 'typescript'\nimport { rangeFromOffsets } from '../diagnostic.js'\nimport type { CompilerModule } from '../module.js'\nimport { findComponentCalls } from './_shared.js'\n\nconst VIEW_BAG_NAMES = new Set(['text', 'each', 'show', 'branch', 'memo', 'selector'])\n\nexport function viewBagImportModule(): CompilerModule {\n return {\n name: 'view-bag-import',\n compilerVersion: '^0.3.0',\n diagnostics: [\n {\n id: 'llui/view-bag-import',\n description:\n 'View bag primitive imported from @llui/dom in a file that defines a component — use the view bag instead.',\n },\n ],\n visitors: {\n [ts.SyntaxKind.SourceFile]: (ctx, node) => {\n const visited = node as ts.SourceFile\n const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true)\n const componentCalls = findComponentCalls(sf)\n if (componentCalls.length === 0) return\n // At least one component() must have a `view:` property for the\n // rule to be meaningful (matches the ESLint rule's semantics).\n let definesView = false\n for (const call of componentCalls) {\n const arg = call.arguments[0]\n if (!arg || !ts.isObjectLiteralExpression(arg)) continue\n for (const prop of arg.properties) {\n if (\n ts.isPropertyAssignment(prop) &&\n ts.isIdentifier(prop.name) &&\n prop.name.text === 'view'\n ) {\n definesView = true\n break\n }\n }\n if (definesView) break\n }\n if (!definesView) return\n for (const stmt of sf.statements) {\n if (!ts.isImportDeclaration(stmt)) continue\n if (!ts.isStringLiteral(stmt.moduleSpecifier)) continue\n if (stmt.moduleSpecifier.text !== '@llui/dom') continue\n const clause = stmt.importClause\n if (!clause || !clause.namedBindings || !ts.isNamedImports(clause.namedBindings)) continue\n for (const spec of clause.namedBindings.elements) {\n const importedName = (spec.propertyName ?? spec.name).text\n if (VIEW_BAG_NAMES.has(importedName)) {\n ctx.reportDiagnostic({\n id: 'llui/view-bag-import',\n severity: 'error',\n category: 'style',\n message:\n `Don't import \\`${importedName}\\` from '@llui/dom' in a file that defines a ` +\n `component. Use the view bag: \\`view: ({ ${importedName}, … }) => [...]\\`. ` +\n `The view-bag version is typed to your component's State.`,\n location: {\n file: sf.fileName,\n range: rangeFromOffsets(sf.text, spec.getStart(sf), spec.getEnd()),\n },\n })\n }\n }\n }\n },\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
export type DispatchMode = 'shared' | 'human-only' | 'agent-only';
|
|
3
|
+
/**
|
|
4
|
+
* Whether the annotation map carries any non-default values. Used to
|
|
5
|
+
* gate `__msgAnnotations` emission — annotations whose every field is
|
|
6
|
+
* default are emission-redundant (the runtime treats absence as the
|
|
7
|
+
* same defaults). Saves ~50 bytes per component for un-annotated Msg
|
|
8
|
+
* unions, which dominates the corpus.
|
|
9
|
+
*/
|
|
10
|
+
export declare function hasNonDefaultAnnotation(a: Record<string, MessageAnnotations>): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Build a TS object-literal expression for the annotation map. Used by
|
|
13
|
+
* `msgAnnotationsModule` for `__msgAnnotations` emission. Variant
|
|
14
|
+
* names are emitted as string literals (not identifiers) so
|
|
15
|
+
* discriminants containing `/`, `-`, reserved words, etc. produce
|
|
16
|
+
* valid JS.
|
|
17
|
+
*/
|
|
18
|
+
export declare function annotationsToObjectLiteral(a: Record<string, MessageAnnotations>): ts.ObjectLiteralExpression;
|
|
19
|
+
export type MessageAnnotations = {
|
|
20
|
+
intent: string | null;
|
|
21
|
+
alwaysAffordable: boolean;
|
|
22
|
+
requiresConfirm: boolean;
|
|
23
|
+
dispatchMode: DispatchMode;
|
|
24
|
+
/**
|
|
25
|
+
* Concrete example dispatches the LLM can copy from. Populated by
|
|
26
|
+
* `@example("text")` JSDoc tags. Each tag becomes one entry, in
|
|
27
|
+
* source order, so authors can mix scenarios ("typical case",
|
|
28
|
+
* "edge case with auth", etc.) without nesting them in a single
|
|
29
|
+
* string.
|
|
30
|
+
*/
|
|
31
|
+
examples: string[];
|
|
32
|
+
/**
|
|
33
|
+
* Non-blocking caution. Surfaced verbatim to the agent at affordance
|
|
34
|
+
* time so the LLM can weigh the consequence ("this overwrites the
|
|
35
|
+
* cloud version", "fires analytics that can't be retracted") before
|
|
36
|
+
* dispatching. Distinct from `requiresConfirm`, which is a runtime
|
|
37
|
+
* gate the user must acknowledge.
|
|
38
|
+
*/
|
|
39
|
+
warning: string | null;
|
|
40
|
+
/**
|
|
41
|
+
* Effect kinds this variant emits when dispatched, declared by the
|
|
42
|
+
* author via `@emits("kind1", "kind2")`. Lets the agent reason
|
|
43
|
+
* about side effects ("this dispatch hits the cloud, so I should
|
|
44
|
+
* batch") without the compiler having to walk update.ts. Authored
|
|
45
|
+
* rather than auto-extracted because real apps emit effects
|
|
46
|
+
* through helpers (`track('foo')`, `saveDelta(d)`) — auto-detecting
|
|
47
|
+
* those would require helper-return-shape analysis with
|
|
48
|
+
* ergonomically-painful failure modes; the declarative form trades
|
|
49
|
+
* automatic discovery for accuracy and simplicity.
|
|
50
|
+
*
|
|
51
|
+
* Empty when no `@emits` tag is present.
|
|
52
|
+
*/
|
|
53
|
+
emits: string[];
|
|
54
|
+
/**
|
|
55
|
+
* Boolean predicate gating whether the variant surfaces in
|
|
56
|
+
* `list_actions`. Authored as `@routeGated("expr")`; the compiler
|
|
57
|
+
* captures the predicate string verbatim and the runtime evaluates
|
|
58
|
+
* it with `state` bound to the current state. The variant only
|
|
59
|
+
* appears in the agent's affordance list when the predicate
|
|
60
|
+
* returns true.
|
|
61
|
+
*
|
|
62
|
+
* Compile-time alternative to `agentAffordances(state) => Msg[]`
|
|
63
|
+
* for the common case of "this Msg is reachable when state.X
|
|
64
|
+
* looks like Y." Co-located with the Msg definition rather than
|
|
65
|
+
* threaded through a separate hook.
|
|
66
|
+
*
|
|
67
|
+
* Examples:
|
|
68
|
+
* @routeGated("state.matrixState.kind === 'loaded'")
|
|
69
|
+
* @routeGated("state.route.kind === 'page' && state.route.slug === 'ranking'")
|
|
70
|
+
* @routeGated("state.auth.status === 'authenticated'")
|
|
71
|
+
*
|
|
72
|
+
* Null when no `@routeGated` tag is present (variant defaults to
|
|
73
|
+
* its dispatchMode-driven affordance behavior).
|
|
74
|
+
*/
|
|
75
|
+
routeGate: string | null;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Walk a Msg-like discriminated-union type alias and extract JSDoc
|
|
79
|
+
* annotations attached to each union member. Returns null if no
|
|
80
|
+
* recognizable union is found so callers can skip emission cleanly.
|
|
81
|
+
*
|
|
82
|
+
* Expected JSDoc grammar (order-independent):
|
|
83
|
+
* @intent("human readable")
|
|
84
|
+
* @alwaysAffordable
|
|
85
|
+
* @requiresConfirm
|
|
86
|
+
* @humanOnly — sugar for dispatchMode: 'human-only'
|
|
87
|
+
* @agentOnly — sugar for dispatchMode: 'agent-only'
|
|
88
|
+
*
|
|
89
|
+
* Unknown tags are ignored; malformed @intent (no quoted string) is
|
|
90
|
+
* treated as "no intent". `@humanOnly` and `@agentOnly` are mutually
|
|
91
|
+
* exclusive — if both are present (which the ESLint rule
|
|
92
|
+
* `agent-exclusive-annotations` reports as an error), the parser
|
|
93
|
+
* falls back to `'shared'` so a misconfigured Msg variant doesn't
|
|
94
|
+
* silently lock out one audience.
|
|
95
|
+
*/
|
|
96
|
+
export declare function extractMsgAnnotations(source: string,
|
|
97
|
+
/**
|
|
98
|
+
* Name of the type alias to extract from. Defaults to `'Msg'` for
|
|
99
|
+
* convention. Passed by the cross-file resolver when the alias has
|
|
100
|
+
* been renamed through imports/re-exports — its local name in the
|
|
101
|
+
* declaring file may differ from `'Msg'`.
|
|
102
|
+
*/
|
|
103
|
+
typeName?: string): Record<string, MessageAnnotations> | null;
|
|
104
|
+
//# sourceMappingURL=msg-annotations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msg-annotations.d.ts","sourceRoot":"","sources":["../src/msg-annotations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAE3B,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,YAAY,GAAG,YAAY,CAAA;AAEjE;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,OAAO,CAStF;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GACpC,EAAE,CAAC,uBAAuB,CAmD5B;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,gBAAgB,EAAE,OAAO,CAAA;IACzB,eAAe,EAAE,OAAO,CAAA;IACxB,YAAY,EAAE,YAAY,CAAA;IAC1B;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB;;;;;;OAMG;IACH,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB;;;;;;;;;;;;OAYG;IACH,KAAK,EAAE,MAAM,EAAE,CAAA;IACf;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB,CAAA;AAaD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM;AACd;;;;;GAKG;AACH,QAAQ,GAAE,MAAc,GACvB,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAgC3C"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Whether the annotation map carries any non-default values. Used to
|
|
4
|
+
* gate `__msgAnnotations` emission — annotations whose every field is
|
|
5
|
+
* default are emission-redundant (the runtime treats absence as the
|
|
6
|
+
* same defaults). Saves ~50 bytes per component for un-annotated Msg
|
|
7
|
+
* unions, which dominates the corpus.
|
|
8
|
+
*/
|
|
9
|
+
export function hasNonDefaultAnnotation(a) {
|
|
10
|
+
for (const v of Object.values(a)) {
|
|
11
|
+
if (v.intent !== null)
|
|
12
|
+
return true;
|
|
13
|
+
if (v.alwaysAffordable)
|
|
14
|
+
return true;
|
|
15
|
+
if (v.requiresConfirm)
|
|
16
|
+
return true;
|
|
17
|
+
if (v.dispatchMode !== 'shared')
|
|
18
|
+
return true;
|
|
19
|
+
if (v.routeGate != null)
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build a TS object-literal expression for the annotation map. Used by
|
|
26
|
+
* `msgAnnotationsModule` for `__msgAnnotations` emission. Variant
|
|
27
|
+
* names are emitted as string literals (not identifiers) so
|
|
28
|
+
* discriminants containing `/`, `-`, reserved words, etc. produce
|
|
29
|
+
* valid JS.
|
|
30
|
+
*/
|
|
31
|
+
export function annotationsToObjectLiteral(a) {
|
|
32
|
+
const props = [];
|
|
33
|
+
for (const [variant, ann] of Object.entries(a)) {
|
|
34
|
+
props.push(ts.factory.createPropertyAssignment(
|
|
35
|
+
// Wrap with createStringLiteral — the printer treats bare strings
|
|
36
|
+
// as identifiers, which produces invalid JS for discriminants
|
|
37
|
+
// containing characters like '/' (e.g. 'Router/RouteChanged'),
|
|
38
|
+
// reserved words ('delete'), or hyphens.
|
|
39
|
+
ts.factory.createStringLiteral(variant), ts.factory.createObjectLiteralExpression([
|
|
40
|
+
ts.factory.createPropertyAssignment('intent', ann.intent === null
|
|
41
|
+
? ts.factory.createNull()
|
|
42
|
+
: ts.factory.createStringLiteral(ann.intent)),
|
|
43
|
+
ts.factory.createPropertyAssignment('alwaysAffordable', ann.alwaysAffordable ? ts.factory.createTrue() : ts.factory.createFalse()),
|
|
44
|
+
ts.factory.createPropertyAssignment('requiresConfirm', ann.requiresConfirm ? ts.factory.createTrue() : ts.factory.createFalse()),
|
|
45
|
+
ts.factory.createPropertyAssignment('dispatchMode', ts.factory.createStringLiteral(ann.dispatchMode)),
|
|
46
|
+
// Only emit `routeGate` when non-null. The runtime treats
|
|
47
|
+
// missing as the no-gate default ("variant follows its
|
|
48
|
+
// dispatchMode-driven affordance behavior") — keeping the
|
|
49
|
+
// wire shape minimal for the common case of un-gated Msgs.
|
|
50
|
+
// Use loose `!= null` so test fixtures that omit the
|
|
51
|
+
// field entirely also fall through to the no-emit path.
|
|
52
|
+
...(ann.routeGate != null
|
|
53
|
+
? [
|
|
54
|
+
ts.factory.createPropertyAssignment('routeGate', ts.factory.createStringLiteral(ann.routeGate)),
|
|
55
|
+
]
|
|
56
|
+
: []),
|
|
57
|
+
], true)));
|
|
58
|
+
}
|
|
59
|
+
return ts.factory.createObjectLiteralExpression(props, true);
|
|
60
|
+
}
|
|
61
|
+
const DEFAULT = {
|
|
62
|
+
intent: null,
|
|
63
|
+
alwaysAffordable: false,
|
|
64
|
+
requiresConfirm: false,
|
|
65
|
+
dispatchMode: 'shared',
|
|
66
|
+
examples: [],
|
|
67
|
+
warning: null,
|
|
68
|
+
emits: [],
|
|
69
|
+
routeGate: null,
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Walk a Msg-like discriminated-union type alias and extract JSDoc
|
|
73
|
+
* annotations attached to each union member. Returns null if no
|
|
74
|
+
* recognizable union is found so callers can skip emission cleanly.
|
|
75
|
+
*
|
|
76
|
+
* Expected JSDoc grammar (order-independent):
|
|
77
|
+
* @intent("human readable")
|
|
78
|
+
* @alwaysAffordable
|
|
79
|
+
* @requiresConfirm
|
|
80
|
+
* @humanOnly — sugar for dispatchMode: 'human-only'
|
|
81
|
+
* @agentOnly — sugar for dispatchMode: 'agent-only'
|
|
82
|
+
*
|
|
83
|
+
* Unknown tags are ignored; malformed @intent (no quoted string) is
|
|
84
|
+
* treated as "no intent". `@humanOnly` and `@agentOnly` are mutually
|
|
85
|
+
* exclusive — if both are present (which the ESLint rule
|
|
86
|
+
* `agent-exclusive-annotations` reports as an error), the parser
|
|
87
|
+
* falls back to `'shared'` so a misconfigured Msg variant doesn't
|
|
88
|
+
* silently lock out one audience.
|
|
89
|
+
*/
|
|
90
|
+
export function extractMsgAnnotations(source,
|
|
91
|
+
/**
|
|
92
|
+
* Name of the type alias to extract from. Defaults to `'Msg'` for
|
|
93
|
+
* convention. Passed by the cross-file resolver when the alias has
|
|
94
|
+
* been renamed through imports/re-exports — its local name in the
|
|
95
|
+
* declaring file may differ from `'Msg'`.
|
|
96
|
+
*/
|
|
97
|
+
typeName = 'Msg') {
|
|
98
|
+
const sf = ts.createSourceFile('msg.ts', source, ts.ScriptTarget.Latest, true);
|
|
99
|
+
const aliases = [];
|
|
100
|
+
sf.forEachChild((n) => {
|
|
101
|
+
if (ts.isTypeAliasDeclaration(n))
|
|
102
|
+
aliases.push(n);
|
|
103
|
+
});
|
|
104
|
+
const named = aliases.find((a) => a.name.text === typeName);
|
|
105
|
+
// Fallback: only when looking for the conventional 'Msg' name AND the
|
|
106
|
+
// file has no `type Msg = …`; pick any union type alias. With an
|
|
107
|
+
// explicit `typeName` from the resolver, we don't fall back — that
|
|
108
|
+
// would silently match the wrong alias.
|
|
109
|
+
const alias = named ?? (typeName === 'Msg' ? aliases.find((a) => ts.isUnionTypeNode(a.type)) : undefined);
|
|
110
|
+
if (!alias || !ts.isUnionTypeNode(alias.type))
|
|
111
|
+
return null;
|
|
112
|
+
const result = {};
|
|
113
|
+
const types = alias.type.types;
|
|
114
|
+
for (let i = 0; i < types.length; i++) {
|
|
115
|
+
const member = types[i];
|
|
116
|
+
if (member === undefined || !ts.isTypeLiteralNode(member))
|
|
117
|
+
continue;
|
|
118
|
+
const variant = readDiscriminantLiteral(member);
|
|
119
|
+
if (!variant)
|
|
120
|
+
continue;
|
|
121
|
+
// Leading JSDoc for union member i is scanned from the end of the
|
|
122
|
+
// previous element (or union.pos for the first member), because
|
|
123
|
+
// TypeScript's parser places comment ranges relative to the token
|
|
124
|
+
// that follows them — and the | bar is not part of the TypeLiteralNode.
|
|
125
|
+
const prev = types[i - 1];
|
|
126
|
+
const scanPos = i === 0 || prev === undefined ? alias.type.pos : prev.end;
|
|
127
|
+
const comment = readLeadingJSDoc(source, scanPos);
|
|
128
|
+
result[variant] = parseAnnotations(comment);
|
|
129
|
+
}
|
|
130
|
+
return Object.keys(result).length === 0 ? null : result;
|
|
131
|
+
}
|
|
132
|
+
function readDiscriminantLiteral(lit) {
|
|
133
|
+
for (const m of lit.members) {
|
|
134
|
+
if (!ts.isPropertySignature(m))
|
|
135
|
+
continue;
|
|
136
|
+
if (!m.name || !ts.isIdentifier(m.name) || m.name.text !== 'type')
|
|
137
|
+
continue;
|
|
138
|
+
if (!m.type || !ts.isLiteralTypeNode(m.type))
|
|
139
|
+
continue;
|
|
140
|
+
const literal = m.type.literal;
|
|
141
|
+
if (ts.isStringLiteral(literal))
|
|
142
|
+
return literal.text;
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
function readLeadingJSDoc(source, scanPos) {
|
|
147
|
+
const ranges = ts.getLeadingCommentRanges(source, scanPos) ?? [];
|
|
148
|
+
const docs = ranges
|
|
149
|
+
.filter((r) => r.kind === ts.SyntaxKind.MultiLineCommentTrivia)
|
|
150
|
+
.map((r) => source.slice(r.pos, r.end))
|
|
151
|
+
.filter((txt) => txt.startsWith('/**'));
|
|
152
|
+
return docs.join('\n');
|
|
153
|
+
}
|
|
154
|
+
function parseAnnotations(comment) {
|
|
155
|
+
if (!comment)
|
|
156
|
+
return { ...DEFAULT, examples: [] };
|
|
157
|
+
const intent = readIntent(comment);
|
|
158
|
+
const human = /@humanOnly\b/.test(comment);
|
|
159
|
+
const agent = /@agentOnly\b/.test(comment);
|
|
160
|
+
// Mutual-exclusion fallback: both tags present means a config bug;
|
|
161
|
+
// the ESLint rule reports it. At parse time, default to 'shared' so
|
|
162
|
+
// we don't silently lock out one audience based on tag order.
|
|
163
|
+
const dispatchMode = human && !agent ? 'human-only' : agent && !human ? 'agent-only' : 'shared';
|
|
164
|
+
return {
|
|
165
|
+
intent,
|
|
166
|
+
alwaysAffordable: /@alwaysAffordable\b/.test(comment),
|
|
167
|
+
requiresConfirm: /@requiresConfirm\b/.test(comment),
|
|
168
|
+
dispatchMode,
|
|
169
|
+
examples: readExamples(comment),
|
|
170
|
+
warning: readWarning(comment),
|
|
171
|
+
emits: readEmits(comment),
|
|
172
|
+
routeGate: readRouteGate(comment),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Match `@routeGated("predicate-expression")` (and curly-quote
|
|
177
|
+
* variant). Returns the verbatim predicate string — the runtime
|
|
178
|
+
* compiles it with `new Function('state', 'return (' + src + ')')`
|
|
179
|
+
* and evaluates against the current state to gate affordances.
|
|
180
|
+
*
|
|
181
|
+
* Mirrors `@validates`'s grammar but with `state` as the bound
|
|
182
|
+
* variable instead of `v` (since the predicate sees the whole app
|
|
183
|
+
* state, not a single field value).
|
|
184
|
+
*/
|
|
185
|
+
function readRouteGate(comment) {
|
|
186
|
+
const match = comment.match(/@routeGated\s*\(\s*["“]([^"”]*)["”]\s*\)/);
|
|
187
|
+
return match?.[1] ?? null;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Match `@emits("k1", "k2", ...)` — comma-separated list of effect
|
|
191
|
+
* kind strings. Each entry can use straight or curly quotes; the
|
|
192
|
+
* separator is `,` with arbitrary whitespace. Returns the kinds in
|
|
193
|
+
* source order (deduped). Empty when the tag is absent or has no
|
|
194
|
+
* quoted strings.
|
|
195
|
+
*/
|
|
196
|
+
function readEmits(comment) {
|
|
197
|
+
// Match the whole `@emits(...)` parenthesized group so we can
|
|
198
|
+
// re-parse the inner content for individual quoted strings. The
|
|
199
|
+
// outer match is non-greedy on the closing paren to avoid eating
|
|
200
|
+
// through later JSDoc.
|
|
201
|
+
const outer = comment.match(/@emits\s*\(([^)]*)\)/);
|
|
202
|
+
if (!outer || outer[1] === undefined)
|
|
203
|
+
return [];
|
|
204
|
+
const inner = outer[1];
|
|
205
|
+
const seen = new Set();
|
|
206
|
+
const out = [];
|
|
207
|
+
const re = /["“]([^"”]*)["”]/g;
|
|
208
|
+
let m;
|
|
209
|
+
while ((m = re.exec(inner)) !== null) {
|
|
210
|
+
const v = m[1];
|
|
211
|
+
if (v === undefined || seen.has(v))
|
|
212
|
+
continue;
|
|
213
|
+
seen.add(v);
|
|
214
|
+
out.push(v);
|
|
215
|
+
}
|
|
216
|
+
return out;
|
|
217
|
+
}
|
|
218
|
+
function readIntent(comment) {
|
|
219
|
+
const match = comment.match(/@intent\s*\(\s*["\u201c]([^"\u201d]*)["\u201d]\s*\)/);
|
|
220
|
+
return match?.[1] ?? null;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Match every `@example("\u2026")` (and curly-quote variant) in source
|
|
224
|
+
* order. Multiple tags on one variant are common \u2014 typical-case,
|
|
225
|
+
* edge-case-with-auth, etc. \u2014 so the parser collects all of them
|
|
226
|
+
* rather than picking the first.
|
|
227
|
+
*/
|
|
228
|
+
function readExamples(comment) {
|
|
229
|
+
const out = [];
|
|
230
|
+
const re = /@example\s*\(\s*["\u201c]([^"\u201d]*)["\u201d]\s*\)/g;
|
|
231
|
+
let m;
|
|
232
|
+
while ((m = re.exec(comment)) !== null) {
|
|
233
|
+
if (m[1] !== undefined)
|
|
234
|
+
out.push(m[1]);
|
|
235
|
+
}
|
|
236
|
+
return out;
|
|
237
|
+
}
|
|
238
|
+
function readWarning(comment) {
|
|
239
|
+
const match = comment.match(/@warning\s*\(\s*["\u201c]([^"\u201d]*)["\u201d]\s*\)/);
|
|
240
|
+
return match?.[1] ?? null;
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=msg-annotations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msg-annotations.js","sourceRoot":"","sources":["../src/msg-annotations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAI3B;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,CAAqC;IAC3E,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACjC,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI;YAAE,OAAO,IAAI,CAAA;QAClC,IAAI,CAAC,CAAC,gBAAgB;YAAE,OAAO,IAAI,CAAA;QACnC,IAAI,CAAC,CAAC,eAAe;YAAE,OAAO,IAAI,CAAA;QAClC,IAAI,CAAC,CAAC,YAAY,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;QAC5C,IAAI,CAAC,CAAC,SAAS,IAAI,IAAI;YAAE,OAAO,IAAI,CAAA;IACtC,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CACxC,CAAqC;IAErC,MAAM,KAAK,GAA4B,EAAE,CAAA;IACzC,KAAK,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,KAAK,CAAC,IAAI,CACR,EAAE,CAAC,OAAO,CAAC,wBAAwB;QACjC,kEAAkE;QAClE,8DAA8D;QAC9D,+DAA+D;QAC/D,yCAAyC;QACzC,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC,OAAO,CAAC,EACvC,EAAE,CAAC,OAAO,CAAC,6BAA6B,CACtC;YACE,EAAE,CAAC,OAAO,CAAC,wBAAwB,CACjC,QAAQ,EACR,GAAG,CAAC,MAAM,KAAK,IAAI;gBACjB,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE;gBACzB,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,CAC/C;YACD,EAAE,CAAC,OAAO,CAAC,wBAAwB,CACjC,kBAAkB,EAClB,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAC1E;YACD,EAAE,CAAC,OAAO,CAAC,wBAAwB,CACjC,iBAAiB,EACjB,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CACzE;YACD,EAAE,CAAC,OAAO,CAAC,wBAAwB,CACjC,cAAc,EACd,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC,GAAG,CAAC,YAAY,CAAC,CACjD;YACD,0DAA0D;YAC1D,uDAAuD;YACvD,0DAA0D;YAC1D,2DAA2D;YAC3D,qDAAqD;YACrD,wDAAwD;YACxD,GAAG,CAAC,GAAG,CAAC,SAAS,IAAI,IAAI;gBACvB,CAAC,CAAC;oBACE,EAAE,CAAC,OAAO,CAAC,wBAAwB,CACjC,WAAW,EACX,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,CAC9C;iBACF;gBACH,CAAC,CAAC,EAAE,CAAC;SACR,EACD,IAAI,CACL,CACF,CACF,CAAA;IACH,CAAC;IACD,OAAO,EAAE,CAAC,OAAO,CAAC,6BAA6B,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;AAC9D,CAAC;AA6DD,MAAM,OAAO,GAAuB;IAClC,MAAM,EAAE,IAAI;IACZ,gBAAgB,EAAE,KAAK;IACvB,eAAe,EAAE,KAAK;IACtB,YAAY,EAAE,QAAQ;IACtB,QAAQ,EAAE,EAAE;IACZ,OAAO,EAAE,IAAI;IACb,KAAK,EAAE,EAAE;IACT,SAAS,EAAE,IAAI;CAChB,CAAA;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAc;AACd;;;;;GAKG;AACH,WAAmB,KAAK;IAExB,MAAM,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAC9E,MAAM,OAAO,GAA8B,EAAE,CAAA;IAC7C,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE;QACpB,IAAI,EAAE,CAAC,sBAAsB,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACnD,CAAC,CAAC,CAAA;IACF,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;IAC3D,sEAAsE;IACtE,iEAAiE;IACjE,mEAAmE;IACnE,wCAAwC;IACxC,MAAM,KAAK,GACT,KAAK,IAAI,CAAC,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IAC7F,IAAI,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAE1D,MAAM,MAAM,GAAuC,EAAE,CAAA;IACrD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAA;IAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACvB,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,EAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC;YAAE,SAAQ;QACnE,MAAM,OAAO,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAA;QAC/C,IAAI,CAAC,OAAO;YAAE,SAAQ;QACtB,kEAAkE;QAClE,gEAAgE;QAChE,kEAAkE;QAClE,wEAAwE;QACxE,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QACzB,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAA;QACzE,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACjD,MAAM,CAAC,OAAO,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;IAC7C,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAA;AACzD,CAAC;AAED,SAAS,uBAAuB,CAAC,GAAuB;IACtD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC;YAAE,SAAQ;QACxC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,SAAQ;QAC3E,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC;YAAE,SAAQ;QACtD,MAAM,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAA;QAC9B,IAAI,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC;YAAE,OAAO,OAAO,CAAC,IAAI,CAAA;IACtD,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAc,EAAE,OAAe;IACvD,MAAM,MAAM,GAAG,EAAE,CAAC,uBAAuB,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,CAAA;IAChE,MAAM,IAAI,GAAG,MAAM;SAChB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,sBAAsB,CAAC;SAC9D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;SACtC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAA;IACzC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACxB,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAe;IACvC,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAA;IACjD,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,CAAA;IAClC,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC1C,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC1C,mEAAmE;IACnE,oEAAoE;IACpE,8DAA8D;IAC9D,MAAM,YAAY,GAChB,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAA;IAC5E,OAAO;QACL,MAAM;QACN,gBAAgB,EAAE,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC;QACrD,eAAe,EAAE,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC;QACnD,YAAY;QACZ,QAAQ,EAAE,YAAY,CAAC,OAAO,CAAC;QAC/B,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC;QAC7B,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC;QACzB,SAAS,EAAE,aAAa,CAAC,OAAO,CAAC;KAClC,CAAA;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,aAAa,CAAC,OAAe;IACpC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAA;IACvE,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;AAC3B,CAAC;AAED;;;;;;GAMG;AACH,SAAS,SAAS,CAAC,OAAe;IAChC,8DAA8D;IAC9D,gEAAgE;IAChE,iEAAiE;IACjE,uBAAuB;IACvB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAA;IACnD,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS;QAAE,OAAO,EAAE,CAAA;IAC/C,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACtB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,MAAM,EAAE,GAAG,mBAAmB,CAAA;IAC9B,IAAI,CAAyB,CAAA;IAC7B,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QACd,IAAI,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,SAAQ;QAC5C,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;QACX,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACb,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAS,UAAU,CAAC,OAAe;IACjC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAA;IAClF,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;AAC3B,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,OAAe;IACnC,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,MAAM,EAAE,GAAG,uDAAuD,CAAA;IAClE,IAAI,CAAyB,CAAA;IAC7B,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACvC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACxC,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAS,WAAW,CAAC,OAAe;IAClC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAA;IACnF,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;AAC3B,CAAC","sourcesContent":["import ts from 'typescript'\n\nexport type DispatchMode = 'shared' | 'human-only' | 'agent-only'\n\n/**\n * Whether the annotation map carries any non-default values. Used to\n * gate `__msgAnnotations` emission — annotations whose every field is\n * default are emission-redundant (the runtime treats absence as the\n * same defaults). Saves ~50 bytes per component for un-annotated Msg\n * unions, which dominates the corpus.\n */\nexport function hasNonDefaultAnnotation(a: Record<string, MessageAnnotations>): boolean {\n for (const v of Object.values(a)) {\n if (v.intent !== null) return true\n if (v.alwaysAffordable) return true\n if (v.requiresConfirm) return true\n if (v.dispatchMode !== 'shared') return true\n if (v.routeGate != null) return true\n }\n return false\n}\n\n/**\n * Build a TS object-literal expression for the annotation map. Used by\n * `msgAnnotationsModule` for `__msgAnnotations` emission. Variant\n * names are emitted as string literals (not identifiers) so\n * discriminants containing `/`, `-`, reserved words, etc. produce\n * valid JS.\n */\nexport function annotationsToObjectLiteral(\n a: Record<string, MessageAnnotations>,\n): ts.ObjectLiteralExpression {\n const props: ts.PropertyAssignment[] = []\n for (const [variant, ann] of Object.entries(a)) {\n props.push(\n ts.factory.createPropertyAssignment(\n // Wrap with createStringLiteral — the printer treats bare strings\n // as identifiers, which produces invalid JS for discriminants\n // containing characters like '/' (e.g. 'Router/RouteChanged'),\n // reserved words ('delete'), or hyphens.\n ts.factory.createStringLiteral(variant),\n ts.factory.createObjectLiteralExpression(\n [\n ts.factory.createPropertyAssignment(\n 'intent',\n ann.intent === null\n ? ts.factory.createNull()\n : ts.factory.createStringLiteral(ann.intent),\n ),\n ts.factory.createPropertyAssignment(\n 'alwaysAffordable',\n ann.alwaysAffordable ? ts.factory.createTrue() : ts.factory.createFalse(),\n ),\n ts.factory.createPropertyAssignment(\n 'requiresConfirm',\n ann.requiresConfirm ? ts.factory.createTrue() : ts.factory.createFalse(),\n ),\n ts.factory.createPropertyAssignment(\n 'dispatchMode',\n ts.factory.createStringLiteral(ann.dispatchMode),\n ),\n // Only emit `routeGate` when non-null. The runtime treats\n // missing as the no-gate default (\"variant follows its\n // dispatchMode-driven affordance behavior\") — keeping the\n // wire shape minimal for the common case of un-gated Msgs.\n // Use loose `!= null` so test fixtures that omit the\n // field entirely also fall through to the no-emit path.\n ...(ann.routeGate != null\n ? [\n ts.factory.createPropertyAssignment(\n 'routeGate',\n ts.factory.createStringLiteral(ann.routeGate),\n ),\n ]\n : []),\n ],\n true,\n ),\n ),\n )\n }\n return ts.factory.createObjectLiteralExpression(props, true)\n}\n\nexport type MessageAnnotations = {\n intent: string | null\n alwaysAffordable: boolean\n requiresConfirm: boolean\n dispatchMode: DispatchMode\n /**\n * Concrete example dispatches the LLM can copy from. Populated by\n * `@example(\"text\")` JSDoc tags. Each tag becomes one entry, in\n * source order, so authors can mix scenarios (\"typical case\",\n * \"edge case with auth\", etc.) without nesting them in a single\n * string.\n */\n examples: string[]\n /**\n * Non-blocking caution. Surfaced verbatim to the agent at affordance\n * time so the LLM can weigh the consequence (\"this overwrites the\n * cloud version\", \"fires analytics that can't be retracted\") before\n * dispatching. Distinct from `requiresConfirm`, which is a runtime\n * gate the user must acknowledge.\n */\n warning: string | null\n /**\n * Effect kinds this variant emits when dispatched, declared by the\n * author via `@emits(\"kind1\", \"kind2\")`. Lets the agent reason\n * about side effects (\"this dispatch hits the cloud, so I should\n * batch\") without the compiler having to walk update.ts. Authored\n * rather than auto-extracted because real apps emit effects\n * through helpers (`track('foo')`, `saveDelta(d)`) — auto-detecting\n * those would require helper-return-shape analysis with\n * ergonomically-painful failure modes; the declarative form trades\n * automatic discovery for accuracy and simplicity.\n *\n * Empty when no `@emits` tag is present.\n */\n emits: string[]\n /**\n * Boolean predicate gating whether the variant surfaces in\n * `list_actions`. Authored as `@routeGated(\"expr\")`; the compiler\n * captures the predicate string verbatim and the runtime evaluates\n * it with `state` bound to the current state. The variant only\n * appears in the agent's affordance list when the predicate\n * returns true.\n *\n * Compile-time alternative to `agentAffordances(state) => Msg[]`\n * for the common case of \"this Msg is reachable when state.X\n * looks like Y.\" Co-located with the Msg definition rather than\n * threaded through a separate hook.\n *\n * Examples:\n * @routeGated(\"state.matrixState.kind === 'loaded'\")\n * @routeGated(\"state.route.kind === 'page' && state.route.slug === 'ranking'\")\n * @routeGated(\"state.auth.status === 'authenticated'\")\n *\n * Null when no `@routeGated` tag is present (variant defaults to\n * its dispatchMode-driven affordance behavior).\n */\n routeGate: string | null\n}\n\nconst DEFAULT: MessageAnnotations = {\n intent: null,\n alwaysAffordable: false,\n requiresConfirm: false,\n dispatchMode: 'shared',\n examples: [],\n warning: null,\n emits: [],\n routeGate: null,\n}\n\n/**\n * Walk a Msg-like discriminated-union type alias and extract JSDoc\n * annotations attached to each union member. Returns null if no\n * recognizable union is found so callers can skip emission cleanly.\n *\n * Expected JSDoc grammar (order-independent):\n * @intent(\"human readable\")\n * @alwaysAffordable\n * @requiresConfirm\n * @humanOnly — sugar for dispatchMode: 'human-only'\n * @agentOnly — sugar for dispatchMode: 'agent-only'\n *\n * Unknown tags are ignored; malformed @intent (no quoted string) is\n * treated as \"no intent\". `@humanOnly` and `@agentOnly` are mutually\n * exclusive — if both are present (which the ESLint rule\n * `agent-exclusive-annotations` reports as an error), the parser\n * falls back to `'shared'` so a misconfigured Msg variant doesn't\n * silently lock out one audience.\n */\nexport function extractMsgAnnotations(\n source: string,\n /**\n * Name of the type alias to extract from. Defaults to `'Msg'` for\n * convention. Passed by the cross-file resolver when the alias has\n * been renamed through imports/re-exports — its local name in the\n * declaring file may differ from `'Msg'`.\n */\n typeName: string = 'Msg',\n): Record<string, MessageAnnotations> | null {\n const sf = ts.createSourceFile('msg.ts', source, ts.ScriptTarget.Latest, true)\n const aliases: ts.TypeAliasDeclaration[] = []\n sf.forEachChild((n) => {\n if (ts.isTypeAliasDeclaration(n)) aliases.push(n)\n })\n const named = aliases.find((a) => a.name.text === typeName)\n // Fallback: only when looking for the conventional 'Msg' name AND the\n // file has no `type Msg = …`; pick any union type alias. With an\n // explicit `typeName` from the resolver, we don't fall back — that\n // would silently match the wrong alias.\n const alias =\n named ?? (typeName === 'Msg' ? aliases.find((a) => ts.isUnionTypeNode(a.type)) : undefined)\n if (!alias || !ts.isUnionTypeNode(alias.type)) return null\n\n const result: Record<string, MessageAnnotations> = {}\n const types = alias.type.types\n for (let i = 0; i < types.length; i++) {\n const member = types[i]\n if (member === undefined || !ts.isTypeLiteralNode(member)) continue\n const variant = readDiscriminantLiteral(member)\n if (!variant) continue\n // Leading JSDoc for union member i is scanned from the end of the\n // previous element (or union.pos for the first member), because\n // TypeScript's parser places comment ranges relative to the token\n // that follows them — and the | bar is not part of the TypeLiteralNode.\n const prev = types[i - 1]\n const scanPos = i === 0 || prev === undefined ? alias.type.pos : prev.end\n const comment = readLeadingJSDoc(source, scanPos)\n result[variant] = parseAnnotations(comment)\n }\n return Object.keys(result).length === 0 ? null : result\n}\n\nfunction readDiscriminantLiteral(lit: ts.TypeLiteralNode): string | null {\n for (const m of lit.members) {\n if (!ts.isPropertySignature(m)) continue\n if (!m.name || !ts.isIdentifier(m.name) || m.name.text !== 'type') continue\n if (!m.type || !ts.isLiteralTypeNode(m.type)) continue\n const literal = m.type.literal\n if (ts.isStringLiteral(literal)) return literal.text\n }\n return null\n}\n\nfunction readLeadingJSDoc(source: string, scanPos: number): string {\n const ranges = ts.getLeadingCommentRanges(source, scanPos) ?? []\n const docs = ranges\n .filter((r) => r.kind === ts.SyntaxKind.MultiLineCommentTrivia)\n .map((r) => source.slice(r.pos, r.end))\n .filter((txt) => txt.startsWith('/**'))\n return docs.join('\\n')\n}\n\nfunction parseAnnotations(comment: string): MessageAnnotations {\n if (!comment) return { ...DEFAULT, examples: [] }\n const intent = readIntent(comment)\n const human = /@humanOnly\\b/.test(comment)\n const agent = /@agentOnly\\b/.test(comment)\n // Mutual-exclusion fallback: both tags present means a config bug;\n // the ESLint rule reports it. At parse time, default to 'shared' so\n // we don't silently lock out one audience based on tag order.\n const dispatchMode: DispatchMode =\n human && !agent ? 'human-only' : agent && !human ? 'agent-only' : 'shared'\n return {\n intent,\n alwaysAffordable: /@alwaysAffordable\\b/.test(comment),\n requiresConfirm: /@requiresConfirm\\b/.test(comment),\n dispatchMode,\n examples: readExamples(comment),\n warning: readWarning(comment),\n emits: readEmits(comment),\n routeGate: readRouteGate(comment),\n }\n}\n\n/**\n * Match `@routeGated(\"predicate-expression\")` (and curly-quote\n * variant). Returns the verbatim predicate string — the runtime\n * compiles it with `new Function('state', 'return (' + src + ')')`\n * and evaluates against the current state to gate affordances.\n *\n * Mirrors `@validates`'s grammar but with `state` as the bound\n * variable instead of `v` (since the predicate sees the whole app\n * state, not a single field value).\n */\nfunction readRouteGate(comment: string): string | null {\n const match = comment.match(/@routeGated\\s*\\(\\s*[\"“]([^\"”]*)[\"”]\\s*\\)/)\n return match?.[1] ?? null\n}\n\n/**\n * Match `@emits(\"k1\", \"k2\", ...)` — comma-separated list of effect\n * kind strings. Each entry can use straight or curly quotes; the\n * separator is `,` with arbitrary whitespace. Returns the kinds in\n * source order (deduped). Empty when the tag is absent or has no\n * quoted strings.\n */\nfunction readEmits(comment: string): string[] {\n // Match the whole `@emits(...)` parenthesized group so we can\n // re-parse the inner content for individual quoted strings. The\n // outer match is non-greedy on the closing paren to avoid eating\n // through later JSDoc.\n const outer = comment.match(/@emits\\s*\\(([^)]*)\\)/)\n if (!outer || outer[1] === undefined) return []\n const inner = outer[1]\n const seen = new Set<string>()\n const out: string[] = []\n const re = /[\"“]([^\"”]*)[\"”]/g\n let m: RegExpExecArray | null\n while ((m = re.exec(inner)) !== null) {\n const v = m[1]\n if (v === undefined || seen.has(v)) continue\n seen.add(v)\n out.push(v)\n }\n return out\n}\n\nfunction readIntent(comment: string): string | null {\n const match = comment.match(/@intent\\s*\\(\\s*[\"\\u201c]([^\"\\u201d]*)[\"\\u201d]\\s*\\)/)\n return match?.[1] ?? null\n}\n\n/**\n * Match every `@example(\"\\u2026\")` (and curly-quote variant) in source\n * order. Multiple tags on one variant are common \\u2014 typical-case,\n * edge-case-with-auth, etc. \\u2014 so the parser collects all of them\n * rather than picking the first.\n */\nfunction readExamples(comment: string): string[] {\n const out: string[] = []\n const re = /@example\\s*\\(\\s*[\"\\u201c]([^\"\\u201d]*)[\"\\u201d]\\s*\\)/g\n let m: RegExpExecArray | null\n while ((m = re.exec(comment)) !== null) {\n if (m[1] !== undefined) out.push(m[1])\n }\n return out\n}\n\nfunction readWarning(comment: string): string | null {\n const match = comment.match(/@warning\\s*\\(\\s*[\"\\u201c]([^\"\\u201d]*)[\"\\u201d]\\s*\\)/)\n return match?.[1] ?? null\n}\n"]}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* The "bare type" of a field. Covers five cases:
|
|
4
|
+
* - primitive keyword as a string: `'string'`, `'number'`, `'boolean'`, `'unknown'`
|
|
5
|
+
* - literal union: `{enum: ['a', 'b']}` for strings, `{enum: [1, 2, 3]}`
|
|
6
|
+
* for numbers, `{enum: [true]}` for booleans. Mixed-type literal
|
|
7
|
+
* unions stay `'unknown'`.
|
|
8
|
+
* - nested object shape: `{kind: 'object', shape: {...}}` — emitted when
|
|
9
|
+
* a field's type is a local interface/type alias the extractor could
|
|
10
|
+
* follow (depth-limited; cross-file references stay `'unknown'`).
|
|
11
|
+
* - array of element type: `{kind: 'array', element: <bare type>}`.
|
|
12
|
+
* - discriminated union of objects: `{kind: 'discriminated-union',
|
|
13
|
+
* discriminant: 'kind', variants: {a: {...}, b: {...}}}`. Emitted
|
|
14
|
+
* when every member of a union is an object literal sharing one
|
|
15
|
+
* literal-string property name with distinct values. Symmetric with
|
|
16
|
+
* how the top-level Msg union itself is encoded — same shape,
|
|
17
|
+
* recursed.
|
|
18
|
+
*
|
|
19
|
+
* The synthesizer in `@llui/agent`'s `list_actions` walks these to build
|
|
20
|
+
* copy-paste-ready payload examples; the validator in `send_message`
|
|
21
|
+
* walks them too (treating object/array as "any" since deep validation
|
|
22
|
+
* is the reducer's job).
|
|
23
|
+
*/
|
|
24
|
+
export type MsgFieldType = string | {
|
|
25
|
+
enum: ReadonlyArray<string | number | boolean>;
|
|
26
|
+
} | {
|
|
27
|
+
kind: 'object';
|
|
28
|
+
shape: Record<string, MsgField>;
|
|
29
|
+
} | {
|
|
30
|
+
kind: 'array';
|
|
31
|
+
element: MsgFieldType;
|
|
32
|
+
} | {
|
|
33
|
+
kind: 'discriminated-union';
|
|
34
|
+
discriminant: string;
|
|
35
|
+
variants: Record<string, Record<string, MsgField>>;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Rich per-field descriptor. Emitted only when there's something
|
|
39
|
+
* beyond the bare type to communicate — optionality, an explicit
|
|
40
|
+
* priority hint, a freeform agent hint, or a runtime validation
|
|
41
|
+
* predicate. When everything but `type` is unset, the producer emits
|
|
42
|
+
* the bare `MsgFieldType` instead so variants without annotations
|
|
43
|
+
* stay byte-cheap in the bundle.
|
|
44
|
+
*/
|
|
45
|
+
export interface MsgFieldRich {
|
|
46
|
+
type: MsgFieldType;
|
|
47
|
+
/** Mirrors TypeScript's `?:` optional marker. Required fields omit this. */
|
|
48
|
+
optional?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Strength signal for optional fields. Borrows RFC 2119's `SHOULD`:
|
|
51
|
+
* the LLM ought to fill it in unless it has a specific reason not
|
|
52
|
+
* to. Required fields don't carry a priority — TS already conveys
|
|
53
|
+
* "must" via the type system. Currently the only level; future
|
|
54
|
+
* extensions could add `'recommended'` or similar.
|
|
55
|
+
*/
|
|
56
|
+
priority?: 'should';
|
|
57
|
+
/** Freeform consequence-shaped explanation. Surfaced verbatim to
|
|
58
|
+
* the LLM at affordance time. */
|
|
59
|
+
hint?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Boolean JS expression that must hold for the field's value to be
|
|
62
|
+
* accepted. The expression has `v` bound to the field's runtime
|
|
63
|
+
* value; everything else is global (Math, JSON, RegExp, etc.).
|
|
64
|
+
* Authored as `@validates("expr")` JSDoc — the compiler captures
|
|
65
|
+
* the source string verbatim and the validator compiles it lazily
|
|
66
|
+
* with `new Function`, caching across calls.
|
|
67
|
+
*
|
|
68
|
+
* Examples:
|
|
69
|
+
* @validates("v >= 0 && v <= 100") // weight 0–100
|
|
70
|
+
* @validates("v.length > 0") // non-empty string
|
|
71
|
+
* @validates("/^[a-z0-9-]+$/.test(v)") // slug format
|
|
72
|
+
*
|
|
73
|
+
* The predicate runs ONLY at the agent boundary. Human-driven
|
|
74
|
+
* dispatches bypass it because TypeScript already validated the
|
|
75
|
+
* call site. Use for invariants the type system can't express
|
|
76
|
+
* (numeric ranges, format predicates, length bounds).
|
|
77
|
+
*/
|
|
78
|
+
validates?: string;
|
|
79
|
+
}
|
|
80
|
+
export type MsgField = MsgFieldType | MsgFieldRich;
|
|
81
|
+
export interface MsgSchema {
|
|
82
|
+
discriminant: string;
|
|
83
|
+
variants: Record<string, Record<string, MsgField>>;
|
|
84
|
+
}
|
|
85
|
+
/** True when `f` is a rich descriptor (object with `type` key). */
|
|
86
|
+
export declare function isRichField(f: MsgField): f is MsgFieldRich;
|
|
87
|
+
/**
|
|
88
|
+
* Build a TS expression for a single field descriptor in a MsgSchema's
|
|
89
|
+
* variant map. Used by `msgSchemaToLiteral` (this file) for the
|
|
90
|
+
* `__msgSchema` / `__effectSchema` emissions. Migrated from inline
|
|
91
|
+
* `buildFieldDescriptorExpr` in transform.ts (v2c/decomp-5).
|
|
92
|
+
*/
|
|
93
|
+
export declare function buildFieldDescriptorExpr(descriptor: MsgField, f: ts.NodeFactory): ts.Expression;
|
|
94
|
+
/**
|
|
95
|
+
* Build the full `{ discriminant, variants }` object literal for a
|
|
96
|
+
* MsgSchema. Symmetric for `__msgSchema` and `__effectSchema` emission
|
|
97
|
+
* (both use the discriminated-union shape).
|
|
98
|
+
*/
|
|
99
|
+
export declare function msgSchemaToLiteral(schema: MsgSchema, f: ts.NodeFactory): ts.ObjectLiteralExpression;
|
|
100
|
+
/** Extracts the bare type from either descriptor form. */
|
|
101
|
+
export declare function fieldType(f: MsgField): MsgFieldType;
|
|
102
|
+
export declare function extractMsgSchema(source: string, typeName?: string): MsgSchema | null;
|
|
103
|
+
export declare function extractEffectSchema(source: string, typeName?: string): MsgSchema | null;
|
|
104
|
+
/**
|
|
105
|
+
* Index of type aliases and interfaces visible from a source file,
|
|
106
|
+
* keyed by name. Lets the field-type resolver follow `Criterion[]` →
|
|
107
|
+
* `interface Criterion { … }` and emit a nested object shape rather
|
|
108
|
+
* than `'unknown'`.
|
|
109
|
+
*
|
|
110
|
+
* The cross-file resolver pipeline (`cross-file-resolver.ts`) builds
|
|
111
|
+
* an enriched index that includes types imported from sibling files —
|
|
112
|
+
* follow `GridSorting` → `'rank' | 'crit-X' | 'crit-Y'` → `{enum: […]}`
|
|
113
|
+
* even when the alias lives in `./state.ts` not the Msg-defining file.
|
|
114
|
+
*/
|
|
115
|
+
export type TypeIndex = Map<string, ts.TypeNode | ts.InterfaceDeclaration>;
|
|
116
|
+
/**
|
|
117
|
+
* Build a single field descriptor from a property signature: type,
|
|
118
|
+
* optionality, and any `@should("…")` JSDoc hint. Emits the compact
|
|
119
|
+
* bare form when there's nothing extra to communicate; otherwise the
|
|
120
|
+
* rich `{type, optional?, priority?, hint?}` shape.
|
|
121
|
+
*
|
|
122
|
+
* Exported so the cross-file resolver (which walks the same property
|
|
123
|
+
* signatures when the Msg type lives in a different file from the
|
|
124
|
+
* `component()` call) can produce identical descriptors. Without
|
|
125
|
+
* sharing this helper, JSDoc hints would silently disappear whenever
|
|
126
|
+
* a Msg union got resolved across module boundaries.
|
|
127
|
+
*/
|
|
128
|
+
export declare function buildFieldDescriptor(member: ts.PropertySignature, source: string, typeIndex?: TypeIndex): MsgField;
|
|
129
|
+
export declare function resolveFieldType(type: ts.TypeNode, typeIndex?: TypeIndex, depth?: number): MsgFieldType;
|
|
130
|
+
//# sourceMappingURL=msg-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msg-schema.d.ts","sourceRoot":"","sources":["../src/msg-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAE3B;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,YAAY,GACpB,MAAM,GACN;IAAE,IAAI,EAAE,aAAa,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GACxC;IACE,IAAI,EAAE,qBAAqB,CAAA;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAA;CACnD,CAAA;AAEL;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,YAAY,CAAA;IAClB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB;sCACkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;;;;;;;;;;;OAiBG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,YAAY,CAAA;AAElD,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAA;CACnD;AAED,mEAAmE;AACnE,wBAAgB,WAAW,CAAC,CAAC,EAAE,QAAQ,GAAG,CAAC,IAAI,YAAY,CAE1D;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC,WAAW,GAAG,EAAE,CAAC,UAAU,CAqE/F;AAQD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,CAAC,EAAE,EAAE,CAAC,WAAW,GAChB,EAAE,CAAC,uBAAuB,CA0B5B;AAED,0DAA0D;AAC1D,wBAAgB,SAAS,CAAC,CAAC,EAAE,QAAQ,GAAG,YAAY,CAEnD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAc,GAAG,SAAS,GAAG,IAAI,CAE3F;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAiB,GAAG,SAAS,GAAG,IAAI,CAEjG;AAqBD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,GAAG,EAAE,CAAC,oBAAoB,CAAC,CAAA;AAsD1E;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,EAAE,CAAC,iBAAiB,EAC5B,MAAM,EAAE,MAAM,EACd,SAAS,GAAE,SAAqB,GAC/B,QAAQ,CAwBV;AAmVD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,EAAE,CAAC,QAAQ,EACjB,SAAS,GAAE,SAAqB,EAChC,KAAK,SAAkB,GACtB,YAAY,CA8Hd"}
|