@wenathlan/vault 2.0.94 → 2.0.118

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 CHANGED
File without changes
package/docs/.gitkeep CHANGED
File without changes
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@wenathlan/vault",
3
- "version": "2.0.94",
3
+ "version": "2.0.118",
4
4
  "description": "vault is the supabase clone of the wenathlan family: it owns the self-hosted database, auth and storage surface, and ships the documentation set and the tests directory as one package",
5
5
  "type": "module",
6
- "private": false,
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/wenathlan/devthink"
9
+ },
7
10
  "license": "GPL-3.0-only",
8
11
  "author": "Wenathlan",
9
12
  "sideEffects": false,
@@ -14,7 +17,22 @@
14
17
  "LICENSE"
15
18
  ],
16
19
  "engines": {
17
- "node": ">=26.8.1 <27"
20
+ "bare": "^1.18.0",
21
+ "bun": "^1.4.2",
22
+ "chrome": "^16.0.912",
23
+ "cnpm": "^9.4.0",
24
+ "corepack": "^0.35.0",
25
+ "deno": "^2.9.5",
26
+ "ecmascript": "^5.0.0",
27
+ "firefox": ">=0.8.0",
28
+ "iojs": "^1.0.0",
29
+ "node": "^26.8.2",
30
+ "npm": "^12.1.0",
31
+ "pnpm": "^12.6.0",
32
+ "teleport": ">=0.2.0",
33
+ "typescript": "^5.8.0",
34
+ "vscode": "^1.118.0",
35
+ "yarn": "^4.17.1"
18
36
  },
19
37
  "publishConfig": {
20
38
  "access": "public"
@@ -24,7 +42,12 @@
24
42
  "build": "vite build",
25
43
  "start": "vite preview --host",
26
44
  "preview": "vite preview --host",
27
- "check": "tsc --noEmit"
45
+ "check": "tsc --noEmit",
46
+ "typecheck": "tsc --noEmit",
47
+ "test": "node --test \"tests/*.test.ts\"",
48
+ "test:watch": "node --test --watch \"tests/*.test.ts\"",
49
+ "lint": "biome check .",
50
+ "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)')\""
28
51
  },
