@telorun/starlark 0.4.1 → 0.12.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # @telorun/starlark
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2ee3598: A resource's declared invocation contract is now enforced.
8
+
9
+ `inputType` / `outputType` were declared all over the standard library and read almost nowhere: a declared `default:` never applied, a misspelled input, a wrong-typed value and a misread result field were all silent, and three modules of roughly forty validated anything at all — each re-deciding it for itself. Underneath was a naming collision: `inputs:` meant _a schema_ on the six `modules/run` kinds and _values_ at every call site, so the run kinds' whole declared contract was inert.
10
+
11
+ One invariant now holds everywhere: **`inputs`/`outputs` are values; `inputType`/`outputType` are schemas.** The normative rules are in `kernel/specs/invocation-contract.md`.
12
+
13
+ **Resolution.** A shared resolver answers "what is this target's contract" for both halves — `telo check` and the runtime — layering the instance's own declaration over the kind's. A kind's contract resolves to the **nearest declaration along `extends`** and **replaces** rather than merges: config and observed state merge because construction and reported state are additive, but a call signature is not, and merging a child's required inputs into its parent's yields a union no caller can satisfy. This also fixes multi-level chains, which previously resolved to nothing.
14
+
15
+ A child that inherits its controller and declares its own contract must bridge it — `inputType` needs `inputs:`, `outputType` needs `result:` — because the inherited controller understands only the shape it was written for. That combination used to pass `telo check`, run, exit 0 and do nothing at all.
16
+
17
+ **Enforcement.** The kernel binds the resolved contract to the instance at creation, so **an instance is never observable unbound**. Enforcing at a handoff would have meant enforcing at every handoff — injection, explicit resolution, scope handles, template dispatch, the chokepoint — and the dominant path is injection: a consumer reads the reference off its own config and invokes it directly. `invoke` and `provide` are bound; `run()` is parameterless and void, so a resource whose contract requires inputs is rejected statically at a run site. Defaults are filled and both directions validated on every call, skipping `x-telo-stream` properties in **both** directions.
18
+
19
+ Violations raise the ambient `ERR_INPUT_INVALID` / `ERR_OUTPUT_INVALID` as structured errors: catchable and typo-checked by name in a `catches:` block, never counted toward a kind's own declared union. `JS.Script`, `Starlark.Script` and `Run.Choice` drop their controller-side validators, and `Run.Choice`'s duplicate `ERR_OUTPUT_INVALID` declaration goes with them; `Image.Blank`, `Image.Overlay`, `Pdf.Rasterizer` and `Pdf.FormFields` drop the input guards that duplicated their own declared bounds, keeping only what a schema cannot express (a CSS colour's validity, a PDF's decodability, a page against the document's real page count).
20
+
21
+ **`Telo.JsonSchema` is now a kernel built-in.** Declaring a data shape stopped being optional once any kind can carry a contract: writing `inputType:` should not require an import, and a library declaring its own contract should not import a module purely to describe itself — the same reasoning that keeps the mandatory log sinks in the kernel. `modules/type` is deprecated and unchanged, so `Type.JsonSchema` and every published manifest importing it keep working; the standard library, examples and templates now write the built-in and have dropped the import.
22
+
23
+ The six `modules/run` kinds declare `inputType:` / `outputType:` in place of the `inputs:` property map, and each result slot is annotated `x-telo-value-schema-from: outputType` so every branch is checked statically — including ones no input selects. Consumers in this repo are migrated; a manifest pinning a published `run` by digest keeps the old spelling and keeps working.
24
+
25
+ A `Telo.JsonSchema` rule's declared `code:` now reaches a `catch` block. Rules raise an error that carries a code but not the marker a catch matches on, so `error.code` was the generic plain-failure code and every `catches:` entry naming a rule code silently never matched — the behaviour `modules/type` has always documented was never the behaviour. Contract enforcement re-raises a rule failure structurally, preserving the author's code; only a structural schema failure becomes the ambient contract code.
26
+
27
+ CEL evaluates an integer literal to a BigInt, which a JSON Schema validator does not accept as `integer` — so with enforcement on, every computed integer reaching a declared integer input would have been rejected for a reason no author could act on. Validation runs against a view where those read as ordinary numbers while the callee still receives the original values, and `Assert.Equals` no longer throws while rendering one.
28
+
29
+ `Type.JsonSchema` is now a controller-less `extends: Telo.JsonSchema` alias rather than a second copy of the same controller, so the deprecated module has no implementation left to drift.
30
+
31
+ **Statically**, `telo check` now validates each call site's `inputs:` against the target's contract, rejects an unmapped contract replacement, rejects a contract-requiring resource wired where the caller cannot supply arguments, and rejects a contract naming a type that is not declared in scope — which the runtime would otherwise only discover at the first dispatch through it, and which was invisible on a KIND because `Telo.Definition` is excluded from ordinary reference validation.
32
+
33
+ Several latent bugs surfaced and are fixed. A wrapped `invoke` dropped every argument after the first, discarding the InvokeContext — so a detached body never observed cancellation and anything holding a resource across it was never released. `base:` validation demanded `metadata` and then rejected it, making any `base:` child of `JS.Script` impossible. The canonical type-schema id carried a URI authority (`telo://<module>/<Type>`), which no JSON Schema validator can resolve, so a `$ref`-based contract was uncompilable; the internal id is now authority-free, with the authored form unchanged.
34
+
35
+ CEL placeholder substitution — how the checker avoids judging a value it cannot know — gained the same discipline: bounds it ignored (`exclusiveMinimum`, `minLength`, `minItems`, `required`, `allOf`-inherited constraints) are now honoured, a `oneOf` / `anyOf` is resolved to the single branch a value is written against so leaves underneath are typed rather than nulled, and a finding located AT a substituted path is dropped outright — a `pattern`-constrained string cannot be satisfied by any stand-in, so the checker declines to judge it. Structural findings survive, because they are located at the container.
36
+
3
37
  ## 0.4.1
4
38
 
5
39
  ### Patch Changes
package/README.md CHANGED
@@ -23,9 +23,9 @@ Built to be language-agnostic and infinitely extensible.
23
23
 
24
24
  ```bash
25
25
  # Reconcile your manifest into a running backend
26
- $ telo ./examples/hello-api.yaml
26
+ $ telo ./examples/todo-app
27
27
 
28
- {"level":30,"time":1771610393008,"pid":1310178,"hostname":"dev","msg":"Server listening at http://127.0.0.1:8844"}
28
+ {"level":30,"time":1771610393008,"pid":1310178,"hostname":"dev","msg":"Server listening at http://127.0.0.1:8077"}
29
29
  ```
30
30
 
31
31
  ## Why use Telo?
@@ -44,165 +44,13 @@ $ telo ./examples/hello-api.yaml
44
44
 
45
45
  ## Example manifest
46
46
 
47
- Here is an example Telo application that defines a simple HTTP API:
48
-
49
- ```yaml
50
- kind: Telo.Application
51
- metadata:
52
- name: feedback
53
- version: 1.0.0
54
- description: |
55
- A complete feedback collection REST API — no code, pure YAML.
56
- Persists entries to SQLite and serves them over HTTP.
57
- targets:
58
- - Migrations
59
- - Server
60
- ---
61
- kind: Telo.Import
62
- metadata:
63
- name: Http
64
- source: std/http-server@0.5.0
65
- ---
66
- kind: Telo.Import
67
- metadata:
68
- name: Sql
69
- source: std/sql@0.3.0
70
- ---
71
- # SQLite database — swap driver/host/database for PostgreSQL with zero YAML changes
72
- kind: Sql.Connection
73
- metadata:
74
- name: Db
75
- driver: sqlite
76
- file: ./tmp/feedback.db
77
- ---
78
- # Migrations: applied automatically before the server starts
79
- kind: Sql.Migrations
80
- metadata:
81
- name: Migrations
82
- connection:
83
- kind: Sql.Connection
84
- name: Db
85
- ---
86
- kind: Sql.Migration
87
- metadata:
88
- name: Migration_20260413_182154_CreateFeedback
89
- version: 20260413_182154_CreateFeedback
90
- sql: |
91
- CREATE TABLE IF NOT EXISTS feedback (
92
- id INTEGER PRIMARY KEY AUTOINCREMENT,
93
- text TEXT NOT NULL,
94
- source TEXT,
95
- score INTEGER NOT NULL DEFAULT 0,
96
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP
97
- )
98
- ---
99
- kind: Http.Server
100
- metadata:
101
- name: Server
102
- baseUrl: http://localhost:8844
103
- port: 8844
104
- logger: true
105
- openapi:
106
- info:
107
- title: Feedback API
108
- version: 1.0.0
109
- mounts:
110
- - path: /v1
111
- type: Http.Api.FeedbackRoutes
112
- ---
113
- kind: Http.Api
114
- metadata:
115
- name: FeedbackRoutes
116
- routes:
117
- # POST /v1/feedback — insert a new entry, score derived from body length heuristic
118
- - request:
119
- path: /feedback
120
- method: POST
121
- schema:
122
- body:
123
- type: object
124
- properties:
125
- text:
126
- type: string
127
- minLength: 1
128
- source:
129
- type: string
130
- required: [text]
131
- handler:
132
- kind: Sql.Exec
133
- connection:
134
- kind: Sql.Connection
135
- name: Db
136
- inputs:
137
- sql: "INSERT INTO feedback (text, source, score) VALUES (?, ?, ?)"
138
- bindings:
139
- - "${{ request.body.text }}"
140
- - "${{ request.body.source }}"
141
- - "${{ size(request.body.text) }}"
142
- response:
143
- - status: 201
144
- headers:
145
- Content-Type: application/json
146
- body:
147
- ok: true
148
- message: Feedback received
149
-
150
- # GET /v1/feedback — list all entries, newest first
151
- - request:
152
- path: /feedback
153
- method: GET
154
- handler:
155
- kind: Sql.Select
156
- connection:
157
- kind: Sql.Connection
158
- name: Db
159
- from: feedback
160
- columns: [id, text, source, score, created_at]
161
- orderBy:
162
- - { column: created_at, direction: desc }
163
- response:
164
- - status: 200
165
- headers:
166
- Content-Type: application/json
167
- body: "${{ result.rows }}"
168
-
169
- # GET /v1/feedback/{id} — fetch a single entry
170
- - request:
171
- path: /feedback/{id}
172
- method: GET
173
- schema:
174
- params:
175
- type: object
176
- properties:
177
- id:
178
- type: integer
179
- required: [id]
180
- handler:
181
- kind: Sql.Select
182
- connection:
183
- kind: Sql.Connection
184
- name: Db
185
- from: feedback
186
- columns: [id, text, source, score, created_at]
187
- where:
188
- - { column: id, op: "=", value: "${{ request.params.id }}" }
189
- response:
190
- - status: 200
191
- when: "size(result.rows) > 0"
192
- headers:
193
- Content-Type: application/json
194
- body: "${{ result.rows[0] }}"
195
- - status: 404
196
- headers:
197
- Content-Type: application/json
198
- body:
199
- ok: false
200
- message: Not found
201
- ```
47
+ See [examples/](./examples/) for a list of working applications.
202
48
 
203
49
  ## Status
204
50
 
205
- Telo is under **active development**. The core runtime, module system, and standard library are functional, but the API surface — including YAML shapes — may change without notice. Not yet recommended for production use.
51
+ Telo is under heavy development. While it is pre-1.0, breaking changes ship in **minor** releases — manifest shapes, kind schemas, and APIs can change between versions. Pin your imports and expect to update manifests when you upgrade. The core runtime, module system, and standard library are functional, but Telo is not yet recommended for production use.
52
+
53
+ **1.0 lands when the Rust kernel reaches feature parity with the Node.js implementation** — that is the milestone that freezes the manifest contract across runtimes.
206
54
 
207
55
  ## The Meaning of Telo
208
56
 
package/dist/script.js CHANGED
@@ -41,24 +41,22 @@ export async function register(ctx) {
41
41
  initialized = true;
42
42
  }
43
43
  }
