@ttt-productions/ttt-core 0.79.22 → 0.79.23

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.
@@ -19,6 +19,19 @@ export declare const FORMER_MEMBER_DISPLAY_NAME = "Former member";
19
19
  * both derive from it.
20
20
  */
21
21
  export declare const ACCOUNT_DELETION_GRACE_DAYS = 30;
22
+ /**
23
+ * Minimum length (inclusive) for a TTT account password. Counted in JavaScript
24
+ * string length units (UTF-16 code units), consistent with HTML input length
25
+ * behavior and the Firebase Auth SDK. Above Firebase's hard floor of 6 and within
26
+ * the supported hosted password-policy range, which is set from this declaration.
27
+ */
28
+ export declare const PASSWORD_MIN_LENGTH = 7;
29
+ /**
30
+ * Maximum length (inclusive) for a TTT account password, in the same UTF-16
31
+ * code-unit units as the minimum. Long enough for passphrases and
32
+ * password-manager output while keeping the product input finite.
33
+ */
34
+ export declare const PASSWORD_MAX_LENGTH = 64;
22
35
  /** Maximum number of craft-skills a user can upload to their profile. Mode-varied. */
23
36
  export declare const CRAFT_SKILL_LIMIT: number;
24
37
  /** Maximum number of work projects an artisan can own (steward) at once. */
@@ -1 +1 @@
1
- {"version":3,"file":"business-user.d.ts","sourceRoot":"","sources":["../../src/constants/business-user.ts"],"names":[],"mappings":"AAKA,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,2EAA2E;AAC3E,eAAO,MAAM,cAAc,QAAmB,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,kBAAkB,CAAC;AAE1D;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAM9C,sFAAsF;AACtF,eAAO,MAAM,iBAAiB,QAAoC,CAAC;AAInE,4EAA4E;AAC5E,eAAO,MAAM,uBAAuB,QAA0C,CAAC;AAE/E,0EAA0E;AAC1E,eAAO,MAAM,4BAA4B,QAA+C,CAAC;AAEzF,qDAAqD;AACrD,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,6CAA6C;AAC7C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C,yFAAyF;AACzF,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAI/C,iFAAiF;AACjF,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAIpC,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAIrC;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAC"}
1
+ {"version":3,"file":"business-user.d.ts","sourceRoot":"","sources":["../../src/constants/business-user.ts"],"names":[],"mappings":"AAKA,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,2EAA2E;AAC3E,eAAO,MAAM,cAAc,QAAmB,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,kBAAkB,CAAC;AAE1D;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAI9C;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAMtC,sFAAsF;AACtF,eAAO,MAAM,iBAAiB,QAAoC,CAAC;AAInE,4EAA4E;AAC5E,eAAO,MAAM,uBAAuB,QAA0C,CAAC;AAE/E,0EAA0E;AAC1E,eAAO,MAAM,4BAA4B,QAA+C,CAAC;AAEzF,qDAAqD;AACrD,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,6CAA6C;AAC7C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C,yFAAyF;AACzF,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAI/C,iFAAiF;AACjF,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAIpC,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAIrC;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAC"}
@@ -22,6 +22,20 @@ export const FORMER_MEMBER_DISPLAY_NAME = 'Former member';
22
22
  * both derive from it.
23
23
  */
24
24
  export const ACCOUNT_DELETION_GRACE_DAYS = 30;
25
+ // --- Account Passwords ---
26
+ /**
27
+ * Minimum length (inclusive) for a TTT account password. Counted in JavaScript
28
+ * string length units (UTF-16 code units), consistent with HTML input length
29
+ * behavior and the Firebase Auth SDK. Above Firebase's hard floor of 6 and within
30
+ * the supported hosted password-policy range, which is set from this declaration.
31
+ */
32
+ export const PASSWORD_MIN_LENGTH = 7;
33
+ /**
34
+ * Maximum length (inclusive) for a TTT account password, in the same UTF-16
35
+ * code-unit units as the minimum. Long enough for passphrases and
36
+ * password-manager output while keeping the product input finite.
37
+ */
38
+ export const PASSWORD_MAX_LENGTH = 64;
25
39
  import { ACTIVE_LIMITS } from './app-mode.js';
