@vosjs/shared 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vosjs/shared",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "The small shared layer under the vos CLI and the vosso studio: the semantic differ, the free-tier limits, frontmatter, params, the font and typeface catalogs.",
5
5
  "license": "MIT",
6
6
  "author": "vosso",
@@ -45,14 +45,6 @@
45
45
  "types": "./dist/limits.d.ts",
46
46
  "import": "./dist/limits.js"
47
47
  },
48
- "./acquisition": {
49
- "types": "./dist/acquisition.d.ts",
50
- "import": "./dist/acquisition.js"
51
- },
52
- "./backdrops": {
53
- "types": "./dist/backdrops.d.ts",
54
- "import": "./dist/backdrops.js"
55
- },
56
48
  "./timelineEdits": {
57
49
  "types": "./dist/timelineEdits.d.ts",
58
50
  "import": "./dist/timelineEdits.js"
@@ -1,59 +0,0 @@
1
- /**
2
- * Signup attribution (first touch), pure.
3
- *
4
- * PostHog already sees every referrer and utm param, but nothing first-party
5
- * survives to the `user` row, and a first touch is the one fact that can
6
- * never be backfilled. The seam is deliberately tiny: a write-once cookie set
7
- * by the web worker on the first arrival that carries a signal (an external
8
- * referrer or utm params — a direct visit sets nothing), read exactly once
9
- * when BetterAuth creates the user, then stamped as JSON on
10
- * `user.acquisition` and never updated.
11
- *
12
- * Everything here is pure so the platform's cookie writer and its signup
13
- * reader can never disagree on the format.
14
- */
15
- declare const FIRST_TOUCH_COOKIE = "vosso_ft";
16
- /** 30 days: long enough to span consider-then-signup, short enough to stay a
17
- * first touch rather than a biography. */
18
- declare const FIRST_TOUCH_MAX_AGE: number;
19
- interface FirstTouch {
20
- /** utm_source, or the referring domain when the arrival was untagged. */
21
- source: string;
22
- /** utm_medium, or 'referral' when derived from a bare referrer. */
23
- medium?: string;
24
- /** utm_campaign, verbatim. */
25
- campaign?: string;
26
- /** External referring domain, when there was one. */
27
- referrer?: string;
28
- /** The path the visit landed on. */
29
- landing?: string;
30
- /** ISO timestamp of the first touch. */
31
- at?: string;
32
- }
33
- /**
34
- * Derive a first touch from a landing request, or null when the visit
35
- * carries no acquisition signal. Null is the common case and the point:
36
- * a direct visit gets no cookie, and an absent `user.acquisition` reads
37
- * honestly as "direct or before the feature", never as a guessed channel.
38
- */
39
- declare function firstTouchOf(input: {
40
- url: URL;
41
- referer: string | null | undefined;
42
- }): FirstTouch | null;
43
- /** Cookie-safe encoding of a first touch. */
44
- declare function encodeFirstTouch(touch: FirstTouch): string;
45
- /**
46
- * Fail-closed parse of first-touch JSON (the shape `user.acquisition`
47
- * stores): anything oversized, unparseable, or missing a string `source`
48
- * is null.
49
- */
50
- declare function parseFirstTouchJson(raw: string | null | undefined): FirstTouch | null;
51
- /**
52
- * Fail-closed cookie decode. The cookie arrives from the wild — a browser
53
- * extension or a hand-edited jar can put anything under our name.
54
- */
55
- declare function decodeFirstTouch(raw: string | null | undefined): FirstTouch | null;
56
- /** Read one cookie's raw value out of a Cookie header. */
57
- declare function readCookieValue(header: string | null | undefined, name: string): string | undefined;
58
-
59
- export { FIRST_TOUCH_COOKIE, FIRST_TOUCH_MAX_AGE, type FirstTouch, decodeFirstTouch, encodeFirstTouch, firstTouchOf, parseFirstTouchJson, readCookieValue };
@@ -1,92 +0,0 @@
1
- // src/acquisition.ts
2
- var FIRST_TOUCH_COOKIE = "vosso_ft";
3
- var FIRST_TOUCH_MAX_AGE = 60 * 60 * 24 * 30;
4
- var FIELD_MAX = 200;
5
- var RAW_MAX = 2e3;
6
- function clean(value) {
7
- const v = value?.trim().slice(0, FIELD_MAX);
8
- return v || void 0;
9
- }
10
- function sameSite(a, b) {
11
- const strip = (h) => h.startsWith("www.") ? h.slice(4) : h;
12
- return strip(a) === strip(b);
13
- }
14
- function firstTouchOf(input) {
15
- const { url, referer } = input;
16
- const source = clean(url.searchParams.get("utm_source"));
17
- const medium = clean(url.searchParams.get("utm_medium"));
18
- const campaign = clean(url.searchParams.get("utm_campaign"));
19
- let referrerDomain;
20
- if (referer) {
21
- try {
22
- const r = new URL(referer);
23
- if (r.hostname && !sameSite(r.hostname, url.hostname)) {
24
- referrerDomain = clean(r.hostname);
25
- }
26
- } catch {
27
- }
28
- }
29
- if (!source && !campaign && !referrerDomain) return null;
30
- const touch = {
31
- source: source ?? referrerDomain ?? "unknown"
32
- };
33
- const derivedMedium = medium ?? (!source && referrerDomain ? "referral" : void 0);
34
- if (derivedMedium) touch.medium = derivedMedium;
35
- if (campaign) touch.campaign = campaign;
36
- if (referrerDomain) touch.referrer = referrerDomain;
37
- return touch;
38
- }
39
- function encodeFirstTouch(touch) {
40
- return encodeURIComponent(JSON.stringify(touch));
41
- }
42
- function parseFirstTouchJson(raw) {
43
- if (!raw || raw.length > RAW_MAX) return null;
44
- try {
45
- const parsed = JSON.parse(raw);
46
- if (typeof parsed !== "object" || parsed === null) return null;
47
- const record = parsed;
48
- if (typeof record.source !== "string" || !record.source) return null;
49
- const touch = { source: record.source.slice(0, FIELD_MAX) };
50
- for (const key of [
51
- "medium",
52
- "campaign",
53
- "referrer",
54
- "landing",
55
- "at"
56
- ]) {
57
- const value = record[key];
58
- if (typeof value === "string" && value)
59
- touch[key] = value.slice(0, FIELD_MAX);
60
- }
61
- return touch;
62
- } catch {
63
- return null;
64
- }
65
- }
66
- function decodeFirstTouch(raw) {
67
- if (!raw || raw.length > RAW_MAX) return null;
68
- try {
69
- return parseFirstTouchJson(decodeURIComponent(raw));
70
- } catch {
71
- return null;
72
- }
73
- }
74
- function readCookieValue(header, name) {
75
- if (!header) return void 0;
76
- for (const part of header.split(";")) {
77
- const eq = part.indexOf("=");
78
- if (eq === -1) continue;
79
- if (part.slice(0, eq).trim() === name) return part.slice(eq + 1).trim();
80
- }
81
- return void 0;
82
- }
83
- export {
84
- FIRST_TOUCH_COOKIE,
85
- FIRST_TOUCH_MAX_AGE,
86
- decodeFirstTouch,
87
- encodeFirstTouch,
88
- firstTouchOf,
89
- parseFirstTouchJson,
90
- readCookieValue
91
- };
92
- //# sourceMappingURL=acquisition.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/acquisition.ts"],"sourcesContent":["/**\n * Signup attribution (first touch), pure.\n *\n * PostHog already sees every referrer and utm param, but nothing first-party\n * survives to the `user` row, and a first touch is the one fact that can\n * never be backfilled. The seam is deliberately tiny: a write-once cookie set\n * by the web worker on the first arrival that carries a signal (an external\n * referrer or utm params — a direct visit sets nothing), read exactly once\n * when BetterAuth creates the user, then stamped as JSON on\n * `user.acquisition` and never updated.\n *\n * Everything here is pure so the platform's cookie writer and its signup\n * reader can never disagree on the format.\n */\n\nexport const FIRST_TOUCH_COOKIE = 'vosso_ft'\n\n/** 30 days: long enough to span consider-then-signup, short enough to stay a\n * first touch rather than a biography. */\nexport const FIRST_TOUCH_MAX_AGE = 60 * 60 * 24 * 30\n\n/** Per-field cap. UTM values are short by convention; anything longer is\n * noise or abuse and gets truncated rather than refused. */\nconst FIELD_MAX = 200\n\n/** Decode guard: a cookie bigger than this is not ours. */\nconst RAW_MAX = 2000\n\nexport interface FirstTouch {\n /** utm_source, or the referring domain when the arrival was untagged. */\n source: string\n /** utm_medium, or 'referral' when derived from a bare referrer. */\n medium?: string\n /** utm_campaign, verbatim. */\n campaign?: string\n /** External referring domain, when there was one. */\n referrer?: string\n /** The path the visit landed on. */\n landing?: string\n /** ISO timestamp of the first touch. */\n at?: string\n}\n\nfunction clean(value: string | null | undefined): string | undefined {\n const v = value?.trim().slice(0, FIELD_MAX)\n return v || undefined\n}\n\n/**\n * Is the referrer an internal navigation rather than an arrival? Compares\n * registrable-host-ish: `www.vos.so` referring to `vos.so` is the same site\n * (canonicalHost 301s www onto the apex, so the second request's referrer is\n * our own www host and must not read as a referral).\n */\nfunction sameSite(a: string, b: string): boolean {\n const strip = (h: string) => (h.startsWith('www.') ? h.slice(4) : h)\n return strip(a) === strip(b)\n}\n\n/**\n * Derive a first touch from a landing request, or null when the visit\n * carries no acquisition signal. Null is the common case and the point:\n * a direct visit gets no cookie, and an absent `user.acquisition` reads\n * honestly as \"direct or before the feature\", never as a guessed channel.\n */\nexport function firstTouchOf(input: {\n url: URL\n referer: string | null | undefined\n}): FirstTouch | null {\n const { url, referer } = input\n const source = clean(url.searchParams.get('utm_source'))\n const medium = clean(url.searchParams.get('utm_medium'))\n const campaign = clean(url.searchParams.get('utm_campaign'))\n\n let referrerDomain: string | undefined\n if (referer) {\n try {\n const r = new URL(referer)\n if (r.hostname && !sameSite(r.hostname, url.hostname)) {\n referrerDomain = clean(r.hostname)\n }\n } catch {\n // An unparseable referrer is no signal.\n }\n }\n\n if (!source && !campaign && !referrerDomain) return null\n\n const touch: FirstTouch = {\n source: source ?? referrerDomain ?? 'unknown',\n }\n const derivedMedium =\n medium ?? (!source && referrerDomain ? 'referral' : undefined)\n if (derivedMedium) touch.medium = derivedMedium\n if (campaign) touch.campaign = campaign\n if (referrerDomain) touch.referrer = referrerDomain\n return touch\n}\n\n/** Cookie-safe encoding of a first touch. */\nexport function encodeFirstTouch(touch: FirstTouch): string {\n return encodeURIComponent(JSON.stringify(touch))\n}\n\n/**\n * Fail-closed parse of first-touch JSON (the shape `user.acquisition`\n * stores): anything oversized, unparseable, or missing a string `source`\n * is null.\n */\nexport function parseFirstTouchJson(\n raw: string | null | undefined,\n): FirstTouch | null {\n if (!raw || raw.length > RAW_MAX) return null\n try {\n const parsed: unknown = JSON.parse(raw)\n if (typeof parsed !== 'object' || parsed === null) return null\n const record = parsed as Record<string, unknown>\n if (typeof record.source !== 'string' || !record.source) return null\n const touch: FirstTouch = { source: record.source.slice(0, FIELD_MAX) }\n for (const key of [\n 'medium',\n 'campaign',\n 'referrer',\n 'landing',\n 'at',\n ] as const) {\n const value = record[key]\n if (typeof value === 'string' && value)\n touch[key] = value.slice(0, FIELD_MAX)\n }\n return touch\n } catch {\n return null\n }\n}\n\n/**\n * Fail-closed cookie decode. The cookie arrives from the wild — a browser\n * extension or a hand-edited jar can put anything under our name.\n */\nexport function decodeFirstTouch(\n raw: string | null | undefined,\n): FirstTouch | null {\n if (!raw || raw.length > RAW_MAX) return null\n try {\n return parseFirstTouchJson(decodeURIComponent(raw))\n } catch {\n return null\n }\n}\n\n/** Read one cookie's raw value out of a Cookie header. */\nexport function readCookieValue(\n header: string | null | undefined,\n name: string,\n): string | undefined {\n if (!header) return undefined\n for (const part of header.split(';')) {\n const eq = part.indexOf('=')\n if (eq === -1) continue\n if (part.slice(0, eq).trim() === name) return part.slice(eq + 1).trim()\n }\n return undefined\n}\n"],"mappings":";AAeO,IAAM,qBAAqB;AAI3B,IAAM,sBAAsB,KAAK,KAAK,KAAK;AAIlD,IAAM,YAAY;AAGlB,IAAM,UAAU;AAiBhB,SAAS,MAAM,OAAsD;AACnE,QAAM,IAAI,OAAO,KAAK,EAAE,MAAM,GAAG,SAAS;AAC1C,SAAO,KAAK;AACd;AAQA,SAAS,SAAS,GAAW,GAAoB;AAC/C,QAAM,QAAQ,CAAC,MAAe,EAAE,WAAW,MAAM,IAAI,EAAE,MAAM,CAAC,IAAI;AAClE,SAAO,MAAM,CAAC,MAAM,MAAM,CAAC;AAC7B;AAQO,SAAS,aAAa,OAGP;AACpB,QAAM,EAAE,KAAK,QAAQ,IAAI;AACzB,QAAM,SAAS,MAAM,IAAI,aAAa,IAAI,YAAY,CAAC;AACvD,QAAM,SAAS,MAAM,IAAI,aAAa,IAAI,YAAY,CAAC;AACvD,QAAM,WAAW,MAAM,IAAI,aAAa,IAAI,cAAc,CAAC;AAE3D,MAAI;AACJ,MAAI,SAAS;AACX,QAAI;AACF,YAAM,IAAI,IAAI,IAAI,OAAO;AACzB,UAAI,EAAE,YAAY,CAAC,SAAS,EAAE,UAAU,IAAI,QAAQ,GAAG;AACrD,yBAAiB,MAAM,EAAE,QAAQ;AAAA,MACnC;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI,CAAC,UAAU,CAAC,YAAY,CAAC,eAAgB,QAAO;AAEpD,QAAM,QAAoB;AAAA,IACxB,QAAQ,UAAU,kBAAkB;AAAA,EACtC;AACA,QAAM,gBACJ,WAAW,CAAC,UAAU,iBAAiB,aAAa;AACtD,MAAI,cAAe,OAAM,SAAS;AAClC,MAAI,SAAU,OAAM,WAAW;AAC/B,MAAI,eAAgB,OAAM,WAAW;AACrC,SAAO;AACT;AAGO,SAAS,iBAAiB,OAA2B;AAC1D,SAAO,mBAAmB,KAAK,UAAU,KAAK,CAAC;AACjD;AAOO,SAAS,oBACd,KACmB;AACnB,MAAI,CAAC,OAAO,IAAI,SAAS,QAAS,QAAO;AACzC,MAAI;AACF,UAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,QAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO;AAC1D,UAAM,SAAS;AACf,QAAI,OAAO,OAAO,WAAW,YAAY,CAAC,OAAO,OAAQ,QAAO;AAChE,UAAM,QAAoB,EAAE,QAAQ,OAAO,OAAO,MAAM,GAAG,SAAS,EAAE;AACtE,eAAW,OAAO;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,GAAY;AACV,YAAM,QAAQ,OAAO,GAAG;AACxB,UAAI,OAAO,UAAU,YAAY;AAC/B,cAAM,GAAG,IAAI,MAAM,MAAM,GAAG,SAAS;AAAA,IACzC;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,SAAS,iBACd,KACmB;AACnB,MAAI,CAAC,OAAO,IAAI,SAAS,QAAS,QAAO;AACzC,MAAI;AACF,WAAO,oBAAoB,mBAAmB,GAAG,CAAC;AAAA,EACpD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,gBACd,QACA,MACoB;AACpB,MAAI,CAAC,OAAQ,QAAO;AACpB,aAAW,QAAQ,OAAO,MAAM,GAAG,GAAG;AACpC,UAAM,KAAK,KAAK,QAAQ,GAAG;AAC3B,QAAI,OAAO,GAAI;AACf,QAAI,KAAK,MAAM,GAAG,EAAE,EAAE,KAAK,MAAM,KAAM,QAAO,KAAK,MAAM,KAAK,CAAC,EAAE,KAAK;AAAA,EACxE;AACA,SAAO;AACT;","names":[]}
@@ -1,29 +0,0 @@
1
- /**
2
- * The backdrop wire contract: what `GET /api/backdrops`
3
- * hands to the studio panel, the picker, the CLI and agents. Absolute URLs
4
- * on purpose — a ProjectDoc travels across environments (save-to-vos, CLI
5
- * takes, server renders), so a backdrop key must resolve everywhere without
6
- * host-side rewriting.
7
- */
8
- declare const BACKDROP_ASSET_BASE = "https://assets.vos.so/";
9
- interface Backdrop {
10
- id: string;
11
- slug: string;
12
- title: string;
13
- /** The loop's length in seconds — a fact of the asset, never a knob. */
14
- duration: number;
15
- /** The 1080p bake's pixel size (the export note reads it). */
16
- width: number;
17
- height: number;
18
- /** The CSS underlay a pick writes into `frame.background`. */
19
- ground: string;
20
- /** Provenance: the vos it was baked from, when it still exists. */
21
- vosId: string | null;
22
- urls: {
23
- '1080p': string | null;
24
- '2k': string | null;
25
- poster: string | null;
26
- };
27
- }
28
-
29
- export { BACKDROP_ASSET_BASE, type Backdrop };
package/dist/backdrops.js DELETED
@@ -1,6 +0,0 @@
1
- // src/backdrops.ts
2
- var BACKDROP_ASSET_BASE = "https://assets.vos.so/";
3
- export {
4
- BACKDROP_ASSET_BASE
5
- };
6
- //# sourceMappingURL=backdrops.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/backdrops.ts"],"sourcesContent":["/**\n * The backdrop wire contract: what `GET /api/backdrops`\n * hands to the studio panel, the picker, the CLI and agents. Absolute URLs\n * on purpose — a ProjectDoc travels across environments (save-to-vos, CLI\n * takes, server renders), so a backdrop key must resolve everywhere without\n * host-side rewriting.\n */\n\nexport const BACKDROP_ASSET_BASE = 'https://assets.vos.so/'\n\nexport interface Backdrop {\n id: string\n slug: string\n title: string\n /** The loop's length in seconds — a fact of the asset, never a knob. */\n duration: number\n /** The 1080p bake's pixel size (the export note reads it). */\n width: number\n height: number\n /** The CSS underlay a pick writes into `frame.background`. */\n ground: string\n /** Provenance: the vos it was baked from, when it still exists. */\n vosId: string | null\n urls: {\n '1080p': string | null\n '2k': string | null\n poster: string | null\n }\n}\n"],"mappings":";AAQO,IAAM,sBAAsB;","names":[]}