44
+ /** `inputType` / `outputType` are declared but never read here: the kernel binds
45
+ * the resolved contract to this instance at creation and validates both
46
+ * directions around `invoke()`. Validating again would double-check every call
47
+ * and let this controller's wording pre-empt the kernel's, which names the
48
+ * target and the side that supplied the bad value. */
44
49
  class StarlarkScript {
45
50
  ctx;
46
51
  manifest;
47
52
  code;
48
- inputValidator;
49
- outputValidator;
50
53
  constructor(ctx, manifest) {
51
54
  this.ctx = ctx;
52
55
  this.manifest = manifest;
53
56
  this.code = manifest.code || "";
54
- this.inputValidator = ctx.createTypeValidator(manifest.inputType);
55
- this.outputValidator = ctx.createTypeValidator(manifest.outputType);
56
57
  }
57
58
  async invoke(input) {
58
- this.inputValidator.validate(input);
59
- const result = await executeStarlark(this.code, input);
60
- this.outputValidator.validate(result);
61
- return result;
59
+ return executeStarlark(this.code, input);
62
60
  }
63
61
  }
64
62
  export async function create(resource, ctx) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/starlark",
3
- "version": "0.4.1",
3
+ "version": "0.12.1",
4
4
  "description": "Telo Starlark module - Starlark script execution for Telo manifests.",
