@happyvertical/smrt-cli 0.53.3 → 0.53.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.
Files changed (76) hide show
  1. package/AGENTS.md +3 -0
  2. package/README.md +64 -0
  3. package/agents/app-commands.md +118 -0
  4. package/bin/smrt-web.mjs +19 -0
  5. package/bin/smrt.js +18 -12
  6. package/dist/{ApiKey-BZmbOTnL-DchyhIPA.js → ApiKey-BZmbOTnL-BbZupUZ3.js} +2 -2
  7. package/dist/ApiKeyCollection-Bfb_nw81-DYAZwx0O.js +87 -0
  8. package/dist/AuditLogCollection-B1YonCAP-Bkj2Ezon.js +193 -0
  9. package/dist/{NostrIdentity-n70E0vck-CLGPdJ1W.js → NostrIdentity-n70E0vck-Di3_eNCq.js} +4 -4
  10. package/dist/NostrIdentityCollection-Ca74dkaL-DvjOg9Xy.js +127 -0
  11. package/dist/{OidcIdentity-Cb1bxreM-DPBTZddF.js → OidcIdentity-Cb1bxreM-LXmC7uAf.js} +2 -2
  12. package/dist/OidcIdentityCollection-TV8VdK69-Cv1ZTeuk.js +86 -0
  13. package/dist/ProfileAssetCollection-B9hacUJa-CPuaWLk4.js +101 -0
  14. package/dist/{ProfileCollection-DuJT7Mum-BUYQQ9xZ.js → ProfileCollection-DuJT7Mum-nXbHrPpX.js} +31 -31
  15. package/dist/ProfileLinkCollection-4uNURz32-CbKZYmYm.js +233 -0
  16. package/dist/ProfileMetadataCollection-DQhbfEfa-_1rQCLTR.js +108 -0
  17. package/dist/ProfileMetafieldCollection-CoBuM_5G-CPjW3l5r.js +145 -0
  18. package/dist/ProfileRelationshipCollection-DUVgaIqf-RCDqYz6O.js +161 -0
  19. package/dist/ProfileRelationshipTermCollection-DtA9OM23-BoGhQBSQ.js +107 -0
  20. package/dist/{ProfileRelationshipType-BRiRka6t-xzRfZRFG.js → ProfileRelationshipType-BRiRka6t-Dezjv5jP.js} +1 -1
  21. package/dist/ProfileRelationshipTypeCollection-BdXwd7Kf-HoTmV1q-.js +57 -0
  22. package/dist/app/cli.d.ts +51 -0
  23. package/dist/app/cli.d.ts.map +1 -0
  24. package/dist/app/errors.d.ts +55 -0
  25. package/dist/app/errors.d.ts.map +1 -0
  26. package/dist/app/identity.d.ts +24 -0
  27. package/dist/app/identity.d.ts.map +1 -0
  28. package/dist/app/index.d.ts +20 -0
  29. package/dist/app/index.d.ts.map +1 -0
  30. package/dist/app/launchers.d.ts +51 -0
  31. package/dist/app/launchers.d.ts.map +1 -0
  32. package/dist/app/operations.d.ts +30 -0
  33. package/dist/app/operations.d.ts.map +1 -0
  34. package/dist/app/portability-assets.d.ts +151 -0
  35. package/dist/app/portability-assets.d.ts.map +1 -0
  36. package/dist/app/portability.d.ts +117 -0
  37. package/dist/app/portability.d.ts.map +1 -0
  38. package/dist/app/process-record.d.ts +54 -0
  39. package/dist/app/process-record.d.ts.map +1 -0
  40. package/dist/app/runtime.d.ts +98 -0
  41. package/dist/app/runtime.d.ts.map +1 -0
  42. package/dist/app/tokens.d.ts +21 -0
  43. package/dist/app/tokens.d.ts.map +1 -0
  44. package/dist/app.d.ts +1 -0
  45. package/dist/app.js +4 -0
  46. package/dist/cli-OZsH5cdL.js +1602 -0
  47. package/dist/cli-generator.d.ts.map +1 -1
  48. package/dist/commands/init.d.ts.map +1 -1
  49. package/dist/commands/utilities.d.ts.map +1 -1
  50. package/dist/{commands-B8N38deG.js → commands-UXK2F_0j.js} +49 -336
  51. package/dist/database-environment-D_Gk1Brf.js +20 -0
  52. package/dist/database-environment.d.ts +43 -0
  53. package/dist/database-environment.d.ts.map +1 -0
  54. package/dist/dist-BVjJWjT-.js +20 -0
  55. package/dist/{dist-B62O-xH6.js → dist-C87Ib9Vx.js} +2 -2
  56. package/dist/{dist-CL9KGm1j.js → dist-DLw_E34G.js} +1654 -1748
  57. package/dist/{dist-BBQyIRsO.js → dist-GIaWaUho.js} +4 -4
  58. package/dist/{dist-C8BOgli4.js → dist-RSE5hCY4.js} +3 -3
  59. package/dist/index.js +19 -9
  60. package/dist/mcp-apps-packaging-CixRnxz_.js +286 -0
  61. package/dist/{oidcProvisioningPrimitives-D9OYu5-F-l8afZs4F.js → oidcProvisioningPrimitives-D9OYu5-F-T9KkkDCd.js} +5 -5
  62. package/dist/portability-CES1YC2m.js +859 -0
  63. package/dist/resolveIdentity-B-PK6RwU-C9ogf8YG.js +269 -0
  64. package/package.json +19 -14
  65. package/dist/ApiKeyCollection-Bfb_nw81-DR5XF_SZ.js +0 -2
  66. package/dist/AuditLogCollection-B1YonCAP-PbzALncL.js +0 -2
  67. package/dist/NostrIdentityCollection-Ca74dkaL-C2r-uDlC.js +0 -2
  68. package/dist/OidcIdentityCollection-TV8VdK69-2fZROnih.js +0 -2
  69. package/dist/ProfileAssetCollection-B9hacUJa-D33sifgF.js +0 -2
  70. package/dist/ProfileLinkCollection-4uNURz32-CHDTinax.js +0 -2
  71. package/dist/ProfileMetadataCollection-DQhbfEfa-BQTogfVX.js +0 -2
  72. package/dist/ProfileMetafieldCollection-CoBuM_5G-BXtoIP02.js +0 -2
  73. package/dist/ProfileRelationshipCollection-DUVgaIqf-CNMliUF3.js +0 -2
  74. package/dist/ProfileRelationshipTermCollection-DtA9OM23-DPOMtMr7.js +0 -2
  75. package/dist/ProfileRelationshipTypeCollection-BdXwd7Kf-Co3PdH01.js +0 -2
  76. package/dist/resolveIdentity-B-PK6RwU-B306Ch1I.js +0 -2
