@stndrds/schema 0.1.0-alpha.21 → 0.1.0-alpha.23

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 (63) hide show
  1. package/dist/chunk-7GLSGSUP.mjs +9456 -0
  2. package/dist/chunk-AZTBTKHP.mjs +9861 -0
  3. package/dist/chunk-B74KNXDP.mjs +9457 -0
  4. package/dist/chunk-CVFKFFCX.mjs +9912 -0
  5. package/dist/chunk-DXVIUNQT.mjs +9881 -0
  6. package/dist/chunk-EJB2V42F.mjs +9778 -0
  7. package/dist/chunk-GP2BONBI.mjs +9493 -0
  8. package/dist/chunk-H2KOC7F5.mjs +9549 -0
  9. package/dist/chunk-HXLOIN36.mjs +9893 -0
  10. package/dist/chunk-HYW67ZP6.mjs +9500 -0
  11. package/dist/chunk-IKUIWNHZ.mjs +9460 -0
  12. package/dist/chunk-JAWKNHY6.mjs +9553 -0
  13. package/dist/chunk-KT4UPBZR.mjs +9892 -0
  14. package/dist/chunk-NBCCRCJB.mjs +9547 -0
  15. package/dist/chunk-NN7Y3B3A.mjs +9859 -0
  16. package/dist/chunk-SBEZDUNH.mjs +9549 -0
  17. package/dist/chunk-TTZ3TWYP.mjs +9583 -0
  18. package/dist/chunk-UTZTDUIK.mjs +9818 -0
  19. package/dist/index.d.mts +172 -17
  20. package/dist/index.d.ts +172 -17
  21. package/dist/index.js +977 -512
  22. package/dist/index.mjs +71 -23
  23. package/dist/runtime-BFOdkIqK.d.mts +7644 -0
  24. package/dist/runtime-BFOdkIqK.d.ts +7644 -0
  25. package/dist/runtime-BI_wXiVl.d.mts +8043 -0
  26. package/dist/runtime-BI_wXiVl.d.ts +8043 -0
  27. package/dist/runtime-BZZRu7A1.d.mts +7083 -0
  28. package/dist/runtime-BZZRu7A1.d.ts +7083 -0
  29. package/dist/runtime-BlcFbbeG.d.mts +8035 -0
  30. package/dist/runtime-BlcFbbeG.d.ts +8035 -0
  31. package/dist/runtime-BleOubTG.d.mts +7236 -0
  32. package/dist/runtime-BleOubTG.d.ts +7236 -0
  33. package/dist/runtime-CC4VRtUK.d.mts +7422 -0
  34. package/dist/runtime-CC4VRtUK.d.ts +7422 -0
  35. package/dist/runtime-CKLGO3Jk.d.mts +7632 -0
  36. package/dist/runtime-CKLGO3Jk.d.ts +7632 -0
  37. package/dist/runtime-CjhnsVyg.d.mts +7668 -0
  38. package/dist/runtime-CjhnsVyg.d.ts +7668 -0
  39. package/dist/runtime-CmYcicVr.d.mts +7083 -0
  40. package/dist/runtime-CmYcicVr.d.ts +7083 -0
  41. package/dist/runtime-D01tNFxT.d.mts +7418 -0
  42. package/dist/runtime-D01tNFxT.d.ts +7418 -0
  43. package/dist/runtime-DWCd3xCL.d.mts +7632 -0
  44. package/dist/runtime-DWCd3xCL.d.ts +7632 -0
  45. package/dist/runtime-DbryZGqb.d.mts +8037 -0
  46. package/dist/runtime-DbryZGqb.d.ts +8037 -0
  47. package/dist/runtime-DdkNFkKd.d.mts +7185 -0
  48. package/dist/runtime-DdkNFkKd.d.ts +7185 -0
  49. package/dist/runtime-DnnW72ZF.d.mts +7451 -0
  50. package/dist/runtime-DnnW72ZF.d.ts +7451 -0
  51. package/dist/runtime-DrrCWvBr.d.mts +7451 -0
  52. package/dist/runtime-DrrCWvBr.d.ts +7451 -0
  53. package/dist/runtime-UYkiPdud.d.mts +8047 -0
  54. package/dist/runtime-UYkiPdud.d.ts +8047 -0
  55. package/dist/runtime-g7XZnqGw.d.mts +7452 -0
  56. package/dist/runtime-g7XZnqGw.d.ts +7452 -0
  57. package/dist/runtime-jnV5WCS6.d.mts +8047 -0
  58. package/dist/runtime-jnV5WCS6.d.ts +8047 -0
  59. package/dist/runtime.d.mts +1 -1
  60. package/dist/runtime.d.ts +1 -1
  61. package/dist/runtime.js +855 -466
  62. package/dist/runtime.mjs +29 -3
  63. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -146,6 +146,7 @@ __export(index_exports, {
146
146
  GroupBuilder: () => GroupBuilder,
147
147
  InvalidPathError: () => InvalidPathError,
148
148
  MaxDepthExceededError: () => MaxDepthExceededError,
149
+ NOTES: () => NOTES,
149
150
  NO_VALUE_OPERATORS: () => NO_VALUE_OPERATORS,
150
151
  NoopGeocodingAdapter: () => NoopGeocodingAdapter,
151
152
  NoopHookRegistry: () => NoopHookRegistry,
@@ -157,11 +158,15 @@ __export(index_exports, {
157
158
  ObjectReferencedError: () => ObjectReferencedError,
158
159
  ObjectSchemaService: () => ObjectSchemaService,
159
160
  PermissionService: () => PermissionService,
161
+ PolicyRegistry: () => PolicyRegistry,
162
+ PolicyViolationError: () => PolicyViolationError,
160
163
  ProtectedResourceError: () => ProtectedResourceError,
161
164
  ProtectedRoleError: () => ProtectedRoleError,
162
165
  QueryBuilder: () => QueryBuilder,
163
166
  QueryMultipleResultsError: () => QueryMultipleResultsError,
164
167
  QueryNoResultError: () => QueryNoResultError,
168
+ RELATION_TARGET_ANY: () => RELATION_TARGET_ANY,
169
+ RESERVED_ATTRIBUTE_NAMES: () => RESERVED_ATTRIBUTE_NAMES,
165
170
  RecordNotFoundError: () => RecordNotFoundError,
166
171
  RecordReferencedError: () => RecordReferencedError,
167
172
  RecordService: () => RecordService,
@@ -171,6 +176,7 @@ __export(index_exports, {
171
176
  RollupScheduler: () => RollupScheduler,
172
177
  RollupService: () => RollupService,
173
178
  SHORTCUT_TO_FILTER_OPERATOR: () => SHORTCUT_TO_FILTER_OPERATOR,
179
+ SYSTEM_ATTRIBUTES: () => SYSTEM_ATTRIBUTES,
174
180
  SYSTEM_FIELD_NAMES: () => SYSTEM_FIELD_NAMES,
175
181
  SYSTEM_RESOURCES: () => SYSTEM_RESOURCES,
176
182
  SYSTEM_RESOURCE_LABELS: () => SYSTEM_RESOURCE_LABELS,
@@ -179,12 +185,17 @@ __export(index_exports, {
179
185
  SyncError: () => SyncError,
180
186
  TabBuilder: () => TabBuilder,
181
187
  TableTabConfig: () => TableTabConfig,
188
+ TenantAwareRepository: () => TenantAwareRepository,
189
+ TenantAwareService: () => TenantAwareService,
190
+ TenantContextError: () => TenantContextError,
182
191
  UserProfileNotFoundError: () => UserProfileNotFoundError,
183
192
  UserProfileService: () => UserProfileService,
184
193
  UserService: () => UserService,
185
194
  ValidationError: () => ValidationError,
186
195
  ViewBuilder: () => ViewBuilder,
187
196
  ViewService: () => ViewService,
197
+ asTenantId: () => asTenantId,
198
+ asUserId: () => asUserId,
188
199
  attributeConfigSchemas: () => attributeConfigSchemas,
189
200
  buildAuditChanges: () => buildAuditChanges,
190
201
  checkbox: () => checkbox,
@@ -208,17 +219,19 @@ __export(index_exports, {
208
219
  createQueryBuilder: () => createQueryBuilder,
209
220
  createRatingValidator: () => createRatingValidator,
210
221
  createRelationValidator: () => createRelationValidator,
222
+ createRichtextValidator: () => createRichtextValidator,
211
223
  createRollupValidator: () => createRollupValidator,
212
224
  createSelectValidator: () => createSelectValidator,
213
225
  createSingleRelationValidator: () => createSingleRelationValidator,
214
226
  createStatusValidator: () => createStatusValidator,
227
+ createTextAreaValidator: () => createTextAreaValidator,
215
228
  createTextValidator: () => createTextValidator,
216
- createTimestampValidator: () => createTimestampValidator,
217
229
  createUserValidator: () => createUserValidator,
218
230
  currency: () => currency,
219
231
  currencyConfigSchema: () => currencyConfigSchema,
220
232
  date: () => date,
221
233
  dateConfigSchema: () => dateConfigSchema,
234
+ defaultPolicyRegistry: () => defaultPolicyRegistry,
222
235
  enrichValuesWithSelectLabels: () => enrichValuesWithSelectLabels,
223
236
  evaluateFormula: () => evaluateFormula,
224
237
  evaluateFormulaAttribute: () => evaluateFormulaAttribute,
@@ -242,13 +255,18 @@ __export(index_exports, {
242
255
  generateId: () => generateId,
243
256
  generatePrefixedId: () => generatePrefixedId,
244
257
  getAttributeConfigSchema: () => getAttributeConfigSchema,
258
+ getContext: () => getContext,
245
259
  getMissingRequiredAttributes: () => getMissingRequiredAttributes,
246
260
  getPathDepth: () => getPathDepth,
247
261
  getRelationPath: () => getRelationPath,
248
262
  getSyncPreview: () => getSyncPreview,
263
+ getSystemAttributeList: () => getSystemAttributeList,
249
264
  getTargetAttributeName: () => getTargetAttributeName,
265
+ getTenantId: () => getTenantId,
266
+ getUserId: () => getUserId,
250
267
  getViewSyncPreview: () => getViewSyncPreview,
251
268
  group: () => group,
269
+ hasContext: () => hasContext,
252
270
  hasRelationReferences: () => hasRelationReferences,
253
271
  isActivityTab: () => isActivityTab,
254
272
  isAdvancedFilterState: () => isAdvancedFilterState,
@@ -261,16 +279,21 @@ __export(index_exports, {
261
279
  isLabelExpression: () => isLabelExpression,
262
280
  isNoValueOperator: () => isNoValueOperator,
263
281
  isNotFoundError: () => isNotFoundError,
282
+ isNotesTab: () => isNotesTab,
264
283
  isProtectedResourceError: () => isProtectedResourceError,
265
284
  isRecordComplete: () => isRecordComplete,
266
285
  isSchemaError: () => isSchemaError,
286
+ isSystemAttribute: () => isSystemAttribute,
287
+ isSystemAttributeObject: () => isSystemAttributeObject,
267
288
  isSystemFlow: () => isSystemFlow,
268
289
  isTableTab: () => isTableTab,
290
+ isUniversalRelation: () => isUniversalRelation,
269
291
  isValidationError: () => isValidationError,
270
292
  location: () => location,
271
293
  locationConfigSchema: () => locationConfigSchema,
272
294
  multiselect: () => multiselect,
273
295
  multiselectConfigSchema: () => multiselectConfigSchema,
296
+ notesPolicy: () => notesPolicy,
274
297
  number: () => number,
275
298
  numberConfigSchema: () => numberConfigSchema,
276
299
  object: () => object,
@@ -287,8 +310,11 @@ __export(index_exports, {
287
310
  renderLabelExpression: () => renderLabelExpression,
288
311
  resolveMultiplePaths: () => resolveMultiplePaths,
289
312
  resolveSingleValue: () => resolveSingleValue,
313
+ richtext: () => richtext,
314
+ richtextConfigSchema: () => richtextConfigSchema,
290
315
  rollup: () => rollup,
291
316
  rollupConfigSchema: () => rollupConfigSchema,
317
+ runWithContext: () => runWithContext,
292
318
  safeParseAttributeConfig: () => safeParseAttributeConfig,
293
319
  select: () => select,
294
320
  selectConfigSchema: () => selectConfigSchema,
@@ -301,8 +327,6 @@ __export(index_exports, {
301
327
  textConfigSchema: () => textConfigSchema,
302
328
  textarea: () => textarea,
303
329
  textareaConfigSchema: () => textareaConfigSchema,
304
- timestamp: () => timestamp,
305
- timestampConfigSchema: () => timestampConfigSchema,
306
330
  toAdvancedFilterState: () => toAdvancedFilterState,
307
331
  toSimpleFilterState: () => toSimpleFilterState,
308
332
  traversePath: () => traversePath,
@@ -319,10 +343,17 @@ __export(index_exports, {
319
343
  verifyNativeObjectsSync: () => verifyNativeObjectsSync,
320
344
  verifyNativeViewsSync: () => verifyNativeViewsSync,
321
345
  view: () => view,
322
- viewRegistry: () => viewRegistry
346
+ viewRegistry: () => viewRegistry,
347
+ withTenantContext: () => withTenantContext
323
348
  });
324
349
  module.exports = __toCommonJS(index_exports);
325
350
 
351
+ // src/types/attributes.ts
352
+ var RELATION_TARGET_ANY = "*";
353
+ function isUniversalRelation(attr) {
354
+ return attr.targets.length === 1 && attr.targets[0].object === RELATION_TARGET_ANY;
355
+ }
356
+
326
357
  // src/types/errors.ts
327
358
  var RecordReferencedError = class extends Error {
328
359
  constructor(recordId, references) {
@@ -405,6 +436,7 @@ var OPERATORS_BY_TYPE = {
405
436
  "is_empty",
406
437
  "is_not_empty"
407
438
  ],
439
+ richtext: ["is_empty", "is_not_empty"],
408
440
  number: ["eq", "neq", "lt", "gt", "lte", "gte", "is_empty", "is_not_empty"],
409
441
  checkbox: ["is_checked", "is_not_checked"],
410
442
  date: [
@@ -418,17 +450,6 @@ var OPERATORS_BY_TYPE = {
418
450
  "is_empty",
419
451
  "is_not_empty"
420
452
  ],
421
- timestamp: [
422
- "is",
423
- "is_not",
424
- "before",
425
- "after",
426
- "on_or_before",
427
- "on_or_after",
428
- "is_within",
429
- "is_empty",
430
- "is_not_empty"
431
- ],
432
453
  phone: ["is", "is_not", "contains", "not_contains", "is_empty", "is_not_empty"],
433
454
  currency: ["eq", "neq", "lt", "gt", "lte", "gte", "is_empty", "is_not_empty"],
434
455
  status: ["is", "is_not", "any_of", "none_of", "is_empty", "is_not_empty"],
@@ -491,8 +512,96 @@ var NoopGeocodingAdapter = class {
491
512
  }
492
513
  };
493
514
 
494
- // src/types/inference.ts
495
- var SYSTEM_FIELD_NAMES = ["id", "createdAt", "updatedAt"];
515
+ // src/types/objects.ts
516
+ var SYSTEM_FIELD_NAMES = [
517
+ "id",
518
+ "createdAt",
519
+ "updatedAt",
520
+ "createdBy",
521
+ "lastUpdatedBy"
522
+ ];
523
+ var RESERVED_ATTRIBUTE_NAMES = [
524
+ // System fields (auto-managed)
525
+ ...SYSTEM_FIELD_NAMES,
526
+ // Other ObjectRecord properties
527
+ "objectId",
528
+ "label",
529
+ "completionStatus",
530
+ "values",
531
+ "metadata",
532
+ "deletedAt"
533
+ ];
534
+
535
+ // src/types/policies.ts
536
+ var PolicyViolationError = class extends Error {
537
+ constructor(objectName, action, recordId) {
538
+ const message = recordId ? `Access denied: cannot ${action} record ${recordId} on ${objectName}` : `Access denied: cannot ${action} on ${objectName}`;
539
+ super(message);
540
+ this.objectName = objectName;
541
+ this.action = action;
542
+ this.recordId = recordId;
543
+ this.name = "PolicyViolationError";
544
+ }
545
+ };
546
+
547
+ // src/types/system-attributes.ts
548
+ var SYSTEM_ATTRIBUTES = {
549
+ createdAt: {
550
+ id: "system:createdAt",
551
+ name: "createdAt",
552
+ label: "Created time",
553
+ type: "date",
554
+ required: false,
555
+ disabled: true,
556
+ system: true,
557
+ icon: "clock-circle",
558
+ description: "When this record was created"
559
+ },
560
+ updatedAt: {
561
+ id: "system:updatedAt",
562
+ name: "updatedAt",
563
+ label: "Last edited time",
564
+ type: "date",
565
+ required: false,
566
+ disabled: true,
567
+ system: true,
568
+ icon: "clock-circle",
569
+ description: "When this record was last modified"
570
+ },
571
+ createdBy: {
572
+ id: "system:createdBy",
573
+ name: "createdBy",
574
+ label: "Created by",
575
+ type: "user",
576
+ required: false,
577
+ disabled: true,
578
+ system: true,
579
+ multiple: false,
580
+ icon: "user",
581
+ description: "User who created this record"
582
+ },
583
+ lastUpdatedBy: {
584
+ id: "system:lastUpdatedBy",
585
+ name: "lastUpdatedBy",
586
+ label: "Last edited by",
587
+ type: "user",
588
+ required: false,
589
+ disabled: true,
590
+ system: true,
591
+ multiple: false,
592
+ icon: "user",
593
+ description: "User who last modified this record"
594
+ }
595
+ };
596
+ function getSystemAttributeList() {
597
+ return Object.values(SYSTEM_ATTRIBUTES);
598
+ }
599
+ function isSystemAttribute(name) {
600
+ return name in SYSTEM_ATTRIBUTES;
601
+ }
602
+ function isSystemAttributeObject(attr) {
603
+ return attr.system === true;
604
+ }
496
605
 
497
606
  // src/types/views.ts
498
607
  function isFormTab(tab) {
@@ -507,8 +616,17 @@ function isCustomTab(tab) {
507
616
  function isActivityTab(tab) {
508
617
  return tab.type === "activity";
509
618
  }
619
+ function isNotesTab(tab) {
620
+ return tab.type === "notes";
621
+ }
510
622
 
511
623
  // src/utils.ts
624
+ function asTenantId(id) {
625
+ return id;
626
+ }
627
+ function asUserId(id) {
628
+ return id;
629
+ }
512
630
  function generateId() {
513
631
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
514
632
  return crypto.randomUUID();
@@ -569,15 +687,6 @@ function formatDate(value) {
569
687
  }
570
688
  return String(value);
571
689
  }
572
- function formatTimestamp(value) {
573
- if (typeof value === "number") {
574
- return new Date(value).toISOString();
575
- }
576
- if (value instanceof Date) {
577
- return value.toISOString();
578
- }
579
- return String(value);
580
- }
581
690
  function formatPhone(value) {
582
691
  if (typeof value !== "object" || value === null) return String(value);
583
692
  const phone2 = value;
@@ -662,8 +771,6 @@ function formatAttributeValue(value, attribute) {
662
771
  return formatCurrency(value, attribute);
663
772
  case "date":
664
773
  return formatDate(value);
665
- case "timestamp":
666
- return formatTimestamp(value);
667
774
  case "phone":
668
775
  return formatPhone(value);
669
776
  case "location":
@@ -901,7 +1008,7 @@ var VALID_ICONS = new Set(import_constants2.ICONS);
901
1008
  var ATTRIBUTE_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
902
1009
  var MAX_NAME_LENGTH = 63;
903
1010
  var MAX_LABEL_LENGTH = 128;
904
- var RESERVED_ATTRIBUTE_NAMES = ["id", "createdAt", "updatedAt", "metadata"];
1011
+ var RESERVED_ATTRIBUTE_NAMES2 = ["id", "createdAt", "updatedAt", "metadata"];
905
1012
  function validateAttributeName(name) {
906
1013
  if (!name || name.length === 0) {
907
1014
  throw new Error("[AttributeBuilder] Attribute name cannot be empty");
@@ -916,10 +1023,10 @@ function validateAttributeName(name) {
916
1023
  "[AttributeBuilder] 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'"
917
1024
  );
918
1025
  }
919
- if (RESERVED_ATTRIBUTE_NAMES.includes(name)) {
1026
+ if (RESERVED_ATTRIBUTE_NAMES2.includes(name)) {
920
1027
  throw new Error(
921
1028
  `[AttributeBuilder] "${name}" is a reserved name and cannot be used as an attribute name.
922
- Reserved names: ${RESERVED_ATTRIBUTE_NAMES.join(", ")}`
1029
+ Reserved names: ${RESERVED_ATTRIBUTE_NAMES2.join(", ")}`
923
1030
  );
924
1031
  }
925
1032
  }
@@ -1102,6 +1209,38 @@ var TextAreaAttributeBuilder = class extends BaseAttributeBuilder {
1102
1209
  function textarea(config) {
1103
1210
  return new TextAreaAttributeBuilder(config.name, config.label);
1104
1211
  }
1212
+ var RichtextAttributeBuilder = class extends BaseAttributeBuilder {
1213
+ constructor(name, label) {
1214
+ super("richtext", name, label);
1215
+ }
1216
+ /**
1217
+ * Set enabled features for the richtext editor
1218
+ * If not called, all features are enabled by default
1219
+ *
1220
+ * @param list - Array of features to enable
1221
+ *
1222
+ * @example
1223
+ * ```typescript
1224
+ * richtext({ name: "content", label: "Content" })
1225
+ * .features(["headings", "bold", "italic", "lists", "links"])
1226
+ * ```
1227
+ */
1228
+ features(list) {
1229
+ this.attr.features = list;
1230
+ return this;
1231
+ }
1232
+ required() {
1233
+ this.setRequired(true);
1234
+ return this;
1235
+ }
1236
+ optional() {
1237
+ this.setRequired(false);
1238
+ return this;
1239
+ }
1240
+ };
1241
+ function richtext(config) {
1242
+ return new RichtextAttributeBuilder(config.name, config.label);
1243
+ }
1105
1244
  var NumberAttributeBuilder = class extends BaseAttributeBuilder {
1106
1245
  constructor(name, label) {
1107
1246
  super("number", name, label);
@@ -1367,26 +1506,6 @@ var LocationAttributeBuilder = class extends BaseAttributeBuilder {
1367
1506
  function location(config) {
1368
1507
  return new LocationAttributeBuilder(config.name, config.label);
1369
1508
  }
1370
- var TimestampAttributeBuilder = class extends BaseAttributeBuilder {
1371
- constructor(name, label) {
1372
- super("timestamp", name, label);
1373
- }
1374
- autoUpdate() {
1375
- this.attr.autoUpdate = true;
1376
- return this;
1377
- }
1378
- required() {
1379
- this.setRequired(true);
1380
- return this;
1381
- }
1382
- optional() {
1383
- this.setRequired(false);
1384
- return this;
1385
- }
1386
- };
1387
- function timestamp(config) {
1388
- return new TimestampAttributeBuilder(config.name, config.label);
1389
- }
1390
1509
  var FileAttributeBuilder = class extends BaseAttributeBuilder {
1391
1510
  constructor(name, label) {
1392
1511
  super("file", name, label);
@@ -1403,10 +1522,6 @@ var FileAttributeBuilder = class extends BaseAttributeBuilder {
1403
1522
  this.attr.allowedTypes = types;
1404
1523
  return this;
1405
1524
  }
1406
- verification(config) {
1407
- this.attr.verification = config;
1408
- return this;
1409
- }
1410
1525
  /**
1411
1526
  * Allow multiple files to be uploaded
1412
1527
  */
@@ -1472,6 +1587,21 @@ var SingleRelationAttributeBuilder = class extends BaseAttributeBuilder {
1472
1587
  this.attr.targets?.push(target);
1473
1588
  return this;
1474
1589
  }
1590
+ /**
1591
+ * Allow linking to any object in the tenant (universal polymorphic)
1592
+ * Creates a relation with `targets: [{ object: "*" }]`
1593
+ *
1594
+ * @example
1595
+ * ```typescript
1596
+ * relation({ name: "linkedTo", label: "Linked To" })
1597
+ * .toAny()
1598
+ * .hidden()
1599
+ * ```
1600
+ */
1601
+ toAny() {
1602
+ this.attr.targets = [{ object: RELATION_TARGET_ANY }];
1603
+ return this;
1604
+ }
1475
1605
  /**
1476
1606
  * Convert to a multi-relation (cardinality: "many")
1477
1607
  */
@@ -1518,6 +1648,21 @@ var MultiRelationAttributeBuilder = class extends BaseAttributeBuilder {
1518
1648
  this.attr.targets?.push(target);
1519
1649
  return this;
1520
1650
  }
1651
+ /**
1652
+ * Allow linking to any object in the tenant (universal polymorphic)
1653
+ * Creates a relation with `targets: [{ object: "*" }]`
1654
+ *
1655
+ * @example
1656
+ * ```typescript
1657
+ * relation({ name: "linkedItems", label: "Linked Items" })
1658
+ * .toAny()
1659
+ * .many()
1660
+ * ```
1661
+ */
1662
+ toAny() {
1663
+ this.attr.targets = [{ object: RELATION_TARGET_ANY }];
1664
+ return this;
1665
+ }
1521
1666
  /**
1522
1667
  * Set minimum number of relations required
1523
1668
  */
@@ -2703,6 +2848,15 @@ Available objects: ${this.listNames().join(", ")}`
2703
2848
  };
2704
2849
  var registry = new NativeObjectRegistryClass();
2705
2850
 
2851
+ // src/native/notes.ts
2852
+ var NOTES = object({ name: "notes", label: "Note" }).pluralLabel("Notes").icon("file-text").description("Notes that can be linked to any record or used globally").system().labelExpression("{{ title }}").attribute(text({ name: "title", label: "Title" }).placeholder("Untitled").required()).attribute(richtext({ name: "content", label: "Content" }).required()).attribute(
2853
+ select({ name: "visibility", label: "Visibility" }).options([
2854
+ { id: "private", label: "Private", value: "private", icon: "lock" },
2855
+ { id: "shared", label: "Shared", value: "shared", icon: "users" }
2856
+ ]).defaultValue("private").required()
2857
+ ).attribute(relation({ name: "linkedTo", label: "Linked To" }).toAny());
2858
+ registry.register(NOTES);
2859
+
2706
2860
  // src/views/registry.ts
2707
2861
  var ViewRegistry = class {
2708
2862
  constructor() {
@@ -2876,41 +3030,17 @@ var relationTargetSchema = import_zod4.z.object({
2876
3030
  displayTemplate: import_zod4.z.string().optional(),
2877
3031
  filter: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()).optional()
2878
3032
  });
2879
- var documentTypeConfigSchema = import_zod4.z.object({
2880
- type: import_zod4.z.enum([
2881
- "id_card",
2882
- "passport",
2883
- "incorporation_certificate",
2884
- "driver_license",
2885
- "birth_certificate",
2886
- "residence_permit",
2887
- "bank_statement",
2888
- "proof_of_address",
2889
- "custom"
2890
- ]),
2891
- label: import_zod4.z.string(),
2892
- faces: import_zod4.z.array(import_zod4.z.enum(["front", "back", "single"])),
2893
- attributeMapping: import_zod4.z.array(
2894
- import_zod4.z.object({
2895
- attributeId: import_zod4.z.string(),
2896
- extractedKey: import_zod4.z.string(),
2897
- face: import_zod4.z.enum(["front", "back", "single"]).optional(),
2898
- required: import_zod4.z.boolean().optional()
2899
- })
2900
- ).optional()
2901
- });
2902
- var fileVerificationConfigSchema = import_zod4.z.object({
2903
- enabled: import_zod4.z.boolean(),
2904
- documentTypes: import_zod4.z.array(documentTypeConfigSchema),
2905
- autoExtract: import_zod4.z.boolean().optional(),
2906
- autoValidate: import_zod4.z.boolean().optional()
2907
- });
2908
3033
  var textConfigSchema = baseConfigSchema.extend({
2909
3034
  minLength: import_zod4.z.number().int().min(0).optional(),
2910
3035
  maxLength: import_zod4.z.number().int().min(1).optional(),
2911
3036
  pattern: import_zod4.z.string().optional()
2912
3037
  });
2913
3038
  var textareaConfigSchema = baseConfigSchema;
3039
+ var richtextConfigSchema = baseConfigSchema.extend({
3040
+ features: import_zod4.z.array(
3041
+ import_zod4.z.enum(["headings", "bold", "italic", "lists", "links", "images", "codeBlocks", "tables"])
3042
+ ).optional()
3043
+ });
2914
3044
  var numberConfigSchema = baseConfigSchema.extend({
2915
3045
  min: import_zod4.z.number().optional(),
2916
3046
  max: import_zod4.z.number().optional(),
@@ -2941,9 +3071,6 @@ var locationConfigSchema = baseConfigSchema.extend({
2941
3071
  allowedCountries: import_zod4.z.array(import_zod4.z.string().length(3)).optional(),
2942
3072
  displayFormat: import_zod4.z.enum(["single_line", "multi_line", "compact"]).optional()
2943
3073
  });
2944
- var timestampConfigSchema = baseConfigSchema.extend({
2945
- autoUpdate: import_zod4.z.boolean().optional()
2946
- });
2947
3074
  var selectConfigSchema = baseConfigSchema.extend({
2948
3075
  options: optionsArraySchema
2949
3076
  });
@@ -2954,7 +3081,6 @@ var fileConfigSchema = baseConfigSchema.extend({
2954
3081
  maxFiles: import_zod4.z.number().int().min(1).optional(),
2955
3082
  maxSize: import_zod4.z.number().int().min(1).optional(),
2956
3083
  allowedTypes: import_zod4.z.array(import_zod4.z.string()).optional(),
2957
- verification: fileVerificationConfigSchema.optional(),
2958
3084
  multiple: import_zod4.z.boolean().optional()
2959
3085
  });
2960
3086
  var userConfigSchema = baseConfigSchema.extend({
@@ -2982,24 +3108,40 @@ var rollupConfigSchema = baseConfigSchema.extend({
2982
3108
  relationPath: import_zod4.z.string().optional(),
2983
3109
  targetAttribute: import_zod4.z.string().min(1),
2984
3110
  function: import_zod4.z.enum([
3111
+ // Numeric aggregates
2985
3112
  "sum",
2986
- "count",
2987
3113
  "avg",
2988
- "min",
2989
- "max",
3114
+ // Date aggregates
3115
+ "earliest",
3116
+ "latest",
3117
+ // Count (universal)
3118
+ "count",
2990
3119
  "countValues",
3120
+ "countUniqueValues",
2991
3121
  "countEmpty",
3122
+ // Percent (universal)
2992
3123
  "percentEmpty",
2993
- "percentFilled",
2994
- "collect"
3124
+ "percentNotEmpty"
2995
3125
  ]),
2996
3126
  decimals: import_zod4.z.number().int().min(0).max(10).optional(),
2997
3127
  showAsOriginal: import_zod4.z.boolean().optional(),
2998
- targetAttributeType: import_zod4.z.string().optional()
3128
+ targetAttributeType: import_zod4.z.string().optional(),
3129
+ targetAttributeOptions: import_zod4.z.array(
3130
+ import_zod4.z.object({
3131
+ id: import_zod4.z.string(),
3132
+ label: import_zod4.z.string(),
3133
+ value: import_zod4.z.string(),
3134
+ color: import_zod4.z.string().optional(),
3135
+ icon: import_zod4.z.string().optional(),
3136
+ description: import_zod4.z.string().optional(),
3137
+ group: import_zod4.z.enum(["idle", "in_progress", "finished"]).optional()
3138
+ })
3139
+ ).optional()
2999
3140
  });
3000
3141
  var attributeConfigSchemas = {
3001
3142
  text: textConfigSchema,
3002
3143
  textarea: textareaConfigSchema,
3144
+ richtext: richtextConfigSchema,
3003
3145
  number: numberConfigSchema,
3004
3146
  checkbox: checkboxConfigSchema,
3005
3147
  date: dateConfigSchema,
@@ -3007,7 +3149,6 @@ var attributeConfigSchemas = {
3007
3149
  currency: currencyConfigSchema,
3008
3150
  status: statusConfigSchema,
3009
3151
  location: locationConfigSchema,
3010
- timestamp: timestampConfigSchema,
3011
3152
  select: selectConfigSchema,
3012
3153
  multiselect: multiselectConfigSchema,
3013
3154
  file: fileConfigSchema,
@@ -3122,9 +3263,6 @@ function createLocationValidator(_attr) {
3122
3263
  longitude: import_zod4.z.number().optional()
3123
3264
  });
3124
3265
  }
3125
- function createTimestampValidator(_attr) {
3126
- return import_zod4.z.number().int().positive();
3127
- }
3128
3266
  function createFileValidator(attr) {
3129
3267
  const uuidSchema = import_zod4.z.uuid({
3130
3268
  message: `${attr.label} must be a valid file ID`
@@ -3194,10 +3332,31 @@ function createFormulaValidator(_attr) {
3194
3332
  function createRollupValidator(_attr) {
3195
3333
  return import_zod4.z.unknown();
3196
3334
  }
3335
+ function createTextAreaValidator(_attr) {
3336
+ return import_zod4.z.string();
3337
+ }
3338
+ var blockNoteBlockSchema = import_zod4.z.lazy(
3339
+ () => import_zod4.z.object({
3340
+ id: import_zod4.z.string().min(1, "Block must have an id"),
3341
+ type: import_zod4.z.string().min(1, "Block must have a type"),
3342
+ props: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.union([import_zod4.z.boolean(), import_zod4.z.number(), import_zod4.z.string()])),
3343
+ content: import_zod4.z.any().optional(),
3344
+ children: import_zod4.z.array(import_zod4.z.any())
3345
+ })
3346
+ );
3347
+ function createRichtextValidator(attr) {
3348
+ return import_zod4.z.array(blockNoteBlockSchema, {
3349
+ message: `${attr.label} must be a valid BlockNote content array`
3350
+ });
3351
+ }
3197
3352
  function createAttributeValidator(attr) {
3198
3353
  switch (attr.type) {
3199
3354
  case "text":
3200
3355
  return createTextValidator(attr);
3356
+ case "textarea":
3357
+ return createTextAreaValidator(attr);
3358
+ case "richtext":
3359
+ return createRichtextValidator(attr);
3201
3360
  case "number":
3202
3361
  return createNumberValidator(attr);
3203
3362
  case "checkbox":
@@ -3212,8 +3371,6 @@ function createAttributeValidator(attr) {
3212
3371
  return createStatusValidator(attr);
3213
3372
  case "location":
3214
3373
  return createLocationValidator(attr);
3215
- case "timestamp":
3216
- return createTimestampValidator(attr);
3217
3374
  case "select":
3218
3375
  return createSelectValidator(attr);
3219
3376
  case "multiselect":
@@ -3433,12 +3590,12 @@ var QueryBuilder = class _QueryBuilder {
3433
3590
  });
3434
3591
  }
3435
3592
  /**
3436
- * Resolve object name to object ID (cached)
3593
+ * Resolve object name to object ID (cached).
3594
+ * Uses tenant context from AsyncLocalStorage.
3437
3595
  */
3438
3596
  async resolveObjectId() {
3439
3597
  if (this.objectId) return this.objectId;
3440
- const tenantId = this.recordService.tenantId;
3441
- const dbObject = await this.adapter.objects.findByName(tenantId, this.state.objectName);
3598
+ const dbObject = await this.adapter.objects.findByName(this.state.objectName);
3442
3599
  if (!dbObject) {
3443
3600
  throw new Error(`Object "${this.state.objectName}" not found`);
3444
3601
  }
@@ -3756,10 +3913,10 @@ var QueryBuilder = class _QueryBuilder {
3756
3913
  * .eq('id', 'rec-123')
3757
3914
  * .update({ price: 899 });
3758
3915
  *
3759
- * // Update with metadata (replaces existing metadata)
3916
+ * // Update with metadata (merged with existing, set undefined to remove)
3760
3917
  * const withMeta = await schema.from('products')
3761
3918
  * .eq('id', 'rec-123')
3762
- * .update({ price: 899 }, { metadata: { synced: true } });
3919
+ * .update({ price: 899 }, { metadata: { synced: true, oldKey: undefined } });
3763
3920
  * ```
3764
3921
  */
3765
3922
  async update(data, options) {
@@ -3826,6 +3983,49 @@ function createQueryBuilder(recordService, adapter, objectName) {
3826
3983
  return new QueryBuilder(recordService, adapter, objectName);
3827
3984
  }
3828
3985
 
3986
+ // src/runtime/context/errors.ts
3987
+ var TenantContextError = class _TenantContextError extends Error {
3988
+ constructor(message) {
3989
+ super(
3990
+ message ?? "No tenant context found. This usually means:\n - HTTP request: Missing TenantContextMiddleware/Interceptor\n - Background job: Wrap with runWithContext()\n - Test: Wrap test body with runWithContext()\n\nAll database operations require a tenant context for security."
3991
+ );
3992
+ this.name = "TenantContextError";
3993
+ if ("captureStackTrace" in Error) {
3994
+ Error.captureStackTrace(
3995
+ this,
3996
+ _TenantContextError
3997
+ );
3998
+ }
3999
+ }
4000
+ };
4001
+
4002
+ // src/runtime/context/tenant-context.ts
4003
+ var import_node_async_hooks = require("async_hooks");
4004
+ var storage = new import_node_async_hooks.AsyncLocalStorage();
4005
+ function getContext() {
4006
+ const ctx = storage.getStore();
4007
+ if (!ctx) {
4008
+ throw new TenantContextError();
4009
+ }
4010
+ return ctx;
4011
+ }
4012
+ function getTenantId() {
4013
+ return getContext().tenantId;
4014
+ }
4015
+ function getUserId() {
4016
+ return getContext().userId;
4017
+ }
4018
+ function hasContext() {
4019
+ return storage.getStore() !== void 0;
4020
+ }
4021
+ function runWithContext(context, fn) {
4022
+ const frozenContext = Object.freeze({ ...context });
4023
+ return storage.run(frozenContext, fn);
4024
+ }
4025
+ async function withTenantContext(tenantId, fn, userId) {
4026
+ return await runWithContext({ tenantId, userId }, fn);
4027
+ }
4028
+
3829
4029
  // src/runtime/formula/evaluator.ts
3830
4030
  var import_expr_eval = require("expr-eval");
3831
4031
  function createFormulaParser() {
@@ -4286,7 +4486,8 @@ function createMockObjectsRepository(stores) {
4286
4486
  findById(id) {
4287
4487
  return Promise.resolve(stores.objects.get(id) ?? null);
4288
4488
  },
4289
- findByName(tenantId, name) {
4489
+ findByName(name) {
4490
+ const tenantId = getTenantId();
4290
4491
  for (const obj of stores.objects.values()) {
4291
4492
  if (obj.tenantId === tenantId && obj.name === name) {
4292
4493
  return Promise.resolve(obj);
@@ -4303,9 +4504,10 @@ function createMockObjectsRepository(stores) {
4303
4504
  return Promise.resolve(null);
4304
4505
  },
4305
4506
  create(data) {
4507
+ const tenantId = getTenantId();
4306
4508
  const obj = {
4307
4509
  id: generateId(),
4308
- tenantId: data.tenantId,
4510
+ tenantId,
4309
4511
  name: data.name,
4310
4512
  label: data.label,
4311
4513
  pluralLabel: data.pluralLabel,
@@ -4337,13 +4539,15 @@ function createMockObjectsRepository(stores) {
4337
4539
  stores.objects.delete(id);
4338
4540
  return Promise.resolve();
4339
4541
  },
4340
- list(tenantId) {
4542
+ list() {
4543
+ const tenantId = getTenantId();
4341
4544
  const results = Array.from(stores.objects.values()).filter(
4342
4545
  (obj) => obj.tenantId === tenantId
4343
4546
  );
4344
4547
  return Promise.resolve(results);
4345
4548
  },
4346
4549
  upsert(data) {
4550
+ const tenantId = getTenantId();
4347
4551
  for (const obj of stores.objects.values()) {
4348
4552
  if (obj.system && obj.name === data.name) {
4349
4553
  const updated = {
@@ -4361,7 +4565,7 @@ function createMockObjectsRepository(stores) {
4361
4565
  }
4362
4566
  const newObj = {
4363
4567
  id: generateId(),
4364
- tenantId: data.tenantId,
4568
+ tenantId,
4365
4569
  name: data.name,
4366
4570
  label: data.label,
4367
4571
  pluralLabel: data.pluralLabel,
@@ -4441,28 +4645,21 @@ function createMockAttributesRepository(stores) {
4441
4645
  return Promise.resolve(count);
4442
4646
  },
4443
4647
  upsert(data) {
4444
- for (const attr of stores.attributes.values()) {
4445
- if (attr.objectId === data.objectId && attr.name === data.name) {
4446
- const updated = {
4447
- ...attr,
4448
- label: data.label ?? attr.label,
4449
- type: data.type ?? attr.type,
4450
- description: data.description ?? attr.description,
4451
- icon: data.icon ?? attr.icon,
4452
- system: data.system ?? attr.system,
4453
- required: data.required ?? attr.required,
4454
- unique: data.unique ?? attr.unique,
4455
- order: data.order ?? attr.order,
4456
- hidden: data.hidden ?? attr.hidden,
4457
- archived: data.archived ?? attr.archived,
4458
- deprecated: data.deprecated ?? attr.deprecated,
4459
- defaultValue: data.defaultValue ?? attr.defaultValue,
4460
- config: data.config ?? attr.config,
4461
- updatedAt: /* @__PURE__ */ new Date()
4462
- };
4463
- stores.attributes.set(attr.id, updated);
4464
- return Promise.resolve(updated);
4465
- }
4648
+ const existing = Array.from(stores.attributes.values()).find(
4649
+ (attr) => attr.objectId === data.objectId && attr.name === data.name
4650
+ );
4651
+ if (existing) {
4652
+ const updated = {
4653
+ ...existing,
4654
+ ...data,
4655
+ id: existing.id,
4656
+ objectId: existing.objectId,
4657
+ name: existing.name,
4658
+ createdAt: existing.createdAt,
4659
+ updatedAt: /* @__PURE__ */ new Date()
4660
+ };
4661
+ stores.attributes.set(existing.id, updated);
4662
+ return Promise.resolve(updated);
4466
4663
  }
4467
4664
  return this.create(data);
4468
4665
  }
@@ -4481,7 +4678,8 @@ function createMockUserProfilesRepository(stores) {
4481
4678
  }
4482
4679
  return Promise.resolve(null);
4483
4680
  },
4484
- findByEmail(tenantId, email) {
4681
+ findByEmail(email) {
4682
+ const tenantId = getTenantId();
4485
4683
  for (const profile of stores.userProfiles.values()) {
4486
4684
  if (profile.tenantId === tenantId && profile.email === email) {
4487
4685
  return Promise.resolve(profile);
@@ -4490,9 +4688,10 @@ function createMockUserProfilesRepository(stores) {
4490
4688
  return Promise.resolve(null);
4491
4689
  },
4492
4690
  create(data) {
4691
+ const tenantId = getTenantId();
4493
4692
  const profile = {
4494
4693
  id: generateId(),
4495
- tenantId: data.tenantId,
4694
+ tenantId,
4496
4695
  authId: data.authId,
4497
4696
  email: data.email,
4498
4697
  firstName: data.firstName,
@@ -4523,14 +4722,16 @@ function createMockUserProfilesRepository(stores) {
4523
4722
  stores.userProfiles.delete(id);
4524
4723
  return Promise.resolve();
4525
4724
  },
4526
- list(tenantId, options) {
4725
+ list(options) {
4726
+ const tenantId = getTenantId();
4527
4727
  let results = Array.from(stores.userProfiles.values()).filter((p) => p.tenantId === tenantId);
4528
4728
  if (options?.limit) {
4529
4729
  results = results.slice(options.offset ?? 0, (options.offset ?? 0) + options.limit);
4530
4730
  }
4531
4731
  return Promise.resolve(results);
4532
4732
  },
4533
- countByRole(tenantId, role) {
4733
+ countByRole(role) {
4734
+ const tenantId = getTenantId();
4534
4735
  const count = Array.from(stores.userProfiles.values()).filter(
4535
4736
  (profile) => profile.tenantId === tenantId && profile.role === role
4536
4737
  ).length;
@@ -4554,9 +4755,10 @@ function createMockFilesRepository(stores) {
4554
4755
  return Promise.resolve(file2 ?? null);
4555
4756
  },
4556
4757
  create(data) {
4758
+ const tenantId = getTenantId();
4557
4759
  const file2 = {
4558
4760
  id: generateId(),
4559
- tenantId: data.tenantId,
4761
+ tenantId,
4560
4762
  name: data.name,
4561
4763
  originalName: data.originalName,
4562
4764
  mimeType: data.mimeType,
@@ -4601,7 +4803,8 @@ function createMockFilesRepository(stores) {
4601
4803
  stores.files.delete(id);
4602
4804
  return Promise.resolve();
4603
4805
  },
4604
- list(tenantId, options) {
4806
+ list(options) {
4807
+ const tenantId = getTenantId();
4605
4808
  let results = Array.from(stores.files.values()).filter(
4606
4809
  (f) => f.tenantId === tenantId && !f.deletedAt
4607
4810
  );
@@ -4613,7 +4816,8 @@ function createMockFilesRepository(stores) {
4613
4816
  }
4614
4817
  return Promise.resolve(results);
4615
4818
  },
4616
- findByFolder(tenantId, folderPath) {
4819
+ findByFolder(folderPath) {
4820
+ const tenantId = getTenantId();
4617
4821
  const results = Array.from(stores.files.values()).filter(
4618
4822
  (f) => f.tenantId === tenantId && f.folderPath === folderPath && !f.deletedAt
4619
4823
  );
@@ -4660,19 +4864,23 @@ function createMockObjectRecordsRepository(stores) {
4660
4864
  return Promise.resolve(results);
4661
4865
  },
4662
4866
  create(data) {
4867
+ const tenantId = getTenantId();
4663
4868
  const internal = {
4664
4869
  id: generateId(),
4665
- tenantId: data.tenantId,
4870
+ tenantId,
4666
4871
  objectId: data.objectId,
4667
4872
  label: data.label,
4668
4873
  completionStatus: data.completionStatus,
4669
4874
  values: data.data,
4670
4875
  metadata: data.metadata ?? {},
4671
4876
  createdAt: /* @__PURE__ */ new Date(),
4672
- updatedAt: /* @__PURE__ */ new Date()
4877
+ updatedAt: /* @__PURE__ */ new Date(),
4878
+ createdBy: data.createdBy,
4879
+ lastUpdatedBy: data.createdBy
4880
+ // Same as createdBy on initial creation
4673
4881
  };
4674
4882
  stores.objectRecords.set(internal.id, internal);
4675
- const { tenantId: _, ...record } = internal;
4883
+ const { tenantId: _t, ...record } = internal;
4676
4884
  return Promise.resolve(record);
4677
4885
  },
4678
4886
  update(id, data) {
@@ -4680,7 +4888,7 @@ function createMockObjectRecordsRepository(stores) {
4680
4888
  if (!existing) {
4681
4889
  return Promise.reject(new Error(`ObjectRecord ${id} not found`));
4682
4890
  }
4683
- const { __completionStatus, __label, __metadata, ...valueData } = data;
4891
+ const { __completionStatus, __label, __metadata, __lastUpdatedBy, ...valueData } = data;
4684
4892
  const updated = {
4685
4893
  ...existing,
4686
4894
  label: __label ?? existing.label,
@@ -4688,7 +4896,8 @@ function createMockObjectRecordsRepository(stores) {
4688
4896
  // Metadata uses replace behavior (not merge)
4689
4897
  metadata: __metadata !== void 0 ? __metadata : existing.metadata,
4690
4898
  values: { ...existing.values, ...valueData },
4691
- updatedAt: /* @__PURE__ */ new Date()
4899
+ updatedAt: /* @__PURE__ */ new Date(),
4900
+ lastUpdatedBy: __lastUpdatedBy ?? existing.lastUpdatedBy
4692
4901
  };
4693
4902
  stores.objectRecords.set(id, updated);
4694
4903
  const { tenantId: _, ...record } = updated;
@@ -4698,7 +4907,8 @@ function createMockObjectRecordsRepository(stores) {
4698
4907
  stores.objectRecords.delete(id);
4699
4908
  return Promise.resolve();
4700
4909
  },
4701
- list(tenantId, objectId, options) {
4910
+ list(objectId, options) {
4911
+ const tenantId = getTenantId();
4702
4912
  let results = Array.from(stores.objectRecords.values()).filter(
4703
4913
  (r) => r.tenantId === tenantId && r.objectId === objectId
4704
4914
  );
@@ -4706,10 +4916,11 @@ function createMockObjectRecordsRepository(stores) {
4706
4916
  if (options?.limit) {
4707
4917
  results = results.slice(options.offset ?? 0, (options.offset ?? 0) + options.limit);
4708
4918
  }
4709
- const records = results.map(({ tenantId: _, ...r }) => r);
4919
+ const records = results.map(({ tenantId: _t, ...r }) => r);
4710
4920
  return Promise.resolve({ records, total });
4711
4921
  },
4712
- search(tenantId, objectId, query, options) {
4922
+ search(objectId, query, options) {
4923
+ const tenantId = getTenantId();
4713
4924
  const lowerQuery = query.toLowerCase();
4714
4925
  let results = Array.from(stores.objectRecords.values()).filter((r) => {
4715
4926
  if (r.tenantId !== tenantId || r.objectId !== objectId) return false;
@@ -4721,10 +4932,12 @@ function createMockObjectRecordsRepository(stores) {
4721
4932
  if (options?.limit) {
4722
4933
  results = results.slice(options.offset ?? 0, (options.offset ?? 0) + options.limit);
4723
4934
  }
4724
- const records = results.map(({ tenantId: _, ...r }) => r);
4935
+ const records = results.map(({ tenantId: _t, ...r }) => r);
4725
4936
  return Promise.resolve({ records, total });
4726
4937
  },
4727
- globalSearch(tenantId, query, options) {
4938
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: This is a mock adapter, we don't need to worry about complexity
4939
+ globalSearch(query, options) {
4940
+ const tenantId = getTenantId();
4728
4941
  const lowerQuery = query.toLowerCase();
4729
4942
  const objectsMap = /* @__PURE__ */ new Map();
4730
4943
  for (const obj of stores.objects.values()) {
@@ -4795,6 +5008,7 @@ function createMockObjectRecordsRepository(stores) {
4795
5008
  // -------------------------------------------------------------------------
4796
5009
  // Schema Integrity Methods
4797
5010
  // -------------------------------------------------------------------------
5011
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: This is a mock adapter, we don't need to worry about complexity
4798
5012
  countRecordsReferencingId(targetId) {
4799
5013
  const relationAttrs = [];
4800
5014
  for (const attr of stores.attributes.values()) {
@@ -4871,6 +5085,7 @@ function createMockObjectRecordsRepository(stores) {
4871
5085
  }
4872
5086
  return Promise.resolve(updated);
4873
5087
  },
5088
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: This is a mock adapter, we don't need to worry about complexity
4874
5089
  findByRelation(objectName, relationAttributeName, targetRecordId) {
4875
5090
  const obj = Array.from(stores.objects.values()).find((o) => o.name === objectName);
4876
5091
  if (!obj) return Promise.resolve([]);
@@ -4898,21 +5113,24 @@ function createMockViewsRepository(stores) {
4898
5113
  findById(id) {
4899
5114
  return Promise.resolve(stores.views.get(id) ?? null);
4900
5115
  },
4901
- findByName(tenantId, objectName, viewName) {
5116
+ findByName(objectName, viewName) {
5117
+ const tenantId = getTenantId();
4902
5118
  return Promise.resolve(
4903
5119
  Array.from(stores.views.values()).find(
4904
5120
  (v) => v.tenantId === tenantId && v.objectName === objectName && v.name === viewName
4905
5121
  ) ?? null
4906
5122
  );
4907
5123
  },
4908
- findByObjectName(tenantId, objectName) {
5124
+ findByObjectName(objectName) {
5125
+ const tenantId = getTenantId();
4909
5126
  return Promise.resolve(
4910
5127
  Array.from(stores.views.values()).filter(
4911
5128
  (v) => v.tenantId === tenantId && v.objectName === objectName
4912
5129
  )
4913
5130
  );
4914
5131
  },
4915
- findAllForTenant(tenantId) {
5132
+ findAllForTenant() {
5133
+ const tenantId = getTenantId();
4916
5134
  return Promise.resolve(
4917
5135
  Array.from(stores.views.values()).filter((v) => v.tenantId === tenantId)
4918
5136
  );
@@ -4930,11 +5148,12 @@ function createMockViewsRepository(stores) {
4930
5148
  );
4931
5149
  },
4932
5150
  create(data) {
5151
+ const tenantId = getTenantId();
4933
5152
  const id = generateId();
4934
5153
  const now = /* @__PURE__ */ new Date();
4935
5154
  const dbView = {
4936
5155
  id,
4937
- tenantId: data.tenantId,
5156
+ tenantId,
4938
5157
  objectName: data.objectName,
4939
5158
  name: data.name,
4940
5159
  label: data.label,
@@ -4978,7 +5197,7 @@ function createMockViewsRepository(stores) {
4978
5197
  return Promise.resolve(deleted);
4979
5198
  },
4980
5199
  async upsert(data) {
4981
- const existing = data.system ? await this.findSystemByName(data.objectName, data.name) : await this.findByName(data.tenantId, data.objectName, data.name);
5200
+ const existing = data.system ? await this.findSystemByName(data.objectName, data.name) : await this.findByName(data.objectName, data.name);
4982
5201
  if (existing) {
4983
5202
  return this.update(existing.id, {
4984
5203
  label: data.label,
@@ -4995,14 +5214,16 @@ function createMockViewsRepository(stores) {
4995
5214
  }
4996
5215
  function createMockPermissionsRepository(stores) {
4997
5216
  return {
4998
- getRolesByTenant(tenantId) {
5217
+ getRoles() {
5218
+ const tenantId = getTenantId();
4999
5219
  const results = Array.from(stores.roles.values()).filter((r) => r.tenantId === tenantId);
5000
5220
  return Promise.resolve(results);
5001
5221
  },
5002
5222
  getRoleById(roleId) {
5003
5223
  return Promise.resolve(stores.roles.get(roleId) ?? null);
5004
5224
  },
5005
- getRoleByName(tenantId, name) {
5225
+ getRoleByName(name) {
5226
+ const tenantId = getTenantId();
5006
5227
  for (const role of stores.roles.values()) {
5007
5228
  if (role.tenantId === tenantId && role.name === name) {
5008
5229
  return Promise.resolve(role);
@@ -5011,9 +5232,10 @@ function createMockPermissionsRepository(stores) {
5011
5232
  return Promise.resolve(null);
5012
5233
  },
5013
5234
  createRole(input) {
5235
+ const tenantId = getTenantId();
5014
5236
  const role = {
5015
5237
  id: generateId(),
5016
- tenantId: input.tenantId,
5238
+ tenantId,
5017
5239
  name: input.name,
5018
5240
  label: input.label,
5019
5241
  description: input.description,
@@ -5079,7 +5301,8 @@ function createMockPermissionsRepository(stores) {
5079
5301
  }
5080
5302
  return Promise.resolve();
5081
5303
  },
5082
- getUserRoles(userProfileId, tenantId) {
5304
+ getUserRoles(userProfileId) {
5305
+ const tenantId = getTenantId();
5083
5306
  const roleIds = Array.from(stores.userRoles.values()).filter((ur) => ur.userProfileId === userProfileId && ur.tenantId === tenantId).map((ur) => ur.roleId);
5084
5307
  const roles = Array.from(stores.roles.values()).filter((r) => roleIds.includes(r.id));
5085
5308
  return Promise.resolve(roles);
@@ -5101,7 +5324,8 @@ function createMockPermissionsRepository(stores) {
5101
5324
  stores.userRoles.set(userRole.id, userRole);
5102
5325
  return Promise.resolve(userRole);
5103
5326
  },
5104
- revokeRole(userProfileId, roleId, tenantId) {
5327
+ revokeRole(userProfileId, roleId) {
5328
+ const tenantId = getTenantId();
5105
5329
  for (const [id, ur] of stores.userRoles.entries()) {
5106
5330
  if (ur.userProfileId === userProfileId && ur.roleId === roleId && ur.tenantId === tenantId) {
5107
5331
  stores.userRoles.delete(id);
@@ -5110,7 +5334,9 @@ function createMockPermissionsRepository(stores) {
5110
5334
  }
5111
5335
  return Promise.resolve();
5112
5336
  },
5113
- getEffectivePermissions(userProfileId, tenantId) {
5337
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: This is a mock adapter, we don't need to worry about complexity
5338
+ getEffectivePermissions(userProfileId) {
5339
+ const tenantId = getTenantId();
5114
5340
  const userRoleIds = Array.from(stores.userRoles.values()).filter((ur) => ur.userProfileId === userProfileId && ur.tenantId === tenantId).map((ur) => ur.roleId);
5115
5341
  const userRoles = Array.from(stores.roles.values()).filter((r) => userRoleIds.includes(r.id));
5116
5342
  const isAdmin = userRoles.some((r) => r.name === "admin");
@@ -5185,6 +5411,148 @@ function createMockAdapter() {
5185
5411
  return adapter;
5186
5412
  }
5187
5413
 
5414
+ // src/runtime/policies/policy.registry.ts
5415
+ var PolicyRegistry = class {
5416
+ constructor() {
5417
+ this.policies = /* @__PURE__ */ new Map();
5418
+ }
5419
+ /**
5420
+ * Register a policy for an object type.
5421
+ * Overwrites any existing policy for the same object.
5422
+ *
5423
+ * @param policy - The policy to register
5424
+ */
5425
+ register(policy) {
5426
+ this.policies.set(policy.objectName, policy);
5427
+ }
5428
+ /**
5429
+ * Unregister a policy for an object type.
5430
+ *
5431
+ * @param objectName - The object name to unregister
5432
+ * @returns true if a policy was removed, false if none existed
5433
+ */
5434
+ unregister(objectName) {
5435
+ return this.policies.delete(objectName);
5436
+ }
5437
+ /**
5438
+ * Get the policy for an object type.
5439
+ *
5440
+ * @param objectName - The object name to look up
5441
+ * @returns The policy if registered, undefined otherwise
5442
+ */
5443
+ get(objectName) {
5444
+ return this.policies.get(objectName);
5445
+ }
5446
+ /**
5447
+ * Check if a policy is registered for an object type.
5448
+ *
5449
+ * @param objectName - The object name to check
5450
+ * @returns true if a policy exists
5451
+ */
5452
+ has(objectName) {
5453
+ return this.policies.has(objectName);
5454
+ }
5455
+ /**
5456
+ * Get all registered object names.
5457
+ */
5458
+ listObjectNames() {
5459
+ return Array.from(this.policies.keys());
5460
+ }
5461
+ /**
5462
+ * Clear all registered policies.
5463
+ */
5464
+ clear() {
5465
+ this.policies.clear();
5466
+ }
5467
+ };
5468
+ var defaultPolicyRegistry = new PolicyRegistry();
5469
+
5470
+ // src/runtime/policies/notes.policy.ts
5471
+ var notesPolicy = {
5472
+ objectName: "notes",
5473
+ /**
5474
+ * Inject visibility filter for list/search operations.
5475
+ *
5476
+ * Creates: (visibility = 'shared') OR (createdBy = userId)
5477
+ *
5478
+ * This filters at the SQL level for better performance.
5479
+ * canAccessRecord provides defense-in-depth as a secondary check.
5480
+ *
5481
+ * Note: When user filters exist, we preserve linkedTo filter (common for notes)
5482
+ * but the visibility OR filter takes precedence. Full filter merging would require
5483
+ * AdvancedFilterState support in the adapter.
5484
+ */
5485
+ applyListFilter(ctx, options) {
5486
+ const visibilityRules = [
5487
+ { attribute: "visibility", operator: "is", value: "shared" },
5488
+ { attribute: "createdBy", operator: "is", value: ctx.userId }
5489
+ ];
5490
+ if (!options?.filters || options.filters.rules.length === 0) {
5491
+ return {
5492
+ ...options,
5493
+ filters: { combinator: "or", rules: visibilityRules }
5494
+ };
5495
+ }
5496
+ const linkedToFilter = options.filters.rules.find((r) => r.attribute === "linkedTo");
5497
+ if (linkedToFilter) {
5498
+ return {
5499
+ ...options,
5500
+ filters: {
5501
+ combinator: "and",
5502
+ rules: [linkedToFilter]
5503
+ }
5504
+ };
5505
+ }
5506
+ return {
5507
+ ...options,
5508
+ filters: { combinator: "or", rules: visibilityRules }
5509
+ };
5510
+ },
5511
+ /**
5512
+ * Check if user can access a specific note.
5513
+ *
5514
+ * Rules:
5515
+ * - Shared notes: accessible by anyone in the tenant
5516
+ * - Private notes: only accessible by the author (createdBy)
5517
+ */
5518
+ canAccessRecord(ctx, record) {
5519
+ const visibility = record.values.visibility;
5520
+ if (visibility === "shared") {
5521
+ return true;
5522
+ }
5523
+ if (visibility === "private") {
5524
+ return record.createdBy === ctx.userId;
5525
+ }
5526
+ return false;
5527
+ },
5528
+ /**
5529
+ * Check if user can modify a note.
5530
+ *
5531
+ * Rules:
5532
+ * - Private notes: only the author can modify
5533
+ * - Shared notes: anyone can modify
5534
+ */
5535
+ canModifyRecord(ctx, record) {
5536
+ const visibility = record.values.visibility;
5537
+ if (visibility === "private") {
5538
+ return record.createdBy === ctx.userId;
5539
+ }
5540
+ return true;
5541
+ },
5542
+ /**
5543
+ * Check if user can delete a note.
5544
+ *
5545
+ * Rules:
5546
+ * - Only the author (createdBy) can delete a note, regardless of visibility
5547
+ */
5548
+ canDeleteRecord(ctx, record) {
5549
+ return record.createdBy === ctx.userId;
5550
+ }
5551
+ };
5552
+
5553
+ // src/runtime/policies/index.ts
5554
+ defaultPolicyRegistry.register(notesPolicy);
5555
+
5188
5556
  // src/runtime/services/audit.helpers.ts
5189
5557
  function buildAuditChanges(oldValues, newValues, fieldsToCheck) {
5190
5558
  const changes = [];
@@ -5200,6 +5568,40 @@ function buildAuditChanges(oldValues, newValues, fieldsToCheck) {
5200
5568
  return changes;
5201
5569
  }
5202
5570
 
5571
+ // src/runtime/services/base.service.ts
5572
+ var TenantAwareService = class {
5573
+ /**
5574
+ * Get the current tenant ID from execution context.
5575
+ * @throws TenantContextError if no context is set
5576
+ */
5577
+ get tenantId() {
5578
+ return getTenantId();
5579
+ }
5580
+ /**
5581
+ * Get the current user ID from execution context, if set.
5582
+ * Returns undefined if no user is associated with the current context.
5583
+ */
5584
+ get userId() {
5585
+ return getUserId();
5586
+ }
5587
+ };
5588
+ var TenantAwareRepository = class {
5589
+ /**
5590
+ * Get the current tenant ID from execution context.
5591
+ * @throws TenantContextError if no context is set
5592
+ */
5593
+ get tenantId() {
5594
+ return getTenantId();
5595
+ }
5596
+ /**
5597
+ * Get the current user ID from execution context, if set.
5598
+ * Returns undefined if no user is associated with the current context.
5599
+ */
5600
+ get userId() {
5601
+ return getUserId();
5602
+ }
5603
+ };
5604
+
5203
5605
  // src/runtime/services/audit.service.ts
5204
5606
  var SENSITIVE_PATTERNS = [
5205
5607
  /password/i,
@@ -5212,10 +5614,10 @@ var SENSITIVE_PATTERNS = [
5212
5614
  /ssn/i,
5213
5615
  /cvv/i
5214
5616
  ];
5215
- var AuditService = class {
5216
- constructor(adapter, tenantId, options) {
5617
+ var AuditService = class extends TenantAwareService {
5618
+ constructor(adapter, options) {
5619
+ super();
5217
5620
  this.adapter = adapter;
5218
- this.tenantId = tenantId;
5219
5621
  this.options = options;
5220
5622
  this.buffer = [];
5221
5623
  this.flushTimer = null;
@@ -5235,9 +5637,7 @@ var AuditService = class {
5235
5637
  */
5236
5638
  async logRecordAction(params) {
5237
5639
  const entry = {
5238
- tenantId: this.tenantId,
5239
5640
  actorId: params.actorId,
5240
- actorEmail: params.actorEmail,
5241
5641
  actorType: "user",
5242
5642
  action: params.action,
5243
5643
  resourceType: "record",
@@ -5255,9 +5655,7 @@ var AuditService = class {
5255
5655
  */
5256
5656
  async logSchemaAction(params) {
5257
5657
  const entry = {
5258
- tenantId: this.tenantId,
5259
5658
  actorId: params.actorId,
5260
- actorEmail: params.actorEmail,
5261
5659
  actorType: "user",
5262
5660
  action: params.action,
5263
5661
  resourceType: params.resourceType,
@@ -5275,9 +5673,7 @@ var AuditService = class {
5275
5673
  */
5276
5674
  async logUserAction(params) {
5277
5675
  const entry = {
5278
- tenantId: this.tenantId,
5279
5676
  actorId: params.actorId,
5280
- actorEmail: params.actorEmail,
5281
5677
  actorType: "user",
5282
5678
  action: params.action,
5283
5679
  resourceType: "user",
@@ -5293,9 +5689,7 @@ var AuditService = class {
5293
5689
  */
5294
5690
  async logRoleAction(params) {
5295
5691
  const entry = {
5296
- tenantId: this.tenantId,
5297
5692
  actorId: params.actorId,
5298
- actorEmail: params.actorEmail,
5299
5693
  actorType: "user",
5300
5694
  action: params.action,
5301
5695
  resourceType: "role",
@@ -5314,9 +5708,7 @@ var AuditService = class {
5314
5708
  */
5315
5709
  async logFileAction(params) {
5316
5710
  const entry = {
5317
- tenantId: this.tenantId,
5318
5711
  actorId: params.actorId,
5319
- actorEmail: params.actorEmail,
5320
5712
  actorType: "user",
5321
5713
  action: params.action,
5322
5714
  resourceType: "file",
@@ -5332,7 +5724,6 @@ var AuditService = class {
5332
5724
  */
5333
5725
  async logSystemAction(params) {
5334
5726
  const entry = {
5335
- tenantId: this.tenantId,
5336
5727
  actorType: "system",
5337
5728
  action: params.action,
5338
5729
  resourceType: params.resourceType,
@@ -5355,7 +5746,7 @@ var AuditService = class {
5355
5746
  if (!this.adapter.audit) {
5356
5747
  return { entries: [], total: 0 };
5357
5748
  }
5358
- return await this.adapter.audit.list(this.tenantId, options);
5749
+ return await this.adapter.audit.list(options);
5359
5750
  }
5360
5751
  /**
5361
5752
  * Get audit history for a specific record
@@ -5364,7 +5755,7 @@ var AuditService = class {
5364
5755
  if (!this.adapter.audit) {
5365
5756
  return [];
5366
5757
  }
5367
- return await this.adapter.audit.getByResource(this.tenantId, "record", recordId, {
5758
+ return await this.adapter.audit.getByResource("record", recordId, {
5368
5759
  orderDirection: "desc"
5369
5760
  });
5370
5761
  }
@@ -5375,7 +5766,7 @@ var AuditService = class {
5375
5766
  if (!this.adapter.audit) {
5376
5767
  return [];
5377
5768
  }
5378
- return await this.adapter.audit.getByActor(this.tenantId, userId, options);
5769
+ return await this.adapter.audit.getByActor(userId, options);
5379
5770
  }
5380
5771
  /**
5381
5772
  * Get audit logs for a specific resource
@@ -5384,7 +5775,7 @@ var AuditService = class {
5384
5775
  if (!this.adapter.audit) {
5385
5776
  return [];
5386
5777
  }
5387
- return await this.adapter.audit.getByResource(this.tenantId, resourceType, resourceId, {
5778
+ return await this.adapter.audit.getByResource(resourceType, resourceId, {
5388
5779
  orderDirection: "desc"
5389
5780
  });
5390
5781
  }
@@ -5431,12 +5822,6 @@ var AuditService = class {
5431
5822
  if (!this.adapter.audit) {
5432
5823
  return;
5433
5824
  }
5434
- if (entry.actorId && !entry.actorEmail && entry.actorType === "user") {
5435
- const profile = await this.adapter.userProfiles.findById(entry.actorId);
5436
- if (profile) {
5437
- entry.actorEmail = profile.email;
5438
- }
5439
- }
5440
5825
  if (this.options?.async) {
5441
5826
  this.buffer.push(entry);
5442
5827
  const batchSize = this.options.batchSize ?? 10;
@@ -5475,13 +5860,11 @@ var AuditService = class {
5475
5860
  };
5476
5861
 
5477
5862
  // src/runtime/services/file.service.ts
5478
- var FileService = class {
5479
- constructor(adapter, tenantId, options) {
5863
+ var FileService = class extends TenantAwareService {
5864
+ constructor(adapter, options) {
5865
+ super();
5480
5866
  this.adapter = adapter;
5481
- this.tenantId = tenantId;
5482
- this.auditService = options?.auditService ?? (adapter.audit ? new AuditService(adapter, tenantId) : void 0);
5483
- this.userId = options?.userId;
5484
- this.userEmail = options?.userEmail;
5867
+ this.auditService = options?.auditService ?? (adapter.audit ? new AuditService(adapter) : void 0);
5485
5868
  }
5486
5869
  // ============================================================================
5487
5870
  // UPLOAD (requires StorageAdapter)
@@ -5529,7 +5912,6 @@ var FileService = class {
5529
5912
  folderPath: input.folderPath
5530
5913
  });
5531
5914
  const file2 = await this.adapter.files.create({
5532
- tenantId: this.tenantId,
5533
5915
  name: input.fileName,
5534
5916
  originalName: input.fileName,
5535
5917
  mimeType: input.mimeType,
@@ -5548,7 +5930,6 @@ var FileService = class {
5548
5930
  await this.auditService.logFileAction({
5549
5931
  action: "file.uploaded",
5550
5932
  actorId: this.userId,
5551
- actorEmail: this.userEmail,
5552
5933
  fileId: file2.id,
5553
5934
  fileName: file2.name,
5554
5935
  metadata: {
@@ -5591,17 +5972,11 @@ var FileService = class {
5591
5972
  * ```
5592
5973
  */
5593
5974
  async createFile(data) {
5594
- if (data.tenantId !== this.tenantId) {
5595
- throw new Error(
5596
- `Tenant mismatch: service initialized with "${this.tenantId}" but data has "${data.tenantId}"`
5597
- );
5598
- }
5599
5975
  const file2 = await this.adapter.files.create(data);
5600
5976
  if (this.auditService && this.userId) {
5601
5977
  await this.auditService.logFileAction({
5602
5978
  action: "file.uploaded",
5603
5979
  actorId: this.userId,
5604
- actorEmail: this.userEmail,
5605
5980
  fileId: file2.id,
5606
5981
  fileName: file2.name,
5607
5982
  metadata: {
@@ -5664,7 +6039,6 @@ var FileService = class {
5664
6039
  await this.auditService.logFileAction({
5665
6040
  action: "file.updated",
5666
6041
  actorId: this.userId,
5667
- actorEmail: this.userEmail,
5668
6042
  fileId,
5669
6043
  fileName: updatedFile.name,
5670
6044
  changes
@@ -5698,7 +6072,6 @@ var FileService = class {
5698
6072
  await this.auditService.logFileAction({
5699
6073
  action: "file.deleted",
5700
6074
  actorId: this.userId,
5701
- actorEmail: this.userEmail,
5702
6075
  fileId,
5703
6076
  fileName: file2.name
5704
6077
  });
@@ -5730,7 +6103,6 @@ var FileService = class {
5730
6103
  await this.auditService.logFileAction({
5731
6104
  action: "file.deleted",
5732
6105
  actorId: this.userId,
5733
- actorEmail: this.userEmail,
5734
6106
  fileId,
5735
6107
  fileName: file2.name,
5736
6108
  metadata: { deletedFromStorage: true }
@@ -5761,7 +6133,6 @@ var FileService = class {
5761
6133
  await this.auditService.logFileAction({
5762
6134
  action: "file.deleted",
5763
6135
  actorId: this.userId,
5764
- actorEmail: this.userEmail,
5765
6136
  fileId,
5766
6137
  fileName: file2.name,
5767
6138
  metadata: { deletedFromStorage: options?.deleteFromStorage ?? false }
@@ -5776,13 +6147,13 @@ var FileService = class {
5776
6147
  * List files for the tenant
5777
6148
  */
5778
6149
  async listFiles(options) {
5779
- return await this.adapter.files.list(this.tenantId, options);
6150
+ return await this.adapter.files.list(options);
5780
6151
  }
5781
6152
  /**
5782
6153
  * List files by folder
5783
6154
  */
5784
6155
  async listFilesByFolder(folderPath) {
5785
- return await this.adapter.files.findByFolder(this.tenantId, folderPath);
6156
+ return await this.adapter.files.findByFolder(folderPath);
5786
6157
  }
5787
6158
  /**
5788
6159
  * List files uploaded by a specific user
@@ -5931,34 +6302,35 @@ var FileService = class {
5931
6302
  };
5932
6303
 
5933
6304
  // src/runtime/services/flow.service.ts
5934
- var FlowService = class {
6305
+ var FlowService = class extends TenantAwareService {
5935
6306
  constructor(adapter, systemFlows = []) {
6307
+ super();
5936
6308
  this.adapter = adapter;
5937
6309
  this.systemFlows = new Map(systemFlows.map((f) => [f.name, f]));
5938
6310
  }
5939
6311
  /**
5940
- * Get all flows for a tenant (system + custom)
6312
+ * Get all flows for current tenant (system + custom)
5941
6313
  */
5942
- async getAllFlows(tenantId) {
6314
+ async getAllFlows() {
5943
6315
  const systemFlowsList = Array.from(this.systemFlows.values());
5944
6316
  if (!this.adapter.flows) {
5945
6317
  return systemFlowsList;
5946
6318
  }
5947
- const dbFlows = await this.adapter.flows.findAllForTenant(tenantId);
6319
+ const dbFlows = await this.adapter.flows.findAllForTenant();
5948
6320
  const customFlows = dbFlows.filter((f) => !f.system).map(this.convertDBFlowToDefinition);
5949
6321
  return [...systemFlowsList, ...customFlows];
5950
6322
  }
5951
6323
  /**
5952
6324
  * Get published flows only
5953
6325
  */
5954
- async getPublishedFlows(tenantId) {
5955
- const allFlows = await this.getAllFlows(tenantId);
6326
+ async getPublishedFlows() {
6327
+ const allFlows = await this.getAllFlows();
5956
6328
  return allFlows.filter((f) => f.status === "published");
5957
6329
  }
5958
6330
  /**
5959
6331
  * Get a specific flow by name
5960
6332
  */
5961
- async getFlow(name, tenantId) {
6333
+ async getFlow(name) {
5962
6334
  const systemFlow = this.systemFlows.get(name);
5963
6335
  if (systemFlow) {
5964
6336
  return systemFlow;
@@ -5966,7 +6338,7 @@ var FlowService = class {
5966
6338
  if (!this.adapter.flows) {
5967
6339
  return null;
5968
6340
  }
5969
- const dbFlow = await this.adapter.flows.findByName(tenantId, name);
6341
+ const dbFlow = await this.adapter.flows.findByName(name);
5970
6342
  if (dbFlow) {
5971
6343
  return this.convertDBFlowToDefinition(dbFlow);
5972
6344
  }
@@ -5988,7 +6360,7 @@ var FlowService = class {
5988
6360
  /**
5989
6361
  * Create a new custom flow (as draft)
5990
6362
  */
5991
- async createFlow(input, tenantId) {
6363
+ async createFlow(input) {
5992
6364
  if (!this.adapter.flows) {
5993
6365
  throw new SchemaError(
5994
6366
  "Flows feature is not enabled. Database adapter does not support flows.",
@@ -5997,7 +6369,7 @@ var FlowService = class {
5997
6369
  );
5998
6370
  }
5999
6371
  this.validateFlowName(input.name);
6000
- const existing = await this.adapter.flows.findByName(tenantId, input.name);
6372
+ const existing = await this.adapter.flows.findByName(input.name);
6001
6373
  if (existing) {
6002
6374
  throw new SchemaError(
6003
6375
  `Flow "${input.name}" already exists`,
@@ -6014,7 +6386,6 @@ var FlowService = class {
6014
6386
  }
6015
6387
  this.validateFlowStructure(input);
6016
6388
  const dbFlow = await this.adapter.flows.create({
6017
- tenantId,
6018
6389
  name: input.name,
6019
6390
  label: input.label,
6020
6391
  description: input.description,
@@ -6167,6 +6538,12 @@ var FlowService = class {
6167
6538
  * Validate flow structure (slots, pages, relations)
6168
6539
  */
6169
6540
  validateFlowStructure(input) {
6541
+ this.validateBasicFlowRequirements(input);
6542
+ const slotIds = this.validateSlotIds(input.slots);
6543
+ this.validateFieldReferences(input.pages, slotIds);
6544
+ this.validateRelationReferences(input.relations, slotIds);
6545
+ }
6546
+ validateBasicFlowRequirements(input) {
6170
6547
  if (!input.slots || input.slots.length === 0) {
6171
6548
  throw new ValidationError("Flow must have at least one slot", [
6172
6549
  { path: ["slots"], message: "Flow must have at least one slot" }
@@ -6177,13 +6554,18 @@ var FlowService = class {
6177
6554
  { path: ["pages"], message: "Flow must have at least one page" }
6178
6555
  ]);
6179
6556
  }
6180
- const slotIds = new Set(input.slots.map((s) => s.id));
6181
- if (slotIds.size !== input.slots.length) {
6557
+ }
6558
+ validateSlotIds(slots) {
6559
+ const slotIds = new Set(slots.map((s) => s.id));
6560
+ if (slotIds.size !== slots.length) {
6182
6561
  throw new ValidationError("Duplicate slot IDs detected", [
6183
6562
  { path: ["slots"], message: "Duplicate slot IDs detected" }
6184
6563
  ]);
6185
6564
  }
6186
- for (const page of input.pages) {
6565
+ return slotIds;
6566
+ }
6567
+ validateFieldReferences(pages, slotIds) {
6568
+ for (const page of pages) {
6187
6569
  for (const row of page.rows) {
6188
6570
  for (const field of row.fields) {
6189
6571
  if (!slotIds.has(field.slotId)) {
@@ -6197,7 +6579,9 @@ var FlowService = class {
6197
6579
  }
6198
6580
  }
6199
6581
  }
6200
- for (const relation2 of input.relations) {
6582
+ }
6583
+ validateRelationReferences(relations, slotIds) {
6584
+ for (const relation2 of relations) {
6201
6585
  if (!slotIds.has(relation2.sourceSlotId)) {
6202
6586
  throw new ValidationError("Relation references unknown source slot", [
6203
6587
  {
@@ -6287,10 +6671,10 @@ var GeocodingService = class {
6287
6671
  };
6288
6672
 
6289
6673
  // src/runtime/services/global-search.service.ts
6290
- var GlobalSearchService = class {
6291
- constructor(adapter, tenantId) {
6674
+ var GlobalSearchService = class extends TenantAwareService {
6675
+ constructor(adapter) {
6676
+ super();
6292
6677
  this.adapter = adapter;
6293
- this.tenantId = tenantId;
6294
6678
  }
6295
6679
  /**
6296
6680
  * Search across all object records for the tenant
@@ -6320,7 +6704,7 @@ var GlobalSearchService = class {
6320
6704
  if (!query || query.trim().length === 0) {
6321
6705
  return { results: [], total: 0 };
6322
6706
  }
6323
- return await this.adapter.objectRecords.globalSearch(this.tenantId, query.trim(), {
6707
+ return await this.adapter.objectRecords.globalSearch(query.trim(), {
6324
6708
  limit: options?.limit ?? 20,
6325
6709
  offset: options?.offset ?? 0,
6326
6710
  objectNames: options?.objectNames,
@@ -6364,23 +6748,22 @@ var GlobalSearchService = class {
6364
6748
  };
6365
6749
 
6366
6750
  // src/runtime/services/object-schema.service.ts
6367
- var _ObjectSchemaService = class _ObjectSchemaService {
6751
+ var ObjectSchemaService = class extends TenantAwareService {
6368
6752
  constructor(adapter, nativeRegistry, options) {
6753
+ super();
6369
6754
  this.adapter = adapter;
6370
6755
  this.nativeRegistry = nativeRegistry;
6371
6756
  this.auditService = options?.auditService;
6372
- this.userId = options?.userId;
6373
- this.userEmail = options?.userEmail;
6374
6757
  }
6375
6758
  /**
6376
- * Create a new custom object
6377
- * Validates name format and ensures system=false
6759
+ * Create a new custom object.
6760
+ * Validates name format and ensures system=false.
6761
+ * Automatically uses tenant context from AsyncLocalStorage.
6378
6762
  *
6379
6763
  * @param definition - Object definition (attributes can be raw definitions or builders)
6380
- * @param tenantId - Tenant ID owning the object
6381
6764
  * @returns Created ObjectDefinition
6382
6765
  */
6383
- async createCustomObject(definition, tenantId) {
6766
+ async createCustomObject(definition) {
6384
6767
  const builder = object({
6385
6768
  name: definition.name,
6386
6769
  label: definition.label
@@ -6394,17 +6777,14 @@ var _ObjectSchemaService = class _ObjectSchemaService {
6394
6777
  builder.attributes(definition.attributes);
6395
6778
  }
6396
6779
  const objectDef = builder.build();
6397
- const existing = await this.adapter.objects.findByName(tenantId, objectDef.name);
6780
+ const existing = await this.adapter.objects.findByName(objectDef.name);
6398
6781
  if (existing) {
6399
6782
  throw new Error(`Object with name "${objectDef.name}" already exists`);
6400
6783
  }
6401
6784
  const validatedConfigs = [];
6402
6785
  if (objectDef.attributes.length > 0) {
6403
6786
  for (const attr of objectDef.attributes) {
6404
- const config = await this.validateAttributeInput(
6405
- attr,
6406
- tenantId
6407
- );
6787
+ const config = await this.validateAttributeInput(attr);
6408
6788
  validatedConfigs.push(config);
6409
6789
  }
6410
6790
  }
@@ -6414,7 +6794,6 @@ var _ObjectSchemaService = class _ObjectSchemaService {
6414
6794
  };
6415
6795
  const { dbObject, createdAttributes } = await this.adapter.transaction(async (tx) => {
6416
6796
  const dbObj = await tx.objects.create({
6417
- tenantId,
6418
6797
  name: objectDef.name,
6419
6798
  label: objectDef.label,
6420
6799
  pluralLabel: objectDef.pluralLabel,
@@ -6454,7 +6833,6 @@ var _ObjectSchemaService = class _ObjectSchemaService {
6454
6833
  await this.auditService.logSchemaAction({
6455
6834
  action: "object.created",
6456
6835
  actorId: this.userId,
6457
- actorEmail: this.userEmail,
6458
6836
  resourceType: "object",
6459
6837
  resourceId: dbObject.id,
6460
6838
  resourceLabel: dbObject.label,
@@ -6465,16 +6843,16 @@ var _ObjectSchemaService = class _ObjectSchemaService {
6465
6843
  return result;
6466
6844
  }
6467
6845
  /**
6468
- * Add a custom attribute to an existing object
6469
- * Can be used on both custom and native objects
6470
- * Attributes added via API are always custom (system=false)
6846
+ * Add a custom attribute to an existing object.
6847
+ * Can be used on both custom and native objects.
6848
+ * Attributes added via API are always custom (system=false).
6849
+ * Automatically uses tenant context from AsyncLocalStorage.
6471
6850
  *
6472
6851
  * @param objectId - Object UUID from database
6473
6852
  * @param attribute - Attribute definition
6474
- * @param tenantId - Tenant ID (required for relation validation)
6475
6853
  * @returns Created Attribute
6476
6854
  */
6477
- async addAttributeToObject(objectId, attribute, tenantId) {
6855
+ async addAttributeToObject(objectId, attribute) {
6478
6856
  const dbObject = await this.adapter.objects.findById(objectId);
6479
6857
  if (!dbObject) {
6480
6858
  throw new Error(`Object with id "${objectId}" not found`);
@@ -6486,7 +6864,7 @@ var _ObjectSchemaService = class _ObjectSchemaService {
6486
6864
  `Attribute with name "${attribute.name}" already exists on object "${dbObject.name}"`
6487
6865
  );
6488
6866
  }
6489
- const config = await this.validateAttributeInput(attribute, tenantId);
6867
+ const config = await this.validateAttributeInput(attribute);
6490
6868
  const dbAttr = await this.adapter.attributes.create({
6491
6869
  objectId,
6492
6870
  name: attribute.name,
@@ -6502,7 +6880,6 @@ var _ObjectSchemaService = class _ObjectSchemaService {
6502
6880
  await this.auditService.logSchemaAction({
6503
6881
  action: "attribute.created",
6504
6882
  actorId: this.userId,
6505
- actorEmail: this.userEmail,
6506
6883
  resourceType: "attribute",
6507
6884
  resourceId: dbAttr.id,
6508
6885
  resourceLabel: dbAttr.label,
@@ -6513,15 +6890,15 @@ var _ObjectSchemaService = class _ObjectSchemaService {
6513
6890
  return this.convertDBAttributeToAttribute(dbAttr);
6514
6891
  }
6515
6892
  /**
6516
- * Update an attribute
6517
- * Can only update custom attributes (system=false)
6893
+ * Update an attribute.
6894
+ * Can only update custom attributes (system=false).
6895
+ * Automatically uses tenant context from AsyncLocalStorage.
6518
6896
  *
6519
6897
  * @param attributeId - Attribute UUID
6520
6898
  * @param updates - Partial attribute updates
6521
- * @param tenantId - Tenant ID (required for relation validation)
6522
6899
  * @returns Updated Attribute
6523
6900
  */
6524
- async updateAttribute(attributeId, updates, tenantId) {
6901
+ async updateAttribute(attributeId, updates) {
6525
6902
  const dbAttr = await this.adapter.attributes.findById(attributeId);
6526
6903
  if (!dbAttr) {
6527
6904
  throw new Error(`Attribute with id "${attributeId}" not found`);
@@ -6551,7 +6928,7 @@ var _ObjectSchemaService = class _ObjectSchemaService {
6551
6928
  };
6552
6929
  const config = this.buildAttributeConfig(mergedInput);
6553
6930
  if (dbAttr.type === "relation" && updates.targets) {
6554
- await this.validateRelationTargets(mergedInput, tenantId);
6931
+ await this.validateRelationTargets(mergedInput);
6555
6932
  }
6556
6933
  const oldValues = {
6557
6934
  label: dbAttr.label,
@@ -6569,7 +6946,6 @@ var _ObjectSchemaService = class _ObjectSchemaService {
6569
6946
  await this.auditService.logSchemaAction({
6570
6947
  action: "attribute.updated",
6571
6948
  actorId: this.userId,
6572
- actorEmail: this.userEmail,
6573
6949
  resourceType: "attribute",
6574
6950
  resourceId: attributeId,
6575
6951
  resourceLabel: updatedDbAttr.label,
@@ -6617,7 +6993,6 @@ var _ObjectSchemaService = class _ObjectSchemaService {
6617
6993
  await this.auditService.logSchemaAction({
6618
6994
  action: "attribute.deleted",
6619
6995
  actorId: this.userId,
6620
- actorEmail: this.userEmail,
6621
6996
  resourceType: "attribute",
6622
6997
  resourceId: attributeId,
6623
6998
  resourceLabel: dbAttr.label,
@@ -6703,7 +7078,6 @@ var _ObjectSchemaService = class _ObjectSchemaService {
6703
7078
  await this.auditService.logSchemaAction({
6704
7079
  action: "object.updated",
6705
7080
  actorId: this.userId,
6706
- actorEmail: this.userEmail,
6707
7081
  resourceType: "object",
6708
7082
  resourceId: objectId,
6709
7083
  resourceLabel: updatedDbObject.label,
@@ -6725,9 +7099,10 @@ var _ObjectSchemaService = class _ObjectSchemaService {
6725
7099
  return this.convertDBObjectToDefinition(updatedDbObject, dbAttributes);
6726
7100
  }
6727
7101
  /**
6728
- * Delete a custom object
6729
- * Can only delete custom objects (system=false)
6730
- * This will also delete all associated attributes and records
7102
+ * Delete a custom object.
7103
+ * Can only delete custom objects (system=false).
7104
+ * This will also delete all associated attributes and records.
7105
+ * Automatically uses tenant context from AsyncLocalStorage.
6731
7106
  *
6732
7107
  * @param objectId - Object UUID
6733
7108
  *
@@ -6736,7 +7111,7 @@ var _ObjectSchemaService = class _ObjectSchemaService {
6736
7111
  * await service.deleteObject("obj-123");
6737
7112
  * ```
6738
7113
  */
6739
- async deleteObject(objectId, tenantId) {
7114
+ async deleteObject(objectId) {
6740
7115
  const dbObject = await this.adapter.objects.findById(objectId);
6741
7116
  if (!dbObject) {
6742
7117
  throw new Error(`Object with id "${objectId}" not found`);
@@ -6746,22 +7121,15 @@ var _ObjectSchemaService = class _ObjectSchemaService {
6746
7121
  `Cannot delete system object "${dbObject.name}". System objects are protected.`
6747
7122
  );
6748
7123
  }
6749
- const effectiveTenantId = tenantId ?? dbObject.tenantId;
6750
- if (effectiveTenantId) {
6751
- const referencingObjects = await this.findObjectsWithRelationTo(
6752
- dbObject.name,
6753
- effectiveTenantId
6754
- );
6755
- if (referencingObjects.length > 0) {
6756
- throw new ObjectReferencedError(dbObject.name, referencingObjects);
6757
- }
7124
+ const referencingObjects = await this.findObjectsWithRelationTo(dbObject.name);
7125
+ if (referencingObjects.length > 0) {
7126
+ throw new ObjectReferencedError(dbObject.name, referencingObjects);
6758
7127
  }
6759
7128
  await this.adapter.objects.delete(objectId);
6760
7129
  if (this.auditService && this.userId) {
6761
7130
  await this.auditService.logSchemaAction({
6762
7131
  action: "object.deleted",
6763
7132
  actorId: this.userId,
6764
- actorEmail: this.userEmail,
6765
7133
  resourceType: "object",
6766
7134
  resourceId: objectId,
6767
7135
  resourceLabel: dbObject.label,
@@ -6787,41 +7155,29 @@ var _ObjectSchemaService = class _ObjectSchemaService {
6787
7155
  "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'"
6788
7156
  );
6789
7157
  }
6790
- if (_ObjectSchemaService.RESERVED_ATTRIBUTE_NAMES.includes(
6791
- name
6792
- )) {
7158
+ if (RESERVED_ATTRIBUTE_NAMES.includes(name)) {
6793
7159
  throw new Error(
6794
7160
  `"${name}" is a reserved name and cannot be used as an attribute name.
6795
- Reserved names: ${_ObjectSchemaService.RESERVED_ATTRIBUTE_NAMES.join(", ")}`
7161
+ Reserved names: ${RESERVED_ATTRIBUTE_NAMES.join(", ")}`
6796
7162
  );
6797
7163
  }
6798
7164
  }
7165
+ // ============================================================================
7166
+ // SCHEMA RETRIEVAL (DRY - all methods use buildObjectDefinition)
7167
+ // ============================================================================
6799
7168
  /**
6800
- * Get complete object schema (system + custom attributes)
6801
- * If object is native, merges registry definition with DB custom attributes
6802
- * If object is custom, returns DB definition only
7169
+ * Get complete object schema (object attributes + system attributes)
7170
+ * Includes system attributes (createdAt, updatedAt, createdBy, lastUpdatedBy)
6803
7171
  *
6804
7172
  * @param objectId - Object UUID from database
6805
- * @returns Complete ObjectDefinition with all attributes
6806
- *
6807
- * @example
6808
- * ```typescript
6809
- * const service = new ObjectSchemaService(adapter, registry);
6810
- * const productSchema = await service.getObjectSchema("obj-123");
6811
- *
6812
- * console.log(productSchema.attributes); // System + custom attributes
6813
- * ```
7173
+ * @returns Complete ObjectDefinition with all attributes including system attributes
6814
7174
  */
6815
7175
  async getObjectSchema(objectId) {
6816
7176
  const dbObject = await this.adapter.objects.findById(objectId);
6817
7177
  if (!dbObject) {
6818
7178
  throw new Error(`Object with id "${objectId}" not found`);
6819
7179
  }
6820
- const dbAttributes = await this.adapter.attributes.findByObjectId(objectId);
6821
- if (dbObject.system) {
6822
- return this.mergeNativeObject(dbObject, dbAttributes);
6823
- }
6824
- return this.convertDBObjectToDefinition(dbObject, dbAttributes);
7180
+ return this.buildObjectDefinition(dbObject);
6825
7181
  }
6826
7182
  /**
6827
7183
  * Get object schema by name (for system/native objects)
@@ -6832,41 +7188,54 @@ Reserved names: ${_ObjectSchemaService.RESERVED_ATTRIBUTE_NAMES.join(", ")}`
6832
7188
  if (!dbObject) {
6833
7189
  throw new Error(`Native object "${name}" not found in database`);
6834
7190
  }
6835
- const dbAttributes = await this.adapter.attributes.findByObjectId(dbObject.id);
6836
- return this.mergeNativeObject(dbObject, dbAttributes);
7191
+ return this.buildObjectDefinition(dbObject);
6837
7192
  }
6838
7193
  /**
6839
- * Get object schema by name for a tenant (supports both native and custom objects)
7194
+ * Get object schema by name (supports both native and custom objects).
7195
+ * Automatically uses tenant context from AsyncLocalStorage.
6840
7196
  */
6841
- async getObjectSchemaByNameForTenant(name, tenantId) {
6842
- let dbObject = await this.adapter.objects.findByName(tenantId, name);
7197
+ async getObjectSchemaByNameForTenant(name) {
7198
+ let dbObject = await this.adapter.objects.findByName(name);
6843
7199
  if (!dbObject) {
6844
7200
  dbObject = await this.adapter.objects.findSystemByName(name);
6845
7201
  }
6846
7202
  if (!dbObject) {
6847
7203
  throw new Error(`Object "${name}" not found`);
6848
7204
  }
7205
+ return this.buildObjectDefinition(dbObject);
7206
+ }
7207
+ /**
7208
+ * List all object schemas.
7209
+ * Automatically uses tenant context from AsyncLocalStorage.
7210
+ */
7211
+ async listObjectSchemas() {
7212
+ const dbObjects = await this.adapter.objects.list();
7213
+ return Promise.all(dbObjects.map((dbObject) => this.buildObjectDefinition(dbObject)));
7214
+ }
7215
+ // ============================================================================
7216
+ // PRIVATE HELPERS - SCHEMA BUILDING
7217
+ // ============================================================================
7218
+ /**
7219
+ * Build complete ObjectDefinition from DB object
7220
+ * Handles both native (merged with registry) and custom objects
7221
+ * Always appends system attributes at the end
7222
+ * @internal
7223
+ */
7224
+ async buildObjectDefinition(dbObject) {
6849
7225
  const dbAttributes = await this.adapter.attributes.findByObjectId(dbObject.id);
6850
- if (dbObject.system) {
6851
- return this.mergeNativeObject(dbObject, dbAttributes);
6852
- }
6853
- return this.convertDBObjectToDefinition(dbObject, dbAttributes);
7226
+ const baseDef = dbObject.system ? this.mergeNativeObject(dbObject, dbAttributes) : this.convertDBObjectToDefinition(dbObject, dbAttributes);
7227
+ return this.withSystemAttributes(baseDef);
6854
7228
  }
6855
7229
  /**
6856
- * List all object schemas for a tenant
7230
+ * Append system attributes to an ObjectDefinition
7231
+ * System attributes are always available on all records (createdAt, updatedAt, createdBy, lastUpdatedBy)
7232
+ * @internal
6857
7233
  */
6858
- async listObjectSchemas(tenantId) {
6859
- const dbObjects = await this.adapter.objects.list(tenantId);
6860
- const schemas = await Promise.all(
6861
- dbObjects.map(async (dbObject) => {
6862
- const dbAttributes = await this.adapter.attributes.findByObjectId(dbObject.id);
6863
- if (dbObject.system) {
6864
- return this.mergeNativeObject(dbObject, dbAttributes);
6865
- }
6866
- return this.convertDBObjectToDefinition(dbObject, dbAttributes);
6867
- })
6868
- );
6869
- return schemas;
7234
+ withSystemAttributes(def) {
7235
+ return {
7236
+ ...def,
7237
+ attributes: [...def.attributes, ...getSystemAttributeList()]
7238
+ };
6870
7239
  }
6871
7240
  /**
6872
7241
  * Merge native object from registry with custom attributes from DB
@@ -6928,15 +7297,15 @@ Reserved names: ${_ObjectSchemaService.RESERVED_ATTRIBUTE_NAMES.join(", ")}`
6928
7297
  };
6929
7298
  }
6930
7299
  /**
6931
- * Validate attribute input and return parsed config
6932
- * Validates: name format, config schema, relation targets
7300
+ * Validate attribute input and return parsed config.
7301
+ * Validates: name format, config schema, relation targets.
6933
7302
  * @internal
6934
7303
  */
6935
- async validateAttributeInput(attribute, tenantId) {
7304
+ async validateAttributeInput(attribute) {
6936
7305
  this.validateAttributeName(attribute.name);
6937
7306
  const config = this.buildAttributeConfig(attribute);
6938
7307
  if (attribute.type === "relation") {
6939
- await this.validateRelationTargets(attribute, tenantId);
7308
+ await this.validateRelationTargets(attribute);
6940
7309
  }
6941
7310
  return config;
6942
7311
  }
@@ -6963,23 +7332,24 @@ Reserved names: ${_ObjectSchemaService.RESERVED_ATTRIBUTE_NAMES.join(", ")}`
6963
7332
  }
6964
7333
  }
6965
7334
  /**
6966
- * Check if an object exists (in tenant, system, or native registry)
7335
+ * Check if an object exists (in tenant, system, or native registry).
7336
+ * Automatically uses tenant context from AsyncLocalStorage.
6967
7337
  * @internal
6968
7338
  */
6969
- async objectExists(name, tenantId) {
6970
- if (tenantId) {
6971
- const obj = await this.adapter.objects.findByName(tenantId, name);
6972
- if (obj) return true;
6973
- }
7339
+ async objectExists(name) {
7340
+ const obj = await this.adapter.objects.findByName(name);
7341
+ if (obj) return true;
6974
7342
  const systemObj = await this.adapter.objects.findSystemByName(name);
6975
7343
  if (systemObj) return true;
6976
7344
  return this.nativeRegistry.getByName(name) !== void 0;
6977
7345
  }
6978
7346
  /**
6979
- * Validate that relation target objects exist
7347
+ * Validate that relation target objects exist.
7348
+ * Skip validation for universal relations (toAny with target "*").
7349
+ * Automatically uses tenant context from AsyncLocalStorage.
6980
7350
  * @internal
6981
7351
  */
6982
- async validateRelationTargets(attribute, tenantId) {
7352
+ async validateRelationTargets(attribute) {
6983
7353
  const targets = attribute.targets;
6984
7354
  if (!targets || targets.length === 0) {
6985
7355
  throw new Error("Relation attribute must have at least one target object");
@@ -6989,11 +7359,14 @@ Reserved names: ${_ObjectSchemaService.RESERVED_ATTRIBUTE_NAMES.join(", ")}`
6989
7359
  if (!isValidCardinality) {
6990
7360
  throw new Error("Relation attribute must specify cardinality: 'one' or 'many'");
6991
7361
  }
7362
+ if (targets.length === 1 && targets[0].object === RELATION_TARGET_ANY) {
7363
+ return;
7364
+ }
6992
7365
  for (const target of targets) {
6993
7366
  if (!target.object) {
6994
7367
  throw new Error("Each relation target must specify an object name");
6995
7368
  }
6996
- const exists = await this.objectExists(target.object, tenantId);
7369
+ const exists = await this.objectExists(target.object);
6997
7370
  if (!exists) {
6998
7371
  throw new Error(
6999
7372
  `Relation target object "${target.object}" does not exist. Make sure the object is created before adding a relation to it.`
@@ -7004,10 +7377,11 @@ Reserved names: ${_ObjectSchemaService.RESERVED_ATTRIBUTE_NAMES.join(", ")}`
7004
7377
  /**
7005
7378
  * Find objects that have a relation attribute targeting the given object.
7006
7379
  * Used to prevent deletion of objects that are relation targets.
7380
+ * Automatically uses tenant context from AsyncLocalStorage.
7007
7381
  * @internal
7008
7382
  */
7009
- async findObjectsWithRelationTo(targetObjectName, tenantId) {
7010
- const allObjects = await this.adapter.objects.list(tenantId);
7383
+ async findObjectsWithRelationTo(targetObjectName) {
7384
+ const allObjects = await this.adapter.objects.list();
7011
7385
  const referencing = [];
7012
7386
  for (const obj of allObjects) {
7013
7387
  if (obj.name === targetObjectName) continue;
@@ -7041,23 +7415,12 @@ Reserved names: ${_ObjectSchemaService.RESERVED_ATTRIBUTE_NAMES.join(", ")}`
7041
7415
  return baseAttr;
7042
7416
  }
7043
7417
  };
7044
- /**
7045
- * Reserved attribute names that cannot be used.
7046
- * These names conflict with system fields on ObjectRecord.
7047
- */
7048
- _ObjectSchemaService.RESERVED_ATTRIBUTE_NAMES = [
7049
- "id",
7050
- "createdAt",
7051
- "updatedAt",
7052
- "metadata"
7053
- ];
7054
- var ObjectSchemaService = _ObjectSchemaService;
7055
7418
 
7056
7419
  // src/runtime/services/permission.service.ts
7057
- var PermissionService = class {
7058
- constructor(adapter, tenantId, options) {
7420
+ var PermissionService = class extends TenantAwareService {
7421
+ constructor(adapter, options) {
7422
+ super();
7059
7423
  this.adapter = adapter;
7060
- this.tenantId = tenantId;
7061
7424
  this.cache = /* @__PURE__ */ new Map();
7062
7425
  /** Track pending permission fetches to prevent duplicate concurrent requests */
7063
7426
  this.pendingFetches = /* @__PURE__ */ new Map();
@@ -7070,8 +7433,6 @@ var PermissionService = class {
7070
7433
  this.cacheTtlMs = options?.cacheTtlMs ?? 6e4;
7071
7434
  this.maxCacheSize = options?.maxCacheSize ?? 1e4;
7072
7435
  this.auditService = options?.auditService;
7073
- this.userId = options?.userId;
7074
- this.userEmail = options?.userEmail;
7075
7436
  }
7076
7437
  // ============================================================================
7077
7438
  // PERMISSION CHECKS
@@ -7226,10 +7587,7 @@ var PermissionService = class {
7226
7587
  * @internal
7227
7588
  */
7228
7589
  async fetchAndCachePermissions(userProfileId, cacheKey) {
7229
- const permissions = await this.permissionsRepo.getEffectivePermissions(
7230
- userProfileId,
7231
- this.tenantId
7232
- );
7590
+ const permissions = await this.permissionsRepo.getEffectivePermissions(userProfileId);
7233
7591
  if (this.cache.size >= this.maxCacheSize) {
7234
7592
  const firstKey = this.cache.keys().next().value;
7235
7593
  if (firstKey) {
@@ -7273,9 +7631,10 @@ var PermissionService = class {
7273
7631
  // ============================================================================
7274
7632
  /**
7275
7633
  * Get all roles for the current tenant.
7634
+ * Automatically uses tenant context from AsyncLocalStorage.
7276
7635
  */
7277
7636
  async getRoles() {
7278
- return await this.permissionsRepo.getRolesByTenant(this.tenantId);
7637
+ return await this.permissionsRepo.getRoles();
7279
7638
  }
7280
7639
  /**
7281
7640
  * Get a role by ID.
@@ -7287,15 +7646,11 @@ var PermissionService = class {
7287
7646
  * Create a new role.
7288
7647
  */
7289
7648
  async createRole(input) {
7290
- const role = await this.permissionsRepo.createRole({
7291
- ...input,
7292
- tenantId: this.tenantId
7293
- });
7649
+ const role = await this.permissionsRepo.createRole(input);
7294
7650
  if (this.auditService && this.userId) {
7295
7651
  await this.auditService.logRoleAction({
7296
7652
  action: "role.created",
7297
7653
  actorId: this.userId,
7298
- actorEmail: this.userEmail,
7299
7654
  roleId: role.id,
7300
7655
  roleLabel: role.label
7301
7656
  });
@@ -7324,7 +7679,6 @@ var PermissionService = class {
7324
7679
  await this.auditService.logRoleAction({
7325
7680
  action: "role.updated",
7326
7681
  actorId: this.userId,
7327
- actorEmail: this.userEmail,
7328
7682
  roleId: role.id,
7329
7683
  roleLabel: role.label,
7330
7684
  changes
@@ -7344,7 +7698,6 @@ var PermissionService = class {
7344
7698
  await this.auditService.logRoleAction({
7345
7699
  action: "role.deleted",
7346
7700
  actorId: this.userId,
7347
- actorEmail: this.userEmail,
7348
7701
  roleId,
7349
7702
  roleLabel: role.label
7350
7703
  });
@@ -7370,7 +7723,6 @@ var PermissionService = class {
7370
7723
  await this.auditService.logRoleAction({
7371
7724
  action: "role.updated",
7372
7725
  actorId: this.userId,
7373
- actorEmail: this.userEmail,
7374
7726
  roleId,
7375
7727
  roleLabel: role?.label ?? roleId,
7376
7728
  metadata: { permissionsUpdated: true, permissionCount: permissions.length }
@@ -7384,7 +7736,7 @@ var PermissionService = class {
7384
7736
  * Get all roles assigned to a user.
7385
7737
  */
7386
7738
  async getUserRoles(userProfileId) {
7387
- return await this.permissionsRepo.getUserRoles(userProfileId, this.tenantId);
7739
+ return await this.permissionsRepo.getUserRoles(userProfileId);
7388
7740
  }
7389
7741
  /**
7390
7742
  * Assign a role to a user.
@@ -7393,7 +7745,6 @@ var PermissionService = class {
7393
7745
  const result = await this.permissionsRepo.assignRole({
7394
7746
  userProfileId,
7395
7747
  roleId,
7396
- tenantId: this.tenantId,
7397
7748
  assignedBy
7398
7749
  });
7399
7750
  this.invalidateCache(userProfileId);
@@ -7402,7 +7753,6 @@ var PermissionService = class {
7402
7753
  await this.auditService.logRoleAction({
7403
7754
  action: "role.assigned",
7404
7755
  actorId: this.userId,
7405
- actorEmail: this.userEmail,
7406
7756
  roleId,
7407
7757
  roleLabel: role?.label ?? roleId,
7408
7758
  targetUserId: userProfileId
@@ -7415,13 +7765,12 @@ var PermissionService = class {
7415
7765
  */
7416
7766
  async revokeRole(userProfileId, roleId) {
7417
7767
  const role = await this.permissionsRepo.getRoleById(roleId);
7418
- await this.permissionsRepo.revokeRole(userProfileId, roleId, this.tenantId);
7768
+ await this.permissionsRepo.revokeRole(userProfileId, roleId);
7419
7769
  this.invalidateCache(userProfileId);
7420
7770
  if (this.auditService && this.userId) {
7421
7771
  await this.auditService.logRoleAction({
7422
7772
  action: "role.revoked",
7423
7773
  actorId: this.userId,
7424
- actorEmail: this.userEmail,
7425
7774
  roleId,
7426
7775
  roleLabel: role?.label ?? roleId,
7427
7776
  targetUserId: userProfileId
@@ -7444,7 +7793,7 @@ var PermissionService = class {
7444
7793
  *
7445
7794
  * @example
7446
7795
  * ```typescript
7447
- * const service = new PermissionService(adapter, tenantId);
7796
+ * const service = new PermissionService(adapter);
7448
7797
  * await service.initializeDefaultRoles();
7449
7798
  * ```
7450
7799
  */
@@ -7462,7 +7811,6 @@ var PermissionService = class {
7462
7811
  continue;
7463
7812
  }
7464
7813
  const role = await this.permissionsRepo.createRole({
7465
- tenantId: this.tenantId,
7466
7814
  name: roleName,
7467
7815
  label: DEFAULT_ROLE_LABELS2[roleName],
7468
7816
  description: DEFAULT_ROLE_DESCRIPTIONS2[roleName]
@@ -7491,8 +7839,9 @@ var PermissionService = class {
7491
7839
  };
7492
7840
 
7493
7841
  // src/runtime/services/relation.service.ts
7494
- var RelationService = class {
7842
+ var RelationService = class extends TenantAwareService {
7495
7843
  constructor(adapter, nativeRegistry) {
7844
+ super();
7496
7845
  this.adapter = adapter;
7497
7846
  this.schemaService = new ObjectSchemaService(adapter, nativeRegistry);
7498
7847
  }
@@ -7543,8 +7892,9 @@ var RelationService = class {
7543
7892
  if (ids.length === 0) {
7544
7893
  return errors;
7545
7894
  }
7546
- const validObjectIds = await this.getValidObjectIds(attr.targets);
7547
- if (validObjectIds.size === 0) {
7895
+ const isUniversal = attr.targets.length === 1 && attr.targets[0].object === RELATION_TARGET_ANY;
7896
+ const validObjectIds = isUniversal ? null : await this.getValidObjectIds(attr.targets);
7897
+ if (!isUniversal && validObjectIds?.size === 0) {
7548
7898
  errors.push({
7549
7899
  attribute: attr.name,
7550
7900
  message: `No valid target objects found for ${attr.label}`
@@ -7558,7 +7908,7 @@ var RelationService = class {
7558
7908
  invalidIds.push(id);
7559
7909
  continue;
7560
7910
  }
7561
- if (!validObjectIds.has(record.objectId)) {
7911
+ if (!isUniversal && validObjectIds && !validObjectIds.has(record.objectId)) {
7562
7912
  invalidIds.push(id);
7563
7913
  }
7564
7914
  }
@@ -7588,6 +7938,7 @@ var RelationService = class {
7588
7938
  }
7589
7939
  /**
7590
7940
  * Get valid object IDs from relation targets
7941
+ * Note: Universal relations (toAny) are handled earlier in validateRelationAttribute
7591
7942
  */
7592
7943
  async getValidObjectIds(targets) {
7593
7944
  const objectIds = /* @__PURE__ */ new Set();
@@ -7619,23 +7970,23 @@ var RelationService = class {
7619
7970
  // OPTIONS & RESOLUTION
7620
7971
  // ============================================================================
7621
7972
  /**
7622
- * Get available options for a relation attribute
7623
- * Searches across all target objects defined in the relation
7973
+ * Get available options for a relation attribute.
7974
+ * Searches across all target objects defined in the relation.
7975
+ * Automatically uses tenant context from AsyncLocalStorage.
7624
7976
  *
7625
7977
  * @param attribute - Relation attribute definition
7626
- * @param tenantId - Tenant ID for multi-tenant isolation
7627
7978
  * @param params - Query parameters
7628
7979
  *
7629
7980
  * @example
7630
7981
  * ```typescript
7631
- * const options = await relationService.getOptions(attribute, "tenant-1", {
7982
+ * const options = await relationService.getOptions(attribute, {
7632
7983
  * query: "nike",
7633
7984
  * page: 1,
7634
7985
  * pageSize: 20
7635
7986
  * });
7636
7987
  * ```
7637
7988
  */
7638
- async getOptions(attribute, tenantId, params = {}) {
7989
+ async getOptions(attribute, params = {}) {
7639
7990
  const { query = "", page = 1, pageSize = 20, targetObject } = params;
7640
7991
  const targets = attribute.targets;
7641
7992
  const filteredTargets = targetObject ? targets.filter((t) => t.object === targetObject) : targets;
@@ -7644,7 +7995,7 @@ var RelationService = class {
7644
7995
  }
7645
7996
  const allOptions = [];
7646
7997
  let totalCount = 0;
7647
- const recordService = new RecordService(this.adapter, tenantId);
7998
+ const recordService = new RecordService(this.adapter);
7648
7999
  for (const target of filteredTargets) {
7649
8000
  const objectSchema = await this.schemaService.getObjectSchemaByName(target.object);
7650
8001
  if (!objectSchema?.id) {
@@ -7680,23 +8031,22 @@ var RelationService = class {
7680
8031
  };
7681
8032
  }
7682
8033
  /**
7683
- * Resolve record IDs to their display labels
7684
- * Useful for displaying current values in the UI
8034
+ * Resolve record IDs to their display labels.
8035
+ * Useful for displaying current values in the UI.
7685
8036
  *
7686
8037
  * Uses batch fetching for performance - fetches all records in one query,
7687
8038
  * then groups by objectId to minimize schema lookups.
7688
8039
  *
7689
8040
  * @param ids - Record IDs to resolve
7690
- * @param _tenantId - Tenant ID for multi-tenant isolation (unused but kept for API compatibility)
7691
8041
  * @param attributeId - Relation attribute ID to use its displayTemplate for label rendering
7692
8042
  *
7693
8043
  * @example
7694
8044
  * ```typescript
7695
- * const resolved = await relationService.resolveIds(["rec-1", "rec-2"], "tenant-1", "attr-123");
8045
+ * const resolved = await relationService.resolveIds(["rec-1", "rec-2"], "attr-123");
7696
8046
  * // [{ id: "rec-1", label: "Nike Air Max", objectName: "products", ... }]
7697
8047
  * ```
7698
8048
  */
7699
- async resolveIds(ids, _tenantId, attributeId) {
8049
+ async resolveIds(ids, attributeId) {
7700
8050
  if (!ids || ids.length === 0) {
7701
8051
  return [];
7702
8052
  }
@@ -7831,13 +8181,8 @@ var RollupService = class {
7831
8181
  if (!record) {
7832
8182
  return { value: null, recordCount: 0 };
7833
8183
  }
7834
- const parentObject = await this.adapter.objects.findById(record.objectId);
7835
- if (!parentObject) {
7836
- return { value: null, recordCount: 0 };
7837
- }
7838
- const tenantId = parentObject.tenantId;
7839
8184
  const sourceObjectName = rollupAttr.relationAttribute;
7840
- const sourceObject = await this.adapter.objects.findByName(tenantId, sourceObjectName);
8185
+ const sourceObject = await this.adapter.objects.findByName(sourceObjectName);
7841
8186
  if (!sourceObject) {
7842
8187
  return { value: null, recordCount: 0 };
7843
8188
  }
@@ -7851,7 +8196,6 @@ var RollupService = class {
7851
8196
  return { value: null, recordCount: 0 };
7852
8197
  }
7853
8198
  const relatedRecords = await this.adapter.objectRecords.findByRelation(
7854
- tenantId,
7855
8199
  sourceObject.id,
7856
8200
  reverseRelationAttr.name,
7857
8201
  recordId
@@ -7865,6 +8209,9 @@ var RollupService = class {
7865
8209
  * Extract and aggregate values from related records
7866
8210
  */
7867
8211
  aggregateValues(relatedRecords, rollupAttr) {
8212
+ if (rollupAttr.function === "count") {
8213
+ return { value: relatedRecords.length, recordCount: relatedRecords.length };
8214
+ }
7868
8215
  const values = relatedRecords.map((r) => r.values[rollupAttr.targetAttribute]).filter((v) => v !== void 0 && v !== null);
7869
8216
  const aggregated = this.aggregate(values, rollupAttr.function);
7870
8217
  const result = typeof aggregated === "number" && rollupAttr.decimals !== void 0 ? Number(aggregated.toFixed(rollupAttr.decimals)) : aggregated;
@@ -7893,32 +8240,38 @@ var RollupService = class {
7893
8240
  */
7894
8241
  aggregate(values, fn) {
7895
8242
  switch (fn) {
8243
+ // Numeric aggregates
7896
8244
  case "sum":
7897
8245
  return this.sumNumbers(values);
7898
8246
  case "avg":
7899
8247
  return this.averageNumbers(values);
7900
- case "min":
7901
- return this.minNumbers(values);
7902
- case "max":
7903
- return this.maxNumbers(values);
8248
+ // Date aggregates
8249
+ case "earliest":
8250
+ return this.earliestDate(values);
8251
+ case "latest":
8252
+ return this.latestDate(values);
8253
+ // Count functions
7904
8254
  case "count":
7905
8255
  return values.length;
7906
8256
  case "countValues":
7907
8257
  return values.filter((v) => v != null && v !== "").length;
8258
+ case "countUniqueValues": {
8259
+ const nonEmpty = values.filter((v) => v != null && v !== "");
8260
+ return new Set(nonEmpty.map((v) => JSON.stringify(v))).size;
8261
+ }
7908
8262
  case "countEmpty":
7909
8263
  return values.filter((v) => v == null || v === "").length;
7910
- case "percentFilled": {
8264
+ // Percent functions
8265
+ case "percentNotEmpty": {
7911
8266
  if (values.length === 0) return 0;
7912
8267
  const filled = values.filter((v) => v != null && v !== "").length;
7913
- return filled / values.length * 100;
8268
+ return filled / values.length;
7914
8269
  }
7915
8270
  case "percentEmpty": {
7916
8271
  if (values.length === 0) return 0;
7917
8272
  const empty = values.filter((v) => v == null || v === "").length;
7918
- return empty / values.length * 100;
8273
+ return empty / values.length;
7919
8274
  }
7920
- case "collect":
7921
- return values;
7922
8275
  }
7923
8276
  }
7924
8277
  /**
@@ -7929,16 +8282,15 @@ var RollupService = class {
7929
8282
  case "sum":
7930
8283
  case "count":
7931
8284
  case "countValues":
8285
+ case "countUniqueValues":
7932
8286
  case "countEmpty":
7933
8287
  case "percentEmpty":
7934
- case "percentFilled":
8288
+ case "percentNotEmpty":
7935
8289
  return 0;
7936
8290
  case "avg":
7937
- case "min":
7938
- case "max":
8291
+ case "earliest":
8292
+ case "latest":
7939
8293
  return null;
7940
- case "collect":
7941
- return [];
7942
8294
  }
7943
8295
  }
7944
8296
  /**
@@ -7956,20 +8308,22 @@ var RollupService = class {
7956
8308
  return nums.reduce((sum, n) => sum + n, 0) / nums.length;
7957
8309
  }
7958
8310
  /**
7959
- * Minimum numeric value
8311
+ * Get earliest date from values
7960
8312
  */
7961
- minNumbers(values) {
7962
- const nums = values.filter((v) => typeof v === "number" && !Number.isNaN(v));
7963
- if (nums.length === 0) return null;
7964
- return Math.min(...nums);
8313
+ earliestDate(values) {
8314
+ const dates = values.filter((v) => v instanceof Date || typeof v === "string").map((v) => v instanceof Date ? v : new Date(v)).filter((d) => !Number.isNaN(d.getTime()));
8315
+ if (dates.length === 0) return null;
8316
+ const earliest = new Date(Math.min(...dates.map((d) => d.getTime())));
8317
+ return earliest.toISOString();
7965
8318
  }
7966
8319
  /**
7967
- * Maximum numeric value
8320
+ * Get latest date from values
7968
8321
  */
7969
- maxNumbers(values) {
7970
- const nums = values.filter((v) => typeof v === "number" && !Number.isNaN(v));
7971
- if (nums.length === 0) return null;
7972
- return Math.max(...nums);
8322
+ latestDate(values) {
8323
+ const dates = values.filter((v) => v instanceof Date || typeof v === "string").map((v) => v instanceof Date ? v : new Date(v)).filter((d) => !Number.isNaN(d.getTime()));
8324
+ if (dates.length === 0) return null;
8325
+ const latest = new Date(Math.max(...dates.map((d) => d.getTime())));
8326
+ return latest.toISOString();
7973
8327
  }
7974
8328
  /**
7975
8329
  * Recalculate all rollup attributes for a record and update it
@@ -8030,12 +8384,7 @@ var RollupService = class {
8030
8384
  */
8031
8385
  async findRecordsWithForwardRollup(changedRecord, changedSchema) {
8032
8386
  const affectedRecords = [];
8033
- const recordObject = await this.adapter.objects.findById(changedRecord.objectId);
8034
- if (!recordObject) {
8035
- return affectedRecords;
8036
- }
8037
- const tenantId = recordObject.tenantId;
8038
- const allObjects = await this.adapter.objects.list(tenantId);
8387
+ const allObjects = await this.adapter.objects.list();
8039
8388
  for (const obj of allObjects) {
8040
8389
  if (obj.id === changedRecord.objectId) {
8041
8390
  continue;
@@ -8064,7 +8413,6 @@ var RollupService = class {
8064
8413
  continue;
8065
8414
  }
8066
8415
  const recordsPointingToChanged = await this.adapter.objectRecords.findByRelation(
8067
- tenantId,
8068
8416
  obj.id,
8069
8417
  relationAttr.name,
8070
8418
  changedRecord.id
@@ -8081,10 +8429,10 @@ var RollupService = class {
8081
8429
  };
8082
8430
 
8083
8431
  // src/runtime/services/user.service.ts
8084
- var UserService = class {
8085
- constructor(adapter, tenantId) {
8432
+ var UserService = class extends TenantAwareService {
8433
+ constructor(adapter) {
8434
+ super();
8086
8435
  this.adapter = adapter;
8087
- this.tenantId = tenantId;
8088
8436
  }
8089
8437
  /**
8090
8438
  * Validate all user attributes in the data
@@ -8198,23 +8546,32 @@ var UserService = class {
8198
8546
  };
8199
8547
 
8200
8548
  // src/runtime/services/record.service.ts
8201
- var RecordService = class {
8202
- constructor(adapter, tenantId, options) {
8549
+ function applyDefaultValues(schema, data) {
8550
+ const result = { ...data };
8551
+ for (const attr of schema.attributes) {
8552
+ if (attr.name in result && result[attr.name] !== void 0) {
8553
+ continue;
8554
+ }
8555
+ if ("defaultValue" in attr && attr.defaultValue !== void 0 && attr.defaultValue !== null) {
8556
+ result[attr.name] = attr.defaultValue;
8557
+ }
8558
+ }
8559
+ return result;
8560
+ }
8561
+ var RecordService = class extends TenantAwareService {
8562
+ constructor(adapter, options) {
8563
+ super();
8203
8564
  this.adapter = adapter;
8204
- this.tenantId = tenantId;
8205
8565
  this.schemaService = new ObjectSchemaService(adapter, registry, {
8206
- userId: options?.userId,
8207
- userEmail: options?.userEmail,
8208
8566
  auditService: options?.auditService
8209
8567
  });
8210
8568
  this.relationService = new RelationService(adapter, registry);
8211
- this.userService = new UserService(adapter, tenantId);
8569
+ this.userService = new UserService(adapter);
8212
8570
  this.rollupService = new RollupService(adapter);
8213
8571
  this.hookRegistry = options?.hookRegistry ?? new NoopHookRegistry();
8214
8572
  this.permissionService = options?.permissionService;
8215
- this.auditService = options?.auditService ?? (adapter.audit ? new AuditService(adapter, tenantId) : void 0);
8216
- this.userId = options?.userId;
8217
- this.userEmail = options?.userEmail;
8573
+ this.auditService = options?.auditService ?? (adapter.audit ? new AuditService(adapter) : void 0);
8574
+ this.policyRegistry = options?.policyRegistry === null ? null : options?.policyRegistry ?? defaultPolicyRegistry;
8218
8575
  }
8219
8576
  /**
8220
8577
  * Check permission for an action on an object.
@@ -8226,6 +8583,67 @@ var RecordService = class {
8226
8583
  await this.permissionService.checkObjectAccess(this.userId, objectName, action);
8227
8584
  }
8228
8585
  }
8586
+ /**
8587
+ * Get policy for an object if one exists and userId is configured.
8588
+ * @internal
8589
+ */
8590
+ getPolicy(objectName) {
8591
+ if (!(this.policyRegistry && this.userId)) {
8592
+ return void 0;
8593
+ }
8594
+ return this.policyRegistry.get(objectName);
8595
+ }
8596
+ /**
8597
+ * Build policy context for the current request.
8598
+ * @internal
8599
+ */
8600
+ buildPolicyContext(objectName) {
8601
+ return {
8602
+ // biome-ignore lint/style/noNonNullAssertion: userId is guaranteed to be set
8603
+ userId: this.userId,
8604
+ tenantId: this.tenantId,
8605
+ objectName
8606
+ };
8607
+ }
8608
+ /**
8609
+ * Check if user can access a record based on policy.
8610
+ * Returns true if no policy exists or user can access.
8611
+ * @internal
8612
+ */
8613
+ checkRecordAccess(policy, record) {
8614
+ if (!policy.canAccessRecord) {
8615
+ return true;
8616
+ }
8617
+ return policy.canAccessRecord(this.buildPolicyContext(policy.objectName), record);
8618
+ }
8619
+ /**
8620
+ * Check if user can modify a record based on policy.
8621
+ * Throws PolicyViolationError if denied.
8622
+ * @internal
8623
+ */
8624
+ checkRecordModify(policy, record) {
8625
+ if (!policy.canModifyRecord) {
8626
+ return;
8627
+ }
8628
+ const canModify = policy.canModifyRecord(this.buildPolicyContext(policy.objectName), record);
8629
+ if (!canModify) {
8630
+ throw new PolicyViolationError(policy.objectName, "update", record.id);
8631
+ }
8632
+ }
8633
+ /**
8634
+ * Check if user can delete a record based on policy.
8635
+ * Throws PolicyViolationError if denied.
8636
+ * @internal
8637
+ */
8638
+ checkRecordDelete(policy, record) {
8639
+ if (!policy.canDeleteRecord) {
8640
+ return;
8641
+ }
8642
+ const canDelete = policy.canDeleteRecord(this.buildPolicyContext(policy.objectName), record);
8643
+ if (!canDelete) {
8644
+ throw new PolicyViolationError(policy.objectName, "delete", record.id);
8645
+ }
8646
+ }
8229
8647
  /**
8230
8648
  * Resolve relation IDs to their display labels
8231
8649
  * @internal
@@ -8248,7 +8666,7 @@ var RecordService = class {
8248
8666
  }
8249
8667
  await Promise.all(
8250
8668
  idsWithAttrId.map(async ({ attrId, ids }) => {
8251
- const resolved = await this.relationService.resolveIds(ids, this.tenantId, attrId);
8669
+ const resolved = await this.relationService.resolveIds(ids, attrId);
8252
8670
  for (const r of resolved) {
8253
8671
  resolvedMap.set(r.id, r.label);
8254
8672
  }
@@ -8336,33 +8754,34 @@ var RecordService = class {
8336
8754
  */
8337
8755
  async createRecord(objectId, data, options) {
8338
8756
  const schema = await this.schemaService.getObjectSchema(objectId);
8757
+ const dataWithDefaults = applyDefaultValues(schema, data);
8339
8758
  await this.checkPermission(schema.name, "create");
8340
- const hookCtx = this.buildCreateHookContext(schema, data, options?.hookMetadata);
8759
+ const hookCtx = this.buildCreateHookContext(schema, dataWithDefaults, options?.hookMetadata);
8341
8760
  if (!options?.skipHooks) {
8342
8761
  await this.hookRegistry.execute("beforeCreate", schema.name, hookCtx);
8343
8762
  }
8344
8763
  if (options?.validate !== false) {
8345
8764
  if (options?.allowDraft) {
8346
- validateDraftOrThrow(schema, data);
8765
+ validateDraftOrThrow(schema, dataWithDefaults);
8347
8766
  } else {
8348
- validateObjectOrThrow(schema, data);
8767
+ validateObjectOrThrow(schema, dataWithDefaults);
8349
8768
  }
8350
8769
  if (!options?.skipRelationValidation) {
8351
- await this.relationService.validateRelationsOrThrow(schema, data);
8770
+ await this.relationService.validateRelationsOrThrow(schema, dataWithDefaults);
8352
8771
  }
8353
8772
  if (!options?.skipUserValidation) {
8354
- await this.userService.validateUsersOrThrow(schema, data);
8773
+ await this.userService.validateUsersOrThrow(schema, dataWithDefaults);
8355
8774
  }
8356
8775
  }
8357
- const completionStatus = computeRecordStatus(schema, data);
8358
- const label = await this.computeLabel(schema, data);
8776
+ const completionStatus = computeRecordStatus(schema, dataWithDefaults);
8777
+ const label = await this.computeLabel(schema, dataWithDefaults);
8359
8778
  const record = await this.adapter.objectRecords.create({
8360
- tenantId: this.tenantId,
8361
8779
  objectId,
8362
- data,
8780
+ data: dataWithDefaults,
8363
8781
  label,
8364
8782
  completionStatus,
8365
- metadata: options?.metadata
8783
+ metadata: options?.metadata,
8784
+ createdBy: this.userId
8366
8785
  });
8367
8786
  if (!options?.skipHooks) {
8368
8787
  const afterCtx = {
@@ -8377,7 +8796,6 @@ var RecordService = class {
8377
8796
  await this.auditService.logRecordAction({
8378
8797
  action: "record.created",
8379
8798
  actorId: this.userId,
8380
- actorEmail: this.userEmail,
8381
8799
  objectName: schema.name,
8382
8800
  objectId: schema.id,
8383
8801
  recordId: record.id,
@@ -8400,6 +8818,12 @@ var RecordService = class {
8400
8818
  return null;
8401
8819
  }
8402
8820
  const schema = await this.schemaService.getObjectSchema(record.objectId);
8821
+ if (!options?.skipPolicyCheck) {
8822
+ const policy = this.getPolicy(schema.name);
8823
+ if (policy && !this.checkRecordAccess(policy, record)) {
8824
+ return null;
8825
+ }
8826
+ }
8403
8827
  let enrichedRecord = record;
8404
8828
  if (!options?.skipFormulas) {
8405
8829
  enrichedRecord = this.enrichWithFormulas(record, schema);
@@ -8445,9 +8869,16 @@ var RecordService = class {
8445
8869
  * ```
8446
8870
  */
8447
8871
  async updateRecord(recordId, data, options) {
8448
- const existing = await this.getRecordOrThrow(recordId);
8872
+ const existing = await this.adapter.objectRecords.findById(recordId);
8873
+ if (!existing) {
8874
+ throw new RecordNotFoundError(recordId);
8875
+ }
8449
8876
  const schema = await this.schemaService.getObjectSchema(existing.objectId);
8450
8877
  await this.checkPermission(schema.name, "update");
8878
+ const policy = this.getPolicy(schema.name);
8879
+ if (policy) {
8880
+ this.checkRecordModify(policy, existing);
8881
+ }
8451
8882
  const mergedData = { ...existing.values, ...data };
8452
8883
  const changedAttributes = Object.keys(data).filter((key) => existing.values[key] !== data[key]);
8453
8884
  const hookCtx = this.buildHookContext(
@@ -8479,10 +8910,16 @@ var RecordService = class {
8479
8910
  const updatePayload = {
8480
8911
  ...data,
8481
8912
  __completionStatus: completionStatus,
8482
- __label: label
8913
+ __label: label,
8914
+ __lastUpdatedBy: this.userId
8483
8915
  };
8484
8916
  if (options?.metadata !== void 0) {
8485
- updatePayload.__metadata = options.metadata;
8917
+ const existingMetadata = existing.metadata ?? {};
8918
+ const mergedMetadata = { ...existingMetadata, ...options.metadata };
8919
+ const cleanedMetadata = Object.fromEntries(
8920
+ Object.entries(mergedMetadata).filter(([, value]) => value !== void 0)
8921
+ );
8922
+ updatePayload.__metadata = cleanedMetadata;
8486
8923
  }
8487
8924
  const updated = await this.adapter.objectRecords.update(recordId, updatePayload);
8488
8925
  if (!options?.skipHooks) {
@@ -8502,7 +8939,6 @@ var RecordService = class {
8502
8939
  await this.auditService.logRecordAction({
8503
8940
  action: "record.updated",
8504
8941
  actorId: this.userId,
8505
- actorEmail: this.userEmail,
8506
8942
  objectName: schema.name,
8507
8943
  objectId: schema.id,
8508
8944
  recordId: updated.id,
@@ -8598,9 +9034,16 @@ var RecordService = class {
8598
9034
  * @param options - Delete options
8599
9035
  */
8600
9036
  async deleteRecord(recordId, options) {
8601
- const record = await this.getRecordOrThrow(recordId);
9037
+ const record = await this.adapter.objectRecords.findById(recordId);
9038
+ if (!record) {
9039
+ throw new RecordNotFoundError(recordId);
9040
+ }
8602
9041
  const schema = await this.schemaService.getObjectSchema(record.objectId);
8603
9042
  await this.checkPermission(schema.name, "delete");
9043
+ const policy = this.getPolicy(schema.name);
9044
+ if (policy) {
9045
+ this.checkRecordDelete(policy, record);
9046
+ }
8604
9047
  if (options?.checkSystem) {
8605
9048
  if (schema.system) {
8606
9049
  throw new ProtectedResourceError("object", schema.name, "delete");
@@ -8625,7 +9068,6 @@ var RecordService = class {
8625
9068
  await this.auditService.logRecordAction({
8626
9069
  action: "record.deleted",
8627
9070
  actorId: this.userId,
8628
- actorEmail: this.userEmail,
8629
9071
  objectName: schema.name,
8630
9072
  objectId: schema.id,
8631
9073
  recordId: record.id,
@@ -8677,7 +9119,6 @@ var RecordService = class {
8677
9119
  await this.auditService.logRecordAction({
8678
9120
  action: "record.restored",
8679
9121
  actorId: this.userId,
8680
- actorEmail: this.userEmail,
8681
9122
  objectName: schema.name,
8682
9123
  objectId: schema.id,
8683
9124
  recordId: restored.id,
@@ -8823,14 +9264,29 @@ var RecordService = class {
8823
9264
  if (this.permissionService && this.userId) {
8824
9265
  await this.checkPermission(schema.name, "read");
8825
9266
  }
8826
- const result = await this.adapter.objectRecords.list(this.tenantId, objectId, options);
9267
+ const policy = options?.skipPolicyFilter ? void 0 : this.getPolicy(schema.name);
9268
+ let effectiveOptions = options;
9269
+ if (policy?.applyListFilter) {
9270
+ effectiveOptions = policy.applyListFilter(this.buildPolicyContext(schema.name), options);
9271
+ }
9272
+ const result = await this.adapter.objectRecords.list(objectId, effectiveOptions);
9273
+ let filteredRecords = result.records;
9274
+ let effectiveTotal = result.total;
9275
+ if (policy?.canAccessRecord) {
9276
+ const ctx = this.buildPolicyContext(schema.name);
9277
+ filteredRecords = result.records.filter((record) => policy.canAccessRecord?.(ctx, record));
9278
+ effectiveTotal = filteredRecords.length;
9279
+ }
8827
9280
  if (!options?.skipFormulas) {
8828
9281
  return {
8829
- records: this.enrichRecordsWithFormulas(result.records, schema),
8830
- total: result.total
9282
+ records: this.enrichRecordsWithFormulas(filteredRecords, schema),
9283
+ total: effectiveTotal
8831
9284
  };
8832
9285
  }
8833
- return result;
9286
+ return {
9287
+ records: filteredRecords,
9288
+ total: effectiveTotal
9289
+ };
8834
9290
  }
8835
9291
  /**
8836
9292
  * Search records using full-text search
@@ -8845,7 +9301,7 @@ var RecordService = class {
8845
9301
  if (this.permissionService && this.userId) {
8846
9302
  await this.checkPermission(schema.name, "read");
8847
9303
  }
8848
- const result = await this.adapter.objectRecords.search(this.tenantId, objectId, query, options);
9304
+ const result = await this.adapter.objectRecords.search(objectId, query, options);
8849
9305
  if (!options?.skipFormulas) {
8850
9306
  return {
8851
9307
  records: this.enrichRecordsWithFormulas(result.records, schema),
@@ -9048,16 +9504,15 @@ var RollupScheduler = class {
9048
9504
  this.maxPending = options?.maxPending ?? 100;
9049
9505
  }
9050
9506
  /**
9051
- * Schedule a rollup recalculation for a parent record
9507
+ * Schedule a rollup recalculation for a parent record.
9052
9508
  *
9053
9509
  * If the same record is already pending, the timer is reset.
9054
9510
  * If too many recalculations are pending, triggers an immediate flush.
9055
9511
  *
9056
9512
  * @param parentId - ID of the parent record to recalculate
9057
9513
  * @param parentObjectId - Object ID of the parent
9058
- * @param tenantId - Tenant ID
9059
9514
  */
9060
- scheduleRecalculation(parentId, parentObjectId, tenantId) {
9515
+ scheduleRecalculation(parentId, parentObjectId) {
9061
9516
  const key = `${parentId}:${parentObjectId}`;
9062
9517
  const existing = this.pending.get(key);
9063
9518
  if (existing) {
@@ -9067,7 +9522,7 @@ var RollupScheduler = class {
9067
9522
  await this.executeRecalculation(parentId, parentObjectId);
9068
9523
  this.pending.delete(key);
9069
9524
  }, this.debounceMs);
9070
- this.pending.set(key, { parentId, parentObjectId, tenantId, timeout });
9525
+ this.pending.set(key, { parentId, parentObjectId, timeout });
9071
9526
  if (this.pending.size >= this.maxPending) {
9072
9527
  this.flush();
9073
9528
  }
@@ -9081,16 +9536,11 @@ var RollupScheduler = class {
9081
9536
  clearTimeout(entry.timeout);
9082
9537
  }
9083
9538
  this.pending.clear();
9084
- const tenantMap = /* @__PURE__ */ new Map();
9085
- for (const [, entry] of entries) {
9086
- tenantMap.set(entry.parentId, entry.tenantId);
9087
- }
9088
9539
  const allIds = entries.map(([, entry]) => entry.parentId);
9089
9540
  const records = await this.adapter.objectRecords.findByIds(allIds);
9090
9541
  for (const record of records) {
9091
9542
  const schema = await this.getSchemaById(record.objectId);
9092
- const tenantId = tenantMap.get(record.id);
9093
- if (schema && tenantId) {
9543
+ if (schema) {
9094
9544
  await this.rollupService.recalculateAndUpdate(record, schema);
9095
9545
  }
9096
9546
  }
@@ -9123,27 +9573,25 @@ var RollupScheduler = class {
9123
9573
  };
9124
9574
 
9125
9575
  // src/runtime/services/user-profile.service.ts
9126
- var UserProfileService = class {
9127
- constructor(adapter, tenantId, options) {
9576
+ var UserProfileService = class extends TenantAwareService {
9577
+ constructor(adapter, options) {
9578
+ super();
9128
9579
  this.adapter = adapter;
9129
- this.tenantId = tenantId;
9130
9580
  this.auditService = options?.auditService;
9131
- this.userId = options?.userId;
9132
- this.userEmail = options?.userEmail;
9133
9581
  }
9134
9582
  /**
9135
- * Create a new user profile (typically after first auth)
9583
+ * Create a new user profile (typically after first auth).
9584
+ * Automatically uses tenant context from AsyncLocalStorage.
9136
9585
  *
9137
- * @param data - User profile creation data
9586
+ * @param data - User profile creation data (without tenantId)
9138
9587
  * @returns Created user profile
9139
9588
  *
9140
9589
  * @example
9141
9590
  * ```typescript
9142
- * const service = new UserProfileService(adapter, "tenant-123");
9591
+ * const service = new UserProfileService(adapter);
9143
9592
  *
9144
9593
  * // After Supabase auth
9145
9594
  * const profile = await service.createProfile({
9146
- * tenantId: "tenant-123",
9147
9595
  * authId: authUser.id,
9148
9596
  * email: authUser.email,
9149
9597
  * firstName: authUser.user_metadata.first_name,
@@ -9154,16 +9602,11 @@ var UserProfileService = class {
9154
9602
  * ```
9155
9603
  */
9156
9604
  async createProfile(data) {
9157
- if (data.tenantId !== this.tenantId) {
9158
- throw new Error(
9159
- `Tenant mismatch: service initialized with "${this.tenantId}" but data has "${data.tenantId}"`
9160
- );
9161
- }
9162
9605
  const existing = await this.adapter.userProfiles.findByAuthId(data.authId);
9163
9606
  if (existing) {
9164
9607
  throw new Error(`User profile with authId "${data.authId}" already exists`);
9165
9608
  }
9166
- const existingEmail = await this.adapter.userProfiles.findByEmail(this.tenantId, data.email);
9609
+ const existingEmail = await this.adapter.userProfiles.findByEmail(data.email);
9167
9610
  if (existingEmail) {
9168
9611
  throw new Error(`User profile with email "${data.email}" already exists in this tenant`);
9169
9612
  }
@@ -9172,7 +9615,6 @@ var UserProfileService = class {
9172
9615
  await this.auditService.logUserAction({
9173
9616
  action: "user.created",
9174
9617
  actorId: this.userId,
9175
- actorEmail: this.userEmail,
9176
9618
  targetUserId: profile.id,
9177
9619
  targetUserEmail: profile.email
9178
9620
  });
@@ -9218,7 +9660,6 @@ var UserProfileService = class {
9218
9660
  * const profile = await service.getOrCreateProfile(
9219
9661
  * authUser.id,
9220
9662
  * {
9221
- * tenantId: "tenant-123",
9222
9663
  * authId: authUser.id,
9223
9664
  * email: authUser.email,
9224
9665
  * role: "member",
@@ -9235,7 +9676,8 @@ var UserProfileService = class {
9235
9676
  return await this.createProfile(data);
9236
9677
  }
9237
9678
  /**
9238
- * Update user profile
9679
+ * Update user profile.
9680
+ * Automatically uses tenant context from AsyncLocalStorage.
9239
9681
  */
9240
9682
  async updateProfile(profileId, data) {
9241
9683
  const existing = await this.getProfileOrThrow(profileId);
@@ -9251,7 +9693,6 @@ var UserProfileService = class {
9251
9693
  await this.auditService.logUserAction({
9252
9694
  action: "user.updated",
9253
9695
  actorId: this.userId,
9254
- actorEmail: this.userEmail,
9255
9696
  targetUserId: profileId,
9256
9697
  targetUserEmail: updated.email,
9257
9698
  changes
@@ -9261,7 +9702,8 @@ var UserProfileService = class {
9261
9702
  return updated;
9262
9703
  }
9263
9704
  /**
9264
- * Delete user profile
9705
+ * Delete user profile.
9706
+ * Automatically uses tenant context from AsyncLocalStorage.
9265
9707
  *
9266
9708
  * @param profileId - Profile UUID
9267
9709
  * @param options - Delete options
@@ -9270,7 +9712,7 @@ var UserProfileService = class {
9270
9712
  const profile = await this.getProfileOrThrow(profileId);
9271
9713
  if (options?.checkAdmin) {
9272
9714
  if (profile.role === "admin") {
9273
- const adminCount = await this.adapter.userProfiles.countByRole(this.tenantId, "admin");
9715
+ const adminCount = await this.adapter.userProfiles.countByRole("admin");
9274
9716
  if (adminCount <= 1) {
9275
9717
  throw new Error("Cannot delete the last admin user");
9276
9718
  }
@@ -9281,17 +9723,17 @@ var UserProfileService = class {
9281
9723
  await this.auditService.logUserAction({
9282
9724
  action: "user.deleted",
9283
9725
  actorId: this.userId,
9284
- actorEmail: this.userEmail,
9285
9726
  targetUserId: profileId,
9286
9727
  targetUserEmail: profile.email
9287
9728
  });
9288
9729
  }
9289
9730
  }
9290
9731
  /**
9291
- * List all user profiles for the tenant
9732
+ * List all user profiles for the tenant.
9733
+ * Automatically uses tenant context from AsyncLocalStorage.
9292
9734
  */
9293
9735
  async listProfiles(options) {
9294
- return await this.adapter.userProfiles.list(this.tenantId, options);
9736
+ return await this.adapter.userProfiles.list(options);
9295
9737
  }
9296
9738
  /**
9297
9739
  * Update last login timestamp
@@ -9326,10 +9768,11 @@ var UserProfileService = class {
9326
9768
  return await this.updateProfile(profileId, { status: newStatus });
9327
9769
  }
9328
9770
  /**
9329
- * Get user by email
9771
+ * Get user by email.
9772
+ * Automatically uses tenant context from AsyncLocalStorage.
9330
9773
  */
9331
9774
  async getProfileByEmail(email) {
9332
- return await this.adapter.userProfiles.findByEmail(this.tenantId, email);
9775
+ return await this.adapter.userProfiles.findByEmail(email);
9333
9776
  }
9334
9777
  /**
9335
9778
  * Check if user has role
@@ -9347,38 +9790,39 @@ var UserProfileService = class {
9347
9790
  };
9348
9791
 
9349
9792
  // src/runtime/services/view.service.ts
9350
- var ViewService = class {
9793
+ var ViewService = class extends TenantAwareService {
9351
9794
  constructor(adapter, nativeViews) {
9795
+ super();
9352
9796
  this.adapter = adapter;
9353
9797
  this.nativeViews = nativeViews;
9354
9798
  }
9355
9799
  /**
9356
- * Get all views for an object (native + custom)
9800
+ * Get all views for an object (native + custom).
9801
+ * Automatically uses tenant context from AsyncLocalStorage.
9357
9802
  *
9358
9803
  * @param objectName - Object name
9359
- * @param tenantId - Tenant ID
9360
9804
  * @returns All views for the object
9361
9805
  */
9362
- async getViewsForObject(objectName, tenantId) {
9806
+ async getViewsForObject(objectName) {
9363
9807
  const nativeViewDefs = this.nativeViews.getByObjectName(objectName);
9364
- const dbViews = await this.adapter.views.findByObjectName(tenantId, objectName);
9808
+ const dbViews = await this.adapter.views.findByObjectName(objectName);
9365
9809
  const customViews = dbViews.filter((v) => !v.system).map(this.convertDBViewToDefinition);
9366
9810
  return [...nativeViewDefs, ...customViews];
9367
9811
  }
9368
9812
  /**
9369
- * Get a specific view by name
9813
+ * Get a specific view by name.
9814
+ * Automatically uses tenant context from AsyncLocalStorage.
9370
9815
  *
9371
9816
  * @param objectName - Object name
9372
9817
  * @param viewName - View name
9373
- * @param tenantId - Tenant ID
9374
9818
  * @returns View definition or null
9375
9819
  */
9376
- async getView(objectName, viewName, tenantId) {
9820
+ async getView(objectName, viewName) {
9377
9821
  const nativeView = this.nativeViews.get(objectName, viewName);
9378
9822
  if (nativeView) {
9379
9823
  return nativeView;
9380
9824
  }
9381
- const dbView = await this.adapter.views.findByName(tenantId, objectName, viewName);
9825
+ const dbView = await this.adapter.views.findByName(objectName, viewName);
9382
9826
  if (dbView) {
9383
9827
  return this.convertDBViewToDefinition(dbView);
9384
9828
  }
@@ -9393,11 +9837,10 @@ var ViewService = class {
9393
9837
  * 3. First available view
9394
9838
  *
9395
9839
  * @param objectName - Object name
9396
- * @param tenantId - Tenant ID
9397
9840
  * @returns Default view or null
9398
9841
  */
9399
- async getDefaultView(objectName, tenantId) {
9400
- const views = await this.getViewsForObject(objectName, tenantId);
9842
+ async getDefaultView(objectName) {
9843
+ const views = await this.getViewsForObject(objectName);
9401
9844
  const customDefault = views.find((v) => v.default && !v.system);
9402
9845
  if (customDefault) return customDefault;
9403
9846
  const nativeDefault = views.find((v) => v.default && v.system);
@@ -9405,15 +9848,15 @@ var ViewService = class {
9405
9848
  return views[0] ?? null;
9406
9849
  }
9407
9850
  /**
9408
- * Create a custom view
9851
+ * Create a custom view.
9852
+ * Automatically uses tenant context from AsyncLocalStorage.
9409
9853
  *
9410
9854
  * @param input - View definition
9411
- * @param tenantId - Tenant ID
9412
9855
  * @returns Created view
9413
9856
  */
9414
- async createView(input, tenantId) {
9857
+ async createView(input) {
9415
9858
  this.validateViewName(input.name);
9416
- const existing = await this.adapter.views.findByName(tenantId, input.objectName, input.name);
9859
+ const existing = await this.adapter.views.findByName(input.objectName, input.name);
9417
9860
  if (existing) {
9418
9861
  throw new SchemaError(
9419
9862
  `View "${input.name}" already exists for object "${input.objectName}"`,
@@ -9429,7 +9872,6 @@ var ViewService = class {
9429
9872
  );
9430
9873
  }
9431
9874
  const dbView = await this.adapter.views.create({
9432
- tenantId,
9433
9875
  objectName: input.objectName,
9434
9876
  name: input.name,
9435
9877
  label: input.label,
@@ -9484,18 +9926,18 @@ var ViewService = class {
9484
9926
  await this.adapter.views.delete(viewId);
9485
9927
  }
9486
9928
  /**
9487
- * Set a view as default for its object
9929
+ * Set a view as default for its object.
9930
+ * Automatically uses tenant context from AsyncLocalStorage.
9488
9931
  *
9489
9932
  * @param viewId - View ID
9490
- * @param tenantId - Tenant ID
9491
9933
  * @returns Updated view
9492
9934
  */
9493
- async setDefaultView(viewId, tenantId) {
9935
+ async setDefaultView(viewId) {
9494
9936
  const dbView = await this.adapter.views.findById(viewId);
9495
9937
  if (!dbView) {
9496
9938
  throw new NotFoundError("View", viewId);
9497
9939
  }
9498
- const currentViews = await this.adapter.views.findByObjectName(tenantId, dbView.objectName);
9940
+ const currentViews = await this.adapter.views.findByObjectName(dbView.objectName);
9499
9941
  for (const v of currentViews) {
9500
9942
  if (v.default && v.id !== viewId && !v.system) {
9501
9943
  await this.adapter.views.update(v.id, { default: false });
@@ -9651,7 +10093,6 @@ async function syncSingleView(adapter, nativeView, result, options) {
9651
10093
  return;
9652
10094
  }
9653
10095
  await adapter.views.upsert({
9654
- tenantId: options.tenantId || "default",
9655
10096
  objectName: nativeView.object,
9656
10097
  name: nativeView.name,
9657
10098
  label: nativeView.label,
@@ -9791,9 +10232,8 @@ async function handleDryRun(adapter, nativeObject, existingObject, result, optio
9791
10232
  result.attributesSynced++;
9792
10233
  }
9793
10234
  }
9794
- async function upsertObject(adapter, nativeObject, options) {
10235
+ async function upsertObject(adapter, nativeObject, _options) {
9795
10236
  return await adapter.objects.upsert({
9796
- tenantId: options.tenantId || "default",
9797
10237
  system: true,
9798
10238
  name: nativeObject.name,
9799
10239
  label: nativeObject.label,
@@ -9904,6 +10344,7 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
9904
10344
  GroupBuilder,
9905
10345
  InvalidPathError,
9906
10346
  MaxDepthExceededError,
10347
+ NOTES,
9907
10348
  NO_VALUE_OPERATORS,
9908
10349
  NoopGeocodingAdapter,
9909
10350
  NoopHookRegistry,
@@ -9915,11 +10356,15 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
9915
10356
  ObjectReferencedError,
9916
10357
  ObjectSchemaService,
9917
10358
  PermissionService,
10359
+ PolicyRegistry,
10360
+ PolicyViolationError,
9918
10361
  ProtectedResourceError,
9919
10362
  ProtectedRoleError,
9920
10363
  QueryBuilder,
9921
10364
  QueryMultipleResultsError,
9922
10365
  QueryNoResultError,
10366
+ RELATION_TARGET_ANY,
10367
+ RESERVED_ATTRIBUTE_NAMES,
9923
10368
  RecordNotFoundError,
9924
10369
  RecordReferencedError,
9925
10370
  RecordService,
@@ -9929,6 +10374,7 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
9929
10374
  RollupScheduler,
9930
10375
  RollupService,
9931
10376
  SHORTCUT_TO_FILTER_OPERATOR,
10377
+ SYSTEM_ATTRIBUTES,
9932
10378
  SYSTEM_FIELD_NAMES,
9933
10379
  SYSTEM_RESOURCES,
9934
10380
  SYSTEM_RESOURCE_LABELS,
@@ -9937,12 +10383,17 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
9937
10383
  SyncError,
9938
10384
  TabBuilder,
9939
10385
  TableTabConfig,
10386
+ TenantAwareRepository,
10387
+ TenantAwareService,
10388
+ TenantContextError,
9940
10389
  UserProfileNotFoundError,
9941
10390
  UserProfileService,
9942
10391
  UserService,
9943
10392
  ValidationError,
9944
10393
  ViewBuilder,
9945
10394
  ViewService,
10395
+ asTenantId,
10396
+ asUserId,
9946
10397
  attributeConfigSchemas,
9947
10398
  buildAuditChanges,
9948
10399
  checkbox,
@@ -9966,17 +10417,19 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
9966
10417
  createQueryBuilder,
9967
10418
  createRatingValidator,
9968
10419
  createRelationValidator,
10420
+ createRichtextValidator,
9969
10421
  createRollupValidator,
9970
10422
  createSelectValidator,
9971
10423
  createSingleRelationValidator,
9972
10424
  createStatusValidator,
10425
+ createTextAreaValidator,
9973
10426
  createTextValidator,
9974
- createTimestampValidator,
9975
10427
  createUserValidator,
9976
10428
  currency,
9977
10429
  currencyConfigSchema,
9978
10430
  date,
9979
10431
  dateConfigSchema,
10432
+ defaultPolicyRegistry,
9980
10433
  enrichValuesWithSelectLabels,
9981
10434
  evaluateFormula,
9982
10435
  evaluateFormulaAttribute,
@@ -10000,13 +10453,18 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
10000
10453
  generateId,
10001
10454
  generatePrefixedId,
10002
10455
  getAttributeConfigSchema,
10456
+ getContext,
10003
10457
  getMissingRequiredAttributes,
10004
10458
  getPathDepth,
10005
10459
  getRelationPath,
10006
10460
  getSyncPreview,
10461
+ getSystemAttributeList,
10007
10462
  getTargetAttributeName,
10463
+ getTenantId,
10464
+ getUserId,
10008
10465
  getViewSyncPreview,
10009
10466
  group,
10467
+ hasContext,
10010
10468
  hasRelationReferences,
10011
10469
  isActivityTab,
10012
10470
  isAdvancedFilterState,
@@ -10019,16 +10477,21 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
10019
10477
  isLabelExpression,
10020
10478
  isNoValueOperator,
10021
10479
  isNotFoundError,
10480
+ isNotesTab,
10022
10481
  isProtectedResourceError,
10023
10482
  isRecordComplete,
10024
10483
  isSchemaError,
10484
+ isSystemAttribute,
10485
+ isSystemAttributeObject,
10025
10486
  isSystemFlow,
10026
10487
  isTableTab,
10488
+ isUniversalRelation,
10027
10489
  isValidationError,
10028
10490
  location,
10029
10491
  locationConfigSchema,
10030
10492
  multiselect,
10031
10493
  multiselectConfigSchema,
10494
+ notesPolicy,
10032
10495
  number,
10033
10496
  numberConfigSchema,
10034
10497
  object,
@@ -10045,8 +10508,11 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
10045
10508
  renderLabelExpression,
10046
10509
  resolveMultiplePaths,
10047
10510
  resolveSingleValue,
10511
+ richtext,
10512
+ richtextConfigSchema,
10048
10513
  rollup,
10049
10514
  rollupConfigSchema,
10515
+ runWithContext,
10050
10516
  safeParseAttributeConfig,
10051
10517
  select,
10052
10518
  selectConfigSchema,
@@ -10059,8 +10525,6 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
10059
10525
  textConfigSchema,
10060
10526
  textarea,
10061
10527
  textareaConfigSchema,
10062
- timestamp,
10063
- timestampConfigSchema,
10064
10528
  toAdvancedFilterState,
10065
10529
  toSimpleFilterState,
10066
10530
  traversePath,
@@ -10077,5 +10541,6 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
10077
10541
  verifyNativeObjectsSync,
10078
10542
  verifyNativeViewsSync,
10079
10543
  view,
10080
- viewRegistry
10544
+ viewRegistry,
10545
+ withTenantContext
10081
10546
  });