@company-semantics/contracts 0.21.0 → 0.21.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@company-semantics/contracts",
3
- "version": "0.21.0",
3
+ "version": "0.21.1",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,31 @@
1
+ # identity/
2
+
3
+ ## Purpose
4
+
5
+ TypeScript types and functions for user identity and display name resolution.
6
+
7
+ ## Invariants
8
+
9
+ - Types with minimal runtime code (pure functions only, no side effects)
10
+ - No external dependencies (contracts policy)
11
+ - `preferredName` is assistant-facing only, not user-edited label
12
+ - `fullName` is user-editable override of derived firstName + lastName
13
+ - `displayName` is NEVER stored — always derived at read time via `resolveDisplayName()`
14
+ - `resolveDisplayName()` is the ONLY approved way to determine assistant addressing
15
+
16
+ ## Public API
17
+
18
+ ### Types
19
+
20
+ - `ISODateString` — ISO 8601 date-time string alias
21
+ - `UserIdentity` — User identity with name fields and timestamps
22
+
23
+ ### Functions
24
+
25
+ - `deriveFullName(firstName, lastName?)` — Derive full name from components
26
+ - `resolveDisplayName(identity)` — Get display name (preferredName or firstName)
27
+
28
+ ## Dependencies
29
+
30
+ **Imports from:** (none — leaf module)
31
+ **Imported by:** company-semantics-backend, company-semantics-app
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Display Name Functions
3
+ *
4
+ * Canonical functions for deriving and resolving user display names.
5
+ * These are the ONLY approved ways to handle display name logic.
6
+ *
7
+ * @see DECISIONS.md ADR-2026-01-020 for design rationale
8
+ */
9
+
10
+ import type { UserIdentity } from './types.js';
11
+
12
+ // =============================================================================
13
+ // Full Name Derivation
14
+ // =============================================================================
15
+
16
+ /**
17
+ * Derive fullName from firstName and lastName.
18
+ *
19
+ * Invariant: fullName MUST be derived automatically unless explicitly provided.
20
+ *
21
+ * @param firstName - User's first name (required)
22
+ * @param lastName - User's last name (optional)
23
+ * @returns Combined full name
24
+ *
25
+ * @example
26
+ * deriveFullName("Ian", "Heidt") // "Ian Heidt"
27
+ * deriveFullName("Ian") // "Ian"
28
+ */
29
+ export function deriveFullName(firstName: string, lastName?: string): string {
30
+ return lastName ? `${firstName} ${lastName}` : firstName;
31
+ }
32
+
33
+ // =============================================================================
34
+ // Display Name Resolution
35
+ // =============================================================================
36
+
37
+ /**
38
+ * Resolve the display name for assistant addressing.
39
+ *
40
+ * This is the ONLY approved way to determine how the assistant
41
+ * or UI should address a user.
42
+ *
43
+ * Invariants:
44
+ * - preferredName overrides if present and non-empty
45
+ * - Falls back to firstName (never blank)
46
+ * - displayName is NEVER stored, always derived at read time
47
+ *
48
+ * @param identity - Object with preferredName and firstName fields
49
+ * @returns The resolved display name
50
+ *
51
+ * @example
52
+ * resolveDisplayName({ firstName: "Ian", preferredName: "Heidt" }) // "Heidt"
53
+ * resolveDisplayName({ firstName: "Ian", preferredName: "" }) // "Ian"
54
+ * resolveDisplayName({ firstName: "Ian" }) // "Ian"
55
+ */
56
+ export function resolveDisplayName(
57
+ identity: Pick<UserIdentity, 'preferredName' | 'firstName'>
58
+ ): string {
59
+ return identity.preferredName?.trim() || identity.firstName;
60
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Identity Types Barrel
3
+ *
4
+ * Re-exports user identity vocabulary types and functions.
5
+ * Import from '@company-semantics/contracts' (root).
6
+ */
7
+
8
+ // Types
9
+ export type { ISODateString, UserIdentity } from './types.js';
10
+
11
+ // Functions
12
+ export { deriveFullName, resolveDisplayName } from './display-name.js';
@@ -0,0 +1,62 @@
1
+ /**
2
+ * User Identity Types
3
+ *
4
+ * Canonical vocabulary for user identity across Company Semantics.
5
+ *
6
+ * @see DECISIONS.md ADR-2026-01-020 for design rationale
7
+ */
8
+
9
+ // =============================================================================
10
+ // Utility Types
11
+ // =============================================================================
12
+
13
+ /**
14
+ * ISO 8601 date-time string.
15
+ * Example: "2025-12-25T14:30:00.000Z"
16
+ */
17
+ export type ISODateString = string;
18
+
19
+ // =============================================================================
20
+ // User Identity
21
+ // =============================================================================
22
+
23
+ /**
24
+ * User identity information.
25
+ * Represents the canonical identity vocabulary for a user.
26
+ *
27
+ * Invariants:
28
+ * - firstName is required (always addressable)
29
+ * - lastName is optional (single-name users exist globally)
30
+ * - fullName is STORED (derived by default, user-editable)
31
+ * - preferredName is STORED (assistant-only addressing)
32
+ * - displayName is NEVER stored (derived at read time via resolveDisplayName)
33
+ */
34
+ export type UserIdentity = {
35
+ /** Unique user identifier */
36
+ userId: string;
37
+
38
+ /** User's first name (required) */
39
+ firstName: string;
40
+
41
+ /** User's last name (optional) */
42
+ lastName?: string;
43
+
44
+ /**
45
+ * Full name for display in UI, exports, and audit logs.
46
+ * Derived by default from firstName + lastName, but user-editable.
47
+ */
48
+ fullName: string;
49
+
50
+ /**
51
+ * Preferred name for AI assistant to use.
52
+ * Assistant-only field; may differ from firstName.
53
+ * If not set, assistant falls back to firstName via resolveDisplayName().
54
+ */
55
+ preferredName?: string;
56
+
57
+ /** When the identity was created */
58
+ createdAt: ISODateString;
59
+
60
+ /** When the identity was last updated */
61
+ updatedAt: ISODateString;
62
+ };