5
5
  "keywords": [
6
6
  "telo",
@@ -33,7 +33,7 @@
33
33
  "devDependencies": {
34
34
  "@types/node": "^20.0.0",
35
35
  "typescript": "^5.0.0",
36
- "@telorun/sdk": "0.16.0"
36
+ "@telorun/sdk": "0.75.0"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@telorun/sdk": "*"
package/src/script.ts CHANGED
@@ -61,25 +61,23 @@ export async function register(ctx: ControllerContext): Promise<void> {
61
61
  }
62
62
  }
63
63
 
64
+ /** `inputType` / `outputType` are declared but never read here: the kernel binds
65
+ * the resolved contract to this instance at creation and validates both
66
+ * directions around `invoke()`. Validating again would double-check every call
67
+ * and let this controller's wording pre-empt the kernel's, which names the
68
+ * target and the side that supplied the bad value. */
64
69
  class StarlarkScript implements ResourceInstance {
65
70
  private code: string;
66
- private inputValidator: DataValidator;
67
- private outputValidator: DataValidator;
68
71
 
69
72
  constructor(
70
73
  readonly ctx: ResourceContext,
71
74
  readonly manifest: ResourceManifest,
72
75
  ) {
73
76
  this.code = manifest.code || "";
74
- this.inputValidator = ctx.createTypeValidator(manifest.inputType);
75
- this.outputValidator = ctx.createTypeValidator(manifest.outputType);
76
77
  }
77
78
 
78
79
  async invoke(input: Record<string, any>): Promise<any> {
79
- this.inputValidator.validate(input);
80
- const result = await executeStarlark(this.code, input);
81
- this.outputValidator.validate(result);
82
- return result;
80
+ return executeStarlark(this.code, input);
83
81
  }
84
82
  }
85
83