tslaws 0.6.3 → 0.7.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 (68) hide show
  1. package/README.md +8 -8
  2. package/dist/api.d.ts +10 -0
  3. package/dist/api.js +7 -0
  4. package/dist/browser.d.ts +2 -2
  5. package/dist/browser.js +2 -6
  6. package/dist/check.d.ts +2 -0
  7. package/dist/check.js +133 -41
  8. package/dist/cli.d.ts +18 -0
  9. package/dist/cli.js +149 -54
  10. package/dist/compile.d.ts +12 -0
  11. package/dist/compile.js +207 -6
  12. package/dist/coverage.d.ts +13 -1
  13. package/dist/coverage.js +16 -23
  14. package/dist/derived.d.ts +8 -0
  15. package/dist/derived.js +52 -0
  16. package/dist/e2e.d.ts +0 -9
  17. package/dist/e2e.js +0 -20
  18. package/dist/generators.d.ts +8 -8
  19. package/dist/generators.js +59 -99
  20. package/dist/globs.d.ts +3 -0
  21. package/dist/globs.js +24 -0
  22. package/dist/init.js +5 -5
  23. package/dist/lawFiles.d.ts +21 -0
  24. package/dist/lawFiles.js +95 -0
  25. package/dist/lawKinds.d.ts +38 -0
  26. package/dist/lawKinds.js +210 -0
  27. package/dist/lawNames.d.ts +6 -0
  28. package/dist/lawNames.js +22 -0
  29. package/dist/list.d.ts +22 -0
  30. package/dist/list.js +36 -0
  31. package/dist/lock.d.ts +16 -3
  32. package/dist/lock.js +23 -4
  33. package/dist/lower.d.ts +10 -0
  34. package/dist/lower.js +178 -0
  35. package/dist/mcp.d.ts +2 -8
  36. package/dist/mcp.js +40 -61
  37. package/dist/migrate/arbitraries.d.ts +23 -0
  38. package/dist/migrate/arbitraries.js +202 -0
  39. package/dist/migrate/convert.d.ts +20 -0
  40. package/dist/migrate/convert.js +915 -0
  41. package/dist/migrate/framework.d.ts +48 -0
  42. package/dist/migrate/framework.js +249 -0
  43. package/dist/migrate/index.d.ts +16 -0
  44. package/dist/migrate/index.js +155 -0
  45. package/dist/migrate/report.d.ts +35 -0
  46. package/dist/migrate/report.js +40 -0
  47. package/dist/migrate/source.d.ts +42 -0
  48. package/dist/migrate/source.js +163 -0
  49. package/dist/migrate/tests.d.ts +44 -0
  50. package/dist/migrate/tests.js +184 -0
  51. package/dist/plan.d.ts +50 -0
  52. package/dist/plan.js +118 -0
  53. package/dist/pool.d.ts +21 -0
  54. package/dist/pool.js +54 -0
  55. package/dist/promote.d.ts +30 -2
  56. package/dist/promote.js +133 -7
  57. package/dist/property.d.ts +12 -26
  58. package/dist/property.js +33 -74
  59. package/dist/propertyRun.d.ts +24 -0
  60. package/dist/propertyRun.js +56 -0
  61. package/dist/propertyWorker.js +7 -9
  62. package/dist/proposals.d.ts +43 -0
  63. package/dist/proposals.js +192 -0
  64. package/dist/regressions.js +23 -2
  65. package/dist/smt.js +9 -8
  66. package/dist/types.d.ts +2 -0
  67. package/hooks/AGENTS.snippet.md +2 -2
  68. package/package.json +8 -6
