@ttsc/playground 0.30.4 → 0.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/lib/src/compiler/buildTsconfigJSON.d.ts +5 -0
- package/lib/src/compiler/buildTsconfigJSON.js +5 -0
- package/lib/src/compiler/buildTsconfigJSON.js.map +1 -1
- package/lib/src/compiler/createTypiaSourcePackMount.d.ts +9 -3
- package/lib/src/compiler/createTypiaSourcePackMount.js +9 -3
- package/lib/src/compiler/createTypiaSourcePackMount.js.map +1 -1
- package/lib/src/compiler/createWorkerCompiler.d.ts +5 -0
- package/lib/src/compiler/createWorkerCompiler.js +5 -0
- package/lib/src/compiler/createWorkerCompiler.js.map +1 -1
- package/lib/src/compiler/installDependenciesIntoMemFS.d.ts +5 -0
- package/lib/src/compiler/installDependenciesIntoMemFS.js +5 -0
- package/lib/src/compiler/installDependenciesIntoMemFS.js.map +1 -1
- package/lib/src/compiler/installTypiaSourcePack.d.ts +7 -2
- package/lib/src/compiler/installTypiaSourcePack.js +7 -2
- package/lib/src/compiler/installTypiaSourcePack.js.map +1 -1
- package/lib/src/compiler/internal/createWorkerCompilerService.d.ts +31 -0
- package/lib/src/compiler/internal/createWorkerCompilerService.js +22 -4
- package/lib/src/compiler/internal/createWorkerCompilerService.js.map +1 -1
- package/lib/src/compiler/internal/joinUnder.d.ts +5 -0
- package/lib/src/compiler/internal/joinUnder.js +5 -0
- package/lib/src/compiler/internal/joinUnder.js.map +1 -1
- package/lib/src/compiler/internal/parseLintDiagnostics.d.ts +5 -0
- package/lib/src/compiler/internal/parseLintDiagnostics.js +8 -3
- package/lib/src/compiler/internal/parseLintDiagnostics.js.map +1 -1
- package/lib/src/compiler/internal/safeParseTypiaTransform.d.ts +14 -1
- package/lib/src/compiler/internal/safeParseTypiaTransform.js +12 -1
- package/lib/src/compiler/internal/safeParseTypiaTransform.js.map +1 -1
- package/lib/src/compiler/lineColumnOf.d.ts +9 -1
- package/lib/src/compiler/lineColumnOf.js +23 -7
- package/lib/src/compiler/lineColumnOf.js.map +1 -1
- package/lib/src/compiler/loadTypiaSourcePack.d.ts +23 -1
- package/lib/src/compiler/loadTypiaSourcePack.js +48 -12
- package/lib/src/compiler/loadTypiaSourcePack.js.map +1 -1
- package/lib/src/compiler/mapDiagnostic.d.ts +7 -2
- package/lib/src/compiler/mapDiagnostic.js +53 -6
- package/lib/src/compiler/mapDiagnostic.js.map +1 -1
- package/lib/src/compiler/normalizeError.d.ts +9 -1
- package/lib/src/compiler/normalizeError.js +9 -1
- package/lib/src/compiler/normalizeError.js.map +1 -1
- package/lib/src/compiler/normalizeNodeModulePath.d.ts +5 -0
- package/lib/src/compiler/normalizeNodeModulePath.js +5 -0
- package/lib/src/compiler/normalizeNodeModulePath.js.map +1 -1
- package/lib/src/compiler/pickEmittedJS.d.ts +9 -2
- package/lib/src/compiler/pickEmittedJS.js +23 -6
- package/lib/src/compiler/pickEmittedJS.js.map +1 -1
- package/lib/src/npm/collectExternalPackageNames.d.ts +17 -0
- package/lib/src/npm/collectExternalPackageNames.js +51 -11
- package/lib/src/npm/collectExternalPackageNames.js.map +1 -1
- package/lib/src/npm/installPlaygroundDependencies.d.ts +10 -2
- package/lib/src/npm/installPlaygroundDependencies.js +18 -4
- package/lib/src/npm/installPlaygroundDependencies.js.map +1 -1
- package/lib/src/npm/internal/npmRegistry.d.ts +149 -2
- package/lib/src/npm/internal/npmRegistry.js +95 -4
- package/lib/src/npm/internal/npmRegistry.js.map +1 -1
- package/lib/src/npm/packageNameFromSpecifier.d.ts +5 -0
- package/lib/src/npm/packageNameFromSpecifier.js +7 -0
- package/lib/src/npm/packageNameFromSpecifier.js.map +1 -1
- package/lib/src/react/ConsoleViewer.d.ts +9 -0
- package/lib/src/react/ConsoleViewer.js +11 -2
- package/lib/src/react/ConsoleViewer.js.map +1 -1
- package/lib/src/react/DependencyProgressModal.d.ts +9 -0
- package/lib/src/react/DependencyProgressModal.js +9 -0
- package/lib/src/react/DependencyProgressModal.js.map +1 -1
- package/lib/src/react/DiagnosticsPanel.d.ts +9 -0
- package/lib/src/react/DiagnosticsPanel.js +9 -0
- package/lib/src/react/DiagnosticsPanel.js.map +1 -1
- package/lib/src/react/ExamplePicker.d.ts +9 -0
- package/lib/src/react/ExamplePicker.js +14 -2
- package/lib/src/react/ExamplePicker.js.map +1 -1
- package/lib/src/react/LintPane.d.ts +5 -0
- package/lib/src/react/LintPane.js +5 -0
- package/lib/src/react/LintPane.js.map +1 -1
- package/lib/src/react/OptionsPanel.d.ts +10 -0
- package/lib/src/react/OptionsPanel.js +19 -4
- package/lib/src/react/OptionsPanel.js.map +1 -1
- package/lib/src/react/PlaygroundShell.d.ts +13 -0
- package/lib/src/react/PlaygroundShell.js +17 -5
- package/lib/src/react/PlaygroundShell.js.map +1 -1
- package/lib/src/react/ResultViewer.d.ts +5 -0
- package/lib/src/react/ResultViewer.js +27 -7
- package/lib/src/react/ResultViewer.js.map +1 -1
- package/lib/src/react/SourceEditor.d.ts +10 -0
- package/lib/src/react/SourceEditor.js +10 -0
- package/lib/src/react/SourceEditor.js.map +1 -1
- package/lib/src/react/createCompilerClient.d.ts +5 -0
- package/lib/src/react/createCompilerClient.js +5 -0
- package/lib/src/react/createCompilerClient.js.map +1 -1
- package/lib/src/react/internal/PlaygroundCompilerLifecycle.d.ts +64 -1
- package/lib/src/react/internal/PlaygroundCompilerLifecycle.js +48 -0
- package/lib/src/react/internal/PlaygroundCompilerLifecycle.js.map +1 -1
- package/lib/src/react/internal/PlaygroundExecutionLifecycle.d.ts +42 -2
- package/lib/src/react/internal/PlaygroundExecutionLifecycle.js +18 -0
- package/lib/src/react/internal/PlaygroundExecutionLifecycle.js.map +1 -1
- package/lib/src/react/internal/recoverTerminalCompilerWorker.d.ts +46 -4
- package/lib/src/react/internal/recoverTerminalCompilerWorker.js +14 -1
- package/lib/src/react/internal/recoverTerminalCompilerWorker.js.map +1 -1
- package/lib/src/sandbox/createSandboxRequire.d.ts +12 -3
- package/lib/src/sandbox/createSandboxRequire.js +26 -12
- package/lib/src/sandbox/createSandboxRequire.js.map +1 -1
- package/lib/src/sandbox/loadTypiaRuntimePack.d.ts +22 -0
- package/lib/src/sandbox/loadTypiaRuntimePack.js +30 -1
- package/lib/src/sandbox/loadTypiaRuntimePack.js.map +1 -1
- package/lib/src/structures/IBuildTsconfigOptions.d.ts +9 -1
- package/lib/src/structures/ICompilerService.d.ts +112 -2
- package/lib/src/structures/IConsoleMessage.d.ts +5 -0
- package/lib/src/structures/ICreateCompilerClientOptions.d.ts +9 -2
- package/lib/src/structures/ICreateWorkerCompilerOptions.d.ts +11 -3
- package/lib/src/structures/IInstallTypiaSourcePackOptions.d.ts +7 -1
- package/lib/src/structures/ILintPluginConfig.d.ts +8 -1
- package/lib/src/structures/ILoadTypiaRuntimePackOptions.d.ts +8 -1
- package/lib/src/structures/IOptionToggle.d.ts +5 -0
- package/lib/src/structures/IPlaygroundDependencyInstallOptions.d.ts +41 -6
- package/lib/src/structures/IPlaygroundDependencyInstallResult.d.ts +8 -1
- package/lib/src/structures/IPlaygroundDependencyPackage.d.ts +9 -1
- package/lib/src/structures/IPlaygroundDependencyProgress.d.ts +9 -1
- package/lib/src/structures/IPlaygroundDependencyProgressPhase.d.ts +7 -2
- package/lib/src/structures/IPlaygroundDependencyRequest.d.ts +8 -1
- package/lib/src/structures/IPlaygroundExample.d.ts +5 -0
- package/lib/src/structures/IPlaygroundInstalledDependency.d.ts +8 -1
- package/lib/src/structures/IPlaygroundShellProps.d.ts +53 -19
- package/lib/src/structures/ISourceEditorProps.d.ts +16 -1
- package/lib/src/structures/ITransformOptions.d.ts +5 -0
- package/lib/src/structures/ITypiaPluginConfig.d.ts +25 -7
- package/package.json +6 -4
- package/src/compiler/buildTsconfigJSON.ts +5 -0
- package/src/compiler/createTypiaSourcePackMount.ts +9 -3
- package/src/compiler/createWorkerCompiler.ts +5 -0
- package/src/compiler/installDependenciesIntoMemFS.ts +5 -0
- package/src/compiler/installTypiaSourcePack.ts +7 -2
- package/src/compiler/internal/createWorkerCompilerService.ts +47 -4
- package/src/compiler/internal/joinUnder.ts +5 -0
- package/src/compiler/internal/parseLintDiagnostics.ts +8 -3
- package/src/compiler/internal/safeParseTypiaTransform.ts +25 -2
- package/src/compiler/lineColumnOf.ts +24 -8
- package/src/compiler/loadTypiaSourcePack.ts +57 -14
- package/src/compiler/mapDiagnostic.ts +67 -7
- package/src/compiler/normalizeError.ts +9 -1
- package/src/compiler/normalizeNodeModulePath.ts +5 -0
- package/src/compiler/pickEmittedJS.ts +22 -5
- package/src/npm/collectExternalPackageNames.ts +54 -13
- package/src/npm/installPlaygroundDependencies.ts +18 -6
- package/src/npm/internal/npmRegistry.ts +156 -4
- package/src/npm/packageNameFromSpecifier.ts +6 -0
- package/src/react/ConsoleViewer.tsx +10 -1
- package/src/react/DependencyProgressModal.tsx +9 -0
- package/src/react/DiagnosticsPanel.tsx +9 -0
- package/src/react/ExamplePicker.tsx +22 -6
- package/src/react/LintPane.tsx +5 -0
- package/src/react/OptionsPanel.tsx +22 -4
- package/src/react/PlaygroundShell.tsx +17 -5
- package/src/react/ResultViewer.tsx +28 -7
- package/src/react/SourceEditor.tsx +10 -0
- package/src/react/createCompilerClient.ts +5 -0
- package/src/react/internal/PlaygroundCompilerLifecycle.ts +64 -1
- package/src/react/internal/PlaygroundExecutionLifecycle.ts +44 -2
- package/src/react/internal/recoverTerminalCompilerWorker.ts +48 -4
- package/src/sandbox/createSandboxRequire.ts +28 -15
- package/src/sandbox/loadTypiaRuntimePack.ts +35 -2
- package/src/structures/IBuildTsconfigOptions.ts +13 -1
- package/src/structures/ICompilerService.ts +119 -2
- package/src/structures/IConsoleMessage.ts +5 -0
- package/src/structures/ICreateCompilerClientOptions.ts +9 -2
- package/src/structures/ICreateWorkerCompilerOptions.ts +15 -3
- package/src/structures/IInstallTypiaSourcePackOptions.ts +11 -1
- package/src/structures/ILintPluginConfig.ts +8 -1
- package/src/structures/ILoadTypiaRuntimePackOptions.ts +8 -1
- package/src/structures/IOptionToggle.ts +5 -0
- package/src/structures/IPlaygroundDependencyInstallOptions.ts +56 -6
- package/src/structures/IPlaygroundDependencyInstallResult.ts +12 -1
- package/src/structures/IPlaygroundDependencyPackage.ts +11 -1
- package/src/structures/IPlaygroundDependencyProgress.ts +9 -1
- package/src/structures/IPlaygroundDependencyProgressPhase.ts +7 -2
- package/src/structures/IPlaygroundDependencyRequest.ts +10 -1
- package/src/structures/IPlaygroundExample.ts +6 -0
- package/src/structures/IPlaygroundInstalledDependency.ts +11 -1
- package/src/structures/IPlaygroundShellProps.ts +62 -22
- package/src/structures/ISourceEditorProps.ts +20 -1
- package/src/structures/ITransformOptions.ts +7 -0
- package/src/structures/ITypiaPluginConfig.ts +31 -7
|
@@ -1,18 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pick the most likely emitted JavaScript file from a compile result's output
|
|
3
|
-
* map. Tries
|
|
4
|
-
*
|
|
3
|
+
* map. Tries the default playground layout (`rootDir` `src`, `outDir` `dist`,
|
|
4
|
+
* which emits `dist/playground.js` for `src/playground.ts`) and other common
|
|
5
|
+
* paths first, then falls back to the first `.js` entry. Returns null when no
|
|
6
|
+
* `.js` was emitted.
|
|
7
|
+
*
|
|
8
|
+
* @evidence contracts/common.md#principled-implementation The first candidate is the default playground layout (the entry's path below the default `src` root under `dist`), the others cover layouts rooted at the project; the first remaining JavaScript entry is an explicitly heuristic fallback, not a general entrypoint solver.
|
|
9
|
+
* @evidence contracts/common.md#clear-and-simple-design A small selector separates UI output choice from compiler emit configuration.
|
|
10
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Layout candidates are product defaults rather than fixture filenames, and absent JavaScript remains null.
|
|
11
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose honestly documents priority and heuristic fallback, separated from tags under the documentation skill.
|
|
5
12
|
*/
|
|
6
13
|
export function pickEmittedJS(
|
|
7
14
|
output: Record<string, string>,
|
|
8
15
|
entryFile: string,
|
|
9
16
|
): string | null {
|
|
10
17
|
const base = entryFile.replace(/\.[cm]?tsx?$/i, ".js");
|
|
11
|
-
|
|
18
|
+
// `buildTsconfigJSON` roots sources at `src` and emits to `dist`, so the
|
|
19
|
+
// default entry `src/playground.ts` is reported as `dist/playground.js`.
|
|
20
|
+
const underSrcRoot = base.replace(/^src\//, "");
|
|
21
|
+
const candidates = [
|
|
22
|
+
`dist/${underSrcRoot}`,
|
|
23
|
+
`dist/${base}`,
|
|
24
|
+
`dist/src/${base}`,
|
|
25
|
+
`src/${base}`,
|
|
26
|
+
base,
|
|
27
|
+
];
|
|
12
28
|
for (const key of candidates) {
|
|
13
29
|
if (output[key] !== undefined) return output[key];
|
|
14
30
|
}
|
|
15
|
-
const
|
|
16
|
-
|
|
31
|
+
for (const key of Object.keys(output)) {
|
|
32
|
+
if (key.endsWith(".js")) return output[key] ?? null;
|
|
33
|
+
}
|
|
17
34
|
return null;
|
|
18
35
|
}
|
|
@@ -4,6 +4,23 @@ import { packageNameFromSpecifier } from "./packageNameFromSpecifier";
|
|
|
4
4
|
/**
|
|
5
5
|
* Scan `source` for `import` / `require` specifiers and return the unique
|
|
6
6
|
* sorted list of bare npm package names that are not in `ignoredPackages`.
|
|
7
|
+
*
|
|
8
|
+
* This is a lexical discovery pass, not name binding: a locally shadowed direct
|
|
9
|
+
* require call still looks like a dependency request. Computed strings are
|
|
10
|
+
* omitted.
|
|
11
|
+
*
|
|
12
|
+
* Static arguments are quoted strings and substitution-free, escape-free
|
|
13
|
+
* templates (`require(`x`)`, `import(`x`)`). Deliberate limits of the
|
|
14
|
+
* TypeScript-source lane: JSX is not lexed (the playground entry is a `.ts`
|
|
15
|
+
* file), so quote or `import` text in JSX children can be misread; a type
|
|
16
|
+
* argument between the callee and its parenthesis (`require<T>("x")`) is not
|
|
17
|
+
* recognized because `require` is not generic; and a source-phase import
|
|
18
|
+
* (`import source x from "y"`) collects `y`, the package it loads.
|
|
19
|
+
*
|
|
20
|
+
* @evidence contracts/common.md#principled-implementation Tokenization distinguishes executable quoted and static-template specifiers from inert comments, strings and regex bodies; package-name normalization and a Set produce unique sorted install requests. Lexical discovery does not resolve shadowed require bindings.
|
|
21
|
+
* @evidence contracts/common.md#clear-and-simple-design Lexer, module-construct recognition and package filtering are separate local responsibilities, without importing the full compiler into browser keystroke discovery.
|
|
22
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Ignored package names are explicit caller policy; source matching does not fabricate dependency results for known examples.
|
|
23
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states discovery domain and binding limitations; helper comments explain escaped strings and lexical boundaries under the documentation skill.
|
|
7
24
|
*/
|
|
8
25
|
export function collectExternalPackageNames(
|
|
9
26
|
source: string,
|
|
@@ -33,14 +50,26 @@ export function collectExternalPackageNames(
|
|
|
33
50
|
function collectModuleSpecifiers(source: string): string[] {
|
|
34
51
|
const tokens = tokenize(source);
|
|
35
52
|
const out: string[] = [];
|
|
53
|
+
// A quoted literal only; `import "x"` and `from "x"` cannot take a template.
|
|
54
|
+
const asQuoted = (token: Token | undefined): string | null =>
|
|
55
|
+
token && token.kind === "string" && !token.template ? token.value : null;
|
|
56
|
+
// A call argument may also be a static template: `require(`x`)`.
|
|
36
57
|
const asString = (token: Token | undefined): string | null =>
|
|
37
58
|
token && token.kind === "string" ? token.value : null;
|
|
38
59
|
const isOpenParen = (token: Token | undefined): boolean =>
|
|
39
60
|
token !== undefined && token.kind === "punct" && token.value === "(";
|
|
40
|
-
const
|
|
41
|
-
token !== undefined &&
|
|
42
|
-
|
|
43
|
-
|
|
61
|
+
const isPunct = (token: Token | undefined, value: string): boolean =>
|
|
62
|
+
token !== undefined && token.kind === "punct" && token.value === value;
|
|
63
|
+
// `obj.name`, `obj?.name` and `this.#name` are not the free binding. The
|
|
64
|
+
// three dots of a spread are not a member access.
|
|
65
|
+
const isMemberAccessAt = (index: number): boolean => {
|
|
66
|
+
const previous = tokens[index - 1];
|
|
67
|
+
if (isPunct(previous, "#") || isPunct(previous, "?.")) return true;
|
|
68
|
+
if (!isPunct(previous, ".")) return false;
|
|
69
|
+
return !(
|
|
70
|
+
isPunct(tokens[index - 2], ".") && isPunct(tokens[index - 3], ".")
|
|
71
|
+
);
|
|
72
|
+
};
|
|
44
73
|
const isOptionalChain = (token: Token | undefined): boolean =>
|
|
45
74
|
token !== undefined && token.kind === "punct" && token.value === "?.";
|
|
46
75
|
|
|
@@ -50,7 +79,7 @@ function collectModuleSpecifiers(source: string): string[] {
|
|
|
50
79
|
|
|
51
80
|
if (token.value === "require") {
|
|
52
81
|
// `obj.require(...)` is an unrelated method call, not CommonJS require.
|
|
53
|
-
if (
|
|
82
|
+
if (isMemberAccessAt(i)) continue;
|
|
54
83
|
const optional =
|
|
55
84
|
isOptionalChain(tokens[i + 1]) && isOpenParen(tokens[i + 2]);
|
|
56
85
|
if (isOpenParen(tokens[i + 1]) || optional) {
|
|
@@ -62,7 +91,7 @@ function collectModuleSpecifiers(source: string): string[] {
|
|
|
62
91
|
|
|
63
92
|
if (token.value === "import" || token.value === "export") {
|
|
64
93
|
// `foo.import(...)` / `import.meta` are not module-loading imports.
|
|
65
|
-
if (token.value === "import" &&
|
|
94
|
+
if (token.value === "import" && isMemberAccessAt(i)) continue;
|
|
66
95
|
// Dynamic `import("x")`.
|
|
67
96
|
if (token.value === "import" && isOpenParen(tokens[i + 1])) {
|
|
68
97
|
const spec = asString(tokens[i + 2]);
|
|
@@ -71,7 +100,7 @@ function collectModuleSpecifiers(source: string): string[] {
|
|
|
71
100
|
}
|
|
72
101
|
// Side-effect `import "x"`.
|
|
73
102
|
if (token.value === "import") {
|
|
74
|
-
const bare =
|
|
103
|
+
const bare = asQuoted(tokens[i + 1]);
|
|
75
104
|
if (bare !== null) {
|
|
76
105
|
out.push(bare);
|
|
77
106
|
continue;
|
|
@@ -103,7 +132,9 @@ function findFromSpecifier(tokens: Token[], start: number): string | null {
|
|
|
103
132
|
return null;
|
|
104
133
|
if (token.kind === "word" && token.value === "from") {
|
|
105
134
|
const next = tokens[i + 1];
|
|
106
|
-
return next && next.kind === "string"
|
|
135
|
+
return next && next.kind === "string" && !next.template
|
|
136
|
+
? next.value
|
|
137
|
+
: null;
|
|
107
138
|
}
|
|
108
139
|
}
|
|
109
140
|
return null;
|
|
@@ -114,14 +145,16 @@ type Token =
|
|
|
114
145
|
| { kind: "word"; value: string }
|
|
115
146
|
// A single- or double-quoted string literal, with escapes decoded to their
|
|
116
147
|
// literal characters so a specifier survives unchanged.
|
|
117
|
-
| { kind: "string"; value: string }
|
|
148
|
+
| { kind: "string"; value: string; template?: true }
|
|
118
149
|
// A punctuation token; compound forms are retained where lexical state or
|
|
119
150
|
// module-call recognition depends on them.
|
|
120
151
|
| {
|
|
121
152
|
kind: "punct";
|
|
122
153
|
value: string;
|
|
154
|
+
|
|
123
155
|
/** Whether a slash after this closing delimiter begins a regex literal. */
|
|
124
156
|
regexAllowedAfter?: boolean;
|
|
157
|
+
|
|
125
158
|
/** Whether this closes a function parameter list for an expression. */
|
|
126
159
|
functionBodyIsExpression?: boolean;
|
|
127
160
|
}
|
|
@@ -362,7 +395,7 @@ function tokenize(source: string): Token[] {
|
|
|
362
395
|
// Line comment.
|
|
363
396
|
if (c === "/" && source[i + 1] === "/") {
|
|
364
397
|
i += 2;
|
|
365
|
-
while (i < n && source[i]
|
|
398
|
+
while (i < n && !isLineTerminator(source[i]!)) i++;
|
|
366
399
|
continue;
|
|
367
400
|
}
|
|
368
401
|
// Block comment.
|
|
@@ -390,9 +423,12 @@ function tokenize(source: string): Token[] {
|
|
|
390
423
|
// JavaScript and must receive the same lexical treatment as top-level code.
|
|
391
424
|
if (c === "`") {
|
|
392
425
|
i++;
|
|
426
|
+
// A template with no substitution and no escape is one static string.
|
|
427
|
+
let staticValue: string | null = "";
|
|
393
428
|
while (i < n) {
|
|
394
429
|
const d = source[i];
|
|
395
430
|
if (d === "\\") {
|
|
431
|
+
staticValue = null;
|
|
396
432
|
i += 2;
|
|
397
433
|
continue;
|
|
398
434
|
}
|
|
@@ -403,15 +439,20 @@ function tokenize(source: string): Token[] {
|
|
|
403
439
|
if (d === "$" && source[i + 1] === "{") {
|
|
404
440
|
const start = i + 2;
|
|
405
441
|
const end = findTemplateSubstitutionEnd(source, start);
|
|
442
|
+
staticValue = null;
|
|
406
443
|
context.pushOther();
|
|
407
|
-
|
|
444
|
+
for (const entryToAppend of tokenize(source.slice(start, end)))
|
|
445
|
+
tokens.push(entryToAppend);
|
|
408
446
|
context.pushOther();
|
|
409
447
|
i = end < n ? end + 1 : end;
|
|
410
448
|
continue;
|
|
411
449
|
}
|
|
450
|
+
if (staticValue !== null) staticValue += d;
|
|
412
451
|
i++;
|
|
413
452
|
}
|
|
414
|
-
|
|
453
|
+
if (staticValue !== null && source[i - 1] === "`")
|
|
454
|
+
tokens.push({ kind: "string", value: staticValue, template: true });
|
|
455
|
+
else context.pushOther();
|
|
415
456
|
continue;
|
|
416
457
|
}
|
|
417
458
|
// Identifier / keyword.
|
|
@@ -475,7 +516,7 @@ function findTemplateSubstitutionEnd(source: string, start: number): number {
|
|
|
475
516
|
}
|
|
476
517
|
if (c === "/" && source[i + 1] === "/") {
|
|
477
518
|
i += 2;
|
|
478
|
-
while (i < source.length && source[i]
|
|
519
|
+
while (i < source.length && !isLineTerminator(source[i]!)) i++;
|
|
479
520
|
continue;
|
|
480
521
|
}
|
|
481
522
|
if (c === "/" && source[i + 1] === "*") {
|
|
@@ -32,8 +32,16 @@ const DEFAULT_MAX_UNPACKED_BYTES = 64 * 1024 * 1024;
|
|
|
32
32
|
* required, optional, and peer dependency fields. Passing a prior call's
|
|
33
33
|
* `resolvedDependencies` validates new edges against the exact mounted graph
|
|
34
34
|
* and reuses compatible packages without downloading their tarballs again. The
|
|
35
|
-
* walk is bounded by `maxPackages` to keep a
|
|
36
|
-
* the tab's network/memory budget.
|
|
35
|
+
* walk is bounded by the nonnegative safe-integer `maxPackages` to keep a
|
|
36
|
+
* single keystroke from exhausting the tab's network/memory budget. The cap
|
|
37
|
+
* counts distinct names completed in this call, including mounted packages
|
|
38
|
+
* revalidated and optional packages omitted. Unrequested mounted state does not
|
|
39
|
+
* consume this call's budget; zero permits no queued package work.
|
|
40
|
+
*
|
|
41
|
+
* @evidence contracts/common.md#principled-implementation The queue unifies required ranges per exposed name and registry identity, pins reused exact versions and rejects incompatible required edges. Optional edges refine only compatible solves; verified tar bytes are confined before files enter consumer namespaces.
|
|
42
|
+
* @evidence contracts/common.md#clear-and-simple-design Graph coordination stays here while registry transport, version selection, archive validation and file mapping have explicit helper boundaries.
|
|
43
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Optional omissions are declared npm semantics; required failures, alias conflicts and integrity failures remain failures instead of fabricated mounted state. Name-only legacy skip state is documented as unable to validate versions.
|
|
44
|
+
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain prior graph reuse and package budgets; queue comments explain late constraints and optional-to-required transitions under the documentation skill.
|
|
37
45
|
*/
|
|
38
46
|
export async function installPlaygroundDependencies(
|
|
39
47
|
packageNames: Iterable<string>,
|
|
@@ -44,6 +52,10 @@ export async function installPlaygroundDependencies(
|
|
|
44
52
|
throw new Error("installPlaygroundDependencies requires fetch.");
|
|
45
53
|
}
|
|
46
54
|
throwIfAborted(options.signal);
|
|
55
|
+
const maxPackages = options.maxPackages ?? DEFAULT_MAX_PACKAGES;
|
|
56
|
+
if (!Number.isSafeInteger(maxPackages) || maxPackages < 0) {
|
|
57
|
+
throw new Error("maxPackages must be a non-negative safe integer.");
|
|
58
|
+
}
|
|
47
59
|
|
|
48
60
|
const ignored = new Set(
|
|
49
61
|
options.ignoredPackages ?? BUILT_IN_PLAYGROUND_PACKAGES,
|
|
@@ -65,9 +77,10 @@ export async function installPlaygroundDependencies(
|
|
|
65
77
|
);
|
|
66
78
|
}
|
|
67
79
|
if (previous !== undefined) {
|
|
68
|
-
|
|
69
|
-
...
|
|
70
|
-
)
|
|
80
|
+
for (const entryToAppend of dependency.requests.map((request) => ({
|
|
81
|
+
...request,
|
|
82
|
+
})))
|
|
83
|
+
previous.requests.push(entryToAppend);
|
|
71
84
|
continue;
|
|
72
85
|
}
|
|
73
86
|
installedDependencies.set(dependency.name, {
|
|
@@ -75,7 +88,6 @@ export async function installPlaygroundDependencies(
|
|
|
75
88
|
requests: dependency.requests.map((request) => ({ ...request })),
|
|
76
89
|
});
|
|
77
90
|
}
|
|
78
|
-
const maxPackages = options.maxPackages ?? DEFAULT_MAX_PACKAGES;
|
|
79
91
|
const maxTarballBytes = options.maxTarballBytes ?? DEFAULT_MAX_TARBALL_BYTES;
|
|
80
92
|
const maxUnpackedBytes =
|
|
81
93
|
options.maxUnpackedBytes ?? DEFAULT_MAX_UNPACKED_BYTES;
|
|
@@ -14,6 +14,15 @@ interface IPackageJson {
|
|
|
14
14
|
peerDependenciesMeta?: Record<string, { optional?: boolean }>;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Version-specific registry manifest; absent dist metadata cannot supply a
|
|
19
|
+
* tarball.
|
|
20
|
+
*
|
|
21
|
+
* @evidence contracts/common.md#principled-implementation Identity, dependency fields and optional distribution witnesses express the registry subset consumed by version selection and authentication.
|
|
22
|
+
* @evidence contracts/common.md#clear-and-simple-design Registry metadata remains separate from unpacked files and mounted state.
|
|
23
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Optional authentication fields represent historical metadata compatibility, not fabricated integrity evidence.
|
|
24
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states subset and absent distribution meaning, separated from tags under the documentation skill.
|
|
25
|
+
*/
|
|
17
26
|
export interface INpmVersionMetadata {
|
|
18
27
|
name: string;
|
|
19
28
|
version: string;
|
|
@@ -28,17 +37,42 @@ export interface INpmVersionMetadata {
|
|
|
28
37
|
};
|
|
29
38
|
}
|
|
30
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Registry packument subset, indexed by exact version with optional tag
|
|
42
|
+
* aliases.
|
|
43
|
+
*
|
|
44
|
+
* @evidence contracts/common.md#principled-implementation Version entries and dist-tag mappings provide the domain over which semver constraints are resolved.
|
|
45
|
+
* @evidence contracts/common.md#clear-and-simple-design The packument index is independent of a particular install request or mounted graph.
|
|
46
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Missing entries remain representable instead of inventing a version from a requested tag.
|
|
47
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose defines version indexing and tag purpose with tag separation under the documentation skill.
|
|
48
|
+
*/
|
|
31
49
|
export interface INpmMetadata {
|
|
32
50
|
name: string;
|
|
33
51
|
"dist-tags"?: Record<string, string>;
|
|
34
52
|
versions: Record<string, INpmVersionMetadata | undefined>;
|
|
35
53
|
}
|
|
36
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Confined package-relative text files and the decoded manifest subset.
|
|
57
|
+
*
|
|
58
|
+
* @evidence contracts/common.md#principled-implementation Files omit the safe archive root while packageJson supplies dependency metadata for graph traversal.
|
|
59
|
+
* @evidence contracts/common.md#clear-and-simple-design Extraction output is separate from consumer-specific mounted path maps.
|
|
60
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Archive contents determine files without consumer-specific manufactured declarations.
|
|
61
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose defines confinement and path namespace with tag separation under the documentation skill.
|
|
62
|
+
*/
|
|
37
63
|
export interface IUnpackedPackage {
|
|
38
64
|
files: Record<string, string>;
|
|
39
65
|
packageJson: IPackageJson;
|
|
40
66
|
}
|
|
41
67
|
|
|
68
|
+
/**
|
|
69
|
+
* One exposed package queued for registry solving, retaining all active edges.
|
|
70
|
+
*
|
|
71
|
+
* @evidence contracts/common.md#principled-implementation Exposed name and optional registryName distinguish aliases; requests preserve intersecting ranges and origin while optionality describes whether omission is permitted.
|
|
72
|
+
* @evidence contracts/common.md#clear-and-simple-design Mutable queue coordination is isolated from published immutable identity records.
|
|
73
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Explicit edge state prevents silently ignoring a late required constraint.
|
|
74
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states queue role and active-edge retention with tag separation under the documentation skill.
|
|
75
|
+
*/
|
|
42
76
|
export interface IQueueItem {
|
|
43
77
|
name: string;
|
|
44
78
|
range: string;
|
|
@@ -48,12 +82,28 @@ export interface IQueueItem {
|
|
|
48
82
|
requests?: IVersionRequest[];
|
|
49
83
|
}
|
|
50
84
|
|
|
85
|
+
/**
|
|
86
|
+
* A normalized semver range or registry tag together with requesting origin.
|
|
87
|
+
*
|
|
88
|
+
* @evidence contracts/common.md#principled-implementation Range and origin retain the constraint while optional marks whether incompatible omission is allowed.
|
|
89
|
+
* @evidence contracts/common.md#clear-and-simple-design This edge record is independent of mounted package identity and registry transport.
|
|
90
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Optionality is a declared graph distinction, not guessed from a missing package response.
|
|
91
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states range, origin and omission role with tag separation under the documentation skill.
|
|
92
|
+
*/
|
|
51
93
|
export interface IVersionRequest {
|
|
52
94
|
optional: boolean;
|
|
53
95
|
range: string;
|
|
54
96
|
requester: string;
|
|
55
97
|
}
|
|
56
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Standard string-URL fetch transport used by registry and tarball requests.
|
|
101
|
+
*
|
|
102
|
+
* @evidence contracts/common.md#principled-implementation RequestInit and Response preserve status, headers, abort and body contracts needed by browser installation.
|
|
103
|
+
* @evidence contracts/common.md#clear-and-simple-design A named callable signature keeps transport injectable without exposing solver state.
|
|
104
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Injection uses an explicit callback instead of replacing global transport internals.
|
|
105
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose defines transport responsibility with tag separation under the documentation skill.
|
|
106
|
+
*/
|
|
57
107
|
export type FetchLike = (
|
|
58
108
|
input: string,
|
|
59
109
|
init?: RequestInit,
|
|
@@ -69,12 +119,30 @@ const TEXT_FILE_REGEXP =
|
|
|
69
119
|
export const DECLARATION_FILE_REGEXP = /\.d\.[cm]?ts$/i;
|
|
70
120
|
const RUNTIME_FILE_REGEXP = /(^package\.json$|\.([cm]?js|json)$)/i;
|
|
71
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Preserve the signal's abort reason; use AbortError only when no reason
|
|
124
|
+
* exists.
|
|
125
|
+
*
|
|
126
|
+
* @evidence contracts/common.md#principled-implementation AbortSignal.aborted gates rejection and its reason remains the deciding thrown value.
|
|
127
|
+
* @evidence contracts/common.md#clear-and-simple-design One guard centralizes cancellation semantics across transport and archive phases.
|
|
128
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Cancellation is caller policy rather than a fabricated timeout or successful empty result.
|
|
129
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose explains reason preservation with tag separation under the documentation skill.
|
|
130
|
+
*/
|
|
72
131
|
export function throwIfAborted(signal: AbortSignal | undefined): void {
|
|
73
132
|
if (!signal?.aborted) return;
|
|
74
133
|
if (signal.reason !== undefined) throw signal.reason;
|
|
75
134
|
throw new DOMException("The operation was aborted.", "AbortError");
|
|
76
135
|
}
|
|
77
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Fetch the public registry packument; only an optional package's 404 is
|
|
139
|
+
* omitted.
|
|
140
|
+
*
|
|
141
|
+
* @evidence contracts/common.md#principled-implementation Encoded package identity forms the registry URL; status handling distinguishes an allowed optional absence from transport failure and decodes the requested metadata.
|
|
142
|
+
* @evidence contracts/common.md#clear-and-simple-design Transport and abort helpers serve one metadata request while version solving stays separate.
|
|
143
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Only the declared optional-404 contract yields null; other failures cannot masquerade as installed metadata.
|
|
144
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose identifies registry ownership and omission semantics with tag separation under the documentation skill.
|
|
145
|
+
*/
|
|
78
146
|
export async function fetchNpmMetadata(
|
|
79
147
|
fetchImpl: FetchLike,
|
|
80
148
|
packageName: string,
|
|
@@ -115,6 +183,15 @@ export async function fetchNpmMetadata(
|
|
|
115
183
|
return metadata;
|
|
116
184
|
}
|
|
117
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Select the highest published exact version satisfying every requested range;
|
|
188
|
+
* tags resolve through registry metadata and missing candidates throw.
|
|
189
|
+
*
|
|
190
|
+
* @evidence contracts/common.md#principled-implementation Semver validates ranges and tag mappings, then filters published versions by the intersection before highest-version selection.
|
|
191
|
+
* @evidence contracts/common.md#clear-and-simple-design This pure selector owns constraint intersection while callers own optional edges and mounted-version pins.
|
|
192
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts No package-specific version is chosen and incompatible constraints remain an error rather than an arbitrary latest fallback.
|
|
193
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose defines highest-version and failure semantics with tag separation under the documentation skill.
|
|
194
|
+
*/
|
|
118
195
|
export function selectVersion(
|
|
119
196
|
metadata: INpmMetadata,
|
|
120
197
|
ranges: readonly string[] | string,
|
|
@@ -145,7 +222,9 @@ export function selectVersion(
|
|
|
145
222
|
semverRanges.every((range) => satisfies(version, range)) &&
|
|
146
223
|
(taggedVersions.size === 0 || taggedVersions.has(version)),
|
|
147
224
|
);
|
|
148
|
-
|
|
225
|
+
// Candidates already satisfy every requested constraint, including the
|
|
226
|
+
// explicit prerelease admission of a range or exact registry tag.
|
|
227
|
+
const selected = maxSatisfying(candidates, "*", { includePrerelease: true });
|
|
149
228
|
if (selected) return selected;
|
|
150
229
|
throw new Error(
|
|
151
230
|
`No version of ${metadata.name} satisfies ${requested
|
|
@@ -154,6 +233,15 @@ export function selectVersion(
|
|
|
154
233
|
);
|
|
155
234
|
}
|
|
156
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Read compressed archive bytes within the validated budget; cancel unused
|
|
238
|
+
* bodies on status failure, excessive declared length or cancellation.
|
|
239
|
+
*
|
|
240
|
+
* @evidence contracts/common.md#principled-implementation Header checks reject obvious excess while streamed accounting enforces actual compressed bytes even when Content-Length is absent or inaccurate.
|
|
241
|
+
* @evidence contracts/common.md#clear-and-simple-design Fetching and body-budget enforcement delegate cancellation and bounded collection to shared helpers.
|
|
242
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Every archive receives the same budget and failures remain visible without returning truncated synthetic success.
|
|
243
|
+
* @evidence contracts/common.md#meaningful-documentation Native paragraphs state byte domain and body disposal with tag separation under the documentation skill.
|
|
244
|
+
*/
|
|
157
245
|
export async function downloadTarball(
|
|
158
246
|
fetchImpl: FetchLike,
|
|
159
247
|
tarball: string,
|
|
@@ -190,7 +278,16 @@ export async function downloadTarball(
|
|
|
190
278
|
);
|
|
191
279
|
}
|
|
192
280
|
|
|
193
|
-
/**
|
|
281
|
+
/**
|
|
282
|
+
* Verify registry authentication metadata against compressed bytes. The
|
|
283
|
+
* strongest supported SRI group wins; absent SRI falls back to shasum, and
|
|
284
|
+
* absent both is accepted without an authentication claim.
|
|
285
|
+
*
|
|
286
|
+
* @evidence contracts/common.md#principled-implementation Web Crypto digests the actual compressed bytes and equalBytes compares against the strongest supported witnesses; malformed metadata or mismatch rejects.
|
|
287
|
+
* @evidence contracts/common.md#clear-and-simple-design Authentication precedes decompression and keeps digest parsing separate from byte comparison.
|
|
288
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Historical unauthenticated metadata is explicitly supported; missing witnesses are not fabricated and weaker hashes cannot override a failing stronger witness.
|
|
289
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states hash precedence and unauthenticated compatibility with tag separation under the documentation skill.
|
|
290
|
+
*/
|
|
194
291
|
export async function verifyTarball(
|
|
195
292
|
tgz: ArrayBuffer,
|
|
196
293
|
dist: { integrity?: string; shasum?: string },
|
|
@@ -199,7 +296,10 @@ export async function verifyTarball(
|
|
|
199
296
|
throwIfAborted(signal);
|
|
200
297
|
if (dist.integrity !== undefined) {
|
|
201
298
|
const candidates = parseIntegrity(dist.integrity);
|
|
202
|
-
const strength =
|
|
299
|
+
const strength = candidates.reduce(
|
|
300
|
+
(maximum, { rank }) => Math.max(maximum, rank),
|
|
301
|
+
0,
|
|
302
|
+
);
|
|
203
303
|
const strongest = candidates.filter(
|
|
204
304
|
(candidate) => candidate.rank === strength,
|
|
205
305
|
);
|
|
@@ -231,6 +331,16 @@ export async function verifyTarball(
|
|
|
231
331
|
}
|
|
232
332
|
}
|
|
233
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Expand one bounded gzip archive and extract supported text files below one
|
|
336
|
+
* safe archive root. Unsupported tar entry kinds are omitted; truncated bodies,
|
|
337
|
+
* unsafe paths and missing end markers reject the archive.
|
|
338
|
+
*
|
|
339
|
+
* @evidence contracts/common.md#principled-implementation Safe integer sizes and padded extents bound every tar slice; PAX and GNU path overrides still pass the common confinement gate before text decoding.
|
|
340
|
+
* @evidence contracts/common.md#clear-and-simple-design The sequential tar reader delegates gzip budgeting, path validation and header decoding to focused local helpers.
|
|
341
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Supported metadata paths use format rules rather than known package names; unsafe entries are rejected instead of redirected into a guessed mount.
|
|
342
|
+
* @evidence contracts/common.md#meaningful-documentation Native paragraphs state extraction domain and failure boundaries; confinement comments explain variable npm roots under the documentation skill.
|
|
343
|
+
*/
|
|
234
344
|
export async function unpackNpmTarball(
|
|
235
345
|
tgz: ArrayBuffer,
|
|
236
346
|
signal: AbortSignal | undefined,
|
|
@@ -336,12 +446,29 @@ async function gunzip(
|
|
|
336
446
|
);
|
|
337
447
|
}
|
|
338
448
|
|
|
449
|
+
/**
|
|
450
|
+
* One package's files mapped to compiler, editor URI and runtime namespaces.
|
|
451
|
+
*
|
|
452
|
+
* @evidence contracts/common.md#principled-implementation Distinct string maps carry the path conventions expected by each consumer without changing the source text.
|
|
453
|
+
* @evidence contracts/common.md#clear-and-simple-design The three named lanes make downstream mounting explicit and avoid repeated namespace inference.
|
|
454
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Namespace mapping follows consumer protocols rather than per-package exceptions.
|
|
455
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose defines the consumer lanes with tag separation under the documentation skill.
|
|
456
|
+
*/
|
|
339
457
|
export interface IMountedFiles {
|
|
340
458
|
compilerFiles: Record<string, string>;
|
|
341
459
|
editorLibs: Record<string, string>;
|
|
342
460
|
runtimeFiles: Record<string, string>;
|
|
343
461
|
}
|
|
344
462
|
|
|
463
|
+
/**
|
|
464
|
+
* Map confined relative files to consumer namespaces, selecting declarations
|
|
465
|
+
* for Monaco and JavaScript/JSON for the CommonJS runtime pack.
|
|
466
|
+
*
|
|
467
|
+
* @evidence contracts/common.md#principled-implementation Each source file gets a compiler key; extension predicates select appropriate editor and runtime content without coercing text.
|
|
468
|
+
* @evidence contracts/common.md#clear-and-simple-design One traversal constructs all three views while archive confinement stays with extraction.
|
|
469
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Extension selection applies uniformly to packages rather than synthesizing missing declarations or runtime code.
|
|
470
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose defines input provenance and view selection with tag separation under the documentation skill.
|
|
471
|
+
*/
|
|
345
472
|
export function mountPackageFiles(
|
|
346
473
|
packageName: string,
|
|
347
474
|
files: Record<string, string>,
|
|
@@ -364,6 +491,15 @@ export function mountPackageFiles(
|
|
|
364
491
|
return { compilerFiles, editorLibs, runtimeFiles };
|
|
365
492
|
}
|
|
366
493
|
|
|
494
|
+
/**
|
|
495
|
+
* Publish supported registry edges: optional dependencies override regular
|
|
496
|
+
* dependencies of the same name; optional peers are not installed.
|
|
497
|
+
*
|
|
498
|
+
* @evidence contracts/common.md#principled-implementation npm dependency precedence and peer optionality determine edge requirements; registry range classification excludes unsupported source transports.
|
|
499
|
+
* @evidence contracts/common.md#clear-and-simple-design This operation emits normalized graph edges while queue deduplication and version solving remain with the installer.
|
|
500
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Required peers remain required; unsupported non-registry sources are omitted as a declared browser-installer limitation, not silently claimed installed.
|
|
501
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states dependency precedence and optional peers; inline comments explain required-peer propagation under the documentation skill.
|
|
502
|
+
*/
|
|
367
503
|
export function enqueuePackageDependencies(
|
|
368
504
|
packageJson: {
|
|
369
505
|
dependencies?: Record<string, string>;
|
|
@@ -416,6 +552,14 @@ function isRegistryRange(range: string): boolean {
|
|
|
416
552
|
return spec.length > 0 && encodeURIComponent(spec) === spec;
|
|
417
553
|
}
|
|
418
554
|
|
|
555
|
+
/**
|
|
556
|
+
* Convert an npm identity to DefinitelyTyped spelling, flattening scoped names.
|
|
557
|
+
*
|
|
558
|
+
* @evidence contracts/common.md#principled-implementation Unscoped names gain @types/ and valid scoped names use the established scope__name convention.
|
|
559
|
+
* @evidence contracts/common.md#clear-and-simple-design One name mapping stays separate from whether a declaration fallback is needed.
|
|
560
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The convention applies to every package identity rather than maintaining a list of known declaration packages.
|
|
561
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose defines the mapping purpose with tag separation under the documentation skill.
|
|
562
|
+
*/
|
|
419
563
|
export function toTypesPackageName(packageName: string): string {
|
|
420
564
|
if (!packageName.startsWith("@")) return `@types/${packageName}`;
|
|
421
565
|
const [scope, name] = packageName.slice(1).split("/");
|
|
@@ -665,7 +809,15 @@ function formatByteLimit(bytes: number): string {
|
|
|
665
809
|
return `${bytes.toLocaleString("en-US")}-byte`;
|
|
666
810
|
}
|
|
667
811
|
|
|
668
|
-
/**
|
|
812
|
+
/**
|
|
813
|
+
* Validate an archive byte budget as a positive safe integer before related
|
|
814
|
+
* work.
|
|
815
|
+
*
|
|
816
|
+
* @evidence contracts/common.md#principled-implementation Safe positive integers support exact byte accounting and a branded result distinguishes budgets that passed the numeric gate.
|
|
817
|
+
* @evidence contracts/common.md#clear-and-simple-design One validation gate serves compressed and expanded budgets without duplicating numeric policy.
|
|
818
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Validation is uniform and rejects invalid limits instead of selecting hidden fixture-specific defaults.
|
|
819
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states units, positivity and validation timing with tag separation under the documentation skill.
|
|
820
|
+
*/
|
|
669
821
|
export function validateNpmByteLimit(
|
|
670
822
|
maxBytes: number,
|
|
671
823
|
kind: "compressed" | "expanded",
|
|
@@ -48,6 +48,11 @@ const BUILTIN_MODULES = new Set([
|
|
|
48
48
|
*
|
|
49
49
|
* Returns `null` for relative paths, hash imports, URL specifiers, and Node
|
|
50
50
|
* built-in modules — the caller doesn't install those from npm.
|
|
51
|
+
*
|
|
52
|
+
* @evidence contracts/common.md#principled-implementation Prefix classification removes non-registry inputs and scoped-name slicing keeps both scope and package while discarding subpaths.
|
|
53
|
+
* @evidence contracts/common.md#clear-and-simple-design One classifier isolates package identity from dependency scanning and installation.
|
|
54
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The built-in table is the pinned Node built-in module root list, not special cases for individual consumer packages.
|
|
55
|
+
* @evidence contracts/common.md#meaningful-documentation Native paragraphs define rejected specifier kinds and npm-install purpose under the documentation skill.
|
|
51
56
|
*/
|
|
52
57
|
export function packageNameFromSpecifier(specifier: string): string | null {
|
|
53
58
|
const nodePrefixed = specifier.startsWith("node:");
|
|
@@ -65,6 +70,7 @@ export function packageNameFromSpecifier(specifier: string): string | null {
|
|
|
65
70
|
const firstSlash = bare.indexOf("/");
|
|
66
71
|
if (firstSlash < 0) return null;
|
|
67
72
|
if (firstSlash === 1) return null;
|
|
73
|
+
if (firstSlash === bare.length - 1) return null;
|
|
68
74
|
const secondSlash = bare.indexOf("/", firstSlash + 1);
|
|
69
75
|
if (secondSlash === firstSlash + 1) return null;
|
|
70
76
|
return secondSlash < 0 ? bare : bare.slice(0, secondSlash);
|
|
@@ -9,6 +9,15 @@ interface ConsoleViewerProps {
|
|
|
9
9
|
empty?: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Render captured console calls in order, with argument values formatted as
|
|
14
|
+
* React text and nested containers truncated after four levels.
|
|
15
|
+
*
|
|
16
|
+
* @evidence contracts/common.md#principled-implementation Console types choose display colors and argument values become escaped React text; depth-limited container recursion handles circular values. Object entry access can invoke getters, so formatting is not a user-code isolation boundary.
|
|
17
|
+
* @evidence contracts/common.md#clear-and-simple-design Row layout delegates color and value formatting to local helpers while the caller owns captured-message state.
|
|
18
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Color choices are presentation constants and the depth limit is a general rendering bound, not source-specific output substitution.
|
|
19
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states ordering, text rendering and truncation semantics, separated from tags under the documentation skill.
|
|
20
|
+
*/
|
|
12
21
|
export function ConsoleViewer({
|
|
13
22
|
messages,
|
|
14
23
|
empty = "No output yet. Click Execute to run the compiled JavaScript.",
|
|
@@ -79,6 +88,7 @@ function formatValue(value: unknown, depth = 0): JSX.Element {
|
|
|
79
88
|
return <span className="text-slate-500">undefined</span>;
|
|
80
89
|
if (typeof value === "function")
|
|
81
90
|
return <span className="text-slate-500">[Function]</span>;
|
|
91
|
+
if (depth > 4) return <span className="text-slate-500">[...]</span>;
|
|
82
92
|
if (Array.isArray(value))
|
|
83
93
|
return (
|
|
84
94
|
<span>
|
|
@@ -101,7 +111,6 @@ function formatValue(value: unknown, depth = 0): JSX.Element {
|
|
|
101
111
|
try {
|
|
102
112
|
const entries = Object.entries(value as Record<string, unknown>);
|
|
103
113
|
if (entries.length === 0) return <span>{"{}"}</span>;
|
|
104
|
-
if (depth > 4) return <span className="text-slate-500">[...]</span>;
|
|
105
114
|
return (
|
|
106
115
|
<span>
|
|
107
116
|
{"{"}
|
|
@@ -7,6 +7,15 @@ interface DependencyProgressModalProps {
|
|
|
7
7
|
packages: readonly string[];
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Present current installation phase and package-count progress; null hides
|
|
12
|
+
* the modal and the package chips show at most eight names.
|
|
13
|
+
*
|
|
14
|
+
* @evidence contracts/common.md#principled-implementation Completed/total package counts produce a clamped display ratio and optional version joins only present package identity.
|
|
15
|
+
* @evidence contracts/common.md#clear-and-simple-design The component is a pure progress view; installation and cancellation stay with its owner.
|
|
16
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The minimum visible bar and chip limit are UI presentation policy rather than fabricated install outcomes.
|
|
17
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose defines visibility, count units and chip truncation with tag separation under the documentation skill.
|
|
18
|
+
*/
|
|
10
19
|
export function DependencyProgressModal({
|
|
11
20
|
progress,
|
|
12
21
|
packages,
|
|
@@ -4,6 +4,15 @@ import { useState } from "react";
|
|
|
4
4
|
|
|
5
5
|
import type { ICompilerService } from "../structures/ICompilerService";
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Display severity totals and a collapsible list of supplied diagnostic
|
|
9
|
+
* locations; expansion changes presentation without rerunning compilation.
|
|
10
|
+
*
|
|
11
|
+
* @evidence contracts/common.md#principled-implementation Counts derive from actual severity fields and location text uses the normalized one-based diagnostic contract.
|
|
12
|
+
* @evidence contracts/common.md#clear-and-simple-design Local expansion state owns presentation only; producers own findings and failure interpretation.
|
|
13
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Empty presentation derives from the supplied list rather than inventing a successful compiler result.
|
|
14
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose explains totals and expansion ownership, separated from tags under the documentation skill.
|
|
15
|
+
*/
|
|
7
16
|
export function DiagnosticsPanel({
|
|
8
17
|
diagnostics,
|
|
9
18
|
}: {
|