@percher/shared 1.1.0 → 1.2.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.
Files changed (56) hide show
  1. package/dist/data-filter.d.ts +27 -0
  2. package/dist/data-filter.d.ts.map +1 -0
  3. package/dist/data-filter.js +288 -0
  4. package/dist/data-filter.js.map +1 -0
  5. package/dist/data-records.d.ts +359 -0
  6. package/dist/data-records.d.ts.map +1 -0
  7. package/dist/data-records.js +124 -0
  8. package/dist/data-records.js.map +1 -0
  9. package/dist/disposable-email-domains.d.ts +11 -0
  10. package/dist/disposable-email-domains.d.ts.map +1 -0
  11. package/dist/disposable-email-domains.js +112 -0
  12. package/dist/disposable-email-domains.js.map +1 -0
  13. package/dist/email-budget.d.ts +60 -0
  14. package/dist/email-budget.d.ts.map +1 -0
  15. package/dist/email-budget.js +135 -0
  16. package/dist/email-budget.js.map +1 -0
  17. package/dist/image-rejection.d.ts +45 -0
  18. package/dist/image-rejection.d.ts.map +1 -0
  19. package/dist/image-rejection.js +27 -0
  20. package/dist/image-rejection.js.map +1 -0
  21. package/dist/index.d.ts +8 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +8 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/internal-sig.d.ts +2 -2
  26. package/dist/internal-sig.js +2 -2
  27. package/dist/legal-changes.d.ts +4 -2
  28. package/dist/legal-changes.d.ts.map +1 -1
  29. package/dist/legal-changes.js +124 -0
  30. package/dist/legal-changes.js.map +1 -1
  31. package/dist/legal.d.ts +14 -14
  32. package/dist/legal.d.ts.map +1 -1
  33. package/dist/legal.js +13 -13
  34. package/dist/legal.js.map +1 -1
  35. package/dist/oauth-scopes.d.ts +8 -0
  36. package/dist/oauth-scopes.d.ts.map +1 -1
  37. package/dist/oauth-scopes.js +11 -13
  38. package/dist/oauth-scopes.js.map +1 -1
  39. package/dist/platform.d.ts +2 -2
  40. package/dist/platform.js +2 -2
  41. package/dist/signup-capacity.d.ts +12 -0
  42. package/dist/signup-capacity.d.ts.map +1 -0
  43. package/dist/signup-capacity.js +29 -0
  44. package/dist/signup-capacity.js.map +1 -0
  45. package/dist/signup-day.d.ts +7 -0
  46. package/dist/signup-day.d.ts.map +1 -0
  47. package/dist/signup-day.js +42 -0
  48. package/dist/signup-day.js.map +1 -0
  49. package/dist/switches.d.ts.map +1 -1
  50. package/dist/switches.js +1 -0
  51. package/dist/switches.js.map +1 -1
  52. package/dist/waitlist-token.d.ts +49 -0
  53. package/dist/waitlist-token.d.ts.map +1 -0
  54. package/dist/waitlist-token.js +71 -0
  55. package/dist/waitlist-token.js.map +1 -0
  56. package/package.json +6 -1
