@pipelex/mthds-ui 0.22.0 → 0.24.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.
@@ -2,7 +2,7 @@ import { C as ConceptInfo, P as PipeBlueprintUnion, a as PresenceMarker, b as Gr
2
2
  export { P as ParsedPipeRef, m as makePipeRef, p as parsePipeRef } from '../pipeRefs-BQJO5nh5.js';
3
3
 
4
4
  type DiagnosticSeverity = "error" | "warning";
5
- type DiagnosticCode = "toml-parse-error" | "missing-domain" | "invalid-bundle-shape" | "invalid-concept-entry" | "invalid-pipe-entry" | "unknown-pipe-type" | "retired-signature-tag" | "invalid-concept-ref" | "missing-pipe-output" | "invalid-sub-pipe" | "incomplete-batch-spec" | "duplicate-concept" | "duplicate-pipe" | "signature-type-mismatch" | "unresolved-pipe-ref" | "cyclic-pipe-ref" | "missing-main-pipe" | "no-entry-pipe" | "conflicting-input-concept";
5
+ type DiagnosticCode = "toml-parse-error" | "missing-domain" | "invalid-bundle-shape" | "invalid-concept-entry" | "invalid-pipe-entry" | "unknown-pipe-type" | "retired-signature-tag" | "invalid-concept-ref" | "unknown-input-slot-key" | "missing-pipe-output" | "invalid-sub-pipe" | "incomplete-batch-spec" | "duplicate-concept" | "duplicate-pipe" | "signature-type-mismatch" | "unresolved-pipe-ref" | "cyclic-pipe-ref" | "missing-main-pipe" | "no-entry-pipe" | "conflicting-input-concept";
6
6
  /**
7
7
  * A non-fatal note collected while parsing or merging. `error` means a whole
8
8
  * unit could not be interpreted (unparseable TOML, a skipped pipe); `warning`
@@ -85,6 +85,36 @@ interface ConceptRefParts {
85
85
  }
86
86
  /** Parse a concept ref string into its parts. Returns null when the ref is not interpretable. */
87
87
  declare function parseConceptRef(raw: unknown): ConceptRefParts | null;
88
+ interface InputSlotParts {
89
+ /** The slot's concept ref parts, or null when the slot is not interpretable. */
90
+ ref: ConceptRefParts | null;
91
+ /**
92
+ * True when an expanded slot table declares no `concept` at all. The key is
93
+ * required, so there is no ref for a diagnostic to be about — the caller says
94
+ * the key is missing rather than blaming a ref the author never wrote.
95
+ */
96
+ missingConcept: boolean;
97
+ /** Keys of an expanded slot table this version of the standard does not define. */
98
+ unknownKeys: string[];
99
+ /**
100
+ * True when the table is not a slot table at all but the wreckage of an
101
+ * unquoted dotted input name — see {@link looksLikeUnquotedDottedName}.
102
+ */
103
+ dottedName: boolean;
104
+ }
105
+ /**
106
+ * Read one `inputs` value, whichever form authored it.
107
+ *
108
+ * `hints` is parsed as a known key and then dropped: intent hints are
109
+ * presentational, and the standard routes them to renderers through the
110
+ * input-form descriptor, not the GraphSpec (see `docs/static-graph.md`).
111
+ * Their shape is not checked here for the same reason — a malformed `hints`
112
+ * table is content this module never reads, so it is the validator's to
113
+ * report, not the renderer's. An unknown key is different: it may well be
114
+ * where a future standard puts something that changes the slot, so it is
115
+ * named rather than passed over.
116
+ */
117
+ declare function parseInputSlot(raw: unknown): InputSlotParts;
88
118
 
89
119
  /**
90
120
  * Parse one `.mthds` TOML string. Returns whatever could be interpreted plus
@@ -134,4 +164,4 @@ declare function buildStaticGraphSpecFromToml(tomlTexts: string | string[], opti
134
164
  */
135
165
  declare function staticDiagnosticsToValidationIssues(diagnostics: Diagnostic[]): ValidationIssue[];
136
166
 
137
- export { type ConceptRefParts, type Diagnostic, type DiagnosticCode, type DiagnosticSeverity, type DomainNamespace, type MergedMethodSet, NATIVE_CONCEPT_CODES, NATIVE_DOMAIN, type ParseMthdsBundleResult, type ParsedBundle, type StaticGraphOptions, type StaticGraphResult, UNKNOWN_DOMAIN, buildStaticGraphSpec, buildStaticGraphSpecFromToml, mergeBundles, parseConceptRef, parseMthdsBundle, staticDiagnosticsToValidationIssues };
167
+ export { type ConceptRefParts, type Diagnostic, type DiagnosticCode, type DiagnosticSeverity, type DomainNamespace, type InputSlotParts, type MergedMethodSet, NATIVE_CONCEPT_CODES, NATIVE_DOMAIN, type ParseMthdsBundleResult, type ParsedBundle, type StaticGraphOptions, type StaticGraphResult, UNKNOWN_DOMAIN, buildStaticGraphSpec, buildStaticGraphSpecFromToml, mergeBundles, parseConceptRef, parseInputSlot, parseMthdsBundle, staticDiagnosticsToValidationIssues };
@@ -17,6 +17,9 @@ function isPlainObject(value) {
17
17
  const proto = Object.getPrototypeOf(value);
18
18
  return proto === Object.prototype || proto === null;
19
19
  }
20
+ function authoredRecord() {
21
+ return /* @__PURE__ */ Object.create(null);
22
+ }
20
23
  function strOrNull(value) {
21
24
  return typeof value === "string" && value.length > 0 ? value : null;
22
25
  }
@@ -70,6 +73,32 @@ function parseConceptRef(raw) {
70
73
  presence: presenceFromSymbol(presence)
71
74
  };
72
75
  }
