@happyvertical/smrt-profiles 0.49.3 → 0.49.5

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.
@@ -3,12 +3,12 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-profiles",
6
- "packageVersion": "0.49.3",
6
+ "packageVersion": "0.49.5",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "c2c5a3b6fe91603ae70e217c0d0f6733273f5ef92ba3fe907868ae33640ba92a",
11
- "packageJson": "fc6fee3d7eb7c45c804be97a4e69b3140c854d025ad21e10ea6e161a6cf89dba",
10
+ "manifest": "4d8217b58c52bdf872ea717606c71c6576bb396d1d7ce9aaa40643ba1c7d11bf",
11
+ "packageJson": "eeb27b34d5d3716afa3a4c9b56534fbf24ec9c292ec796633795a028bb148f0a",
12
12
  "agents": "475e041f9b6a04721dda5d475337a22f72c80c8fdc8be95acca5011535744b15"
13
13
  },
14
14
  "exports": [
@@ -1549,7 +1549,7 @@
1549
1549
  "name": "ProfileCollection",
1550
1550
  "qualifiedName": "@happyvertical/smrt-profiles:ProfileCollection",
1551
1551
  "collection": "profiles",
1552
- "tableName": "profile_collections",
1552
+ "tableName": "profiles",
1553
1553
  "packageName": "@happyvertical/smrt-profiles",
1554
1554
  "extends": "SmrtCollection",
1555
1555
  "fields": [],
@@ -2012,7 +2012,7 @@
2012
2012
  "name": "ProfileMetadataCollection",
2013
2013
  "qualifiedName": "@happyvertical/smrt-profiles:ProfileMetadataCollection",
2014
2014
  "collection": "profilemetadatas",
2015
- "tableName": "profile_metadata_collections",
2015
+ "tableName": "profile_metadata",
2016
2016
  "packageName": "@happyvertical/smrt-profiles",
2017
2017
  "extends": "SmrtCollection",
2018
2018
  "fields": [],
@@ -2121,7 +2121,7 @@
2121
2121
  "name": "ProfileMetafieldCollection",
2122
2122
  "qualifiedName": "@happyvertical/smrt-profiles:ProfileMetafieldCollection",
2123
2123
  "collection": "profilemetafields",
2124
- "tableName": "profile_metafield_collections",
2124
+ "tableName": "profile_metafields",
2125
2125
  "packageName": "@happyvertical/smrt-profiles",
2126
2126
  "extends": "SmrtCollection",
2127
2127
  "fields": [],
@@ -2201,7 +2201,7 @@
2201
2201
  "name": "ProfileRelationshipCollection",
2202
2202
  "qualifiedName": "@happyvertical/smrt-profiles:ProfileRelationshipCollection",
2203
2203
  "collection": "profilerelationships",
2204
- "tableName": "profile_relationship_collections",
2204
+ "tableName": "profile_relationships",
2205
2205
  "packageName": "@happyvertical/smrt-profiles",
2206
2206
  "extends": "SmrtCollection",
2207
2207
  "fields": [],
@@ -2343,7 +2343,7 @@
2343
2343
  "name": "ProfileRelationshipTermCollection",
2344
2344
  "qualifiedName": "@happyvertical/smrt-profiles:ProfileRelationshipTermCollection",
2345
2345
  "collection": "profilerelationshipterms",
2346
- "tableName": "profile_relationship_term_collections",
2346
+ "tableName": "profile_relationship_terms",
2347
2347
  "packageName": "@happyvertical/smrt-profiles",
2348
2348
  "extends": "SmrtCollection",
2349
2349
  "fields": [],
@@ -2451,7 +2451,7 @@
2451
2451
  "name": "ProfileRelationshipTypeCollection",
2452
2452
  "qualifiedName": "@happyvertical/smrt-profiles:ProfileRelationshipTypeCollection",
2453
2453
  "collection": "profilerelationshiptypes",
2454
- "tableName": "profile_relationship_type_collections",
2454
+ "tableName": "profile_relationship_types",
2455
2455
  "packageName": "@happyvertical/smrt-profiles",
2456
2456
  "extends": "SmrtCollection",
2457
2457
  "fields": [],
@@ -2583,7 +2583,7 @@
2583
2583
  "name": "ProfileTypeCollection",
2584
2584
  "qualifiedName": "@happyvertical/smrt-profiles:ProfileTypeCollection",
2585
2585
  "collection": "profiletypes",
2586
- "tableName": "profile_type_collections",
2586
+ "tableName": "profile_types",
2587
2587
  "packageName": "@happyvertical/smrt-profiles",
2588
2588
  "extends": "SmrtCollection",
2589
2589
  "fields": [],
@@ -13931,7 +13931,7 @@
13931
13931
  "junctionCollections": 1,
13932
13932
  "hierarchicalObjects": 0,
13933
13933
  "polymorphicAssociations": 0,
13934
- "uuidColumns": 61
13934
+ "uuidColumns": 85
13935
13935
  },
13936
13936
  "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 (optional; identity uniqueness is arbitrated by `oidc_profile_email_reservations.email_key`, not a DB constraint — legacy duplicates are tolerated and fail closed, #2359), 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"
13937
13937
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-profiles",
3
- "version": "0.49.3",
3
+ "version": "0.49.5",
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",
@@ -30,10 +30,10 @@
30
30
  "@happyvertical/ai": "^0.89.6",
31
31
  "@happyvertical/files": "^0.89.6",
32
32
  "@happyvertical/logger": "^0.89.6",
33
- "@happyvertical/smrt-assets": "0.49.3",
34
- "@happyvertical/smrt-core": "0.49.3",
35
- "@happyvertical/smrt-prompts": "0.49.3",
36
- "@happyvertical/smrt-tenancy": "0.49.3",
33
+ "@happyvertical/smrt-assets": "0.49.5",
34
+ "@happyvertical/smrt-core": "0.49.5",
35
+ "@happyvertical/smrt-prompts": "0.49.5",
36
+ "@happyvertical/smrt-tenancy": "0.49.5",
37
37
  "@happyvertical/sql": "^0.89.6",
38
38
  "@happyvertical/utils": "^0.89.6",
39
39
  "@noble/curves": "^1.9.7",
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "devDependencies": {
43
43
  "@faker-js/faker": "^10.5.0",
44
- "@happyvertical/smrt-vitest": "0.49.3",
44
+ "@happyvertical/smrt-vitest": "0.49.5",
45
45
  "@types/node": "24.13.2",
46
46
  "fast-glob": "3.3.3",
47
47
  "typescript": "5.9.3",