@vosjs/shared 0.2.0 → 0.4.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/README.md +18 -17
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -11
- package/dist/index.js.map +1 -1
- package/package.json +2 -26
- package/dist/acquisition.d.ts +0 -59
- package/dist/acquisition.js +0 -92
- package/dist/acquisition.js.map +0 -1
- package/dist/backdrops.d.ts +0 -29
- package/dist/backdrops.js +0 -6
- package/dist/backdrops.js.map +0 -1
- package/dist/chunk-K7EIJSYQ.js +0 -1
- package/dist/chunk-K7EIJSYQ.js.map +0 -1
- package/dist/chunk-RDD5SU6G.js +0 -25
- package/dist/chunk-RDD5SU6G.js.map +0 -1
- package/dist/diff/index.d.ts +0 -65
- package/dist/diff/index.js +0 -568
- package/dist/diff/index.js.map +0 -1
- package/dist/limits.d.ts +0 -76
- package/dist/limits.js +0 -59
- package/dist/limits.js.map +0 -1
- package/dist/types/index.d.ts +0 -2
- package/dist/types/index.js +0 -2
- package/dist/types/index.js.map +0 -1
- package/dist/utils/index.d.ts +0 -23
- package/dist/utils/index.js +0 -13
- package/dist/utils/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @vosjs/shared
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
studio
|
|
5
|
-
into [`@vosso/vos-plugin`](../vos-plugin). MIT.
|
|
3
|
+
The small shared layer under the `vos` CLI and the document model
|
|
4
|
+
(`@vosjs/studio-core`). Dependency-free. MIT.
|
|
6
5
|
|
|
7
|
-
##
|
|
6
|
+
## Subpaths
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
| Import | What it holds |
|
|
9
|
+
| ----------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
10
|
+
| `@vosjs/shared` | The font and typeface catalogs (`FONT_CATALOG`, `TYPEFACE_CATALOG`) and their lookups and URL helpers |
|
|
11
|
+
| `@vosjs/shared/params` | The remix-knob contract of `config.params`: `ParamSpec`, `readParams`, `applyParamValue` |
|
|
12
|
+
| `@vosjs/shared/frontmatter` | A small `---` frontmatter parser for recipe files |
|
|
13
|
+
| `@vosjs/shared/timelineEdits` | `TimelineEdit` and the source wrapper that applies edits over a program's `createTimeline` |
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
| `generateId()` | A random UUID (`crypto.randomUUID`). |
|
|
17
|
-
| `sleep(ms)` | Promise that resolves after `ms`. |
|
|
18
|
-
| `safeJsonParse(json, fallback)` | Parse JSON, returning `fallback` on error. |
|
|
15
|
+
The catalogs are generated data: the families, weights and typefaces the
|
|
16
|
+
hosted catalog serves, with their URLs. They are the one place this package
|
|
17
|
+
names a host, and a consumer may point `FONT_CDN_BASE`-shaped URLs elsewhere.
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
## What is deliberately not here
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
Anything one platform decides: quota and plan tables, attribution, a
|
|
22
|
+
hosted endpoint's wire types, a hosted changelog's differ. Those live beside
|
|
23
|
+
the service that enforces or serves them. Keep this package the lowest
|
|
24
|
+
layer: a product opinion belongs in `@vosjs/studio-core` or the host.
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
import "./chunk-K7EIJSYQ.js";
|
|
2
|
-
import {
|
|
3
|
-
formatLabel,
|
|
4
|
-
generateId,
|
|
5
|
-
safeJsonParse,
|
|
6
|
-
sleep
|
|
7
|
-
} from "./chunk-RDD5SU6G.js";
|
|
8
1
|
import {
|
|
9
2
|
FONT_CATALOG,
|
|
10
3
|
FONT_CDN_BASE,
|
|
@@ -50,11 +43,7 @@ export {
|
|
|
50
43
|
fontFaceUrl,
|
|
51
44
|
fontManifest,
|
|
52
45
|
fontStack,
|
|
53
|
-
formatLabel,
|
|
54
|
-
generateId,
|
|
55
46
|
nearestFontWeight,
|
|
56
|
-
safeJsonParse,
|
|
57
|
-
sleep,
|
|
58
47
|
typefaceUrl
|
|
59
48
|
};
|
|
60
49
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/typefaceCatalog.ts"],"sourcesContent":["/**\n * Generated by the vosso build; do not edit by hand.\n * Curation lives in the build script; regenerate + `pnpm assets:push`, then\n * commit this file. One catalog: the 3D prop panel, take lowering, and the\n * CLI lints all read it.\n */\n\nexport interface TypefaceEntry {\n /** Catalog family name (matches the webfont catalog). */\n family: string\n /** File slug — typefaces/{slug}.typeface.json on assets.vos.so. */\n slug: string\n /** The converted weight (one file per family; extrusion wants bold). */\n weight: number\n}\n\nexport const TYPEFACE_CDN_BASE = 'https://assets.vos.so/typefaces'\n\n/** The house default 3D face. */\nexport const DEFAULT_TYPEFACE_SLUG = 'lexend'\n\nexport const TYPEFACE_CATALOG: TypefaceEntry[] = [\n { family: 'Lexend', slug: 'lexend', weight: 700 },\n { family: 'Space Grotesk', slug: 'space-grotesk', weight: 700 },\n { family: 'Bebas Neue', slug: 'bebas-neue', weight: 400 },\n { family: 'Archivo Black', slug: 'archivo-black', weight: 400 },\n { family: 'Anton', slug: 'anton', weight: 400 },\n { family: 'Righteous', slug: 'righteous', weight: 400 },\n { family: 'Playfair Display', slug: 'playfair-display', weight: 700 },\n { family: 'Fraunces', slug: 'fraunces', weight: 700 },\n { family: 'Pacifico', slug: 'pacifico', weight: 400 },\n { family: 'JetBrains Mono', slug: 'jetbrains-mono', weight: 700 },\n]\n\nexport function typefaceUrl(slug: string): string {\n return `${TYPEFACE_CDN_BASE}/${slug}.typeface.json`\n}\n\n/** Case-insensitive lookup by slug or family name (null = not in catalog). */\nexport function findTypeface(nameOrSlug: string): TypefaceEntry | null {\n const needle = nameOrSlug.trim().toLowerCase()\n return (\n TYPEFACE_CATALOG.find(\n (t) => t.slug === needle || t.family.toLowerCase() === needle,\n ) ?? null\n )\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/typefaceCatalog.ts"],"sourcesContent":["/**\n * Generated by the vosso build; do not edit by hand.\n * Curation lives in the build script; regenerate + `pnpm assets:push`, then\n * commit this file. One catalog: the 3D prop panel, take lowering, and the\n * CLI lints all read it.\n */\n\nexport interface TypefaceEntry {\n /** Catalog family name (matches the webfont catalog). */\n family: string\n /** File slug — typefaces/{slug}.typeface.json on assets.vos.so. */\n slug: string\n /** The converted weight (one file per family; extrusion wants bold). */\n weight: number\n}\n\nexport const TYPEFACE_CDN_BASE = 'https://assets.vos.so/typefaces'\n\n/** The house default 3D face. */\nexport const DEFAULT_TYPEFACE_SLUG = 'lexend'\n\nexport const TYPEFACE_CATALOG: TypefaceEntry[] = [\n { family: 'Lexend', slug: 'lexend', weight: 700 },\n { family: 'Space Grotesk', slug: 'space-grotesk', weight: 700 },\n { family: 'Bebas Neue', slug: 'bebas-neue', weight: 400 },\n { family: 'Archivo Black', slug: 'archivo-black', weight: 400 },\n { family: 'Anton', slug: 'anton', weight: 400 },\n { family: 'Righteous', slug: 'righteous', weight: 400 },\n { family: 'Playfair Display', slug: 'playfair-display', weight: 700 },\n { family: 'Fraunces', slug: 'fraunces', weight: 700 },\n { family: 'Pacifico', slug: 'pacifico', weight: 400 },\n { family: 'JetBrains Mono', slug: 'jetbrains-mono', weight: 700 },\n]\n\nexport function typefaceUrl(slug: string): string {\n return `${TYPEFACE_CDN_BASE}/${slug}.typeface.json`\n}\n\n/** Case-insensitive lookup by slug or family name (null = not in catalog). */\nexport function findTypeface(nameOrSlug: string): TypefaceEntry | null {\n const needle = nameOrSlug.trim().toLowerCase()\n return (\n TYPEFACE_CATALOG.find(\n (t) => t.slug === needle || t.family.toLowerCase() === needle,\n ) ?? null\n )\n}\n"],"mappings":";;;;;;;;;;;AAgBO,IAAM,oBAAoB;AAG1B,IAAM,wBAAwB;AAE9B,IAAM,mBAAoC;AAAA,EAC/C,EAAE,QAAQ,UAAU,MAAM,UAAU,QAAQ,IAAI;AAAA,EAChD,EAAE,QAAQ,iBAAiB,MAAM,iBAAiB,QAAQ,IAAI;AAAA,EAC9D,EAAE,QAAQ,cAAc,MAAM,cAAc,QAAQ,IAAI;AAAA,EACxD,EAAE,QAAQ,iBAAiB,MAAM,iBAAiB,QAAQ,IAAI;AAAA,EAC9D,EAAE,QAAQ,SAAS,MAAM,SAAS,QAAQ,IAAI;AAAA,EAC9C,EAAE,QAAQ,aAAa,MAAM,aAAa,QAAQ,IAAI;AAAA,EACtD,EAAE,QAAQ,oBAAoB,MAAM,oBAAoB,QAAQ,IAAI;AAAA,EACpE,EAAE,QAAQ,YAAY,MAAM,YAAY,QAAQ,IAAI;AAAA,EACpD,EAAE,QAAQ,YAAY,MAAM,YAAY,QAAQ,IAAI;AAAA,EACpD,EAAE,QAAQ,kBAAkB,MAAM,kBAAkB,QAAQ,IAAI;AAClE;AAEO,SAAS,YAAY,MAAsB;AAChD,SAAO,GAAG,iBAAiB,IAAI,IAAI;AACrC;AAGO,SAAS,aAAa,YAA0C;AACrE,QAAM,SAAS,WAAW,KAAK,EAAE,YAAY;AAC7C,SACE,iBAAiB;AAAA,IACf,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,OAAO,YAAY,MAAM;AAAA,EACzD,KAAK;AAET;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vosjs/shared",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "The small shared layer under the vos CLI and the
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "The small shared layer under the vos CLI and the document model: the font and typeface catalogs, the remix params contract, frontmatter, and the timeline-edit wrapper.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "vosso",
|
|
7
7
|
"homepage": "https://vos.so",
|
|
@@ -21,18 +21,6 @@
|
|
|
21
21
|
"types": "./dist/index.d.ts",
|
|
22
22
|
"import": "./dist/index.js"
|
|
23
23
|
},
|
|
24
|
-
"./types": {
|
|
25
|
-
"types": "./dist/types/index.d.ts",
|
|
26
|
-
"import": "./dist/types/index.js"
|
|
27
|
-
},
|
|
28
|
-
"./utils": {
|
|
29
|
-
"types": "./dist/utils/index.d.ts",
|
|
30
|
-
"import": "./dist/utils/index.js"
|
|
31
|
-
},
|
|
32
|
-
"./diff": {
|
|
33
|
-
"types": "./dist/diff/index.d.ts",
|
|
34
|
-
"import": "./dist/diff/index.js"
|
|
35
|
-
},
|
|
36
24
|
"./frontmatter": {
|
|
37
25
|
"types": "./dist/frontmatter.d.ts",
|
|
38
26
|
"import": "./dist/frontmatter.js"
|
|
@@ -41,18 +29,6 @@
|
|
|
41
29
|
"types": "./dist/params.d.ts",
|
|
42
30
|
"import": "./dist/params.js"
|
|
43
31
|
},
|
|
44
|
-
"./limits": {
|
|
45
|
-
"types": "./dist/limits.d.ts",
|
|
46
|
-
"import": "./dist/limits.js"
|
|
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
32
|
"./timelineEdits": {
|
|
57
33
|
"types": "./dist/timelineEdits.d.ts",
|
|
58
34
|
"import": "./dist/timelineEdits.js"
|
package/dist/acquisition.d.ts
DELETED
|
@@ -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 };
|
package/dist/acquisition.js
DELETED
|
@@ -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
|
package/dist/acquisition.js.map
DELETED
|
@@ -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":[]}
|
package/dist/backdrops.d.ts
DELETED
|
@@ -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
package/dist/backdrops.js.map
DELETED
|
@@ -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":[]}
|
package/dist/chunk-K7EIJSYQ.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=chunk-K7EIJSYQ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/chunk-RDD5SU6G.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// src/utils/index.ts
|
|
2
|
-
function formatLabel(filename) {
|
|
3
|
-
return filename.replace(/\.[^/.]+$/, "").split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
4
|
-
}
|
|
5
|
-
function generateId() {
|
|
6
|
-
return crypto.randomUUID();
|
|
7
|
-
}
|
|
8
|
-
function sleep(ms) {
|
|
9
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
|
-
}
|
|
11
|
-
function safeJsonParse(json, fallback) {
|
|
12
|
-
try {
|
|
13
|
-
return JSON.parse(json);
|
|
14
|
-
} catch {
|
|
15
|
-
return fallback;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export {
|
|
20
|
-
formatLabel,
|
|
21
|
-
generateId,
|
|
22
|
-
sleep,
|
|
23
|
-
safeJsonParse
|
|
24
|
-
};
|
|
25
|
-
//# sourceMappingURL=chunk-RDD5SU6G.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/index.ts"],"sourcesContent":["/**\n * Shared Utilities\n * Common utility functions used across frontend and backend\n */\n\n/**\n * Format a filename to a human-readable label\n * @example formatLabel('basic-fade') => 'Basic Fade'\n */\nexport function formatLabel(filename: string): string {\n return filename\n .replace(/\\.[^/.]+$/, '') // Remove extension\n .split(/[-_]/)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ')\n}\n\n/**\n * Generate a unique ID\n */\nexport function generateId(): string {\n return crypto.randomUUID()\n}\n\n/**\n * Sleep for a specified duration\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n/**\n * Safely parse JSON with a fallback\n */\nexport function safeJsonParse<T>(json: string, fallback: T): T {\n try {\n return JSON.parse(json) as T\n } catch {\n return fallback\n }\n}\n"],"mappings":";AASO,SAAS,YAAY,UAA0B;AACpD,SAAO,SACJ,QAAQ,aAAa,EAAE,EACvB,MAAM,MAAM,EACZ,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,EAAE,YAAY,CAAC,EACxE,KAAK,GAAG;AACb;AAKO,SAAS,aAAqB;AACnC,SAAO,OAAO,WAAW;AAC3B;AAKO,SAAS,MAAM,IAA2B;AAC/C,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAKO,SAAS,cAAiB,MAAc,UAAgB;AAC7D,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|
package/dist/diff/index.d.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The semantic differ: pure functions over two
|
|
3
|
-
* immutable version states — no I/O, no recorded gesture log ("derive,
|
|
4
|
-
* don't record"). Runs identically in Workers, the CLI, and the studio;
|
|
5
|
-
* same two inputs ⇒ byte-identical ops and summary.
|
|
6
|
-
*/
|
|
7
|
-
interface Op {
|
|
8
|
-
op: 'add' | 'remove' | 'modify';
|
|
9
|
-
/**
|
|
10
|
-
* Doc tracks: zoom | tilt | speed | overlay | object | audio, plus scoped
|
|
11
|
-
* singletons (segments | frame | cursor | cam | export | camera).
|
|
12
|
-
* Config tracks: knob (param key space) | look | data | code | config.
|
|
13
|
-
*/
|
|
14
|
-
track: string;
|
|
15
|
-
/** Span/clip id, param key, function name, or field name. */
|
|
16
|
-
id: string;
|
|
17
|
-
/** Changed properties: key → [from, to]. Absent on code ops. */
|
|
18
|
-
props?: Record<string, [unknown, unknown]>;
|
|
19
|
-
/** Code-function modifies only: magnitude of the line-level change. */
|
|
20
|
-
lines?: number;
|
|
21
|
-
/**
|
|
22
|
-
* True when the change is a cascaded consequence of another gesture
|
|
23
|
-
* (e.g. a trim shifting downstream spans), not a direct edit.
|
|
24
|
-
*/
|
|
25
|
-
cascade?: boolean;
|
|
26
|
-
}
|
|
27
|
-
/** One intervening version's worth of changes, as the pull contract ships it. */
|
|
28
|
-
interface VersionChanges {
|
|
29
|
-
versionId: string;
|
|
30
|
-
versionNumber: number;
|
|
31
|
-
origin: string;
|
|
32
|
-
label: string | null;
|
|
33
|
-
note: string | null;
|
|
34
|
-
ops: Op[];
|
|
35
|
-
summary: string;
|
|
36
|
-
}
|
|
37
|
-
/** Stable composite key for net-effect folding. */
|
|
38
|
-
declare function opKey(op: Op): string;
|
|
39
|
-
|
|
40
|
-
type AnyRecord = Record<string, unknown>;
|
|
41
|
-
|
|
42
|
-
declare function diffConfig(a: AnyRecord, b: AnyRecord): Op[];
|
|
43
|
-
|
|
44
|
-
declare function diffDoc(a: AnyRecord, b: AnyRecord): Op[];
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Net effect per node across a sequence of op sets (the coalescing rules):
|
|
48
|
-
* initial→final only, no intermediate drags. add→modify folds into the add;
|
|
49
|
-
* add→remove vanishes; modify chains keep the first `from` and last `to`
|
|
50
|
-
* (dropping props that net to no change); remove→add of the same id is one
|
|
51
|
-
* modify (the node was replaced in place). Output is deterministically
|
|
52
|
-
* ordered: track declaration order, then output-time anchor, then id.
|
|
53
|
-
*/
|
|
54
|
-
declare function coalesce(opsInOrder: readonly Op[]): Op[];
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Deterministic prose over an op list — the line that lands in the agent's
|
|
58
|
-
* context ("zoom z3: end 4.2s→4.6s, level 2.2→1.8; tilt t1 removed").
|
|
59
|
-
* Same ops ⇒ byte-identical string; unit-pinned like the geometry mirrors.
|
|
60
|
-
*/
|
|
61
|
-
declare function summarize(ops: readonly Op[], options?: {
|
|
62
|
-
maxItems?: number;
|
|
63
|
-
}): string;
|
|
64
|
-
|
|
65
|
-
export { type Op, type VersionChanges, coalesce, diffConfig, diffDoc, opKey, summarize };
|