@@ -0,0 +1,192 @@
1
+ // Proposals: laws an agent (or a person) suggests, kept in `.tslaws/proposals/` at the project root. The directory is
2
+ // tool-owned and git-ignored, outside the tree the lock covers, so a proposal protects nothing and an edit to the tree
3
+ // can never turn an accepted law into a proposed one. Only this module writes there; it never touches the lock.
4
+ //
5
+ // Imports in a proposal that start with `./` are written relative to the PROJECT ROOT (`./src/cart`), which is what an
6
+ // author at the project root expects. They are stored relative to the file (`../../src/cart`), so the stored file is
7
+ // ordinary TypeScript that resolves, type-checks and runs where it is. `../` and package imports are left alone.
8
+ // `promote` rewrites the stored form again for the directory the law moves to.
9
+ import { constants, existsSync, lstatSync, mkdirSync, openSync, readFileSync, realpathSync, writeSync, closeSync, appendFileSync, writeFileSync, } from 'node:fs';
10
+ import { join, posix } from 'node:path';
11
+ import { moduleSpecifiers } from "./lawKinds.js";
12
+ import { proposalsDir } from "./lawFiles.js";
13
+ import { parseLawFile } from "./promote.js";
14
+ /** A refusal with a message for the person or agent that asked. */
15
+ export class ProposalError extends Error {
16
+ }
17
+ const nameShape = /^[A-Za-z0-9][A-Za-z0-9._-]*\.(?:ts|mts|cts)$/;
18
+ const isLink = (path) => {
19
+ try {
20
+ return lstatSync(path).isSymbolicLink();
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ };
26
+ /**
27
+ * The proposals directory, checked on the real file system: neither `.tslaws` nor `proposals` may be a link (or a
28
+ * junction) that leads out of the project. Creates the directory when `create` is set.
29
+ */
30
+ export function proposalsDirectory(cwd, create) {
31
+ let real = realpathSync(cwd);
32
+ let shown = cwd;
33
+ for (const part of ['.tslaws', 'proposals']) {
34
+ shown = join(shown, part);
35
+ const expected = join(real, part);
36
+ if (existsSync(shown) || isLink(shown)) {
37
+ if (isLink(shown) || realpathSync(shown) !== expected)
38
+ throw new ProposalError(`${posix.join('.tslaws', part)} is a link that leaves the project; proposals are written inside the project only`);
39
+ }
40
+ else if (create)
41
+ mkdirSync(shown);
42
+ else
43
+ return undefined;
44
+ real = expected;
45
+ }
46
+ return shown;
47
+ }
48
+ /**
49
+ * The project-relative path of a proposal: `name.ts` or `.tslaws/proposals/name.ts`. Anything that names another
50
+ * directory, climbs out, is absolute or is not a plain `.ts` file name is refused.
51
+ */
52
+ export function proposalName(file) {
53
+ const given = file.replaceAll('\\', '/').replace(/^\.\//, '');
54
+ const bare = given.startsWith(`${proposalsDir}/`) ? given.slice(proposalsDir.length + 1) : given;
55
+ if (!nameShape.test(bare) || /\.d\.(?:ts|mts|cts)$/.test(bare) || bare.includes('..'))
56
+ throw new ProposalError(`'${file}' is not a proposal file: proposals are plain .ts files directly inside ${proposalsDir}/ (for example ${proposalsDir}/cart.ts)`);
57
+ return `${proposalsDir}/${bare}`;
58
+ }
59
+ /** The absolute path a write to a proposal may use, after refusing links. Creates the directory. */
60
+ function writablePath(cwd, file) {
61
+ const rel = proposalName(file);
62
+ const dir = proposalsDirectory(cwd, true);
63
+ const abs = join(dir, rel.slice(proposalsDir.length + 1));
64
+ if (isLink(abs))
65
+ throw new ProposalError(`${rel} is a link; a proposal must be a plain file`);
66
+ if (existsSync(abs) && !lstatSync(abs).isFile())
67
+ throw new ProposalError(`${rel} is not a plain file`);
68
+ return { rel, abs };
69
+ }
70
+ /** The absolute path of an existing proposal for reading or removing; refuses links the same way. */
71
+ export function existingProposal(cwd, file) {
72
+ const rel = proposalName(file);
73
+ const dir = proposalsDirectory(cwd, false);
74
+ const abs = dir === undefined ? undefined : join(dir, rel.slice(proposalsDir.length + 1));
75
+ if (abs === undefined || !existsSync(abs))
76
+ throw new ProposalError(`no proposal ${rel}`);
77
+ if (isLink(abs) || !lstatSync(abs).isFile())
78
+ throw new ProposalError(`${rel} is not a plain file`);
79
+ return { rel, abs };
80
+ }
81
+ function rewrite(text, change) {
82
+ let out = text;
83
+ for (const spec of moduleSpecifiers(text, 'proposal.ts').reverse()) {
84
+ const next = change(spec.value);
85
+ if (next !== undefined && next !== spec.value)
86
+ out = out.slice(0, spec.start) + next + out.slice(spec.end);
87
+ }
88
+ return out;
89
+ }
90
+ const dotted = (path) => (path.startsWith('.') ? path : `./${path}`);
91
+ /** Stores root-relative `./x` imports relative to the proposals directory. Idempotent for stored text. */
92
+ export function toStoredImports(text) {
93
+ return rewrite(text, (specifier) => {
94
+ if (!specifier.startsWith('./'))
95
+ return undefined;
96
+ const fromRoot = posix.normalize(specifier);
97
+ if (fromRoot.startsWith('..'))
98
+ throw new ProposalError(`import '${specifier}' leaves the project; imports starting with ./ are relative to the project root`);
99
+ return dotted(posix.relative(proposalsDir, fromRoot));
100
+ });
101
+ }
102
+ /** Re-expresses the relative imports of a stored proposal for a file in the project-relative directory `toDir`. */
103
+ export function importsForTarget(text, toDir) {
104
+ return rewrite(text, (specifier) => {
105
+ if (!specifier.startsWith('.'))
106
+ return undefined;
107
+ return dotted(posix.relative(toDir === '' ? '.' : toDir, posix.join(proposalsDir, specifier)));
108
+ });
109
+ }
110
+ /** The proposal's own text after `parseLawFile`: only imports and law declarations are allowed in one. */
111
+ function parts(source) {
112
+ const parsed = parseLawFile(source);
113
+ let rest = source;
114
+ for (const part of [...parsed.laws, ...parsed.imports.map((text) => ({ text }))])
115
+ rest = rest.replace(part.text, '');
116
+ if (rest.trim() !== '')
117
+ throw new ProposalError('a proposal may contain only imports and law declarations');
118
+ return parsed;
119
+ }
120
+ /** The proposed file with the proposal's laws appended (same-named laws replaced) and its imports added once. */
121
+ export function mergeProposal(current, imports, laws) {
122
+ const parsed = parseLawFile(current);
123
+ let text = current;
124
+ for (const law of [...parsed.laws].reverse()) {
125
+ if (laws.some((l) => l.name === law.name))
126
+ text = text.slice(0, law.start) + text.slice(law.end);
127
+ }
128
+ const have = new Set(parseLawFile(text).imports.map((i) => i.replace(/\s+/g, ' ').trim()));
129
+ const missing = imports.filter((i) => !have.has(i.replace(/\s+/g, ' ').trim()));
130
+ const head = missing.length > 0 ? `${missing.join('\n')}\n` : '';
131
+ const body = `${text.trim() === '' ? '' : `${text.trim()}\n\n`}${laws.map((l) => l.text.trim()).join('\n\n')}\n`;
132
+ return `${head}${head !== '' && !body.startsWith('import') ? '\n' : ''}${body}`;
133
+ }
134
+ function write(abs, text) {
135
+ // No following of a link planted at the last moment, where the platform can say so.
136
+ const fd = openSync(abs, constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC | (constants.O_NOFOLLOW ?? 0), 0o644);
137
+ try {
138
+ writeSync(fd, text);
139
+ }
140
+ finally {
141
+ closeSync(fd);
142
+ }
143
+ }
144
+ /**
145
+ * Keeps the proposals directory out of git, on first use: appends `.tslaws/proposals` to an existing .gitignore (or
146
+ * creates one in a git checkout) unless `.tslaws` or the directory is already listed. `tslaws init` leaves this to us.
147
+ */
148
+ function ignoreProposals(cwd) {
149
+ const ignore = join(cwd, '.gitignore');
150
+ const has = existsSync(ignore);
151
+ if (!has && !existsSync(join(cwd, '.git')))
152
+ return;
153
+ const text = has ? readFileSync(ignore, 'utf8') : '';
154
+ const listed = text.split(/\r?\n/).map((l) => l.trim().replace(/^\//, '').replace(/\/$/, ''));
155
+ if (listed.includes('.tslaws') || listed.includes('.tslaws/proposals'))
156
+ return;
157
+ const lead = text === '' || text.endsWith('\n') ? '' : '\n';
158
+ if (has)
159
+ appendFileSync(ignore, `${lead}.tslaws/proposals\n`);
160
+ else
161
+ writeFileSync(ignore, '.tslaws/proposals\n');
162
+ }
163
+ /**
164
+ * Adds laws to a proposal (appended; a law of the same name is replaced). `source` holds only imports and `export law`
165
+ * declarations; with `only`, it must declare that law. Writes inside `.tslaws/proposals/` and nowhere else.
166
+ */
167
+ export function addProposal(cwd, file, source, only) {
168
+ const { rel, abs } = writablePath(cwd, file);
169
+ const stored = toStoredImports(source);
170
+ const parsed = parts(stored);
171
+ if (parsed.laws.length === 0)
172
+ throw new ProposalError('source must declare at least one law (`export law name(...) ...`)');
173
+ if (only !== undefined && !parsed.laws.some((l) => l.name === only))
174
+ throw new ProposalError(`source must contain \`export law ${only}\`…`);
175
+ const current = existsSync(abs) ? readFileSync(abs, 'utf8') : '';
176
+ write(abs, mergeProposal(current, parsed.imports, parsed.laws));
177
+ ignoreProposals(cwd);
178
+ return { file: rel, laws: parsed.laws.map((l) => l.name) };
179
+ }
180
+ /**
181
+ * Stores the root-relative imports of a proposal someone placed in the directory by hand, so it resolves where it is.
182
+ * Returns whether the file changed. Does nothing for a path that is not a plain file in the directory.
183
+ */
184
+ export function normaliseProposal(cwd, file) {
185
+ const { abs } = existingProposal(cwd, file);
186
+ const text = readFileSync(abs, 'utf8');
187
+ const stored = toStoredImports(text);
188
+ if (stored === text)
189
+ return false;
190
+ write(abs, stored);
191
+ return true;
192
+ }
@@ -1,5 +1,6 @@
1
1
  // The regression corpus: every counterexample a property test finds is kept under `.tslaws/regressions` and replayed
2
2
  // before the random search on later runs, so a bug that was found once cannot silently come back.
3
+ import { createHash } from 'node:crypto';
3
4
  import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
4
5
  import { join } from 'node:path';
5
6
  /** The most examples kept per law. */
@@ -38,7 +39,12 @@ export function encodeValue(value) {
38
39
  throw new Error('cannot store a non-plain object');
39
40
  const fields = {};
40
41
  for (const key of Object.keys(value))
41
- fields[key] = encodeValue(value[key]);
42
+ Object.defineProperty(fields, key, {
43
+ value: encodeValue(value[key]),
44
+ enumerable: true,
45
+ writable: true,
46
+ configurable: true,
47
+ });
42
48
  return { $t: 'object', v: fields };
43
49
  }
44
50
  throw new Error(`cannot store a ${typeof value}`);
@@ -84,8 +90,21 @@ export function decodeExample(text) {
84
90
  const root = (cwd) => join(cwd, '.tslaws', 'regressions');
85
91
  /** `.tslaws/regressions/<law file with / as __>/<law>.json` */
86
92
  export function regressionFile(cwd, file, name) {
93
+ return join(root(cwd), file.replace(/[\\/]/g, '__'), `${safeName(name)}.json`);
94
+ }
95
+ function legacyFile(cwd, file, name) {
87
96
  return join(root(cwd), file.replace(/[\\/]/g, '__'), `${name}.json`);
88
97
  }
98
+ /** A law name as a file name on every platform: characters Windows refuses become `_`, and a hash keeps two names apart. */
99
+ function safeName(name) {
100
+ const safe = [...name]
101
+ .map((ch) => (ch.charCodeAt(0) < 32 || '<>:"/\\|?*'.includes(ch) ? '_' : ch))
102
+ .join('')
103
+ .replace(/[. ]+$/, '');
104
+ if (safe === name)
105
+ return name;
106
+ return `${safe}-${createHash('sha256').update(name).digest('hex').slice(0, 8)}`;
107
+ }
89
108
  function readCorpus(path) {
90
109
  try {
91
110
  const corpus = JSON.parse(readFileSync(path, 'utf8'));
@@ -97,7 +116,9 @@ function readCorpus(path) {
97
116
  }
98
117
  /** The stored examples of a law as the texts `encodeExample` makes, newest first. */
99
118
  export function loadExamples(cwd, file, name) {
100
- const corpus = readCorpus(regressionFile(cwd, file, name));
119
+ // Before names were made safe for Windows the file was named with the law's name as written: read that too, so
120
+ // counterexamples an earlier version stored are still replayed (the next save writes the new name).
121
+ const corpus = readCorpus(regressionFile(cwd, file, name)) ?? readCorpus(legacyFile(cwd, file, name));
101
122
  return corpus === undefined ? [] : corpus.examples.map((e) => JSON.stringify(e));
102
123
  }
103
124
  /** Stores `args` as the newest example; an equal older one moves to the front. Returns false when `args` cannot be stored. */
package/dist/smt.js CHANGED
@@ -11,6 +11,7 @@
11
11
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
12
12
  import { createRequire } from 'node:module';
13
13
  import { Worker } from 'node:worker_threads';
14
+ import { jobs, WorkerPool } from "./pool.js";
14
15
  import { dirname, join } from 'node:path';
15
16
  import ts from 'typescript';
16
17
  import { Executor, SpuriousModel, Unsupported, and } from "./symbolic.js";
@@ -219,7 +220,6 @@ parentPort.on('message', async ({ id, query, values }) => {
219
220
  }
220
221
  });
221
222
  `;
222
- let worker;
223
223
  let nextId = 0;
224
224
  function startWorker() {
225
225
  const w = new Worker(workerSource, {
@@ -229,6 +229,8 @@ function startWorker() {
229
229
  w.unref();
230
230
  return w;
231
231
  }
232
+ // Each Z3 instance holds a large WebAssembly heap; more than four at once run out of memory on the bigger queries.
233
+ const solvers = new WorkerPool(startWorker, Math.min(4, jobs()));
232
234
  /**
233
235
  * Runs the query, and runs it again in a fresh worker when the solver itself fell over (a WebAssembly allocation that
234
236
  * fails on a machine short of memory). A query that merely ran too long is not retried: it would only run long again.
@@ -247,8 +249,7 @@ async function solve(query, values, timeoutMs) {
247
249
  }
248
250
  /** One try: gives up on the whole worker after the timeout plus a grace period. */
249
251
  async function solveOnce(query, values, timeoutMs) {
250
- worker ??= startWorker();
251
- const current = worker;
252
+ const current = await solvers.acquire();
252
253
  const id = (nextId += 1);
253
254
  return new Promise((resolve, reject) => {
254
255
  const drop = () => {
@@ -257,9 +258,7 @@ async function solveOnce(query, values, timeoutMs) {
257
258
  current.off('error', onError);
258
259
  };
259
260
  const kill = () => {
260
- void current.terminate();
261
- if (worker === current)
262
- worker = undefined;
261
+ solvers.discard(current);
263
262
  };
264
263
  const timer = setTimeout(() => {
265
264
  drop();
@@ -274,8 +273,10 @@ async function solveOnce(query, values, timeoutMs) {
274
273
  kill();
275
274
  reject(new Unsupported(`the solver crashed: ${reply.error}`));
276
275
  }
277
- else
276
+ else {
277
+ solvers.release(current);
278
278
  resolve({ out: reply.out ?? '', vals: reply.vals ?? '' });
279
+ }
279
280
  };
280
281
  const onError = (error) => {
281
282
  drop();
@@ -334,7 +335,7 @@ function containsInt(shape) {
334
335
  async function attempt(input, intLimit) {
335
336
  {
336
337
  const source = ts.createSourceFile(input.jsFile, readFileSync(input.jsFile, 'utf8'), ts.ScriptTarget.ES2022, true, ts.ScriptKind.JS);
337
- const fn = source.statements.find((s) => ts.isFunctionDeclaration(s) && s.name?.text === input.law.name);
338
+ const fn = source.statements.find((s) => ts.isFunctionDeclaration(s) && s.name?.text === (input.law.symbol ?? input.law.name));
338
339
  if (fn === undefined || fn.body === undefined)
339
340
  throw new Unsupported('law function not found in the compiled output');
340
341
  if (fn.parameters.length !== input.shapes.length)
package/dist/types.d.ts CHANGED
@@ -2,6 +2,8 @@ export type LawStatus = 'proved' | 'bounded' | 'tested' | 'open' | 'failed' | 't
2
2
  export interface ManifestLaw {
3
3
  file: string;
4
4
  name: string;
5
+ /** The exported function the law lowers to, when its name is not itself an identifier. Defaults to `name`. */
6
+ symbol?: string;
5
7
  exported: boolean;
6
8
  proved: boolean;
7
9
  line: number;
@@ -1,10 +1,10 @@
1
1
  ## Laws
2
2
 
3
- This project states behaviour as `law` declarations in `*.laws.ts`. They are the person's side of the contract.
3
+ This project states behaviour as laws in `*.laws.ts`, written as calls, `law('name', (a: int) => ...)` and `law.proved('name', ...)`, or, with the tsc-laws compiler, as `export law name(a: int) { ... }` declarations. Use the syntax the existing laws use and never mix the two in one project. They are the person's side of the contract.
4
4
 
5
5
  - Run `npx tslaws check` before you finish or commit. It must exit 0: every law `proved` or `tested`, none `open`, `failed` or `tampered`.
6
6
  - Every exported function must be reached by a law (`uncovered` fails). Do not add `@tslaws-exempt` yourself; the lock rejects it. If code needs a law, ask for one or write it in a `*.proposed.laws.ts`.
7
7
  - A failed law means the code is wrong. Fix the code. Never edit, weaken, delete or add a law in a `*.laws.ts` file, and never run `tslaws accept`; the lock (`tsc-laws.lock`) will reject the change anyway.
8
- - To suggest a law, write it in `<name>.proposed.laws.ts` and run `npx tslaws check --proposed` to see whether it holds. A person promotes it.
8
+ - To suggest a law, write it in `<name>.proposed.laws.ts` (in the project's syntax) and run `npx tslaws check --proposed` to see whether it holds. A person promotes it.
9
9
  - If the `tslaws` MCP server is configured (`npx tslaws mcp`), use its tools `tslaws_check`, `tslaws_check_proposed`, `tslaws_explain` and `tslaws_propose` instead of the shell; they cannot accept laws or change the lock. `tslaws promote` moves proposed laws into the accepted file and is the person's step, like `tslaws accept`; never run either.
10
10
  - `proved` laws need a proof the prover can find for every input. Keep proved code to `int`/`bigint`/`boolean`/`string`, records and `const`/`if`/`return`/calls; arrays and plain loops only reach `bounded`. To prove a scalar loop for every input, write `invariant(...)` first in its body (it is code, so it is yours to write). Use the `int` type from `tslaws` for whole numbers.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tslaws",
3
- "version": "0.6.3",
3
+ "version": "0.7.0",
4
4
  "description": "Runs and checks `law` declarations compiled by tsc-laws: property tests, SMT proofs, tamper lock.",
5
5
  "bin": {
6
6
  "tslaws": "bin/tslaws.js"
@@ -42,12 +42,14 @@
42
42
  "types": "./dist/adapters/element.d.ts",
43
43
  "default": "./dist/adapters/element.js"
44
44
  },
45
+ "./internal/*": "./dist/*.js",
45
46
  "./package.json": "./package.json"
46
47
  },
47
48
  "scripts": {
48
49
  "build": "tsc -p tsconfig.json",
49
50
  "test": "node --test --test-concurrency=1 test/*.test.ts",
50
51
  "check": "node bin/tslaws.js check",
52
+ "laws": "node bin/tslaws.js check laws/core.laws.ts",
51
53
  "check:watch": "node bin/tslaws.js check --watch"
52
54
  },
53
55
  "dependencies": {
@@ -71,11 +73,11 @@
71
73
  }
72
74
  },
73
75
  "optionalDependencies": {
74
- "tslaws-compiler-darwin-arm64": "0.6.3",
75
- "tslaws-compiler-darwin-x64": "0.6.3",
76
- "tslaws-compiler-linux-arm64-gnu": "0.6.3",
77
- "tslaws-compiler-linux-x64-gnu": "0.6.3",
78
- "tslaws-compiler-win32-x64-msvc": "0.6.3"
76
+ "tslaws-compiler-darwin-arm64": "0.7.0",
77
+ "tslaws-compiler-darwin-x64": "0.7.0",
78
+ "tslaws-compiler-linux-arm64-gnu": "0.7.0",
79
+ "tslaws-compiler-linux-x64-gnu": "0.7.0",
80
+ "tslaws-compiler-win32-x64-msvc": "0.7.0"
79
81
  },
80
82
  "publishConfig": {
81
83
  "access": "public"