@fasl-work/caos-app-shell 0.19.11 → 0.19.12

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 CHANGED
@@ -1,3 +1,15 @@
1
+ ## [0.19.012] - 2026-11-01
2
+
3
+ ### Fixed
4
+
5
+ - Verify complete JSON artifact documents when navigation interrupts a page-owned body read or a conditional response has no body. One bounded GET preserves the observed URL and credentials while removing range/cache preconditions; transport retries are recorded. HTML, empty, malformed and failed responses remain failures.
6
+ - Keep artifact validation independent of page-context lifetime without changing readiness declarations, idle-rate limits, traversal rules or component styles.
7
+ - Permit the gate self-test to use the configured external temporary root; CI uses its runner temporary root.
8
+
9
+ ### Adoption
10
+
11
+ - Pin npm 0.19.12 and run the installed gate against the deployed origin. Components and scientific calculations are unchanged.
12
+
1
13
  ## [0.19.011] - 2026-11-01
2
14
 
3
15
  ### Fixed
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.19.011
1
+ 0.19.012
@@ -0,0 +1,29 @@
1
+ /** Complete JSON verification, including a bounded retry after a page-owned read is interrupted. */
2
+ export async function readJsonArtifact(response) {
3
+ const url = response.url();
4
+ let text = '', transportRetry = false, originalReadError;
5
+ try {
6
+ // The partial header map omits browser cookies; capture complete on-wire headers before teardown.
7
+ const headers = { ...(await response.request().allHeaders()) };
8
+ try {
9
+ if (response.status() === 304) throw new Error('Conditional response has no complete document body');
10
+ text = await response.text();
11
+ } catch (error) {
12
+ transportRetry = true;
13
+ originalReadError = String(error);
14
+ // Request the full original document, retaining credentials but not cache/range preconditions.
15
+ for (const name of Object.keys(headers)) {
16
+ if (['if-none-match', 'if-modified-since', 'range', 'if-range', 'content-length', 'host', 'connection'].includes(name.toLowerCase())) delete headers[name];
17
+ }
18
+ const retry = await fetch(url, { headers, redirect: 'error', cache: 'no-store', signal: AbortSignal.timeout(10000) });
19
+ if (!retry.ok) throw new Error(`Complete artifact GET answered HTTP ${retry.status}`);
20
+ text = await retry.text();
21
+ }
22
+ const first = text.trimStart().slice(0, 1);
23
+ const value = JSON.parse(text);
24
+ if (value === null || typeof value !== 'object') throw new Error('Artifact must be a JSON object or array');
25
+ return { ok: true, first, transportRetry, ...(originalReadError ? { originalReadError } : {}) };
26
+ } catch (error) {
27
+ return { ok: false, first: text.trimStart().slice(0, 1), transportRetry, error: String(error), ...(originalReadError ? { originalReadError } : {}) };
28
+ }
29
+ }
package/gate/run.mjs CHANGED
@@ -38,6 +38,7 @@
38
38
  // plus the ADR-0071 and ADR-0017 measures (viewport, one-row tabs, rail without scroll, centred prose, captions,
39
39
  // references).
40
40
 