@@ -0,0 +1,60 @@
1
+ /** Resend free tier: 100/day, 3,000/month. UTC calendar windows. */
2
+ export declare const EMAIL_BUDGET_LIMITS: {
3
+ readonly daily: 95;
4
+ readonly monthly: 2900;
5
+ readonly notificationDaily: 45;
6
+ readonly crashPerAppDaily: 3;
7
+ };
8
+ export type EmailCategory = "auth" | "operator" | "legal" | "notification";
9
+ interface EmailBudgetCounts {
10
+ total: number;
11
+ auth: number;
12
+ operator: number;
13
+ legal: number;
14
+ notification: number;
15
+ }
16
+ export interface EmailBudgetConnection {
17
+ run(query: string, params?: (string | number)[]): void;
18
+ /** Drizzle's raw Bun SQLite queries return positional rows. */
19
+ get<T extends unknown[]>(query: string, params?: (string | number)[]): T | undefined;
20
+ }
21
+ interface EmailBudgetDatabase extends EmailBudgetConnection {
22
+ immediate<T>(work: (connection: EmailBudgetConnection) => T): T;
23
+ }
24
+ interface EmailBudgetDecision {
25
+ allowed: boolean;
26
+ category: EmailCategory;
27
+ dayKey: string;
28
+ reason?: "notification_daily" | "daily" | "monthly" | "crash_daily";
29
+ overCeiling: boolean;
30
+ }
31
+ /** Dashboard-only startup must coexist with the API's migration. */
32
+ export declare const EMAIL_BUDGET_SCHEMA = "\nCREATE TABLE IF NOT EXISTS email_budget_counters (\n period TEXT PRIMARY KEY NOT NULL,\n total INTEGER NOT NULL DEFAULT 0,\n auth INTEGER NOT NULL DEFAULT 0,\n operator INTEGER NOT NULL DEFAULT 0,\n legal INTEGER NOT NULL DEFAULT 0,\n notification INTEGER NOT NULL DEFAULT 0,\n warning INTEGER NOT NULL DEFAULT 0\n);\nCREATE TABLE IF NOT EXISTS email_budget_crashes (\n day TEXT NOT NULL,\n app_id TEXT NOT NULL,\n count INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (day, app_id)\n);";
33
+ /** Attempts, not successful deliveries: uncertain sends never release capacity. */
34
+ export declare class EmailBudget {
35
+ private readonly db;
36
+ private readonly now;
37
+ onWarning?: (usage: ReturnType<EmailBudget["usage"]>) => Promise<unknown>;
38
+ constructor(db: EmailBudgetDatabase, now?: () => Date);
39
+ reserve(category: EmailCategory, options?: {
40
+ count?: number;
41
+ crashAppId?: string;
42
+ }): EmailBudgetDecision;
43
+ usage(at?: Date): {
44
+ dayKey: string;
45
+ monthKey: string;
46
+ day: EmailBudgetCounts;
47
+ month: EmailBudgetCounts;
48
+ limits: {
49
+ readonly daily: 95;
50
+ readonly monthly: 2900;
51
+ readonly notificationDaily: 45;
52
+ readonly crashPerAppDaily: 3;
53
+ };
54
+ uncountedOperatorSenders: string[];
55
+ };
56
+ /** The dashboard leaves a durable pending warning; API monitoring drains it. */
57
+ dispatchWarning(): Promise<void>;
58
+ }
59
+ export {};
60
+ //# sourceMappingURL=email-budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-budget.d.ts","sourceRoot":"","sources":["../src/email-budget.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,eAAO,MAAM,mBAAmB;aAC9B,KAAK,EAAE,EAAE;aACT,OAAO,EAAE,IAAK;aACd,iBAAiB,EAAE,EAAE;aACrB,gBAAgB,EAAE,CAAC;CACX,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,cAAc,CAAC;AAC3E,UAAU,iBAAiB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB;AACD,MAAM,WAAW,qBAAqB;IACpC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC;IACvD,+DAA+D;IAC/D,GAAG,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;CACtF;AACD,UAAU,mBAAoB,SAAQ,qBAAqB;IACzD,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,UAAU,EAAE,qBAAqB,KAAK,CAAC,GAAG,CAAC,CAAC;CACjE;AACD,UAAU,mBAAmB;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,oBAAoB,GAAG,OAAO,GAAG,SAAS,GAAG,aAAa,CAAC;IACpE,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,oEAAoE;AACpE,eAAO,MAAM,mBAAmB,kfAe7B,CAAC;AAoBJ,mFAAmF;AACnF,qBAAa,WAAW;IAIpB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAJtB,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAE1E,YACmB,EAAE,EAAE,mBAAmB,EACvB,GAAG,GAAE,MAAM,IAAuB,EACjD;IAEJ,OAAO,CACL,QAAQ,EAAE,aAAa,EACvB,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAO,GACpD,mBAAmB,CAgErB;IAED,KAAK,CAAC,EAAE,OAAa;;;;;;4BAhJd,EAAE;8BACA,IAAK;wCACK,EAAE;uCACH,CAAC;;;MAwJlB;IAED,gFAAgF;IAC1E,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAarC;CACF"}
@@ -0,0 +1,135 @@
1
+ /** Resend free tier: 100/day, 3,000/month. UTC calendar windows. */
2
+ export const EMAIL_BUDGET_LIMITS = {
3
+ daily: 95,
4
+ monthly: 2_900,
5
+ notificationDaily: 45,
6
+ crashPerAppDaily: 3,
7
+ };
8
+ /** Dashboard-only startup must coexist with the API's migration. */
9
+ export const EMAIL_BUDGET_SCHEMA = `
10
+ CREATE TABLE IF NOT EXISTS email_budget_counters (
11
+ period TEXT PRIMARY KEY NOT NULL,
12
+ total INTEGER NOT NULL DEFAULT 0,
13
+ auth INTEGER NOT NULL DEFAULT 0,
14
+ operator INTEGER NOT NULL DEFAULT 0,
15
+ legal INTEGER NOT NULL DEFAULT 0,
16
+ notification INTEGER NOT NULL DEFAULT 0,
17
+ warning INTEGER NOT NULL DEFAULT 0
18
+ );
19
+ CREATE TABLE IF NOT EXISTS email_budget_crashes (
20
+ day TEXT NOT NULL,
21
+ app_id TEXT NOT NULL,
22
+ count INTEGER NOT NULL DEFAULT 0,
23
+ PRIMARY KEY (day, app_id)
24
+ );`;
25
+ const emptyCounts = () => ({
26
+ total: 0,
27
+ auth: 0,
28
+ operator: 0,
29
+ legal: 0,
30
+ notification: 0,
31
+ });
32
+ function readCounts(db, period) {
33
+ const row = db.get("SELECT total, auth, operator, legal, notification FROM email_budget_counters WHERE period = ?", [period]);
34
+ if (!row)
35
+ return emptyCounts();
36
+ const [total, auth, operator, legal, notification] = row;
37
+ return { total, auth, operator, legal, notification };
38
+ }
39
+ /** Attempts, not successful deliveries: uncertain sends never release capacity. */
40
+ export class EmailBudget {
41
+ db;
42
+ now;
43
+ onWarning;
44
+ constructor(db, now = () => new Date()) {
45
+ this.db = db;
46
+ this.now = now;
47
+ }
48
+ reserve(category, options = {}) {
49
+ if (!["auth", "operator", "legal", "notification"].includes(category))
50
+ throw new Error("Invalid email category");
51
+ const count = options.count ?? 1;
52
+ if (!Number.isSafeInteger(count) || count < 1)
53
+ throw new Error("Invalid email recipient count");
54
+ if (options.crashAppId !== undefined && (category !== "notification" || !options.crashAppId))
55
+ throw new Error("Invalid crash email category or app");
56
+ const dayKey = this.now().toISOString().slice(0, 10);
57
+ const periods = [`day:${dayKey}`, `month:${dayKey.slice(0, 7)}`];
58
+ const decision = this.db.immediate((db) => {
59
+ for (const period of periods)
60
+ db.run("INSERT OR IGNORE INTO email_budget_counters (period) VALUES (?)", [period]);
61
+ const day = readCounts(db, periods[0]);
62
+ const month = readCounts(db, periods[1]);
63
+ const reason = day.total + count > EMAIL_BUDGET_LIMITS.daily
64
+ ? "daily"
65
+ : month.total + count > EMAIL_BUDGET_LIMITS.monthly
66
+ ? "monthly"
67
+ : category === "notification" &&
68
+ day.notification + count > EMAIL_BUDGET_LIMITS.notificationDaily
69
+ ? "notification_daily"
70
+ : undefined;
71
+ if (category === "notification" && reason)
72
+ return { allowed: false, category, dayKey, reason, overCeiling: false };
73
+ if (options.crashAppId !== undefined) {
74
+ const crash = db.get("SELECT count FROM email_budget_crashes WHERE day = ? AND app_id = ?", [dayKey, options.crashAppId]);
75
+ if ((crash?.[0] ?? 0) >= EMAIL_BUDGET_LIMITS.crashPerAppDaily)
76
+ return {
77
+ allowed: false,
78
+ category,
79
+ dayKey,
80
+ reason: "crash_daily",
81
+ overCeiling: false,
82
+ };
83
+ db.run("INSERT INTO email_budget_crashes (day, app_id, count) VALUES (?, ?, 1) ON CONFLICT(day, app_id) DO UPDATE SET count = count + 1", [dayKey, options.crashAppId]);
84
+ }
85
+ for (const period of periods)
86
+ db.run(`UPDATE email_budget_counters SET total = total + ?, ${category} = ${category} + ? WHERE period = ?`, [count, count, period]);
87
+ if (reason)
88
+ db.run("UPDATE email_budget_counters SET warning = 1 WHERE period = ? AND warning = 0", [
89
+ periods[0],
90
+ ]);
91
+ return { allowed: true, category, dayKey, reason, overCeiling: reason !== undefined };
92
+ });
93
+ if (!decision.allowed || decision.overCeiling) {
94
+ const line = JSON.stringify({
95
+ event: decision.allowed ? "email_budget_priority_overflow" : "email_budget_skipped",
96
+ ...decision,
97
+ crashAppId: options.crashAppId,
98
+ });
99
+ if (decision.overCeiling)
100
+ console.error(line);
101
+ else
102
+ console.warn(line);
103
+ }
104
+ return decision;
105
+ }
106
+ usage(at = this.now()) {
107
+ const dayKey = at.toISOString().slice(0, 10);
108
+ const monthKey = dayKey.slice(0, 7);
109
+ return {
110
+ dayKey,
111
+ monthKey,
112
+ day: readCounts(this.db, `day:${dayKey}`),
113
+ month: readCounts(this.db, `month:${monthKey}`),
114
+ limits: EMAIL_BUDGET_LIMITS,
115
+ uncountedOperatorSenders: ["infra/scripts/lib/backup-alert.py"],
116
+ };
117
+ }
118
+ /** The dashboard leaves a durable pending warning; API monitoring drains it. */
119
+ async dispatchWarning() {
120
+ if (!this.onWarning)
121
+ return;
122
+ const claimed = this.db.get("UPDATE email_budget_counters SET warning = 2 WHERE period = (SELECT period FROM email_budget_counters WHERE period LIKE 'day:%' AND warning = 1 ORDER BY period LIMIT 1) AND warning = 1 RETURNING period");
123
+ if (!claimed)
124
+ return;
125
+ const [period] = claimed;
126
+ // Claim before dispatch: an operator warning itself goes through this budget.
127
+ try {
128
+ await this.onWarning(this.usage(new Date(`${period.slice(4)}T00:00:00Z`)));
129
+ }
130
+ catch {
131
+ console.error(JSON.stringify({ event: "email_budget_warning_failed", period }));
132
+ }
133
+ }
134
+ }
135
+ //# sourceMappingURL=email-budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-budget.js","sourceRoot":"","sources":["../src/email-budget.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Why a moderator turned down an owner-uploaded image. The API stores the
3
+ * stable `code`; the label is looked up when it is shown, so its wording
4
+ * can change without touching stored rows. Labels are written for the app
5
+ * owner, who reads them on their app's Settings page.
6
+ */
7
+ export type ImageKind = "icon" | "preview";
8
+ export declare const IMAGE_REJECTION_REASONS: readonly [{
9
+ readonly code: "not_screenshot";
10
+ readonly label: "Not a screenshot of the app (logo or marketing image)";
11
+ readonly kinds: readonly ["preview"];
12
+ }, {
13
+ readonly code: "login_or_empty";
14
+ readonly label: "Shows only a login or empty screen";
15
+ readonly kinds: readonly ["preview"];
16
+ }, {
17
+ readonly code: "personal_data";
18
+ readonly label: "Contains personal data";
19
+ readonly kinds: readonly ["icon", "preview"];
20
+ }, {
21
+ readonly code: "unreadable";
22
+ readonly label: "Too small or unreadable";
23
+ readonly kinds: readonly ["icon", "preview"];
24
+ }, {
25
+ readonly code: "mismatch";
26
+ readonly label: "Does not match the app";
27
+ readonly kinds: readonly ["icon", "preview"];
28
+ }, {
29
+ readonly code: "aup";
30
+ readonly label: "Breaks the Acceptable Use Policy";
31
+ readonly kinds: readonly ["icon", "preview"];
32
+ }];
33
+ export type ImageRejectionCode = (typeof IMAGE_REJECTION_REASONS)[number]["code"];
34
+ /** The longest moderator note (or legacy free-text reason) the API stores. */
35
+ export declare const IMAGE_REJECTION_NOTE_MAX = 280;
36
+ /** The reasons a moderator may pick for this kind of image, in display order. */
37
+ export declare function imageRejectionReasons(kind: ImageKind): {
38
+ code: ImageRejectionCode;
39
+ label: string;
40
+ }[];
41
+ /** The owner-facing label for a stored code; null for an unknown or absent code. */
42
+ export declare function imageRejectionLabel(code: string | null | undefined): string | null;
43
+ /** True when `code` is a reason a moderator may pick for this kind of image. */
44
+ export declare function isImageRejectionCode(kind: ImageKind, code: unknown): code is ImageRejectionCode;
45
+ //# sourceMappingURL=image-rejection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-rejection.d.ts","sourceRoot":"","sources":["../src/image-rejection.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAE3C,eAAO,MAAM,uBAAuB;mBAE1B,gBAAgB;oBACf,uDAAuD;;;mBAGxD,gBAAgB;oBAAS,oCAAoC;;;mBAC7D,eAAe;oBAAS,wBAAwB;;;mBAChD,YAAY;oBAAS,yBAAyB;;;mBAC9C,UAAU;oBAAS,wBAAwB;;;mBAC3C,KAAK;oBAAS,kCAAkC;;EACkC,CAAC;AAE7F,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAElF,8EAA8E;AAC9E,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,iFAAiF;AACjF,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,SAAS,GACd;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAI/C;AAED,oFAAoF;AACpF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAElF;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,kBAAkB,CAE/F"}
@@ -0,0 +1,27 @@
1
+ export const IMAGE_REJECTION_REASONS = [
2
+ {
3
+ code: "not_screenshot",
4
+ label: "Not a screenshot of the app (logo or marketing image)",
5
+ kinds: ["preview"],
6
+ },
7
+ { code: "login_or_empty", label: "Shows only a login or empty screen", kinds: ["preview"] },
8
+ { code: "personal_data", label: "Contains personal data", kinds: ["icon", "preview"] },
9
+ { code: "unreadable", label: "Too small or unreadable", kinds: ["icon", "preview"] },
10
+ { code: "mismatch", label: "Does not match the app", kinds: ["icon", "preview"] },
11
+ { code: "aup", label: "Breaks the Acceptable Use Policy", kinds: ["icon", "preview"] },
12
+ ];
13
+ /** The longest moderator note (or legacy free-text reason) the API stores. */
14
+ export const IMAGE_REJECTION_NOTE_MAX = 280;
15
+ /** The reasons a moderator may pick for this kind of image, in display order. */
16
+ export function imageRejectionReasons(kind) {
17
+ return IMAGE_REJECTION_REASONS.filter((r) => r.kinds.includes(kind)).map(({ code, label }) => ({ code, label }));
18
+ }
19
+ /** The owner-facing label for a stored code; null for an unknown or absent code. */
20
+ export function imageRejectionLabel(code) {
21
+ return IMAGE_REJECTION_REASONS.find((r) => r.code === code)?.label ?? null;
22
+ }
23
+ /** True when `code` is a reason a moderator may pick for this kind of image. */
24
+ export function isImageRejectionCode(kind, code) {
25
+ return imageRejectionReasons(kind).some((r) => r.code === code);
26
+ }
27
+ //# sourceMappingURL=image-rejection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-rejection.js","sourceRoot":"","sources":["../src/image-rejection.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC;QACE,IAAI,EAAE,gBAAgB;QACtB,KAAK,EAAE,uDAAuD;QAC9D,KAAK,EAAE,CAAC,SAAS,CAAC;KACnB;IACD,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,oCAAoC,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE;IAC3F,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,wBAAwB,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE;IACtF,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,yBAAyB,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE;IACpF,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,wBAAwB,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE;IACjF,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,kCAAkC,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE;CACI,CAAC;AAI7F,8EAA8E;AAC9E,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAE5C,iFAAiF;AACjF,MAAM,UAAU,qBAAqB,CACnC,IAAe;IAEf,OAAO,uBAAuB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACzC,CAAC,CAAC,KAA8B,CAAC,QAAQ,CAAC,IAAI,CAAC,CACjD,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,mBAAmB,CAAC,IAA+B;IACjE,OAAO,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC;AAC7E,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,oBAAoB,CAAC,IAAe,EAAE,IAAa;IACjE,OAAO,qBAAqB,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAClE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -3,20 +3,27 @@ export { BILLING_TEST_ACCOUNTS_ENV, isBillingTestAccount, parseBillingTestAccoun
3
3
  export { BRAND, ICON_COLOURS, type IconColourKey, iconColourHex } from "./brand.js";
