@happyvertical/smrt-profiles 0.40.70 → 0.41.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.
Files changed (48) hide show
  1. package/AGENTS.md +1 -1
  2. package/dist/chunks/{ApiKey-Br9NGBnr.js → ApiKey-CMm509C4.js} +5 -7
  3. package/dist/chunks/ApiKey-CMm509C4.js.map +1 -0
  4. package/dist/chunks/{ApiKeyCollection-CrTjdMz8.js → ApiKeyCollection-C6p7XgTz.js} +3 -3
  5. package/dist/chunks/{ApiKeyCollection-CrTjdMz8.js.map → ApiKeyCollection-C6p7XgTz.js.map} +1 -1
  6. package/dist/chunks/{AuditLogCollection-BBIEI6BD.js → AuditLogCollection-lJfGFdIQ.js} +2 -2
  7. package/dist/chunks/{AuditLogCollection-BBIEI6BD.js.map → AuditLogCollection-lJfGFdIQ.js.map} +1 -1
  8. package/dist/chunks/{NostrIdentity-DF_a_WZk.js → NostrIdentity-CgdM4ng0.js} +6 -8
  9. package/dist/chunks/{NostrIdentity-DF_a_WZk.js.map → NostrIdentity-CgdM4ng0.js.map} +1 -1
  10. package/dist/chunks/{NostrIdentityCollection-BRYUgt47.js → NostrIdentityCollection-BnHZOsUI.js} +2 -2
  11. package/dist/chunks/{NostrIdentityCollection-BRYUgt47.js.map → NostrIdentityCollection-BnHZOsUI.js.map} +1 -1
  12. package/dist/chunks/{OidcIdentity-Bd68oFpZ.js → OidcIdentity-DANnAbfU.js} +3 -3
  13. package/dist/chunks/{OidcIdentity-Bd68oFpZ.js.map → OidcIdentity-DANnAbfU.js.map} +1 -1
  14. package/dist/chunks/{OidcIdentityCollection-DmllRnCw.js → OidcIdentityCollection-eSM4gzHL.js} +3 -3
  15. package/dist/chunks/{OidcIdentityCollection-DmllRnCw.js.map → OidcIdentityCollection-eSM4gzHL.js.map} +1 -1
  16. package/dist/chunks/{ProfileAssetCollection-DS2Nh2IQ.js → ProfileAssetCollection-A_PpEaDq.js} +3 -3
  17. package/dist/chunks/{ProfileAssetCollection-DS2Nh2IQ.js.map → ProfileAssetCollection-A_PpEaDq.js.map} +1 -1
  18. package/dist/chunks/{ProfileCollection-BOwbNCgG.js → ProfileCollection-BsKhR5Dp.js} +24 -25
  19. package/dist/chunks/ProfileCollection-BsKhR5Dp.js.map +1 -0
  20. package/dist/chunks/{ProfileMetadataCollection-loKVv_pj.js → ProfileMetadataCollection-Ceq-QV-1.js} +2 -2
  21. package/dist/chunks/{ProfileMetadataCollection-loKVv_pj.js.map → ProfileMetadataCollection-Ceq-QV-1.js.map} +1 -1
  22. package/dist/chunks/{ProfileMetafieldCollection-CACMY1hl.js → ProfileMetafieldCollection-CM374Dbh.js} +2 -2
  23. package/dist/chunks/{ProfileMetafieldCollection-CACMY1hl.js.map → ProfileMetafieldCollection-CM374Dbh.js.map} +1 -1
  24. package/dist/chunks/{ProfileRelationshipCollection-CzQKfvMy.js → ProfileRelationshipCollection-DuPcpzp1.js} +5 -5
  25. package/dist/chunks/{ProfileRelationshipCollection-CzQKfvMy.js.map → ProfileRelationshipCollection-DuPcpzp1.js.map} +1 -1
  26. package/dist/chunks/{ProfileRelationshipTermCollection-DzmU48P6.js → ProfileRelationshipTermCollection-Bw32vYB8.js} +2 -2
  27. package/dist/chunks/{ProfileRelationshipTermCollection-DzmU48P6.js.map → ProfileRelationshipTermCollection-Bw32vYB8.js.map} +1 -1
  28. package/dist/chunks/{ProfileRelationshipType-BjoaCt-u.js → ProfileRelationshipType-CZm2X1qY.js} +2 -2
  29. package/dist/chunks/{ProfileRelationshipType-BjoaCt-u.js.map → ProfileRelationshipType-CZm2X1qY.js.map} +1 -1
  30. package/dist/chunks/{ProfileRelationshipTypeCollection-Bh4xJ4-I.js → ProfileRelationshipTypeCollection-CtoeCpp_.js} +3 -3
  31. package/dist/chunks/{ProfileRelationshipTypeCollection-Bh4xJ4-I.js.map → ProfileRelationshipTypeCollection-CtoeCpp_.js.map} +1 -1
  32. package/dist/chunks/{oidcProvisioningPrimitives-BWHPpJde.js → oidcProvisioningPrimitives-Bzv4VTcn.js} +23 -8
  33. package/dist/chunks/oidcProvisioningPrimitives-Bzv4VTcn.js.map +1 -0
  34. package/dist/chunks/{resolveIdentity-F6aErDyh.js → resolveIdentity-DusCiNZo.js} +16 -16
  35. package/dist/chunks/{resolveIdentity-F6aErDyh.js.map → resolveIdentity-DusCiNZo.js.map} +1 -1
  36. package/dist/index.d.ts +12 -3
  37. package/dist/index.js +24 -26
  38. package/dist/index.js.map +1 -1
  39. package/dist/internal/oidc-provisioning.js +2 -2
  40. package/dist/manifest.json +359 -235
  41. package/dist/smrt-knowledge.json +40 -5
  42. package/dist/types.d.ts +8 -2
  43. package/dist/utils.d.ts +8 -2
  44. package/dist/utils.js +4 -4
  45. package/package.json +6 -6
  46. package/dist/chunks/ApiKey-Br9NGBnr.js.map +0 -1
  47. package/dist/chunks/ProfileCollection-BOwbNCgG.js.map +0 -1
  48. package/dist/chunks/oidcProvisioningPrimitives-BWHPpJde.js.map +0 -1
