@velarscript/cli 0.11.1 → 0.12.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.
Files changed (74) hide show
  1. package/README.md +15 -10
  2. package/dist/cli.js +138 -40
  3. package/dist/cli.js.map +1 -1
  4. package/dist/deployment-verifier.d.ts.map +1 -1
  5. package/dist/deployment-verifier.js +1 -3
  6. package/dist/deployment-verifier.js.map +1 -1
  7. package/dist/extension-metadata.d.ts.map +1 -1
  8. package/dist/extension-metadata.js +1 -0
  9. package/dist/extension-metadata.js.map +1 -1
  10. package/dist/language-server-tool.d.ts +0 -1
  11. package/dist/language-server-tool.d.ts.map +1 -1
  12. package/dist/language-server-tool.js +0 -4
  13. package/dist/language-server-tool.js.map +1 -1
  14. package/dist/node-application.d.ts +13 -0
  15. package/dist/node-application.d.ts.map +1 -0
  16. package/dist/node-application.js +276 -0
  17. package/dist/node-application.js.map +1 -0
  18. package/dist/official-language-server-extensions.d.ts.map +1 -1
  19. package/dist/official-language-server-extensions.js +13 -1
  20. package/dist/official-language-server-extensions.js.map +1 -1
  21. package/dist/production-build.d.ts +1 -1
  22. package/dist/production-build.d.ts.map +1 -1
  23. package/dist/production-build.js +0 -2
  24. package/dist/production-build.js.map +1 -1
  25. package/dist/production-verifier.js +1 -17
  26. package/dist/production-verifier.js.map +1 -1
  27. package/dist/project-format.d.ts +1 -1
  28. package/dist/project-format.d.ts.map +1 -1
  29. package/dist/project-format.js +1 -1
  30. package/dist/project-format.js.map +1 -1
  31. package/dist/project.d.ts +3 -0
  32. package/dist/project.d.ts.map +1 -1
  33. package/dist/project.js +64 -0
  34. package/dist/project.js.map +1 -1
  35. package/dist/standard-modules.d.ts +3 -12
  36. package/dist/standard-modules.d.ts.map +1 -1
  37. package/dist/standard-modules.js +13 -3045
  38. package/dist/standard-modules.js.map +1 -1
  39. package/dist/static-deployment.d.ts +1 -8
  40. package/dist/static-deployment.d.ts.map +1 -1
  41. package/dist/static-deployment.js +3 -28
  42. package/dist/static-deployment.js.map +1 -1
  43. package/dist/test-output.d.ts +1 -1
  44. package/dist/test-output.d.ts.map +1 -1
  45. package/dist/test-output.js.map +1 -1
  46. package/dist/version.d.ts +2 -2
  47. package/dist/version.d.ts.map +1 -1
  48. package/dist/version.js +2 -2
  49. package/dist/version.js.map +1 -1
  50. package/package.json +9 -11
  51. package/skill/ai-skill-desktop.md +84 -0
  52. package/skill/ai-skill-node.md +287 -0
  53. package/skill/ai-skill-web.md +150 -0
  54. package/skill/ai-skill.md +36 -263
  55. package/dist/build-engine-tool.d.ts +0 -3
  56. package/dist/build-engine-tool.d.ts.map +0 -1
  57. package/dist/build-engine-tool.js +0 -34
  58. package/dist/build-engine-tool.js.map +0 -1
  59. package/dist/packaged-official-tool.d.ts +0 -8
  60. package/dist/packaged-official-tool.d.ts.map +0 -1
  61. package/dist/packaged-official-tool.js +0 -32
  62. package/dist/packaged-official-tool.js.map +0 -1
  63. package/dist/project-task-bundle-entry.d.ts +0 -2
  64. package/dist/project-task-bundle-entry.d.ts.map +0 -1
  65. package/dist/project-task-bundle-entry.js +0 -18
  66. package/dist/project-task-bundle-entry.js.map +0 -1
  67. package/dist/project-task-invocation.d.ts +0 -16
  68. package/dist/project-task-invocation.d.ts.map +0 -1
  69. package/dist/project-task-invocation.js +0 -72
  70. package/dist/project-task-invocation.js.map +0 -1
  71. package/dist/project-task-tool.d.ts +0 -3
  72. package/dist/project-task-tool.d.ts.map +0 -1
  73. package/dist/project-task-tool.js +0 -61
  74. package/dist/project-task-tool.js.map +0 -1
package/skill/ai-skill.md CHANGED
@@ -1,16 +1,17 @@
1
- # The VelarScript AI skill brief
1
+ # The VelarScript Core AI skill brief
2
2
 
3
3
  This is the language brief for AI agents working in a VelarScript project. It
4
- is agent-agnostic markdown, it ships inside the toolchain, and `velar skill`
4
+ is agent-agnostic markdown, it ships inside the toolchain, and `velar skill core`
5
5
  prints it verbatim — no network, no external documentation. Every code fence
6
6
  in this file compiles against the current compiler; the brief cannot drift
7
- from the language.
7
+ from the language. Projects using a framework also load the separate `web`,
8
+ `node`, or `desktop` brief named by their `AGENTS.md`.
8
9
 
9
10
  ## What VelarScript is
10
11
 