4
4
  export { CLIENT_IP_ATTESTATION_MAX_SKEW_MS, CLIENT_IP_ATTESTATION_VERSION, CLIENT_IP_HEADER, CLIENT_IP_MAX_LENGTH, CLIENT_IP_PROOF_HEADER, type ClientIpAttestationParts, clientIpAttestationPayload, formatClientIpProof, parseClientIpProof, } from "./client-ip-attestation.js";
5
5
  export { crashAssistantPrompt } from "./crash-prompt.js";
6
+ export { type DataExpressionErrorReason, type DataExpressionResult, type DataQueryInvalidExtra, type DataSchemaField, parseDataFields, parseDataFilter, parseDataSort, } from "./data-filter.js";
7
+ export { DATA_ACCESS_MODES, DATA_ACCESS_SCOPES, DATA_ACCESS_SETTER_ERROR_STATUS, DATA_ACT_AS_UNAVAILABLE_REASONS, DATA_ACTING_AS, DATA_COLLECTION_RE, DATA_IDENTITY_KINDS, DATA_IDENTITY_LABEL_FIELD_TYPES, DATA_IDENTITY_LABELS, DATA_LIMITS, DATA_NOT_RUNNING_REASONS, DATA_RATE_LIMIT_SOURCES, DATA_RECORD_ERROR_STATUS, DATA_RECORD_ID_RE, DATA_RULE_CLASSES, DATA_RULE_DENIED_REASONS, DATA_RULE_OPS, type DataAccess, type DataAccessMode, type DataAccessNoticePayload, type DataAccessScope, type DataAccessSet, type DataAccessSetterErrorCode, type DataAccessUpdate, type DataActAsUnavailableExtra, type DataActAsUnavailableReason, type DataActingAs, type DataCollectionAccess, type DataCollectionSummary, type DataConflictExtra, type DataCreateInput, type DataFieldSummary, type DataIdentity, type DataIdentityCandidatesRequest, type DataIdentityCandidatesResponse, type DataIdentityCollection, type DataIdentityLabelFieldType, type DataNotRunningReason, type DataPaginatedRecords, type DataQueryOptions, type DataRateLimitedExtra, type DataRateLimitSource, type DataRecord, type DataRecordErrorCode, type DataRuleClass, type DataRuleDeniedExtra, type DataRuleDeniedReason, type DataRuleOp, type DataSchemaResponse, type DataTokenAccessNoticePayload, type DataTokenAccessRow, type DataTokenAccessUpdate, type DataUpdateInput, type DataValidationFields, type OAuthGrantsDataTools, } from "./data-records.js";
8
+ export { disposableDomainCount, isDisposableEmailDomain } from "./disposable-email-domains.js";
6
9
  export { DOMAIN_RE } from "./domain-name.js";