package/AGENTS.md CHANGED
@@ -4,7 +4,7 @@ Central identity system with multi-auth, relationships, controlled metadata, and
4
4
 
5
5
  ## Models
6
6
 
7
- - **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.
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
9
  - **ProfileRelationship**: bidirectional — creating one auto-creates reciprocal inverse. `contextProfileId` for tertiary relationships. `ProfileRelationshipTerm` tracks start/end dates.
10
10
  - **ProfileMetafield**: controlled vocabulary with `validationSchema`. **ProfileMetadata**: per-profile values linked to metafields.
@@ -1,5 +1,5 @@
1
- import { y as __exportAll } from "./NostrIdentity-DF_a_WZk.js";
2
- import { SmrtObject, foreignKey, smrt } from "@happyvertical/smrt-core";
1
+ import { y as __exportAll } from "./NostrIdentity-CgdM4ng0.js";
2
+ import { SmrtObject, field, foreignKey, smrt } from "@happyvertical/smrt-core";
3
3
  import { createHash, randomBytes } from "node:crypto";
4
4
  //#region src/models/ApiKey.ts
5
5
  var ApiKey_exports = /* @__PURE__ */ __exportAll({ ApiKey: () => ApiKey });
@@ -13,9 +13,6 @@ var __decorateClass = (decorators, target, key, kind) => {
13
13
  };