76
+ var INPUT_SLOT_KEYS = /* @__PURE__ */ new Set(["concept", "hints"]);
77
+ var CONCEPT_CODE_RE = /^[A-Z][A-Za-z0-9]*$/;
78
+ function looksLikeUnquotedDottedName(raw, unknownKeys) {
79
+ if (raw.concept !== void 0 || unknownKeys.length === 0) return false;
80
+ return unknownKeys.every((key) => {
81
+ const parts = parseConceptRef(raw[key]);
82
+ return parts !== null && CONCEPT_CODE_RE.test(parts.code);
83
+ });
84
+ }
85
+ function parseInputSlot(raw) {
86
+ if (!isPlainObject(raw)) {
87
+ return {
88
+ ref: parseConceptRef(raw),
89
+ missingConcept: false,
90
+ unknownKeys: [],
91
+ dottedName: false
92
+ };
93
+ }
94
+ const unknownKeys = Object.keys(raw).filter((key) => !INPUT_SLOT_KEYS.has(key));
95
+ return {
96
+ ref: parseConceptRef(raw.concept),
97
+ missingConcept: raw.concept === void 0,
98
+ unknownKeys,
99
+ dottedName: looksLikeUnquotedDottedName(raw, unknownKeys)
100
+ };
101
+ }
73
102
  function qualifiedStructureClassName(domain, code) {
74
103
  return `${domain.replaceAll(".", "\xB7")}__${code}`;
75
104
  }
@@ -98,22 +127,32 @@ function resolveConceptInfo(parts, currentDomain, localConcepts) {
98
127
  if (parts.domain !== null && parts.domain !== currentDomain) {
99
128
  return stubConceptInfo(parts.code, parts.domain);
100
129
  }
101
- const local = localConcepts[parts.code];
102
- if (local) return local;
130
+ if (Object.hasOwn(localConcepts, parts.code)) return localConcepts[parts.code];
103
131
  if (parts.domain === null && isNativeConceptCode(parts.code)) {
104
132
  return nativeConceptInfo(parts.code);
105
133
  }
106
134
  return stubConceptInfo(parts.code, currentDomain);
107
135
  }