10
+ export { EMAIL_BUDGET_LIMITS, EMAIL_BUDGET_SCHEMA, EmailBudget, type EmailBudgetConnection, type EmailCategory, } from "./email-budget.js";
7
11
  export { ENV_MASK, isMaskedEnvValue, maskEnvValue } from "./env-mask.js";
8
12
  export { ENV_NAME_RE } from "./env-name.js";
9
13
  export { escapeHtml } from "./escape-html.js";
14
+ export { IMAGE_REJECTION_NOTE_MAX, IMAGE_REJECTION_REASONS, type ImageKind, type ImageRejectionCode, imageRejectionLabel, imageRejectionReasons, isImageRejectionCode, } from "./image-rejection.js";
10
15
  export { INTERNAL_SIG_HEADER, INTERNAL_SIG_MAX_SKEW_MS, INTERNAL_SIG_VERSION, INTERNAL_TS_HEADER, type InternalSigParts, internalSigPayload, } from "./internal-sig.js";
11
16
  export { CURRENT_TERMS_VERSION, LEGAL_DOCUMENT_HASHES, LEGAL_DOCUMENT_LAST_UPDATED, LEGAL_DOCUMENT_VERSIONS, type TermsAcceptanceEvidence, } from "./legal.js";
12
17
  export { canonicalLegalChangeJson, compareVersions, declaredVersion, LEGAL_CHANGE_ID_PATTERN, LEGAL_CHANGES, LEGAL_VERSION_PATTERN, type LegalChange, type LegalChangeDocument, type LegalChangeEnforcement, type LegalChangeReason, type LegalDocumentKey, MATERIAL_CHANGE_NOTICE_MS, openMaterialChanges, pendingAcknowledgement, SIGNUP_DISCLOSURE_HEADER, } from "./legal-changes.js";
13
18
  export { MAX_ANON_TARBALL_BYTES, MAX_INLINE_PUBLISH_BYTES, MAX_OPEN_UPLOAD_SESSIONS_PER_USER, MAX_TARBALL_BYTES, MAX_UPLOAD_CHUNK_BYTES, MAX_UPLOAD_SESSION_FILES, MIN_UPLOAD_CHUNK_BYTES, UPLOAD_SESSION_MAX_LIFETIME_MS, UPLOAD_SESSION_TTL_MS, } from "./limits.js";
14
19
  export { MAX_LOG_SEARCH_LENGTH } from "./logs.js";
15
20
  export { MAX_OAUTH_CLIENT_NAME_DISPLAY_CODE_POINTS, normaliseOAuthClientName, } from "./oauth-client-name.js";
