@wenathlan/cobaia 2.0.166 → 2.0.168
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/.dockerignore +1 -1
- package/App.tsx +2 -31
- package/Cargo.toml +17 -0
- package/Dockerfile +162 -3
- package/MANIFEST.in +7 -0
- package/biome.jsonc +3 -3
- package/build.gradle.kts +1 -1
- package/bun.lock +396 -343
- package/cobaia.csproj +2 -2
- package/cobaia.gemspec +2 -2
- package/cobaia.java +1 -1
- package/cobaia.rs +8 -0
- package/db.ts +12 -17
- package/docker-compose.yml +58 -0
- package/docker.config +74 -0
- package/homebrew.rb +25 -0
- package/keywords.txt +1 -0
- package/monetization.ts +248 -0
- package/nginx.conf +106 -0
- package/package.json +74 -76
- package/pnpm-workspace.yaml +59 -38
- package/pom.xml +2 -2
- package/pyproject.toml +23 -0
- package/routerbase.ts +1 -0
- package/sandboxmodality.ts +26 -40
- package/sol/Sol.tsx +1 -1
- package/sol/apps/apps.tsx +2 -2
- package/sol/blog/blog.tsx +2 -2
- package/sol/home/home.tsx +2 -2
- package/sol/intro/intro.tsx +1 -1
- package/sol/onboarding/onboarding.tsx +1 -1
- package/sol/pricing/pricing.tsx +1 -1
- package/sol/shell/Shell.tsx +2 -2
- package/sol/support/support.tsx +1 -1
- package/sol/wingets/IntroFooter.tsx +1 -1
- package/sol/wingets/IntroPlans.tsx +1 -1
- package/sol/wingets/ShellAdsslot.tsx +1 -1
- package/tauri.conf.json +1 -1
- package/{tests → test}/adsconfig.test.ts +2 -2
- package/{tests → test}/checkoutlinks.test.ts +2 -2
- package/{tests → test}/entryflow.test.ts +2 -2
- package/{tests → test}/familyurl.test.ts +1 -1
- package/{tests → test}/posts.test.ts +2 -2
- package/{tests → test}/sandboxmodality.test.ts +1 -1
- package/tsconfig.json +1 -0
- package/vite.config.ts +14 -0
- package/vitest.config.ts +9 -1
- package/wingets.ts +21 -0
- package/adsconfig.ts +0 -59
- package/checkoutlinks.ts +0 -80
- package/posts.ts +0 -54
- package/roster.ts +0 -29
- package/sol/wingets/index.ts +0 -20
package/sol/apps/apps.tsx
CHANGED
|
@@ -16,8 +16,8 @@ import type { CSSProperties } from "react";
|
|
|
16
16
|
import { useContent } from "../../content.ts";
|
|
17
17
|
import { familyurl } from "../../familyurl";
|
|
18
18
|
import { label, labelfill } from "../../labels";
|
|
19
|
-
import { roster } from "../../
|
|
20
|
-
import { AdSlot } from "
|
|
19
|
+
import { roster } from "../../monetization.ts";
|
|
20
|
+
import { AdSlot } from "../../wingets";
|
|
21
21
|
|
|
22
22
|
export function Apps() {
|
|
23
23
|
useContent();
|
package/sol/blog/blog.tsx
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
import type { CSSProperties } from "react";
|
|
17
17
|
import { useContent } from "../../content.ts";
|
|
18
18
|
import { label, labelfill } from "../../labels";
|
|
19
|
-
import { type blogpost, postindex, postwords } from "../../
|
|
20
|
-
import { AdSlot } from "
|
|
19
|
+
import { type blogpost, postindex, postwords } from "../../monetization";
|
|
20
|
+
import { AdSlot } from "../../wingets";
|
|
21
21
|
|
|
22
22
|
/** the meta line of one post: the date and the honest word count. */
|
|
23
23
|
function PostMeta({ post }: { post: blogpost }) {
|
package/sol/home/home.tsx
CHANGED
|
@@ -21,8 +21,8 @@ import { Link } from "wouter";
|
|
|
21
21
|
import { useContent } from "../../content.ts";
|
|
22
22
|
import { familyurl } from "../../familyurl";
|
|
23
23
|
import { type LabelKey, label, labelfill } from "../../labels";
|
|
24
|
-
import { roster } from "../../
|
|
25
|
-
import { AdSlot, BrandMark, EntryGate } from "
|
|
24
|
+
import { roster } from "../../monetization.ts";
|
|
25
|
+
import { AdSlot, BrandMark, EntryGate } from "../../wingets";
|
|
26
26
|
|
|
27
27
|
/** the featured deploy units of the landing preview (the full directory
|
|
28
28
|
* lives on /apps): the OS, the engine, the store and one studio — read
|
package/sol/intro/intro.tsx
CHANGED
|
@@ -31,7 +31,7 @@ import { useCallback, useEffect, useState } from "react";
|
|
|
31
31
|
import { Link } from "wouter";
|
|
32
32
|
import { label } from "../../labels";
|
|
33
33
|
import { markOnboardingStage } from "../../onboardingstate.ts";
|
|
34
|
-
import { BrandMark, IntroFooter, IntroPlans, IntroSplash } from "
|
|
34
|
+
import { BrandMark, IntroFooter, IntroPlans, IntroSplash } from "../../wingets";
|
|
35
35
|
|
|
36
36
|
/** the lifecycle of the opening act: resolving (the motion preference
|
|
37
37
|
* decides) → playing (the akash floor over the landing) → done (the
|
|
@@ -19,7 +19,7 @@ import { useState } from "react";
|
|
|
19
19
|
import { useLocation } from "wouter";
|
|
20
20
|
import { type LabelKey, label } from "../../labels";
|
|
21
21
|
import { markOnboardingStage } from "../../onboardingstate.ts";
|
|
22
|
-
import { BrandMark } from "
|
|
22
|
+
import { BrandMark } from "../../wingets";
|
|
23
23
|
|
|
24
24
|
/** the three frames of the first run, in order — the copy rides the store
|
|
25
25
|
* (the id keeps the ladder segments keyed, the key answers the eyebrow). */
|
package/sol/pricing/pricing.tsx
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* are plain hrefs the visitor chooses to follow.
|
|
17
17
|
*/
|
|
18
18
|
import type { CSSProperties } from "react";
|
|
19
|
-
import { checkoutlinks, type checkoutplan, isconfigured } from "../../
|
|
19
|
+
import { checkoutlinks, type checkoutplan, isconfigured } from "../../monetization";
|
|
20
20
|
import { useContent } from "../../content.ts";
|
|
21
21
|
import { label, labelfill } from "../../labels";
|
|
22
22
|
|
package/sol/shell/Shell.tsx
CHANGED
|
@@ -26,8 +26,8 @@ import { Link, useLocation } from "wouter";
|
|
|
26
26
|
import { useContent } from "../../content.ts";
|
|
27
27
|
import { familyurl } from "../../familyurl";
|
|
28
28
|
import { type LabelKey, label } from "../../labels";
|
|
29
|
-
import { roster } from "../../
|
|
30
|
-
import { BrandMark } from "
|
|
29
|
+
import { roster } from "../../monetization.ts";
|
|
30
|
+
import { BrandMark } from "../../wingets";
|
|
31
31
|
|
|
32
32
|
/** the section links of the top bar, in reading order (home is the mark).
|
|
33
33
|
* the href rides the route table, the text rides the label accessor. */
|
package/sol/support/support.tsx
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* nothing that asks for support ever sits beside an advertisement.
|
|
14
14
|
*/
|
|
15
15
|
import type { CSSProperties } from "react";
|
|
16
|
-
import { isconfigured, supportlinks } from "../../
|
|
16
|
+
import { isconfigured, supportlinks } from "../../monetization";
|
|
17
17
|
import { useContent } from "../../content.ts";
|
|
18
18
|
import { label } from "../../labels";
|
|
19
19
|
|
|
@@ -13,7 +13,7 @@ import type { CSSProperties } from "react";
|
|
|
13
13
|
import { useContent } from "../../content.ts";
|
|
14
14
|
import { familyurl } from "../../familyurl";
|
|
15
15
|
import { type LabelKey, label } from "../../labels";
|
|
16
|
-
import { roster } from "../../
|
|
16
|
+
import { roster } from "../../monetization.ts";
|
|
17
17
|
|
|
18
18
|
/** the site doors of the footer row, in reading order (the labels ride the
|
|
19
19
|
* shell foot keys — the same words the chrome's footer carries). */
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import type { CSSProperties } from "react";
|
|
17
17
|
import { Link } from "wouter";
|
|
18
|
-
import { checkoutlinks, type checkoutplan, isconfigured } from "../../
|
|
18
|
+
import { checkoutlinks, type checkoutplan, isconfigured } from "../../monetization";
|
|
19
19
|
import { useContent } from "../../content.ts";
|
|
20
20
|
import { label, labelfill } from "../../labels";
|
|
21
21
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* automated traffic ever touches an ad surface.
|
|
16
16
|
*/
|
|
17
17
|
import type { CSSProperties } from "react";
|
|
18
|
-
import { ADS_EMPTY, ADS_LABEL, adsslotfor, adsslotlive } from "../../
|
|
18
|
+
import { ADS_EMPTY, ADS_LABEL, adsslotfor, adsslotlive } from "../../monetization.ts";
|
|
19
19
|
import { labelfill } from "../../labels";
|
|
20
20
|
|
|
21
21
|
type AdSlotProps = {
|
package/tauri.conf.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// # adsconfig.test — the honest ad slot registry of the family site.
|
|
2
|
-
// Run: pnpm vitest run
|
|
2
|
+
// Run: pnpm vitest run test/adsconfig.test.ts
|
|
3
3
|
// The suite watches the promise the ad slot makes: one reserved slot per
|
|
4
4
|
// content page (home, apps, blog), every slot id shipping EMPTY (no ad
|
|
5
5
|
// network script, no id, no key is ever committed — the loader enters after
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// aspect-ratio shapes the containers reserve so the page never shifts.
|
|
8
8
|
import assert from "node:assert/strict";
|
|
9
9
|
import { describe, expect, it } from "vitest";
|
|
10
|
-
import { ADS_EMPTY, ADS_LABEL, ADSSLOTS, adsslotfor, adsslotlive } from "../
|
|
10
|
+
import { ADS_EMPTY, ADS_LABEL, ADSSLOTS, adsslotfor, adsslotlive } from "../monetization.ts";
|
|
11
11
|
|
|
12
12
|
describe("the ad slot registry", () => {
|
|
13
13
|
it("reserves one slot per content page and nothing else", () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// # checkoutlinks.test — the billing configuration of the family site.
|
|
2
|
-
// Run: pnpm vitest run
|
|
2
|
+
// Run: pnpm vitest run test/checkoutlinks.test.ts
|
|
3
3
|
// The suite watches the promise the pricing page makes: the three plans
|
|
4
4
|
// exist with the placeholder prices (US$0 / 5 / 19 a month), every checkout
|
|
5
5
|
// href ships EMPTY so the interface renders the honest empty state instead
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// the billing table cites, so a card never renders a bare key.
|
|
11
11
|
import assert from "node:assert/strict";
|
|
12
12
|
import { describe, expect, it } from "vitest";
|
|
13
|
-
import { checkoutlinks, isconfigured, planbyid, supportlinks } from "../
|
|
13
|
+
import { checkoutlinks, isconfigured, planbyid, supportlinks } from "../monetization.ts";
|
|
14
14
|
import { LABEL_MANIFEST } from "../labels.ts";
|
|
15
15
|
|
|
16
16
|
describe("checkout links", () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// # entryflow.test — the construction-flow contract of the cobaia: the
|
|
2
2
|
// standardized family law (intro → onboarding → entrada) the owner ordered
|
|
3
3
|
// and the db-backed content law behind it. Run: pnpm vitest run
|
|
4
|
-
//
|
|
4
|
+
// test/entryflow.test.ts
|
|
5
5
|
//
|
|
6
6
|
// The suite watches three promises:
|
|
7
7
|
// 1. the flow — the intro mounts before the onboarding, the onboarding
|
|
@@ -115,7 +115,7 @@ describe("the content law (the rows live in the db, the manifest is the seed)",
|
|
|
115
115
|
});
|
|
116
116
|
|
|
117
117
|
it("keeps the faces reading through the typed doors — no hardcoded row in a page", () => {
|
|
118
|
-
expect(HOME).toMatch(/from "\.\.\/\.\.\/
|
|
118
|
+
expect(HOME).toMatch(/from "\.\.\/\.\.\/monetization\.ts"/);
|
|
119
119
|
expect(HOME).not.toMatch(/const ROSTER/);
|
|
120
120
|
expect(read("../sol/apps/apps.tsx")).not.toMatch(/const ROSTER/);
|
|
121
121
|
expect(read("../sol/pricing/pricing.tsx")).not.toMatch(/const PLANS/);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// # familyurl.test — the address book contract of the family deploy units.
|
|
2
|
-
// Run: pnpm vitest run
|
|
2
|
+
// Run: pnpm vitest run test/familyurl.test.ts
|
|
3
3
|
// The suite watches the promise the directory and the footer make: every
|
|
4
4
|
// family member resolves one level above this application, always with the
|
|
5
5
|
// trailing slash, and pasted segments or stray slashes land on the same
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// # posts.test — the seed blog of the family site.
|
|
2
|
-
// Run: pnpm vitest run
|
|
2
|
+
// Run: pnpm vitest run test/posts.test.ts
|
|
3
3
|
// The suite watches the promise the blog page makes: the index is sorted
|
|
4
4
|
// newest first no matter the array order, the slugs are unique and url-safe,
|
|
5
5
|
// every seed post carries a real ~300 word body (the ad networks approve
|
|
6
6
|
// content, not containers) and the honest word counter agrees with itself.
|
|
7
7
|
import assert from "node:assert/strict";
|
|
8
8
|
import { describe, expect, it } from "vitest";
|
|
9
|
-
import { postindex, posts, postwords } from "../
|
|
9
|
+
import { postindex, posts, postwords } from "../monetization.ts";
|
|
10
10
|
|
|
11
11
|
// the content lane answers the same rows the seed carries (wave of the
|
|
12
12
|
// content law): the const retired, the accessor serves the identical data
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// modality id pins the seat for the platform's join to validate at
|
|
10
10
|
// dispatch — this module never judges engine vocabulary). runnable with
|
|
11
11
|
// vitest:
|
|
12
|
-
// pnpm vitest run
|
|
12
|
+
// pnpm vitest run test/sandboxmodality.test.ts
|
|
13
13
|
import assert from "node:assert/strict";
|
|
14
14
|
import { lanesof } from "@wenathlan/saddle/workloadlaw";
|
|
15
15
|
import { describe, it } from "vitest";
|
package/tsconfig.json
CHANGED
package/vite.config.ts
CHANGED
|
@@ -23,8 +23,22 @@ export default defineConfig({
|
|
|
23
23
|
* the mechanism is never duplicated inside cobaia. the entry retires
|
|
24
24
|
* when the published envelope carries the built label store. */
|
|
25
25
|
"@wenathlan/vault/labelstore": path.resolve(import.meta.dirname, "../vault/labelstore.ts"),
|
|
26
|
+
"@wenathlan/vault/onboardingstore": path.resolve(import.meta.dirname, "../vault/onboardingstore.ts"),
|
|
26
27
|
"@": path.resolve(import.meta.dirname, "sol"),
|
|
27
28
|
},
|
|
29
|
+
/** the single-instance law (the PR12 renderparity heal): the family
|
|
30
|
+
* sources the thin doors re-export (devthink/reveal.ts and the sibling
|
|
31
|
+
* bridges) live outside this app root, and the resolver answers their
|
|
32
|
+
* bare react and wouter imports against the folder the file sits in —
|
|
33
|
+
* a second physical copy rides the bundle when the sibling tree carries
|
|
34
|
+
* its own node_modules (the two-react crash: the wouter context hook
|
|
35
|
+
* then answers the dispatcher of the copy that never renders, and the
|
|
36
|
+
* app dies at the workbench-failure face on both doors, the typed url
|
|
37
|
+
* and the panel window alike). the dedupe forces every react,
|
|
38
|
+
* react-dom and wouter specifier of the graph onto one resolved
|
|
39
|
+
* instance — the cross-app source rides, the duplication dies, one
|
|
40
|
+
* design answers both doors. */
|
|
41
|
+
dedupe: ["react", "react-dom", "react-dom/client", "wouter"],
|
|
28
42
|
},
|
|
29
43
|
root: import.meta.dirname,
|
|
30
44
|
build: {
|
package/vitest.config.ts
CHANGED
|
@@ -11,6 +11,14 @@ export default defineConfig({
|
|
|
11
11
|
* the mechanism is never duplicated inside cobaia. the entry retires
|
|
12
12
|
* when the published envelope carries the built label store. */
|
|
13
13
|
"@wenathlan/vault/labelstore": path.resolve(import.meta.dirname, "../vault/labelstore.ts"),
|
|
14
|
+
/** the onboarding store bridge of the ownership law: the app's
|
|
15
|
+
* seen-flags mechanism rides the vault factory through the published
|
|
16
|
+
* library specifier ("@wenathlan/vault/onboardingstore"), and this
|
|
17
|
+
* bridge resolves the specifier to the monorepo source the sibling
|
|
18
|
+
* engine carries — the mechanism is never duplicated inside cobaia.
|
|
19
|
+
* the entry retires when the published envelope carries the built
|
|
20
|
+
* onboarding store. */
|
|
21
|
+
"@wenathlan/vault/onboardingstore": path.resolve(import.meta.dirname, "../vault/onboardingstore.ts"),
|
|
14
22
|
|
|
15
23
|
/** the storage lane bridge of the ownership law: the app's sqlite
|
|
16
24
|
* mechanism rides the vault factory through the published library
|
|
@@ -32,7 +40,7 @@ export default defineConfig({
|
|
|
32
40
|
},
|
|
33
41
|
},
|
|
34
42
|
test: {
|
|
35
|
-
include: ["
|
|
43
|
+
include: ["test/*.test.ts"],
|
|
36
44
|
environment: "node",
|
|
37
45
|
/* 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
46
|
testTimeout: Number(process.env.COBAIA_TEST_TIMEOUT_MS ?? 5000),
|
package/wingets.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* wingets.ts — the universal winget surface of cobaia: every winget of the
|
|
3
|
+
* sol/wingets folder answers from this one module at the app root, so the
|
|
4
|
+
* pages import their components from a single place and the dotted winget
|
|
5
|
+
* file name lives exactly once (here) instead of repeating across every
|
|
6
|
+
* page — the components stay complete independent files inside the
|
|
7
|
+
* sol/wingets folder.
|
|
8
|
+
*/
|
|
9
|
+
export { type familymember, roster } from "./monetization.ts";
|
|
10
|
+
export * from "./sol/wingets/HomeEntrygate.tsx";
|
|
11
|
+
export { default as EntryGate } from "./sol/wingets/HomeEntrygate.tsx";
|
|
12
|
+
export * from "./sol/wingets/IntroFooter.tsx";
|
|
13
|
+
export { default as IntroFooter } from "./sol/wingets/IntroFooter.tsx";
|
|
14
|
+
export * from "./sol/wingets/IntroPlans.tsx";
|
|
15
|
+
export { default as IntroPlans } from "./sol/wingets/IntroPlans.tsx";
|
|
16
|
+
export * from "./sol/wingets/IntroSplash.tsx";
|
|
17
|
+
export { default as IntroSplash } from "./sol/wingets/IntroSplash.tsx";
|
|
18
|
+
export * from "./sol/wingets/ShellAdsslot.tsx";
|
|
19
|
+
export { default as ShellAdsslot } from "./sol/wingets/ShellAdsslot.tsx";
|
|
20
|
+
export * from "./sol/wingets/ShellBrand.tsx";
|
|
21
|
+
export { default as ShellBrand } from "./sol/wingets/ShellBrand.tsx";
|
package/adsconfig.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* adsconfig.ts — the honest ad slot registry of the family site, pure data
|
|
3
|
-
* at the app root. The family site carries ONE reserved ad container per
|
|
4
|
-
* content page (home, the app directory, the blog index), always at the end
|
|
5
|
-
* of the content, always labeled "advertisement", always a fixed aspect
|
|
6
|
-
* ratio so the page never shifts when a real unit arrives.
|
|
7
|
-
*
|
|
8
|
-
* THE SCRIPT RULE: no ad network script lives in this repository. The
|
|
9
|
-
* AdSense (or equivalent) loader is injected AFTER the network approves the
|
|
10
|
-
* site, through the deploy environment only. Until then the slot ids stay
|
|
11
|
-
* empty and the container renders the honest empty note. No network call,
|
|
12
|
-
* no click encouragement, no automated traffic ever touches an ad surface —
|
|
13
|
-
* invalid traffic is the one thing that kills both the ad account and the
|
|
14
|
-
* domain reputation, so the family does not do it.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/** one reserved ad slot: the config id and the reserved shape. */
|
|
18
|
-
export type adsslot = {
|
|
19
|
-
/** the ad network slot id — empty until the owner configures it after approval */
|
|
20
|
-
slotid: string;
|
|
21
|
-
/** the display format the reserved box mimics */
|
|
22
|
-
format: string;
|
|
23
|
-
/** the css aspect-ratio value the container reserves (never shifts layout) */
|
|
24
|
-
ratio: string;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/** the fixed label every ad container renders, visible and honest. */
|
|
28
|
-
export const ADS_LABEL = "advertisement";
|
|
29
|
-
|
|
30
|
-
/** the honest note the container renders while no slot id is configured. */
|
|
31
|
-
export const ADS_EMPTY = "ad slot not configured yet";
|
|
32
|
-
|
|
33
|
-
/** the reserved slots per content page, keyed by the page folder name. */
|
|
34
|
-
export const ADSSLOTS: Readonly<Record<string, adsslot>> = {
|
|
35
|
-
home: { slotid: "", format: "display", ratio: "728 / 90" },
|
|
36
|
-
apps: { slotid: "", format: "in-feed", ratio: "3 / 1" },
|
|
37
|
-
blog: { slotid: "", format: "in-article", ratio: "3 / 1" },
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Reads the reserved slot of one content page.
|
|
42
|
-
*
|
|
43
|
-
* @param page the page folder name (home, apps, blog).
|
|
44
|
-
* @returns the slot, or undefined when the page carries no slot.
|
|
45
|
-
*/
|
|
46
|
-
export function adsslotfor(page: string): adsslot | undefined {
|
|
47
|
-
return ADSSLOTS[page];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Answers whether a slot is configured with a real network id. An empty
|
|
52
|
-
* (or whitespace) slot id means the container stays an honest placeholder.
|
|
53
|
-
*
|
|
54
|
-
* @param slot the reserved slot entry.
|
|
55
|
-
* @returns true only when the slot id carries a value.
|
|
56
|
-
*/
|
|
57
|
-
export function adsslotlive(slot: adsslot | undefined): boolean {
|
|
58
|
-
return slot !== undefined && slot.slotid.trim().length > 0;
|
|
59
|
-
}
|
package/checkoutlinks.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* checkoutlinks.ts — the typed accessor of the billing configuration of
|
|
3
|
-
* the family site: the thin door the pricing page, the support page and
|
|
4
|
-
* the intro plans zone import, answering the plan table and the support
|
|
5
|
-
* channels of the content lane (content.ts — the manifest; db.ts — the
|
|
6
|
-
* content_rows table; the platform gateway — GET /api/v1/ui/content). The
|
|
7
|
-
* data never lives here anymore: checkoutlinks() and supportlinks() answer
|
|
8
|
-
* the live rows the platform served, the manifest seed otherwise (the
|
|
9
|
-
* uistore chain). The payment links themselves are configuration, never
|
|
10
|
-
* code: the owner pins the checkout hrefs (Stripe Payment Links or Paddle
|
|
11
|
-
* overlays) into the store once billing goes live. Until then every href
|
|
12
|
-
* ships empty and the interface renders the honest empty state — "checkout
|
|
13
|
-
* not configured yet" — instead of pretending.
|
|
14
|
-
*
|
|
15
|
-
* The words never ride here: every plan sentence (name, billing period,
|
|
16
|
-
* blurb, features) answers from the label store (labels.ts — the manifest
|
|
17
|
-
* the seed carries into the site database), so the pricing cards and the
|
|
18
|
-
* intro plans zone read their prose through the typed accessor and this
|
|
19
|
-
* lane keeps only the billing values — ids, prices and links.
|
|
20
|
-
*
|
|
21
|
-
* No network call ever happens here: the links are plain hrefs the visitor
|
|
22
|
-
* chooses to follow, no key is committed (not even a placeholder that looks
|
|
23
|
-
* real) and no script is loaded. The prices are placeholders (US$0 / 5 / 19
|
|
24
|
-
* per month) until the billing platform decision lands.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
import {
|
|
28
|
-
type checkoutplan,
|
|
29
|
-
contenttable,
|
|
30
|
-
contentvalue,
|
|
31
|
-
seedPlans,
|
|
32
|
-
seedSupportLinks,
|
|
33
|
-
type supportlinks as supportlinkstable,
|
|
34
|
-
} from "./content.ts";
|
|
35
|
-
|
|
36
|
-
export type { checkoutplan, supportlinkstable as supportlinks };
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* The typed accessor of the pricing table: the live db rows when the
|
|
40
|
-
* platform answered, the manifest seed otherwise — in display order (the
|
|
41
|
-
* prose answers from the label store; only the billing values ride this
|
|
42
|
-
* lane).
|
|
43
|
-
*
|
|
44
|
-
* @returns the plans of the /pricing page.
|
|
45
|
-
*/
|
|
46
|
-
export function checkoutlinks(): readonly checkoutplan[] {
|
|
47
|
-
return contenttable("plans", seedPlans);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* The typed accessor of the support channels: the live db row when the
|
|
52
|
-
* platform answered, the manifest seed otherwise — the same rule as the
|
|
53
|
-
* plans: hrefs stay empty until the owner configures them.
|
|
54
|
-
*
|
|
55
|
-
* @returns the support channels of the /support page.
|
|
56
|
-
*/
|
|
57
|
-
export function supportlinks(): Readonly<supportlinkstable> {
|
|
58
|
-
return contentvalue("supportlinks", seedSupportLinks);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Answers whether a configured link is live. An empty (or whitespace) href
|
|
63
|
-
* means the feature is not configured yet and the interface must say so.
|
|
64
|
-
*
|
|
65
|
-
* @param href the configured link href.
|
|
66
|
-
* @returns true only when the href carries a real target.
|
|
67
|
-
*/
|
|
68
|
-
export function isconfigured(href: string): boolean {
|
|
69
|
-
return href.trim().length > 0;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Finds one plan of the table by its stable id.
|
|
74
|
-
*
|
|
75
|
-
* @param id the plan id (free, pro, studio).
|
|
76
|
-
* @returns the plan, or undefined when no plan carries the id.
|
|
77
|
-
*/
|
|
78
|
-
export function planbyid(id: string): checkoutplan | undefined {
|
|
79
|
-
return checkoutlinks().find((plan) => plan.id === id);
|
|
80
|
-
}
|
package/posts.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* posts.ts — the typed accessor of the seed blog: the thin door the blog
|
|
3
|
-
* page and the tests import, answering the posts of the content lane
|
|
4
|
-
* (content.ts — the manifest; db.ts — the content_rows table; the platform
|
|
5
|
-
* gateway — GET /api/v1/ui/content). The data never lives here anymore:
|
|
6
|
-
* posts() answers the live rows the platform served, the manifest seed
|
|
7
|
-
* otherwise (the uistore chain), the index sort and the honest word count
|
|
8
|
-
* stay exactly where they rode, and the blog index (/blog) renders the
|
|
9
|
-
* sorted index and the full bodies through this door — real, written
|
|
10
|
-
* content about how the family is built, so the site is useful on its own
|
|
11
|
-
* merits (the ad networks approve content, not containers). New posts
|
|
12
|
-
* append to the manifest; the index sorts by date, so the manifest order
|
|
13
|
-
* never matters.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { type blogpost, contenttable, seedPosts } from "./content.ts";
|
|
17
|
-
|
|
18
|
-
export type { blogpost };
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* The typed accessor of the blog: the live db rows when the platform
|
|
22
|
-
* answered, the manifest seed otherwise.
|
|
23
|
-
*
|
|
24
|
-
* @returns the posts of the blog.
|
|
25
|
-
*/
|
|
26
|
-
export function posts(): readonly blogpost[] {
|
|
27
|
-
return contenttable("posts", seedPosts);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Builds the blog index: the posts sorted by date, newest first. The ISO
|
|
32
|
-
* date sort is lexicographic and stable, so the index order never depends
|
|
33
|
-
* on the row order the store answers.
|
|
34
|
-
*
|
|
35
|
-
* @returns the posts of the blog, newest first.
|
|
36
|
-
*/
|
|
37
|
-
export function postindex(): readonly blogpost[] {
|
|
38
|
-
return [...posts()].sort((a, b) => {
|
|
39
|
-
if (a.date < b.date) return 1;
|
|
40
|
-
if (a.date > b.date) return -1;
|
|
41
|
-
return 0;
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Counts the words of one post's body — the honest length measure the tests
|
|
47
|
-
* and the page footer can both read.
|
|
48
|
-
*
|
|
49
|
-
* @param post the post to measure.
|
|
50
|
-
* @returns the total word count across the body paragraphs.
|
|
51
|
-
*/
|
|
52
|
-
export function postwords(post: blogpost): number {
|
|
53
|
-
return post.body.join(" ").split(/\s+/).filter(Boolean).length;
|
|
54
|
-
}
|
package/roster.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* roster.ts — the typed accessor of the family directory data: the thin
|
|
3
|
-
* door the /apps page, the shell footer, the home preview and the intro
|
|
4
|
-
* footer import, answering the roster of the content lane (content.ts —
|
|
5
|
-
* the manifest; db.ts — the content_rows table; the platform gateway — GET
|
|
6
|
-
* /api/v1/ui/content). The data never lives here anymore: roster() answers
|
|
7
|
-
* the live rows the platform served, the manifest seed otherwise (the
|
|
8
|
-
* uistore chain). Every deploy unit beside the cobaia, with the one accent
|
|
9
|
-
* each carries (the spec §12 table, mirrored from the argan contract; the
|
|
10
|
-
* devthink OS rides the family sun) and the honest role each app declares
|
|
11
|
-
* in its own devthink.toml — condensed to one line, nothing embellished:
|
|
12
|
-
* the page and the tests read the same table through this door, so the
|
|
13
|
-
* directory can never drift from the family.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { contenttable, type familymember, seedRoster } from "./content.ts";
|
|
17
|
-
|
|
18
|
-
export type { familymember };
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* The typed accessor of the directory: the live db rows when the platform
|
|
22
|
-
* answered, the manifest seed otherwise — rail order, the OS first, then
|
|
23
|
-
* the applications.
|
|
24
|
-
*
|
|
25
|
-
* @returns the directory roster.
|
|
26
|
-
*/
|
|
27
|
-
export function roster(): readonly familymember[] {
|
|
28
|
-
return contenttable("roster", seedRoster);
|
|
29
|
-
}
|
package/sol/wingets/index.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* index.ts — the universal winget surface of cobaia: every winget of the
|
|
3
|
-
* sol/wingets folder answers from this one module, so the pages import
|
|
4
|
-
* their components from a single place and the dotted winget file name
|
|
5
|
-
* lives exactly once (here) instead of repeating across every page —
|
|
6
|
-
* the components stay complete independent files beside this surface.
|
|
7
|
-
*/
|
|
8
|
-
export * from "../../roster.ts";
|
|
9
|
-
export * from "./HomeEntrygate.tsx";
|
|
10
|
-
export { default as EntryGate } from "./HomeEntrygate.tsx";
|
|
11
|
-
export * from "./IntroFooter.tsx";
|
|
12
|
-
export { default as IntroFooter } from "./IntroFooter.tsx";
|
|
13
|
-
export * from "./IntroPlans.tsx";
|
|
14
|
-
export { default as IntroPlans } from "./IntroPlans.tsx";
|
|
15
|
-
export * from "./IntroSplash.tsx";
|
|
16
|
-
export { default as IntroSplash } from "./IntroSplash.tsx";
|
|
17
|
-
export * from "./ShellAdsslot.tsx";
|
|
18
|
-
export { default as ShellAdsslot } from "./ShellAdsslot.tsx";
|
|
19
|
-
export * from "./ShellBrand.tsx";
|
|
20
|
-
export { default as ShellBrand } from "./ShellBrand.tsx";
|