@llui/compiler-introspection 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/binding-descriptors.d.ts +5 -0
- package/dist/binding-descriptors.d.ts.map +1 -0
- package/dist/binding-descriptors.js +48 -0
- package/dist/binding-descriptors.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -0
- package/dist/msg-annotations.d.ts +9 -0
- package/dist/msg-annotations.d.ts.map +1 -0
- package/dist/msg-annotations.js +54 -0
- package/dist/msg-annotations.js.map +1 -0
- package/dist/msg-schema.d.ts +10 -0
- package/dist/msg-schema.d.ts.map +1 -0
- package/dist/msg-schema.js +70 -0
- package/dist/msg-schema.js.map +1 -0
- package/dist/schema-hash.d.ts +14 -0
- package/dist/schema-hash.d.ts.map +1 -0
- package/dist/schema-hash.js +69 -0
- package/dist/schema-hash.js.map +1 -0
- package/dist/state-schema.d.ts +8 -0
- package/dist/state-schema.d.ts.map +1 -0
- package/dist/state-schema.js +55 -0
- package/dist/state-schema.js.map +1 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Franco Ponticelli
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binding-descriptors.d.ts","sourceRoot":"","sources":["../src/binding-descriptors.ts"],"names":[],"mappings":"AAyBA,OAAO,EAGL,wBAAwB,EAEzB,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpD,OAAO,EAAE,wBAAwB,EAAE,CAAA;AAEnC,eAAO,MAAM,wBAAwB,EAAE,cAuBtC,CAAA"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// `binding-descriptors` — agent-runtime hook tagging. Pre-transform
|
|
2
|
+
// CompilerModule wrapping `tagDispatchHandlers` + `injectScopeVariantRegistrations`.
|
|
3
|
+
//
|
|
4
|
+
// Both functions mutate the AST in ways the visitor model can't
|
|
5
|
+
// cleanly express:
|
|
6
|
+
// - `tagDispatchHandlers`: walks every arrow/fn expression, wraps
|
|
7
|
+
// those whose body contains `send({type:'X'})` with
|
|
8
|
+
// `Object.assign(arrow, {__lluiVariants: ['X']})`. The visitor
|
|
9
|
+
// pattern records findings but doesn't rewrite; rewriting is what
|
|
10
|
+
// this pass does.
|
|
11
|
+
// - `injectScopeVariantRegistrations`: walks for `*.connect(get,
|
|
12
|
+
// send, ...)` call sites and inserts `__registerScopeVariants([...])`
|
|
13
|
+
// adjacent statements. The visitor pattern can't insert siblings
|
|
14
|
+
// into the source file at arbitrary positions.
|
|
15
|
+
//
|
|
16
|
+
// Both are pre-passes — they produce a rewritten SourceFile that the
|
|
17
|
+
// main visitor walks. v2c §2.1's "walker runs once per file" invariant
|
|
18
|
+
// is preserved: the VISITOR walk runs once. preTransform passes are
|
|
19
|
+
// additional but typically cheap and targeted.
|
|
20
|
+
//
|
|
21
|
+
// The `injected` flag from `injectScopeVariantRegistrations` is
|
|
22
|
+
// surfaced via a shared analysis slot — the umbrella's
|
|
23
|
+
// `cleanupImports` reads it to know whether to add the
|
|
24
|
+
// `__registerScopeVariants` runtime helper to the @llui/dom imports.
|
|
25
|
+
import { tagDispatchHandlers, injectScopeVariantRegistrations, BINDING_DESCRIPTORS_SLOT, } from '@llui/compiler';
|
|
26
|
+
export { BINDING_DESCRIPTORS_SLOT };
|
|
27
|
+
export const bindingDescriptorsModule = {
|
|
28
|
+
name: 'binding-descriptors',
|
|
29
|
+
compilerVersion: '^0.3.0',
|
|
30
|
+
diagnostics: [],
|
|
31
|
+
visitors: {},
|
|
32
|
+
preTransform(ctx, sf) {
|
|
33
|
+
// injectScopeVariantRegistrations runs FIRST so its
|
|
34
|
+
// `collectLocalFns` resolver still sees raw arrow initializers in
|
|
35
|
+
// const declarations (the universal tagger below replaces those
|
|
36
|
+
// initializers with `Object.assign(...)` wrappers).
|
|
37
|
+
const injection = injectScopeVariantRegistrations(sf, ctx.factory);
|
|
38
|
+
const tagged = tagDispatchHandlers(injection.sf, ctx.factory);
|
|
39
|
+
// Surface the injected flag for the umbrella's cleanupImports
|
|
40
|
+
// pass. The umbrella reads this from `analysis.perModule` after
|
|
41
|
+
// `registry.run()` returns.
|
|
42
|
+
ctx.analysis.perModule.set(BINDING_DESCRIPTORS_SLOT, {
|
|
43
|
+
scopeRegistrationsInjected: injection.injected,
|
|
44
|
+
});
|
|
45
|
+
return tagged;
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=binding-descriptors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binding-descriptors.js","sourceRoot":"","sources":["../src/binding-descriptors.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,qFAAqF;AACrF,EAAE;AACF,gEAAgE;AAChE,mBAAmB;AACnB,oEAAoE;AACpE,wDAAwD;AACxD,mEAAmE;AACnE,sEAAsE;AACtE,sBAAsB;AACtB,mEAAmE;AACnE,0EAA0E;AAC1E,qEAAqE;AACrE,mDAAmD;AACnD,EAAE;AACF,qEAAqE;AACrE,uEAAuE;AACvE,oEAAoE;AACpE,+CAA+C;AAC/C,EAAE;AACF,gEAAgE;AAChE,uDAAuD;AACvD,uDAAuD;AACvD,qEAAqE;AAErE,OAAO,EACL,mBAAmB,EACnB,+BAA+B,EAC/B,wBAAwB,GAEzB,MAAM,gBAAgB,CAAA;AAGvB,OAAO,EAAE,wBAAwB,EAAE,CAAA;AAEnC,MAAM,CAAC,MAAM,wBAAwB,GAAmB;IACtD,IAAI,EAAE,qBAAqB;IAC3B,eAAe,EAAE,QAAQ;IACzB,WAAW,EAAE,EAAE;IACf,QAAQ,EAAE,EAAE;IAEZ,YAAY,CAAC,GAAG,EAAE,EAAE;QAClB,oDAAoD;QACpD,kEAAkE;QAClE,gEAAgE;QAChE,oDAAoD;QACpD,MAAM,SAAS,GAAG,+BAA+B,CAAC,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;QAClE,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;QAE7D,8DAA8D;QAC9D,gEAAgE;QAChE,4BAA4B;QAC5B,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,wBAAwB,EAAE;YACnD,0BAA0B,EAAE,SAAS,CAAC,QAAQ;SACrB,CAAC,CAAA;QAE5B,OAAO,MAAM,CAAA;IACf,CAAC;CACF,CAAA","sourcesContent":["// `binding-descriptors` — agent-runtime hook tagging. Pre-transform\n// CompilerModule wrapping `tagDispatchHandlers` + `injectScopeVariantRegistrations`.\n//\n// Both functions mutate the AST in ways the visitor model can't\n// cleanly express:\n// - `tagDispatchHandlers`: walks every arrow/fn expression, wraps\n// those whose body contains `send({type:'X'})` with\n// `Object.assign(arrow, {__lluiVariants: ['X']})`. The visitor\n// pattern records findings but doesn't rewrite; rewriting is what\n// this pass does.\n// - `injectScopeVariantRegistrations`: walks for `*.connect(get,\n// send, ...)` call sites and inserts `__registerScopeVariants([...])`\n// adjacent statements. The visitor pattern can't insert siblings\n// into the source file at arbitrary positions.\n//\n// Both are pre-passes — they produce a rewritten SourceFile that the\n// main visitor walks. v2c §2.1's \"walker runs once per file\" invariant\n// is preserved: the VISITOR walk runs once. preTransform passes are\n// additional but typically cheap and targeted.\n//\n// The `injected` flag from `injectScopeVariantRegistrations` is\n// surfaced via a shared analysis slot — the umbrella's\n// `cleanupImports` reads it to know whether to add the\n// `__registerScopeVariants` runtime helper to the @llui/dom imports.\n\nimport {\n tagDispatchHandlers,\n injectScopeVariantRegistrations,\n BINDING_DESCRIPTORS_SLOT,\n type BindingDescriptorsSlot,\n} from '@llui/compiler'\nimport type { CompilerModule } from '@llui/compiler'\n\nexport { BINDING_DESCRIPTORS_SLOT }\n\nexport const bindingDescriptorsModule: CompilerModule = {\n name: 'binding-descriptors',\n compilerVersion: '^0.3.0',\n diagnostics: [],\n visitors: {},\n\n preTransform(ctx, sf) {\n // injectScopeVariantRegistrations runs FIRST so its\n // `collectLocalFns` resolver still sees raw arrow initializers in\n // const declarations (the universal tagger below replaces those\n // initializers with `Object.assign(...)` wrappers).\n const injection = injectScopeVariantRegistrations(sf, ctx.factory)\n const tagged = tagDispatchHandlers(injection.sf, ctx.factory)\n\n // Surface the injected flag for the umbrella's cleanupImports\n // pass. The umbrella reads this from `analysis.perModule` after\n // `registry.run()` returns.\n ctx.analysis.perModule.set(BINDING_DESCRIPTORS_SLOT, {\n scopeRegistrationsInjected: injection.injected,\n } as BindingDescriptorsSlot)\n\n return tagged\n },\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type IntrospectionFactory } from '@llui/compiler';
|
|
2
|
+
import { msgSchemaModule, type MsgSchemaModuleOptions } from './msg-schema.js';
|
|
3
|
+
import { stateSchemaModule, type StateSchemaModuleOptions } from './state-schema.js';
|
|
4
|
+
import { msgAnnotationsModule, type MsgAnnotationsModuleOptions } from './msg-annotations.js';
|
|
5
|
+
import { schemaHashModule, SCHEMA_HASH_INPUTS_SLOT, type SchemaHashInputs } from './schema-hash.js';
|
|
6
|
+
import { bindingDescriptorsModule, BINDING_DESCRIPTORS_SLOT } from './binding-descriptors.js';
|
|
7
|
+
export { msgSchemaModule, type MsgSchemaModuleOptions, stateSchemaModule, type StateSchemaModuleOptions, msgAnnotationsModule, type MsgAnnotationsModuleOptions, schemaHashModule, SCHEMA_HASH_INPUTS_SLOT, type SchemaHashInputs, bindingDescriptorsModule, BINDING_DESCRIPTORS_SLOT, };
|
|
8
|
+
/**
|
|
9
|
+
* Builds the introspection module set for a single source file.
|
|
10
|
+
* Activation order matches the v2c/decomp-7 design:
|
|
11
|
+
* 1. `bindingDescriptorsModule` — preTransform fires first so the
|
|
12
|
+
* universal handler-tagger + scope-variant-registration runs
|
|
13
|
+
* before any visitor or emit phase sees the file.
|
|
14
|
+
* 2. `msgSchemaModule`, `stateSchemaModule`, `msgAnnotationsModule`
|
|
15
|
+
* — producer modules populate the schema-hash inputs slot.
|
|
16
|
+
* 3. `schemaHashModule` — emit reads the populated slot.
|
|
17
|
+
*
|
|
18
|
+
* Per-module activation gates mirror what `transformLlui` did inline
|
|
19
|
+
* before decomp-26:
|
|
20
|
+
* - `msgSchemaModule` when at least one of Msg / Effect schemas extracted
|
|
21
|
+
* - `stateSchemaModule` when State schema extracted
|
|
22
|
+
* - `msgAnnotationsModule` when annotation map non-null (includes empty)
|
|
23
|
+
* - `schemaHashModule` always (well-defined hash over null inputs)
|
|
24
|
+
* - `bindingDescriptorsModule` always when introspection is on
|
|
25
|
+
*
|
|
26
|
+
* The "always-on schemaHash" comes from spec §7.4: the hash ships in
|
|
27
|
+
* prod too, used by HMR re-send gating regardless of agent mode.
|
|
28
|
+
*/
|
|
29
|
+
export declare const introspectionFactory: IntrospectionFactory;
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,KAAK,oBAAoB,EAAuC,MAAM,gBAAgB,CAAA;AAC/F,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAC9E,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,MAAM,mBAAmB,CAAA;AACpF,OAAO,EAAE,oBAAoB,EAAE,KAAK,2BAA2B,EAAE,MAAM,sBAAsB,CAAA;AAC7F,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACnG,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAA;AAK7F,OAAO,EACL,eAAe,EACf,KAAK,sBAAsB,EAC3B,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,gBAAgB,EAChB,uBAAuB,EACvB,KAAK,gBAAgB,EACrB,wBAAwB,EACxB,wBAAwB,GACzB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,oBAAoB,EAAE,oBAqClC,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// @llui/compiler-introspection — runtime introspection metadata (opt-in).
|
|
2
|
+
//
|
|
3
|
+
// Emits the compile-time metadata that downstream runtime
|
|
4
|
+
// introspectors consume: the end-user-facing `@llui/agent-bridge`
|
|
5
|
+
// (in-app agent) and the dev-facing `@llui/mcp` (dev LLM tooling).
|
|
6
|
+
// Both reads the same compiled output; this package owns the
|
|
7
|
+
// emission side, the runtime packages own the consumption side.
|
|
8
|
+
//
|
|
9
|
+
// Modules:
|
|
10
|
+
// - msg-schema __msgSchema + __effectSchema
|
|
11
|
+
// - msg-annotations __msgAnnotations
|
|
12
|
+
// - state-schema __stateSchema
|
|
13
|
+
// - schema-hash __schemaHash (HMR re-send gating)
|
|
14
|
+
// - binding-descriptors preTransform pass tagging handler
|
|
15
|
+
// arrows with __lluiVariants
|
|
16
|
+
//
|
|
17
|
+
// Activation: opt-in. The package registers a factory with
|
|
18
|
+
// `@llui/compiler` via `registerIntrospectionFactory`. The host
|
|
19
|
+
// (test setup, Vite plugin, MCP) imports this package and calls
|
|
20
|
+
// `registerIntrospectionFactory(introspectionFactory)` once at
|
|
21
|
+
// process start. transformLlui then activates the modules when
|
|
22
|
+
// `shouldEmitAgentMetadata` is true.
|
|
23
|
+
import {} from '@llui/compiler';
|
|
24
|
+
import { msgSchemaModule } from './msg-schema.js';
|
|
25
|
+
import { stateSchemaModule } from './state-schema.js';
|
|
26
|
+
import { msgAnnotationsModule } from './msg-annotations.js';
|
|
27
|
+
import { schemaHashModule, SCHEMA_HASH_INPUTS_SLOT } from './schema-hash.js';
|
|
28
|
+
import { bindingDescriptorsModule, BINDING_DESCRIPTORS_SLOT } from './binding-descriptors.js';
|
|
29
|
+
// Re-export individual modules + slots so adapters that want fine-
|
|
30
|
+
// grained control (e.g. a test that wants only schemaHash) can still
|
|
31
|
+
// reach them. The default consumption path is the factory below.
|
|
32
|
+
export { msgSchemaModule, stateSchemaModule, msgAnnotationsModule, schemaHashModule, SCHEMA_HASH_INPUTS_SLOT, bindingDescriptorsModule, BINDING_DESCRIPTORS_SLOT, };
|
|
33
|
+
/**
|
|
34
|
+
* Builds the introspection module set for a single source file.
|
|
35
|
+
* Activation order matches the v2c/decomp-7 design:
|
|
36
|
+
* 1. `bindingDescriptorsModule` — preTransform fires first so the
|
|
37
|
+
* universal handler-tagger + scope-variant-registration runs
|
|
38
|
+
* before any visitor or emit phase sees the file.
|
|
39
|
+
* 2. `msgSchemaModule`, `stateSchemaModule`, `msgAnnotationsModule`
|
|
40
|
+
* — producer modules populate the schema-hash inputs slot.
|
|
41
|
+
* 3. `schemaHashModule` — emit reads the populated slot.
|
|
42
|
+
*
|
|
43
|
+
* Per-module activation gates mirror what `transformLlui` did inline
|
|
44
|
+
* before decomp-26:
|
|
45
|
+
* - `msgSchemaModule` when at least one of Msg / Effect schemas extracted
|
|
46
|
+
* - `stateSchemaModule` when State schema extracted
|
|
47
|
+
* - `msgAnnotationsModule` when annotation map non-null (includes empty)
|
|
48
|
+
* - `schemaHashModule` always (well-defined hash over null inputs)
|
|
49
|
+
* - `bindingDescriptorsModule` always when introspection is on
|
|
50
|
+
*
|
|
51
|
+
* The "always-on schemaHash" comes from spec §7.4: the hash ships in
|
|
52
|
+
* prod too, used by HMR re-send gating regardless of agent mode.
|
|
53
|
+
*/
|
|
54
|
+
export const introspectionFactory = (input) => {
|
|
55
|
+
const modules = [];
|
|
56
|
+
// Agent-gated modules — only fire when shouldEmitAgentMetadata is true.
|
|
57
|
+
// binding-descriptors and the schema producers all emit metadata the
|
|
58
|
+
// end-user agent / dev MCP reads; out of agent mode they're dead weight.
|
|
59
|
+
if (input.shouldEmitAgentMetadata) {
|
|
60
|
+
// binding-descriptors fires FIRST (preTransform pass) so subsequent
|
|
61
|
+
// module visitors see the post-mutation source file.
|
|
62
|
+
modules.push(bindingDescriptorsModule);
|
|
63
|
+
// Producer modules — order matters: they populate the schema-hash
|
|
64
|
+
// inputs slot before schemaHashModule's emit consumes it.
|
|
65
|
+
const msgSchema = input.msgSchema;
|
|
66
|
+
const effectSchema = input.effectSchema;
|
|
67
|
+
if (msgSchema || effectSchema) {
|
|
68
|
+
modules.push(msgSchemaModule({ msgSchema, effectSchema }));
|
|
69
|
+
}
|
|
70
|
+
const stateSchema = input.stateSchema;
|
|
71
|
+
if (stateSchema) {
|
|
72
|
+
modules.push(stateSchemaModule({ stateSchema }));
|
|
73
|
+
}
|
|
74
|
+
const msgAnnotations = input.msgAnnotations;
|
|
75
|
+
if (msgAnnotations !== null) {
|
|
76
|
+
modules.push(msgAnnotationsModule({ msgAnnotations }));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// schemaHashModule registers UNCONDITIONALLY — hash is well-defined
|
|
80
|
+
// for null inputs (deterministic). HMR re-send gating consumes the
|
|
81
|
+
// hash in prod builds (spec §7.4) even when no other introspection
|
|
82
|
+
// metadata is emitted.
|
|
83
|
+
modules.push(schemaHashModule);
|
|
84
|
+
return modules;
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,EAAE;AACF,0DAA0D;AAC1D,kEAAkE;AAClE,mEAAmE;AACnE,6DAA6D;AAC7D,gEAAgE;AAChE,EAAE;AACF,WAAW;AACX,0DAA0D;AAC1D,8CAA8C;AAC9C,2CAA2C;AAC3C,+DAA+D;AAC/D,+DAA+D;AAC/D,wDAAwD;AACxD,EAAE;AACF,2DAA2D;AAC3D,gEAAgE;AAChE,gEAAgE;AAChE,+DAA+D;AAC/D,+DAA+D;AAC/D,qCAAqC;AAErC,OAAO,EAAkE,MAAM,gBAAgB,CAAA;AAC/F,OAAO,EAAE,eAAe,EAA+B,MAAM,iBAAiB,CAAA;AAC9E,OAAO,EAAE,iBAAiB,EAAiC,MAAM,mBAAmB,CAAA;AACpF,OAAO,EAAE,oBAAoB,EAAoC,MAAM,sBAAsB,CAAA;AAC7F,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAyB,MAAM,kBAAkB,CAAA;AACnG,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAA;AAE7F,mEAAmE;AACnE,qEAAqE;AACrE,iEAAiE;AACjE,OAAO,EACL,eAAe,EAEf,iBAAiB,EAEjB,oBAAoB,EAEpB,gBAAgB,EAChB,uBAAuB,EAEvB,wBAAwB,EACxB,wBAAwB,GACzB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAyB,CAAC,KAAK,EAAE,EAAE;IAClE,MAAM,OAAO,GAAqB,EAAE,CAAA;IAEpC,wEAAwE;IACxE,qEAAqE;IACrE,yEAAyE;IACzE,IAAI,KAAK,CAAC,uBAAuB,EAAE,CAAC;QAClC,oEAAoE;QACpE,qDAAqD;QACrD,OAAO,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAA;QAEtC,kEAAkE;QAClE,0DAA0D;QAC1D,MAAM,SAAS,GAAG,KAAK,CAAC,SAA6B,CAAA;QACrD,MAAM,YAAY,GAAG,KAAK,CAAC,YAAgC,CAAA;QAC3D,IAAI,SAAS,IAAI,YAAY,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC,CAAA;QAC5D,CAAC;QACD,MAAM,WAAW,GAAG,KAAK,CAAC,WAAqE,CAAA;QAC/F,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAA;QAClD,CAAC;QACD,MAAM,cAAc,GAAG,KAAK,CAAC,cAEP,CAAA;QACtB,IAAI,cAAc,KAAK,IAAI,EAAE,CAAC;YAC5B,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAA;QACxD,CAAC;IACH,CAAC;IAED,oEAAoE;IACpE,mEAAmE;IACnE,mEAAmE;IACnE,uBAAuB;IACvB,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;IAE9B,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA","sourcesContent":["// @llui/compiler-introspection — runtime introspection metadata (opt-in).\n//\n// Emits the compile-time metadata that downstream runtime\n// introspectors consume: the end-user-facing `@llui/agent-bridge`\n// (in-app agent) and the dev-facing `@llui/mcp` (dev LLM tooling).\n// Both reads the same compiled output; this package owns the\n// emission side, the runtime packages own the consumption side.\n//\n// Modules:\n// - msg-schema __msgSchema + __effectSchema\n// - msg-annotations __msgAnnotations\n// - state-schema __stateSchema\n// - schema-hash __schemaHash (HMR re-send gating)\n// - binding-descriptors preTransform pass tagging handler\n// arrows with __lluiVariants\n//\n// Activation: opt-in. The package registers a factory with\n// `@llui/compiler` via `registerIntrospectionFactory`. The host\n// (test setup, Vite plugin, MCP) imports this package and calls\n// `registerIntrospectionFactory(introspectionFactory)` once at\n// process start. transformLlui then activates the modules when\n// `shouldEmitAgentMetadata` is true.\n\nimport { type IntrospectionFactory, type CompilerModule, type MsgSchema } from '@llui/compiler'\nimport { msgSchemaModule, type MsgSchemaModuleOptions } from './msg-schema.js'\nimport { stateSchemaModule, type StateSchemaModuleOptions } from './state-schema.js'\nimport { msgAnnotationsModule, type MsgAnnotationsModuleOptions } from './msg-annotations.js'\nimport { schemaHashModule, SCHEMA_HASH_INPUTS_SLOT, type SchemaHashInputs } from './schema-hash.js'\nimport { bindingDescriptorsModule, BINDING_DESCRIPTORS_SLOT } from './binding-descriptors.js'\n\n// Re-export individual modules + slots so adapters that want fine-\n// grained control (e.g. a test that wants only schemaHash) can still\n// reach them. The default consumption path is the factory below.\nexport {\n msgSchemaModule,\n type MsgSchemaModuleOptions,\n stateSchemaModule,\n type StateSchemaModuleOptions,\n msgAnnotationsModule,\n type MsgAnnotationsModuleOptions,\n schemaHashModule,\n SCHEMA_HASH_INPUTS_SLOT,\n type SchemaHashInputs,\n bindingDescriptorsModule,\n BINDING_DESCRIPTORS_SLOT,\n}\n\n/**\n * Builds the introspection module set for a single source file.\n * Activation order matches the v2c/decomp-7 design:\n * 1. `bindingDescriptorsModule` — preTransform fires first so the\n * universal handler-tagger + scope-variant-registration runs\n * before any visitor or emit phase sees the file.\n * 2. `msgSchemaModule`, `stateSchemaModule`, `msgAnnotationsModule`\n * — producer modules populate the schema-hash inputs slot.\n * 3. `schemaHashModule` — emit reads the populated slot.\n *\n * Per-module activation gates mirror what `transformLlui` did inline\n * before decomp-26:\n * - `msgSchemaModule` when at least one of Msg / Effect schemas extracted\n * - `stateSchemaModule` when State schema extracted\n * - `msgAnnotationsModule` when annotation map non-null (includes empty)\n * - `schemaHashModule` always (well-defined hash over null inputs)\n * - `bindingDescriptorsModule` always when introspection is on\n *\n * The \"always-on schemaHash\" comes from spec §7.4: the hash ships in\n * prod too, used by HMR re-send gating regardless of agent mode.\n */\nexport const introspectionFactory: IntrospectionFactory = (input) => {\n const modules: CompilerModule[] = []\n\n // Agent-gated modules — only fire when shouldEmitAgentMetadata is true.\n // binding-descriptors and the schema producers all emit metadata the\n // end-user agent / dev MCP reads; out of agent mode they're dead weight.\n if (input.shouldEmitAgentMetadata) {\n // binding-descriptors fires FIRST (preTransform pass) so subsequent\n // module visitors see the post-mutation source file.\n modules.push(bindingDescriptorsModule)\n\n // Producer modules — order matters: they populate the schema-hash\n // inputs slot before schemaHashModule's emit consumes it.\n const msgSchema = input.msgSchema as MsgSchema | null\n const effectSchema = input.effectSchema as MsgSchema | null\n if (msgSchema || effectSchema) {\n modules.push(msgSchemaModule({ msgSchema, effectSchema }))\n }\n const stateSchema = input.stateSchema as Parameters<typeof stateSchemaModule>[0]['stateSchema']\n if (stateSchema) {\n modules.push(stateSchemaModule({ stateSchema }))\n }\n const msgAnnotations = input.msgAnnotations as Parameters<\n typeof msgAnnotationsModule\n >[0]['msgAnnotations']\n if (msgAnnotations !== null) {\n modules.push(msgAnnotationsModule({ msgAnnotations }))\n }\n }\n\n // schemaHashModule registers UNCONDITIONALLY — hash is well-defined\n // for null inputs (deterministic). HMR re-send gating consumes the\n // hash in prod builds (spec §7.4) even when no other introspection\n // metadata is emitted.\n modules.push(schemaHashModule)\n\n return modules\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CompilerModule } from '@llui/compiler';
|
|
2
|
+
import { type MessageAnnotations } from '@llui/compiler';
|
|
3
|
+
export interface MsgAnnotationsModuleOptions {
|
|
4
|
+
/** Pre-computed annotation map. Null when extraction failed; empty
|
|
5
|
+
* record when there are no Msg variants at all. */
|
|
6
|
+
msgAnnotations: Record<string, MessageAnnotations> | null;
|
|
7
|
+
}
|
|
8
|
+
export declare function msgAnnotationsModule(opts: MsgAnnotationsModuleOptions): CompilerModule;
|
|
9
|
+
//# 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":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAwB,MAAM,gBAAgB,CAAA;AAC1E,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAA;AAIvB,MAAM,WAAW,2BAA2B;IAC1C;uDACmD;IACnD,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAAA;CAC1D;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,2BAA2B,GAAG,cAAc,CAsCtF"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// `msg-annotations` — emits `__msgAnnotations` per `component()` call
|
|
2
|
+
// when the annotation map has any non-default field, and populates the
|
|
3
|
+
// schema-hash inputs slot. Factory module taking the pre-computed
|
|
4
|
+
// annotation map (from `extractMsgAnnotations` / cross-file resolver
|
|
5
|
+
// upstream).
|
|
6
|
+
//
|
|
7
|
+
// Migrated from inline `injectMsgAnnotations` in transform.ts (v2c/
|
|
8
|
+
// decomp-4). The "gate on hasNonDefaultAnnotation" rule preserves: when
|
|
9
|
+
// every variant carries default intent/alwaysAffordable/etc., the
|
|
10
|
+
// emission is suppressed (runtime treats absence as defaults). This is
|
|
11
|
+
// a real win for un-annotated Msg unions, which dominate.
|
|
12
|
+
import { annotationsToObjectLiteral, hasNonDefaultAnnotation, } from '@llui/compiler';
|
|
13
|
+
import { SCHEMA_HASH_INPUTS_SLOT } from './schema-hash.js';
|
|
14
|
+
import { findComponentCalls } from '@llui/compiler';
|
|
15
|
+
export function msgAnnotationsModule(opts) {
|
|
16
|
+
return {
|
|
17
|
+
name: 'msg-annotations',
|
|
18
|
+
compilerVersion: '^0.3.0',
|
|
19
|
+
diagnostics: [],
|
|
20
|
+
// Targets captured in `emit` — see `_shared.ts`.
|
|
21
|
+
visitors: {},
|
|
22
|
+
emit(_ctx, analysis) {
|
|
23
|
+
const annotations = opts.msgAnnotations;
|
|
24
|
+
// Populate schema-hash inputs regardless of `hasNonDefault` — the
|
|
25
|
+
// hash is over the full annotation map (defaults included), so it
|
|
26
|
+
// must reflect the resolver's output verbatim.
|
|
27
|
+
if (annotations !== null) {
|
|
28
|
+
const hashInputs = analysis.perModule.get(SCHEMA_HASH_INPUTS_SLOT);
|
|
29
|
+
if (hashInputs) {
|
|
30
|
+
hashInputs.msgAnnotations = annotations;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
analysis.perModule.set(SCHEMA_HASH_INPUTS_SLOT, {
|
|
34
|
+
msgSchema: null,
|
|
35
|
+
stateSchema: null,
|
|
36
|
+
msgAnnotations: annotations,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (!annotations || !hasNonDefaultAnnotation(annotations))
|
|
41
|
+
return [];
|
|
42
|
+
const calls = findComponentCalls(analysis.sourceFile);
|
|
43
|
+
if (calls.length === 0)
|
|
44
|
+
return [];
|
|
45
|
+
return calls.map((call) => ({
|
|
46
|
+
module: 'msg-annotations',
|
|
47
|
+
field: '__msgAnnotations',
|
|
48
|
+
value: annotationsToObjectLiteral(annotations),
|
|
49
|
+
target: call,
|
|
50
|
+
}));
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=msg-annotations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msg-annotations.js","sourceRoot":"","sources":["../src/msg-annotations.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,uEAAuE;AACvE,kEAAkE;AAClE,qEAAqE;AACrE,aAAa;AACb,EAAE;AACF,oEAAoE;AACpE,wEAAwE;AACxE,kEAAkE;AAClE,uEAAuE;AACvE,0DAA0D;AAG1D,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,GAExB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,uBAAuB,EAAyB,MAAM,kBAAkB,CAAA;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAQnD,MAAM,UAAU,oBAAoB,CAAC,IAAiC;IACpE,OAAO;QACL,IAAI,EAAE,iBAAiB;QACvB,eAAe,EAAE,QAAQ;QACzB,WAAW,EAAE,EAAE;QACf,iDAAiD;QACjD,QAAQ,EAAE,EAAE;QAEZ,IAAI,CAAC,IAAI,EAAE,QAAQ;YACjB,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,CAAA;YACvC,kEAAkE;YAClE,kEAAkE;YAClE,+CAA+C;YAC/C,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;gBACzB,MAAM,UAAU,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,uBAAuB,CAEpD,CAAA;gBACb,IAAI,UAAU,EAAE,CAAC;oBACf,UAAU,CAAC,cAAc,GAAG,WAAW,CAAA;gBACzC,CAAC;qBAAM,CAAC;oBACN,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,uBAAuB,EAAE;wBAC9C,SAAS,EAAE,IAAI;wBACf,WAAW,EAAE,IAAI;wBACjB,cAAc,EAAE,WAAW;qBACR,CAAC,CAAA;gBACxB,CAAC;YACH,CAAC;YACD,IAAI,CAAC,WAAW,IAAI,CAAC,uBAAuB,CAAC,WAAW,CAAC;gBAAE,OAAO,EAAE,CAAA;YACpE,MAAM,KAAK,GAAG,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;YACrD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAA;YACjC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC1B,MAAM,EAAE,iBAAiB;gBACzB,KAAK,EAAE,kBAAkB;gBACzB,KAAK,EAAE,0BAA0B,CAAC,WAAW,CAAC;gBAC9C,MAAM,EAAE,IAAI;aACb,CAAC,CAAC,CAAA;QACL,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["// `msg-annotations` — emits `__msgAnnotations` per `component()` call\n// when the annotation map has any non-default field, and populates the\n// schema-hash inputs slot. Factory module taking the pre-computed\n// annotation map (from `extractMsgAnnotations` / cross-file resolver\n// upstream).\n//\n// Migrated from inline `injectMsgAnnotations` in transform.ts (v2c/\n// decomp-4). The \"gate on hasNonDefaultAnnotation\" rule preserves: when\n// every variant carries default intent/alwaysAffordable/etc., the\n// emission is suppressed (runtime treats absence as defaults). This is\n// a real win for un-annotated Msg unions, which dominate.\n\nimport type { CompilerModule, EmissionContribution } from '@llui/compiler'\nimport {\n annotationsToObjectLiteral,\n hasNonDefaultAnnotation,\n type MessageAnnotations,\n} from '@llui/compiler'\nimport { SCHEMA_HASH_INPUTS_SLOT, type SchemaHashInputs } from './schema-hash.js'\nimport { findComponentCalls } from '@llui/compiler'\n\nexport interface MsgAnnotationsModuleOptions {\n /** Pre-computed annotation map. Null when extraction failed; empty\n * record when there are no Msg variants at all. */\n msgAnnotations: Record<string, MessageAnnotations> | null\n}\n\nexport function msgAnnotationsModule(opts: MsgAnnotationsModuleOptions): CompilerModule {\n return {\n name: 'msg-annotations',\n compilerVersion: '^0.3.0',\n diagnostics: [],\n // Targets captured in `emit` — see `_shared.ts`.\n visitors: {},\n\n emit(_ctx, analysis): EmissionContribution[] {\n const annotations = opts.msgAnnotations\n // Populate schema-hash inputs regardless of `hasNonDefault` — the\n // hash is over the full annotation map (defaults included), so it\n // must reflect the resolver's output verbatim.\n if (annotations !== null) {\n const hashInputs = analysis.perModule.get(SCHEMA_HASH_INPUTS_SLOT) as\n | SchemaHashInputs\n | undefined\n if (hashInputs) {\n hashInputs.msgAnnotations = annotations\n } else {\n analysis.perModule.set(SCHEMA_HASH_INPUTS_SLOT, {\n msgSchema: null,\n stateSchema: null,\n msgAnnotations: annotations,\n } as SchemaHashInputs)\n }\n }\n if (!annotations || !hasNonDefaultAnnotation(annotations)) return []\n const calls = findComponentCalls(analysis.sourceFile)\n if (calls.length === 0) return []\n return calls.map((call) => ({\n module: 'msg-annotations',\n field: '__msgAnnotations',\n value: annotationsToObjectLiteral(annotations),\n target: call,\n }))\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CompilerModule } from '@llui/compiler';
|
|
2
|
+
import { type MsgSchema } from '@llui/compiler';
|
|
3
|
+
export interface MsgSchemaModuleOptions {
|
|
4
|
+
/** Pre-computed Msg schema; null when extraction failed. */
|
|
5
|
+
msgSchema: MsgSchema | null;
|
|
6
|
+
/** Pre-computed Effect schema; null when not present in source. */
|
|
7
|
+
effectSchema: MsgSchema | null;
|
|
8
|
+
}
|
|
9
|
+
export declare function msgSchemaModule(opts: MsgSchemaModuleOptions): CompilerModule;
|
|
10
|
+
//# 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":"AAiBA,OAAO,KAAK,EAAE,cAAc,EAAwB,MAAM,gBAAgB,CAAA;AAC1E,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAInE,MAAM,WAAW,sBAAsB;IACrC,4DAA4D;IAC5D,SAAS,EAAE,SAAS,GAAG,IAAI,CAAA;IAC3B,mEAAmE;IACnE,YAAY,EAAE,SAAS,GAAG,IAAI,CAAA;CAC/B;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,cAAc,CAkD5E"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// `msg-schema` — emits `__msgSchema` + `__effectSchema` per
|
|
2
|
+
// `component()` call and populates the schema-hash inputs slot.
|
|
3
|
+
// Factory module taking the pre-computed MsgSchema for Msg and Effect.
|
|
4
|
+
//
|
|
5
|
+
// Migrated from inline `injectMsgSchema` + `injectEffectSchema` in
|
|
6
|
+
// transform.ts (v2c/decomp-5). The literal builder
|
|
7
|
+
// `msgSchemaToLiteral` (from `msg-schema.ts`) is shared between the
|
|
8
|
+
// two emissions — Msg and Effect use the same discriminated-union
|
|
9
|
+
// shape on the wire.
|
|
10
|
+
//
|
|
11
|
+
// When this module is in the active list alongside `state-schema`
|
|
12
|
+
// and `msg-annotations`, the schema-hash inputs slot is fully
|
|
13
|
+
// populated and `schemaHashModule` produces the authoritative
|
|
14
|
+
// `__schemaHash` emission. At that point the inline
|
|
15
|
+
// `injectSchemaHash` in transform.ts also deletes — see
|
|
16
|
+
// v2c/decomp-5's migration.
|
|
17
|
+
import { msgSchemaToLiteral } from '@llui/compiler';
|
|
18
|
+
import { SCHEMA_HASH_INPUTS_SLOT } from './schema-hash.js';
|
|
19
|
+
import { findComponentCalls } from '@llui/compiler';
|
|
20
|
+
export function msgSchemaModule(opts) {
|
|
21
|
+
return {
|
|
22
|
+
name: 'msg-schema',
|
|
23
|
+
compilerVersion: '^0.3.0',
|
|
24
|
+
diagnostics: [],
|
|
25
|
+
// Targets captured in `emit` — see `_shared.ts`.
|
|
26
|
+
visitors: {},
|
|
27
|
+
emit(ctx, analysis) {
|
|
28
|
+
// Populate schema-hash inputs slot with our msgSchema input — the
|
|
29
|
+
// schemaHashModule will pick it up alongside whatever
|
|
30
|
+
// stateSchemaModule and msgAnnotationsModule wrote.
|
|
31
|
+
if (opts.msgSchema !== null) {
|
|
32
|
+
const hashInputs = analysis.perModule.get(SCHEMA_HASH_INPUTS_SLOT);
|
|
33
|
+
if (hashInputs) {
|
|
34
|
+
hashInputs.msgSchema = opts.msgSchema;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
analysis.perModule.set(SCHEMA_HASH_INPUTS_SLOT, {
|
|
38
|
+
msgSchema: opts.msgSchema,
|
|
39
|
+
stateSchema: null,
|
|
40
|
+
msgAnnotations: null,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const calls = findComponentCalls(analysis.sourceFile);
|
|
45
|
+
if (calls.length === 0)
|
|
46
|
+
return [];
|
|
47
|
+
const out = [];
|
|
48
|
+
for (const call of calls) {
|
|
49
|
+
if (opts.msgSchema !== null) {
|
|
50
|
+
out.push({
|
|
51
|
+
module: 'msg-schema',
|
|
52
|
+
field: '__msgSchema',
|
|
53
|
+
value: msgSchemaToLiteral(opts.msgSchema, ctx.factory),
|
|
54
|
+
target: call,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (opts.effectSchema !== null) {
|
|
58
|
+
out.push({
|
|
59
|
+
module: 'msg-schema',
|
|
60
|
+
field: '__effectSchema',
|
|
61
|
+
value: msgSchemaToLiteral(opts.effectSchema, ctx.factory),
|
|
62
|
+
target: call,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=msg-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msg-schema.js","sourceRoot":"","sources":["../src/msg-schema.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,gEAAgE;AAChE,uEAAuE;AACvE,EAAE;AACF,mEAAmE;AACnE,mDAAmD;AACnD,oEAAoE;AACpE,kEAAkE;AAClE,qBAAqB;AACrB,EAAE;AACF,kEAAkE;AAClE,8DAA8D;AAC9D,8DAA8D;AAC9D,oDAAoD;AACpD,wDAAwD;AACxD,4BAA4B;AAG5B,OAAO,EAAE,kBAAkB,EAAkB,MAAM,gBAAgB,CAAA;AACnE,OAAO,EAAE,uBAAuB,EAAyB,MAAM,kBAAkB,CAAA;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AASnD,MAAM,UAAU,eAAe,CAAC,IAA4B;IAC1D,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,eAAe,EAAE,QAAQ;QACzB,WAAW,EAAE,EAAE;QACf,iDAAiD;QACjD,QAAQ,EAAE,EAAE;QAEZ,IAAI,CAAC,GAAG,EAAE,QAAQ;YAChB,kEAAkE;YAClE,sDAAsD;YACtD,oDAAoD;YACpD,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;gBAC5B,MAAM,UAAU,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,uBAAuB,CAEpD,CAAA;gBACb,IAAI,UAAU,EAAE,CAAC;oBACf,UAAU,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAA;gBACvC,CAAC;qBAAM,CAAC;oBACN,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,uBAAuB,EAAE;wBAC9C,SAAS,EAAE,IAAI,CAAC,SAAS;wBACzB,WAAW,EAAE,IAAI;wBACjB,cAAc,EAAE,IAAI;qBACD,CAAC,CAAA;gBACxB,CAAC;YACH,CAAC;YACD,MAAM,KAAK,GAAG,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;YACrD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAA;YACjC,MAAM,GAAG,GAA2B,EAAE,CAAA;YACtC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;oBAC5B,GAAG,CAAC,IAAI,CAAC;wBACP,MAAM,EAAE,YAAY;wBACpB,KAAK,EAAE,aAAa;wBACpB,KAAK,EAAE,kBAAkB,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC;wBACtD,MAAM,EAAE,IAAI;qBACb,CAAC,CAAA;gBACJ,CAAC;gBACD,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;oBAC/B,GAAG,CAAC,IAAI,CAAC;wBACP,MAAM,EAAE,YAAY;wBACpB,KAAK,EAAE,gBAAgB;wBACvB,KAAK,EAAE,kBAAkB,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,CAAC,OAAO,CAAC;wBACzD,MAAM,EAAE,IAAI;qBACb,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;YACD,OAAO,GAAG,CAAA;QACZ,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["// `msg-schema` — emits `__msgSchema` + `__effectSchema` per\n// `component()` call and populates the schema-hash inputs slot.\n// Factory module taking the pre-computed MsgSchema for Msg and Effect.\n//\n// Migrated from inline `injectMsgSchema` + `injectEffectSchema` in\n// transform.ts (v2c/decomp-5). The literal builder\n// `msgSchemaToLiteral` (from `msg-schema.ts`) is shared between the\n// two emissions — Msg and Effect use the same discriminated-union\n// shape on the wire.\n//\n// When this module is in the active list alongside `state-schema`\n// and `msg-annotations`, the schema-hash inputs slot is fully\n// populated and `schemaHashModule` produces the authoritative\n// `__schemaHash` emission. At that point the inline\n// `injectSchemaHash` in transform.ts also deletes — see\n// v2c/decomp-5's migration.\n\nimport type { CompilerModule, EmissionContribution } from '@llui/compiler'\nimport { msgSchemaToLiteral, type MsgSchema } from '@llui/compiler'\nimport { SCHEMA_HASH_INPUTS_SLOT, type SchemaHashInputs } from './schema-hash.js'\nimport { findComponentCalls } from '@llui/compiler'\n\nexport interface MsgSchemaModuleOptions {\n /** Pre-computed Msg schema; null when extraction failed. */\n msgSchema: MsgSchema | null\n /** Pre-computed Effect schema; null when not present in source. */\n effectSchema: MsgSchema | null\n}\n\nexport function msgSchemaModule(opts: MsgSchemaModuleOptions): CompilerModule {\n return {\n name: 'msg-schema',\n compilerVersion: '^0.3.0',\n diagnostics: [],\n // Targets captured in `emit` — see `_shared.ts`.\n visitors: {},\n\n emit(ctx, analysis): EmissionContribution[] {\n // Populate schema-hash inputs slot with our msgSchema input — the\n // schemaHashModule will pick it up alongside whatever\n // stateSchemaModule and msgAnnotationsModule wrote.\n if (opts.msgSchema !== null) {\n const hashInputs = analysis.perModule.get(SCHEMA_HASH_INPUTS_SLOT) as\n | SchemaHashInputs\n | undefined\n if (hashInputs) {\n hashInputs.msgSchema = opts.msgSchema\n } else {\n analysis.perModule.set(SCHEMA_HASH_INPUTS_SLOT, {\n msgSchema: opts.msgSchema,\n stateSchema: null,\n msgAnnotations: null,\n } as SchemaHashInputs)\n }\n }\n const calls = findComponentCalls(analysis.sourceFile)\n if (calls.length === 0) return []\n const out: EmissionContribution[] = []\n for (const call of calls) {\n if (opts.msgSchema !== null) {\n out.push({\n module: 'msg-schema',\n field: '__msgSchema',\n value: msgSchemaToLiteral(opts.msgSchema, ctx.factory),\n target: call,\n })\n }\n if (opts.effectSchema !== null) {\n out.push({\n module: 'msg-schema',\n field: '__effectSchema',\n value: msgSchemaToLiteral(opts.effectSchema, ctx.factory),\n target: call,\n })\n }\n }\n return out\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type MessageAnnotations, type CompilerModule } from '@llui/compiler';
|
|
2
|
+
/**
|
|
3
|
+
* Slot shape modules write to populate this module's inputs. Sibling
|
|
4
|
+
* agent modules (msg-schema, state-schema, msg-annotations) write their
|
|
5
|
+
* outputs here under the same conventional slot name.
|
|
6
|
+
*/
|
|
7
|
+
export interface SchemaHashInputs {
|
|
8
|
+
msgSchema: unknown;
|
|
9
|
+
stateSchema: unknown;
|
|
10
|
+
msgAnnotations: Record<string, MessageAnnotations> | null;
|
|
11
|
+
}
|
|
12
|
+
export declare const SCHEMA_HASH_INPUTS_SLOT = "schema-hash:inputs";
|
|
13
|
+
export declare const schemaHashModule: CompilerModule;
|
|
14
|
+
//# sourceMappingURL=schema-hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-hash.d.ts","sourceRoot":"","sources":["../src/schema-hash.ts"],"names":[],"mappings":"AA2BA,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,cAAc,EAGpB,MAAM,gBAAgB,CAAA;AAEvB;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,WAAW,EAAE,OAAO,CAAA;IACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAAA;CAC1D;AAED,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAE3D,eAAO,MAAM,gBAAgB,EAAE,cAyC9B,CAAA"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// `schema-hash` — CompilerModule wrapper around `computeSchemaHash`.
|
|
2
|
+
//
|
|
3
|
+
// Per `MODULE-MAPPING.md`, this is an **agent** concern: the hash signals
|
|
4
|
+
// schema drift across HMR cycles, and only matters when the agent
|
|
5
|
+
// runtime cares about Msg/State schema stability. When the agent module
|
|
6
|
+
// is disabled, this module is excluded from the registry — bundles
|
|
7
|
+
// don't carry `__schemaHash` and the HMR re-send heuristic falls back
|
|
8
|
+
// to "always re-send".
|
|
9
|
+
//
|
|
10
|
+
// The module reads its inputs from sibling-module slots:
|
|
11
|
+
// - `msg-schema`'s slot for the MsgSchema object
|
|
12
|
+
// - `state-schema`'s slot for the StateSchema object
|
|
13
|
+
// - `msg-annotations`'s slot for the annotation map
|
|
14
|
+
//
|
|
15
|
+
// Today none of those sibling modules exist yet — the monolith's
|
|
16
|
+
// `transform.ts` still owns the schema extraction. So this module's
|
|
17
|
+
// `emit` falls back to "no contribution" when the slots are empty.
|
|
18
|
+
// The agent-pipeline decomposition push fills the sibling slots and
|
|
19
|
+
// flips this module from no-op to authoritative.
|
|
20
|
+
//
|
|
21
|
+
// Test fixture: `test/poc-module-schema-hash.test.ts` constructs a
|
|
22
|
+
// fake AnalysisContext with populated slots and asserts the emitted
|
|
23
|
+
// __schemaHash matches `computeSchemaHash`'s direct output. This proves
|
|
24
|
+
// the registry+module path produces a byte-identical hash without
|
|
25
|
+
// requiring the full sibling-module pipeline.
|
|
26
|
+
import ts from 'typescript';
|
|
27
|
+
import { computeSchemaHash, findComponentCalls, } from '@llui/compiler';
|
|
28
|
+
export const SCHEMA_HASH_INPUTS_SLOT = 'schema-hash:inputs';
|
|
29
|
+
export const schemaHashModule = {
|
|
30
|
+
name: 'schema-hash',
|
|
31
|
+
compilerVersion: '^0.3.0',
|
|
32
|
+
dependsOn: [],
|
|
33
|
+
diagnostics: [],
|
|
34
|
+
// No visitor pass — this module is a pure emit consumer. Sibling
|
|
35
|
+
// modules populate the inputs slot during their own visitor pass.
|
|
36
|
+
visitors: {},
|
|
37
|
+
emit(ctx, analysis) {
|
|
38
|
+
const slot = analysis.perModule.get(SCHEMA_HASH_INPUTS_SLOT);
|
|
39
|
+
// The monolith emits `__schemaHash` unconditionally for every
|
|
40
|
+
// compiled `component()` — `computeSchemaHash({null, null, null})`
|
|
41
|
+
// is a well-defined deterministic digest. Match that behavior: an
|
|
42
|
+
// absent slot means "no agent-side producers ran", which produces
|
|
43
|
+
// a hash over null inputs. Producer modules populating the slot
|
|
44
|
+
// change the hash; their absence is itself a stable input.
|
|
45
|
+
const inputs = slot ?? {
|
|
46
|
+
msgSchema: null,
|
|
47
|
+
stateSchema: null,
|
|
48
|
+
msgAnnotations: null,
|
|
49
|
+
};
|
|
50
|
+
const hash = computeSchemaHash({
|
|
51
|
+
msgSchema: inputs.msgSchema,
|
|
52
|
+
stateSchema: inputs.stateSchema,
|
|
53
|
+
msgAnnotations: inputs.msgAnnotations,
|
|
54
|
+
});
|
|
55
|
+
// Emit per-component-call so the bridge can splice the same hash
|
|
56
|
+
// into every `component()` call's config-arg. File-global emission
|
|
57
|
+
// would also work (every component in a file shares the same
|
|
58
|
+
// hash), but per-target matches the per-call splice the monolith
|
|
59
|
+
// performed via injectSchemaHash and avoids changing the bridge's
|
|
60
|
+
// existing field-distribution semantics for this migration.
|
|
61
|
+
return findComponentCalls(ctx.sourceFile).map((call) => ({
|
|
62
|
+
module: 'schema-hash',
|
|
63
|
+
field: '__schemaHash',
|
|
64
|
+
value: ts.factory.createStringLiteral(hash),
|
|
65
|
+
target: call,
|
|
66
|
+
}));
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=schema-hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-hash.js","sourceRoot":"","sources":["../src/schema-hash.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,0EAA0E;AAC1E,kEAAkE;AAClE,wEAAwE;AACxE,mEAAmE;AACnE,sEAAsE;AACtE,uBAAuB;AACvB,EAAE;AACF,yDAAyD;AACzD,mDAAmD;AACnD,uDAAuD;AACvD,sDAAsD;AACtD,EAAE;AACF,iEAAiE;AACjE,oEAAoE;AACpE,mEAAmE;AACnE,oEAAoE;AACpE,iDAAiD;AACjD,EAAE;AACF,mEAAmE;AACnE,oEAAoE;AACpE,wEAAwE;AACxE,kEAAkE;AAClE,8CAA8C;AAE9C,OAAO,EAAE,MAAM,YAAY,CAAA;AAC3B,OAAO,EACL,iBAAiB,EAIjB,kBAAkB,GACnB,MAAM,gBAAgB,CAAA;AAavB,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAAoB,CAAA;AAE3D,MAAM,CAAC,MAAM,gBAAgB,GAAmB;IAC9C,IAAI,EAAE,aAAa;IACnB,eAAe,EAAE,QAAQ;IACzB,SAAS,EAAE,EAAE;IACb,WAAW,EAAE,EAAE;IAEf,iEAAiE;IACjE,kEAAkE;IAClE,QAAQ,EAAE,EAAE;IAEZ,IAAI,CAAC,GAAG,EAAE,QAAQ;QAChB,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,uBAAuB,CAAiC,CAAA;QAC5F,8DAA8D;QAC9D,mEAAmE;QACnE,kEAAkE;QAClE,kEAAkE;QAClE,gEAAgE;QAChE,2DAA2D;QAC3D,MAAM,MAAM,GAAqB,IAAI,IAAI;YACvC,SAAS,EAAE,IAAI;YACf,WAAW,EAAE,IAAI;YACjB,cAAc,EAAE,IAAI;SACrB,CAAA;QACD,MAAM,IAAI,GAAG,iBAAiB,CAAC;YAC7B,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,cAAc,EAAE,MAAM,CAAC,cAAc;SACtC,CAAC,CAAA;QACF,iEAAiE;QACjE,mEAAmE;QACnE,6DAA6D;QAC7D,iEAAiE;QACjE,kEAAkE;QAClE,4DAA4D;QAC5D,OAAO,kBAAkB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACvD,MAAM,EAAE,aAAa;YACrB,KAAK,EAAE,cAAc;YACrB,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC;YAC3C,MAAM,EAAE,IAAI;SACb,CAAC,CAAC,CAAA;IACL,CAAC;CACF,CAAA","sourcesContent":["// `schema-hash` — CompilerModule wrapper around `computeSchemaHash`.\n//\n// Per `MODULE-MAPPING.md`, this is an **agent** concern: the hash signals\n// schema drift across HMR cycles, and only matters when the agent\n// runtime cares about Msg/State schema stability. When the agent module\n// is disabled, this module is excluded from the registry — bundles\n// don't carry `__schemaHash` and the HMR re-send heuristic falls back\n// to \"always re-send\".\n//\n// The module reads its inputs from sibling-module slots:\n// - `msg-schema`'s slot for the MsgSchema object\n// - `state-schema`'s slot for the StateSchema object\n// - `msg-annotations`'s slot for the annotation map\n//\n// Today none of those sibling modules exist yet — the monolith's\n// `transform.ts` still owns the schema extraction. So this module's\n// `emit` falls back to \"no contribution\" when the slots are empty.\n// The agent-pipeline decomposition push fills the sibling slots and\n// flips this module from no-op to authoritative.\n//\n// Test fixture: `test/poc-module-schema-hash.test.ts` constructs a\n// fake AnalysisContext with populated slots and asserts the emitted\n// __schemaHash matches `computeSchemaHash`'s direct output. This proves\n// the registry+module path produces a byte-identical hash without\n// requiring the full sibling-module pipeline.\n\nimport ts from 'typescript'\nimport {\n computeSchemaHash,\n type MessageAnnotations,\n type CompilerModule,\n type EmissionContribution,\n findComponentCalls,\n} from '@llui/compiler'\n\n/**\n * Slot shape modules write to populate this module's inputs. Sibling\n * agent modules (msg-schema, state-schema, msg-annotations) write their\n * outputs here under the same conventional slot name.\n */\nexport interface SchemaHashInputs {\n msgSchema: unknown\n stateSchema: unknown\n msgAnnotations: Record<string, MessageAnnotations> | null\n}\n\nexport const SCHEMA_HASH_INPUTS_SLOT = 'schema-hash:inputs'\n\nexport const schemaHashModule: CompilerModule = {\n name: 'schema-hash',\n compilerVersion: '^0.3.0',\n dependsOn: [],\n diagnostics: [],\n\n // No visitor pass — this module is a pure emit consumer. Sibling\n // modules populate the inputs slot during their own visitor pass.\n visitors: {},\n\n emit(ctx, analysis): EmissionContribution[] {\n const slot = analysis.perModule.get(SCHEMA_HASH_INPUTS_SLOT) as SchemaHashInputs | undefined\n // The monolith emits `__schemaHash` unconditionally for every\n // compiled `component()` — `computeSchemaHash({null, null, null})`\n // is a well-defined deterministic digest. Match that behavior: an\n // absent slot means \"no agent-side producers ran\", which produces\n // a hash over null inputs. Producer modules populating the slot\n // change the hash; their absence is itself a stable input.\n const inputs: SchemaHashInputs = slot ?? {\n msgSchema: null,\n stateSchema: null,\n msgAnnotations: null,\n }\n const hash = computeSchemaHash({\n msgSchema: inputs.msgSchema,\n stateSchema: inputs.stateSchema,\n msgAnnotations: inputs.msgAnnotations,\n })\n // Emit per-component-call so the bridge can splice the same hash\n // into every `component()` call's config-arg. File-global emission\n // would also work (every component in a file shares the same\n // hash), but per-target matches the per-call splice the monolith\n // performed via injectSchemaHash and avoids changing the bridge's\n // existing field-distribution semantics for this migration.\n return findComponentCalls(ctx.sourceFile).map((call) => ({\n module: 'schema-hash',\n field: '__schemaHash',\n value: ts.factory.createStringLiteral(hash),\n target: call,\n }))\n },\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CompilerModule } from '@llui/compiler';
|
|
2
|
+
import { type StateSchema } from '@llui/compiler';
|
|
3
|
+
export interface StateSchemaModuleOptions {
|
|
4
|
+
/** Pre-computed schema (cross-file aware) — null when extraction failed. */
|
|
5
|
+
stateSchema: StateSchema | null;
|
|
6
|
+
}
|
|
7
|
+
export declare function stateSchemaModule(opts: StateSchemaModuleOptions): CompilerModule;
|
|
8
|
+
//# sourceMappingURL=state-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-schema.d.ts","sourceRoot":"","sources":["../src/state-schema.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAwB,MAAM,gBAAgB,CAAA;AAC1E,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAIrE,MAAM,WAAW,wBAAwB;IACvC,4EAA4E;IAC5E,WAAW,EAAE,WAAW,GAAG,IAAI,CAAA;CAChC;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,GAAG,cAAc,CA0ChF"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// `state-schema` — emits `__stateSchema` per `component()` call and
|
|
2
|
+
// populates the schema-hash inputs slot. Factory module: takes the
|
|
3
|
+
// pre-computed StateSchema (from `extractStateSchema` upstream, with
|
|
4
|
+
// cross-file resolution if available) and emits its literal form
|
|
5
|
+
// targeted at each component() call site.
|
|
6
|
+
//
|
|
7
|
+
// Migrated from the inline `injectStateSchema` in transform.ts. The
|
|
8
|
+
// monolith's `extractStateSchema(...)` call lives in transformLlui's
|
|
9
|
+
// setup section and gets passed into the factory; the module consumes
|
|
10
|
+
// it without re-running the extractor. This keeps the extraction
|
|
11
|
+
// authoritative at the umbrella level (where pre-extracted cross-file
|
|
12
|
+
// data + type-source overrides are already wired) and frees the module
|
|
13
|
+
// from re-implementing input resolution.
|
|
14
|
+
import { stateTypeToLiteral } from '@llui/compiler';
|
|
15
|
+
import { SCHEMA_HASH_INPUTS_SLOT } from './schema-hash.js';
|
|
16
|
+
import { findComponentCalls } from '@llui/compiler';
|
|
17
|
+
export function stateSchemaModule(opts) {
|
|
18
|
+
return {
|
|
19
|
+
name: 'state-schema',
|
|
20
|
+
compilerVersion: '^0.3.0',
|
|
21
|
+
diagnostics: [],
|
|
22
|
+
// Targets captured in `emit` — see `_shared.ts`.
|
|
23
|
+
visitors: {},
|
|
24
|
+
emit(ctx, analysis) {
|
|
25
|
+
if (!opts.stateSchema)
|
|
26
|
+
return [];
|
|
27
|
+
const calls = findComponentCalls(analysis.sourceFile);
|
|
28
|
+
if (calls.length === 0)
|
|
29
|
+
return [];
|
|
30
|
+
// Populate the schema-hash inputs slot. The schema-hash module
|
|
31
|
+
// reads this on its own emit pass and computes the hash. Module
|
|
32
|
+
// ordering is observable per v2c §2.1: when `stateSchemaModule`
|
|
33
|
+
// appears in the active module list before `schemaHashModule`,
|
|
34
|
+
// the inputs slot is populated when schema-hash's emit runs.
|
|
35
|
+
const hashInputs = analysis.perModule.get(SCHEMA_HASH_INPUTS_SLOT);
|
|
36
|
+
if (hashInputs) {
|
|
37
|
+
hashInputs.stateSchema = opts.stateSchema;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
analysis.perModule.set(SCHEMA_HASH_INPUTS_SLOT, {
|
|
41
|
+
msgSchema: null,
|
|
42
|
+
stateSchema: opts.stateSchema,
|
|
43
|
+
msgAnnotations: null,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return calls.map((call) => ({
|
|
47
|
+
module: 'state-schema',
|
|
48
|
+
field: '__stateSchema',
|
|
49
|
+
value: stateTypeToLiteral({ kind: 'object', fields: opts.stateSchema.fields }, ctx.factory),
|
|
50
|
+
target: call,
|
|
51
|
+
}));
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=state-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-schema.js","sourceRoot":"","sources":["../src/state-schema.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,mEAAmE;AACnE,qEAAqE;AACrE,iEAAiE;AACjE,0CAA0C;AAC1C,EAAE;AACF,oEAAoE;AACpE,qEAAqE;AACrE,sEAAsE;AACtE,iEAAiE;AACjE,sEAAsE;AACtE,uEAAuE;AACvE,yCAAyC;AAGzC,OAAO,EAAE,kBAAkB,EAAoB,MAAM,gBAAgB,CAAA;AACrE,OAAO,EAAE,uBAAuB,EAAyB,MAAM,kBAAkB,CAAA;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAOnD,MAAM,UAAU,iBAAiB,CAAC,IAA8B;IAC9D,OAAO;QACL,IAAI,EAAE,cAAc;QACpB,eAAe,EAAE,QAAQ;QACzB,WAAW,EAAE,EAAE;QACf,iDAAiD;QACjD,QAAQ,EAAE,EAAE;QAEZ,IAAI,CAAC,GAAG,EAAE,QAAQ;YAChB,IAAI,CAAC,IAAI,CAAC,WAAW;gBAAE,OAAO,EAAE,CAAA;YAChC,MAAM,KAAK,GAAG,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;YACrD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAA;YAEjC,+DAA+D;YAC/D,gEAAgE;YAChE,gEAAgE;YAChE,+DAA+D;YAC/D,6DAA6D;YAC7D,MAAM,UAAU,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,uBAAuB,CAEpD,CAAA;YACb,IAAI,UAAU,EAAE,CAAC;gBACf,UAAU,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAA;YAC3C,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,uBAAuB,EAAE;oBAC9C,SAAS,EAAE,IAAI;oBACf,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,cAAc,EAAE,IAAI;iBACD,CAAC,CAAA;YACxB,CAAC;YAED,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC1B,MAAM,EAAE,cAAc;gBACtB,KAAK,EAAE,eAAe;gBACtB,KAAK,EAAE,kBAAkB,CACvB,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,WAAY,CAAC,MAAM,EAAE,EACpD,GAAG,CAAC,OAAO,CACZ;gBACD,MAAM,EAAE,IAAI;aACb,CAAC,CAAC,CAAA;QACL,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["// `state-schema` — emits `__stateSchema` per `component()` call and\n// populates the schema-hash inputs slot. Factory module: takes the\n// pre-computed StateSchema (from `extractStateSchema` upstream, with\n// cross-file resolution if available) and emits its literal form\n// targeted at each component() call site.\n//\n// Migrated from the inline `injectStateSchema` in transform.ts. The\n// monolith's `extractStateSchema(...)` call lives in transformLlui's\n// setup section and gets passed into the factory; the module consumes\n// it without re-running the extractor. This keeps the extraction\n// authoritative at the umbrella level (where pre-extracted cross-file\n// data + type-source overrides are already wired) and frees the module\n// from re-implementing input resolution.\n\nimport type { CompilerModule, EmissionContribution } from '@llui/compiler'\nimport { stateTypeToLiteral, type StateSchema } from '@llui/compiler'\nimport { SCHEMA_HASH_INPUTS_SLOT, type SchemaHashInputs } from './schema-hash.js'\nimport { findComponentCalls } from '@llui/compiler'\n\nexport interface StateSchemaModuleOptions {\n /** Pre-computed schema (cross-file aware) — null when extraction failed. */\n stateSchema: StateSchema | null\n}\n\nexport function stateSchemaModule(opts: StateSchemaModuleOptions): CompilerModule {\n return {\n name: 'state-schema',\n compilerVersion: '^0.3.0',\n diagnostics: [],\n // Targets captured in `emit` — see `_shared.ts`.\n visitors: {},\n\n emit(ctx, analysis): EmissionContribution[] {\n if (!opts.stateSchema) return []\n const calls = findComponentCalls(analysis.sourceFile)\n if (calls.length === 0) return []\n\n // Populate the schema-hash inputs slot. The schema-hash module\n // reads this on its own emit pass and computes the hash. Module\n // ordering is observable per v2c §2.1: when `stateSchemaModule`\n // appears in the active module list before `schemaHashModule`,\n // the inputs slot is populated when schema-hash's emit runs.\n const hashInputs = analysis.perModule.get(SCHEMA_HASH_INPUTS_SLOT) as\n | SchemaHashInputs\n | undefined\n if (hashInputs) {\n hashInputs.stateSchema = opts.stateSchema\n } else {\n analysis.perModule.set(SCHEMA_HASH_INPUTS_SLOT, {\n msgSchema: null,\n stateSchema: opts.stateSchema,\n msgAnnotations: null,\n } as SchemaHashInputs)\n }\n\n return calls.map((call) => ({\n module: 'state-schema',\n field: '__stateSchema',\n value: stateTypeToLiteral(\n { kind: 'object', fields: opts.stateSchema!.fields },\n ctx.factory,\n ),\n target: call,\n }))\n },\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@llui/compiler-introspection",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"typescript": "^6.0.2",
|
|
19
|
+
"@llui/compiler": "0.3.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^22.0.0"
|
|
23
|
+
},
|
|
24
|
+
"description": "LLui compiler — runtime introspection metadata (opt-in). Schema/annotation/hash emission consumed by @llui/agent-bridge (end-user agent) and @llui/mcp (dev MCP).",
|
|
25
|
+
"keywords": [
|
|
26
|
+
"llui",
|
|
27
|
+
"compiler",
|
|
28
|
+
"introspection",
|
|
29
|
+
"schema"
|
|
30
|
+
],
|
|
31
|
+
"author": "Franco Ponticelli <franco.ponticelli@gmail.com>",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/fponticelli/llui.git",
|
|
36
|
+
"directory": "packages/compiler-introspection"
|
|
37
|
+
},
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/fponticelli/llui/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/fponticelli/llui/tree/main/packages/compiler-introspection#readme",
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc -p tsconfig.build.json",
|
|
44
|
+
"check": "tsc --noEmit",
|
|
45
|
+
"lint": "eslint src",
|
|
46
|
+
"test": "vitest run"
|
|
47
|
+
}
|
|
48
|
+
}
|