16
- export { describeOAuthScope, OAUTH_SCOPE_LABELS, parseOAuthScopes } from "./oauth-scopes.js";
21
+ export { DATA_ACCESS_MODE_LABELS, describeOAuthScope, OAUTH_SCOPE_LABELS, parseOAuthScopes, } from "./oauth-scopes.js";
17
22
  export { DEFAULT_DOMAIN_SUFFIX, DEFAULT_MCP_HTTP_PORT, DEFAULT_POCKETBASE_IMAGE, MCP_TOOL_COUNT_HOSTED, MCP_TOOL_COUNT_LOCAL, } from "./platform.js";
18
23
  export { canonicalInjectedKey, isPercherInjected, PERCHER_INJECTED_ENV_KEYS, } from "./platform-env.js";
19
24
  export { DEFAULT_NODE_VERSION_LABEL, DEFAULT_NODE_VERSION_MAJOR } from "./runtime.js";
25
+ export { parseSignupCapacity, SIGNUP_CAPACITY_MAX_AGE_MS, SIGNUP_CAPACITY_MESSAGE, type SignupCapacityReason, type SignupCapacityState, } from "./signup-capacity.js";
26
+ export { signupDayWindow } from "./signup-day.js";
20
27
  export { normalizeSignupInviteCode, SIGNUP_INVITE_CODE_ALPHABET, SIGNUP_INVITE_CODE_LENGTH, SIGNUP_INVITE_CODE_PREFIX, SIGNUP_INVITE_HEADER, } from "./signup-invite.js";
21
28
  export { assertValidSwitches, type BooleanSwitch, type EnumSwitch, type PresenceSwitch, reportSwitches, SWITCH_BY_NAME, SWITCHES, type SwitchReader, type SwitchReport, type SwitchSpec, type SwitchState, switchEnabled, switchState, switchValue, } from "./switches.js";
22
29
  export { BUN_MAX_IDLE_TIMEOUT_SECONDS, HTTP_IDLE_TIMEOUT_SECONDS, type HttpIdleTimeoutSurface, TIMEOUTS, type TimeoutKey, } from "./timeouts.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -3,20 +3,27 @@ export { BILLING_TEST_ACCOUNTS_ENV, isBillingTestAccount, parseBillingTestAccoun
3
3
  export { BRAND, ICON_COLOURS, iconColourHex } from "./brand.js";
4
4
  export { CLIENT_IP_ATTESTATION_MAX_SKEW_MS, CLIENT_IP_ATTESTATION_VERSION, CLIENT_IP_HEADER, CLIENT_IP_MAX_LENGTH, CLIENT_IP_PROOF_HEADER, clientIpAttestationPayload, formatClientIpProof, parseClientIpProof, } from "./client-ip-attestation.js";
5
5
  export { crashAssistantPrompt } from "./crash-prompt.js";
6
+ export { parseDataFields, parseDataFilter, parseDataSort, } from "./data-filter.js";
7
+ export { DATA_ACCESS_MODES, DATA_ACCESS_SCOPES, DATA_ACCESS_SETTER_ERROR_STATUS, DATA_ACT_AS_UNAVAILABLE_REASONS, DATA_ACTING_AS, DATA_COLLECTION_RE, DATA_IDENTITY_KINDS, DATA_IDENTITY_LABEL_FIELD_TYPES, DATA_IDENTITY_LABELS, DATA_LIMITS, DATA_NOT_RUNNING_REASONS, DATA_RATE_LIMIT_SOURCES, DATA_RECORD_ERROR_STATUS, DATA_RECORD_ID_RE, DATA_RULE_CLASSES, DATA_RULE_DENIED_REASONS, DATA_RULE_OPS, } from "./data-records.js";
8
+ export { disposableDomainCount, isDisposableEmailDomain } from "./disposable-email-domains.js";
6
9
  export { DOMAIN_RE } from "./domain-name.js";
10
+ export { EMAIL_BUDGET_LIMITS, EMAIL_BUDGET_SCHEMA, EmailBudget, } from "./email-budget.js";
7
11
  export { ENV_MASK, isMaskedEnvValue, maskEnvValue } from "./env-mask.js";
8
12
  export { ENV_NAME_RE } from "./env-name.js";
9
13
  export { escapeHtml } from "./escape-html.js";
14
+ export { IMAGE_REJECTION_NOTE_MAX, IMAGE_REJECTION_REASONS, imageRejectionLabel, imageRejectionReasons, isImageRejectionCode, } from "./image-rejection.js";
10
15
  export { INTERNAL_SIG_HEADER, INTERNAL_SIG_MAX_SKEW_MS, INTERNAL_SIG_VERSION, INTERNAL_TS_HEADER, internalSigPayload, } from "./internal-sig.js";
11
16
  export { CURRENT_TERMS_VERSION, LEGAL_DOCUMENT_HASHES, LEGAL_DOCUMENT_LAST_UPDATED, LEGAL_DOCUMENT_VERSIONS, } from "./legal.js";
12
17
  export { canonicalLegalChangeJson, compareVersions, declaredVersion, LEGAL_CHANGE_ID_PATTERN, LEGAL_CHANGES, LEGAL_VERSION_PATTERN, MATERIAL_CHANGE_NOTICE_MS, openMaterialChanges, pendingAcknowledgement, SIGNUP_DISCLOSURE_HEADER, } from "./legal-changes.js";
13
18
  export { MAX_ANON_TARBALL_BYTES, MAX_INLINE_PUBLISH_BYTES, MAX_OPEN_UPLOAD_SESSIONS_PER_USER, MAX_TARBALL_BYTES, MAX_UPLOAD_CHUNK_BYTES, MAX_UPLOAD_SESSION_FILES, MIN_UPLOAD_CHUNK_BYTES, UPLOAD_SESSION_MAX_LIFETIME_MS, UPLOAD_SESSION_TTL_MS, } from "./limits.js";
14
19
  export { MAX_LOG_SEARCH_LENGTH } from "./logs.js";
15
20
  export { MAX_OAUTH_CLIENT_NAME_DISPLAY_CODE_POINTS, normaliseOAuthClientName, } from "./oauth-client-name.js";
16
- export { describeOAuthScope, OAUTH_SCOPE_LABELS, parseOAuthScopes } from "./oauth-scopes.js";
21
+ export { DATA_ACCESS_MODE_LABELS, describeOAuthScope, OAUTH_SCOPE_LABELS, parseOAuthScopes, } from "./oauth-scopes.js";
17
22
  export { DEFAULT_DOMAIN_SUFFIX, DEFAULT_MCP_HTTP_PORT, DEFAULT_POCKETBASE_IMAGE, MCP_TOOL_COUNT_HOSTED, MCP_TOOL_COUNT_LOCAL, } from "./platform.js";
