fastscript 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +41 -3
- package/node_modules/@fastscript/core-private/src/fs-error-codes.mjs +2 -2
- package/node_modules/@fastscript/core-private/src/fs-normalize.mjs +33 -3
- package/node_modules/@fastscript/core-private/src/fs-parser.mjs +197 -57
- package/node_modules/@fastscript/core-private/src/typecheck.mjs +5 -3
- package/package.json +26 -5
- package/src/benchmark-discipline.mjs +39 -0
- package/src/cli.mjs +37 -2
- package/src/conversion-manifest.mjs +101 -0
- package/src/diagnostics.mjs +100 -0
- package/src/fs-error-codes.mjs +2 -2
- package/src/fs-normalize.mjs +33 -3
- package/src/generated/docs-search-index.mjs +545 -174
- package/src/migrate-rollback.mjs +144 -0
- package/src/migrate.mjs +1275 -47
- package/src/migration-wizard.mjs +37 -11
- package/src/module-loader.mjs +13 -1
- package/src/permissions-cli.mjs +112 -0
- package/src/profile.mjs +95 -0
- package/src/regression-guard.mjs +245 -0
- package/src/runtime-permissions.mjs +299 -0
- package/src/trace.mjs +95 -0
- package/src/validate.mjs +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
- `spec/V2_0_MIGRATION_PROOF_REPORT.md`
|
|
15
15
|
- `spec/V2_0_PERFORMANCE_PROTOCOL_REPORT.md`
|
|
16
16
|
|
|
17
|
+
## v3.0.0 - 2026-04-15
|
|
18
|
+
- Launch FastScript v3 as the active public line with `.fs` positioned as a universal JS/TS container
|
|
19
|
+
- Publish v3 website/docs alignment, `/docs/v3` latest-track routing, and proof-backed benchmark messaging
|
|
20
|
+
- Elevate JS/TS syntax proof and `.fs` parity proof into release discipline and proof-pack publishing
|
|
21
|
+
- Refresh the proprietary/source-available product story and position the next FastScript AI assistant wave on the roadmap
|
|
22
|
+
|
|
17
23
|
## v2.0.0 - 2026-04-14
|
|
18
24
|
- Ratify the FastScript v2.0 language/runtime surface and freeze the public spec pack
|
|
19
25
|
- Complete ambient runtime, standard library, DOM/platform, inference, and zero-JS authored app proof gates
|
package/README.md
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
# FastScript
|
|
2
2
|
|
|
3
|
-
FastScript is a JavaScript-first full-stack
|
|
3
|
+
FastScript is a proprietary, source-available JavaScript-first full-stack runtime built around a first-class `.fs` file format.
|
|
4
4
|
|
|
5
5
|
It is designed to feel easier to read than heavyweight stack combinations while staying compatible with the JavaScript ecosystem developers already use.
|
|
6
6
|
|
|
7
|
+
- Write normal JavaScript or TypeScript in `.fs`
|
|
7
8
|
- Write pages, APIs, middleware, jobs, and database workflows in `.fs`
|
|
8
9
|
- Keep compatibility with normal `.js` packages and modules
|
|
10
|
+
- Treat FastScript-specific syntax as optional sugar, not a requirement
|
|
9
11
|
- Compile to optimized JavaScript for production deployment
|
|
10
12
|
- Deploy the same app to Node, Vercel, or Cloudflare
|
|
11
13
|
- Run one quality gate for formatting, linting, typecheck, tests, smoke checks, benchmarks, and interop
|
|
14
|
+
- Build on the same language/runtime foundation that powers the future FastScript AI coding products
|
|
12
15
|
|
|
13
|
-
FastScript
|
|
16
|
+
FastScript v3 is built for product teams that want a simpler, faster full-stack pipeline without surrendering compatibility with the ground-level JavaScript platform.
|
|
17
|
+
|
|
18
|
+
The v3 product contract is simple:
|
|
19
|
+
|
|
20
|
+
- `.fs` is a universal JS/TS container for the FastScript runtime
|
|
21
|
+
- FastScript-specific syntax is optional sugar
|
|
22
|
+
- valid JS/TS failures in `.fs` are FastScript compatibility bugs
|
|
23
|
+
- the speed story is earned by the runtime/compiler/toolchain and backed by release proof artifacts
|
|
14
24
|
|
|
15
25
|
## What FastScript Is
|
|
16
26
|
|
|
17
27
|
FastScript combines:
|
|
18
28
|
|
|
19
|
-
1. A source
|
|
29
|
+
1. A JS/TS-compatible source container: `.fs`
|
|
20
30
|
2. A compiler and CLI
|
|
21
31
|
3. A full-stack app runtime
|
|
22
32
|
4. A deployment pipeline for multiple targets
|
|
@@ -58,6 +68,15 @@ See:
|
|
|
58
68
|
- `benchmarks/latest-report.md`
|
|
59
69
|
- `benchmarks/suite-latest.json`
|
|
60
70
|
- `benchmarks/interop-latest.json`
|
|
71
|
+
- `docs/PROOF_PACK.md`
|
|
72
|
+
|
|
73
|
+
## Why Teams Pick FastScript
|
|
74
|
+
|
|
75
|
+
- Smaller client payloads than heavier framework baselines
|
|
76
|
+
- Faster build loops and stricter release gates in one toolchain
|
|
77
|
+
- One runtime-native source container for pages, APIs, jobs, and middleware
|
|
78
|
+
- Package compatibility without giving up a proprietary language/runtime moat
|
|
79
|
+
- A direct path into the next FastScript AI assistant stack
|
|
61
80
|
|
|
62
81
|
## Install
|
|
63
82
|
|
|
@@ -333,6 +352,9 @@ npm run validate
|
|
|
333
352
|
|
|
334
353
|
```bash
|
|
335
354
|
npm run migrate
|
|
355
|
+
npm run convert
|
|
356
|
+
npm run migrate:rollback
|
|
357
|
+
npm run manifest
|
|
336
358
|
npm run wizard:migrate
|
|
337
359
|
npm run export:js
|
|
338
360
|
npm run export:ts
|
|
@@ -341,10 +363,21 @@ npm run typecheck
|
|
|
341
363
|
npm run typecheck:pass
|
|
342
364
|
npm run lint:fs
|
|
343
365
|
npm run lint:fs:pass
|
|
366
|
+
npm run diagnostics
|
|
367
|
+
npm run permissions
|
|
344
368
|
npm run format
|
|
345
369
|
npm run format:check
|
|
346
370
|
```
|
|
347
371
|
|
|
372
|
+
`migrate` now runs strict compatibility-first conversion (rename-only + import extension rewrites + manifest/validation/fidelity reports).
|
|
373
|
+
Example: `npm run migrate -- app --dry-run`
|
|
374
|
+
Full proof mode: `npm run migrate -- app --fidelity-level full --fail-on-unproven-fidelity`
|
|
375
|
+
|
|
376
|
+
Latest trust artifacts are written to `.fastscript/conversion/latest`:
|
|
377
|
+
`conversion-manifest.json`, `diff-preview.json`, `validation-report.json`, `fidelity-report.json`.
|
|
378
|
+
|
|
379
|
+
Authored `.fs` now accepts normal JS/TS/JSX/TSX-style code directly. Proof artifacts for the current parity contract are written to `.fastscript/proofs/js-ts-syntax-proof.json` and `.fastscript/proofs/fs-parity-matrix.json`.
|
|
380
|
+
|
|
348
381
|
### Database and data
|
|
349
382
|
|
|
350
383
|
```bash
|
|
@@ -382,6 +415,10 @@ npm run qa:all
|
|
|
382
415
|
|
|
383
416
|
```bash
|
|
384
417
|
npm run bench
|
|
418
|
+
npm run bench:discipline
|
|
419
|
+
npm run regression
|
|
420
|
+
npm run profile
|
|
421
|
+
npm run trace
|
|
385
422
|
npm run bench:report
|
|
386
423
|
npm run benchmark:suite
|
|
387
424
|
npm run interop:report
|
|
@@ -551,6 +588,7 @@ Key docs in this repo:
|
|
|
551
588
|
- `docs/KNOWN_LIMITATIONS.md`
|
|
552
589
|
- `docs/CONTRIBUTING.md`
|
|
553
590
|
- `docs/OBSERVABILITY.md` (public stub; full version lives in private core)
|
|
591
|
+
- `docs/RUNTIME_PERMISSIONS.md`
|
|
554
592
|
- `docs/ROLLOUT_GUIDE.md` (public stub; full version lives in private core)
|
|
555
593
|
- `SECURITY.md`
|
|
556
594
|
|
|
@@ -16,8 +16,8 @@ export const FS_ERROR_CODES = Object.freeze({
|
|
|
16
16
|
},
|
|
17
17
|
FS1004: {
|
|
18
18
|
severity: "error",
|
|
19
|
-
message: "
|
|
20
|
-
hint: "
|
|
19
|
+
message: "Legacy compatibility frontend conflict.",
|
|
20
|
+
hint: "Ordinary TS type-only syntax in `.fs` should parse. If this appears, treat it as a FastScript compatibility bug.",
|
|
21
21
|
},
|
|
22
22
|
FS1005: {
|
|
23
23
|
severity: "error",
|
|
@@ -60,28 +60,58 @@ export function stripTypeScriptHints(source) {
|
|
|
60
60
|
continue;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
if (/^\s*declare\s+(global|module|namespace)\b/.test(next)) {
|
|
64
|
+
out.push(`// ${next.trim()} (removed by fastscript migrate)`);
|
|
65
|
+
if (next.includes("{")) {
|
|
66
|
+
skippingBlock = true;
|
|
67
|
+
const opens = (next.match(/{/g) || []).length;
|
|
68
|
+
const closes = (next.match(/}/g) || []).length;
|
|
69
|
+
blockDepth = Math.max(1, opens - closes);
|
|
70
|
+
}
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (/^\s*declare\s+/.test(next)) {
|
|
75
|
+
out.push(`// ${next.trim()} (removed by fastscript migrate)`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
63
79
|
next = next.replace(/\bimport\s+type\b/g, "import");
|
|
64
80
|
next = next.replace(/\bexport\s+type\b/g, "export");
|
|
81
|
+
next = next.replace(/\btype\s+([A-Za-z_$][\w$]*)\s+as\s+/g, "$1 as ");
|
|
82
|
+
next = next.replace(/\b(?:public|private|protected|readonly|declare|override|abstract)\s+/g, "");
|
|
83
|
+
next = next.replace(/\s+implements\s+[A-Za-z_$][\w$<>\[\]\|&, ?.]+/g, "");
|
|
84
|
+
next = next.replace(/([A-Za-z_$][\w$]*)!([?:;=,\)])/g, "$1$2");
|
|
65
85
|
|
|
66
86
|
next = next.replace(
|
|
67
87
|
/^(\s*)(const|let|var)\s+([A-Za-z_$][\w$]*)\s*:\s*([^=;]+)([=;].*)$/,
|
|
68
88
|
"$1$2 $3 $5",
|
|
69
89
|
);
|
|
90
|
+
next = next.replace(
|
|
91
|
+
/^(\s*)(const|let|var)\s+([A-Za-z_$][\w$]*)\s*<[^>]+>\s*=\s*/,
|
|
92
|
+
"$1$2 $3 = ",
|
|
93
|
+
);
|
|
70
94
|
|
|
71
|
-
if (/\bfunction\b/.test(next) || /\)\s*=>/.test(next)) {
|
|
95
|
+
if (/\bfunction\b/.test(next) || /\bfn\b/.test(next) || /\)\s*=>/.test(next)) {
|
|
72
96
|
next = next.replace(/\(([^)]*)\)/, (_, params) => {
|
|
73
97
|
const cleaned = params.replace(
|
|
74
|
-
/([A-Za-z_$][\w$]*)
|
|
98
|
+
/([A-Za-z_$][\w$]*)(\?)?\s*:\s*([A-Za-z_$][\w$<>\[\]\|&, ?.:{}=]*)/g,
|
|
75
99
|
"$1",
|
|
76
100
|
);
|
|
77
101
|
return `(${cleaned})`;
|
|
78
102
|
});
|
|
79
|
-
next = next.replace(/\)\s*:\s*([A-Za-z_$][\w$<>\[\]\|&,
|
|
103
|
+
next = next.replace(/\)\s*:\s*([A-Za-z_$][\w$<>\[\]\|&, ?.:{}=]*)\s*\{/g, ") {");
|
|
104
|
+
next = next.replace(/\)\s*:\s*([A-Za-z_$][\w$<>\[\]\|&, ?.:{}=]*)\s*=>/g, ") =>");
|
|
80
105
|
next = next.replace(/\bfunction\s+([A-Za-z_$][\w$]*)\s*<[^>]+>\s*\(/g, "function $1(");
|
|
106
|
+
next = next.replace(/\bfn\s+([A-Za-z_$][\w$]*)\s*<[^>]+>\s*\(/g, "fn $1(");
|
|
107
|
+
next = next.replace(/=\s*async\s*<[^>]+>\s*\(/g, "= async (");
|
|
108
|
+
next = next.replace(/=\s*<[^>]+>\s*\(/g, "= (");
|
|
81
109
|
}
|
|
82
110
|
|
|
83
111
|
next = next.replace(/^\s*<([A-Za-z_$][\w$,\s]*)>\s*\(/, "(");
|
|
84
112
|
next = next.replace(/\)\s*=>\s*<[A-Za-z_$][\w$<>\[\]\|&, ?.]*>/g, ") =>");
|
|
113
|
+
next = next.replace(/\s+as\s+const\b/g, "");
|
|
114
|
+
next = next.replace(/\s+as\s+[A-Za-z_$][\w$<>\[\]\|&, ?.:{}=]*/g, "");
|
|
85
115
|
next = next.replace(/\sas\s+const\b/g, "");
|
|
86
116
|
next = next.replace(/\s+satisfies\s+[A-Za-z_$][\w$<>\[\]\|&, ?.]*/g, "");
|
|
87
117
|
out.push(next);
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { parse as acornParse, tokenizer as acornTokenizer } from "acorn";
|
|
2
|
+
import * as esbuild from "esbuild";
|
|
2
3
|
import { resolveErrorMeta } from "./fs-error-codes.mjs";
|
|
3
4
|
|
|
4
5
|
export const FASTSCRIPT_AST_VERSION = "1.0.0";
|
|
5
6
|
|
|
6
7
|
const STATEMENT_BOUNDARY_TOKENS = new Set([";", "{", "}"]);
|
|
7
|
-
const
|
|
8
|
+
const BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
9
|
+
const BASE64_LOOKUP = new Map(Array.from(BASE64).map((char, index) => [char, index]));
|
|
8
10
|
|
|
9
11
|
function createLineStarts(source) {
|
|
10
12
|
const text = String(source ?? "");
|
|
@@ -436,9 +438,23 @@ function collectRewriteOperations(source, { file = "", lineStarts, significant }
|
|
|
436
438
|
|
|
437
439
|
if (token.value === "fn" && token.type === "identifier") {
|
|
438
440
|
const prev = previousSignificant(significant, idx);
|
|
441
|
+
const asyncToken = prev?.value === "async" ? prev : null;
|
|
442
|
+
const asyncIndex = asyncToken ? idx - 1 : -1;
|
|
443
|
+
const beforeAsync = asyncToken ? previousSignificant(significant, asyncIndex) : null;
|
|
439
444
|
const startsStatement = isStatementStart(significant, idx);
|
|
440
445
|
const followsExport = prev?.value === "export" && prev.end.line === token.start.line;
|
|
441
|
-
|
|
446
|
+
const followsDefaultExport =
|
|
447
|
+
prev?.value === "default" &&
|
|
448
|
+
previousSignificant(significant, idx - 1)?.value === "export" &&
|
|
449
|
+
previousSignificant(significant, idx - 1)?.end.line === token.start.line;
|
|
450
|
+
const followsAsync =
|
|
451
|
+
asyncToken &&
|
|
452
|
+
(
|
|
453
|
+
isStatementStart(significant, asyncIndex) ||
|
|
454
|
+
beforeAsync?.value === "export" ||
|
|
455
|
+
beforeAsync?.value === "default"
|
|
456
|
+
);
|
|
457
|
+
if (startsStatement || followsExport || followsDefaultExport || followsAsync) {
|
|
442
458
|
const nameToken = nextSignificant(significant, idx, 1);
|
|
443
459
|
const openParenToken = nextSignificant(significant, idx, 2);
|
|
444
460
|
if (isIdentifierToken(nameToken) && openParenToken?.value === "(") {
|
|
@@ -465,40 +481,6 @@ function collectRewriteOperations(source, { file = "", lineStarts, significant }
|
|
|
465
481
|
continue;
|
|
466
482
|
}
|
|
467
483
|
|
|
468
|
-
if (TYPE_DECLARATION_KEYWORDS.has(token.value) && token.type === "identifier" && isStatementStart(significant, idx)) {
|
|
469
|
-
const nextToken = nextSignificant(significant, idx, 1);
|
|
470
|
-
if (!isIdentifierToken(nextToken)) continue;
|
|
471
|
-
const shouldHandleType = token.value === "type" && nextSignificant(significant, idx, 2)?.value === "=";
|
|
472
|
-
const shouldHandleBlock = (token.value === "interface" || token.value === "enum") && nextSignificant(significant, idx, 2);
|
|
473
|
-
if (!shouldHandleType && !shouldHandleBlock) continue;
|
|
474
|
-
|
|
475
|
-
const span = declarationSpanFromKeyword(source, lineStarts, sourceLength, significant, idx);
|
|
476
|
-
const snippet = source.slice(span.start, span.end).trim();
|
|
477
|
-
diagnostics.push(
|
|
478
|
-
createDiagnostic({
|
|
479
|
-
source,
|
|
480
|
-
lineStarts,
|
|
481
|
-
file,
|
|
482
|
-
code: "FS1004",
|
|
483
|
-
span,
|
|
484
|
-
fixes: [
|
|
485
|
-
{
|
|
486
|
-
message: "Remove declaration from runtime source.",
|
|
487
|
-
span,
|
|
488
|
-
text: "",
|
|
489
|
-
},
|
|
490
|
-
],
|
|
491
|
-
}),
|
|
492
|
-
);
|
|
493
|
-
ops.push(
|
|
494
|
-
operation(
|
|
495
|
-
span.start,
|
|
496
|
-
span.end,
|
|
497
|
-
`/* ${snippet || token.value} (removed by fastscript compiler) */`,
|
|
498
|
-
"erase-type",
|
|
499
|
-
),
|
|
500
|
-
);
|
|
501
|
-
}
|
|
502
484
|
}
|
|
503
485
|
|
|
504
486
|
const lexicalTodoPattern = /\bTODO_ERROR\b/g;
|
|
@@ -589,7 +571,104 @@ function applyRewriteOperations(source, ops) {
|
|
|
589
571
|
return { code, mapGeneratedToSource, rewrites };
|
|
590
572
|
}
|
|
591
573
|
|
|
592
|
-
|
|
574
|
+
function decodeVlqValue(mapping, startIndex) {
|
|
575
|
+
let result = 0;
|
|
576
|
+
let shift = 0;
|
|
577
|
+
let index = startIndex;
|
|
578
|
+
|
|
579
|
+
while (index < mapping.length) {
|
|
580
|
+
const value = BASE64_LOOKUP.get(mapping[index]);
|
|
581
|
+
if (value == null) break;
|
|
582
|
+
index += 1;
|
|
583
|
+
const continuation = (value & 32) !== 0;
|
|
584
|
+
result |= (value & 31) << shift;
|
|
585
|
+
shift += 5;
|
|
586
|
+
if (!continuation) {
|
|
587
|
+
const signed = (result & 1) ? -(result >> 1) : result >> 1;
|
|
588
|
+
return { value: signed, next: index };
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return { value: 0, next: index };
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function decodeSourceMapMappings(mappings) {
|
|
596
|
+
const lines = [];
|
|
597
|
+
let sourceIndex = 0;
|
|
598
|
+
let sourceLine = 0;
|
|
599
|
+
let sourceColumn = 0;
|
|
600
|
+
let generatedLine = 0;
|
|
601
|
+
let generatedColumn = 0;
|
|
602
|
+
|
|
603
|
+
for (const line of String(mappings || "").split(";")) {
|
|
604
|
+
const segments = [];
|
|
605
|
+
generatedColumn = 0;
|
|
606
|
+
if (line) {
|
|
607
|
+
for (const rawSegment of line.split(",")) {
|
|
608
|
+
if (!rawSegment) continue;
|
|
609
|
+
let cursor = 0;
|
|
610
|
+
const values = [];
|
|
611
|
+
while (cursor < rawSegment.length) {
|
|
612
|
+
const decoded = decodeVlqValue(rawSegment, cursor);
|
|
613
|
+
values.push(decoded.value);
|
|
614
|
+
cursor = decoded.next;
|
|
615
|
+
}
|
|
616
|
+
if (!values.length) continue;
|
|
617
|
+
generatedColumn += values[0];
|
|
618
|
+
if (values.length >= 4) {
|
|
619
|
+
sourceIndex += values[1];
|
|
620
|
+
sourceLine += values[2];
|
|
621
|
+
sourceColumn += values[3];
|
|
622
|
+
segments.push({ generatedLine, generatedColumn, sourceIndex, sourceLine, sourceColumn });
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
lines.push(segments);
|
|
627
|
+
generatedLine += 1;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
return lines;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function buildGeneratedOffsetMapFromSourceMap({ generated, mapText, sourceLength }) {
|
|
634
|
+
const parsed = JSON.parse(String(mapText || "{}"));
|
|
635
|
+
const lineStarts = createLineStarts(generated);
|
|
636
|
+
const sourceLineStarts = createLineStarts(parsed?.sourcesContent?.[0] || "");
|
|
637
|
+
const decoded = decodeSourceMapMappings(parsed.mappings || "");
|
|
638
|
+
const out = [];
|
|
639
|
+
|
|
640
|
+
for (let lineIndex = 0; lineIndex < lineStarts.length; lineIndex += 1) {
|
|
641
|
+
const lineStart = lineStarts[lineIndex];
|
|
642
|
+
const lineEnd = lineStarts[lineIndex + 1] ?? generated.length;
|
|
643
|
+
const segments = decoded[lineIndex] || [];
|
|
644
|
+
let active = segments[0] || null;
|
|
645
|
+
let segmentIndex = 0;
|
|
646
|
+
|
|
647
|
+
for (let offset = lineStart; offset < lineEnd; offset += 1) {
|
|
648
|
+
const column = offset - lineStart;
|
|
649
|
+
while (segmentIndex + 1 < segments.length && segments[segmentIndex + 1].generatedColumn <= column) {
|
|
650
|
+
segmentIndex += 1;
|
|
651
|
+
active = segments[segmentIndex];
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
if (!active) {
|
|
655
|
+
out.push(clamp(offset, 0, sourceLength));
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const delta = Math.max(0, column - active.generatedColumn);
|
|
660
|
+
const mappedBase = sourceLineStarts[active.sourceLine] ?? 0;
|
|
661
|
+
out.push(clamp(mappedBase + active.sourceColumn + delta, 0, sourceLength));
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
out.push(sourceLength);
|
|
666
|
+
return out;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function composeOffsetMaps(finalToIntermediate, intermediateToSource, sourceLength) {
|
|
670
|
+
return finalToIntermediate.map((offset) => mapGeneratedOffsetToSourceOffset(intermediateToSource, offset, sourceLength));
|
|
671
|
+
}
|
|
593
672
|
|
|
594
673
|
function encodeVlq(value) {
|
|
595
674
|
let num = value < 0 ? ((-value) << 1) + 1 : value << 1;
|
|
@@ -796,9 +875,66 @@ function parseGeneratedJavaScript(generated, { file = "" } = {}) {
|
|
|
796
875
|
onToken: parseTokens,
|
|
797
876
|
onComment: parseComments,
|
|
798
877
|
});
|
|
799
|
-
return {
|
|
878
|
+
return {
|
|
879
|
+
estree,
|
|
880
|
+
parseTokens,
|
|
881
|
+
parseComments,
|
|
882
|
+
error: null,
|
|
883
|
+
frontend: {
|
|
884
|
+
usedCompatibilityFrontend: false,
|
|
885
|
+
mapGeneratedToInput: null,
|
|
886
|
+
generatedCode: generated,
|
|
887
|
+
},
|
|
888
|
+
};
|
|
800
889
|
} catch (error) {
|
|
801
|
-
|
|
890
|
+
try {
|
|
891
|
+
const transformed = esbuild.transformSync(generated, {
|
|
892
|
+
loader: "tsx",
|
|
893
|
+
format: "esm",
|
|
894
|
+
target: "esnext",
|
|
895
|
+
sourcefile: file || "<memory>",
|
|
896
|
+
sourcemap: "external",
|
|
897
|
+
jsx: "transform",
|
|
898
|
+
});
|
|
899
|
+
const compatTokens = [];
|
|
900
|
+
const compatComments = [];
|
|
901
|
+
const estree = acornParse(transformed.code, {
|
|
902
|
+
ecmaVersion: "latest",
|
|
903
|
+
sourceType: "module",
|
|
904
|
+
allowHashBang: true,
|
|
905
|
+
locations: true,
|
|
906
|
+
ranges: true,
|
|
907
|
+
onToken: compatTokens,
|
|
908
|
+
onComment: compatComments,
|
|
909
|
+
});
|
|
910
|
+
return {
|
|
911
|
+
estree,
|
|
912
|
+
parseTokens: compatTokens,
|
|
913
|
+
parseComments: compatComments,
|
|
914
|
+
error: null,
|
|
915
|
+
frontend: {
|
|
916
|
+
usedCompatibilityFrontend: true,
|
|
917
|
+
mapGeneratedToInput: buildGeneratedOffsetMapFromSourceMap({
|
|
918
|
+
generated: transformed.code,
|
|
919
|
+
mapText: transformed.map,
|
|
920
|
+
sourceLength: generated.length,
|
|
921
|
+
}),
|
|
922
|
+
generatedCode: transformed.code,
|
|
923
|
+
},
|
|
924
|
+
};
|
|
925
|
+
} catch {
|
|
926
|
+
return {
|
|
927
|
+
estree: null,
|
|
928
|
+
parseTokens,
|
|
929
|
+
parseComments,
|
|
930
|
+
error,
|
|
931
|
+
frontend: {
|
|
932
|
+
usedCompatibilityFrontend: false,
|
|
933
|
+
mapGeneratedToInput: null,
|
|
934
|
+
generatedCode: generated,
|
|
935
|
+
},
|
|
936
|
+
};
|
|
937
|
+
}
|
|
802
938
|
}
|
|
803
939
|
}
|
|
804
940
|
|
|
@@ -842,7 +978,21 @@ export function parseFastScript(source, { file = "", mode = "lenient", recover =
|
|
|
842
978
|
|
|
843
979
|
const diagnostics = [];
|
|
844
980
|
|
|
845
|
-
|
|
981
|
+
const rewrites = collectRewriteOperations(text, {
|
|
982
|
+
file,
|
|
983
|
+
lineStarts,
|
|
984
|
+
significant: tokenization.significant,
|
|
985
|
+
});
|
|
986
|
+
diagnostics.push(...rewrites.diagnostics);
|
|
987
|
+
|
|
988
|
+
const transformed = applyRewriteOperations(text, rewrites.ops);
|
|
989
|
+
const parsedGenerated = parseGeneratedJavaScript(transformed.code, { file });
|
|
990
|
+
const finalGeneratedCode = parsedGenerated.frontend?.generatedCode || transformed.code;
|
|
991
|
+
const finalMapGeneratedToSource = parsedGenerated.frontend?.mapGeneratedToInput
|
|
992
|
+
? composeOffsetMaps(parsedGenerated.frontend.mapGeneratedToInput, transformed.mapGeneratedToSource, text.length)
|
|
993
|
+
: transformed.mapGeneratedToSource;
|
|
994
|
+
|
|
995
|
+
if (tokenization.lexicalError && !parsedGenerated.frontend?.usedCompatibilityFrontend) {
|
|
846
996
|
const loc = tokenization.lexicalError.loc || { line: 1, column: 0 };
|
|
847
997
|
const lineStart = lineStarts[clamp((loc.line || 1) - 1, 0, lineStarts.length - 1)] ?? 0;
|
|
848
998
|
const offset = clamp(lineStart + (loc.column || 0), 0, text.length);
|
|
@@ -858,19 +1008,9 @@ export function parseFastScript(source, { file = "", mode = "lenient", recover =
|
|
|
858
1008
|
);
|
|
859
1009
|
}
|
|
860
1010
|
|
|
861
|
-
const rewrites = collectRewriteOperations(text, {
|
|
862
|
-
file,
|
|
863
|
-
lineStarts,
|
|
864
|
-
significant: tokenization.significant,
|
|
865
|
-
});
|
|
866
|
-
diagnostics.push(...rewrites.diagnostics);
|
|
867
|
-
|
|
868
|
-
const transformed = applyRewriteOperations(text, rewrites.ops);
|
|
869
|
-
const parsedGenerated = parseGeneratedJavaScript(transformed.code, { file });
|
|
870
|
-
|
|
871
1011
|
if (parsedGenerated.error) {
|
|
872
|
-
const parseOffset = clamp(parsedGenerated.error.pos ?? 0, 0,
|
|
873
|
-
const sourceOffset = mapGeneratedOffsetToSourceOffset(
|
|
1012
|
+
const parseOffset = clamp(parsedGenerated.error.pos ?? 0, 0, finalGeneratedCode.length);
|
|
1013
|
+
const sourceOffset = mapGeneratedOffsetToSourceOffset(finalMapGeneratedToSource, parseOffset, text.length);
|
|
874
1014
|
diagnostics.push(
|
|
875
1015
|
createDiagnostic({
|
|
876
1016
|
source: text,
|
|
@@ -884,13 +1024,13 @@ export function parseFastScript(source, { file = "", mode = "lenient", recover =
|
|
|
884
1024
|
}
|
|
885
1025
|
|
|
886
1026
|
const estree = ensureProgramShape(parsedGenerated.estree);
|
|
887
|
-
remapAstNodeLocations(estree, text, lineStarts,
|
|
1027
|
+
remapAstNodeLocations(estree, text, lineStarts, finalMapGeneratedToSource);
|
|
888
1028
|
|
|
889
1029
|
const cst = buildCst({ source: text, file, lineStarts, tokens, estree });
|
|
890
1030
|
const sourceMap = buildSourceMap({
|
|
891
1031
|
source: text,
|
|
892
|
-
generated:
|
|
893
|
-
mapGeneratedToSource:
|
|
1032
|
+
generated: finalGeneratedCode,
|
|
1033
|
+
mapGeneratedToSource: finalMapGeneratedToSource,
|
|
894
1034
|
file,
|
|
895
1035
|
});
|
|
896
1036
|
|
|
@@ -913,9 +1053,9 @@ export function parseFastScript(source, { file = "", mode = "lenient", recover =
|
|
|
913
1053
|
cst,
|
|
914
1054
|
tokens,
|
|
915
1055
|
diagnostics: normalizedDiagnostics,
|
|
916
|
-
transformedCode:
|
|
1056
|
+
transformedCode: finalGeneratedCode,
|
|
917
1057
|
sourceMap,
|
|
918
|
-
mapGeneratedToSource:
|
|
1058
|
+
mapGeneratedToSource: finalMapGeneratedToSource,
|
|
919
1059
|
rewrites: transformed.rewrites,
|
|
920
1060
|
source: text,
|
|
921
1061
|
};
|
|
@@ -3,6 +3,7 @@ import { extname, join, resolve } from "node:path";
|
|
|
3
3
|
import { parseFastScript } from "./fs-parser.mjs";
|
|
4
4
|
import { resolveErrorMeta } from "./fs-error-codes.mjs";
|
|
5
5
|
import { inferRouteMeta, inferRouteParamTypes, sortRoutesByPriority } from "./routes.mjs";
|
|
6
|
+
import { stripTypeScriptHints } from "./fs-normalize.mjs";
|
|
6
7
|
|
|
7
8
|
const APP_DIR = resolve("app");
|
|
8
9
|
const PAGES_DIR = resolve("app/pages");
|
|
@@ -1087,8 +1088,9 @@ function analyzeStatement(node, scope, state, fnContext) {
|
|
|
1087
1088
|
}
|
|
1088
1089
|
|
|
1089
1090
|
function analyzeFileTypes(file, source) {
|
|
1090
|
-
const
|
|
1091
|
-
const
|
|
1091
|
+
const text = String(source ?? "");
|
|
1092
|
+
const lineStarts = createLineStarts(text);
|
|
1093
|
+
const parsed = parseFastScript(text, { file, mode: "lenient", recover: true });
|
|
1092
1094
|
const diagnostics = [...parsed.diagnostics];
|
|
1093
1095
|
|
|
1094
1096
|
if (!parsed.estree || !Array.isArray(parsed.estree.body)) {
|
|
@@ -1097,7 +1099,7 @@ function analyzeFileTypes(file, source) {
|
|
|
1097
1099
|
|
|
1098
1100
|
scopeCounter = 0;
|
|
1099
1101
|
const rootScope = new Scope(null, "module");
|
|
1100
|
-
const state = { file, source, lineStarts, diagnostics, scopes: [rootScope], allowedRuntimes: inferAllowedRuntimeContextsFromFile(file) };
|
|
1102
|
+
const state = { file, source: text, lineStarts, diagnostics, scopes: [rootScope], allowedRuntimes: inferAllowedRuntimeContextsFromFile(file) };
|
|
1101
1103
|
|
|
1102
1104
|
declareBuiltins(rootScope, file);
|
|
1103
1105
|
hoistDeclarations(parsed.estree.body, rootScope, state);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastscript",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "FastScript v3 universal JS/TS runtime with first-class .fs source.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"publishConfig": {
|
|
@@ -27,8 +27,13 @@
|
|
|
27
27
|
"create:fullstack": "node ./src/cli.mjs create fullstack --template fullstack",
|
|
28
28
|
"check": "node ./src/cli.mjs check",
|
|
29
29
|
"migrate": "node ./src/cli.mjs migrate",
|
|
30
|
+
"convert": "node ./src/cli.mjs convert",
|
|
31
|
+
"migrate:rollback": "node ./src/cli.mjs migrate:rollback",
|
|
32
|
+
"manifest": "node ./src/cli.mjs manifest",
|
|
30
33
|
"wizard:migrate": "node ./src/cli.mjs wizard:migrate app/pages/index.fs",
|
|
31
34
|
"bench": "node ./src/cli.mjs bench",
|
|
35
|
+
"bench:discipline": "node ./src/cli.mjs bench:discipline",
|
|
36
|
+
"regression": "node ./src/cli.mjs regression --mode all",
|
|
32
37
|
"export:js": "node ./src/cli.mjs export --to js --out exported-js-app",
|
|
33
38
|
"export:ts": "node ./src/cli.mjs export --to ts --out exported-ts-app",
|
|
34
39
|
"compat": "node ./src/cli.mjs compat",
|
|
@@ -36,6 +41,10 @@
|
|
|
36
41
|
"repo:lock": "node ./scripts/ensure-canonical-repo.mjs",
|
|
37
42
|
"typecheck": "node ./src/cli.mjs typecheck --mode fail",
|
|
38
43
|
"typecheck:pass": "node ./src/cli.mjs typecheck --mode pass",
|
|
44
|
+
"profile": "node ./src/cli.mjs profile --command build --runs 1",
|
|
45
|
+
"trace": "node ./src/cli.mjs trace --pipeline check,typecheck,build,bench",
|
|
46
|
+
"diagnostics": "node ./src/cli.mjs diagnostics --mode report",
|
|
47
|
+
"permissions": "node ./src/cli.mjs permissions --mode report",
|
|
39
48
|
"format": "node ./src/cli.mjs format --write",
|
|
40
49
|
"format:check": "node ./src/cli.mjs format --check",
|
|
41
50
|
"lint:fs": "node ./src/cli.mjs lint --mode fail",
|
|
@@ -45,7 +54,7 @@
|
|
|
45
54
|
"db:rollback": "node ./src/cli.mjs db:rollback --count 1",
|
|
46
55
|
"deploy:node": "node ./src/cli.mjs deploy --target node",
|
|
47
56
|
"deploy:vercel": "node ./src/cli.mjs deploy --target vercel",
|
|
48
|
-
"deploy:cloudflare": "node ./
|
|
57
|
+
"deploy:cloudflare": "node ./scripts/deploy-cloudflare-site.mjs",
|
|
49
58
|
"worker": "node ./src/cli.mjs worker",
|
|
50
59
|
"worker:replay-dead-letter": "node ./src/cli.mjs worker replay-dead-letter --limit 50",
|
|
51
60
|
"style:generate": "node ./scripts/style-generate.mjs",
|
|
@@ -60,6 +69,17 @@
|
|
|
60
69
|
"test:db-cli": "node ./scripts/test-db-cli.mjs",
|
|
61
70
|
"test:fs-diag": "node ./scripts/test-fs-diagnostics.mjs",
|
|
62
71
|
"test:roundtrip": "node ./scripts/test-roundtrip.mjs",
|
|
72
|
+
"test:compatibility-first-runtime": "node ./scripts/test-compatibility-first-runtime.mjs",
|
|
73
|
+
"test:authored-ts-in-fs": "node ./scripts/test-authored-ts-in-fs.mjs",
|
|
74
|
+
"test:js-ts-syntax-proof": "node ./scripts/test-js-ts-syntax-proof.mjs",
|
|
75
|
+
"test:fs-parity-corpus": "node ./scripts/test-fs-parity-corpus.mjs",
|
|
76
|
+
"test:conversion-rollback-manifest": "node ./scripts/test-conversion-rollback-manifest.mjs",
|
|
77
|
+
"test:toolchain-observability": "node ./scripts/test-toolchain-observability.mjs",
|
|
78
|
+
"test:runtime-permissions": "node ./scripts/test-runtime-permissions.mjs",
|
|
79
|
+
"test:benchmark-discipline": "node ./scripts/test-benchmark-discipline.mjs",
|
|
80
|
+
"test:regression-guard": "node ./scripts/test-regression-guard.mjs",
|
|
81
|
+
"test:ecosystem-compatibility-contract": "node ./scripts/test-ecosystem-compatibility-contract.mjs",
|
|
82
|
+
"test:release-discipline": "node ./scripts/test-release-discipline.mjs",
|
|
63
83
|
"test:validation": "node ./scripts/test-validation.mjs",
|
|
64
84
|
"test:webhook-storage": "node ./scripts/test-webhook-storage.mjs",
|
|
65
85
|
"test:jobs": "node ./scripts/test-jobs.mjs",
|
|
@@ -109,7 +129,7 @@
|
|
|
109
129
|
"kpi:track": "node ./scripts/kpi-track.mjs",
|
|
110
130
|
"deploy:zero-downtime": "node ./scripts/zero-downtime-deploy.mjs",
|
|
111
131
|
"merge:gate": "node ./scripts/release-merge-gate.mjs",
|
|
112
|
-
"test:core": "npm run test:middleware && npm run test:auth && npm run test:db && npm run test:db-cli && npm run test:validation && npm run test:webhook-storage && npm run test:jobs && npm run test:plugins && npm run test:fs-diag && npm run test:metrics && npm run test:roundtrip && npm run test:sourcemap-fidelity && npm run test:cache-parity && npm run test:typecheck && npm run test:typecheck-depth && npm run test:format-lint && npm run test:style-rules && npm run test:style-primitives && npm run test:routes && npm run test:runtime-contract && npm run test:determinism && npm run test:parser-fuzz && npm run test:security-baseline && npm run test:deploy-adapters && npm run test:interop-matrix && npm run test:vscode-language && npm run test:runtime-context-rules && npm run test:runtime-scope-diagnostics && npm run test:v2:ambient-runtime && npm run test:v2:stdlib-methods && npm run test:v2:stdlib-matrix && npm run test:v2:dom-globals && npm run test:v2:dom-patterns && npm run test:v2:inference-corpus && npm run test:v2:inference-patterns && npm run test:v2:zero-js-app && npm run test:v2:zero-js-corpora && npm run test:conformance",
|
|
132
|
+
"test:core": "npm run test:middleware && npm run test:auth && npm run test:db && npm run test:db-cli && npm run test:validation && npm run test:webhook-storage && npm run test:jobs && npm run test:plugins && npm run test:fs-diag && npm run test:metrics && npm run test:roundtrip && npm run test:compatibility-first-runtime && npm run test:authored-ts-in-fs && npm run test:js-ts-syntax-proof && npm run test:fs-parity-corpus && npm run test:conversion-rollback-manifest && npm run test:toolchain-observability && npm run test:runtime-permissions && npm run test:benchmark-discipline && npm run test:regression-guard && npm run test:ecosystem-compatibility-contract && npm run test:release-discipline && npm run test:sourcemap-fidelity && npm run test:cache-parity && npm run test:typecheck && npm run test:typecheck-depth && npm run test:format-lint && npm run test:style-rules && npm run test:style-primitives && npm run test:routes && npm run test:runtime-contract && npm run test:determinism && npm run test:parser-fuzz && npm run test:security-baseline && npm run test:deploy-adapters && npm run test:interop-matrix && npm run test:vscode-language && npm run test:runtime-context-rules && npm run test:runtime-scope-diagnostics && npm run test:v2:ambient-runtime && npm run test:v2:stdlib-methods && npm run test:v2:stdlib-matrix && npm run test:v2:dom-globals && npm run test:v2:dom-patterns && npm run test:v2:inference-corpus && npm run test:v2:inference-patterns && npm run test:v2:zero-js-app && npm run test:v2:zero-js-corpora && npm run test:conformance",
|
|
113
133
|
"qa:gate": "npm run repo:lock && npm run format:check && npm run lint:fs && npm run typecheck && npm run validate && npm run test:core && npm run smoke:dev && npm run smoke:start",
|
|
114
134
|
"qa:all": "npm run repo:lock && npm run format:check && npm run lint:fs && npm run typecheck && npm run docs:index && npm run docs:api-ref && npm run validate && npm run test:core && npm run smoke:dev && npm run smoke:start && npm run bench:report && npm run benchmark:suite && npm run interop:report && npm run sbom:generate && npm run proof:publish && npm run backup:create && npm run backup:verify",
|
|
115
135
|
"release:patch": "node ./scripts/release.mjs patch",
|
|
@@ -118,7 +138,8 @@
|
|
|
118
138
|
"pack:check": "node ./scripts/prepare-npm-release.mjs --pack-dry-run",
|
|
119
139
|
"hooks:install": "node ./scripts/install-hooks.mjs",
|
|
120
140
|
"public:bundle": "node ./scripts/prepare-public-docs-bundle.mjs",
|
|
121
|
-
"release:npm:prepare": "node ./scripts/prepare-npm-release.mjs"
|
|
141
|
+
"release:npm:prepare": "node ./scripts/prepare-npm-release.mjs",
|
|
142
|
+
"deploy:cloudflare:adapter": "node ./src/cli.mjs deploy --target cloudflare"
|
|
122
143
|
},
|
|
123
144
|
"engines": {
|
|
124
145
|
"node": ">=20.0.0"
|