@happyvertical/smrt-profiles 0.40.69 → 0.40.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. package/AGENTS.md +9 -4
  2. package/dist/chunks/{OidcIdentity-CFbUlB_Z.js → OidcIdentity-Bd68oFpZ.js} +3 -3
  3. package/dist/chunks/{OidcIdentity-CFbUlB_Z.js.map → OidcIdentity-Bd68oFpZ.js.map} +1 -1
  4. package/dist/chunks/{OidcIdentityCollection-BLxSaqJT.js → OidcIdentityCollection-DmllRnCw.js} +2 -2
  5. package/dist/chunks/{OidcIdentityCollection-BLxSaqJT.js.map → OidcIdentityCollection-DmllRnCw.js.map} +1 -1
  6. package/dist/chunks/{ProfileAssetCollection-FhW8q7_1.js → ProfileAssetCollection-DS2Nh2IQ.js} +2 -2
  7. package/dist/chunks/{ProfileAssetCollection-FhW8q7_1.js.map → ProfileAssetCollection-DS2Nh2IQ.js.map} +1 -1
  8. package/dist/chunks/{ProfileCollection-DdJ3W4GG.js → ProfileCollection-BOwbNCgG.js} +7 -14
  9. package/dist/chunks/ProfileCollection-BOwbNCgG.js.map +1 -0
  10. package/dist/chunks/{oidcProvisioningPrimitives-C9XSQNZj.js → oidcProvisioningPrimitives-BWHPpJde.js} +3 -3
  11. package/dist/chunks/{oidcProvisioningPrimitives-C9XSQNZj.js.map → oidcProvisioningPrimitives-BWHPpJde.js.map} +1 -1
  12. package/dist/chunks/{resolveIdentity-BpkxPkz7.js → resolveIdentity-F6aErDyh.js} +15 -15
  13. package/dist/chunks/{resolveIdentity-BpkxPkz7.js.map → resolveIdentity-F6aErDyh.js.map} +1 -1
  14. package/dist/index.js +10 -10
  15. package/dist/internal/oidc-provisioning.js +2 -2
  16. package/dist/manifest.json +61 -1
  17. package/dist/oidc-provisioning.d.ts +13 -1
  18. package/dist/smrt-knowledge.json +5 -5
  19. package/dist/utils.js +1 -1
  20. package/package.json +6 -6
  21. package/dist/chunks/ProfileCollection-DdJ3W4GG.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { d as coordinateOidcProvisioning, f as isOidcAbortedTransactionError, m as saveOidcRaceArbiter, p as isOidcProvisioningRaceConflict } from "../chunks/ProfileCollection-DdJ3W4GG.js";
2
- import { n as insertOidcProvisioningIdentity$1, t as createOidcProvisioningPerson$1 } from "../chunks/oidcProvisioningPrimitives-C9XSQNZj.js";
1
+ import { d as coordinateOidcProvisioning, f as isOidcAbortedTransactionError, m as saveOidcRaceArbiter, p as isOidcProvisioningRaceConflict } from "../chunks/ProfileCollection-BOwbNCgG.js";
2
+ import { n as insertOidcProvisioningIdentity$1, t as createOidcProvisioningPerson$1 } from "../chunks/oidcProvisioningPrimitives-BWHPpJde.js";
3
3
  //#region src/internal/oidc-provisioning.ts
4
4
  async function createOidcProvisioningPerson(db, input) {
5
5
  return await createOidcProvisioningPerson$1(db, input);
@@ -2,7 +2,7 @@
2
2
  "version": "1.0.0",
3
3
  "timestamp": 0,
4
4
  "packageName": "@happyvertical/smrt-profiles",
5
- "packageVersion": "0.40.69",
5
+ "packageVersion": "0.40.70",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-profiles:ApiKeyCollection": {
8
8
  "name": "apikeycollection",
@@ -2821,6 +2821,12 @@
2821
2821
  "context"
2822
2822
  ],
2823
2823
  "unique": true
2824
+ },
2825
+ {
2826
+ "name": "audit_logs_tenant_id_idx",
2827
+ "columns": [
2828
+ "tenant_id"
2829
+ ]
2824
2830
  }
2825
2831
  ],
2826
2832
  "version": "42bf3450"
@@ -4908,6 +4914,12 @@
4908
4914
  "columns": [
4909
4915
  "email_key"
4910
4916
  ]
4917
+ },
4918
+ {
4919
+ "name": "profiles_tenant_id_idx",
4920
+ "columns": [
4921
+ "tenant_id"
4922
+ ]
4911
4923
  }
4912
4924
  ],
4913
4925
  "version": "6f91abae"
@@ -5079,6 +5091,12 @@
5079
5091
  "relationship"
5080
5092
  ],
5081
5093
  "unique": true
5094
+ },
5095
+ {
5096
+ "name": "profile_assets_tenant_id_idx",
5097
+ "columns": [
5098
+ "tenant_id"
5099
+ ]
5082
5100
  }
5083
5101
  ],
5084
5102
  "version": "251cd922"
@@ -5771,6 +5789,12 @@
5771
5789
  "columns": [
5772
5790
  "_meta_type"
5773
5791
  ]
5792
+ },
5793
+ {
5794
+ "name": "profile_metadata_tenant_id_idx",
5795
+ "columns": [
5796
+ "tenant_id"
5797
+ ]
5774
5798
  }
5775
5799
  ],
5776
5800
  "version": "d4570a13"
@@ -6487,6 +6511,12 @@
6487
6511
  "columns": [
6488
6512
  "_meta_type"
6489
6513
  ]
6514
+ },
6515
+ {
6516
+ "name": "profile_metafields_tenant_id_idx",
6517
+ "columns": [
6518
+ "tenant_id"
6519
+ ]
6490
6520
  }
6491
6521
  ],
6492
6522
  "version": "afe420df"
@@ -7234,6 +7264,12 @@
7234
7264
  "columns": [
7235
7265
  "_meta_type"
7236
7266
  ]
7267
+ },
7268
+ {
7269
+ "name": "profile_relationships_tenant_id_idx",
7270
+ "columns": [
7271
+ "tenant_id"
7272
+ ]
7237
7273
  }
7238
7274
  ],
7239
7275
  "version": "8a5a4b64"
@@ -9247,6 +9283,12 @@
9247
9283
  "columns": [
9248
9284
  "_meta_type"
9249
9285
  ]
9286
+ },
9287
+ {
9288
+ "name": "profile_types_tenant_id_idx",
9289
+ "columns": [
9290
+ "tenant_id"
9291
+ ]
9250
9292
  }
9251
9293
  ],
9252
9294
  "version": "0fb37aeb"
@@ -10340,6 +10382,12 @@
10340
10382
  "columns": [
10341
10383
  "email_key"
10342
10384
  ]
10385
+ },
10386
+ {
10387
+ "name": "profiles_tenant_id_idx",
10388
+ "columns": [
10389
+ "tenant_id"
10390
+ ]
10343
10391
  }
10344
10392
  ],
10345
10393
  "version": "23f35a07"
@@ -11433,6 +11481,12 @@
11433
11481
  "columns": [
11434
11482
  "email_key"
11435
11483
  ]
11484
+ },
11485
+ {
11486
+ "name": "profiles_tenant_id_idx",
11487
+ "columns": [
11488
+ "tenant_id"
11489
+ ]
11436
11490
  }
11437
11491
  ],
11438
11492
  "version": "144faf5c"
@@ -12526,6 +12580,12 @@
12526
12580
  "columns": [
12527
12581
  "email_key"
12528
12582
  ]
12583
+ },
12584
+ {
12585
+ "name": "profiles_tenant_id_idx",
12586
+ "columns": [
12587
+ "tenant_id"
12588
+ ]
12529
12589
  }
12530
12590
  ],
12531
12591
  "version": "38738f31"
@@ -27,7 +27,19 @@ declare type DatabaseInterface_2 = Awaited<ReturnType<typeof getDatabase>>;
27
27
  /** Trusted integration insert after the caller proves Profile ownership. */
28
28
  export declare function insertOidcProvisioningIdentity<TIdentity>(db: DatabaseInterface, input: OidcProvisioningIdentityInput): Promise<TIdentity>;
29
29
 
30
- /** Detect an adapter error caused by an aborted transaction. */
30
+ /**
31
+ * Detect an adapter error caused by an aborted transaction.
32
+ *
33
+ * Reduced to the framework contract in #2366. The heuristic this replaced
34
+ * matched a bare `/transaction.*aborted/` against every message in the chain,
35
+ * which would misclassify a genuine foreign-key or check violation whose text
36
+ * happened to mention a transaction. Core now recognizes PostgreSQL's
37
+ * `25P02 in_failed_sql_transaction` from the driver's own SQLSTATE, whichever
38
+ * layer wrapped it.
39
+ *
40
+ * Kept as a named export because it is part of this package's provisioning
41
+ * API surface; the body is now a single delegation.
42
+ */
31
43
  export declare function isOidcAbortedTransactionError(error: unknown): boolean;
32
44
 
33
45
  /** Classify only the durable constraints used to converge OIDC retries. */
@@ -3,13 +3,13 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-profiles",
6
- "packageVersion": "0.40.69",
6
+ "packageVersion": "0.40.70",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "2a54d08b660587efdc927ffb50e229c3c62d2722ef682b9dbf76d5b627588b9c",
11
- "packageJson": "900b60068fc240d7e23b66a33b0391cb61a0d7bcb4df7818997529bad6f0bc43",
12
- "agents": "7fa3e5a4ecd53e6483b58d60aee82a9ef1bca0c7433454b316fc09cef938248f"
10
+ "manifest": "d7725bba86bc689f3fe1f7c4132c9f9bf05bc43bad3c34cb418104b43ad547af",
11
+ "packageJson": "e058f7f9c231c78f9291ca7e686b6142b3846058d8fab04faa56ca794076acc1",
12
+ "agents": "8b9fbd8e1f536f7700bc6b95395a610430fd88acb2907c80a70be0fcad30f2d7"
13
13
  },
14
14
  "exports": [
15
15
  ".",
@@ -8655,5 +8655,5 @@
8655
8655
  "polymorphicAssociations": 0,
8656
8656
  "uuidColumns": 61
8657
8657
  },
