@stndrds/schema 0.1.0-alpha.14

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 (128) hide show
  1. package/dist/chunk-3ABIOLKO.mjs +4624 -0
  2. package/dist/chunk-3IRX2BDY.mjs +3970 -0
  3. package/dist/chunk-4OTLPWSP.mjs +4448 -0
  4. package/dist/chunk-54ODQBUN.mjs +3867 -0
  5. package/dist/chunk-6HIA3FR2.mjs +4230 -0
  6. package/dist/chunk-6P3XHNLD.mjs +4171 -0
  7. package/dist/chunk-7LJECNAS.mjs +4167 -0
  8. package/dist/chunk-A2WNLUKV.mjs +4475 -0
  9. package/dist/chunk-AA3234RM.mjs +4475 -0
  10. package/dist/chunk-ADNXDLLD.mjs +3136 -0
  11. package/dist/chunk-B4DCDQUI.mjs +4695 -0
  12. package/dist/chunk-B4PSXY6I.mjs +4527 -0
  13. package/dist/chunk-BJHJBZCM.mjs +4228 -0
  14. package/dist/chunk-C3ZWTO6G.mjs +4474 -0
  15. package/dist/chunk-CLOGNKDD.mjs +4040 -0
  16. package/dist/chunk-EW4V63PA.mjs +3053 -0
  17. package/dist/chunk-FR2LCO6R.mjs +2485 -0
  18. package/dist/chunk-GJSF2RKL.mjs +2786 -0
  19. package/dist/chunk-GO5NPP2X.mjs +4726 -0
  20. package/dist/chunk-H3ZYJUDS.mjs +3050 -0
  21. package/dist/chunk-IDDLSFSK.mjs +4230 -0
  22. package/dist/chunk-JRADUJQ7.mjs +4726 -0
  23. package/dist/chunk-NEDR6N7R.mjs +3118 -0
  24. package/dist/chunk-NESPRMDF.mjs +2482 -0
  25. package/dist/chunk-NWFGRJBW.mjs +4612 -0
  26. package/dist/chunk-NXJ57GSN.mjs +2918 -0
  27. package/dist/chunk-O4C5FCTS.mjs +3961 -0
  28. package/dist/chunk-O4PRB27Q.mjs +2513 -0
  29. package/dist/chunk-OJDFNUCR.mjs +4463 -0
  30. package/dist/chunk-OSEKVBHC.mjs +4627 -0
  31. package/dist/chunk-QX4U6U6K.mjs +2484 -0
  32. package/dist/chunk-RMDQ6LKV.mjs +3046 -0
  33. package/dist/chunk-SBXKDATH.mjs +2673 -0
  34. package/dist/chunk-SKMP3AP3.mjs +2918 -0
  35. package/dist/chunk-STMS7WZD.mjs +2623 -0
  36. package/dist/chunk-T3T6PTFG.mjs +3057 -0
  37. package/dist/chunk-TPW72RAH.mjs +4624 -0
  38. package/dist/chunk-TUQEGNGV.mjs +2534 -0
  39. package/dist/chunk-TVKFS3YN.mjs +2449 -0
  40. package/dist/chunk-TZTH3BHG.mjs +3110 -0
  41. package/dist/chunk-W6MU3XPG.mjs +4612 -0
  42. package/dist/chunk-WL7Z3YFR.mjs +4603 -0
  43. package/dist/chunk-XNVVYHH3.mjs +4602 -0
  44. package/dist/chunk-XTA3WY64.mjs +2918 -0
  45. package/dist/chunk-YV7DEZFZ.mjs +2528 -0
  46. package/dist/chunk-ZLJPJND6.mjs +4474 -0
  47. package/dist/chunk-ZNZXTCEX.mjs +4234 -0
  48. package/dist/index.d.mts +1222 -0
  49. package/dist/index.d.ts +1222 -0
  50. package/dist/index.js +5320 -0
  51. package/dist/index.mjs +635 -0
  52. package/dist/runtime-2iKarXtl.d.mts +1924 -0
  53. package/dist/runtime-2iKarXtl.d.ts +1924 -0
  54. package/dist/runtime-4l2ddHZ7.d.mts +2242 -0
  55. package/dist/runtime-4l2ddHZ7.d.ts +2243 -0
  56. package/dist/runtime-8hKS6zAv.d.mts +2226 -0
  57. package/dist/runtime-8hKS6zAv.d.ts +2226 -0
  58. package/dist/runtime-B6yjtoR3.d.mts +4142 -0
  59. package/dist/runtime-B6yjtoR3.d.ts +4142 -0
  60. package/dist/runtime-BGCShrZB.d.mts +2001 -0
  61. package/dist/runtime-BGCShrZB.d.ts +2001 -0
  62. package/dist/runtime-BRW4NGAk.d.mts +1668 -0
  63. package/dist/runtime-BRW4NGAk.d.ts +1668 -0
  64. package/dist/runtime-BnfBr314.d.mts +1683 -0
  65. package/dist/runtime-BnfBr314.d.ts +1683 -0
  66. package/dist/runtime-BrqhiJUF.d.mts +4151 -0
  67. package/dist/runtime-BrqhiJUF.d.ts +4151 -0
  68. package/dist/runtime-Bvj8c-BE.d.mts +4277 -0
  69. package/dist/runtime-Bvj8c-BE.d.ts +4277 -0
  70. package/dist/runtime-C03qT4qU.d.mts +4267 -0
  71. package/dist/runtime-C03qT4qU.d.ts +4267 -0
  72. package/dist/runtime-CaCZ7mSf.d.mts +2974 -0
  73. package/dist/runtime-CaCZ7mSf.d.ts +2974 -0
  74. package/dist/runtime-CaIzNX0Z.d.mts +4091 -0
  75. package/dist/runtime-CaIzNX0Z.d.ts +4091 -0
  76. package/dist/runtime-ChPTgmLP.d.mts +4320 -0
  77. package/dist/runtime-ChPTgmLP.d.ts +4320 -0
  78. package/dist/runtime-CnImZ1Vv.d.mts +4267 -0
  79. package/dist/runtime-CnImZ1Vv.d.ts +4267 -0
  80. package/dist/runtime-Cp-B26Nj.d.mts +2890 -0
  81. package/dist/runtime-Cp-B26Nj.d.ts +2890 -0
  82. package/dist/runtime-Cq8jfk3c.d.mts +4277 -0
  83. package/dist/runtime-Cq8jfk3c.d.ts +4277 -0
  84. package/dist/runtime-CqDFXhhP.d.mts +1685 -0
  85. package/dist/runtime-CqDFXhhP.d.ts +1685 -0
  86. package/dist/runtime-CqtpZLdL.d.mts +4280 -0
  87. package/dist/runtime-CqtpZLdL.d.ts +4280 -0
  88. package/dist/runtime-D-4DblaZ.d.mts +4147 -0
  89. package/dist/runtime-D-4DblaZ.d.ts +4147 -0
  90. package/dist/runtime-D59sSqNl.d.mts +4146 -0
  91. package/dist/runtime-D59sSqNl.d.ts +4146 -0
  92. package/dist/runtime-DEfPk3wT.d.mts +1840 -0
  93. package/dist/runtime-DEfPk3wT.d.ts +1840 -0
  94. package/dist/runtime-DGV-7vES.d.mts +1685 -0
  95. package/dist/runtime-DGV-7vES.d.ts +1685 -0
  96. package/dist/runtime-DNacghqc.d.mts +3971 -0
  97. package/dist/runtime-DNacghqc.d.ts +3971 -0
  98. package/dist/runtime-DPLsUHYK.d.mts +3977 -0
  99. package/dist/runtime-DPLsUHYK.d.ts +3977 -0
  100. package/dist/runtime-DTxCw60F.d.mts +2973 -0
  101. package/dist/runtime-DTxCw60F.d.ts +2973 -0
  102. package/dist/runtime-DUXJ-zNS.d.mts +2788 -0
  103. package/dist/runtime-DUXJ-zNS.d.ts +2788 -0
  104. package/dist/runtime-DbljInhV.d.mts +2237 -0
  105. package/dist/runtime-DbljInhV.d.ts +2237 -0
  106. package/dist/runtime-DcH3y6Yu.d.mts +4272 -0
  107. package/dist/runtime-DcH3y6Yu.d.ts +4272 -0
  108. package/dist/runtime-DdBFz85g.d.mts +4229 -0
  109. package/dist/runtime-DdBFz85g.d.ts +4229 -0
  110. package/dist/runtime-DjMWhjkm.d.mts +4321 -0
  111. package/dist/runtime-DjMWhjkm.d.ts +4321 -0
  112. package/dist/runtime-Djz3fOBE.d.mts +2078 -0
  113. package/dist/runtime-Djz3fOBE.d.ts +2078 -0
  114. package/dist/runtime-Dli8g3Aq.d.mts +2082 -0
  115. package/dist/runtime-Dli8g3Aq.d.ts +2082 -0
  116. package/dist/runtime-a8J378im.d.mts +4123 -0
  117. package/dist/runtime-a8J378im.d.ts +4123 -0
  118. package/dist/runtime-dp3rb3Lf.d.mts +2968 -0
  119. package/dist/runtime-dp3rb3Lf.d.ts +2968 -0
  120. package/dist/runtime-iWXlSN_H.d.mts +1683 -0
  121. package/dist/runtime-iWXlSN_H.d.ts +1683 -0
  122. package/dist/runtime-yMql0npY.d.mts +4321 -0
  123. package/dist/runtime-yMql0npY.d.ts +4321 -0
  124. package/dist/runtime.d.mts +3 -0
  125. package/dist/runtime.d.ts +3 -0
  126. package/dist/runtime.js +3626 -0
  127. package/dist/runtime.mjs +48 -0
  128. package/package.json +58 -0