11
- VelarScript (Vel) is an extensible programming language for the AI era where the
12
- framework is the language: `component`, `state`, and `look` are keywords rather
13
- than imports, so one checked language covers markup, styling, state, and tests.
12
+ VelarScript (Vel) is an extensible programming language for the AI era. Core
13
+ owns the general-purpose language; a framework extension may add checked
14
+ syntax without changing Core's grammar or strings.
14
15
  You write the Vel and every later change; the owner reads the result; the
15
16
  compiler guards each change. Vel's parents are JavaScript and Python: ask the
16
17
  mother about behavior — the program runs inside her — and the father about spelling.
@@ -46,23 +47,21 @@ JavaScript global, so there are exactly four and the list is closed:
46
47
  `escapeHtml`, `codePoint`, `fromCodePoint`, `matches`, `findMatch`,
47
48
  `findMatches`, `replaceMatches`, `splitPattern`). A string method is a core
48
49
  operation; `Text.*` is the extension toolbox, and nothing moves between them.
49
- Web visual builders are named imports from `velar/look` there is no `Look`
50
- global in JavaScript, so there is no `Look.` prefix here either. These names
51
- need no import and cannot be shadowed: `const Text = 1` is rejected. `print`,
50
+ These names need no import and cannot be shadowed: `const Text = 1` is rejected. `print`,
52
51
  `str`, `number`, `equals`, and `range` are likewise in the Core prelude —
53
52
  `equals(a, b)` is the one content-comparison spelling. One roster grants and
54
53
  protects these names. Capabilities stay explicit imports. Durations use `ms` or
55
54
  `s`, so write `await Promise.sleep(250ms)`, not a bare number.
56
55
 
57
- Use checked binary/random/task, manifest Workers, pull WebSockets, Node SQLite,
58
- Web binary IndexedDB, and official adapters. A direct `for index in range(...):` is a native counter; range as a value is a List. Use `UInt16Buffer` for 16-bit numeric state,
56
+ Use checked binary, random, task, and official adapter APIs. A direct
57
+ `for index in range(...):` is a native counter; range as a value is a List. Use `UInt16Buffer` for 16-bit numeric state,
59
58
  `UInt8Buffer` for compact data, and bounded `UInt32Builder`/`Float32Builder` values for variable-size numeric output.
60
59
 
61
60
  ## Project setup
62
61
 
63
62
  A VelarScript project is a directory containing a `velar.json` manifest. Let
64
- the toolchain write it — `velar create my-app` scaffolds the Web template, and
65
- `--template node|desktop|docs|library|component` picks another. Each writes
63
+ the toolchain write it — `velar create my-lib --template library` scaffolds a
64
+ Core source library; other templates select their own framework brief. Each writes
66
65
  `velar.json`, a `package.json` whose scripts are the gates, a `src/` tree, a
67
66
  passing test, and an `AGENTS.md`.
68
67
 
@@ -70,7 +69,7 @@ Writing the manifest yourself: `formatVersion` is required, `extensions` may be
70
69
  omitted by a project that loads none, `entry` defaults to `src/main.vel`,
71
70
  `outDir` to `dist`, and `publicDir` to `public`.
72
71
 
73
- A Core project (CLI, library, Node) loads no extensions:
72
+ A Core project (CLI or library) loads no extensions:
74
73
 
75
74
  ```json
76
75
  {
@@ -80,83 +79,14 @@ A Core project (CLI, library, Node) loads no extensions:
80
79
  }
81
80
  ```
82
81
 
83
- A Web project activates the Web extension **by package name**. That one line is
84
- what turns on `component`, JSX, `state`, `computed`, `resource`, `action`,
85
- `watch`, `look`, and `mount`; without it `component` is an unknown declaration
86
- keyword and every JSX token is a parse error:
87
-
88
- ```json
89
- {
90
- "formatVersion": 2,
91
- "entry": "src/main.vel",
92
- "extensions": ["@velarscript/web"]
93
- }
94
- ```
95
-
96
- An extension owns its own manifest key — `"web": {"title": "My App"}` sets the
97
- document title. Anything else in `velar.json` is rejected by name.
98
-
99
- **Files the toolchain owns.** `dist/` is build output, and in a Web build the
100
- toolchain writes `dist/index.html` itself — the title comes from `web.title`, the
101
- favicon from `web.icon` (a `publicDir`-relative `.svg`/`.png`/`.ico` path; the
102
- build fails if no such file exists), the
103
- mount host is `<div id="app"></div>`, and assets are content-hashed. Never author
104
- that file; a `public/index.html` is overwritten by the generated one, while
105
- everything else in `public/` is copied through. `.velar/` is scratch; both belong in `.gitignore`.
82
+ An extension owns its syntax, modules, and manifest keys. Anything outside the
83
+ active owners' closed manifest vocabulary is rejected by name. `dist/` is build
84
+ output and `.velar/` is scratch; both belong in `.gitignore`.
106
85
 
107
86
  **Tests.** `velar test` finds every `*.test.vel` file under the project (skipping
108
87
  `outDir` and `publicDir`) and runs its `test "name":` blocks. The name is a
109
88
  sentence about the code, quoted verbatim by the reporter and unique in its
110
89
  module; the body may `await` directly and needs no `export`. A file that declares no tests is a failure rather than a skip.
