toilscript 0.1.13 → 0.1.14

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "imports": {
3
- "toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.13/dist/toilscript.js",
4
- "toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.13/dist/cli.js",
3
+ "toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.14/dist/toilscript.js",
4
+ "toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.14/dist/cli.js",
5
5
  "binaryen": "https://cdn.jsdelivr.net/npm/binaryen@129.0.0-nightly.20260428/index.js",
6
6
  "long": "https://cdn.jsdelivr.net/npm/long@5.3.2/index.js"
7
7
  }
package/dist/web.js CHANGED
@@ -1,8 +1,8 @@
1
- var ASSEMBLYSCRIPT_VERSION = "0.1.13";
1
+ var ASSEMBLYSCRIPT_VERSION = "0.1.14";
2
2
  var ASSEMBLYSCRIPT_IMPORTMAP = {
3
3
  "imports": {
4
- "toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.13/dist/toilscript.js",
5
- "toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.13/dist/cli.js",
4
+ "toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.14/dist/toilscript.js",
5
+ "toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.14/dist/cli.js",
6
6
  "binaryen": "https://cdn.jsdelivr.net/npm/binaryen@129.0.0-nightly.20260428/index.js",
7
7
  "long": "https://cdn.jsdelivr.net/npm/long@5.3.2/index.js"
8
8
  }
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "toilscript",
9
9
  "wasm"
10
10
  ],
11
- "version": "0.1.13",
11
+ "version": "0.1.14",
12
12
  "author": "Daniel Wirtz <dcode+assemblyscript@dcode.io>",
13
13
  "license": "Apache-2.0",
14
14
  "homepage": "https://github.com/dacely-cloud/toilscript",
@@ -323,3 +323,54 @@ declare class i256 {
323
323
  isNeg(): bool;
324
324
  isZero(): bool;
325
325
  }