41
+ import { readJsonArtifact } from './artifacts.mjs';
41
42
  import { existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
42
43
  import { join } from 'node:path';
43
44
  import { decodePng, paintedBox, unionArea } from './png.mjs';
@@ -135,6 +136,7 @@ export async function runGate(o) {
135
136
  let states = 0;
136
137
  const fail = (check, where, message) => failures.push({ check, route: where.route ?? '', mode: where.mode ?? '', trail: where.trail ?? '', message });
137
138
  const artifacts = new Map();
139
+ const artifactChecks = [];
138
140
 
139
141
  const browser = await chromium.launch();
140
142
  const openContext = async ([w, h], theme, lang, wide = false) => {
@@ -193,10 +195,7 @@ export async function runGate(o) {
193
195
  if ((type === 'fetch' || type === 'xhr') && pathname.endsWith('.json') && r.status() < 400 && !artifacts.has(pathname)) {
194
196
  artifacts.set(
195
197
  pathname,
196
- r.text().then(
197
- (t) => t.trimStart().slice(0, 1),
198
- () => '',
199
- ),
198
+ readJsonArtifact(r),
200
199
  );
201
200
  }
202
201
  });
@@ -444,8 +443,9 @@ export async function runGate(o) {
444
443
 
445
444
  // ---- G4: artifacts answered as JSON.
446
445
  for (const [pathname, first] of artifacts) {
447
- const c = await first;
448
- if (c !== '{' && c !== '[') fail('G4', { route: pathname }, `the artifact ${pathname} did not answer JSON (starts with "${c}"; the HTML fallback?)`);
446
+ const result = await first;
447
+ artifactChecks.push({ path: pathname, ...result });
448
+ if (!result.ok) fail('G4', { route: pathname }, `the artifact ${pathname} did not answer JSON (starts with "${result.first}"; ${result.error ?? 'the HTML fallback?'})`);
449
449
  }
450
450
  } finally {
451
451
  await browser.close();
@@ -1131,6 +1131,7 @@ export async function runGate(o) {
1131
1131
  checks,
1132
1132
  captures: captures.length,
1133
1133
  failures,
1134
+ artifactChecks,
1134
1135
  };
1135
1136
  writeFileSync(join(opt.out, 'gate-report.json'), JSON.stringify(report, null, 2));
1136
1137
  // G14: the captures are for a person to read (the gate measures, it does not judge content); one page shows them.
package/package.json CHANGED
@@ -1,100 +1,100 @@
1
- {
2
- "name": "@fasl-work/caos-app-shell",
3
- "version": "0.19.11",
4
- "description": "Shared web-app shell, workbench primitives, content primitives, design system and measured gate for the CAOS / Faena public apps (ADR-0016, ADR-0017, ADR-0071).",
5
- "license": "MIT",
6
- "author": "Felipe Santibáñez-Leal",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/fsantibanezleal/CAOS_APP_SHELL.git"
10
- },
11
- "type": "module",
12
- "publishConfig": {
13
- "access": "public"
14
- },
15
- "sideEffects": [
16
- "*.css"
17
- ],
18
- "exports": {
19
- ".": {
20
- "types": "./dist/index.d.ts",
21
- "import": "./dist/index.js"
22
- },
23
- "./chart": {
24
- "types": "./dist/chart.d.ts",
25
- "import": "./dist/chart.js"
26
- },
27
- "./keys": {
28
- "types": "./dist/keys.d.ts",
29
- "import": "./dist/keys.js"
30
- },
31
- "./styles.css": "./styles.css",
32
- "./chart.css": "./chart.css",
33
- "./reserved-classes.json": "./reserved-classes.json",
34
- "./executive.schema.json": "./src/content/executive.schema.json",
35
- "./serve": "./gate/serve.mjs",
36
- "./gate": "./gate/run.mjs",
37
- "./package.json": "./package.json"
38
- },
39
- "files": [
40
- "dist",
41
- "styles.css",
42
- "chart.css",
43
- "bin",
44
- "gate",
45
- "reserved-classes.json",
46
- "src/content/executive.schema.json",
47
- "VERSION",
48
- "CHANGELOG.md"
49
- ],
50
- "scripts": {
51
- "build": "tsup",
52
- "test": "node --import tsx --test \"test/**/*.test.ts\" \"test/**/*.test.tsx\"",
53
- "test:browser": "node --import tsx --test \"e2e/**/*.test.ts\"",
54
- "prepare": "npm run build",
55
- "prepublishOnly": "npm run build",
56
- "prebuild": "node scripts/reserved-classes.mjs",
57
- "test:gate": "node --test test/gate-selftest.mjs",
58
- "check:sdd": "python scripts/check_sdd.py ."
59
- },
60
- "peerDependencies": {
61
- "katex": "^0.16.0 || ^0.17.0 || ^0.18.0 || ^0.19.0",
62
- "lucide-react": ">=0.400.0",
63
- "react": "^18 || ^19",
64
- "react-dom": "^18 || ^19",
65
- "react-router": "^6 || ^7 || ^8",
66
- "zustand": "^4 || ^5",
67
- "playwright": ">=1.40",
68
- "uplot": "^1.6.30"
69
- },
70
- "devDependencies": {
71
- "@playwright/test": "1.63.0",
72
- "@types/react": "^19.0.0",
73
- "@types/react-dom": "^19.0.0",
74
- "esbuild": "0.28.1",
75
- "katex": "0.19.0",
76
- "lucide-react": "1.43.0",
77
- "react": "^19.0.0",
78
- "react-dom": "^19.0.0",
79
- "react-router": "^8.3.0",
80
- "tsup": "^8.3.5",
81
- "tsx": "^4.23.0",
82
- "typescript": "^5.7.3",
83
- "zustand": "^5.0.2",
84
- "uplot": "1.6.32"
85
- },
86
- "overrides": {
87
- "esbuild": "0.28.1"
88
- },
89
- "bin": {
90
- "caos-shell-gate": "bin/caos-shell-gate.mjs"
91
- },
92
- "peerDependenciesMeta": {
93
- "playwright": {
94
- "optional": true
95
- },
96
- "uplot": {
97
- "optional": true
98
- }
99
- }
100
- }
1
+ {
2
+ "name": "@fasl-work/caos-app-shell",
3
+ "version": "0.19.12",
4
+ "description": "Shared web-app shell, workbench primitives, content primitives, design system and measured gate for the CAOS / Faena public apps (ADR-0016, ADR-0017, ADR-0071).",
5
+ "license": "MIT",
6
+ "author": "Felipe Santibáñez-Leal",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/fsantibanezleal/CAOS_APP_SHELL.git"
10
+ },
11
+ "type": "module",
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "sideEffects": [
16
+ "*.css"
17
+ ],
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js"
22
+ },
23
+ "./chart": {
24
+ "types": "./dist/chart.d.ts",
25
+ "import": "./dist/chart.js"
26
+ },
27
+ "./keys": {
28
+ "types": "./dist/keys.d.ts",
29
+ "import": "./dist/keys.js"
30
+ },
31
+ "./styles.css": "./styles.css",
32
+ "./chart.css": "./chart.css",
33
+ "./reserved-classes.json": "./reserved-classes.json",
34
+ "./executive.schema.json": "./src/content/executive.schema.json",
35
+ "./serve": "./gate/serve.mjs",
36
+ "./gate": "./gate/run.mjs",
37
+ "./package.json": "./package.json"
38
+ },
39
+ "files": [
40
+ "dist",
41
+ "styles.css",
42
+ "chart.css",
43
+ "bin",
44
+ "gate",
45
+ "reserved-classes.json",
46
+ "src/content/executive.schema.json",
47
+ "VERSION",
48
+ "CHANGELOG.md"
49
+ ],
50
+ "scripts": {
51
+ "build": "tsup",
52
+ "test": "node --import tsx --test \"test/**/*.test.ts\" \"test/**/*.test.tsx\"",
53
+ "test:browser": "node --import tsx --test \"e2e/**/*.test.ts\"",
54
+ "prepare": "npm run build",
55
+ "prepublishOnly": "npm run build",
56
+ "prebuild": "node scripts/reserved-classes.mjs",
57
+ "test:gate": "node --test test/gate-selftest.mjs",
58
+ "check:sdd": "python scripts/check_sdd.py ."
59
+ },
60
+ "peerDependencies": {
61
+ "katex": "^0.16.0 || ^0.17.0 || ^0.18.0 || ^0.19.0",
62
+ "lucide-react": ">=0.400.0",
63
+ "react": "^18 || ^19",
64
+ "react-dom": "^18 || ^19",
65
+ "react-router": "^6 || ^7 || ^8",
66
+ "zustand": "^4 || ^5",
67
+ "playwright": ">=1.40",
68
+ "uplot": "^1.6.30"
69
+ },
70
+ "devDependencies": {
71
+ "@playwright/test": "1.63.0",
72
+ "@types/react": "^19.0.0",
73
+ "@types/react-dom": "^19.0.0",
74
+ "esbuild": "0.28.1",
75
+ "katex": "0.19.0",
76
+ "lucide-react": "1.43.0",
77
+ "react": "^19.0.0",
78
+ "react-dom": "^19.0.0",
79
+ "react-router": "^8.3.0",
80
+ "tsup": "^8.3.5",
81
+ "tsx": "^4.23.0",
82
+ "typescript": "^5.7.3",
83
+ "zustand": "^5.0.2",
84
+ "uplot": "1.6.32"
85
+ },
86
+ "overrides": {
87
+ "esbuild": "0.28.1"
88
+ },
89
+ "bin": {
90
+ "caos-shell-gate": "bin/caos-shell-gate.mjs"
91
+ },
92
+ "peerDependenciesMeta": {
93
+ "playwright": {
94
+ "optional": true
95
+ },
96
+ "uplot": {
97
+ "optional": true
98
+ }
99
+ }
100
+ }