@ttt-productions/ttt-core 0.79.26 → 0.79.27
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/dist/doc-schemas/account-deletion.d.ts +27 -0
- package/dist/doc-schemas/account-deletion.d.ts.map +1 -1
- package/dist/doc-schemas/account-deletion.js +23 -0
- package/dist/doc-schemas/account-deletion.js.map +1 -1
- package/dist/doc-schemas/backend-state.d.ts +23 -0
- package/dist/doc-schemas/backend-state.d.ts.map +1 -1
- package/dist/doc-schemas/backend-state.js +41 -0
- package/dist/doc-schemas/backend-state.js.map +1 -1
- package/dist/doc-schemas/index.d.ts +0 -1
- package/dist/doc-schemas/index.d.ts.map +1 -1
- package/dist/doc-schemas/index.js +0 -1
- package/dist/doc-schemas/index.js.map +1 -1
- package/dist/doc-schemas/media-assets.d.ts +10 -4
- package/dist/doc-schemas/media-assets.d.ts.map +1 -1
- package/dist/doc-schemas/media-assets.js +21 -9
- package/dist/doc-schemas/media-assets.js.map +1 -1
- package/dist/doc-schemas/registry.d.ts +22 -1
- package/dist/doc-schemas/registry.d.ts.map +1 -1
- package/dist/doc-schemas/registry.js +7 -8
- package/dist/doc-schemas/registry.js.map +1 -1
- package/dist/paths/collections.d.ts +3 -2
- package/dist/paths/collections.d.ts.map +1 -1
- package/dist/paths/collections.js +13 -6
- package/dist/paths/collections.js.map +1 -1
- package/dist/paths/path-builders.d.ts +2 -2
- package/dist/paths/path-builders.d.ts.map +1 -1
- package/dist/paths/path-builders.js +7 -4
- package/dist/paths/path-builders.js.map +1 -1
- package/dist/types/audit.js.map +1 -1
- package/package.json +3 -3
- package/dist/doc-schemas/safety/reviewer-security.d.ts +0 -128
- package/dist/doc-schemas/safety/reviewer-security.d.ts.map +0 -1
- package/dist/doc-schemas/safety/reviewer-security.js +0 -106
- package/dist/doc-schemas/safety/reviewer-security.js.map +0 -1
|
@@ -43,6 +43,33 @@ export type ActiveDeletionRequestStatus = {
|
|
|
43
43
|
* classification above in union-declaration order — so it can also feed a Firestore
|
|
44
44
|
* `where('status', 'in', ACTIVE_DELETION_REQUEST_STATUSES)` query directly. */
|
|
45
45
|
export declare const ACTIVE_DELETION_REQUEST_STATUSES: readonly ActiveDeletionRequestStatus[];
|
|
46
|
+
/**
|
|
47
|
+
* MID-SCRUB = the destructive erasure has STARTED and has not finished. These statuses are
|
|
48
|
+
* neither active (nothing left to cancel or re-offer) nor terminal, so the scrub drain's
|
|
49
|
+
* due-request query never sees them again — which is exactly why a request stranded here needs
|
|
50
|
+
* its own recovery query (the stranded-erasure sweep over an expired `erasureLease`).
|
|
51
|
+
*
|
|
52
|
+
* Declared as an EXHAUSTIVE Record over the status union for the same reason the ACTIVE
|
|
53
|
+
* classification is: adding a lifecycle status must be a compile error here until that status is
|
|
54
|
+
* classified, rather than silently defaulting to "not mid-scrub" and dropping out of recovery.
|
|
55
|
+
*/
|
|
56
|
+
declare const DELETION_REQUEST_STATUS_IS_MID_SCRUB: {
|
|
57
|
+
readonly pending: false;
|
|
58
|
+
readonly cancelled: false;
|
|
59
|
+
readonly scrubbing: true;
|
|
60
|
+
readonly leased: true;
|
|
61
|
+
readonly parkedOnHold: false;
|
|
62
|
+
readonly completed: false;
|
|
63
|
+
readonly superseded: false;
|
|
64
|
+
};
|
|
65
|
+
/** The statuses classified MID-SCRUB above, as a literal union — derived, never restated. */
|
|
66
|
+
export type MidScrubDeletionRequestStatus = {
|
|
67
|
+
[K in AccountDeletionRequestStatus]: (typeof DELETION_REQUEST_STATUS_IS_MID_SCRUB)[K] extends true ? K : never;
|
|
68
|
+
}[AccountDeletionRequestStatus];
|
|
69
|
+
/** The MID-SCRUB (erasure started, not finished) deletion-request statuses, projected from the
|
|
70
|
+
* classification above in union-declaration order — so it can feed the stranded-erasure recovery
|
|
71
|
+
* `where('status', 'in', MID_SCRUB_DELETION_REQUEST_STATUSES)` query directly. */
|
|
72
|
+
export declare const MID_SCRUB_DELETION_REQUEST_STATUSES: readonly MidScrubDeletionRequestStatus[];
|
|
46
73
|
/** True for an ACTIVE (open, still-cancellable) deletion request. Takes `unknown` because every
|
|
47
74
|
* caller reads the status off an unvalidated Firestore doc: anything outside the canonical active
|
|
48
75
|
* set — a terminal/mid-scrub status, an unrecognized value, a missing field — is false. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-deletion.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/account-deletion.ts"],"names":[],"mappings":"AA4BA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,gDAAgD;AAChD,eAAO,MAAM,kCAAkC;;;;;;;;EAU7C,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAE9F;;;;;;;;;;;;;;;GAeG;AACH,QAAA,MAAM,iCAAiC;;;;;;;;CAQ2B,CAAC;AAEnE,0FAA0F;AAC1F,MAAM,MAAM,2BAA2B,GAAG;KACvC,CAAC,IAAI,4BAA4B,GAAG,CAAC,OAAO,iCAAiC,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,CAAC,GAAG,KAAK;CAC5G,CAAC,4BAA4B,CAAC,CAAC;AAEhC;;gFAEgF;AAChF,eAAO,MAAM,gCAAgC,EAAE,SAAS,2BAA2B,EAEmB,CAAC;AAEvG;;4FAE4F;AAC5F,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,2BAA2B,CAE9F;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;kBAmBtB,CAAC;AACZ,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,yEAAyE;AACzE,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA+ChC,CAAC;AACZ,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"account-deletion.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/account-deletion.ts"],"names":[],"mappings":"AA4BA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,gDAAgD;AAChD,eAAO,MAAM,kCAAkC;;;;;;;;EAU7C,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAE9F;;;;;;;;;;;;;;;GAeG;AACH,QAAA,MAAM,iCAAiC;;;;;;;;CAQ2B,CAAC;AAEnE,0FAA0F;AAC1F,MAAM,MAAM,2BAA2B,GAAG;KACvC,CAAC,IAAI,4BAA4B,GAAG,CAAC,OAAO,iCAAiC,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,CAAC,GAAG,KAAK;CAC5G,CAAC,4BAA4B,CAAC,CAAC;AAEhC;;gFAEgF;AAChF,eAAO,MAAM,gCAAgC,EAAE,SAAS,2BAA2B,EAEmB,CAAC;AAEvG;;;;;;;;;GASG;AACH,QAAA,MAAM,oCAAoC;;;;;;;;CAQwB,CAAC;AAEnE,6FAA6F;AAC7F,MAAM,MAAM,6BAA6B,GAAG;KACzC,CAAC,IAAI,4BAA4B,GAAG,CAAC,OAAO,oCAAoC,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,CAAC,GAAG,KAAK;CAC/G,CAAC,4BAA4B,CAAC,CAAC;AAEhC;;mFAEmF;AACnF,eAAO,MAAM,mCAAmC,EAAE,SAAS,6BAA6B,EAEmB,CAAC;AAE5G;;4FAE4F;AAC5F,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,2BAA2B,CAE9F;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;kBAmBtB,CAAC;AACZ,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,yEAAyE;AACzE,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA+ChC,CAAC;AACZ,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC"}
|
|
@@ -67,6 +67,29 @@ const DELETION_REQUEST_STATUS_IS_ACTIVE = {
|
|
|
67
67
|
* classification above in union-declaration order — so it can also feed a Firestore
|
|
68
68
|
* `where('status', 'in', ACTIVE_DELETION_REQUEST_STATUSES)` query directly. */
|
|
69
69
|
export const ACTIVE_DELETION_REQUEST_STATUSES = Object.keys(DELETION_REQUEST_STATUS_IS_ACTIVE).filter((status) => DELETION_REQUEST_STATUS_IS_ACTIVE[status]);
|
|
70
|
+
/**
|
|
71
|
+
* MID-SCRUB = the destructive erasure has STARTED and has not finished. These statuses are
|
|
72
|
+
* neither active (nothing left to cancel or re-offer) nor terminal, so the scrub drain's
|
|
73
|
+
* due-request query never sees them again — which is exactly why a request stranded here needs
|
|
74
|
+
* its own recovery query (the stranded-erasure sweep over an expired `erasureLease`).
|
|
75
|
+
*
|
|
76
|
+
* Declared as an EXHAUSTIVE Record over the status union for the same reason the ACTIVE
|
|
77
|
+
* classification is: adding a lifecycle status must be a compile error here until that status is
|
|
78
|
+
* classified, rather than silently defaulting to "not mid-scrub" and dropping out of recovery.
|
|
79
|
+
*/
|
|
80
|
+
const DELETION_REQUEST_STATUS_IS_MID_SCRUB = {
|
|
81
|
+
pending: false,
|
|
82
|
+
cancelled: false,
|
|
83
|
+
scrubbing: true,
|
|
84
|
+
leased: true,
|
|
85
|
+
parkedOnHold: false,
|
|
86
|
+
completed: false,
|
|
87
|
+
superseded: false,
|
|
88
|
+
};
|
|
89
|
+
/** The MID-SCRUB (erasure started, not finished) deletion-request statuses, projected from the
|
|
90
|
+
* classification above in union-declaration order — so it can feed the stranded-erasure recovery
|
|
91
|
+
* `where('status', 'in', MID_SCRUB_DELETION_REQUEST_STATUSES)` query directly. */
|
|
92
|
+
export const MID_SCRUB_DELETION_REQUEST_STATUSES = Object.keys(DELETION_REQUEST_STATUS_IS_MID_SCRUB).filter((status) => DELETION_REQUEST_STATUS_IS_MID_SCRUB[status]);
|
|
70
93
|
/** True for an ACTIVE (open, still-cancellable) deletion request. Takes `unknown` because every
|
|
71
94
|
* caller reads the status off an unvalidated Firestore doc: anything outside the canonical active
|
|
72
95
|
* set — a terminal/mid-scrub status, an unrecognized value, a missing field — is false. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-deletion.js","sourceRoot":"","sources":["../../src/doc-schemas/account-deletion.ts"],"names":[],"mappings":"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","sourcesContent":["// Account-deletion / GDPR-erasure request SCHEMA — `accountDeletionRequests/{uid}`.\n//\n// N3 data-deletion flow. A user clicks \"delete account\" on their profile → a\n// request doc is created (one per user, doc id = uid, create-if-absent) and the\n// account is immediately deactivated/locked out. The request runs a 30-day grace\n// window (logging back in CANCELS it). After the window, the scheduled scrub\n// worker checks for any BLOCKING safety hold over the user's data:\n// - no hold → anonymize-in-place (scrub PII, release reserved name, anonymize\n// chat history, tombstone memberships) → `completed`.\n// - hold → `parkedOnHold` (re-checked periodically): resumes + `completed`\n// once cleared, or → `superseded` if it becomes a ban-for-cause /\n// evidence-preservation outcome (the erasure is overridden and the\n// data retained as evidence).\n//\n// The scrub itself deletes/anonymizes data across many collections; this doc is\n// ONLY the request lifecycle + the worker's resumable cursor.\n//\n// [C-02] Erasure-vs-safety-hold race fence. The destructive scrub and a safety\n// hold landing mid-scrub race over the SAME account. The fence is an epoch LEASE\n// stamped on THIS doc (`erasureLease`): the scrub transactionally acquires the\n// lease ONLY when the account owns no active blocking hold; every destructive\n// write re-checks the SAME epoch in its own transaction; and `commitSafetyHold`\n// (the SINGLE shared hold authority) reads this doc's live lease and PARKS the\n// erasure (revoking the epoch) before committing a hold against a leased account.\n// A revoked epoch makes every still-running destructive step a no-op. The lease\n// lives HERE — not a parallel safety-only deletion subsystem — so the one shared\n// hold/destructive contract owns the fence (engineering-rules ARCH-001).\n\nimport { z } from 'zod';\n\n/** Lifecycle of an account-deletion request. */\nexport const AccountDeletionRequestStatusSchema = z.enum([\n 'pending', // grace window running; user can still cancel by logging in\n 'cancelled', // user cancelled (logged back in) before the window elapsed\n 'scrubbing', // the scrub worker has started the anonymize-in-place erasure\n // [C-02] the scrub holds an ACTIVE erasure lease and is mid-destruction. A hold\n // landing now revokes the lease (→ parkedOnHold) so the scrub aborts deterministically.\n 'leased',\n 'parkedOnHold', // erasure deferred — a blocking safety hold covers the user's data\n 'completed', // erasure finished\n 'superseded', // overridden by a ban-for-cause / evidence-preservation outcome\n]);\nexport type AccountDeletionRequestStatus = z.infer<typeof AccountDeletionRequestStatusSchema>;\n\n/**\n * ACTIVE = the request is still OPEN and still cancellable. This is the one predicate the\n * lifecycle's enforcement points share — `requestAccountDeletion`'s idempotent re-return of the\n * existing schedule, `cancelAccountDeletion`'s cancel gate, the scrub worker's due-request query,\n * and the client's live own-request read — each of which previously restated\n * `status === 'pending' || status === 'parkedOnHold'` locally (ARCH-102).\n *\n * Declared as an EXHAUSTIVE Record over the status union, not as a subset array: a subset array\n * only catches a REMOVED member, whereas adding a lifecycle status must be a compile error here\n * until that status is classified. A new status silently defaulting to \"not active\" is exactly the\n * drift this classification exists to prevent.\n *\n * `scrubbing` / `leased` are deliberately NOT active — the destructive erasure has already begun,\n * so there is no schedule left to re-offer and nothing left to cancel. `cancelled` / `completed` /\n * `superseded` are terminal.\n */\nconst DELETION_REQUEST_STATUS_IS_ACTIVE = {\n pending: true,\n cancelled: false,\n scrubbing: false,\n leased: false,\n parkedOnHold: true,\n completed: false,\n superseded: false,\n} as const satisfies Record<AccountDeletionRequestStatus, boolean>;\n\n/** The statuses classified ACTIVE above, as a literal union — derived, never restated. */\nexport type ActiveDeletionRequestStatus = {\n [K in AccountDeletionRequestStatus]: (typeof DELETION_REQUEST_STATUS_IS_ACTIVE)[K] extends true ? K : never;\n}[AccountDeletionRequestStatus];\n\n/** The ACTIVE (open, still-cancellable) deletion-request statuses, projected from the\n * classification above in union-declaration order — so it can also feed a Firestore\n * `where('status', 'in', ACTIVE_DELETION_REQUEST_STATUSES)` query directly. */\nexport const ACTIVE_DELETION_REQUEST_STATUSES: readonly ActiveDeletionRequestStatus[] = (\n Object.keys(DELETION_REQUEST_STATUS_IS_ACTIVE) as AccountDeletionRequestStatus[]\n).filter((status): status is ActiveDeletionRequestStatus => DELETION_REQUEST_STATUS_IS_ACTIVE[status]);\n\n/** True for an ACTIVE (open, still-cancellable) deletion request. Takes `unknown` because every\n * caller reads the status off an unvalidated Firestore doc: anything outside the canonical active\n * set — a terminal/mid-scrub status, an unrecognized value, a missing field — is false. */\nexport function isActiveDeletionRequest(status: unknown): status is ActiveDeletionRequestStatus {\n return typeof status === 'string' && (ACTIVE_DELETION_REQUEST_STATUSES as readonly string[]).includes(status);\n}\n\n/**\n * [C-02] The erasure LEASE / epoch token — the race fence between the destructive\n * scrub and a mid-scrub safety hold.\n *\n * The scrub acquires this transactionally ONLY when the account owns no active\n * blocking hold; it then re-verifies the SAME `epoch` inside every destructive\n * Firestore transaction and immediately before AND after each non-transactional\n * external delete (Auth/Storage). `commitSafetyHold` reads this lease and, when it\n * is `active`, REVOKES it (sets `revokedByHold`/`revokedAt`, status → `parkedOnHold`)\n * in the SAME transaction that commits the hold — so a hold deterministically wins\n * the race and the scrub, finding its epoch revoked on the next check, aborts into\n * a recoverable parked state without finishing the destruction.\n */\nexport const ErasureLeaseV1Schema = z\n .object({\n /** Monotonic acquisition token. The \"live lease\" identity: every destructive step\n * re-reads the doc and proceeds ONLY if this still equals the epoch it acquired. */\n epoch: z.number().int().nonnegative(),\n /** 'active' = the scrub holds the lease; 'revoked' = a hold reclaimed it mid-scrub\n * (the scrub must abort); 'released' = the scrub finished/relinquished cleanly. */\n state: z.enum(['active', 'revoked', 'released']),\n acquiredAt: z.number(),\n /** Staleness boundary. A still-`active` lease past this is presumed crashed and may\n * be safely superseded by a fresh acquisition (idempotent retry / crash recovery). */\n expiresAt: z.number(),\n /** Last destructive phase the scrub reached under this epoch (observability + recovery). */\n phase: z.string().optional(),\n /** Set when a hold revoked the lease — the caseId/refId of the winning hold (audit trail). */\n revokedByHoldRefId: z.string().min(1).optional(),\n revokedAt: z.number().optional(),\n releasedAt: z.number().optional(),\n })\n .strict();\nexport type ErasureLeaseV1 = z.infer<typeof ErasureLeaseV1Schema>;\n\n/** `accountDeletionRequests/{uid}` — one per user; doc id IS the uid. */\nexport const AccountDeletionRequestV1Schema = z\n .object({\n schemaVersion: z.literal(1),\n uid: z.string().min(1),\n status: AccountDeletionRequestStatusSchema,\n requestedAt: z.number(),\n // requestedAt + graceDays; the scrub worker queries status='pending' AND\n // scheduledScrubAt <= now (composite index, ttt-prod firestore.indexes.json).\n scheduledScrubAt: z.number(),\n graceDays: z.number(), // recorded for the audit/compliance trail (30 at launch)\n cancelledAt: z.number().optional(),\n // Token-revocation obligation — the request doc IS the durable ledger (BACKEND-302 /\n // QUALITY-104); no separate queue, worker collection, or dead-letter state exists.\n //\n // `tokenRevocationNextAttemptAt` is the DRAIN KEY: present ⇔ the revocation is still\n // OWED. Set to `now` when the request is created; the scheduled worker's revocation\n // drain queries `tokenRevocationNextAttemptAt <= now` (single-field range — deliberately\n // no status clause and no composite index; every terminal transition clears the field,\n // and the drain clears it on any non-active doc it meets, so \"owed\" means exactly\n // \"field present\"). A failed attempt moves it forward (bounded backoff); a successful\n // revoke DELETES it and stamps `tokensRevokedAt` in the same fenced update.\n //\n // The stamp is GENERATION-FENCED on `requestedAt`: the revoker snapshots `requestedAt`\n // before calling Auth and stamps transactionally only if the doc still carries that\n // value with an active status — a delayed revoke from a cancelled request must never\n // mark a newly reopened request satisfied. Revocation is idempotent, so a skipped or\n // lost stamp merely causes one redundant re-revoke.\n tokenRevocationNextAttemptAt: z.number().optional(),\n // When the user's refresh tokens were revoked for THIS request (\"account deletion signs\n // the user out of every session everywhere\"). The callable's immediate revoke is only\n // the accelerator (BACKEND-301); the worker drain above is the guarantee.\n tokensRevokedAt: z.number().optional(),\n scrubStartedAt: z.number().optional(),\n // Why a scrub is parked (e.g. 'blocking safety hold over user data') — kept for\n // the GDPR compliance record so a deferred erasure is explainable.\n parkedReason: z.string().optional(),\n parkedAt: z.number().optional(),\n lastHoldCheckAt: z.number().optional(),\n completedAt: z.number().optional(),\n supersededAt: z.number().optional(),\n supersededReason: z.string().optional(),\n // [C-02] the erasure-vs-hold race fence (see ErasureLeaseV1). Absent until the\n // scrub first acquires the lease; thereafter it carries the live epoch + state.\n erasureLease: ErasureLeaseV1Schema.optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n })\n .strict();\nexport type AccountDeletionRequestV1 = z.infer<typeof AccountDeletionRequestV1Schema>;\n"]}
|
|
1
|
+
{"version":3,"file":"account-deletion.js","sourceRoot":"","sources":["../../src/doc-schemas/account-deletion.ts"],"names":[],"mappings":"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","sourcesContent":["// Account-deletion / GDPR-erasure request SCHEMA — `accountDeletionRequests/{uid}`.\n//\n// N3 data-deletion flow. A user clicks \"delete account\" on their profile → a\n// request doc is created (one per user, doc id = uid, create-if-absent) and the\n// account is immediately deactivated/locked out. The request runs a 30-day grace\n// window (logging back in CANCELS it). After the window, the scheduled scrub\n// worker checks for any BLOCKING safety hold over the user's data:\n// - no hold → anonymize-in-place (scrub PII, release reserved name, anonymize\n// chat history, tombstone memberships) → `completed`.\n// - hold → `parkedOnHold` (re-checked periodically): resumes + `completed`\n// once cleared, or → `superseded` if it becomes a ban-for-cause /\n// evidence-preservation outcome (the erasure is overridden and the\n// data retained as evidence).\n//\n// The scrub itself deletes/anonymizes data across many collections; this doc is\n// ONLY the request lifecycle + the worker's resumable cursor.\n//\n// [C-02] Erasure-vs-safety-hold race fence. The destructive scrub and a safety\n// hold landing mid-scrub race over the SAME account. The fence is an epoch LEASE\n// stamped on THIS doc (`erasureLease`): the scrub transactionally acquires the\n// lease ONLY when the account owns no active blocking hold; every destructive\n// write re-checks the SAME epoch in its own transaction; and `commitSafetyHold`\n// (the SINGLE shared hold authority) reads this doc's live lease and PARKS the\n// erasure (revoking the epoch) before committing a hold against a leased account.\n// A revoked epoch makes every still-running destructive step a no-op. The lease\n// lives HERE — not a parallel safety-only deletion subsystem — so the one shared\n// hold/destructive contract owns the fence (engineering-rules ARCH-001).\n\nimport { z } from 'zod';\n\n/** Lifecycle of an account-deletion request. */\nexport const AccountDeletionRequestStatusSchema = z.enum([\n 'pending', // grace window running; user can still cancel by logging in\n 'cancelled', // user cancelled (logged back in) before the window elapsed\n 'scrubbing', // the scrub worker has started the anonymize-in-place erasure\n // [C-02] the scrub holds an ACTIVE erasure lease and is mid-destruction. A hold\n // landing now revokes the lease (→ parkedOnHold) so the scrub aborts deterministically.\n 'leased',\n 'parkedOnHold', // erasure deferred — a blocking safety hold covers the user's data\n 'completed', // erasure finished\n 'superseded', // overridden by a ban-for-cause / evidence-preservation outcome\n]);\nexport type AccountDeletionRequestStatus = z.infer<typeof AccountDeletionRequestStatusSchema>;\n\n/**\n * ACTIVE = the request is still OPEN and still cancellable. This is the one predicate the\n * lifecycle's enforcement points share — `requestAccountDeletion`'s idempotent re-return of the\n * existing schedule, `cancelAccountDeletion`'s cancel gate, the scrub worker's due-request query,\n * and the client's live own-request read — each of which previously restated\n * `status === 'pending' || status === 'parkedOnHold'` locally (ARCH-102).\n *\n * Declared as an EXHAUSTIVE Record over the status union, not as a subset array: a subset array\n * only catches a REMOVED member, whereas adding a lifecycle status must be a compile error here\n * until that status is classified. A new status silently defaulting to \"not active\" is exactly the\n * drift this classification exists to prevent.\n *\n * `scrubbing` / `leased` are deliberately NOT active — the destructive erasure has already begun,\n * so there is no schedule left to re-offer and nothing left to cancel. `cancelled` / `completed` /\n * `superseded` are terminal.\n */\nconst DELETION_REQUEST_STATUS_IS_ACTIVE = {\n pending: true,\n cancelled: false,\n scrubbing: false,\n leased: false,\n parkedOnHold: true,\n completed: false,\n superseded: false,\n} as const satisfies Record<AccountDeletionRequestStatus, boolean>;\n\n/** The statuses classified ACTIVE above, as a literal union — derived, never restated. */\nexport type ActiveDeletionRequestStatus = {\n [K in AccountDeletionRequestStatus]: (typeof DELETION_REQUEST_STATUS_IS_ACTIVE)[K] extends true ? K : never;\n}[AccountDeletionRequestStatus];\n\n/** The ACTIVE (open, still-cancellable) deletion-request statuses, projected from the\n * classification above in union-declaration order — so it can also feed a Firestore\n * `where('status', 'in', ACTIVE_DELETION_REQUEST_STATUSES)` query directly. */\nexport const ACTIVE_DELETION_REQUEST_STATUSES: readonly ActiveDeletionRequestStatus[] = (\n Object.keys(DELETION_REQUEST_STATUS_IS_ACTIVE) as AccountDeletionRequestStatus[]\n).filter((status): status is ActiveDeletionRequestStatus => DELETION_REQUEST_STATUS_IS_ACTIVE[status]);\n\n/**\n * MID-SCRUB = the destructive erasure has STARTED and has not finished. These statuses are\n * neither active (nothing left to cancel or re-offer) nor terminal, so the scrub drain's\n * due-request query never sees them again — which is exactly why a request stranded here needs\n * its own recovery query (the stranded-erasure sweep over an expired `erasureLease`).\n *\n * Declared as an EXHAUSTIVE Record over the status union for the same reason the ACTIVE\n * classification is: adding a lifecycle status must be a compile error here until that status is\n * classified, rather than silently defaulting to \"not mid-scrub\" and dropping out of recovery.\n */\nconst DELETION_REQUEST_STATUS_IS_MID_SCRUB = {\n pending: false,\n cancelled: false,\n scrubbing: true,\n leased: true,\n parkedOnHold: false,\n completed: false,\n superseded: false,\n} as const satisfies Record<AccountDeletionRequestStatus, boolean>;\n\n/** The statuses classified MID-SCRUB above, as a literal union — derived, never restated. */\nexport type MidScrubDeletionRequestStatus = {\n [K in AccountDeletionRequestStatus]: (typeof DELETION_REQUEST_STATUS_IS_MID_SCRUB)[K] extends true ? K : never;\n}[AccountDeletionRequestStatus];\n\n/** The MID-SCRUB (erasure started, not finished) deletion-request statuses, projected from the\n * classification above in union-declaration order — so it can feed the stranded-erasure recovery\n * `where('status', 'in', MID_SCRUB_DELETION_REQUEST_STATUSES)` query directly. */\nexport const MID_SCRUB_DELETION_REQUEST_STATUSES: readonly MidScrubDeletionRequestStatus[] = (\n Object.keys(DELETION_REQUEST_STATUS_IS_MID_SCRUB) as AccountDeletionRequestStatus[]\n).filter((status): status is MidScrubDeletionRequestStatus => DELETION_REQUEST_STATUS_IS_MID_SCRUB[status]);\n\n/** True for an ACTIVE (open, still-cancellable) deletion request. Takes `unknown` because every\n * caller reads the status off an unvalidated Firestore doc: anything outside the canonical active\n * set — a terminal/mid-scrub status, an unrecognized value, a missing field — is false. */\nexport function isActiveDeletionRequest(status: unknown): status is ActiveDeletionRequestStatus {\n return typeof status === 'string' && (ACTIVE_DELETION_REQUEST_STATUSES as readonly string[]).includes(status);\n}\n\n/**\n * [C-02] The erasure LEASE / epoch token — the race fence between the destructive\n * scrub and a mid-scrub safety hold.\n *\n * The scrub acquires this transactionally ONLY when the account owns no active\n * blocking hold; it then re-verifies the SAME `epoch` inside every destructive\n * Firestore transaction and immediately before AND after each non-transactional\n * external delete (Auth/Storage). `commitSafetyHold` reads this lease and, when it\n * is `active`, REVOKES it (sets `revokedByHold`/`revokedAt`, status → `parkedOnHold`)\n * in the SAME transaction that commits the hold — so a hold deterministically wins\n * the race and the scrub, finding its epoch revoked on the next check, aborts into\n * a recoverable parked state without finishing the destruction.\n */\nexport const ErasureLeaseV1Schema = z\n .object({\n /** Monotonic acquisition token. The \"live lease\" identity: every destructive step\n * re-reads the doc and proceeds ONLY if this still equals the epoch it acquired. */\n epoch: z.number().int().nonnegative(),\n /** 'active' = the scrub holds the lease; 'revoked' = a hold reclaimed it mid-scrub\n * (the scrub must abort); 'released' = the scrub finished/relinquished cleanly. */\n state: z.enum(['active', 'revoked', 'released']),\n acquiredAt: z.number(),\n /** Staleness boundary. A still-`active` lease past this is presumed crashed and may\n * be safely superseded by a fresh acquisition (idempotent retry / crash recovery). */\n expiresAt: z.number(),\n /** Last destructive phase the scrub reached under this epoch (observability + recovery). */\n phase: z.string().optional(),\n /** Set when a hold revoked the lease — the caseId/refId of the winning hold (audit trail). */\n revokedByHoldRefId: z.string().min(1).optional(),\n revokedAt: z.number().optional(),\n releasedAt: z.number().optional(),\n })\n .strict();\nexport type ErasureLeaseV1 = z.infer<typeof ErasureLeaseV1Schema>;\n\n/** `accountDeletionRequests/{uid}` — one per user; doc id IS the uid. */\nexport const AccountDeletionRequestV1Schema = z\n .object({\n schemaVersion: z.literal(1),\n uid: z.string().min(1),\n status: AccountDeletionRequestStatusSchema,\n requestedAt: z.number(),\n // requestedAt + graceDays; the scrub worker queries status='pending' AND\n // scheduledScrubAt <= now (composite index, ttt-prod firestore.indexes.json).\n scheduledScrubAt: z.number(),\n graceDays: z.number(), // recorded for the audit/compliance trail (30 at launch)\n cancelledAt: z.number().optional(),\n // Token-revocation obligation — the request doc IS the durable ledger (BACKEND-302 /\n // QUALITY-104); no separate queue, worker collection, or dead-letter state exists.\n //\n // `tokenRevocationNextAttemptAt` is the DRAIN KEY: present ⇔ the revocation is still\n // OWED. Set to `now` when the request is created; the scheduled worker's revocation\n // drain queries `tokenRevocationNextAttemptAt <= now` (single-field range — deliberately\n // no status clause and no composite index; every terminal transition clears the field,\n // and the drain clears it on any non-active doc it meets, so \"owed\" means exactly\n // \"field present\"). A failed attempt moves it forward (bounded backoff); a successful\n // revoke DELETES it and stamps `tokensRevokedAt` in the same fenced update.\n //\n // The stamp is GENERATION-FENCED on `requestedAt`: the revoker snapshots `requestedAt`\n // before calling Auth and stamps transactionally only if the doc still carries that\n // value with an active status — a delayed revoke from a cancelled request must never\n // mark a newly reopened request satisfied. Revocation is idempotent, so a skipped or\n // lost stamp merely causes one redundant re-revoke.\n tokenRevocationNextAttemptAt: z.number().optional(),\n // When the user's refresh tokens were revoked for THIS request (\"account deletion signs\n // the user out of every session everywhere\"). The callable's immediate revoke is only\n // the accelerator (BACKEND-301); the worker drain above is the guarantee.\n tokensRevokedAt: z.number().optional(),\n scrubStartedAt: z.number().optional(),\n // Why a scrub is parked (e.g. 'blocking safety hold over user data') — kept for\n // the GDPR compliance record so a deferred erasure is explainable.\n parkedReason: z.string().optional(),\n parkedAt: z.number().optional(),\n lastHoldCheckAt: z.number().optional(),\n completedAt: z.number().optional(),\n supersededAt: z.number().optional(),\n supersededReason: z.string().optional(),\n // [C-02] the erasure-vs-hold race fence (see ErasureLeaseV1). Absent until the\n // scrub first acquires the lease; thereafter it carries the live epoch + state.\n erasureLease: ErasureLeaseV1Schema.optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n })\n .strict();\nexport type AccountDeletionRequestV1 = z.infer<typeof AccountDeletionRequestV1Schema>;\n"]}
|
|
@@ -21,6 +21,29 @@ export declare const SafetyReconcilerCursorSchema: z.ZodObject<{
|
|
|
21
21
|
updatedAt: z.ZodNumber;
|
|
22
22
|
}, z.core.$strip>;
|
|
23
23
|
export type SafetyReconcilerCursor = z.infer<typeof SafetyReconcilerCursorSchema>;
|
|
24
|
+
export declare const SweepStateSchema: z.ZodObject<{
|
|
25
|
+
reaperLastRunAt: z.ZodOptional<z.ZodNumber>;
|
|
26
|
+
reaperCursorPath: z.ZodOptional<z.ZodString>;
|
|
27
|
+
fullScanLastRunAt: z.ZodOptional<z.ZodNumber>;
|
|
28
|
+
updatedAt: z.ZodNumber;
|
|
29
|
+
}, z.core.$strip>;
|
|
30
|
+
export type SweepState = z.infer<typeof SweepStateSchema>;
|
|
31
|
+
export declare const NcmecCompletionProofRecordV1Schema: z.ZodObject<{
|
|
32
|
+
caseId: z.ZodString;
|
|
33
|
+
submissionId: z.ZodString;
|
|
34
|
+
channel: z.ZodEnum<{
|
|
35
|
+
manualPortal: "manualPortal";
|
|
36
|
+
}>;
|
|
37
|
+
proofType: z.ZodEnum<{
|
|
38
|
+
portalConfirmation: "portalConfirmation";
|
|
39
|
+
}>;
|
|
40
|
+
portalReceiptArtifactId: z.ZodString;
|
|
41
|
+
proofText: z.ZodOptional<z.ZodString>;
|
|
42
|
+
ncmecReportId: z.ZodString;
|
|
43
|
+
recordedByUid: z.ZodString;
|
|
44
|
+
recordedAt: z.ZodNumber;
|
|
45
|
+
}, z.core.$strip>;
|
|
46
|
+
export type NcmecCompletionProofRecordV1 = z.infer<typeof NcmecCompletionProofRecordV1Schema>;
|
|
24
47
|
export declare const NcmecPortalReceiptArtifactV1Schema: z.ZodObject<{
|
|
25
48
|
schemaVersion: z.ZodLiteral<1>;
|
|
26
49
|
caseId: z.ZodString;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend-state.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/backend-state.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"backend-state.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/backend-state.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,eAAO,MAAM,2BAA2B;;;iBAGtC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAOhF,eAAO,MAAM,oBAAoB;;;;;;;;;;iBAS/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAMlE,eAAO,MAAM,4BAA4B;;;iBAIvC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AASlF,eAAO,MAAM,gBAAgB;;;;;iBAS3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAU1D,eAAO,MAAM,kCAAkC;;;;;;;;;;;;;;iBAc7C,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAS9F,eAAO,MAAM,kCAAkC;;;;;;;;;;;;iBAgB7C,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAQ9F,eAAO,MAAM,mCAAmC;;;;;;;;iBAU9C,CAAC;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mCAAmC,CAAC,CAAC"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// the path ONE owner, and COLLECTION_SCHEMAS is the inventory the schema-doc generator and
|
|
5
5
|
// the drift-check read. Types are inferred via z.infer.
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
+
import { ChildSafetyNcmecCompletionChannelSchema, ChildSafetyNcmecCompletionProofTypeSchema, } from './safety/case.js';
|
|
7
8
|
// _systemData/hallMediaReaperCursor — the scheduled Hall-media orphan reaper's scan cursor.
|
|
8
9
|
// `createdAtCursor` is the highest mediaAssets `createdAt` the reaper has POSITIVELY cleared
|
|
9
10
|
// (candidate resolved referenced / reaped / already-deleted), so each pass resumes past
|
|
@@ -37,6 +38,46 @@ export const SafetyReconcilerCursorSchema = z.object({
|
|
|
37
38
|
afterValue: z.number().optional(),
|
|
38
39
|
updatedAt: z.number(),
|
|
39
40
|
});
|
|
41
|
+
// sweepState/{sweepName} — durable cadence + cursor state for one scheduled user sweep. One doc
|
|
42
|
+
// per sweep name; the fields a given sweep uses are the ones it declares, so every field except
|
|
43
|
+
// `updatedAt` is optional. `orphanRegistrationCleanup` carries all three (the ~daily privateData
|
|
44
|
+
// reaper's last completed pass + its rolling page cursor, and the ~weekly `listUsers()` scan);
|
|
45
|
+
// `reconcileAccountStatus` carries only `fullScanLastRunAt`. The stamps are wall-clock so a
|
|
46
|
+
// container recycle cannot reset the cadence.
|
|
47
|
+
// (functions/src/users/orphanRegistrationCleanup.ts, functions/src/users/reconcileAccountStatus.ts)
|
|
48
|
+
export const SweepStateSchema = z.object({
|
|
49
|
+
/** Wall-clock ms the last COMPLETED privateData-reaper pass finished (absent = never). */
|
|
50
|
+
reaperLastRunAt: z.number().optional(),
|
|
51
|
+
/** Full document path of the last `privateData` doc the reaper examined. Empty/absent means no
|
|
52
|
+
* pass is in flight; a non-empty value means one is mid-scan and must continue. */
|
|
53
|
+
reaperCursorPath: z.string().optional(),
|
|
54
|
+
/** Wall-clock ms the last full `listUsers()` scan ran (absent = never). */
|
|
55
|
+
fullScanLastRunAt: z.number().optional(),
|
|
56
|
+
updatedAt: z.number(),
|
|
57
|
+
});
|
|
58
|
+
// childSafetyCases/{caseId}/ncmecSubmissions/{submissionId}/ncmecCompletionProof/record —
|
|
59
|
+
// [H-05] the IMMUTABLE record of a verified manual-portal completion. Written create-if-absent in
|
|
60
|
+
// the SAME transaction as the submission's `completed` transition, so a submission can never reach
|
|
61
|
+
// `completed` without a bound proof record; a re-completion keeps the original. The submission's
|
|
62
|
+
// `completionProofRef` stores this doc's path, never an operator free-text string.
|
|
63
|
+
// [Q13/H-06] `portalReceiptArtifactId` binds the proof to a verified NcmecPortalReceiptArtifactV1
|
|
64
|
+
// on the same (caseId, submissionId) — an arbitrary operator string is never accepted.
|
|
65
|
+
// (functions/src/safety/ncmecReporting.ts)
|
|
66
|
+
export const NcmecCompletionProofRecordV1Schema = z.object({
|
|
67
|
+
caseId: z.string(),
|
|
68
|
+
submissionId: z.string(),
|
|
69
|
+
channel: ChildSafetyNcmecCompletionChannelSchema,
|
|
70
|
+
proofType: ChildSafetyNcmecCompletionProofTypeSchema,
|
|
71
|
+
/** Id of the verified portal-receipt artifact this completion is bound to. */
|
|
72
|
+
portalReceiptArtifactId: z.string(),
|
|
73
|
+
/** Optional operator free-text note about the confirmation. */
|
|
74
|
+
proofText: z.string().optional(),
|
|
75
|
+
/** The NCMEC-assigned report id — REQUIRED. The number IS the proof; there is no
|
|
76
|
+
* "filed, number pending" grace. */
|
|
77
|
+
ncmecReportId: z.string(),
|
|
78
|
+
recordedByUid: z.string(),
|
|
79
|
+
recordedAt: z.number(),
|
|
80
|
+
});
|
|
40
81
|
// childSafetyCases/{caseId}/portalReceiptArtifacts/{artifactId} — [Q13/H-06] the immutable
|
|
41
82
|
// record of a PDF/screenshot of the completed NCMEC CyberTipline submission, stored in the
|
|
42
83
|
// restricted evidence vault. `markNcmecPortalComplete` accepts only the id of a verified
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend-state.js","sourceRoot":"","sources":["../../src/doc-schemas/backend-state.ts"],"names":[],"mappings":"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","sourcesContent":["// Backend-only state document SCHEMAS — Cloud-Functions-owned bookkeeping docs that no\n// client ever reads or writes (firestore.rules denies client access to every path below).\n// They are registered here for the same reason every other stored shape is: ARCH-104 gives\n// the path ONE owner, and COLLECTION_SCHEMAS is the inventory the schema-doc generator and\n// the drift-check read. Types are inferred via z.infer.\n\nimport { z } from 'zod';\n\n// _systemData/hallMediaReaperCursor — the scheduled Hall-media orphan reaper's scan cursor.\n// `createdAtCursor` is the highest mediaAssets `createdAt` the reaper has POSITIVELY cleared\n// (candidate resolved referenced / reaped / already-deleted), so each pass resumes past\n// permanently-live Hall assets instead of re-reading the same oldest page forever.\n// (functions/src/media/reapOrphanedHallMediaCopies.ts)\nexport const HallMediaReaperCursorSchema = z.object({\n createdAtCursor: z.number(),\n updatedAt: z.number(),\n});\nexport type HallMediaReaperCursor = z.infer<typeof HallMediaReaperCursorSchema>;\n\n// operatorStepUp/{uid} — [H-08] per-operator TOTP step-up state: the authenticator secret\n// plus the currently open grant window. The secret is returned to the client EXACTLY ONCE by\n// enrollOperatorStepUp and is never readable through Firestore afterwards; `status` goes\n// 'pending' → 'active' on confirm, and each successful verify pushes `grantExpiresAt` out by\n// the step-up window. (functions/src/safety/operatorStepUp.ts)\nexport const OperatorStepUpSchema = z.object({\n secret: z.string(),\n status: z.enum(['pending', 'active']),\n /** Absent until the first successful verify; a grant is live while now < grantExpiresAt. */\n grantExpiresAt: z.number().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n /** Set when enrollment is confirmed (status flips to 'active'). */\n confirmedAt: z.number().optional(),\n});\nexport type OperatorStepUp = z.infer<typeof OperatorStepUpSchema>;\n\n// safetyReconcilerCursors/{cursorKey} — one persisted pagination cursor per reconciler\n// needs-work backstop sweep (quarantine enqueue, NCMEC enqueue). The sweep pages a\n// needs-work-filtered query from `afterValue` and WRAPS to the start when exhausted, so\n// coverage does not stall on the oldest rows. (functions/src/safety/reconcilerCursor.ts)\nexport const SafetyReconcilerCursorSchema = z.object({\n /** `orderBy` value of the last row examined last pass (exclusive lower bound). Absent ⇒ 0. */\n afterValue: z.number().optional(),\n updatedAt: z.number(),\n});\nexport type SafetyReconcilerCursor = z.infer<typeof SafetyReconcilerCursorSchema>;\n\n// childSafetyCases/{caseId}/portalReceiptArtifacts/{artifactId} — [Q13/H-06] the immutable\n// record of a PDF/screenshot of the completed NCMEC CyberTipline submission, stored in the\n// restricted evidence vault. `markNcmecPortalComplete` accepts only the id of a verified\n// artifact bound to the SAME (caseId, submissionId); an arbitrary operator string is never\n// proof. Written create-only, never overwritten, and carries the server-verified storage facts\n// (generation + sha256) so a later transactional verification can confirm the same object\n// still exists unchanged. (functions/src/safety/ncmecOperatorCommands.ts)\nexport const NcmecPortalReceiptArtifactV1Schema = z.object({\n schemaVersion: z.literal(1),\n caseId: z.string(),\n submissionId: z.string(),\n /** Key of the object in the restricted evidence vault bucket. */\n evidenceVaultKey: z.string(),\n /** GCS object generation at capture time (immutable identity peg). */\n objectGeneration: z.string(),\n /** SHA-256 hex digest of the object bytes at capture time. */\n sha256: z.string(),\n contentType: z.string(),\n sizeBytes: z.number(),\n capturedAt: z.number(),\n registeredByUid: z.string(),\n /** Optional operator free-text describing what the artifact is. */\n description: z.string().optional(),\n});\nexport type NcmecPortalReceiptArtifactV1 = z.infer<typeof NcmecPortalReceiptArtifactV1Schema>;\n\n// childSafetyCases/{caseId}/ncmecPortalCorrections/{correctionId} — the immutable record that\n// the operator filed a CORRECTION on the NCMEC manual portal. There is no in-app correction\n// submission pipeline; this records the portal filing. Its EXISTENCE is the gate on the\n// corrected-no-apparent-violation disposition, so it is never a bare-discretion close. Each\n// correction is a fresh id — a later correction is an additional record, never a mutation of\n// the prior one. (functions/src/safety/recordNcmecPortalCorrection.ts)\nexport const NcmecPortalCorrectionRecordV1Schema = z.object({\n schemaVersion: z.literal(1),\n caseId: z.string(),\n /** The portal-assigned NCMEC report id being corrected. */\n ncmecReportId: z.string(),\n /** When the operator filed the correction on the portal (operator-supplied). */\n correctionFiledAt: z.number(),\n reason: z.string(),\n recordedByUid: z.string(),\n recordedAt: z.number(),\n});\nexport type NcmecPortalCorrectionRecordV1 = z.infer<typeof NcmecPortalCorrectionRecordV1Schema>;\n"]}
|
|
1
|
+
{"version":3,"file":"backend-state.js","sourceRoot":"","sources":["../../src/doc-schemas/backend-state.ts"],"names":[],"mappings":"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","sourcesContent":["// Backend-only state document SCHEMAS — Cloud-Functions-owned bookkeeping docs that no\n// client ever reads or writes (firestore.rules denies client access to every path below).\n// They are registered here for the same reason every other stored shape is: ARCH-104 gives\n// the path ONE owner, and COLLECTION_SCHEMAS is the inventory the schema-doc generator and\n// the drift-check read. Types are inferred via z.infer.\n\nimport { z } from 'zod';\nimport {\n ChildSafetyNcmecCompletionChannelSchema,\n ChildSafetyNcmecCompletionProofTypeSchema,\n} from './safety/case.js';\n\n// _systemData/hallMediaReaperCursor — the scheduled Hall-media orphan reaper's scan cursor.\n// `createdAtCursor` is the highest mediaAssets `createdAt` the reaper has POSITIVELY cleared\n// (candidate resolved referenced / reaped / already-deleted), so each pass resumes past\n// permanently-live Hall assets instead of re-reading the same oldest page forever.\n// (functions/src/media/reapOrphanedHallMediaCopies.ts)\nexport const HallMediaReaperCursorSchema = z.object({\n createdAtCursor: z.number(),\n updatedAt: z.number(),\n});\nexport type HallMediaReaperCursor = z.infer<typeof HallMediaReaperCursorSchema>;\n\n// operatorStepUp/{uid} — [H-08] per-operator TOTP step-up state: the authenticator secret\n// plus the currently open grant window. The secret is returned to the client EXACTLY ONCE by\n// enrollOperatorStepUp and is never readable through Firestore afterwards; `status` goes\n// 'pending' → 'active' on confirm, and each successful verify pushes `grantExpiresAt` out by\n// the step-up window. (functions/src/safety/operatorStepUp.ts)\nexport const OperatorStepUpSchema = z.object({\n secret: z.string(),\n status: z.enum(['pending', 'active']),\n /** Absent until the first successful verify; a grant is live while now < grantExpiresAt. */\n grantExpiresAt: z.number().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n /** Set when enrollment is confirmed (status flips to 'active'). */\n confirmedAt: z.number().optional(),\n});\nexport type OperatorStepUp = z.infer<typeof OperatorStepUpSchema>;\n\n// safetyReconcilerCursors/{cursorKey} — one persisted pagination cursor per reconciler\n// needs-work backstop sweep (quarantine enqueue, NCMEC enqueue). The sweep pages a\n// needs-work-filtered query from `afterValue` and WRAPS to the start when exhausted, so\n// coverage does not stall on the oldest rows. (functions/src/safety/reconcilerCursor.ts)\nexport const SafetyReconcilerCursorSchema = z.object({\n /** `orderBy` value of the last row examined last pass (exclusive lower bound). Absent ⇒ 0. */\n afterValue: z.number().optional(),\n updatedAt: z.number(),\n});\nexport type SafetyReconcilerCursor = z.infer<typeof SafetyReconcilerCursorSchema>;\n\n// sweepState/{sweepName} — durable cadence + cursor state for one scheduled user sweep. One doc\n// per sweep name; the fields a given sweep uses are the ones it declares, so every field except\n// `updatedAt` is optional. `orphanRegistrationCleanup` carries all three (the ~daily privateData\n// reaper's last completed pass + its rolling page cursor, and the ~weekly `listUsers()` scan);\n// `reconcileAccountStatus` carries only `fullScanLastRunAt`. The stamps are wall-clock so a\n// container recycle cannot reset the cadence.\n// (functions/src/users/orphanRegistrationCleanup.ts, functions/src/users/reconcileAccountStatus.ts)\nexport const SweepStateSchema = z.object({\n /** Wall-clock ms the last COMPLETED privateData-reaper pass finished (absent = never). */\n reaperLastRunAt: z.number().optional(),\n /** Full document path of the last `privateData` doc the reaper examined. Empty/absent means no\n * pass is in flight; a non-empty value means one is mid-scan and must continue. */\n reaperCursorPath: z.string().optional(),\n /** Wall-clock ms the last full `listUsers()` scan ran (absent = never). */\n fullScanLastRunAt: z.number().optional(),\n updatedAt: z.number(),\n});\nexport type SweepState = z.infer<typeof SweepStateSchema>;\n\n// childSafetyCases/{caseId}/ncmecSubmissions/{submissionId}/ncmecCompletionProof/record —\n// [H-05] the IMMUTABLE record of a verified manual-portal completion. Written create-if-absent in\n// the SAME transaction as the submission's `completed` transition, so a submission can never reach\n// `completed` without a bound proof record; a re-completion keeps the original. The submission's\n// `completionProofRef` stores this doc's path, never an operator free-text string.\n// [Q13/H-06] `portalReceiptArtifactId` binds the proof to a verified NcmecPortalReceiptArtifactV1\n// on the same (caseId, submissionId) — an arbitrary operator string is never accepted.\n// (functions/src/safety/ncmecReporting.ts)\nexport const NcmecCompletionProofRecordV1Schema = z.object({\n caseId: z.string(),\n submissionId: z.string(),\n channel: ChildSafetyNcmecCompletionChannelSchema,\n proofType: ChildSafetyNcmecCompletionProofTypeSchema,\n /** Id of the verified portal-receipt artifact this completion is bound to. */\n portalReceiptArtifactId: z.string(),\n /** Optional operator free-text note about the confirmation. */\n proofText: z.string().optional(),\n /** The NCMEC-assigned report id — REQUIRED. The number IS the proof; there is no\n * \"filed, number pending\" grace. */\n ncmecReportId: z.string(),\n recordedByUid: z.string(),\n recordedAt: z.number(),\n});\nexport type NcmecCompletionProofRecordV1 = z.infer<typeof NcmecCompletionProofRecordV1Schema>;\n\n// childSafetyCases/{caseId}/portalReceiptArtifacts/{artifactId} — [Q13/H-06] the immutable\n// record of a PDF/screenshot of the completed NCMEC CyberTipline submission, stored in the\n// restricted evidence vault. `markNcmecPortalComplete` accepts only the id of a verified\n// artifact bound to the SAME (caseId, submissionId); an arbitrary operator string is never\n// proof. Written create-only, never overwritten, and carries the server-verified storage facts\n// (generation + sha256) so a later transactional verification can confirm the same object\n// still exists unchanged. (functions/src/safety/ncmecOperatorCommands.ts)\nexport const NcmecPortalReceiptArtifactV1Schema = z.object({\n schemaVersion: z.literal(1),\n caseId: z.string(),\n submissionId: z.string(),\n /** Key of the object in the restricted evidence vault bucket. */\n evidenceVaultKey: z.string(),\n /** GCS object generation at capture time (immutable identity peg). */\n objectGeneration: z.string(),\n /** SHA-256 hex digest of the object bytes at capture time. */\n sha256: z.string(),\n contentType: z.string(),\n sizeBytes: z.number(),\n capturedAt: z.number(),\n registeredByUid: z.string(),\n /** Optional operator free-text describing what the artifact is. */\n description: z.string().optional(),\n});\nexport type NcmecPortalReceiptArtifactV1 = z.infer<typeof NcmecPortalReceiptArtifactV1Schema>;\n\n// childSafetyCases/{caseId}/ncmecPortalCorrections/{correctionId} — the immutable record that\n// the operator filed a CORRECTION on the NCMEC manual portal. There is no in-app correction\n// submission pipeline; this records the portal filing. Its EXISTENCE is the gate on the\n// corrected-no-apparent-violation disposition, so it is never a bare-discretion close. Each\n// correction is a fresh id — a later correction is an additional record, never a mutation of\n// the prior one. (functions/src/safety/recordNcmecPortalCorrection.ts)\nexport const NcmecPortalCorrectionRecordV1Schema = z.object({\n schemaVersion: z.literal(1),\n caseId: z.string(),\n /** The portal-assigned NCMEC report id being corrected. */\n ncmecReportId: z.string(),\n /** When the operator filed the correction on the portal (operator-supplied). */\n correctionFiledAt: z.number(),\n reason: z.string(),\n recordedByUid: z.string(),\n recordedAt: z.number(),\n});\nexport type NcmecPortalCorrectionRecordV1 = z.infer<typeof NcmecPortalCorrectionRecordV1Schema>;\n"]}
|
|
@@ -30,7 +30,6 @@ export * from './safety/provenance.js';
|
|
|
30
30
|
export * from './safety/sagas.js';
|
|
31
31
|
export * from './safety/monitors.js';
|
|
32
32
|
export * from './safety/age.js';
|
|
33
|
-
export * from './safety/reviewer-security.js';
|
|
34
33
|
export * from './ncii/allegations.js';
|
|
35
34
|
export * from './ncii/requests.js';
|
|
36
35
|
export * from './ncii/cases.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/index.ts"],"names":[],"mappings":"AAMA,cAAc,WAAW,CAAC;AAC1B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AAGjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/index.ts"],"names":[],"mappings":"AAMA,cAAc,WAAW,CAAC;AAC1B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AAGjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,oBAAoB,CAAC;AAEnC,cAAc,eAAe,CAAC"}
|
|
@@ -36,7 +36,6 @@ export * from './safety/provenance.js';
|
|
|
36
36
|
export * from './safety/sagas.js';
|
|
37
37
|
export * from './safety/monitors.js';
|
|
38
38
|
export * from './safety/age.js';
|
|
39
|
-
export * from './safety/reviewer-security.js';
|
|
40
39
|
// ===== Trust & Safety — NCII / TAKE IT DOWN (§A11) =====
|
|
41
40
|
export * from './ncii/allegations.js';
|
|
42
41
|
export * from './ncii/requests.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/doc-schemas/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,sFAAsF;AACtF,sFAAsF;AACtF,wFAAwF;AACxF,mFAAmF;AAEnF,cAAc,WAAW,CAAC;AAC1B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AAEjC,6EAA6E;AAC7E,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/doc-schemas/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,sFAAsF;AACtF,sFAAsF;AACtF,wFAAwF;AACxF,mFAAmF;AAEnF,cAAc,WAAW,CAAC;AAC1B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AAEjC,6EAA6E;AAC7E,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAEhC,0DAA0D;AAC1D,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AAEjC,sDAAsD;AACtD,cAAc,oBAAoB,CAAC;AAEnC,cAAc,eAAe,CAAC","sourcesContent":["// Barrel for the Firestore document SCHEMAS (Zod) and the collection registry.\n// Exposed on the `@ttt-productions/ttt-core/doc-schemas` subpath. The schema registry\n// (COLLECTION_SCHEMAS) is consumed by the schema-doc generator (ttt-packages) and the\n// drift-check (ttt-prod). Document TYPES are inferred from these schemas and surface on\n// the main barrel via ../types/*; this subpath is the home of the runtime schemas.\n\nexport * from './user.js';\nexport * from './firestore-primitives.js';\nexport * from './media-assets.js';\nexport * from './media-activation-jobs.js';\nexport * from './publicUser.js';\nexport * from './account-deletion.js';\nexport * from './work-project.js';\nexport * from './content.js';\nexport * from './social.js';\nexport * from './square-announcement-jobs.js';\nexport * from './payments.js';\nexport * from './commissions.js';\nexport * from './messaging.js';\nexport * from './system.js';\nexport * from './moderation.js';\nexport * from './report-docs.js';\nexport * from './audit.js';\nexport * from './notifications.js';\nexport * from './notification-ledger.js';\nexport * from './chat-sync.js';\nexport * from './operational.js';\n\n// ===== Trust & Safety — shared foundation + cluster schemas (§A1–A11) =====\nexport * from './safety/foundation.js';\nexport * from './safety/report.js';\nexport * from './safety/case-alert.js';\nexport * from './safety/case.js';\nexport * from './safety/case-aliases.js';\nexport * from './safety/holds.js';\nexport * from './safety/evidence.js';\nexport * from './safety/provenance.js';\nexport * from './safety/sagas.js';\nexport * from './safety/monitors.js';\nexport * from './safety/age.js';\n\n// ===== Trust & Safety — NCII / TAKE IT DOWN (§A11) =====\nexport * from './ncii/allegations.js';\nexport * from './ncii/requests.js';\nexport * from './ncii/cases.js';\nexport * from './ncii/holds.js';\nexport * from './ncii/removal.js';\nexport * from './ncii/config.js';\n\n// ===== Backend-only cursor / operational state =====\nexport * from './backend-state.js';\n\nexport * from './registry.js';\n"]}
|
|
@@ -159,6 +159,16 @@ export declare const MediaRetentionPolicySchema: z.ZodEnum<{
|
|
|
159
159
|
legalHold: "legalHold";
|
|
160
160
|
}>;
|
|
161
161
|
export type MediaRetentionPolicy = z.infer<typeof MediaRetentionPolicySchema>;
|
|
162
|
+
/** Not shared to the realm — the default standing for every work file. */
|
|
163
|
+
export declare const REALM_FILE_NONE_STATUS: "none";
|
|
164
|
+
/** The single AWAITING-DECISION standing. Exported as a VALUE as well as a schema so a
|
|
165
|
+
* consumer can branch on it (`status === REALM_FILE_PENDING_APPROVAL_STATUS`) without
|
|
166
|
+
* re-quoting the member — the redeclaration guard pins this literal to this file. */
|
|
167
|
+
export declare const REALM_FILE_PENDING_APPROVAL_STATUS: "pendingApproval";
|
|
168
|
+
/** Approved into the realm's shared pool, not marked canon. */
|
|
169
|
+
export declare const REALM_FILE_NON_CANON_STATUS: "nonCanon";
|
|
170
|
+
/** Approved into the realm's shared pool AND marked canon by the Realm steward. */
|
|
171
|
+
export declare const REALM_FILE_CANON_STATUS: "canon";
|
|
162
172
|
export declare const REALM_FILE_CANON_STATUS_VALUES: readonly ["none", "pendingApproval", "nonCanon", "canon"];
|
|
163
173
|
export declare const RealmFileCanonStatusSchema: z.ZodEnum<{
|
|
164
174
|
none: "none";
|
|
@@ -177,10 +187,6 @@ export declare const RealmFileApprovedStatusSchema: z.ZodEnum<{
|
|
|
177
187
|
canon: "canon";
|
|
178
188
|
}>;
|
|
179
189
|
export type RealmFileApprovedStatus = z.infer<typeof RealmFileApprovedStatusSchema>;
|
|
180
|
-
/** The single AWAITING-DECISION standing. Exported as a VALUE as well as a schema so a
|
|
181
|
-
* consumer can branch on it (`status === REALM_FILE_PENDING_APPROVAL_STATUS`) without
|
|
182
|
-
* re-quoting the member — the redeclaration guard pins this literal to this file. */
|
|
183
|
-
export declare const REALM_FILE_PENDING_APPROVAL_STATUS: "pendingApproval";
|
|
184
190
|
/** The steward promotion-queue projection accepts only this, so an approved or unshared row
|
|
185
191
|
* can never be typed into the queue. */
|
|
186
192
|
export declare const RealmFilePendingApprovalStatusSchema: z.ZodLiteral<"pendingApproval">;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-assets.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/media-assets.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,6DAA6D;AAC7D,eAAO,MAAM,qBAAqB;;;;;EAAsD,CAAC;AACzF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;4DAC4D;AAC5D,eAAO,MAAM,wBAAwB;;;;;EAA2D,CAAC;AACjG,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E;;uCAEuC;AACvC,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;EAsBpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAM5E;;uFAEuF;AACvF,eAAO,MAAM,sBAAsB;;;;EAAsC,CAAC;AAC1E,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,8CAA+C,CAAC;AAC/E,eAAO,MAAM,qBAAqB;;;;;EAA6B,CAAC;AAChE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;iCAKiC;AACjC,eAAO,MAAM,qBAAqB;;;;;;;;EAQhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA0B5B,CAAC;AACZ,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,eAAO,MAAM,uBAAuB;;;;;;;kBAyBzB,CAAC;AACZ,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,0BAA0B;;;;EAIrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"media-assets.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/media-assets.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,6DAA6D;AAC7D,eAAO,MAAM,qBAAqB;;;;;EAAsD,CAAC;AACzF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;4DAC4D;AAC5D,eAAO,MAAM,wBAAwB;;;;;EAA2D,CAAC;AACjG,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E;;uCAEuC;AACvC,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;EAsBpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAM5E;;uFAEuF;AACvF,eAAO,MAAM,sBAAsB;;;;EAAsC,CAAC;AAC1E,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,8CAA+C,CAAC;AAC/E,eAAO,MAAM,qBAAqB;;;;;EAA6B,CAAC;AAChE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;iCAKiC;AACjC,eAAO,MAAM,qBAAqB;;;;;;;;EAQhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA0B5B,CAAC;AACZ,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,eAAO,MAAM,uBAAuB;;;;;;;kBAyBzB,CAAC;AACZ,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,0BAA0B;;;;EAIrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAkB9E,0EAA0E;AAC1E,eAAO,MAAM,sBAAsB,EAAG,MAAe,CAAC;AACtD;;sFAEsF;AACtF,eAAO,MAAM,kCAAkC,EAAG,iBAA0B,CAAC;AAC7E,+DAA+D;AAC/D,eAAO,MAAM,2BAA2B,EAAG,UAAmB,CAAC;AAC/D,mFAAmF;AACnF,eAAO,MAAM,uBAAuB,EAAG,OAAgB,CAAC;AAExD,eAAO,MAAM,8BAA8B,2DAKjC,CAAC;AACX,eAAO,MAAM,0BAA0B;;;;;EAAyC,CAAC;AACjF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E;;;uFAGuF;AACvF,eAAO,MAAM,iCAAiC,gCAGpC,CAAC;AACX,eAAO,MAAM,6BAA6B;;;EAA4C,CAAC;AACvF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF;yCACyC;AACzC,eAAO,MAAM,oCAAoC,iCAAgD,CAAC;AAClG,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oCAAoC,CAAC,CAAC;AAElG;;;kEAGkE;AAClE,eAAO,MAAM,+BAA+B,mDAGlC,CAAC;AACX,eAAO,MAAM,2BAA2B;;;;EAA0C,CAAC;AACnF,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAMhF,iFAAiF;AACjF,eAAO,MAAM,6BAA6B;;;;EAA+C,CAAC;AAC1F,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF;iFACiF;AACjF,eAAO,MAAM,gCAAgC;;;;EAAkD,CAAC;AAChG,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAE1F;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;4BAkBlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;EAoBrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E;;;;;;GAMG;AACH,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAiBnC,CAAC;AACZ,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAE5F,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAsI3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|
|
@@ -148,23 +148,35 @@ export const MediaRetentionPolicySchema = z.enum([
|
|
|
148
148
|
// canon — approved and marked canon (realm steward toggles canon ↔ nonCanon)
|
|
149
149
|
// Moderation/takedown is NOT a value here — a hidden file is hidden via the shared
|
|
150
150
|
// media path (`servingStatus: 'hidden'`), so the gallery filters on servingStatus.
|
|
151
|
+
//
|
|
152
|
+
// Each member is declared ONCE as a named constant below and every set is assembled from
|
|
153
|
+
// those names, so a member constant and the sets that contain it cannot drift apart.
|
|
154
|
+
/** Not shared to the realm — the default standing for every work file. */
|
|
155
|
+
export const REALM_FILE_NONE_STATUS = 'none';
|
|
156
|
+
/** The single AWAITING-DECISION standing. Exported as a VALUE as well as a schema so a
|
|
157
|
+
* consumer can branch on it (`status === REALM_FILE_PENDING_APPROVAL_STATUS`) without
|
|
158
|
+
* re-quoting the member — the redeclaration guard pins this literal to this file. */
|
|
159
|
+
export const REALM_FILE_PENDING_APPROVAL_STATUS = 'pendingApproval';
|
|
160
|
+
/** Approved into the realm's shared pool, not marked canon. */
|
|
161
|
+
export const REALM_FILE_NON_CANON_STATUS = 'nonCanon';
|
|
162
|
+
/** Approved into the realm's shared pool AND marked canon by the Realm steward. */
|
|
163
|
+
export const REALM_FILE_CANON_STATUS = 'canon';
|
|
151
164
|
export const REALM_FILE_CANON_STATUS_VALUES = [
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
165
|
+
REALM_FILE_NONE_STATUS,
|
|
166
|
+
REALM_FILE_PENDING_APPROVAL_STATUS,
|
|
167
|
+
REALM_FILE_NON_CANON_STATUS,
|
|
168
|
+
REALM_FILE_CANON_STATUS,
|
|
156
169
|
];
|
|
157
170
|
export const RealmFileCanonStatusSchema = z.enum(REALM_FILE_CANON_STATUS_VALUES);
|
|
158
171
|
/** The two APPROVED (steward-accepted, realm-served) standings. The member gallery query
|
|
159
172
|
* and its projection accept exactly these — never the full status union, so a pending or
|
|
160
173
|
* unshared row can never be typed into an approved-files response. Derived from the one
|
|
161
174
|
* union above; consumers import this instead of re-quoting the members (ARCH-102). */
|
|
162
|
-
export const REALM_FILE_APPROVED_STATUS_VALUES = [
|
|
175
|
+
export const REALM_FILE_APPROVED_STATUS_VALUES = [
|
|
176
|
+
REALM_FILE_NON_CANON_STATUS,
|
|
177
|
+
REALM_FILE_CANON_STATUS,
|
|
178
|
+
];
|
|
163
179
|
export const RealmFileApprovedStatusSchema = z.enum(REALM_FILE_APPROVED_STATUS_VALUES);
|
|
164
|
-
/** The single AWAITING-DECISION standing. Exported as a VALUE as well as a schema so a
|
|
165
|
-
* consumer can branch on it (`status === REALM_FILE_PENDING_APPROVAL_STATUS`) without
|
|
166
|
-
* re-quoting the member — the redeclaration guard pins this literal to this file. */
|
|
167
|
-
export const REALM_FILE_PENDING_APPROVAL_STATUS = 'pendingApproval';
|
|
168
180
|
/** The steward promotion-queue projection accepts only this, so an approved or unshared row
|
|
169
181
|
* can never be typed into the queue. */
|
|
170
182
|
export const RealmFilePendingApprovalStatusSchema = z.literal(REALM_FILE_PENDING_APPROVAL_STATUS);
|