8658
- "agentDoc": "# @happyvertical/smrt-profiles\n\nCentral identity system with multi-auth, relationships, controlled metadata, and audit logging.\n\n## Models\n\n- **Profile** (STI base → Bot, Organization, Person): email (globally unique exact value), readonly indexed `emailKey` derived with `normalizeIdentityEmail()` for adapter-independent identity lookup, `typeId` FK to ProfileType, plus a `metadata` `@oneToMany('ProfileMetadata')` relationship for controlled per-profile values.\n- **ProfileAsset**: dedicated owned-asset join in `profile_assets` with `relationship` and `sortOrder`.\n- **ProfileRelationship**: bidirectional — creating one auto-creates reciprocal inverse. `contextProfileId` for tertiary relationships. `ProfileRelationshipTerm` tracks start/end dates.\n- **ProfileMetafield**: controlled vocabulary with `validationSchema`. **ProfileMetadata**: per-profile values linked to metafields.\n- **AuditLog**: action, resourceType/Id, `source` (web/cli/ci/webhook/mcp), `onBehalfOfId` for CI pass-through identity. `allowSuperAdminBypass: true`.\n\n## Auth Methods\n\n| Model | Pattern |\n|-------|---------|\n| NostrIdentity | Encrypted keypair (AES-256-GCM). Requires `SERVER_MASTER_SECRET` env var for decryption. NIP-05 address generation. |\n| OidcIdentity | Multiple issuers (Keycloak/Google/GitHub). Lookup by `issuer + subject` pair. Transactional provisioning derives the readonly nullable unique `identityKey` and backfills legacy rows. |\n| ApiKey | SHA-256 hashed. **Plaintext returned once only** on `generate()`. `keyPrefix` for identification. Scope-based with expiry. |\n| MagicLinkToken | One-time token with expiry for passwordless auth. |\n\n## Identity Resolution\n\nAuth helpers in `src/auth/` build profiles from external identity claims:\n\n- `resolveIdentity()` — top-level dispatcher that returns/creates a Profile from Nostr signatures, OIDC claims, magic link tokens, or API keys.\n- `createProfileFromOidc(claims, provider, options)` — creates `Profile` + `OidcIdentity` for first-time OIDC sign-in using a transaction-capable root database in `options.db`.\n- `ProfileCollection.findUniqueGlobalPersonByEmail(email)` — supported\n verified-identity lookup; fails closed on tenant-scoped, non-Person, or\n duplicate case-insensitive matches.\n- `ProfileCollection.requireCanonicalGlobalPerson(profileId, email?)` —\n validates an application-selected Profile against the same canonical global\n Person invariant.\n- `ProfileCollection.reserveCanonicalIdentityEmail(profileId, email?)` —\n validates and synchronizes the private unique\n `oidc_profile_email_reservations.email_key` used as the database arbiter for\n concurrent external-identity provisioning. Omitting `email` uses the\n Profile's stored address, moving or removing an existing reservation as the\n canonical Profile changes.\n- `createProfileFromNostr(email, nostrData)` — creates `Profile` + `NostrIdentity` for Nostr-authenticated users.\n\n## Key Methods\n\n- `Profile.getAssets()` / `addAsset()` / `removeAsset()` and the matching `ProfileCollection` wrappers — canonical owned asset helpers backed by `profile_assets`.\n- `Profile.addMetadata(metafieldSlug, value)` / `Profile.getMetadata()` — validates against metafield schema. `ProfileCollection.batchGetMetadata()` / `batchUpdateMetadata()` for bulk reads/writes.\n- `Profile.getRelationships({ direction: 'from'|'to'|'all' })` — direction matters.\n- `Profile.getRelationshipsFrom()` / `getRelationshipsTo()` — R10-generated `@oneToMany` accessors. ProfileRelationship has two FKs back to Profile, so each `@oneToMany` annotates its inverse explicitly (`{ foreignKey: 'fromProfileId' }` / `'toProfileId'`). Return raw `ProfileRelationship[]`; use `getRelationships()` for slug/direction filtering.\n- AI: `generateBio()` (uses `smrtProfiles.profile.generateBio` prompt via `@happyvertical/smrt-prompts`), `matches(criteria)` (delegates to `is()`).\n\n## Prompt Registry\n\n`generateBio()` is registered with `@happyvertical/smrt-prompts` so tenants can override template/model/params at runtime:\n\n```typescript\nimport { smrtProfilesGenerateBioPrompt } from '@happyvertical/smrt-profiles';\n// key: 'smrtProfiles.profile.generateBio'\n```\n\n## Gotchas\n\n- **SERVER_MASTER_SECRET required** for Nostr private key decryption — centralized key management\n- **API key never returned again**: `ApiKey.generate()` returns plaintext once; only `keyPrefix` visible later\n- **OIDC unique per issuer+subject**: same subject from different issuers =\n different identities. Both claims are opaque and case-sensitive; preserve\n exact whitespace after using trim only to reject blank values.\n- **OIDC identity mutations are trusted-only**: generated REST, MCP, and CLI\n surfaces are read-only. Link or update identities through transactional\n provisioning APIs so callers cannot rebind issuer/subject authority to an\n arbitrary Profile. Deprecated `OidcIdentity.findOrCreate()` preserves only\n transaction-safe exact-link reuse and refuses to create new links;\n `OidcIdentityCollection.linkToProfile()` and `Profile.linkOidcIdentity()`\n delegate to that same non-creating compatibility path.\n- **Apply schema migrations for identity race keys**: run `smrt db:status`,\n `smrt db:migrate`, then `smrt db:status` before deploying. Transactional OIDC\n provisioning populates `OidcIdentity.identityKey` and\n `oidc_profile_email_reservations`; legacy rows reserve an address only after\n safe validation. Stop/upgrade old Profile writers, then run the public,\n transactional and idempotent `backfillProfileEmailKeys(db)` from one deploy\n process. Verified-email provisioning and public email-key lookup require the\n standard `_smrt_backfills` readiness marker and fail closed immediately when\n it is absent. This keeps table scans in the explicit deploy step; runtime\n reads use the indexed key and validate only returned candidates.\n- **Public OIDC provisioning requires transactions**:\n `createProfileFromOidc()` owns a root transaction or uses a savepoint on an\n already-bound handle. Root adapters must expose `beginTransaction`;\n transaction-only handles are ambiguous and fail closed. Pass the root\n database for adapters without nested savepoints, including DuckDB;\n provisioning fails before durable writes if neither path is safe. Trusted\n framework packages use the private\n `@happyvertical/smrt-profiles/internal/oidc-provisioning` subpath instead of\n duplicating adapter probing, locking, or retry policy, and supply both exact\n issuer/subject and normalized-email lock keys in deterministic order. The\n coordinator additionally serializes all SQLite/DuckDB provisioning\n transactions per database URL because those adapters cannot overlap\n unrelated root transactions safely and retries bounded PostgreSQL\n deadlock/serialization failures. New OIDC Profiles use per-profile,\n non-semantic slugs so duplicate display names never invoke natural-key upsert.\n Caller-owned transactions never execute `_smrt_backfills` DDL; paths that\n perform canonical email lookup or reservation require the table to already\n exist or the caller must retry with the root database. Exact issuer/subject\n reuse skips the email-key readiness-marker lookup, but root coordination still\n initializes the shared tracker table. Caller-owned exact reuse does not\n consult the tracker and therefore does not require that table.\n- **Profile-only OIDC linking fails closed on existing email matches**:\n the typed canonical scenario contract is\n `src/testing/oidcProvisioningDecisionMatrix.ts`, executed by both Profiles\n and Users tests; keep public docs pointed at it instead of adding a second\n behavioral table.\n `createProfileFromOidc()` preserves exact issuer/subject reuse, including\n legacy tenant-scoped and non-Person links, but profiles cannot prove whether a\n User owns a same-email Profile. New identities therefore never attach to an\n existing email match through this helper; User/session provisioning still\n rejects unsafe linked Profiles before creating authentication state. Use\n `UserCollection.getOrCreateFromOidc()` for owner-aware verified-email reuse and\n its supported transaction-bound hooks. A pre-provisioned owned Person remains\n fail-closed unless the application explicitly uses `authorizeProfileOwner`\n to select both the canonical Profile and its existing approved sole owner;\n the users package revalidates those IDs, emails, and identity authority in\n the provisioning transaction.\n- **Email storage and identity matching differ**: `Profile.email` has an\n exact-value DB constraint. Identity boundaries query readonly indexed\n `Profile.emailKey`, derived by the shared TypeScript\n `normalizeIdentityEmail()` helper rather than adapter-specific SQL casing or\n trimming, and deliberately fail closed on duplicate normalized keys.\n- **Manifest objects must remain root-importable**: the non-API\n `OidcProfileEmailReservation` model and collection are public root exports\n because generated consumer registration imports every advertised manifest\n symbol from `@happyvertical/smrt-profiles`. Release-pack validation imports\n the actual generated register file from the packed tarball.\n- **Optional tenancy** on Profile; AuditLog allows super-admin bypass\n"
8658
+ "agentDoc": "# @happyvertical/smrt-profiles\n\nCentral identity system with multi-auth, relationships, controlled metadata, and audit logging.\n\n## Models\n\n- **Profile** (STI base → Bot, Organization, Person): email (globally unique exact value), readonly indexed `emailKey` derived with `normalizeIdentityEmail()` for adapter-independent identity lookup, `typeId` FK to ProfileType, plus a `metadata` `@oneToMany('ProfileMetadata')` relationship for controlled per-profile values.\n- **ProfileAsset**: dedicated owned-asset join in `profile_assets` with `relationship` and `sortOrder`.\n- **ProfileRelationship**: bidirectional — creating one auto-creates reciprocal inverse. `contextProfileId` for tertiary relationships. `ProfileRelationshipTerm` tracks start/end dates.\n- **ProfileMetafield**: controlled vocabulary with `validationSchema`. **ProfileMetadata**: per-profile values linked to metafields.\n- **AuditLog**: action, resourceType/Id, `source` (web/cli/ci/webhook/mcp), `onBehalfOfId` for CI pass-through identity. `allowSuperAdminBypass: true`.\n\n## Auth Methods\n\n| Model | Pattern |\n|-------|---------|\n| NostrIdentity | Encrypted keypair (AES-256-GCM). Requires `SERVER_MASTER_SECRET` env var for decryption. NIP-05 address generation. |\n| OidcIdentity | Multiple issuers (Keycloak/Google/GitHub). Lookup by `issuer + subject` pair. Transactional provisioning derives the readonly nullable unique `identityKey` and backfills legacy rows. |\n| ApiKey | SHA-256 hashed. **Plaintext returned once only** on `generate()`. `keyPrefix` for identification. Scope-based with expiry. |\n| MagicLinkToken | One-time token with expiry for passwordless auth. |\n\n## Identity Resolution\n\nAuth helpers in `src/auth/` build profiles from external identity claims:\n\n- `resolveIdentity()` — top-level dispatcher that returns/creates a Profile from Nostr signatures, OIDC claims, magic link tokens, or API keys.\n- `createProfileFromOidc(claims, provider, options)` — creates `Profile` + `OidcIdentity` for first-time OIDC sign-in using a transaction-capable root database in `options.db`.\n- `ProfileCollection.findUniqueGlobalPersonByEmail(email)` — supported\n verified-identity lookup; fails closed on tenant-scoped, non-Person, or\n duplicate case-insensitive matches.\n- `ProfileCollection.requireCanonicalGlobalPerson(profileId, email?)` —\n validates an application-selected Profile against the same canonical global\n Person invariant.\n- `ProfileCollection.reserveCanonicalIdentityEmail(profileId, email?)` —\n validates and synchronizes the private unique\n `oidc_profile_email_reservations.email_key` used as the database arbiter for\n concurrent external-identity provisioning. Omitting `email` uses the\n Profile's stored address, moving or removing an existing reservation as the\n canonical Profile changes.\n- `createProfileFromNostr(email, nostrData)` — creates `Profile` + `NostrIdentity` for Nostr-authenticated users.\n\n## Key Methods\n\n- `Profile.getAssets()` / `addAsset()` / `removeAsset()` and the matching `ProfileCollection` wrappers — canonical owned asset helpers backed by `profile_assets`.\n- `Profile.addMetadata(metafieldSlug, value)` / `Profile.getMetadata()` — validates against metafield schema. `ProfileCollection.batchGetMetadata()` / `batchUpdateMetadata()` for bulk reads/writes.\n- `Profile.getRelationships({ direction: 'from'|'to'|'all' })` — direction matters.\n- `Profile.getRelationshipsFrom()` / `getRelationshipsTo()` — R10-generated `@oneToMany` accessors. ProfileRelationship has two FKs back to Profile, so each `@oneToMany` annotates its inverse explicitly (`{ foreignKey: 'fromProfileId' }` / `'toProfileId'`). Return raw `ProfileRelationship[]`; use `getRelationships()` for slug/direction filtering.\n- AI: `generateBio()` (uses `smrtProfiles.profile.generateBio` prompt via `@happyvertical/smrt-prompts`), `matches(criteria)` (delegates to `is()`).\n\n## Prompt Registry\n\n`generateBio()` is registered with `@happyvertical/smrt-prompts` so tenants can override template/model/params at runtime:\n\n```typescript\nimport { smrtProfilesGenerateBioPrompt } from '@happyvertical/smrt-profiles';\n// key: 'smrtProfiles.profile.generateBio'\n```\n\n## Gotchas\n\n- **SERVER_MASTER_SECRET required** for Nostr private key decryption — centralized key management\n- **API key never returned again**: `ApiKey.generate()` returns plaintext once; only `keyPrefix` visible later\n- **OIDC unique per issuer+subject**: same subject from different issuers =\n different identities. Both claims are opaque and case-sensitive; preserve\n exact whitespace after using trim only to reject blank values.\n- **OIDC identity mutations are trusted-only**: generated REST, MCP, and CLI\n surfaces are read-only. Link or update identities through transactional\n provisioning APIs so callers cannot rebind issuer/subject authority to an\n arbitrary Profile. Deprecated `OidcIdentity.findOrCreate()` preserves only\n transaction-safe exact-link reuse and refuses to create new links;\n `OidcIdentityCollection.linkToProfile()` and `Profile.linkOidcIdentity()`\n delegate to that same non-creating compatibility path.\n- **Apply schema migrations for identity race keys**: run `smrt db:status`,\n `smrt db:migrate`, then `smrt db:status` before deploying. Transactional OIDC\n provisioning populates `OidcIdentity.identityKey` and\n `oidc_profile_email_reservations`; legacy rows reserve an address only after\n safe validation. Stop/upgrade old Profile writers, then run the public,\n transactional and idempotent `backfillProfileEmailKeys(db)` from one deploy\n process. Verified-email provisioning and public email-key lookup require the\n standard `_smrt_backfills` readiness marker and fail closed immediately when\n it is absent. This keeps table scans in the explicit deploy step; runtime\n reads use the indexed key and validate only returned candidates.\n- **Public OIDC provisioning requires transactions**:\n `createProfileFromOidc()` owns a root transaction or uses a savepoint on an\n already-bound handle. Root adapters must expose `beginTransaction`;\n transaction-only handles are ambiguous and fail closed. Pass the root\n database for adapters without nested savepoints, including DuckDB;\n provisioning fails before durable writes if neither path is safe. Trusted\n framework packages use the private\n `@happyvertical/smrt-profiles/internal/oidc-provisioning` subpath instead of\n duplicating adapter probing, locking, or retry policy, and supply both exact\n issuer/subject and normalized-email lock keys in deterministic order. The\n coordinator additionally serializes every root-handle statement it owns per\n database URL on SQLite/DuckDB — shared `_smrt_backfills` initialization, the\n provisioning transaction, and the post-commit rebind — because those adapters\n multiplex one native connection and cannot overlap unrelated root\n transactions safely. **Never overlap two statements on one such handle,\n transaction-bound or not** — no `Promise.all` over reads, not even\n primary-key rebinds — because DuckDB fails the losing prepared statement or\n aborts the process outright. It retries\n bounded PostgreSQL deadlock/serialization failures. New OIDC Profiles use per-profile,\n non-semantic slugs so duplicate display names never invoke natural-key upsert.\n Caller-owned transactions never execute `_smrt_backfills` DDL; paths that\n perform canonical email lookup or reservation require the table to already\n exist or the caller must retry with the root database. Exact issuer/subject\n reuse skips the email-key readiness-marker lookup, but root coordination still\n initializes the shared tracker table. Caller-owned exact reuse does not\n consult the tracker and therefore does not require that table.\n- **Profile-only OIDC linking fails closed on existing email matches**:\n the typed canonical scenario contract is\n `src/testing/oidcProvisioningDecisionMatrix.ts`, executed by both Profiles\n and Users tests; keep public docs pointed at it instead of adding a second\n behavioral table.\n `createProfileFromOidc()` preserves exact issuer/subject reuse, including\n legacy tenant-scoped and non-Person links, but profiles cannot prove whether a\n User owns a same-email Profile. New identities therefore never attach to an\n existing email match through this helper; User/session provisioning still\n rejects unsafe linked Profiles before creating authentication state. Use\n `UserCollection.getOrCreateFromOidc()` for owner-aware verified-email reuse and\n its supported transaction-bound hooks. A pre-provisioned owned Person remains\n fail-closed unless the application explicitly uses `authorizeProfileOwner`\n to select both the canonical Profile and its existing approved sole owner;\n the users package revalidates those IDs, emails, and identity authority in\n the provisioning transaction.\n- **Email storage and identity matching differ**: `Profile.email` has an\n exact-value DB constraint. Identity boundaries query readonly indexed\n `Profile.emailKey`, derived by the shared TypeScript\n `normalizeIdentityEmail()` helper rather than adapter-specific SQL casing or\n trimming, and deliberately fail closed on duplicate normalized keys.\n- **Manifest objects must remain root-importable**: the non-API\n `OidcProfileEmailReservation` model and collection are public root exports\n because generated consumer registration imports every advertised manifest\n symbol from `@happyvertical/smrt-profiles`. Release-pack validation imports\n the actual generated register file from the packed tarball.\n- **Optional tenancy** on Profile; AuditLog allows super-admin bypass\n"
8659
8659
  }