326
+
327
+ /**
328
+ * The dynamic JSON value tree. A `@data` class's `toJSON()` returns one of these, and
329
+ * `JSON.parse(...)` produces one. Globalized by std/assembly/json, so it needs no import.
330
+ */
331
+ declare class JSON {
332
+ /** A JSON null. */
333
+ static nul(): JSON;
334
+ /** An empty JSON object, build it with `.set(key, value)`. */
335
+ static obj(): JSON;
336
+ /** An empty JSON array, build it with `.push(value)`. */
337
+ static arr(): JSON;
338
+ /** Wrap a scalar/string/bool/array as a JSON value. */
339
+ static of<T>(value: T): JSON;
340
+ /** Parse JSON text into a value tree (returns an error value on malformed input). */
341
+ static parse(text: string): JSON;
342
+ /** Serialize a scalar/string/bool/array value to a JSON string. */
343
+ static stringify<T>(value: T): string;
344
+
345
+ /** Append a value to a JSON array; returns `this` for chaining. */
346
+ push(value: JSON): JSON;
347
+ /** Set a key on a JSON object; returns `this` for chaining. */
348
+ set(key: string, value: JSON): JSON;
349
+
350
+ isNull(): bool;
351
+ isBool(): bool;
352
+ isNumber(): bool;
353
+ isString(): bool;
354
+ isArray(): bool;
355
+ isObject(): bool;
356
+
357
+ asBool(): bool;
358
+ asF64(): f64;
359
+ asI64(): i64;
360
+ asU64(): u64;
361
+ asString(): string;
362
+
363
+ /** Element count of an array (or 0). */
364
+ length(): i32;
365
+ /** Element at `index` of an array. */
366
+ at(index: i32): JSON;
367
+ /** Whether an object has `key`. */
368
+ has(key: string): bool;
369
+ /** Value for `key` on an object. */
370
+ get(key: string): JSON;
371
+ /** The keys of an object. */
372
+ objectKeys(): Array<string>;
373
+
374
+ /** Serialize this value tree to a JSON string. */
375
+ toString(): string;
376
+ }
package/std/ts-plugin.cjs CHANGED
@@ -1,26 +1,22 @@
1
1
  /**
2
2
  * ToilScript TypeScript Language Service plugin.
3
3
  *
4
- * Stock TypeScript has no grammar for toil-native decorators applied to
5
- * top-level functions and globals (`@main`, `@inline`, `@unmanaged`, ...), so
6
- * the editor's language service flags them as errors even though the toilscript
7
- * compiler handles them correctly. It also can't see the members the compiler
8
- * injects into a `@data` class, and treats a class/method that is only ever used
9
- * via a toil decorator (`@data`, `@rest`, `@service`, `@remote`, ...) as unused.
10
- * This plugin runs only inside the editor's language service (VS Code, WebStorm,
11
- * etc. - never `tsc`/compiler builds) and removes exactly those false positives:
4
+ * Stock TypeScript can't see what the toilscript compiler does to a source file:
5
+ * the native decorators on top-level functions (`@main`, `@inline`, ...), the
6
+ * members it injects into a `@data` class (`encode`/`decode`/`toJSON`/...), or the
7
+ * fact that a class used only via a toil decorator is actually used. This plugin
8
+ * runs only inside the editor's language service (VS Code, WebStorm, etc. - never
9
+ * `tsc`/compiler builds) and bridges that gap:
12
10
  *
13
- * TS1206 "Decorators are not valid here."
14
- * TS1249 "A decorator can only decorate a method implementation, not an overload."
15
- * TS2339 "Property '<m>' does not exist on type '<T>'." - but ONLY when `<m>` is a
16
- * `@data`-injected member and `<T>` is a `@data` class (a typo on any other
17
- * type still surfaces).
18
- * TS6133 / TS6196 "'<x>' is declared but never used." - but ONLY when `<x>` is a
19
- * class/method/function carrying a toil-native decorator (so the compiler
20
- * uses it); an undecorated unused declaration is still greyed out.
11
+ * 1. Declaration-merges TYPED members onto each `@data` class (so `value.toJSON()`
12
+ * is `JSON`, `Type.decode(bytes)` is the class, etc.) by appending ambient decls
13
+ * to the file the editor sees. Diagnostics in that appended region are hidden.
14
+ * 2. As a fallback (if 1 doesn't apply), still removes the false positives:
15
+ * TS1206 / TS1249 decorator grammar
16
+ * TS2339 a `@data`-injected member accessed on a `@data` class
17
+ * TS6133 / TS6196 a toil-decorated class/method reported as unused
21
18
  *
22
- * Every other diagnostic - unknown types, bad calls, missing names - passes
23
- * through untouched, so real type errors are still surfaced.
19
+ * Every other diagnostic passes through untouched, so real errors still surface.
24
20
  */
25
21
  const DECORATOR_GRAMMAR_CODES = new Set([1206, 1249]);
26
22
  const PROPERTY_NOT_EXIST = 2339;
@@ -98,12 +94,10 @@ function init(modules) {
98
94
  );
99
95
  }
100
96
 
101
- /** Whether `expr` is (an instance of, or the static side of) a `@data` class. */
102
97
  function resolvesToDataClass(expr, checker) {
103
98
  const type = checker.getTypeAtLocation(expr);
104
99
  const typeSym = type && type.getSymbol && type.getSymbol();
105
100
  if (typeSym && declsAreDataClass(typeSym.declarations)) return true;
106
- // Static access like `Player.decode(...)`: resolve the identifier's own symbol.
107
101
  const sym = checker.getSymbolAtLocation(expr);
108
102
  return !!sym && declsAreDataClass(sym.declarations);
109
103
  }
@@ -120,9 +114,66 @@ function init(modules) {
120
114
  );
121
115
  }
122
116
 