18
23
  export { canonicalInjectedKey, isPercherInjected, PERCHER_INJECTED_ENV_KEYS, } from "./platform-env.js";
19
24
  export { DEFAULT_NODE_VERSION_LABEL, DEFAULT_NODE_VERSION_MAJOR } from "./runtime.js";
25
+ export { parseSignupCapacity, SIGNUP_CAPACITY_MAX_AGE_MS, SIGNUP_CAPACITY_MESSAGE, } from "./signup-capacity.js";
26
+ export { signupDayWindow } from "./signup-day.js";
20
27
  export { normalizeSignupInviteCode, SIGNUP_INVITE_CODE_ALPHABET, SIGNUP_INVITE_CODE_LENGTH, SIGNUP_INVITE_CODE_PREFIX, SIGNUP_INVITE_HEADER, } from "./signup-invite.js";
21
28
  export { assertValidSwitches, reportSwitches, SWITCH_BY_NAME, SWITCHES, switchEnabled, switchState, switchValue, } from "./switches.js";
22
29
  export { BUN_MAX_IDLE_TIMEOUT_SECONDS, HTTP_IDLE_TIMEOUT_SECONDS, TIMEOUTS, } from "./timeouts.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,YAAY,EAAsB,aAAa,EAAE,MAAM,SAAS,CAAC;AACjF,OAAO,EACL,iCAAiC,EACjC,6BAA6B,EAC7B,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EAEtB,0BAA0B,EAC1B,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAElB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,2BAA2B,EAC3B,uBAAuB,GAExB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EAMrB,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,sBAAsB,EACtB,wBAAwB,EACxB,iCAAiC,EACjC,iBAAiB,EACjB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,8BAA8B,EAC9B,qBAAqB,GACtB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EACL,yCAAyC,EACzC,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC1F,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,yBAAyB,EACzB,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EAInB,cAAc,EACd,cAAc,EACd,QAAQ,EAKR,aAAa,EACb,WAAW,EACX,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,4BAA4B,EAC5B,yBAAyB,EAEzB,QAAQ,GAET,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -8,8 +8,8 @@
8
8
  * window, and the raw secret never has to ride on the wire once enforce is on.
9
9
  *
10
10
  * This module is the single source of truth for the canonical string both sides
11
- * HMAC, so the API verifier and every signer (the dashboard today; the external
12
- * CLABBE runner per docs/integrations/CLABBE_SECURITY_INGEST.md) can't drift. It
11
+ * HMAC, so the API verifier and every signer (the dashboard and the external
12
+ * security runner, per the security-ingest contract in docs/integrations) can't drift. It
13
13
  * is dependency-free on purpose — no crypto here — so it bundles cleanly into the
14
14
  * dashboard (Node) as well as the API (Bun). Each side wraps it with its own
15
15
  * `node:crypto` HMAC.
@@ -8,8 +8,8 @@
8
8
  * window, and the raw secret never has to ride on the wire once enforce is on.
9
9
  *
10
10
  * This module is the single source of truth for the canonical string both sides
11
- * HMAC, so the API verifier and every signer (the dashboard today; the external
12
- * CLABBE runner per docs/integrations/CLABBE_SECURITY_INGEST.md) can't drift. It
11
+ * HMAC, so the API verifier and every signer (the dashboard and the external
12
+ * security runner, per the security-ingest contract in docs/integrations) can't drift. It
13
13
  * is dependency-free on purpose — no crypto here — so it bundles cleanly into the
14
14
  * dashboard (Node) as well as the API (Bun). Each side wraps it with its own
15
15
  * `node:crypto` HMAC.
@@ -46,9 +46,9 @@ export interface LegalChange {
46
46
  materiality: "material" | "editorial";
47
47
  reason: LegalChangeReason;
48
48
  enforcement: LegalChangeEnforcement;
49
- /** ISO instant, UTC. When the notice may start going out. */
49
+ /** ISO instant, UTC. First send instant of the notice batch. */
50
50
  announceAt: string;
51
- /** ISO instant, UTC. When the new documents govern. */
51
+ /** ISO instant, UTC. Existing-account effect; material changes need 30 full days after the last send. */
52
52
  effectiveAt: string;
53
53
  /** Every document whose version header moves in this change. */
54
54
  documents: readonly LegalChangeDocument[];
@@ -62,6 +62,8 @@ export interface LegalChange {
62
62
  };
63
63
  /** Set when the same change also modifies the Service itself (2019/770 Art. 19). */
64
64
  modifiesService?: boolean;
65
+ /** This change governs new accounts from signup, before the existing-account date. */
66
+ appliesToNewAccountsFromSignup?: boolean;
65
67
  }
66
68
  /** Newest last. Editorial corrections record the published document history. */
67
69
  export declare const LEGAL_CHANGES: readonly LegalChange[];