@@ -0,0 +1,3053 @@
1
+ // src/utils.ts
2
+ function generateId() {
3
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
4
+ return crypto.randomUUID();
5
+ }
6
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
7
+ const r = Math.random() * 16 | 0;
8
+ const v = c === "x" ? r : r & 3 | 8;
9
+ return v.toString(16);
10
+ });
11
+ }
12
+ function generatePrefixedId(prefix) {
13
+ return `${prefix}_${generateId()}`;
14
+ }
15
+
16
+ // src/runtime/mock-adapter.ts
17
+ function createMockObjectsRepository(stores) {
18
+ return {
19
+ findById(id) {
20
+ return Promise.resolve(stores.objects.get(id) ?? null);
21
+ },
22
+ findByName(tenantId, name) {
23
+ for (const obj of stores.objects.values()) {
24
+ if (obj.tenantId === tenantId && obj.name === name) {
25
+ return Promise.resolve(obj);
26
+ }
27
+ }
28
+ return Promise.resolve(null);
29
+ },
30
+ findSystemByName(name) {
31
+ for (const obj of stores.objects.values()) {
32
+ if (obj.system && obj.name === name) {
33
+ return Promise.resolve(obj);
34
+ }
35
+ }
36
+ return Promise.resolve(null);
37
+ },
38
+ create(data) {
39
+ const obj = {
40
+ id: generateId(),
41
+ tenantId: data.tenantId,
42
+ name: data.name,
43
+ label: data.label,
44
+ description: data.description,
45
+ icon: data.icon,
46
+ system: data.system ?? false,
47
+ metadata: data.metadata,
48
+ createdAt: /* @__PURE__ */ new Date(),
49
+ updatedAt: /* @__PURE__ */ new Date()
50
+ };
51
+ stores.objects.set(obj.id, obj);
52
+ return Promise.resolve(obj);
53
+ },
54
+ update(id, data) {
55
+ const existing = stores.objects.get(id);
56
+ if (!existing) {
57
+ return Promise.reject(new Error(`Object ${id} not found`));
58
+ }
59
+ const updated = {
60
+ ...existing,
61
+ ...data,
62
+ updatedAt: /* @__PURE__ */ new Date()
63
+ };
64
+ stores.objects.set(id, updated);
65
+ return Promise.resolve(updated);
66
+ },
67
+ delete(id) {
68
+ stores.objects.delete(id);
69
+ return Promise.resolve();
70
+ },
71
+ list(tenantId) {
72
+ const results = Array.from(stores.objects.values()).filter(
73
+ (obj) => obj.tenantId === tenantId
74
+ );
75
+ return Promise.resolve(results);
76
+ },
77
+ upsert(data) {
78
+ for (const obj of stores.objects.values()) {
79
+ if (obj.system && obj.name === data.name) {
80
+ const updated = {
81
+ ...obj,
82
+ label: data.label,
83
+ description: data.description,
84
+ icon: data.icon,
85
+ metadata: data.metadata,
86
+ updatedAt: /* @__PURE__ */ new Date()
87
+ };
88
+ stores.objects.set(obj.id, updated);
89
+ return Promise.resolve(updated);
90
+ }
91
+ }
92
+ const newObj = {
93
+ id: generateId(),
94
+ tenantId: data.tenantId,
95
+ name: data.name,
96
+ label: data.label,
97
+ description: data.description,
98
+ icon: data.icon,
99
+ system: data.system,
100
+ metadata: data.metadata,
101
+ createdAt: /* @__PURE__ */ new Date(),
102
+ updatedAt: /* @__PURE__ */ new Date()
103
+ };
104
+ stores.objects.set(newObj.id, newObj);
105
+ return Promise.resolve(newObj);
106
+ }
107
+ };
108
+ }
109
+ function createMockAttributesRepository(stores) {
110
+ return {
111
+ findById(id) {
112
+ return Promise.resolve(stores.attributes.get(id) ?? null);
113
+ },
114
+ findByObjectId(objectId) {
115
+ const results = Array.from(stores.attributes.values()).filter(
116
+ (attr) => attr.objectId === objectId
117
+ );
118
+ return Promise.resolve(results);
119
+ },
120
+ create(data) {
121
+ const attr = {
122
+ id: generateId(),
123
+ objectId: data.objectId,
124
+ name: data.name,
125
+ label: data.label,
126
+ type: data.type,
127
+ description: data.description,
128
+ icon: data.icon,
129
+ system: data.system ?? false,
130
+ required: data.required ?? false,
131
+ unique: data.unique ?? false,
132
+ order: data.order ?? 0,
133
+ hidden: data.hidden,
134
+ archived: data.archived,
135
+ deprecated: data.deprecated,
136
+ defaultValue: data.defaultValue,
137
+ config: data.config ?? {},
138
+ createdAt: /* @__PURE__ */ new Date(),
139
+ updatedAt: /* @__PURE__ */ new Date()
140
+ };
141
+ stores.attributes.set(attr.id, attr);
142
+ return Promise.resolve(attr);
143
+ },
144
+ update(id, data) {
145
+ const existing = stores.attributes.get(id);
146
+ if (!existing) {
147
+ return Promise.reject(new Error(`Attribute ${id} not found`));
148
+ }
149
+ const updated = {
150
+ ...existing,
151
+ ...data,
152
+ updatedAt: /* @__PURE__ */ new Date()
153
+ };
154
+ stores.attributes.set(id, updated);
155
+ return Promise.resolve(updated);
156
+ },
157
+ delete(id) {
158
+ stores.attributes.delete(id);
159
+ return Promise.resolve();
160
+ },
161
+ deleteByNames(objectId, excludeNames) {
162
+ let count = 0;
163
+ for (const [id, attr] of stores.attributes.entries()) {
164
+ if (attr.objectId === objectId && !excludeNames.includes(attr.name)) {
165
+ stores.attributes.delete(id);
166
+ count++;
167
+ }
168
+ }
169
+ return Promise.resolve(count);
170
+ },
171
+ upsert(data) {
172
+ for (const attr of stores.attributes.values()) {
173
+ if (attr.objectId === data.objectId && attr.name === data.name) {
174
+ const updated = {
175
+ ...attr,
176
+ label: data.label ?? attr.label,
177
+ type: data.type ?? attr.type,
178
+ description: data.description ?? attr.description,
179
+ icon: data.icon ?? attr.icon,
180
+ system: data.system ?? attr.system,
181
+ required: data.required ?? attr.required,
182
+ unique: data.unique ?? attr.unique,
183
+ order: data.order ?? attr.order,
184
+ hidden: data.hidden ?? attr.hidden,
185
+ archived: data.archived ?? attr.archived,
186
+ deprecated: data.deprecated ?? attr.deprecated,
187
+ defaultValue: data.defaultValue ?? attr.defaultValue,
188
+ config: data.config ?? attr.config,
189
+ updatedAt: /* @__PURE__ */ new Date()
190
+ };
191
+ stores.attributes.set(attr.id, updated);
192
+ return Promise.resolve(updated);
193
+ }
194
+ }
195
+ return this.create(data);
196
+ }
197
+ };
198
+ }
199
+ function createMockUserProfilesRepository(stores) {
200
+ return {
201
+ findById(id) {
202
+ return Promise.resolve(stores.userProfiles.get(id) ?? null);
203
+ },
204
+ findByAuthId(authId) {
205
+ for (const profile of stores.userProfiles.values()) {
206
+ if (profile.authId === authId) {
207
+ return Promise.resolve(profile);
208
+ }
209
+ }
210
+ return Promise.resolve(null);
211
+ },
212
+ findByEmail(tenantId, email) {
213
+ for (const profile of stores.userProfiles.values()) {
214
+ if (profile.tenantId === tenantId && profile.email === email) {
215
+ return Promise.resolve(profile);
216
+ }
217
+ }
218
+ return Promise.resolve(null);
219
+ },
220
+ create(data) {
221
+ const profile = {
222
+ id: generateId(),
223
+ tenantId: data.tenantId,
224
+ authId: data.authId,
225
+ email: data.email,
226
+ firstName: data.firstName,
227
+ lastName: data.lastName,
228
+ avatarUrl: data.avatarUrl,
229
+ role: data.role ?? "member",
230
+ status: data.status ?? "active",
231
+ createdAt: /* @__PURE__ */ new Date(),
232
+ updatedAt: /* @__PURE__ */ new Date()
233
+ };
234
+ stores.userProfiles.set(profile.id, profile);
235
+ return Promise.resolve(profile);
236
+ },
237
+ update(id, data) {
238
+ const existing = stores.userProfiles.get(id);
239
+ if (!existing) {
240
+ return Promise.reject(new Error(`UserProfile ${id} not found`));
241
+ }
242
+ const updated = {
243
+ ...existing,
244
+ ...data,
245
+ updatedAt: /* @__PURE__ */ new Date()
246
+ };
247
+ stores.userProfiles.set(id, updated);
248
+ return Promise.resolve(updated);
249
+ },
250
+ delete(id) {
251
+ stores.userProfiles.delete(id);
252
+ return Promise.resolve();
253
+ },
254
+ list(tenantId, options) {
255
+ let results = Array.from(stores.userProfiles.values()).filter((p) => p.tenantId === tenantId);
256
+ if (options?.limit) {
257
+ results = results.slice(options.offset ?? 0, (options.offset ?? 0) + options.limit);
258
+ }
259
+ return Promise.resolve(results);
260
+ },
261
+ updateLastLogin(id) {
262
+ const profile = stores.userProfiles.get(id);
263
+ if (profile) {
264
+ profile.lastLoginAt = /* @__PURE__ */ new Date();
265
+ stores.userProfiles.set(id, profile);
266
+ }
267
+ return Promise.resolve();
268
+ }
269
+ };
270
+ }
271
+ function createMockFilesRepository(stores) {
272
+ return {
273
+ findById(id) {
274
+ const file2 = stores.files.get(id);
275
+ if (file2?.deletedAt) return Promise.resolve(null);
276
+ return Promise.resolve(file2 ?? null);
277
+ },
278
+ create(data) {
279
+ const file2 = {
280
+ id: generateId(),
281
+ tenantId: data.tenantId,
282
+ name: data.name,
283
+ originalName: data.originalName,
284
+ mimeType: data.mimeType,
285
+ size: data.size,
286
+ storageProvider: data.storageProvider,
287
+ storagePath: data.storagePath,
288
+ storageBucket: data.storageBucket,
289
+ url: data.url,
290
+ uploadedBy: data.uploadedBy,
291
+ folderPath: data.folderPath,
292
+ tags: data.tags,
293
+ visibility: data.visibility ?? "private",
294
+ allowedUsers: data.allowedUsers,
295
+ createdAt: /* @__PURE__ */ new Date(),
296
+ updatedAt: /* @__PURE__ */ new Date()
297
+ };
298
+ stores.files.set(file2.id, file2);
299
+ return Promise.resolve(file2);
300
+ },
301
+ update(id, data) {
302
+ const existing = stores.files.get(id);
303
+ if (!existing) {
304
+ return Promise.reject(new Error(`File ${id} not found`));
305
+ }
306
+ const updated = {
307
+ ...existing,
308
+ ...data,
309
+ updatedAt: /* @__PURE__ */ new Date()
310
+ };
311
+ stores.files.set(id, updated);
312
+ return Promise.resolve(updated);
313
+ },
314
+ delete(id) {
315
+ const file2 = stores.files.get(id);
316
+ if (file2) {
317
+ file2.deletedAt = /* @__PURE__ */ new Date();
318
+ stores.files.set(id, file2);
319
+ }
320
+ return Promise.resolve();
321
+ },
322
+ hardDelete(id) {
323
+ stores.files.delete(id);
324
+ return Promise.resolve();
325
+ },
326
+ list(tenantId, options) {
327
+ let results = Array.from(stores.files.values()).filter(
328
+ (f) => f.tenantId === tenantId && !f.deletedAt
329
+ );
330
+ if (options?.mimeType) {
331
+ results = results.filter((f) => f.mimeType === options.mimeType);
332
+ }
333
+ if (options?.limit) {
334
+ results = results.slice(options.offset ?? 0, (options.offset ?? 0) + options.limit);
335
+ }
336
+ return Promise.resolve(results);
337
+ },
338
+ findByFolder(tenantId, folderPath) {
339
+ const results = Array.from(stores.files.values()).filter(
340
+ (f) => f.tenantId === tenantId && f.folderPath === folderPath && !f.deletedAt
341
+ );
342
+ return Promise.resolve(results);
343
+ },
344
+ findByUploader(uploadedBy) {
345
+ const results = Array.from(stores.files.values()).filter(
346
+ (f) => f.uploadedBy === uploadedBy && !f.deletedAt
347
+ );
348
+ return Promise.resolve(results);
349
+ }
350
+ };
351
+ }
352
+ function createMockObjectRecordsRepository(stores) {
353
+ return {
354
+ findById(id) {
355
+ const internal = stores.objectRecords.get(id);
356
+ if (!internal) return Promise.resolve(null);
357
+ const { tenantId: _, ...record } = internal;
358
+ return Promise.resolve(record);
359
+ },
360
+ create(data) {
361
+ const internal = {
362
+ id: generateId(),
363
+ tenantId: data.tenantId,
364
+ objectId: data.objectId,
365
+ completionStatus: data.completionStatus,
366
+ values: data.data,
367
+ createdAt: /* @__PURE__ */ new Date(),
368
+ updatedAt: /* @__PURE__ */ new Date()
369
+ };
370
+ stores.objectRecords.set(internal.id, internal);
371
+ const { tenantId: _, ...record } = internal;
372
+ return Promise.resolve(record);
373
+ },
374
+ update(id, data) {
375
+ const existing = stores.objectRecords.get(id);
376
+ if (!existing) {
377
+ return Promise.reject(new Error(`ObjectRecord ${id} not found`));
378
+ }
379
+ const { __completionStatus, ...valueData } = data;
380
+ const updated = {
381
+ ...existing,
382
+ completionStatus: __completionStatus ?? existing.completionStatus,
383
+ values: { ...existing.values, ...valueData },
384
+ updatedAt: /* @__PURE__ */ new Date()
385
+ };
386
+ stores.objectRecords.set(id, updated);
387
+ const { tenantId: _, ...record } = updated;
388
+ return Promise.resolve(record);
389
+ },
390
+ delete(id) {
391
+ stores.objectRecords.delete(id);
392
+ return Promise.resolve();
393
+ },
394
+ list(tenantId, objectId, options) {
395
+ let results = Array.from(stores.objectRecords.values()).filter(
396
+ (r) => r.tenantId === tenantId && r.objectId === objectId
397
+ );
398
+ const total = results.length;
399
+ if (options?.limit) {
400
+ results = results.slice(options.offset ?? 0, (options.offset ?? 0) + options.limit);
401
+ }
402
+ const records = results.map(({ tenantId: _, ...r }) => r);
403
+ return Promise.resolve({ records, total });
404
+ },
405
+ search(tenantId, objectId, query, options) {
406
+ const lowerQuery = query.toLowerCase();
407
+ let results = Array.from(stores.objectRecords.values()).filter((r) => {
408
+ if (r.tenantId !== tenantId || r.objectId !== objectId) return false;
409
+ return Object.values(r.values).some(
410
+ (val) => String(val).toLowerCase().includes(lowerQuery)
411
+ );
412
+ });
413
+ const total = results.length;
414
+ if (options?.limit) {
415
+ results = results.slice(options.offset ?? 0, (options.offset ?? 0) + options.limit);
416
+ }
417
+ const records = results.map(({ tenantId: _, ...r }) => r);
418
+ return Promise.resolve({ records, total });
419
+ }
420
+ };
421
+ }
422
+ function createMockAdapter() {
423
+ const stores = {
424
+ objects: /* @__PURE__ */ new Map(),
425
+ attributes: /* @__PURE__ */ new Map(),
426
+ userProfiles: /* @__PURE__ */ new Map(),
427
+ files: /* @__PURE__ */ new Map(),
428
+ objectRecords: /* @__PURE__ */ new Map()
429
+ };
430
+ const adapter = {
431
+ objects: createMockObjectsRepository(stores),
432
+ attributes: createMockAttributesRepository(stores),
433
+ userProfiles: createMockUserProfilesRepository(stores),
434
+ files: createMockFilesRepository(stores),
435
+ objectRecords: createMockObjectRecordsRepository(stores),
436
+ async transaction(callback) {
437
+ return await callback(adapter);
438
+ },
439
+ // Expose stores for testing inspection
440
+ _stores: stores,
441
+ // Reset all stores (useful between tests)
442
+ reset() {
443
+ stores.objects.clear();
444
+ stores.attributes.clear();
445
+ stores.userProfiles.clear();
446
+ stores.files.clear();
447
+ stores.objectRecords.clear();
448
+ }
449
+ };
450
+ return adapter;
451
+ }
452
+
453
+ // src/runtime/services/file.service.ts
454
+ var FileService = class {
455
+ constructor(adapter, tenantId) {
456
+ this.adapter = adapter;
457
+ this.tenantId = tenantId;
458
+ }
459
+ /**
460
+ * Create a new file record (after upload to storage)
461
+ *
462
+ * @param data - File metadata
463
+ * @returns Created file record
464
+ *
465
+ * @example
466
+ * ```typescript
467
+ * const service = new FileService(adapter, "tenant-123");
468
+ *
469
+ * // After uploading to S3
470
+ * const file = await service.createFile({
471
+ * tenantId: "tenant-123",
472
+ * name: "contract-2025.pdf",
473
+ * originalName: "Contract Acme Corp 2025.pdf",
474
+ * mimeType: "application/pdf",
475
+ * size: 2458624,
476
+ * storageProvider: "s3",
477
+ * storagePath: "tenants/123/files/2025/contract.pdf",
478
+ * storageBucket: "my-app-files",
479
+ * url: "https://cdn.example.com/files/file-123",
480
+ * uploadedBy: "profile-456",
481
+ * visibility: "private"
482
+ * });
483
+ * ```
484
+ */
485
+ async createFile(data) {
486
+ if (data.tenantId !== this.tenantId) {
487
+ throw new Error(
488
+ `Tenant mismatch: service initialized with "${this.tenantId}" but data has "${data.tenantId}"`
489
+ );
490
+ }
491
+ return await this.adapter.files.create(data);
492
+ }
493
+ /**
494
+ * Get file by ID
495
+ */
496
+ async getFile(fileId) {
497
+ const file2 = await this.adapter.files.findById(fileId);
498
+ if (file2?.deletedAt) {
499
+ return null;
500
+ }
501
+ return file2;
502
+ }
503
+ /**
504
+ * Get file by ID or throw
505
+ */
506
+ async getFileOrThrow(fileId) {
507
+ const file2 = await this.getFile(fileId);
508
+ if (!file2) {
509
+ throw new Error(`File with id "${fileId}" not found`);
510
+ }
511
+ return file2;
512
+ }
513
+ /**
514
+ * Update file metadata
515
+ *
516
+ * @param fileId - File UUID
517
+ * @param data - Data to update
518
+ * @returns Updated file
519
+ */
520
+ async updateFile(fileId, data) {
521
+ await this.getFileOrThrow(fileId);
522
+ return await this.adapter.files.update(fileId, data);
523
+ }
524
+ /**
525
+ * Delete file (soft delete)
526
+ *
527
+ * @param fileId - File UUID
528
+ * @param options - Delete options
529
+ */
530
+ async deleteFile(fileId, options) {
531
+ if (options?.checkOwnership && options.userId) {
532
+ const file2 = await this.getFileOrThrow(fileId);
533
+ if (file2.uploadedBy !== options.userId) {
534
+ throw new Error("You can only delete files you uploaded");
535
+ }
536
+ }
537
+ if (options?.hard) {
538
+ await this.adapter.files.hardDelete(fileId);
539
+ } else {
540
+ await this.adapter.files.delete(fileId);
541
+ }
542
+ }
543
+ /**
544
+ * List files for the tenant
545
+ */
546
+ async listFiles(options) {
547
+ return await this.adapter.files.list(this.tenantId, options);
548
+ }
549
+ /**
550
+ * List files by folder
551
+ */
552
+ async listFilesByFolder(folderPath) {
553
+ return await this.adapter.files.findByFolder(this.tenantId, folderPath);
554
+ }
555
+ /**
556
+ * List files uploaded by a specific user
557
+ */
558
+ async listFilesByUploader(uploadedBy) {
559
+ return await this.adapter.files.findByUploader(uploadedBy);
560
+ }
561
+ /**
562
+ * Change file visibility
563
+ *
564
+ * @param fileId - File UUID
565
+ * @param visibility - New visibility level
566
+ * @param allowedUsers - Users allowed to access (if restricted)
567
+ */
568
+ async changeVisibility(fileId, visibility, allowedUsers) {
569
+ const updateData = { visibility };
570
+ if (visibility === "restricted" && allowedUsers) {
571
+ updateData.allowedUsers = allowedUsers;
572
+ }
573
+ return await this.updateFile(fileId, updateData);
574
+ }
575
+ /**
576
+ * Grant access to a file for specific users
577
+ *
578
+ * @param fileId - File UUID
579
+ * @param userIds - User IDs to grant access
580
+ */
581
+ async grantAccess(fileId, userIds) {
582
+ const file2 = await this.getFileOrThrow(fileId);
583
+ const currentAllowed = file2.allowedUsers || [];
584
+ const newAllowed = [.../* @__PURE__ */ new Set([...currentAllowed, ...userIds])];
585
+ return await this.updateFile(fileId, {
586
+ visibility: "restricted",
587
+ allowedUsers: newAllowed
588
+ });
589
+ }
590
+ /**
591
+ * Revoke access to a file for specific users
592
+ *
593
+ * @param fileId - File UUID
594
+ * @param userIds - User IDs to revoke access
595
+ */
596
+ async revokeAccess(fileId, userIds) {
597
+ const file2 = await this.getFileOrThrow(fileId);
598
+ const currentAllowed = file2.allowedUsers || [];
599
+ const newAllowed = currentAllowed.filter((id) => !userIds.includes(id));
600
+ return await this.updateFile(fileId, {
601
+ allowedUsers: newAllowed
602
+ });
603
+ }
604
+ /**
605
+ * Check if user has access to a file
606
+ *
607
+ * @param fileId - File UUID
608
+ * @param userId - User ID to check
609
+ * @returns true if user can access the file
610
+ */
611
+ async canAccess(fileId, userId) {
612
+ const file2 = await this.getFile(fileId);
613
+ if (!file2) {
614
+ return false;
615
+ }
616
+ if (file2.visibility === "public") {
617
+ return true;
618
+ }
619
+ if (file2.uploadedBy === userId) {
620
+ return true;
621
+ }
622
+ if (file2.visibility === "restricted") {
623
+ return file2.allowedUsers?.includes(userId) ?? false;
624
+ }
625
+ return false;
626
+ }
627
+ /**
628
+ * Move file to different folder
629
+ */
630
+ async moveToFolder(fileId, newFolderPath) {
631
+ return await this.updateFile(fileId, { folderPath: newFolderPath });
632
+ }
633
+ /**
634
+ * Add tags to file
635
+ */
636
+ async addTags(fileId, tags) {
637
+ const file2 = await this.getFileOrThrow(fileId);
638
+ const currentTags = file2.tags || [];
639
+ const newTags = [.../* @__PURE__ */ new Set([...currentTags, ...tags])];
640
+ return await this.updateFile(fileId, { tags: newTags });
641
+ }
642
+ /**
643
+ * Remove tags from file
644
+ */
645
+ async removeTags(fileId, tags) {
646
+ const file2 = await this.getFileOrThrow(fileId);
647
+ const currentTags = file2.tags || [];
648
+ const newTags = currentTags.filter((tag) => !tags.includes(tag));
649
+ return await this.updateFile(fileId, { tags: newTags });
650
+ }
651
+ };
652
+
653
+ // src/builders/attribute-builders.ts
654
+ import { z } from "zod";
655
+ var BaseAttributeBuilder = class {
656
+ constructor(type, name, label) {
657
+ this.validateVariableName(name);
658
+ this.attr = {
659
+ name,
660
+ label,
661
+ type,
662
+ required: false
663
+ };
664
+ }
665
+ /**
666
+ * Validate attribute name as valid variable identifier
667
+ * Accepts: camelCase, snake_case, PascalCase, UPPER_CASE
668
+ */
669
+ validateVariableName(name) {
670
+ const variableNameSchema = z.string().min(1, "Attribute name cannot be empty").max(63, "Attribute name is too long (max 63 characters)").regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/, {
671
+ message: "Invalid attribute name format.\nName must be a valid variable identifier:\n \u2705 Valid: 'firstName', 'first_name', 'FirstName', 'FIRST_NAME'\n \u274C Invalid: 'first-name', 'first name', '123name', 'first.name'"
672
+ });
673
+ try {
674
+ variableNameSchema.parse(name);
675
+ } catch (error) {
676
+ if (error instanceof z.ZodError) {
677
+ throw new Error(`[AttributeBuilder] ${error.errors[0].message}`);
678
+ }
679
+ throw error;
680
+ }
681
+ }
682
+ setRequired(value) {
683
+ this.attr.required = value;
684
+ }
685
+ system() {
686
+ this.attr.system = true;
687
+ return this;
688
+ }
689
+ disabled() {
690
+ this.attr.disabled = true;
691
+ return this;
692
+ }
693
+ hidden() {
694
+ this.attr.hidden = true;
695
+ return this;
696
+ }
697
+ placeholder(value) {
698
+ this.attr.placeholder = value;
699
+ return this;
700
+ }
701
+ description(value) {
702
+ this.attr.description = value;
703
+ return this;
704
+ }
705
+ icon(value) {
706
+ this.attr.icon = value;
707
+ return this;
708
+ }
709
+ order(value) {
710
+ this.attr.order = value;
711
+ return this;
712
+ }
713
+ defaultValue(value) {
714
+ this.attr.defaultValue = value;
715
+ return this;
716
+ }
717
+ metadata(value) {
718
+ this.attr.metadata = value;
719
+ return this;
720
+ }
721
+ build() {
722
+ return this.attr;
723
+ }
724
+ };
725
+ var TextAttributeBuilder = class extends BaseAttributeBuilder {
726
+ constructor(name, label) {
727
+ super("text", name, label);
728
+ }
729
+ minLength(value) {
730
+ this.attr.minLength = value;
731
+ return this;
732
+ }
733
+ maxLength(value) {
734
+ this.attr.maxLength = value;
735
+ return this;
736
+ }
737
+ pattern(value) {
738
+ this.attr.pattern = value;
739
+ return this;
740
+ }
741
+ required() {
742
+ this.setRequired(true);
743
+ return this;
744
+ }
745
+ optional() {
746
+ this.setRequired(false);
747
+ return this;
748
+ }
749
+ /**
750
+ * Set email validation pattern
751
+ */
752
+ email() {
753
+ this.attr.pattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
754
+ return this;
755
+ }
756
+ /**
757
+ * Set URL validation pattern
758
+ */
759
+ url() {
760
+ this.attr.pattern = "^https?:\\/\\/[\\w\\-]+(\\.[\\w\\-]+)+[/#?]?.*$";
761
+ return this;
762
+ }
763
+ /**
764
+ * Set slug validation pattern
765
+ */
766
+ slug() {
767
+ this.attr.pattern = "^[a-z0-9]+(?:-[a-z0-9]+)*$";
768
+ return this;
769
+ }
770
+ };
771
+ function text(config) {
772
+ return new TextAttributeBuilder(config.name, config.label);
773
+ }
774
+ var TextAreaAttributeBuilder = class extends BaseAttributeBuilder {
775
+ constructor(name, label) {
776
+ super("textarea", name, label);
777
+ }
778
+ required() {
779
+ this.setRequired(true);
780
+ return this;
781
+ }
782
+ optional() {
783
+ this.setRequired(false);
784
+ return this;
785
+ }
786
+ };
787
+ function textarea(config) {
788
+ return new TextAreaAttributeBuilder(config.name, config.label);
789
+ }
790
+ var NumberAttributeBuilder = class extends BaseAttributeBuilder {
791
+ constructor(name, label) {
792
+ super("number", name, label);
793
+ }
794
+ min(value) {
795
+ this.attr.min = value;
796
+ return this;
797
+ }
798
+ max(value) {
799
+ this.attr.max = value;
800
+ return this;
801
+ }
802
+ integer() {
803
+ this.attr.unit = "integer";
804
+ this.attr.decimals = 0;
805
+ return this;
806
+ }
807
+ decimal(decimals = 2) {
808
+ this.attr.unit = "decimal";
809
+ this.attr.decimals = decimals;
810
+ return this;
811
+ }
812
+ percentage() {
813
+ this.attr.unit = "percentage";
814
+ return this;
815
+ }
816
+ required() {
817
+ this.setRequired(true);
818
+ return this;
819
+ }
820
+ optional() {
821
+ this.setRequired(false);
822
+ return this;
823
+ }
824
+ };
825
+ function number(config) {
826
+ return new NumberAttributeBuilder(config.name, config.label);
827
+ }
828
+ var CheckboxAttributeBuilder = class extends BaseAttributeBuilder {
829
+ constructor(name, label) {
830
+ super("checkbox", name, label);
831
+ }
832
+ required() {
833
+ this.setRequired(true);
834
+ return this;
835
+ }
836
+ optional() {
837
+ this.setRequired(false);
838
+ return this;
839
+ }
840
+ };
841
+ function checkbox(config) {
842
+ return new CheckboxAttributeBuilder(config.name, config.label);
843
+ }
844
+ var DateAttributeBuilder = class extends BaseAttributeBuilder {
845
+ constructor(name, label) {
846
+ super("date", name, label);
847
+ }
848
+ format(value) {
849
+ this.attr.dateFormat = value;
850
+ return this;
851
+ }
852
+ minDate(value) {
853
+ this.attr.minDate = value;
854
+ return this;
855
+ }
856
+ maxDate(value) {
857
+ this.attr.maxDate = value;
858
+ return this;
859
+ }
860
+ required() {
861
+ this.setRequired(true);
862
+ return this;
863
+ }
864
+ optional() {
865
+ this.setRequired(false);
866
+ return this;
867
+ }
868
+ };
869
+ function date(config) {
870
+ return new DateAttributeBuilder(config.name, config.label);
871
+ }
872
+ var PhoneAttributeBuilder = class extends BaseAttributeBuilder {
873
+ constructor(name, label) {
874
+ super("phone", name, label);
875
+ }
876
+ defaultCountry(value) {
877
+ this.attr.defaultCountryCode = value;
878
+ return this;
879
+ }
880
+ required() {
881
+ this.setRequired(true);
882
+ return this;
883
+ }
884
+ optional() {
885
+ this.setRequired(false);
886
+ return this;
887
+ }
888
+ };
889
+ function phone(config) {
890
+ return new PhoneAttributeBuilder(config.name, config.label);
891
+ }
892
+ var CurrencyAttributeBuilder = class extends BaseAttributeBuilder {
893
+ constructor(name, label) {
894
+ super("currency", name, label);
895
+ }
896
+ defaultCurrency(value) {
897
+ this.attr.defaultCurrency = value;
898
+ return this;
899
+ }
900
+ allowedCurrencies(value) {
901
+ this.attr.allowedCurrencies = value;
902
+ return this;
903
+ }
904
+ required() {
905
+ this.setRequired(true);
906
+ return this;
907
+ }
908
+ optional() {
909
+ this.setRequired(false);
910
+ return this;
911
+ }
912
+ };
913
+ function currency(config) {
914
+ return new CurrencyAttributeBuilder(config.name, config.label);
915
+ }
916
+ var StatusAttributeBuilder = class extends BaseAttributeBuilder {
917
+ constructor(name, label) {
918
+ super("status", name, label);
919
+ }
920
+ /**
921
+ * Set all options at once (preserves literal types for values)
922
+ */
923
+ options(value) {
924
+ this.attr.options = value;
925
+ return this;
926
+ }
927
+ option(config) {
928
+ if (!this.attr.options) {
929
+ this.attr.options = [];
930
+ }
931
+ this.attr.options.push(config);
932
+ return this;
933
+ }
934
+ required() {
935
+ this.setRequired(true);
936
+ return this;
937
+ }
938
+ optional() {
939
+ this.setRequired(false);
940
+ return this;
941
+ }
942
+ };
943
+ function status(config) {
944
+ return new StatusAttributeBuilder(config.name, config.label);
945
+ }
946
+ var SelectAttributeBuilder = class extends BaseAttributeBuilder {
947
+ constructor(name, label) {
948
+ super("select", name, label);
949
+ }
950
+ /**
951
+ * Set all options at once (preserves literal types for values)
952
+ */
953
+ options(value) {
954
+ this.attr.options = value;
955
+ return this;
956
+ }
957
+ option(config) {
958
+ if (!this.attr.options) {
959
+ this.attr.options = [];
960
+ }
961
+ this.attr.options.push(config);
962
+ return this;
963
+ }
964
+ required() {
965
+ this.setRequired(true);
966
+ return this;
967
+ }
968
+ optional() {
969
+ this.setRequired(false);
970
+ return this;
971
+ }
972
+ };
973
+ function select(config) {
974
+ return new SelectAttributeBuilder(config.name, config.label);
975
+ }
976
+ var MultiselectAttributeBuilder = class extends BaseAttributeBuilder {
977
+ constructor(name, label) {
978
+ super("multiselect", name, label);
979
+ }
980
+ /**
981
+ * Set all options at once (preserves literal types for values)
982
+ */
983
+ options(value) {
984
+ this.attr.options = value;
985
+ return this;
986
+ }
987
+ option(config) {
988
+ if (!this.attr.options) {
989
+ this.attr.options = [];
990
+ }
991
+ this.attr.options.push(config);
992
+ return this;
993
+ }
994
+ required() {
995
+ this.setRequired(true);
996
+ return this;
997
+ }
998
+ optional() {
999
+ this.setRequired(false);
1000
+ return this;
1001
+ }
1002
+ };
1003
+ function multiselect(config) {
1004
+ return new MultiselectAttributeBuilder(config.name, config.label);
1005
+ }
1006
+ var LocationAttributeBuilder = class extends BaseAttributeBuilder {
1007
+ constructor(name, label) {
1008
+ super("location", name, label);
1009
+ }
1010
+ granularity(value) {
1011
+ this.attr.granularity = value;
1012
+ return this;
1013
+ }
1014
+ enableAutocomplete() {
1015
+ this.attr.enableAutocomplete = true;
1016
+ return this;
1017
+ }
1018
+ enableMap() {
1019
+ this.attr.enableMap = true;
1020
+ return this;
1021
+ }
1022
+ defaultCountry(value) {
1023
+ this.attr.defaultCountry = value;
1024
+ return this;
1025
+ }
1026
+ allowedCountries(value) {
1027
+ this.attr.allowedCountries = value;
1028
+ return this;
1029
+ }
1030
+ displayFormat(value) {
1031
+ this.attr.displayFormat = value;
1032
+ return this;
1033
+ }
1034
+ required() {
1035
+ this.setRequired(true);
1036
+ return this;
1037
+ }
1038
+ optional() {
1039
+ this.setRequired(false);
1040
+ return this;
1041
+ }
1042
+ };
1043
+ function location(config) {
1044
+ return new LocationAttributeBuilder(config.name, config.label);
1045
+ }
1046
+ var TimestampAttributeBuilder = class extends BaseAttributeBuilder {
1047
+ constructor(name, label) {
1048
+ super("timestamp", name, label);
1049
+ }
1050
+ autoUpdate() {
1051
+ this.attr.autoUpdate = true;
1052
+ return this;
1053
+ }
1054
+ required() {
1055
+ this.setRequired(true);
1056
+ return this;
1057
+ }
1058
+ optional() {
1059
+ this.setRequired(false);
1060
+ return this;
1061
+ }
1062
+ };
1063
+ function timestamp(config) {
1064
+ return new TimestampAttributeBuilder(config.name, config.label);
1065
+ }
1066
+ var FileAttributeBuilder = class extends BaseAttributeBuilder {
1067
+ constructor(name, label) {
1068
+ super("file", name, label);
1069
+ }
1070
+ maxFiles(value) {
1071
+ this.attr.maxFiles = value;
1072
+ return this;
1073
+ }
1074
+ maxSize(bytes) {
1075
+ this.attr.maxSize = bytes;
1076
+ return this;
1077
+ }
1078
+ allowedTypes(types) {
1079
+ this.attr.allowedTypes = types;
1080
+ return this;
1081
+ }
1082
+ verification(config) {
1083
+ this.attr.verification = config;
1084
+ return this;
1085
+ }
1086
+ required() {
1087
+ this.setRequired(true);
1088
+ return this;
1089
+ }
1090
+ optional() {
1091
+ this.setRequired(false);
1092
+ return this;
1093
+ }
1094
+ };
1095
+ function file(config) {
1096
+ return new FileAttributeBuilder(config.name, config.label);
1097
+ }
1098
+ var UserAttributeBuilder = class extends BaseAttributeBuilder {
1099
+ constructor(name, label) {
1100
+ super("user", name, label);
1101
+ }
1102
+ allowedRoles(roles) {
1103
+ this.attr.allowedRoles = roles;
1104
+ return this;
1105
+ }
1106
+ required() {
1107
+ this.setRequired(true);
1108
+ return this;
1109
+ }
1110
+ optional() {
1111
+ this.setRequired(false);
1112
+ return this;
1113
+ }
1114
+ };
1115
+ function user(config) {
1116
+ return new UserAttributeBuilder(config.name, config.label);
1117
+ }
1118
+ var SingleRelationAttributeBuilder = class extends BaseAttributeBuilder {
1119
+ constructor(name, label) {
1120
+ super("relation", name, label);
1121
+ this.attr.cardinality = "one";
1122
+ this.attr.targets = [];
1123
+ }
1124
+ /**
1125
+ * Add a target object that can be linked
1126
+ * @param objectName - Name of the object (e.g., "companies")
1127
+ * @param options - Display template and filter options
1128
+ */
1129
+ to(objectName, options) {
1130
+ const target = {
1131
+ object: objectName,
1132
+ ...options
1133
+ };
1134
+ this.attr.targets?.push(target);
1135
+ return this;
1136
+ }
1137
+ /**
1138
+ * Set the deletion strategy when target record is deleted
1139
+ * @param strategy - "restrict" (default), "cascade", or "set_null"
1140
+ */
1141
+ onDelete(strategy) {
1142
+ this.attr.onDelete = strategy;
1143
+ return this;
1144
+ }
1145
+ /**
1146
+ * Convert to a multi-relation (cardinality: "many")
1147
+ */
1148
+ many() {
1149
+ const multiBuilder = new MultiRelationAttributeBuilder(
1150
+ this.attr.name,
1151
+ this.attr.label ?? "",
1152
+ {
1153
+ targets: this.attr.targets,
1154
+ onDelete: this.attr.onDelete,
1155
+ isRequired: this.attr.required
1156
+ }
1157
+ );
1158
+ return multiBuilder;
1159
+ }
1160
+ required() {
1161
+ this.setRequired(true);
1162
+ return this;
1163
+ }
1164
+ optional() {
1165
+ this.setRequired(false);
1166
+ return this;
1167
+ }
1168
+ };
1169
+ var MultiRelationAttributeBuilder = class extends BaseAttributeBuilder {
1170
+ constructor(name, label, initOptions) {
1171
+ super("relation", name, label);
1172
+ this.attr.cardinality = "many";
1173
+ this.attr.targets = initOptions?.targets ?? [];
1174
+ this.attr.defaultValue = [];
1175
+ if (initOptions?.onDelete) {
1176
+ this.attr.onDelete = initOptions.onDelete;
1177
+ }
1178
+ if (initOptions?.isRequired) {
1179
+ this.setRequired(true);
1180
+ }
1181
+ }
1182
+ /**
1183
+ * Add a target object that can be linked
1184
+ * @param objectName - Name of the object (e.g., "companies")
1185
+ * @param options - Display template and filter options
1186
+ */
1187
+ to(objectName, options) {
1188
+ const target = {
1189
+ object: objectName,
1190
+ ...options
1191
+ };
1192
+ this.attr.targets?.push(target);
1193
+ return this;
1194
+ }
1195
+ /**
1196
+ * Set the deletion strategy when target record is deleted
1197
+ * @param strategy - "restrict" (default), "cascade", or "set_null"
1198
+ */
1199
+ onDelete(strategy) {
1200
+ this.attr.onDelete = strategy;
1201
+ return this;
1202
+ }
1203
+ /**
1204
+ * Set minimum number of relations required
1205
+ */
1206
+ minItems(count) {
1207
+ this.attr.minItems = count;
1208
+ return this;
1209
+ }
1210
+ /**
1211
+ * Set maximum number of relations allowed
1212
+ */
1213
+ maxItems(count) {
1214
+ this.attr.maxItems = count;
1215
+ return this;
1216
+ }
1217
+ required() {
1218
+ this.setRequired(true);
1219
+ return this;
1220
+ }
1221
+ optional() {
1222
+ this.setRequired(false);
1223
+ return this;
1224
+ }
1225
+ };
1226
+ function relation(config) {
1227
+ return new SingleRelationAttributeBuilder(config.name, config.label);
1228
+ }
1229
+ var RatingAttributeBuilder = class extends BaseAttributeBuilder {
1230
+ constructor(name, label) {
1231
+ super("rating", name, label);
1232
+ }
1233
+ max(value) {
1234
+ this.attr.max = value;
1235
+ return this;
1236
+ }
1237
+ iconType(value) {
1238
+ this.attr.iconType = value;
1239
+ return this;
1240
+ }
1241
+ required() {
1242
+ this.setRequired(true);
1243
+ return this;
1244
+ }
1245
+ optional() {
1246
+ this.setRequired(false);
1247
+ return this;
1248
+ }
1249
+ };
1250
+ function rating(config) {
1251
+ return new RatingAttributeBuilder(config.name, config.label);
1252
+ }
1253
+
1254
+ // src/builders/object-builder.ts
1255
+ import z2 from "zod";
1256
+ var ObjectBuilder = class {
1257
+ constructor(config) {
1258
+ this.validateName(config.name);
1259
+ this.obj = {
1260
+ name: config.name,
1261
+ label: config.label,
1262
+ attributes: [],
1263
+ system: false
1264
+ };
1265
+ }
1266
+ /**
1267
+ * Set object description
1268
+ */
1269
+ description(value) {
1270
+ this.obj.description = value;
1271
+ return this;
1272
+ }
1273
+ /**
1274
+ * Set object icon
1275
+ */
1276
+ icon(value) {
1277
+ this.obj.icon = value;
1278
+ return this;
1279
+ }
1280
+ /**
1281
+ * Mark as system/native object (defined in code, protected)
1282
+ * System objects cannot be deleted/modified by clients
1283
+ */
1284
+ system() {
1285
+ this.obj.system = true;
1286
+ return this;
1287
+ }
1288
+ /**
1289
+ * Add an attribute to the object with type accumulation
1290
+ *
1291
+ * The return type accumulates the attribute's name and value type
1292
+ * into the TRecord generic, enabling full inference.
1293
+ *
1294
+ * @param attr - Attribute definition or builder (auto-builds if builder)
1295
+ */
1296
+ attribute(attr) {
1297
+ if (!this.obj.attributes) {
1298
+ this.obj.attributes = [];
1299
+ }
1300
+ const finalAttr = "build" in attr && typeof attr.build === "function" ? attr.build() : attr;
1301
+ this.obj.attributes.push(finalAttr);
1302
+ return this;
1303
+ }
1304
+ /**
1305
+ * Add multiple attributes at once
1306
+ * Note: For full type inference, prefer chaining .attribute() calls
1307
+ */
1308
+ attributes(attrs) {
1309
+ if (!this.obj.attributes) {
1310
+ this.obj.attributes = [];
1311
+ }
1312
+ const finalAttrs = attrs.map((attr) => "build" in attr ? attr.build() : attr);
1313
+ this.obj.attributes.push(...finalAttrs);
1314
+ return this;
1315
+ }
1316
+ /**
1317
+ * Set metadata
1318
+ */
1319
+ metadata(value) {
1320
+ this.obj.metadata = value;
1321
+ return this;
1322
+ }
1323
+ /**
1324
+ * Set the title attribute (the text attribute used to display the object's name)
1325
+ * Must reference a text attribute that has been added to the object.
1326
+ * If not set, the first text attribute becomes the title attribute.
1327
+ *
1328
+ * @param attributeName - The name of a text attribute
1329
+ */
1330
+ titleAttribute(attributeName) {
1331
+ this._titleAttribute = attributeName;
1332
+ return this;
1333
+ }
1334
+ /**
1335
+ * Build the final ObjectDefinition
1336
+ * @throws Error if required fields are missing or invalid
1337
+ */
1338
+ build() {
1339
+ if (!this.obj.name) {
1340
+ throw new Error("[ObjectBuilder] Missing required field: name");
1341
+ }
1342
+ if (!this.obj.label) {
1343
+ throw new Error("[ObjectBuilder] Missing required field: label");
1344
+ }
1345
+ const textAttributes = (this.obj.attributes ?? []).filter((attr) => attr.type === "text");
1346
+ if (textAttributes.length === 0) {
1347
+ throw new Error(
1348
+ `[ObjectBuilder] Object "${this.obj.name}" must have at least one text attribute.
1349
+ A text attribute is required to serve as the object's title/display name.
1350
+ \u2705 Example: .attribute(text({ name: 'name', label: 'Name' }).required())`
1351
+ );
1352
+ }
1353
+ const titleAttrName = this._titleAttribute ?? textAttributes[0].name;
1354
+ const titleAttr = textAttributes.find((attr) => attr.name === titleAttrName);
1355
+ if (!titleAttr) {
1356
+ const availableTextAttrs = textAttributes.map((a) => `'${a.name}'`).join(", ");
1357
+ throw new Error(
1358
+ `[ObjectBuilder] titleAttribute "${titleAttrName}" must be a text attribute.
1359
+ Available text attributes: ${availableTextAttrs}`
1360
+ );
1361
+ }
1362
+ return {
1363
+ ...this.obj,
1364
+ titleAttribute: titleAttrName
1365
+ };
1366
+ }
1367
+ /**
1368
+ * Validate object name format
1369
+ * Must be kebab-case (slug format), max 63 characters
1370
+ * @internal
1371
+ */
1372
+ validateName(name) {
1373
+ if (name === void 0) {
1374
+ return;
1375
+ }
1376
+ const objectNameSchema = z2.string().min(1, "Object name cannot be empty").max(63, "Object name is too long (max 63 characters)").regex(/^[a-z][a-z0-9-]*$/, {
1377
+ message: "Invalid object name format.\nName must be in kebab-case (slug format):\n \u2705 Valid: 'products', 'user-profiles', 'order-items'\n \u274C Invalid: 'Products', 'userProfiles', 'user_profiles'"
1378
+ });
1379
+ try {
1380
+ objectNameSchema.parse(name);
1381
+ } catch (error) {
1382
+ if (error instanceof z2.ZodError) {
1383
+ throw new Error(`[ObjectBuilder] ${error.errors[0].message}`);
1384
+ }
1385
+ throw error;
1386
+ }
1387
+ }
1388
+ };
1389
+ function object(config) {
1390
+ return new ObjectBuilder(config);
1391
+ }
1392
+
1393
+ // src/runtime/services/object-schema.service.ts
1394
+ var ObjectSchemaService = class {
1395
+ constructor(adapter, nativeRegistry) {
1396
+ this.adapter = adapter;
1397
+ this.nativeRegistry = nativeRegistry;
1398
+ }
1399
+ /**
1400
+ * Create a new custom object
1401
+ * Validates name format and ensures system=false
1402
+ *
1403
+ * @param definition - Object definition (attributes can be raw definitions or builders)
1404
+ * @param tenantId - Tenant ID owning the object
1405
+ * @returns Created ObjectDefinition
1406
+ */
1407
+ async createCustomObject(definition, tenantId) {
1408
+ const builder = object({
1409
+ name: definition.name,
1410
+ label: definition.label
1411
+ });
1412
+ if (definition.description) builder.description(definition.description);
1413
+ if (definition.icon) builder.icon(definition.icon);
1414
+ if (definition.metadata) builder.metadata(definition.metadata);
1415
+ if (definition.attributes && definition.attributes.length > 0) {
1416
+ builder.attributes(definition.attributes);
1417
+ }
1418
+ const objectDef = builder.build();
1419
+ const existing = await this.adapter.objects.findByName(tenantId, objectDef.name);
1420
+ if (existing) {
1421
+ throw new Error(`Object with name "${objectDef.name}" already exists`);
1422
+ }
1423
+ const metadata = {
1424
+ ...objectDef.metadata,
1425
+ icon: objectDef.icon
1426
+ };
1427
+ const dbObject = await this.adapter.objects.create({
1428
+ tenantId,
1429
+ name: objectDef.name,
1430
+ label: objectDef.label,
1431
+ description: objectDef.description,
1432
+ system: false,
1433
+ // Always false for custom objects
1434
+ metadata
1435
+ });
1436
+ const createdAttributes = [];
1437
+ if (objectDef.attributes.length > 0) {
1438
+ for (const attr of objectDef.attributes) {
1439
+ const dbAttr = await this.adapter.attributes.create({
1440
+ objectId: dbObject.id,
1441
+ name: attr.name,
1442
+ label: attr.label,
1443
+ type: attr.type,
1444
+ required: attr.required ?? false,
1445
+ system: false,
1446
+ // Always false for custom attributes
1447
+ config: {
1448
+ ...attr,
1449
+ // Remove common fields stored in columns
1450
+ id: void 0,
1451
+ name: void 0,
1452
+ label: void 0,
1453
+ type: void 0,
1454
+ required: void 0,
1455
+ system: void 0
1456
+ }
1457
+ });
1458
+ createdAttributes.push(this.convertDBAttributeToAttribute(dbAttr));
1459
+ }
1460
+ }
1461
+ return {
1462
+ ...objectDef,
1463
+ id: dbObject.id,
1464
+ attributes: createdAttributes,
1465
+ system: false
1466
+ };
1467
+ }
1468
+ /**
1469
+ * Add a custom attribute to an existing object
1470
+ * Can be used on both custom and native objects
1471
+ * Attributes added via API are always custom (system=false)
1472
+ *
1473
+ * @param objectId - Object UUID from database
1474
+ * @param attribute - Attribute definition
1475
+ * @returns Created Attribute
1476
+ */
1477
+ async addAttributeToObject(objectId, attribute) {
1478
+ this.validateAttributeName(attribute.name);
1479
+ const dbObject = await this.adapter.objects.findById(objectId);
1480
+ if (!dbObject) {
1481
+ throw new Error(`Object with id "${objectId}" not found`);
1482
+ }
1483
+ const existingAttributes = await this.adapter.attributes.findByObjectId(objectId);
1484
+ const duplicate = existingAttributes.find((attr) => attr.name === attribute.name);
1485
+ if (duplicate) {
1486
+ throw new Error(
1487
+ `Attribute with name "${attribute.name}" already exists on object "${dbObject.name}"`
1488
+ );
1489
+ }
1490
+ const dbAttr = await this.adapter.attributes.create({
1491
+ objectId,
1492
+ name: attribute.name,
1493
+ label: attribute.label,
1494
+ type: attribute.type,
1495
+ required: attribute.required ?? false,
1496
+ unique: attribute.unique ?? false,
1497
+ system: false,
1498
+ // Always false for attributes added via API
1499
+ config: {
1500
+ // Store all extra properties in config
1501
+ ...attribute,
1502
+ // Remove common fields stored in columns
1503
+ name: void 0,
1504
+ label: void 0,
1505
+ type: void 0,
1506
+ required: void 0,
1507
+ unique: void 0,
1508
+ system: void 0
1509
+ }
1510
+ });
1511
+ return this.convertDBAttributeToAttribute(dbAttr);
1512
+ }
1513
+ /**
1514
+ * Update an attribute
1515
+ * Can only update custom attributes (system=false)
1516
+ *
1517
+ * @param attributeId - Attribute UUID
1518
+ * @param updates - Partial attribute updates
1519
+ * @returns Updated Attribute
1520
+ */
1521
+ async updateAttribute(attributeId, updates) {
1522
+ const dbAttr = await this.adapter.attributes.findById(attributeId);
1523
+ if (!dbAttr) {
1524
+ throw new Error(`Attribute with id "${attributeId}" not found`);
1525
+ }
1526
+ if (dbAttr.system) {
1527
+ throw new Error(
1528
+ `Cannot modify system attribute "${dbAttr.name}". System attributes are protected.`
1529
+ );
1530
+ }
1531
+ if (updates.name && updates.name !== dbAttr.name) {
1532
+ throw new Error(
1533
+ "Attribute name cannot be changed after creation. Create a new attribute instead."
1534
+ );
1535
+ }
1536
+ if (updates.type && updates.type !== dbAttr.type) {
1537
+ throw new Error(
1538
+ "Attribute type cannot be changed after creation. Create a new attribute instead."
1539
+ );
1540
+ }
1541
+ const updatedDbAttr = await this.adapter.attributes.update(attributeId, {
1542
+ label: updates.label,
1543
+ required: updates.required,
1544
+ config: updates ? {
1545
+ ...dbAttr.config,
1546
+ ...updates,
1547
+ // Remove common fields
1548
+ name: void 0,
1549
+ label: void 0,
1550
+ type: void 0,
1551
+ required: void 0,
1552
+ unique: void 0,
1553
+ system: void 0
1554
+ } : dbAttr.config
1555
+ });
1556
+ return this.convertDBAttributeToAttribute(updatedDbAttr);
1557
+ }
1558
+ /**
1559
+ * Delete an attribute
1560
+ * Can only delete custom attributes (system=false)
1561
+ *
1562
+ * @param attributeId - Attribute UUID
1563
+ */
1564
+ async deleteAttribute(attributeId) {
1565
+ const dbAttr = await this.adapter.attributes.findById(attributeId);
1566
+ if (!dbAttr) {
1567
+ throw new Error(`Attribute with id "${attributeId}" not found`);
1568
+ }
1569
+ if (dbAttr.system) {
1570
+ throw new Error(
1571
+ `Cannot delete system attribute "${dbAttr.name}". System attributes are protected.`
1572
+ );
1573
+ }
1574
+ await this.adapter.attributes.delete(attributeId);
1575
+ }
1576
+ /**
1577
+ * List attributes for an object
1578
+ *
1579
+ * @param objectId - Object UUID
1580
+ * @param options - Filter options
1581
+ * @returns List of attributes
1582
+ */
1583
+ async listAttributes(objectId, options) {
1584
+ const dbAttributes = await this.adapter.attributes.findByObjectId(objectId);
1585
+ let filtered = dbAttributes;
1586
+ if (options?.systemOnly) {
1587
+ filtered = dbAttributes.filter((attr) => attr.system);
1588
+ } else if (options?.customOnly) {
1589
+ filtered = dbAttributes.filter((attr) => !attr.system);
1590
+ }
1591
+ return filtered.map((attr) => this.convertDBAttributeToAttribute(attr));
1592
+ }
1593
+ /**
1594
+ * Validate attribute name format (variable identifier)
1595
+ * @internal
1596
+ */
1597
+ validateAttributeName(name) {
1598
+ const variableNameRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
1599
+ if (!name || name.length === 0) {
1600
+ throw new Error("Attribute name cannot be empty");
1601
+ }
1602
+ if (name.length > 63) {
1603
+ throw new Error("Attribute name is too long (max 63 characters)");
1604
+ }
1605
+ if (!variableNameRegex.test(name)) {
1606
+ throw new Error(
1607
+ "Invalid attribute name format.\nName must be a valid variable identifier:\n \u2705 Valid: 'firstName', 'first_name', 'FirstName', 'FIRST_NAME'\n \u274C Invalid: 'first-name', 'first name', '123name', 'first.name'"
1608
+ );
1609
+ }
1610
+ }
1611
+ /**
1612
+ * Get complete object schema (system + custom attributes)
1613
+ * If object is native, merges registry definition with DB custom attributes
1614
+ * If object is custom, returns DB definition only
1615
+ *
1616
+ * @param objectId - Object UUID from database
1617
+ * @returns Complete ObjectDefinition with all attributes
1618
+ *
1619
+ * @example
1620
+ * ```typescript
1621
+ * const service = new ObjectSchemaService(adapter, registry);
1622
+ * const productSchema = await service.getObjectSchema("obj-123");
1623
+ *
1624
+ * console.log(productSchema.attributes); // System + custom attributes
1625
+ * ```
1626
+ */
1627
+ async getObjectSchema(objectId) {
1628
+ const dbObject = await this.adapter.objects.findById(objectId);
1629
+ if (!dbObject) {
1630
+ throw new Error(`Object with id "${objectId}" not found`);
1631
+ }
1632
+ const dbAttributes = await this.adapter.attributes.findByObjectId(objectId);
1633
+ if (dbObject.system) {
1634
+ return this.mergeNativeObject(dbObject, dbAttributes);
1635
+ }
1636
+ return this.convertDBObjectToDefinition(dbObject, dbAttributes);
1637
+ }
1638
+ /**
1639
+ * Get object schema by name (for system/native objects)
1640
+ */
1641
+ async getObjectSchemaByName(name) {
1642
+ const dbObject = await this.adapter.objects.findSystemByName(name);
1643
+ if (!dbObject) {
1644
+ throw new Error(`Native object "${name}" not found in database`);
1645
+ }
1646
+ const dbAttributes = await this.adapter.attributes.findByObjectId(dbObject.id);
1647
+ return this.mergeNativeObject(dbObject, dbAttributes);
1648
+ }
1649
+ /**
1650
+ * List all object schemas for a tenant
1651
+ */
1652
+ async listObjectSchemas(tenantId) {
1653
+ const dbObjects = await this.adapter.objects.list(tenantId);
1654
+ const schemas = await Promise.all(
1655
+ dbObjects.map(async (dbObject) => {
1656
+ const dbAttributes = await this.adapter.attributes.findByObjectId(dbObject.id);
1657
+ if (dbObject.system) {
1658
+ return this.mergeNativeObject(dbObject, dbAttributes);
1659
+ }
1660
+ return this.convertDBObjectToDefinition(dbObject, dbAttributes);
1661
+ })
1662
+ );
1663
+ return schemas;
1664
+ }
1665
+ /**
1666
+ * Merge native object from registry with custom attributes from DB
1667
+ * @internal
1668
+ */
1669
+ mergeNativeObject(dbObject, dbAttributes) {
1670
+ const nativeObject = this.nativeRegistry.getByName(dbObject.name);
1671
+ if (!nativeObject) {
1672
+ throw new Error(
1673
+ `Native object "${dbObject.name}" not found in registry. Make sure to register it before querying.`
1674
+ );
1675
+ }
1676
+ const systemAttributes = dbAttributes.filter((attr) => attr.system);
1677
+ const customAttributes = dbAttributes.filter((attr) => !attr.system);
1678
+ const systemAttrs = systemAttributes.map(
1679
+ (dbAttr) => this.convertDBAttributeToAttribute(dbAttr)
1680
+ );
1681
+ const customAttrs = customAttributes.map(
1682
+ (dbAttr) => this.convertDBAttributeToAttribute(dbAttr)
1683
+ );
1684
+ return {
1685
+ ...nativeObject,
1686
+ id: dbObject.id,
1687
+ attributes: [
1688
+ ...systemAttrs,
1689
+ // System attributes from DB (have IDs after sync)
1690
+ ...customAttrs
1691
+ // Custom attributes from DB
1692
+ ]
1693
+ };
1694
+ }
1695
+ /**
1696
+ * Convert DB object + attributes to ObjectDefinition
1697
+ * @internal
1698
+ */
1699
+ convertDBObjectToDefinition(dbObject, dbAttributes) {
1700
+ const attributes = dbAttributes.map(
1701
+ (dbAttr) => this.convertDBAttributeToAttribute(dbAttr)
1702
+ );
1703
+ const titleAttribute = dbObject.metadata?.titleAttribute ?? attributes.find((attr) => attr.type === "text")?.name ?? attributes[0]?.name ?? "name";
1704
+ return {
1705
+ id: dbObject.id,
1706
+ name: dbObject.name,
1707
+ label: dbObject.label,
1708
+ description: dbObject.description,
1709
+ icon: dbObject.metadata?.icon,
1710
+ titleAttribute,
1711
+ attributes,
1712
+ system: dbObject.system,
1713
+ metadata: dbObject.metadata
1714
+ };
1715
+ }
1716
+ /**
1717
+ * Convert DB attribute to Attribute type
1718
+ * @internal
1719
+ */
1720
+ convertDBAttributeToAttribute(dbAttr) {
1721
+ const baseAttr = {
1722
+ ...dbAttr.config,
1723
+ id: dbAttr.id,
1724
+ name: dbAttr.name,
1725
+ label: dbAttr.label,
1726
+ type: dbAttr.type,
1727
+ required: dbAttr.required,
1728
+ system: dbAttr.system,
1729
+ unique: dbAttr.unique
1730
+ };
1731
+ return baseAttr;
1732
+ }
1733
+ };
1734
+
1735
+ // src/native/registry.ts
1736
+ var NativeObjectRegistryClass = class {
1737
+ constructor() {
1738
+ this.objects = /* @__PURE__ */ new Map();
1739
+ }
1740
+ /**
1741
+ * Register one or more native objects
1742
+ * Auto-builds if ObjectBuilder is passed instead of ObjectDefinition
1743
+ * @param objects - Single object/builder or array of objects/builders to register
1744
+ * @returns this (for chaining)
1745
+ * @throws Error if object is invalid or already registered
1746
+ *
1747
+ * @example
1748
+ * ```typescript
1749
+ * // No .build() needed - registry auto-builds!
1750
+ * const PRODUCT = object({ id: "obj-product", name: "products", label: "Product" })
1751
+ * .system()
1752
+ * .attribute(text({ id: "attr-name", name: "name", label: "Name" }));
1753
+ *
1754
+ * registry.register(PRODUCT); // ← Auto-builds here
1755
+ *
1756
+ * // Can still explicitly .build() if you want
1757
+ * registry.register(PRODUCT.build());
1758
+ *
1759
+ * // Register multiple
1760
+ * registry.register([PRODUCT, ORDER, CUSTOMER]);
1761
+ * ```
1762
+ */
1763
+ register(objects) {
1764
+ const objectsArray = Array.isArray(objects) ? objects : [objects];
1765
+ for (const obj of objectsArray) {
1766
+ const object2 = "build" in obj && typeof obj.build === "function" ? obj.build() : obj;
1767
+ this.validateAndRegister(object2);
1768
+ }
1769
+ return this;
1770
+ }
1771
+ /**
1772
+ * Validate and register a single object
1773
+ * @internal
1774
+ */
1775
+ validateAndRegister(object2) {
1776
+ if (!object2.system) {
1777
+ throw new Error(
1778
+ `[NativeObjectRegistry] Cannot register object "${object2.name}" (id: ${object2.id}): missing system flag.
1779
+ Native objects must have system=true. Did you forget to call .system() in your builder?`
1780
+ );
1781
+ }
1782
+ if (this.objects.has(object2.name)) {
1783
+ const existing = this.objects.get(object2.name);
1784
+ throw new Error(
1785
+ `[NativeObjectRegistry] Duplicate object name "${object2.name}":
1786
+ - Existing: "${existing?.label}" (id: ${existing?.id})
1787
+ - New: "${object2.label}" (id: ${object2.id})
1788
+ Please use unique names for each native object.`
1789
+ );
1790
+ }
1791
+ this.objects.set(object2.name, object2);
1792
+ }
1793
+ /**
1794
+ * Get a native object by its name
1795
+ * @param name - The object name
1796
+ * @returns The object definition or undefined if not found
1797
+ *
1798
+ * @example
1799
+ * ```typescript
1800
+ * const product = registry.getByName("products");
1801
+ * if (product) {
1802
+ * console.log(product.label); // "Product"
1803
+ * }
1804
+ * ```
1805
+ */
1806
+ getByName(name) {
1807
+ return this.objects.get(name);
1808
+ }
1809
+ /**
1810
+ * Get a native object or throw if not found
1811
+ * @param name - The object name
1812
+ * @returns The object definition
1813
+ * @throws Error if not found
1814
+ */
1815
+ getByNameOrThrow(name) {
1816
+ const object2 = this.objects.get(name);
1817
+ if (!object2) {
1818
+ throw new Error(
1819
+ `[NativeObjectRegistry] Native object "${name}" not found.
1820
+ Available objects: ${this.listNames().join(", ")}`
1821
+ );
1822
+ }
1823
+ return object2;
1824
+ }
1825
+ /**
1826
+ * Get all registered native objects
1827
+ * @returns Array of all registered object definitions
1828
+ */
1829
+ getAll() {
1830
+ return Array.from(this.objects.values());
1831
+ }
1832
+ /**
1833
+ * Get all registered native object names
1834
+ * @returns Array of object names
1835
+ */
1836
+ listNames() {
1837
+ return Array.from(this.objects.keys());
1838
+ }
1839
+ /**
1840
+ * Check if a native object is registered
1841
+ * @param name - The object name
1842
+ * @returns true if registered, false otherwise
1843
+ */
1844
+ has(name) {
1845
+ return this.objects.has(name);
1846
+ }
1847
+ /**
1848
+ * Get the number of registered objects
1849
+ */
1850
+ get size() {
1851
+ return this.objects.size;
1852
+ }
1853
+ /**
1854
+ * Clear all registered objects (useful for testing)
1855
+ * @internal
1856
+ */
1857
+ clear() {
1858
+ this.objects.clear();
1859
+ }
1860
+ /**
1861
+ * Get registry summary for debugging
1862
+ */
1863
+ summary() {
1864
+ const objects = this.getAll();
1865
+ return [
1866
+ `[NativeObjectRegistry] ${this.size} native object(s) registered:`,
1867
+ ...objects.map(
1868
+ (obj) => ` - ${obj.name}: "${obj.label}" (${obj.attributes.length} attributes)`
1869
+ )
1870
+ ].join("\n");
1871
+ }
1872
+ /**
1873
+ * Log registry summary to console
1874
+ */
1875
+ debug() {
1876
+ console.info(this.summary());
1877
+ }
1878
+ };
1879
+ var registry = new NativeObjectRegistryClass();
1880
+
1881
+ // src/validation/validators.ts
1882
+ import { z as z3 } from "zod";
1883
+ function createTextValidator(attr) {
1884
+ let schema = z3.string();
1885
+ if (attr.minLength !== void 0) {
1886
+ schema = schema.min(
1887
+ attr.minLength,
1888
+ `${attr.label} must be at least ${attr.minLength} characters`
1889
+ );
1890
+ }
1891
+ if (attr.maxLength !== void 0) {
1892
+ schema = schema.max(
1893
+ attr.maxLength,
1894
+ `${attr.label} must be at most ${attr.maxLength} characters`
1895
+ );
1896
+ }
1897
+ if (attr.pattern) {
1898
+ schema = schema.regex(new RegExp(attr.pattern), `${attr.label} format is invalid`);
1899
+ }
1900
+ return schema;
1901
+ }
1902
+ function createNumberValidator(attr) {
1903
+ let schema = z3.number();
1904
+ if (attr.min !== void 0) {
1905
+ schema = schema.min(attr.min, `${attr.label} must be at least ${attr.min}`);
1906
+ }
1907
+ if (attr.max !== void 0) {
1908
+ schema = schema.max(attr.max, `${attr.label} must be at most ${attr.max}`);
1909
+ }
1910
+ if (attr.unit === "integer") {
1911
+ schema = schema.int(`${attr.label} must be an integer`);
1912
+ }
1913
+ return schema;
1914
+ }
1915
+ function createCheckboxValidator(_attr) {
1916
+ return z3.boolean();
1917
+ }
1918
+ function createDateValidator(attr) {
1919
+ return z3.string().datetime({ message: `${attr.label} must be a valid ISO date` });
1920
+ }
1921
+ function createPhoneValidator(_attr) {
1922
+ return z3.object({
1923
+ countryCode: z3.string().length(3),
1924
+ phoneNumber: z3.string().min(1)
1925
+ });
1926
+ }
1927
+ function createCurrencyValidator(_attr) {
1928
+ return z3.object({
1929
+ code: z3.string().length(3),
1930
+ value: z3.number().min(0)
1931
+ });
1932
+ }
1933
+ function createStatusValidator(attr) {
1934
+ const validValues = attr.options.map((opt) => opt.value);
1935
+ return z3.enum(validValues, {
1936
+ errorMap: () => ({
1937
+ message: `${attr.label} must be one of: ${validValues.join(", ")}`
1938
+ })
1939
+ });
1940
+ }
1941
+ function createSelectValidator(attr) {
1942
+ const validValues = attr.options.map((opt) => opt.value);
1943
+ return z3.enum(validValues, {
1944
+ errorMap: () => ({
1945
+ message: `${attr.label} must be one of: ${validValues.join(", ")}`
1946
+ })
1947
+ });
1948
+ }
1949
+ function createMultiselectValidator(attr) {
1950
+ const validValues = attr.options.map((opt) => opt.value);
1951
+ return z3.array(
1952
+ z3.enum(validValues, {
1953
+ errorMap: () => ({
1954
+ message: `Each value must be one of: ${validValues.join(", ")}`
1955
+ })
1956
+ })
1957
+ );
1958
+ }
1959
+ function createLocationValidator(_attr) {
1960
+ return z3.object({
1961
+ address: z3.string().optional(),
1962
+ address2: z3.string().optional(),
1963
+ city: z3.string().optional(),
1964
+ state: z3.string().optional(),
1965
+ postalCode: z3.string().optional(),
1966
+ country: z3.string().length(3).optional(),
1967
+ latitude: z3.number().optional(),
1968
+ longitude: z3.number().optional()
1969
+ });
1970
+ }
1971
+ function createTimestampValidator(_attr) {
1972
+ return z3.number().int().positive();
1973
+ }
1974
+ function createFileValidator(_attr) {
1975
+ return z3.string().uuid();
1976
+ }
1977
+ function createUserValidator(_attr) {
1978
+ return z3.string().uuid();
1979
+ }
1980
+ function createSingleRelationValidator(attr) {
1981
+ const uuidSchema = z3.string().uuid({
1982
+ message: `${attr.label} must be a valid record ID`
1983
+ });
1984
+ return z3.union([uuidSchema, z3.null()]);
1985
+ }
1986
+ function createMultiRelationValidator(attr) {
1987
+ const uuidSchema = z3.string().uuid({
1988
+ message: `Each ${attr.label} item must be a valid record ID`
1989
+ });
1990
+ let arraySchema = z3.array(uuidSchema);
1991
+ if (attr.minItems !== void 0) {
1992
+ arraySchema = arraySchema.min(
1993
+ attr.minItems,
1994
+ `${attr.label} must have at least ${attr.minItems} item${attr.minItems > 1 ? "s" : ""}`
1995
+ );
1996
+ }
1997
+ if (attr.maxItems !== void 0) {
1998
+ arraySchema = arraySchema.max(
1999
+ attr.maxItems,
2000
+ `${attr.label} must have at most ${attr.maxItems} item${attr.maxItems > 1 ? "s" : ""}`
2001
+ );
2002
+ }
2003
+ return arraySchema;
2004
+ }
2005
+ function createRelationValidator(attr) {
2006
+ if (attr.cardinality === "many") {
2007
+ return createMultiRelationValidator(attr);
2008
+ }
2009
+ return createSingleRelationValidator(attr);
2010
+ }
2011
+ function createRatingValidator(attr) {
2012
+ let schema = z3.number().int().min(0);
2013
+ if (attr.max !== void 0) {
2014
+ schema = schema.max(attr.max, `${attr.label} must be at most ${attr.max}`);
2015
+ }
2016
+ return schema;
2017
+ }
2018
+ function createAttributeValidator(attr) {
2019
+ switch (attr.type) {
2020
+ case "text":
2021
+ return createTextValidator(attr);
2022
+ case "number":
2023
+ return createNumberValidator(attr);
2024
+ case "checkbox":
2025
+ return createCheckboxValidator(attr);
2026
+ case "date":
2027
+ return createDateValidator(attr);
2028
+ case "phone":
2029
+ return createPhoneValidator(attr);
2030
+ case "currency":
2031
+ return createCurrencyValidator(attr);
2032
+ case "status":
2033
+ return createStatusValidator(attr);
2034
+ case "location":
2035
+ return createLocationValidator(attr);
2036
+ case "timestamp":
2037
+ return createTimestampValidator(attr);
2038
+ case "select":
2039
+ return createSelectValidator(attr);
2040
+ case "multiselect":
2041
+ return createMultiselectValidator(attr);
2042
+ case "file":
2043
+ return createFileValidator(attr);
2044
+ case "user":
2045
+ return createUserValidator(attr);
2046
+ case "relation":
2047
+ return createRelationValidator(attr);
2048
+ case "rating":
2049
+ return createRatingValidator(attr);
2050
+ default:
2051
+ return z3.unknown();
2052
+ }
2053
+ }
2054
+ function createObjectValidator(objectDef) {
2055
+ const shape = {};
2056
+ for (const attr of objectDef.attributes) {
2057
+ let validator = createAttributeValidator(attr);
2058
+ if (!attr.required) {
2059
+ validator = validator.optional();
2060
+ }
2061
+ shape[attr.name] = validator;
2062
+ }
2063
+ return z3.object(shape);
2064
+ }
2065
+ function validateAttribute(attr, value) {
2066
+ const validator = createAttributeValidator(attr);
2067
+ if (!attr.required && (value === void 0 || value === null)) {
2068
+ return { success: true, data: { [attr.name]: value } };
2069
+ }
2070
+ const result = validator.safeParse(value);
2071
+ if (result.success) {
2072
+ return {
2073
+ success: true,
2074
+ data: { [attr.name]: result.data }
2075
+ };
2076
+ }
2077
+ return {
2078
+ success: false,
2079
+ errors: result.error.errors.map((err) => ({
2080
+ path: [attr.name, ...err.path.map(String)],
2081
+ message: err.message
2082
+ }))
2083
+ };
2084
+ }
2085
+ function validateObject(objectDef, data) {
2086
+ const validator = createObjectValidator(objectDef);
2087
+ const result = validator.safeParse(data);
2088
+ if (result.success) {
2089
+ return {
2090
+ success: true,
2091
+ data: result.data
2092
+ };
2093
+ }
2094
+ return {
2095
+ success: false,
2096
+ errors: result.error.errors.map((err) => ({
2097
+ path: err.path.map(String),
2098
+ message: err.message
2099
+ }))
2100
+ };
2101
+ }
2102
+ function validateObjectOrThrow(objectDef, data) {
2103
+ const result = validateObject(objectDef, data);
2104
+ if (!result.success) {
2105
+ const errorMessages = result.errors?.map((err) => `${err.path.join(".")}: ${err.message}`).join("\n") || "Unknown validation error";
2106
+ throw new Error(`Validation failed for ${objectDef.label}:
2107
+ ${errorMessages}`);
2108
+ }
2109
+ return result.data;
2110
+ }
2111
+ function createDraftValidator(objectDef) {
2112
+ const shape = {};
2113
+ for (const attr of objectDef.attributes) {
2114
+ const validator = createAttributeValidator(attr).optional();
2115
+ shape[attr.name] = validator;
2116
+ }
2117
+ return z3.object(shape);
2118
+ }
2119
+ function validateDraft(objectDef, data) {
2120
+ const validator = createDraftValidator(objectDef);
2121
+ const result = validator.safeParse(data);
2122
+ if (result.success) {
2123
+ return {
2124
+ success: true,
2125
+ data: result.data
2126
+ };
2127
+ }
2128
+ return {
2129
+ success: false,
2130
+ errors: result.error.errors.map((err) => ({
2131
+ path: err.path.map(String),
2132
+ message: err.message
2133
+ }))
2134
+ };
2135
+ }
2136
+ function validateDraftOrThrow(objectDef, data) {
2137
+ const result = validateDraft(objectDef, data);
2138
+ if (!result.success) {
2139
+ const errorMessages = result.errors?.map((err) => `${err.path.join(".")}: ${err.message}`).join("\n") || "Unknown validation error";
2140
+ throw new Error(`Draft validation failed for ${objectDef.label}:
2141
+ ${errorMessages}`);
2142
+ }
2143
+ return result.data;
2144
+ }
2145
+ function isValuePresent(value) {
2146
+ if (value === void 0 || value === null) {
2147
+ return false;
2148
+ }
2149
+ if (typeof value === "string" && value.trim() === "") {
2150
+ return false;
2151
+ }
2152
+ return true;
2153
+ }
2154
+ function getMissingRequiredAttributes(objectDef, data) {
2155
+ const missing = [];
2156
+ for (const attr of objectDef.attributes) {
2157
+ if (attr.required && !isValuePresent(data[attr.name])) {
2158
+ missing.push(attr);
2159
+ }
2160
+ }
2161
+ return missing;
2162
+ }
2163
+ function isRecordComplete(objectDef, data) {
2164
+ const missing = getMissingRequiredAttributes(objectDef, data);
2165
+ if (missing.length > 0) {
2166
+ return false;
2167
+ }
2168
+ const validation = validateObject(objectDef, data);
2169
+ return validation.success;
2170
+ }
2171
+ function computeRecordStatus(objectDef, data) {
2172
+ return isRecordComplete(objectDef, data) ? "complete" : "draft";
2173
+ }
2174
+
2175
+ // src/runtime/services/relation.service.ts
2176
+ var RelationService = class {
2177
+ constructor(adapter, nativeRegistry) {
2178
+ this.adapter = adapter;
2179
+ this.schemaService = new ObjectSchemaService(adapter, nativeRegistry);
2180
+ }
2181
+ /**
2182
+ * Validate all relation attributes in the data
2183
+ *
2184
+ * @param schema - Object schema containing attribute definitions
2185
+ * @param data - Record data to validate
2186
+ * @returns Validation result with errors if any
2187
+ *
2188
+ * @example
2189
+ * ```typescript
2190
+ * const result = await relationService.validateRelations(schema, {
2191
+ * company: "rec-123",
2192
+ * contacts: ["rec-456", "rec-789"]
2193
+ * });
2194
+ *
2195
+ * if (!result.valid) {
2196
+ * console.log(result.errors);
2197
+ * // [{ attribute: "company", message: "Record not found", invalidIds: ["rec-123"] }]
2198
+ * }
2199
+ * ```
2200
+ */
2201
+ async validateRelations(schema, data) {
2202
+ const errors = [];
2203
+ const relationAttrs = schema.attributes.filter(
2204
+ (attr) => attr.type === "relation"
2205
+ );
2206
+ for (const attr of relationAttrs) {
2207
+ const value = data[attr.name];
2208
+ if (value === void 0 || value === null) {
2209
+ continue;
2210
+ }
2211
+ const attrErrors = await this.validateRelationAttribute(attr, value);
2212
+ errors.push(...attrErrors);
2213
+ }
2214
+ return {
2215
+ valid: errors.length === 0,
2216
+ errors
2217
+ };
2218
+ }
2219
+ /**
2220
+ * Validate a single relation attribute value
2221
+ */
2222
+ async validateRelationAttribute(attr, value) {
2223
+ const errors = [];
2224
+ const ids = this.extractIds(attr, value);
2225
+ if (ids.length === 0) {
2226
+ return errors;
2227
+ }
2228
+ const validObjectIds = await this.getValidObjectIds(attr.targets);
2229
+ if (validObjectIds.size === 0) {
2230
+ errors.push({
2231
+ attribute: attr.name,
2232
+ message: `No valid target objects found for ${attr.label}`
2233
+ });
2234
+ return errors;
2235
+ }
2236
+ const invalidIds = [];
2237
+ for (const id of ids) {
2238
+ const record = await this.adapter.objectRecords.findById(id);
2239
+ if (!record) {
2240
+ invalidIds.push(id);
2241
+ continue;
2242
+ }
2243
+ if (!validObjectIds.has(record.objectId)) {
2244
+ invalidIds.push(id);
2245
+ }
2246
+ }
2247
+ if (invalidIds.length > 0) {
2248
+ errors.push({
2249
+ attribute: attr.name,
2250
+ message: `Invalid or non-existent records for ${attr.label}`,
2251
+ invalidIds
2252
+ });
2253
+ }
2254
+ return errors;
2255
+ }
2256
+ /**
2257
+ * Extract IDs from relation value based on cardinality
2258
+ */
2259
+ extractIds(attr, value) {
2260
+ if (attr.cardinality === "many") {
2261
+ if (!Array.isArray(value)) {
2262
+ return [];
2263
+ }
2264
+ return value.filter((v) => typeof v === "string");
2265
+ }
2266
+ if (typeof value === "string") {
2267
+ return [value];
2268
+ }
2269
+ return [];
2270
+ }
2271
+ /**
2272
+ * Get valid object IDs from relation targets
2273
+ */
2274
+ async getValidObjectIds(targets) {
2275
+ const objectIds = /* @__PURE__ */ new Set();
2276
+ for (const target of targets) {
2277
+ try {
2278
+ const objectSchema = await this.schemaService.getObjectSchemaByName(target.object);
2279
+ if (objectSchema?.id) {
2280
+ objectIds.add(objectSchema.id);
2281
+ }
2282
+ } catch {
2283
+ }
2284
+ }
2285
+ return objectIds;
2286
+ }
2287
+ /**
2288
+ * Validate relations and throw if invalid
2289
+ */
2290
+ async validateRelationsOrThrow(schema, data) {
2291
+ const result = await this.validateRelations(schema, data);
2292
+ if (!result.valid) {
2293
+ const messages = result.errors.map((e) => `${e.attribute}: ${e.message}`).join("; ");
2294
+ throw new Error(`Relation validation failed: ${messages}`);
2295
+ }
2296
+ }
2297
+ // ============================================================================
2298
+ // OPTIONS & RESOLUTION
2299
+ // ============================================================================
2300
+ /**
2301
+ * Get available options for a relation attribute
2302
+ * Searches across all target objects defined in the relation
2303
+ *
2304
+ * @param attribute - Relation attribute definition
2305
+ * @param tenantId - Tenant ID for multi-tenant isolation
2306
+ * @param params - Query parameters
2307
+ *
2308
+ * @example
2309
+ * ```typescript
2310
+ * const options = await relationService.getOptions(attribute, "tenant-1", {
2311
+ * query: "nike",
2312
+ * page: 1,
2313
+ * pageSize: 20
2314
+ * });
2315
+ * ```
2316
+ */
2317
+ async getOptions(attribute, tenantId, params = {}) {
2318
+ const { query = "", page = 1, pageSize = 20, targetObject } = params;
2319
+ const targets = attribute.targets;
2320
+ const filteredTargets = targetObject ? targets.filter((t) => t.object === targetObject) : targets;
2321
+ if (filteredTargets.length === 0) {
2322
+ return { options: [], hasMore: false, total: 0 };
2323
+ }
2324
+ const allOptions = [];
2325
+ let totalCount = 0;
2326
+ const recordService = new RecordService(this.adapter, tenantId);
2327
+ for (const target of filteredTargets) {
2328
+ const objectSchema = await this.schemaService.getObjectSchemaByName(target.object);
2329
+ if (!objectSchema?.id) {
2330
+ continue;
2331
+ }
2332
+ const result = query ? await recordService.searchRecords(objectSchema.id, query, {
2333
+ limit: pageSize,
2334
+ offset: (page - 1) * pageSize
2335
+ }) : await recordService.listRecords(objectSchema.id, {
2336
+ limit: pageSize,
2337
+ offset: (page - 1) * pageSize
2338
+ });
2339
+ totalCount += result.total;
2340
+ for (const record of result.records) {
2341
+ const label = this.resolveLabel(
2342
+ record.values,
2343
+ objectSchema.titleAttribute,
2344
+ target.displayTemplate
2345
+ );
2346
+ allOptions.push({
2347
+ id: record.id,
2348
+ objectId: objectSchema.id,
2349
+ objectName: target.object,
2350
+ label,
2351
+ data: record.values
2352
+ });
2353
+ }
2354
+ }
2355
+ const hasMore = totalCount > page * pageSize;
2356
+ return {
2357
+ options: allOptions.slice(0, pageSize),
2358
+ hasMore,
2359
+ total: totalCount
2360
+ };
2361
+ }
2362
+ /**
2363
+ * Resolve record IDs to their display labels
2364
+ * Useful for displaying current values in the UI
2365
+ *
2366
+ * @param ids - Record IDs to resolve
2367
+ * @param tenantId - Tenant ID for multi-tenant isolation
2368
+ *
2369
+ * @example
2370
+ * ```typescript
2371
+ * const resolved = await relationService.resolveIds(["rec-1", "rec-2"], "tenant-1");
2372
+ * // [{ id: "rec-1", label: "Nike Air Max", objectName: "products", ... }]
2373
+ * ```
2374
+ */
2375
+ async resolveIds(ids, tenantId) {
2376
+ if (!ids || ids.length === 0) {
2377
+ return [];
2378
+ }
2379
+ const recordService = new RecordService(this.adapter, tenantId);
2380
+ const resolved = [];
2381
+ for (const id of ids) {
2382
+ const record = await recordService.getRecord(id);
2383
+ if (!record) {
2384
+ continue;
2385
+ }
2386
+ const objectSchema = await this.schemaService.getObjectSchema(record.objectId);
2387
+ if (!objectSchema) {
2388
+ continue;
2389
+ }
2390
+ const label = this.resolveLabel(record.values, objectSchema.titleAttribute);
2391
+ resolved.push({
2392
+ id: record.id,
2393
+ objectId: record.objectId,
2394
+ objectName: objectSchema.name,
2395
+ label,
2396
+ data: record.values
2397
+ });
2398
+ }
2399
+ return resolved;
2400
+ }
2401
+ /**
2402
+ * Find a relation attribute by ID
2403
+ */
2404
+ async findAttributeById(attributeId) {
2405
+ const attribute = await this.adapter.attributes.findById(attributeId);
2406
+ if (!attribute || attribute.type !== "relation") {
2407
+ return null;
2408
+ }
2409
+ return {
2410
+ ...attribute.config,
2411
+ ...attribute
2412
+ };
2413
+ }
2414
+ /**
2415
+ * Resolve display label from record values
2416
+ * Uses displayTemplate if provided, otherwise falls back to titleAttribute
2417
+ */
2418
+ resolveLabel(values, titleAttribute, displayTemplate) {
2419
+ if (displayTemplate) {
2420
+ return displayTemplate.replace(/\{(\w+)\}/g, (_, key) => {
2421
+ const value = values[key];
2422
+ return value !== void 0 && value !== null ? String(value) : "";
2423
+ });
2424
+ }
2425
+ const titleValue = values[titleAttribute];
2426
+ return titleValue !== void 0 && titleValue !== null ? String(titleValue) : "(Untitled)";
2427
+ }
2428
+ };
2429
+
2430
+ // src/runtime/services/record.service.ts
2431
+ var RecordService = class {
2432
+ constructor(adapter, tenantId) {
2433
+ this.adapter = adapter;
2434
+ this.tenantId = tenantId;
2435
+ this.schemaService = new ObjectSchemaService(adapter, registry);
2436
+ this.relationService = new RelationService(adapter, registry);
2437
+ }
2438
+ /**
2439
+ * Create a new record with validation
2440
+ *
2441
+ * @param objectId - Object UUID
2442
+ * @param data - Record data (attribute values)
2443
+ * @param options - Creation options
2444
+ * @returns Created record with computed completionStatus
2445
+ *
2446
+ * @example
2447
+ * ```typescript
2448
+ * const service = new RecordService(adapter, "tenant-123");
2449
+ *
2450
+ * // Create a complete record (strict validation)
2451
+ * const product = await service.createRecord("obj-product", {
2452
+ * name: "Nike Air Max",
2453
+ * price: 129.99,
2454
+ * status: "active"
2455
+ * });
2456
+ * // → product.completionStatus = "complete"
2457
+ *
2458
+ * // Create a draft record (allows missing required fields)
2459
+ * const draft = await service.createRecord("obj-product", {
2460
+ * name: "Draft Product"
2461
+ * }, { allowDraft: true });
2462
+ * // → draft.completionStatus = "draft"
2463
+ * ```
2464
+ */
2465
+ async createRecord(objectId, data, options) {
2466
+ const schema = await this.schemaService.getObjectSchema(objectId);
2467
+ if (options?.validate !== false) {
2468
+ if (options?.allowDraft) {
2469
+ validateDraftOrThrow(schema, data);
2470
+ } else {
2471
+ validateObjectOrThrow(schema, data);
2472
+ }
2473
+ if (!options?.skipRelationValidation) {
2474
+ await this.relationService.validateRelationsOrThrow(schema, data);
2475
+ }
2476
+ }
2477
+ const completionStatus = computeRecordStatus(schema, data);
2478
+ const record = await this.adapter.objectRecords.create({
2479
+ tenantId: this.tenantId,
2480
+ objectId,
2481
+ data,
2482
+ completionStatus
2483
+ });
2484
+ return record;
2485
+ }
2486
+ /**
2487
+ * Get a record by ID
2488
+ *
2489
+ * @param recordId - Record UUID
2490
+ * @param options - Query options
2491
+ * @returns Record or null if not found
2492
+ */
2493
+ async getRecord(recordId, options) {
2494
+ const record = await this.adapter.objectRecords.findById(recordId);
2495
+ if (!record) {
2496
+ return null;
2497
+ }
2498
+ if (options?.includeSchema) {
2499
+ const schema = await this.schemaService.getObjectSchema(record.objectId);
2500
+ const recordWithSchema = record;
2501
+ recordWithSchema.schema = schema;
2502
+ return recordWithSchema;
2503
+ }
2504
+ return record;
2505
+ }
2506
+ /**
2507
+ * Get a record by ID or throw if not found
2508
+ */
2509
+ async getRecordOrThrow(recordId) {
2510
+ const record = await this.getRecord(recordId);
2511
+ if (!record) {
2512
+ throw new Error(`Record with id "${recordId}" not found`);
2513
+ }
2514
+ return record;
2515
+ }
2516
+ /**
2517
+ * Update a record with validation
2518
+ *
2519
+ * The completion status is automatically recalculated after each update.
2520
+ * A draft record becomes complete when all required fields are filled.
2521
+ *
2522
+ * @param recordId - Record UUID
2523
+ * @param data - Partial data to update
2524
+ * @param options - Update options
2525
+ * @returns Updated record with recalculated completionStatus
2526
+ *
2527
+ * @example
2528
+ * ```typescript
2529
+ * // Update a draft record to make it complete
2530
+ * const updated = await service.updateRecord(draftId, {
2531
+ * price: 99.99,
2532
+ * status: "active"
2533
+ * });
2534
+ * // → updated.completionStatus = "complete" if all required fields now present
2535
+ * ```
2536
+ */
2537
+ async updateRecord(recordId, data, options) {
2538
+ const existing = await this.getRecordOrThrow(recordId);
2539
+ const schema = await this.schemaService.getObjectSchema(existing.objectId);
2540
+ const mergedData = { ...existing.values, ...data };
2541
+ if (options?.validate !== false) {
2542
+ if (options?.partial) {
2543
+ validateDraftOrThrow(schema, mergedData);
2544
+ } else {
2545
+ validateObjectOrThrow(schema, mergedData);
2546
+ }
2547
+ if (!options?.skipRelationValidation) {
2548
+ await this.relationService.validateRelationsOrThrow(schema, data);
2549
+ }
2550
+ }
2551
+ const completionStatus = computeRecordStatus(schema, mergedData);
2552
+ const updated = await this.adapter.objectRecords.update(recordId, {
2553
+ ...data,
2554
+ __completionStatus: completionStatus
2555
+ });
2556
+ return updated;
2557
+ }
2558
+ /**
2559
+ * Delete a record
2560
+ *
2561
+ * @param recordId - Record UUID
2562
+ * @param options - Delete options
2563
+ */
2564
+ async deleteRecord(recordId, options) {
2565
+ if (options?.checkSystem) {
2566
+ const record = await this.getRecordOrThrow(recordId);
2567
+ const schema = await this.schemaService.getObjectSchema(record.objectId);
2568
+ if (schema.system) {
2569
+ throw new Error(`Cannot delete record of system object "${schema.label}"`);
2570
+ }
2571
+ }
2572
+ await this.adapter.objectRecords.delete(recordId);
2573
+ }
2574
+ /**
2575
+ * List records for an object with pagination
2576
+ *
2577
+ * @param objectId - Object UUID
2578
+ * @param options - List options
2579
+ * @returns Records and total count
2580
+ */
2581
+ async listRecords(objectId, options) {
2582
+ return await this.adapter.objectRecords.list(this.tenantId, objectId, options);
2583
+ }
2584
+ /**
2585
+ * Search records using full-text search
2586
+ *
2587
+ * @param objectId - Object UUID
2588
+ * @param query - Search query
2589
+ * @param options - Search options
2590
+ * @returns Matching records and total count
2591
+ */
2592
+ async searchRecords(objectId, query, options) {
2593
+ return await this.adapter.objectRecords.search(this.tenantId, objectId, query, options);
2594
+ }
2595
+ /**
2596
+ * Validate data against object schema without saving
2597
+ *
2598
+ * @param objectId - Object UUID
2599
+ * @param data - Data to validate
2600
+ * @returns Validation result
2601
+ */
2602
+ async validateData(objectId, data) {
2603
+ const schema = await this.schemaService.getObjectSchema(objectId);
2604
+ return validateObject(schema, data);
2605
+ }
2606
+ /**
2607
+ * Compute the completion status for given data without saving.
2608
+ * Useful for UI to show draft/complete status before submitting.
2609
+ *
2610
+ * @param objectId - Object UUID
2611
+ * @param data - Data to check
2612
+ * @returns Computed completion status
2613
+ */
2614
+ async computeStatus(objectId, data) {
2615
+ const schema = await this.schemaService.getObjectSchema(objectId);
2616
+ return computeRecordStatus(schema, data);
2617
+ }
2618
+ /**
2619
+ * Refresh the completion status of an existing record.
2620
+ * Useful when schema changes and you need to recompute statuses.
2621
+ *
2622
+ * @param recordId - Record UUID
2623
+ * @returns Updated completion status
2624
+ */
2625
+ async refreshRecordStatus(recordId) {
2626
+ const record = await this.getRecordOrThrow(recordId);
2627
+ const schema = await this.schemaService.getObjectSchema(record.objectId);
2628
+ const newStatus = computeRecordStatus(schema, record.values);
2629
+ if (record.completionStatus !== newStatus) {
2630
+ await this.adapter.objectRecords.update(recordId, {
2631
+ __completionStatus: newStatus
2632
+ });
2633
+ }
2634
+ return newStatus;
2635
+ }
2636
+ };
2637
+
2638
+ // src/runtime/services/user-profile.service.ts
2639
+ var UserProfileService = class {
2640
+ constructor(adapter, tenantId) {
2641
+ this.adapter = adapter;
2642
+ this.tenantId = tenantId;
2643
+ }
2644
+ /**
2645
+ * Create a new user profile (typically after first auth)
2646
+ *
2647
+ * @param data - User profile creation data
2648
+ * @returns Created user profile
2649
+ *
2650
+ * @example
2651
+ * ```typescript
2652
+ * const service = new UserProfileService(adapter, "tenant-123");
2653
+ *
2654
+ * // After Supabase auth
2655
+ * const profile = await service.createProfile({
2656
+ * tenantId: "tenant-123",
2657
+ * authId: authUser.id,
2658
+ * email: authUser.email,
2659
+ * firstName: authUser.user_metadata.first_name,
2660
+ * lastName: authUser.user_metadata.last_name,
2661
+ * role: "member",
2662
+ * status: "active"
2663
+ * });
2664
+ * ```
2665
+ */
2666
+ async createProfile(data) {
2667
+ if (data.tenantId !== this.tenantId) {
2668
+ throw new Error(
2669
+ `Tenant mismatch: service initialized with "${this.tenantId}" but data has "${data.tenantId}"`
2670
+ );
2671
+ }
2672
+ const existing = await this.adapter.userProfiles.findByAuthId(data.authId);
2673
+ if (existing) {
2674
+ throw new Error(`User profile with authId "${data.authId}" already exists`);
2675
+ }
2676
+ const existingEmail = await this.adapter.userProfiles.findByEmail(this.tenantId, data.email);
2677
+ if (existingEmail) {
2678
+ throw new Error(`User profile with email "${data.email}" already exists in this tenant`);
2679
+ }
2680
+ return await this.adapter.userProfiles.create(data);
2681
+ }
2682
+ /**
2683
+ * Get user profile by ID
2684
+ */
2685
+ async getProfile(profileId) {
2686
+ return await this.adapter.userProfiles.findById(profileId);
2687
+ }
2688
+ /**
2689
+ * Get user profile by ID or throw
2690
+ */
2691
+ async getProfileOrThrow(profileId) {
2692
+ const profile = await this.getProfile(profileId);
2693
+ if (!profile) {
2694
+ throw new Error(`User profile with id "${profileId}" not found`);
2695
+ }
2696
+ return profile;
2697
+ }
2698
+ /**
2699
+ * Get user profile by auth provider ID
2700
+ *
2701
+ * @param authId - Auth provider user ID (Supabase, Clerk, etc.)
2702
+ * @returns User profile or null
2703
+ */
2704
+ async getProfileByAuthId(authId) {
2705
+ return await this.adapter.userProfiles.findByAuthId(authId);
2706
+ }
2707
+ /**
2708
+ * Get or create user profile (idempotent operation)
2709
+ * Useful for auth callbacks - ensures profile exists
2710
+ *
2711
+ * @param authId - Auth provider user ID
2712
+ * @param data - Profile data to create if doesn't exist
2713
+ * @returns Existing or newly created profile
2714
+ *
2715
+ * @example
2716
+ * ```typescript
2717
+ * // In Supabase auth callback
2718
+ * const profile = await service.getOrCreateProfile(
2719
+ * authUser.id,
2720
+ * {
2721
+ * tenantId: "tenant-123",
2722
+ * authId: authUser.id,
2723
+ * email: authUser.email,
2724
+ * role: "member",
2725
+ * status: "active"
2726
+ * }
2727
+ * );
2728
+ * ```
2729
+ */
2730
+ async getOrCreateProfile(authId, data) {
2731
+ const existing = await this.getProfileByAuthId(authId);
2732
+ if (existing) {
2733
+ return existing;
2734
+ }
2735
+ return await this.createProfile(data);
2736
+ }
2737
+ /**
2738
+ * Update user profile
2739
+ */
2740
+ async updateProfile(profileId, data) {
2741
+ await this.getProfileOrThrow(profileId);
2742
+ return await this.adapter.userProfiles.update(profileId, data);
2743
+ }
2744
+ /**
2745
+ * Delete user profile
2746
+ *
2747
+ * @param profileId - Profile UUID
2748
+ * @param options - Delete options
2749
+ */
2750
+ async deleteProfile(profileId, options) {
2751
+ if (options?.checkAdmin) {
2752
+ const profile = await this.getProfileOrThrow(profileId);
2753
+ if (profile.role === "admin") {
2754
+ const allProfiles = await this.listProfiles();
2755
+ const adminCount = allProfiles.filter((p) => p.role === "admin").length;
2756
+ if (adminCount <= 1) {
2757
+ throw new Error("Cannot delete the last admin user");
2758
+ }
2759
+ }
2760
+ }
2761
+ await this.adapter.userProfiles.delete(profileId);
2762
+ }
2763
+ /**
2764
+ * List all user profiles for the tenant
2765
+ */
2766
+ async listProfiles(options) {
2767
+ return await this.adapter.userProfiles.list(this.tenantId, options);
2768
+ }
2769
+ /**
2770
+ * Update last login timestamp
2771
+ *
2772
+ * @param profileId - Profile UUID
2773
+ *
2774
+ * @example
2775
+ * ```typescript
2776
+ * // After successful auth
2777
+ * await service.updateLastLogin(profile.id);
2778
+ * ```
2779
+ */
2780
+ async updateLastLogin(profileId) {
2781
+ await this.adapter.userProfiles.updateLastLogin(profileId);
2782
+ }
2783
+ /**
2784
+ * Change user role
2785
+ *
2786
+ * @param profileId - Profile UUID
2787
+ * @param newRole - New role
2788
+ */
2789
+ async changeRole(profileId, newRole) {
2790
+ return await this.updateProfile(profileId, { role: newRole });
2791
+ }
2792
+ /**
2793
+ * Change user status
2794
+ *
2795
+ * @param profileId - Profile UUID
2796
+ * @param newStatus - New status
2797
+ */
2798
+ async changeStatus(profileId, newStatus) {
2799
+ return await this.updateProfile(profileId, { status: newStatus });
2800
+ }
2801
+ /**
2802
+ * Get user by email
2803
+ */
2804
+ async getProfileByEmail(email) {
2805
+ return await this.adapter.userProfiles.findByEmail(this.tenantId, email);
2806
+ }
2807
+ /**
2808
+ * Check if user has role
2809
+ */
2810
+ async hasRole(profileId, role) {
2811
+ const profile = await this.getProfile(profileId);
2812
+ return profile?.role === role;
2813
+ }
2814
+ /**
2815
+ * Check if user is admin
2816
+ */
2817
+ async isAdmin(profileId) {
2818
+ return await this.hasRole(profileId, "admin");
2819
+ }
2820
+ };
2821
+
2822
+ // src/runtime/sync.ts
2823
+ var BASE_ATTRIBUTE_KEYS = /* @__PURE__ */ new Set([
2824
+ "id",
2825
+ "name",
2826
+ "label",
2827
+ "type",
2828
+ "required",
2829
+ "system",
2830
+ "unique",
2831
+ "order"
2832
+ ]);
2833
+ function extractAttributeConfig(attr) {
2834
+ return Object.fromEntries(
2835
+ Object.entries(attr).filter(
2836
+ ([key, value]) => !BASE_ATTRIBUTE_KEYS.has(key) && value !== void 0
2837
+ )
2838
+ );
2839
+ }
2840
+ async function syncNativeObjects(adapter, nativeRegistry, options = {}) {
2841
+ const result = {
2842
+ success: true,
2843
+ objectsSynced: 0,
2844
+ attributesSynced: 0,
2845
+ objectsCreated: 0,
2846
+ objectsUpdated: 0,
2847
+ attributesCreated: 0,
2848
+ attributesUpdated: 0,
2849
+ attributesDeleted: 0,
2850
+ errors: []
2851
+ };
2852
+ const nativeObjects = nativeRegistry.getAll();
2853
+ if (options.verbose) {
2854
+ console.info(`[SyncService] Starting sync for ${nativeObjects.length} native objects...`);
2855
+ }
2856
+ try {
2857
+ await adapter.transaction(async (tx) => {
2858
+ for (const nativeObject of nativeObjects) {
2859
+ try {
2860
+ await syncSingleObject(tx, nativeObject, result, options);
2861
+ } catch (error) {
2862
+ result.success = false;
2863
+ result.errors.push({
2864
+ objectName: nativeObject.name,
2865
+ error: error instanceof Error ? error.message : String(error)
2866
+ });
2867
+ if (options.verbose) {
2868
+ console.error(`[SyncService] \u2717 Failed to sync ${nativeObject.name}:`, error);
2869
+ }
2870
+ }
2871
+ }
2872
+ });
2873
+ } catch (error) {
2874
+ result.success = false;
2875
+ result.errors.push({
2876
+ objectName: "transaction",
2877
+ error: error instanceof Error ? error.message : String(error)
2878
+ });
2879
+ }
2880
+ if (options.verbose) {
2881
+ console.info(
2882
+ `[SyncService] ${result.success ? "\u2713" : "\u2717"} Sync complete:
2883
+ Objects: ${result.objectsCreated} created, ${result.objectsUpdated} updated
2884
+ Attributes: ${result.attributesCreated} created, ${result.attributesUpdated} updated, ${result.attributesDeleted} deleted
2885
+ Errors: ${result.errors.length}`
2886
+ );
2887
+ }
2888
+ return result;
2889
+ }
2890
+ async function syncSingleObject(adapter, nativeObject, result, options) {
2891
+ if (!nativeObject.system) {
2892
+ throw new Error(`Object ${nativeObject.name} is not marked as system`);
2893
+ }
2894
+ const existingObject = await adapter.objects.findSystemByName(nativeObject.name);
2895
+ const isNew = !existingObject;
2896
+ updateObjectStats(result, isNew);
2897
+ if (options.dryRun) {
2898
+ await handleDryRun(adapter, nativeObject, existingObject, result, options, isNew);
2899
+ return;
2900
+ }
2901
+ const dbObject = await upsertObject(adapter, nativeObject, options);
2902
+ await syncAttributes(adapter, nativeObject, dbObject, existingObject, result);
2903
+ if (!isNew) {
2904
+ await cleanupAttributes(adapter, dbObject.id, nativeObject, result);
2905
+ }
2906
+ logSyncSuccess(nativeObject, options);
2907
+ }
2908
+ function updateObjectStats(result, isNew) {
2909
+ if (isNew) {
2910
+ result.objectsCreated++;
2911
+ } else {
2912
+ result.objectsUpdated++;
2913
+ }
2914
+ result.objectsSynced++;
2915
+ }
2916
+ async function handleDryRun(adapter, nativeObject, existingObject, result, options, isNew) {
2917
+ if (options.verbose) {
2918
+ console.info(`[SyncService] Would ${isNew ? "create" : "update"} object: ${nativeObject.name}`);
2919
+ }
2920
+ for (const attr of nativeObject.attributes) {
2921
+ const existingAttr = existingObject ? await adapter.attributes.findByObjectId(existingObject.id).then((attrs) => attrs.find((a) => a.name === attr.name)) : null;
2922
+ if (existingAttr) {
2923
+ result.attributesUpdated++;
2924
+ } else {
2925
+ result.attributesCreated++;
2926
+ }
2927
+ result.attributesSynced++;
2928
+ }
2929
+ }
2930
+ async function upsertObject(adapter, nativeObject, options) {
2931
+ return await adapter.objects.upsert({
2932
+ tenantId: options.tenantId || "default",
2933
+ system: true,
2934
+ name: nativeObject.name,
2935
+ label: nativeObject.label,
2936
+ description: nativeObject.description,
2937
+ metadata: nativeObject.metadata
2938
+ });
2939
+ }
2940
+ async function syncAttributes(adapter, nativeObject, dbObject, existingObject, result) {
2941
+ for (const [index, attr] of nativeObject.attributes.entries()) {
2942
+ const existingAttr = existingObject ? await adapter.attributes.findByObjectId(dbObject.id).then((attrs) => attrs.find((a) => a.name === attr.name)) : null;
2943
+ await adapter.attributes.upsert({
2944
+ objectId: dbObject.id,
2945
+ name: attr.name,
2946
+ label: attr.label,
2947
+ type: attr.type,
2948
+ description: attr.description,
2949
+ icon: attr.icon,
2950
+ system: attr.system ?? true,
2951
+ required: attr.required ?? false,
2952
+ order: attr.order ?? index,
2953
+ hidden: attr.hidden,
2954
+ archived: attr.archived,
2955
+ deprecated: attr.deprecated,
2956
+ defaultValue: attr.defaultValue,
2957
+ config: extractAttributeConfig(attr)
2958
+ });
2959
+ if (existingAttr) {
2960
+ result.attributesUpdated++;
2961
+ } else {
2962
+ result.attributesCreated++;
2963
+ }
2964
+ result.attributesSynced++;
2965
+ }
2966
+ }
2967
+ async function cleanupAttributes(adapter, objectId, nativeObject, result) {
2968
+ const attributeNames = nativeObject.attributes.map((attr) => attr.name);
2969
+ const deletedCount = await adapter.attributes.deleteByNames(objectId, attributeNames);
2970
+ result.attributesDeleted += deletedCount;
2971
+ }
2972
+ function logSyncSuccess(nativeObject, options) {
2973
+ if (options.verbose) {
2974
+ console.info(
2975
+ `[SyncService] \u2713 Synced ${nativeObject.name}: ${nativeObject.attributes.length} attributes`
2976
+ );
2977
+ }
2978
+ }
2979
+ async function verifyNativeObjectsSync(adapter, nativeRegistry) {
2980
+ const nativeObjects = nativeRegistry.getAll();
2981
+ for (const obj of nativeObjects) {
2982
+ if (!obj.system) continue;
2983
+ const existing = await adapter.objects.findSystemByName(obj.name);
2984
+ if (!existing) {
2985
+ return false;
2986
+ }
2987
+ }
2988
+ return true;
2989
+ }
2990
+ async function getSyncPreview(adapter, nativeRegistry) {
2991
+ return await syncNativeObjects(adapter, nativeRegistry, { dryRun: true });
2992
+ }
2993
+
2994
+ export {
2995
+ generateId,
2996
+ generatePrefixedId,
2997
+ text,
2998
+ textarea,
2999
+ number,
3000
+ checkbox,
3001
+ date,
3002
+ phone,
3003
+ currency,
3004
+ status,
3005
+ select,
3006
+ multiselect,
3007
+ location,
3008
+ timestamp,
3009
+ file,
3010
+ user,
3011
+ relation,
3012
+ rating,
3013
+ ObjectBuilder,
3014
+ object,
3015
+ registry,
3016
+ createTextValidator,
3017
+ createNumberValidator,
3018
+ createCheckboxValidator,
3019
+ createDateValidator,
3020
+ createPhoneValidator,
3021
+ createCurrencyValidator,
3022
+ createStatusValidator,
3023
+ createSelectValidator,
3024
+ createMultiselectValidator,
3025
+ createLocationValidator,
3026
+ createTimestampValidator,
3027
+ createFileValidator,
3028
+ createUserValidator,
3029
+ createSingleRelationValidator,
3030
+ createMultiRelationValidator,
3031
+ createRelationValidator,
3032
+ createRatingValidator,
3033
+ createAttributeValidator,
3034
+ createObjectValidator,
3035
+ validateAttribute,
3036
+ validateObject,
3037
+ validateObjectOrThrow,
3038
+ createDraftValidator,
3039
+ validateDraft,
3040
+ validateDraftOrThrow,
3041
+ getMissingRequiredAttributes,
3042
+ isRecordComplete,
3043
+ computeRecordStatus,
3044
+ createMockAdapter,
3045
+ FileService,
3046
+ ObjectSchemaService,
3047
+ RelationService,
3048
+ RecordService,
3049
+ UserProfileService,
3050
+ syncNativeObjects,
3051
+ verifyNativeObjectsSync,
3052
+ getSyncPreview
3053
+ };