@happyvertical/smrt-profiles 0.51.38 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +3 -1
- package/README.md +33 -1
- package/dist/chunks/{OidcIdentity-K8V4u0CR.js → OidcIdentity-Cb1bxreM.js} +3 -3
- package/dist/chunks/{OidcIdentity-K8V4u0CR.js.map → OidcIdentity-Cb1bxreM.js.map} +1 -1
- package/dist/chunks/{OidcIdentityCollection-gnrLoF08.js → OidcIdentityCollection-TV8VdK69.js} +2 -2
- package/dist/chunks/{OidcIdentityCollection-gnrLoF08.js.map → OidcIdentityCollection-TV8VdK69.js.map} +1 -1
- package/dist/chunks/{ProfileAssetCollection-CmbeSgDi.js → ProfileAssetCollection-B9hacUJa.js} +10 -4
- package/dist/chunks/ProfileAssetCollection-B9hacUJa.js.map +1 -0
- package/dist/chunks/{ProfileCollection-DTT4KNpQ.js → ProfileCollection-DuJT7Mum.js} +52 -14
- package/dist/chunks/ProfileCollection-DuJT7Mum.js.map +1 -0
- package/dist/chunks/ProfileLinkCollection-4uNURz32.js +228 -0
- package/dist/chunks/ProfileLinkCollection-4uNURz32.js.map +1 -0
- package/dist/chunks/{ProfileMetadataCollection-DxX_BJKV.js → ProfileMetadataCollection-DQhbfEfa.js} +2 -1
- package/dist/chunks/ProfileMetadataCollection-DQhbfEfa.js.map +1 -0
- package/dist/chunks/{ProfileMetafieldCollection-CMKhVi2I.js → ProfileMetafieldCollection-CoBuM_5G.js} +2 -1
- package/dist/chunks/ProfileMetafieldCollection-CoBuM_5G.js.map +1 -0
- package/dist/chunks/{oidcProvisioningPrimitives-0-3n3D6f.js → oidcProvisioningPrimitives-D9OYu5-F.js} +3 -3
- package/dist/chunks/{oidcProvisioningPrimitives-0-3n3D6f.js.map → oidcProvisioningPrimitives-D9OYu5-F.js.map} +1 -1
- package/dist/chunks/{resolveIdentity-BrJDCjYi.js → resolveIdentity-B-PK6RwU.js} +14 -14
- package/dist/chunks/{resolveIdentity-BrJDCjYi.js.map → resolveIdentity-B-PK6RwU.js.map} +1 -1
- package/dist/collections/ProfileLinkCollection.d.ts +40 -0
- package/dist/collections/ProfileLinkCollection.d.ts.map +1 -0
- package/dist/collections/index.d.ts +1 -0
- package/dist/collections/index.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -13
- package/dist/index.js.map +1 -1
- package/dist/internal/oidc-provisioning.js +2 -2
- package/dist/manifest.json +589 -13
- package/dist/models/Profile.d.ts +26 -0
- package/dist/models/Profile.d.ts.map +1 -1
- package/dist/models/ProfileAsset.d.ts.map +1 -1
- package/dist/models/ProfileLink.d.ts +54 -0
- package/dist/models/ProfileLink.d.ts.map +1 -0
- package/dist/models/ProfileMetadata.d.ts.map +1 -1
- package/dist/models/ProfileMetafield.d.ts.map +1 -1
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +537 -7
- package/dist/utils.js +4 -4
- package/package.json +12 -12
- package/dist/chunks/ProfileAssetCollection-CmbeSgDi.js.map +0 -1
- package/dist/chunks/ProfileCollection-DTT4KNpQ.js.map +0 -1
- package/dist/chunks/ProfileMetadataCollection-DxX_BJKV.js.map +0 -1
- package/dist/chunks/ProfileMetafieldCollection-CMKhVi2I.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -6,8 +6,10 @@ Central identity system with multi-auth, relationships, controlled metadata, and
|
|
|
6
6
|
|
|
7
7
|
- **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.
|
|
8
8
|
- **ProfileAsset**: dedicated owned-asset join in `profile_assets` with `relationship` and `sortOrder`.
|
|
9
|
+
- **ProfileLink**: ordered web links in `profile_links` (`platform` from `PROFILE_LINK_PLATFORMS`, validated http(s) `url`, optional `label`, `sortOrder`). `profileId` FK is `onDelete: 'CASCADE'`. Slug is the id (never url/label, so a new link cannot adopt another row through the natural key). Write through `ProfileLinkCollection.replaceForProfile()` / `reorder()` (one transaction each; resolve the profile in the current tenant context and stamp its tenant) or `Profile.setLinks()` / `getLinks()`.
|
|
10
|
+
- **Profile.isPublic**: optional public-figure flag (boolean, default false). Records the fact only; consumers decide visibility.
|
|
9
11
|
- **ProfileRelationship**: bidirectional — creating one auto-creates reciprocal inverse. `contextProfileId` for tertiary relationships. `ProfileRelationshipTerm` tracks start/end dates.
|
|
10
|
-
- **ProfileMetafield**: controlled vocabulary with `validationSchema`. **ProfileMetadata**: per-profile values linked to metafields.
|
|
12
|
+
- **ProfileMetafield**: controlled vocabulary with `validationSchema`. **ProfileMetadata**: per-profile values linked to metafields. `addMetadata()` stamps new rows with the profile's `tenantId` (never rely on auto-population, which super-admin/system context skips, #3235) and resolves the metafield as profile-tenant first, then global, never another tenant's.
|
|
11
13
|
- **AuditLog**: action, resourceType/Id, `source` (web/cli/ci/webhook/mcp), `onBehalfOfId` for CI pass-through identity. `allowSuperAdminBypass: true`.
|
|
12
14
|
|
|
13
15
|
## Auth Methods
|
package/README.md
CHANGED
|
@@ -59,6 +59,36 @@ const avatarAssets = await profile.getAssets('avatar');
|
|
|
59
59
|
const galleryAssets = await profiles.getAssets(profile.id!, 'gallery');
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
### Web links
|
|
63
|
+
|
|
64
|
+
A profile's social accounts and websites, in display order. Several links per
|
|
65
|
+
platform are allowed; the same address is kept once. New links take the
|
|
66
|
+
profile's tenant, and they are removed with the profile.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { ProfileLinkCollection } from '@happyvertical/smrt-profiles';
|
|
70
|
+
|
|
71
|
+
await profile.setLinks([
|
|
72
|
+
{ platform: 'facebook', url: 'https://facebook.com/mayor' },
|
|
73
|
+
{ platform: 'website', url: 'https://mayor.example/' },
|
|
74
|
+
{ platform: 'other', url: 'https://blog.example/', label: 'Blog' },
|
|
75
|
+
]); // replaces the whole list in one transaction
|
|
76
|
+
|
|
77
|
+
const links = await profile.getLinks(); // ordered by sortOrder
|
|
78
|
+
|
|
79
|
+
const profileLinks = await ProfileLinkCollection.create({ db });
|
|
80
|
+
await profileLinks.reorder(profile.id!, [links[2].id!, links[0].id!, links[1].id!]);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Platforms: `facebook`, `instagram`, `x`, `linkedin`, `youtube`, `tiktok`,
|
|
84
|
+
`website`, `other` (`PROFILE_LINK_PLATFORMS`). Addresses must be `http://` or
|
|
85
|
+
`https://` (`normalizeProfileLinkUrl`). A link's slug is its id, never its
|
|
86
|
+
address or label.
|
|
87
|
+
|
|
88
|
+
`Profile.isPublic` (default `false`) is a public-figure flag: the profile is
|
|
89
|
+
publicly known (an elected official, a public body). It only records the fact;
|
|
90
|
+
the application decides what it shows.
|
|
91
|
+
|
|
62
92
|
## API
|
|
63
93
|
|
|
64
94
|
### Models
|
|
@@ -76,6 +106,7 @@ const galleryAssets = await profiles.getAssets(profile.id!, 'gallery');
|
|
|
76
106
|
| `ProfileRelationshipType` | Relationship classification with reciprocal flag |
|
|
77
107
|
| `ProfileRelationshipTerm` | Time-bounded relationship periods |
|
|
78
108
|
| `ProfileAsset` | Dedicated owned-asset join stored in `profile_assets` with `relationship` and `sortOrder` |
|
|
109
|
+
| `ProfileLink` | Ordered web link on a profile (`profile_links`: platform, url, optional label, sortOrder; cascades with the profile) |
|
|
79
110
|
|
|
80
111
|
### Auth Models
|
|
81
112
|
|
|
@@ -94,6 +125,7 @@ const galleryAssets = await profiles.getAssets(profile.id!, 'gallery');
|
|
|
94
125
|
| `ProfileCollection` | CRUD and query for profiles |
|
|
95
126
|
| `ProfileAssetCollection` | Direct access to `profile_assets` rows plus asset helper wrappers |
|
|
96
127
|
| `ProfileTypeCollection` | Profile type management |
|
|
128
|
+
| `ProfileLinkCollection` | `listForProfile`, `replaceForProfile` (ordered replace in one transaction), `reorder` |
|
|
97
129
|
| `ProfileMetadataCollection` | Metadata value operations |
|
|
98
130
|
| `ProfileMetafieldCollection` | Metafield vocabulary management |
|
|
99
131
|
| `ProfileRelationshipCollection` | Relationship queries |
|
|
@@ -218,7 +250,7 @@ authorization, the owned Profile still returns `profile_owned`.
|
|
|
218
250
|
|
|
219
251
|
### Key Types
|
|
220
252
|
|
|
221
|
-
`ProfileOptions`, `ProfileTypeOptions`, `ProfileMetadataOptions`, `ProfileMetafieldOptions`, `ProfileRelationshipOptions`, `ProfileRelationshipTypeOptions`, `ProfileRelationshipTermOptions`, `OidcIdentityOptions`, `CanonicalPersonProfileErrorCode`, `NostrIdentityOptions`, `ApiKeyOptions`, `GenerateKeyResult`, `MagicLinkTokenOptions`, `GenerateTokenResult`, `AuditLogOptions`, `AuditSource`, `AuthContext`, `ResolveIdentityResult`, `InitiateResult`, `VerifyResult`, `MagicLinkConfig`, `MagicLinkService`, `Nip05HandlerConfig`, `Nip05HandlerResult`, `Nip05Request`, `Nip05Response`, `NostrEvent`, `NostrKeypair`, `EncryptedKey`, `ValidationSchema`, `ValidatorFunction`, `ReciprocalHandler`
|
|
253
|
+
`ProfileOptions`, `ProfileTypeOptions`, `ProfileLinkOptions`, `ProfileLinkPlatform`, `ProfileLinkInput`, `ProfileMetadataOptions`, `ProfileMetafieldOptions`, `ProfileRelationshipOptions`, `ProfileRelationshipTypeOptions`, `ProfileRelationshipTermOptions`, `OidcIdentityOptions`, `CanonicalPersonProfileErrorCode`, `NostrIdentityOptions`, `ApiKeyOptions`, `GenerateKeyResult`, `MagicLinkTokenOptions`, `GenerateTokenResult`, `AuditLogOptions`, `AuditSource`, `AuthContext`, `ResolveIdentityResult`, `InitiateResult`, `VerifyResult`, `MagicLinkConfig`, `MagicLinkService`, `Nip05HandlerConfig`, `Nip05HandlerResult`, `Nip05Request`, `Nip05Response`, `NostrEvent`, `NostrKeypair`, `EncryptedKey`, `ValidationSchema`, `ValidatorFunction`, `ReciprocalHandler`
|
|
222
254
|
|
|
223
255
|
## Dependencies
|
|
224
256
|
|
|
@@ -35,7 +35,7 @@ var OidcIdentity = class extends SmrtObject {
|
|
|
35
35
|
* Find identity by issuer and subject
|
|
36
36
|
*/
|
|
37
37
|
static async findBySubject(issuer, subject, options = {}) {
|
|
38
|
-
const { OidcIdentityCollection } = await import("./OidcIdentityCollection-
|
|
38
|
+
const { OidcIdentityCollection } = await import("./OidcIdentityCollection-TV8VdK69.js").then((n) => n.r);
|
|
39
39
|
return await (await OidcIdentityCollection.create(options)).findBySubject(issuer, subject);
|
|
40
40
|
}
|
|
41
41
|
/** Build the collision-free natural key for one OIDC issuer subject. */
|
|
@@ -58,7 +58,7 @@ var OidcIdentity = class extends SmrtObject {
|
|
|
58
58
|
static async findOrCreate(profile, oidcData, options = {}) {
|
|
59
59
|
const profileId = profile.id;
|
|
60
60
|
if (typeof profileId !== "string" || !profileId) throw new Error("OidcIdentity.findOrCreate() requires a saved Profile.");
|
|
61
|
-
const { reuseExistingOidcIdentityForProfile } = await import("./resolveIdentity-
|
|
61
|
+
const { reuseExistingOidcIdentityForProfile } = await import("./resolveIdentity-B-PK6RwU.js").then((n) => n.i);
|
|
62
62
|
const profileOptions = profile.options ?? {};
|
|
63
63
|
return (await reuseExistingOidcIdentityForProfile(profileId, {
|
|
64
64
|
email: oidcData.email,
|
|
@@ -116,4 +116,4 @@ OidcIdentity = __decorateClass([smrt({
|
|
|
116
116
|
//#endregion
|
|
117
117
|
export { OidcIdentity as t };
|
|
118
118
|
|
|
119
|
-
//# sourceMappingURL=OidcIdentity-
|
|
119
|
+
//# sourceMappingURL=OidcIdentity-Cb1bxreM.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OidcIdentity-
|
|
1
|
+
{"version":3,"file":"OidcIdentity-Cb1bxreM.js","names":[],"sources":["../../src/models/OidcIdentity.ts"],"sourcesContent":["/**\n * OidcIdentity - Links OIDC provider identities to Profile\n *\n * Stores the mapping between external OIDC providers (Keycloak, Google, GitHub)\n * and internal Profile records. Multiple identities can link to a single profile.\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { Profile } from './Profile';\n\nexport interface OidcIdentityOptions extends SmrtObjectOptions {\n profileId?: string;\n provider?: string;\n issuer?: string;\n subject?: string;\n email?: string;\n lastUsedAt?: Date | null;\n}\n\n@smrt({\n tableName: 'oidc_identities',\n // Identity linking is an authentication authority change. Generated routes\n // authenticate callers but do not authorize Profile ownership, so mutations\n // must stay behind the trusted provisioning APIs below.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class OidcIdentity extends SmrtObject {\n /**\n * Link to the Profile (Person, Organization, Bot)\n */\n @foreignKey('Profile', { required: true })\n profileId?: string;\n\n /**\n * Provider name (e.g., 'keycloak', 'google', 'github')\n */\n @field({ type: 'text' })\n provider: string = '';\n\n /**\n * OIDC issuer URL (e.g., https://keycloak.example.com/realms/bmp)\n */\n @field({ type: 'text', indexed: true })\n issuer: string = '';\n\n /**\n * OIDC subject claim - unique identifier from the provider\n */\n @field({ type: 'text', indexed: true })\n subject: string = '';\n\n /**\n * Stable issuer+subject key used as the database race arbiter.\n *\n * Nullable for legacy rows; every newly linked or reused identity backfills\n * it. A separate unique constraint makes concurrent first login fail with a\n * retryable conflict instead of creating two identities.\n */\n @field({ type: 'text', nullable: true, unique: true, readonly: true })\n identityKey: string | null = null;\n\n /**\n * Cached email from the IdP (for display/lookup)\n */\n @field({ type: 'text' })\n email: string = '';\n\n /**\n * Last time this identity was used for authentication\n */\n @field({ type: 'datetime', nullable: true })\n lastUsedAt: Date | null = null;\n\n constructor(options: OidcIdentityOptions = {}) {\n super(options);\n if (options.profileId) this.profileId = options.profileId;\n if (options.provider) this.provider = options.provider;\n if (options.issuer) this.issuer = options.issuer;\n if (options.subject) this.subject = options.subject;\n if (options.email) this.email = options.email;\n if (options.lastUsedAt !== undefined) this.lastUsedAt = options.lastUsedAt;\n }\n\n /**\n * Get the linked Profile\n */\n async getProfile(): Promise<Profile | null> {\n return (await this.getRelated('profileId')) as Profile | null;\n }\n\n /**\n * Find identity by issuer and subject\n */\n static async findBySubject(\n issuer: string,\n subject: string,\n options: SmrtObjectOptions = {},\n ): Promise<OidcIdentity | null> {\n const { OidcIdentityCollection } = await import(\n '../collections/OidcIdentityCollection'\n );\n const collection = await OidcIdentityCollection.create(options);\n return await collection.findBySubject(issuer, subject);\n }\n\n /** Build the collision-free natural key for one OIDC issuer subject. */\n static buildIdentityKey(issuer: string, subject: string): string {\n return JSON.stringify([issuer, subject]);\n }\n\n /** Keep the durable key derived from its natural-key source fields. */\n override async save(): Promise<this> {\n this.identityKey =\n this.issuer.trim() && this.subject.trim()\n ? OidcIdentity.buildIdentityKey(this.issuer, this.subject)\n : null;\n return super.save();\n }\n\n /**\n * Reuse an existing exact identity for its unchanged Profile.\n *\n * @deprecated Authentication links must be created through transactional\n * provisioning. This compatibility method only refreshes a unique mapping\n * that already belongs to the supplied Profile, including legacy Profile\n * types, and deliberately refuses to create or rebind authority.\n */\n static async findOrCreate(\n profile: Profile,\n oidcData: {\n provider: string;\n issuer: string;\n subject: string;\n email?: string;\n },\n options: SmrtObjectOptions = {},\n ): Promise<OidcIdentity> {\n const profileId = profile.id;\n if (typeof profileId !== 'string' || !profileId) {\n throw new Error('OidcIdentity.findOrCreate() requires a saved Profile.');\n }\n const { reuseExistingOidcIdentityForProfile } = await import(\n '../auth/resolveIdentity'\n );\n const profileOptions = profile.options ?? {};\n const result = await reuseExistingOidcIdentityForProfile(\n profileId,\n {\n email: oidcData.email,\n iss: oidcData.issuer,\n sub: oidcData.subject,\n },\n oidcData.provider,\n {\n ...profileOptions,\n ...options,\n db: options.db ?? profileOptions.db,\n },\n );\n return result.oidcIdentity;\n }\n\n /**\n * Record usage of this identity\n */\n async recordUsage(): Promise<void> {\n // Trusted provisioning or another authentication may refresh this exact\n // identity through a second instance. Rehydrate before each bounded attempt\n // so this usage-only write preserves those changes while retaining CAS.\n for (let attempt = 0; attempt < 4; attempt += 1) {\n await this.loadFromId();\n this.lastUsedAt = new Date();\n try {\n await this.save();\n return;\n } catch (error) {\n if (\n attempt === 3 ||\n !(\n error instanceof Error &&\n 'code' in error &&\n error.code === 'RUNTIME_REVISION_CONFLICT'\n )\n ) {\n throw error;\n }\n }\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/chunks/{OidcIdentityCollection-gnrLoF08.js → OidcIdentityCollection-TV8VdK69.js}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { y as __exportAll } from "./NostrIdentity-n70E0vck.js";
|
|
2
|
-
import { t as OidcIdentity } from "./OidcIdentity-
|
|
2
|
+
import { t as OidcIdentity } from "./OidcIdentity-Cb1bxreM.js";
|
|
3
3
|
import { SmrtCollection } from "@happyvertical/smrt-core";
|
|
4
4
|
//#region src/collections/OidcIdentityCollection.ts
|
|
5
5
|
var OidcIdentityCollection_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -79,4 +79,4 @@ var OidcIdentityCollection = class extends SmrtCollection {
|
|
|
79
79
|
//#endregion
|
|
80
80
|
export { OidcIdentityCollection as n, OidcIdentityCollection_exports as r, AmbiguousOidcIdentityError as t };
|
|
81
81
|
|
|
82
|
-
//# sourceMappingURL=OidcIdentityCollection-
|
|
82
|
+
//# sourceMappingURL=OidcIdentityCollection-TV8VdK69.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OidcIdentityCollection-
|
|
1
|
+
{"version":3,"file":"OidcIdentityCollection-TV8VdK69.js","names":[],"sources":["../../src/collections/OidcIdentityCollection.ts"],"sourcesContent":["/**\n * OidcIdentityCollection - Collection for managing OIDC identity records\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { OidcIdentity } from '../models/OidcIdentity';\nimport type { Profile } from '../models/Profile';\n\n/** More than one legacy row maps the same opaque OIDC issuer and subject. */\nexport class AmbiguousOidcIdentityError extends Error {\n constructor(\n readonly issuer: string,\n readonly subject: string,\n ) {\n super('Multiple OIDC identities match the same issuer and subject.');\n this.name = 'AmbiguousOidcIdentityError';\n }\n}\n\nexport class OidcIdentityCollection extends SmrtCollection<OidcIdentity> {\n static readonly _itemClass = OidcIdentity;\n\n /**\n * Find identities for a profile\n */\n async findByProfile(profileId: string): Promise<OidcIdentity[]> {\n return await this.list({\n where: { profileId },\n });\n }\n\n /**\n * Find identity by issuer and subject\n */\n async findBySubject(\n issuer: string,\n subject: string,\n ): Promise<OidcIdentity | null> {\n const result = await this.db.query(\n `SELECT CAST(id AS VARCHAR) AS id,\n CAST(profile_id AS VARCHAR) AS profile_id\n FROM oidc_identities\n WHERE issuer = ? AND subject = ?\n LIMIT 2`,\n issuer,\n subject,\n );\n if (result.rows.length > 1) {\n throw new AmbiguousOidcIdentityError(issuer, subject);\n }\n const row = result.rows[0];\n const id = row?.id;\n const profileId = row?.profile_id;\n if (typeof id !== 'string' || typeof profileId !== 'string') return null;\n const identity = await this.get({ id });\n if (!identity) return null;\n // Preserve portable identities at this authentication boundary when\n // native DuckDB exposes UUID results through internal objects.\n identity.id = id;\n identity.profileId = profileId;\n return identity;\n }\n\n /**\n * Find identities by provider\n */\n async findByProvider(provider: string): Promise<OidcIdentity[]> {\n return await this.list({\n where: { provider },\n });\n }\n\n /**\n * Reuse an existing exact OIDC identity for its unchanged Profile.\n *\n * @deprecated New authentication links require the owner-aware,\n * transactional provisioning APIs. This compatibility helper may refresh a\n * legacy Profile type, but refuses to create or rebind authority.\n */\n async linkToProfile(\n profile: Profile,\n oidcData: {\n provider: string;\n issuer: string;\n subject: string;\n email?: string;\n },\n ): Promise<OidcIdentity> {\n return OidcIdentity.findOrCreate(profile, oidcData, this.options);\n }\n\n /**\n * Unlink an OIDC identity from a profile\n */\n async unlinkFromProfile(\n profileId: string,\n issuer: string,\n subject: string,\n ): Promise<boolean> {\n const identity = await this.findOne({\n where: { profileId, issuer, subject },\n });\n\n if (identity) {\n await identity.delete();\n return true;\n }\n return false;\n }\n}\n"],"mappings":";;;;;;;;AASO,IAAM,6BAAN,cAAyC,MAAM;CACpD,YACW,QACA,SACT;EACA,MAAM,6DAA6D;EAH1D,KAAA,SAAA;EACA,KAAA,UAAA;EAGT,KAAK,OAAO;CACd;CALW;CACA;AAKb;AAEO,IAAM,yBAAN,cAAqC,eAA6B;CACvE,OAAgB,aAAa;;;;CAK7B,MAAM,cAAc,WAA4C;EAC9D,OAAO,MAAM,KAAK,KAAK,EACrB,OAAO,EAAE,UAAU,EACrB,CAAC;CACH;;;;CAKA,MAAM,cACJ,QACA,SAC8B;EAC9B,MAAM,SAAS,MAAM,KAAK,GAAG,MAC3B;;;;kBAKA,QACA,OACF;EACA,IAAI,OAAO,KAAK,SAAS,GACvB,MAAM,IAAI,2BAA2B,QAAQ,OAAO;EAEtD,MAAM,MAAM,OAAO,KAAK;EACxB,MAAM,KAAK,KAAK;EAChB,MAAM,YAAY,KAAK;EACvB,IAAI,OAAO,OAAO,YAAY,OAAO,cAAc,UAAU,OAAO;EACpE,MAAM,WAAW,MAAM,KAAK,IAAI,EAAE,GAAG,CAAC;EACtC,IAAI,CAAC,UAAU,OAAO;EAGtB,SAAS,KAAK;EACd,SAAS,YAAY;EACrB,OAAO;CACT;;;;CAKA,MAAM,eAAe,UAA2C;EAC9D,OAAO,MAAM,KAAK,KAAK,EACrB,OAAO,EAAE,SAAS,EACpB,CAAC;CACH;;;;;;;;CASA,MAAM,cACJ,SACA,UAMuB;EACvB,OAAO,aAAa,aAAa,SAAS,UAAU,KAAK,OAAO;CAClE;;;;CAKA,MAAM,kBACJ,WACA,QACA,SACkB;EAClB,MAAM,WAAW,MAAM,KAAK,QAAQ,EAClC,OAAO;GAAE;GAAW;GAAQ;EAAQ,EACtC,CAAC;EAED,IAAI,UAAU;GACZ,MAAM,SAAS,OAAO;GACtB,OAAO;EACT;EACA,OAAO;CACT;AACF"}
|
package/dist/chunks/{ProfileAssetCollection-CmbeSgDi.js → ProfileAssetCollection-B9hacUJa.js}
RENAMED
|
@@ -27,8 +27,14 @@ var ProfileAsset = class extends SmrtObject {
|
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
29
|
__decorateClass$1([tenantId({ nullable: true })], ProfileAsset.prototype, "tenantId", 2);
|
|
30
|
-
__decorateClass$1([foreignKey("Profile", {
|
|
31
|
-
|
|
30
|
+
__decorateClass$1([foreignKey("Profile", {
|
|
31
|
+
required: true,
|
|
32
|
+
onDelete: "CASCADE"
|
|
33
|
+
})], ProfileAsset.prototype, "profileId", 2);
|
|
34
|
+
__decorateClass$1([crossPackageRef("@happyvertical/smrt-assets:Asset", {
|
|
35
|
+
required: true,
|
|
36
|
+
onDelete: "CASCADE"
|
|
37
|
+
})], ProfileAsset.prototype, "assetId", 2);
|
|
32
38
|
__decorateClass$1([field({ required: true })], ProfileAsset.prototype, "relationship", 2);
|
|
33
39
|
__decorateClass$1([field()], ProfileAsset.prototype, "sortOrder", 2);
|
|
34
40
|
ProfileAsset = __decorateClass$1([TenantScoped({ mode: "optional" }), smrt({
|
|
@@ -66,7 +72,7 @@ var ProfileAssetCollection = class extends SmrtJunction {
|
|
|
66
72
|
profileCollectionPromise = null;
|
|
67
73
|
async getProfileCollection() {
|
|
68
74
|
if (!this.profileCollectionPromise) {
|
|
69
|
-
const { ProfileCollection } = await import("./ProfileCollection-
|
|
75
|
+
const { ProfileCollection } = await import("./ProfileCollection-DuJT7Mum.js").then((n) => n.r);
|
|
70
76
|
this.profileCollectionPromise = ProfileCollection.create({ db: this.db });
|
|
71
77
|
}
|
|
72
78
|
return this.profileCollectionPromise;
|
|
@@ -90,4 +96,4 @@ ProfileAssetCollection = __decorateClass([smrt({
|
|
|
90
96
|
//#endregion
|
|
91
97
|
export { ProfileAssetCollection_exports as n, ProfileAsset as r, ProfileAssetCollection as t };
|
|
92
98
|
|
|
93
|
-
//# sourceMappingURL=ProfileAssetCollection-
|
|
99
|
+
//# sourceMappingURL=ProfileAssetCollection-B9hacUJa.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProfileAssetCollection-B9hacUJa.js","names":[],"sources":["../../src/models/ProfileAsset.ts","../../src/collections/ProfileAssetCollection.ts"],"sourcesContent":["import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n field,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\nexport interface ProfileAssetOptions extends SmrtObjectOptions {\n profileId?: string;\n assetId?: string;\n relationship?: string;\n sortOrder?: number;\n tenantId?: string | null;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'profile_assets',\n conflictColumns: ['profile_id', 'asset_id', 'relationship'],\n api: false,\n mcp: false,\n cli: false,\n})\nexport class ProfileAsset extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('Profile', { required: true, onDelete: 'CASCADE' })\n profileId = '';\n\n @crossPackageRef('@happyvertical/smrt-assets:Asset', {\n required: true,\n onDelete: 'CASCADE',\n })\n assetId = '';\n\n @field({ required: true })\n relationship = 'attachment';\n\n @field()\n sortOrder = 0;\n\n constructor(options: ProfileAssetOptions = {}) {\n super(options);\n if (options.profileId) this.profileId = options.profileId;\n if (options.assetId) this.assetId = options.assetId;\n if (options.relationship) this.relationship = options.relationship;\n if (options.sortOrder !== undefined) this.sortOrder = options.sortOrder;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n }\n}\n","import type { Asset } from '@happyvertical/smrt-assets';\nimport {\n addOwnedAssetFromCollection,\n getOwnedAssetsFromCollection,\n removeOwnedAssetFromCollection,\n} from '@happyvertical/smrt-assets';\nimport type { SmrtCollectionOptions } from '@happyvertical/smrt-core';\nimport { SmrtJunction, smrt } from '@happyvertical/smrt-core';\nimport { ProfileAsset } from '../models/ProfileAsset';\nimport type { ProfileCollection } from './ProfileCollection';\n\nexport interface ProfileAssetCollectionOptions extends SmrtCollectionOptions {}\n\n@smrt({\n api: false,\n mcp: false,\n cli: false,\n})\nexport class ProfileAssetCollection extends SmrtJunction<ProfileAsset> {\n static readonly _itemClass = ProfileAsset;\n protected leftField = 'profileId';\n protected rightField = 'assetId';\n\n private profileCollectionPromise: Promise<ProfileCollection> | null = null;\n\n private async getProfileCollection(): Promise<ProfileCollection> {\n if (!this.profileCollectionPromise) {\n const { ProfileCollection } = await import('./ProfileCollection');\n this.profileCollectionPromise = ProfileCollection.create({ db: this.db });\n }\n\n return this.profileCollectionPromise;\n }\n\n async getAssets(profileId: string, relationship?: string): Promise<Asset[]> {\n return getOwnedAssetsFromCollection(\n await this.getProfileCollection(),\n profileId,\n relationship,\n );\n }\n\n async addAsset(\n profileId: string,\n asset: Asset,\n relationship = 'attachment',\n sortOrder = 0,\n ): Promise<void> {\n await addOwnedAssetFromCollection(\n await this.getProfileCollection(),\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 await this.getProfileCollection(),\n 'Profile',\n profileId,\n assetId,\n relationship,\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA0BO,IAAM,eAAN,cAA2B,WAAW;CAE3C,WAA0B;CAG1B,YAAY;CAMZ,UAAU;CAGV,eAAe;CAGf,YAAY;CAEZ,YAAY,UAA+B,CAAC,GAAG;EAC7C,MAAM,OAAO;EACb,IAAI,QAAQ,WAAW,KAAK,YAAY,QAAQ;EAChD,IAAI,QAAQ,SAAS,KAAK,UAAU,QAAQ;EAC5C,IAAI,QAAQ,cAAc,KAAK,eAAe,QAAQ;EACtD,IAAI,QAAQ,cAAc,KAAA,GAAW,KAAK,YAAY,QAAQ;EAC9D,IAAI,QAAQ,aAAa,KAAA,GAAW,KAAK,WAAW,QAAQ;CAC9D;AACF;AAzBE,kBAAA,CADC,SAAS,EAAE,UAAU,KAAK,CAAC,CAAA,GADjB,aAEX,WAAA,YAAA,CAAA;AAGA,kBAAA,CADC,WAAW,WAAW;CAAE,UAAU;CAAM,UAAU;AAAU,CAAC,CAAA,GAJnD,aAKX,WAAA,aAAA,CAAA;AAMA,kBAAA,CAJC,gBAAgB,oCAAoC;CACnD,UAAU;CACV,UAAU;AACZ,CAAC,CAAA,GAVU,aAWX,WAAA,WAAA,CAAA;AAGA,kBAAA,CADC,MAAM,EAAE,UAAU,KAAK,CAAC,CAAA,GAbd,aAcX,WAAA,gBAAA,CAAA;AAGA,kBAAA,CADC,MAAM,CAAA,GAhBI,aAiBX,WAAA,aAAA,CAAA;AAjBW,eAAN,kBAAA,CARN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,WAAW;CACX,iBAAiB;EAAC;EAAc;EAAY;CAAc;CAC1D,KAAK;CACL,KAAK;CACL,KAAK;AACP,CAAC,CAAA,GACY,YAAA;;;;;;;;;;;;;;;;;;;ACRN,IAAM,yBAAN,cAAqC,aAA2B;CAE3D,YAAY;CACZ,aAAa;CAEf,2BAA8D;CAEtE,MAAc,uBAAmD;EAC/D,IAAI,CAAC,KAAK,0BAA0B;GAClC,MAAM,EAAE,sBAAsB,MAAM,OAAO,kCAAA,CAAA,MAAA,MAAA,EAAA,CAAA;GAC3C,KAAK,2BAA2B,kBAAkB,OAAO,EAAE,IAAI,KAAK,GAAG,CAAC;EAC1E;EAEA,OAAO,KAAK;CACd;CAEA,MAAM,UAAU,WAAmB,cAAyC;EAC1E,OAAO,6BACL,MAAM,KAAK,qBAAqB,GAChC,WACA,YACF;CACF;CAEA,MAAM,SACJ,WACA,OACA,eAAe,cACf,YAAY,GACG;EACf,MAAM,4BACJ,MAAM,KAAK,qBAAqB,GAChC,WACA,WACA,OACA,cACA,SACF;CACF;CAEA,MAAM,YACJ,WACA,SACA,cACe;EACf,MAAM,+BACJ,MAAM,KAAK,qBAAqB,GAChC,WACA,WACA,SACA,YACF;CACF;AACF;AApDE,cADW,wBACK,cAAa,YAAA;AADlB,yBAAN,gBAAA,CALN,KAAK;CACJ,KAAK;CACL,KAAK;CACL,KAAK;AACP,CAAC,CAAA,GACY,sBAAA"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { y as __exportAll } from "./NostrIdentity-n70E0vck.js";
|
|
2
2
|
import { RuntimeError, SmrtCollection, SmrtObject, ValidationError, field, foreignKey, isAbortedTransactionError, oneToMany, smrt } from "@happyvertical/smrt-core";
|
|
3
3
|
import { definePrompt, resolvePrompt } from "@happyvertical/smrt-prompts";
|
|
4
|
-
import { TenantScoped, queryGlobal, queryWithGlobals, tenantId, withSystemContext } from "@happyvertical/smrt-tenancy";
|
|
4
|
+
import { TenantScoped, getTenantId, queryGlobal, queryWithGlobals, tenantId, withSystemContext } from "@happyvertical/smrt-tenancy";
|
|
5
5
|
import { BackfillTableUnavailableError, BackfillTracker } from "@happyvertical/smrt-core/migrations";
|
|
6
|
-
import { addOwnedAssetFromCollection, assertValidOwnedAssetRelationship, assertValidOwnedAssetSortOrder, getOwnedAssetsFromCollection, removeOwnedAssetFromCollection, resolveOwnedAssetsById } from "@happyvertical/smrt-assets";
|
|
6
|
+
import { addOwnedAssetFromCollection, assertAssetLinkable, assertValidOwnedAssetRelationship, assertValidOwnedAssetSortOrder, getOwnedAssetsFromCollection, removeOwnedAssetFromCollection, resolveOwnedAssetsById } from "@happyvertical/smrt-assets";
|
|
7
7
|
//#region src/prompts.ts
|
|
8
8
|
var smrtProfilesGenerateBioPrompt = definePrompt({
|
|
9
9
|
key: "smrtProfiles.profile.generateBio",
|
|
@@ -407,6 +407,7 @@ var Profile = class extends SmrtObject {
|
|
|
407
407
|
emailKey = null;
|
|
408
408
|
name = "";
|
|
409
409
|
description;
|
|
410
|
+
isPublic = false;
|
|
410
411
|
metadata = [];
|
|
411
412
|
relationshipsFrom = [];
|
|
412
413
|
relationshipsTo = [];
|
|
@@ -416,6 +417,7 @@ var Profile = class extends SmrtObject {
|
|
|
416
417
|
if (options.email !== void 0) this.email = options.email;
|
|
417
418
|
if (options.name) this.name = options.name;
|
|
418
419
|
if (options.description !== void 0) this.description = options.description;
|
|
420
|
+
if (options.isPublic !== void 0) this.isPublic = options.isPublic;
|
|
419
421
|
}
|
|
420
422
|
/** Keep the durable identity key derived from the public email field. */
|
|
421
423
|
async save() {
|
|
@@ -448,9 +450,9 @@ var Profile = class extends SmrtObject {
|
|
|
448
450
|
* @param value - The value to set
|
|
449
451
|
*/
|
|
450
452
|
async addMetadata(metafieldSlug, value) {
|
|
451
|
-
const { ProfileMetafieldCollection } = await import("./ProfileMetafieldCollection-
|
|
452
|
-
const { ProfileMetadataCollection } = await import("./ProfileMetadataCollection-
|
|
453
|
-
const metafield = await (await ProfileMetafieldCollection.create(this.options)
|
|
453
|
+
const { ProfileMetafieldCollection } = await import("./ProfileMetafieldCollection-CoBuM_5G.js").then((n) => n.n);
|
|
454
|
+
const { ProfileMetadataCollection } = await import("./ProfileMetadataCollection-DQhbfEfa.js").then((n) => n.n);
|
|
455
|
+
const metafield = await this.resolveMetafield(await ProfileMetafieldCollection.create(this.options), metafieldSlug);
|
|
454
456
|
if (!metafield) throw new Error(`Metafield '${metafieldSlug}' not found`);
|
|
455
457
|
await metafield.validateValue(value);
|
|
456
458
|
const metadataCollection = await ProfileMetadataCollection.create(this.options);
|
|
@@ -464,13 +466,15 @@ var Profile = class extends SmrtObject {
|
|
|
464
466
|
if (existing.length > 0) {
|
|
465
467
|
const metadata = existing[0];
|
|
466
468
|
metadata.value = String(value);
|
|
469
|
+
if (!metadata.tenantId && this.tenantId) metadata.tenantId = this.tenantId;
|
|
467
470
|
await metadata.save();
|
|
468
471
|
} else {
|
|
469
472
|
if (!this.id || !metafield.id) throw new Error("Profile.addMetadata requires a persisted profile and metafield (missing id)");
|
|
470
473
|
await (await metadataCollection.create({
|
|
471
474
|
profileId: this.id,
|
|
472
475
|
metafieldId: metafield.id,
|
|
473
|
-
value: String(value)
|
|
476
|
+
value: String(value),
|
|
477
|
+
...this.tenantId ? { tenantId: this.tenantId } : {}
|
|
474
478
|
})).save();
|
|
475
479
|
}
|
|
476
480
|
}
|
|
@@ -480,7 +484,7 @@ var Profile = class extends SmrtObject {
|
|
|
480
484
|
* @returns Object with metafield slugs as keys
|
|
481
485
|
*/
|
|
482
486
|
async getMetadata() {
|
|
483
|
-
const { ProfileMetadataCollection } = await import("./ProfileMetadataCollection-
|
|
487
|
+
const { ProfileMetadataCollection } = await import("./ProfileMetadataCollection-DQhbfEfa.js").then((n) => n.n);
|
|
484
488
|
return await (await ProfileMetadataCollection.create(this.options)).getMetadataObject(this.id);
|
|
485
489
|
}
|
|
486
490
|
/**
|
|
@@ -497,9 +501,9 @@ var Profile = class extends SmrtObject {
|
|
|
497
501
|
* @param metafieldSlug - The slug of the metafield to remove
|
|
498
502
|
*/
|
|
499
503
|
async removeMetadata(metafieldSlug) {
|
|
500
|
-
const { ProfileMetafieldCollection } = await import("./ProfileMetafieldCollection-
|
|
501
|
-
const { ProfileMetadataCollection } = await import("./ProfileMetadataCollection-
|
|
502
|
-
const metafield = await (await ProfileMetafieldCollection.create(this.options)
|
|
504
|
+
const { ProfileMetafieldCollection } = await import("./ProfileMetafieldCollection-CoBuM_5G.js").then((n) => n.n);
|
|
505
|
+
const { ProfileMetadataCollection } = await import("./ProfileMetadataCollection-DQhbfEfa.js").then((n) => n.n);
|
|
506
|
+
const metafield = await this.resolveMetafield(await ProfileMetafieldCollection.create(this.options), metafieldSlug);
|
|
503
507
|
if (!metafield) throw new Error(`Metafield '${metafieldSlug}' not found`);
|
|
504
508
|
const existing = await (await ProfileMetadataCollection.create(this.options)).list({
|
|
505
509
|
where: {
|
|
@@ -510,8 +514,37 @@ var Profile = class extends SmrtObject {
|
|
|
510
514
|
});
|
|
511
515
|
if (existing.length > 0) await existing[0].delete();
|
|
512
516
|
}
|
|
517
|
+
/**
|
|
518
|
+
* The metafield a slug names for this profile: the profile tenant's own
|
|
519
|
+
* definition first, then a global (NULL-tenant) one. A profile never takes
|
|
520
|
+
* another tenant's definition, which a super-admin or system read would
|
|
521
|
+
* otherwise return. A tenant-less profile falls back to the active tenant
|
|
522
|
+
* context's own definition only (none under system context).
|
|
523
|
+
*/
|
|
524
|
+
async resolveMetafield(metafields, slug) {
|
|
525
|
+
const candidates = await metafields.list({ where: { slug } });
|
|
526
|
+
const ownerTenantId = this.tenantId || getTenantId();
|
|
527
|
+
return candidates.find((candidate) => ownerTenantId && candidate.tenantId === ownerTenantId) ?? candidates.find((candidate) => !candidate.tenantId) ?? null;
|
|
528
|
+
}
|
|
529
|
+
async getProfileLinkCollection() {
|
|
530
|
+
const { ProfileLinkCollection } = await import("./ProfileLinkCollection-4uNURz32.js").then((n) => n.r);
|
|
531
|
+
return ProfileLinkCollection.create({ db: this.db });
|
|
532
|
+
}
|
|
533
|
+
/** This profile's web links in display order. */
|
|
534
|
+
async getLinks() {
|
|
535
|
+
if (!this.id) return [];
|
|
536
|
+
return (await this.getProfileLinkCollection()).listForProfile(this.id);
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Replaces this profile's web links with `links`, in that order, in one
|
|
540
|
+
* transaction (see `ProfileLinkCollection.replaceForProfile`).
|
|
541
|
+
*/
|
|
542
|
+
async setLinks(links) {
|
|
543
|
+
if (!this.id) throw new Error("Cannot set links on an unsaved profile");
|
|
544
|
+
return (await this.getProfileLinkCollection()).replaceForProfile(this.id, links);
|
|
545
|
+
}
|
|
513
546
|
async getProfileAssetCollection() {
|
|
514
|
-
const { ProfileAssetCollection } = await import("./ProfileAssetCollection-
|
|
547
|
+
const { ProfileAssetCollection } = await import("./ProfileAssetCollection-B9hacUJa.js").then((n) => n.n);
|
|
515
548
|
return ProfileAssetCollection.create({ db: this.db });
|
|
516
549
|
}
|
|
517
550
|
async getAssets(relationship) {
|
|
@@ -521,6 +554,7 @@ var Profile = class extends SmrtObject {
|
|
|
521
554
|
}
|
|
522
555
|
async addAsset(asset, relationship = "attachment", sortOrder = 0) {
|
|
523
556
|
if (!this.id || !asset.id) throw new Error("Cannot associate unsaved profile or asset");
|
|
557
|
+
await assertAssetLinkable(this.db, asset.id, this.tenantId, "profile");
|
|
524
558
|
assertValidOwnedAssetRelationship(relationship);
|
|
525
559
|
assertValidOwnedAssetSortOrder(sortOrder);
|
|
526
560
|
await (await this.getProfileAssetCollection()).attach(this.id, asset.id, {
|
|
@@ -736,7 +770,7 @@ var Profile = class extends SmrtObject {
|
|
|
736
770
|
* @returns Array of OIDC identity records
|
|
737
771
|
*/
|
|
738
772
|
async getOidcIdentities() {
|
|
739
|
-
const { OidcIdentityCollection } = await import("./OidcIdentityCollection-
|
|
773
|
+
const { OidcIdentityCollection } = await import("./OidcIdentityCollection-TV8VdK69.js").then((n) => n.r);
|
|
740
774
|
return await (await OidcIdentityCollection.create(this.options)).findByProfile(this.id);
|
|
741
775
|
}
|
|
742
776
|
/**
|
|
@@ -749,7 +783,7 @@ var Profile = class extends SmrtObject {
|
|
|
749
783
|
* type, but refuses to create or rebind authority.
|
|
750
784
|
*/
|
|
751
785
|
async linkOidcIdentity(oidcData) {
|
|
752
|
-
const { OidcIdentityCollection } = await import("./OidcIdentityCollection-
|
|
786
|
+
const { OidcIdentityCollection } = await import("./OidcIdentityCollection-TV8VdK69.js").then((n) => n.r);
|
|
753
787
|
return await (await OidcIdentityCollection.create(this.options)).linkToProfile(this, oidcData);
|
|
754
788
|
}
|
|
755
789
|
/**
|
|
@@ -804,6 +838,10 @@ __decorateClass([field({
|
|
|
804
838
|
readonly: true
|
|
805
839
|
})], Profile.prototype, "emailKey", 2);
|
|
806
840
|
__decorateClass([field({ required: true })], Profile.prototype, "name", 2);
|
|
841
|
+
__decorateClass([field({
|
|
842
|
+
type: "boolean",
|
|
843
|
+
default: false
|
|
844
|
+
})], Profile.prototype, "isPublic", 2);
|
|
807
845
|
__decorateClass([oneToMany("ProfileMetadata")], Profile.prototype, "metadata", 2);
|
|
808
846
|
__decorateClass([oneToMany("ProfileRelationship", { foreignKey: "fromProfileId" })], Profile.prototype, "relationshipsFrom", 2);
|
|
809
847
|
__decorateClass([oneToMany("ProfileRelationship", { foreignKey: "toProfileId" })], Profile.prototype, "relationshipsTo", 2);
|
|
@@ -1153,4 +1191,4 @@ function readRequiredString(row, key) {
|
|
|
1153
1191
|
//#endregion
|
|
1154
1192
|
export { smrtProfilesGenerateBioPrompt as _, Profile as a, backfillProfileEmailKeys as c, isOidcProvisioningRaceConflict as d, saveOidcRaceArbiter as f, promptMessageOptions as g, ProfileType_exports as h, OidcProfileEmailReservationCollection as i, coordinateOidcProvisioning as l, ProfileType as m, ProfileCollection as n, OidcProfileEmailReservation as o, normalizeIdentityEmail as p, ProfileCollection_exports as r, PROFILE_EMAIL_KEY_BACKFILL_NAME as s, CanonicalPersonProfileError as t, isOidcAbortedTransactionError as u };
|
|
1155
1193
|
|
|
1156
|
-
//# sourceMappingURL=ProfileCollection-
|
|
1194
|
+
//# sourceMappingURL=ProfileCollection-DuJT7Mum.js.map
|