@@ -1 +1 @@
1
- {"version":3,"file":"legal-changes.d.ts","sourceRoot":"","sources":["../src/legal-changes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAElD;;;;;;;;;;;;;;;;;GAiBG;AAEH,sFAAsF;AACtF,MAAM,MAAM,iBAAiB,GACzB,kBAAkB,GAClB,UAAU,GACV,kBAAkB,GAClB,gBAAgB,GAChB,iBAAiB,GACjB,eAAe,CAAC;AAEpB;;;;;;;;;;GAUG;AACH,MAAM,MAAM,sBAAsB,GAAG,aAAa,GAAG,sBAAsB,GAAG,kBAAkB,CAAC;AAEjG,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,uBAAuB,CAAC;AAEpE,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,gBAAgB,CAAC;IACtB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,kGAAkG;IAClG,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,WAAW;IAC1B,2FAA2F;IAC3F,EAAE,EAAE,MAAM,CAAC;IACX,oEAAoE;IACpE,WAAW,EAAE,UAAU,GAAG,WAAW,CAAC;IACtC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,WAAW,EAAE,sBAAsB,CAAC;IACpC,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,SAAS,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC1C;;;OAGG;IACH,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,oFAAoF;IACpF,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,gFAAgF;AAChF,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EA+D/C,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,gCAAgC,CAAC;AAEtE,gEAAgE;AAChE,eAAO,MAAM,yBAAyB,QAA2B,CAAC;AAElE,iFAAiF;AACjF,eAAO,MAAM,uBAAuB,QAAiD,CAAC;AAEtF,2FAA2F;AAC3F,eAAO,MAAM,qBAAqB,QAAyB,CAAC;AAE5D;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAgBpE;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,SAAS,WAAW,EAAkB,GAC9C,WAAW,EAAE,CAIf;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,gBAAgB,EACrB,OAAO,GAAE,SAAS,WAAW,EAAkB,GAC9C,MAAM,CAMR;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CA6BnE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,oBAAoB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/C,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,SAAS,WAAW,EAAkB,GAC9C,WAAW,GAAG,IAAI,CAiBpB"}
1
+ {"version":3,"file":"legal-changes.d.ts","sourceRoot":"","sources":["../src/legal-changes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAElD;;;;;;;;;;;;;;;;;GAiBG;AAEH,sFAAsF;AACtF,MAAM,MAAM,iBAAiB,GACzB,kBAAkB,GAClB,UAAU,GACV,kBAAkB,GAClB,gBAAgB,GAChB,iBAAiB,GACjB,eAAe,CAAC;AAEpB;;;;;;;;;;GAUG;AACH,MAAM,MAAM,sBAAsB,GAAG,aAAa,GAAG,sBAAsB,GAAG,kBAAkB,CAAC;AAEjG,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,uBAAuB,CAAC;AAEpE,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,gBAAgB,CAAC;IACtB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,kGAAkG;IAClG,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,WAAW;IAC1B,2FAA2F;IAC3F,EAAE,EAAE,MAAM,CAAC;IACX,oEAAoE;IACpE,WAAW,EAAE,UAAU,GAAG,WAAW,CAAC;IACtC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,WAAW,EAAE,sBAAsB,CAAC;IACpC,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,yGAAyG;IACzG,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,SAAS,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC1C;;;OAGG;IACH,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,oFAAoF;IACpF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,sFAAsF;IACtF,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAC1C;AAED,gFAAgF;AAChF,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EA0L/C,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,gCAAgC,CAAC;AAEtE,gEAAgE;AAChE,eAAO,MAAM,yBAAyB,QAA2B,CAAC;AAElE,iFAAiF;AACjF,eAAO,MAAM,uBAAuB,QAAiD,CAAC;AAEtF,2FAA2F;AAC3F,eAAO,MAAM,qBAAqB,QAAyB,CAAC;AAE5D;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAiBpE;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,SAAS,WAAW,EAAkB,GAC9C,WAAW,EAAE,CAIf;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,gBAAgB,EACrB,OAAO,GAAE,SAAS,WAAW,EAAkB,GAC9C,MAAM,CAMR;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CA6BnE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,oBAAoB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/C,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,SAAS,WAAW,EAAkB,GAC9C,WAAW,GAAG,IAAI,CAiBpB"}
@@ -63,6 +63,129 @@ export const LEGAL_CHANGES = [
63
63
  sv: "Kraschdiagnostik tas nu fram med fasta regler inom Percher, och kraschloggar skickas inte till någon extern AI-leverantör. Möjligheten till extern AI-kraschanalys, som var avstängd, tas bort och dess leverantör listas inte längre. Avsnitt 3.3 hänvisar uttryckligen till avsnitt 9 och tvingande lag för ansvar. Ingen ny behandling och ingen ändring av dina rättigheter eller skyldigheter.",
64
64
  },
65
65
  },
66
+ {
67
+ // Agent recommendation: inventory correction only; ship with the export implementation.
68
+ id: "2026-10-11-portability-export-inventory",
69
+ materiality: "editorial",
70
+ reason: "clarification",
71
+ enforcement: "notice_only",
72
+ announceAt: "2026-10-11T00:00:00.000Z",
73
+ effectiveAt: "2026-10-11T00:00:00.000Z",
74
+ documents: [{ key: "dataPortability", from: "1.0", to: "1.0.1" }],
75
+ summary: {
76
+ en: "The Data Portability Register lists the exported source, retained deployment history, visitor statistics, build and runtime logs, and crash reports. It explains archive link exclusions, log masking and unavailable data. This updates the technical inventory without changing switching rights or obligations.",
77
+ sv: "Dataportabilitetsregistret listar exporterad källkod, bevarad driftsättningshistorik, besöksstatistik, bygg- och körningsloggar samt kraschrapporter. Det förklarar undantagna arkivlänkar, maskering av loggar och otillgängliga data. Den tekniska förteckningen uppdateras utan ändring av rättigheter eller skyldigheter vid byte.",
78
+ },
79
+ },
80
+ {
81
+ // Editorial inventory only (ops declaration 2026-10-11, no mail): storage keys, exported fields, dates.
82
+ id: "2026-10-11-browser-storage-inventory",
83
+ materiality: "editorial",
84
+ reason: "clarification",
85
+ enforcement: "notice_only",
86
+ announceAt: "2026-10-11T00:00:00.000Z",
87
+ effectiveAt: "2026-10-11T00:00:00.000Z",
88
+ documents: [
89
+ { key: "cookies", from: "1.0.3", to: "1.0.4" },
90
+ { key: "terms", from: "1.0.2", to: "1.0.3" },
91
+ { key: "dataPortability", from: "1.0.1", to: "1.0.2" },
92
+ ],
93
+ summary: {
94
+ en: "The Cookie Policy lists existing rollback and admin interface storage, corrects the theme-key description and identifies application/deployment identifiers. The Register lists exported moderation codes and preview orientations. The Terms last-updated dates are aligned. No change to processing, rights or obligations.",
95
+ sv: "Cookiepolicyn listar befintlig lagring för återställning och administratörsgränssnittet, rättar beskrivningen av temanycklar och anger applikations- och publiceringsidentifierare. Registret listar exporterade modereringskoder och förhandsbildsorienteringar. Villkorens uppdateringsdatum samordnas. Ingen ändring av behandling, rättigheter eller skyldigheter.",
96
+ },
97
+ },
98
+ {
99
+ // Editorial (owner decision 2026-10-11: current facts go live at merge, no waiting date; no mail).
100
+ id: "2026-10-11-privacy-portability-current-facts",
101
+ materiality: "editorial",
102
+ reason: "legal_compliance",
103
+ enforcement: "notice_only",
104
+ announceAt: "2026-10-11T00:00:00.000Z",
105
+ effectiveAt: "2026-10-11T00:00:00.000Z",
106
+ documents: [
107
+ { key: "privacy", from: "1.0.2", to: "1.0.3" },
108
+ { key: "dataPortability", from: "1.0.2", to: "1.0.3" },
109
+ ],
110
+ summary: {
111
+ en: "Clarifies existing encryption, account password storage, log retention and beta invitation-use records. No processing or rights change.",
112
+ sv: "Förtydligar befintlig kryptering, lagring av kontolösenord, loggarnas lagringstider och uppgifter om användning av betainbjudningar. Ingen ändring av behandling eller rättigheter.",
113
+ },
114
+ },
115
+ {
116
+ // Editorial inventory (owner decision 2026-10-11: current facts go live at merge; no mail).
117
+ id: "2026-10-11-portability-image-exports",
118
+ materiality: "editorial",
119
+ reason: "clarification",
120
+ enforcement: "notice_only",
121
+ announceAt: "2026-10-11T00:00:00.000Z",
122
+ effectiveAt: "2026-10-11T00:00:00.000Z",
123
+ documents: [{ key: "dataPortability", from: "1.0.3", to: "1.0.4" }],
124
+ summary: {
125
+ en: "The Data Portability Register lists image downloads and source-less app bundles.",
126
+ sv: "Dataportabilitetsregistret listar nedladdning av bilder och appaket utan källkod.",
127
+ },
128
+ },
129
+ {
130
+ // Editorial (owner decision 2026-10-12: CLI/MCP usage telemetry is opt-in; no mail).
131
+ id: "2026-10-12-telemetry-opt-in",
132
+ materiality: "editorial",
133
+ reason: "clarification",
134
+ enforcement: "notice_only",
135
+ announceAt: "2026-10-12T00:00:00.000Z",
136
+ effectiveAt: "2026-10-12T00:00:00.000Z",
137
+ documents: [{ key: "privacy", from: "1.0.3", to: "1.0.4" }],
138
+ summary: {
139
+ en: "CLI and MCP usage statistics are sent only after the user turns them on, by answering yes to the CLI's one-time question or running percher telemetry on, and can be turned off at any time. The legal basis for these statistics is consent.",
140
+ sv: "Användningsstatistik från CLI och MCP skickas först när användaren slagit på den, genom att svara ja på CLI:ns engångsfråga eller köra percher telemetry on, och kan stängas av när som helst. Den rättsliga grunden för statistiken är samtycke.",
141
+ },
142
+ },
143
+ {
144
+ // No mail: every pre-publication account is the controller's own (owner decision 2026-10-07).
145
+ id: "2026-11-07-beta-dashboard-app-data",
146
+ materiality: "material",
147
+ reason: "service_change",
148
+ enforcement: "notice_only",
149
+ announceAt: "2026-10-07T10:00:00.000Z",
150
+ effectiveAt: "2026-11-07T00:00:00.000Z",
151
+ appliesToNewAccountsFromSignup: true,
152
+ documents: [{ key: "subprocessors", from: "1.0.2", to: "1.1" }],
153
+ summary: {
154
+ en: "During the beta, App Data (logs, files, database content and diagnostics) for ordinary support goes through dashboard cases, including public support access. For accounts created on or after publication, this applies from signup. For earlier accounts, it applies from the effective date; until then, cases are requested but not required and emailed App Data is handled under current terms. The abuse@, security@ and legal@ reporting channels are unchanged. Percher's own support emails contain notifications and links, never case content.",
155
+ sv: "Under betan lämnas Appdata (loggar, filer, databasinnehåll och diagnostik) för vanlig support via ärenden i dashboarden, inklusive publik supportåtkomst. För konton skapade på eller efter publiceringen gäller detta från registreringen. För äldre konton gäller det från ikraftträdandedatumet; tills dess önskas men krävs inte ärenden, och Appdata via e-post hanteras enligt nuvarande villkor. Rapporteringskanalerna abuse@, security@ och legal@ är oförändrade. Perchers egna e-postmeddelanden om support innehåller aviseringar och länkar, aldrig ärendeinnehåll.",
156
+ },
157
+ },
158
+ {
159
+ // No mail while every pre-publication account is the controller's own (owner decision 2026-10-11).
160
+ id: "2026-11-16-subprocessor-spacemail",
161
+ materiality: "material",
162
+ reason: "legal_compliance",
163
+ enforcement: "notice_only",
164
+ announceAt: "2026-10-11T14:30:00.000Z",
165
+ effectiveAt: "2026-11-16T00:00:00.000Z",
166
+ appliesToNewAccountsFromSignup: true,
167
+ documents: [{ key: "subprocessors", from: "1.1", to: "1.2" }],
168
+ summary: {
169
+ en: "Spaceship, Inc. (Spacemail) is listed as a sub-processor. It hosts the support@, abuse@, security@ and legal@percher.app mailboxes in the United States, including addresses and message content, which may include App Data. Transfers rely on the Standard Contractual Clauses (Module Two) in Spaceship's Data Processing Addendum. Spaceship may use OpenAI's Moderation tool, with OpenAI as its sub-processor, to scan parts of emails for spam and phishing.",
170
+ sv: "Spaceship, Inc. (Spacemail) anges som underbiträde. Spaceship driver e-postlådorna support@, abuse@, security@ och legal@percher.app och behandlar adresser och meddelandeinnehåll, som kan innehålla Appdata, i USA. Överföringarna sker med stöd av standardavtalsklausulerna (modul två) i Spaceships personuppgiftsbiträdesavtal. Spaceship kan använda OpenAI:s Moderation-verktyg, med OpenAI som underbiträde, för att skanna delar av e-post efter skräppost och nätfiske.",
171
+ },
172
+ },
173
+ {
174
+ // No mail while every pre-publication account is the controller's own (owner decision 2026-10-11:
175
+ // the DPA clause is the only material item; the Terms promise 30 days' notice, so it keeps its date).
176
+ id: "2026-11-16-dpa-prior-subprocessor-notice",
177
+ materiality: "material",
178
+ reason: "legal_compliance",
179
+ enforcement: "notice_only",
180
+ announceAt: "2026-10-11T17:45:00.000Z",
181
+ effectiveAt: "2026-11-16T00:00:00.000Z",
182
+ appliesToNewAccountsFromSignup: true,
183
+ documents: [{ key: "dpa", from: "1.0", to: "1.1" }],
184
+ summary: {
185
+ en: "The DPA requires notice before a subprocessor is added or replaced, including urgent changes. The existing right to object on reasonable data-protection grounds remains.",
186
+ sv: "DPA:n kräver information innan ett underbiträde läggs till eller byts ut, även vid brådskande ändringar. Den befintliga rätten att invända på skäliga dataskyddsgrunder kvarstår.",
187
+ },
188
+ },
66
189
  ];
67
190
  /**
68
191
  * Header carrying the change ids the signup form actually rendered.
@@ -88,6 +211,7 @@ export const LEGAL_VERSION_PATTERN = /^\d+\.\d+(?:\.\d+)?$/;
88
211
  export function canonicalLegalChangeJson(change) {
89
212
  return JSON.stringify({
90
213
  announceAt: change.announceAt,
214
+ ...(change.appliesToNewAccountsFromSignup ? { appliesToNewAccountsFromSignup: true } : {}),
91
215
  documents: change.documents.map((document) => ({
92
216
  from: document.from,
93
217
  key: document.key,