117
+ /**
118
+ * Ambient declarations to append so the editor types each `@data` class's injected
119
+ * codec members. Returns "" when the file declares no `@data` class. Uses only
120
+ * editor-visible globals (`Uint8Array`, `JSON`, `u32`), so the appended block is
121
+ * itself error-free; any diagnostics there are filtered anyway.
122
+ */
123
+ function dataAugmentation(text) {
124
+ if (text.indexOf('@data') < 0) return '';
125
+ let sf;
126
+ try {
127
+ sf = ts.createSourceFile('__toil_aug__.ts', text, ts.ScriptTarget.Latest, true);
128
+ } catch {
129
+ return '';
130
+ }
131
+ let out = '';
132
+ sf.forEachChild((node) => {
133
+ if (ts.isClassDeclaration(node) && node.name && declHasDecorator(node, 'data')) {
134
+ const n = node.name.text;
135
+ out +=
136
+ `\n// toilscript: editor types for the compiler-injected @data ${n} codec\n` +
137
+ `interface ${n} { encode(): Uint8Array; toJSON(): JSON; }\n` +
138
+ `declare namespace ${n} { function decode(buf: Uint8Array): ${n}; function fromJSON(v: JSON): ${n}; function dataId(): u32; }\n`;
139
+ }
140
+ });
141
+ return out;
142
+ }
143
+
123
144
  return {
124
145
  create(info) {
125
146
  const ls = info.languageService;
147
+ const host = info.languageServiceHost;
148
+
149
+ // Original text length per augmented file, so diagnostics in the appended region drop.
150
+ const originalLength = new Map();
151
+
152
+ // Inject the typed @data members into the editor's view of each source file.
153
+ if (typeof host.getScriptSnapshot === 'function' && typeof host.getScriptVersion === 'function') {
154
+ const origSnapshot = host.getScriptSnapshot.bind(host);
155
+ const origVersion = host.getScriptVersion.bind(host);
156
+ host.getScriptSnapshot = (fileName) => {
157
+ const snap = origSnapshot(fileName);
158
+ if (!snap) return snap;
159
+ let aug = '';
160
+ try {
161
+ aug = dataAugmentation(snap.getText(0, snap.getLength()));
162
+ } catch {
163
+ aug = '';
164
+ }
165
+ if (!aug) {
166
+ originalLength.delete(fileName);
167
+ return snap;
168
+ }
169
+ const text = snap.getText(0, snap.getLength());
170
+ originalLength.set(fileName, text.length);
171
+ return ts.ScriptSnapshot.fromString(text + aug);
172
+ };
173
+ // A stable suffix so the service re-reads once and picks up the augmentation; it
174
+ // still changes whenever the underlying file changes.
175
+ host.getScriptVersion = (fileName) => origVersion(fileName) + ':toil-data';
176
+ }
126
177
 
127
178
  // Proxy the language service, forwarding everything to the real one.
128
179
  const proxy = Object.create(null);
@@ -131,7 +182,12 @@ function init(modules) {
131
182
  proxy[key] = typeof value === 'function' ? value.bind(ls) : value;
132
183
  }
133
184
 
134
- // A TS2339 for a `@data`-injected member accessed on a `@data` class.
185
+ const inAugmentedRegion = (fileName, diag) => {
186
+ const len = originalLength.get(fileName);
187
+ return len != null && diag.start != null && diag.start >= len;
188
+ };
189
+
190
+ // Fallback: a TS2339 for a `@data`-injected member on a `@data` class.
135
191
  const isInjectedDataMember = (fileName, diag) => {
136
192
  if (diag.code !== PROPERTY_NOT_EXIST || diag.start == null) return false;
137
193
  const program = ls.getProgram();
@@ -164,6 +220,7 @@ function init(modules) {
164
220
  diagnostics.filter(
165
221
  (d) =>
166
222
  !DECORATOR_GRAMMAR_CODES.has(d.code) &&
223
+ !inAugmentedRegion(fileName, d) &&
167
224
  !isInjectedDataMember(fileName, d) &&
168
225
  !isToilDecoratedUnused(fileName, d),
169
226
  );
@@ -173,10 +230,11 @@ function init(modules) {
173
230
  proxy.getSyntacticDiagnostics = (fileName) =>
174
231
  strip(fileName, ls.getSyntacticDiagnostics(fileName));
175
232
 
176
- // The "unused" greying is a suggestion diagnostic when `noUnusedLocals` is off.
177
233
  if (typeof ls.getSuggestionDiagnostics === 'function') {
178
234
  proxy.getSuggestionDiagnostics = (fileName) =>
179
- ls.getSuggestionDiagnostics(fileName).filter((d) => !isToilDecoratedUnused(fileName, d));
235
+ ls
236
+ .getSuggestionDiagnostics(fileName)
237
+ .filter((d) => !inAugmentedRegion(fileName, d) && !isToilDecoratedUnused(fileName, d));
180
238
  }
181
239
 
182
240
  return proxy;