111
- `velar test --browser` runs `*.browser.test.vel` in a real browser — and bare
112
- `--browser` is **Chromium only**. Three engines is `--browser=all` (or
113
- `--browser all`), which is what any cross-browser claim costs;
114
- `--browser=firefox` and `--browser=webkit` pick one.
115
-
116
- **Separate the mounted entrypoint from testable code.** A test runs in Node with
117
- no DOM, so a headless test that imports the module calling `mount` fails on
118
- `document`. Keep the entry trivial and put everything worth testing in modules it
119
- imports:
120
-
121
- ```velar fragment
122
- // src/main.vel — the mounted entrypoint; no test imports this file
123
- import {App} from "./app.vel"
124
-
125
- mount(<App />, "#app")
126
- ```
127
-
128
- Components, functions, and types live in `src/app.vel` and its neighbours;
129
- `src/app.test.vel` tests those exports headlessly and
130
- `src/app.browser.test.vel` drives the mounted application.
131
-
132
- **A browser test drives the page; it does not build one.** Its body runs in the
133
- test process against a page already running the production build, so `mount`,
134
- JSX, and `document` are unavailable there — the driving surface is
135
- `velar/web-test`, and selectors are ordinary CSS:
136
-
137
- ```velar fragment
138
- import {expect} from "velar/test"
139
- import {browser, localStorage} from "velar/web-test"
140
-
141
- test "adding a link shows it in the list":
142
- await browser.open("/")
143
- await browser.fill("#title", "Vel")
144
- await browser.click("#add")
145
- await browser.waitForText("[data-item]", "Vel")
146
- expect(await browser.text("[data-count]")).toBe("1")
147
- expect(await localStorage.get("reading")).toBe(`[{"title":"Vel"}]`)
148
- ```
149
-
150
- `browser` also carries `reload`, `press`, `select`, `attribute`, `count`,
151
- `visible`, `waitFor`, `currentPath`, and `viewport`; `localStorage` and
152
- `sessionStorage` read and write the page's raw storage. Any unhandled error —
153
- in the page or in the test process — fails the test.
154
-
155
- `velar/web-test` may be imported **only** from a `*.browser.test.vel` module.
156
- Reaching for it from application code, from a plain `*.test.vel`, through
157
- `import js`, or through a re-export is rejected on the import line — rename the
158
- module or move the browser test into one of its own. Application code that
159
- needs the page reaches it through `velar/browser` instead.
160
90
 
161
91
  ## The traps your reflexes will hit
162
92
 
@@ -173,17 +103,17 @@ them; the first two are the **silent traps** in the list — read them twice.
173
103
  | `interface X:`, `record X:`, `struct X:` | `type X:` — one keyword for record shapes and aliases. |
174
104
  | `items.length` | `items.size` (also on strings, Sets, Maps). |
175
105
  | `items.push(x)` | `items.append(x)`. There is no `splice`/`shift`/`unshift`/mutating `sort`; use `insert`, `pop`, `remove`, `extend`, and the copying `sorted()`/`reversed()`. |
106
+ | `const items = []`, `const tags = Set()` | An empty collection takes its type where it is written: `const items: List<string> = []`. A later `append`/`add`/`set` never types the declaration. A contextual position supplies it too, so `take(Set())` and `return Map()` need no annotation. |
176
107
  | `if value:` truthiness | Conditions accept only `bool`/`bool?`. Test presence explicitly: `if value != null:`. |
177
108
  | `value is null` | `value == null` / `value != null` — `is` tests runtime types, `null` is a value. |
178
109
  | `switch`, or an `if`/`else if` ladder over an enum | `match` with `case _:` as the only fallback. |
179
- | Renaming a binding away from `type`, `json`, `state`, `from`, `match`, `as`, `action`, `resource`, `watch`, `look`, `component` | Don't. Declaration words are contextual: each declares only in its own shape, so `const {type, from} = event` and `const state = "ready"` are ordinary code in Core and Web alike. `enum` and `case` are the exceptions — `enum` is a real VelarScript keyword, `case` is reserved by JavaScript — so neither can be a binding name; both stay fine as record fields, member names, and `match` branches. |
180
- | A component's `mounted:` / `cleanup:` block | `@mounted:` / `@cleanup:`. Lifecycle hooks live in the language's `@` namespace, which is why a component can also declare its own `def mounted()`. |
110
+ | Renaming a binding away from `type`, `json`, `from`, `match`, or `as` | Don't. Declaration words are contextual: each declares only in its own shape, so `const {type, from} = event` is ordinary code. `enum` and `case` are the exceptions — `enum` is a real VelarScript keyword and `case` is reserved by JavaScript — so neither can be a binding name; both stay fine as record fields, member names, and `match` branches. |
111
+ | Treating `@` as a decorator, call, value, or user extension point | `@` has one meaning: `@name` qualifies the name into the closed compiler-owned namespace of the current context. Core or the active syntax-owning compiler extension owns the vocabulary; source cannot declare, import, alias, pass, or construct an `@name`. The class contracts `@dispose:` and `@iterate:` follow this one rule. |
181
112
  | Two statements on one line | One statement per line; there are no semicolons. A line starting with `.` or `?.` continues the previous line, so method chains format normally. |