@@ -0,0 +1,233 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
+ import { i as tenantId, r as TenantScoped } from "./dist-C87Ib9Vx.js";
3
+ import { n as __exportAll$1 } from "./NostrIdentity-n70E0vck-Di3_eNCq.js";
4
+ import { SmrtCollection, SmrtObject, field, foreignKey, isEmbeddedDatabase, smrt, withEmbeddedWriteTransaction } from "@happyvertical/smrt-core";
5
+ //#region ../profiles/dist/chunks/ProfileLinkCollection-4uNURz32.js
6
+ var ProfileLinkCollection_4uNURz32_exports = /* @__PURE__ */ __exportAll({
7
+ a: () => PROFILE_LINK_MAX_URL_LENGTH,
8
+ c: () => isProfileLinkPlatform,
9
+ i: () => 200,
10
+ l: () => normalizeProfileLinkUrl,
11
+ n: () => ProfileLinkCollection,
12
+ o: () => PROFILE_LINK_PLATFORMS,
13
+ r: () => ProfileLinkCollection_exports,
14
+ s: () => ProfileLink,
15
+ t: () => 50
16
+ });
17
+ var __defProp = Object.defineProperty;
18
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
19
+ var __decorateClass = (decorators, target, key, kind) => {
20
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
21
+ for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
22
+ if (kind && result) __defProp(target, key, result);
23
+ return result;
24
+ };
25
+ var PROFILE_LINK_PLATFORMS = [
26
+ "facebook",
27
+ "instagram",
28
+ "x",
29
+ "linkedin",
30
+ "youtube",
31
+ "tiktok",
32
+ "website",
33
+ "other"
34
+ ];
35
+ function isProfileLinkPlatform(value) {
36
+ return typeof value === "string" && PROFILE_LINK_PLATFORMS.includes(value);
37
+ }
38
+ function normalizeProfileLinkUrl(raw) {
39
+ const text = String(raw ?? "").trim();
40
+ if (!text) throw new Error("A profile link needs a web address");
41
+ if (text.length > 2048 || /\s/.test(text)) throw new Error("A profile link must be a single web address");
42
+ let url;
43
+ try {
44
+ url = new URL(text);
45
+ } catch {
46
+ throw new Error(`Not a web address: ${text}`);
47
+ }
48
+ if (url.protocol !== "https:" && url.protocol !== "http:") throw new Error("A profile link must start with http:// or https://");
49
+ if (url.username || url.password) throw new Error("A profile link cannot carry a user name or password");
50
+ if (!url.hostname) throw new Error(`Not a web address: ${text}`);
51
+ return url.toString();
52
+ }
53
+ var ProfileLink = class extends SmrtObject {
54
+ tenantId = null;
55
+ profileId = "";
56
+ platform = "website";
57
+ url = "";
58
+ label = null;
59
+ sortOrder = 0;
60
+ constructor(options = {}) {
61
+ super(options);
62
+ if (options.profileId) this.profileId = options.profileId;
63
+ if (options.platform) this.platform = options.platform;
64
+ if (options.url) this.url = options.url;
65
+ if (options.label !== void 0) this.label = options.label;
66
+ if (options.sortOrder !== void 0) this.sortOrder = options.sortOrder;
67
+ if (options.tenantId !== void 0) this.tenantId = options.tenantId;
68
+ }
69
+ /**
70
+ * A link's address and label are not its identity: two people can link the
71
+ * same page, and one person can relabel a link. Without an explicit slug
72
+ * the default (label-derived) slug would make a new link adopt another row
73
+ * through the (tenant, slug, context) natural key, so a link's default slug
74
+ * is its id.
75
+ */
76
+ async getSlug() {
77
+ if (!this.slug && this.id) this.slug = String(this.id);
78
+ return super.getSlug();
79
+ }
80
+ /** Validates and normalizes the link before it is written. */
81
+ async save(...args) {
82
+ if (!this.profileId) throw new Error("A profile link needs a profile");
83
+ if (!isProfileLinkPlatform(this.platform)) throw new Error(`Unknown profile link platform: ${this.platform}`);
84
+ this.url = normalizeProfileLinkUrl(this.url);
85
+ const label = this.label?.trim() ?? "";
86
+ if (label.length > 200) throw new Error("A profile link label is too long");
87
+ this.label = label || null;
88
+ if (!Number.isInteger(this.sortOrder) || this.sortOrder < 0) throw new Error("A profile link sortOrder must be a whole number ≥ 0");
89
+ return super.save(...args);
90
+ }
91
+ };
92
+ __decorateClass([tenantId({ nullable: true })], ProfileLink.prototype, "tenantId", 2);
93
+ __decorateClass([foreignKey("Profile", {
94
+ required: true,
95
+ onDelete: "CASCADE"
96
+ })], ProfileLink.prototype, "profileId", 2);
97
+ __decorateClass([field({
98
+ required: true,
99
+ maxLength: 32
100
+ })], ProfileLink.prototype, "platform", 2);
101
+ __decorateClass([field({
102
+ required: true,
103
+ maxLength: 2048
104
+ })], ProfileLink.prototype, "url", 2);
105
+ __decorateClass([field({
106
+ nullable: true,
107
+ maxLength: 200
108
+ })], ProfileLink.prototype, "label", 2);
109
+ __decorateClass([field()], ProfileLink.prototype, "sortOrder", 2);
110
+ ProfileLink = __decorateClass([TenantScoped({ mode: "optional" }), smrt({
111
+ tableName: "profile_links",
112
+ api: false,
113
+ mcp: false,
114
+ cli: false
115
+ })], ProfileLink);
116
+ var ProfileLinkCollection_exports = /* @__PURE__ */ __exportAll$1({
117
+ PROFILE_LINK_LIMIT: () => 50,
118
+ ProfileLinkCollection: () => ProfileLinkCollection
119
+ });
120
+ var ProfileLinkCollection = class ProfileLinkCollection extends SmrtCollection {
121
+ static _itemClass = ProfileLink;
122
+ /** The profile's links in display order (sortOrder, then age). */
123
+ async listForProfile(profileId) {
124
+ if (!profileId) return [];
125
+ return (await this.list({ where: { profileId } })).sort(compareLinks);
126
+ }
127
+ /**
128
+ * Makes the profile's links exactly `links`, in that order, in one
129
+ * transaction: a link whose address is already on the profile keeps its row
130
+ * (and id), new addresses are added, and links left out are removed. The
131
+ * same address given twice is kept once, at its first position. New links
132
+ * take the profile's tenant. Throws, changing nothing, when the profile is
133
+ * not visible in the current tenant context or a link is invalid.
134
+ */
135
+ async replaceForProfile(profileId, links) {
136
+ const wanted = normalizeInputs(links);
137
+ return this.inTransaction(async (bound) => {
138
+ const tenantId = await bound.requireProfileTenant(profileId);
139
+ const existing = await bound.listForProfile(profileId);
140
+ const byUrl = /* @__PURE__ */ new Map();
141
+ for (const link of existing) if (!byUrl.has(link.url)) byUrl.set(link.url, link);
142
+ const kept = /* @__PURE__ */ new Set();
143
+ const result = [];
144
+ for (const [index, input] of wanted.entries()) {
145
+ const current = byUrl.get(input.url);
146
+ if (current) {
147
+ kept.add(current);
148
+ const changed = current.platform !== input.platform || (current.label ?? null) !== input.label || current.sortOrder !== index || !current.tenantId && tenantId !== null;
149
+ current.platform = input.platform;
150
+ current.label = input.label;
151
+ current.sortOrder = index;
152
+ if (!current.tenantId && tenantId) current.tenantId = tenantId;
153
+ if (changed) await current.save();
154
+ result.push(current);
155
+ continue;
156
+ }
157
+ const created = await bound.create({
158
+ profileId,
159
+ platform: input.platform,
160
+ url: input.url,
161
+ label: input.label,
162
+ sortOrder: index,
163
+ ...tenantId ? { tenantId } : {}
164
+ });
165
+ result.push(created);
166
+ }
167
+ for (const link of existing) if (!kept.has(link)) await link.delete();
168
+ return result;
169
+ });
170
+ }
171
+ /**
172
+ * Puts the profile's links in the order of `linkIds`, which must name every
173
+ * link on the profile exactly once. One transaction; throws, changing
174
+ * nothing, on a missing, extra or repeated id.
175
+ */
176
+ async reorder(profileId, linkIds) {
177
+ return this.inTransaction(async (bound) => {
178
+ await bound.requireProfileTenant(profileId);
179
+ const existing = await bound.listForProfile(profileId);
180
+ const byId = new Map(existing.map((link) => [String(link.id), link]));
181
+ const ids = linkIds.map(String);
182
+ if (ids.length !== existing.length || new Set(ids).size !== ids.length || ids.some((id) => !byId.has(id))) throw new Error("Reorder must list every link on the profile exactly once");
183
+ const ordered = [];
184
+ for (const [index, id] of ids.entries()) {
185
+ const link = byId.get(id);
186
+ if (link.sortOrder !== index) {
187
+ link.sortOrder = index;
188
+ await link.save();
189
+ }
190
+ ordered.push(link);
191
+ }
192
+ return ordered;
193
+ });
194
+ }
195
+ /** The profile's tenant; throws when the profile is not visible here. */
196
+ async requireProfileTenant(profileId) {
197
+ if (!profileId) throw new Error("A profile link needs a profile");
198
+ const { ProfileCollection } = await import("./ProfileCollection-DuJT7Mum-nXbHrPpX.js").then((n) => n.o).then((n) => n.r);
199
+ const profile = await (await ProfileCollection.create({ db: this.db })).get({ id: profileId });
200
+ if (!profile) throw new Error(`Profile '${profileId}' not found`);
201
+ return profile.tenantId ?? null;
202
+ }
203
+ async inTransaction(work) {
204
+ const db = this.db;
205
+ return withEmbeddedWriteTransaction(db, isEmbeddedDatabase(db), async (transaction) => work(await ProfileLinkCollection.create({ db: transaction })));
206
+ }
207
+ };
208
+ function compareLinks(a, b) {
209
+ if (a.sortOrder !== b.sortOrder) return a.sortOrder - b.sortOrder;
210
+ const aTime = new Date(a.created_at ?? 0).getTime();
211
+ const bTime = new Date(b.created_at ?? 0).getTime();
212
+ if (aTime !== bTime) return aTime - bTime;
213
+ return String(a.id).localeCompare(String(b.id));
214
+ }
215
+ function normalizeInputs(links) {
216
+ if (links.length > 50) throw new Error(`A profile can hold at most 50 links (got ${links.length})`);
217
+ const seen = /* @__PURE__ */ new Set();
218
+ const out = [];
219
+ for (const link of links) {
220
+ if (!isProfileLinkPlatform(link.platform)) throw new Error(`Unknown profile link platform: ${link.platform}`);
221
+ const url = normalizeProfileLinkUrl(link.url);
222
+ if (seen.has(url)) continue;
223
+ seen.add(url);
224
+ out.push({
225
+ platform: link.platform,
226
+ url,
227
+ label: link.label?.trim() ? link.label.trim() : null
228
+ });
229
+ }
230
+ return out;
231
+ }
232
+ //#endregion
233
+ export { isProfileLinkPlatform as a, ProfileLinkCollection_4uNURz32_exports as i, ProfileLink as n, normalizeProfileLinkUrl as o, ProfileLinkCollection as r, PROFILE_LINK_PLATFORMS as t };
@@ -0,0 +1,108 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
+ import { i as tenantId, r as TenantScoped } from "./dist-C87Ib9Vx.js";
3
+ import { n as __exportAll$1 } from "./NostrIdentity-n70E0vck-Di3_eNCq.js";
4
+ import { SmrtCollection, SmrtObject, field, foreignKey, smrt } from "@happyvertical/smrt-core";
5
+ //#region ../profiles/dist/chunks/ProfileMetadataCollection-DQhbfEfa.js
6
+ var ProfileMetadataCollection_DQhbfEfa_exports = /* @__PURE__ */ __exportAll({
7
+ n: () => ProfileMetadataCollection_exports,
8
+ r: () => ProfileMetadata,
9
+ t: () => ProfileMetadataCollection
10
+ });
11
+ var __defProp = Object.defineProperty;
12
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
+ var __decorateClass = (decorators, target, key, kind) => {
14
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
15
+ for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
16
+ if (kind && result) __defProp(target, key, result);
17
+ return result;
18
+ };
19
+ var ProfileMetadata = class extends SmrtObject {
20
+ tenantId = null;
21
+ profileId;
22
+ metafieldId;
23
+ value = "";
24
+ constructor(options = {}) {
25
+ super(options);
26
+ if (options.profileId !== void 0) this.profileId = options.profileId;
27
+ if (options.metafieldId !== void 0) this.metafieldId = options.metafieldId;
28
+ if (options.value) this.value = options.value;
29
+ if (options.tenantId !== void 0) this.tenantId = options.tenantId;
30
+ }
31
+ /**
32
+ * Validate this metadata value against the metafield's validation schema
33
+ *
34
+ * @returns True if valid, throws error if invalid
35
+ */
36
+ async validate() {
37
+ const metafield = await this.loadRelated("metafieldId");
38
+ if (!metafield) throw new Error("Metafield not found");
39
+ return await metafield.validateValue(this.value);
40
+ }
41
+ /**
42
+ * Get the metafield slug for this metadata
43
+ *
44
+ * @returns The slug of the metafield
45
+ */
46
+ async getMetafieldSlug() {
47
+ return (await this.loadRelated("metafieldId"))?.slug || "";
48
+ }
49
+ };
50
+ __decorateClass([tenantId({ nullable: true })], ProfileMetadata.prototype, "tenantId", 2);
51
+ __decorateClass([foreignKey("Profile", { required: true })], ProfileMetadata.prototype, "profileId", 2);
52
+ __decorateClass([foreignKey("ProfileMetafield", { required: true })], ProfileMetadata.prototype, "metafieldId", 2);
53
+ __decorateClass([field({ required: true })], ProfileMetadata.prototype, "value", 2);
54
+ ProfileMetadata = __decorateClass([TenantScoped({ mode: "optional" }), smrt({
55
+ tableStrategy: "sti",
56
+ api: { include: [
57
+ "list",
58
+ "get",
59
+ "create",
60
+ "update",
61
+ "delete"
62
+ ] },
63
+ mcp: { include: ["list", "get"] },
64
+ cli: { skipApiCheck: true }
65
+ })], ProfileMetadata);
66
+ var ProfileMetadataCollection_exports = /* @__PURE__ */ __exportAll$1({ ProfileMetadataCollection: () => ProfileMetadataCollection });
67
+ var ProfileMetadataCollection = class extends SmrtCollection {
68
+ static _itemClass = ProfileMetadata;
69
+ /**
70
+ * Get all metadata for a profile
71
+ *
72
+ * @param profileId - The profile UUID
73
+ * @returns Array of ProfileMetadata instances
74
+ */
75
+ async getByProfile(profileId) {
76
+ return await this.list({ where: { profileId } });
77
+ }
78
+ /**
79
+ * Get metadata as key-value object for a profile
80
+ *
81
+ * @param profileId - The profile UUID
82
+ * @returns Object with metafield slugs as keys
83
+ */
84
+ async getMetadataObject(profileId) {
85
+ const metadata = await this.getByProfile(profileId);
86
+ const result = {};
87
+ for (const item of metadata) {
88
+ const slug = await item.getMetafieldSlug();
89
+ if (slug) result[slug] = item.value;
90
+ }
91
+ return result;
92
+ }
93
+ /**
94
+ * Find all profiles with a specific metadata key-value pair
95
+ *
96
+ * @param metafieldId - The metafield UUID
97
+ * @param value - The value to match
98
+ * @returns Array of profile UUIDs
99
+ */
100
+ async findProfilesByMetadata(metafieldId, value) {
101
+ return (await this.list({ where: {
102
+ metafieldId,
103
+ value: String(value)
104
+ } })).map((m) => m.profileId).filter((id) => typeof id === "string");
105
+ }
106
+ };
107
+ //#endregion
108
+ export { ProfileMetadataCollection as n, ProfileMetadataCollection_DQhbfEfa_exports as r, ProfileMetadata as t };
@@ -0,0 +1,145 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
+ import { i as tenantId, r as TenantScoped } from "./dist-C87Ib9Vx.js";
3
+ import { n as __exportAll$1 } from "./NostrIdentity-n70E0vck-Di3_eNCq.js";
4
+ import { SmrtCollection, SmrtObject, field, smrt } from "@happyvertical/smrt-core";
5
+ //#region ../profiles/dist/chunks/ProfileMetafieldCollection-CoBuM_5G.js
6
+ var ProfileMetafieldCollection_CoBuM_5G_exports = /* @__PURE__ */ __exportAll({
7
+ n: () => ProfileMetafieldCollection_exports,
8
+ r: () => ProfileMetafield,
9
+ t: () => ProfileMetafieldCollection
10
+ });
11
+ var __defProp = Object.defineProperty;
12
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
+ var __decorateClass = (decorators, target, key, kind) => {
14
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
15
+ for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
16
+ if (kind && result) __defProp(target, key, result);
17
+ return result;
18
+ };
19
+ function getCustomValidators() {
20
+ if (!globalThis.__smrtProfileMetafieldValidators) globalThis.__smrtProfileMetafieldValidators = /* @__PURE__ */ new Map();
21
+ return globalThis.__smrtProfileMetafieldValidators;
22
+ }
23
+ var ProfileMetafield = class extends SmrtObject {
24
+ tenantId = null;
25
+ name = "";
26
+ description;
27
+ validation;
28
+ constructor(options = {}) {
29
+ super(options);
30
+ if (options.name) this.name = options.name;
31
+ if (options.description !== void 0) this.description = options.description;
32
+ if (options.validation !== void 0) this.validation = options.validation;
33
+ if (options.tenantId !== void 0) this.tenantId = options.tenantId;
34
+ }
35
+ /**
36
+ * Convenience method for slug-based lookup
37
+ *
38
+ * @param slug - The slug to search for
39
+ * @returns ProfileMetafield instance or null if not found
40
+ */
41
+ static async getBySlug(_slug) {
42
+ return null;
43
+ }
44
+ /**
45
+ * Register a custom validator function
46
+ *
47
+ * @param name - Name of the validator (used in validation.custom field)
48
+ * @param validator - The validator function
49
+ */
50
+ static registerValidator(name, validator) {
51
+ getCustomValidators().set(name, validator);
52
+ }
53
+ /**
54
+ * Get a registered custom validator
55
+ *
56
+ * @param name - Name of the validator
57
+ * @returns The validator function or undefined
58
+ */
59
+ static getValidator(name) {
60
+ return getCustomValidators().get(name);
61
+ }
62
+ /**
63
+ * Validate a value against this metafield's validation schema
64
+ *
65
+ * @param value - The value to validate
66
+ * @returns True if valid, throws ValidationError if invalid
67
+ */
68
+ async validateValue(value) {
69
+ if (!this.validation) return true;
70
+ const schema = this.validation;
71
+ if (schema.type) {
72
+ const actualType = typeof value;
73
+ const expectedType = schema.type;
74
+ if (expectedType === "number" && actualType !== "number") throw new Error(schema.message || `Expected ${expectedType}, got ${actualType}`);
75
+ if (expectedType === "string" && actualType !== "string") throw new Error(schema.message || `Expected ${expectedType}, got ${actualType}`);
76
+ if (expectedType === "boolean" && actualType !== "boolean") throw new Error(schema.message || `Expected ${expectedType}, got ${actualType}`);
77
+ }
78
+ if (schema.pattern && typeof value === "string") {
79
+ if (!new RegExp(schema.pattern).test(value)) throw new Error(schema.message || "Pattern validation failed");
80
+ }
81
+ if (schema.minLength && typeof value === "string") {
82
+ if (value.length < schema.minLength) throw new Error(schema.message || `Minimum length is ${schema.minLength}, got ${value.length}`);
83
+ }
84
+ if (schema.maxLength && typeof value === "string") {
85
+ if (value.length > schema.maxLength) throw new Error(schema.message || `Maximum length is ${schema.maxLength}, got ${value.length}`);
86
+ }
87
+ if (schema.min !== void 0 && typeof value === "number") {
88
+ if (value < schema.min) throw new Error(schema.message || `Value must be at least ${schema.min}`);
89
+ }
90
+ if (schema.max !== void 0 && typeof value === "number") {
91
+ if (value > schema.max) throw new Error(schema.message || `Value must be at most ${schema.max}`);
92
+ }
93
+ if (schema.custom) {
94
+ const validator = ProfileMetafield.getValidator(schema.custom);
95
+ if (!validator) throw new Error(`Custom validator '${schema.custom}' not registered`);
96
+ if (!await validator(value)) throw new Error(schema.message || "Custom validation failed");
97
+ }
98
+ return true;
99
+ }
100
+ };
101
+ __decorateClass([tenantId({ nullable: true })], ProfileMetafield.prototype, "tenantId", 2);
102
+ __decorateClass([field({ required: true })], ProfileMetafield.prototype, "name", 2);
103
+ ProfileMetafield = __decorateClass([TenantScoped({ mode: "optional" }), smrt({
104
+ tableStrategy: "sti",
105
+ api: { include: [
106
+ "list",
107
+ "get",
108
+ "create",
109
+ "update"
110
+ ] },
111
+ mcp: { include: ["list", "get"] },
112
+ cli: { skipApiCheck: true }
113
+ })], ProfileMetafield);
114
+ var ProfileMetafieldCollection_exports = /* @__PURE__ */ __exportAll$1({ ProfileMetafieldCollection: () => ProfileMetafieldCollection });
115
+ var ProfileMetafieldCollection = class extends SmrtCollection {
116
+ static _itemClass = ProfileMetafield;
117
+ /**
118
+ * Get metafield by slug
119
+ *
120
+ * @param slug - The slug to search for
121
+ * @returns ProfileMetafield instance or null
122
+ */
123
+ async getBySlug(slug) {
124
+ return await this.get({ slug });
125
+ }
126
+ /**
127
+ * Get or create a metafield by slug
128
+ *
129
+ * @param slug - The slug to search for
130
+ * @param defaults - Default values if creating
131
+ * @returns ProfileMetafield instance
132
+ */
133
+ async getOrCreateBySlug(slug, defaults) {
134
+ const existing = await this.getBySlug(slug);
135
+ if (existing) return existing;
136
+ const metafield = await this.create({
137
+ slug,
138
+ ...defaults
139
+ });
140
+ await metafield.save();
141
+ return metafield;
142
+ }
143
+ };
144
+ //#endregion
145
+ export { ProfileMetafieldCollection as n, ProfileMetafieldCollection_CoBuM_5G_exports as r, ProfileMetafield as t };
@@ -0,0 +1,161 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
+ import { i as tenantId, r as TenantScoped } from "./dist-C87Ib9Vx.js";
3
+ import { n as __exportAll$1 } from "./NostrIdentity-n70E0vck-Di3_eNCq.js";
4
+ import { SmrtCollection, SmrtObject, foreignKey, oneToMany, smrt } from "@happyvertical/smrt-core";
5
+ //#region ../profiles/dist/chunks/ProfileRelationshipCollection-DUVgaIqf.js
6
+ var ProfileRelationshipCollection_DUVgaIqf_exports = /* @__PURE__ */ __exportAll({
7
+ n: () => ProfileRelationshipCollection_exports,
8
+ r: () => ProfileRelationship,
9
+ t: () => ProfileRelationshipCollection
10
+ });
11
+ var __defProp = Object.defineProperty;
12
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
+ var __decorateClass = (decorators, target, key, kind) => {
14
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
15
+ for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
16
+ if (kind && result) __defProp(target, key, result);
17
+ return result;
18
+ };
19
+ var ProfileRelationship = class extends SmrtObject {
20
+ tenantId = null;
21
+ fromProfileId;
22
+ toProfileId;
23
+ typeId;
24
+ contextProfileId;
25
+ terms = [];
26
+ constructor(options = {}) {
27
+ super(options);
28
+ if (options.fromProfileId !== void 0) this.fromProfileId = options.fromProfileId;
29
+ if (options.toProfileId !== void 0) this.toProfileId = options.toProfileId;
30
+ if (options.typeId !== void 0) this.typeId = options.typeId;
31
+ if (options.contextProfileId !== void 0) this.contextProfileId = options.contextProfileId;
32
+ }
33
+ /**
34
+ * Get the relationship type slug
35
+ *
36
+ * @returns The slug of the relationship type
37
+ */
38
+ async getTypeSlug() {
39
+ return (await this.loadRelated("typeId"))?.slug || "";
40
+ }
41
+ /**
42
+ * Add a term (time period) to this relationship
43
+ *
44
+ * @param startedAt - Start date of the term
45
+ * @param endedAt - Optional end date of the term
46
+ */
47
+ async addTerm(startedAt, endedAt) {
48
+ if (!this.id) throw new Error("ProfileRelationship.addTerm requires a persisted relationship (missing id)");
49
+ const relationshipId = this.id;
50
+ const { ProfileRelationshipTermCollection } = await import("./ProfileRelationshipTermCollection-DtA9OM23-BoGhQBSQ.js").then((n) => n.r).then((n) => n.n);
51
+ await (await (await ProfileRelationshipTermCollection.create(this.options)).create({
52
+ relationshipId,
53
+ startedAt,
54
+ endedAt
55
+ })).save();
56
+ }
57
+ /**
58
+ * End the current active term
59
+ *
60
+ * @param endedAt - End date for the term
61
+ */
62
+ async endCurrentTerm(endedAt) {
63
+ const activeTerm = await this.getActiveTerm();
64
+ if (activeTerm) await activeTerm.end(endedAt);
65
+ }
66
+ /**
67
+ * Get all terms for this relationship
68
+ *
69
+ * @returns Array of ProfileRelationshipTerm instances
70
+ */
71
+ async getTerms() {
72
+ const { ProfileRelationshipTermCollection } = await import("./ProfileRelationshipTermCollection-DtA9OM23-BoGhQBSQ.js").then((n) => n.r).then((n) => n.n);
73
+ return await (await ProfileRelationshipTermCollection.create(this.options)).getByRelationship(this.id);
74
+ }
75
+ /**
76
+ * Get the active term (no end date)
77
+ *
78
+ * @returns Current term or null if none active
79
+ */
80
+ async getActiveTerm() {
81
+ const { ProfileRelationshipTermCollection } = await import("./ProfileRelationshipTermCollection-DtA9OM23-BoGhQBSQ.js").then((n) => n.r).then((n) => n.n);
82
+ return await (await ProfileRelationshipTermCollection.create(this.options)).getActiveTerm(this.id);
83
+ }
84
+ };
85
+ __decorateClass([tenantId({ nullable: true })], ProfileRelationship.prototype, "tenantId", 2);
86
+ __decorateClass([foreignKey("Profile", { required: true })], ProfileRelationship.prototype, "fromProfileId", 2);
87
+ __decorateClass([foreignKey("Profile", { required: true })], ProfileRelationship.prototype, "toProfileId", 2);
88
+ __decorateClass([foreignKey("ProfileRelationshipType", { required: true })], ProfileRelationship.prototype, "typeId", 2);
89
+ __decorateClass([foreignKey("Profile")], ProfileRelationship.prototype, "contextProfileId", 2);
90
+ __decorateClass([oneToMany("ProfileRelationshipTerm")], ProfileRelationship.prototype, "terms", 2);
91
+ ProfileRelationship = __decorateClass([TenantScoped({ mode: "optional" }), smrt({
92
+ tableStrategy: "sti",
93
+ api: { include: [
94
+ "list",
95
+ "get",
96
+ "create",
97
+ "delete"
98
+ ] },
99
+ mcp: { include: ["list", "get"] },
100
+ cli: { skipApiCheck: true }
101
+ })], ProfileRelationship);
102
+ var ProfileRelationshipCollection_exports = /* @__PURE__ */ __exportAll$1({ ProfileRelationshipCollection: () => ProfileRelationshipCollection });
103
+ var ProfileRelationshipCollection = class extends SmrtCollection {
104
+ static _itemClass = ProfileRelationship;
105
+ /**
106
+ * Get all relationships from a profile
107
+ *
108
+ * @param fromProfileId - The origin profile UUID
109
+ * @param typeId - Optional filter by relationship type UUID
110
+ * @returns Array of ProfileRelationship instances
111
+ */
112
+ async getFromProfile(fromProfileId, typeId) {
113
+ const where = { fromProfileId };
114
+ if (typeId) where.typeId = typeId;
115
+ return await this.list({ where });
116
+ }
117
+ /**
118
+ * Get all relationships to a profile
119
+ *
120
+ * @param toProfileId - The target profile UUID
121
+ * @param typeId - Optional filter by relationship type UUID
122
+ * @returns Array of ProfileRelationship instances
123
+ */
124
+ async getToProfile(toProfileId, typeId) {
125
+ const where = { toProfileId };
126
+ if (typeId) where.typeId = typeId;
127
+ return await this.list({ where });
128
+ }
129
+ /**
130
+ * Get all relationships for a profile (both directions)
131
+ *
132
+ * @param profileId - The profile UUID
133
+ * @param typeId - Optional filter by relationship type UUID
134
+ * @returns Array of ProfileRelationship instances
135
+ */
136
+ async getForProfile(profileId, typeId) {
137
+ const fromRelationships = await this.getFromProfile(profileId, typeId);
138
+ const toRelationships = await this.getToProfile(profileId, typeId);
139
+ return [...fromRelationships, ...toRelationships];
140
+ }
141
+ /**
142
+ * Check if a relationship exists between two profiles
143
+ *
144
+ * @param fromProfileId - The origin profile UUID
145
+ * @param toProfileId - The target profile UUID
146
+ * @param typeId - The relationship type UUID
147
+ * @returns True if relationship exists
148
+ */
149
+ async exists(fromProfileId, toProfileId, typeId) {
150
+ return (await this.list({
151
+ where: {
152
+ fromProfileId,
153
+ toProfileId,
154
+ typeId
155
+ },
156
+ limit: 1
157
+ })).length > 0;
158
+ }
159
+ };
160
+ //#endregion
161
+ export { ProfileRelationshipCollection as n, ProfileRelationshipCollection_DUVgaIqf_exports as r, ProfileRelationship as t };