26
40
  // --- User Profile Craft Skills ---
27
41
  /** Maximum number of craft-skills a user can upload to their profile. Mode-varied. */
@@ -1 +1 @@
1
- {"version":3,"file":"business-user.js","sourceRoot":"","sources":["../../src/constants/business-user.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,+BAA+B;AAE/B,6BAA6B;AAE7B,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,2EAA2E;AAC3E,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,eAAe,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,oCAAoC;AAEpC,sFAAsF;AACtF,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC;AAEnE,yEAAyE;AAEzE,4EAA4E;AAC5E,MAAM,CAAC,MAAM,uBAAuB,GAAG,aAAa,CAAC,IAAI,CAAC,oBAAoB,CAAC;AAE/E,0EAA0E;AAC1E,MAAM,CAAC,MAAM,4BAA4B,GAAG,aAAa,CAAC,IAAI,CAAC,yBAAyB,CAAC;AAEzF,qDAAqD;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAEtC,6CAA6C;AAC7C,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,yFAAyF;AACzF,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,0BAA0B;AAE1B,iFAAiF;AACjF,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAEpC,iBAAiB;AAEjB,6DAA6D;AAC7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,gCAAgC;AAEhC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC","sourcesContent":["// User identity + profile business-rule constants — display names, craft skills,\n// mention history, and search.\n\n// --- User Display Names ---\n\n/** Minimum length for a user display name (inclusive). */\nexport const USERNAME_MIN_LENGTH = 3;\n\n/** Maximum length for a user display name (inclusive). */\nexport const USERNAME_MAX_LENGTH = 20;\n\n/** Allowed characters in a user display name: letters and numbers only. */\nexport const USERNAME_REGEX = /^[a-zA-Z0-9]+$/;\n\n/**\n * Display-name sentinel for an erased account (N3 data-deletion / GDPR erasure).\n * The `publicUsers/{uid}` doc is kept (so uid→name resolvers keep working) with\n * `displayName` set to this and `anonymizedAt` stamped; uid-keyed authorship /\n * share-tombstone records render with this label.\n */\nexport const FORMER_MEMBER_DISPLAY_NAME = 'Former member';\n\n/**\n * N3 account deletion / GDPR erasure — the grace window (days) between the deletion\n * request and the destructive scrub. Logging back in during the window is the one cancel.\n * Persisted on each request as `graceDays` (compliance trail) and stated as product policy\n * in the user-facing data-deletion documents — ONE declaration; the callable and the copy\n * both derive from it.\n */\nexport const ACCOUNT_DELETION_GRACE_DAYS = 30;\n\nimport { ACTIVE_LIMITS } from './app-mode.js';\n\n// --- User Profile Craft Skills ---\n\n/** Maximum number of craft-skills a user can upload to their profile. Mode-varied. */\nexport const CRAFT_SKILL_LIMIT = ACTIVE_LIMITS.user.maxCraftSkills;\n\n// --- Work-project participation caps (mode-varied, server-enforced) ---\n\n/** Maximum number of work projects an artisan can own (steward) at once. */\nexport const MAX_OWNED_WORK_PROJECTS = ACTIVE_LIMITS.user.maxOwnedWorkProjects;\n\n/** Maximum number of active guild memberships a user can hold at once. */\nexport const MAX_ASSOCIATED_WORK_PROJECTS = ACTIVE_LIMITS.user.maxAssociatedWorkProjects;\n\n/** Maximum number of tags allowed per craftSkill. */\nexport const MAX_CRAFT_SKILL_TAGS = 5;\n\n/** Maximum length for a craft-skill name. */\nexport const MAX_CRAFT_SKILL_NAME_LENGTH = 200;\n\n/** Non-US artisan-interest country/region free-text (input schema + privateData doc). */\nexport const MAX_ARTISAN_LOCATION_LENGTH = 100;\n\n// --- Mention History ---\n\n/** Maximum number of recent mentions kept in the user's mention-history hook. */\nexport const MAX_HISTORY_ITEMS = 10;\n\n// --- Search ---\n\n/** Maximum number of results returned by the search hook. */\nexport const SEARCH_RESULT_LIMIT = 6;\n\n// --- First-visit site tour ---\n\n/**\n * Current first-visit site-tour version. The `updateSiteTourPreference` callable stamps\n * this onto `privateData.siteTour.completedVersion` at completion, and the landing\n * eligibility check suppresses the automatic invitation while a member's\n * `completedVersion` equals it. Bump when the permanent-shell-controls tour changes\n * enough to re-invite every member. See\n * ttt-prod docs/design/landing-backstage-guide-and-first-visit-plan.md §10.\n */\nexport const SITE_TOUR_CURRENT_VERSION = 1;\n"]}
1
+ {"version":3,"file":"business-user.js","sourceRoot":"","sources":["../../src/constants/business-user.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,+BAA+B;AAE/B,6BAA6B;AAE7B,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,2EAA2E;AAC3E,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,eAAe,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAE9C,4BAA4B;AAE5B;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,oCAAoC;AAEpC,sFAAsF;AACtF,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC;AAEnE,yEAAyE;AAEzE,4EAA4E;AAC5E,MAAM,CAAC,MAAM,uBAAuB,GAAG,aAAa,CAAC,IAAI,CAAC,oBAAoB,CAAC;AAE/E,0EAA0E;AAC1E,MAAM,CAAC,MAAM,4BAA4B,GAAG,aAAa,CAAC,IAAI,CAAC,yBAAyB,CAAC;AAEzF,qDAAqD;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAEtC,6CAA6C;AAC7C,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,yFAAyF;AACzF,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,0BAA0B;AAE1B,iFAAiF;AACjF,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAEpC,iBAAiB;AAEjB,6DAA6D;AAC7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,gCAAgC;AAEhC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC","sourcesContent":["// User identity + profile business-rule constants — display names, craft skills,\n// mention history, and search.\n\n// --- User Display Names ---\n\n/** Minimum length for a user display name (inclusive). */\nexport const USERNAME_MIN_LENGTH = 3;\n\n/** Maximum length for a user display name (inclusive). */\nexport const USERNAME_MAX_LENGTH = 20;\n\n/** Allowed characters in a user display name: letters and numbers only. */\nexport const USERNAME_REGEX = /^[a-zA-Z0-9]+$/;\n\n/**\n * Display-name sentinel for an erased account (N3 data-deletion / GDPR erasure).\n * The `publicUsers/{uid}` doc is kept (so uid→name resolvers keep working) with\n * `displayName` set to this and `anonymizedAt` stamped; uid-keyed authorship /\n * share-tombstone records render with this label.\n */\nexport const FORMER_MEMBER_DISPLAY_NAME = 'Former member';\n\n/**\n * N3 account deletion / GDPR erasure — the grace window (days) between the deletion\n * request and the destructive scrub. Logging back in during the window is the one cancel.\n * Persisted on each request as `graceDays` (compliance trail) and stated as product policy\n * in the user-facing data-deletion documents — ONE declaration; the callable and the copy\n * both derive from it.\n */\nexport const ACCOUNT_DELETION_GRACE_DAYS = 30;\n\n// --- Account Passwords ---\n\n/**\n * Minimum length (inclusive) for a TTT account password. Counted in JavaScript\n * string length units (UTF-16 code units), consistent with HTML input length\n * behavior and the Firebase Auth SDK. Above Firebase's hard floor of 6 and within\n * the supported hosted password-policy range, which is set from this declaration.\n */\nexport const PASSWORD_MIN_LENGTH = 7;\n\n/**\n * Maximum length (inclusive) for a TTT account password, in the same UTF-16\n * code-unit units as the minimum. Long enough for passphrases and\n * password-manager output while keeping the product input finite.\n */\nexport const PASSWORD_MAX_LENGTH = 64;\n\nimport { ACTIVE_LIMITS } from './app-mode.js';\n\n// --- User Profile Craft Skills ---\n\n/** Maximum number of craft-skills a user can upload to their profile. Mode-varied. */\nexport const CRAFT_SKILL_LIMIT = ACTIVE_LIMITS.user.maxCraftSkills;\n\n// --- Work-project participation caps (mode-varied, server-enforced) ---\n\n/** Maximum number of work projects an artisan can own (steward) at once. */\nexport const MAX_OWNED_WORK_PROJECTS = ACTIVE_LIMITS.user.maxOwnedWorkProjects;\n\n/** Maximum number of active guild memberships a user can hold at once. */\nexport const MAX_ASSOCIATED_WORK_PROJECTS = ACTIVE_LIMITS.user.maxAssociatedWorkProjects;\n\n/** Maximum number of tags allowed per craftSkill. */\nexport const MAX_CRAFT_SKILL_TAGS = 5;\n\n/** Maximum length for a craft-skill name. */\nexport const MAX_CRAFT_SKILL_NAME_LENGTH = 200;\n\n/** Non-US artisan-interest country/region free-text (input schema + privateData doc). */\nexport const MAX_ARTISAN_LOCATION_LENGTH = 100;\n\n// --- Mention History ---\n\n/** Maximum number of recent mentions kept in the user's mention-history hook. */\nexport const MAX_HISTORY_ITEMS = 10;\n\n// --- Search ---\n\n/** Maximum number of results returned by the search hook. */\nexport const SEARCH_RESULT_LIMIT = 6;\n\n// --- First-visit site tour ---\n\n/**\n * Current first-visit site-tour version. The `updateSiteTourPreference` callable stamps\n * this onto `privateData.siteTour.completedVersion` at completion, and the landing\n * eligibility check suppresses the automatic invitation while a member's\n * `completedVersion` equals it. Bump when the permanent-shell-controls tour changes\n * enough to re-invite every member. See\n * ttt-prod docs/design/landing-backstage-guide-and-first-visit-plan.md §10.\n */\nexport const SITE_TOUR_CURRENT_VERSION = 1;\n"]}
@@ -1,2 +1,3 @@
1
1
  export * from './format.js';
2
+ export * from './password.js';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export * from './format.js';
2
+ export * from './password.js';
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC","sourcesContent":["export * from './format.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC","sourcesContent":["export * from './format.js';\nexport * from './password.js';\n"]}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The one TTT account-password contract, shared by registration, password reset,
3
+ * and any future password surface. Length only — TTT enforces no composition
4
+ * requirements, so spaces, printable Unicode, and emoji are all allowed.
5
+ *
6
+ * Deliberately NOT named `validatePassword`: Firebase's own SDK exports a
7
+ * `validatePassword(auth, password)` that reads mutable hosted project policy.
8
+ * This helper is the stable product contract; the two must never be confused.
9
+ *
10
+ * The value is never trimmed, normalized, or otherwise mutated, and confirmation
11
+ * matching is the caller's job. Length is JavaScript string length (UTF-16 code
12
+ * units), matching HTML input length behavior and the Firebase SDK — a
13
+ * surrogate-pair emoji therefore counts as 2.
14
+ *
15
+ * Client validation is UX; the Firebase-enforced password policy remains the
16
+ * security boundary (ENG-006).
17
+ *
18
+ * @returns `null` when the password is acceptable (both boundaries inclusive),
19
+ * otherwise the user-facing reason it was rejected.
20
+ */
21
+ export declare function validateTttPassword(password: string): string | null;
22
+ //# sourceMappingURL=password.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password.d.ts","sourceRoot":"","sources":["../../src/utils/password.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQnE"}
@@ -0,0 +1,31 @@
1
+ import { PASSWORD_MIN_LENGTH, PASSWORD_MAX_LENGTH } from '../constants/business-user.js';
2
+ /**
3
+ * The one TTT account-password contract, shared by registration, password reset,
4
+ * and any future password surface. Length only — TTT enforces no composition
5
+ * requirements, so spaces, printable Unicode, and emoji are all allowed.
6
+ *
7
+ * Deliberately NOT named `validatePassword`: Firebase's own SDK exports a
8
+ * `validatePassword(auth, password)` that reads mutable hosted project policy.
9
+ * This helper is the stable product contract; the two must never be confused.
10
+ *
11
+ * The value is never trimmed, normalized, or otherwise mutated, and confirmation
12
+ * matching is the caller's job. Length is JavaScript string length (UTF-16 code
13
+ * units), matching HTML input length behavior and the Firebase SDK — a
14
+ * surrogate-pair emoji therefore counts as 2.
15
+ *
16
+ * Client validation is UX; the Firebase-enforced password policy remains the
17
+ * security boundary (ENG-006).
18
+ *
19
+ * @returns `null` when the password is acceptable (both boundaries inclusive),
20
+ * otherwise the user-facing reason it was rejected.
21
+ */
22
+ export function validateTttPassword(password) {
23
+ if (password.length < PASSWORD_MIN_LENGTH) {
24
+ return `Password must be at least ${PASSWORD_MIN_LENGTH} characters.`;
25
+ }
26
+ if (password.length > PASSWORD_MAX_LENGTH) {
27
+ return `Password must be no more than ${PASSWORD_MAX_LENGTH} characters.`;
28
+ }
29
+ return null;
30
+ }
31
+ //# sourceMappingURL=password.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password.js","sourceRoot":"","sources":["../../src/utils/password.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEzF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,IAAI,QAAQ,CAAC,MAAM,GAAG,mBAAmB,EAAE,CAAC;QAC1C,OAAO,6BAA6B,mBAAmB,cAAc,CAAC;IACxE,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,GAAG,mBAAmB,EAAE,CAAC;QAC1C,OAAO,iCAAiC,mBAAmB,cAAc,CAAC;IAC5E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import { PASSWORD_MIN_LENGTH, PASSWORD_MAX_LENGTH } from '../constants/business-user.js';\n\n/**\n * The one TTT account-password contract, shared by registration, password reset,\n * and any future password surface. Length only — TTT enforces no composition\n * requirements, so spaces, printable Unicode, and emoji are all allowed.\n *\n * Deliberately NOT named `validatePassword`: Firebase's own SDK exports a\n * `validatePassword(auth, password)` that reads mutable hosted project policy.\n * This helper is the stable product contract; the two must never be confused.\n *\n * The value is never trimmed, normalized, or otherwise mutated, and confirmation\n * matching is the caller's job. Length is JavaScript string length (UTF-16 code\n * units), matching HTML input length behavior and the Firebase SDK — a\n * surrogate-pair emoji therefore counts as 2.\n *\n * Client validation is UX; the Firebase-enforced password policy remains the\n * security boundary (ENG-006).\n *\n * @returns `null` when the password is acceptable (both boundaries inclusive),\n * otherwise the user-facing reason it was rejected.\n */\nexport function validateTttPassword(password: string): string | null {\n if (password.length < PASSWORD_MIN_LENGTH) {\n return `Password must be at least ${PASSWORD_MIN_LENGTH} characters.`;\n }\n if (password.length > PASSWORD_MAX_LENGTH) {\n return `Password must be no more than ${PASSWORD_MAX_LENGTH} characters.`;\n }\n return null;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/ttt-core",
3
- "version": "0.79.22",
3
+ "version": "0.79.23",
4
4
  "description": "Core types, Firestore path constants, and shared constants for TTT Productions",
5
5
  "repository": {
6
6
  "type": "git",