@wenathlan/argan 2.0.151 → 2.0.153

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wenathlan/argan",
3
- "version": "2.0.151",
3
+ "version": "2.0.153",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/wenathlan/devthink"
@@ -27,7 +27,7 @@
27
27
  "ecmascript": "^5.0.0",
28
28
  "firefox": ">=0.8.0",
29
29
  "iojs": "^1.0.0",
30
- "node": ">=26.12.1 <27",
30
+ "node": ">=26.13.0 <27",
31
31
  "npm": "^12.1.0",
32
32
  "pnpm": "^12.6.0",
33
33
  "teleport": ">=0.2.0",
@@ -38,7 +38,6 @@
38
38
  "files": [
39
39
  "README.md",
40
40
  "docs",
41
- "site",
42
41
  "tests"
43
42
  ],
44
43
  "homepage": "https://github.com/wenathlan/devthink/tree/main/argan",
@@ -61,23 +60,40 @@
61
60
  "restore:entry": "node -e \"const{readFileSync}=require('node:fs');const{execSync}=require('node:child_process');const script=/script[^>]*App\\.tsx/;const triad=/family=Inter[\\s\\S]*Geist\\+Mono/;const face=(h)=>script.test(h)&&triad.test(h);if(face(readFileSync('index.html','utf8'))){console.log('index.html already rides the source face');process.exit(0)}if(!face(execSync('git show HEAD:./index.html',{encoding:'utf8'}))){console.error('refusing: HEAD index.html is not the canonical source face yet (script App.tsx + the font triad) - land the source-face fix first; nothing was touched');process.exit(1)}execSync('git checkout -- index.html',{stdio:'inherit'});console.log('index.html restored from HEAD (the source face)')\""
62
61
  },
63
62
  "dependencies": {
63
+ "@fluentui/react-icons": "^2.0.345",
64
+ "@fontsource-variable/bricolage-grotesque": "^5.3.0",
65
+ "@fontsource-variable/geist-mono": "^5.3.0",
66
+ "@fontsource-variable/inter": "^5.3.0",
67
+ "@fontsource-variable/jetbrains-mono": "^5.3.0",
68
+ "@fontsource-variable/outfit": "^5.3.0",
64
69
  "@hono/node-server": "^2.1.5",
70
+ "@libsql/client": "^0.18.0",
65
71
  "@prisma/adapter-libsql": "^7.10.1",
66
- "hono": "^4.13.13",
72
+ "@prisma/client": "^7.10.1",
73
+ "@wenathlan/saddle": "^2.1.6",
74
+ "@wenathlan/vault": "^2.0.152",
75
+ "gsap": "^3.15.0",
76
+ "hono": "^4.13.16",
67
77
  "ink": "^7.1.1",
78
+ "lucide-react": "^1.66.0",
79
+ "motion": "^12.43.0",
80
+ "react": "^19.3.1",
68
81
  "react-devtools-core": "^8.0.0",
82
+ "react-dom": "^19.3.1",
83
+ "three": "^0.187.0",
84
+ "wouter": "^3.13.0",
69
85
  "z-ai-web-dev-sdk": "^0.0.18"
70
86
  },