182
113
  | `count++` | `count += 1` |
183
114
  | `call(name: value)` named argument | `call(name=value)` |
184
115
  | Importing `range` | `range(...)` is a Core prelude function and needs no import. |
185
116
  | `import {sqrt} from "velar/math"` | `Math.sqrt(x)`; `Math.` is permanent. `velar fix` performs the rewrite. |
186
- | `Look.spacing(16px)` | `spacing(16px)` with `import {spacing} from "velar/look"`; the `Look.` prefix is retired. `velar fix` performs the rewrite. |
187
117
  | `"""triple-quoted"""` for a block of text | A layout string: a double quote followed immediately by a newline opens it; a quote back at the opening line's indentation closes it. Backtick strings are real, but always single-line. |
188
118
  | Escaping `\"` through a JSON, HTML, or selector string | Use backticks: `` `{"name":"Nova"}` `` is the same `string` value, with `"` as ordinary text. Prefixes are orthogonal (`` f` ``, `` r` ``, `` rf` ``), and `velar format` picks the delimiter for you (`"` by default, backticks when the text contains `"`), so write whichever is convenient. |
189
119
  | `007`, `.5` | Write `7`, `0.5`. Explicit `0xFF`, `0b1010`, and `0o17` integers are supported; legacy leading-zero octal is not. Group long digits with `_` — `1_000_000`. `Infinity` and `NaN` are not literals: write `1 / 0` and `0 / 0`. |
@@ -191,12 +121,10 @@ them; the first two are the **silent traps** in the list — read them twice.
191
121
  | A line that is only a value — `x == 5`, `items[0]`, `"a note"` | A statement must do something: call, assign, `await`, or `async`. A computed-and-discarded value is a compile error, and a bare string is not a docstring — use `//`. |
192
122
  | A block comment that starts or ends beside code on a multi-line span | `/* */` exists and nests — commenting out a region that already holds a comment works — but a multi-line one takes whole lines: only `/*` on its opening line, only `*/` on its closing line. Within a single line it can sit anywhere: `call(/* why */ value)`. |
193
123
  | `x if cond else y` | `cond ? x : y` |
194
- | `&&`, `\|\|`, `!`, `===`, `var`, `elif`, `None`, `undefined` | `and`, `or`, `not`, `==`, `let`/`const`, `else if`, `null`, `null`. |
124
+ | `&&`, `\|\|`, `!value`, `===`, `var`, `elif`, `None`, `undefined` | `and`, `or`, `not value`, `==`, `let`/`const`, `else if`, `null`, `null`. `!` **after** a value is a different operator: `value!` unwraps `T?` to `T` and raises `AssertionError` when it is absent. `!=` still wins by longest match, so an unwrap before an equality test needs its space — `value! == other`. |
195
125
  | `f"{user}"` or `str(user)` on a record | Text conversion accepts strings, numbers, bools, enums, and `null` only. `print(user)` inspects a value; permanent `Json.stringify(user)` builds data text without an import. |
196
126
  | Calling an async function and moving on | A dropped Promise is a compile error. `await task()` to wait; `async task()` to run it detached. |
197
127
  | `flag or name ?? fallback` | Parenthesize — `??` never shares an unparenthesized chain with `and`/`or`. |
198
- | `onClick={handler}` | `on:click={handler}`; form binding is `bind:value={state}`. |
199
- | Implicit `{props.children}` | Declare it: a `children: WebNode` prop receives the JSX tag body. |
200
128
  | `map[key]` reads | `map.get(key)` returns `T?`. On Lists, `[index]` throws on a bug; `.get(index)` returns `null` when absence is an expected answer. |
201
129
  | `[...text]` or `list(text)` for characters | `text.split("")` — the empty separator splits per Unicode code point. |
202
130
  | `x !== x` or `Number.isNaN(x)` | Number predicates are members: `x.isNaN()`, `x.isFinite()`, `x.isInteger()`. `NaN == NaN` is `true` — equality is SameValueZero. |
@@ -235,11 +163,7 @@ it names a result. They stand next to each other most often on a callback prop
235
163
  type Transform = (value: number) -> number
236
164
 
237
165
  const double: Transform = value => value * 2
238
-
239
- component Stepper(step: number, onChange: (next: number) -> null):
240
- return <button type="button" on:click={() => onChange(step + 1)}>+1</button>
241
-
242
- mount(<Stepper step={double(1)} onChange={next => print(next)} />, "#app")
166
+ print(double(2))
243
167
  ```
244
168
 
245
169
  Parameter names in a function type are optional but worth writing:
@@ -300,7 +224,7 @@ class Session:
300
224
  const session = Session("session-1")
301
225
  ```
302
226
 
303
- `@name` members belong to the language and can never collide with yours.
227
+ `@name` qualifies a compiler-owned contextual name and can never collide with yours.
304
228
  `@dispose:` is the release contract — never called directly — that
305
229
  `using name = expression` runs on every exit from the owning scope (block end,
306
230
  `return`, `break`, `continue`, throw), in reverse declaration order. A derived
@@ -312,69 +236,6 @@ escapes are rejected — return the data you read from it, or move the `using` u
312
236
  to the scope that really owns it. A JavaScript handle is owned by composition:
313
237
  hold it in a field of a class whose `@dispose:` releases it.
314
238
 
315
- Components (Web extension) return JSX directly — there is no `render` block.
316
- `state` holds a fact, `computed name = ...` derives and is read bare, `action`
317
- performs a user operation with reactive `pending`/`error`:
318
-
319
- ```velar
320
- component Counter(label: string):
321
- state count = 0
322
- computed caption = f"{label}: {count}"
323
-
324
- action reset():
325
- count = 0
326
-
327
- def bump():
328
- count += 1
329
-
330
- return <section>
331
- <button type="button" on:click={bump}>{caption}</button>
332
- <button type="button" disabled={reset.pending} on:click={reset}>Reset</button>
333
- </section>
334
-
335
- mount(<Counter label="Clicks" />, "#app")
336
- ```
337
-
338
- `look` is the checked visual language — a value, composed per element with
339
- `look={...}`. CSS keywords are quoted strings; property names are real DOM
340
- camelCase; units are literal:
341
-
342
- ```velar
343
- import {border, rgb, spacing} from "velar/look"
344
-
345
- const buttonLook = look:
346
- border = border(0px, rgb(220, 224, 235))
347
- borderRadius = 10px
348
- padding = spacing(10px, 14px)
349
- cursor = "pointer"
350
-
351
- if @hover:
352
- background = rgb(235, 240, 255)
353
-
354
- component SaveButton(children: WebNode):
355
- return <button look={buttonLook} type="button">{children}</button>
356
- ```
357
-
358
- A `look:` literal is built once, so its conditions and values cannot read state; put a reactive visual on the element with `look={active ? a : b}` or `look:color={...}`. Declare checked motion as a module-level `keyframes:` value and pass it to `animate` from `velar/look`; disable nonessential motion at the CSS layer:
359
-
360
- ```velar
361
- import {animate} from "velar/look"
362
-
363
- const spin = keyframes:
364
- from:
365
- rotate = 0deg
366
- to:
367
- rotate = 1turn
368
-
369
- const rotatingLook = look:
370
- if not motion.reduced:
371
- animation = animate(spin, 1s, easing="linear", loop=true)
372
- ```
373
-
374
- The `animation` property accepts only `Animation`, `List<Animation>`, or `null`; a CSS animation string is rejected. Bind a changing animation on the element with `look:animation={active ? animate(spin, 1s) : null}`. Native animation longhands remain outside Look because `animate` owns the checked contract.
375
-
376
- Form state binds with `bind:value={name}` (also a writable path such as `bind:value={form.email}`), `bind:checked={flag}`, and `bind:group={choice}` — radio state holds the selected input's `value`, checkbox `List<string>` state holds the checked values; the event object has no `target`.
377
-
378
239
  ## The idioms
379
240
 
380
241
  These are the canonical shapes, distilled from the project's best-practices
@@ -399,8 +260,8 @@ def measure(text: string) -> TextMeasure:
399
260
  ### Null discipline
400
261
 
401
262
  One spelling per job: test presence with `!= null`, default with `??`,
402
- reach through maybes with `?.`, and narrow once then use the value
403
- directly:
263
+ reach through maybes with `?.`, unwrap what cannot be absent with `!`, and
264
+ narrow once — then use the value directly:
404
265
 
405
266
  ```velar
406
267
  type Ticket:
@@ -416,6 +277,12 @@ def owner(ticket: Ticket) -> string:
416
277
  return ticket.assignee ?? "unassigned"
417
278
  ```
418
279
 
280
+ `value!` unwraps `T?` to `T` and raises `AssertionError` where the value turns
281
+ out to be absent — it checks, it never merely claims. Use it where absence
282
+ would be a bug and there is nothing more to say about it, such as an index the
283
+ code just populated. When the failure has something to tell a reader, the
284
+ statement form says it: `assert value != null else "..."`.
285
+
419
286
  ### Chains over cursors
420
287
 
421
288
  Collection work is method chains; a hand-advanced index loop is the
@@ -490,11 +357,11 @@ def firstLine(text: string) -> string:
490
357
 
491
358
  Callbacks stay arrows while they are one expression; promote two-statement
492
359
  logic to a named `def`. Name arguments where a bare value would read as a
493
- mystery: `buttonLook(dangerous=true)`, never `buttonLook(true)`.
360
+ mystery: `connect(retry=true)`, never `connect(true)`.
494
361
 
495
362
  ### Strings
496
363
 
497
- Build text with f-strings — numbers, bools, enums, and Web unit values with a declared text form interpolate directly.
364
+ Build text with f-strings — numbers, bools, and enums interpolate directly.
498
365
  Data becomes text through permanent `Json.stringify`. Multi-line text is
499
366
  a layout string, not a stack of `\n` escapes. Text that contains `"` — a JSON
500
367
  fixture, a quoted selector — goes in backticks instead of being escaped:
@@ -516,96 +383,6 @@ print(usage)
516
383
  print(Json.stringify({open: count}))
517
384
  ```
518
385
 
519
- ### Components: four cells, one job each
520
-
521
- `state` holds a fact. `computed` derives from facts. `resource` loads async
522
- data. `action` performs a user operation. Read a resource as
523
- `value != null`; render nothing with `null`; key dynamic children:
524
-
525
- ```velar fragment
526
- component TicketPanel(id: string):
527
- state draft = ""
528
- resource ticket: Ticket = loadTicket(id)
529
- computed heading = ticket.value?.title ?? "Loading"
530
-
531
- action save():
532
- await saveDraft(id, draft)
533
-
534
- watch id:
535
- async ticket.reload()
536
-
537
- return <section>
538
- <h2>{heading}</h2>
539
- <textarea bind:value={draft}></textarea>
540
- <button disabled={save.pending} on:click={save}>Save</button>
541
- </section>
542
- ```
543
-
544
- Lifecycle is two sibling blocks in the language's own `@` namespace —
545
- `@mounted:` runs once after the DOM is inserted and may `await`; `@cleanup:`
546
- runs once before the component is destroyed and is synchronous:
547
-
548
- ```velar fragment
549
- component Chart(points: List<number>):
550
- let canvas: CanvasElement? = null
551
-
552
- @mounted:
553
- if canvas != null:
554
- drawChart(canvas, points)
555
-
556
- @cleanup:
557
- releaseChart()
558
-
559
- return <canvas ref={canvas}></canvas>
560
- ```
561
-
562
- **A resource loads once, at mount, and does not refetch when its inputs
563
- change** — a new `id` prop leaves the old data on screen. "Refetch when the
564
- input changes" is the `watch` above: watch the input, and start `reload()` with
565
- the detached `async` statement, because a watch body is synchronous. `reload()`
566
- re-evaluates the initializer against the current inputs, keeps the last value if
567
- it fails, and puts the failure in `error`. Actions do not queue either: two
568
- clicks run two calls, `pending` means any call is active, so guard with
569
- `disabled={save.pending}`.
570
-
571
- Conditional rendering is an ordinary expression — there are no magic JSX
572
- control-flow attributes:
573
-
574
- ```velar fragment
575
- component Panel:
576
- return <section>
577
- {loading ? <p aria-busy="true">Loading…</p> : <Results items={items} />}
578
- <ul>
579
- {items.map(item => <li key={item.id}>{item.title}</li>)}
580
- </ul>
581
- </section>
582
- ```
583
-
584
- Component props are live reactive inputs and their data is mutable by default.
585
- Writing `item.title = next` through a prop publishes through the same deep
586
- reactive path as writing the source state. Write `items: readonly List<Item>`
587
- when the component author deliberately wants a read-only contract; that
588
- explicit view travels into helpers and nested data without copying or freezing.
589
-
590
- `velar/storage` persists JSON and validates on the way back in, so a read needs
591
- a **named** runtime type — a primitive or generic spelling is a type, not a
592
- value:
593
-
594
- ```velar fragment
595
- import {storage} from "velar/storage"
596
-
597
- type SavedItem:
598
- title: string
599
-
600
- type SavedItems = List<SavedItem>
601
-
602
- const items = storage.get("reading", SavedItems, [])
603
- storage.set("reading", items)
604
- ```
605
-
606
- The third argument is the fallback for missing or invalid data; `session` and
607
- `database(name)` from the same module carry the same shape.
608
-
609
386
  ### Errors and async
610
387
 
611
388
  Throw `Error` (or a subclass) with a message that names the broken rule.
@@ -700,9 +477,7 @@ data and assign the result on the Vel side.
700
477
  `undefined` even answers `false` to `== null`. The import statement is the
701
478
  only correctness boundary — validate with `Type.parse` there, before the
702
479
  value touches typed code.
703
- 3. **Styling beyond Look** `import css unsafe "./file.css" before look`
704
- (or `after look`); trusted markup renders through `unsafe:html`.
705
- 4. **A suspected compiler defect blocking you** — run `velar repro` (below),
480
+ 3. **A suspected compiler defect blocking you** run `velar repro` (below),
706
481
  then take the final exit: `velar build` output is readable, source-mapped
707
482
  JavaScript that runs without the toolchain.
708
483
 
@@ -735,11 +510,9 @@ compatibility promise, changing a word costs nothing yet.
735
510
  ## Where to look up what this brief leaves out
736
511
 
737
512
  The repository carries a **tour** that shows every spelling exactly once, as
738
- compiling projects you can run: `examples/tour/core/` (17 numbered chapters,
739
- values through testing), `examples/tour/web/` (13 components, Look, routing,
740
- both kinds of test), `examples/tour/desktop/` (4). When you are about to guess
741
- at a spelling, open the chapter instead. `examples/app/` is the companion — one
742
- real application, showing how the pieces are put together.
513
+ compiling projects you can run. Core is in `examples/tour/core/`; the framework
514
+ briefs point to their own projects. When you are about to guess at a spelling,
515
+ open the relevant chapter instead.
743
516
 
744
517
  ## The meta-rule
745
518
 
@@ -1,3 +0,0 @@
1
- export declare const VELAR_BUILD_ENGINE_TOOL_ID = "velar-build-engine";
2
- export declare function buildBuildEngineTool(outputFile: string): Promise<void>;
3
- //# sourceMappingURL=build-engine-tool.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"build-engine-tool.d.ts","sourceRoot":"","sources":["../src/build-engine-tool.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,0BAA0B,uBAAuB,CAAC;AAE/D,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB5E"}
@@ -1,34 +0,0 @@
1
- import { access, chmod, copyFile, mkdir, realpath, stat } from "node:fs/promises";
2
- import { constants } from "node:fs";
3
- import { createRequire } from "node:module";
4
- import { dirname, isAbsolute, resolve } from "node:path";
5
- export const VELAR_BUILD_ENGINE_TOOL_ID = "velar-build-engine";
6
- export async function buildBuildEngineTool(outputFile) {
7
- outputFile = resolve(outputFile);
8
- const require = createRequire(import.meta.url);
9
- const packageName = `@esbuild/${process.platform}-${process.arch}`;
10
- let source;
11
- const packagedSource = process.env.ESBUILD_BINARY_PATH;
12
- if (packagedSource !== undefined) {
13
- if (!isAbsolute(packagedSource) || packagedSource.length > 4096 || packagedSource.includes("\0")) {
14
- throw new Error("ESBUILD_BINARY_PATH must identify a bounded absolute official build engine");
15
- }
16
- source = await realpath(packagedSource);
17
- }
18
- else {
19
- try {
20
- source = require.resolve(`${packageName}/bin/esbuild`);
21
- }
22
- catch {
23
- throw new Error(`Official build engine is unavailable for ${process.platform}-${process.arch}`);
24
- }
25
- }
26
- const information = await stat(source);
27
- if (!information.isFile())
28
- throw new Error("Official build engine must be an ordinary file");
29
- await access(source, constants.X_OK);
30
- await mkdir(dirname(outputFile), { recursive: true });
31
- await copyFile(source, outputFile);
32
- await chmod(outputFile, 0o755);
33
- }
34
- //# sourceMappingURL=build-engine-tool.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"build-engine-tool.js","sourceRoot":"","sources":["../src/build-engine-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzD,MAAM,CAAC,MAAM,0BAA0B,GAAG,oBAAoB,CAAC;AAE/D,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,UAAkB;IAC3D,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACjC,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,YAAY,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;IACnE,IAAI,MAAc,CAAC;IACnB,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;IACvD,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,cAAc,CAAC,MAAM,GAAG,IAAI,IAAI,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACjG,MAAM,IAAI,KAAK,CAAC,4EAA4E,CAAC,CAAC;QAChG,CAAC;QACD,MAAM,GAAG,MAAM,QAAQ,CAAC,cAAc,CAAC,CAAC;IAC1C,CAAC;SAAM,CAAC;QACN,IAAI,CAAC;YACH,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,WAAW,cAAc,CAAC,CAAC;QACzD,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,4CAA4C,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QAClG,CAAC;IACH,CAAC;IACD,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IAC7F,MAAM,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,MAAM,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;AACjC,CAAC"}
@@ -1,8 +0,0 @@
1
- /**
2
- * A self-contained Desktop installation rebuilds only the target renderer.
3
- * Its official tools are immutable versioned inputs, so nested packaging
4
- * copies them from the Worker-validated application template instead of
5
- * looking for compiler sources or node_modules outside the package.
6
- */
7
- export declare function copyPackagedOfficialTool(outputFile: string, bundledRelativePath: string, mode: number): Promise<boolean>;
8
- //# sourceMappingURL=packaged-official-tool.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"packaged-official-tool.d.ts","sourceRoot":"","sources":["../src/packaged-official-tool.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,MAAM,EAClB,mBAAmB,EAAE,MAAM,EAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,OAAO,CAAC,CAmBlB"}
@@ -1,32 +0,0 @@
1
- import { chmod, copyFile, mkdir, realpath, stat } from "node:fs/promises";
2
- import { dirname, isAbsolute, join, relative, resolve } from "node:path";
3
- const desktopPackageTemplateEnvironment = "VELAR_DESKTOP_PACKAGE_TEMPLATE_ROOT";
4
- /**
5
- * A self-contained Desktop installation rebuilds only the target renderer.
6
- * Its official tools are immutable versioned inputs, so nested packaging
7
- * copies them from the Worker-validated application template instead of
8
- * looking for compiler sources or node_modules outside the package.
9
- */
10
- export async function copyPackagedOfficialTool(outputFile, bundledRelativePath, mode) {
11
- const value = process.env[desktopPackageTemplateEnvironment];
12
- if (value === undefined)
13
- return false;
14
- if (!isAbsolute(value) || value.length > 4096 || value.includes("\0")) {
15
- throw new Error(`${desktopPackageTemplateEnvironment} must be a bounded absolute Desktop Resources path`);
16
- }
17
- const root = await realpath(value);
18
- const source = await realpath(join(root, bundledRelativePath));
19
- const fromRoot = relative(root, source);
20
- if (fromRoot === "" || fromRoot === ".." || fromRoot.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute(fromRoot)) {
21
- throw new Error("Packaged official tool must stay inside the Desktop Resources template");
22
- }
23
- const information = await stat(source);
24
- if (!information.isFile())
25
- throw new Error("Packaged official tool must be an ordinary file");
26
- outputFile = resolve(outputFile);
27
- await mkdir(dirname(outputFile), { recursive: true });
28
- await copyFile(source, outputFile);
29
- await chmod(outputFile, mode);
30
- return true;
31
- }
32
- //# sourceMappingURL=packaged-official-tool.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"packaged-official-tool.js","sourceRoot":"","sources":["../src/packaged-official-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzE,MAAM,iCAAiC,GAAG,qCAAqC,CAAC;AAEhF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,UAAkB,EAClB,mBAA2B,EAC3B,IAAY;IAEZ,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IAC7D,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,IAAI,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,GAAG,iCAAiC,oDAAoD,CAAC,CAAC;IAC5G,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxC,IAAI,QAAQ,KAAK,EAAE,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1I,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;IAC5F,CAAC;IACD,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IAC9F,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACjC,MAAM,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,MAAM,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAC9B,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=project-task-bundle-entry.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"project-task-bundle-entry.d.ts","sourceRoot":"","sources":["../src/project-task-bundle-entry.ts"],"names":[],"mappings":""}
@@ -1,18 +0,0 @@
1
- import { velarApplicationPackageHost as desktopApplicationPackageHost } from "@velarscript/desktop/package-host";
2
- import { registerBundledApplicationPackageHost } from "./bundled-application-package-host-registry.js";
3
- import { installOfficialLanguageServerExtensions } from "./official-language-server-extensions.js";
4
- import { projectTaskBrowserWorkerCliArguments, projectTaskBrowserWorkerEnvironment, projectTaskCliArguments, } from "./project-task-invocation.js";
5
- registerBundledApplicationPackageHost(desktopApplicationPackageHost);
6
- const cliArguments = process.env[projectTaskBrowserWorkerEnvironment] === undefined
7
- ? projectTaskCliArguments(process.argv.slice(2))
8
- : projectTaskBrowserWorkerCliArguments(process.argv.slice(2), process.env[projectTaskBrowserWorkerEnvironment]);
9
- if (typeof cliArguments === "string") {
10
- process.stderr.write(`velar project task: ${cliArguments}\n`);
11
- process.exitCode = 2;
12
- }
13
- else {
14
- process.argv = [...process.argv.slice(0, 2), ...cliArguments];
15
- installOfficialLanguageServerExtensions();
16
- await import("./cli.js");
17
- }
18
- //# sourceMappingURL=project-task-bundle-entry.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"project-task-bundle-entry.js","sourceRoot":"","sources":["../src/project-task-bundle-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,IAAI,6BAA6B,EAAE,MAAM,mCAAmC,CAAC;AACjH,OAAO,EAAE,qCAAqC,EAAE,MAAM,gDAAgD,CAAC;AACvG,OAAO,EAAE,uCAAuC,EAAE,MAAM,0CAA0C,CAAC;AACnG,OAAO,EACL,oCAAoC,EACpC,mCAAmC,EACnC,uBAAuB,GACxB,MAAM,8BAA8B,CAAC;AAEtC,qCAAqC,CAAC,6BAA6B,CAAC,CAAC;AAErE,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,KAAK,SAAS;IACjF,CAAC,CAAC,uBAAuB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC,CAAC,oCAAoC,CACpC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EACrB,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CACjD,CAAC;AACJ,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;IACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,YAAY,IAAI,CAAC,CAAC;IAC9D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC;KAAM,CAAC;IACN,OAAO,CAAC,IAAI,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,YAAY,CAAC,CAAC;IAC9D,uCAAuC,EAAE,CAAC;IAC1C,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;AAC3B,CAAC"}
@@ -1,16 +0,0 @@
1
- export declare const projectTaskCommands: readonly ["check", "test", "browserTest", "build", "fix", "package", "run"];
2
- export type ProjectTaskCommand = typeof projectTaskCommands[number];
3
- export declare const projectTaskBrowserWorkerEnvironment = "VELAR_BROWSER_TEST_WORKER_V1";
4
- /**
5
- * Translates Desktop's closed task vocabulary into the exact public CLI
6
- * invocation. The host never accepts arbitrary CLI options; adding a task is
7
- * an explicit capability change here and in velar/desktop.
8
- */
9
- export declare function projectTaskCliArguments(arguments_: readonly string[]): readonly string[] | string;
10
- /**
11
- * The browser owner supervises its test body in a second invocation of the
12
- * same immutable tool. Desktop strips this private marker from the initial
13
- * task environment, so only the official supervisor can enter this path.
14
- */
15
- export declare function projectTaskBrowserWorkerCliArguments(arguments_: readonly string[], serializedLimits: string | undefined): readonly string[] | string;
16
- //# sourceMappingURL=project-task-invocation.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"project-task-invocation.d.ts","sourceRoot":"","sources":["../src/project-task-invocation.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,YAC9B,OAAO,EACP,MAAM,EACN,aAAa,EACb,OAAO,EACP,KAAK,EACL,SAAS,EACT,KAAK,CACG,CAAC;AAEX,MAAM,MAAM,kBAAkB,GAAG,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAEpE,eAAO,MAAM,mCAAmC,iCAAiC,CAAC;AAMlF;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,GAAG,MAAM,CAajG;AAED;;;;GAIG;AACH,wBAAgB,oCAAoC,CAClD,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,gBAAgB,EAAE,MAAM,GAAG,SAAS,GACnC,SAAS,MAAM,EAAE,GAAG,MAAM,CA4B5B"}