108
- function resolveStuffSpec(raw, currentDomain, localConcepts) {
109
- const parts = parseConceptRef(raw);
110
- if (parts === null) return null;
136
+ function stuffSpecFromParts(parts, currentDomain, localConcepts) {
111
137
  return {
112
138
  concept: resolveConceptInfo(parts, currentDomain, localConcepts),
113
139
  multiplicity: parts.multiplicity,
114
140
  presence: parts.presence
115
141
  };
116
142
  }
143
+ function resolveStuffSpec(raw, currentDomain, localConcepts) {
144
+ const parts = parseConceptRef(raw);
145
+ return parts === null ? null : stuffSpecFromParts(parts, currentDomain, localConcepts);
146
+ }
147
+ function resolveInputSlot(raw, currentDomain, localConcepts) {
148
+ const { ref, missingConcept, unknownKeys, dottedName } = parseInputSlot(raw);
149
+ return {
150
+ spec: ref === null ? null : stuffSpecFromParts(ref, currentDomain, localConcepts),
151
+ missingConcept,
152
+ unknownKeys,
153
+ dottedName
154
+ };
155
+ }
117
156
 
118
157
  // src/static-graph/parseMthdsBundle.ts
119
158
  import { parse as parseToml } from "smol-toml";
@@ -135,7 +174,7 @@ function stringArrayOrNull(value) {
135
174
  return value.filter((item) => typeof item === "string");
136
175
  }
137
176
  function normalizeInputs(raw, pipeCode, ctx) {
138
- const inputs = {};
177
+ const inputs = authoredRecord();
139
178
  if (raw === void 0 || raw === null) return inputs;
140
179
  if (!isPlainObject(raw)) {
141
180
  ctx.diagnostics.push({
@@ -146,13 +185,34 @@ function normalizeInputs(raw, pipeCode, ctx) {
146
185
  });
147
186
  return inputs;
148
187
  }
149
- for (const [name, ref] of Object.entries(raw)) {
150
- const spec = resolveStuffSpec(ref, ctx.domain, ctx.concepts);
188
+ for (const [name, slot] of Object.entries(raw)) {
189
+ const { spec, missingConcept, unknownKeys, dottedName } = resolveInputSlot(
190
+ slot,
191
+ ctx.domain,
192
+ ctx.concepts
193
+ );
194
+ if (dottedName) {
195
+ ctx.diagnostics.push({
196
+ severity: "warning",
197
+ code: "invalid-concept-ref",
198
+ message: `pipe "${pipeCode}": input "${name}" reads as a slot table but looks like an unquoted dotted input name \u2014 write it as one quoted key ("${name}.${unknownKeys.join(".")}" = \u2026) \u2014 skipped`,
199
+ path: `pipe.${pipeCode}.inputs.${name}`
200
+ });
201
+ continue;
202
+ }
203
+ if (unknownKeys.length > 0) {
204
+ ctx.diagnostics.push({
205
+ severity: "warning",
206
+ code: "unknown-input-slot-key",
207
+ message: `pipe "${pipeCode}": input "${name}" declares ${unknownKeys.map((key) => `"${key}"`).join(", ")}, which the input slot form does not define \u2014 ignored here, and the runtime refuses the bundle`,
208
+ path: `pipe.${pipeCode}.inputs.${name}`
209
+ });
210
+ }
151
211
  if (spec === null) {
152
212
  ctx.diagnostics.push({
153
213
  severity: "warning",
154
214
  code: "invalid-concept-ref",
155
- message: `pipe "${pipeCode}": input "${name}" has an uninterpretable concept ref \u2014 skipped`,
215
+ message: missingConcept ? `pipe "${pipeCode}": input "${name}" is a slot table with no "concept" \u2014 the key is required \u2014 skipped` : `pipe "${pipeCode}": input "${name}" has an uninterpretable concept ref \u2014 skipped`,
156
216
  path: `pipe.${pipeCode}.inputs.${name}`
157
217
  });
158
218
  continue;
@@ -514,7 +574,7 @@ function fieldSchema(spec) {
514
574
  }
515
575
  function deriveJsonSchema(structure, title, description) {
516
576
  if (!isPlainObject(structure)) return void 0;
517
- const properties = {};
577
+ const properties = authoredRecord();
518
578
  const required = [];
519
579
  for (const [name, spec] of Object.entries(structure)) {
520
580
  if (typeof spec === "string") {
@@ -552,7 +612,7 @@ function qualifyRefines(raw, domain, declaredCodes, conceptCode, diagnostics) {
552
612
  }
553
613
  function parseConcepts(raw, domain, diagnostics) {
554
614
  var _a, _b;
555
- const concepts = {};
615
+ const concepts = authoredRecord();
556
616
  if (raw === void 0) return concepts;
557
617
  if (!isPlainObject(raw)) {
558
618
  diagnostics.push({
@@ -603,8 +663,8 @@ function emptyBundle() {
603
663
  description: null,
604
664
  main_pipe: null,
605
665
  system_prompt: null,
606
- concepts: {},
607
- pipes: {}
666
+ concepts: authoredRecord(),
667
+ pipes: authoredRecord()
608
668
  };
609
669
  }
610
670
  function parseMthdsBundle(tomlText) {
@@ -675,7 +735,7 @@ function enrichPipeConcepts(pipe, domains) {
675
735
  const declared = (_a = domains[spec.concept.domain_code]) == null ? void 0 : _a.concepts[spec.concept.code];
676
736
  return declared !== void 0 && declared !== spec.concept ? __spreadProps(__spreadValues({}, spec), { concept: declared }) : spec;
677
737
  };
678
- const inputs = {};
738
+ const inputs = authoredRecord();
679
739
  for (const [name, spec] of Object.entries(pipe.inputs)) {
680
740
  inputs[name] = enrichSpec(spec);
681
741
  }
@@ -684,13 +744,17 @@ function enrichPipeConcepts(pipe, domains) {
684
744
  function mergeBundles(bundles) {
685
745
  var _a, _b, _c;
686
746
  const diagnostics = [];
687
- const domains = {};
747
+ const domains = authoredRecord();
688
748
  let mainDomain = null;
689
749
  let mainPipe = null;
690
750
  let description = null;
691
751
  for (const bundle of bundles) {
692
752
  const domain = (_a = bundle.domain) != null ? _a : UNKNOWN_DOMAIN;
693
- const namespace = (_b = domains[domain]) != null ? _b : domains[domain] = { domain, concepts: {}, pipes: {} };
753
+ const namespace = (_b = domains[domain]) != null ? _b : domains[domain] = {
754
+ domain,
755
+ concepts: authoredRecord(),
756
+ pipes: authoredRecord()
757
+ };
694
758
  if (mainDomain === null && bundle.domain !== null) mainDomain = bundle.domain;
695
759
  if (mainPipe === null && bundle.main_pipe !== null) {
696
760
  mainPipe = bundle.main_pipe;
@@ -1370,6 +1434,7 @@ export {
1370
1434
  makePipeRef,
1371
1435
  mergeBundles,
1372
1436
  parseConceptRef,
1437
+ parseInputSlot,
1373
1438
  parseMthdsBundle,
1374
1439
  parsePipeRef,
1375
1440
  staticDiagnosticsToValidationIssues