71
87
  "devDependencies": {
72
- "@biomejs/biome": "^2.6.3",
73
- "@capacitor/android": "^8.5.3",
74
- "@capacitor/cli": "^8.5.3",
75
- "@capacitor/core": "^8.5.3",
76
- "@capacitor/ios": "^8.5.3",
77
- "@fluentui/react-icons": "^2.0.344",
88
+ "@biomejs/biome": "^2.6.4",
89
+ "@capacitor/android": "^8.6.0",
90
+ "@capacitor/cli": "^8.6.0",
91
+ "@capacitor/core": "^8.6.0",
92
+ "@capacitor/ios": "^8.6.0",
93
+ "@fluentui/react-icons": "^2.0.345",
78
94
  "@hookform/resolvers": "^5.9.2",
79
95
  "@libsql/client": "^0.18.0",
80
- "@opencode-ai/sdk": "^1.19.4",
96
+ "@opencode-ai/sdk": "^1.19.5",
81
97
  "@prisma/client": "^7.10.1",
82
98
  "@radix-ui/react-accordion": "^1.3.1",
83
99
  "@radix-ui/react-alert-dialog": "^1.2.1",
@@ -110,41 +126,50 @@
110
126
  "@tailwindcss/vite": "^4.3.3",
111
127
  "@tsconfig/node26": "^26.0.1",
112
128
  "@types/better-sqlite3": "^9.6.0",
113
- "@types/chrome": "^0.3.6",
129
+ "@types/chrome": "^0.3.7",
114
130
  "@types/express": "^5.0.6",
115
- "@types/node": "^26.6.5",
131
+ "@types/google.maps": "^3.66.9",
132
+ "@types/node": "^26.6.6",
116
133
  "@types/react": "^19.3.0",
117
134
  "@types/react-dom": "^19.3.0",
135
+ "@types/three": "^0.187.0",
118
136
  "@vitejs/plugin-react": "^6.1.2",
119
137
  "add": "^2.0.9",
120
- "autoprefixer": "^10.6.1",
121
- "axios": "^1.20.0",
138
+ "autoprefixer": "^10.6.2",
139
+ "axios": "^1.21.0",
122
140
  "better-sqlite3": "^13.0.3",
123
141
  "bun-types": "^1.4.4",
142
+ "cheerio": "^1.2.0",
124
143
  "class-variance-authority": "^0.7.1",
125
144
  "clsx": "^2.1.1",
126
145
  "cmdk": "^1.1.1",
127
146
  "drizzle-kit": "^0.31.11",
128
147
  "drizzle-orm": "^0.45.4",
129
148
  "embla-carousel-react": "^8.6.0",
149
+ "emittery": "^2.2.0",
130
150
  "esbuild": "^0.28.2",
131
151
  "express": "^5.3.0",
132
152
  "fast-check": "^4.10.2",
133
153
  "framer-motion": "^13.5.1",
154
+ "hono": "^4.13.16",
134
155
  "input-otp": "^1.5.0",
135
- "lucide-react": "^1.61.0",
156
+ "jsdom": "26.1.0",
157
+ "lucide-react": "^1.66.0",
136
158
  "mysql2": "^3.24.6",
137
159
  "nanoid": "^6.0.2",
138
160
  "next-themes": "^0.4.6",
139
- "pnpm": "^12.15.0",
140
- "postcss": "^8.5.31",
161
+ "p-limit": "^7.3.3",
162
+ "p-retry": "^8.0.1",
163
+ "playwright": "^1.64.1",
164
+ "pnpm": "^12.16.1",
165
+ "postcss": "^8.5.32",
141
166
  "prettier": "^3.9.10",
142
167
  "prisma": "^7.10.1",
143
168
  "react": "^19.3.1",
144
169
  "react-day-picker": "^10.0.2",
145
170
  "react-dom": "^19.3.1",
146
171
  "react-hook-form": "^7.91.0",
147
- "react-resizable-panels": "^4.15.0",
172
+ "react-resizable-panels": "^4.16.0",
148
173
  "recharts": "^3.10.1",
149
174
  "sonner": "^2.0.8",
150
175
  "streamdown": "^2.7.1",
@@ -157,6 +182,7 @@
157
182
  "typescript": "^7.0.2",
158
183
  "vaul": "^1.1.2",
159
184
  "vite": "^8.3.5",
185
+ "vite-plugin-manus-runtime": "^0.0.59",
160
186
  "vitest": "^5.0.5",
161
187
  "wouter": "^3.13.0",
162
188
  "yaml": "^2.9.1",
@@ -174,8 +200,11 @@
174
200
  "proxy-addr": "2.0.8",
175
201
  "body-parser": "^2.4.0",
176
202
  "fast-uri": "^3.1.9",
177
- "follow-redirects": "^1.16.1",
203
+ "follow-redirects": "^1.16.2",
204
+ "minimatch": "^11.0.3",
205
+ "glob": "^14.0.3",
178
206
  "zod": "^4.6.7",
179
- "undici": "^7.30.1"
207
+ "undici": "^7.30.1",
208
+ "shell-quote": "^1.11.0"
180
209
  }