package/dist/utils.js CHANGED
@@ -4,7 +4,7 @@ async function getProfileMetadata(profileId, options = {}) {
4
4
  return await (await ProfileMetadataCollection.create(options)).getMetadataObject(profileId);
5
5
  }
6
6
  async function setProfileMetadata(profileId, metafieldSlug, value, options = {}) {
7
- const { ProfileCollection } = await import("./chunks/ProfileCollection-DdJ3W4GG.js").then((n) => n.r);
7
+ const { ProfileCollection } = await import("./chunks/ProfileCollection-BOwbNCgG.js").then((n) => n.r);
8
8
  const profile = await (await ProfileCollection.create(options)).get({ id: profileId });
9
9
  if (!profile) throw new Error(`Profile '${profileId}' not found`);
10
10
  await profile.addMetadata(metafieldSlug, value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-profiles",
3
- "version": "0.40.69",
3
+ "version": "0.40.70",
4
4
  "description": "Profile management system with relationships, metadata, and reciprocal associations for SMRT framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,10 +34,10 @@
34
34
  "@happyvertical/utils": "^0.88.0",
35
35
  "@noble/curves": "^1.9.7",
36
36
  "bech32": "^2.0.0",
37
- "@happyvertical/smrt-assets": "0.40.69",
38
- "@happyvertical/smrt-prompts": "0.40.69",
39
- "@happyvertical/smrt-tenancy": "0.40.69",
40
- "@happyvertical/smrt-core": "0.40.69"
37
+ "@happyvertical/smrt-assets": "0.40.70",
38
+ "@happyvertical/smrt-core": "0.40.70",
39
+ "@happyvertical/smrt-prompts": "0.40.70",
40
+ "@happyvertical/smrt-tenancy": "0.40.70"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@faker-js/faker": "^10.5.0",
@@ -46,7 +46,7 @@
46
46
  "typescript": "5.9.3",
47
47
  "vite": "8.1.4",
48
48
  "vitest": "4.1.10",
49
- "@happyvertical/smrt-vitest": "0.40.69"
49
+ "@happyvertical/smrt-vitest": "0.40.70"
50
50
  },
51
51
  "keywords": [
52
52
  "ai",
@@ -1 +0,0 @@
1
- {"version":3,"file":"ProfileCollection-DdJ3W4GG.js","names":["tx"],"sources":["../../src/prompts.ts","../../src/auth/normalizeIdentityEmail.ts","../../src/auth/oidcProvisioningCoordinator.ts","../../src/migrations/backfillProfileEmailKeys.ts","../../src/models/OidcProfileEmailReservation.ts","../../src/models/ProfileType.ts","../../src/models/Profile.ts","../../src/collections/OidcProfileEmailReservationCollection.ts","../../src/collections/ProfileCollection.ts"],"sourcesContent":["/**\n * Prompt registrations for the @happyvertical/smrt-profiles package.\n *\n * Prompts are registered at module-load time via `definePrompt()` so that\n * tenant-aware overrides can be applied at call time via `resolvePrompt()`.\n *\n * Mirrors the pattern used by `@happyvertical/smrt-content` (see\n * `content-prompts.ts`) and `@happyvertical/smrt-facts` (see `prompts.ts`).\n */\n\nimport {\n definePrompt,\n type ResolvedPromptAI,\n} from '@happyvertical/smrt-prompts';\n\n// Bio generation only uses non-PII profile fields (name + description).\n// Email is intentionally NOT passed to the AI provider — see review on PR\n// #1209 — to minimize PII exposure. If a downstream tenant needs email in\n// the bio, they can override the template via PromptOverride.\nexport const smrtProfilesGenerateBioPrompt = definePrompt({\n key: 'smrtProfiles.profile.generateBio',\n template: `Write a short, professional bio for this person.\n\nProfile name: {profileName}\nProfile description: {profileDescription}\n\nReturn only the bio text, with no commentary or surrounding quotation marks.`,\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\nexport function promptMessageOptions(ai: ResolvedPromptAI) {\n return {\n ...(ai.params || {}),\n ...(ai.model ? { model: ai.model } : {}),\n ...(typeof ai.temperature === 'number'\n ? { temperature: ai.temperature }\n : {}),\n ...(typeof ai.maxTokens === 'number' ? { maxTokens: ai.maxTokens } : {}),\n };\n}\n","/**\n * Canonicalize an email used as an external identity key.\n *\n * Keep this in application code: SQL `trim()` and `lower()` have\n * adapter-specific whitespace and Unicode behavior.\n */\nexport function normalizeIdentityEmail(email: string): string {\n const normalized = email.trim().toLowerCase();\n if (!normalized) {\n throw new Error('Identity email normalization requires a non-blank email.');\n }\n return normalized;\n}\n","import { ValidationError } from '@happyvertical/smrt-core';\nimport {\n BackfillTableUnavailableError,\n BackfillTracker,\n} from '@happyvertical/smrt-core/migrations';\nimport type { getDatabase } from '@happyvertical/sql';\n\ntype DatabaseInterface = Awaited<ReturnType<typeof getDatabase>>;\n\nconst provisioningLocks = new Map<\n string | object,\n Map<string, Promise<void>>\n>();\nconst sqliteRetryLocks = new Map<string | object, Promise<void>>();\nlet savepointSequence = 0;\n\nconst OIDC_RACE_FIELD_NAMES = new Set([\n 'email_key',\n 'emailKey',\n 'identity_key',\n 'identityKey',\n 'profile_id',\n 'profileId',\n]);\nconst OIDC_RACE_MESSAGE_PATTERNS = [\n /oidc_identities[._].*identity_key/iu,\n /oidc_identities_identity_key/iu,\n /oidc_profile_email_reservations[._].*(?:email_key|profile_id)/iu,\n /profile_types_slug_context_meta_type/iu,\n];\n\nclass OidcRaceArbiterConflict extends Error {\n constructor(cause: unknown) {\n super('An OIDC provisioning race arbiter reported a unique conflict.', {\n cause,\n });\n this.name = 'OidcRaceArbiterConflict';\n }\n}\n\nclass SavepointUnavailableError extends Error {\n constructor(cause: unknown) {\n super('OIDC provisioning could not start a transaction savepoint.', {\n cause,\n });\n this.name = 'SavepointUnavailableError';\n }\n}\n\n/** Shared transaction and concurrency policy for OIDC provisioning. */\nexport interface OidcProvisioningCoordinatorOptions<T> {\n /** Root or transaction-bound database used for every provisioning write. */\n db: DatabaseInterface;\n /**\n * Stable keys used for local serialization. Supply both the exact\n * issuer/subject key and normalized email when available.\n */\n lockKeys: readonly string[];\n /** Package-specific durable race-arbiter classifier. */\n isRaceConflict: (error: unknown) => boolean;\n /** Package-specific transaction failure mapping. */\n createTransactionError: (message: string, cause?: unknown) => Error;\n /** Package-specific terminal concurrency failure mapping. */\n createConcurrencyError: (cause?: unknown) => Error;\n /** Provision atomically using the supplied transaction-bound handle. */\n provision: (tx: DatabaseInterface) => Promise<T>;\n /**\n * Rehydrate root-owned results after commit so models do not retain a\n * released transaction client. Omit for non-model results.\n */\n rebindRootResult?: (result: T, rootDb: DatabaseInterface) => Promise<T>;\n}\n\n/**\n * Coordinate one OIDC provisioning operation across adapters.\n *\n * Root adapters must expose `beginTransaction`. Already-bound adapters must\n * support savepoints. An adapter that exposes only `transaction()` is\n * intentionally ambiguous and fails closed instead of risking a nested\n * transaction that could roll back its caller's work.\n */\nexport async function coordinateOidcProvisioning<T>(\n options: OidcProvisioningCoordinatorOptions<T>,\n): Promise<T> {\n // Establish the shared backfill table outside the provisioning transaction.\n // Transaction-bound callers are handled below via savepoints and must not\n // leak DDL into their caller's transaction.\n const rootBackfillTableReady =\n typeof options.db.beginTransaction === 'function';\n if (rootBackfillTableReady) {\n await new BackfillTracker({ db: options.db }).initialize();\n }\n return withProvisioningLocks(options.db, options.lockKeys, async () => {\n const result = await retryProvisioning(options, () =>\n withProvisioningTransaction(\n options.db,\n (tx) => {\n // Marker reads inside provisioning transactions must never run DDL.\n // Root callers inherit a verified durable initialization; already-\n // bound callers may read an existing table but fail closed if it is\n // absent because no root handle is available for safe recovery.\n if (rootBackfillTableReady) {\n BackfillTracker.inheritInitialization(tx, options.db);\n } else {\n BackfillTracker.requireExistingTable(tx);\n }\n return options.provision(tx);\n },\n options.createTransactionError,\n ),\n );\n return rootBackfillTableReady && options.rebindRootResult\n ? options.rebindRootResult(result, options.db)\n : result;\n });\n}\n\nasync function withProvisioningLocks<T>(\n db: DatabaseInterface,\n lockKeys: readonly string[],\n callback: () => Promise<T>,\n): Promise<T> {\n // SQLite and DuckDB root handles cannot safely overlap transactions, even\n // when the identities are unrelated. Their database-scoped adapter lock is\n // acquired before the narrower identity/email locks shared across handles.\n const keys = [\n ...new Set([\n ...(!isPostgresDatabase(db) || typeof db.beginTransaction !== 'function'\n ? ['adapter-transaction']\n : []),\n ...lockKeys,\n ]),\n ].sort();\n const acquire = (index: number): Promise<T> =>\n index >= keys.length\n ? callback()\n : withProvisioningLock(db, keys[index], () => acquire(index + 1));\n return acquire(0);\n}\n\n/** Detect an adapter error caused by an aborted transaction. */\nexport function isOidcAbortedTransactionError(error: unknown): boolean {\n const seen = new Set<unknown>();\n let current: unknown = error;\n while (current instanceof Error && !seen.has(current)) {\n seen.add(current);\n if (\n /current transaction is aborted|transaction.*aborted/iu.test(\n current.message,\n )\n ) {\n return true;\n }\n current = (current as { cause?: unknown }).cause;\n }\n return false;\n}\n\nexport interface OidcProvisioningRaceClassifierOptions {\n /** Package-specific database constraint messages. */\n messagePatterns?: readonly RegExp[];\n}\n\nfunction testRaceMessagePattern(pattern: RegExp, message: string): boolean {\n // Callers may supply stateful global/sticky expressions. Do not let a prior\n // classification change the result of the next provisioning attempt.\n pattern.lastIndex = 0;\n try {\n return pattern.test(message);\n } finally {\n pattern.lastIndex = 0;\n }\n}\n\n/** Classify only the durable constraints used to converge OIDC retries. */\nexport function isOidcProvisioningRaceConflict(\n error: unknown,\n options: OidcProvisioningRaceClassifierOptions = {},\n): boolean {\n if (isOidcAbortedTransactionError(error)) return true;\n const pending: unknown[] = [error];\n const seen = new Set<unknown>();\n const messagePatterns = [\n ...OIDC_RACE_MESSAGE_PATTERNS,\n ...(options.messagePatterns ?? []),\n ];\n while (pending.length > 0) {\n const current = pending.pop();\n if (!current || typeof current !== 'object' || seen.has(current)) continue;\n seen.add(current);\n if (current instanceof OidcRaceArbiterConflict) return true;\n if (\n current instanceof Error &&\n current.name === 'CanonicalPersonProfileError' &&\n (current as Error & { code?: unknown }).code === 'reservation_conflict'\n ) {\n return true;\n }\n if (\n current instanceof ValidationError &&\n current.code === 'VALIDATION_UNIQUE_CONSTRAINT' &&\n OIDC_RACE_FIELD_NAMES.has(String(current.details?.fieldName ?? ''))\n ) {\n return true;\n }\n if (\n current instanceof Error &&\n messagePatterns.some((pattern) =>\n testRaceMessagePattern(pattern, current.message),\n )\n ) {\n return true;\n }\n const details = current as {\n cause?: unknown;\n context?: unknown;\n details?: unknown;\n originalError?: unknown;\n };\n pending.push(\n details.cause,\n details.context,\n details.details,\n details.originalError,\n );\n if (current instanceof AggregateError) pending.push(...current.errors);\n }\n return false;\n}\n\n/** Wrap a known OIDC arbiter write so only its unique failure is retryable. */\nexport async function saveOidcRaceArbiter<T>(\n operation: () => Promise<T>,\n): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (\n (error instanceof ValidationError &&\n error.code === 'VALIDATION_UNIQUE_CONSTRAINT') ||\n isOidcAbortedTransactionError(error)\n ) {\n throw new OidcRaceArbiterConflict(error);\n }\n throw error;\n }\n}\n\nasync function withProvisioningLock<T>(\n db: DatabaseInterface,\n lockKey: string,\n callback: () => Promise<T>,\n): Promise<T> {\n // Non-Postgres handles for the same URL share a local lock. Durable unique\n // keys remain the cross-process and PostgreSQL race arbiters.\n const databaseKey = getDatabaseLockKey(db);\n const locks = provisioningLocks.get(databaseKey) ?? new Map();\n provisioningLocks.set(databaseKey, locks);\n const previous = locks.get(lockKey) ?? Promise.resolve();\n let release!: () => void;\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const queued = previous.then(() => current);\n locks.set(lockKey, queued);\n await previous;\n try {\n return await callback();\n } finally {\n release();\n if (locks.get(lockKey) === queued) locks.delete(lockKey);\n if (locks.size === 0) provisioningLocks.delete(databaseKey);\n }\n}\n\nfunction getDatabaseLockKey(db: DatabaseInterface): string | object {\n const url = db.url;\n if (\n isPostgresDatabase(db) &&\n typeof db.beginTransaction !== 'function' &&\n db.client !== null &&\n (typeof db.client === 'object' || typeof db.client === 'function')\n ) {\n return db.client;\n }\n return url && !isPostgresDatabase(db) ? url : (db as object);\n}\n\nfunction isPostgresDatabase(db: DatabaseInterface): boolean {\n return /^postgres(?:ql)?:/iu.test(db.url ?? '');\n}\n\nasync function retryProvisioning<T>(\n options: OidcProvisioningCoordinatorOptions<T>,\n callback: () => Promise<T>,\n): Promise<T> {\n let serializeSqliteRetry = false;\n for (let attempt = 0; attempt < 8; attempt += 1) {\n try {\n return await (serializeSqliteRetry\n ? withSqliteRetryLock(options.db, callback)\n : callback());\n } catch (error) {\n if (error instanceof BackfillTableUnavailableError) {\n if (typeof options.db.beginTransaction === 'function' && attempt < 7) {\n BackfillTracker.invalidateInitialization(options.db);\n await new BackfillTracker({ db: options.db }).initialize();\n continue;\n }\n throw options.createTransactionError(\n 'Transaction-bound OIDC provisioning requires an existing _smrt_backfills table; pass the root database so SMRT can initialize it safely.',\n error,\n );\n }\n const sqliteBusy = isSqliteBusyError(error);\n const postgresTransactionRetry = isPostgresTransactionRetryError(error);\n const raceConflict = options.isRaceConflict(error);\n if (!sqliteBusy && !postgresTransactionRetry && !raceConflict)\n throw error;\n if (sqliteBusy && attempt < 7) {\n serializeSqliteRetry = true;\n await waitForRetry(Math.min(100 * 2 ** attempt, 1_000));\n continue;\n }\n if (postgresTransactionRetry && attempt < 7) {\n await waitForRetry(Math.min(25 * 2 ** attempt, 500));\n continue;\n }\n if (raceConflict && attempt === 0) continue;\n throw options.createConcurrencyError(error);\n }\n }\n throw options.createConcurrencyError();\n}\n\nfunction isPostgresTransactionRetryError(error: unknown): boolean {\n const pending: unknown[] = [error];\n const seen = new Set<unknown>();\n const messagePattern =\n /deadlock detected|could not serialize access|serialization failure/iu;\n while (pending.length > 0) {\n const current = pending.pop();\n if (!current || typeof current !== 'object' || seen.has(current)) continue;\n seen.add(current);\n const details = current as {\n cause?: unknown;\n code?: unknown;\n context?: unknown;\n originalError?: unknown;\n };\n if (details.code === '40P01' || details.code === '40001') return true;\n if (current instanceof Error && messagePattern.test(current.message)) {\n return true;\n }\n pending.push(details.cause, details.context, details.originalError);\n if (current instanceof AggregateError) pending.push(...current.errors);\n }\n return false;\n}\n\nasync function withSqliteRetryLock<T>(\n db: DatabaseInterface,\n callback: () => Promise<T>,\n): Promise<T> {\n const key = db.url || (db as object);\n const previous = sqliteRetryLocks.get(key) ?? Promise.resolve();\n let release!: () => void;\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const queued = previous.then(() => current);\n sqliteRetryLocks.set(key, queued);\n await previous;\n try {\n return await callback();\n } finally {\n release();\n if (sqliteRetryLocks.get(key) === queued) sqliteRetryLocks.delete(key);\n }\n}\n\nfunction isSqliteBusyError(error: unknown): boolean {\n const pending: unknown[] = [error];\n const seen = new Set<unknown>();\n const busyPattern =\n /SQLITE_(?:BUSY|LOCKED)|database(?: table)? is locked|cannot (?:commit transaction|release savepoint) - SQL statements in progress/iu;\n while (pending.length > 0) {\n const current = pending.pop();\n if (typeof current === 'string') {\n if (busyPattern.test(current)) return true;\n continue;\n }\n if (!current || typeof current !== 'object' || seen.has(current)) continue;\n seen.add(current);\n if (current instanceof Error) {\n if (busyPattern.test(current.message)) return true;\n pending.push((current as { cause?: unknown }).cause);\n if (current instanceof AggregateError) pending.push(...current.errors);\n }\n const details = current as {\n context?: unknown;\n details?: unknown;\n originalError?: unknown;\n };\n pending.push(details.context, details.details, details.originalError);\n }\n return false;\n}\n\nasync function waitForRetry(milliseconds: number): Promise<void> {\n await new Promise<void>((resolve) => setTimeout(resolve, milliseconds));\n}\n\nasync function withProvisioningTransaction<T>(\n db: DatabaseInterface,\n callback: (tx: DatabaseInterface) => Promise<T>,\n createTransactionError: (message: string, cause?: unknown) => Error,\n): Promise<T> {\n if (typeof db.beginTransaction === 'function') {\n if (db.transaction) return db.transaction((tx) => callback(tx));\n const tx = await db.beginTransaction();\n try {\n const result = await callback(tx);\n await tx.commit();\n return result;\n } catch (error) {\n try {\n if (tx.isActive()) await tx.rollback();\n } catch (rollbackError) {\n throw createTransactionError(\n 'OIDC provisioning could not roll back its root transaction.',\n new AggregateError([error, rollbackError]),\n );\n }\n throw error;\n }\n }\n\n try {\n return await withSavepoint(db, () => callback(db), createTransactionError);\n } catch (error) {\n if (!(error instanceof SavepointUnavailableError)) throw error;\n throw createTransactionError(\n 'OIDC provisioning requires a root database or a transaction handle that supports savepoints.',\n error.cause,\n );\n }\n}\n\nasync function withSavepoint<T>(\n db: DatabaseInterface,\n callback: () => Promise<T>,\n createTransactionError: (message: string, cause?: unknown) => Error,\n): Promise<T> {\n savepointSequence = (savepointSequence + 1) % Number.MAX_SAFE_INTEGER;\n const savepoint = `smrt_oidc_${savepointSequence.toString(36)}`;\n try {\n await db.query(`SAVEPOINT ${savepoint}`);\n } catch (error) {\n throw new SavepointUnavailableError(error);\n }\n\n try {\n const result = await callback();\n await db.query(`RELEASE SAVEPOINT ${savepoint}`);\n return result;\n } catch (error) {\n try {\n await db.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);\n await db.query(`RELEASE SAVEPOINT ${savepoint}`);\n } catch (rollbackError) {\n throw createTransactionError(\n 'OIDC provisioning could not roll back its transaction savepoint.',\n new AggregateError([error, rollbackError]),\n );\n }\n throw error;\n }\n}\n","import { BackfillTracker } from '@happyvertical/smrt-core/migrations';\nimport type { getDatabase } from '@happyvertical/sql';\nimport { normalizeIdentityEmail } from '../auth/normalizeIdentityEmail.js';\n\ntype DatabaseInterface = Awaited<ReturnType<typeof getDatabase>>;\n\nexport const PROFILE_EMAIL_KEY_BACKFILL_NAME =\n '@happyvertical/smrt-profiles:profile-email-keys:v1';\n\nexport interface BackfillProfileEmailKeysResult {\n updated: number;\n}\n\n/**\n * Populate adapter-independent Profile email keys after the schema migration.\n *\n * Run this from one deploy process after legacy writers are stopped or\n * upgraded. Every read and update runs in one transaction, and repeats are a\n * no-op.\n */\nexport async function backfillProfileEmailKeys(\n db: DatabaseInterface,\n): Promise<BackfillProfileEmailKeysResult> {\n if (!db.transaction) {\n throw new Error(\n 'Profile email-key backfill requires a root database with transaction().',\n );\n }\n\n const tracker = new BackfillTracker({ db });\n await tracker.initialize();\n\n return db.transaction(async (tx) => {\n BackfillTracker.inheritInitialization(tx, db);\n const result = await tx.query(\n 'SELECT id, email, email_key FROM profiles ORDER BY id',\n );\n const updates: Array<{ emailKey: string | null; id: string }> = [];\n\n for (const row of result.rows) {\n const id = typeof row.id === 'string' ? row.id : '';\n if (!id) {\n throw new Error(\n 'Profile email-key backfill found a row without an id.',\n );\n }\n const email = typeof row.email === 'string' ? row.email : '';\n const emailKey = email.trim() ? normalizeIdentityEmail(email) : null;\n const storedKey =\n typeof row.email_key === 'string' ? row.email_key : null;\n if (storedKey !== emailKey) updates.push({ emailKey, id });\n }\n\n for (const update of updates) {\n await tx.query(\n 'UPDATE profiles SET email_key = ? WHERE id = ?',\n update.emailKey,\n update.id,\n );\n }\n\n await new BackfillTracker({ db: tx }).recordApplied(\n PROFILE_EMAIL_KEY_BACKFILL_NAME,\n {\n description: 'Canonical Profile identity email keys are current.',\n packageName: '@happyvertical/smrt-profiles',\n },\n );\n\n return { updated: updates.length };\n });\n}\n","import {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\n\nexport interface OidcProfileEmailReservationOptions extends SmrtObjectOptions {\n emailKey?: string;\n profileId?: string;\n}\n\n/** Private database arbiter for canonical OIDC email provisioning races. */\n@smrt({\n tableName: 'oidc_profile_email_reservations',\n api: false,\n mcp: false,\n cli: false,\n})\nexport class OidcProfileEmailReservation extends SmrtObject {\n @foreignKey('Profile', { required: true, unique: true })\n profileId?: string;\n\n @field({ type: 'text', required: true, unique: true, readonly: true })\n emailKey: string = '';\n\n constructor(options: OidcProfileEmailReservationOptions = {}) {\n super(options);\n if (options.profileId !== undefined) this.profileId = options.profileId;\n if (options.emailKey !== undefined) this.emailKey = options.emailKey;\n }\n}\n","/**\n * ProfileType model - Lookup table defining profile types\n *\n * Represents the nature of a profile (e.g., 'human', 'org', 'robot').\n * Uses UUID primary key with unique slug for human-readable lookups.\n */\n\nimport {\n field,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\nexport interface ProfileTypeOptions extends SmrtObjectOptions {\n slug?: string;\n name?: string;\n description?: string;\n tenantId?: string | null;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class ProfileType extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n // id: UUID (auto-generated by SmrtObject)\n // slug is inherited from SmrtObject (auto-generated from name)\n @field({ required: true })\n name: string = '';\n\n description?: string;\n\n constructor(options: ProfileTypeOptions = {}) {\n super(options);\n if (options.name) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n }\n\n /**\n * Convenience method for slug-based lookup\n *\n * @param slug - The slug to search for\n * @returns ProfileType instance or null if not found\n */\n static async getBySlug(_slug: string): Promise<ProfileType | null> {\n // Will be auto-implemented by SMRT\n return null;\n }\n}\n","/**\n * Profile model - Core entity representing any profile type\n *\n * Central table holding all primary entities with type, email, name, and description.\n * Uses UUID primary key with relationships to ProfileType for classification.\n */\n\nimport type { Asset } from '@happyvertical/smrt-assets';\nimport {\n assertValidOwnedAssetRelationship,\n assertValidOwnedAssetSortOrder,\n resolveOwnedAssetsById,\n} from '@happyvertical/smrt-assets';\n\nimport {\n field,\n foreignKey,\n oneToMany,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { resolvePrompt } from '@happyvertical/smrt-prompts';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { normalizeIdentityEmail } from '../auth/normalizeIdentityEmail';\nimport {\n promptMessageOptions,\n smrtProfilesGenerateBioPrompt,\n} from '../prompts';\nimport type { ApiKey, GenerateKeyResult } from './ApiKey';\nimport type { AuditLog } from './AuditLog';\nimport type { NostrIdentity } from './NostrIdentity';\nimport type { OidcIdentity } from './OidcIdentity';\nimport type { ProfileMetadata } from './ProfileMetadata';\nimport type { ProfileRelationship } from './ProfileRelationship';\nimport { ProfileType } from './ProfileType';\n\nexport interface ProfileOptions extends SmrtObjectOptions {\n typeId?: string;\n email?: string;\n name?: string;\n description?: string;\n tenantId?: string | null;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update'] },\n cli: true,\n})\nexport class Profile extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n // id: UUID (auto-generated by SmrtObject)\n @foreignKey('ProfileType', { required: true })\n typeId?: string; // References ProfileType.id\n\n @field({ unique: true })\n email?: string; // Optional email address\n\n /** Adapter-independent identity lookup key derived from email on save. */\n @field({ type: 'text', nullable: true, indexed: true, readonly: true })\n emailKey: string | null = null;\n\n @field({ required: true })\n name: string = ''; // Display name\n\n description?: string; // Short bio or description\n\n // Relationships (not stored as columns)\n @oneToMany('ProfileMetadata')\n metadata: ProfileMetadata[] = [];\n\n // ProfileRelationship declares multiple foreign keys back to Profile\n // (fromProfileId / toProfileId / contextProfileId), so each oneToMany names\n // its inverse side explicitly. This both disambiguates `loadRelatedMany`\n // and gives the R10-generated `getRelationshipsFrom()` / `getRelationshipsTo()`\n // accessors the correct inverse foreign key.\n @oneToMany('ProfileRelationship', { foreignKey: 'fromProfileId' })\n relationshipsFrom: ProfileRelationship[] = [];\n\n @oneToMany('ProfileRelationship', { foreignKey: 'toProfileId' })\n relationshipsTo: ProfileRelationship[] = [];\n\n constructor(options: ProfileOptions = {}) {\n super(options);\n if (options.typeId !== undefined) this.typeId = options.typeId;\n if (options.email !== undefined) this.email = options.email;\n if (options.name) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n }\n\n /** Keep the durable identity key derived from the public email field. */\n override async save(): Promise<this> {\n this.emailKey = this.email?.trim()\n ? normalizeIdentityEmail(this.email)\n : null;\n return super.save();\n }\n\n /**\n * Get the profile type slug for this profile\n *\n * @returns The slug of the profile type\n */\n async getTypeSlug(): Promise<string> {\n const type = await this.loadRelated('typeId');\n return type?.slug || '';\n }\n\n /**\n * Set the profile type by slug\n *\n * @param slug - The slug of the profile type\n * @throws Error if profile type not found\n */\n async setTypeBySlug(slug: string): Promise<void> {\n const type = await ProfileType.getBySlug(slug);\n if (!type) throw new Error(`Profile type '${slug}' not found`);\n this.typeId = type.id as string;\n }\n\n /**\n * Add metadata to this profile\n *\n * @param metafieldSlug - The slug of the metafield\n * @param value - The value to set\n */\n async addMetadata(metafieldSlug: string, value: unknown): Promise<void> {\n const { ProfileMetafieldCollection } = await import(\n '../collections/ProfileMetafieldCollection'\n );\n const { ProfileMetadataCollection } = await import(\n '../collections/ProfileMetadataCollection'\n );\n\n // Get or create metafield collection\n const metafieldCollection = await ProfileMetafieldCollection.create(\n this.options,\n );\n\n // Find the metafield by slug\n const metafield = await metafieldCollection.getBySlug(metafieldSlug);\n if (!metafield) {\n throw new Error(`Metafield '${metafieldSlug}' not found`);\n }\n\n // Validate the value\n await metafield.validateValue(value);\n\n // Get or create metadata collection\n const metadataCollection = await ProfileMetadataCollection.create(\n this.options,\n );\n\n // Check if metadata already exists\n const existing = await metadataCollection.list({\n where: { profileId: this.id, metafieldId: metafield.id },\n limit: 1,\n });\n\n if (existing.length > 0) {\n // Update existing\n const metadata = existing[0];\n metadata.value = String(value);\n await metadata.save();\n } else {\n // Create new\n if (!this.id || !metafield.id) {\n throw new Error(\n 'Profile.addMetadata requires a persisted profile and metafield (missing id)',\n );\n }\n const metadata = await metadataCollection.create({\n profileId: this.id,\n metafieldId: metafield.id,\n value: String(value),\n });\n await metadata.save();\n }\n }\n\n /**\n * Get all metadata for this profile as key-value object\n *\n * @returns Object with metafield slugs as keys\n */\n async getMetadata(): Promise<Record<string, string>> {\n const { ProfileMetadataCollection } = await import(\n '../collections/ProfileMetadataCollection'\n );\n\n const metadataCollection = await ProfileMetadataCollection.create(\n this.options,\n );\n\n return await metadataCollection.getMetadataObject(this.id as string);\n }\n\n /**\n * Update multiple metadata values\n *\n * @param metadata - Object with metafield slugs as keys and values\n */\n async updateMetadata(metadata: Record<string, unknown>): Promise<void> {\n for (const [metafieldSlug, value] of Object.entries(metadata)) {\n await this.addMetadata(metafieldSlug, value);\n }\n }\n\n /**\n * Remove metadata by metafield slug\n *\n * @param metafieldSlug - The slug of the metafield to remove\n */\n async removeMetadata(metafieldSlug: string): Promise<void> {\n const { ProfileMetafieldCollection } = await import(\n '../collections/ProfileMetafieldCollection'\n );\n const { ProfileMetadataCollection } = await import(\n '../collections/ProfileMetadataCollection'\n );\n\n const metafieldCollection = await ProfileMetafieldCollection.create(\n this.options,\n );\n\n const metafield = await metafieldCollection.getBySlug(metafieldSlug);\n if (!metafield) {\n throw new Error(`Metafield '${metafieldSlug}' not found`);\n }\n\n const metadataCollection = await ProfileMetadataCollection.create(\n this.options,\n );\n\n const existing = await metadataCollection.list({\n where: { profileId: this.id, metafieldId: metafield.id },\n limit: 1,\n });\n\n if (existing.length > 0) {\n await existing[0].delete();\n }\n }\n\n private async getProfileAssetCollection() {\n const { ProfileAssetCollection } = await import(\n '../collections/ProfileAssetCollection'\n );\n return ProfileAssetCollection.create({ db: this.db });\n }\n async getAssets(relationship?: string): Promise<Asset[]> {\n if (!this.id) {\n return [];\n }\n\n const profileAssets = await this.getProfileAssetCollection();\n const linkedAssets = await profileAssets.byLeft(\n this.id,\n relationship ? { relationship } : {},\n );\n\n return resolveOwnedAssetsById(\n this.db,\n linkedAssets.map((link) => link.assetId),\n this.tenantId,\n );\n }\n\n async addAsset(\n asset: Asset,\n relationship = 'attachment',\n sortOrder = 0,\n ): Promise<void> {\n if (!this.id || !asset.id) {\n throw new Error('Cannot associate unsaved profile or asset');\n }\n\n assertValidOwnedAssetRelationship(relationship);\n assertValidOwnedAssetSortOrder(sortOrder);\n\n const profileAssets = await this.getProfileAssetCollection();\n await profileAssets.attach(this.id, asset.id, {\n relationship,\n sortOrder,\n tenantId: this.tenantId,\n });\n }\n\n async removeAsset(assetId: string, relationship?: string): Promise<void> {\n if (!this.id) {\n return;\n }\n\n const profileAssets = await this.getProfileAssetCollection();\n await profileAssets.detach(\n this.id,\n assetId,\n relationship ? { relationship } : {},\n );\n }\n\n /**\n * Add a relationship to another profile\n *\n * @param toProfile - The target profile\n * @param relationshipSlug - The type of relationship\n * @param contextProfile - Optional context profile for tertiary relationships\n */\n async addRelationship(\n toProfile: Profile,\n relationshipSlug: string,\n contextProfile?: Profile,\n ): Promise<void> {\n const { ProfileRelationshipTypeCollection } = await import(\n '../collections/ProfileRelationshipTypeCollection'\n );\n const { ProfileRelationshipCollection } = await import(\n '../collections/ProfileRelationshipCollection'\n );\n const { ProfileRelationshipType } = await import(\n './ProfileRelationshipType'\n );\n\n // Get relationship type\n const relationshipTypeCollection =\n await ProfileRelationshipTypeCollection.create(this.options);\n\n const relationshipType =\n await relationshipTypeCollection.getBySlug(relationshipSlug);\n if (!relationshipType) {\n throw new Error(`Relationship type '${relationshipSlug}' not found`);\n }\n\n // Check if relationship already exists\n const relationshipCollection = await ProfileRelationshipCollection.create(\n this.options,\n );\n\n const exists = await relationshipCollection.exists(\n this.id as string,\n toProfile.id as string,\n relationshipType.id as string,\n );\n\n if (!exists) {\n // Create the relationship\n const relationship = await relationshipCollection.create({\n fromProfileId: this.id ?? undefined,\n toProfileId: toProfile.id ?? undefined,\n typeId: relationshipType.id ?? undefined,\n contextProfileId: contextProfile?.id ?? undefined,\n });\n await relationship.save();\n\n // Handle reciprocal relationships. A reciprocal handler creates the\n // inverse edge by calling addRelationship() back on `toProfile`. Gating\n // this on `!exists` is what terminates the recursion: the inverse call\n // creates its own edge and re-enters, but by then the original edge\n // already exists, so the handler is skipped and the cycle stops. Firing\n // it unconditionally instead recurses forever (the bundled friend /\n // spouse / partner / colleague / sibling handlers all call back here).\n if (relationshipType.reciprocal) {\n const handler =\n ProfileRelationshipType.getReciprocalHandler(relationshipSlug);\n if (handler) {\n await handler(this, toProfile, contextProfile);\n }\n }\n }\n }\n\n /**\n * Get all relationships for this profile\n *\n * @param options - Filter options (typeSlug, direction)\n * @returns Array of ProfileRelationship instances\n */\n async getRelationships(options?: {\n typeSlug?: string;\n direction?: 'from' | 'to' | 'all';\n }): Promise<ProfileRelationship[]> {\n const { ProfileRelationshipCollection } = await import(\n '../collections/ProfileRelationshipCollection'\n );\n const { ProfileRelationshipTypeCollection } = await import(\n '../collections/ProfileRelationshipTypeCollection'\n );\n\n const relationshipCollection = await ProfileRelationshipCollection.create(\n this.options,\n );\n\n const direction = options?.direction || 'all';\n\n // Get type ID if typeSlug is provided\n let typeId: string | undefined;\n if (options?.typeSlug) {\n const relationshipTypeCollection =\n await ProfileRelationshipTypeCollection.create(this.options);\n\n const relationshipType = await relationshipTypeCollection.getBySlug(\n options.typeSlug,\n );\n typeId = relationshipType?.id ?? undefined;\n }\n\n // Fetch relationships based on direction\n if (direction === 'from') {\n return await relationshipCollection.getFromProfile(\n this.id as string,\n typeId,\n );\n } else if (direction === 'to') {\n return await relationshipCollection.getToProfile(\n this.id as string,\n typeId,\n );\n } else {\n return await relationshipCollection.getForProfile(\n this.id as string,\n typeId,\n );\n }\n }\n\n /**\n * Get related profiles\n *\n * @param relationshipSlug - Optional filter by relationship type slug\n * @returns Array of related Profile instances\n */\n async getRelatedProfiles(relationshipSlug?: string): Promise<Profile[]> {\n const { ProfileCollection } = await import(\n '../collections/ProfileCollection'\n );\n\n const relationships = await this.getRelationships({\n typeSlug: relationshipSlug,\n direction: 'all',\n });\n\n const profileCollection = await ProfileCollection.create(this.options);\n\n const relatedProfiles: Profile[] = [];\n const seenIds = new Set<string>();\n\n for (const relationship of relationships) {\n // Get the other profile (not this one)\n // Convert Field instances to strings for comparison\n const fromId = String(relationship.fromProfileId);\n const toId = String(relationship.toProfileId);\n const thisId = String(this.id);\n const otherId = fromId === thisId ? toId : fromId;\n\n if (!seenIds.has(otherId)) {\n seenIds.add(otherId);\n const profile = await profileCollection.get({ id: otherId });\n if (profile) {\n relatedProfiles.push(profile);\n }\n }\n }\n\n return relatedProfiles;\n }\n\n /**\n * Remove a relationship to another profile\n *\n * @param toProfile - The target profile\n * @param relationshipSlug - The type of relationship to remove\n */\n async removeRelationship(\n toProfile: Profile,\n relationshipSlug: string,\n ): Promise<void> {\n const { ProfileRelationshipTypeCollection } = await import(\n '../collections/ProfileRelationshipTypeCollection'\n );\n const { ProfileRelationshipCollection } = await import(\n '../collections/ProfileRelationshipCollection'\n );\n\n // Get relationship type\n const relationshipTypeCollection =\n await ProfileRelationshipTypeCollection.create(this.options);\n\n const relationshipType =\n await relationshipTypeCollection.getBySlug(relationshipSlug);\n if (!relationshipType) {\n throw new Error(`Relationship type '${relationshipSlug}' not found`);\n }\n\n // Find and delete the relationship\n const relationshipCollection = await ProfileRelationshipCollection.create(\n this.options,\n );\n\n const relationships = await relationshipCollection.list({\n where: {\n fromProfileId: this.id,\n toProfileId: toProfile.id,\n typeId: relationshipType.id,\n },\n });\n\n for (const relationship of relationships) {\n await relationship.delete();\n }\n\n // Handle reciprocal relationships - delete the inverse\n if (relationshipType.reciprocal) {\n const inverseRelationships = await relationshipCollection.list({\n where: {\n fromProfileId: toProfile.id,\n toProfileId: this.id,\n typeId: relationshipType.id,\n },\n });\n\n for (const relationship of inverseRelationships) {\n await relationship.delete();\n }\n }\n }\n\n /**\n * AI-powered: Generate a professional bio for this profile\n *\n * Uses the `smrtProfiles.profile.generateBio` prompt registered via\n * `@happyvertical/smrt-prompts`, allowing tenant- or instance-level\n * overrides of the template, model, and parameters at runtime.\n *\n * @returns Generated bio text\n */\n async generateBio(): Promise<string> {\n // Resolve `db` from either the canonical `db` option or its `persistence`\n // alias. SmrtClass maps `persistence → db` lazily during `initialize()`,\n // so on a freshly-constructed Profile that has not yet been initialized,\n // `this.options.db` may be undefined while `this.options.persistence` is\n // set. Falling back here ensures stored app- and tenant-level prompt\n // overrides in `_smrt_prompt_overrides` are honored on the first call —\n // before `getAiClient()` triggers full initialization further below.\n const db = this.options.db ?? this.options.persistence;\n\n const resolvedPrompt = await resolvePrompt(\n smrtProfilesGenerateBioPrompt.key,\n {\n db,\n tenantId: this.tenantId,\n variables: {\n profileName: this.name || '',\n profileDescription: this.description || '',\n },\n },\n );\n\n const ai = await this.getAiClient();\n const response = await ai.message(\n resolvedPrompt.text,\n promptMessageOptions(resolvedPrompt.ai),\n );\n\n return response.trim();\n }\n\n /**\n * AI-powered: Check if profile matches criteria\n *\n * @param criteria - Criteria to match against\n * @returns True if matches criteria\n */\n async matches(criteria: string): Promise<boolean> {\n return await this.is(criteria);\n }\n\n /**\n * Find profiles by metadata key-value pair\n *\n * @param metafieldSlug - The metafield slug to search\n * @param value - The value to match\n * @returns Array of matching profiles\n */\n static async findByMetadata(\n _metafieldSlug: string,\n _value: unknown,\n ): Promise<Profile[]> {\n // Will be auto-implemented by SMRT\n return [];\n }\n\n /**\n * Find profiles by type slug\n *\n * @param typeSlug - The profile type slug\n * @returns Array of matching profiles\n */\n static async findByType(_typeSlug: string): Promise<Profile[]> {\n // Will be auto-implemented by SMRT\n return [];\n }\n\n /**\n * Find related profiles for a given profile\n *\n * @param profileId - The profile UUID\n * @param relationshipSlug - Optional filter by relationship type\n * @returns Array of related profiles\n */\n static async findRelated(\n _profileId: string,\n _relationshipSlug?: string,\n ): Promise<Profile[]> {\n // Will be auto-implemented by SMRT\n return [];\n }\n\n /**\n * Search profiles by email\n *\n * @param email - The email to search for\n * @returns Profile or null if not found\n */\n static async searchByEmail(_email: string): Promise<Profile | null> {\n // Will be auto-implemented by SMRT\n return null;\n }\n\n // ========================\n // Auth-related methods\n // ========================\n\n /**\n * Get all API keys for this profile\n *\n * @returns Array of API keys\n */\n async getApiKeys(): Promise<ApiKey[]> {\n const { ApiKeyCollection } = await import(\n '../collections/ApiKeyCollection'\n );\n const collection = await ApiKeyCollection.create(this.options);\n return await collection.findByProfile(this.id as string);\n }\n\n /**\n * Get active (non-revoked, non-expired) API keys for this profile\n *\n * @returns Array of active API keys\n */\n async getActiveApiKeys(): Promise<ApiKey[]> {\n const { ApiKeyCollection } = await import(\n '../collections/ApiKeyCollection'\n );\n const collection = await ApiKeyCollection.create(this.options);\n return await collection.findActiveByProfile(this.id as string);\n }\n\n /**\n * Generate a new API key for this profile\n *\n * @param options - Key options (name, scopes, expiration)\n * @returns The generated key (plaintext) and ApiKey record\n */\n async generateApiKey(options: {\n name: string;\n scopes?: string[];\n expiresAt?: Date | null;\n }): Promise<GenerateKeyResult> {\n const { ApiKey } = await import('./ApiKey');\n return await ApiKey.generate(this, {\n ...options,\n db: this.options?.db,\n });\n }\n\n /**\n * Get all OIDC identities linked to this profile\n *\n * @returns Array of OIDC identity records\n */\n async getOidcIdentities(): Promise<OidcIdentity[]> {\n const { OidcIdentityCollection } = await import(\n '../collections/OidcIdentityCollection'\n );\n const collection = await OidcIdentityCollection.create(this.options);\n return await collection.findByProfile(this.id as string);\n }\n\n /**\n * Reuse an existing exact OIDC identity for this unchanged Profile.\n *\n * @param oidcData - OIDC provider data\n * @returns The linked OIDC identity record\n * @deprecated New authentication links require owner-aware transactional\n * provisioning. This compatibility helper may refresh a legacy Profile\n * type, but refuses to create or rebind authority.\n */\n async linkOidcIdentity(oidcData: {\n provider: string;\n issuer: string;\n subject: string;\n email?: string;\n }): Promise<OidcIdentity> {\n const { OidcIdentityCollection } = await import(\n '../collections/OidcIdentityCollection'\n );\n const collection = await OidcIdentityCollection.create(this.options);\n return await collection.linkToProfile(this, oidcData);\n }\n\n /**\n * Get all Nostr identities linked to this profile\n *\n * @returns Array of Nostr identity records\n */\n async getNostrIdentities(): Promise<NostrIdentity[]> {\n const { NostrIdentityCollection } = await import(\n '../collections/NostrIdentityCollection'\n );\n const collection = await NostrIdentityCollection.create(this.options);\n return await collection.findByProfile(this.id as string);\n }\n\n /**\n * Link a new Nostr identity to this profile\n *\n * @param nostrData - Nostr identity data (encrypted keypair)\n * @returns The linked Nostr identity record\n */\n async linkNostrIdentity(nostrData: {\n pubkey: string;\n encryptedPrivkey: string;\n encryptionIv: string;\n encryptionTag: string;\n email: string;\n nip05Username?: string;\n }): Promise<NostrIdentity> {\n const { NostrIdentityCollection } = await import(\n '../collections/NostrIdentityCollection'\n );\n const collection = await NostrIdentityCollection.create(this.options);\n return await collection.linkToProfile(this, nostrData);\n }\n\n /**\n * Get audit logs for actions performed by this profile\n *\n * @param limit - Maximum number of logs to return\n * @returns Array of audit log entries\n */\n async getAuditLogs(limit: number = 50): Promise<AuditLog[]> {\n const { AuditLogCollection } = await import(\n '../collections/AuditLogCollection'\n );\n const collection = await AuditLogCollection.create(this.options);\n return await collection.getRecentActivity(this.id as string, limit);\n }\n\n /**\n * Record an audit log entry for an action by this profile\n *\n * @param options - Audit log options\n * @returns The created audit log entry\n */\n async recordAction(options: {\n action: string;\n resourceType: string;\n resourceId: string;\n source?: 'web' | 'cli' | 'ci' | 'webhook' | 'mcp';\n metadata?: Record<string, unknown>;\n onBehalfOf?: Profile | null;\n }): Promise<AuditLog> {\n const { AuditLogCollection } = await import(\n '../collections/AuditLogCollection'\n );\n const collection = await AuditLogCollection.create(this.options);\n return await collection.record({\n profile: this,\n ...options,\n });\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { OidcProfileEmailReservation } from '../models/OidcProfileEmailReservation';\n\n/** Internal collection for canonical OIDC email reservations. */\nexport class OidcProfileEmailReservationCollection extends SmrtCollection<OidcProfileEmailReservation> {\n static readonly _itemClass = OidcProfileEmailReservation;\n}\n","/**\n * ProfileCollection - Collection manager for Profile objects\n *\n * Provides advanced querying and batch operations for Profile entities.\n */\n\nimport type { Asset } from '@happyvertical/smrt-assets';\nimport {\n addOwnedAssetFromCollection,\n getOwnedAssetsFromCollection,\n removeOwnedAssetFromCollection,\n} from '@happyvertical/smrt-assets';\nimport { SmrtCollection, ValidationError } from '@happyvertical/smrt-core';\nimport { BackfillTracker } from '@happyvertical/smrt-core/migrations';\nimport {\n queryGlobal,\n queryWithGlobals,\n withSystemContext,\n} from '@happyvertical/smrt-tenancy';\nimport type { getDatabase } from '@happyvertical/sql';\nimport { normalizeIdentityEmail } from '../auth/normalizeIdentityEmail';\nimport { isOidcAbortedTransactionError } from '../auth/oidcProvisioningCoordinator';\nimport { PROFILE_EMAIL_KEY_BACKFILL_NAME } from '../migrations/backfillProfileEmailKeys.js';\nimport { OidcProfileEmailReservation } from '../models/OidcProfileEmailReservation';\nimport { Profile } from '../models/Profile';\nimport { OidcProfileEmailReservationCollection } from './OidcProfileEmailReservationCollection';\n\nconst PERSON_META_TYPES = new Set([\n '@happyvertical/smrt-profiles:Person',\n 'Person',\n]);\n\ntype DatabaseInterface = Awaited<ReturnType<typeof getDatabase>>;\n\nexport type CanonicalPersonProfileErrorCode =\n | 'ambiguous_email'\n | 'email_key_backfill_required'\n | 'email_mismatch'\n | 'missing_profile'\n | 'non_person'\n | 'reservation_conflict'\n | 'tenant_scoped';\n\n/** A Profile cannot safely represent one global human identity. */\nexport class CanonicalPersonProfileError extends Error {\n constructor(\n readonly code: CanonicalPersonProfileErrorCode,\n message: string,\n ) {\n super(message);\n this.name = 'CanonicalPersonProfileError';\n }\n}\n\ninterface CanonicalProfileRow extends Record<string, unknown> {\n _meta_type?: unknown;\n email?: unknown;\n email_key?: unknown;\n id?: unknown;\n tenant_id?: unknown;\n}\n\nexport class ProfileCollection extends SmrtCollection<Profile> {\n static readonly _itemClass = Profile;\n private emailKeysReadyPromise: Promise<void> | null = null;\n\n /**\n * Find a profile by email address\n *\n * @param email - The email address to search for\n * @returns The matching profile or null\n */\n async findByEmail(email: string): Promise<Profile | null> {\n const normalizedEmail = normalizeIdentityEmail(email);\n const profiles = await this.list({\n where: { email: normalizedEmail },\n limit: 1,\n });\n return profiles[0] ?? null;\n }\n\n /**\n * Resolve one unambiguous global Person by normalized email.\n *\n * Unlike `findByEmail()`, this identity-boundary helper deliberately reads\n * across tenant scopes and fails closed when any matching row is\n * tenant-scoped, is not a Person STI row, or when more than one row matches\n * case-insensitively. It is intended for verified external identities.\n */\n async findUniqueGlobalPersonByEmail(email: string): Promise<Profile | null> {\n const normalizedEmail = normalizeIdentityEmail(email);\n const rows = await this.loadCanonicalEmailRows(normalizedEmail);\n\n if (rows.length === 0) return null;\n this.assertCanonicalRows(rows, normalizedEmail);\n return this.requireHydratedProfile(readRequiredString(rows[0], 'id'));\n }\n\n /**\n * Validate and hydrate a supplied canonical Profile.\n *\n * The Profile must be the sole case-insensitive match for its stored email.\n * When `email` is provided, that address must also match the stored email.\n */\n async requireCanonicalGlobalPerson(\n profileId: string,\n email?: string,\n ): Promise<Profile> {\n const db = this.requireDatabase();\n await this.ensureEmailKeysReady();\n const result = await withSystemContext(() =>\n db.query(\n `SELECT id, tenant_id, _meta_type, email, email_key\n FROM profiles\n WHERE id = ?\n LIMIT 1`,\n profileId,\n ),\n );\n const row = result.rows[0] as CanonicalProfileRow | undefined;\n\n if (!row) {\n throw new CanonicalPersonProfileError(\n 'missing_profile',\n `Profile ${profileId} does not exist.`,\n );\n }\n\n this.assertRowEmailKeyCurrent(row);\n this.assertCanonicalRows([row]);\n\n const storedEmail = readString(row, 'email');\n const normalizedEmail =\n email !== undefined\n ? normalizeIdentityEmail(email)\n : storedEmail\n ? normalizeIdentityEmail(storedEmail)\n : undefined;\n if (normalizedEmail !== undefined) {\n const matches = await this.loadCanonicalEmailRows(normalizedEmail);\n this.assertCanonicalRows(matches, normalizedEmail);\n if (\n matches.length !== 1 ||\n readRequiredString(matches[0], 'id') !== profileId\n ) {\n throw new CanonicalPersonProfileError(\n 'email_mismatch',\n `Profile ${profileId} is not the unique global Person for ${normalizedEmail}.`,\n );\n }\n }\n\n return this.requireHydratedProfile(profileId);\n }\n\n /**\n * Reserve the normalized email for a previously validated canonical Person.\n *\n * The unique stored key turns a concurrent OIDC first-login race into a\n * retryable database conflict. Legacy Profiles remain unaffected until an\n * identity boundary safely claims them.\n */\n async reserveCanonicalIdentityEmail(\n profileId: string,\n email?: string,\n ): Promise<Profile> {\n const profile = await this.requireCanonicalGlobalPerson(profileId, email);\n const normalizedEmail =\n email !== undefined\n ? normalizeIdentityEmail(email)\n : profile.email?.trim()\n ? normalizeIdentityEmail(profile.email)\n : null;\n const db = this.requireDatabase();\n const reservations = await OidcProfileEmailReservationCollection.create({\n db,\n });\n const byProfile = await reservations.list({\n where: { profileId },\n limit: 2,\n });\n if (byProfile.length > 1) {\n throw new CanonicalPersonProfileError(\n 'ambiguous_email',\n `Multiple canonical identity reservations exist for Profile ${profileId}.`,\n );\n }\n if (normalizedEmail === null) {\n await byProfile[0]?.delete();\n return profile;\n }\n\n const byEmail = await reservations.list({\n where: { emailKey: normalizedEmail },\n limit: 2,\n });\n const activeEmailReservations: typeof byEmail = [];\n for (const reservation of byEmail) {\n if (\n reservation.profileId !== profileId &&\n (await this.isStaleEmailReservation(reservation, normalizedEmail))\n ) {\n await reservation.delete();\n } else {\n activeEmailReservations.push(reservation);\n }\n }\n const conflictingEmail = activeEmailReservations.find(\n (reservation) => reservation.profileId !== profileId,\n );\n if (conflictingEmail) {\n throw new CanonicalPersonProfileError(\n 'reservation_conflict',\n `${normalizedEmail} is already reserved for a different canonical Person.`,\n );\n }\n if (activeEmailReservations.length > 1) {\n throw new CanonicalPersonProfileError(\n 'ambiguous_email',\n `Multiple canonical identity reservations exist for ${normalizedEmail}.`,\n );\n }\n\n const existing = byProfile[0];\n if (existing) {\n if (existing.emailKey !== normalizedEmail) {\n existing.emailKey = normalizedEmail;\n await saveIdentityEmailReservation(existing, normalizedEmail);\n }\n return profile;\n }\n if (!activeEmailReservations[0]) {\n const reservation = new OidcProfileEmailReservation({\n db,\n emailKey: normalizedEmail,\n profileId,\n });\n await reservation.initialize();\n await saveIdentityEmailReservation(reservation, normalizedEmail);\n }\n return profile;\n }\n\n /**\n * Find profiles by type slug\n *\n * @param typeSlug - The profile type slug to filter by\n * @returns Array of matching profiles\n */\n async findByType(typeSlug: string): Promise<Profile[]> {\n // Will use eager loading when available\n const allProfiles = await this.list({});\n\n const filtered: Profile[] = [];\n for (const profile of allProfiles) {\n const slug = await profile.getTypeSlug();\n if (slug === typeSlug) {\n filtered.push(profile);\n }\n }\n\n return filtered;\n }\n\n private requireDatabase(): DatabaseInterface {\n const db = this.options.db;\n if (!db || typeof db !== 'object' || !('query' in db)) {\n throw new Error(\n 'Canonical Profile identity resolution requires an initialized database.',\n );\n }\n return db as DatabaseInterface;\n }\n\n private async loadCanonicalEmailRows(\n normalizedEmail: string,\n ): Promise<CanonicalProfileRow[]> {\n const db = this.requireDatabase();\n await this.ensureEmailKeysReady();\n const result = await withSystemContext(async () => {\n return db.query(\n `SELECT id, tenant_id, _meta_type, email, email_key\n FROM profiles\n WHERE email_key = ?\n ORDER BY created_at ASC, id ASC\n LIMIT 2`,\n normalizedEmail,\n );\n });\n const rows = result.rows as CanonicalProfileRow[];\n for (const row of rows) this.assertRowEmailKeyCurrent(row, normalizedEmail);\n return rows;\n }\n\n /** Require the deploy-time backfill marker before indexed identity reads. */\n private async ensureEmailKeysReady(): Promise<void> {\n if (!this.emailKeysReadyPromise) {\n this.emailKeysReadyPromise = this.checkEmailKeysReady().catch((error) => {\n this.emailKeysReadyPromise = null;\n throw error;\n });\n }\n return this.emailKeysReadyPromise;\n }\n\n private async checkEmailKeysReady(): Promise<void> {\n const db = this.requireDatabase();\n if (\n await new BackfillTracker({ db }).isApplied(\n PROFILE_EMAIL_KEY_BACKFILL_NAME,\n )\n )\n return;\n throw new CanonicalPersonProfileError(\n 'email_key_backfill_required',\n 'Profile identity email keys are not marked ready; run backfillProfileEmailKeys() before identity lookup.',\n );\n }\n\n private async isStaleEmailReservation(\n reservation: OidcProfileEmailReservation,\n reservedEmail: string,\n ): Promise<boolean> {\n const profileId = reservation.profileId;\n if (!profileId) return true;\n const result = await this.requireDatabase().query(\n 'SELECT email FROM profiles WHERE id = ? LIMIT 1',\n profileId,\n );\n const row = result.rows[0] as CanonicalProfileRow | undefined;\n if (!row) return true;\n const email = readString(row, 'email');\n return !email?.trim() || normalizeIdentityEmail(email) !== reservedEmail;\n }\n\n private assertRowEmailKeyCurrent(\n row: CanonicalProfileRow,\n normalizedEmail?: string,\n ): void {\n const email = readString(row, 'email') ?? '';\n const expectedKey = email.trim() ? normalizeIdentityEmail(email) : null;\n const storedKey = readString(row, 'email_key') ?? null;\n if (\n storedKey !== expectedKey ||\n (normalizedEmail !== undefined && expectedKey !== normalizedEmail)\n ) {\n throw new CanonicalPersonProfileError(\n 'email_key_backfill_required',\n `Profile ${String(row.id ?? '<unknown>')} has a missing or stale identity email key; run backfillProfileEmailKeys() before identity lookup.`,\n );\n }\n }\n\n private assertCanonicalRows(\n rows: CanonicalProfileRow[],\n normalizedEmail?: string,\n ): void {\n const tenantScoped = rows.find((row) => readString(row, 'tenant_id'));\n if (tenantScoped) {\n throw new CanonicalPersonProfileError(\n 'tenant_scoped',\n `Profile ${readRequiredString(tenantScoped, 'id')} for ${normalizedEmail ?? 'the supplied identity'} is tenant-scoped.`,\n );\n }\n\n const nonPerson = rows.find(\n (row) => !PERSON_META_TYPES.has(readString(row, '_meta_type') ?? ''),\n );\n if (nonPerson) {\n throw new CanonicalPersonProfileError(\n 'non_person',\n `Profile ${readRequiredString(nonPerson, 'id')} for ${normalizedEmail ?? 'the supplied identity'} is not a Person.`,\n );\n }\n\n if (rows.length > 1) {\n throw new CanonicalPersonProfileError(\n 'ambiguous_email',\n `Multiple Profiles use ${normalizedEmail ?? 'the supplied identity email'}.`,\n );\n }\n }\n\n private async requireHydratedProfile(profileId: string): Promise<Profile> {\n const profile = await withSystemContext(() => this.get({ id: profileId }));\n if (!profile) {\n throw new CanonicalPersonProfileError(\n 'missing_profile',\n `Profile ${profileId} could not be hydrated.`,\n );\n }\n return profile;\n }\n\n /**\n * Batch get metadata for multiple profiles\n *\n * @param profileIds - Array of profile UUIDs\n * @returns Map of profile ID to metadata object\n */\n async batchGetMetadata(\n profileIds: string[],\n ): Promise<Map<string, Record<string, string>>> {\n const result = new Map<string, Record<string, string>>();\n\n for (const profileId of profileIds) {\n const profile = await this.get({ id: profileId });\n if (profile) {\n const metadata = await profile.getMetadata();\n result.set(profileId, metadata);\n }\n }\n\n return result;\n }\n\n /**\n * Batch update metadata for multiple profiles\n *\n * @param updates - Array of { profileId, data } objects\n */\n async batchUpdateMetadata(\n updates: Array<{ profileId: string; data: Record<string, unknown> }>,\n ): Promise<void> {\n for (const update of updates) {\n const profile = await this.get({ id: update.profileId });\n if (profile) {\n await profile.updateMetadata(update.data);\n }\n }\n }\n\n /**\n * Find related profiles for a given profile\n *\n * @param profileId - The profile UUID\n * @param relationshipSlug - Optional filter by relationship type\n * @returns Array of related profiles\n */\n async findRelated(\n profileId: string,\n relationshipSlug?: string,\n ): Promise<Profile[]> {\n const profile = await this.get({ id: profileId });\n if (!profile) return [];\n\n return await profile.getRelatedProfiles(relationshipSlug);\n }\n\n async getAssets(profileId: string, relationship?: string): Promise<Asset[]> {\n return getOwnedAssetsFromCollection(this, profileId, relationship);\n }\n\n async addAsset(\n profileId: string,\n asset: Asset,\n relationship = 'attachment',\n sortOrder = 0,\n ): Promise<void> {\n await addOwnedAssetFromCollection(\n this,\n 'Profile',\n profileId,\n asset,\n relationship,\n sortOrder,\n );\n }\n\n async removeAsset(\n profileId: string,\n assetId: string,\n relationship?: string,\n ): Promise<void> {\n await removeOwnedAssetFromCollection(\n this,\n 'Profile',\n profileId,\n assetId,\n relationship,\n );\n }\n\n /**\n * Get the relationship network for a profile up to a maximum depth\n *\n * @param profileId - The starting profile UUID\n * @param options - Configuration options\n * @returns Map of profile ID to depth level\n */\n async getRelationshipNetwork(\n profileId: string,\n options: { maxDepth?: number } = {},\n ): Promise<Map<string, number>> {\n const maxDepth = options.maxDepth || 2;\n const network = new Map<string, number>();\n const visited = new Set<string>();\n const queue: Array<{ id: string; depth: number }> = [\n { id: profileId, depth: 0 },\n ];\n\n while (queue.length > 0) {\n const current = queue.shift();\n if (!current) {\n break;\n }\n\n if (visited.has(current.id) || current.depth > maxDepth) {\n continue;\n }\n\n visited.add(current.id);\n network.set(current.id, current.depth);\n\n if (current.depth < maxDepth) {\n const related = await this.findRelated(current.id);\n for (const profile of related) {\n if (profile.id && !visited.has(profile.id)) {\n queue.push({ id: profile.id, depth: current.depth + 1 });\n }\n }\n }\n }\n\n return network;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Tenant-scoped helper methods\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Find all profiles belonging to a specific tenant\n *\n * @param tenantId - The tenant UUID to filter by\n * @returns Array of profiles for this tenant\n */\n async findByTenant(tenantId: string): Promise<Profile[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global profiles (no tenant association).\n *\n * Routes through the shared tenant-global helper so it does not throw under\n * an active tenant context (an explicit `tenant_id IS NULL` filter would be\n * flagged as an isolation violation). (#1600)\n *\n * @returns Array of profiles with null tenantId\n */\n async findGlobal(): Promise<Profile[]> {\n return queryGlobal<Profile>(this);\n }\n\n /**\n * Find profiles belonging to a tenant plus all global profiles.\n *\n * Fails closed if an active tenant context requests a different tenant's\n * rows; the admin/system path keeps the cross-tenant capability. (#1600)\n *\n * @param tenantId - The tenant UUID to include\n * @returns Array of tenant-specific and global profiles\n */\n async findWithGlobals(tenantId: string): Promise<Profile[]> {\n return queryWithGlobals<Profile>(this, tenantId, 'Profile.findWithGlobals');\n }\n}\n\nasync function saveIdentityEmailReservation(\n reservation: OidcProfileEmailReservation,\n normalizedEmail: string,\n): Promise<void> {\n try {\n await reservation.save();\n } catch (error) {\n if (\n (error instanceof ValidationError &&\n error.code === 'VALIDATION_UNIQUE_CONSTRAINT') ||\n isOidcAbortedTransactionError(error)\n ) {\n throw new CanonicalPersonProfileError(\n 'reservation_conflict',\n `${normalizedEmail} was concurrently reserved for another canonical Person.`,\n );\n }\n throw error;\n }\n}\n\nfunction readString(row: Record<string, unknown>, key: string): string | null {\n const value = row[key];\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nfunction readRequiredString(row: Record<string, unknown>, key: string): string {\n const value = readString(row, key);\n if (!value) {\n throw new Error(`Canonical Profile identity row is missing ${key}.`);\n }\n return value;\n}\n"],"mappings":"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"}