@wenathlan/cobaia 0.0.0-stage → 2.0.155
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/App.tsx +52 -0
- package/Dockerfile +45 -0
- package/Sol/Sol.tsx +91 -0
- package/Sol/apps/apps.tsx +66 -0
- package/Sol/blog/blog.tsx +86 -0
- package/Sol/home/home.tsx +140 -0
- package/Sol/intro/intro.tsx +187 -0
- package/Sol/legal/legal.tsx +76 -0
- package/Sol/notfound/notfound.tsx +32 -0
- package/Sol/pricing/pricing.tsx +98 -0
- package/Sol/shell/Shell.tsx +161 -0
- package/Sol/sol.css +1944 -0
- package/Sol/support/support.tsx +102 -0
- package/Sol/wingets/IntroFooter.tsx +69 -0
- package/Sol/wingets/IntroPlans.tsx +93 -0
- package/Sol/wingets/IntroSplash.tsx +105 -0
- package/Sol/wingets/ShellAdsslot.tsx +47 -0
- package/Sol/wingets/ShellBrand.tsx +86 -0
- package/Sol/wingets/index.ts +18 -0
- package/_headers +4 -0
- package/_redirects +1 -0
- package/adsconfig.ts +59 -0
- package/biome.jsonc +134 -0
- package/bun.lock +2331 -0
- package/capacitor.config.ts +51 -0
- package/checkoutlinks.ts +80 -0
- package/content.ts +425 -0
- package/db.ts +219 -0
- package/devthink.toml +5 -0
- package/docs/.gitkeep +0 -0
- package/familyurl.ts +22 -0
- package/favicon.svg +1 -0
- package/icon.svg +1 -0
- package/index.html +26 -0
- package/labels.ts +291 -0
- package/manifest.webmanifest +56 -0
- package/mime.types +12 -0
- package/netlify.toml +2 -0
- package/package.json +200 -4
- package/pnpm-workspace.yaml +82 -0
- package/posts.ts +54 -0
- package/robots.txt +90 -0
- package/roster.ts +29 -0
- package/sandboxmodality.ts +138 -0
- package/schema.prisma +31 -0
- package/sitemap.xml +42 -0
- package/tests/adsconfig.test.ts +51 -0
- package/tests/checkoutlinks.test.ts +73 -0
- package/tests/familyurl.test.ts +40 -0
- package/tests/posts.test.ts +76 -0
- package/tests/sandboxmodality.test.ts +77 -0
- package/tsconfig.json +22 -0
- package/tsconfig.node.json +22 -0
- package/vercel.json +3 -0
- package/vite.config.ts +44 -0
- package/vitest.config.ts +42 -0
- package/wrangler.toml +7 -0
- package/README.md +0 -3
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// # adsconfig.test — the honest ad slot registry of the family site.
|
|
2
|
+
// Run: pnpm vitest run tests/adsconfig.test.ts
|
|
3
|
+
// The suite watches the promise the ad slot makes: one reserved slot per
|
|
4
|
+
// content page (home, apps, blog), every slot id shipping EMPTY (no ad
|
|
5
|
+
// network script, no id, no key is ever committed — the loader enters after
|
|
6
|
+
// approval through the deploy environment), the fixed visible label, and the
|
|
7
|
+
// aspect-ratio shapes the containers reserve so the page never shifts.
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
import { describe, expect, it } from "vitest";
|
|
10
|
+
import { ADS_EMPTY, ADS_LABEL, ADSSLOTS, adsslotfor, adsslotlive } from "../adsconfig.ts";
|
|
11
|
+
|
|
12
|
+
describe("the ad slot registry", () => {
|
|
13
|
+
it("reserves one slot per content page and nothing else", () => {
|
|
14
|
+
assert.deepEqual(Object.keys(ADSSLOTS), ["home", "apps", "blog"]);
|
|
15
|
+
assert.deepEqual(
|
|
16
|
+
Object.values(ADSSLOTS).map((slot) => slot.format),
|
|
17
|
+
["display", "in-feed", "in-article"],
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("ships every slot id empty — the honest placeholder", () => {
|
|
22
|
+
for (const [page, slot] of Object.entries(ADSSLOTS)) {
|
|
23
|
+
assert.equal(slot.slotid, "", `${page} must not carry a committed ad network id`);
|
|
24
|
+
assert.equal(adsslotlive(slot), false, `${page} must read as not configured`);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("labels the container advertisement, visibly and always", () => {
|
|
29
|
+
assert.equal(ADS_LABEL, "advertisement");
|
|
30
|
+
assert.ok(ADS_EMPTY.length > 0);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("reserves fixed aspect ratios so the page never shifts", () => {
|
|
34
|
+
for (const slot of Object.values(ADSSLOTS)) {
|
|
35
|
+
assert.match(slot.ratio, /^\d+ \/ \d+$/);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("answers the live contract on real slot ids only", () => {
|
|
40
|
+
assert.equal(adsslotlive(undefined), false);
|
|
41
|
+
assert.equal(adsslotlive({ slotid: " ", format: "display", ratio: "728 / 90" }), false);
|
|
42
|
+
assert.equal(adsslotlive({ slotid: "ca-pub-0000000000000000", format: "display", ratio: "728 / 90" }), true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("returns nothing for pages without a slot", () => {
|
|
46
|
+
expect(adsslotfor("pricing")).toBeUndefined();
|
|
47
|
+
expect(adsslotfor("support")).toBeUndefined();
|
|
48
|
+
expect(adsslotfor("legal")).toBeUndefined();
|
|
49
|
+
expect(adsslotfor("notfound")).toBeUndefined();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// # checkoutlinks.test — the billing configuration of the family site.
|
|
2
|
+
// Run: pnpm vitest run tests/checkoutlinks.test.ts
|
|
3
|
+
// The suite watches the promise the pricing page makes: the three plans
|
|
4
|
+
// exist with the placeholder prices (US$0 / 5 / 19 a month), every checkout
|
|
5
|
+
// href ships EMPTY so the interface renders the honest empty state instead
|
|
6
|
+
// of a dead link, the support channels obey the same rule, and the
|
|
7
|
+
// configured/not-configured contract answers only on real values. The plan
|
|
8
|
+
// prose (names, billing period, blurbs, features) lives in the label
|
|
9
|
+
// manifest — the suite pins that the store carries a sentence for every key
|
|
10
|
+
// the billing table cites, so a card never renders a bare key.
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { describe, expect, it } from "vitest";
|
|
13
|
+
import { checkoutlinks, isconfigured, planbyid, supportlinks } from "../checkoutlinks.ts";
|
|
14
|
+
import { LABEL_MANIFEST } from "../labels.ts";
|
|
15
|
+
|
|
16
|
+
describe("checkout links", () => {
|
|
17
|
+
it("carries the three honest plans in display order", () => {
|
|
18
|
+
assert.deepEqual(
|
|
19
|
+
checkoutlinks().map((plan) => plan.id),
|
|
20
|
+
["free", "pro", "studio"],
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("prices the plans at the placeholder us$0 / 5 / 19 a month", () => {
|
|
25
|
+
const prices = new Map(checkoutlinks().map((plan) => [plan.id, plan.priceusd]));
|
|
26
|
+
expect(prices.get("free")).toBe(0);
|
|
27
|
+
expect(prices.get("pro")).toBe(5);
|
|
28
|
+
expect(prices.get("studio")).toBe(19);
|
|
29
|
+
for (const plan of checkoutlinks()) {
|
|
30
|
+
assert.ok(plan.featureKeys.length >= 3, `${plan.id} must list at least three honest features`);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("answers every plan sentence from the label manifest — zero hardcoded prose", () => {
|
|
35
|
+
const prose = (key: string): string => (LABEL_MANIFEST as Record<string, string>)[key];
|
|
36
|
+
for (const plan of checkoutlinks()) {
|
|
37
|
+
assert.ok(prose(plan.nameKey), `${plan.id} name key must answer from the manifest`);
|
|
38
|
+
assert.ok(prose(plan.perKey), `${plan.id} period key must answer from the manifest`);
|
|
39
|
+
assert.ok(prose(plan.blurbKey), `${plan.id} blurb key must answer from the manifest`);
|
|
40
|
+
for (const featureKey of plan.featureKeys) {
|
|
41
|
+
assert.ok(prose(featureKey), `${plan.id} feature key must answer from the manifest`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
assert.equal(prose("pricing.plan.free.name"), "Free");
|
|
45
|
+
assert.equal(prose("pricing.plan.pro.name"), "Pro");
|
|
46
|
+
assert.equal(prose("pricing.plan.studio.name"), "Studio");
|
|
47
|
+
assert.equal(prose("pricing.plan.free.per"), "month");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("ships every checkout href empty — the honest empty state", () => {
|
|
51
|
+
for (const plan of checkoutlinks()) {
|
|
52
|
+
assert.equal(plan.href, "", `${plan.id} must not carry a fake checkout link`);
|
|
53
|
+
assert.equal(isconfigured(plan.href), false, `${plan.id} must read as not configured`);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("keeps the support channels unconfigured until the owner sets them", () => {
|
|
58
|
+
assert.equal(supportlinks().sponsors, "");
|
|
59
|
+
assert.equal(supportlinks().kofi, "");
|
|
60
|
+
assert.equal(isconfigured(supportlinks().sponsors), false);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("answers the configured contract on real values only", () => {
|
|
64
|
+
assert.equal(isconfigured(""), false);
|
|
65
|
+
assert.equal(isconfigured(" "), false);
|
|
66
|
+
assert.equal(isconfigured("https://buy.stripe.com/example"), true);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("resolves plans by id and nothing else", () => {
|
|
70
|
+
assert.equal(planbyid("pro")?.id, "pro");
|
|
71
|
+
assert.equal(planbyid("enterprise"), undefined);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// # familyurl.test — the address book contract of the family deploy units.
|
|
2
|
+
// Run: pnpm vitest run tests/familyurl.test.ts
|
|
3
|
+
// The suite watches the promise the directory and the footer make: every
|
|
4
|
+
// family member resolves one level above this application, always with the
|
|
5
|
+
// trailing slash, and pasted segments or stray slashes land on the same
|
|
6
|
+
// address. Pure string shaping — no network, no side effects.
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { describe, it } from "vitest";
|
|
9
|
+
import { familyurl } from "../familyurl.ts";
|
|
10
|
+
|
|
11
|
+
describe("the familyurl contract", () => {
|
|
12
|
+
it("resolves a bare slug one level above, with the trailing slash", () => {
|
|
13
|
+
assert.equal(familyurl("vault"), "../vault/");
|
|
14
|
+
assert.equal(familyurl("devthink"), "../devthink/");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("trims stray slashes from pasted segments", () => {
|
|
18
|
+
assert.equal(familyurl("/forge/"), "../forge/");
|
|
19
|
+
assert.equal(familyurl("forge/"), "../forge/");
|
|
20
|
+
assert.equal(familyurl("/forge"), "../forge/");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("resolves the whole roster the same way", () => {
|
|
24
|
+
const roster = [
|
|
25
|
+
"devthink",
|
|
26
|
+
"saddle",
|
|
27
|
+
"forge",
|
|
28
|
+
"foundry",
|
|
29
|
+
"vault",
|
|
30
|
+
"getry",
|
|
31
|
+
"cadria",
|
|
32
|
+
"debonair",
|
|
33
|
+
"stealthhead",
|
|
34
|
+
"argan",
|
|
35
|
+
];
|
|
36
|
+
for (const slug of roster) {
|
|
37
|
+
assert.equal(familyurl(slug), `../${slug}/`);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// # posts.test — the seed blog of the family site.
|
|
2
|
+
// Run: pnpm vitest run tests/posts.test.ts
|
|
3
|
+
// The suite watches the promise the blog page makes: the index is sorted
|
|
4
|
+
// newest first no matter the array order, the slugs are unique and url-safe,
|
|
5
|
+
// every seed post carries a real ~300 word body (the ad networks approve
|
|
6
|
+
// content, not containers) and the honest word counter agrees with itself.
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { describe, expect, it } from "vitest";
|
|
9
|
+
import { postindex, posts, postwords } from "../posts.ts";
|
|
10
|
+
|
|
11
|
+
// the content lane answers the same rows the seed carries (wave of the
|
|
12
|
+
// content law): the const retired, the accessor serves the identical data
|
|
13
|
+
|
|
14
|
+
describe("the seed blog", () => {
|
|
15
|
+
it("carries the three seed posts about the family", () => {
|
|
16
|
+
assert.equal(posts().length, 3);
|
|
17
|
+
assert.deepEqual(
|
|
18
|
+
posts().map((post) => post.slug),
|
|
19
|
+
["ten-sites-one-engine", "one-css-one-tree", "no-backend-no-tracking"],
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("indexes the posts newest first", () => {
|
|
24
|
+
const index = postindex();
|
|
25
|
+
assert.deepEqual(
|
|
26
|
+
index.map((post) => post.slug),
|
|
27
|
+
["ten-sites-one-engine", "one-css-one-tree", "no-backend-no-tracking"],
|
|
28
|
+
);
|
|
29
|
+
for (let i = 1; i < index.length; i++) {
|
|
30
|
+
assert.ok(index[i - 1].date >= index[i].date, "the index must be sorted by date, newest first");
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("does not mutate the source order when indexing", () => {
|
|
35
|
+
const before = posts()
|
|
36
|
+
.map((post) => post.slug)
|
|
37
|
+
.join(",");
|
|
38
|
+
postindex();
|
|
39
|
+
assert.equal(
|
|
40
|
+
posts()
|
|
41
|
+
.map((post) => post.slug)
|
|
42
|
+
.join(","),
|
|
43
|
+
before,
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("keeps the slugs unique and url-safe", () => {
|
|
48
|
+
const slugs = posts().map((post) => post.slug);
|
|
49
|
+
assert.equal(new Set(slugs).size, slugs.length);
|
|
50
|
+
for (const slug of slugs) {
|
|
51
|
+
assert.match(slug, /^[a-z0-9]+(-[a-z0-9]+)*$/);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("dates every post in iso form and before the wave", () => {
|
|
56
|
+
for (const post of posts()) {
|
|
57
|
+
assert.match(post.date, /^\d{4}-\d{2}-\d{2}$/);
|
|
58
|
+
assert.ok(post.date <= "2026-10-10", "a seed post cannot be dated in the future");
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("writes a real ~300 word body for every post", () => {
|
|
63
|
+
for (const post of posts()) {
|
|
64
|
+
const words = postwords(post);
|
|
65
|
+
assert.ok(words >= 250, `${post.slug} carries ${words} words — the seed posts ship real content`);
|
|
66
|
+
assert.ok(words <= 380, `${post.slug} carries ${words} words — the seed posts stay ~300`);
|
|
67
|
+
assert.ok(post.excerpt.length > 40, `${post.slug} must carry a real excerpt`);
|
|
68
|
+
assert.ok(post.body.length >= 3, `${post.slug} must carry more than two paragraphs`);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("counts words the honest way (split, no empties)", () => {
|
|
73
|
+
const first = posts()[0];
|
|
74
|
+
expect(postwords(first)).toBe(first.body.join(" ").split(/\s+/).filter(Boolean).length);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// # sandboxmodality.test — the workload lane declaration of cobaia (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 cobaia, 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 { lanesof } from "@wenathlan/saddle/workloadlaw";
|
|
15
|
+
import { describe, it } from "vitest";
|
|
16
|
+
import { APPSLUG, laneof, lanesandbox, lanesandboxfromenv, SANDBOXLANEENV, WORKLOADLANES } from "../sandboxmodality.ts";
|
|
17
|
+
|
|
18
|
+
describe("the workload lane mirror (the engine catalog lockstep)", () => {
|
|
19
|
+
it("answers the engine catalog rows for cobaia — the byte pin", () => {
|
|
20
|
+
const engine = lanesof("cobaia");
|
|
21
|
+
assert.equal(engine.length, WORKLOADLANES.length, "the mirror carries every cobaia lane");
|
|
22
|
+
for (let index = 0; index < engine.length; index += 1) {
|
|
23
|
+
const row = engine[index];
|
|
24
|
+
const mirror = WORKLOADLANES[index];
|
|
25
|
+
assert.equal(mirror.app, row.app);
|
|
26
|
+
assert.equal(mirror.lane, row.lane, `lane ${index} id drifts from the engine`);
|
|
27
|
+
assert.equal(mirror.role, row.role, `lane ${row.lane} role drifts from the engine`);
|
|
28
|
+
assert.deepEqual(mirror.demand, row.demand, `lane ${row.lane} demand drifts from the engine`);
|
|
29
|
+
assert.equal(mirror.today, row.today, `lane ${row.lane} today drifts from the engine`);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("carries the app slug on every row and answers the lane lookup", () => {
|
|
34
|
+
for (const row of WORKLOADLANES) {
|
|
35
|
+
assert.equal(row.app, APPSLUG);
|
|
36
|
+
}
|
|
37
|
+
const first = laneof("plan-checkout");
|
|
38
|
+
assert.ok(first !== undefined);
|
|
39
|
+
assert.equal(first.today, "app-lane");
|
|
40
|
+
assert.equal(laneof("no-such-lane"), undefined);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("the sandbox lane seat (the deploy decides)", () => {
|
|
45
|
+
it("answers the declared local default when the deploy carries no choice", () => {
|
|
46
|
+
const absent = lanesandbox(undefined);
|
|
47
|
+
assert.deepEqual(absent, { ok: true, seat: "local", sandboxed: false });
|
|
48
|
+
const blank = lanesandbox(" ");
|
|
49
|
+
assert.deepEqual(blank, { ok: true, seat: "local", sandboxed: false });
|
|
50
|
+
const explicit = lanesandbox("local");
|
|
51
|
+
assert.deepEqual(explicit, { ok: true, seat: "local", sandboxed: false });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("opts the lanes onto the platform join when the deploy chooses join", () => {
|
|
55
|
+
const joined = lanesandbox("join");
|
|
56
|
+
assert.deepEqual(joined, { ok: true, seat: "join", sandboxed: true, pinned: false });
|
|
57
|
+
const padded = lanesandbox(" join ");
|
|
58
|
+
assert.deepEqual(padded, { ok: true, seat: "join", sandboxed: true, pinned: false });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("pins the named modality for the platform join to validate", () => {
|
|
62
|
+
const pinned = lanesandbox("runner-node");
|
|
63
|
+
assert.deepEqual(pinned, { ok: true, seat: "runner-node", sandboxed: true, pinned: true });
|
|
64
|
+
// the module never judges engine vocabulary: the platform's join
|
|
65
|
+
// validates the pinned id at dispatch, the mirror only names it
|
|
66
|
+
const unusual = lanesandbox("microvm-firecracker");
|
|
67
|
+
assert.deepEqual(unusual, { ok: true, seat: "microvm-firecracker", sandboxed: true, pinned: true });
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("reads the seat off the environment lookup the deploy pins", () => {
|
|
71
|
+
assert.equal(SANDBOXLANEENV, "COBAIA_SANDBOX_LANE");
|
|
72
|
+
const unset = lanesandboxfromenv(() => undefined);
|
|
73
|
+
assert.deepEqual(unset, { ok: true, seat: "local", sandboxed: false });
|
|
74
|
+
const set = lanesandboxfromenv((key) => (key === SANDBOXLANEENV ? "join" : undefined));
|
|
75
|
+
assert.deepEqual(set, { ok: true, seat: "join", sandboxed: true, pinned: false });
|
|
76
|
+
});
|
|
77
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// the cobaia typescript baseline.
|
|
2
|
+
{
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"noEmit": true,
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleResolution": "bundler",
|
|
8
|
+
"paths": {
|
|
9
|
+
"@wenathlan/vault/labelstore": ["../vault/labelstore.ts"],
|
|
10
|
+
"@wenathlan/vault/storagelane": ["../vault/storagelane.ts"]
|
|
11
|
+
},
|
|
12
|
+
"strict": true,
|
|
13
|
+
"lib": ["esnext", "dom", "dom.iterable"],
|
|
14
|
+
"jsx": "react-jsx",
|
|
15
|
+
"esModuleInterop": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"allowImportingTsExtensions": true,
|
|
18
|
+
"types": ["vite/client", "node"]
|
|
19
|
+
},
|
|
20
|
+
"include": ["*.ts", "*.tsx", "Sol/**/*.ts", "Sol/**/*.tsx"],
|
|
21
|
+
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2023"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
|
|
8
|
+
/* Bundler mode */
|
|
9
|
+
"moduleResolution": "bundler",
|
|
10
|
+
"allowImportingTsExtensions": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"moduleDetection": "force",
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
|
|
15
|
+
/* Linting */
|
|
16
|
+
"strict": true,
|
|
17
|
+
"noUnusedLocals": true,
|
|
18
|
+
"noUnusedParameters": true,
|
|
19
|
+
"noFallthroughCasesInSwitch": true
|
|
20
|
+
},
|
|
21
|
+
"include": ["vite.config.ts"]
|
|
22
|
+
}
|
package/vercel.json
ADDED
package/vite.config.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Style: cobaia sol — the vite config lives at the app root while the theme stays in Sol/: static build emitting the hashed bundles directly into the app root folder (publish = "."), with a repository-scoped base path so the theme answers any deploy root (pages subpath, vercel, netlify, workers). The Tailwind 4 vite plugin compiles the theme stylesheet's `@import "tailwindcss"` head (the @theme source of truth + the light/hcf custom variants). */
|
|
2
|
+
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
5
|
+
import react from "@vitejs/plugin-react";
|
|
6
|
+
import { defineConfig } from "vite";
|
|
7
|
+
|
|
8
|
+
function pagesBasePath(value: string | undefined): string {
|
|
9
|
+
const candidate = value?.trim() || "/";
|
|
10
|
+
if (candidate === "." || candidate === "./") return "./";
|
|
11
|
+
return candidate === "/" ? candidate : `/${candidate.replace(/^\/+|\/+$/g, "")}/`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default defineConfig({
|
|
15
|
+
base: pagesBasePath(process.env.VITE_BASE_PATH),
|
|
16
|
+
plugins: [react(), tailwindcss()],
|
|
17
|
+
resolve: {
|
|
18
|
+
alias: {
|
|
19
|
+
/** the label store bridge of the ownership law: the app's copy
|
|
20
|
+
* mechanism rides the vault factory through the published library
|
|
21
|
+
* specifier ("@wenathlan/vault/labelstore"), and this bridge resolves
|
|
22
|
+
* the specifier to the monorepo source the sibling engine carries —
|
|
23
|
+
* the mechanism is never duplicated inside cobaia. the entry retires
|
|
24
|
+
* when the published envelope carries the built label store. */
|
|
25
|
+
"@wenathlan/vault/labelstore": path.resolve(import.meta.dirname, "../vault/labelstore.ts"),
|
|
26
|
+
"@": path.resolve(import.meta.dirname, "Sol"),
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
root: import.meta.dirname,
|
|
30
|
+
build: {
|
|
31
|
+
outDir: path.resolve(import.meta.dirname, "."),
|
|
32
|
+
emptyOutDir: false,
|
|
33
|
+
assetsDir: "",
|
|
34
|
+
},
|
|
35
|
+
/** the theme (Sol/) and the root logics (checkoutlinks, posts, adsconfig) share one app root, so the dev server allows the app root. */
|
|
36
|
+
server: {
|
|
37
|
+
fs: {
|
|
38
|
+
allow: [import.meta.dirname, path.resolve(import.meta.dirname, "../vault")],
|
|
39
|
+
},
|
|
40
|
+
host: true,
|
|
41
|
+
strictPort: false,
|
|
42
|
+
allowedHosts: true,
|
|
43
|
+
},
|
|
44
|
+
});
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { defineConfig } from "vitest/config";
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
resolve: {
|
|
6
|
+
alias: {
|
|
7
|
+
/** the label store bridge of the ownership law: the app's copy
|
|
8
|
+
* mechanism rides the vault factory through the published library
|
|
9
|
+
* specifier ("@wenathlan/vault/labelstore"), and this bridge resolves
|
|
10
|
+
* the specifier to the monorepo source the sibling engine carries —
|
|
11
|
+
* the mechanism is never duplicated inside cobaia. the entry retires
|
|
12
|
+
* when the published envelope carries the built label store. */
|
|
13
|
+
"@wenathlan/vault/labelstore": path.resolve(import.meta.dirname, "../vault/labelstore.ts"),
|
|
14
|
+
|
|
15
|
+
/** the storage lane bridge of the ownership law: the app's sqlite
|
|
16
|
+
* mechanism rides the vault factory through the published library
|
|
17
|
+
* specifier ("@wenathlan/vault/storagelane"), and this bridge resolves
|
|
18
|
+
* the specifier to the monorepo source the sibling engine carries —
|
|
19
|
+
* the bootstrap and the helpers are never duplicated inside cobaia.
|
|
20
|
+
* the entry retires when the published envelope carries the built
|
|
21
|
+
* storage lane. */
|
|
22
|
+
"@wenathlan/vault/storagelane": path.resolve(import.meta.dirname, "../vault/storagelane.ts"),
|
|
23
|
+
|
|
24
|
+
/** the workload law bridge of the family: the app battery pins the
|
|
25
|
+
* lane mirror against the engine catalog through the published
|
|
26
|
+
* library specifier ("@wenathlan/saddle/workloadlaw"), and this
|
|
27
|
+
* bridge resolves the specifier to the monorepo source the sibling
|
|
28
|
+
* engine carries — the catalog is never duplicated inside cobaia.
|
|
29
|
+
* the entry retires when the published envelope carries the built
|
|
30
|
+
* workload law. */
|
|
31
|
+
"@wenathlan/saddle/workloadlaw": path.resolve(import.meta.dirname, "../saddle/workloadlaw.ts"),
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
test: {
|
|
35
|
+
include: ["tests/*.test.ts"],
|
|
36
|
+
environment: "node",
|
|
37
|
+
/* the timeout stays five seconds on every native runner and scales through the env the emulated arm64 container leg sets (a qemu-interpreted second answers the same green suite many times slower), so an emulated runner never fails a test a native runner passed */
|
|
38
|
+
testTimeout: Number(process.env.COBAIA_TEST_TIMEOUT_MS ?? 5000),
|
|
39
|
+
hookTimeout: Number(process.env.COBAIA_TEST_TIMEOUT_MS ?? 10000),
|
|
40
|
+
coverage: { enabled: false },
|
|
41
|
+
},
|
|
42
|
+
});
|
package/wrangler.toml
ADDED
package/README.md
DELETED