181
210
  }
@@ -37,9 +37,11 @@ describe("design contract", () => {
37
37
  }
38
38
  });
39
39
 
40
- it("carries the argan jade #34d8a8 as the one signal (mark story #14b98c)", () => {
41
- // the jade token stands literal and the primary rides it — both stay pinned
42
- expect(CSS).toMatch(/--arg-jade: #34d8a8/);
40
+ it("carries the white sun as the one signal with the jade story in the mark (#14b98c)", () => {
41
+ // the white sun answers the single-theme law: the jade rides the signal
42
+ // alias and the story survives in the drawn brand mark
43
+ expect(CSS).toMatch(/--color-signal: #ffffff/);
44
+ expect(CSS).toMatch(/--arg-jade: var\(--color-signal\)/);
43
45
  expect(CSS).toMatch(/--sol-primary: var\(--arg-jade\)/);
44
46
  expect(MARK).toMatch(/#14b98c/);
45
47
  });
@@ -50,14 +52,14 @@ describe("design contract", () => {
50
52
  });
51
53
 
52
54
  it("never ships an all-properties transition", () => {
53
- expect(/transition:\s*all/.test(CSS), "transition: all is a reprovação").toBe(false);
55
+ expect(/transition:\s*all/.test(CSS), "transition: all is a rejection").toBe(false);
54
56
  });
55
57
 
56
58
  it("never enters an element from zero scale (the windows dtPopIntro bounce excepted)", () => {
57
59
  const sansPopIntro = CSS.replace(/@keyframes dtPopIntro \{[\s\S]*?\n\}/g, "");
58
60
  expect(
59
61
  /scale\(0\)/.test(sansPopIntro),
60
- "scale(0) entries are a reprovação (the dtPopIntro bounce of the windows recipe excepted)",
62
+ "scale(0) entries are a rejection (the dtPopIntro bounce of the windows recipe excepted)",
61
63
  ).toBe(false);
62
64
  });
63
65
 
@@ -120,7 +122,7 @@ describe("design contract", () => {
120
122
  });
121
123
 
122
124
  it("carries the flow contract sources (the entry gate behind the home anchor, the shared state module)", () => {
123
- const GATE = read("../Sol/widgets/HomeEntrygate.tsx");
125
+ const GATE = read("../Sol/wingets/HomeEntrygate.tsx");
124
126
  const STATE = read("../onboardingstate.ts");
125
127
  const HOME = read("../Sol/home/home.tsx");
126
128
  const ONBOARDING = read("../Sol/onboarding/onboarding.tsx");
@@ -0,0 +1,114 @@
1
+ // # mimetypes.test — honest unit tests for the gateway media type catalog, runnable
2
+ // with the vitest runner:
3
+ // pnpm test
4
+ // The suite pins the owner law of wave 2-a: three thousand mimetypes on the internal
5
+ // api surface (3217 rows, every family the gateway answers for), the canonical pins of
6
+ // the extension lookup (.ts answers video/mp2t, .tsx answers application/typescript),
7
+ // the one-row-per-extension collision law and the catalog accessor that serves the
8
+ // catalog offline.
9
+ import assert from "node:assert/strict";
10
+ import { describe, it } from "vitest";
11
+ import { listMimetypes } from "../catalog.ts";
12
+ import {
13
+ extensionsForType,
14
+ findMimeType,
15
+ MIMETYPES,
16
+ MIMETYPE_COUNT,
17
+ mimeFamilyOf,
18
+ mimeTypeForExtension,
19
+ mimetypesByFamily,
20
+ } from "../mimetypes.ts";
21
+
22
+ /** The families the law names (the type tree the catalog must cover). */
23
+ const LAW_FAMILIES: readonly string[] = [
24
+ "text", "image", "audio", "video", "application", "font", "model", "chemical",
25
+ "message", "multipart",
26
+ ];
27
+
28
+ describe("mimetype catalog scale (the three-thousand law)", () => {
29
+ it("answers 3217 media types — past the three thousand line", () => {
30
+ assert.equal(MIMETYPE_COUNT, 3217);
31
+ assert.ok(MIMETYPE_COUNT >= 3000, "the catalog must clear three thousand");
32
+ });
33
+
34
+ it("carries one row per media type", () => {
35
+ const seen = new Set<string>();
36
+ for (const row of MIMETYPES) {
37
+ assert.ok(!seen.has(row.type), `the type ${row.type} rides twice`);
38
+ seen.add(row.type);
39
+ }
40
+ assert.equal(seen.size, MIMETYPE_COUNT);
41
+ });
42
+
43
+ it("covers every family of the owner law", () => {
44
+ const families = new Set(MIMETYPES.map((row) => row.family as string));
45
+ for (const family of LAW_FAMILIES) {
46
+ assert.ok(families.has(family), `the catalog must cover ${family}`);
47
+ }
48
+ assert.equal(families.size, 10);
49
+ });
50
+
51
+ it("answers the family of every row from the type tree before the slash", () => {
52
+ for (const row of MIMETYPES) {
53
+ assert.equal(row.family, row.type.split("/")[0]);
54
+ }
55
+ });
56
+ });
57
+
58
+ describe("mimetype canonical pins (the extension lookup law)", () => {
59
+ it("answers .ts with video/mp2t and .tsx with application/typescript", () => {
60
+ assert.equal(mimeTypeForExtension("ts")?.type, "video/mp2t");
61
+ assert.equal(mimeTypeForExtension(".tsx")?.type, "application/typescript");
62
+ assert.equal(mimeTypeForExtension("TS")?.type, "video/mp2t");
63
+ assert.equal(mimeTypeForExtension(".TSX")?.type, "application/typescript");
64
+ });
65
+
66
+ it("answers the everyday extensions of the web family", () => {
67
+ assert.equal(mimeTypeForExtension("html")?.type, "text/html");
68
+ assert.equal(mimeTypeForExtension(".css")?.type, "text/css");
69
+ assert.equal(mimeTypeForExtension("js")?.type, "application/javascript");
70
+ assert.equal(mimeTypeForExtension("json")?.type, "application/json");
71
+ assert.equal(mimeTypeForExtension("svg")?.type, "image/svg+xml");
72
+ assert.equal(mimeTypeForExtension("md")?.type, "text/markdown");
73
+ assert.equal(mimeTypeForExtension("wasm")?.type, "application/wasm");
74
+ });
75
+
76
+ it("resolves types case-insensitively and answers unknowns with undefined", () => {
77
+ assert.equal(findMimeType("VIDEO/MP2T")?.family, "video");
78
+ assert.equal(findMimeType(" application/typescript ")?.extensions.includes("tsx"), true);
79
+ assert.equal(mimeFamilyOf("font/woff2"), "font");
80
+ assert.equal(findMimeType("video/mp3t"), undefined);
81
+ assert.equal(mimeTypeForExtension("nope"), undefined);
82
+ assert.equal(mimeTypeForExtension(""), undefined);
83
+ });
84
+
85
+ it("lists the extensions of one type and the rows of one family", () => {
86
+ assert.deepEqual(extensionsForType("video/mp2t"), ["ts"]);
87
+ assert.deepEqual(extensionsForType("font/woff"), ["woff"]);
88
+ assert.equal(extensionsForType("video/mp3t").length, 0);
89
+ assert.equal(mimetypesByFamily("font").length, 6);
90
+ assert.ok(mimetypesByFamily("text").every((row) => row.family === "text"));
91
+ });
92
+
93
+ it("keeps every extension on exactly one row (the collision law)", () => {
94
+ const claimed = new Map<string, string>();
95
+ for (const row of MIMETYPES) {
96
+ for (const extension of row.extensions) {
97
+ const owner = claimed.get(extension);
98
+ assert.ok(owner === undefined, `.${extension} rides both ${owner} and ${row.type}`);
99
+ claimed.set(extension, row.type);
100
+ }
101
+ }
102
+ assert.ok(claimed.size > 1500, "the catalog maps a dense extension set");
103
+ assert.equal(claimed.get("ts"), "video/mp2t");
104
+ assert.equal(claimed.get("tsx"), "application/typescript");
105
+ });
106
+ });
107
+
108
+ describe("mimetype catalog on the catalog surface", () => {
109
+ it("serves the catalog through the typed accessor offline (the registry seed)", async () => {
110
+ const rows = await listMimetypes();
111
+ assert.equal(rows.length, MIMETYPE_COUNT);
112
+ assert.ok(rows.some((row) => row.type === "video/mp2t"));
113
+ });
114
+ });
@@ -0,0 +1,106 @@
1
+ // # protocols.test — honest unit tests for the gateway protocol mesh, runnable with
2
+ // the vitest runner:
3
+ // pnpm test
4
+ // The suite pins the owner law of wave 2-a: more than one hundred protocols on the
5
+ // internal api surface (144 tls-capable wires over the hundred line, 231 rows above
6
+ // the two hundred line), the canonical schemes of the mesh, the family grouping, the
7
+ // tone derivation and the catalog accessor that serves the mesh offline.
8
+ import assert from "node:assert/strict";
9
+ import { describe, it } from "vitest";
10
+ import { listProtocols } from "../catalog.ts";
11
+ import {
12
+ findProtocol,
13
+ PROTOCOLS,
14
+ PROTOCOL_COUNT,
15
+ protocolGroups,
16
+ protocolsByGroup,
17
+ protocolSchemes,
18
+ protocolTone,
19
+ tlsCapableProtocols,
20
+ } from "../protocols.ts";
21
+
22
+ /** The schemes the owner law names (the transfer and application wires of the mesh). */
23
+ const LAW_SCHEMES: readonly string[] = [
24
+ "https", "wss", "webtransport", "webcodecs", "webrtc", "webdav", "webdavs", "sftp",
25
+ "ftps", "smtp", "smtps", "imap", "imaps", "pop3", "pop3s", "dns", "doh", "dot",
26
+ "quic", "h3", "h2", "h2c", "spdy", "socks5", "socks5h", "tor", "i2p", "ipfs",
27
+ "hypercore", "gemini", "gopher", "ldap", "ldaps", "rdp", "vnc", "ssh", "telnet",
28
+ "mqtt", "mqtts", "amqp", "amqps", "stomp", "nats", "grpc", "grpcs", "capnproto",
29
+ "msgpack-rpc", "thrift", "xmlrpc", "jsonrpc", "soap", "graphql-ws", "sse", "longpoll",
30
+ "echo", "discard", "chargen", "daytime", "ntp", "snmp", "syslog", "tftp",
31
+ ];
32
+
33
+ describe("protocol mesh scale (the hundred law)", () => {
34
+ it("answers 231 protocols — more than one hundred, above the two hundred line", () => {
35
+ assert.equal(PROTOCOL_COUNT, 231);
36
+ assert.ok(PROTOCOL_COUNT > 100, "the mesh must clear one hundred");
37
+ assert.ok(PROTOCOL_COUNT >= 200, "the mesh must clear the two hundred line");
38
+ });
39
+
40
+ it("carries 144 tls-capable wires — the https-grade count clears one hundred", () => {
41
+ const capable = tlsCapableProtocols();
42
+ assert.equal(capable.length, 144);
43
+ assert.ok(capable.length > 100, "the tls-capable count must clear one hundred");
44
+ });
45
+
46
+ it("registers every scheme of the owner law", () => {
47
+ const schemes = new Set(protocolSchemes());
48
+ for (const scheme of LAW_SCHEMES) {
49
+ assert.ok(schemes.has(scheme), `the mesh must register ${scheme}`);
50
+ }
51
+ });
52
+
53
+ it("answers one row per scheme with a name, port, spec, family and state", () => {
54
+ const seen = new Set<string>();
55
+ for (const row of PROTOCOLS) {
56
+ assert.ok(!seen.has(row.scheme), `the scheme ${row.scheme} rides twice`);
57
+ seen.add(row.scheme);
58
+ assert.ok(row.name.length > 0, `${row.scheme} needs a name`);
59
+ assert.ok(row.port.length > 0, `${row.scheme} needs a port answer`);
60
+ assert.ok(row.spec.length > 0, `${row.scheme} needs a spec`);
61
+ assert.ok(row.state.length > 0, `${row.scheme} needs a state`);
62
+ }
63
+ assert.equal(seen.size, PROTOCOL_COUNT);
64
+ });
65
+ });
66
+
67
+ describe("protocol mesh grouping and lookups", () => {
68
+ it("groups the mesh into the fifteen family keywords", () => {
69
+ const groups = protocolGroups();
70
+ assert.equal(groups.length, 15);
71
+ for (const group of groups) {
72
+ assert.ok(protocolsByGroup(group).length > 0, `the ${group} family carries rows`);
73
+ }
74
+ });
75
+
76
+ it("answers the dns family with the argan home transports", () => {
77
+ const dns = protocolsByGroup("dns");
78
+ assert.equal(dns.length, 12);
79
+ assert.ok(dns.every((row) => row.group === "dns"));
80
+ const doh = dns.find((row) => row.scheme === "doh");
81
+ assert.equal(doh?.spec, "RFC 8484");
82
+ assert.equal(doh?.state, "preferred");
83
+ });
84
+
85
+ it("resolves schemes case-insensitively and answers unknowns with undefined", () => {
86
+ assert.equal(findProtocol("WSS")?.name, "WebSocket Secure");
87
+ assert.equal(findProtocol(" https ")?.port, "443");
88
+ assert.equal(findProtocol("gopher")?.security, "cleartext");
89
+ assert.equal(findProtocol("telstar"), undefined);
90
+ });
91
+
92
+ it("derives the pane tone from the security posture", () => {
93
+ assert.equal(protocolTone(findProtocol("wss")!), "success");
94
+ assert.equal(protocolTone(findProtocol("imap")!), "info");
95
+ assert.equal(protocolTone(findProtocol("telnet")!), "default");
96
+ assert.equal(protocolTone(findProtocol("http")!), "warning");
97
+ });
98
+ });
99
+
100
+ describe("protocol mesh on the catalog surface", () => {
101
+ it("serves the mesh through the typed accessor offline (the registry seed)", async () => {
102
+ const rows = await listProtocols();
103
+ assert.equal(rows.length, PROTOCOL_COUNT);
104
+ assert.ok(rows.some((row) => row.scheme === "webtransport"));
105
+ });
106
+ });
@@ -0,0 +1,84 @@
1
+ // # sandboxmodality.test — the workload lane declaration of argan (wave
2
+ // WL1): the mirrored rows answer the engine catalog byte for byte (the
3
+ // lockstep pin — every lane, role, demand and honest today the engine
4
+ // FAMILYLANES table carries for argan, compared through the library
5
+ // bridge so the vendor can never drift in silence), every row names the
6
+ // app slug, and the seat law answers the deploy's opt-in (absent or blank
7
+ // answers the declared local default, "join" opts the lanes onto the
8
+ // platform's join resolver — the nuclear modality law, and a named
9
+ // modality id pins the seat for the platform's join to validate at
10
+ // dispatch — this module never judges engine vocabulary). runnable with
11
+ // vitest:
12
+ // pnpm vitest run tests/sandboxmodality.test.ts
13
+ import assert from "node:assert/strict";
14
+ import { describe, it } from "vitest";
15
+ import { lanesof } from "@wenathlan/saddle/workloadlaw";
16
+ import {
17
+ APPSLUG,
18
+ SANDBOXLANEENV,
19
+ WORKLOADLANES,
20
+ laneof,
21
+ lanesandbox,
22
+ lanesandboxfromenv,
23
+ } from "../sandboxmodality.ts";
24
+
25
+ describe("the workload lane mirror (the engine catalog lockstep)", () => {
26
+ it("answers the engine catalog rows for argan — the byte pin", () => {
27
+ const engine = lanesof("argan");
28
+ assert.equal(engine.length, WORKLOADLANES.length, "the mirror carries every argan lane");
29
+ for (let index = 0; index < engine.length; index += 1) {
30
+ const row = engine[index];
31
+ const mirror = WORKLOADLANES[index];
32
+ assert.equal(mirror.app, row.app);
33
+ assert.equal(mirror.lane, row.lane, `lane ${index} id drifts from the engine`);
34
+ assert.equal(mirror.role, row.role, `lane ${row.lane} role drifts from the engine`);
35
+ assert.deepEqual(mirror.demand, row.demand, `lane ${row.lane} demand drifts from the engine`);
36
+ assert.equal(mirror.today, row.today, `lane ${row.lane} today drifts from the engine`);
37
+ }
38
+ });
39
+
40
+ it("carries the app slug on every row and answers the lane lookup", () => {
41
+ for (const row of WORKLOADLANES) {
42
+ assert.equal(row.app, APPSLUG);
43
+ }
44
+ const first = laneof("dnssec-validate");
45
+ assert.ok(first !== undefined);
46
+ assert.equal(first.today, "app-lane");
47
+ assert.equal(laneof("no-such-lane"), undefined);
48
+ });
49
+ });
50
+
51
+ describe("the sandbox lane seat (the deploy decides)", () => {
52
+ it("answers the declared local default when the deploy carries no choice", () => {
53
+ const absent = lanesandbox(undefined);
54
+ assert.deepEqual(absent, { ok: true, seat: "local", sandboxed: false });
55
+ const blank = lanesandbox(" ");
56
+ assert.deepEqual(blank, { ok: true, seat: "local", sandboxed: false });
57
+ const explicit = lanesandbox("local");
58
+ assert.deepEqual(explicit, { ok: true, seat: "local", sandboxed: false });
59
+ });
60
+
61
+ it("opts the lanes onto the platform join when the deploy chooses join", () => {
62
+ const joined = lanesandbox("join");
63
+ assert.deepEqual(joined, { ok: true, seat: "join", sandboxed: true, pinned: false });
64
+ const padded = lanesandbox(" join ");
65
+ assert.deepEqual(padded, { ok: true, seat: "join", sandboxed: true, pinned: false });
66
+ });
67
+
68
+ it("pins the named modality for the platform join to validate", () => {
69
+ const pinned = lanesandbox("runner-node");
70
+ assert.deepEqual(pinned, { ok: true, seat: "runner-node", sandboxed: true, pinned: true });
71
+ // the module never judges engine vocabulary: the platform's join
72
+ // validates the pinned id at dispatch, the mirror only names it
73
+ const unusual = lanesandbox("microvm-firecracker");
74
+ assert.deepEqual(unusual, { ok: true, seat: "microvm-firecracker", sandboxed: true, pinned: true });
75
+ });
76
+
77
+ it("reads the seat off the environment lookup the deploy pins", () => {
78
+ assert.equal(SANDBOXLANEENV, "ARGAN_SANDBOX_LANE");
79
+ const unset = lanesandboxfromenv(() => undefined);
80
+ assert.deepEqual(unset, { ok: true, seat: "local", sandboxed: false });
81
+ const set = lanesandboxfromenv((key) => (key === SANDBOXLANEENV ? "join" : undefined));
82
+ assert.deepEqual(set, { ok: true, seat: "join", sandboxed: true, pinned: false });
83
+ });
84
+ });
package/tests/.gitkeep DELETED
File without changes