@telorun/sdk 0.70.0 → 0.73.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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/resource-context.d.ts +6 -0
- package/dist/resource-context.d.ts.map +1 -1
- package/dist/runtime-seam.d.ts +20 -0
- package/dist/runtime-seam.d.ts.map +1 -1
- package/dist/type-schema-ref.d.ts +11 -0
- package/dist/type-schema-ref.d.ts.map +1 -1
- package/dist/type-schema-ref.js +15 -0
- package/dist/value-type.d.ts +149 -0
- package/dist/value-type.d.ts.map +1 -0
- package/dist/value-type.js +286 -0
- package/dist/value-types/entries/index.d.ts +3 -0
- package/dist/value-types/entries/index.d.ts.map +1 -0
- package/dist/value-types/entries/index.js +13 -0
- package/dist/value-types/entries/telo-bytes.json +7 -0
- package/dist/value-types/entries/telo-stream.json +14 -0
- package/dist/value-types/entries/telo-tcp-port.json +7 -0
- package/dist/value-types/entries/telo-udp-port.json +6 -0
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/resource-context.ts +6 -0
- package/src/runtime-seam.ts +21 -0
- package/src/type-schema-ref.ts +16 -0
- package/src/value-type.ts +380 -0
- package/src/value-types/entries/index.ts +14 -0
- package/src/value-types/entries/telo-bytes.json +7 -0
- package/src/value-types/entries/telo-stream.json +14 -0
- package/src/value-types/entries/telo-tcp-port.json +7 -0
- package/src/value-types/entries/telo-udp-port.json +6 -0
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,12 @@ export interface LoadOptions {
|
|
|
20
20
|
* inline imports resolve and execute identically to authored `Telo.Import`
|
|
21
21
|
* documents. Mirrors the analyzer loader's option of the same name. */
|
|
22
22
|
desugarImports?: boolean;
|
|
23
|
+
/** When true, legacy manifest spellings are rewritten to the current ones
|
|
24
|
+
* before the manifests are returned, so a module published years ago loads
|
|
25
|
+
* against today's vocabulary. On for every runtime load; off only for a
|
|
26
|
+
* round-trip view that must show the author's own text. Mirrors the analyzer
|
|
27
|
+
* loader's option of the same name. */
|
|
28
|
+
migrate?: boolean;
|
|
23
29
|
}
|
|
24
30
|
export interface DataValidator {
|
|
25
31
|
validate(data: any): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-context.d.ts","sourceRoot":"","sources":["../src/resource-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,aAAa,EACb,QAAQ,EACR,eAAe,EACf,SAAS,EACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B;gFAC4E;IAC5E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;4EAGwE;IACxE,cAAc,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"resource-context.d.ts","sourceRoot":"","sources":["../src/resource-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,aAAa,EACb,QAAQ,EACR,eAAe,EACf,SAAS,EACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B;gFAC4E;IAC5E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;4EAGwE;IACxE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;4CAIwC;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,aAAc,YAAW,aAAa;IACjD,OAAO;IAIP,QAAQ;CAGT;AAED,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAC,GAAG;IAAE,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEhG;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IAClC,2DAA2D;IAC3D,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,eAAgB,SAAQ,iBAAiB;IACxD,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B;;;;;4EAKwE;IACxE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,IAAI,CAAC;IACzC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD;;iCAE6B;IAC7B,wBAAwB,IAAI,kBAAkB,CAAC;IAC/C;+EAC2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CAAC,CAAC,EACR,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,EACxD,IAAI,CAAC,EAAE,aAAa,GACnB,OAAO,CAAC,CAAC,CAAC,CAAC;IACd;kFAC8E;IAC9E,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC3D,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,aAAa,GAAG,SAAS,GAAG,SAAS,CAAC;IACpE;;;mFAG+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,CAAC,EAAE,aAAa,GAAG,SAAS,SAAS,EAAE,CAAC;IAChF;;;;sCAIkC;IAClC,WAAW,CAAC,IAAI,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,kBAAkB,CAAA;KAAE,GAAG,aAAa,CAAC;IACzE;;;;;2EAKuE;IACvE,WAAW,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAC9C;;;;;kBAKc;IACd,QAAQ,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpF;;;qCAGiC;IACjC,MAAM,CAAC,OAAO,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO,EACf,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,GAAG,CAAC,CAAC;IAChB,cAAc,CAAC,OAAO,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,EAAE,OAAO,EACf,GAAG,CAAC,EAAE,aAAa,GAClB,OAAO,CAAC,GAAG,CAAC,CAAC;IAChB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAAC;IACvE,gBAAgB,CAAC,QAAQ,EAAE,GAAG,GAAG,IAAI,CAAC;IACtC,iBAAiB,IAAI,iBAAiB,CAAC;IACvC,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,iBAAiB,CAAC;IAChE,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IACnD;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1D;kFAC8E;IAC9E,eAAe,CAAC,QAAQ,EAAE,GAAG,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtF;;;;;;OAMG;IACH,UAAU,CAAC,CAAC,EACV,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,SAAS,IAAI,CAAC,EAC7C,QAAQ,EAAE,MAAM,MAAM,EACtB,OAAO,CAAC,EAAE,MAAM,GACf,CAAC,CAAC;IACL,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC;IAC9C;;;;;yEAKqE;IACrE,qBAAqB,CAAC,MAAM,EAAE,GAAG,GAAG,aAAa,CAAC;IAClD,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACnD,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC/C,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IACzD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,SAAS,CAAC;IACtD,kFAAkF;IAClF,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,GAAG,aAAa,CAAC;IACtF,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,kBAAkB,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjG,kBAAkB,CAAC,UAAU,EAAE,GAAG,GAAG,IAAI,CAAC;IAC1C;yDACqD;IACrD,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IAC3F;;;;OAIG;IACH,mBAAmB,IAAI,gBAAgB,GAAG,SAAS,CAAC;IACpD;;;;;;;;;;;OAWG;IACH,WAAW,IAAI,MAAM,GAAG,SAAS,CAAC;IAClC;;;;iEAI6D;IAC7D,cAAc,IAAI,MAAM,GAAG,SAAS,CAAC;IACrC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD;wDACoD;IACpD,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC5E;;;sBAGkB;IAClB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACxD;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,cAAc,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;CACxC"}
|
package/dist/runtime-seam.d.ts
CHANGED
|
@@ -88,6 +88,17 @@ export interface RuntimeCheckOptions {
|
|
|
88
88
|
* detail of one editor transport, and this contract is read by a Rust or Go
|
|
89
89
|
* kernel too. */
|
|
90
90
|
export type CheckDiagnosticSeverity = "error" | "warning" | "info" | "hint";
|
|
91
|
+
/** A mechanically applicable repair for a finding: `replacement` is the whole
|
|
92
|
+
* corrected value at `path` — never a fragment — so a consumer applies it
|
|
93
|
+
* without parsing the language inside.
|
|
94
|
+
*
|
|
95
|
+
* That is why `path` travels with it. `source` / `line` / `column` locate a
|
|
96
|
+
* finding for a human reading text; a module applying a repair works on the
|
|
97
|
+
* parsed manifest, where a line number is not an address. A repair without its
|
|
98
|
+
* anchor is one nothing can apply. */
|
|
99
|
+
export interface CheckDiagnosticFix {
|
|
100
|
+
replacement: string;
|
|
101
|
+
}
|
|
91
102
|
/** One analyzer finding, flattened to data. Positions are zero-based, matching
|
|
92
103
|
* the analyzer's own range model. */
|
|
93
104
|
export interface CheckDiagnostic {
|
|
@@ -98,6 +109,15 @@ export interface CheckDiagnostic {
|
|
|
98
109
|
source?: string;
|
|
99
110
|
line?: number;
|
|
100
111
|
column?: number;
|
|
112
|
+
/** `<kind>/<name>` of the resource the finding is pinned to. */
|
|
113
|
+
resource?: string;
|
|
114
|
+
/** Dotted path of the offending value within that resource
|
|
115
|
+
* (`steps[0].inputs.flag`) — the address `fix` applies at. */
|
|
116
|
+
path?: string;
|
|
117
|
+
/** Present only when the repair is decidable — a fix that might not be
|
|
118
|
+
* correct is worse than none, since the point of the field is that it can be
|
|
119
|
+
* applied without review. */
|
|
120
|
+
fix?: CheckDiagnosticFix;
|
|
101
121
|
}
|
|
102
122
|
export interface RuntimeCheckResult {
|
|
103
123
|
diagnostics: CheckDiagnostic[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-seam.d.ts","sourceRoot":"","sources":["../src/runtime-seam.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;;;;;;;;OAYG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACtE;;;;;OAKG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACnF;AAED,MAAM,WAAW,iBAAiB;IAChC;;qBAEiB;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1C;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC;sCACkC;IAClC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC;;;;;;;;;;OAUG;IACH,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,mBAAmB;IAClC;;oCAEgC;IAChC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;kBAEkB;AAClB,MAAM,MAAM,uBAAuB,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5E;sCACsC;AACtC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,uBAAuB,CAAC;IAClC,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime-seam.d.ts","sourceRoot":"","sources":["../src/runtime-seam.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;;;;;;;;OAYG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACtE;;;;;OAKG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACnF;AAED,MAAM,WAAW,iBAAiB;IAChC;;qBAEiB;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1C;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC;sCACkC;IAClC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC;;;;;;;;;;OAUG;IACH,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,mBAAmB;IAClC;;oCAEgC;IAChC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;kBAEkB;AAClB,MAAM,MAAM,uBAAuB,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5E;;;;;;;uCAOuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;sCACsC;AACtC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,uBAAuB,CAAC;IAClC,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;mEAC+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;kCAE8B;IAC9B,GAAG,CAAC,EAAE,kBAAkB,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B;iEAC6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
|
@@ -28,6 +28,17 @@ export declare const TELO_TYPE_SCHEME = "telo://";
|
|
|
28
28
|
* and the loader rewrites it to this. Published manifests are unaffected.
|
|
29
29
|
*/
|
|
30
30
|
export declare function canonicalTypeSchemaId(moduleName: string, typeName: string): string;
|
|
31
|
+
/** The inverse of {@link canonicalTypeSchemaId}. Returns null for any other
|
|
32
|
+
* string, including the authoring authority form and fragment-bearing built-ins.
|
|
33
|
+
*
|
|
34
|
+
* A resolver reads a named shape through this rather than by bare name: the
|
|
35
|
+
* canonical id carries the OWNING MODULE, so two libraries declaring a shape of
|
|
36
|
+
* the same name stay distinct. Resolving by name alone was how an alias got
|
|
37
|
+
* silently dropped. */
|
|
38
|
+
export declare function parseCanonicalTypeSchemaId(ref: unknown): {
|
|
39
|
+
moduleName: string;
|
|
40
|
+
typeName: string;
|
|
41
|
+
} | null;
|
|
31
42
|
/**
|
|
32
43
|
* Resolve `extends` into a single self-contained object schema by deep-merging an
|
|
33
44
|
* ordered list of already-resolved schemas (parents first, the own schema last):
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type-schema-ref.d.ts","sourceRoot":"","sources":["../src/type-schema-ref.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAE1C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAElF;AAiBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqCzB;AAED,0EAA0E;AAC1E,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CAKjE"}
|
|
1
|
+
{"version":3,"file":"type-schema-ref.d.ts","sourceRoot":"","sources":["../src/type-schema-ref.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAE1C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAElF;AAED;;;;;;wBAMwB;AACxB,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,OAAO,GACX;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAKjD;AAiBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqCzB;AAED,0EAA0E;AAC1E,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CAKjE"}
|
package/dist/type-schema-ref.js
CHANGED
|
@@ -30,6 +30,21 @@ export const TELO_TYPE_SCHEME = "telo://";
|
|
|
30
30
|
export function canonicalTypeSchemaId(moduleName, typeName) {
|
|
31
31
|
return `telo:${moduleName}/${typeName}`;
|
|
32
32
|
}
|
|
33
|
+
/** The inverse of {@link canonicalTypeSchemaId}. Returns null for any other
|
|
34
|
+
* string, including the authoring authority form and fragment-bearing built-ins.
|
|
35
|
+
*
|
|
36
|
+
* A resolver reads a named shape through this rather than by bare name: the
|
|
37
|
+
* canonical id carries the OWNING MODULE, so two libraries declaring a shape of
|
|
38
|
+
* the same name stay distinct. Resolving by name alone was how an alias got
|
|
39
|
+
* silently dropped. */
|
|
40
|
+
export function parseCanonicalTypeSchemaId(ref) {
|
|
41
|
+
if (typeof ref !== "string")
|
|
42
|
+
return null;
|
|
43
|
+
const match = /^telo:([^/#:]+)\/([^#/]+)$/.exec(ref);
|
|
44
|
+
if (!match)
|
|
45
|
+
return null;
|
|
46
|
+
return { moduleName: match[1], typeName: match[2] };
|
|
47
|
+
}
|
|
33
48
|
/** Top-level keywords merged structurally rather than copied wholesale when
|
|
34
49
|
* resolving `extends`: object shape (`properties` / `required` /
|
|
35
50
|
* `additionalProperties`) is deep-merged, and composition keywords (`allOf` /
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `x-telo-type` — the one annotation that says what the value at a slot IS,
|
|
3
|
+
* beyond what JSON Schema's `type` vocabulary can express, and the single
|
|
4
|
+
* accessor every surface reads it through (the `ref-slot.ts` precedent).
|
|
5
|
+
*
|
|
6
|
+
* It replaced three keywords that answered one question differently: a nominal
|
|
7
|
+
* brand from a closed kernel table (`x-telo-type: TcpPort`), raw bytes
|
|
8
|
+
* (`x-telo-binary: true`), and a live handle (`x-telo-stream: true`). They
|
|
9
|
+
* differed in POSTURE toward the JSON Schema layer — refine, replace, exempt —
|
|
10
|
+
* not in kind, so each spelled as its own keyword meant a fourth cost eleven
|
|
11
|
+
* files across four packages, and left three defects: a typo'd brand degraded
|
|
12
|
+
* silently, bytes had no CEL identity, and a module string-matched the keyword
|
|
13
|
+
* because there was nothing on its surface to read.
|
|
14
|
+
*
|
|
15
|
+
* THE VOCABULARY IS DATA; THE BINDING TO A LANGUAGE IS NOT. Entries live at
|
|
16
|
+
* `sdk/value-types/*.json` (see the README there) and are copied in by the SDK's
|
|
17
|
+
* `prepare`. Every runtime that hosts Telo reads the same files — the Rust half
|
|
18
|
+
* types an `!include-bytes` slot from them in a kernel with no CEL engine — so
|
|
19
|
+
* an entry declares a symbolic `binding`, never a constructor name, and each
|
|
20
|
+
* runtime carries its own table mapping that key to its own identity.
|
|
21
|
+
*
|
|
22
|
+
* THE REGISTRY IS IN THE SDK because it is dependency-free and Node-built-in-free
|
|
23
|
+
* (so a browser-side analyzer can read it), because `Stream` already lives here,
|
|
24
|
+
* and because it is the only placement a module controller can reach: a module
|
|
25
|
+
* may import `@telorun/sdk` and nothing else.
|
|
26
|
+
*/
|
|
27
|
+
export declare const X_TELO_TYPE = "x-telo-type";
|
|
28
|
+
/** How a value is represented, which is the one thing an entry declares.
|
|
29
|
+
*
|
|
30
|
+
* - `json` — an ordinary value its declared schema already validates. The
|
|
31
|
+
* name adds nominal identity for static wiring, nothing else.
|
|
32
|
+
* - `instance` — not JSON at all. This is what makes a value unauthorable: no
|
|
33
|
+
* YAML literal is ever a byte buffer or a stream handle. */
|
|
34
|
+
export type ValueTypeRepresentation = "json" | "instance";
|
|
35
|
+
/** A named type parameter. Every parameter is optional and defaults to *any*,
|
|
36
|
+
* so an unparameterized use of a generic type stays legal. Named rather than
|
|
37
|
+
* positional so a diagnostic can say `of` instead of "argument 0", and so a
|
|
38
|
+
* second parameter can be added without a migration. */
|
|
39
|
+
export interface ValueTypeParameter {
|
|
40
|
+
readonly name: string;
|
|
41
|
+
readonly description?: string;
|
|
42
|
+
}
|
|
43
|
+
/** One value type, exactly as its entry file declares it. */
|
|
44
|
+
export interface ValueTypeEntry {
|
|
45
|
+
/** `Telo.`-qualified. The closed vocabulary an author writes at the name slot. */
|
|
46
|
+
readonly name: string;
|
|
47
|
+
readonly representation: ValueTypeRepresentation;
|
|
48
|
+
/** `json` only — the JSON Schema type this name refines. */
|
|
49
|
+
readonly base?: string;
|
|
50
|
+
/** `instance` only — the symbolic key a runtime's binding table maps. */
|
|
51
|
+
readonly binding?: string;
|
|
52
|
+
/** An instance whose consumption has effects, so it is exempt from validation
|
|
53
|
+
* rather than asserted. Exemption is from VALIDATION, never from TYPING. */
|
|
54
|
+
readonly live: boolean;
|
|
55
|
+
readonly parameters: readonly ValueTypeParameter[];
|
|
56
|
+
readonly description: string;
|
|
57
|
+
}
|
|
58
|
+
/** What one runtime can say about an `instance` representation. Node's identity
|
|
59
|
+
* is a constructor (`instanceof` is the assertion) plus the CEL type an
|
|
60
|
+
* expression at such a slot carries. */
|
|
61
|
+
export interface ValueTypeBinding {
|
|
62
|
+
/** The constructor an assertion tests against. `Buffer` extends `Uint8Array`,
|
|
63
|
+
* so a Node buffer satisfies `bytes` without a second rule. */
|
|
64
|
+
readonly constructor: Function;
|
|
65
|
+
/** The CEL type a value of this representation carries. */
|
|
66
|
+
readonly celType: string;
|
|
67
|
+
/** A stand-in the analyzer substitutes for a CEL leaf at such a slot, so the
|
|
68
|
+
* static check and the runtime assertion agree BY CONSTRUCTION rather than by
|
|
69
|
+
* two rules kept in step. Absent for a `live` type, whose value is never
|
|
70
|
+
* validated and so needs nothing to satisfy. */
|
|
71
|
+
readonly placeholder?: () => unknown;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Node's binding table — the ONLY per-language artifact in the whole mechanism.
|
|
75
|
+
*
|
|
76
|
+
* Keyed by an entry's symbolic `binding`, never by its name, so a runtime that
|
|
77
|
+
* represents two entries the same way says so once and a rename of a type does
|
|
78
|
+
* not touch any table.
|
|
79
|
+
*/
|
|
80
|
+
export declare const VALUE_TYPE_BINDINGS: Readonly<Record<string, ValueTypeBinding>>;
|
|
81
|
+
/**
|
|
82
|
+
* Read one entry file's parsed data.
|
|
83
|
+
*
|
|
84
|
+
* Reading is STRICT and the vocabulary is closed at every level. A malformed or
|
|
85
|
+
* typo'd entry is an authoring mistake whose only other outcome is a type that
|
|
86
|
+
* quietly is not in the vocabulary — which reads to an author as "unknown name",
|
|
87
|
+
* pointing at their manifest instead of at the entry.
|
|
88
|
+
*/
|
|
89
|
+
export declare function parseValueTypeEntry(file: string, data: unknown): ValueTypeEntry;
|
|
90
|
+
/** Every declared value type, keyed by its `Telo.`-qualified name. */
|
|
91
|
+
export declare const VALUE_TYPES: ReadonlyMap<string, ValueTypeEntry>;
|
|
92
|
+
/** The declared names, in entry order — what `telo cel types` and the generated
|
|
93
|
+
* docs section enumerate. */
|
|
94
|
+
export declare function valueTypeNames(): string[];
|
|
95
|
+
/** A read `x-telo-type` annotation: the type it names plus its type arguments. */
|
|
96
|
+
export interface ValueTypeSlot {
|
|
97
|
+
/** The name exactly as written, which is also the canonical one — the
|
|
98
|
+
* vocabulary is closed, so there is nothing to resolve. */
|
|
99
|
+
readonly name: string;
|
|
100
|
+
/** The registry entry, or undefined when the name is not a declared type.
|
|
101
|
+
* Present separately from `name` so a diagnostic can report the name the
|
|
102
|
+
* author wrote rather than swallowing an unknown one. */
|
|
103
|
+
readonly entry: ValueTypeEntry | undefined;
|
|
104
|
+
/** Type arguments by parameter name. Each value is a schema node. */
|
|
105
|
+
readonly args: Readonly<Record<string, unknown>>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Read the annotation off a schema node.
|
|
109
|
+
*
|
|
110
|
+
* Two spellings, one meaning: a bare name (`x-telo-type: Telo.Bytes`) and the
|
|
111
|
+
* object form carrying arguments (`{ name: Telo.Stream, of: … }`). Returns
|
|
112
|
+
* undefined when the node carries no annotation at all — an unknown NAME still
|
|
113
|
+
* returns a slot, with `entry` undefined, because silently reading it as "no
|
|
114
|
+
* value type" is the degrade this annotation replaced.
|
|
115
|
+
*/
|
|
116
|
+
export declare function readValueTypeSlot(schema: unknown): ValueTypeSlot | undefined;
|
|
117
|
+
/** The entry a schema node declares, or undefined. The common read. */
|
|
118
|
+
export declare function valueTypeOf(schema: unknown): ValueTypeEntry | undefined;
|
|
119
|
+
/** True when this node declares a value type at all (known or not). */
|
|
120
|
+
export declare function isValueTypeSlot(schema: unknown): boolean;
|
|
121
|
+
/** True when the node declares a `live` type, so its value is exempt from
|
|
122
|
+
* validation — never traversed, never asserted. Typing is unaffected. */
|
|
123
|
+
export declare function isLiveSlot(schema: unknown): boolean;
|
|
124
|
+
/** True when the node declares a type represented as a runtime instance —
|
|
125
|
+
* the values no manifest literal can ever be. */
|
|
126
|
+
export declare function isInstanceSlot(schema: unknown): boolean;
|
|
127
|
+
/** The binding row for a schema node's declared type, or undefined when it
|
|
128
|
+
* declares none / declares a `json` one. */
|
|
129
|
+
export declare function bindingOf(schema: unknown): ValueTypeBinding | undefined;
|
|
130
|
+
/** The stand-in for a CEL leaf at this slot, or undefined when the slot declares
|
|
131
|
+
* no instance type (ordinary JSON, so the schema's own shape decides) or a live
|
|
132
|
+
* one (nothing validates it, so nothing has to satisfy anything). */
|
|
133
|
+
export declare function valueTypePlaceholder(schema: unknown): unknown | undefined;
|
|
134
|
+
/**
|
|
135
|
+
* The CEL type a value at this slot carries.
|
|
136
|
+
*
|
|
137
|
+
* A `json` representation carries its own NAME as a nominal brand — which is the
|
|
138
|
+
* whole point of one, since a `Telo.TcpPort` and a `Telo.UdpPort` are structurally
|
|
139
|
+
* identical. An `instance` carries whatever its binding says.
|
|
140
|
+
*/
|
|
141
|
+
export declare function celTypeOfValueType(entry: ValueTypeEntry): string;
|
|
142
|
+
/** The CEL type a brand degrades to where the consuming slot declares none —
|
|
143
|
+
* gradual typing, so a `Telo.TcpPort` flows freely into a plain integer field.
|
|
144
|
+
* Undefined for an `instance`, which has no base to fall back to. */
|
|
145
|
+
export declare function celBaseOfValueType(entry: ValueTypeEntry): string | undefined;
|
|
146
|
+
/** Every `json` representation's CEL brand → the base type it refines. The
|
|
147
|
+
* gradual-typing table, derived rather than hand-written. */
|
|
148
|
+
export declare function valueBrandBases(): Record<string, string>;
|
|
149
|
+
//# sourceMappingURL=value-type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"value-type.d.ts","sourceRoot":"","sources":["../src/value-type.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAKH,eAAO,MAAM,WAAW,gBAAgB,CAAC;AAEzC;;;;;4EAK4E;AAC5E,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,UAAU,CAAC;AAE1D;;;yDAGyD;AACzD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE,uBAAuB,CAAC;IACjD,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;iFAC6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACnD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;yCAEyC;AACzC,MAAM,WAAW,gBAAgB;IAC/B;oEACgE;IAChE,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC;IAC/B,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;qDAGiD;IACjD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;CACtC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAG1E,CAAC;AA+DF;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,cAAc,CA2D/E;AAqCD,sEAAsE;AACtE,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAmB,CAAC;AAEhF;8BAC8B;AAC9B,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAEzC;AAED,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC5B;gEAC4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;8DAE0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,CAAC;IAC3C,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAClD;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CAuB5E;AAED,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAEvE;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAExD;AAED;0EAC0E;AAC1E,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAEnD;AAED;kDACkD;AAClD,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAEvD;AAED;6CAC6C;AAC7C,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB,GAAG,SAAS,CAGvE;AAED;;sEAEsE;AACtE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAEzE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAIhE;AAED;;sEAEsE;AACtE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAE5E;AAED;8DAC8D;AAC9D,wBAAgB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOxD"}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `x-telo-type` — the one annotation that says what the value at a slot IS,
|
|
3
|
+
* beyond what JSON Schema's `type` vocabulary can express, and the single
|
|
4
|
+
* accessor every surface reads it through (the `ref-slot.ts` precedent).
|
|
5
|
+
*
|
|
6
|
+
* It replaced three keywords that answered one question differently: a nominal
|
|
7
|
+
* brand from a closed kernel table (`x-telo-type: TcpPort`), raw bytes
|
|
8
|
+
* (`x-telo-binary: true`), and a live handle (`x-telo-stream: true`). They
|
|
9
|
+
* differed in POSTURE toward the JSON Schema layer — refine, replace, exempt —
|
|
10
|
+
* not in kind, so each spelled as its own keyword meant a fourth cost eleven
|
|
11
|
+
* files across four packages, and left three defects: a typo'd brand degraded
|
|
12
|
+
* silently, bytes had no CEL identity, and a module string-matched the keyword
|
|
13
|
+
* because there was nothing on its surface to read.
|
|
14
|
+
*
|
|
15
|
+
* THE VOCABULARY IS DATA; THE BINDING TO A LANGUAGE IS NOT. Entries live at
|
|
16
|
+
* `sdk/value-types/*.json` (see the README there) and are copied in by the SDK's
|
|
17
|
+
* `prepare`. Every runtime that hosts Telo reads the same files — the Rust half
|
|
18
|
+
* types an `!include-bytes` slot from them in a kernel with no CEL engine — so
|
|
19
|
+
* an entry declares a symbolic `binding`, never a constructor name, and each
|
|
20
|
+
* runtime carries its own table mapping that key to its own identity.
|
|
21
|
+
*
|
|
22
|
+
* THE REGISTRY IS IN THE SDK because it is dependency-free and Node-built-in-free
|
|
23
|
+
* (so a browser-side analyzer can read it), because `Stream` already lives here,
|
|
24
|
+
* and because it is the only placement a module controller can reach: a module
|
|
25
|
+
* may import `@telorun/sdk` and nothing else.
|
|
26
|
+
*/
|
|
27
|
+
import { Stream } from "./stream.js";
|
|
28
|
+
import { VALUE_TYPE_ENTRY_FILES } from "./value-types/entries/index.js";
|
|
29
|
+
export const X_TELO_TYPE = "x-telo-type";
|
|
30
|
+
/**
|
|
31
|
+
* Node's binding table — the ONLY per-language artifact in the whole mechanism.
|
|
32
|
+
*
|
|
33
|
+
* Keyed by an entry's symbolic `binding`, never by its name, so a runtime that
|
|
34
|
+
* represents two entries the same way says so once and a rename of a type does
|
|
35
|
+
* not touch any table.
|
|
36
|
+
*/
|
|
37
|
+
export const VALUE_TYPE_BINDINGS = {
|
|
38
|
+
bytes: { constructor: Uint8Array, celType: "bytes", placeholder: () => new Uint8Array() },
|
|
39
|
+
stream: { constructor: Stream, celType: "Stream" },
|
|
40
|
+
};
|
|
41
|
+
/** The CEL type a `json` representation's declared base carries. A brand's own
|
|
42
|
+
* name is the CEL type; this is what it degrades to when the consuming slot
|
|
43
|
+
* declares no brand of its own (gradual typing). */
|
|
44
|
+
const CEL_TYPE_FOR_BASE = {
|
|
45
|
+
integer: "int",
|
|
46
|
+
number: "double",
|
|
47
|
+
string: "string",
|
|
48
|
+
boolean: "bool",
|
|
49
|
+
array: "list",
|
|
50
|
+
object: "map",
|
|
51
|
+
};
|
|
52
|
+
class ValueTypeEntryError extends Error {
|
|
53
|
+
constructor(file, detail) {
|
|
54
|
+
super(`Invalid value-type entry '${file}': ${detail}`);
|
|
55
|
+
this.name = "ValueTypeEntryError";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const ENTRY_KEYS = [
|
|
59
|
+
"name",
|
|
60
|
+
"representation",
|
|
61
|
+
"base",
|
|
62
|
+
"binding",
|
|
63
|
+
"live",
|
|
64
|
+
"parameters",
|
|
65
|
+
"description",
|
|
66
|
+
"$comment",
|
|
67
|
+
];
|
|
68
|
+
function isPlainObject(value) {
|
|
69
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
70
|
+
}
|
|
71
|
+
function requireString(file, node, key) {
|
|
72
|
+
const value = node[key];
|
|
73
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
74
|
+
throw new ValueTypeEntryError(file, `'${key}' must be a non-empty string`);
|
|
75
|
+
}
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
function readParameters(file, raw) {
|
|
79
|
+
if (raw === undefined)
|
|
80
|
+
return [];
|
|
81
|
+
if (!Array.isArray(raw))
|
|
82
|
+
throw new ValueTypeEntryError(file, "'parameters' must be a sequence");
|
|
83
|
+
return raw.map((entry, i) => {
|
|
84
|
+
if (!isPlainObject(entry)) {
|
|
85
|
+
throw new ValueTypeEntryError(file, `parameters[${i}] must be a mapping`);
|
|
86
|
+
}
|
|
87
|
+
for (const key of Object.keys(entry)) {
|
|
88
|
+
if (key !== "name" && key !== "description") {
|
|
89
|
+
throw new ValueTypeEntryError(file, `parameters[${i}] has no key '${key}'`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const name = requireString(file, entry, "name");
|
|
93
|
+
return entry.description === undefined
|
|
94
|
+
? { name }
|
|
95
|
+
: { name, description: requireString(file, entry, "description") };
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Read one entry file's parsed data.
|
|
100
|
+
*
|
|
101
|
+
* Reading is STRICT and the vocabulary is closed at every level. A malformed or
|
|
102
|
+
* typo'd entry is an authoring mistake whose only other outcome is a type that
|
|
103
|
+
* quietly is not in the vocabulary — which reads to an author as "unknown name",
|
|
104
|
+
* pointing at their manifest instead of at the entry.
|
|
105
|
+
*/
|
|
106
|
+
export function parseValueTypeEntry(file, data) {
|
|
107
|
+
if (!isPlainObject(data))
|
|
108
|
+
throw new ValueTypeEntryError(file, "an entry must be a mapping");
|
|
109
|
+
for (const key of Object.keys(data)) {
|
|
110
|
+
if (!ENTRY_KEYS.includes(key)) {
|
|
111
|
+
throw new ValueTypeEntryError(file, `an entry has no key '${key}'. Known keys: ${ENTRY_KEYS.join(", ")}.`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const name = requireString(file, data, "name");
|
|
115
|
+
if (!name.startsWith("Telo.")) {
|
|
116
|
+
throw new ValueTypeEntryError(file, `'name' must be Telo.-qualified — a representation is kernel-owned and cannot be module-defined`);
|
|
117
|
+
}
|
|
118
|
+
const representation = requireString(file, data, "representation");
|
|
119
|
+
if (representation !== "json" && representation !== "instance") {
|
|
120
|
+
throw new ValueTypeEntryError(file, `'representation' must be 'json' or 'instance'`);
|
|
121
|
+
}
|
|
122
|
+
if (data.live !== undefined && typeof data.live !== "boolean") {
|
|
123
|
+
throw new ValueTypeEntryError(file, "'live' must be a boolean when present");
|
|
124
|
+
}
|
|
125
|
+
// The two representations take disjoint parameters, and mixing them is a
|
|
126
|
+
// statement with no meaning: a `json` value has no constructor to assert, and
|
|
127
|
+
// an `instance` has no JSON base to refine.
|
|
128
|
+
if (representation === "json") {
|
|
129
|
+
if (data.binding !== undefined) {
|
|
130
|
+
throw new ValueTypeEntryError(file, "a 'json' representation takes no 'binding'");
|
|
131
|
+
}
|
|
132
|
+
const base = requireString(file, data, "base");
|
|
133
|
+
if (!(base in CEL_TYPE_FOR_BASE)) {
|
|
134
|
+
throw new ValueTypeEntryError(file, `'base' '${base}' is not a JSON Schema type (${Object.keys(CEL_TYPE_FOR_BASE).join(", ")})`);
|
|
135
|
+
}
|
|
136
|
+
if (data.live === true) {
|
|
137
|
+
throw new ValueTypeEntryError(file, "a 'json' representation cannot be 'live' — it is data");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
if (data.base !== undefined) {
|
|
142
|
+
throw new ValueTypeEntryError(file, "an 'instance' representation takes no 'base'");
|
|
143
|
+
}
|
|
144
|
+
requireString(file, data, "binding");
|
|
145
|
+
}
|
|
146
|
+
const entry = {
|
|
147
|
+
name,
|
|
148
|
+
representation,
|
|
149
|
+
...(representation === "json" ? { base: data.base } : { binding: data.binding }),
|
|
150
|
+
live: data.live === true,
|
|
151
|
+
parameters: readParameters(file, data.parameters),
|
|
152
|
+
description: requireString(file, data, "description"),
|
|
153
|
+
};
|
|
154
|
+
return entry;
|
|
155
|
+
}
|
|
156
|
+
function buildRegistry() {
|
|
157
|
+
const registry = new Map();
|
|
158
|
+
for (const [file, data] of VALUE_TYPE_ENTRY_FILES) {
|
|
159
|
+
const entry = parseValueTypeEntry(file, data);
|
|
160
|
+
if (registry.has(entry.name)) {
|
|
161
|
+
throw new ValueTypeEntryError(file, `'${entry.name}' is already declared by another entry`);
|
|
162
|
+
}
|
|
163
|
+
// A binding with no row in THIS host's table is a hard error, never a
|
|
164
|
+
// skipped assertion: a type that cannot be asserted would silently exempt
|
|
165
|
+
// every slot declaring it, converting a contract into a hole. The same class
|
|
166
|
+
// of failure as an unrecognized `use` token degrading to the legacy reading.
|
|
167
|
+
if (entry.binding !== undefined && !(entry.binding in VALUE_TYPE_BINDINGS)) {
|
|
168
|
+
throw new ValueTypeEntryError(file, `binding '${entry.binding}' has no row in this runtime's table — a value type ` +
|
|
169
|
+
`whose assertion cannot be produced would silently exempt every slot that declares it`);
|
|
170
|
+
}
|
|
171
|
+
registry.set(entry.name, entry);
|
|
172
|
+
}
|
|
173
|
+
// Defence in depth against the packaging mistake, because the failure it
|
|
174
|
+
// produces is indistinguishable from an author's typo: every `x-telo-type`
|
|
175
|
+
// becomes an unknown name, reported against manifests that are correct. The
|
|
176
|
+
// build script refuses a missing source directory; this refuses the state that
|
|
177
|
+
// would reach a user if some other path ever produced it.
|
|
178
|
+
if (registry.size === 0) {
|
|
179
|
+
throw new Error("The value-type vocabulary is empty. `sdk/value-types/*.json` did not reach this " +
|
|
180
|
+
"build — check the file allowlist of whatever packaged it. Continuing would report " +
|
|
181
|
+
"every declared value type as an unknown name.");
|
|
182
|
+
}
|
|
183
|
+
return registry;
|
|
184
|
+
}
|
|
185
|
+
/** Every declared value type, keyed by its `Telo.`-qualified name. */
|
|
186
|
+
export const VALUE_TYPES = buildRegistry();
|
|
187
|
+
/** The declared names, in entry order — what `telo cel types` and the generated
|
|
188
|
+
* docs section enumerate. */
|
|
189
|
+
export function valueTypeNames() {
|
|
190
|
+
return [...VALUE_TYPES.keys()];
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Read the annotation off a schema node.
|
|
194
|
+
*
|
|
195
|
+
* Two spellings, one meaning: a bare name (`x-telo-type: Telo.Bytes`) and the
|
|
196
|
+
* object form carrying arguments (`{ name: Telo.Stream, of: … }`). Returns
|
|
197
|
+
* undefined when the node carries no annotation at all — an unknown NAME still
|
|
198
|
+
* returns a slot, with `entry` undefined, because silently reading it as "no
|
|
199
|
+
* value type" is the degrade this annotation replaced.
|
|
200
|
+
*/
|
|
201
|
+
export function readValueTypeSlot(schema) {
|
|
202
|
+
if (!isPlainObject(schema))
|
|
203
|
+
return undefined;
|
|
204
|
+
const raw = schema[X_TELO_TYPE];
|
|
205
|
+
if (raw === undefined)
|
|
206
|
+
return undefined;
|
|
207
|
+
if (typeof raw === "string") {
|
|
208
|
+
return { name: raw, entry: VALUE_TYPES.get(raw), args: {} };
|
|
209
|
+
}
|
|
210
|
+
if (isPlainObject(raw)) {
|
|
211
|
+
const name = typeof raw.name === "string" ? raw.name : "";
|
|
212
|
+
const args = {};
|
|
213
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
214
|
+
if (key === "name")
|
|
215
|
+
continue;
|
|
216
|
+
// A bare NAME as an argument is sugar for a schema node carrying only that
|
|
217
|
+
// annotation, so `of: Telo.Bytes` and `of: { x-telo-type: Telo.Bytes }`
|
|
218
|
+
// are one thing. Normalized HERE, in the single reader, so no consumer
|
|
219
|
+
// re-derives it — a comparator that saw the string form would compare a
|
|
220
|
+
// string against a schema and quietly conclude nothing.
|
|
221
|
+
args[key] = typeof value === "string" ? { [X_TELO_TYPE]: value } : value;
|
|
222
|
+
}
|
|
223
|
+
return { name, entry: VALUE_TYPES.get(name), args };
|
|
224
|
+
}
|
|
225
|
+
return { name: "", entry: undefined, args: {} };
|
|
226
|
+
}
|
|
227
|
+
/** The entry a schema node declares, or undefined. The common read. */
|
|
228
|
+
export function valueTypeOf(schema) {
|
|
229
|
+
return readValueTypeSlot(schema)?.entry;
|
|
230
|
+
}
|
|
231
|
+
/** True when this node declares a value type at all (known or not). */
|
|
232
|
+
export function isValueTypeSlot(schema) {
|
|
233
|
+
return readValueTypeSlot(schema) !== undefined;
|
|
234
|
+
}
|
|
235
|
+
/** True when the node declares a `live` type, so its value is exempt from
|
|
236
|
+
* validation — never traversed, never asserted. Typing is unaffected. */
|
|
237
|
+
export function isLiveSlot(schema) {
|
|
238
|
+
return valueTypeOf(schema)?.live === true;
|
|
239
|
+
}
|
|
240
|
+
/** True when the node declares a type represented as a runtime instance —
|
|
241
|
+
* the values no manifest literal can ever be. */
|
|
242
|
+
export function isInstanceSlot(schema) {
|
|
243
|
+
return valueTypeOf(schema)?.representation === "instance";
|
|
244
|
+
}
|
|
245
|
+
/** The binding row for a schema node's declared type, or undefined when it
|
|
246
|
+
* declares none / declares a `json` one. */
|
|
247
|
+
export function bindingOf(schema) {
|
|
248
|
+
const binding = valueTypeOf(schema)?.binding;
|
|
249
|
+
return binding === undefined ? undefined : VALUE_TYPE_BINDINGS[binding];
|
|
250
|
+
}
|
|
251
|
+
/** The stand-in for a CEL leaf at this slot, or undefined when the slot declares
|
|
252
|
+
* no instance type (ordinary JSON, so the schema's own shape decides) or a live
|
|
253
|
+
* one (nothing validates it, so nothing has to satisfy anything). */
|
|
254
|
+
export function valueTypePlaceholder(schema) {
|
|
255
|
+
return bindingOf(schema)?.placeholder?.();
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* The CEL type a value at this slot carries.
|
|
259
|
+
*
|
|
260
|
+
* A `json` representation carries its own NAME as a nominal brand — which is the
|
|
261
|
+
* whole point of one, since a `Telo.TcpPort` and a `Telo.UdpPort` are structurally
|
|
262
|
+
* identical. An `instance` carries whatever its binding says.
|
|
263
|
+
*/
|
|
264
|
+
export function celTypeOfValueType(entry) {
|
|
265
|
+
if (entry.representation === "json")
|
|
266
|
+
return entry.name;
|
|
267
|
+
const binding = VALUE_TYPE_BINDINGS[entry.binding];
|
|
268
|
+
return binding.celType;
|
|
269
|
+
}
|
|
270
|
+
/** The CEL type a brand degrades to where the consuming slot declares none —
|
|
271
|
+
* gradual typing, so a `Telo.TcpPort` flows freely into a plain integer field.
|
|
272
|
+
* Undefined for an `instance`, which has no base to fall back to. */
|
|
273
|
+
export function celBaseOfValueType(entry) {
|
|
274
|
+
return entry.representation === "json" ? CEL_TYPE_FOR_BASE[entry.base] : undefined;
|
|
275
|
+
}
|
|
276
|
+
/** Every `json` representation's CEL brand → the base type it refines. The
|
|
277
|
+
* gradual-typing table, derived rather than hand-written. */
|
|
278
|
+
export function valueBrandBases() {
|
|
279
|
+
const out = {};
|
|
280
|
+
for (const entry of VALUE_TYPES.values()) {
|
|
281
|
+
const base = celBaseOfValueType(entry);
|
|
282
|
+
if (base !== undefined)
|
|
283
|
+
out[entry.name] = base;
|
|
284
|
+
}
|
|
285
|
+
return out;
|
|
286
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/value-types/entries/index.ts"],"names":[],"mappings":"AAOA,yEAAyE;AACzE,eAAO,MAAM,sBAAsB,EAAE,aAAa,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAKxF,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// GENERATED by scripts/copy-value-type-entries.mjs — do not edit, and do not commit.
|
|
2
|
+
// Source: sdk/value-types/*.json (lexically ordered).
|
|
3
|
+
import e0 from "./telo-bytes.json" with { type: "json" };
|
|
4
|
+
import e1 from "./telo-stream.json" with { type: "json" };
|
|
5
|
+
import e2 from "./telo-tcp-port.json" with { type: "json" };
|
|
6
|
+
import e3 from "./telo-udp-port.json" with { type: "json" };
|
|
7
|
+
/** Every value-type entry file, in the order the registry reads them. */
|
|
8
|
+
export const VALUE_TYPE_ENTRY_FILES = [
|
|
9
|
+
["telo-bytes.json", e0],
|
|
10
|
+
["telo-stream.json", e1],
|
|
11
|
+
["telo-tcp-port.json", e2],
|
|
12
|
+
["telo-udp-port.json", e3],
|
|
13
|
+
];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Bytes are not expressible in JSON Schema's type vocabulary. `type: object` is satisfied by every object, so a mistyped literal reached the controller instead of failing check; `type: binary` is not an option, since a validator refuses to COMPILE an unknown type and a published telo.yaml would stop being JSON Schema for the hub, the editor and every third-party reader. Declaring the representation instead is what makes the check fall out: no YAML literal is ever a byte buffer, so a literal at a byte slot is rejected statically and a value arriving by reference passes.",
|
|
3
|
+
"name": "Telo.Bytes",
|
|
4
|
+
"representation": "instance",
|
|
5
|
+
"binding": "bytes",
|
|
6
|
+
"description": "Raw bytes. Never authorable inline — a byte slot is filled by reference (an `!include-bytes` embed, a resource output, a CEL expression), and the runtime asserts the value really is a byte buffer."
|
|
7
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "`live` is what separates a stream from bytes: consuming it has effects, so it is EXEMPT from validation rather than asserted — iterating a stream to check its elements is precisely what the exemption forbids. The exemption is from VALIDATION, never from TYPING: `of` still travels through every schema-typing walk, because that is where the argument check reads it.",
|
|
3
|
+
"name": "Telo.Stream",
|
|
4
|
+
"representation": "instance",
|
|
5
|
+
"binding": "stream",
|
|
6
|
+
"live": true,
|
|
7
|
+
"parameters": [
|
|
8
|
+
{
|
|
9
|
+
"name": "of",
|
|
10
|
+
"description": "The element the stream yields. Any schema node — an inline shape, a value type, a `!ref` to a named shape, or another parameterized type. Omitted means any element."
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"description": "A live handle over a sequence of values, consumed by reading. Its elements are never buffered or validated, and member access past it is rejected — a consumer iterates it instead."
|
|
14
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "A `json` representation adds nominal identity to a value the declared schema already validates. A TcpPort and a UdpPort are both integers, so nothing structural tells them apart — the name is the whole difference, and it is what makes wiring one into the other's slot a static error.",
|
|
3
|
+
"name": "Telo.TcpPort",
|
|
4
|
+
"representation": "json",
|
|
5
|
+
"base": "integer",
|
|
6
|
+
"description": "A TCP port number. Distinct from a UDP port even though both are integers, so wiring one into the other's slot is a static error."
|
|
7
|
+
}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
package/src/resource-context.ts
CHANGED
|
@@ -27,6 +27,12 @@ export interface LoadOptions {
|
|
|
27
27
|
* inline imports resolve and execute identically to authored `Telo.Import`
|
|
28
28
|
* documents. Mirrors the analyzer loader's option of the same name. */
|
|
29
29
|
desugarImports?: boolean;
|
|
30
|
+
/** When true, legacy manifest spellings are rewritten to the current ones
|
|
31
|
+
* before the manifests are returned, so a module published years ago loads
|
|
32
|
+
* against today's vocabulary. On for every runtime load; off only for a
|
|
33
|
+
* round-trip view that must show the author's own text. Mirrors the analyzer
|
|
34
|
+
* loader's option of the same name. */
|
|
35
|
+
migrate?: boolean;
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
export interface DataValidator {
|
package/src/runtime-seam.ts
CHANGED
|
@@ -94,6 +94,18 @@ export interface RuntimeCheckOptions {
|
|
|
94
94
|
* kernel too. */
|
|
95
95
|
export type CheckDiagnosticSeverity = "error" | "warning" | "info" | "hint";
|
|
96
96
|
|
|
97
|
+
/** A mechanically applicable repair for a finding: `replacement` is the whole
|
|
98
|
+
* corrected value at `path` — never a fragment — so a consumer applies it
|
|
99
|
+
* without parsing the language inside.
|
|
100
|
+
*
|
|
101
|
+
* That is why `path` travels with it. `source` / `line` / `column` locate a
|
|
102
|
+
* finding for a human reading text; a module applying a repair works on the
|
|
103
|
+
* parsed manifest, where a line number is not an address. A repair without its
|
|
104
|
+
* anchor is one nothing can apply. */
|
|
105
|
+
export interface CheckDiagnosticFix {
|
|
106
|
+
replacement: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
97
109
|
/** One analyzer finding, flattened to data. Positions are zero-based, matching
|
|
98
110
|
* the analyzer's own range model. */
|
|
99
111
|
export interface CheckDiagnostic {
|
|
@@ -104,6 +116,15 @@ export interface CheckDiagnostic {
|
|
|
104
116
|
source?: string;
|
|
105
117
|
line?: number;
|
|
106
118
|
column?: number;
|
|
119
|
+
/** `<kind>/<name>` of the resource the finding is pinned to. */
|
|
120
|
+
resource?: string;
|
|
121
|
+
/** Dotted path of the offending value within that resource
|
|
122
|
+
* (`steps[0].inputs.flag`) — the address `fix` applies at. */
|
|
123
|
+
path?: string;
|
|
124
|
+
/** Present only when the repair is decidable — a fix that might not be
|
|
125
|
+
* correct is worse than none, since the point of the field is that it can be
|
|
126
|
+
* applied without review. */
|
|
127
|
+
fix?: CheckDiagnosticFix;
|
|
107
128
|
}
|
|
108
129
|
|
|
109
130
|
export interface RuntimeCheckResult {
|
package/src/type-schema-ref.ts
CHANGED
|
@@ -33,6 +33,22 @@ export function canonicalTypeSchemaId(moduleName: string, typeName: string): str
|
|
|
33
33
|
return `telo:${moduleName}/${typeName}`;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/** The inverse of {@link canonicalTypeSchemaId}. Returns null for any other
|
|
37
|
+
* string, including the authoring authority form and fragment-bearing built-ins.
|
|
38
|
+
*
|
|
39
|
+
* A resolver reads a named shape through this rather than by bare name: the
|
|
40
|
+
* canonical id carries the OWNING MODULE, so two libraries declaring a shape of
|
|
41
|
+
* the same name stay distinct. Resolving by name alone was how an alias got
|
|
42
|
+
* silently dropped. */
|
|
43
|
+
export function parseCanonicalTypeSchemaId(
|
|
44
|
+
ref: unknown,
|
|
45
|
+
): { moduleName: string; typeName: string } | null {
|
|
46
|
+
if (typeof ref !== "string") return null;
|
|
47
|
+
const match = /^telo:([^/#:]+)\/([^#/]+)$/.exec(ref);
|
|
48
|
+
if (!match) return null;
|
|
49
|
+
return { moduleName: match[1]!, typeName: match[2]! };
|
|
50
|
+
}
|
|
51
|
+
|
|
36
52
|
/** Top-level keywords merged structurally rather than copied wholesale when
|
|
37
53
|
* resolving `extends`: object shape (`properties` / `required` /
|
|
38
54
|
* `additionalProperties`) is deep-merged, and composition keywords (`allOf` /
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `x-telo-type` — the one annotation that says what the value at a slot IS,
|
|
3
|
+
* beyond what JSON Schema's `type` vocabulary can express, and the single
|
|
4
|
+
* accessor every surface reads it through (the `ref-slot.ts` precedent).
|
|
5
|
+
*
|
|
6
|
+
* It replaced three keywords that answered one question differently: a nominal
|
|
7
|
+
* brand from a closed kernel table (`x-telo-type: TcpPort`), raw bytes
|
|
8
|
+
* (`x-telo-binary: true`), and a live handle (`x-telo-stream: true`). They
|
|
9
|
+
* differed in POSTURE toward the JSON Schema layer — refine, replace, exempt —
|
|
10
|
+
* not in kind, so each spelled as its own keyword meant a fourth cost eleven
|
|
11
|
+
* files across four packages, and left three defects: a typo'd brand degraded
|
|
12
|
+
* silently, bytes had no CEL identity, and a module string-matched the keyword
|
|
13
|
+
* because there was nothing on its surface to read.
|
|
14
|
+
*
|
|
15
|
+
* THE VOCABULARY IS DATA; THE BINDING TO A LANGUAGE IS NOT. Entries live at
|
|
16
|
+
* `sdk/value-types/*.json` (see the README there) and are copied in by the SDK's
|
|
17
|
+
* `prepare`. Every runtime that hosts Telo reads the same files — the Rust half
|
|
18
|
+
* types an `!include-bytes` slot from them in a kernel with no CEL engine — so
|
|
19
|
+
* an entry declares a symbolic `binding`, never a constructor name, and each
|
|
20
|
+
* runtime carries its own table mapping that key to its own identity.
|
|
21
|
+
*
|
|
22
|
+
* THE REGISTRY IS IN THE SDK because it is dependency-free and Node-built-in-free
|
|
23
|
+
* (so a browser-side analyzer can read it), because `Stream` already lives here,
|
|
24
|
+
* and because it is the only placement a module controller can reach: a module
|
|
25
|
+
* may import `@telorun/sdk` and nothing else.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { Stream } from "./stream.js";
|
|
29
|
+
import { VALUE_TYPE_ENTRY_FILES } from "./value-types/entries/index.js";
|
|
30
|
+
|
|
31
|
+
export const X_TELO_TYPE = "x-telo-type";
|
|
32
|
+
|
|
33
|
+
/** How a value is represented, which is the one thing an entry declares.
|
|
34
|
+
*
|
|
35
|
+
* - `json` — an ordinary value its declared schema already validates. The
|
|
36
|
+
* name adds nominal identity for static wiring, nothing else.
|
|
37
|
+
* - `instance` — not JSON at all. This is what makes a value unauthorable: no
|
|
38
|
+
* YAML literal is ever a byte buffer or a stream handle. */
|
|
39
|
+
export type ValueTypeRepresentation = "json" | "instance";
|
|
40
|
+
|
|
41
|
+
/** A named type parameter. Every parameter is optional and defaults to *any*,
|
|
42
|
+
* so an unparameterized use of a generic type stays legal. Named rather than
|
|
43
|
+
* positional so a diagnostic can say `of` instead of "argument 0", and so a
|
|
44
|
+
* second parameter can be added without a migration. */
|
|
45
|
+
export interface ValueTypeParameter {
|
|
46
|
+
readonly name: string;
|
|
47
|
+
readonly description?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** One value type, exactly as its entry file declares it. */
|
|
51
|
+
export interface ValueTypeEntry {
|
|
52
|
+
/** `Telo.`-qualified. The closed vocabulary an author writes at the name slot. */
|
|
53
|
+
readonly name: string;
|
|
54
|
+
readonly representation: ValueTypeRepresentation;
|
|
55
|
+
/** `json` only — the JSON Schema type this name refines. */
|
|
56
|
+
readonly base?: string;
|
|
57
|
+
/** `instance` only — the symbolic key a runtime's binding table maps. */
|
|
58
|
+
readonly binding?: string;
|
|
59
|
+
/** An instance whose consumption has effects, so it is exempt from validation
|
|
60
|
+
* rather than asserted. Exemption is from VALIDATION, never from TYPING. */
|
|
61
|
+
readonly live: boolean;
|
|
62
|
+
readonly parameters: readonly ValueTypeParameter[];
|
|
63
|
+
readonly description: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** What one runtime can say about an `instance` representation. Node's identity
|
|
67
|
+
* is a constructor (`instanceof` is the assertion) plus the CEL type an
|
|
68
|
+
* expression at such a slot carries. */
|
|
69
|
+
export interface ValueTypeBinding {
|
|
70
|
+
/** The constructor an assertion tests against. `Buffer` extends `Uint8Array`,
|
|
71
|
+
* so a Node buffer satisfies `bytes` without a second rule. */
|
|
72
|
+
readonly constructor: Function;
|
|
73
|
+
/** The CEL type a value of this representation carries. */
|
|
74
|
+
readonly celType: string;
|
|
75
|
+
/** A stand-in the analyzer substitutes for a CEL leaf at such a slot, so the
|
|
76
|
+
* static check and the runtime assertion agree BY CONSTRUCTION rather than by
|
|
77
|
+
* two rules kept in step. Absent for a `live` type, whose value is never
|
|
78
|
+
* validated and so needs nothing to satisfy. */
|
|
79
|
+
readonly placeholder?: () => unknown;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Node's binding table — the ONLY per-language artifact in the whole mechanism.
|
|
84
|
+
*
|
|
85
|
+
* Keyed by an entry's symbolic `binding`, never by its name, so a runtime that
|
|
86
|
+
* represents two entries the same way says so once and a rename of a type does
|
|
87
|
+
* not touch any table.
|
|
88
|
+
*/
|
|
89
|
+
export const VALUE_TYPE_BINDINGS: Readonly<Record<string, ValueTypeBinding>> = {
|
|
90
|
+
bytes: { constructor: Uint8Array, celType: "bytes", placeholder: () => new Uint8Array() },
|
|
91
|
+
stream: { constructor: Stream, celType: "Stream" },
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/** The CEL type a `json` representation's declared base carries. A brand's own
|
|
95
|
+
* name is the CEL type; this is what it degrades to when the consuming slot
|
|
96
|
+
* declares no brand of its own (gradual typing). */
|
|
97
|
+
const CEL_TYPE_FOR_BASE: Readonly<Record<string, string>> = {
|
|
98
|
+
integer: "int",
|
|
99
|
+
number: "double",
|
|
100
|
+
string: "string",
|
|
101
|
+
boolean: "bool",
|
|
102
|
+
array: "list",
|
|
103
|
+
object: "map",
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
class ValueTypeEntryError extends Error {
|
|
107
|
+
constructor(file: string, detail: string) {
|
|
108
|
+
super(`Invalid value-type entry '${file}': ${detail}`);
|
|
109
|
+
this.name = "ValueTypeEntryError";
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const ENTRY_KEYS = [
|
|
114
|
+
"name",
|
|
115
|
+
"representation",
|
|
116
|
+
"base",
|
|
117
|
+
"binding",
|
|
118
|
+
"live",
|
|
119
|
+
"parameters",
|
|
120
|
+
"description",
|
|
121
|
+
"$comment",
|
|
122
|
+
] as const;
|
|
123
|
+
|
|
124
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
125
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function requireString(file: string, node: Record<string, unknown>, key: string): string {
|
|
129
|
+
const value = node[key];
|
|
130
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
131
|
+
throw new ValueTypeEntryError(file, `'${key}' must be a non-empty string`);
|
|
132
|
+
}
|
|
133
|
+
return value;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function readParameters(file: string, raw: unknown): ValueTypeParameter[] {
|
|
137
|
+
if (raw === undefined) return [];
|
|
138
|
+
if (!Array.isArray(raw)) throw new ValueTypeEntryError(file, "'parameters' must be a sequence");
|
|
139
|
+
return raw.map((entry, i) => {
|
|
140
|
+
if (!isPlainObject(entry)) {
|
|
141
|
+
throw new ValueTypeEntryError(file, `parameters[${i}] must be a mapping`);
|
|
142
|
+
}
|
|
143
|
+
for (const key of Object.keys(entry)) {
|
|
144
|
+
if (key !== "name" && key !== "description") {
|
|
145
|
+
throw new ValueTypeEntryError(file, `parameters[${i}] has no key '${key}'`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const name = requireString(file, entry, "name");
|
|
149
|
+
return entry.description === undefined
|
|
150
|
+
? { name }
|
|
151
|
+
: { name, description: requireString(file, entry, "description") };
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Read one entry file's parsed data.
|
|
157
|
+
*
|
|
158
|
+
* Reading is STRICT and the vocabulary is closed at every level. A malformed or
|
|
159
|
+
* typo'd entry is an authoring mistake whose only other outcome is a type that
|
|
160
|
+
* quietly is not in the vocabulary — which reads to an author as "unknown name",
|
|
161
|
+
* pointing at their manifest instead of at the entry.
|
|
162
|
+
*/
|
|
163
|
+
export function parseValueTypeEntry(file: string, data: unknown): ValueTypeEntry {
|
|
164
|
+
if (!isPlainObject(data)) throw new ValueTypeEntryError(file, "an entry must be a mapping");
|
|
165
|
+
for (const key of Object.keys(data)) {
|
|
166
|
+
if (!(ENTRY_KEYS as readonly string[]).includes(key)) {
|
|
167
|
+
throw new ValueTypeEntryError(
|
|
168
|
+
file,
|
|
169
|
+
`an entry has no key '${key}'. Known keys: ${ENTRY_KEYS.join(", ")}.`,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const name = requireString(file, data, "name");
|
|
175
|
+
if (!name.startsWith("Telo.")) {
|
|
176
|
+
throw new ValueTypeEntryError(
|
|
177
|
+
file,
|
|
178
|
+
`'name' must be Telo.-qualified — a representation is kernel-owned and cannot be module-defined`,
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
const representation = requireString(file, data, "representation");
|
|
182
|
+
if (representation !== "json" && representation !== "instance") {
|
|
183
|
+
throw new ValueTypeEntryError(file, `'representation' must be 'json' or 'instance'`);
|
|
184
|
+
}
|
|
185
|
+
if (data.live !== undefined && typeof data.live !== "boolean") {
|
|
186
|
+
throw new ValueTypeEntryError(file, "'live' must be a boolean when present");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// The two representations take disjoint parameters, and mixing them is a
|
|
190
|
+
// statement with no meaning: a `json` value has no constructor to assert, and
|
|
191
|
+
// an `instance` has no JSON base to refine.
|
|
192
|
+
if (representation === "json") {
|
|
193
|
+
if (data.binding !== undefined) {
|
|
194
|
+
throw new ValueTypeEntryError(file, "a 'json' representation takes no 'binding'");
|
|
195
|
+
}
|
|
196
|
+
const base = requireString(file, data, "base");
|
|
197
|
+
if (!(base in CEL_TYPE_FOR_BASE)) {
|
|
198
|
+
throw new ValueTypeEntryError(
|
|
199
|
+
file,
|
|
200
|
+
`'base' '${base}' is not a JSON Schema type (${Object.keys(CEL_TYPE_FOR_BASE).join(", ")})`,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
if (data.live === true) {
|
|
204
|
+
throw new ValueTypeEntryError(file, "a 'json' representation cannot be 'live' — it is data");
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
if (data.base !== undefined) {
|
|
208
|
+
throw new ValueTypeEntryError(file, "an 'instance' representation takes no 'base'");
|
|
209
|
+
}
|
|
210
|
+
requireString(file, data, "binding");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const entry: ValueTypeEntry = {
|
|
214
|
+
name,
|
|
215
|
+
representation,
|
|
216
|
+
...(representation === "json" ? { base: data.base as string } : { binding: data.binding as string }),
|
|
217
|
+
live: data.live === true,
|
|
218
|
+
parameters: readParameters(file, data.parameters),
|
|
219
|
+
description: requireString(file, data, "description"),
|
|
220
|
+
};
|
|
221
|
+
return entry;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function buildRegistry(): ReadonlyMap<string, ValueTypeEntry> {
|
|
225
|
+
const registry = new Map<string, ValueTypeEntry>();
|
|
226
|
+
for (const [file, data] of VALUE_TYPE_ENTRY_FILES) {
|
|
227
|
+
const entry = parseValueTypeEntry(file, data);
|
|
228
|
+
if (registry.has(entry.name)) {
|
|
229
|
+
throw new ValueTypeEntryError(file, `'${entry.name}' is already declared by another entry`);
|
|
230
|
+
}
|
|
231
|
+
// A binding with no row in THIS host's table is a hard error, never a
|
|
232
|
+
// skipped assertion: a type that cannot be asserted would silently exempt
|
|
233
|
+
// every slot declaring it, converting a contract into a hole. The same class
|
|
234
|
+
// of failure as an unrecognized `use` token degrading to the legacy reading.
|
|
235
|
+
if (entry.binding !== undefined && !(entry.binding in VALUE_TYPE_BINDINGS)) {
|
|
236
|
+
throw new ValueTypeEntryError(
|
|
237
|
+
file,
|
|
238
|
+
`binding '${entry.binding}' has no row in this runtime's table — a value type ` +
|
|
239
|
+
`whose assertion cannot be produced would silently exempt every slot that declares it`,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
registry.set(entry.name, entry);
|
|
243
|
+
}
|
|
244
|
+
// Defence in depth against the packaging mistake, because the failure it
|
|
245
|
+
// produces is indistinguishable from an author's typo: every `x-telo-type`
|
|
246
|
+
// becomes an unknown name, reported against manifests that are correct. The
|
|
247
|
+
// build script refuses a missing source directory; this refuses the state that
|
|
248
|
+
// would reach a user if some other path ever produced it.
|
|
249
|
+
if (registry.size === 0) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
"The value-type vocabulary is empty. `sdk/value-types/*.json` did not reach this " +
|
|
252
|
+
"build — check the file allowlist of whatever packaged it. Continuing would report " +
|
|
253
|
+
"every declared value type as an unknown name.",
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
return registry;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Every declared value type, keyed by its `Telo.`-qualified name. */
|
|
260
|
+
export const VALUE_TYPES: ReadonlyMap<string, ValueTypeEntry> = buildRegistry();
|
|
261
|
+
|
|
262
|
+
/** The declared names, in entry order — what `telo cel types` and the generated
|
|
263
|
+
* docs section enumerate. */
|
|
264
|
+
export function valueTypeNames(): string[] {
|
|
265
|
+
return [...VALUE_TYPES.keys()];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** A read `x-telo-type` annotation: the type it names plus its type arguments. */
|
|
269
|
+
export interface ValueTypeSlot {
|
|
270
|
+
/** The name exactly as written, which is also the canonical one — the
|
|
271
|
+
* vocabulary is closed, so there is nothing to resolve. */
|
|
272
|
+
readonly name: string;
|
|
273
|
+
/** The registry entry, or undefined when the name is not a declared type.
|
|
274
|
+
* Present separately from `name` so a diagnostic can report the name the
|
|
275
|
+
* author wrote rather than swallowing an unknown one. */
|
|
276
|
+
readonly entry: ValueTypeEntry | undefined;
|
|
277
|
+
/** Type arguments by parameter name. Each value is a schema node. */
|
|
278
|
+
readonly args: Readonly<Record<string, unknown>>;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Read the annotation off a schema node.
|
|
283
|
+
*
|
|
284
|
+
* Two spellings, one meaning: a bare name (`x-telo-type: Telo.Bytes`) and the
|
|
285
|
+
* object form carrying arguments (`{ name: Telo.Stream, of: … }`). Returns
|
|
286
|
+
* undefined when the node carries no annotation at all — an unknown NAME still
|
|
287
|
+
* returns a slot, with `entry` undefined, because silently reading it as "no
|
|
288
|
+
* value type" is the degrade this annotation replaced.
|
|
289
|
+
*/
|
|
290
|
+
export function readValueTypeSlot(schema: unknown): ValueTypeSlot | undefined {
|
|
291
|
+
if (!isPlainObject(schema)) return undefined;
|
|
292
|
+
const raw = schema[X_TELO_TYPE];
|
|
293
|
+
if (raw === undefined) return undefined;
|
|
294
|
+
|
|
295
|
+
if (typeof raw === "string") {
|
|
296
|
+
return { name: raw, entry: VALUE_TYPES.get(raw), args: {} };
|
|
297
|
+
}
|
|
298
|
+
if (isPlainObject(raw)) {
|
|
299
|
+
const name = typeof raw.name === "string" ? raw.name : "";
|
|
300
|
+
const args: Record<string, unknown> = {};
|
|
301
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
302
|
+
if (key === "name") continue;
|
|
303
|
+
// A bare NAME as an argument is sugar for a schema node carrying only that
|
|
304
|
+
// annotation, so `of: Telo.Bytes` and `of: { x-telo-type: Telo.Bytes }`
|
|
305
|
+
// are one thing. Normalized HERE, in the single reader, so no consumer
|
|
306
|
+
// re-derives it — a comparator that saw the string form would compare a
|
|
307
|
+
// string against a schema and quietly conclude nothing.
|
|
308
|
+
args[key] = typeof value === "string" ? { [X_TELO_TYPE]: value } : value;
|
|
309
|
+
}
|
|
310
|
+
return { name, entry: VALUE_TYPES.get(name), args };
|
|
311
|
+
}
|
|
312
|
+
return { name: "", entry: undefined, args: {} };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** The entry a schema node declares, or undefined. The common read. */
|
|
316
|
+
export function valueTypeOf(schema: unknown): ValueTypeEntry | undefined {
|
|
317
|
+
return readValueTypeSlot(schema)?.entry;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** True when this node declares a value type at all (known or not). */
|
|
321
|
+
export function isValueTypeSlot(schema: unknown): boolean {
|
|
322
|
+
return readValueTypeSlot(schema) !== undefined;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** True when the node declares a `live` type, so its value is exempt from
|
|
326
|
+
* validation — never traversed, never asserted. Typing is unaffected. */
|
|
327
|
+
export function isLiveSlot(schema: unknown): boolean {
|
|
328
|
+
return valueTypeOf(schema)?.live === true;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** True when the node declares a type represented as a runtime instance —
|
|
332
|
+
* the values no manifest literal can ever be. */
|
|
333
|
+
export function isInstanceSlot(schema: unknown): boolean {
|
|
334
|
+
return valueTypeOf(schema)?.representation === "instance";
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** The binding row for a schema node's declared type, or undefined when it
|
|
338
|
+
* declares none / declares a `json` one. */
|
|
339
|
+
export function bindingOf(schema: unknown): ValueTypeBinding | undefined {
|
|
340
|
+
const binding = valueTypeOf(schema)?.binding;
|
|
341
|
+
return binding === undefined ? undefined : VALUE_TYPE_BINDINGS[binding];
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** The stand-in for a CEL leaf at this slot, or undefined when the slot declares
|
|
345
|
+
* no instance type (ordinary JSON, so the schema's own shape decides) or a live
|
|
346
|
+
* one (nothing validates it, so nothing has to satisfy anything). */
|
|
347
|
+
export function valueTypePlaceholder(schema: unknown): unknown | undefined {
|
|
348
|
+
return bindingOf(schema)?.placeholder?.();
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* The CEL type a value at this slot carries.
|
|
353
|
+
*
|
|
354
|
+
* A `json` representation carries its own NAME as a nominal brand — which is the
|
|
355
|
+
* whole point of one, since a `Telo.TcpPort` and a `Telo.UdpPort` are structurally
|
|
356
|
+
* identical. An `instance` carries whatever its binding says.
|
|
357
|
+
*/
|
|
358
|
+
export function celTypeOfValueType(entry: ValueTypeEntry): string {
|
|
359
|
+
if (entry.representation === "json") return entry.name;
|
|
360
|
+
const binding = VALUE_TYPE_BINDINGS[entry.binding!];
|
|
361
|
+
return binding!.celType;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** The CEL type a brand degrades to where the consuming slot declares none —
|
|
365
|
+
* gradual typing, so a `Telo.TcpPort` flows freely into a plain integer field.
|
|
366
|
+
* Undefined for an `instance`, which has no base to fall back to. */
|
|
367
|
+
export function celBaseOfValueType(entry: ValueTypeEntry): string | undefined {
|
|
368
|
+
return entry.representation === "json" ? CEL_TYPE_FOR_BASE[entry.base!] : undefined;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/** Every `json` representation's CEL brand → the base type it refines. The
|
|
372
|
+
* gradual-typing table, derived rather than hand-written. */
|
|
373
|
+
export function valueBrandBases(): Record<string, string> {
|
|
374
|
+
const out: Record<string, string> = {};
|
|
375
|
+
for (const entry of VALUE_TYPES.values()) {
|
|
376
|
+
const base = celBaseOfValueType(entry);
|
|
377
|
+
if (base !== undefined) out[entry.name] = base;
|
|
378
|
+
}
|
|
379
|
+
return out;
|
|
380
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// GENERATED by scripts/copy-value-type-entries.mjs — do not edit, and do not commit.
|
|
2
|
+
// Source: sdk/value-types/*.json (lexically ordered).
|
|
3
|
+
import e0 from "./telo-bytes.json" with { type: "json" };
|
|
4
|
+
import e1 from "./telo-stream.json" with { type: "json" };
|
|
5
|
+
import e2 from "./telo-tcp-port.json" with { type: "json" };
|
|
6
|
+
import e3 from "./telo-udp-port.json" with { type: "json" };
|
|
7
|
+
|
|
8
|
+
/** Every value-type entry file, in the order the registry reads them. */
|
|
9
|
+
export const VALUE_TYPE_ENTRY_FILES: ReadonlyArray<readonly [file: string, data: unknown]> = [
|
|
10
|
+
["telo-bytes.json", e0],
|
|
11
|
+
["telo-stream.json", e1],
|
|
12
|
+
["telo-tcp-port.json", e2],
|
|
13
|
+
["telo-udp-port.json", e3],
|
|
14
|
+
];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Bytes are not expressible in JSON Schema's type vocabulary. `type: object` is satisfied by every object, so a mistyped literal reached the controller instead of failing check; `type: binary` is not an option, since a validator refuses to COMPILE an unknown type and a published telo.yaml would stop being JSON Schema for the hub, the editor and every third-party reader. Declaring the representation instead is what makes the check fall out: no YAML literal is ever a byte buffer, so a literal at a byte slot is rejected statically and a value arriving by reference passes.",
|
|
3
|
+
"name": "Telo.Bytes",
|
|
4
|
+
"representation": "instance",
|
|
5
|
+
"binding": "bytes",
|
|
6
|
+
"description": "Raw bytes. Never authorable inline — a byte slot is filled by reference (an `!include-bytes` embed, a resource output, a CEL expression), and the runtime asserts the value really is a byte buffer."
|
|
7
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "`live` is what separates a stream from bytes: consuming it has effects, so it is EXEMPT from validation rather than asserted — iterating a stream to check its elements is precisely what the exemption forbids. The exemption is from VALIDATION, never from TYPING: `of` still travels through every schema-typing walk, because that is where the argument check reads it.",
|
|
3
|
+
"name": "Telo.Stream",
|
|
4
|
+
"representation": "instance",
|
|
5
|
+
"binding": "stream",
|
|
6
|
+
"live": true,
|
|
7
|
+
"parameters": [
|
|
8
|
+
{
|
|
9
|
+
"name": "of",
|
|
10
|
+
"description": "The element the stream yields. Any schema node — an inline shape, a value type, a `!ref` to a named shape, or another parameterized type. Omitted means any element."
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"description": "A live handle over a sequence of values, consumed by reading. Its elements are never buffered or validated, and member access past it is rejected — a consumer iterates it instead."
|
|
14
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "A `json` representation adds nominal identity to a value the declared schema already validates. A TcpPort and a UdpPort are both integers, so nothing structural tells them apart — the name is the whole difference, and it is what makes wiring one into the other's slot a static error.",
|
|
3
|
+
"name": "Telo.TcpPort",
|
|
4
|
+
"representation": "json",
|
|
5
|
+
"base": "integer",
|
|
6
|
+
"description": "A TCP port number. Distinct from a UDP port even though both are integers, so wiring one into the other's slot is a static error."
|
|
7
|
+
}
|