14
14
  var ApiKey = class extends SmrtObject {
15
15
  profileId;
16
- /**
17
- * SHA-256 hash of the API key
18
- */
19
16
  keyHash = "";
20
17
  /**
21
18
  * First 8 characters of the key for identification (e.g., "sk_live_a1b2...")
@@ -116,7 +113,7 @@ var ApiKey = class extends SmrtObject {
116
113
  */
117
114
  static async verify(key, options = {}) {
118
115
  const keyHash = ApiKey.hashKey(key);
119
- const { ApiKeyCollection } = await import("./ApiKeyCollection-CrTjdMz8.js").then((n) => n.n);
116
+ const { ApiKeyCollection } = await import("./ApiKeyCollection-C6p7XgTz.js").then((n) => n.n);
120
117
  const apiKey = await (await ApiKeyCollection.create(options)).findOne({ where: { keyHash } });
121
118
  if (!apiKey) return null;
122
119
  if (!apiKey.isValid()) return null;
@@ -125,6 +122,7 @@ var ApiKey = class extends SmrtObject {
125
122
  }
126
123
  };
127
124
  __decorateClass([foreignKey("Profile", { required: true })], ApiKey.prototype, "profileId", 2);
125
+ __decorateClass([field({ indexed: true })], ApiKey.prototype, "keyHash", 2);
128
126
  ApiKey = __decorateClass([smrt({
129
127
  tableName: "api_keys",
130
128
  api: { include: ["list", "get"] },
@@ -142,4 +140,4 @@ ApiKey = __decorateClass([smrt({
142
140
  //#endregion
143
141
  export { ApiKey_exports as n, ApiKey as t };
144
142
 
145
- //# sourceMappingURL=ApiKey-Br9NGBnr.js.map
143
+ //# sourceMappingURL=ApiKey-CMm509C4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApiKey-CMm509C4.js","names":[],"sources":["../../src/models/ApiKey.ts"],"sourcesContent":["/**\n * ApiKey - API keys for CLI and CI authentication\n *\n * Provides secure API key management with hashing, scopes, and expiration.\n * Keys are stored as hashes - the plaintext is only returned on creation.\n */\n\nimport { createHash, randomBytes } from 'node:crypto';\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 ApiKeyOptions extends SmrtObjectOptions {\n profileId?: string;\n name?: string;\n scopes?: string[];\n expiresAt?: Date | null;\n}\n\nexport interface GenerateKeyResult {\n key: string;\n apiKey: ApiKey;\n}\n\n@smrt({\n tableName: 'api_keys',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n // create/revoke are admin operations invoked in-process via the CLI; only\n // read-only list/get are exposed over HTTP.\n cli: { include: ['list', 'get', 'create', 'revoke'], skipApiCheck: true },\n})\nexport class ApiKey extends SmrtObject {\n /**\n * Link to the Profile (Person, Organization, Bot)\n */\n @foreignKey('Profile', { required: true })\n profileId?: string;\n\n /**\n * SHA-256 hash of the API key.\n *\n * Indexed: `verify()` looks keys up by this hash on every authenticated\n * request (#2364, epic #2382 finding A3).\n */\n @field({ indexed: true })\n keyHash: string = '';\n\n /**\n * First 8 characters of the key for identification (e.g., \"sk_live_a1b2...\")\n */\n keyPrefix: string = '';\n\n /**\n * Human-readable name for the key (e.g., \"CI Bot\", \"Local CLI\")\n */\n name: string = '';\n\n /**\n * Scopes/permissions for this key\n */\n scopes: string[] = [];\n\n /**\n * Last time this key was used\n */\n lastUsedAt: Date | null = null;\n\n /**\n * When this key expires (null = never)\n */\n expiresAt: Date | null = null;\n\n /**\n * When this key was revoked (null = active)\n */\n revokedAt: Date | null = null;\n\n constructor(options: ApiKeyOptions = {}) {\n super(options);\n if (options.profileId) this.profileId = options.profileId;\n if (options.name) this.name = options.name;\n if (options.scopes) this.scopes = options.scopes;\n if (options.expiresAt !== undefined) this.expiresAt = options.expiresAt;\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 * Check if this key is valid (not expired, not revoked)\n */\n isValid(): boolean {\n if (this.revokedAt) return false;\n if (this.expiresAt && this.expiresAt < new Date()) return false;\n return true;\n }\n\n /**\n * Check if this key has a specific scope\n */\n hasScope(scope: string): boolean {\n return this.scopes.includes(scope) || this.scopes.includes('*');\n }\n\n /**\n * Revoke this key\n */\n async revoke(): Promise<void> {\n this.revokedAt = new Date();\n await this.save();\n }\n\n /**\n * Record usage of this key\n */\n async recordUsage(): Promise<void> {\n this.lastUsedAt = new Date();\n await this.save();\n }\n\n /**\n * Hash an API key\n */\n static hashKey(key: string): string {\n return createHash('sha256').update(key).digest('hex');\n }\n\n /**\n * Generate a new API key for a profile\n */\n static async generate(\n profile: Profile,\n options: {\n name: string;\n scopes?: string[];\n expiresAt?: Date | null;\n db?: SmrtObjectOptions['db'];\n },\n ): Promise<GenerateKeyResult> {\n // Generate a random key: sk_live_<32 random bytes as hex>\n const randomPart = randomBytes(32).toString('hex');\n const key = `sk_live_${randomPart}`;\n const keyPrefix = key.substring(0, 16);\n const keyHash = ApiKey.hashKey(key);\n\n const apiKey = new ApiKey({\n db: options.db || profile.options?.db,\n profileId: profile.id as string,\n name: options.name,\n scopes: options.scopes || ['*'],\n expiresAt: options.expiresAt ?? null,\n });\n apiKey.keyHash = keyHash;\n apiKey.keyPrefix = keyPrefix;\n\n await apiKey.initialize();\n await apiKey.save();\n\n return { key, apiKey };\n }\n\n /**\n * Verify an API key and return the ApiKey record if valid\n */\n static async verify(\n key: string,\n options: SmrtObjectOptions = {},\n ): Promise<ApiKey | null> {\n const keyHash = ApiKey.hashKey(key);\n\n const { ApiKeyCollection } = await import(\n '../collections/ApiKeyCollection'\n );\n const collection = await ApiKeyCollection.create(options);\n\n const apiKey = await collection.findOne({\n where: { keyHash },\n });\n\n if (!apiKey) return null;\n if (!apiKey.isValid()) return null;\n\n // Record usage\n await apiKey.recordUsage();\n\n return apiKey;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAqCO,IAAM,SAAN,cAAqB,WAAW;CAKrC;CASA,UAAkB;;;;CAKlB,YAAoB;;;;CAKpB,OAAe;;;;CAKf,SAAmB,CAAC;;;;CAKpB,aAA0B;;;;CAK1B,YAAyB;;;;CAKzB,YAAyB;CAEzB,YAAY,UAAyB,CAAC,GAAG;EACvC,MAAM,OAAO;EACb,IAAI,QAAQ,WAAW,KAAK,YAAY,QAAQ;EAChD,IAAI,QAAQ,MAAM,KAAK,OAAO,QAAQ;EACtC,IAAI,QAAQ,QAAQ,KAAK,SAAS,QAAQ;EAC1C,IAAI,QAAQ,cAAc,KAAA,GAAW,KAAK,YAAY,QAAQ;CAChE;;;;CAKA,MAAM,aAAsC;EAC1C,OAAQ,MAAM,KAAK,WAAW,WAAW;CAC3C;;;;CAKA,UAAmB;EACjB,IAAI,KAAK,WAAW,OAAO;EAC3B,IAAI,KAAK,aAAa,KAAK,4BAAY,IAAI,KAAK,GAAG,OAAO;EAC1D,OAAO;CACT;;;;CAKA,SAAS,OAAwB;EAC/B,OAAO,KAAK,OAAO,SAAS,KAAK,KAAK,KAAK,OAAO,SAAS,GAAG;CAChE;;;;CAKA,MAAM,SAAwB;EAC5B,KAAK,4BAAY,IAAI,KAAK;EAC1B,MAAM,KAAK,KAAK;CAClB;;;;CAKA,MAAM,cAA6B;EACjC,KAAK,6BAAa,IAAI,KAAK;EAC3B,MAAM,KAAK,KAAK;CAClB;;;;CAKA,OAAO,QAAQ,KAAqB;EAClC,OAAO,WAAW,QAAQ,CAAA,CAAE,OAAO,GAAG,CAAA,CAAE,OAAO,KAAK;CACtD;;;;CAKA,aAAa,SACX,SACA,SAM4B;EAG5B,MAAM,MAAM,WADO,YAAY,EAAE,CAAA,CAAE,SAAS,KACrB;EACvB,MAAM,YAAY,IAAI,UAAU,GAAG,EAAE;EACrC,MAAM,UAAU,OAAO,QAAQ,GAAG;EAElC,MAAM,SAAS,IAAI,OAAO;GACxB,IAAI,QAAQ,MAAM,QAAQ,SAAS;GACnC,WAAW,QAAQ;GACnB,MAAM,QAAQ;GACd,QAAQ,QAAQ,UAAU,CAAC,GAAG;GAC9B,WAAW,QAAQ,aAAa;EAClC,CAAC;EACD,OAAO,UAAU;EACjB,OAAO,YAAY;EAEnB,MAAM,OAAO,WAAW;EACxB,MAAM,OAAO,KAAK;EAElB,OAAO;GAAE;GAAK;EAAO;CACvB;;;;CAKA,aAAa,OACX,KACA,UAA6B,CAAC,GACN;EACxB,MAAM,UAAU,OAAO,QAAQ,GAAG;EAElC,MAAM,EAAE,qBAAqB,MAAM,OACjC,iCAAA,CAAA,MAAA,MAAA,EAAA,CAAA;EAIF,MAAM,SAAS,OAAM,MAFI,iBAAiB,OAAO,OAAO,EAAA,CAExB,QAAQ,EACtC,OAAO,EAAE,QAAQ,EACnB,CAAC;EAED,IAAI,CAAC,QAAQ,OAAO;EACpB,IAAI,CAAC,OAAO,QAAQ,GAAG,OAAO;EAG9B,MAAM,OAAO,YAAY;EAEzB,OAAO;CACT;AACF;AA3JE,gBAAA,CADC,WAAW,WAAW,EAAE,UAAU,KAAK,CAAC,CAAA,GAJ9B,OAKX,WAAA,aAAA,CAAA;AASA,gBAAA,CADC,MAAM,EAAE,SAAS,KAAK,CAAC,CAAA,GAbb,OAcX,WAAA,WAAA,CAAA;AAdW,SAAN,gBAAA,CARN,KAAK;CACJ,WAAW;CACX,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAGhC,KAAK;EAAE,SAAS;GAAC;GAAQ;GAAO;GAAU;EAAQ;EAAG,cAAc;CAAK;AAC1E,CAAC,CAAA,GACY,MAAA"}
@@ -1,5 +1,5 @@
1
- import { y as __exportAll } from "./NostrIdentity-DF_a_WZk.js";
2
- import { t as ApiKey } from "./ApiKey-Br9NGBnr.js";
1
+ import { y as __exportAll } from "./NostrIdentity-CgdM4ng0.js";
2
+ import { t as ApiKey } from "./ApiKey-CMm509C4.js";
3
3
  import { SmrtCollection } from "@happyvertical/smrt-core";
4
4
  //#region src/collections/ApiKeyCollection.ts
5
5
  var ApiKeyCollection_exports = /* @__PURE__ */ __exportAll({ ApiKeyCollection: () => ApiKeyCollection });
@@ -80,4 +80,4 @@ var ApiKeyCollection = class extends SmrtCollection {
80
80
  //#endregion
81
81
  export { ApiKeyCollection_exports as n, ApiKeyCollection as t };
82
82
 
83
- //# sourceMappingURL=ApiKeyCollection-CrTjdMz8.js.map
83
+ //# sourceMappingURL=ApiKeyCollection-C6p7XgTz.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ApiKeyCollection-CrTjdMz8.js","names":[],"sources":["../../src/collections/ApiKeyCollection.ts"],"sourcesContent":["/**\n * ApiKeyCollection - Collection for managing API keys\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ApiKey, type GenerateKeyResult } from '../models/ApiKey';\nimport type { Profile } from '../models/Profile';\n\nexport class ApiKeyCollection extends SmrtCollection<ApiKey> {\n static readonly _itemClass = ApiKey;\n\n /**\n * Find all keys for a profile\n */\n async findByProfile(profileId: string): Promise<ApiKey[]> {\n return await this.list({\n where: { profileId },\n });\n }\n\n /**\n * Find active (non-revoked, non-expired) keys for a profile\n */\n async findActiveByProfile(profileId: string): Promise<ApiKey[]> {\n const keys = await this.findByProfile(profileId);\n return keys.filter((key) => key.isValid());\n }\n\n /**\n * Find key by hash\n */\n async findByHash(keyHash: string): Promise<ApiKey | null> {\n return await this.findOne({\n where: { keyHash },\n });\n }\n\n /**\n * Verify an API key string and return the record if valid\n */\n async verify(key: string): Promise<ApiKey | null> {\n const keyHash = ApiKey.hashKey(key);\n const apiKey = await this.findByHash(keyHash);\n\n if (!apiKey) return null;\n if (!apiKey.isValid()) return null;\n\n // Record usage\n await apiKey.recordUsage();\n\n return apiKey;\n }\n\n /**\n * Generate a new key for a profile\n */\n async generateForProfile(\n profile: Profile,\n options: {\n name: string;\n scopes?: string[];\n expiresAt?: Date | null;\n },\n ): Promise<GenerateKeyResult> {\n return await ApiKey.generate(profile, {\n ...options,\n db: this.options.db,\n });\n }\n\n /**\n * Revoke all keys for a profile\n */\n async revokeAllForProfile(profileId: string): Promise<number> {\n const keys = await this.findActiveByProfile(profileId);\n for (const key of keys) {\n await key.revoke();\n }\n return keys.length;\n }\n\n /**\n * Revoke a specific key by prefix\n */\n async revokeByPrefix(keyPrefix: string): Promise<boolean> {\n const key = await this.findOne({\n where: { keyPrefix },\n });\n\n if (key?.isValid()) {\n await key.revoke();\n return true;\n }\n return false;\n }\n\n /**\n * Clean up expired keys (soft delete by setting revokedAt)\n */\n async cleanupExpired(): Promise<number> {\n const now = new Date();\n const allKeys = await this.list({});\n let cleaned = 0;\n\n for (const key of allKeys) {\n if (key.expiresAt && key.expiresAt < now && !key.revokedAt) {\n key.revokedAt = now;\n await key.save();\n cleaned++;\n }\n }\n\n return cleaned;\n }\n}\n"],"mappings":";;;;;AAQO,IAAM,mBAAN,cAA+B,eAAuB;CAC3D,OAAgB,aAAa;;;;CAK7B,MAAM,cAAc,WAAsC;EACxD,OAAO,MAAM,KAAK,KAAK,EACrB,OAAO,EAAE,UAAU,EACrB,CAAC;CACH;;;;CAKA,MAAM,oBAAoB,WAAsC;EAE9D,QAAO,MADY,KAAK,cAAc,SAAS,EAAA,CACnC,QAAQ,QAAQ,IAAI,QAAQ,CAAC;CAC3C;;;;CAKA,MAAM,WAAW,SAAyC;EACxD,OAAO,MAAM,KAAK,QAAQ,EACxB,OAAO,EAAE,QAAQ,EACnB,CAAC;CACH;;;;CAKA,MAAM,OAAO,KAAqC;EAChD,MAAM,UAAU,OAAO,QAAQ,GAAG;EAClC,MAAM,SAAS,MAAM,KAAK,WAAW,OAAO;EAE5C,IAAI,CAAC,QAAQ,OAAO;EACpB,IAAI,CAAC,OAAO,QAAQ,GAAG,OAAO;EAG9B,MAAM,OAAO,YAAY;EAEzB,OAAO;CACT;;;;CAKA,MAAM,mBACJ,SACA,SAK4B;EAC5B,OAAO,MAAM,OAAO,SAAS,SAAS;GACpC,GAAG;GACH,IAAI,KAAK,QAAQ;EACnB,CAAC;CACH;;;;CAKA,MAAM,oBAAoB,WAAoC;EAC5D,MAAM,OAAO,MAAM,KAAK,oBAAoB,SAAS;EACrD,KAAA,MAAW,OAAO,MAChB,MAAM,IAAI,OAAO;EAEnB,OAAO,KAAK;CACd;;;;CAKA,MAAM,eAAe,WAAqC;EACxD,MAAM,MAAM,MAAM,KAAK,QAAQ,EAC7B,OAAO,EAAE,UAAU,EACrB,CAAC;EAED,IAAI,KAAK,QAAQ,GAAG;GAClB,MAAM,IAAI,OAAO;GACjB,OAAO;EACT;EACA,OAAO;CACT;;;;CAKA,MAAM,iBAAkC;EACtC,MAAM,sBAAM,IAAI,KAAK;EACrB,MAAM,UAAU,MAAM,KAAK,KAAK,CAAC,CAAC;EAClC,IAAI,UAAU;EAEd,KAAA,MAAW,OAAO,SAChB,IAAI,IAAI,aAAa,IAAI,YAAY,OAAO,CAAC,IAAI,WAAW;GAC1D,IAAI,YAAY;GAChB,MAAM,IAAI,KAAK;GACf;EACF;EAGF,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"ApiKeyCollection-C6p7XgTz.js","names":[],"sources":["../../src/collections/ApiKeyCollection.ts"],"sourcesContent":["/**\n * ApiKeyCollection - Collection for managing API keys\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ApiKey, type GenerateKeyResult } from '../models/ApiKey';\nimport type { Profile } from '../models/Profile';\n\nexport class ApiKeyCollection extends SmrtCollection<ApiKey> {\n static readonly _itemClass = ApiKey;\n\n /**\n * Find all keys for a profile\n */\n async findByProfile(profileId: string): Promise<ApiKey[]> {\n return await this.list({\n where: { profileId },\n });\n }\n\n /**\n * Find active (non-revoked, non-expired) keys for a profile\n */\n async findActiveByProfile(profileId: string): Promise<ApiKey[]> {\n const keys = await this.findByProfile(profileId);\n return keys.filter((key) => key.isValid());\n }\n\n /**\n * Find key by hash\n */\n async findByHash(keyHash: string): Promise<ApiKey | null> {\n return await this.findOne({\n where: { keyHash },\n });\n }\n\n /**\n * Verify an API key string and return the record if valid\n */\n async verify(key: string): Promise<ApiKey | null> {\n const keyHash = ApiKey.hashKey(key);\n const apiKey = await this.findByHash(keyHash);\n\n if (!apiKey) return null;\n if (!apiKey.isValid()) return null;\n\n // Record usage\n await apiKey.recordUsage();\n\n return apiKey;\n }\n\n /**\n * Generate a new key for a profile\n */\n async generateForProfile(\n profile: Profile,\n options: {\n name: string;\n scopes?: string[];\n expiresAt?: Date | null;\n },\n ): Promise<GenerateKeyResult> {\n return await ApiKey.generate(profile, {\n ...options,\n db: this.options.db,\n });\n }\n\n /**\n * Revoke all keys for a profile\n */\n async revokeAllForProfile(profileId: string): Promise<number> {\n const keys = await this.findActiveByProfile(profileId);\n for (const key of keys) {\n await key.revoke();\n }\n return keys.length;\n }\n\n /**\n * Revoke a specific key by prefix\n */\n async revokeByPrefix(keyPrefix: string): Promise<boolean> {\n const key = await this.findOne({\n where: { keyPrefix },\n });\n\n if (key?.isValid()) {\n await key.revoke();\n return true;\n }\n return false;\n }\n\n /**\n * Clean up expired keys (soft delete by setting revokedAt)\n */\n async cleanupExpired(): Promise<number> {\n const now = new Date();\n const allKeys = await this.list({});\n let cleaned = 0;\n\n for (const key of allKeys) {\n if (key.expiresAt && key.expiresAt < now && !key.revokedAt) {\n key.revokedAt = now;\n await key.save();\n cleaned++;\n }\n }\n\n return cleaned;\n }\n}\n"],"mappings":";;;;;AAQO,IAAM,mBAAN,cAA+B,eAAuB;CAC3D,OAAgB,aAAa;;;;CAK7B,MAAM,cAAc,WAAsC;EACxD,OAAO,MAAM,KAAK,KAAK,EACrB,OAAO,EAAE,UAAU,EACrB,CAAC;CACH;;;;CAKA,MAAM,oBAAoB,WAAsC;EAE9D,QAAO,MADY,KAAK,cAAc,SAAS,EAAA,CACnC,QAAQ,QAAQ,IAAI,QAAQ,CAAC;CAC3C;;;;CAKA,MAAM,WAAW,SAAyC;EACxD,OAAO,MAAM,KAAK,QAAQ,EACxB,OAAO,EAAE,QAAQ,EACnB,CAAC;CACH;;;;CAKA,MAAM,OAAO,KAAqC;EAChD,MAAM,UAAU,OAAO,QAAQ,GAAG;EAClC,MAAM,SAAS,MAAM,KAAK,WAAW,OAAO;EAE5C,IAAI,CAAC,QAAQ,OAAO;EACpB,IAAI,CAAC,OAAO,QAAQ,GAAG,OAAO;EAG9B,MAAM,OAAO,YAAY;EAEzB,OAAO;CACT;;;;CAKA,MAAM,mBACJ,SACA,SAK4B;EAC5B,OAAO,MAAM,OAAO,SAAS,SAAS;GACpC,GAAG;GACH,IAAI,KAAK,QAAQ;EACnB,CAAC;CACH;;;;CAKA,MAAM,oBAAoB,WAAoC;EAC5D,MAAM,OAAO,MAAM,KAAK,oBAAoB,SAAS;EACrD,KAAA,MAAW,OAAO,MAChB,MAAM,IAAI,OAAO;EAEnB,OAAO,KAAK;CACd;;;;CAKA,MAAM,eAAe,WAAqC;EACxD,MAAM,MAAM,MAAM,KAAK,QAAQ,EAC7B,OAAO,EAAE,UAAU,EACrB,CAAC;EAED,IAAI,KAAK,QAAQ,GAAG;GAClB,MAAM,IAAI,OAAO;GACjB,OAAO;EACT;EACA,OAAO;CACT;;;;CAKA,MAAM,iBAAkC;EACtC,MAAM,sBAAM,IAAI,KAAK;EACrB,MAAM,UAAU,MAAM,KAAK,KAAK,CAAC,CAAC;EAClC,IAAI,UAAU;EAEd,KAAA,MAAW,OAAO,SAChB,IAAI,IAAI,aAAa,IAAI,YAAY,OAAO,CAAC,IAAI,WAAW;GAC1D,IAAI,YAAY;GAChB,MAAM,IAAI,KAAK;GACf;EACF;EAGF,OAAO;CACT;AACF"}
@@ -1,4 +1,4 @@
1
- import { y as __exportAll } from "./NostrIdentity-DF_a_WZk.js";
1
+ import { y as __exportAll } from "./NostrIdentity-CgdM4ng0.js";
2
2
  import { SmrtCollection, SmrtObject, foreignKey, smrt } from "@happyvertical/smrt-core";
3
3
  import { TenantScoped, tenantId } from "@happyvertical/smrt-tenancy";
4
4
  //#region src/models/AuditLog.ts
@@ -188,4 +188,4 @@ var AuditLogCollection = class extends SmrtCollection {
188
188
  //#endregion
189
189
  export { AuditLogCollection_exports as n, AuditLog as r, AuditLogCollection as t };
190
190
 
191
- //# sourceMappingURL=AuditLogCollection-BBIEI6BD.js.map
191
+ //# sourceMappingURL=AuditLogCollection-lJfGFdIQ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AuditLogCollection-BBIEI6BD.js","names":[],"sources":["../../src/models/AuditLog.ts","../../src/collections/AuditLogCollection.ts"],"sourcesContent":["/**\n * AuditLog - Tracks all actions for full audit trail\n *\n * Records who did what, when, and how (web, CLI, CI, webhook, MCP).\n * Supports pass-through identity for CI workflows.\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { Profile } from './Profile';\n\nexport type AuditSource = 'web' | 'cli' | 'ci' | 'webhook' | 'mcp';\n\nexport interface AuditLogOptions extends SmrtObjectOptions {\n profileId?: string;\n action?: string;\n resourceType?: string;\n resourceId?: string;\n source?: AuditSource;\n metadata?: Record<string, unknown>;\n onBehalfOfId?: string | null;\n tenantId?: string | null;\n}\n\n@TenantScoped({ mode: 'optional', allowSuperAdminBypass: true })\n@smrt({\n tableName: 'audit_logs',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list'] },\n})\nexport class AuditLog extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * The profile who performed the action\n */\n @foreignKey('Profile', { required: true })\n profileId?: string;\n\n /**\n * Action performed (e.g., 'issue.update', 'feedback.incorporate')\n */\n action: string = '';\n\n /**\n * Type of resource affected (e.g., 'Issue', 'Repository')\n */\n resourceType: string = '';\n\n /**\n * ID of the affected resource\n */\n resourceId: string = '';\n\n /**\n * Source of the action\n */\n source: AuditSource = 'web';\n\n /**\n * Additional context (CI run ID, request ID, etc.)\n */\n metadata: Record<string, unknown> = {};\n\n /**\n * For pass-through identity in CI - the actual person who triggered\n */\n @foreignKey('Profile', { nullable: true })\n onBehalfOfId?: string | null;\n\n constructor(options: AuditLogOptions = {}) {\n super(options);\n if (options.profileId) this.profileId = options.profileId;\n if (options.action) this.action = options.action;\n if (options.resourceType) this.resourceType = options.resourceType;\n if (options.resourceId) this.resourceId = options.resourceId;\n if (options.source) this.source = options.source;\n if (options.metadata) this.metadata = options.metadata;\n if (options.onBehalfOfId !== undefined)\n this.onBehalfOfId = options.onBehalfOfId;\n }\n\n /**\n * Get the profile who performed the action\n */\n async getProfile(): Promise<Profile | null> {\n return (await this.getRelated('profileId')) as Profile | null;\n }\n\n /**\n * Get the profile on whose behalf the action was performed (if any)\n */\n async getOnBehalfOf(): Promise<Profile | null> {\n if (!this.onBehalfOfId) return null;\n return (await this.getRelated('onBehalfOfId')) as Profile | null;\n }\n\n /**\n * Get the effective actor (onBehalfOf if set, otherwise profile)\n */\n async getEffectiveActor(): Promise<Profile | null> {\n const onBehalfOf = await this.getOnBehalfOf();\n if (onBehalfOf) return onBehalfOf;\n return await this.getProfile();\n }\n\n /**\n * Create an audit log entry\n */\n static async record(\n options: AuditLogOptions & {\n profile: Profile;\n onBehalfOf?: Profile | null;\n },\n ): Promise<AuditLog> {\n const log = new AuditLog({\n ...options,\n profileId: options.profile.id as string,\n onBehalfOfId: options.onBehalfOf?.id as string | undefined,\n });\n await log.initialize();\n await log.save();\n return log;\n }\n}\n","/**\n * AuditLogCollection - Collection for querying audit logs\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AuditLog, type AuditSource } from '../models/AuditLog';\nimport type { Profile } from '../models/Profile';\n\nexport class AuditLogCollection extends SmrtCollection<AuditLog> {\n static readonly _itemClass = AuditLog;\n\n /**\n * Find logs for a profile (actions they performed)\n */\n async findByProfile(profileId: string): Promise<AuditLog[]> {\n return await this.list({\n where: { profileId },\n orderBy: 'createdAt DESC',\n });\n }\n\n /**\n * Find logs for a resource\n */\n async findByResource(\n resourceType: string,\n resourceId: string,\n ): Promise<AuditLog[]> {\n return await this.list({\n where: { resourceType, resourceId },\n orderBy: 'createdAt DESC',\n });\n }\n\n /**\n * Find logs by action type\n */\n async findByAction(action: string): Promise<AuditLog[]> {\n return await this.list({\n where: { action },\n orderBy: 'createdAt DESC',\n });\n }\n\n /**\n * Find logs by source\n */\n async findBySource(source: AuditSource): Promise<AuditLog[]> {\n return await this.list({\n where: { source },\n orderBy: 'createdAt DESC',\n });\n }\n\n /**\n * Find logs where actions were performed on behalf of someone\n */\n async findOnBehalfOf(profileId: string): Promise<AuditLog[]> {\n return await this.list({\n where: { onBehalfOfId: profileId },\n orderBy: 'createdAt DESC',\n });\n }\n\n /**\n * Record a new audit log entry\n */\n async record(options: {\n profile: Profile;\n action: string;\n resourceType: string;\n resourceId: string;\n source?: AuditSource;\n metadata?: Record<string, unknown>;\n onBehalfOf?: Profile | null;\n }): Promise<AuditLog> {\n const log = new AuditLog({\n ...this.options,\n profileId: options.profile.id as string,\n action: options.action,\n resourceType: options.resourceType,\n resourceId: options.resourceId,\n source: options.source || 'web',\n metadata: options.metadata || {},\n onBehalfOfId: options.onBehalfOf?.id as string | undefined,\n });\n await log.initialize();\n await log.save();\n return log;\n }\n\n /**\n * Get recent activity for a profile\n */\n async getRecentActivity(\n profileId: string,\n limit: number = 50,\n ): Promise<AuditLog[]> {\n return await this.list({\n where: { profileId },\n orderBy: 'createdAt DESC',\n limit,\n });\n }\n\n /**\n * Get activity timeline for a resource\n */\n async getResourceTimeline(\n resourceType: string,\n resourceId: string,\n ): Promise<AuditLog[]> {\n return await this.list({\n where: { resourceType, resourceId },\n orderBy: 'createdAt ASC',\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AuditLogCollection-lJfGFdIQ.js","names":[],"sources":["../../src/models/AuditLog.ts","../../src/collections/AuditLogCollection.ts"],"sourcesContent":["/**\n * AuditLog - Tracks all actions for full audit trail\n *\n * Records who did what, when, and how (web, CLI, CI, webhook, MCP).\n * Supports pass-through identity for CI workflows.\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { Profile } from './Profile';\n\nexport type AuditSource = 'web' | 'cli' | 'ci' | 'webhook' | 'mcp';\n\nexport interface AuditLogOptions extends SmrtObjectOptions {\n profileId?: string;\n action?: string;\n resourceType?: string;\n resourceId?: string;\n source?: AuditSource;\n metadata?: Record<string, unknown>;\n onBehalfOfId?: string | null;\n tenantId?: string | null;\n}\n\n@TenantScoped({ mode: 'optional', allowSuperAdminBypass: true })\n@smrt({\n tableName: 'audit_logs',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list'] },\n})\nexport class AuditLog extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * The profile who performed the action\n */\n @foreignKey('Profile', { required: true })\n profileId?: string;\n\n /**\n * Action performed (e.g., 'issue.update', 'feedback.incorporate')\n */\n action: string = '';\n\n /**\n * Type of resource affected (e.g., 'Issue', 'Repository')\n */\n resourceType: string = '';\n\n /**\n * ID of the affected resource\n */\n resourceId: string = '';\n\n /**\n * Source of the action\n */\n source: AuditSource = 'web';\n\n /**\n * Additional context (CI run ID, request ID, etc.)\n */\n metadata: Record<string, unknown> = {};\n\n /**\n * For pass-through identity in CI - the actual person who triggered\n */\n @foreignKey('Profile', { nullable: true })\n onBehalfOfId?: string | null;\n\n constructor(options: AuditLogOptions = {}) {\n super(options);\n if (options.profileId) this.profileId = options.profileId;\n if (options.action) this.action = options.action;\n if (options.resourceType) this.resourceType = options.resourceType;\n if (options.resourceId) this.resourceId = options.resourceId;\n if (options.source) this.source = options.source;\n if (options.metadata) this.metadata = options.metadata;\n if (options.onBehalfOfId !== undefined)\n this.onBehalfOfId = options.onBehalfOfId;\n }\n\n /**\n * Get the profile who performed the action\n */\n async getProfile(): Promise<Profile | null> {\n return (await this.getRelated('profileId')) as Profile | null;\n }\n\n /**\n * Get the profile on whose behalf the action was performed (if any)\n */\n async getOnBehalfOf(): Promise<Profile | null> {\n if (!this.onBehalfOfId) return null;\n return (await this.getRelated('onBehalfOfId')) as Profile | null;\n }\n\n /**\n * Get the effective actor (onBehalfOf if set, otherwise profile)\n */\n async getEffectiveActor(): Promise<Profile | null> {\n const onBehalfOf = await this.getOnBehalfOf();\n if (onBehalfOf) return onBehalfOf;\n return await this.getProfile();\n }\n\n /**\n * Create an audit log entry\n */\n static async record(\n options: AuditLogOptions & {\n profile: Profile;\n onBehalfOf?: Profile | null;\n },\n ): Promise<AuditLog> {\n const log = new AuditLog({\n ...options,\n profileId: options.profile.id as string,\n onBehalfOfId: options.onBehalfOf?.id as string | undefined,\n });\n await log.initialize();\n await log.save();\n return log;\n }\n}\n","/**\n * AuditLogCollection - Collection for querying audit logs\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AuditLog, type AuditSource } from '../models/AuditLog';\nimport type { Profile } from '../models/Profile';\n\nexport class AuditLogCollection extends SmrtCollection<AuditLog> {\n static readonly _itemClass = AuditLog;\n\n /**\n * Find logs for a profile (actions they performed)\n */\n async findByProfile(profileId: string): Promise<AuditLog[]> {\n return await this.list({\n where: { profileId },\n orderBy: 'createdAt DESC',\n });\n }\n\n /**\n * Find logs for a resource\n */\n async findByResource(\n resourceType: string,\n resourceId: string,\n ): Promise<AuditLog[]> {\n return await this.list({\n where: { resourceType, resourceId },\n orderBy: 'createdAt DESC',\n });\n }\n\n /**\n * Find logs by action type\n */\n async findByAction(action: string): Promise<AuditLog[]> {\n return await this.list({\n where: { action },\n orderBy: 'createdAt DESC',\n });\n }\n\n /**\n * Find logs by source\n */\n async findBySource(source: AuditSource): Promise<AuditLog[]> {\n return await this.list({\n where: { source },\n orderBy: 'createdAt DESC',\n });\n }\n\n /**\n * Find logs where actions were performed on behalf of someone\n */\n async findOnBehalfOf(profileId: string): Promise<AuditLog[]> {\n return await this.list({\n where: { onBehalfOfId: profileId },\n orderBy: 'createdAt DESC',\n });\n }\n\n /**\n * Record a new audit log entry\n */\n async record(options: {\n profile: Profile;\n action: string;\n resourceType: string;\n resourceId: string;\n source?: AuditSource;\n metadata?: Record<string, unknown>;\n onBehalfOf?: Profile | null;\n }): Promise<AuditLog> {\n const log = new AuditLog({\n ...this.options,\n profileId: options.profile.id as string,\n action: options.action,\n resourceType: options.resourceType,\n resourceId: options.resourceId,\n source: options.source || 'web',\n metadata: options.metadata || {},\n onBehalfOfId: options.onBehalfOf?.id as string | undefined,\n });\n await log.initialize();\n await log.save();\n return log;\n }\n\n /**\n * Get recent activity for a profile\n */\n async getRecentActivity(\n profileId: string,\n limit: number = 50,\n ): Promise<AuditLog[]> {\n return await this.list({\n where: { profileId },\n orderBy: 'createdAt DESC',\n limit,\n });\n }\n\n /**\n * Get activity timeline for a resource\n */\n async getResourceTimeline(\n resourceType: string,\n resourceId: string,\n ): Promise<AuditLog[]> {\n return await this.list({\n where: { resourceType, resourceId },\n orderBy: 'createdAt ASC',\n });\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { SmrtObject, foreignKey, smrt } from "@happyvertical/smrt-core";
1
+ import { SmrtObject, field, foreignKey, smrt } from "@happyvertical/smrt-core";
2
2
  import { createCipheriv, createDecipheriv, createHash, hkdfSync, randomBytes } from "node:crypto";
3
3
  //#region \0rolldown/runtime.js
4
4
  var __defProp$1 = Object.defineProperty;
@@ -3010,9 +3010,6 @@ var __decorateClass = (decorators, target, key, kind) => {
3010
3010
  };
3011
3011
  var NostrIdentity = class extends SmrtObject {
3012
3012
  profileId;
3013
- /**
3014
- * Hex-encoded secp256k1 public key (64 characters)
3015
- */
3016
3013
  pubkey = "";
3017
3014
  /**
3018
3015
  * AES-256-GCM encrypted private key (base64-encoded ciphertext)
@@ -3064,21 +3061,21 @@ var NostrIdentity = class extends SmrtObject {
3064
3061
  * Find identity by public key
3065
3062
  */
3066
3063
  static async findByPubkey(pubkey, options = {}) {
3067
- const { NostrIdentityCollection } = await import("./NostrIdentityCollection-BRYUgt47.js").then((n) => n.n);
3064
+ const { NostrIdentityCollection } = await import("./NostrIdentityCollection-BnHZOsUI.js").then((n) => n.n);
3068
3065
  return await (await NostrIdentityCollection.create(options)).findOne({ where: { pubkey } });
3069
3066
  }
3070
3067
  /**
3071
3068
  * Find identity by email
3072
3069
  */
3073
3070
  static async findByEmail(email, options = {}) {
3074
- const { NostrIdentityCollection } = await import("./NostrIdentityCollection-BRYUgt47.js").then((n) => n.n);
3071
+ const { NostrIdentityCollection } = await import("./NostrIdentityCollection-BnHZOsUI.js").then((n) => n.n);
3075
3072
  return await (await NostrIdentityCollection.create(options)).findOne({ where: { email: email.toLowerCase() } });
3076
3073
  }
3077
3074
  /**
3078
3075
  * Find identity by NIP-05 username
3079
3076
  */
3080
3077
  static async findByNip05(username, options = {}) {
3081
- const { NostrIdentityCollection } = await import("./NostrIdentityCollection-BRYUgt47.js").then((n) => n.n);
3078
+ const { NostrIdentityCollection } = await import("./NostrIdentityCollection-BnHZOsUI.js").then((n) => n.n);
3082
3079
  return await (await NostrIdentityCollection.create(options)).findOne({ where: { nip05Username: username.toLowerCase() } });
3083
3080
  }
3084
3081
  /**
@@ -3136,6 +3133,7 @@ var NostrIdentity = class extends SmrtObject {
3136
3133
  }
3137
3134
  };
3138
3135
  __decorateClass([foreignKey("Profile", { required: true })], NostrIdentity.prototype, "profileId", 2);
3136
+ __decorateClass([field({ indexed: true })], NostrIdentity.prototype, "pubkey", 2);
3139
3137
  NostrIdentity = __decorateClass([smrt({
3140
3138
  tableName: "nostr_identities",
3141
3139
  api: { include: ["list", "get"] },
@@ -3145,4 +3143,4 @@ NostrIdentity = __decorateClass([smrt({
3145
3143
  //#endregion
3146
3144
  export { verifyAuthEvent as _, decryptPrivkey as a, generateNostrKeypair as c, isValidPubkey as d, npubToPubkey as f, signEvent as g, pubkeyToNpub as h, createAuthEvent as i, getPublicKey as l, privkeyToNsec as m, NostrIdentity_exports as n, deriveEncryptionKey as o, nsecToPrivkey as p, computeEventId as r, encryptPrivkey as s, NostrIdentity as t, isValidPrivkey as u, verifyNostrSignature as v, __exportAll as y };
3147
3145
 
3148
- //# sourceMappingURL=NostrIdentity-DF_a_WZk.js.map
3146
+ //# sourceMappingURL=NostrIdentity-CgdM4ng0.js.map