29
52
  "dependencies": {
30
53
  "@hono/node-server": "^2.1.3",
@@ -40,6 +63,7 @@
40
63
  "@capacitor/cli": "^8.5.2",
41
64
  "@capacitor/core": "^8.5.2",
42
65
  "@capacitor/ios": "^8.5.2",
66
+ "@fluentui/react-icons": "^2.0.344",
43
67
  "@hookform/resolvers": "^5.9.1",
44
68
  "@libsql/client": "^0.18.0",
45
69
  "@opencode-ai/sdk": "^1.18.34",
package/tests/.gitkeep CHANGED
File without changes
@@ -0,0 +1,127 @@
1
+ // # buckets.test — the bucket shelf engine of the vault, runnable with the
2
+ // node built-in runner (no dependencies, no install):
3
+ // node --test tests/buckets.test.ts
4
+ // The shelf rolls the rehearsal ledger up per bucket: policy, rows, bytes,
5
+ // lfs share — deterministic from the seed, honest about unknowns.
6
+ import assert from "node:assert/strict";
7
+ import { describe, it } from "node:test";
8
+ import {
9
+ type BucketRule,
10
+ bucketShelf,
11
+ bucketsInUse,
12
+ objectsOf,
13
+ retentionLabel,
14
+ ruleFor,
15
+ seedRules,
16
+ shelfBytes,
17
+ shelfOf,
18
+ } from "../buckets.ts";
19
+ import { VAULT_BUCKETS, seedVault } from "../objects.ts";
20
+
21
+ describe("policies", () => {
22
+ const rules = seedRules(912731);
23
+
24
+ it("musters one policy per named bucket, in shelf order", () => {
25
+ assert.deepEqual(
26
+ rules.map((rule) => rule.bucket),
27
+ [...VAULT_BUCKETS],
28
+ );
29
+ for (const rule of rules) {
30
+ assert.ok(rule.days >= 0, "a policy never goes negative");
31
+ assert.ok(rule.note.length > 0, "every policy carries its honest why");
32
+ }
33
+ });
34
+
35
+ it("keeps the forever policy at zero and the finite ones positive", () => {
36
+ const forever = rules.find((rule) => rule.bucket === "family-state");
37
+ assert.ok(forever, "the family state bucket exists");
38
+ assert.equal(forever.days, 0, "the family state keeps forever");
39
+ for (const rule of rules) {
40
+ if (rule.bucket !== "family-state") assert.ok(rule.days > 0, `${rule.bucket} rides a finite window`);
41
+ }
42
+ });
43
+
44
+ it("answers the same shelf for the same seed", () => {
45
+ assert.deepEqual(seedRules(912731), rules);
46
+ assert.notDeepEqual(seedRules(912732), rules);
47
+ });
48
+
49
+ it("answers ruleFor and refuses unknown buckets", () => {
50
+ assert.equal(ruleFor(rules, "family-state")?.days, 0);
51
+ assert.equal(ruleFor(rules, "no-such-bucket"), undefined);
52
+ });
53
+ });
54
+
55
+ describe("shelf", () => {
56
+ const vault = seedVault(20260314);
57
+ const rules = seedRules(912731);
58
+ const shelf = shelfOf(vault, rules);
59
+
60
+ it("rolls every named bucket up, even empty ones", () => {
61
+ assert.equal(shelf.length, VAULT_BUCKETS.length);
62
+ for (const entry of shelf) {
63
+ assert.ok(entry.objects >= 0);
64
+ assert.ok(entry.bytes >= 0);
65
+ assert.ok(entry.lfsShare >= 0 && entry.lfsShare <= 1);
66
+ }
67
+ });
68
+
69
+ it("accounts every row and byte on the shelf", () => {
70
+ const rows = shelf.reduce((total, entry) => total + entry.objects, 0);
71
+ assert.equal(rows, vault.length, "no row is lost between the ledger and the shelf");
72
+ const bytes = shelfBytes(shelf);
73
+ const ledgerBytes = vault.reduce((total, object) => total + object.size, 0);
74
+ assert.equal(bytes, ledgerBytes, "the shelf byte total equals the ledger byte total");
75
+ });
76
+
77
+ it("keeps the lfs share honest per bucket", () => {
78
+ for (const entry of shelf) {
79
+ const mine = objectsOf(vault, entry.rule.bucket);
80
+ const lfsBytes = mine.filter((object) => object.lfs).reduce((total, object) => total + object.size, 0);
81
+ const expected = entry.bytes === 0 ? 0 : lfsBytes / entry.bytes;
82
+ assert.ok(Math.abs(entry.lfsShare - expected) < 1e-9);
83
+ assert.equal(entry.blobRows, mine.length - entry.lfsRows);
84
+ }
85
+ });
86
+
87
+ it("answers the biggest row of the shelf", () => {
88
+ for (const entry of shelf) {
89
+ const mine = objectsOf(vault, entry.rule.bucket);
90
+ if (mine.length === 0) {
91
+ assert.equal(entry.biggest, undefined);
92
+ continue;
93
+ }
94
+ const max = Math.max(...mine.map((object) => object.size));
95
+ assert.equal(entry.biggest!.size, max);
96
+ }
97
+ });
98
+
99
+ it("lists only the buckets the ledger actually touches", () => {
100
+ const inUse = bucketsInUse(vault);
101
+ assert.ok(inUse.length > 0);
102
+ assert.ok(inUse.length <= VAULT_BUCKETS.length);
103
+ for (const bucket of inUse) assert.ok(VAULT_BUCKETS.includes(bucket as (typeof VAULT_BUCKETS)[number]));
104
+ assert.deepEqual(bucketsInUse([]), []);
105
+ });
106
+
107
+ it("shelves a bucket the ledger never touches as an honest empty row", () => {
108
+ const rules2: BucketRule[] = [...rules, { bucket: "ghost-bucket", days: 12, note: "a policy without rows" }];
109
+ const entry = bucketShelf(vault, rules2, "ghost-bucket");
110
+ assert.equal(entry.objects, 0);
111
+ assert.equal(entry.bytes, 0);
112
+ assert.equal(entry.lfsShare, 0);
113
+ assert.equal(entry.biggest, undefined);
114
+ });
115
+ });
116
+
117
+ describe("retention labels", () => {
118
+ it("renders the honest human label of a window", () => {
119
+ assert.equal(retentionLabel(0), "sealed forever");
120
+ assert.equal(retentionLabel(1), "1 day");
121
+ assert.equal(retentionLabel(30), "1 month");
122
+ assert.equal(retentionLabel(60), "2 months");
123
+ assert.equal(retentionLabel(365), "1 year");
124
+ assert.equal(retentionLabel(730), "2 years");
125
+ assert.equal(retentionLabel(45), "45 days");
126
+ });
127
+ });
@@ -0,0 +1,163 @@
1
+ // # design.test — the design contract of the vault surface, runnable with
2
+ // the node built-in runner (no dependencies, no install):
3
+ // node --test tests/design.test.ts
4
+ // The file reads the shipped sources (css, entry face, brand mark, app
5
+ // identity) and pins the family laws of the reform wave: no banned hue, no
6
+ // pill, no transition:all, the trylle type triad, the source-face entry,
7
+ // the gelo identity under the windows hands and the honest page set of the
8
+ // application identity.
9
+ import assert from "node:assert/strict";
10
+ import { readFileSync } from "node:fs";
11
+ import { describe, it } from "node:test";
12
+
13
+ function read(path: string): string {
14
+ return readFileSync(new URL(path, import.meta.url), "utf8");
15
+ }
16
+
17
+ const CSS = read("../Sol/sol.css");
18
+ const ENTRY = read("../index.html");
19
+ const MARK = read("../Sol/shell/BrandMark.tsx");
20
+ const SHELL = read("../Sol/shell/Shell.tsx");
21
+ const TOML = read("../devthink.toml");
22
+ const SOL = read("../Sol/Sol.tsx");
23
+
24
+ const BANNED = [/3b82f6/i, /2563eb/i, /f97316/i];
25
+
26
+ /** strips the windows pin keyframe — the one sanctioned scale(0) entrance —
27
+ * so the floor law can be asserted on everything else. */
28
+ function cssWithoutPinIntro(source: string): string {
29
+ return source.replace(/@keyframes dtPopIntro \{.*\} \}/, "");
30
+ }
31
+
32
+ describe("design contract", () => {
33
+ it("ships no banned hue in the stylesheet, the entry or the mark", () => {
34
+ for (const source of [CSS, ENTRY, MARK]) {
35
+ for (const banned of BANNED) {
36
+ assert.ok(!banned.test(source), `banned hue ${banned} found`);
37
+ }
38
+ }
39
+ });
40
+
41
+ it("keeps the gelo identity and the windows accent in their lanes", () => {
42
+ assert.match(CSS, /--sol-primary: #a3d7e6/, "the gelo identity token stands");
43
+ assert.match(CSS, /--win-accent: #4cc2ff/, "the windows accent governs focus and the bar cta");
44
+ assert.match(CSS, /--win-accent: #0067c0/, "the light theme follows the windows light law");
45
+ assert.match(MARK, /#a3d7e6/, "the mark rides the gelo");
46
+ });
47
+
48
+ it("retires the gold lamplight and the green ghost from the identity", () => {
49
+ assert.ok(!/3ecf8e/i.test(CSS + ENTRY), "the green ghost is gone");
50
+ const gold = CSS.lastIndexOf("#eab308");
51
+ const ice = CSS.lastIndexOf("#a3d7e6");
52
+ assert.ok(ice > gold, "the gelo retint must restate the identity after the gold era");
53
+ });
54
+
55
+ it("wears the gelo identity on the icons and the manifest", () => {
56
+ const FAVICON = read("../favicon.svg");
57
+ const ICON = read("../icon.svg");
58
+ const MANIFEST = read("../manifest.webmanifest");
59
+ assert.match(FAVICON, /#a3d7e6/, "the favicon rides the gelo");
60
+ assert.match(ICON, /#5b93a6|#c9e9f2/, "the icon rides the gelo family");
61
+ assert.match(MANIFEST, /"theme_color": "#a3d7e6"/, "the manifest theme color is the gelo");
62
+ assert.match(MANIFEST, /"background_color": "#0a0d10"/, "the manifest background is the abyss base");
63
+ assert.match(ENTRY, /theme-color" content="#0a0d10"/, "the entry theme color is the abyss base");
64
+ });
65
+
66
+ it("keeps the pill out of the stylesheet (no 999px, no stadium radius)", () => {
67
+ assert.ok(!CSS.includes("999px"), "the pill radius is retired");
68
+ assert.ok(!CSS.includes("--r-pill"), "no pill token survives");
69
+ });
70
+
71
+ it("never ships transition: all", () => {
72
+ assert.ok(!/transition:\s*all/.test(CSS), "transition: all is a reprovação");
73
+ });
74
+
75
+ it("never enters from scale(0) outside the windows pin law", () => {
76
+ // the ONE sanctioned scale(0) entrance is the dtPopIntro pin bounce —
77
+ // the windows recipe (0 → 1.125 → 0.725 → 1); everything else floors
78
+ assert.ok(/@keyframes dtPopIntro/.test(CSS), "the windows pin entrance stands");
79
+ assert.ok(!/scale\(0\)/.test(cssWithoutPinIntro(CSS)), "scale(0) outside dtPopIntro is a reprovação");
80
+ });
81
+
82
+ it("declares the trylle type triad (Inter Variable surfaces, Geist Mono numbers)", () => {
83
+ assert.match(CSS, /"Inter Variable"/);
84
+ assert.match(CSS, /"Geist Mono Variable"/);
85
+ assert.match(ENTRY, /family=Inter/);
86
+ assert.match(ENTRY, /Geist\+Mono/);
87
+ });
88
+
89
+ it("keeps the entry on the source face (./App.tsx, no hashed ghosts)", () => {
90
+ assert.match(ENTRY, /script[^>]*\.\/App\.tsx/);
91
+ assert.ok(!/-[0-9a-f]{8}\.(js|css)/.test(ENTRY), "no hashed bundle rides the entry");
92
+ assert.match(ENTRY, /href="\.\/favicon\.svg"/);
93
+ });
94
+
95
+ it("guards the reduced-motion stop on every layer", () => {
96
+ const blocks = CSS.match(/@media \(prefers-reduced-motion: reduce\)/g) ?? [];
97
+ assert.ok(blocks.length >= 4, `expected the hard stop on every layer, found ${blocks.length}`);
98
+ });
99
+
100
+ it("wears the windows hands on the shell (press law, pin entrance, resize, inactive)", () => {
101
+ assert.match(CSS, /dtPopIntro/, "the pin entrance keyframe stands");
102
+ assert.match(SHELL, /RESIZE_EDGES/, "the eight resize handles mount");
103
+ assert.match(CSS, /win-rz\[data-edge="se"\]/, "the resize handles ride the frame edges");
104
+ assert.match(CSS, /grayscale\(80%\)/, "the inactive window law stands");
105
+ assert.match(CSS, /rgb\(232 17 35\)/, "the close caption wears the windows red");
106
+ });
107
+
108
+ it("declares the honest page set (home, objects, buckets, retention, notfound)", () => {
109
+ assert.match(TOML, /pages = \["home", "objects", "buckets", "retention", "notfound"\]/);
110
+ for (const route of ['"/objects"', '"/buckets"', '"/retention"', '"/404"']) {
111
+ assert.ok(SOL.includes(`path=${route}`), `route ${route} must mount`);
112
+ }
113
+ });
114
+
115
+ it("mounts the flow law (the entry gate on /, intro and onboarding in the chain)", () => {
116
+ const GATE = read("../Sol/home/entrygate.tsx");
117
+ const HOME = read("../Sol/home/home.tsx");
118
+ const INTRO = read("../Sol/intro/intro.tsx");
119
+ const ONBOARDING = read("../Sol/onboarding/onboarding.tsx");
120
+ assert.match(GATE, /navigate\("\/intro"/, "an unseen intro navigates to the splash");
121
+ assert.match(GATE, /navigate\("\/onboarding"/, "an unseen onboarding navigates to the walk");
122
+ assert.match(HOME, /EntryGate/, "the home anchor consults the gate");
123
+ assert.match(INTRO, /markOnboardingStage\("intro"\)/, "the intro marks introseen");
124
+ assert.match(INTRO, /\/onboarding/, "the intro hands DIRECTLY to the walk");
125
+ assert.match(ONBOARDING, /markOnboardingStage\("onboarding"\)/, "the onboarding marks onboardingseen");
126
+ });
127
+
128
+ it("catalogues the seen state on the database, not just the browser", () => {
129
+ const PRISMA = read("../schema.prisma");
130
+ const DB = read("../db.ts");
131
+ const STATE = read("../onboardingstate.ts");
132
+ assert.match(PRISMA, /model OnboardingState \{/, "the prisma model stands");
133
+ assert.match(PRISMA, /introseen\s+Boolean/);
134
+ assert.match(PRISMA, /onboardingseen\s+Boolean/);
135
+ assert.match(DB, /onboarding_states/, "the sqlite mirror exists");
136
+ assert.match(DB, /getOnboardingState/, "the reader accessor exists");
137
+ assert.match(DB, /setOnboardingStage/, "the writer accessor exists");
138
+ assert.doesNotMatch(DB, /["']\s*\+\s*(visitor|stage)\b/, "no concatenated sql rides the accessors");
139
+ assert.match(STATE, /vault\.onboarding\.seen/, "the localStorage cache mirrors the db");
140
+ assert.match(STATE, /\/api\/v1\/onboarding/, "the accessor rides the platform route");
141
+ });
142
+
143
+ it("keeps tabular numerals on the platform numbers", () => {
144
+ assert.match(CSS, /font-variant-numeric:\s*tabular-nums/);
145
+ });
146
+
147
+ it("rests the title-bar mark on the landing (logo discipline)", () => {
148
+ assert.match(CSS, /data-landing/);
149
+ assert.ok(SHELL.includes("data-landing"), "the shell carries the landing attribute");
150
+ });
151
+
152
+ it("runs the family flow signature inside the mark strokes", () => {
153
+ assert.match(MARK, /vtMarkFlow/, "the flow layer exists on the vault mark");
154
+ assert.match(MARK, /vtm-flow/, "the flow gradient is declared");
155
+ assert.match(MARK, /prefers-reduced-motion: reduce[\s\S]*\.vtMarkFlow \{ opacity: 0/, "the flow hard-stops on reduced motion");
156
+ });
157
+
158
+ it("links the family row from the one table (zero drift between chrome and landing)", () => {
159
+ assert.match(SHELL, /from "\.\.\/\.\.\/family"/, "the shell reads family.ts");
160
+ const HOME = read("../Sol/home/home.tsx");
161
+ assert.match(HOME, /familyrow/, "the landing footer reads the same table");
162
+ });
163
+ });
@@ -0,0 +1,105 @@
1
+ // # family.test — the family table the chrome rides, runnable with the node
2
+ // built-in runner (no dependencies, no install):
3
+ // node --test tests/family.test.ts
4
+ // Every test watches the promise the row makes: the nine siblings beside
5
+ // vault are listed once, every slug resolves through the familyurl contract
6
+ // into a climb-one-level url, every member carries the spec §12 accent with
7
+ // its own dot ink, and no accent ever collides with the banned palette.
8
+ import assert from "node:assert/strict";
9
+ import { describe, it } from "node:test";
10
+ import { FAMILY_ACCENTS, FAMILY_SLUGS, familyAccentOf, familyrow } from "../family.ts";
11
+ import { familyurl } from "../familyurl.ts";
12
+
13
+ const BANNED = new Set(["#3b82f6", "#2563eb", "#f97316"]);
14
+
15
+ describe("family table", () => {
16
+ it("lists the nine siblings beside vault, never vault itself", () => {
17
+ assert.deepEqual(FAMILY_SLUGS, [
18
+ "devthink",
19
+ "cadria",
20
+ "stealthhead",
21
+ "debonair",
22
+ "argan",
23
+ "saddle",
24
+ "forge",
25
+ "foundry",
26
+ "getry",
27
+ ]);
28
+ assert.ok(!FAMILY_SLUGS.includes("vault"));
29
+ });
30
+
31
+ it("answers an accent for every listed sibling", () => {
32
+ for (const slug of FAMILY_SLUGS) {
33
+ const member = FAMILY_ACCENTS[slug];
34
+ assert.ok(member, `${slug} must carry an accent`);
35
+ assert.equal(member.slug, slug);
36
+ assert.match(member.accent, /^#[0-9a-fA-F]{6}$/);
37
+ assert.match(member.dotInk, /^#[0-9a-fA-F]{6}$/);
38
+ }
39
+ // the table holds exactly the ten deploy units — no orphan accents
40
+ assert.deepEqual(Object.keys(FAMILY_ACCENTS).sort(), [
41
+ "argan",
42
+ "cadria",
43
+ "debonair",
44
+ "devthink",
45
+ "forge",
46
+ "foundry",
47
+ "getry",
48
+ "saddle",
49
+ "stealthhead",
50
+ "vault",
51
+ ]);
52
+ });
53
+
54
+ it("keeps the spec §12 accents stable, verbatim", () => {
55
+ assert.equal(FAMILY_ACCENTS.devthink.accent, "#F59E0B");
56
+ assert.equal(FAMILY_ACCENTS.argan.accent, "#34d8a8");
57
+ assert.equal(FAMILY_ACCENTS.cadria.accent, "#f472b6");
58
+ assert.equal(FAMILY_ACCENTS.debonair.accent, "#d9962e");
59
+ assert.equal(FAMILY_ACCENTS.forge.accent, "#a2cb3a");
60
+ assert.equal(FAMILY_ACCENTS.foundry.accent, "#2fb8b5");
61
+ assert.equal(FAMILY_ACCENTS.getry.accent, "#8b5cf6");
62
+ assert.equal(FAMILY_ACCENTS.saddle.accent, "#e86f2d");
63
+ assert.equal(FAMILY_ACCENTS.stealthhead.accent, "#e8563f");
64
+ assert.equal(FAMILY_ACCENTS.vault.accent, "#a3d7e6");
65
+ });
66
+
67
+ it("keeps the spec accent-ink column stable", () => {
68
+ assert.equal(FAMILY_ACCENTS.forge.dotInk, "#161d03");
69
+ assert.equal(FAMILY_ACCENTS.foundry.dotInk, "#03211f");
70
+ assert.equal(FAMILY_ACCENTS.vault.dotInk, "#072330");
71
+ });
72
+
73
+ it("resolves every member through the familyurl contract", () => {
74
+ for (const slug of FAMILY_SLUGS) {
75
+ assert.equal(familyurl(slug), `../${slug}/`);
76
+ }
77
+ });
78
+
79
+ it("never ships a banned hue in the row or the table", () => {
80
+ for (const member of [...familyrow(), ...Object.values(FAMILY_ACCENTS)]) {
81
+ assert.ok(!BANNED.has(member.accent.toLowerCase()), `${member.slug} rides a banned accent`);
82
+ }
83
+ });
84
+
85
+ it("builds the rail row in slug order without gaps", () => {
86
+ const row = familyrow();
87
+ assert.equal(row.length, FAMILY_SLUGS.length);
88
+ assert.deepEqual(
89
+ row.map((member) => member.slug),
90
+ [...FAMILY_SLUGS],
91
+ );
92
+ });
93
+
94
+ it("answers familyAccentOf for the house and falls back for unknowns", () => {
95
+ assert.equal(familyAccentOf("vault")?.accent, "#a3d7e6");
96
+ assert.equal(familyAccentOf("forge")?.accent, "#a2cb3a");
97
+ assert.equal(familyAccentOf("nope"), undefined);
98
+ });
99
+
100
+ it("keeps the familyurl contract on the verbatim climb-one-level shape", () => {
101
+ assert.equal(familyurl("cadria"), "../cadria/");
102
+ assert.equal(familyurl("/cadria/"), "../cadria/");
103
+ assert.equal(familyurl("cadria"), familyurl(" cadria ".trim()));
104
+ });
105
+ });
@@ -0,0 +1,164 @@
1
+ // # objects.test — the object ledger engine of the vault, runnable with the
2
+ // node built-in runner (no dependencies, no install):
3
+ // node --test tests/objects.test.ts
4
+ // The engine rides the REAL storage contract (storage.ts): the channel
5
+ // comes from storageModeFor, the digest validates through digestValid, the
6
+ // record passes storageRecordValid — and the rehearsal is deterministic
7
+ // from the seed (the same seed always seals the same vault).
8
+ import assert from "node:assert/strict";
9
+ import { describe, it } from "node:test";
10
+ import {
11
+ VAULT_BUCKETS,
12
+ type VaultObject,
13
+ channelPreview,
14
+ digestFor,
15
+ digestShort,
16
+ fnv1a,
17
+ fnv1aHex,
18
+ formatBytes,
19
+ pointerTextOf,
20
+ sealObject,
21
+ seedVault,
22
+ vaultCensus,
23
+ verifyObject,
24
+ } from "../objects.ts";
25
+ import { digestValid, storageModeFor, storageRecordValid } from "../storage.ts";
26
+
27
+ describe("seal engine", () => {
28
+ it("splits the channels at the real 1,000,000-byte threshold", () => {
29
+ assert.equal(storageModeFor(1_000_000, "text/plain"), "blob");
30
+ assert.equal(storageModeFor(1_000_001, "text/plain"), "lfs");
31
+ assert.equal(storageModeFor(4, "image/png"), "lfs");
32
+ assert.equal(storageModeFor(4, "video/mp4"), "lfs");
33
+ assert.equal(storageModeFor(4, "model/onnx"), "lfs");
34
+ assert.equal(storageModeFor(4, "application/octet-stream"), "lfs");
35
+ assert.equal(storageModeFor(2_000_000, "text/markdown"), "lfs");
36
+ });
37
+
38
+ it("derives digests in the exact shape the contract demands", () => {
39
+ const lfs = digestFor("identity · lfs", true);
40
+ const blob = digestFor("identity · blob", false);
41
+ assert.equal(lfs.length, 64, "lfs rides the sha-256 width");
42
+ assert.equal(blob.length, 40, "blob rides the sha-1 width");
43
+ assert.match(lfs, /^[0-9a-f]{64}$/);
44
+ assert.match(blob, /^[0-9a-f]{40}$/);
45
+ assert.ok(digestValid(lfs, true));
46
+ assert.ok(digestValid(blob, false));
47
+ assert.equal(digestFor("identity · lfs", true), lfs, "the digest is deterministic");
48
+ });
49
+
50
+ it("seals a deposit through the real record gate", () => {
51
+ const sealed = sealObject("family-state", "/sites/state.db", "application/x-sqlite3", 48_211_046, 0);
52
+ assert.equal(sealed.refusal, undefined);
53
+ const record = {
54
+ id: sealed.object.id,
55
+ path: sealed.object.path,
56
+ mime: sealed.object.mime,
57
+ size: sealed.object.size,
58
+ lfs: sealed.object.lfs,
59
+ digest: sealed.object.digest,
60
+ };
61
+ assert.ok(storageRecordValid(record), "the sealed record must pass the five checks");
62
+ assert.equal(sealed.object.lfs, true, "a 48 MB row rides lfs");
63
+ });
64
+
65
+ it("refuses a deposit the contract rejects — and says why", () => {
66
+ const sealed = sealObject("family-state", "relative/path", "text/plain", 12, 0);
67
+ assert.ok(sealed.refusal, "a relative path is refused");
68
+ assert.match(sealed.refusal!, /refuses the deposit/);
69
+ });
70
+
71
+ it("never issues a banned or guessed digest", () => {
72
+ const sealed = sealObject("site-assets", "/media/cover.png", "image/png", 2_244_880, 3);
73
+ assert.ok(digestValid(sealed.object.digest, sealed.object.lfs));
74
+ assert.equal(verifyObject(sealed.object), true, "re-sealing is byte-equal");
75
+ });
76
+
77
+ it("verifies a tampered object as broken", () => {
78
+ const sealed = sealObject("run-evidence", "/runs/421337.log", "text/plain", 12_800, 9);
79
+ const tampered: VaultObject = { ...sealed.object, size: sealed.object.size + 1 };
80
+ assert.equal(verifyObject(tampered), false, "a moved byte breaks the seal");
81
+ });
82
+
83
+ it("renders the three-line lfs pointer from the real writer", () => {
84
+ const sealed = sealObject("backups", "/backups/ledger.tzst", "application/x-tzst", 220_118_016, 0);
85
+ const pointer = pointerTextOf(sealed.object);
86
+ const lines = pointer.split("\n");
87
+ assert.equal(lines[0], "version https://git-lfs.github.com/spec/v1");
88
+ assert.equal(lines[1], `oid sha256:${sealed.object.digest}`);
89
+ assert.equal(lines[2], `size ${sealed.object.size}`);
90
+ });
91
+ });
92
+
93
+ describe("rehearsal vault", () => {
94
+ const vault = seedVault(20260314);
95
+
96
+ it("musters the same rows for the same seed, byte for byte", () => {
97
+ const again = seedVault(20260314);
98
+ assert.deepEqual(again, vault);
99
+ const other = seedVault(20260315);
100
+ assert.notDeepEqual(other, vault);
101
+ });
102
+
103
+ it("rides only the named buckets and every row is sealed and verified", () => {
104
+ for (const object of vault) {
105
+ assert.ok(VAULT_BUCKETS.includes(object.bucket as (typeof VAULT_BUCKETS)[number]), `${object.path} rides a named bucket`);
106
+ assert.equal(verifyObject(object), true, `${object.path} is sealed intact`);
107
+ assert.ok(object.size >= 0);
108
+ assert.ok(object.path.startsWith("/"));
109
+ }
110
+ });
111
+
112
+ it("carries both channels across the rehearsal", () => {
113
+ const census = vaultCensus(vault);
114
+ assert.ok(census.lfsRows > 0, "the lfs channel is exercised");
115
+ assert.ok(census.blobRows > 0, "the blob channel is exercised");
116
+ assert.equal(census.lfsRows + census.blobRows, census.total);
117
+ assert.equal(census.lfsBytes + census.blobBytes, census.bytes);
118
+ });
119
+
120
+ it("answers the census with the biggest row on top", () => {
121
+ const census = vaultCensus(vault);
122
+ assert.equal(census.total, vault.length);
123
+ assert.ok(census.biggest, "the biggest row exists");
124
+ for (const object of vault) {
125
+ assert.ok(census.biggest!.size >= object.size);
126
+ }
127
+ assert.equal(vaultCensus([]).total, 0);
128
+ assert.equal(vaultCensus([]).biggest, undefined);
129
+ });
130
+ });
131
+
132
+ describe("ledger render", () => {
133
+ it("formats bytes with one unit and tabular intent", () => {
134
+ assert.equal(formatBytes(0), "0 B");
135
+ assert.equal(formatBytes(512), "512 B");
136
+ assert.equal(formatBytes(1024), "1.0 KB");
137
+ assert.equal(formatBytes(1_572_864), "1.5 MB");
138
+ assert.equal(formatBytes(1_073_741_824), "1.0 GB");
139
+ assert.equal(formatBytes(2_244_880), "2.1 MB");
140
+ });
141
+
142
+ it("shortens a digest without inventing characters", () => {
143
+ const long = "a".repeat(64);
144
+ assert.equal(digestShort(long), "aaaaaaaa…aaaa");
145
+ assert.equal(digestShort("abcd1234"), "abcd1234");
146
+ });
147
+
148
+ it("keeps the family fnv-1a byte-exact", () => {
149
+ assert.equal(fnv1a(""), 0x811c9dc5);
150
+ assert.equal(fnv1a("a"), 0xe40c292c);
151
+ assert.equal(fnv1aHex("hello"), fnv1a("hello").toString(16).padStart(8, "0"));
152
+ assert.match(fnv1aHex("hello"), /^[0-9a-f]{8}$/);
153
+ });
154
+
155
+ it("previews the channel answer before anything is sealed", () => {
156
+ const lfs = channelPreview("video/mp4", 12);
157
+ assert.equal(lfs.channel, "lfs");
158
+ assert.match(lfs.why, /binary master or over the threshold|binary master/);
159
+ assert.match(lfs.digestShape, /sha-256/);
160
+ const blob = channelPreview("text/markdown", 1200);
161
+ assert.equal(blob.channel, "blob");
162
+ assert.match(blob.digestShape, /sha-1/);
163
+ });
164
+ });
@@ -0,0 +1,64 @@
1
+ // # onboardingstate.test — the pure contract of the seen flags: the host
2
+ // validation of the platform base (https only, public hosts only) and the
3
+ // seen-wins stage merge the accessor and the pages share. runnable with
4
+ // the node built-in runner (no dependencies, no install):
5
+ // node --test tests/onboardingstate.test.ts
6
+ import assert from "node:assert/strict";
7
+ import { describe, it } from "node:test";
8
+ import { hostallowed, mergeStage } from "../onboardingstate.ts";
9
+
10
+ describe("onboarding state", () => {
11
+ it("accepts public https bases", () => {
12
+ assert.equal(hostallowed("https://gateway.wenathlan.dev"), true);
13
+ assert.equal(hostallowed("https://gateway.wenathlan.dev/"), true);
14
+ assert.equal(hostallowed("https://vault.example.com:8443"), true);
15
+ });
16
+
17
+ it("refuses plaintext, localhost, loopback and every private or reserved range", () => {
18
+ const refused = [
19
+ "http://gateway.wenathlan.dev",
20
+ "https://localhost",
21
+ "https://localhost:8787",
22
+ "https://app.localhost",
23
+ "https://box.local",
24
+ "https://service.internal",
25
+ "https://127.0.0.1",
26
+ "https://0.0.0.0",
27
+ "https://10.0.0.9",
28
+ "https://172.16.0.1",
29
+ "https://172.31.255.255",
30
+ "https://192.168.1.4",
31
+ "https://169.254.10.10",
32
+ "https://100.64.0.1",
33
+ "https://192.0.2.7",
34
+ "https://198.51.100.7",
35
+ "https://203.0.113.9",
36
+ "https://[::1]",
37
+ "https://[::ffff:127.0.0.1]",
38
+ "https://[::ffff:10.0.0.1]",
39
+ "https://[fd00::1]",
40
+ "https://[fe80::1]",
41
+ "not a url",
42
+ "",
43
+ ];
44
+ for (const base of refused) assert.equal(hostallowed(base), false, base);
45
+ });
46
+
47
+ it("merges stages seen-wins — a stage never unsees", () => {
48
+ const fresh = { introseen: false, onboardingseen: false };
49
+ assert.deepEqual(mergeStage(fresh, "intro"), { introseen: true, onboardingseen: false });
50
+ assert.deepEqual(mergeStage(fresh, "onboarding"), { introseen: false, onboardingseen: true });
51
+ const half = { introseen: true, onboardingseen: false };
52
+ assert.deepEqual(mergeStage(half, "onboarding"), { introseen: true, onboardingseen: true });
53
+ assert.deepEqual(mergeStage(mergeStage(half, "onboarding"), "intro"), {
54
+ introseen: true,
55
+ onboardingseen: true,
56
+ });
57
+ });
58
+
59
+ it("never mutates the state it merges", () => {
60
+ const state = { introseen: false, onboardingseen: false };
61
+ mergeStage(state, "intro");
62
+ assert.deepEqual(state, { introseen: false, onboardingseen: false });
63
+ });
64
+ });
@@ -0,0 +1,133 @@
1
+ // # retention.test — the clock engine of the vault, runnable with the node
2
+ // built-in runner (no dependencies, no install):
3
+ // node --test tests/retention.test.ts
4
+ // The clock is rehearsal time: one tick is one day, the sweep is a PLAN
5
+ // (pure — nothing is deleted), and the same ledger, rules and tick always
6
+ // answer the same verdicts.
7
+ import assert from "node:assert/strict";
8
+ import { describe, it } from "node:test";
9
+ import { seedRules } from "../buckets.ts";
10
+ import { sealObject, seedVault } from "../objects.ts";
11
+ import {
12
+ ROLL_STEP,
13
+ firstDueTick,
14
+ horizonTick,
15
+ retentionVerdicts,
16
+ sweepPlan,
17
+ tickLabel,
18
+ verdictOf,
19
+ } from "../retention.ts";
20
+
21
+ const rules = seedRules(912731);
22
+ const vault = seedVault(20260314);
23
+
24
+ describe("verdicts", () => {
25
+ it("answers protected for the forever policy, whatever the age", () => {
26
+ const familyRow = vault.find((object) => object.bucket === "family-state");
27
+ assert.ok(familyRow, "the rehearsal rides the family-state bucket");
28
+ const verdict = verdictOf(familyRow, rules, 10_000);
29
+ assert.equal(verdict.state, "protected");
30
+ assert.match(verdict.why, /keeps forever/);
31
+ });
32
+
33
+ it("answers sealed inside the window and due past it", () => {
34
+ const object = sealObject("backups", "/backups/ledger.tzst", "application/x-tzst", 220_118_016, 0).object;
35
+ const backupRule = rules.find((rule) => rule.bucket === "backups")!;
36
+ assert.ok(backupRule.days > 0, "the backups window is finite in this rehearsal");
37
+ assert.equal(verdictOf(object, rules, backupRule.days).state, "sealed", "the last day is still sealed");
38
+ assert.equal(verdictOf(object, rules, backupRule.days + 1).state, "due", "one day past the window, the row is due");
39
+ assert.equal(verdictOf(object, rules, 0).state, "sealed");
40
+ });
41
+
42
+ it("keeps the age non-negative and the why honest", () => {
43
+ const object = vault[0];
44
+ for (const now of [0, object.sealedAt, object.sealedAt + 100]) {
45
+ const verdict = verdictOf(object, rules, now);
46
+ assert.ok(verdict.ageDays >= 0);
47
+ assert.ok(verdict.why.length > 0);
48
+ }
49
+ });
50
+
51
+ it("answers every row of the ledger, in ledger order", () => {
52
+ const verdicts = retentionVerdicts(vault, rules, 0);
53
+ assert.equal(verdicts.length, vault.length);
54
+ for (let index = 0; index < verdicts.length; index++) {
55
+ assert.equal(verdicts[index].object.id, vault[index].id);
56
+ }
57
+ });
58
+
59
+ it("is pure: the same inputs answer the same verdicts", () => {
60
+ const once = retentionVerdicts(vault, rules, 400);
61
+ const twice = retentionVerdicts(vault, rules, 400);
62
+ assert.deepEqual(twice, once);
63
+ });
64
+ });
65
+
66
+ describe("sweep plan", () => {
67
+ it("collects only due rows and never mutates the ledger", () => {
68
+ const before = JSON.stringify(vault);
69
+ const plan = sweepPlan(vault, rules, 400);
70
+ assert.equal(JSON.stringify(vault), before, "the plan is pure");
71
+ for (const verdict of plan.due) {
72
+ assert.equal(verdict.state, "due");
73
+ }
74
+ const planBytes = plan.due.reduce((total, verdict) => total + verdict.object.size, 0);
75
+ assert.equal(plan.bytes, planBytes);
76
+ });
77
+
78
+ it("accounts the plan bytes per bucket", () => {
79
+ const plan = sweepPlan(vault, rules, 400);
80
+ let bucketTotal = 0;
81
+ for (const bytes of Object.values(plan.byBucket)) bucketTotal += bytes;
82
+ assert.equal(bucketTotal, plan.bytes);
83
+ const buckets = new Set(plan.due.map((verdict) => verdict.object.bucket));
84
+ assert.deepEqual(Object.keys(plan.byBucket).sort(), [...buckets].sort());
85
+ });
86
+
87
+ it("answers an empty plan before any window can expire", () => {
88
+ const plan = sweepPlan(vault, rules, 0);
89
+ assert.equal(plan.due.length, 0);
90
+ assert.equal(plan.bytes, 0);
91
+ assert.deepEqual(plan.byBucket, {});
92
+ });
93
+ });
94
+
95
+ describe("the clock", () => {
96
+ it("binds the first due tick to the earliest seal plus window", () => {
97
+ const first = firstDueTick(vault, rules);
98
+ assert.ok(Number.isFinite(first));
99
+ let expected = Number.POSITIVE_INFINITY;
100
+ for (const object of vault) {
101
+ const rule = rules.find((entry) => entry.bucket === object.bucket);
102
+ if (!rule || rule.days === 0) continue;
103
+ expected = Math.min(expected, object.sealedAt + rule.days);
104
+ }
105
+ assert.equal(first, expected);
106
+ });
107
+
108
+ it("binds the horizon to the far end of the finite windows", () => {
109
+ const horizon = horizonTick(vault, rules);
110
+ let expected = 0;
111
+ for (const object of vault) {
112
+ const rule = rules.find((entry) => entry.bucket === object.bucket);
113
+ if (!rule || rule.days === 0) continue;
114
+ expected = Math.max(expected, object.sealedAt + rule.days);
115
+ }
116
+ assert.equal(horizon, expected);
117
+ assert.ok(horizon >= firstDueTick(vault, rules));
118
+ });
119
+
120
+ it("answers never for a shelf that only keeps forever", () => {
121
+ const foreverVault = vault.filter((object) => object.bucket === "family-state");
122
+ assert.ok(foreverVault.length > 0);
123
+ assert.equal(firstDueTick(foreverVault, rules), Number.POSITIVE_INFINITY);
124
+ assert.equal(horizonTick(foreverVault, rules), 0);
125
+ });
126
+
127
+ it("rolls in deterministic steps and labels the days", () => {
128
+ assert.equal(ROLL_STEP, 7);
129
+ assert.equal(tickLabel(0), "day 0");
130
+ assert.equal(tickLabel(1), "day 1");
131
+ assert.equal(tickLabel(28), "day 28");
132
+ });
133
+ });