@ttt-productions/ttt-core 0.79.53 → 0.79.54
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/constants/index.d.ts +1 -0
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/constants/index.js +1 -0
- package/dist/constants/index.js.map +1 -1
- package/dist/constants/pledge-and-bouquet-copy.d.ts +13 -0
- package/dist/constants/pledge-and-bouquet-copy.d.ts.map +1 -0
- package/dist/constants/pledge-and-bouquet-copy.js +19 -0
- package/dist/constants/pledge-and-bouquet-copy.js.map +1 -0
- package/dist/doc-schemas/ncii/requests.d.ts +4 -4
- package/dist/doc-schemas/public-documents.d.ts +5 -0
- package/dist/doc-schemas/public-documents.d.ts.map +1 -1
- package/dist/doc-schemas/public-documents.js +8 -5
- package/dist/doc-schemas/public-documents.js.map +1 -1
- package/dist/doc-schemas/registry.d.ts +1 -1
- package/dist/doc-schemas/user.js +3 -3
- package/dist/doc-schemas/user.js.map +1 -1
- package/dist/schemas/users.d.ts +13 -0
- package/dist/schemas/users.d.ts.map +1 -1
- package/dist/schemas/users.js +13 -0
- package/dist/schemas/users.js.map +1 -1
- package/dist/types/audit.d.ts +1 -1
- package/dist/types/audit.d.ts.map +1 -1
- package/dist/types/audit.js.map +1 -1
- package/dist/utils/app-config.d.ts +47 -0
- package/dist/utils/app-config.d.ts.map +1 -1
- package/dist/utils/app-config.js +55 -1
- package/dist/utils/app-config.js.map +1 -1
- package/dist/utils/public-documents.d.ts +7 -0
- package/dist/utils/public-documents.d.ts.map +1 -1
- package/dist/utils/public-documents.js +8 -0
- package/dist/utils/public-documents.js.map +1 -1
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ export * from './options.js';
|
|
|
7
7
|
export * from './admin-labels.js';
|
|
8
8
|
export * from './craft-skill-statements.js';
|
|
9
9
|
export * from './publish-attestations.js';
|
|
10
|
+
export * from './pledge-and-bouquet-copy.js';
|
|
10
11
|
export * from './public-documents.js';
|
|
11
12
|
export * from './legal-review-notice-state.js';
|
|
12
13
|
export * from './legal-review-notice.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uBAAuB,CAAC;AACtC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC"}
|
package/dist/constants/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export * from './options.js';
|
|
|
7
7
|
export * from './admin-labels.js';
|
|
8
8
|
export * from './craft-skill-statements.js';
|
|
9
9
|
export * from './publish-attestations.js';
|
|
10
|
+
export * from './pledge-and-bouquet-copy.js';
|
|
10
11
|
export * from './public-documents.js';
|
|
11
12
|
export * from './legal-review-notice-state.js';
|
|
12
13
|
export * from './legal-review-notice.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC","sourcesContent":["export * from './app-mode.js';\nexport * from './business.js';\nexport * from './chat.js';\nexport * from './conversation-files.js';\nexport * from './moderation.js';\nexport * from './options.js';\nexport * from './admin-labels.js';\nexport * from './craft-skill-statements.js';\nexport * from './publish-attestations.js';\nexport * from './public-documents.js';\nexport * from './legal-review-notice-state.js';\nexport * from './legal-review-notice.js';\nexport * from './safety-confirmation-phrases.js';\nexport * from './safety-telemetry-patterns.js';\nexport * from './pagination.js';\nexport * from './release-flags.js';\nexport * from './retention.js';\nexport * from './scheduled-jobs.js';\nexport * from './media-download.js';\nexport * from './media-processing.js';\nexport * from './storage-keys.js';\nexport * from './company-mascots.js';\nexport * from './center-stage-rooms.js';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uBAAuB,CAAC;AACtC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC","sourcesContent":["export * from './app-mode.js';\nexport * from './business.js';\nexport * from './chat.js';\nexport * from './conversation-files.js';\nexport * from './moderation.js';\nexport * from './options.js';\nexport * from './admin-labels.js';\nexport * from './craft-skill-statements.js';\nexport * from './publish-attestations.js';\nexport * from './pledge-and-bouquet-copy.js';\nexport * from './public-documents.js';\nexport * from './legal-review-notice-state.js';\nexport * from './legal-review-notice.js';\nexport * from './safety-confirmation-phrases.js';\nexport * from './safety-telemetry-patterns.js';\nexport * from './pagination.js';\nexport * from './release-flags.js';\nexport * from './retention.js';\nexport * from './scheduled-jobs.js';\nexport * from './media-download.js';\nexport * from './media-processing.js';\nexport * from './storage-keys.js';\nexport * from './company-mascots.js';\nexport * from './center-stage-rooms.js';\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a pledge relates to badges and other Charter honors, said the same way on every pledge
|
|
3
|
+
* surface: a badge comes when your pledges reach a certain total, and every honor is thank-you
|
|
4
|
+
* recognition, never something a pledge buys.
|
|
5
|
+
*/
|
|
6
|
+
export declare const PLEDGE_BADGE_RECOGNITION_COPY = "When your pledges reach a certain total, you earn a badge on your profile. Badges and other Charter honors are thank-you recognition, never something a pledge buys.";
|
|
7
|
+
/**
|
|
8
|
+
* Where Bouquets and artisan payouts stand. True only while neither Bouquet purchasing
|
|
9
|
+
* (BOUQUET_STAGE_1_LIVE) nor payouts (BOUQUET_STAGE_3_LIVE) is live, so flipping either flag
|
|
10
|
+
* means changing this sentence in the same release.
|
|
11
|
+
*/
|
|
12
|
+
export declare const BOUQUET_APPRECIATION_AND_PAYOUTS_PLANNED_COPY = "Bouquets and artisan payouts are planned, not built yet.";
|
|
13
|
+
//# sourceMappingURL=pledge-and-bouquet-copy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pledge-and-bouquet-copy.d.ts","sourceRoot":"","sources":["../../src/constants/pledge-and-bouquet-copy.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,yKAC8H,CAAC;AAEzK;;;;GAIG;AACH,eAAO,MAAM,6CAA6C,6DAA6D,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// PLEDGE AND BOUQUET COPY — user-facing truths about pledges and the Bouquet
|
|
3
|
+
// system, each ONE declaration that every surface stating it renders verbatim.
|
|
4
|
+
// Settled wording: never reword a constant in place to fit one surface; a
|
|
5
|
+
// surface that needs to say something different says it with its own words.
|
|
6
|
+
// ============================================================================
|
|
7
|
+
/**
|
|
8
|
+
* How a pledge relates to badges and other Charter honors, said the same way on every pledge
|
|
9
|
+
* surface: a badge comes when your pledges reach a certain total, and every honor is thank-you
|
|
10
|
+
* recognition, never something a pledge buys.
|
|
11
|
+
*/
|
|
12
|
+
export const PLEDGE_BADGE_RECOGNITION_COPY = 'When your pledges reach a certain total, you earn a badge on your profile. Badges and other Charter honors are thank-you recognition, never something a pledge buys.';
|
|
13
|
+
/**
|
|
14
|
+
* Where Bouquets and artisan payouts stand. True only while neither Bouquet purchasing
|
|
15
|
+
* (BOUQUET_STAGE_1_LIVE) nor payouts (BOUQUET_STAGE_3_LIVE) is live, so flipping either flag
|
|
16
|
+
* means changing this sentence in the same release.
|
|
17
|
+
*/
|
|
18
|
+
export const BOUQUET_APPRECIATION_AND_PAYOUTS_PLANNED_COPY = 'Bouquets and artisan payouts are planned, not built yet.';
|
|
19
|
+
//# sourceMappingURL=pledge-and-bouquet-copy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pledge-and-bouquet-copy.js","sourceRoot":"","sources":["../../src/constants/pledge-and-bouquet-copy.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,6EAA6E;AAC7E,+EAA+E;AAC/E,0EAA0E;AAC1E,4EAA4E;AAC5E,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,CAAC,MAAM,6BAA6B,GACxC,sKAAsK,CAAC;AAEzK;;;;GAIG;AACH,MAAM,CAAC,MAAM,6CAA6C,GAAG,0DAA0D,CAAC","sourcesContent":["// ============================================================================\n// PLEDGE AND BOUQUET COPY — user-facing truths about pledges and the Bouquet\n// system, each ONE declaration that every surface stating it renders verbatim.\n// Settled wording: never reword a constant in place to fit one surface; a\n// surface that needs to say something different says it with its own words.\n// ============================================================================\n\n/**\n * How a pledge relates to badges and other Charter honors, said the same way on every pledge\n * surface: a badge comes when your pledges reach a certain total, and every honor is thank-you\n * recognition, never something a pledge buys.\n */\nexport const PLEDGE_BADGE_RECOGNITION_COPY =\n 'When your pledges reach a certain total, you earn a badge on your profile. Badges and other Charter honors are thank-you recognition, never something a pledge buys.';\n\n/**\n * Where Bouquets and artisan payouts stand. True only while neither Bouquet purchasing\n * (BOUQUET_STAGE_1_LIVE) nor payouts (BOUQUET_STAGE_3_LIVE) is live, so flipping either flag\n * means changing this sentence in the same release.\n */\nexport const BOUQUET_APPRECIATION_AND_PAYOUTS_PLANNED_COPY = 'Bouquets and artisan payouts are planned, not built yet.';\n"]}
|
|
@@ -108,9 +108,9 @@ export type TakeItDownElectronicSignatureV1 = z.infer<typeof TakeItDownElectroni
|
|
|
108
108
|
export declare const AuthorityProofScanStatusSchema: z.ZodEnum<{
|
|
109
109
|
pending: "pending";
|
|
110
110
|
rejected: "rejected";
|
|
111
|
+
unavailable: "unavailable";
|
|
111
112
|
clean: "clean";
|
|
112
113
|
matched: "matched";
|
|
113
|
-
unavailable: "unavailable";
|
|
114
114
|
}>;
|
|
115
115
|
export type AuthorityProofScanStatus = z.infer<typeof AuthorityProofScanStatusSchema>;
|
|
116
116
|
/** [H-02] Server-owned authority-proof scan state. Embedded on the authorized-
|
|
@@ -120,9 +120,9 @@ export declare const AuthorityProofScanStateV1Schema: z.ZodObject<{
|
|
|
120
120
|
scanStatus: z.ZodEnum<{
|
|
121
121
|
pending: "pending";
|
|
122
122
|
rejected: "rejected";
|
|
123
|
+
unavailable: "unavailable";
|
|
123
124
|
clean: "clean";
|
|
124
125
|
matched: "matched";
|
|
125
|
-
unavailable: "unavailable";
|
|
126
126
|
}>;
|
|
127
127
|
objectGeneration: z.ZodString;
|
|
128
128
|
objectHash: z.ZodString;
|
|
@@ -144,9 +144,9 @@ export declare const TakeItDownAuthorizedRepresentativeV1Schema: z.ZodObject<{
|
|
|
144
144
|
scanStatus: z.ZodEnum<{
|
|
145
145
|
pending: "pending";
|
|
146
146
|
rejected: "rejected";
|
|
147
|
+
unavailable: "unavailable";
|
|
147
148
|
clean: "clean";
|
|
148
149
|
matched: "matched";
|
|
149
|
-
unavailable: "unavailable";
|
|
150
150
|
}>;
|
|
151
151
|
objectGeneration: z.ZodString;
|
|
152
152
|
objectHash: z.ZodString;
|
|
@@ -179,9 +179,9 @@ export declare const TakeItDownRequesterPrivateV1Schema: z.ZodObject<{
|
|
|
179
179
|
scanStatus: z.ZodEnum<{
|
|
180
180
|
pending: "pending";
|
|
181
181
|
rejected: "rejected";
|
|
182
|
+
unavailable: "unavailable";
|
|
182
183
|
clean: "clean";
|
|
183
184
|
matched: "matched";
|
|
184
|
-
unavailable: "unavailable";
|
|
185
185
|
}>;
|
|
186
186
|
objectGeneration: z.ZodString;
|
|
187
187
|
objectHash: z.ZodString;
|
|
@@ -10,6 +10,11 @@ export declare const PublicDocumentIdSchema: z.ZodEnum<{
|
|
|
10
10
|
}>;
|
|
11
11
|
/** A published version number: v1 is the first. */
|
|
12
12
|
export declare const PublicDocumentVersionNumberSchema: z.ZodNumber;
|
|
13
|
+
/**
|
|
14
|
+
* A version reference where 0 means "none yet" (never accepted / never required / no base / never
|
|
15
|
+
* published). Every recorded "which version was accepted" field derives from it.
|
|
16
|
+
*/
|
|
17
|
+
export declare const PublicDocumentVersionOrNoneSchema: z.ZodNumber;
|
|
13
18
|
/** An acceptance level: 0 = nothing required / nothing accepted; each requiring release adds 1. */
|
|
14
19
|
export declare const PublicDocumentAcceptanceLevelSchema: z.ZodNumber;
|
|
15
20
|
/** A document paired with one of its published versions. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-documents.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/public-documents.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAU9F,eAAO,MAAM,sBAAsB;;;;;;;EAA8B,CAAC;AAElE,mDAAmD;AACnD,eAAO,MAAM,iCAAiC,aAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"public-documents.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/public-documents.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,8 +22,11 @@ import { LegalReviewNoticeRevisionSchema } from './legal-review-notice.js';
|
|
|
22
22
|
export const PublicDocumentIdSchema = z.enum(PUBLIC_DOCUMENT_IDS);
|
|
23
23
|
/** A published version number: v1 is the first. */
|
|
24
24
|
export const PublicDocumentVersionNumberSchema = z.number().int().positive();
|
|
25
|
-
/**
|
|
26
|
-
|
|
25
|
+
/**
|
|
26
|
+
* A version reference where 0 means "none yet" (never accepted / never required / no base / never
|
|
27
|
+
* published). Every recorded "which version was accepted" field derives from it.
|
|
28
|
+
*/
|
|
29
|
+
export const PublicDocumentVersionOrNoneSchema = z.number().int().nonnegative();
|
|
27
30
|
/** An acceptance level: 0 = nothing required / nothing accepted; each requiring release adds 1. */
|
|
28
31
|
export const PublicDocumentAcceptanceLevelSchema = z.number().int().nonnegative();
|
|
29
32
|
/** A document paired with one of its published versions. */
|
|
@@ -96,7 +99,7 @@ export const PublicDocumentVersionSchema = perDocument({
|
|
|
96
99
|
* current version, and one whose content equals the current version.
|
|
97
100
|
*/
|
|
98
101
|
export const PublicDocumentDraftSchema = perDocument({
|
|
99
|
-
baseVersion:
|
|
102
|
+
baseVersion: PublicDocumentVersionOrNoneSchema,
|
|
100
103
|
savedBy: z.string().min(1),
|
|
101
104
|
savedAt: z.number(),
|
|
102
105
|
});
|
|
@@ -122,7 +125,7 @@ export const PublicDocumentReleaseSchema = z.object({
|
|
|
122
125
|
export const PublicDocumentVersionStateSchema = z
|
|
123
126
|
.object({
|
|
124
127
|
currentVersion: PublicDocumentVersionNumberSchema,
|
|
125
|
-
requiredVersion:
|
|
128
|
+
requiredVersion: PublicDocumentVersionOrNoneSchema,
|
|
126
129
|
})
|
|
127
130
|
.strict()
|
|
128
131
|
.refine((state) => state.requiredVersion <= state.currentVersion, {
|
|
@@ -149,7 +152,7 @@ export const PublicDocumentVersionBlockSchema = z
|
|
|
149
152
|
*/
|
|
150
153
|
export const PublicDocumentAcceptanceSchema = z
|
|
151
154
|
.object({
|
|
152
|
-
documentVersions: z.partialRecord(PublicDocumentIdSchema,
|
|
155
|
+
documentVersions: z.partialRecord(PublicDocumentIdSchema, PublicDocumentVersionOrNoneSchema),
|
|
153
156
|
acceptedLevel: PublicDocumentAcceptanceLevelSchema,
|
|
154
157
|
legalReviewNoticeRevision: LegalReviewNoticeRevisionSchema.optional(),
|
|
155
158
|
acceptedAt: z.number(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-documents.js","sourceRoot":"","sources":["../../src/doc-schemas/public-documents.ts"],"names":[],"mappings":"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","sourcesContent":["// Versioned public documents — the Firestore contracts of the release system.\n//\n// _appConfig/{documentId} current projection (public; the\n// existing page readers read it)\n// publicDocuments/{documentId}/publicDocumentVersions/v{n} immutable published version\n// publicDocumentDrafts/{documentId} the private working copy\n// publicDocumentReleases/{releaseId} one release record per publish\n// _appConfig/app.publicDocumentVersions the version block every session\n// already reads live\n// userProfiles/{uid}/privateData/{uid}.publicDocumentAcceptance\n// what the person last accepted\n//\n// All are backend-written. Versions are whole numbers v1, v2, … per document, assigned by the\n// one publish transaction; a correction is a new version, never an edit. \"Require acceptance\"\n// is recorded on the release and in the version block — never as a second version number.\n// Acceptance HISTORY lives only in the append-only `publicDocuments.accepted` audit events.\n\nimport { z } from 'zod';\nimport { SPECIAL_DOCS } from '../paths/collections.js';\nimport { PUBLIC_DOCUMENT_IDS, type PublicDocumentId } from '../constants/public-documents.js';\nimport {\n LegalPageDocumentSchema,\n RulesAndAgreementsSchema,\n FuturePlansDocumentSchema,\n TakeItDownPageCopySchema,\n DmcaPolicyDocumentSchema,\n} from './content.js';\nimport { LegalReviewNoticeRevisionSchema } from './legal-review-notice.js';\n\nexport const PublicDocumentIdSchema = z.enum(PUBLIC_DOCUMENT_IDS);\n\n/** A published version number: v1 is the first. */\nexport const PublicDocumentVersionNumberSchema = z.number().int().positive();\n\n/** A version reference where 0 means \"none yet\" (never accepted / never required / no base). */\nconst versionOrNoneSchema = z.number().int().nonnegative();\n\n/** An acceptance level: 0 = nothing required / nothing accepted; each requiring release adds 1. */\nexport const PublicDocumentAcceptanceLevelSchema = z.number().int().nonnegative();\n\n/** A document paired with one of its published versions. */\nexport const PublicDocumentVersionRefSchema = z\n .object({\n documentId: PublicDocumentIdSchema,\n version: PublicDocumentVersionNumberSchema,\n })\n .strict();\nexport type PublicDocumentVersionRef = z.infer<typeof PublicDocumentVersionRefSchema>;\n\n// ── Content ──────────────────────────────────────────────────────────────────\n// A document's CONTENT is its current-projection shape minus `version` / `lastUpdated`, so the\n// existing page readers, the working copy, and the immutable version all share one shape.\n\nexport const LegalPageContentSchema = LegalPageDocumentSchema.pick({ sections: true });\nexport const RulesAndAgreementsContentSchema = RulesAndAgreementsSchema.pick({ rules: true, agreements: true });\nexport const FuturePlansContentSchema = FuturePlansDocumentSchema.pick({ plans: true });\nexport const TakeItDownPageCopyContentSchema = TakeItDownPageCopySchema.pick({ strings: true });\nexport const DmcaPolicyContentSchema = DmcaPolicyDocumentSchema.pick({\n intro: true,\n contactBlocks: true,\n sections: true,\n});\n\n/** Each document's stored content schema. */\nexport const PUBLIC_DOCUMENT_CONTENT_SCHEMAS = {\n [SPECIAL_DOCS.TERMS_PAGE]: LegalPageContentSchema,\n [SPECIAL_DOCS.PRIVACY_PAGE]: LegalPageContentSchema,\n [SPECIAL_DOCS.RULES_AND_AGREEMENTS]: RulesAndAgreementsContentSchema,\n [SPECIAL_DOCS.FUTURE_PLANS]: FuturePlansContentSchema,\n [SPECIAL_DOCS.DMCA_POLICY]: DmcaPolicyContentSchema,\n [SPECIAL_DOCS.TAKE_IT_DOWN_PAGE_COPY]: TakeItDownPageCopyContentSchema,\n} as const satisfies Record<PublicDocumentId, z.ZodType>;\n\n/** The content of document `D`. */\nexport type PublicDocumentContent<D extends PublicDocumentId = PublicDocumentId> = z.infer<\n (typeof PUBLIC_DOCUMENT_CONTENT_SCHEMAS)[D]\n>;\n\n/** Each document's current-projection schema (`_appConfig/{documentId}`). */\nexport const PUBLIC_DOCUMENT_PROJECTION_SCHEMAS = {\n [SPECIAL_DOCS.TERMS_PAGE]: LegalPageDocumentSchema,\n [SPECIAL_DOCS.PRIVACY_PAGE]: LegalPageDocumentSchema,\n [SPECIAL_DOCS.RULES_AND_AGREEMENTS]: RulesAndAgreementsSchema,\n [SPECIAL_DOCS.FUTURE_PLANS]: FuturePlansDocumentSchema,\n [SPECIAL_DOCS.DMCA_POLICY]: DmcaPolicyDocumentSchema,\n [SPECIAL_DOCS.TAKE_IT_DOWN_PAGE_COPY]: TakeItDownPageCopySchema,\n} as const satisfies Record<PublicDocumentId, z.ZodType>;\n\n/**\n * The current projection the publish writes for document `D`: its content plus the version and\n * the publish time. Assignable to the page's existing document type.\n */\nexport type PublicDocumentProjection<D extends PublicDocumentId = PublicDocumentId> =\n PublicDocumentContent<D> & { version: number; lastUpdated: number };\n\n// One discriminated branch per document, so a version or draft's `content` is typed by its\n// `documentId`.\nfunction perDocument<const S extends z.ZodRawShape>(shape: S) {\n const branch = <const D extends PublicDocumentId>(documentId: D) =>\n z.object({ documentId: z.literal(documentId), content: PUBLIC_DOCUMENT_CONTENT_SCHEMAS[documentId], ...shape });\n return z.discriminatedUnion('documentId', [\n branch(SPECIAL_DOCS.TERMS_PAGE),\n branch(SPECIAL_DOCS.PRIVACY_PAGE),\n branch(SPECIAL_DOCS.RULES_AND_AGREEMENTS),\n branch(SPECIAL_DOCS.FUTURE_PLANS),\n branch(SPECIAL_DOCS.DMCA_POLICY),\n branch(SPECIAL_DOCS.TAKE_IT_DOWN_PAGE_COPY),\n ]);\n}\n\n// ── Immutable version ────────────────────────────────────────────────────────\n\n/**\n * `publicDocuments/{documentId}/publicDocumentVersions/v{version}` — one published version,\n * written once by the publish transaction and never edited: the full content, the version,\n * the release that published it, the publisher's uid, and the server time.\n */\nexport const PublicDocumentVersionSchema = perDocument({\n version: PublicDocumentVersionNumberSchema,\n releaseId: z.string().min(1),\n publishedBy: z.string().min(1),\n publishedAt: z.number(),\n});\nexport type PublicDocumentVersion = z.infer<typeof PublicDocumentVersionSchema>;\n\n// ── Working copy ─────────────────────────────────────────────────────────────\n\n/**\n * `publicDocumentDrafts/{documentId}` — the one private working copy of a document, saved from\n * its Admin editor. `baseVersion` is the published version the edit started from (0 when the\n * document has never been published): the publish rejects a draft whose base is no longer the\n * current version, and one whose content equals the current version.\n */\nexport const PublicDocumentDraftSchema = perDocument({\n baseVersion: versionOrNoneSchema,\n savedBy: z.string().min(1),\n savedAt: z.number(),\n});\nexport type PublicDocumentDraft = z.infer<typeof PublicDocumentDraftSchema>;\n\n// ── Release record ───────────────────────────────────────────────────────────\n\n/**\n * `publicDocumentReleases/{releaseId}` — one record per publish: which documents moved to\n * which versions, whether the release requires acceptance, the required-acceptance level in\n * force after it (raised by one only when it requires acceptance), and who published it when.\n */\nexport const PublicDocumentReleaseSchema = z.object({\n releaseId: z.string().min(1),\n documents: z.array(PublicDocumentVersionRefSchema).min(1),\n requireAcceptance: z.boolean(),\n requiredAcceptanceLevel: PublicDocumentAcceptanceLevelSchema,\n publishedBy: z.string().min(1),\n publishedAt: z.number(),\n});\nexport type PublicDocumentRelease = z.infer<typeof PublicDocumentReleaseSchema>;\n\n// ── Version block on _appConfig/app ──────────────────────────────────────────\n\n/**\n * One document's entry in the version block: its current published version and the latest\n * version a release REQUIRED acceptance of (0 when none has).\n */\nexport const PublicDocumentVersionStateSchema = z\n .object({\n currentVersion: PublicDocumentVersionNumberSchema,\n requiredVersion: versionOrNoneSchema,\n })\n .strict()\n .refine((state) => state.requiredVersion <= state.currentVersion, {\n message: 'requiredVersion cannot exceed currentVersion',\n });\nexport type PublicDocumentVersionState = z.infer<typeof PublicDocumentVersionStateSchema>;\n\n/**\n * `_appConfig/app.publicDocumentVersions` — written only by the publish transaction. A document\n * never published has no entry. `requiredAcceptanceLevel` rises by one with every release that\n * requires acceptance; a session whose accepted level is below it is asked to accept.\n */\nexport const PublicDocumentVersionBlockSchema = z\n .object({\n documents: z.partialRecord(PublicDocumentIdSchema, PublicDocumentVersionStateSchema),\n requiredAcceptanceLevel: PublicDocumentAcceptanceLevelSchema,\n })\n .strict();\nexport type PublicDocumentVersionBlock = z.infer<typeof PublicDocumentVersionBlockSchema>;\n\n// ── The person's acceptance summary (private doc) ────────────────────────────\n\n/**\n * `privateData/{uid}.publicDocumentAcceptance` — the LATEST accepted version of each document\n * and the accepted level, replaced whole on every acceptance (registration or the re-acceptance\n * prompt). A document absent from `documentVersions` counts as version 0. The notice revision in\n * force at that acceptance is recorded when the notice was active. History is not kept here — it\n * is the `publicDocuments.accepted` audit events.\n */\nexport const PublicDocumentAcceptanceSchema = z\n .object({\n documentVersions: z.partialRecord(PublicDocumentIdSchema, versionOrNoneSchema),\n acceptedLevel: PublicDocumentAcceptanceLevelSchema,\n legalReviewNoticeRevision: LegalReviewNoticeRevisionSchema.optional(),\n acceptedAt: z.number(),\n })\n .strict();\nexport type PublicDocumentAcceptance = z.infer<typeof PublicDocumentAcceptanceSchema>;\n"]}
|
|
1
|
+
{"version":3,"file":"public-documents.js","sourceRoot":"","sources":["../../src/doc-schemas/public-documents.ts"],"names":[],"mappings":"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","sourcesContent":["// Versioned public documents — the Firestore contracts of the release system.\n//\n// _appConfig/{documentId} current projection (public; the\n// existing page readers read it)\n// publicDocuments/{documentId}/publicDocumentVersions/v{n} immutable published version\n// publicDocumentDrafts/{documentId} the private working copy\n// publicDocumentReleases/{releaseId} one release record per publish\n// _appConfig/app.publicDocumentVersions the version block every session\n// already reads live\n// userProfiles/{uid}/privateData/{uid}.publicDocumentAcceptance\n// what the person last accepted\n//\n// All are backend-written. Versions are whole numbers v1, v2, … per document, assigned by the\n// one publish transaction; a correction is a new version, never an edit. \"Require acceptance\"\n// is recorded on the release and in the version block — never as a second version number.\n// Acceptance HISTORY lives only in the append-only `publicDocuments.accepted` audit events.\n\nimport { z } from 'zod';\nimport { SPECIAL_DOCS } from '../paths/collections.js';\nimport { PUBLIC_DOCUMENT_IDS, type PublicDocumentId } from '../constants/public-documents.js';\nimport {\n LegalPageDocumentSchema,\n RulesAndAgreementsSchema,\n FuturePlansDocumentSchema,\n TakeItDownPageCopySchema,\n DmcaPolicyDocumentSchema,\n} from './content.js';\nimport { LegalReviewNoticeRevisionSchema } from './legal-review-notice.js';\n\nexport const PublicDocumentIdSchema = z.enum(PUBLIC_DOCUMENT_IDS);\n\n/** A published version number: v1 is the first. */\nexport const PublicDocumentVersionNumberSchema = z.number().int().positive();\n\n/**\n * A version reference where 0 means \"none yet\" (never accepted / never required / no base / never\n * published). Every recorded \"which version was accepted\" field derives from it.\n */\nexport const PublicDocumentVersionOrNoneSchema = z.number().int().nonnegative();\n\n/** An acceptance level: 0 = nothing required / nothing accepted; each requiring release adds 1. */\nexport const PublicDocumentAcceptanceLevelSchema = z.number().int().nonnegative();\n\n/** A document paired with one of its published versions. */\nexport const PublicDocumentVersionRefSchema = z\n .object({\n documentId: PublicDocumentIdSchema,\n version: PublicDocumentVersionNumberSchema,\n })\n .strict();\nexport type PublicDocumentVersionRef = z.infer<typeof PublicDocumentVersionRefSchema>;\n\n// ── Content ──────────────────────────────────────────────────────────────────\n// A document's CONTENT is its current-projection shape minus `version` / `lastUpdated`, so the\n// existing page readers, the working copy, and the immutable version all share one shape.\n\nexport const LegalPageContentSchema = LegalPageDocumentSchema.pick({ sections: true });\nexport const RulesAndAgreementsContentSchema = RulesAndAgreementsSchema.pick({ rules: true, agreements: true });\nexport const FuturePlansContentSchema = FuturePlansDocumentSchema.pick({ plans: true });\nexport const TakeItDownPageCopyContentSchema = TakeItDownPageCopySchema.pick({ strings: true });\nexport const DmcaPolicyContentSchema = DmcaPolicyDocumentSchema.pick({\n intro: true,\n contactBlocks: true,\n sections: true,\n});\n\n/** Each document's stored content schema. */\nexport const PUBLIC_DOCUMENT_CONTENT_SCHEMAS = {\n [SPECIAL_DOCS.TERMS_PAGE]: LegalPageContentSchema,\n [SPECIAL_DOCS.PRIVACY_PAGE]: LegalPageContentSchema,\n [SPECIAL_DOCS.RULES_AND_AGREEMENTS]: RulesAndAgreementsContentSchema,\n [SPECIAL_DOCS.FUTURE_PLANS]: FuturePlansContentSchema,\n [SPECIAL_DOCS.DMCA_POLICY]: DmcaPolicyContentSchema,\n [SPECIAL_DOCS.TAKE_IT_DOWN_PAGE_COPY]: TakeItDownPageCopyContentSchema,\n} as const satisfies Record<PublicDocumentId, z.ZodType>;\n\n/** The content of document `D`. */\nexport type PublicDocumentContent<D extends PublicDocumentId = PublicDocumentId> = z.infer<\n (typeof PUBLIC_DOCUMENT_CONTENT_SCHEMAS)[D]\n>;\n\n/** Each document's current-projection schema (`_appConfig/{documentId}`). */\nexport const PUBLIC_DOCUMENT_PROJECTION_SCHEMAS = {\n [SPECIAL_DOCS.TERMS_PAGE]: LegalPageDocumentSchema,\n [SPECIAL_DOCS.PRIVACY_PAGE]: LegalPageDocumentSchema,\n [SPECIAL_DOCS.RULES_AND_AGREEMENTS]: RulesAndAgreementsSchema,\n [SPECIAL_DOCS.FUTURE_PLANS]: FuturePlansDocumentSchema,\n [SPECIAL_DOCS.DMCA_POLICY]: DmcaPolicyDocumentSchema,\n [SPECIAL_DOCS.TAKE_IT_DOWN_PAGE_COPY]: TakeItDownPageCopySchema,\n} as const satisfies Record<PublicDocumentId, z.ZodType>;\n\n/**\n * The current projection the publish writes for document `D`: its content plus the version and\n * the publish time. Assignable to the page's existing document type.\n */\nexport type PublicDocumentProjection<D extends PublicDocumentId = PublicDocumentId> =\n PublicDocumentContent<D> & { version: number; lastUpdated: number };\n\n// One discriminated branch per document, so a version or draft's `content` is typed by its\n// `documentId`.\nfunction perDocument<const S extends z.ZodRawShape>(shape: S) {\n const branch = <const D extends PublicDocumentId>(documentId: D) =>\n z.object({ documentId: z.literal(documentId), content: PUBLIC_DOCUMENT_CONTENT_SCHEMAS[documentId], ...shape });\n return z.discriminatedUnion('documentId', [\n branch(SPECIAL_DOCS.TERMS_PAGE),\n branch(SPECIAL_DOCS.PRIVACY_PAGE),\n branch(SPECIAL_DOCS.RULES_AND_AGREEMENTS),\n branch(SPECIAL_DOCS.FUTURE_PLANS),\n branch(SPECIAL_DOCS.DMCA_POLICY),\n branch(SPECIAL_DOCS.TAKE_IT_DOWN_PAGE_COPY),\n ]);\n}\n\n// ── Immutable version ────────────────────────────────────────────────────────\n\n/**\n * `publicDocuments/{documentId}/publicDocumentVersions/v{version}` — one published version,\n * written once by the publish transaction and never edited: the full content, the version,\n * the release that published it, the publisher's uid, and the server time.\n */\nexport const PublicDocumentVersionSchema = perDocument({\n version: PublicDocumentVersionNumberSchema,\n releaseId: z.string().min(1),\n publishedBy: z.string().min(1),\n publishedAt: z.number(),\n});\nexport type PublicDocumentVersion = z.infer<typeof PublicDocumentVersionSchema>;\n\n// ── Working copy ─────────────────────────────────────────────────────────────\n\n/**\n * `publicDocumentDrafts/{documentId}` — the one private working copy of a document, saved from\n * its Admin editor. `baseVersion` is the published version the edit started from (0 when the\n * document has never been published): the publish rejects a draft whose base is no longer the\n * current version, and one whose content equals the current version.\n */\nexport const PublicDocumentDraftSchema = perDocument({\n baseVersion: PublicDocumentVersionOrNoneSchema,\n savedBy: z.string().min(1),\n savedAt: z.number(),\n});\nexport type PublicDocumentDraft = z.infer<typeof PublicDocumentDraftSchema>;\n\n// ── Release record ───────────────────────────────────────────────────────────\n\n/**\n * `publicDocumentReleases/{releaseId}` — one record per publish: which documents moved to\n * which versions, whether the release requires acceptance, the required-acceptance level in\n * force after it (raised by one only when it requires acceptance), and who published it when.\n */\nexport const PublicDocumentReleaseSchema = z.object({\n releaseId: z.string().min(1),\n documents: z.array(PublicDocumentVersionRefSchema).min(1),\n requireAcceptance: z.boolean(),\n requiredAcceptanceLevel: PublicDocumentAcceptanceLevelSchema,\n publishedBy: z.string().min(1),\n publishedAt: z.number(),\n});\nexport type PublicDocumentRelease = z.infer<typeof PublicDocumentReleaseSchema>;\n\n// ── Version block on _appConfig/app ──────────────────────────────────────────\n\n/**\n * One document's entry in the version block: its current published version and the latest\n * version a release REQUIRED acceptance of (0 when none has).\n */\nexport const PublicDocumentVersionStateSchema = z\n .object({\n currentVersion: PublicDocumentVersionNumberSchema,\n requiredVersion: PublicDocumentVersionOrNoneSchema,\n })\n .strict()\n .refine((state) => state.requiredVersion <= state.currentVersion, {\n message: 'requiredVersion cannot exceed currentVersion',\n });\nexport type PublicDocumentVersionState = z.infer<typeof PublicDocumentVersionStateSchema>;\n\n/**\n * `_appConfig/app.publicDocumentVersions` — written only by the publish transaction. A document\n * never published has no entry. `requiredAcceptanceLevel` rises by one with every release that\n * requires acceptance; a session whose accepted level is below it is asked to accept.\n */\nexport const PublicDocumentVersionBlockSchema = z\n .object({\n documents: z.partialRecord(PublicDocumentIdSchema, PublicDocumentVersionStateSchema),\n requiredAcceptanceLevel: PublicDocumentAcceptanceLevelSchema,\n })\n .strict();\nexport type PublicDocumentVersionBlock = z.infer<typeof PublicDocumentVersionBlockSchema>;\n\n// ── The person's acceptance summary (private doc) ────────────────────────────\n\n/**\n * `privateData/{uid}.publicDocumentAcceptance` — the LATEST accepted version of each document\n * and the accepted level, replaced whole on every acceptance (registration or the re-acceptance\n * prompt). A document absent from `documentVersions` counts as version 0. The notice revision in\n * force at that acceptance is recorded when the notice was active. History is not kept here — it\n * is the `publicDocuments.accepted` audit events.\n */\nexport const PublicDocumentAcceptanceSchema = z\n .object({\n documentVersions: z.partialRecord(PublicDocumentIdSchema, PublicDocumentVersionOrNoneSchema),\n acceptedLevel: PublicDocumentAcceptanceLevelSchema,\n legalReviewNoticeRevision: LegalReviewNoticeRevisionSchema.optional(),\n acceptedAt: z.number(),\n })\n .strict();\nexport type PublicDocumentAcceptance = z.infer<typeof PublicDocumentAcceptanceSchema>;\n"]}
|
|
@@ -6272,9 +6272,9 @@ export declare const COLLECTION_SCHEMAS: {
|
|
|
6272
6272
|
scanStatus: z.ZodEnum<{
|
|
6273
6273
|
pending: "pending";
|
|
6274
6274
|
rejected: "rejected";
|
|
6275
|
+
unavailable: "unavailable";
|
|
6275
6276
|
clean: "clean";
|
|
6276
6277
|
matched: "matched";
|
|
6277
|
-
unavailable: "unavailable";
|
|
6278
6278
|
}>;
|
|
6279
6279
|
objectGeneration: z.ZodString;
|
|
6280
6280
|
objectHash: z.ZodString;
|
package/dist/doc-schemas/user.js
CHANGED
|
@@ -10,7 +10,7 @@ import { CRAFT_SKILL_TAG_VALUES } from '../constants/options.js';
|
|
|
10
10
|
import { userPrivateDataAgeFieldsShape } from './safety/age.js';
|
|
11
11
|
import { ContentMediaKindSchema } from './media-assets.js';
|
|
12
12
|
import { calendarDateSchema } from '../schemas/atoms.js';
|
|
13
|
-
import { PublicDocumentAcceptanceSchema } from './public-documents.js';
|
|
13
|
+
import { PublicDocumentAcceptanceSchema, PublicDocumentVersionOrNoneSchema } from './public-documents.js';
|
|
14
14
|
// The canonical stored media kind — declared once in doc-schemas/media-assets.ts.
|
|
15
15
|
const mediaKindSchema = ContentMediaKindSchema;
|
|
16
16
|
/** The ONE canonical application account status (userProfiles.status is the
|
|
@@ -241,11 +241,11 @@ export const UserPrivateDataSchema = z.object({
|
|
|
241
241
|
squareStreetzAgreementsDate: z.number().optional(),
|
|
242
242
|
// The rules-and-agreements `version` in force when the user accepted; 0 when no versioned
|
|
243
243
|
// rules doc existed yet, so a later real version 1 still re-prompts.
|
|
244
|
-
squareStreetzAgreementsVersion:
|
|
244
|
+
squareStreetzAgreementsVersion: PublicDocumentVersionOrNoneSchema.optional(),
|
|
245
245
|
// The rules-and-agreements version in force when the user accepted the Artisan upgrade's
|
|
246
246
|
// agreement, read server-side in the grant transaction (the grant time is the profile's
|
|
247
247
|
// `artisanCreator` stamp). Same 0 sentinel as `squareStreetzAgreementsVersion`.
|
|
248
|
-
artisanCreatorAgreementsVersion:
|
|
248
|
+
artisanCreatorAgreementsVersion: PublicDocumentVersionOrNoneSchema.optional(),
|
|
249
249
|
// Epoch ms when the user accepted the one-time Hall download acknowledgement
|
|
250
250
|
// (personal offline use only, no redistribution). Written server-side by the
|
|
251
251
|
// acceptHallDownloadAcknowledgement callable; gates the Hall download button.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user.js","sourceRoot":"","sources":["../../src/doc-schemas/user.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,oFAAoF;AACpF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAE/E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,6BAA6B,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC;AAEvE,kFAAkF;AAClF,MAAM,eAAe,GAAG,sBAAsB,CAAC;AAE/C;;yDAEyD;AACzD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;AAGjF,0EAA0E;AAC1E,8FAA8F;AAC9F,uFAAuF;AACvF,uFAAuF;AACvF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC;AAGtF;;0EAE0E;AAC1E,MAAM,CAAC,MAAM,iCAAiC,GAAmB,YAAY,CAAC;AAE9E,yFAAyF;AACzF,uFAAuF;AACvF,wFAAwF;AACxF,iCAAiC;AACjC,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC;KAC7C,MAAM,CAAC;IACN,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACrC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACtB,CAAC;KACD,MAAM,EAAE,CAAC;AAGZ,kFAAkF;AAClF,iFAAiF;AACjF,wBAAwB;AACxB,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC;KACzC,MAAM,CAAC;IACN,aAAa,EAAE,oBAAoB;IACnC,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACzC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;CACrB,CAAC;KACD,MAAM,EAAE,CAAC;AAGZ,yFAAyF;AACzF,yFAAyF;AACzF,8EAA8E;AAC9E,MAAM,mBAAmB,GAAG;IAC1B,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,kFAAkF;IAClF,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAC7C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,IAAI,EAAE,eAAe;IACrB,2EAA2E;IAC3E,6EAA6E;IAC7E,yEAAyE;IACzE,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE;IACnB,WAAW,EAAE,2BAA2B;CACzC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IAC3D,CAAC,CAAC,MAAM,CAAC;QACP,GAAG,mBAAmB;QACtB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC;KAC5B,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,GAAG,mBAAmB;QACtB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC;QAC7B,2EAA2E;QAC3E,MAAM,EAAE,+BAA+B;KACxC,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,GAAG,mBAAmB;QACtB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;KAC/B,CAAC;CACH,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE;IAC1B,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE;IAC7B,cAAc,EAAE,eAAe;IAC/B,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAC7C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,6EAA6E;IAC7E,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE;IACnB,uFAAuF;IACvF,eAAe;IACf,IAAI,EAAE,oBAAoB;IAC1B,wFAAwF;IACxF,qFAAqF;IACrF,4EAA4E;IAC5E,MAAM,EAAE,+BAA+B,CAAC,QAAQ,EAAE;CACnD,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,IAAI,EAAE,eAAe;CACtB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;IACzB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE;IAC9B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC;CAC1C,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;IACzB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE;IAC9B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;CACrB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE;IACjC,0EAA0E;IAC1E,oEAAoE;IACpE,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACvD,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACrC,mFAAmF;IACnF,sFAAsF;IACtF,uFAAuF;IACvF,kFAAkF;IAClF,iDAAiD;IACjD,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAClC,MAAM,EAAE,uBAAuB,CAAC,QAAQ,EAAE;IAC1C,+FAA+F;IAC/F,2FAA2F;IAC3F,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACtC,2EAA2E;IAC3E,8DAA8D;IAC9D,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC7C,iFAAiF;IACjF,qFAAqF;IACrF,sFAAsF;IACtF,uFAAuF;IACvF,kFAAkF;IAClF,oBAAoB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3C,kBAAkB,EAAE,uBAAuB,CAAC,QAAQ,EAAE;IACtD,qFAAqF;IACrF,0FAA0F;IAC1F,oGAAoG;IACpG,wBAAwB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAChD,iBAAiB,EAAE,CAAC,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,QAAQ,EAAE;IAC7D,sBAAsB,EAAE,CAAC,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC,QAAQ,EAAE;IACvE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,4EAA4E;IAC5E,4EAA4E;IAC5E,2EAA2E;IAC3E,iDAAiD;IACjD,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACpC,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,GAAG,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC3B,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC9B,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC5B,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC/B,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC7B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B;;;;;;;;;;;OAWG;IACH,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;IACvD,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;CAC1D,CAAC,CAAC;AAGH;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,uFAAuF;IACvF,kFAAkF;IAClF,yCAAyC;IACzC,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC7C,wCAAwC;IACxC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAClC,uFAAuF;IACvF,sFAAsF;IACtF,YAAY,EAAE,kBAAkB,CAAC,QAAQ,EAAE,EAAE,eAAe;IAC5D,qFAAqF;IACrF,wEAAwE;IACxE,0BAA0B,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAClD,CAAC,CAAC;AAGH;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,mFAAmF;IACnF,0EAA0E;IAC1E,kFAAkF;IAClF,gEAAgE;IAChE,wBAAwB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/C,yFAAyF;IACzF,6FAA6F;IAC7F,uCAAuC;IACvC,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzC,+FAA+F;IAC/F,8FAA8F;IAC9F,kGAAkG;IAClG,2FAA2F;IAC3F,oBAAoB,EAAE,CAAC;SACpB,MAAM,CAAC;QACN,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,2BAA2B,CAAC;QAC3D,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC,QAAQ,EAAE;QAC9D,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;KACxB,CAAC;SACD,QAAQ,EAAE;IACb,2BAA2B,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAClD,0FAA0F;IAC1F,qEAAqE;IACrE,8BAA8B,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACrD,yFAAyF;IACzF,wFAAwF;IACxF,gFAAgF;IAChF,+BAA+B,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;IAC1E,6EAA6E;IAC7E,6EAA6E;IAC7E,8EAA8E;IAC9E,0BAA0B,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjD,UAAU,EAAE,oBAAoB,CAAC,QAAQ,EAAE;IAC3C,kFAAkF;IAClF,6FAA6F;IAC7F,yFAAyF;IACzF,wBAAwB,EAAE,8BAA8B,CAAC,QAAQ,EAAE;IACnE,4EAA4E;IAC5E,qFAAqF;IACrF,+EAA+E;IAC/E,QAAQ,EAAE,uBAAuB,CAAC,QAAQ,EAAE;IAC5C,gFAAgF;IAChF,+EAA+E;IAC/E,0EAA0E;IAC1E,sDAAsD;IACtD,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACrC,gFAAgF;IAChF,+EAA+E;IAC/E,mEAAmE;IACnE,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACpC,mFAAmF;IACnF,iFAAiF;IACjF,4BAA4B;IAC5B,GAAG,6BAA6B;CACjC,CAAC,CAAC","sourcesContent":["// User-related Firestore document SCHEMAS — the single source of truth for the\n// `userProfiles/{uid}` doc, its `privateData/{uid}` subdoc, and craft-skill shapes.\n// (The `publicUsers/{uid}` mirror lives in ./publicUser.) TypeScript types are\n// inferred from these schemas via `z.infer`, so a document's shape and its type\n// cannot drift apart. See docs/design/firestore-schema-registry.md (ttt-prod).\n\nimport { z } from 'zod';\nimport { HALL_WING_TYPE_KEYS } from '../types/content.js';\nimport { MAX_ARTISAN_LOCATION_LENGTH } from '../constants/business.js';\nimport { CRAFT_SKILL_TAG_VALUES } from '../constants/options.js';\nimport { userPrivateDataAgeFieldsShape } from './safety/age.js';\nimport { ContentMediaKindSchema } from './media-assets.js';\nimport { calendarDateSchema } from '../schemas/atoms.js';\nimport { PublicDocumentAcceptanceSchema } from './public-documents.js';\n\n// The canonical stored media kind — declared once in doc-schemas/media-assets.ts.\nconst mediaKindSchema = ContentMediaKindSchema;\n\n/** The ONE canonical application account status (userProfiles.status is the\n * authoritative value; the chat access projection and the status-reconcile queue\n * derive from the same set). Never re-declare inline. */\nexport const UserAccountStatusSchema = z.enum(['active', 'suspended', 'banned']);\nexport type UserAccountStatus = z.infer<typeof UserAccountStatusSchema>;\n\n// Craft-skill kind — mandatory choice at upload, no default (see ttt-prod\n// docs/design/profile-and-craft-skills.md). 'original' = the artisan's own original creation;\n// 'mimicOnTtt' = a performance/recreation of work that originated in the TTT community\n// (requires a source reference); 'mimicOffTtt' = a performance/recreation of work from\n// outside TTT (covers covers + public-domain works, no source reference).\nexport const CraftSkillKindSchema = z.enum(['original', 'mimicOnTtt', 'mimicOffTtt']);\nexport type CraftSkillKind = z.infer<typeof CraftSkillKindSchema>;\n\n/** The ONE craft-skill kind that carries a `source` reference. The discriminated\n * CraftSkillSchema arm below and every upload-time validator derive from this named\n * declaration rather than re-quoting the member (ENG-005 / ARCH-102). */\nexport const CRAFT_SKILL_KIND_REQUIRING_SOURCE: CraftSkillKind = 'mimicOnTtt';\n\n// Source reference for a 'mimicOnTtt' skill — the original artisan OR the original work,\n// linked mention-style (id only, no name snapshots, resolved at render per the Display\n// Identity Invariant). 'workProject' is the safe framing today; linking a specific hall\n// item is optional future scope.\nexport const CraftSkillSourceReferenceSchema = z\n .object({\n type: z.enum(['user', 'workProject']),\n id: z.string().min(1),\n })\n .strict();\nexport type CraftSkillSourceReference = z.infer<typeof CraftSkillSourceReferenceSchema>;\n\n// The agreed attestation recorded on the doc: which statement (keyed by kind) the\n// uploader agreed to, its version, and when (epoch ms, always a number — never a\n// Firestore Timestamp).\nexport const CraftSkillAttestationSchema = z\n .object({\n statementKind: CraftSkillKindSchema,\n statementVersion: z.number().int().min(1),\n agreedAt: z.number(),\n })\n .strict();\nexport type CraftSkillAttestation = z.infer<typeof CraftSkillAttestationSchema>;\n\n// Base craft-skill fields shared across all kinds. The `kind` + `source` relationship is\n// layered on top as a discriminated union so an invalid combination (e.g. a source on an\n// 'original', or a missing source on a 'mimicOnTtt') fails schema validation.\nconst craftSkillBaseShape = {\n id: z.string(),\n name: z.string(),\n mediaAssetId: z.string(),\n // Canonical discipline-tag allowlist — never open strings (DJ ruling 2026-07-13).\n tags: z.array(z.enum(CRAFT_SKILL_TAG_VALUES)),\n createdAt: z.number(),\n type: mediaKindSchema,\n // Moderation visibility flag. Absent/false = visible; true = hidden by the\n // craft-skill hide cascade (report auto-hide or admin action). Mirrored onto\n // every taggedCraftSkills index doc so discovery surfaces can filter it.\n hidden: z.boolean(),\n attestation: CraftSkillAttestationSchema,\n};\n\nexport const CraftSkillSchema = z.discriminatedUnion('kind', [\n z.object({\n ...craftSkillBaseShape,\n kind: z.literal('original'),\n }),\n z.object({\n ...craftSkillBaseShape,\n kind: z.literal('mimicOnTtt'),\n // REQUIRED for the on-TTT mimic kind: the linked original artisan or work.\n source: CraftSkillSourceReferenceSchema,\n }),\n z.object({\n ...craftSkillBaseShape,\n kind: z.literal('mimicOffTtt'),\n }),\n]);\nexport type CraftSkill = z.infer<typeof CraftSkillSchema>;\n\nexport const CraftSkillReferenceSchema = z.object({\n craftSkillId: z.string(),\n compositeId: z.string(),\n userId: z.string(),\n craftSkillName: z.string(),\n craftSkillAssetId: z.string(),\n craftSkillType: mediaKindSchema,\n tags: z.array(z.enum(CRAFT_SKILL_TAG_VALUES)),\n createdAt: z.number(),\n // Mirror of CraftSkill.hidden; lets the tag-browse filter hidden skills out.\n hidden: z.boolean(),\n // Mirror of CraftSkill.kind so the tag-browse card can render the kind badge without a\n // second read.\n kind: CraftSkillKindSchema,\n // Mirror of CraftSkill.source — present ONLY when kind === 'mimicOnTtt' (the source-doc\n // discriminated union guarantees that invariant on the authoritative side). Lets the\n // browse card render the clickable \"someone performed my work\" source link.\n source: CraftSkillSourceReferenceSchema.optional(),\n});\nexport type CraftSkillReference = z.infer<typeof CraftSkillReferenceSchema>;\n\nexport const MinimalCraftSkillSchema = z.object({\n id: z.string(),\n name: z.string(),\n mediaAssetId: z.string(),\n type: mediaKindSchema,\n});\nexport type MinimalCraftSkill = z.infer<typeof MinimalCraftSkillSchema>;\n\nexport const OwnedWorkProjectSchema = z.object({\n workProjectId: z.string(),\n workingTitle: z.string(),\n workingDescription: z.string(),\n type: z.string(),\n createdOn: z.number(),\n hallWingType: z.enum(HALL_WING_TYPE_KEYS),\n});\nexport type OwnedWorkProject = z.infer<typeof OwnedWorkProjectSchema>;\n\nexport const AssociatedWorkProjectSchema = z.object({\n workProjectId: z.string(),\n workingTitle: z.string(),\n workingDescription: z.string(),\n type: z.string(),\n joinedOn: z.number(),\n});\nexport type AssociatedWorkProject = z.infer<typeof AssociatedWorkProjectSchema>;\n\nexport const FullUserSchema = z.object({\n uid: z.string(),\n displayName: z.string(),\n displayName_lowercase: z.string(),\n // One asset, variants full/medium/small in the mediaAssets registry; URLs\n // are built at render time (media-assets-and-protected-serving.md).\n profilePictureAssetId: z.string().nullable().optional(),\n artisanCreator: z.number().optional(),\n // Cosmetic supporter badge. Set true via the Admin SDK inside the Stripe webhook's\n // pledge-record transaction once the user completes a pledge; rendered on the profile\n // page. Lives here (not publicUsers) because userProfiles/{uid} is already readable by\n // any authenticated user, so other viewers see the badge with no mirror. Not PII.\n // Revoked (→ false) on refund once refunds ship.\n hasPledged: z.boolean().optional(),\n status: UserAccountStatusSchema.optional(),\n // Stamped by the one status writer on every status change: when, and the acting admin/operator\n // uid (a system actor writes its own id). Absent on an account whose status never changed.\n statusUpdatedAt: z.number().optional(),\n // Either the authenticated actor's uid or a stable namespaced system actor\n // identifier (for example `system:autoHashLock`); never null.\n statusUpdatedBy: z.string().min(1).optional(),\n // Chat-edge-rebuild account-access domain (Contract B / round-10 blocker 1): the\n // single ban/unban ordering version + state the chat `accountAccess` sync events key\n // on. Backend-only-writable. Deliberate defaults when ABSENT: a never-touched account\n // is `{ accountAccessVersion: 0, accountAccessState: 'active' }` (NOT banned) — bumped\n // atomically on suspend/ban/unban, which enqueue the accountAccessChanged fanout.\n accountAccessVersion: z.number().optional(),\n accountAccessState: UserAccountStatusSchema.optional(),\n // Moderation: set true by the forceDisplayNameReset callable when an admin resets an\n // abusive display name. The app gates the user into a forced \"pick a new name\" flow until\n // they complete it via setMyDisplayName (which clears this). Backend-only-writable (like `status`).\n displayNameResetRequired: z.boolean().optional(),\n ownedWorkProjects: z.array(OwnedWorkProjectSchema).optional(),\n associatedWorkProjects: z.array(AssociatedWorkProjectSchema).optional(),\n createdAt: z.number(),\n // N3 data-deletion / GDPR erasure: epoch ms when the account-deletion scrub\n // anonymized this profile in place. When set, the identity fields have been\n // scrubbed (displayName → the FORMER_MEMBER_DISPLAY_NAME sentinel) and the\n // account is a tombstone. Backend-only-writable.\n anonymizedAt: z.number().optional(),\n});\nexport type FullUser = z.infer<typeof FullUserSchema>;\n\nexport const UserAgreementsSchema = z.object({\n age: z.boolean().optional(),\n nudity: z.boolean().optional(),\n meet: z.boolean().optional(),\n cookies: z.boolean().optional(),\n terms: z.boolean().optional(),\n agreedOn: z.number().optional(),\n /**\n * The Terms / Privacy versions accepted at registration, read server-side from the live\n * docs at acceptance time — never client-supplied, never a hardcoded constant. Kept for the\n * accounts that recorded them; the current per-document record is\n * `privateData.publicDocumentAcceptance`.\n *\n * `0` is the sentinel for \"registered while no legal document was published\" (a fresh\n * environment's first account). Published versions start at 1, so 0 can never equal a live\n * version — which is deliberate: it keeps the record from claiming consent that never\n * happened, and leaves the version-bump re-acceptance path free to catch the account once\n * real legal content ships.\n */\n termsVersion: z.number().int().nonnegative().optional(),\n privacyVersion: z.number().int().nonnegative().optional(),\n});\nexport type UserAgreements = z.infer<typeof UserAgreementsSchema>;\n\n/**\n * First-visit site-tour state (account-durable, server-written) stored on\n * `privateData/{uid}.siteTour`. A missing `siteTour` field means the member has never\n * handled the tour. Automatic-invitation eligibility is evaluated in this order:\n * 1. `automaticInvitesDisabledAt` present → never invite automatically (\"Don't show\n * this again\");\n * 2. `notTodayDate` equals the member's current LOCAL calendar date → do not invite\n * that day (\"Not today\");\n * 3. `completedVersion` equals `SITE_TOUR_CURRENT_VERSION` → already handled;\n * 4. otherwise → offer the tour.\n * Written ONLY by the `updateSiteTourPreference` callable via the Admin SDK; never\n * client-written. `notTodayDate` is a strict `YYYY-MM-DD` local-date string (same\n * date-string convention as the age cluster's `attestedDateOfBirth`). See\n * docs/design/landing-backstage-guide-and-first-visit-plan.md §10.\n */\nexport const UserSiteTourStateSchema = z.object({\n // The tour version the member completed. The callable stamps SITE_TOUR_CURRENT_VERSION\n // server-side at completion (the version is server-owned, never client-supplied);\n // absent until a completion is recorded.\n completedVersion: z.number().int().optional(),\n // Epoch ms when the tour was completed.\n completedAt: z.number().optional(),\n // The member's LOCAL calendar date (YYYY-MM-DD) recorded by \"Not today\"; the automatic\n // invitation is suppressed for exactly that date, then offered again on a later date.\n notTodayDate: calendarDateSchema.optional(), // 'YYYY-MM-DD'\n // Epoch ms when the member chose \"Don't show this again\" (permanent automatic-invite\n // dismissal). Manual replay from Help ignores this without clearing it.\n automaticInvitesDisabledAt: z.number().optional(),\n});\nexport type UserSiteTourState = z.infer<typeof UserSiteTourStateSchema>;\n\n/**\n * Owner-only account state at `userProfiles/{uid}/privateData/{uid}` (readable\n * only by the owner; never mirrored to publicUsers).\n */\nexport const UserPrivateDataSchema = z.object({\n email: z.string(),\n // Epoch ms when the user applied for news/political posting (not a boolean) so the\n // applicant queue can be ordered/triaged by application date. Mirrors the\n // `artisanCreator?: number` grant-timestamp pattern. Truthy when set, so existing\n // truthiness reads keep working; absent until the user applies.\n isWaitingForNewsApproval: z.number().optional(),\n // Epoch ms when the user attested they are a U.S.-based person while becoming an artisan\n // (required at artisan signup for now). Mirrors the ageAttested attestation pattern; written\n // server-side by becomeArtisanCreator.\n usPersonAttestedAt: z.number().optional(),\n // Non-U.S. artisan-interest \"waitlist\" recorded ONLY for adult accounts when a non-U.S. person\n // asks to be notified once creator signups open in their region. Owner-only, backend-written.\n // `requestedAt` is the immutable first-come-first-serve timestamp; `country`/`region` let signups\n // be opened by jurisdiction as each region's laws clear. Mirrors isWaitingForNewsApproval.\n nonUsArtisanInterest: z\n .object({\n country: z.string().min(1).max(MAX_ARTISAN_LOCATION_LENGTH),\n region: z.string().max(MAX_ARTISAN_LOCATION_LENGTH).optional(),\n requestedAt: z.number(),\n })\n .optional(),\n squareStreetzAgreementsDate: z.number().optional(),\n // The rules-and-agreements `version` in force when the user accepted; 0 when no versioned\n // rules doc existed yet, so a later real version 1 still re-prompts.\n squareStreetzAgreementsVersion: z.number().optional(),\n // The rules-and-agreements version in force when the user accepted the Artisan upgrade's\n // agreement, read server-side in the grant transaction (the grant time is the profile's\n // `artisanCreator` stamp). Same 0 sentinel as `squareStreetzAgreementsVersion`.\n artisanCreatorAgreementsVersion: z.number().int().nonnegative().optional(),\n // Epoch ms when the user accepted the one-time Hall download acknowledgement\n // (personal offline use only, no redistribution). Written server-side by the\n // acceptHallDownloadAcknowledgement callable; gates the Hall download button.\n hallDownloadAcknowledgedAt: z.number().optional(),\n agreements: UserAgreementsSchema.optional(),\n // The latest accepted version of each public document and the accepted level (see\n // PublicDocumentAcceptanceSchema). Written by registration and the acceptance callable only;\n // the `docsAccepted` claim mirrors `acceptedLevel`. Absent = nothing accepted (level 0).\n publicDocumentAcceptance: PublicDocumentAcceptanceSchema.optional(),\n // First-visit site-tour state (account-durable; the SOLE authority for tour\n // eligibility — no local-storage cache/mirror). Absent = never handled. Written only\n // by the updateSiteTourPreference callable. See UserSiteTourStateSchema above.\n siteTour: UserSiteTourStateSchema.optional(),\n // Moderation: human-readable reason for the current account status (set when an\n // admin suspends/bans the user). Written by the setUserStatus callable via the\n // Admin SDK; readable only by the owner (shown in their restricted view).\n // Cleared when the account is reinstated to 'active'.\n statusReason: z.string().optional(),\n statusReasonAt: z.number().optional(),\n // Trust & Safety (§A8): silent interim safety-lock flag. Backend-only-writable,\n // restricted to the owner's view. Set true while a child-safety/NCII action is\n // pending against the account; the app gates the user accordingly.\n safetyLocked: z.boolean().optional(),\n // Trust & Safety (§A7): server-write-only age/registration fields, merged from the\n // standalone age cluster shape (./safety/age.js) so the canonical schema and the\n // age cluster cannot drift.\n ...userPrivateDataAgeFieldsShape,\n});\nexport type UserPrivateData = z.infer<typeof UserPrivateDataSchema>;\n"]}
|
|
1
|
+
{"version":3,"file":"user.js","sourceRoot":"","sources":["../../src/doc-schemas/user.ts"],"names":[],"mappings":"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","sourcesContent":["// User-related Firestore document SCHEMAS — the single source of truth for the\n// `userProfiles/{uid}` doc, its `privateData/{uid}` subdoc, and craft-skill shapes.\n// (The `publicUsers/{uid}` mirror lives in ./publicUser.) TypeScript types are\n// inferred from these schemas via `z.infer`, so a document's shape and its type\n// cannot drift apart. See docs/design/firestore-schema-registry.md (ttt-prod).\n\nimport { z } from 'zod';\nimport { HALL_WING_TYPE_KEYS } from '../types/content.js';\nimport { MAX_ARTISAN_LOCATION_LENGTH } from '../constants/business.js';\nimport { CRAFT_SKILL_TAG_VALUES } from '../constants/options.js';\nimport { userPrivateDataAgeFieldsShape } from './safety/age.js';\nimport { ContentMediaKindSchema } from './media-assets.js';\nimport { calendarDateSchema } from '../schemas/atoms.js';\nimport { PublicDocumentAcceptanceSchema, PublicDocumentVersionOrNoneSchema } from './public-documents.js';\n\n// The canonical stored media kind — declared once in doc-schemas/media-assets.ts.\nconst mediaKindSchema = ContentMediaKindSchema;\n\n/** The ONE canonical application account status (userProfiles.status is the\n * authoritative value; the chat access projection and the status-reconcile queue\n * derive from the same set). Never re-declare inline. */\nexport const UserAccountStatusSchema = z.enum(['active', 'suspended', 'banned']);\nexport type UserAccountStatus = z.infer<typeof UserAccountStatusSchema>;\n\n// Craft-skill kind — mandatory choice at upload, no default (see ttt-prod\n// docs/design/profile-and-craft-skills.md). 'original' = the artisan's own original creation;\n// 'mimicOnTtt' = a performance/recreation of work that originated in the TTT community\n// (requires a source reference); 'mimicOffTtt' = a performance/recreation of work from\n// outside TTT (covers covers + public-domain works, no source reference).\nexport const CraftSkillKindSchema = z.enum(['original', 'mimicOnTtt', 'mimicOffTtt']);\nexport type CraftSkillKind = z.infer<typeof CraftSkillKindSchema>;\n\n/** The ONE craft-skill kind that carries a `source` reference. The discriminated\n * CraftSkillSchema arm below and every upload-time validator derive from this named\n * declaration rather than re-quoting the member (ENG-005 / ARCH-102). */\nexport const CRAFT_SKILL_KIND_REQUIRING_SOURCE: CraftSkillKind = 'mimicOnTtt';\n\n// Source reference for a 'mimicOnTtt' skill — the original artisan OR the original work,\n// linked mention-style (id only, no name snapshots, resolved at render per the Display\n// Identity Invariant). 'workProject' is the safe framing today; linking a specific hall\n// item is optional future scope.\nexport const CraftSkillSourceReferenceSchema = z\n .object({\n type: z.enum(['user', 'workProject']),\n id: z.string().min(1),\n })\n .strict();\nexport type CraftSkillSourceReference = z.infer<typeof CraftSkillSourceReferenceSchema>;\n\n// The agreed attestation recorded on the doc: which statement (keyed by kind) the\n// uploader agreed to, its version, and when (epoch ms, always a number — never a\n// Firestore Timestamp).\nexport const CraftSkillAttestationSchema = z\n .object({\n statementKind: CraftSkillKindSchema,\n statementVersion: z.number().int().min(1),\n agreedAt: z.number(),\n })\n .strict();\nexport type CraftSkillAttestation = z.infer<typeof CraftSkillAttestationSchema>;\n\n// Base craft-skill fields shared across all kinds. The `kind` + `source` relationship is\n// layered on top as a discriminated union so an invalid combination (e.g. a source on an\n// 'original', or a missing source on a 'mimicOnTtt') fails schema validation.\nconst craftSkillBaseShape = {\n id: z.string(),\n name: z.string(),\n mediaAssetId: z.string(),\n // Canonical discipline-tag allowlist — never open strings (DJ ruling 2026-07-13).\n tags: z.array(z.enum(CRAFT_SKILL_TAG_VALUES)),\n createdAt: z.number(),\n type: mediaKindSchema,\n // Moderation visibility flag. Absent/false = visible; true = hidden by the\n // craft-skill hide cascade (report auto-hide or admin action). Mirrored onto\n // every taggedCraftSkills index doc so discovery surfaces can filter it.\n hidden: z.boolean(),\n attestation: CraftSkillAttestationSchema,\n};\n\nexport const CraftSkillSchema = z.discriminatedUnion('kind', [\n z.object({\n ...craftSkillBaseShape,\n kind: z.literal('original'),\n }),\n z.object({\n ...craftSkillBaseShape,\n kind: z.literal('mimicOnTtt'),\n // REQUIRED for the on-TTT mimic kind: the linked original artisan or work.\n source: CraftSkillSourceReferenceSchema,\n }),\n z.object({\n ...craftSkillBaseShape,\n kind: z.literal('mimicOffTtt'),\n }),\n]);\nexport type CraftSkill = z.infer<typeof CraftSkillSchema>;\n\nexport const CraftSkillReferenceSchema = z.object({\n craftSkillId: z.string(),\n compositeId: z.string(),\n userId: z.string(),\n craftSkillName: z.string(),\n craftSkillAssetId: z.string(),\n craftSkillType: mediaKindSchema,\n tags: z.array(z.enum(CRAFT_SKILL_TAG_VALUES)),\n createdAt: z.number(),\n // Mirror of CraftSkill.hidden; lets the tag-browse filter hidden skills out.\n hidden: z.boolean(),\n // Mirror of CraftSkill.kind so the tag-browse card can render the kind badge without a\n // second read.\n kind: CraftSkillKindSchema,\n // Mirror of CraftSkill.source — present ONLY when kind === 'mimicOnTtt' (the source-doc\n // discriminated union guarantees that invariant on the authoritative side). Lets the\n // browse card render the clickable \"someone performed my work\" source link.\n source: CraftSkillSourceReferenceSchema.optional(),\n});\nexport type CraftSkillReference = z.infer<typeof CraftSkillReferenceSchema>;\n\nexport const MinimalCraftSkillSchema = z.object({\n id: z.string(),\n name: z.string(),\n mediaAssetId: z.string(),\n type: mediaKindSchema,\n});\nexport type MinimalCraftSkill = z.infer<typeof MinimalCraftSkillSchema>;\n\nexport const OwnedWorkProjectSchema = z.object({\n workProjectId: z.string(),\n workingTitle: z.string(),\n workingDescription: z.string(),\n type: z.string(),\n createdOn: z.number(),\n hallWingType: z.enum(HALL_WING_TYPE_KEYS),\n});\nexport type OwnedWorkProject = z.infer<typeof OwnedWorkProjectSchema>;\n\nexport const AssociatedWorkProjectSchema = z.object({\n workProjectId: z.string(),\n workingTitle: z.string(),\n workingDescription: z.string(),\n type: z.string(),\n joinedOn: z.number(),\n});\nexport type AssociatedWorkProject = z.infer<typeof AssociatedWorkProjectSchema>;\n\nexport const FullUserSchema = z.object({\n uid: z.string(),\n displayName: z.string(),\n displayName_lowercase: z.string(),\n // One asset, variants full/medium/small in the mediaAssets registry; URLs\n // are built at render time (media-assets-and-protected-serving.md).\n profilePictureAssetId: z.string().nullable().optional(),\n artisanCreator: z.number().optional(),\n // Cosmetic supporter badge. Set true via the Admin SDK inside the Stripe webhook's\n // pledge-record transaction once the user completes a pledge; rendered on the profile\n // page. Lives here (not publicUsers) because userProfiles/{uid} is already readable by\n // any authenticated user, so other viewers see the badge with no mirror. Not PII.\n // Revoked (→ false) on refund once refunds ship.\n hasPledged: z.boolean().optional(),\n status: UserAccountStatusSchema.optional(),\n // Stamped by the one status writer on every status change: when, and the acting admin/operator\n // uid (a system actor writes its own id). Absent on an account whose status never changed.\n statusUpdatedAt: z.number().optional(),\n // Either the authenticated actor's uid or a stable namespaced system actor\n // identifier (for example `system:autoHashLock`); never null.\n statusUpdatedBy: z.string().min(1).optional(),\n // Chat-edge-rebuild account-access domain (Contract B / round-10 blocker 1): the\n // single ban/unban ordering version + state the chat `accountAccess` sync events key\n // on. Backend-only-writable. Deliberate defaults when ABSENT: a never-touched account\n // is `{ accountAccessVersion: 0, accountAccessState: 'active' }` (NOT banned) — bumped\n // atomically on suspend/ban/unban, which enqueue the accountAccessChanged fanout.\n accountAccessVersion: z.number().optional(),\n accountAccessState: UserAccountStatusSchema.optional(),\n // Moderation: set true by the forceDisplayNameReset callable when an admin resets an\n // abusive display name. The app gates the user into a forced \"pick a new name\" flow until\n // they complete it via setMyDisplayName (which clears this). Backend-only-writable (like `status`).\n displayNameResetRequired: z.boolean().optional(),\n ownedWorkProjects: z.array(OwnedWorkProjectSchema).optional(),\n associatedWorkProjects: z.array(AssociatedWorkProjectSchema).optional(),\n createdAt: z.number(),\n // N3 data-deletion / GDPR erasure: epoch ms when the account-deletion scrub\n // anonymized this profile in place. When set, the identity fields have been\n // scrubbed (displayName → the FORMER_MEMBER_DISPLAY_NAME sentinel) and the\n // account is a tombstone. Backend-only-writable.\n anonymizedAt: z.number().optional(),\n});\nexport type FullUser = z.infer<typeof FullUserSchema>;\n\nexport const UserAgreementsSchema = z.object({\n age: z.boolean().optional(),\n nudity: z.boolean().optional(),\n meet: z.boolean().optional(),\n cookies: z.boolean().optional(),\n terms: z.boolean().optional(),\n agreedOn: z.number().optional(),\n /**\n * The Terms / Privacy versions accepted at registration, read server-side from the live\n * docs at acceptance time — never client-supplied, never a hardcoded constant. Kept for the\n * accounts that recorded them; the current per-document record is\n * `privateData.publicDocumentAcceptance`.\n *\n * `0` is the sentinel for \"registered while no legal document was published\" (a fresh\n * environment's first account). Published versions start at 1, so 0 can never equal a live\n * version — which is deliberate: it keeps the record from claiming consent that never\n * happened, and leaves the version-bump re-acceptance path free to catch the account once\n * real legal content ships.\n */\n termsVersion: z.number().int().nonnegative().optional(),\n privacyVersion: z.number().int().nonnegative().optional(),\n});\nexport type UserAgreements = z.infer<typeof UserAgreementsSchema>;\n\n/**\n * First-visit site-tour state (account-durable, server-written) stored on\n * `privateData/{uid}.siteTour`. A missing `siteTour` field means the member has never\n * handled the tour. Automatic-invitation eligibility is evaluated in this order:\n * 1. `automaticInvitesDisabledAt` present → never invite automatically (\"Don't show\n * this again\");\n * 2. `notTodayDate` equals the member's current LOCAL calendar date → do not invite\n * that day (\"Not today\");\n * 3. `completedVersion` equals `SITE_TOUR_CURRENT_VERSION` → already handled;\n * 4. otherwise → offer the tour.\n * Written ONLY by the `updateSiteTourPreference` callable via the Admin SDK; never\n * client-written. `notTodayDate` is a strict `YYYY-MM-DD` local-date string (same\n * date-string convention as the age cluster's `attestedDateOfBirth`). See\n * docs/design/landing-backstage-guide-and-first-visit-plan.md §10.\n */\nexport const UserSiteTourStateSchema = z.object({\n // The tour version the member completed. The callable stamps SITE_TOUR_CURRENT_VERSION\n // server-side at completion (the version is server-owned, never client-supplied);\n // absent until a completion is recorded.\n completedVersion: z.number().int().optional(),\n // Epoch ms when the tour was completed.\n completedAt: z.number().optional(),\n // The member's LOCAL calendar date (YYYY-MM-DD) recorded by \"Not today\"; the automatic\n // invitation is suppressed for exactly that date, then offered again on a later date.\n notTodayDate: calendarDateSchema.optional(), // 'YYYY-MM-DD'\n // Epoch ms when the member chose \"Don't show this again\" (permanent automatic-invite\n // dismissal). Manual replay from Help ignores this without clearing it.\n automaticInvitesDisabledAt: z.number().optional(),\n});\nexport type UserSiteTourState = z.infer<typeof UserSiteTourStateSchema>;\n\n/**\n * Owner-only account state at `userProfiles/{uid}/privateData/{uid}` (readable\n * only by the owner; never mirrored to publicUsers).\n */\nexport const UserPrivateDataSchema = z.object({\n email: z.string(),\n // Epoch ms when the user applied for news/political posting (not a boolean) so the\n // applicant queue can be ordered/triaged by application date. Mirrors the\n // `artisanCreator?: number` grant-timestamp pattern. Truthy when set, so existing\n // truthiness reads keep working; absent until the user applies.\n isWaitingForNewsApproval: z.number().optional(),\n // Epoch ms when the user attested they are a U.S.-based person while becoming an artisan\n // (required at artisan signup for now). Mirrors the ageAttested attestation pattern; written\n // server-side by becomeArtisanCreator.\n usPersonAttestedAt: z.number().optional(),\n // Non-U.S. artisan-interest \"waitlist\" recorded ONLY for adult accounts when a non-U.S. person\n // asks to be notified once creator signups open in their region. Owner-only, backend-written.\n // `requestedAt` is the immutable first-come-first-serve timestamp; `country`/`region` let signups\n // be opened by jurisdiction as each region's laws clear. Mirrors isWaitingForNewsApproval.\n nonUsArtisanInterest: z\n .object({\n country: z.string().min(1).max(MAX_ARTISAN_LOCATION_LENGTH),\n region: z.string().max(MAX_ARTISAN_LOCATION_LENGTH).optional(),\n requestedAt: z.number(),\n })\n .optional(),\n squareStreetzAgreementsDate: z.number().optional(),\n // The rules-and-agreements `version` in force when the user accepted; 0 when no versioned\n // rules doc existed yet, so a later real version 1 still re-prompts.\n squareStreetzAgreementsVersion: PublicDocumentVersionOrNoneSchema.optional(),\n // The rules-and-agreements version in force when the user accepted the Artisan upgrade's\n // agreement, read server-side in the grant transaction (the grant time is the profile's\n // `artisanCreator` stamp). Same 0 sentinel as `squareStreetzAgreementsVersion`.\n artisanCreatorAgreementsVersion: PublicDocumentVersionOrNoneSchema.optional(),\n // Epoch ms when the user accepted the one-time Hall download acknowledgement\n // (personal offline use only, no redistribution). Written server-side by the\n // acceptHallDownloadAcknowledgement callable; gates the Hall download button.\n hallDownloadAcknowledgedAt: z.number().optional(),\n agreements: UserAgreementsSchema.optional(),\n // The latest accepted version of each public document and the accepted level (see\n // PublicDocumentAcceptanceSchema). Written by registration and the acceptance callable only;\n // the `docsAccepted` claim mirrors `acceptedLevel`. Absent = nothing accepted (level 0).\n publicDocumentAcceptance: PublicDocumentAcceptanceSchema.optional(),\n // First-visit site-tour state (account-durable; the SOLE authority for tour\n // eligibility — no local-storage cache/mirror). Absent = never handled. Written only\n // by the updateSiteTourPreference callable. See UserSiteTourStateSchema above.\n siteTour: UserSiteTourStateSchema.optional(),\n // Moderation: human-readable reason for the current account status (set when an\n // admin suspends/bans the user). Written by the setUserStatus callable via the\n // Admin SDK; readable only by the owner (shown in their restricted view).\n // Cleared when the account is reinstated to 'active'.\n statusReason: z.string().optional(),\n statusReasonAt: z.number().optional(),\n // Trust & Safety (§A8): silent interim safety-lock flag. Backend-only-writable,\n // restricted to the owner's view. Set true while a child-safety/NCII action is\n // pending against the account; the app gates the user accordingly.\n safetyLocked: z.boolean().optional(),\n // Trust & Safety (§A7): server-write-only age/registration fields, merged from the\n // standalone age cluster shape (./safety/age.js) so the canonical schema and the\n // age cluster cannot drift.\n ...userPrivateDataAgeFieldsShape,\n});\nexport type UserPrivateData = z.infer<typeof UserPrivateDataSchema>;\n"]}
|
package/dist/schemas/users.d.ts
CHANGED
|
@@ -4,6 +4,19 @@ import { z } from 'zod';
|
|
|
4
4
|
export declare const displayNameSchema: z.ZodString;
|
|
5
5
|
export declare const AcceptSquareStreetzAgreementsInputSchema: z.ZodObject<{}, z.core.$strict>;
|
|
6
6
|
export type AcceptSquareStreetzAgreementsInput = z.infer<typeof AcceptSquareStreetzAgreementsInputSchema>;
|
|
7
|
+
/**
|
|
8
|
+
* `social.squareStreetzAgreementsAccepted` — the `metadata` of the event written with the
|
|
9
|
+
* private acceptance record. The card incorporates the Rules & Agreements page by reference, so
|
|
10
|
+
* the version is that page's: `acceptedVersion` is the one this acceptance recorded (0 while no
|
|
11
|
+
* Rules version is published), and `previousAcceptedVersion` the one the record held before,
|
|
12
|
+
* null when none was recorded (the first acceptance). The actor and server time ride the event
|
|
13
|
+
* envelope.
|
|
14
|
+
*/
|
|
15
|
+
export declare const SquareStreetzAgreementsAcceptedAuditPayloadSchema: z.ZodObject<{
|
|
16
|
+
acceptedVersion: z.ZodNumber;
|
|
17
|
+
previousAcceptedVersion: z.ZodNullable<z.ZodNumber>;
|
|
18
|
+
}, z.core.$strict>;
|
|
19
|
+
export type SquareStreetzAgreementsAcceptedAuditPayload = z.infer<typeof SquareStreetzAgreementsAcceptedAuditPayloadSchema>;
|
|
7
20
|
export declare const AcceptHallDownloadAcknowledgementInputSchema: z.ZodObject<{}, z.core.$strict>;
|
|
8
21
|
export type AcceptHallDownloadAcknowledgementInput = z.infer<typeof AcceptHallDownloadAcknowledgementInputSchema>;
|
|
9
22
|
export declare const BecomeArtisanCreatorInputSchema: z.ZodObject<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/schemas/users.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/schemas/users.ts"],"names":[],"mappings":"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"}
|
package/dist/schemas/users.js
CHANGED
|
@@ -2,6 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { userIdSchema, calendarDateSchema } from './atoms.js';
|
|
3
3
|
import { USERNAME_MIN_LENGTH, USERNAME_MAX_LENGTH, USERNAME_REGEX, MAX_ARTISAN_LOCATION_LENGTH, MAX_INTERNAL_REASON_LENGTH, MAX_USER_SEARCH_QUERY_LENGTH, } from '../constants/business.js';
|
|
4
4
|
import { UserAccountStatusSchema } from '../doc-schemas/user.js';
|
|
5
|
+
import { PublicDocumentVersionOrNoneSchema } from '../doc-schemas/public-documents.js';
|
|
5
6
|
import { ShownPublicDocumentVersionsSchema, PublicDocumentsRefreshRequiredResultSchema, } from './public-documents.js';
|
|
6
7
|
/** The ONE composed display-name field validator (length bounds + charset). Every
|
|
7
8
|
* callable accepting a display name spreads this — never a re-composed copy. */
|
|
@@ -11,6 +12,18 @@ export const displayNameSchema = z
|
|
|
11
12
|
.max(USERNAME_MAX_LENGTH)
|
|
12
13
|
.regex(USERNAME_REGEX);
|
|
13
14
|
export const AcceptSquareStreetzAgreementsInputSchema = z.object({}).strict();
|
|
15
|
+
/**
|
|
16
|
+
* `social.squareStreetzAgreementsAccepted` — the `metadata` of the event written with the
|
|
17
|
+
* private acceptance record. The card incorporates the Rules & Agreements page by reference, so
|
|
18
|
+
* the version is that page's: `acceptedVersion` is the one this acceptance recorded (0 while no
|
|
19
|
+
* Rules version is published), and `previousAcceptedVersion` the one the record held before,
|
|
20
|
+
* null when none was recorded (the first acceptance). The actor and server time ride the event
|
|
21
|
+
* envelope.
|
|
22
|
+
*/
|
|
23
|
+
export const SquareStreetzAgreementsAcceptedAuditPayloadSchema = z.object({
|
|
24
|
+
acceptedVersion: PublicDocumentVersionOrNoneSchema,
|
|
25
|
+
previousAcceptedVersion: PublicDocumentVersionOrNoneSchema.nullable(),
|
|
26
|
+
}).strict();
|
|
14
27
|
// One-time acknowledgement gate before a user may download Hall content for personal
|
|
15
28
|
// offline use (final wording owned by the legal-and-content step). No payload — the
|
|
16
29
|
// callable records the timestamp in the caller's privateData. See
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users.js","sourceRoot":"","sources":["../../src/schemas/users.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport { userIdSchema, calendarDateSchema } from './atoms.js';\nimport {\n USERNAME_MIN_LENGTH,\n USERNAME_MAX_LENGTH,\n USERNAME_REGEX,\n MAX_ARTISAN_LOCATION_LENGTH,\n MAX_INTERNAL_REASON_LENGTH,\n MAX_USER_SEARCH_QUERY_LENGTH,\n} from '../constants/business.js';\nimport { UserAccountStatusSchema } from '../doc-schemas/user.js';\nimport {\n ShownPublicDocumentVersionsSchema,\n PublicDocumentsRefreshRequiredResultSchema,\n} from './public-documents.js';\n\n/** The ONE composed display-name field validator (length bounds + charset). Every\n * callable accepting a display name spreads this — never a re-composed copy. */\nexport const displayNameSchema = z\n .string()\n .min(USERNAME_MIN_LENGTH)\n .max(USERNAME_MAX_LENGTH)\n .regex(USERNAME_REGEX);\n\nexport const AcceptSquareStreetzAgreementsInputSchema = z.object({}).strict();\nexport type AcceptSquareStreetzAgreementsInput = z.infer<typeof AcceptSquareStreetzAgreementsInputSchema>;\n\n// One-time acknowledgement gate before a user may download Hall content for personal\n// offline use (final wording owned by the legal-and-content step). No payload — the\n// callable records the timestamp in the caller's privateData. See\n// docs/design/media-assets-and-protected-serving.md (Hall/Library downloads).\nexport const AcceptHallDownloadAcknowledgementInputSchema = z.object({}).strict();\nexport type AcceptHallDownloadAcknowledgementInput = z.infer<typeof AcceptHallDownloadAcknowledgementInputSchema>;\n\n// The become-creator dialog's \"I confirm I am 18 years of age or older\" checkbox must reach the\n// server: the callable rejects when absent and records the attestation on the\n// artisanCreator.grantedToUser audit event (immutable, timestamped, with IP/UA) so it is provable.\nexport const BecomeArtisanCreatorInputSchema = z.object({\n ageAttested: z.literal(true),\n // \"I confirm I am a person located in the United States\" — required for now (only U.S. persons\n // may become artisans). Mirrors ageAttested: the callable rejects when absent and records the\n // attestation timestamp (privateData.usPersonAttestedAt) + the grant audit event.\n usPersonAttested: z.literal(true),\n // [Q21.5 REVISED — DJ 2026-07-06] The artisan's date of birth — the ONLY point in the system\n // where a DOB is collected for persistence (registration/upgrade derive the bracket and store\n // nothing). The dialog copy tells the user this must be THEIR birthday: when the payout system\n // ships, it has to match their payout (KYC) identity. The callable re-derives 18+ server-side\n // from this value; an under-18 derivation rejects (failure audit, nothing stored). Stored as\n // privateData attestedDateOfBirth + attestedDobSource 'artisanOnboarding'.\n dob: z.object({\n year: z.number().int(),\n month: z.number().int().min(1).max(12),\n day: z.number().int().min(1).max(31),\n }).strict(),\n}).strict();\nexport type BecomeArtisanCreatorInput = z.infer<typeof BecomeArtisanCreatorInputSchema>;\n\nexport const MarkWaitingForNewsApprovalInputSchema = z.object({}).strict();\nexport type MarkWaitingForNewsApprovalInput = z.infer<typeof MarkWaitingForNewsApprovalInputSchema>;\n\n// First-visit site-tour preference writer (updateSiteTourPreference). This is the ONE\n// server writer for `privateData/{uid}.siteTour`; the client never writes tour state\n// directly. Discriminated on `action`:\n// - deferToday: records the member's LOCAL calendar `date` (\"Not today\"); the automatic\n// invitation is suppressed for exactly that date.\n// - dismissAutomaticInvites: records the permanent \"Don't show this again\" time; no payload.\n// - completeTour: records completion; the callable stamps SITE_TOUR_CURRENT_VERSION and\n// completedAt server-side, so no version is accepted from the client (mirrors the\n// acknowledgement callables that record the authoritative value server-side rather than\n// trusting a client-supplied one). Bump SITE_TOUR_CURRENT_VERSION to re-invite everyone.\n// `date` uses the same strict YYYY-MM-DD convention as the age cluster's attestedDateOfBirth.\nexport const UpdateSiteTourPreferenceInputSchema = z.discriminatedUnion('action', [\n z.object({\n action: z.literal('deferToday'),\n date: calendarDateSchema, // 'YYYY-MM-DD' (member's LOCAL calendar date)\n }).strict(),\n z.object({\n action: z.literal('dismissAutomaticInvites'),\n }).strict(),\n z.object({\n action: z.literal('completeTour'),\n }).strict(),\n]);\nexport type UpdateSiteTourPreferenceInput = z.infer<typeof UpdateSiteTourPreferenceInputSchema>;\n\n// The device-local shape behind the ONE sanctioned optimistic-hide exception: a site-tour choice\n// the member has already made, persisted at `siteTourPendingStorageKey(uid)`\n// (constants/storage-keys.ts) while its `updateSiteTourPreference` write is unconfirmed. It exists\n// so the overlay can close on the click instead of after a callable round-trip, and so a failed\n// write is replayed on the next boot rather than silently lost.\n//\n// It is NOT a cache, mirror, or second authority: `privateData/{uid}.siteTour` remains the sole\n// source of tour eligibility, and this entry only OVERLAYS it (suppressing an invitation the member\n// has already dismissed) until the server state converges.\n//\n// Every field is load-bearing against a specific failure:\n// - `schemaVersion` lets a future shape change be recognized instead of silently mis-parsed.\n// - `id` is the compare-and-remove token: on callable success the owner removes the entry ONLY if\n// the stored id still matches the one it sent, so a slow response in tab A can never erase a\n// newer choice made in tab B.\n// - `uid` must equal the authenticated key owner before the entry may affect eligibility or be\n// replayed — defence in depth behind the uid-scoped key itself.\n// - `createdAt` is epoch ms, for ordering/diagnostics only; it is never an expiry.\n// - `completeTour` carries the `tourVersion` the member actually completed. On reconciliation an\n// entry whose version differs from SITE_TOUR_CURRENT_VERSION is STALE and is discarded, so an\n// old pending completion can never complete a newer tour. The callable payload stays\n// version-free (the server stamps the authoritative version) — this is a replay guard, not\n// server authority.\n//\n// STRICT and fully validated on read: malformed or unknown persisted data is removed and\n// eligibility falls back to the server. Invalid storage must never hide the tour forever.\nexport const SiteTourPendingPreferenceSchema = z.discriminatedUnion('action', [\n z.object({\n schemaVersion: z.literal(1),\n id: z.string().min(1),\n uid: userIdSchema,\n createdAt: z.number(),\n action: z.literal('deferToday'),\n date: calendarDateSchema, // 'YYYY-MM-DD' (member's LOCAL calendar date)\n }).strict(),\n z.object({\n schemaVersion: z.literal(1),\n id: z.string().min(1),\n uid: userIdSchema,\n createdAt: z.number(),\n action: z.literal('dismissAutomaticInvites'),\n }).strict(),\n z.object({\n schemaVersion: z.literal(1),\n id: z.string().min(1),\n uid: userIdSchema,\n createdAt: z.number(),\n action: z.literal('completeTour'),\n tourVersion: z.number().int().positive(),\n }).strict(),\n]);\nexport type SiteTourPendingPreference = z.infer<typeof SiteTourPendingPreferenceSchema>;\n\n// Non-U.S. artisan-interest signup (the FCFS \"waitlist\" fields on privateData). Mirrors\n// MarkWaitingForNewsApproval but carries the applicant's country (+ optional region) so signups\n// can be opened by jurisdiction as each region's laws clear. The callable records it ONLY for\n// adult accounts (never a minor's location).\nexport const MarkNonUsArtisanInterestInputSchema = z.object({\n country: z.string().min(1).max(MAX_ARTISAN_LOCATION_LENGTH),\n region: z.string().max(MAX_ARTISAN_LOCATION_LENGTH).optional(),\n}).strict();\nexport type MarkNonUsArtisanInterestInput = z.infer<typeof MarkNonUsArtisanInterestInputSchema>;\n\nexport const RegisterUserInputSchema = z.object({\n displayName: displayNameSchema,\n agreements: z.object({\n age: z.literal(true),\n nudity: z.literal(true),\n meet: z.literal(true),\n cookies: z.literal(true),\n terms: z.literal(true),\n }).strict(),\n // The public-document versions the signup page SHOWED when the person agreed: every published\n // document at its current version, `changedPublicDocuments(block, undefined)` off the live\n // `_appConfig/app` version block. Empty before the first release (the first-admin bootstrap).\n // Registration re-reads the block in its transaction and answers `refreshRequired`, writing\n // nothing, when a publish raced the page — the same comparison the acceptance prompt's Accept makes.\n publicDocuments: ShownPublicDocumentVersionsSchema,\n}).strict();\nexport type RegisterUserInput = z.infer<typeof RegisterUserInputSchema>;\n\nexport const RegisterUserResultSchema = z.discriminatedUnion('status', [\n // The account is registered with the documents shown accepted (or already was — an idempotent\n // replay). Refresh the token: the `docsAccepted` claim was set after the commit.\n z.object({ status: z.literal('registered') }),\n PublicDocumentsRefreshRequiredResultSchema,\n]);\nexport type RegisterUserResult = z.infer<typeof RegisterUserResultSchema>;\n\nexport const SetUserStatusInputSchema = z.object({\n userId: userIdSchema,\n status: UserAccountStatusSchema,\n // Required by the callable when status is 'suspended' or 'banned' (shown to the\n // user in their restricted view and recorded on the audit event). Optional here\n // so reinstating to 'active' can omit it; the callable enforces presence.\n reason: z.string().trim().min(1).max(MAX_INTERNAL_REASON_LENGTH).optional(),\n}).strict();\nexport type SetUserStatusInput = z.infer<typeof SetUserStatusInputSchema>;\n\n// Admin forces an abusive display name off a user: the name is swapped to a neutral\n// placeholder, the old name is blocked from reuse, and the user must pick a new one.\nexport const ForceDisplayNameResetInputSchema = z.object({\n userId: userIdSchema,\n // Optional admin note recorded on the audit event.\n reason: z.string().trim().min(1).max(MAX_INTERNAL_REASON_LENGTH).optional(),\n}).strict();\nexport type ForceDisplayNameResetInput = z.infer<typeof ForceDisplayNameResetInputSchema>;\n\n// User completes a forced display-name reset by choosing a new name. Only honored while\n// the caller's userProfiles doc has `displayNameResetRequired === true`.\nexport const SetMyDisplayNameInputSchema = z.object({\n displayName: displayNameSchema,\n}).strict();\nexport type SetMyDisplayNameInput = z.infer<typeof SetMyDisplayNameInputSchema>;\n\n// Unauthenticated soft-check the register page runs BEFORE creating the Auth user so a\n// taken name never orphans an auth account. Mirrors the RegisterUser displayName contract\n// (same length + regex). The authoritative claim still happens inside registerUser.\nexport const CheckDisplayNameAvailableInputSchema = z.object({\n displayName: displayNameSchema,\n}).strict();\nexport type CheckDisplayNameAvailableInput = z.infer<typeof CheckDisplayNameAvailableInputSchema>;\n\n// Teen→adult upgrade: a transient DOB (never persisted) the callable re-derives adult\n// eligibility from server-side against a fresh attestation. Only the shape is validated\n// here; the age math + attestation binding live in the callable.\nexport const UpgradeAccountToAdultInputSchema = z.object({\n dob: z.object({\n year: z.number().int(),\n month: z.number().int().min(1).max(12),\n day: z.number().int().min(1).max(31),\n }).strict(),\n}).strict();\nexport type UpgradeAccountToAdultInput = z.infer<typeof UpgradeAccountToAdultInputSchema>;\n\n// Admin-only user lookup for the User Management view (prefix search on publicUsers by\n// displayName). Only the trimmed query string is validated here; authz (admin/jr-admin)\n// lives in the callable.\nexport const SearchPublicUsersInputSchema = z.object({\n query: z.string().trim().min(1).max(MAX_USER_SEARCH_QUERY_LENGTH),\n}).strict();\nexport type SearchPublicUsersInput = z.infer<typeof SearchPublicUsersInputSchema>;\n\n// Admin exact-account lookup (\"look up by email or uid\" in User Management): a solo full\n// admin resolves ONE account by its exact email, uid, or display name. The app detects\n// email-vs-uid-vs-name both client- and server-side, so there is ONE query field (mirrors\n// SearchPublicUsersInput's single trimmed-query shape + bound). Only the trimmed query is\n// validated here; authz (full admin) lives in the callable.\nexport const LookupUserByEmailOrUidInputSchema = z.object({\n query: z.string().trim().min(1).max(MAX_USER_SEARCH_QUERY_LENGTH),\n}).strict();\nexport type LookupUserByEmailOrUidInput = z.infer<typeof LookupUserByEmailOrUidInputSchema>;\n\n// N3 account deletion — wire result contracts for the two user-facing callables (both take\n// no input). Previously declared independently in functions/src and src/hooks — one drifting\n// pair per callable. Non-strict (server → client result posture).\nexport const RequestAccountDeletionResultSchema = z.object({\n success: z.literal(true),\n /** When the scheduled scrub will run (requestedAt + the grace window), epoch ms. */\n scheduledScrubAt: z.number(),\n /** True when an ACTIVE request already existed and its schedule was re-returned. */\n alreadyPending: z.boolean(),\n});\nexport type RequestAccountDeletionResult = z.infer<typeof RequestAccountDeletionResultSchema>;\n\nexport const CancelAccountDeletionResultSchema = z.object({\n success: z.literal(true),\n /** False when there was no active request to cancel (idempotent no-op). */\n cancelled: z.boolean(),\n});\nexport type CancelAccountDeletionResult = z.infer<typeof CancelAccountDeletionResultSchema>;\n\n"]}
|
|
1
|
+
{"version":3,"file":"users.js","sourceRoot":"","sources":["../../src/schemas/users.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport { userIdSchema, calendarDateSchema } from './atoms.js';\nimport {\n USERNAME_MIN_LENGTH,\n USERNAME_MAX_LENGTH,\n USERNAME_REGEX,\n MAX_ARTISAN_LOCATION_LENGTH,\n MAX_INTERNAL_REASON_LENGTH,\n MAX_USER_SEARCH_QUERY_LENGTH,\n} from '../constants/business.js';\nimport { UserAccountStatusSchema } from '../doc-schemas/user.js';\nimport { PublicDocumentVersionOrNoneSchema } from '../doc-schemas/public-documents.js';\nimport {\n ShownPublicDocumentVersionsSchema,\n PublicDocumentsRefreshRequiredResultSchema,\n} from './public-documents.js';\n\n/** The ONE composed display-name field validator (length bounds + charset). Every\n * callable accepting a display name spreads this — never a re-composed copy. */\nexport const displayNameSchema = z\n .string()\n .min(USERNAME_MIN_LENGTH)\n .max(USERNAME_MAX_LENGTH)\n .regex(USERNAME_REGEX);\n\nexport const AcceptSquareStreetzAgreementsInputSchema = z.object({}).strict();\nexport type AcceptSquareStreetzAgreementsInput = z.infer<typeof AcceptSquareStreetzAgreementsInputSchema>;\n\n/**\n * `social.squareStreetzAgreementsAccepted` — the `metadata` of the event written with the\n * private acceptance record. The card incorporates the Rules & Agreements page by reference, so\n * the version is that page's: `acceptedVersion` is the one this acceptance recorded (0 while no\n * Rules version is published), and `previousAcceptedVersion` the one the record held before,\n * null when none was recorded (the first acceptance). The actor and server time ride the event\n * envelope.\n */\nexport const SquareStreetzAgreementsAcceptedAuditPayloadSchema = z.object({\n acceptedVersion: PublicDocumentVersionOrNoneSchema,\n previousAcceptedVersion: PublicDocumentVersionOrNoneSchema.nullable(),\n}).strict();\nexport type SquareStreetzAgreementsAcceptedAuditPayload = z.infer<typeof SquareStreetzAgreementsAcceptedAuditPayloadSchema>;\n\n// One-time acknowledgement gate before a user may download Hall content for personal\n// offline use (final wording owned by the legal-and-content step). No payload — the\n// callable records the timestamp in the caller's privateData. See\n// docs/design/media-assets-and-protected-serving.md (Hall/Library downloads).\nexport const AcceptHallDownloadAcknowledgementInputSchema = z.object({}).strict();\nexport type AcceptHallDownloadAcknowledgementInput = z.infer<typeof AcceptHallDownloadAcknowledgementInputSchema>;\n\n// The become-creator dialog's \"I confirm I am 18 years of age or older\" checkbox must reach the\n// server: the callable rejects when absent and records the attestation on the\n// artisanCreator.grantedToUser audit event (immutable, timestamped, with IP/UA) so it is provable.\nexport const BecomeArtisanCreatorInputSchema = z.object({\n ageAttested: z.literal(true),\n // \"I confirm I am a person located in the United States\" — required for now (only U.S. persons\n // may become artisans). Mirrors ageAttested: the callable rejects when absent and records the\n // attestation timestamp (privateData.usPersonAttestedAt) + the grant audit event.\n usPersonAttested: z.literal(true),\n // [Q21.5 REVISED — DJ 2026-07-06] The artisan's date of birth — the ONLY point in the system\n // where a DOB is collected for persistence (registration/upgrade derive the bracket and store\n // nothing). The dialog copy tells the user this must be THEIR birthday: when the payout system\n // ships, it has to match their payout (KYC) identity. The callable re-derives 18+ server-side\n // from this value; an under-18 derivation rejects (failure audit, nothing stored). Stored as\n // privateData attestedDateOfBirth + attestedDobSource 'artisanOnboarding'.\n dob: z.object({\n year: z.number().int(),\n month: z.number().int().min(1).max(12),\n day: z.number().int().min(1).max(31),\n }).strict(),\n}).strict();\nexport type BecomeArtisanCreatorInput = z.infer<typeof BecomeArtisanCreatorInputSchema>;\n\nexport const MarkWaitingForNewsApprovalInputSchema = z.object({}).strict();\nexport type MarkWaitingForNewsApprovalInput = z.infer<typeof MarkWaitingForNewsApprovalInputSchema>;\n\n// First-visit site-tour preference writer (updateSiteTourPreference). This is the ONE\n// server writer for `privateData/{uid}.siteTour`; the client never writes tour state\n// directly. Discriminated on `action`:\n// - deferToday: records the member's LOCAL calendar `date` (\"Not today\"); the automatic\n// invitation is suppressed for exactly that date.\n// - dismissAutomaticInvites: records the permanent \"Don't show this again\" time; no payload.\n// - completeTour: records completion; the callable stamps SITE_TOUR_CURRENT_VERSION and\n// completedAt server-side, so no version is accepted from the client (mirrors the\n// acknowledgement callables that record the authoritative value server-side rather than\n// trusting a client-supplied one). Bump SITE_TOUR_CURRENT_VERSION to re-invite everyone.\n// `date` uses the same strict YYYY-MM-DD convention as the age cluster's attestedDateOfBirth.\nexport const UpdateSiteTourPreferenceInputSchema = z.discriminatedUnion('action', [\n z.object({\n action: z.literal('deferToday'),\n date: calendarDateSchema, // 'YYYY-MM-DD' (member's LOCAL calendar date)\n }).strict(),\n z.object({\n action: z.literal('dismissAutomaticInvites'),\n }).strict(),\n z.object({\n action: z.literal('completeTour'),\n }).strict(),\n]);\nexport type UpdateSiteTourPreferenceInput = z.infer<typeof UpdateSiteTourPreferenceInputSchema>;\n\n// The device-local shape behind the ONE sanctioned optimistic-hide exception: a site-tour choice\n// the member has already made, persisted at `siteTourPendingStorageKey(uid)`\n// (constants/storage-keys.ts) while its `updateSiteTourPreference` write is unconfirmed. It exists\n// so the overlay can close on the click instead of after a callable round-trip, and so a failed\n// write is replayed on the next boot rather than silently lost.\n//\n// It is NOT a cache, mirror, or second authority: `privateData/{uid}.siteTour` remains the sole\n// source of tour eligibility, and this entry only OVERLAYS it (suppressing an invitation the member\n// has already dismissed) until the server state converges.\n//\n// Every field is load-bearing against a specific failure:\n// - `schemaVersion` lets a future shape change be recognized instead of silently mis-parsed.\n// - `id` is the compare-and-remove token: on callable success the owner removes the entry ONLY if\n// the stored id still matches the one it sent, so a slow response in tab A can never erase a\n// newer choice made in tab B.\n// - `uid` must equal the authenticated key owner before the entry may affect eligibility or be\n// replayed — defence in depth behind the uid-scoped key itself.\n// - `createdAt` is epoch ms, for ordering/diagnostics only; it is never an expiry.\n// - `completeTour` carries the `tourVersion` the member actually completed. On reconciliation an\n// entry whose version differs from SITE_TOUR_CURRENT_VERSION is STALE and is discarded, so an\n// old pending completion can never complete a newer tour. The callable payload stays\n// version-free (the server stamps the authoritative version) — this is a replay guard, not\n// server authority.\n//\n// STRICT and fully validated on read: malformed or unknown persisted data is removed and\n// eligibility falls back to the server. Invalid storage must never hide the tour forever.\nexport const SiteTourPendingPreferenceSchema = z.discriminatedUnion('action', [\n z.object({\n schemaVersion: z.literal(1),\n id: z.string().min(1),\n uid: userIdSchema,\n createdAt: z.number(),\n action: z.literal('deferToday'),\n date: calendarDateSchema, // 'YYYY-MM-DD' (member's LOCAL calendar date)\n }).strict(),\n z.object({\n schemaVersion: z.literal(1),\n id: z.string().min(1),\n uid: userIdSchema,\n createdAt: z.number(),\n action: z.literal('dismissAutomaticInvites'),\n }).strict(),\n z.object({\n schemaVersion: z.literal(1),\n id: z.string().min(1),\n uid: userIdSchema,\n createdAt: z.number(),\n action: z.literal('completeTour'),\n tourVersion: z.number().int().positive(),\n }).strict(),\n]);\nexport type SiteTourPendingPreference = z.infer<typeof SiteTourPendingPreferenceSchema>;\n\n// Non-U.S. artisan-interest signup (the FCFS \"waitlist\" fields on privateData). Mirrors\n// MarkWaitingForNewsApproval but carries the applicant's country (+ optional region) so signups\n// can be opened by jurisdiction as each region's laws clear. The callable records it ONLY for\n// adult accounts (never a minor's location).\nexport const MarkNonUsArtisanInterestInputSchema = z.object({\n country: z.string().min(1).max(MAX_ARTISAN_LOCATION_LENGTH),\n region: z.string().max(MAX_ARTISAN_LOCATION_LENGTH).optional(),\n}).strict();\nexport type MarkNonUsArtisanInterestInput = z.infer<typeof MarkNonUsArtisanInterestInputSchema>;\n\nexport const RegisterUserInputSchema = z.object({\n displayName: displayNameSchema,\n agreements: z.object({\n age: z.literal(true),\n nudity: z.literal(true),\n meet: z.literal(true),\n cookies: z.literal(true),\n terms: z.literal(true),\n }).strict(),\n // The public-document versions the signup page SHOWED when the person agreed: every published\n // document at its current version, `changedPublicDocuments(block, undefined)` off the live\n // `_appConfig/app` version block. Empty before the first release (the first-admin bootstrap).\n // Registration re-reads the block in its transaction and answers `refreshRequired`, writing\n // nothing, when a publish raced the page — the same comparison the acceptance prompt's Accept makes.\n publicDocuments: ShownPublicDocumentVersionsSchema,\n}).strict();\nexport type RegisterUserInput = z.infer<typeof RegisterUserInputSchema>;\n\nexport const RegisterUserResultSchema = z.discriminatedUnion('status', [\n // The account is registered with the documents shown accepted (or already was — an idempotent\n // replay). Refresh the token: the `docsAccepted` claim was set after the commit.\n z.object({ status: z.literal('registered') }),\n PublicDocumentsRefreshRequiredResultSchema,\n]);\nexport type RegisterUserResult = z.infer<typeof RegisterUserResultSchema>;\n\nexport const SetUserStatusInputSchema = z.object({\n userId: userIdSchema,\n status: UserAccountStatusSchema,\n // Required by the callable when status is 'suspended' or 'banned' (shown to the\n // user in their restricted view and recorded on the audit event). Optional here\n // so reinstating to 'active' can omit it; the callable enforces presence.\n reason: z.string().trim().min(1).max(MAX_INTERNAL_REASON_LENGTH).optional(),\n}).strict();\nexport type SetUserStatusInput = z.infer<typeof SetUserStatusInputSchema>;\n\n// Admin forces an abusive display name off a user: the name is swapped to a neutral\n// placeholder, the old name is blocked from reuse, and the user must pick a new one.\nexport const ForceDisplayNameResetInputSchema = z.object({\n userId: userIdSchema,\n // Optional admin note recorded on the audit event.\n reason: z.string().trim().min(1).max(MAX_INTERNAL_REASON_LENGTH).optional(),\n}).strict();\nexport type ForceDisplayNameResetInput = z.infer<typeof ForceDisplayNameResetInputSchema>;\n\n// User completes a forced display-name reset by choosing a new name. Only honored while\n// the caller's userProfiles doc has `displayNameResetRequired === true`.\nexport const SetMyDisplayNameInputSchema = z.object({\n displayName: displayNameSchema,\n}).strict();\nexport type SetMyDisplayNameInput = z.infer<typeof SetMyDisplayNameInputSchema>;\n\n// Unauthenticated soft-check the register page runs BEFORE creating the Auth user so a\n// taken name never orphans an auth account. Mirrors the RegisterUser displayName contract\n// (same length + regex). The authoritative claim still happens inside registerUser.\nexport const CheckDisplayNameAvailableInputSchema = z.object({\n displayName: displayNameSchema,\n}).strict();\nexport type CheckDisplayNameAvailableInput = z.infer<typeof CheckDisplayNameAvailableInputSchema>;\n\n// Teen→adult upgrade: a transient DOB (never persisted) the callable re-derives adult\n// eligibility from server-side against a fresh attestation. Only the shape is validated\n// here; the age math + attestation binding live in the callable.\nexport const UpgradeAccountToAdultInputSchema = z.object({\n dob: z.object({\n year: z.number().int(),\n month: z.number().int().min(1).max(12),\n day: z.number().int().min(1).max(31),\n }).strict(),\n}).strict();\nexport type UpgradeAccountToAdultInput = z.infer<typeof UpgradeAccountToAdultInputSchema>;\n\n// Admin-only user lookup for the User Management view (prefix search on publicUsers by\n// displayName). Only the trimmed query string is validated here; authz (admin/jr-admin)\n// lives in the callable.\nexport const SearchPublicUsersInputSchema = z.object({\n query: z.string().trim().min(1).max(MAX_USER_SEARCH_QUERY_LENGTH),\n}).strict();\nexport type SearchPublicUsersInput = z.infer<typeof SearchPublicUsersInputSchema>;\n\n// Admin exact-account lookup (\"look up by email or uid\" in User Management): a solo full\n// admin resolves ONE account by its exact email, uid, or display name. The app detects\n// email-vs-uid-vs-name both client- and server-side, so there is ONE query field (mirrors\n// SearchPublicUsersInput's single trimmed-query shape + bound). Only the trimmed query is\n// validated here; authz (full admin) lives in the callable.\nexport const LookupUserByEmailOrUidInputSchema = z.object({\n query: z.string().trim().min(1).max(MAX_USER_SEARCH_QUERY_LENGTH),\n}).strict();\nexport type LookupUserByEmailOrUidInput = z.infer<typeof LookupUserByEmailOrUidInputSchema>;\n\n// N3 account deletion — wire result contracts for the two user-facing callables (both take\n// no input). Previously declared independently in functions/src and src/hooks — one drifting\n// pair per callable. Non-strict (server → client result posture).\nexport const RequestAccountDeletionResultSchema = z.object({\n success: z.literal(true),\n /** When the scheduled scrub will run (requestedAt + the grace window), epoch ms. */\n scheduledScrubAt: z.number(),\n /** True when an ACTIVE request already existed and its schedule was re-returned. */\n alreadyPending: z.boolean(),\n});\nexport type RequestAccountDeletionResult = z.infer<typeof RequestAccountDeletionResultSchema>;\n\nexport const CancelAccountDeletionResultSchema = z.object({\n success: z.literal(true),\n /** False when there was no active request to cancel (idempotent no-op). */\n cancelled: z.boolean(),\n});\nexport type CancelAccountDeletionResult = z.infer<typeof CancelAccountDeletionResultSchema>;\n\n"]}
|
package/dist/types/audit.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AuditEvent } from '@ttt-productions/audit-core';
|
|
2
2
|
import type { SystemRole } from '../schemas/atoms.js';
|
|
3
|
-
export type AuditEventType = 'artisanCreator.grantedToUser' | 'user.accountRegistered' | 'user.statusChanged' | 'user.accountSuspended' | 'user.accountBanned' | 'user.accountUnbanned' | 'user.displayNameResetForced' | 'user.displayNameChanged' | 'user.nonUsArtisanInterestRecorded' | 'account.deletionRequested' | 'account.deletionCancelled' | 'account.erasureCompleted' | 'account.erasureParkedOnHold' | 'account.erasureSuperseded' | 'admin.systemRoleGranted' | 'admin.systemRoleRevoked' | 'admin.systemRoleSyncedFromDirectEdit' | 'admin.contentAppealReviewed' | 'admin.thresholdItemReviewed' | 'admin.futurePlansUpdated' | 'admin.rulesAndAgreementsUpdated' | 'publicDocuments.released' | 'publicDocuments.accepted' | 'admin.termsPageUpdated' | 'admin.privacyPageUpdated' | 'admin.takeItDownPageCopyUpdated' | 'admin.reservedUsernamesSeeded' | 'admin.blockedFranchiseNamesSeeded' | 'admin.rulesAndAgreementsSeeded' | 'admin.futurePlansSeeded' | 'admin.termsPageSeeded' | 'admin.privacyPageSeeded' | 'admin.takeItDownPageCopySeeded' | 'admin.dispatchDeleted' | 'admin.shortLinkDeleted' | 'admin.trademarkChecked' | 'content.parodyDisclaimerApplied' | 'workProject.created' | 'workProject.guildmateUserGuildStandingChanged' | 'workProject.guildmateUserTradeProfessionsChanged' | 'workProject.guildMemberDeparted' | 'workProject.publicDetailsUpdated' | 'workProject.workAssetDeleted' | 'workProject.fileFolderCreated' | 'workProject.fileFolderRenamed' | 'workProject.fileFolderAccessChanged' | 'workProject.fileFolderDeleted' | 'workProject.guildInviteSent' | 'workProject.guildInviteAccepted' | 'workProject.guildInviteDeclined' | 'workProject.guildInviteCancelled' | 'workProject.guildInviteStakeSharesUpdated' | 'workProject.commissionListingCreated' | 'workProject.commissionProposalCreated' | 'workProject.commissionClosed' | 'workProject.commissionProposalSaveToggled' | 'workProject.commissionProposalRejected' | 'workProject.stakeSharesChanged' | 'workProject.stakeShares.created' | 'workProject.stakeShares.increased' | 'workProject.stakeShares.anomaly' | 'workFile.realmShareRequested' | 'workFile.realmShareRequestWithdrawn' | 'workFile.realmSharePromotionApproved' | 'workFile.realmSharePromotionDeclined' | 'workFile.realmFileFolderAssignmentChanged' | 'workFile.realmCanonChanged' | 'workFile.unsharedFromRealm' | 'workFile.sharedToRealm' | 'craftSkill.userCraftSkillDeleted' | 'craftSkill.hidden' | 'craftSkill.restored' | 'craftSkill.removed' | 'content.itemReported' | 'content.violationRecorded' | 'content.thresholdItemSubmitted' | 'content.thresholdItemWithdrawn' | 'content.hallItemPublished' | 'content.hallContentChangeRequestSubmitted' | 'admin.hallContentChangeRequestResolved' | 'content.appealSubmitted' | 'content.violationAccepted' | 'content.hidden' | 'content.restored' | 'content.removed' | 'content.taleDetailsUpdated' | 'content.taleWorkGenresUpdated' | 'content.chapterCreated' | 'content.chapterDetailsUpdated' | 'content.tuneDetailsUpdated' | 'content.tuneWorkGenresUpdated' | 'content.tuneTrackCreated' | 'content.tuneTrackDetailsUpdated' | 'content.televisionDetailsUpdated' | 'content.televisionWorkGenresUpdated' | 'content.televisionEpisodeCreated' | 'content.televisionEpisodeDetailsUpdated' | 'payment.sessionCreated' | 'payment.pledgePaymentCompleted' | 'payment.pledgePaymentAbandoned' | 'payment.pledgePaymentFailed' | 'payment.pledgePaymentQuarantined' | 'payment.pledgePaymentRefunded' | 'payment.pledgePaymentDisputeOpened' | 'payment.pledgePaymentDisputeClosed' | 'payment.pledgeRefundRequested' | 'payment.pledgeRefundRequestResolved' | 'payment.pledgeLedger.recorded' | 'payment.pledgeLedger.anomaly' | 'admin.taskCheckedOut' | 'admin.taskCheckedIn' | 'admin.taskReleased' | 'admin.taskAutoReleased' | 'admin.taskDeferredLater' | 'workProject.auditionCreated' | 'admin.auditionCreated' | 'audition.entryCreated' | 'audition.closedByUser' | 'audition.curatedBatchFailed' | 'system.manualIntervention' | 'system.appConfigUpdated' | 'admin.appModeFlipRecorded' | 'system.pendingMediaArchived' | 'system.orphanUploadsCleanedUp' | 'admin.profanityListSeeded' | 'admin.profanityListCurated' | 'social.squareStreetzPostLiked' | 'social.squareStreetzPostUnliked' | 'social.targetFollowed' | 'social.targetUnfollowed' | 'chat.guildChatChannelCreated' | 'chat.guildChatChannelUpdated' | 'chat.guildChatChannelArchived' | 'chat.guildChatChannelUnarchived' | 'chat.guildChatChannelDeleted' | 'chat.adminThreadStarted' | 'chat.adminThreadStatusChanged' | 'conversationFile.deleted' | 'chat.moderationActionRequested' | 'chat.moderationActionApplied' | 'chat.moderationActionFailed' | 'chat.moderationContextRead' | 'notification.broadcastSent' | 'notification.adminArchived' | 'workProject.released' | 'workProject.hidden' | 'workProject.restored' | 'workProject.moderationPlaceholderApplied' | 'workProject.moderationRetitleCleared' | 'workRealm.created' | 'workRealm.released' | 'workRealm.detailsUpdated' | 'workRealm.hidden' | 'workRealm.restored' | 'workRealm.moderationPlaceholderApplied' | 'workRealm.moderationRetitleCleared' | 'workRealm.fileFolderCreated' | 'workRealm.fileFolderUpdated' | 'workRealm.fileFolderDeleted' | 'hallItem.moderationPlaceholderApplied' | 'report.childSafetyCaseOpened' | 'report.nciiCaseOpened' | 'childSafety.reportDispositionSet' | 'childSafety.holdPlaced' | 'childSafety.holdReleased' | 'childSafety.accountActionApplied' | 'childSafety.accountActionReverted' | 'childSafety.evidenceManifestCreated' | 'childSafety.evidenceDisposed' | 'childSafety.quarantineCompleted' | 'childSafety.ncmecSubmissionCompleted' | 'childSafety.ncmecPortalReceiptArtifactRecorded' | 'childSafety.legalProcessRecorded' | 'childSafety.falsePositiveCorrected' | 'user.ageAttested' | 'user.ageUpgradedToAdult' | 'user.orphanAuthDeleted' | 'user.accountClosedUnder13' | 'user.safetyLocked' | 'ncii.requestReceived' | 'ncii.requestSupplemented' | 'ncii.completenessDetermined' | 'ncii.validityDecided' | 'ncii.tempHoldPlaced' | 'ncii.tempHoldReleased' | 'ncii.removalCompleted' | 'ncii.removalUnableToComplete' | 'ncii.evidenceMarked' | 'ncii.evidenceScanValidatedMatch' | 'ncii.hashBlockReversed' | 'ncii.requesterPiiScrubbed' | 'ncii.policyConfigUpdated' | 'ncii.minorAssessmentSet' | 'ncii.crossoverLegReconciled' | 'safety.privilegedReauthPerformed' | 'safety.privilegedRawLocatorRead' | 'safety.deadlineBreachRecorded' | 'safety.reviewerCapabilityGranted' | 'safety.reviewerCapabilityRevoked';
|
|
3
|
+
export type AuditEventType = 'artisanCreator.grantedToUser' | 'user.accountRegistered' | 'user.statusChanged' | 'user.accountSuspended' | 'user.accountBanned' | 'user.accountUnbanned' | 'user.displayNameResetForced' | 'user.displayNameChanged' | 'user.nonUsArtisanInterestRecorded' | 'account.deletionRequested' | 'account.deletionCancelled' | 'account.erasureCompleted' | 'account.erasureParkedOnHold' | 'account.erasureSuperseded' | 'admin.systemRoleGranted' | 'admin.systemRoleRevoked' | 'admin.systemRoleSyncedFromDirectEdit' | 'admin.contentAppealReviewed' | 'admin.thresholdItemReviewed' | 'admin.futurePlansUpdated' | 'admin.rulesAndAgreementsUpdated' | 'publicDocuments.released' | 'publicDocuments.accepted' | 'admin.termsPageUpdated' | 'admin.privacyPageUpdated' | 'admin.takeItDownPageCopyUpdated' | 'admin.reservedUsernamesSeeded' | 'admin.blockedFranchiseNamesSeeded' | 'admin.rulesAndAgreementsSeeded' | 'admin.futurePlansSeeded' | 'admin.termsPageSeeded' | 'admin.privacyPageSeeded' | 'admin.takeItDownPageCopySeeded' | 'admin.dispatchDeleted' | 'admin.shortLinkDeleted' | 'admin.trademarkChecked' | 'content.parodyDisclaimerApplied' | 'workProject.created' | 'workProject.guildmateUserGuildStandingChanged' | 'workProject.guildmateUserTradeProfessionsChanged' | 'workProject.guildMemberDeparted' | 'workProject.publicDetailsUpdated' | 'workProject.workAssetDeleted' | 'workProject.fileFolderCreated' | 'workProject.fileFolderRenamed' | 'workProject.fileFolderAccessChanged' | 'workProject.fileFolderDeleted' | 'workProject.guildInviteSent' | 'workProject.guildInviteAccepted' | 'workProject.guildInviteDeclined' | 'workProject.guildInviteCancelled' | 'workProject.guildInviteStakeSharesUpdated' | 'workProject.commissionListingCreated' | 'workProject.commissionProposalCreated' | 'workProject.commissionClosed' | 'workProject.commissionProposalSaveToggled' | 'workProject.commissionProposalRejected' | 'workProject.stakeSharesChanged' | 'workProject.stakeShares.created' | 'workProject.stakeShares.increased' | 'workProject.stakeShares.anomaly' | 'workFile.realmShareRequested' | 'workFile.realmShareRequestWithdrawn' | 'workFile.realmSharePromotionApproved' | 'workFile.realmSharePromotionDeclined' | 'workFile.realmFileFolderAssignmentChanged' | 'workFile.realmCanonChanged' | 'workFile.unsharedFromRealm' | 'workFile.sharedToRealm' | 'craftSkill.userCraftSkillDeleted' | 'craftSkill.hidden' | 'craftSkill.restored' | 'craftSkill.removed' | 'content.itemReported' | 'content.violationRecorded' | 'content.thresholdItemSubmitted' | 'content.thresholdItemWithdrawn' | 'content.hallItemPublished' | 'content.hallContentChangeRequestSubmitted' | 'admin.hallContentChangeRequestResolved' | 'content.appealSubmitted' | 'content.violationAccepted' | 'content.hidden' | 'content.restored' | 'content.removed' | 'content.taleDetailsUpdated' | 'content.taleWorkGenresUpdated' | 'content.chapterCreated' | 'content.chapterDetailsUpdated' | 'content.tuneDetailsUpdated' | 'content.tuneWorkGenresUpdated' | 'content.tuneTrackCreated' | 'content.tuneTrackDetailsUpdated' | 'content.televisionDetailsUpdated' | 'content.televisionWorkGenresUpdated' | 'content.televisionEpisodeCreated' | 'content.televisionEpisodeDetailsUpdated' | 'payment.sessionCreated' | 'payment.pledgePaymentCompleted' | 'payment.pledgePaymentAbandoned' | 'payment.pledgePaymentFailed' | 'payment.pledgePaymentQuarantined' | 'payment.pledgePaymentRefunded' | 'payment.pledgePaymentDisputeOpened' | 'payment.pledgePaymentDisputeClosed' | 'payment.pledgeRefundRequested' | 'payment.pledgeRefundRequestResolved' | 'payment.pledgeLedger.recorded' | 'payment.pledgeLedger.anomaly' | 'admin.taskCheckedOut' | 'admin.taskCheckedIn' | 'admin.taskReleased' | 'admin.taskAutoReleased' | 'admin.taskDeferredLater' | 'workProject.auditionCreated' | 'admin.auditionCreated' | 'audition.entryCreated' | 'audition.closedByUser' | 'audition.curatedBatchFailed' | 'system.manualIntervention' | 'system.appConfigUpdated' | 'admin.appModeFlipRecorded' | 'system.pendingMediaArchived' | 'system.orphanUploadsCleanedUp' | 'admin.profanityListSeeded' | 'admin.profanityListCurated' | 'social.squareStreetzAgreementsAccepted' | 'social.squareStreetzPostLiked' | 'social.squareStreetzPostUnliked' | 'social.targetFollowed' | 'social.targetUnfollowed' | 'chat.guildChatChannelCreated' | 'chat.guildChatChannelUpdated' | 'chat.guildChatChannelArchived' | 'chat.guildChatChannelUnarchived' | 'chat.guildChatChannelDeleted' | 'chat.adminThreadStarted' | 'chat.adminThreadStatusChanged' | 'conversationFile.deleted' | 'chat.moderationActionRequested' | 'chat.moderationActionApplied' | 'chat.moderationActionFailed' | 'chat.moderationContextRead' | 'notification.broadcastSent' | 'notification.adminArchived' | 'workProject.released' | 'workProject.hidden' | 'workProject.restored' | 'workProject.moderationPlaceholderApplied' | 'workProject.moderationRetitleCleared' | 'workRealm.created' | 'workRealm.released' | 'workRealm.detailsUpdated' | 'workRealm.hidden' | 'workRealm.restored' | 'workRealm.moderationPlaceholderApplied' | 'workRealm.moderationRetitleCleared' | 'workRealm.fileFolderCreated' | 'workRealm.fileFolderUpdated' | 'workRealm.fileFolderDeleted' | 'hallItem.moderationPlaceholderApplied' | 'report.childSafetyCaseOpened' | 'report.nciiCaseOpened' | 'childSafety.reportDispositionSet' | 'childSafety.holdPlaced' | 'childSafety.holdReleased' | 'childSafety.accountActionApplied' | 'childSafety.accountActionReverted' | 'childSafety.evidenceManifestCreated' | 'childSafety.evidenceDisposed' | 'childSafety.quarantineCompleted' | 'childSafety.ncmecSubmissionCompleted' | 'childSafety.ncmecPortalReceiptArtifactRecorded' | 'childSafety.legalProcessRecorded' | 'childSafety.falsePositiveCorrected' | 'user.ageAttested' | 'user.ageUpgradedToAdult' | 'user.orphanAuthDeleted' | 'user.accountClosedUnder13' | 'user.safetyLocked' | 'ncii.requestReceived' | 'ncii.requestSupplemented' | 'ncii.completenessDetermined' | 'ncii.validityDecided' | 'ncii.tempHoldPlaced' | 'ncii.tempHoldReleased' | 'ncii.removalCompleted' | 'ncii.removalUnableToComplete' | 'ncii.evidenceMarked' | 'ncii.evidenceScanValidatedMatch' | 'ncii.hashBlockReversed' | 'ncii.requesterPiiScrubbed' | 'ncii.policyConfigUpdated' | 'ncii.minorAssessmentSet' | 'ncii.crossoverLegReconciled' | 'safety.privilegedReauthPerformed' | 'safety.privilegedRawLocatorRead' | 'safety.deadlineBreachRecorded' | 'safety.reviewerCapabilityGranted' | 'safety.reviewerCapabilityRevoked';
|
|
4
4
|
/**
|
|
5
5
|
* How the actor was acting when the audited action was performed. This is
|
|
6
6
|
* orthogonal to the event name — the same event type can be produced by
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/types/audit.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,MAAM,cAAc,GAEtB,8BAA8B,GAC9B,wBAAwB,GACxB,oBAAoB,GACpB,uBAAuB,GACvB,oBAAoB,GACpB,sBAAsB,GACtB,6BAA6B,GAC7B,yBAAyB,GACzB,mCAAmC,GAEnC,2BAA2B,GAC3B,2BAA2B,GAC3B,0BAA0B,GAC1B,6BAA6B,GAC7B,2BAA2B,GAE3B,yBAAyB,GACzB,yBAAyB,GACzB,sCAAsC,GACtC,6BAA6B,GAC7B,6BAA6B,GAC7B,0BAA0B,GAC1B,iCAAiC,GAIjC,0BAA0B,GAC1B,0BAA0B,GAE1B,wBAAwB,GACxB,0BAA0B,GAC1B,iCAAiC,GAEjC,+BAA+B,GAC/B,mCAAmC,GACnC,gCAAgC,GAChC,yBAAyB,GACzB,uBAAuB,GACvB,yBAAyB,GACzB,gCAAgC,GAGhC,uBAAuB,GACvB,wBAAwB,GAExB,wBAAwB,GACxB,iCAAiC,GAEjC,qBAAqB,GACrB,+CAA+C,GAC/C,kDAAkD,GAElD,iCAAiC,GACjC,kCAAkC,GAClC,8BAA8B,GAG9B,+BAA+B,GAC/B,+BAA+B,GAC/B,qCAAqC,GACrC,+BAA+B,GAC/B,6BAA6B,GAC7B,iCAAiC,GACjC,iCAAiC,GACjC,kCAAkC,GAClC,2CAA2C,GAC3C,sCAAsC,GACtC,uCAAuC,GACvC,8BAA8B,GAC9B,2CAA2C,GAC3C,wCAAwC,GACxC,gCAAgC,GAChC,iCAAiC,GACjC,mCAAmC,GACnC,iCAAiC,GAKjC,8BAA8B,GAC9B,qCAAqC,GACrC,sCAAsC,GACtC,sCAAsC,GAEtC,2CAA2C,GAG3C,4BAA4B,GAC5B,4BAA4B,GAO5B,wBAAwB,GAExB,kCAAkC,GAClC,mBAAmB,GACnB,qBAAqB,GACrB,oBAAoB,GAEpB,sBAAsB,GACtB,2BAA2B,GAC3B,gCAAgC,GAGhC,gCAAgC,GAChC,2BAA2B,GAG3B,2CAA2C,GAC3C,wCAAwC,GACxC,yBAAyB,GACzB,2BAA2B,GAC3B,gBAAgB,GAChB,kBAAkB,GAClB,iBAAiB,GAEjB,4BAA4B,GAC5B,+BAA+B,GAC/B,wBAAwB,GACxB,+BAA+B,GAC/B,4BAA4B,GAC5B,+BAA+B,GAC/B,0BAA0B,GAC1B,iCAAiC,GACjC,kCAAkC,GAClC,qCAAqC,GACrC,kCAAkC,GAClC,yCAAyC,GAEzC,wBAAwB,GACxB,gCAAgC,GAChC,gCAAgC,GAChC,6BAA6B,GAC7B,kCAAkC,GAIlC,+BAA+B,GAC/B,oCAAoC,GACpC,oCAAoC,GACpC,+BAA+B,GAC/B,qCAAqC,GAErC,+BAA+B,GAC/B,8BAA8B,GAE9B,sBAAsB,GACtB,qBAAqB,GACrB,oBAAoB,GACpB,wBAAwB,GACxB,yBAAyB,GAEzB,6BAA6B,GAC7B,uBAAuB,GACvB,uBAAuB,GACvB,uBAAuB,GAGvB,6BAA6B,GAE7B,2BAA2B,GAC3B,yBAAyB,GAGzB,2BAA2B,GAC3B,6BAA6B,GAC7B,+BAA+B,GAC/B,2BAA2B,GAC3B,4BAA4B,
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/types/audit.ts"],"names":[],"mappings":"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"}
|
package/dist/types/audit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/types/audit.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,oEAAoE;AACpE,yEAAyE;AACzE,sEAAsE;AACtE,4DAA4D;AAC5D,EAAE;AACF,wEAAwE;AACxE,qBAAqB","sourcesContent":["// TTT audit event catalog. The `AuditEventType` union is the canonical list\n// of all sensitive actions the system logs. The `TTTAuditActor` and\n// `TTTAuditTarget` shapes describe who performed the action and what was\n// affected. `TTTAuditEvent` specializes the generic `AuditEvent` from\n// @ttt-productions/audit-core with the TTT-specific shapes.\n//\n// Backend writers and future native/TV/console readers all consume this\n// catalog from here.\n\nimport type { AuditEvent } from '@ttt-productions/audit-core';\nimport type { SystemRole } from '../schemas/atoms.js';\n\nexport type AuditEventType =\n // user status\n | 'artisanCreator.grantedToUser'\n | 'user.accountRegistered'\n | 'user.statusChanged'\n | 'user.accountSuspended'\n | 'user.accountBanned'\n | 'user.accountUnbanned'\n | 'user.displayNameResetForced'\n | 'user.displayNameChanged'\n | 'user.nonUsArtisanInterestRecorded'\n // account deletion / GDPR erasure (N3)\n | 'account.deletionRequested'\n | 'account.deletionCancelled'\n | 'account.erasureCompleted'\n | 'account.erasureParkedOnHold'\n | 'account.erasureSuperseded'\n // admin actions\n | 'admin.systemRoleGranted'\n | 'admin.systemRoleRevoked'\n | 'admin.systemRoleSyncedFromDirectEdit'\n | 'admin.contentAppealReviewed'\n | 'admin.thresholdItemReviewed'\n | 'admin.futurePlansUpdated'\n | 'admin.rulesAndAgreementsUpdated'\n // versioned public documents. Payload shapes: PublicDocumentsReleasedAuditPayload /\n // PublicDocumentsAcceptedAuditPayload in ../schemas/public-documents.ts. `accepted` is the\n // ONLY acceptance history (registration and the re-acceptance prompt both write it).\n | 'publicDocuments.released'\n | 'publicDocuments.accepted'\n // content-pages migration (DJ ruling 2026-07-06): per-page admin editors\n | 'admin.termsPageUpdated'\n | 'admin.privacyPageUpdated'\n | 'admin.takeItDownPageCopyUpdated'\n // admin launch-seed actions (Ready for Launch tab)\n | 'admin.reservedUsernamesSeeded'\n | 'admin.blockedFranchiseNamesSeeded'\n | 'admin.rulesAndAgreementsSeeded'\n | 'admin.futurePlansSeeded'\n | 'admin.termsPageSeeded'\n | 'admin.privacyPageSeeded'\n | 'admin.takeItDownPageCopySeeded'\n // admin direct-delete callables (replaced the last two direct-SDK admin-browser deletes):\n // deleteAdminDispatch (support thread + its conversationMessages) and deleteShortLink.\n | 'admin.dispatchDeleted'\n | 'admin.shortLinkDeleted'\n // trademark-assist (advisory check at approval) + parody/real-people disclaimer baked at approval\n | 'admin.trademarkChecked'\n | 'content.parodyDisclaimerApplied'\n // workProject\n | 'workProject.created'\n | 'workProject.guildmateUserGuildStandingChanged'\n | 'workProject.guildmateUserTradeProfessionsChanged'\n // Member self-leave (leaveWorkProject) — pre-publish voluntary exit, shares forfeited to the pool.\n | 'workProject.guildMemberDeparted'\n | 'workProject.publicDetailsUpdated'\n | 'workProject.workAssetDeleted'\n // work-file folders (S7). fileFolderAccessChanged is an ACL change (folder trade-profession\n // view/upload/delete lists gate custom-folder confidentiality) — audited with before/after lists.\n | 'workProject.fileFolderCreated'\n | 'workProject.fileFolderRenamed'\n | 'workProject.fileFolderAccessChanged'\n | 'workProject.fileFolderDeleted'\n | 'workProject.guildInviteSent'\n | 'workProject.guildInviteAccepted'\n | 'workProject.guildInviteDeclined'\n | 'workProject.guildInviteCancelled'\n | 'workProject.guildInviteStakeSharesUpdated'\n | 'workProject.commissionListingCreated'\n | 'workProject.commissionProposalCreated'\n | 'workProject.commissionClosed'\n | 'workProject.commissionProposalSaveToggled'\n | 'workProject.commissionProposalRejected'\n | 'workProject.stakeSharesChanged'\n | 'workProject.stakeShares.created'\n | 'workProject.stakeShares.increased'\n | 'workProject.stakeShares.anomaly'\n // realm shared files — the promotion approval gate. A Work file admin REQUESTS promotion;\n // the Realm steward approves (assigning the folder) or declines; the requester may withdraw\n // while the request is still pending. Every one of these carries the request id, so a\n // request and its resolution are one traceable pair.\n | 'workFile.realmShareRequested'\n | 'workFile.realmShareRequestWithdrawn'\n | 'workFile.realmSharePromotionApproved'\n | 'workFile.realmSharePromotionDeclined'\n // The steward moves an already-approved shared file between realm folders.\n | 'workFile.realmFileFolderAssignmentChanged'\n // Canon toggle on an approved file + the ADMIN-only un-share (sharing is permanent for\n // members once approved; the only way out is an admin acting on a support thread).\n | 'workFile.realmCanonChanged'\n | 'workFile.unsharedFromRealm'\n // RETIRED-ON-ADOPTION: the pre-approval-gate instant-share event. Its one emitter\n // (runUpdateWorkFileRealmShare) becomes a REQUEST and emits\n // `workFile.realmShareRequested`; the moment a file actually enters the pool is\n // `workFile.realmSharePromotionApproved`. Kept in the union only so the approval-gate\n // package release stays additive (a removal is a breaking contract change); delete it in\n // the follow-up release once no emitter remains.\n | 'workFile.sharedToRealm'\n // craft-skills\n | 'craftSkill.userCraftSkillDeleted'\n | 'craftSkill.hidden'\n | 'craftSkill.restored'\n | 'craftSkill.removed'\n // content\n | 'content.itemReported'\n | 'content.violationRecorded'\n | 'content.thresholdItemSubmitted'\n // Member-initiated pull-back of a still-pending threshold submission (withdraw callable —\n // deletes the threshold doc + admin task, flips the live sub-item back to unpublished).\n | 'content.thresholdItemWithdrawn'\n | 'content.hallItemPublished'\n // Published change requests (text-only at launch): a member proposes new text-field values\n // for a PUBLISHED hall item; an admin approves (server writes values in place) or denies.\n | 'content.hallContentChangeRequestSubmitted'\n | 'admin.hallContentChangeRequestResolved'\n | 'content.appealSubmitted'\n | 'content.violationAccepted'\n | 'content.hidden'\n | 'content.restored'\n | 'content.removed'\n // content (workProject content)\n | 'content.taleDetailsUpdated'\n | 'content.taleWorkGenresUpdated'\n | 'content.chapterCreated'\n | 'content.chapterDetailsUpdated'\n | 'content.tuneDetailsUpdated'\n | 'content.tuneWorkGenresUpdated'\n | 'content.tuneTrackCreated'\n | 'content.tuneTrackDetailsUpdated'\n | 'content.televisionDetailsUpdated'\n | 'content.televisionWorkGenresUpdated'\n | 'content.televisionEpisodeCreated'\n | 'content.televisionEpisodeDetailsUpdated'\n // money\n | 'payment.sessionCreated'\n | 'payment.pledgePaymentCompleted'\n | 'payment.pledgePaymentAbandoned'\n | 'payment.pledgePaymentFailed'\n | 'payment.pledgePaymentQuarantined'\n // money — refund + dispute lifecycle (post-launch handlers; types added now to avoid a\n // second package publish). Refund REQUESTS are the user-initiated ask; the refund itself is\n // the money movement. Disputes are Stripe-initiated (chargeback) open/close.\n | 'payment.pledgePaymentRefunded'\n | 'payment.pledgePaymentDisputeOpened'\n | 'payment.pledgePaymentDisputeClosed'\n | 'payment.pledgeRefundRequested'\n | 'payment.pledgeRefundRequestResolved'\n // money — independent ledger-integrity trigger (mirrors workProject.stakeShares.*)\n | 'payment.pledgeLedger.recorded'\n | 'payment.pledgeLedger.anomaly'\n // admin task lifecycle\n | 'admin.taskCheckedOut'\n | 'admin.taskCheckedIn'\n | 'admin.taskReleased'\n | 'admin.taskAutoReleased'\n | 'admin.taskDeferredLater'\n // audition\n | 'workProject.auditionCreated'\n | 'admin.auditionCreated'\n | 'audition.entryCreated'\n | 'audition.closedByUser'\n // system-actor event: a curated audition's option batch failed to assemble (media\n // rejection) and its curatedAssemblyStatus flipped to 'failed' (runFailCuratedAudition).\n | 'audition.curatedBatchFailed'\n // system\n | 'system.manualIntervention'\n | 'system.appConfigUpdated'\n // records (never controls) a charter↔full APP_MODE flip after deploy — written\n // by the idempotent recordAppModeFlip admin callable\n | 'admin.appModeFlipRecorded'\n | 'system.pendingMediaArchived'\n | 'system.orphanUploadsCleanedUp'\n | 'admin.profanityListSeeded'\n | 'admin.profanityListCurated'\n // social\n | 'social.squareStreetzPostLiked'\n | 'social.squareStreetzPostUnliked'\n | 'social.targetFollowed'\n | 'social.targetUnfollowed'\n // chat\n | 'chat.guildChatChannelCreated'\n | 'chat.guildChatChannelUpdated'\n | 'chat.guildChatChannelArchived'\n | 'chat.guildChatChannelUnarchived'\n | 'chat.guildChatChannelDeleted'\n | 'chat.adminThreadStarted'\n | 'chat.adminThreadStatusChanged'\n // conversation files — every successful delete is audited with UID references and\n // canonical resource ids (the conversation ref, conversationFileId, mediaAssetId).\n | 'conversationFile.deleted'\n // chat moderation (DO-owned hide/delete via chatAdminActionCommands) — append-only,\n // deterministic ids hash('chat-moderation-{requested,applied,failed}', requestId, …).\n | 'chat.moderationActionRequested'\n | 'chat.moderationActionApplied'\n | 'chat.moderationActionFailed'\n // case-bound admin context read (read-on-demand, REVIEW-ONLY) — audits the request\n // AND outcome; one event per read keyed deterministically by the read's requestId.\n | 'chat.moderationContextRead'\n // notification (admin-only). Actor mode is always adminReview / adminOverride.\n // Payload shapes: NotificationBroadcastSentAuditMetadata /\n // NotificationAdminArchivedAuditMetadata in ../schemas/notification.ts.\n | 'notification.broadcastSent'\n | 'notification.adminArchived'\n // workProject\n | 'workProject.released'\n | 'workProject.hidden'\n | 'workProject.restored'\n | 'workProject.moderationPlaceholderApplied'\n | 'workProject.moderationRetitleCleared'\n | 'workRealm.created'\n | 'workRealm.released'\n | 'workRealm.detailsUpdated'\n | 'workRealm.hidden'\n | 'workRealm.restored'\n | 'workRealm.moderationPlaceholderApplied'\n | 'workRealm.moderationRetitleCleared'\n // Realm shared-file FOLDER management (steward-curated, Realm-wide-visible names —\n // mirrors the workProject.fileFolder* family). The Realm folder has no access lists, so\n // there is no `AccessChanged` sibling: a rename is the only in-place edit.\n | 'workRealm.fileFolderCreated'\n | 'workRealm.fileFolderUpdated'\n | 'workRealm.fileFolderDeleted'\n // hall-content moderation text-clear remedy (extends the workProject/workRealm placeholder\n // family across Tale/Tune/Television details + their chapter/track/episode sub-items and the\n // published Hall projections). Same past-tense placeholder-applied shape as the pair above.\n | 'hallItem.moderationPlaceholderApplied'\n // trust & safety — child safety / CSAM\n | 'report.childSafetyCaseOpened'\n | 'report.nciiCaseOpened'\n | 'childSafety.reportDispositionSet'\n | 'childSafety.holdPlaced'\n | 'childSafety.holdReleased'\n | 'childSafety.accountActionApplied'\n | 'childSafety.accountActionReverted'\n | 'childSafety.evidenceManifestCreated'\n | 'childSafety.evidenceDisposed'\n | 'childSafety.quarantineCompleted'\n | 'childSafety.ncmecSubmissionCompleted'\n | 'childSafety.ncmecPortalReceiptArtifactRecorded'\n | 'childSafety.legalProcessRecorded'\n | 'childSafety.falsePositiveCorrected'\n // trust & safety — age / registration\n | 'user.ageAttested'\n | 'user.ageUpgradedToAdult'\n | 'user.orphanAuthDeleted'\n | 'user.accountClosedUnder13'\n | 'user.safetyLocked'\n // trust & safety — NCII / TAKE IT DOWN\n | 'ncii.requestReceived'\n | 'ncii.requestSupplemented'\n | 'ncii.completenessDetermined'\n | 'ncii.validityDecided'\n | 'ncii.tempHoldPlaced'\n | 'ncii.tempHoldReleased'\n | 'ncii.removalCompleted'\n | 'ncii.removalUnableToComplete'\n // operator marks NCII evidence (e.g. flags/annotates an evidence item on the case) — a reviewed\n // evidence-handling action, audited like the other ncii evidence events.\n | 'ncii.evidenceMarked'\n | 'ncii.evidenceScanValidatedMatch'\n | 'ncii.hashBlockReversed'\n | 'ncii.requesterPiiScrubbed'\n | 'ncii.policyConfigUpdated'\n // operator sets the child-safety crossover minorAssessment on an NCII case (e.g. → possibleMinor):\n // routing action that opens/links a PARALLEL child-safety review + deny-serving + PhotoDNA, WITHOUT\n // touching the NCII removal clock and WITHOUT auto-NCMEC. Payload records from→to assessment.\n | 'ncii.minorAssessmentSet'\n // [H-2] a failed possible-minor crossover leg (serving-deny / PhotoDNA) was reconciled or replayed\n // by an operator/reconciler (crossoverLegs.<leg> failed → done). Payload records which leg + outcome.\n | 'ncii.crossoverLegReconciled'\n // trust & safety — operator security\n // Passkey enrollment/assertion reuses safety.privilegedReauthPerformed (the reauth catalog entry, as\n // the TOTP step-up enroll/confirm/verify phases already do) — no distinct passkey-registration type.\n | 'safety.privilegedReauthPerformed'\n // [P2-08] A privileged operator read of a TAKE IT DOWN request's RAW target locator (the\n // reveal-on-demand, review-only read of the sensitive locator itself — distinct from the\n // reveal reauth above, which is the step-up). One event per read. Mirrors the\n // chat.moderationContextRead privileged-read convention.\n | 'safety.privilegedRawLocatorRead'\n | 'safety.deadlineBreachRecorded'\n // [M-6] per-operator reviewer-capability grant lifecycle. Granting/revoking a\n // SafetyReviewerCapability is an authorization change — always audited.\n | 'safety.reviewerCapabilityGranted'\n | 'safety.reviewerCapabilityRevoked';\n\n/**\n * How the actor was acting when the audited action was performed. This is\n * orthogonal to the event name — the same event type can be produced by\n * different actor modes (e.g. a normal member vs. an admin override).\n *\n * - 'user' — authenticated action not tied to Work guild membership\n * - 'guildmateUser' — acting through normal Work guild (guild standing) permissions\n * - 'adminReview' — admin queue/support work on admin surfaces (no guild-permission concept)\n * - 'adminOverride' — system-admin authority superseding normal user/guild permission\n * - 'system' — backend automation: trigger, scheduled job, migration, media pipeline\n */\nexport type TTTAuditActorMode =\n | 'user'\n | 'guildmateUser'\n | 'adminReview'\n | 'adminOverride'\n | 'system';\n\n/** Fields common to every audited actor regardless of mode. `isAdmin` is\n * claim-present metadata only — it records that the caller held an admin\n * claim at the time, NOT that the action was an override. */\nexport type TTTAuditActorBase = {\n uid: string | null;\n isAdmin: boolean;\n};\n\n/** TTT actor shape, discriminated on `actorMode`. The admin system role is\n * REQUIRED on admin modes (adminReview / adminOverride) and FORBIDDEN on\n * non-admin modes (user / guildmateUser / system) — so an admin action can\n * never be audited without recording full-vs-jr, and a non-admin action can\n * never carry a spurious role. */\nexport type TTTAuditActor =\n | (TTTAuditActorBase & {\n actorMode: 'user' | 'guildmateUser' | 'system';\n systemRole?: never;\n })\n | (TTTAuditActorBase & {\n actorMode: 'adminReview' | 'adminOverride';\n systemRole: SystemRole;\n });\n\n/**\n * TTT target shape: the uid the audited action affects (if any), plus the\n * Firestore path of the affected document (if any).\n */\nexport type TTTAuditTarget = { uid: string | null; ref: string | null };\n\n/**\n * The canonical TTT audit event document shape. Specializes the generic\n * `AuditEvent` from @ttt-productions/audit-core with the TTT event-type\n * union, actor shape, target shape, and a generic metadata bag.\n */\nexport type TTTAuditEvent = AuditEvent<\n AuditEventType,\n TTTAuditActor,\n TTTAuditTarget,\n Record<string, unknown>\n>;\n\n"]}
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/types/audit.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,oEAAoE;AACpE,yEAAyE;AACzE,sEAAsE;AACtE,4DAA4D;AAC5D,EAAE;AACF,wEAAwE;AACxE,qBAAqB","sourcesContent":["// TTT audit event catalog. The `AuditEventType` union is the canonical list\n// of all sensitive actions the system logs. The `TTTAuditActor` and\n// `TTTAuditTarget` shapes describe who performed the action and what was\n// affected. `TTTAuditEvent` specializes the generic `AuditEvent` from\n// @ttt-productions/audit-core with the TTT-specific shapes.\n//\n// Backend writers and future native/TV/console readers all consume this\n// catalog from here.\n\nimport type { AuditEvent } from '@ttt-productions/audit-core';\nimport type { SystemRole } from '../schemas/atoms.js';\n\nexport type AuditEventType =\n // user status\n | 'artisanCreator.grantedToUser'\n | 'user.accountRegistered'\n | 'user.statusChanged'\n | 'user.accountSuspended'\n | 'user.accountBanned'\n | 'user.accountUnbanned'\n | 'user.displayNameResetForced'\n | 'user.displayNameChanged'\n | 'user.nonUsArtisanInterestRecorded'\n // account deletion / GDPR erasure (N3)\n | 'account.deletionRequested'\n | 'account.deletionCancelled'\n | 'account.erasureCompleted'\n | 'account.erasureParkedOnHold'\n | 'account.erasureSuperseded'\n // admin actions\n | 'admin.systemRoleGranted'\n | 'admin.systemRoleRevoked'\n | 'admin.systemRoleSyncedFromDirectEdit'\n | 'admin.contentAppealReviewed'\n | 'admin.thresholdItemReviewed'\n | 'admin.futurePlansUpdated'\n | 'admin.rulesAndAgreementsUpdated'\n // versioned public documents. Payload shapes: PublicDocumentsReleasedAuditPayload /\n // PublicDocumentsAcceptedAuditPayload in ../schemas/public-documents.ts. `accepted` is the\n // ONLY acceptance history (registration and the re-acceptance prompt both write it).\n | 'publicDocuments.released'\n | 'publicDocuments.accepted'\n // content-pages migration (DJ ruling 2026-07-06): per-page admin editors\n | 'admin.termsPageUpdated'\n | 'admin.privacyPageUpdated'\n | 'admin.takeItDownPageCopyUpdated'\n // admin launch-seed actions (Ready for Launch tab)\n | 'admin.reservedUsernamesSeeded'\n | 'admin.blockedFranchiseNamesSeeded'\n | 'admin.rulesAndAgreementsSeeded'\n | 'admin.futurePlansSeeded'\n | 'admin.termsPageSeeded'\n | 'admin.privacyPageSeeded'\n | 'admin.takeItDownPageCopySeeded'\n // admin direct-delete callables (replaced the last two direct-SDK admin-browser deletes):\n // deleteAdminDispatch (support thread + its conversationMessages) and deleteShortLink.\n | 'admin.dispatchDeleted'\n | 'admin.shortLinkDeleted'\n // trademark-assist (advisory check at approval) + parody/real-people disclaimer baked at approval\n | 'admin.trademarkChecked'\n | 'content.parodyDisclaimerApplied'\n // workProject\n | 'workProject.created'\n | 'workProject.guildmateUserGuildStandingChanged'\n | 'workProject.guildmateUserTradeProfessionsChanged'\n // Member self-leave (leaveWorkProject) — pre-publish voluntary exit, shares forfeited to the pool.\n | 'workProject.guildMemberDeparted'\n | 'workProject.publicDetailsUpdated'\n | 'workProject.workAssetDeleted'\n // work-file folders (S7). fileFolderAccessChanged is an ACL change (folder trade-profession\n // view/upload/delete lists gate custom-folder confidentiality) — audited with before/after lists.\n | 'workProject.fileFolderCreated'\n | 'workProject.fileFolderRenamed'\n | 'workProject.fileFolderAccessChanged'\n | 'workProject.fileFolderDeleted'\n | 'workProject.guildInviteSent'\n | 'workProject.guildInviteAccepted'\n | 'workProject.guildInviteDeclined'\n | 'workProject.guildInviteCancelled'\n | 'workProject.guildInviteStakeSharesUpdated'\n | 'workProject.commissionListingCreated'\n | 'workProject.commissionProposalCreated'\n | 'workProject.commissionClosed'\n | 'workProject.commissionProposalSaveToggled'\n | 'workProject.commissionProposalRejected'\n | 'workProject.stakeSharesChanged'\n | 'workProject.stakeShares.created'\n | 'workProject.stakeShares.increased'\n | 'workProject.stakeShares.anomaly'\n // realm shared files — the promotion approval gate. A Work file admin REQUESTS promotion;\n // the Realm steward approves (assigning the folder) or declines; the requester may withdraw\n // while the request is still pending. Every one of these carries the request id, so a\n // request and its resolution are one traceable pair.\n | 'workFile.realmShareRequested'\n | 'workFile.realmShareRequestWithdrawn'\n | 'workFile.realmSharePromotionApproved'\n | 'workFile.realmSharePromotionDeclined'\n // The steward moves an already-approved shared file between realm folders.\n | 'workFile.realmFileFolderAssignmentChanged'\n // Canon toggle on an approved file + the ADMIN-only un-share (sharing is permanent for\n // members once approved; the only way out is an admin acting on a support thread).\n | 'workFile.realmCanonChanged'\n | 'workFile.unsharedFromRealm'\n // RETIRED-ON-ADOPTION: the pre-approval-gate instant-share event. Its one emitter\n // (runUpdateWorkFileRealmShare) becomes a REQUEST and emits\n // `workFile.realmShareRequested`; the moment a file actually enters the pool is\n // `workFile.realmSharePromotionApproved`. Kept in the union only so the approval-gate\n // package release stays additive (a removal is a breaking contract change); delete it in\n // the follow-up release once no emitter remains.\n | 'workFile.sharedToRealm'\n // craft-skills\n | 'craftSkill.userCraftSkillDeleted'\n | 'craftSkill.hidden'\n | 'craftSkill.restored'\n | 'craftSkill.removed'\n // content\n | 'content.itemReported'\n | 'content.violationRecorded'\n | 'content.thresholdItemSubmitted'\n // Member-initiated pull-back of a still-pending threshold submission (withdraw callable —\n // deletes the threshold doc + admin task, flips the live sub-item back to unpublished).\n | 'content.thresholdItemWithdrawn'\n | 'content.hallItemPublished'\n // Published change requests (text-only at launch): a member proposes new text-field values\n // for a PUBLISHED hall item; an admin approves (server writes values in place) or denies.\n | 'content.hallContentChangeRequestSubmitted'\n | 'admin.hallContentChangeRequestResolved'\n | 'content.appealSubmitted'\n | 'content.violationAccepted'\n | 'content.hidden'\n | 'content.restored'\n | 'content.removed'\n // content (workProject content)\n | 'content.taleDetailsUpdated'\n | 'content.taleWorkGenresUpdated'\n | 'content.chapterCreated'\n | 'content.chapterDetailsUpdated'\n | 'content.tuneDetailsUpdated'\n | 'content.tuneWorkGenresUpdated'\n | 'content.tuneTrackCreated'\n | 'content.tuneTrackDetailsUpdated'\n | 'content.televisionDetailsUpdated'\n | 'content.televisionWorkGenresUpdated'\n | 'content.televisionEpisodeCreated'\n | 'content.televisionEpisodeDetailsUpdated'\n // money\n | 'payment.sessionCreated'\n | 'payment.pledgePaymentCompleted'\n | 'payment.pledgePaymentAbandoned'\n | 'payment.pledgePaymentFailed'\n | 'payment.pledgePaymentQuarantined'\n // money — refund + dispute lifecycle (post-launch handlers; types added now to avoid a\n // second package publish). Refund REQUESTS are the user-initiated ask; the refund itself is\n // the money movement. Disputes are Stripe-initiated (chargeback) open/close.\n | 'payment.pledgePaymentRefunded'\n | 'payment.pledgePaymentDisputeOpened'\n | 'payment.pledgePaymentDisputeClosed'\n | 'payment.pledgeRefundRequested'\n | 'payment.pledgeRefundRequestResolved'\n // money — independent ledger-integrity trigger (mirrors workProject.stakeShares.*)\n | 'payment.pledgeLedger.recorded'\n | 'payment.pledgeLedger.anomaly'\n // admin task lifecycle\n | 'admin.taskCheckedOut'\n | 'admin.taskCheckedIn'\n | 'admin.taskReleased'\n | 'admin.taskAutoReleased'\n | 'admin.taskDeferredLater'\n // audition\n | 'workProject.auditionCreated'\n | 'admin.auditionCreated'\n | 'audition.entryCreated'\n | 'audition.closedByUser'\n // system-actor event: a curated audition's option batch failed to assemble (media\n // rejection) and its curatedAssemblyStatus flipped to 'failed' (runFailCuratedAudition).\n | 'audition.curatedBatchFailed'\n // system\n | 'system.manualIntervention'\n | 'system.appConfigUpdated'\n // records (never controls) a charter↔full APP_MODE flip after deploy — written\n // by the idempotent recordAppModeFlip admin callable\n | 'admin.appModeFlipRecorded'\n | 'system.pendingMediaArchived'\n | 'system.orphanUploadsCleanedUp'\n | 'admin.profanityListSeeded'\n | 'admin.profanityListCurated'\n // social. Payload shape of the Square agreements acceptance:\n // SquareStreetzAgreementsAcceptedAuditPayload in ../schemas/users.ts.\n | 'social.squareStreetzAgreementsAccepted'\n | 'social.squareStreetzPostLiked'\n | 'social.squareStreetzPostUnliked'\n | 'social.targetFollowed'\n | 'social.targetUnfollowed'\n // chat\n | 'chat.guildChatChannelCreated'\n | 'chat.guildChatChannelUpdated'\n | 'chat.guildChatChannelArchived'\n | 'chat.guildChatChannelUnarchived'\n | 'chat.guildChatChannelDeleted'\n | 'chat.adminThreadStarted'\n | 'chat.adminThreadStatusChanged'\n // conversation files — every successful delete is audited with UID references and\n // canonical resource ids (the conversation ref, conversationFileId, mediaAssetId).\n | 'conversationFile.deleted'\n // chat moderation (DO-owned hide/delete via chatAdminActionCommands) — append-only,\n // deterministic ids hash('chat-moderation-{requested,applied,failed}', requestId, …).\n | 'chat.moderationActionRequested'\n | 'chat.moderationActionApplied'\n | 'chat.moderationActionFailed'\n // case-bound admin context read (read-on-demand, REVIEW-ONLY) — audits the request\n // AND outcome; one event per read keyed deterministically by the read's requestId.\n | 'chat.moderationContextRead'\n // notification (admin-only). Actor mode is always adminReview / adminOverride.\n // Payload shapes: NotificationBroadcastSentAuditMetadata /\n // NotificationAdminArchivedAuditMetadata in ../schemas/notification.ts.\n | 'notification.broadcastSent'\n | 'notification.adminArchived'\n // workProject\n | 'workProject.released'\n | 'workProject.hidden'\n | 'workProject.restored'\n | 'workProject.moderationPlaceholderApplied'\n | 'workProject.moderationRetitleCleared'\n | 'workRealm.created'\n | 'workRealm.released'\n | 'workRealm.detailsUpdated'\n | 'workRealm.hidden'\n | 'workRealm.restored'\n | 'workRealm.moderationPlaceholderApplied'\n | 'workRealm.moderationRetitleCleared'\n // Realm shared-file FOLDER management (steward-curated, Realm-wide-visible names —\n // mirrors the workProject.fileFolder* family). The Realm folder has no access lists, so\n // there is no `AccessChanged` sibling: a rename is the only in-place edit.\n | 'workRealm.fileFolderCreated'\n | 'workRealm.fileFolderUpdated'\n | 'workRealm.fileFolderDeleted'\n // hall-content moderation text-clear remedy (extends the workProject/workRealm placeholder\n // family across Tale/Tune/Television details + their chapter/track/episode sub-items and the\n // published Hall projections). Same past-tense placeholder-applied shape as the pair above.\n | 'hallItem.moderationPlaceholderApplied'\n // trust & safety — child safety / CSAM\n | 'report.childSafetyCaseOpened'\n | 'report.nciiCaseOpened'\n | 'childSafety.reportDispositionSet'\n | 'childSafety.holdPlaced'\n | 'childSafety.holdReleased'\n | 'childSafety.accountActionApplied'\n | 'childSafety.accountActionReverted'\n | 'childSafety.evidenceManifestCreated'\n | 'childSafety.evidenceDisposed'\n | 'childSafety.quarantineCompleted'\n | 'childSafety.ncmecSubmissionCompleted'\n | 'childSafety.ncmecPortalReceiptArtifactRecorded'\n | 'childSafety.legalProcessRecorded'\n | 'childSafety.falsePositiveCorrected'\n // trust & safety — age / registration\n | 'user.ageAttested'\n | 'user.ageUpgradedToAdult'\n | 'user.orphanAuthDeleted'\n | 'user.accountClosedUnder13'\n | 'user.safetyLocked'\n // trust & safety — NCII / TAKE IT DOWN\n | 'ncii.requestReceived'\n | 'ncii.requestSupplemented'\n | 'ncii.completenessDetermined'\n | 'ncii.validityDecided'\n | 'ncii.tempHoldPlaced'\n | 'ncii.tempHoldReleased'\n | 'ncii.removalCompleted'\n | 'ncii.removalUnableToComplete'\n // operator marks NCII evidence (e.g. flags/annotates an evidence item on the case) — a reviewed\n // evidence-handling action, audited like the other ncii evidence events.\n | 'ncii.evidenceMarked'\n | 'ncii.evidenceScanValidatedMatch'\n | 'ncii.hashBlockReversed'\n | 'ncii.requesterPiiScrubbed'\n | 'ncii.policyConfigUpdated'\n // operator sets the child-safety crossover minorAssessment on an NCII case (e.g. → possibleMinor):\n // routing action that opens/links a PARALLEL child-safety review + deny-serving + PhotoDNA, WITHOUT\n // touching the NCII removal clock and WITHOUT auto-NCMEC. Payload records from→to assessment.\n | 'ncii.minorAssessmentSet'\n // [H-2] a failed possible-minor crossover leg (serving-deny / PhotoDNA) was reconciled or replayed\n // by an operator/reconciler (crossoverLegs.<leg> failed → done). Payload records which leg + outcome.\n | 'ncii.crossoverLegReconciled'\n // trust & safety — operator security\n // Passkey enrollment/assertion reuses safety.privilegedReauthPerformed (the reauth catalog entry, as\n // the TOTP step-up enroll/confirm/verify phases already do) — no distinct passkey-registration type.\n | 'safety.privilegedReauthPerformed'\n // [P2-08] A privileged operator read of a TAKE IT DOWN request's RAW target locator (the\n // reveal-on-demand, review-only read of the sensitive locator itself — distinct from the\n // reveal reauth above, which is the step-up). One event per read. Mirrors the\n // chat.moderationContextRead privileged-read convention.\n | 'safety.privilegedRawLocatorRead'\n | 'safety.deadlineBreachRecorded'\n // [M-6] per-operator reviewer-capability grant lifecycle. Granting/revoking a\n // SafetyReviewerCapability is an authorization change — always audited.\n | 'safety.reviewerCapabilityGranted'\n | 'safety.reviewerCapabilityRevoked';\n\n/**\n * How the actor was acting when the audited action was performed. This is\n * orthogonal to the event name — the same event type can be produced by\n * different actor modes (e.g. a normal member vs. an admin override).\n *\n * - 'user' — authenticated action not tied to Work guild membership\n * - 'guildmateUser' — acting through normal Work guild (guild standing) permissions\n * - 'adminReview' — admin queue/support work on admin surfaces (no guild-permission concept)\n * - 'adminOverride' — system-admin authority superseding normal user/guild permission\n * - 'system' — backend automation: trigger, scheduled job, migration, media pipeline\n */\nexport type TTTAuditActorMode =\n | 'user'\n | 'guildmateUser'\n | 'adminReview'\n | 'adminOverride'\n | 'system';\n\n/** Fields common to every audited actor regardless of mode. `isAdmin` is\n * claim-present metadata only — it records that the caller held an admin\n * claim at the time, NOT that the action was an override. */\nexport type TTTAuditActorBase = {\n uid: string | null;\n isAdmin: boolean;\n};\n\n/** TTT actor shape, discriminated on `actorMode`. The admin system role is\n * REQUIRED on admin modes (adminReview / adminOverride) and FORBIDDEN on\n * non-admin modes (user / guildmateUser / system) — so an admin action can\n * never be audited without recording full-vs-jr, and a non-admin action can\n * never carry a spurious role. */\nexport type TTTAuditActor =\n | (TTTAuditActorBase & {\n actorMode: 'user' | 'guildmateUser' | 'system';\n systemRole?: never;\n })\n | (TTTAuditActorBase & {\n actorMode: 'adminReview' | 'adminOverride';\n systemRole: SystemRole;\n });\n\n/**\n * TTT target shape: the uid the audited action affects (if any), plus the\n * Firestore path of the affected document (if any).\n */\nexport type TTTAuditTarget = { uid: string | null; ref: string | null };\n\n/**\n * The canonical TTT audit event document shape. Specializes the generic\n * `AuditEvent` from @ttt-productions/audit-core with the TTT event-type\n * union, actor shape, target shape, and a generic metadata bag.\n */\nexport type TTTAuditEvent = AuditEvent<\n AuditEventType,\n TTTAuditActor,\n TTTAuditTarget,\n Record<string, unknown>\n>;\n\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type AppConfig } from '../doc-schemas/system.js';
|
|
2
|
+
import type { PublicDocumentVersionBlock } from '../doc-schemas/public-documents.js';
|
|
2
3
|
/**
|
|
3
4
|
* Every operator lever of `_appConfig/app`, each at a value its AppConfigSchema field accepts.
|
|
4
5
|
* The release-owned `publicDocumentVersions` block is not a lever.
|
|
@@ -25,6 +26,26 @@ export declare const DEFAULT_APP_CONFIG: AppConfigLevers;
|
|
|
25
26
|
* the maintenance takeover screen and in the callable refusal alike.
|
|
26
27
|
*/
|
|
27
28
|
export declare const DEFAULT_MAINTENANCE_MESSAGE = "TTT Productions is down for maintenance right now. We will be back shortly \u2014 thank you for your patience.";
|
|
29
|
+
/**
|
|
30
|
+
* The code the callable maintenance refusal is thrown with. `unavailable` is also what a real
|
|
31
|
+
* outage produces, so the code alone never identifies the refusal — its details do.
|
|
32
|
+
*/
|
|
33
|
+
export declare const MAINTENANCE_REFUSAL_CODE: "unavailable";
|
|
34
|
+
/** The `details.reason` the callable maintenance refusal carries. */
|
|
35
|
+
export declare const MAINTENANCE_REFUSAL_REASON: "maintenance";
|
|
36
|
+
/** Structured details carried by the callable maintenance refusal. */
|
|
37
|
+
export interface MaintenanceRefusalDetails {
|
|
38
|
+
readonly reason: typeof MAINTENANCE_REFUSAL_REASON;
|
|
39
|
+
}
|
|
40
|
+
/** True when `details` is the maintenance refusal's structured details. */
|
|
41
|
+
export declare function isMaintenanceRefusalDetails(details: unknown): details is MaintenanceRefusalDetails;
|
|
42
|
+
/**
|
|
43
|
+
* True when `err` is the callable maintenance refusal — an `unavailable` error in either
|
|
44
|
+
* spelling whose details carry `reason: 'maintenance'`. Works on the server-side error and on
|
|
45
|
+
* the client-side callable error alike. Decided by the code and the details, never the message
|
|
46
|
+
* text, so a genuine outage is never mistaken for a designed refusal.
|
|
47
|
+
*/
|
|
48
|
+
export declare function isMaintenanceRefusalError(err: unknown): boolean;
|
|
28
49
|
/**
|
|
29
50
|
* The complete `_appConfig/app` doc to write after applying `update` to `existing` (the doc as
|
|
30
51
|
* read in the writer's transaction; `undefined` or `null` when it does not exist). The update's
|
|
@@ -50,4 +71,30 @@ export declare function readAppConfigLever<L extends AppConfigLeverKey>(snapshot
|
|
|
50
71
|
* never throws.
|
|
51
72
|
*/
|
|
52
73
|
export declare function readAppConfigLevers(snapshot: Readonly<Record<string, unknown>> | null | undefined): AppConfigLevers;
|
|
74
|
+
/**
|
|
75
|
+
* What an unvalidated `_appConfig/app` snapshot holds in its release-owned version block:
|
|
76
|
+
* - `absent` — no block, so nothing has been released (every document at version 0, nothing
|
|
77
|
+
* required); `block` is `undefined`, which every public-document rule reads that way;
|
|
78
|
+
* - `valid` — the block, parsed;
|
|
79
|
+
* - `invalid` — a block is stored but its AppConfigSchema field refuses it (`null` included);
|
|
80
|
+
* `issue` names the first refused field. It carries no block, so no rule can be computed
|
|
81
|
+
* from it by accident.
|
|
82
|
+
*/
|
|
83
|
+
export type PublicDocumentVersionBlockReading = {
|
|
84
|
+
readonly status: 'absent';
|
|
85
|
+
readonly block: undefined;
|
|
86
|
+
} | {
|
|
87
|
+
readonly status: 'valid';
|
|
88
|
+
readonly block: PublicDocumentVersionBlock;
|
|
89
|
+
} | {
|
|
90
|
+
readonly status: 'invalid';
|
|
91
|
+
readonly issue: string;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* The version block off an unvalidated `_appConfig/app` snapshot (the raw stored doc; `null` or
|
|
95
|
+
* `undefined` when it is missing or not yet read), judged by its AppConfigSchema field. Unlike a
|
|
96
|
+
* lever there is no default to fall back to, so an invalid block is reported, never read as
|
|
97
|
+
* absent: what to do about it is the caller's policy. Pure; never throws.
|
|
98
|
+
*/
|
|
99
|
+
export declare function readPublicDocumentVersionBlock(snapshot: Readonly<Record<string, unknown>> | null | undefined): PublicDocumentVersionBlockReading;
|
|
53
100
|
//# sourceMappingURL=app-config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-config.d.ts","sourceRoot":"","sources":["../../src/utils/app-config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app-config.d.ts","sourceRoot":"","sources":["../../src/utils/app-config.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC3E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAErF;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,wBAAwB,CAAC,CAAC,CAAC,CAAC;AAE5F,oEAAoE;AACpE,MAAM,MAAM,iBAAiB,GAAG,MAAM,eAAe,CAAC;AAEtD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,EAAE,eAO/B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,2BAA2B,mHACqE,CAAC;AAE9G;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAG,aAAsB,CAAC;AAE/D,qEAAqE;AACrE,eAAO,MAAM,0BAA0B,EAAG,aAAsB,CAAC;AAEjE,sEAAsE;AACtE,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,MAAM,EAAE,OAAO,0BAA0B,CAAC;CACpD;AASD,2EAA2E;AAC3E,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,yBAAyB,CAGlG;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAM/D;AAMD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAC9D,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GACnC,SAAS,CAcX;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,iBAAiB,EAC5D,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAC9D,KAAK,EAAE,CAAC,GACP,eAAe,CAAC,CAAC,CAAC,CAKpB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,GAC7D,eAAe,CAMjB;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,iCAAiC,GACzC;IAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GACxD;IAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,0BAA0B,CAAA;CAAE,GACxE;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAO3D;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,GAC7D,iCAAiC,CAQnC"}
|
package/dist/utils/app-config.js
CHANGED
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
// `readAppConfigLever(snapshot, lever)`: the lever's stored value when its AppConfigSchema field
|
|
13
13
|
// accepts it, otherwise its DEFAULT_APP_CONFIG value, one lever at a time so a bad field never
|
|
14
14
|
// costs the good ones. `readAppConfigLevers` is every lever through it. The backend's cached
|
|
15
|
-
// snapshot reader and the client shell both read through these.
|
|
15
|
+
// snapshot reader and the client shell both read through these. The release-owned version block
|
|
16
|
+
// has no default to fall back to, so its reader, `readPublicDocumentVersionBlock`, reports what it
|
|
17
|
+
// found instead — absent, valid, or invalid — and each consumer applies its own policy.
|
|
16
18
|
//
|
|
17
19
|
// See docs/design/ttt-productions-version-gate.md (ttt-prod) § The `_appConfig/app` document and
|
|
18
20
|
// § Operational levers.
|
|
@@ -43,6 +45,37 @@ export const DEFAULT_APP_CONFIG = Object.freeze({
|
|
|
43
45
|
* the maintenance takeover screen and in the callable refusal alike.
|
|
44
46
|
*/
|
|
45
47
|
export const DEFAULT_MAINTENANCE_MESSAGE = 'TTT Productions is down for maintenance right now. We will be back shortly — thank you for your patience.';
|
|
48
|
+
/**
|
|
49
|
+
* The code the callable maintenance refusal is thrown with. `unavailable` is also what a real
|
|
50
|
+
* outage produces, so the code alone never identifies the refusal — its details do.
|
|
51
|
+
*/
|
|
52
|
+
export const MAINTENANCE_REFUSAL_CODE = 'unavailable';
|
|
53
|
+
/** The `details.reason` the callable maintenance refusal carries. */
|
|
54
|
+
export const MAINTENANCE_REFUSAL_REASON = 'maintenance';
|
|
55
|
+
// The refusal code as the server (`unavailable`) and the Firebase client SDK
|
|
56
|
+
// (`functions/unavailable`) spell it.
|
|
57
|
+
const MAINTENANCE_REFUSAL_CODES = new Set([
|
|
58
|
+
MAINTENANCE_REFUSAL_CODE,
|
|
59
|
+
`functions/${MAINTENANCE_REFUSAL_CODE}`,
|
|
60
|
+
]);
|
|
61
|
+
/** True when `details` is the maintenance refusal's structured details. */
|
|
62
|
+
export function isMaintenanceRefusalDetails(details) {
|
|
63
|
+
if (!details || typeof details !== 'object')
|
|
64
|
+
return false;
|
|
65
|
+
return details.reason === MAINTENANCE_REFUSAL_REASON;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* True when `err` is the callable maintenance refusal — an `unavailable` error in either
|
|
69
|
+
* spelling whose details carry `reason: 'maintenance'`. Works on the server-side error and on
|
|
70
|
+
* the client-side callable error alike. Decided by the code and the details, never the message
|
|
71
|
+
* text, so a genuine outage is never mistaken for a designed refusal.
|
|
72
|
+
*/
|
|
73
|
+
export function isMaintenanceRefusalError(err) {
|
|
74
|
+
if (!err || typeof err !== 'object')
|
|
75
|
+
return false;
|
|
76
|
+
const e = err;
|
|
77
|
+
return (typeof e.code === 'string' && MAINTENANCE_REFUSAL_CODES.has(e.code) && isMaintenanceRefusalDetails(e.details));
|
|
78
|
+
}
|
|
46
79
|
function definedFields(fields) {
|
|
47
80
|
return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));
|
|
48
81
|
}
|
|
@@ -94,4 +127,25 @@ export function readAppConfigLevers(snapshot) {
|
|
|
94
127
|
}
|
|
95
128
|
return levers;
|
|
96
129
|
}
|
|
130
|
+
const ABSENT_PUBLIC_DOCUMENT_VERSION_BLOCK = Object.freeze({
|
|
131
|
+
status: 'absent',
|
|
132
|
+
block: undefined,
|
|
133
|
+
});
|
|
134
|
+
/**
|
|
135
|
+
* The version block off an unvalidated `_appConfig/app` snapshot (the raw stored doc; `null` or
|
|
136
|
+
* `undefined` when it is missing or not yet read), judged by its AppConfigSchema field. Unlike a
|
|
137
|
+
* lever there is no default to fall back to, so an invalid block is reported, never read as
|
|
138
|
+
* absent: what to do about it is the caller's policy. Pure; never throws.
|
|
139
|
+
*/
|
|
140
|
+
export function readPublicDocumentVersionBlock(snapshot) {
|
|
141
|
+
const stored = snapshot?.publicDocumentVersions;
|
|
142
|
+
if (stored === undefined)
|
|
143
|
+
return ABSENT_PUBLIC_DOCUMENT_VERSION_BLOCK;
|
|
144
|
+
const parsed = AppConfigSchema.shape.publicDocumentVersions.safeParse(stored);
|
|
145
|
+
if (parsed.success && parsed.data !== undefined)
|
|
146
|
+
return { status: 'valid', block: parsed.data };
|
|
147
|
+
const issue = parsed.success ? undefined : parsed.error.issues[0];
|
|
148
|
+
const field = ['publicDocumentVersions', ...(issue?.path ?? []).map(String)].join('.');
|
|
149
|
+
return { status: 'invalid', issue: `${field}: ${issue?.message ?? 'invalid'}` };
|
|
150
|
+
}
|
|
97
151
|
//# sourceMappingURL=app-config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-config.js","sourceRoot":"","sources":["../../src/utils/app-config.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,EAAE;AACF,0FAA0F;AAC1F,gGAAgG;AAChG,+FAA+F;AAC/F,+FAA+F;AAC/F,8FAA8F;AAC9F,qFAAqF;AACrF,EAAE;AACF,8FAA8F;AAC9F,iFAAiF;AACjF,iGAAiG;AACjG,+FAA+F;AAC/F,6FAA6F;AAC7F,gEAAgE;AAChE,EAAE;AACF,iGAAiG;AACjG,wBAAwB;AAExB,OAAO,EAAE,eAAe,EAAkB,MAAM,0BAA0B,CAAC;AAW3E;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAoB,MAAM,CAAC,MAAM,CAAC;IAC/D,UAAU,EAAE,EAAE;IACd,eAAe,EAAE,KAAK;IACtB,kBAAkB,EAAE,EAAE;IACtB,mBAAmB,EAAE,IAAI;IACzB,mBAAmB,EAAE,EAAE;IACvB,mBAAmB,EAAE,CAAC;CACvB,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,2BAA2B,GACtC,2GAA2G,CAAC;AAE9G,SAAS,aAAa,CAAC,MAAyC;IAC9D,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC;AAC/F,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAA8D,EAC9D,MAAoC;IAEpC,MAAM,MAAM,GAAG,eAAe,CAAC,SAAS,CAAC;QACvC,GAAG,kBAAkB;QACrB,GAAG,aAAa,CAAC,QAAQ,IAAI,EAAE,CAAC;QAChC,GAAG,aAAa,CAAC,MAAM,CAAC;KACzB,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC;QAClD,MAAM,IAAI,KAAK,CACb,qEAAqE,KAAK,KAAK,KAAK,EAAE,OAAO,IAAI,SAAS,+BAA+B,CAC1I,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,QAA8D,EAC9D,KAAQ;IAER,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IACzE,OAAO,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAChD,CAAC,CAAE,MAAM,CAAC,IAA2B;QACrC,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAA8D;IAE9D,MAAM,MAAM,GAAgD,EAAE,CAAC;IAC/D,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAwB,EAAE,CAAC;QAC3E,MAAM,CAAC,KAAK,CAAC,GAAG,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,MAAyB,CAAC;AACnC,CAAC","sourcesContent":["// The `_appConfig/app` singleton at rest, and the ONE rule each for writing to it and reading it.\n//\n// WRITE. Several backend writers touch this doc (the admin App configuration card through\n// updateAppConfig, the public-document release publish, a seed), and each sends only the fields\n// it owns. Written as a bare merge, the first of them to run after the doc is gone (a fresh or\n// wiped environment) creates a doc WITHOUT the fields AppConfigSchema requires. The write rule\n// is `mergeAppConfigUpdate(existing, update)`: the writer's update on top of the doc it read,\n// with any field that doc lacks taken from DEFAULT_APP_CONFIG, validated as a whole.\n//\n// READ. The stored doc is untrusted — an operator can console-edit any field into any shape —\n// and a config read must never take the product down by itself. The read rule is\n// `readAppConfigLever(snapshot, lever)`: the lever's stored value when its AppConfigSchema field\n// accepts it, otherwise its DEFAULT_APP_CONFIG value, one lever at a time so a bad field never\n// costs the good ones. `readAppConfigLevers` is every lever through it. The backend's cached\n// snapshot reader and the client shell both read through these.\n//\n// See docs/design/ttt-productions-version-gate.md (ttt-prod) § The `_appConfig/app` document and\n// § Operational levers.\n\nimport { AppConfigSchema, type AppConfig } from '../doc-schemas/system.js';\n\n/**\n * Every operator lever of `_appConfig/app`, each at a value its AppConfigSchema field accepts.\n * The release-owned `publicDocumentVersions` block is not a lever.\n */\nexport type AppConfigLevers = Readonly<Required<Omit<AppConfig, 'publicDocumentVersions'>>>;\n\n/** The name of one operator lever — a key of DEFAULT_APP_CONFIG. */\nexport type AppConfigLeverKey = keyof AppConfigLevers;\n\n/**\n * Every operator lever of `_appConfig/app` at rest — the doc a fresh environment behaves as if\n * it held, and the value each reader falls back to for a missing or unreadable field. The OPEN\n * posture: maintenance off, registration open, no banner, no throttle.\n *\n * `appVersion` is `''`: no version has been published. VersionGate skips a falsy version, so it\n * stays dormant — no reload for any browser, whatever version it stored before — until an\n * operator sets a real one through updateAppConfig (whose input refuses `''`). From then on the\n * gate compares as usual.\n *\n * The release-owned `publicDocumentVersions` block is not a lever and has no default: it is\n * absent until the first release.\n */\nexport const DEFAULT_APP_CONFIG: AppConfigLevers = Object.freeze({\n appVersion: '',\n maintenanceMode: false,\n maintenanceMessage: '',\n registrationEnabled: true,\n announcementMessage: '',\n rateLimitMultiplier: 1,\n});\n\n/**\n * The line shown while maintenance is on and the operator left `maintenanceMessage` blank — on\n * the maintenance takeover screen and in the callable refusal alike.\n */\nexport const DEFAULT_MAINTENANCE_MESSAGE =\n 'TTT Productions is down for maintenance right now. We will be back shortly — thank you for your patience.';\n\nfunction definedFields(fields: Readonly<Record<string, unknown>>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));\n}\n\n/**\n * The complete `_appConfig/app` doc to write after applying `update` to `existing` (the doc as\n * read in the writer's transaction; `undefined` or `null` when it does not exist). The update's\n * fields win; `existing` supplies the rest; any field still missing comes from\n * DEFAULT_APP_CONFIG. An `undefined` update field changes nothing. Pure.\n *\n * Throws when the result would not parse under AppConfigSchema — an invalid update, or an\n * invalid field already on the doc — so nothing invalid is ever written over it.\n */\nexport function mergeAppConfigUpdate(\n existing: Readonly<Record<string, unknown>> | null | undefined,\n update: Readonly<Partial<AppConfig>>,\n): AppConfig {\n const parsed = AppConfigSchema.safeParse({\n ...DEFAULT_APP_CONFIG,\n ...definedFields(existing ?? {}),\n ...definedFields(update),\n });\n if (!parsed.success) {\n const issue = parsed.error.issues[0];\n const field = issue?.path.join('.') || 'document';\n throw new Error(\n `_appConfig/app would not match AppConfigSchema after this update (${field}: ${issue?.message ?? 'invalid'}). Nothing should be written.`,\n );\n }\n return parsed.data;\n}\n\n/**\n * One operator lever off an unvalidated `_appConfig/app` snapshot (the raw stored doc; `null` or\n * `undefined` when it is missing or not yet read): the stored value when its AppConfigSchema\n * field accepts it, otherwise the lever's DEFAULT_APP_CONFIG value — for an absent field and for\n * a value the schema refuses (a wrong type, an over-cap message, an out-of-range multiplier)\n * alike. Pure; never throws.\n */\nexport function readAppConfigLever<L extends AppConfigLeverKey>(\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\n lever: L,\n): AppConfigLevers[L] {\n const parsed = AppConfigSchema.shape[lever].safeParse(snapshot?.[lever]);\n return parsed.success && parsed.data !== undefined\n ? (parsed.data as AppConfigLevers[L])\n : DEFAULT_APP_CONFIG[lever];\n}\n\n/**\n * Every operator lever off an unvalidated `_appConfig/app` snapshot, each read through\n * `readAppConfigLever` — so a field the schema refuses falls back to its default while every\n * valid field survives, and a missing doc reads as DEFAULT_APP_CONFIG: the open posture. Pure;\n * never throws.\n */\nexport function readAppConfigLevers(\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\n): AppConfigLevers {\n const levers: Partial<Record<AppConfigLeverKey, unknown>> = {};\n for (const lever of Object.keys(DEFAULT_APP_CONFIG) as AppConfigLeverKey[]) {\n levers[lever] = readAppConfigLever(snapshot, lever);\n }\n return levers as AppConfigLevers;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"app-config.js","sourceRoot":"","sources":["../../src/utils/app-config.ts"],"names":[],"mappings":"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","sourcesContent":["// The `_appConfig/app` singleton at rest, and the ONE rule each for writing to it and reading it.\n//\n// WRITE. Several backend writers touch this doc (the admin App configuration card through\n// updateAppConfig, the public-document release publish, a seed), and each sends only the fields\n// it owns. Written as a bare merge, the first of them to run after the doc is gone (a fresh or\n// wiped environment) creates a doc WITHOUT the fields AppConfigSchema requires. The write rule\n// is `mergeAppConfigUpdate(existing, update)`: the writer's update on top of the doc it read,\n// with any field that doc lacks taken from DEFAULT_APP_CONFIG, validated as a whole.\n//\n// READ. The stored doc is untrusted — an operator can console-edit any field into any shape —\n// and a config read must never take the product down by itself. The read rule is\n// `readAppConfigLever(snapshot, lever)`: the lever's stored value when its AppConfigSchema field\n// accepts it, otherwise its DEFAULT_APP_CONFIG value, one lever at a time so a bad field never\n// costs the good ones. `readAppConfigLevers` is every lever through it. The backend's cached\n// snapshot reader and the client shell both read through these. The release-owned version block\n// has no default to fall back to, so its reader, `readPublicDocumentVersionBlock`, reports what it\n// found instead — absent, valid, or invalid — and each consumer applies its own policy.\n//\n// See docs/design/ttt-productions-version-gate.md (ttt-prod) § The `_appConfig/app` document and\n// § Operational levers.\n\nimport { AppConfigSchema, type AppConfig } from '../doc-schemas/system.js';\nimport type { PublicDocumentVersionBlock } from '../doc-schemas/public-documents.js';\n\n/**\n * Every operator lever of `_appConfig/app`, each at a value its AppConfigSchema field accepts.\n * The release-owned `publicDocumentVersions` block is not a lever.\n */\nexport type AppConfigLevers = Readonly<Required<Omit<AppConfig, 'publicDocumentVersions'>>>;\n\n/** The name of one operator lever — a key of DEFAULT_APP_CONFIG. */\nexport type AppConfigLeverKey = keyof AppConfigLevers;\n\n/**\n * Every operator lever of `_appConfig/app` at rest — the doc a fresh environment behaves as if\n * it held, and the value each reader falls back to for a missing or unreadable field. The OPEN\n * posture: maintenance off, registration open, no banner, no throttle.\n *\n * `appVersion` is `''`: no version has been published. VersionGate skips a falsy version, so it\n * stays dormant — no reload for any browser, whatever version it stored before — until an\n * operator sets a real one through updateAppConfig (whose input refuses `''`). From then on the\n * gate compares as usual.\n *\n * The release-owned `publicDocumentVersions` block is not a lever and has no default: it is\n * absent until the first release.\n */\nexport const DEFAULT_APP_CONFIG: AppConfigLevers = Object.freeze({\n appVersion: '',\n maintenanceMode: false,\n maintenanceMessage: '',\n registrationEnabled: true,\n announcementMessage: '',\n rateLimitMultiplier: 1,\n});\n\n/**\n * The line shown while maintenance is on and the operator left `maintenanceMessage` blank — on\n * the maintenance takeover screen and in the callable refusal alike.\n */\nexport const DEFAULT_MAINTENANCE_MESSAGE =\n 'TTT Productions is down for maintenance right now. We will be back shortly — thank you for your patience.';\n\n/**\n * The code the callable maintenance refusal is thrown with. `unavailable` is also what a real\n * outage produces, so the code alone never identifies the refusal — its details do.\n */\nexport const MAINTENANCE_REFUSAL_CODE = 'unavailable' as const;\n\n/** The `details.reason` the callable maintenance refusal carries. */\nexport const MAINTENANCE_REFUSAL_REASON = 'maintenance' as const;\n\n/** Structured details carried by the callable maintenance refusal. */\nexport interface MaintenanceRefusalDetails {\n readonly reason: typeof MAINTENANCE_REFUSAL_REASON;\n}\n\n// The refusal code as the server (`unavailable`) and the Firebase client SDK\n// (`functions/unavailable`) spell it.\nconst MAINTENANCE_REFUSAL_CODES: ReadonlySet<string> = new Set([\n MAINTENANCE_REFUSAL_CODE,\n `functions/${MAINTENANCE_REFUSAL_CODE}`,\n]);\n\n/** True when `details` is the maintenance refusal's structured details. */\nexport function isMaintenanceRefusalDetails(details: unknown): details is MaintenanceRefusalDetails {\n if (!details || typeof details !== 'object') return false;\n return (details as Record<string, unknown>).reason === MAINTENANCE_REFUSAL_REASON;\n}\n\n/**\n * True when `err` is the callable maintenance refusal — an `unavailable` error in either\n * spelling whose details carry `reason: 'maintenance'`. Works on the server-side error and on\n * the client-side callable error alike. Decided by the code and the details, never the message\n * text, so a genuine outage is never mistaken for a designed refusal.\n */\nexport function isMaintenanceRefusalError(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const e = err as { code?: unknown; details?: unknown };\n return (\n typeof e.code === 'string' && MAINTENANCE_REFUSAL_CODES.has(e.code) && isMaintenanceRefusalDetails(e.details)\n );\n}\n\nfunction definedFields(fields: Readonly<Record<string, unknown>>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));\n}\n\n/**\n * The complete `_appConfig/app` doc to write after applying `update` to `existing` (the doc as\n * read in the writer's transaction; `undefined` or `null` when it does not exist). The update's\n * fields win; `existing` supplies the rest; any field still missing comes from\n * DEFAULT_APP_CONFIG. An `undefined` update field changes nothing. Pure.\n *\n * Throws when the result would not parse under AppConfigSchema — an invalid update, or an\n * invalid field already on the doc — so nothing invalid is ever written over it.\n */\nexport function mergeAppConfigUpdate(\n existing: Readonly<Record<string, unknown>> | null | undefined,\n update: Readonly<Partial<AppConfig>>,\n): AppConfig {\n const parsed = AppConfigSchema.safeParse({\n ...DEFAULT_APP_CONFIG,\n ...definedFields(existing ?? {}),\n ...definedFields(update),\n });\n if (!parsed.success) {\n const issue = parsed.error.issues[0];\n const field = issue?.path.join('.') || 'document';\n throw new Error(\n `_appConfig/app would not match AppConfigSchema after this update (${field}: ${issue?.message ?? 'invalid'}). Nothing should be written.`,\n );\n }\n return parsed.data;\n}\n\n/**\n * One operator lever off an unvalidated `_appConfig/app` snapshot (the raw stored doc; `null` or\n * `undefined` when it is missing or not yet read): the stored value when its AppConfigSchema\n * field accepts it, otherwise the lever's DEFAULT_APP_CONFIG value — for an absent field and for\n * a value the schema refuses (a wrong type, an over-cap message, an out-of-range multiplier)\n * alike. Pure; never throws.\n */\nexport function readAppConfigLever<L extends AppConfigLeverKey>(\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\n lever: L,\n): AppConfigLevers[L] {\n const parsed = AppConfigSchema.shape[lever].safeParse(snapshot?.[lever]);\n return parsed.success && parsed.data !== undefined\n ? (parsed.data as AppConfigLevers[L])\n : DEFAULT_APP_CONFIG[lever];\n}\n\n/**\n * Every operator lever off an unvalidated `_appConfig/app` snapshot, each read through\n * `readAppConfigLever` — so a field the schema refuses falls back to its default while every\n * valid field survives, and a missing doc reads as DEFAULT_APP_CONFIG: the open posture. Pure;\n * never throws.\n */\nexport function readAppConfigLevers(\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\n): AppConfigLevers {\n const levers: Partial<Record<AppConfigLeverKey, unknown>> = {};\n for (const lever of Object.keys(DEFAULT_APP_CONFIG) as AppConfigLeverKey[]) {\n levers[lever] = readAppConfigLever(snapshot, lever);\n }\n return levers as AppConfigLevers;\n}\n\n/**\n * What an unvalidated `_appConfig/app` snapshot holds in its release-owned version block:\n * - `absent` — no block, so nothing has been released (every document at version 0, nothing\n * required); `block` is `undefined`, which every public-document rule reads that way;\n * - `valid` — the block, parsed;\n * - `invalid` — a block is stored but its AppConfigSchema field refuses it (`null` included);\n * `issue` names the first refused field. It carries no block, so no rule can be computed\n * from it by accident.\n */\nexport type PublicDocumentVersionBlockReading =\n | { readonly status: 'absent'; readonly block: undefined }\n | { readonly status: 'valid'; readonly block: PublicDocumentVersionBlock }\n | { readonly status: 'invalid'; readonly issue: string };\n\nconst ABSENT_PUBLIC_DOCUMENT_VERSION_BLOCK: PublicDocumentVersionBlockReading = Object.freeze({\n status: 'absent',\n block: undefined,\n});\n\n/**\n * The version block off an unvalidated `_appConfig/app` snapshot (the raw stored doc; `null` or\n * `undefined` when it is missing or not yet read), judged by its AppConfigSchema field. Unlike a\n * lever there is no default to fall back to, so an invalid block is reported, never read as\n * absent: what to do about it is the caller's policy. Pure; never throws.\n */\nexport function readPublicDocumentVersionBlock(\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\n): PublicDocumentVersionBlockReading {\n const stored = snapshot?.publicDocumentVersions;\n if (stored === undefined) return ABSENT_PUBLIC_DOCUMENT_VERSION_BLOCK;\n const parsed = AppConfigSchema.shape.publicDocumentVersions.safeParse(stored);\n if (parsed.success && parsed.data !== undefined) return { status: 'valid', block: parsed.data };\n const issue = parsed.success ? undefined : parsed.error.issues[0];\n const field = ['publicDocumentVersions', ...(issue?.path ?? []).map(String)].join('.');\n return { status: 'invalid', issue: `${field}: ${issue?.message ?? 'invalid'}` };\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type PublicDocumentId } from '../constants/public-documents.js';
|
|
2
2
|
import type { PublicDocumentAcceptance, PublicDocumentContent, PublicDocumentProjection, PublicDocumentVersionBlock, PublicDocumentVersionRef } from '../doc-schemas/public-documents.js';
|
|
3
3
|
import type { UserPrivateData } from '../doc-schemas/user.js';
|
|
4
|
+
import type { PublicDocumentVersionBlockReading } from './app-config.js';
|
|
4
5
|
/** The version block before any release: every document at version 0, nothing required. */
|
|
5
6
|
export declare const EMPTY_PUBLIC_DOCUMENT_VERSION_BLOCK: PublicDocumentVersionBlock;
|
|
6
7
|
/** A document's current published version; 0 when it has never been published. */
|
|
@@ -9,6 +10,12 @@ export declare function currentPublicDocumentVersion(block: PublicDocumentVersio
|
|
|
9
10
|
export declare function requiredPublicDocumentVersion(block: PublicDocumentVersionBlock | undefined, documentId: PublicDocumentId): number;
|
|
10
11
|
/** The required-acceptance level in force; 0 when nothing has ever required acceptance. */
|
|
11
12
|
export declare function requiredPublicDocumentAcceptanceLevel(block: PublicDocumentVersionBlock | undefined): number;
|
|
13
|
+
/**
|
|
14
|
+
* The required-acceptance level a version-block reading states: 0 when the block is absent, the
|
|
15
|
+
* block's level when it is valid, and `null` when it is invalid — an invalid block states no
|
|
16
|
+
* level, so the caller decides what to require.
|
|
17
|
+
*/
|
|
18
|
+
export declare function requiredPublicDocumentAcceptanceLevelOfReading(reading: PublicDocumentVersionBlockReading): number | null;
|
|
12
19
|
/** The level a person has accepted; 0 when they have accepted nothing. */
|
|
13
20
|
export declare function acceptedPublicDocumentAcceptanceLevel(acceptance: PublicDocumentAcceptance | undefined): number;
|
|
14
21
|
/** A document's accepted version; 0 when the person never accepted it. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-documents.d.ts","sourceRoot":"","sources":["../../src/utils/public-documents.ts"],"names":[],"mappings":"AAKA,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAE9F,OAAO,KAAK,EACV,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC1B,wBAAwB,EAEzB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"public-documents.d.ts","sourceRoot":"","sources":["../../src/utils/public-documents.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,6 +21,14 @@ export function requiredPublicDocumentVersion(block, documentId) {
|
|
|
21
21
|
export function requiredPublicDocumentAcceptanceLevel(block) {
|
|
22
22
|
return block?.requiredAcceptanceLevel ?? 0;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* The required-acceptance level a version-block reading states: 0 when the block is absent, the
|
|
26
|
+
* block's level when it is valid, and `null` when it is invalid — an invalid block states no
|
|
27
|
+
* level, so the caller decides what to require.
|
|
28
|
+
*/
|
|
29
|
+
export function requiredPublicDocumentAcceptanceLevelOfReading(reading) {
|
|
30
|
+
return reading.status === 'invalid' ? null : requiredPublicDocumentAcceptanceLevel(reading.block);
|
|
31
|
+
}
|
|
24
32
|
/** The level a person has accepted; 0 when they have accepted nothing. */
|
|
25
33
|
export function acceptedPublicDocumentAcceptanceLevel(acceptance) {
|
|
26
34
|
return acceptance?.acceptedLevel ?? 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-documents.js","sourceRoot":"","sources":["../../src/utils/public-documents.ts"],"names":[],"mappings":"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","sourcesContent":["// Pure rules of the versioned public-document system — the version math the one publish\n// transaction applies, and the comparisons both the acceptance prompt (client) and the\n// acceptance callable (server) make. One owner, so the prompt and the server can never\n// disagree about what changed or what is required.\n\nimport { PUBLIC_DOCUMENT_IDS, type PublicDocumentId } from '../constants/public-documents.js';\nimport { SPECIAL_DOCS } from '../paths/collections.js';\nimport type {\n PublicDocumentAcceptance,\n PublicDocumentContent,\n PublicDocumentProjection,\n PublicDocumentVersionBlock,\n PublicDocumentVersionRef,\n PublicDocumentVersionState,\n} from '../doc-schemas/public-documents.js';\nimport type { UserPrivateData } from '../doc-schemas/user.js';\n\n/** The version block before any release: every document at version 0, nothing required. */\nexport const EMPTY_PUBLIC_DOCUMENT_VERSION_BLOCK: PublicDocumentVersionBlock = Object.freeze({\n documents: Object.freeze({}),\n requiredAcceptanceLevel: 0,\n}) as PublicDocumentVersionBlock;\n\n/** A document's current published version; 0 when it has never been published. */\nexport function currentPublicDocumentVersion(\n block: PublicDocumentVersionBlock | undefined,\n documentId: PublicDocumentId,\n): number {\n return block?.documents[documentId]?.currentVersion ?? 0;\n}\n\n/** The latest version of a document a release required acceptance of; 0 when none has. */\nexport function requiredPublicDocumentVersion(\n block: PublicDocumentVersionBlock | undefined,\n documentId: PublicDocumentId,\n): number {\n return block?.documents[documentId]?.requiredVersion ?? 0;\n}\n\n/** The required-acceptance level in force; 0 when nothing has ever required acceptance. */\nexport function requiredPublicDocumentAcceptanceLevel(block: PublicDocumentVersionBlock | undefined): number {\n return block?.requiredAcceptanceLevel ?? 0;\n}\n\n/** The level a person has accepted; 0 when they have accepted nothing. */\nexport function acceptedPublicDocumentAcceptanceLevel(acceptance: PublicDocumentAcceptance | undefined): number {\n return acceptance?.acceptedLevel ?? 0;\n}\n\n/** A document's accepted version; 0 when the person never accepted it. */\nexport function acceptedPublicDocumentVersion(\n acceptance: PublicDocumentAcceptance | undefined,\n documentId: PublicDocumentId,\n): number {\n return acceptance?.documentVersions[documentId] ?? 0;\n}\n\n/** True when the person must accept before continuing: the required level is above theirs. */\nexport function isPublicDocumentAcceptanceRequired(\n block: PublicDocumentVersionBlock | undefined,\n acceptance: PublicDocumentAcceptance | undefined,\n): boolean {\n return requiredPublicDocumentAcceptanceLevel(block) > acceptedPublicDocumentAcceptanceLevel(acceptance);\n}\n\n/**\n * Every document whose CURRENT version is newer than the one the person accepted — including\n * changes from releases that did not require acceptance — at its current version, in canonical\n * order. This is exactly the list the acceptance prompt shows and the acceptance callable\n * expects back.\n */\nexport function changedPublicDocuments(\n block: PublicDocumentVersionBlock | undefined,\n acceptance: PublicDocumentAcceptance | undefined,\n): PublicDocumentVersionRef[] {\n const changed: PublicDocumentVersionRef[] = [];\n for (const documentId of PUBLIC_DOCUMENT_IDS) {\n const version = currentPublicDocumentVersion(block, documentId);\n if (version > acceptedPublicDocumentVersion(acceptance, documentId)) changed.push({ documentId, version });\n }\n return changed;\n}\n\n/** The two private-doc fields the Square agreements card records. */\nexport type SquareStreetzAgreementsRecord = Pick<\n UserPrivateData,\n 'squareStreetzAgreementsDate' | 'squareStreetzAgreementsVersion'\n>;\n\n/**\n * True when the person's Square agreements still stand: they recorded an acceptance date AND\n * the Rules & Agreements version they accepted is at or above the latest version a release\n * REQUIRED acceptance of. The card incorporates the Rules page by reference, so a Rules release\n * that required acceptance asks again; one that did not never does. The composer (client) and\n * every Square post path (server) decide with this one rule. A missing version block means no\n * Rules release has required acceptance yet.\n */\nexport function squareStreetzAgreementsSatisfied(\n privateData: SquareStreetzAgreementsRecord | null | undefined,\n block: PublicDocumentVersionBlock | undefined,\n): boolean {\n const acceptedAt = privateData?.squareStreetzAgreementsDate;\n const acceptedRulesVersion = privateData?.squareStreetzAgreementsVersion;\n return (\n typeof acceptedAt === 'number' &&\n typeof acceptedRulesVersion === 'number' &&\n acceptedRulesVersion >= requiredPublicDocumentVersion(block, SPECIAL_DOCS.RULES_AND_AGREEMENTS)\n );\n}\n\n/**\n * True when two document/version lists name the same pairs (order-insensitive). The acceptance\n * callable compares the versions the prompt showed with `changedPublicDocuments` now; any\n * difference means a publish raced the prompt, so it refreshes and asks again.\n */\nexport function samePublicDocumentVersions(\n a: readonly PublicDocumentVersionRef[],\n b: readonly PublicDocumentVersionRef[],\n): boolean {\n if (a.length !== b.length) return false;\n const key = (ref: PublicDocumentVersionRef) => `${ref.documentId}@${ref.version}`;\n const left = new Set(a.map(key));\n if (left.size !== a.length) return false;\n return b.every((ref) => left.has(key(ref)));\n}\n\n/**\n * The summary a person holds after accepting everything current — every published document at\n * its current version and the current required level. Registration and the acceptance callable\n * both write exactly this. `legalReviewNoticeRevision` is the active notice revision, or null\n * while the notice is off (then it is not recorded).\n */\nexport function publicDocumentAcceptanceSummary(\n block: PublicDocumentVersionBlock | undefined,\n recorded: { acceptedAt: number; legalReviewNoticeRevision: string | null },\n): PublicDocumentAcceptance {\n const documentVersions: PublicDocumentAcceptance['documentVersions'] = {};\n for (const documentId of PUBLIC_DOCUMENT_IDS) {\n const version = currentPublicDocumentVersion(block, documentId);\n if (version > 0) documentVersions[documentId] = version;\n }\n return {\n documentVersions,\n acceptedLevel: requiredPublicDocumentAcceptanceLevel(block),\n ...(recorded.legalReviewNoticeRevision !== null\n ? { legalReviewNoticeRevision: recorded.legalReviewNoticeRevision }\n : {}),\n acceptedAt: recorded.acceptedAt,\n };\n}\n\n/** What one release does to the version block. */\nexport interface PublicDocumentReleasePlan {\n /** The version block to write. */\n block: PublicDocumentVersionBlock;\n /** Each released document at its newly assigned version, in canonical order. */\n documents: PublicDocumentVersionRef[];\n /** The required-acceptance level after this release. */\n requiredAcceptanceLevel: number;\n}\n\n/**\n * Plan a release: each included document moves to its next whole-number version; when the\n * release requires acceptance, each also becomes that document's required version and the\n * required level rises by exactly one. Documents not in the release are untouched. Pure — the\n * publish transaction reads the block, plans, then writes the versions, projections, block,\n * release record, and audit event together. Throws on an empty or duplicated document list.\n */\nexport function planPublicDocumentRelease(\n block: PublicDocumentVersionBlock | undefined,\n documentIds: readonly PublicDocumentId[],\n requireAcceptance: boolean,\n): PublicDocumentReleasePlan {\n if (documentIds.length === 0) throw new Error('A release must include at least one document.');\n const included = new Set(documentIds);\n if (included.size !== documentIds.length) throw new Error('A release lists each document once.');\n\n const previousLevel = requiredPublicDocumentAcceptanceLevel(block);\n const nextLevel = requireAcceptance ? previousLevel + 1 : previousLevel;\n const nextDocuments: Partial<Record<PublicDocumentId, PublicDocumentVersionState>> = {\n ...(block?.documents ?? {}),\n };\n const released: PublicDocumentVersionRef[] = [];\n\n for (const documentId of PUBLIC_DOCUMENT_IDS) {\n if (!included.has(documentId)) continue;\n const previous = block?.documents[documentId];\n const version = (previous?.currentVersion ?? 0) + 1;\n nextDocuments[documentId] = {\n currentVersion: version,\n requiredVersion: requireAcceptance ? version : (previous?.requiredVersion ?? 0),\n };\n released.push({ documentId, version });\n }\n\n return {\n block: { documents: nextDocuments, requiredAcceptanceLevel: nextLevel },\n documents: released,\n requiredAcceptanceLevel: nextLevel,\n };\n}\n\n/** The current projection the publish writes for a document: its content, version, and time. */\nexport function buildPublicDocumentProjection<D extends PublicDocumentId>(\n content: PublicDocumentContent<D>,\n version: number,\n publishedAt: number,\n): PublicDocumentProjection<D> {\n return { ...content, version, lastUpdated: publishedAt };\n}\n\n// Key-order-insensitive canonical form of JSON-like content (strings, numbers, booleans,\n// null, arrays, plain objects). An `undefined` object field is the same as an absent one.\nfunction canonicalJson(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;\n if (value !== null && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(',')}}`;\n }\n return JSON.stringify(value) ?? 'null';\n}\n\n/**\n * True when two contents of the same document are the same words and structure (field order\n * ignored). The publish rejects a working copy equal to the current version, and the Admin\n * release form keeps Publish disabled for one.\n */\nexport function publicDocumentContentEquals<D extends PublicDocumentId>(\n a: PublicDocumentContent<D>,\n b: PublicDocumentContent<D>,\n): boolean {\n return canonicalJson(a) === canonicalJson(b);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"public-documents.js","sourceRoot":"","sources":["../../src/utils/public-documents.ts"],"names":[],"mappings":"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","sourcesContent":["// Pure rules of the versioned public-document system — the version math the one publish\n// transaction applies, and the comparisons both the acceptance prompt (client) and the\n// acceptance callable (server) make. One owner, so the prompt and the server can never\n// disagree about what changed or what is required.\n\nimport { PUBLIC_DOCUMENT_IDS, type PublicDocumentId } from '../constants/public-documents.js';\nimport { SPECIAL_DOCS } from '../paths/collections.js';\nimport type {\n PublicDocumentAcceptance,\n PublicDocumentContent,\n PublicDocumentProjection,\n PublicDocumentVersionBlock,\n PublicDocumentVersionRef,\n PublicDocumentVersionState,\n} from '../doc-schemas/public-documents.js';\nimport type { UserPrivateData } from '../doc-schemas/user.js';\nimport type { PublicDocumentVersionBlockReading } from './app-config.js';\n\n/** The version block before any release: every document at version 0, nothing required. */\nexport const EMPTY_PUBLIC_DOCUMENT_VERSION_BLOCK: PublicDocumentVersionBlock = Object.freeze({\n documents: Object.freeze({}),\n requiredAcceptanceLevel: 0,\n}) as PublicDocumentVersionBlock;\n\n/** A document's current published version; 0 when it has never been published. */\nexport function currentPublicDocumentVersion(\n block: PublicDocumentVersionBlock | undefined,\n documentId: PublicDocumentId,\n): number {\n return block?.documents[documentId]?.currentVersion ?? 0;\n}\n\n/** The latest version of a document a release required acceptance of; 0 when none has. */\nexport function requiredPublicDocumentVersion(\n block: PublicDocumentVersionBlock | undefined,\n documentId: PublicDocumentId,\n): number {\n return block?.documents[documentId]?.requiredVersion ?? 0;\n}\n\n/** The required-acceptance level in force; 0 when nothing has ever required acceptance. */\nexport function requiredPublicDocumentAcceptanceLevel(block: PublicDocumentVersionBlock | undefined): number {\n return block?.requiredAcceptanceLevel ?? 0;\n}\n\n/**\n * The required-acceptance level a version-block reading states: 0 when the block is absent, the\n * block's level when it is valid, and `null` when it is invalid — an invalid block states no\n * level, so the caller decides what to require.\n */\nexport function requiredPublicDocumentAcceptanceLevelOfReading(\n reading: PublicDocumentVersionBlockReading,\n): number | null {\n return reading.status === 'invalid' ? null : requiredPublicDocumentAcceptanceLevel(reading.block);\n}\n\n/** The level a person has accepted; 0 when they have accepted nothing. */\nexport function acceptedPublicDocumentAcceptanceLevel(acceptance: PublicDocumentAcceptance | undefined): number {\n return acceptance?.acceptedLevel ?? 0;\n}\n\n/** A document's accepted version; 0 when the person never accepted it. */\nexport function acceptedPublicDocumentVersion(\n acceptance: PublicDocumentAcceptance | undefined,\n documentId: PublicDocumentId,\n): number {\n return acceptance?.documentVersions[documentId] ?? 0;\n}\n\n/** True when the person must accept before continuing: the required level is above theirs. */\nexport function isPublicDocumentAcceptanceRequired(\n block: PublicDocumentVersionBlock | undefined,\n acceptance: PublicDocumentAcceptance | undefined,\n): boolean {\n return requiredPublicDocumentAcceptanceLevel(block) > acceptedPublicDocumentAcceptanceLevel(acceptance);\n}\n\n/**\n * Every document whose CURRENT version is newer than the one the person accepted — including\n * changes from releases that did not require acceptance — at its current version, in canonical\n * order. This is exactly the list the acceptance prompt shows and the acceptance callable\n * expects back.\n */\nexport function changedPublicDocuments(\n block: PublicDocumentVersionBlock | undefined,\n acceptance: PublicDocumentAcceptance | undefined,\n): PublicDocumentVersionRef[] {\n const changed: PublicDocumentVersionRef[] = [];\n for (const documentId of PUBLIC_DOCUMENT_IDS) {\n const version = currentPublicDocumentVersion(block, documentId);\n if (version > acceptedPublicDocumentVersion(acceptance, documentId)) changed.push({ documentId, version });\n }\n return changed;\n}\n\n/** The two private-doc fields the Square agreements card records. */\nexport type SquareStreetzAgreementsRecord = Pick<\n UserPrivateData,\n 'squareStreetzAgreementsDate' | 'squareStreetzAgreementsVersion'\n>;\n\n/**\n * True when the person's Square agreements still stand: they recorded an acceptance date AND\n * the Rules & Agreements version they accepted is at or above the latest version a release\n * REQUIRED acceptance of. The card incorporates the Rules page by reference, so a Rules release\n * that required acceptance asks again; one that did not never does. The composer (client) and\n * every Square post path (server) decide with this one rule. A missing version block means no\n * Rules release has required acceptance yet.\n */\nexport function squareStreetzAgreementsSatisfied(\n privateData: SquareStreetzAgreementsRecord | null | undefined,\n block: PublicDocumentVersionBlock | undefined,\n): boolean {\n const acceptedAt = privateData?.squareStreetzAgreementsDate;\n const acceptedRulesVersion = privateData?.squareStreetzAgreementsVersion;\n return (\n typeof acceptedAt === 'number' &&\n typeof acceptedRulesVersion === 'number' &&\n acceptedRulesVersion >= requiredPublicDocumentVersion(block, SPECIAL_DOCS.RULES_AND_AGREEMENTS)\n );\n}\n\n/**\n * True when two document/version lists name the same pairs (order-insensitive). The acceptance\n * callable compares the versions the prompt showed with `changedPublicDocuments` now; any\n * difference means a publish raced the prompt, so it refreshes and asks again.\n */\nexport function samePublicDocumentVersions(\n a: readonly PublicDocumentVersionRef[],\n b: readonly PublicDocumentVersionRef[],\n): boolean {\n if (a.length !== b.length) return false;\n const key = (ref: PublicDocumentVersionRef) => `${ref.documentId}@${ref.version}`;\n const left = new Set(a.map(key));\n if (left.size !== a.length) return false;\n return b.every((ref) => left.has(key(ref)));\n}\n\n/**\n * The summary a person holds after accepting everything current — every published document at\n * its current version and the current required level. Registration and the acceptance callable\n * both write exactly this. `legalReviewNoticeRevision` is the active notice revision, or null\n * while the notice is off (then it is not recorded).\n */\nexport function publicDocumentAcceptanceSummary(\n block: PublicDocumentVersionBlock | undefined,\n recorded: { acceptedAt: number; legalReviewNoticeRevision: string | null },\n): PublicDocumentAcceptance {\n const documentVersions: PublicDocumentAcceptance['documentVersions'] = {};\n for (const documentId of PUBLIC_DOCUMENT_IDS) {\n const version = currentPublicDocumentVersion(block, documentId);\n if (version > 0) documentVersions[documentId] = version;\n }\n return {\n documentVersions,\n acceptedLevel: requiredPublicDocumentAcceptanceLevel(block),\n ...(recorded.legalReviewNoticeRevision !== null\n ? { legalReviewNoticeRevision: recorded.legalReviewNoticeRevision }\n : {}),\n acceptedAt: recorded.acceptedAt,\n };\n}\n\n/** What one release does to the version block. */\nexport interface PublicDocumentReleasePlan {\n /** The version block to write. */\n block: PublicDocumentVersionBlock;\n /** Each released document at its newly assigned version, in canonical order. */\n documents: PublicDocumentVersionRef[];\n /** The required-acceptance level after this release. */\n requiredAcceptanceLevel: number;\n}\n\n/**\n * Plan a release: each included document moves to its next whole-number version; when the\n * release requires acceptance, each also becomes that document's required version and the\n * required level rises by exactly one. Documents not in the release are untouched. Pure — the\n * publish transaction reads the block, plans, then writes the versions, projections, block,\n * release record, and audit event together. Throws on an empty or duplicated document list.\n */\nexport function planPublicDocumentRelease(\n block: PublicDocumentVersionBlock | undefined,\n documentIds: readonly PublicDocumentId[],\n requireAcceptance: boolean,\n): PublicDocumentReleasePlan {\n if (documentIds.length === 0) throw new Error('A release must include at least one document.');\n const included = new Set(documentIds);\n if (included.size !== documentIds.length) throw new Error('A release lists each document once.');\n\n const previousLevel = requiredPublicDocumentAcceptanceLevel(block);\n const nextLevel = requireAcceptance ? previousLevel + 1 : previousLevel;\n const nextDocuments: Partial<Record<PublicDocumentId, PublicDocumentVersionState>> = {\n ...(block?.documents ?? {}),\n };\n const released: PublicDocumentVersionRef[] = [];\n\n for (const documentId of PUBLIC_DOCUMENT_IDS) {\n if (!included.has(documentId)) continue;\n const previous = block?.documents[documentId];\n const version = (previous?.currentVersion ?? 0) + 1;\n nextDocuments[documentId] = {\n currentVersion: version,\n requiredVersion: requireAcceptance ? version : (previous?.requiredVersion ?? 0),\n };\n released.push({ documentId, version });\n }\n\n return {\n block: { documents: nextDocuments, requiredAcceptanceLevel: nextLevel },\n documents: released,\n requiredAcceptanceLevel: nextLevel,\n };\n}\n\n/** The current projection the publish writes for a document: its content, version, and time. */\nexport function buildPublicDocumentProjection<D extends PublicDocumentId>(\n content: PublicDocumentContent<D>,\n version: number,\n publishedAt: number,\n): PublicDocumentProjection<D> {\n return { ...content, version, lastUpdated: publishedAt };\n}\n\n// Key-order-insensitive canonical form of JSON-like content (strings, numbers, booleans,\n// null, arrays, plain objects). An `undefined` object field is the same as an absent one.\nfunction canonicalJson(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;\n if (value !== null && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(',')}}`;\n }\n return JSON.stringify(value) ?? 'null';\n}\n\n/**\n * True when two contents of the same document are the same words and structure (field order\n * ignored). The publish rejects a working copy equal to the current version, and the Admin\n * release form keeps Publish disabled for one.\n */\nexport function publicDocumentContentEquals<D extends PublicDocumentId>(\n a: PublicDocumentContent<D>,\n b: PublicDocumentContent<D>,\n): boolean {\n return canonicalJson(a) === canonicalJson(b);\n}\n"]}
|