@sdxc/spec 0.0.0-pre.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/LICENSE.md +21 -0
- package/README.md +924 -0
- package/dist/ast.d.ts +193 -0
- package/dist/ast.js +9 -0
- package/dist/builtins.d.ts +29 -0
- package/dist/builtins.js +66 -0
- package/dist/cli.d.ts +21 -0
- package/dist/cli.js +297 -0
- package/dist/diagnostics.d.ts +47 -0
- package/dist/diagnostics.js +8 -0
- package/dist/errors.d.ts +131 -0
- package/dist/errors.js +159 -0
- package/dist/executor.d.ts +66 -0
- package/dist/executor.js +320 -0
- package/dist/expectation.d.ts +61 -0
- package/dist/expectation.js +222 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +36 -0
- package/dist/lexer.d.ts +22 -0
- package/dist/lexer.js +284 -0
- package/dist/loader.d.ts +21 -0
- package/dist/loader.js +81 -0
- package/dist/parser.d.ts +24 -0
- package/dist/parser.js +502 -0
- package/dist/permissions.d.ts +139 -0
- package/dist/permissions.js +325 -0
- package/dist/plugin.d.ts +90 -0
- package/dist/plugin.js +9 -0
- package/dist/plugins/browser.d.ts +24 -0
- package/dist/plugins/browser.js +896 -0
- package/dist/plugins/cli.d.ts +17 -0
- package/dist/plugins/cli.js +134 -0
- package/dist/plugins/db-e2e-probe.d.ts +14 -0
- package/dist/plugins/db-e2e-probe.js +112 -0
- package/dist/plugins/db.d.ts +19 -0
- package/dist/plugins/db.js +199 -0
- package/dist/plugins/demo.d.ts +17 -0
- package/dist/plugins/demo.js +70 -0
- package/dist/plugins/env.d.ts +18 -0
- package/dist/plugins/env.js +87 -0
- package/dist/plugins/fs.d.ts +16 -0
- package/dist/plugins/fs.js +415 -0
- package/dist/plugins/http.d.ts +19 -0
- package/dist/plugins/http.js +505 -0
- package/dist/plugins/jwt.d.ts +17 -0
- package/dist/plugins/jwt.js +342 -0
- package/dist/plugins/sample.d.ts +27 -0
- package/dist/plugins/sample.js +400 -0
- package/dist/plugins/url.d.ts +18 -0
- package/dist/plugins/url.js +126 -0
- package/dist/project-config.d.ts +163 -0
- package/dist/project-config.js +497 -0
- package/dist/registry.d.ts +56 -0
- package/dist/registry.js +110 -0
- package/dist/reporter.d.ts +30 -0
- package/dist/reporter.js +237 -0
- package/dist/run.d.ts +74 -0
- package/dist/run.js +179 -0
- package/dist/runner.d.ts +52 -0
- package/dist/runner.js +38 -0
- package/dist/source.d.ts +37 -0
- package/dist/source.js +31 -0
- package/dist/sources.d.ts +45 -0
- package/dist/sources.js +54 -0
- package/dist/tokens.d.ts +34 -0
- package/dist/tokens.js +25 -0
- package/dist/transport-stdio.d.ts +34 -0
- package/dist/transport-stdio.js +400 -0
- package/dist/values.d.ts +48 -0
- package/dist/values.js +52 -0
- package/dist/workers.d.ts +40 -0
- package/dist/workers.js +26 -0
- package/dist/workspace-none.d.ts +23 -0
- package/dist/workspace-none.js +33 -0
- package/dist/workspace.d.ts +47 -0
- package/dist/workspace.js +116 -0
- package/package.json +28 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The isolated per-test workspace: a runtime primitive every capability
|
|
3
|
+
* shares, not a filesystem-plugin detail. Each test gets a fresh ephemeral
|
|
4
|
+
* directory that `fs` tools write into, `cli` processes start in, and
|
|
5
|
+
* assertions inspect; the runtime cleans it up when the test ends.
|
|
6
|
+
*
|
|
7
|
+
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
8
|
+
* @copyright Sergio Xalambrí 2026
|
|
9
|
+
*/
|
|
10
|
+
import { lstatSync, realpathSync } from "node:fs";
|
|
11
|
+
import { mkdtemp, realpath, rm } from "node:fs/promises";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
import { dirname, isAbsolute, join, resolve as resolvePath, sep } from "node:path";
|
|
14
|
+
import { failure, isFailure, success } from "@sdxc/result";
|
|
15
|
+
import { SpecError, WorkspaceEscapeError } from "./errors.js";
|
|
16
|
+
/**
|
|
17
|
+
* Create a fresh isolated workspace: a temp directory whose real path
|
|
18
|
+
* (often behind a symlink) bounds every relative path, symlinked
|
|
19
|
+
* ancestors included; absolute paths delegate to the host-fs permission.
|
|
20
|
+
*
|
|
21
|
+
* @param permissions - The run's permission set, consulted for absolute paths.
|
|
22
|
+
* @returns The workspace, or the error that prevented creating its directory.
|
|
23
|
+
*/
|
|
24
|
+
export async function createWorkspace(permissions) {
|
|
25
|
+
let root;
|
|
26
|
+
try {
|
|
27
|
+
let created = await mkdtemp(join(tmpdir(), "spec-workspace-"));
|
|
28
|
+
root = await realpath(created);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
let reason = error instanceof Error ? error.message : String(error);
|
|
32
|
+
return failure(new SpecError("tool-error", `Failed to create a test workspace: ${reason}`));
|
|
33
|
+
}
|
|
34
|
+
let workspace = {
|
|
35
|
+
root,
|
|
36
|
+
resolve(path) {
|
|
37
|
+
if (isAbsolute(path)) {
|
|
38
|
+
let resolved = resolvePath(path);
|
|
39
|
+
let checked = permissions.checkHostFs(resolved);
|
|
40
|
+
if (isFailure(checked))
|
|
41
|
+
return checked;
|
|
42
|
+
return success(resolved);
|
|
43
|
+
}
|
|
44
|
+
let resolved = resolvePath(root, path);
|
|
45
|
+
if (!isInside(root, resolved))
|
|
46
|
+
return failure(new WorkspaceEscapeError(path));
|
|
47
|
+
let ancestor = deepestExistingAncestor(resolved);
|
|
48
|
+
let realAncestor;
|
|
49
|
+
try {
|
|
50
|
+
realAncestor = realpathSync(ancestor);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return failure(new WorkspaceEscapeError(path));
|
|
54
|
+
}
|
|
55
|
+
let followed = realAncestor + resolved.slice(ancestor.length);
|
|
56
|
+
if (!isInside(root, followed))
|
|
57
|
+
return failure(new WorkspaceEscapeError(path));
|
|
58
|
+
return success(resolved);
|
|
59
|
+
},
|
|
60
|
+
async cleanup() {
|
|
61
|
+
try {
|
|
62
|
+
await rm(root, { recursive: true, force: true });
|
|
63
|
+
}
|
|
64
|
+
catch { }
|
|
65
|
+
return undefined;
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
return success(workspace);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Walk up from a resolved path to the deepest component that exists on disk
|
|
72
|
+
* (symlinks count as existing without being followed), so symlinked ancestors
|
|
73
|
+
* can be re-resolved before the containment check.
|
|
74
|
+
*
|
|
75
|
+
* @param path - An absolute, syntactically resolved path.
|
|
76
|
+
* @returns The deepest existing ancestor, at worst the filesystem root.
|
|
77
|
+
*/
|
|
78
|
+
function deepestExistingAncestor(path) {
|
|
79
|
+
let current = path;
|
|
80
|
+
while (!entryExists(current)) {
|
|
81
|
+
let parent = dirname(current);
|
|
82
|
+
if (parent === current)
|
|
83
|
+
break;
|
|
84
|
+
current = parent;
|
|
85
|
+
}
|
|
86
|
+
return current;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Does a filesystem entry exist at this path, without following symlinks?
|
|
90
|
+
*
|
|
91
|
+
* @param path - The absolute path to probe.
|
|
92
|
+
* @returns Whether lstat finds an entry there.
|
|
93
|
+
*/
|
|
94
|
+
function entryExists(path) {
|
|
95
|
+
try {
|
|
96
|
+
lstatSync(path);
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Path-segment-aware containment test: the root contains itself and its
|
|
105
|
+
* descendants, never a sibling that merely shares a name prefix.
|
|
106
|
+
*
|
|
107
|
+
* @param root - The workspace root, already an absolute real path.
|
|
108
|
+
* @param path - The absolute path being checked.
|
|
109
|
+
* @returns Whether the path stays inside the root.
|
|
110
|
+
*/
|
|
111
|
+
function isInside(root, path) {
|
|
112
|
+
if (path === root)
|
|
113
|
+
return true;
|
|
114
|
+
let prefix = root.endsWith(sep) ? root : root + sep;
|
|
115
|
+
return path.startsWith(prefix);
|
|
116
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sdxc/spec",
|
|
3
|
+
"version": "0.0.0-pre.1",
|
|
4
|
+
"description": "Executable specification runner for .spec files",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"bin": {
|
|
7
|
+
"spec": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./dist/index.js",
|
|
12
|
+
"./workers": "./dist/workers.js"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@sdxc/duration": "0.0.0-pre.1",
|
|
16
|
+
"@sdxc/result": "0.0.0-pre.1",
|
|
17
|
+
"@sdxc/sample": "0.0.0-pre.1"
|
|
18
|
+
},
|
|
19
|
+
"gitHead": "bbc1db99f3fcc3c903251a70582c6b0cf8e808cb",
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/sergiodxa/monorepo.git",
|
|
26
|
+
"directory": "packages/spec"
|
|
27
|
+
}
|
|
28
|
+
}
|