@stndrds/schema 0.1.0-alpha.14 → 0.1.0-alpha.16

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 (72) hide show
  1. package/dist/chunk-2ASXJITY.mjs +5360 -0
  2. package/dist/chunk-5AWYVQKR.mjs +5310 -0
  3. package/dist/chunk-65HUQZHK.mjs +5374 -0
  4. package/dist/chunk-73P4F2OU.mjs +5809 -0
  5. package/dist/chunk-BHKKVBPP.mjs +5527 -0
  6. package/dist/chunk-BTVPMGYW.mjs +6327 -0
  7. package/dist/chunk-BUWVNOSK.mjs +5457 -0
  8. package/dist/chunk-C7HZ55GJ.mjs +6882 -0
  9. package/dist/chunk-CCEHW4E7.mjs +6071 -0
  10. package/dist/chunk-CNR3BZLU.mjs +5163 -0
  11. package/dist/chunk-DLMVX6DN.mjs +6074 -0
  12. package/dist/chunk-DUUORFHI.mjs +5379 -0
  13. package/dist/chunk-DWTD7YPJ.mjs +74 -0
  14. package/dist/chunk-FAR5ENGR.mjs +78 -0
  15. package/dist/chunk-ITWOVZOW.mjs +5809 -0
  16. package/dist/chunk-JDUTFH3J.mjs +6940 -0
  17. package/dist/chunk-KJLKY5X7.mjs +6314 -0
  18. package/dist/chunk-KTJXMGBR.mjs +5397 -0
  19. package/dist/chunk-NOVHQ7B2.mjs +6327 -0
  20. package/dist/chunk-PMFQ4ZL4.mjs +6230 -0
  21. package/dist/chunk-QAGN53HE.mjs +6327 -0
  22. package/dist/chunk-QC47SMCZ.mjs +5467 -0
  23. package/dist/chunk-UUOAVXAI.mjs +6327 -0
  24. package/dist/chunk-VYZ3BOSB.mjs +6946 -0
  25. package/dist/chunk-XAX75NSP.mjs +6321 -0
  26. package/dist/chunk-Z6LIJ5PO.mjs +5362 -0
  27. package/dist/chunk-ZG3PT3RL.mjs +6944 -0
  28. package/dist/default-roles-G4TOWUVM.mjs +20 -0
  29. package/dist/default-roles-QEZPRSUL.mjs +20 -0
  30. package/dist/index.d.mts +419 -16
  31. package/dist/index.d.ts +419 -16
  32. package/dist/index.js +2580 -406
  33. package/dist/index.mjs +80 -163
  34. package/dist/runtime-B1_syHVj.d.mts +4787 -0
  35. package/dist/runtime-B1_syHVj.d.ts +4787 -0
  36. package/dist/runtime-B7N-oycb.d.mts +4854 -0
  37. package/dist/runtime-B7N-oycb.d.ts +4854 -0
  38. package/dist/runtime-BDoAIuTe.d.mts +4872 -0
  39. package/dist/runtime-BDoAIuTe.d.ts +4872 -0
  40. package/dist/runtime-BPYJWZfC.d.mts +5130 -0
  41. package/dist/runtime-BPYJWZfC.d.ts +5130 -0
  42. package/dist/runtime-BVRmzg3d.d.mts +4718 -0
  43. package/dist/runtime-BVRmzg3d.d.ts +4718 -0
  44. package/dist/runtime-BrXwIidf.d.mts +4794 -0
  45. package/dist/runtime-BrXwIidf.d.ts +4794 -0
  46. package/dist/runtime-CC_HZhVN.d.mts +5224 -0
  47. package/dist/runtime-CC_HZhVN.d.ts +5224 -0
  48. package/dist/runtime-CEx57sBZ.d.mts +4791 -0
  49. package/dist/runtime-CEx57sBZ.d.ts +4791 -0
  50. package/dist/runtime-COGvuXIq.d.mts +4790 -0
  51. package/dist/runtime-COGvuXIq.d.ts +4790 -0
  52. package/dist/runtime-CSGWjztI.d.mts +5166 -0
  53. package/dist/runtime-CSGWjztI.d.ts +5166 -0
  54. package/dist/runtime-DRc_OhnM.d.mts +5142 -0
  55. package/dist/runtime-DRc_OhnM.d.ts +5142 -0
  56. package/dist/runtime-DUxWyRcq.d.mts +5399 -0
  57. package/dist/runtime-DUxWyRcq.d.ts +5399 -0
  58. package/dist/runtime-D_S3m9jf.d.mts +5142 -0
  59. package/dist/runtime-D_S3m9jf.d.ts +5142 -0
  60. package/dist/runtime-Db3rRxbJ.d.mts +4821 -0
  61. package/dist/runtime-Db3rRxbJ.d.ts +4821 -0
  62. package/dist/runtime-DlCiIIfr.d.mts +5130 -0
  63. package/dist/runtime-DlCiIIfr.d.ts +5130 -0
  64. package/dist/runtime-LO17NDk1.d.mts +5292 -0
  65. package/dist/runtime-LO17NDk1.d.ts +5292 -0
  66. package/dist/runtime-WqYxjhjJ.d.mts +5125 -0
  67. package/dist/runtime-WqYxjhjJ.d.ts +5125 -0
  68. package/dist/runtime.d.mts +1 -1
  69. package/dist/runtime.d.ts +1 -1
  70. package/dist/runtime.js +2178 -370
  71. package/dist/runtime.mjs +9 -1
  72. package/package.json +3 -3
@@ -0,0 +1,4821 @@
1
+ import { ColorId, IconName, CountryIso3, CurrencyCode, MimeType } from '@stndrds/constants';
2
+ import { z } from 'zod';
3
+
4
+ /**
5
+ * UUID string type for all identifiers
6
+ */
7
+ type Uuid = string;
8
+ /**
9
+ * Tenant identifier for multi-tenant isolation
10
+ * Phase 1: Type defined but not yet used in interfaces
11
+ * Phase 2+: Will be added to ObjectRecord and services for SaaS mode
12
+ */
13
+ type TenantId = string;
14
+ /**
15
+ * Generate a unique UUID v4
16
+ * Uses crypto.randomUUID() when available (Node.js 19+, modern browsers)
17
+ * Falls back to a manual implementation for older environments
18
+ *
19
+ * @returns A UUID v4 string
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const id = generateId();
24
+ * // "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
25
+ * ```
26
+ */
27
+ declare function generateId(): Uuid;
28
+ /**
29
+ * Generate a prefixed ID for better debugging and readability
30
+ *
31
+ * @param prefix - Prefix for the ID (e.g., "obj", "attr", "rec")
32
+ * @returns A prefixed UUID string
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const objectId = generatePrefixedId("obj");
37
+ * // "obj_a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
38
+ *
39
+ * const attrId = generatePrefixedId("attr");
40
+ * // "attr_a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
41
+ * ```
42
+ */
43
+ declare function generatePrefixedId(prefix: string): Uuid;
44
+
45
+ type AttributeType = "text" | "textarea" | "number" | "checkbox" | "date" | "phone" | "currency" | "status" | "location" | "timestamp" | "select" | "multiselect" | "file" | "user" | "relation" | "rating";
46
+ /**
47
+ * Status group categorization
48
+ */
49
+ type StatusGroup = "idle" | "in_progress" | "finished";
50
+ /**
51
+ * Unified option type for select-like fields
52
+ */
53
+ interface Option {
54
+ id: string;
55
+ label: string;
56
+ value: string;
57
+ color?: ColorId;
58
+ icon?: IconName;
59
+ description?: string;
60
+ group?: StatusGroup;
61
+ }
62
+ /**
63
+ * Attribute grouping for UI organization
64
+ */
65
+ interface AttributeGroup {
66
+ id: string;
67
+ label: string;
68
+ description?: string;
69
+ attributeIds: string[];
70
+ collapsible?: boolean;
71
+ collapsed?: boolean;
72
+ order?: number;
73
+ }
74
+ interface BaseAttribute<DefaultValueType = unknown> {
75
+ id: Uuid;
76
+ name: string;
77
+ label: string;
78
+ type: AttributeType;
79
+ required: boolean;
80
+ disabled?: boolean;
81
+ placeholder?: string;
82
+ description?: string;
83
+ defaultValue?: DefaultValueType;
84
+ icon?: IconName;
85
+ order?: number;
86
+ hidden?: boolean;
87
+ archived?: boolean;
88
+ deprecated?: boolean;
89
+ system?: boolean;
90
+ metadata?: Record<string, unknown>;
91
+ }
92
+ interface TextAttribute extends BaseAttribute<string> {
93
+ type: "text";
94
+ minLength?: number;
95
+ maxLength?: number;
96
+ pattern?: string;
97
+ }
98
+ type NumberUnit = "integer" | "decimal" | "percentage";
99
+ interface NumberAttribute extends BaseAttribute<number> {
100
+ type: "number";
101
+ min?: number;
102
+ max?: number;
103
+ unit?: NumberUnit;
104
+ decimals?: number;
105
+ }
106
+ interface CheckboxAttribute extends BaseAttribute<boolean> {
107
+ type: "checkbox";
108
+ }
109
+ type DateFormat = "short" | "long" | "full" | "relative";
110
+ type DateValue = string | "today";
111
+ interface DateAttribute extends BaseAttribute<string> {
112
+ type: "date";
113
+ dateFormat?: DateFormat;
114
+ minDate?: DateValue;
115
+ maxDate?: DateValue;
116
+ }
117
+ interface Phone {
118
+ countryCode: CountryIso3;
119
+ phoneNumber: string;
120
+ }
121
+ interface PhoneAttribute extends BaseAttribute<Phone> {
122
+ type: "phone";
123
+ defaultCountryCode?: CountryIso3;
124
+ }
125
+ interface Currency {
126
+ code: CurrencyCode;
127
+ value: number;
128
+ }
129
+ interface CurrencyAttribute extends BaseAttribute<Currency> {
130
+ type: "currency";
131
+ defaultCurrency?: CurrencyCode;
132
+ allowedCurrencies?: CurrencyCode[];
133
+ }
134
+ /**
135
+ * StatusAttribute - For workflow states with semantic grouping (idle/in_progress/finished)
136
+ * Use this for: Task status, Order status, Project phases, Process states
137
+ * Use SelectAttribute for: Categories, Types, simple choices without workflow
138
+ */
139
+ interface StatusAttribute extends BaseAttribute<string> {
140
+ type: "status";
141
+ options: Option[];
142
+ }
143
+ interface Location {
144
+ address?: string;
145
+ address2?: string;
146
+ city?: string;
147
+ state?: string;
148
+ postalCode?: string;
149
+ country?: CountryIso3;
150
+ latitude?: number;
151
+ longitude?: number;
152
+ }
153
+ type LocationGranularity = "full" | "address" | "city" | "state" | "country" | "coordinates";
154
+ interface LocationAttribute extends BaseAttribute<Location> {
155
+ type: "location";
156
+ granularity: LocationGranularity;
157
+ enableAutocomplete?: boolean;
158
+ enableMap?: boolean;
159
+ defaultCountry?: CountryIso3;
160
+ allowedCountries?: CountryIso3[];
161
+ displayFormat?: "single_line" | "multi_line" | "compact";
162
+ }
163
+ interface TimestampAttribute extends BaseAttribute<number> {
164
+ type: "timestamp";
165
+ autoUpdate?: boolean;
166
+ }
167
+ /**
168
+ * SelectAttribute - For simple single-choice selection
169
+ * Use this for: Categories, Document types, Departments, Priorities
170
+ * Options can be grouped (e.g., countries by continent) but no workflow logic
171
+ */
172
+ interface SelectAttribute extends BaseAttribute<string> {
173
+ type: "select";
174
+ options: Option[];
175
+ }
176
+ interface MultiselectAttribute extends BaseAttribute<string[]> {
177
+ type: "multiselect";
178
+ options: Option[];
179
+ }
180
+ type DocumentType = "id_card" | "passport" | "incorporation_certificate" | "driver_license" | "birth_certificate" | "residence_permit" | "bank_statement" | "proof_of_address" | "custom";
181
+ type DocumentFace = "front" | "back" | "single";
182
+ interface DocumentTypeConfig {
183
+ type: DocumentType;
184
+ label: string;
185
+ faces: DocumentFace[];
186
+ attributeMapping?: {
187
+ attributeId: string;
188
+ extractedKey: string;
189
+ face?: DocumentFace;
190
+ required?: boolean;
191
+ }[];
192
+ }
193
+ interface FileVerificationConfig {
194
+ enabled: boolean;
195
+ documentTypes: DocumentTypeConfig[];
196
+ autoExtract?: boolean;
197
+ autoValidate?: boolean;
198
+ }
199
+ interface FileAttribute extends BaseAttribute<string> {
200
+ type: "file";
201
+ maxFiles?: number;
202
+ maxSize?: number;
203
+ allowedTypes?: MimeType[] | readonly MimeType[];
204
+ verification?: FileVerificationConfig;
205
+ }
206
+ interface UserAttribute extends BaseAttribute<string> {
207
+ type: "user";
208
+ allowedRoles?: string[];
209
+ }
210
+ /**
211
+ * Target object for a relation - defines which objects can be linked
212
+ */
213
+ interface RelationTarget {
214
+ /** Object name (e.g., "companies", "contacts") */
215
+ object: string;
216
+ /**
217
+ * Display template for the label using mustache-like syntax
218
+ * @example "{name}" or "{firstName} {lastName} — {email}"
219
+ */
220
+ displayTemplate?: string;
221
+ /**
222
+ * Optional filter to restrict available records
223
+ * @example { status: "active" }
224
+ */
225
+ filter?: Record<string, unknown>;
226
+ }
227
+ /**
228
+ * Base properties shared by both single and multi relation attributes
229
+ *
230
+ * Note: Deletion behavior is always "restrict" - if a record is referenced
231
+ * by other records, it cannot be deleted until those references are removed.
232
+ * This is enforced by RecordService.deleteRecord() which throws
233
+ * RecordReferencedError when attempting to delete a referenced record.
234
+ */
235
+ interface RelationAttributeBase extends Omit<BaseAttribute<unknown>, "defaultValue"> {
236
+ type: "relation";
237
+ /** Target objects that can be linked */
238
+ targets: RelationTarget[];
239
+ }
240
+ /**
241
+ * Single relation attribute (one-to-one or many-to-one)
242
+ * Stores a single record ID or null
243
+ */
244
+ interface SingleRelationAttribute extends RelationAttributeBase {
245
+ cardinality: "one";
246
+ defaultValue?: string | null;
247
+ }
248
+ /**
249
+ * Multi relation attribute (one-to-many or many-to-many)
250
+ * Stores an array of record IDs
251
+ */
252
+ interface MultiRelationAttribute extends RelationAttributeBase {
253
+ cardinality: "many";
254
+ defaultValue?: string[];
255
+ /** Minimum number of relations required */
256
+ minItems?: number;
257
+ /** Maximum number of relations allowed */
258
+ maxItems?: number;
259
+ }
260
+ /**
261
+ * RelationAttribute links to other objects/records
262
+ * Discriminated union by cardinality for type-safe value handling
263
+ *
264
+ * @example Single relation (many-to-one)
265
+ * ```typescript
266
+ * relation({ name: "company", label: "Company" })
267
+ * .to("companies")
268
+ * .required()
269
+ * // → Value: "rec-uuid-123" | null
270
+ * ```
271
+ *
272
+ * @example Multi relation (many-to-many)
273
+ * ```typescript
274
+ * relation({ name: "contacts", label: "Contacts" })
275
+ * .to("contacts", { displayTemplate: "{firstName} {lastName}" })
276
+ * .many()
277
+ * .maxItems(5)
278
+ * // → Value: ["rec-1", "rec-2", ...]
279
+ * ```
280
+ *
281
+ * @example Polymorphic relation (multiple target objects)
282
+ * ```typescript
283
+ * relation({ name: "linked", label: "Linked Items" })
284
+ * .to("companies")
285
+ * .to("contacts")
286
+ * .to("deals")
287
+ * .many()
288
+ * // → Can link to records from any of these objects
289
+ * ```
290
+ */
291
+ type RelationAttribute = SingleRelationAttribute | MultiRelationAttribute;
292
+ interface TextAreaAttribute extends BaseAttribute<string> {
293
+ type: "textarea";
294
+ }
295
+ interface RatingAttribute extends BaseAttribute<number> {
296
+ type: "rating";
297
+ max?: number;
298
+ iconType?: "star" | "heart" | "thumbs" | "number";
299
+ }
300
+ type Attribute = TextAttribute | TextAreaAttribute | NumberAttribute | CheckboxAttribute | DateAttribute | PhoneAttribute | CurrencyAttribute | StatusAttribute | LocationAttribute | TimestampAttribute | SelectAttribute | MultiselectAttribute | FileAttribute | UserAttribute | RelationAttribute | RatingAttribute;
301
+
302
+ /**
303
+ * Type of resource that can be audited
304
+ */
305
+ type AuditResourceType = "record" | "object" | "attribute" | "user" | "role" | "settings";
306
+ /**
307
+ * Actions that can be audited
308
+ */
309
+ type AuditAction = "record.created" | "record.updated" | "record.deleted" | "record.restored" | "object.created" | "object.updated" | "object.deleted" | "attribute.created" | "attribute.updated" | "attribute.deleted" | "user.created" | "user.updated" | "user.deleted" | "user.login" | "user.logout" | "role.created" | "role.updated" | "role.deleted" | "role.assigned" | "role.revoked" | "settings.updated";
310
+ /**
311
+ * Actor type - who performed the action
312
+ */
313
+ type AuditActorType = "user" | "system";
314
+ /**
315
+ * Detail of a change on a field
316
+ */
317
+ interface AuditChange {
318
+ /** Field name that was changed */
319
+ field: string;
320
+ /** Previous value (undefined for new fields) */
321
+ oldValue: unknown;
322
+ /** New value (undefined for deleted fields) */
323
+ newValue: unknown;
324
+ }
325
+ /**
326
+ * Audit log entry - immutable record of an action
327
+ */
328
+ interface AuditLogEntry {
329
+ id: Uuid;
330
+ tenantId: Uuid;
331
+ /** User profile ID of the actor (null for system actions) */
332
+ actorId?: string;
333
+ /** Denormalized email for search (in case user is deleted) */
334
+ actorEmail?: string;
335
+ /** Type of actor */
336
+ actorType: AuditActorType;
337
+ action: AuditAction;
338
+ resourceType: AuditResourceType;
339
+ resourceId: Uuid;
340
+ /** Human-readable label (record name, object label, etc.) */
341
+ resourceLabel?: string;
342
+ /** Object technical name (e.g., "companies", "contacts") */
343
+ objectName?: string;
344
+ /** Object UUID */
345
+ objectId?: Uuid;
346
+ changes?: AuditChange[];
347
+ /** Extra context (IP, user-agent, requestId, etc.) */
348
+ metadata?: Record<string, unknown>;
349
+ createdAt: Date;
350
+ }
351
+ /**
352
+ * Input for creating an audit log entry
353
+ */
354
+ interface CreateAuditLogInput {
355
+ tenantId: string;
356
+ actorId?: string;
357
+ actorEmail?: string;
358
+ actorType?: AuditActorType;
359
+ action: AuditAction;
360
+ resourceType: AuditResourceType;
361
+ resourceId: string;
362
+ resourceLabel?: string;
363
+ objectName?: string;
364
+ objectId?: string;
365
+ changes?: AuditChange[];
366
+ metadata?: Record<string, unknown>;
367
+ }
368
+ /**
369
+ * Options for listing audit logs
370
+ */
371
+ interface AuditListOptions {
372
+ limit?: number;
373
+ offset?: number;
374
+ action?: AuditAction | AuditAction[];
375
+ resourceType?: AuditResourceType | AuditResourceType[];
376
+ objectName?: string;
377
+ actorId?: string;
378
+ from?: Date;
379
+ to?: Date;
380
+ orderBy?: "createdAt";
381
+ orderDirection?: "asc" | "desc";
382
+ }
383
+ /**
384
+ * Options for AuditService
385
+ */
386
+ interface AuditServiceOptions {
387
+ /**
388
+ * If true, audit logging is async (fire-and-forget).
389
+ * Better performance but logs might be lost on crash.
390
+ * @default false
391
+ */
392
+ async?: boolean;
393
+ /**
394
+ * Batch size for async mode.
395
+ * Logs are buffered and flushed when batch is full or after flushIntervalMs.
396
+ * @default 10
397
+ */
398
+ batchSize?: number;
399
+ /**
400
+ * Flush interval in ms for async mode.
401
+ * @default 1000
402
+ */
403
+ flushIntervalMs?: number;
404
+ }
405
+
406
+ /**
407
+ * Timestamps for tracking creation and updates
408
+ */
409
+ interface Timestamps {
410
+ createdAt: Date;
411
+ updatedAt: Date;
412
+ }
413
+ /**
414
+ * Object definition - Represents a database table/entity
415
+ */
416
+ interface ObjectDefinition {
417
+ id?: Uuid;
418
+ name: string;
419
+ label: string;
420
+ pluralLabel?: string;
421
+ description?: string;
422
+ icon?: IconName;
423
+ /**
424
+ * Template expression used to compute the object's display label.
425
+ * Supports variable interpolation and pipes for formatting.
426
+ *
427
+ * @example
428
+ * ```typescript
429
+ * // Simple attribute reference
430
+ * labelExpression: "{{ name }}"
431
+ *
432
+ * // Multiple attributes
433
+ * labelExpression: "{{ firstName }} {{ lastName }}"
434
+ *
435
+ * // With pipes for formatting
436
+ * labelExpression: "{{ code | UPPER }} - {{ name | capitalize }}"
437
+ * ```
438
+ *
439
+ * Available pipes: UPPER, LOWER, capitalize, trim
440
+ */
441
+ labelExpression: string;
442
+ attributes: Attribute[];
443
+ system?: boolean;
444
+ metadata?: Record<string, unknown>;
445
+ }
446
+ /**
447
+ * Links an attribute to an object
448
+ */
449
+ interface ObjectAttribute {
450
+ objectId: Uuid;
451
+ attributeId: Uuid;
452
+ order?: number;
453
+ required?: boolean;
454
+ }
455
+ /**
456
+ * Completion status of a record based on data completeness.
457
+ *
458
+ * - `draft`: Record is missing one or more required attribute values.
459
+ * Can be saved but is considered incomplete.
460
+ * - `complete`: All required attribute values are present and valid.
461
+ * Record is ready for use.
462
+ *
463
+ * This is different from workflow status (e.g., "pending", "approved").
464
+ * Completion status is computed dynamically based on the object schema.
465
+ */
466
+ type CompletionStatus = "draft" | "complete";
467
+ /**
468
+ * Record - Instance of an Object (a row in the database)
469
+ */
470
+ interface ObjectRecord extends Timestamps {
471
+ id: Uuid;
472
+ objectId: Uuid;
473
+ /**
474
+ * Display label computed from the object's labelExpression.
475
+ * Computed dynamically based on record values.
476
+ *
477
+ * @example "John Doe" (from "{{ firstName }} {{ lastName }}")
478
+ */
479
+ label: string;
480
+ /**
481
+ * Completion status of the record.
482
+ * - `draft`: Missing required values, record is incomplete
483
+ * - `complete`: All required values present and valid
484
+ *
485
+ * Computed dynamically based on the object's schema.
486
+ */
487
+ completionStatus: CompletionStatus;
488
+ values: Record<string, unknown>;
489
+ /**
490
+ * Soft delete timestamp.
491
+ * If set, the record is considered deleted but can be restored.
492
+ * Queries exclude soft-deleted records by default.
493
+ */
494
+ deletedAt?: Date | null;
495
+ }
496
+
497
+ /**
498
+ * Storage provider type
499
+ */
500
+ type StorageProvider = "s3" | "gcs" | "azure" | "local" | "cloudflare-r2" | string;
501
+ /**
502
+ * File visibility level
503
+ */
504
+ type FileVisibility = "public" | "private" | "restricted";
505
+ /**
506
+ * File - Represents uploaded file metadata and storage info
507
+ *
508
+ * ARCHITECTURE:
509
+ * - Fixed table (no custom attributes)
510
+ * - Manages file storage, permissions, and metadata
511
+ * - uploadedBy links to user_profiles table
512
+ * - Supports soft delete via deletedAt
513
+ *
514
+ * @example
515
+ * ```typescript
516
+ * const file: File = {
517
+ * id: "file-123",
518
+ * tenantId: "tenant-456",
519
+ * name: "contract-2025.pdf",
520
+ * originalName: "Contract Acme Corp 2025.pdf",
521
+ * mimeType: "application/pdf",
522
+ * size: 2458624,
523
+ * storageProvider: "s3",
524
+ * storagePath: "tenants/456/files/2025/11/contract-2025.pdf",
525
+ * storageBucket: "my-app-files",
526
+ * url: "https://cdn.example.com/files/file-123",
527
+ * uploadedBy: "profile-789",
528
+ * folderPath: "/contracts/2025",
529
+ * tags: ["contract", "legal"],
530
+ * visibility: "restricted",
531
+ * allowedUsers: ["profile-789", "profile-456"],
532
+ * createdAt: new Date(),
533
+ * updatedAt: new Date(),
534
+ * };
535
+ * ```
536
+ */
537
+ interface File extends Timestamps {
538
+ id: Uuid;
539
+ tenantId: Uuid;
540
+ /**
541
+ * File name (sanitized for storage)
542
+ */
543
+ name: string;
544
+ /**
545
+ * Original file name (as uploaded by user)
546
+ */
547
+ originalName: string;
548
+ /**
549
+ * MIME type (e.g., "application/pdf", "image/jpeg")
550
+ */
551
+ mimeType: MimeType | string;
552
+ /**
553
+ * File size in bytes
554
+ */
555
+ size: number;
556
+ /**
557
+ * Storage provider (s3, gcs, azure, local, etc.)
558
+ */
559
+ storageProvider: StorageProvider;
560
+ /**
561
+ * Path in the storage bucket
562
+ */
563
+ storagePath: string;
564
+ /**
565
+ * Storage bucket name (if applicable)
566
+ */
567
+ storageBucket?: string;
568
+ /**
569
+ * Public or signed URL to access the file
570
+ */
571
+ url: string;
572
+ /**
573
+ * User who uploaded the file (FK to user_profiles)
574
+ */
575
+ uploadedBy: Uuid;
576
+ /**
577
+ * Folder path for organization (e.g., "/contracts/2025")
578
+ */
579
+ folderPath?: string;
580
+ /**
581
+ * Tags for search and categorization
582
+ */
583
+ tags?: string[];
584
+ /**
585
+ * File visibility level
586
+ * - public: Anyone with the URL can access
587
+ * - private: Only uploadedBy can access
588
+ * - restricted: Only specific users (allowedUsers) can access
589
+ */
590
+ visibility: FileVisibility;
591
+ /**
592
+ * List of user IDs authorized to access this file (if visibility = "restricted")
593
+ */
594
+ allowedUsers?: Uuid[];
595
+ /**
596
+ * Soft delete timestamp (null = not deleted)
597
+ */
598
+ deletedAt?: Date;
599
+ }
600
+ /**
601
+ * Data required to create a new file record
602
+ */
603
+ interface CreateFile {
604
+ tenantId: Uuid;
605
+ name: string;
606
+ originalName: string;
607
+ mimeType: MimeType | string;
608
+ size: number;
609
+ storageProvider: StorageProvider;
610
+ storagePath: string;
611
+ storageBucket?: string;
612
+ url: string;
613
+ uploadedBy: Uuid;
614
+ folderPath?: string;
615
+ tags?: string[];
616
+ visibility?: FileVisibility;
617
+ allowedUsers?: Uuid[];
618
+ }
619
+ /**
620
+ * Data for updating an existing file record
621
+ */
622
+ interface UpdateFile {
623
+ name?: string;
624
+ folderPath?: string;
625
+ tags?: string[];
626
+ visibility?: FileVisibility;
627
+ allowedUsers?: Uuid[];
628
+ }
629
+
630
+ /** Operators for text-based attributes */
631
+ type TextFilterOperator = "is" | "is_not" | "contains" | "not_contains" | "starts_with" | "ends_with" | "is_empty" | "is_not_empty";
632
+ /** Operators for number-based attributes */
633
+ type NumberFilterOperator = "eq" | "neq" | "lt" | "gt" | "lte" | "gte" | "is_empty" | "is_not_empty";
634
+ /** Operators for checkbox */
635
+ type CheckboxFilterOperator = "is_checked" | "is_not_checked";
636
+ /** Operators for date-based attributes */
637
+ type DateFilterOperator = "is" | "is_not" | "before" | "after" | "on_or_before" | "on_or_after" | "is_within" | "is_empty" | "is_not_empty";
638
+ /** Operators for select-based attributes (supports single or multi-value filtering) */
639
+ type SelectFilterOperator = "is" | "is_not" | "any_of" | "none_of" | "is_empty" | "is_not_empty";
640
+ /** Operators for multiselect-based attributes */
641
+ type MultiselectFilterOperator = "contains" | "not_contains" | "is_empty" | "is_not_empty";
642
+ /** Operators for relation-based attributes (supports single or multi-value filtering) */
643
+ type RelationFilterOperator = "any_of" | "none_of" | "contains" | "not_contains" | "is_empty" | "is_not_empty";
644
+ /** All possible filter operators */
645
+ type FilterOperator = TextFilterOperator | NumberFilterOperator | CheckboxFilterOperator | DateFilterOperator | SelectFilterOperator | MultiselectFilterOperator | RelationFilterOperator;
646
+ /** Relative date value for "is_within" operator */
647
+ interface RelativeDateValue {
648
+ amount: number;
649
+ unit: "days" | "weeks" | "months" | "years";
650
+ direction: "past" | "future";
651
+ }
652
+ /** Currency filter value with amount and optional currency codes
653
+ * - code: undefined or [] = any currency
654
+ * - code: string[] = filter by specific currencies
655
+ */
656
+ interface CurrencyFilterValue {
657
+ value: number | null;
658
+ code?: string[];
659
+ }
660
+ /** Phone filter value with number and optional country code */
661
+ interface PhoneFilterValue {
662
+ phoneNumber: string | null;
663
+ countryCode?: string;
664
+ }
665
+ /** Filter value can be various types depending on the attribute */
666
+ type FilterValue = string | number | boolean | string[] | RelativeDateValue | CurrencyFilterValue | PhoneFilterValue | null;
667
+ /** A single filter rule */
668
+ interface FilterRule {
669
+ /** Attribute name to filter on */
670
+ attribute: string;
671
+ /** Filter operator */
672
+ operator: FilterOperator;
673
+ /** Filter value (null for operators like is_empty) */
674
+ value: FilterValue;
675
+ }
676
+ /** Combinator for filter rules */
677
+ type FilterCombinator = "and" | "or";
678
+ /** Complete filter state (simple mode) */
679
+ interface FilterState {
680
+ /** How to combine rules */
681
+ combinator: FilterCombinator;
682
+ /** List of filter rules */
683
+ rules: FilterRule[];
684
+ }
685
+ /**
686
+ * A filter group containing rules (used in advanced mode)
687
+ * Groups can be nested up to 2 levels deep
688
+ */
689
+ interface FilterGroup {
690
+ /** Unique identifier for this group */
691
+ id: string;
692
+ /** How to combine rules within this group */
693
+ combinator: FilterCombinator;
694
+ /** List of filter rules in this group */
695
+ rules: FilterRule[];
696
+ }
697
+ /**
698
+ * Advanced filter state with nested groups
699
+ * Structure: AdvancedFilterState -> FilterGroup[] -> FilterRule[]
700
+ * Maximum 2 levels of nesting
701
+ */
702
+ interface AdvancedFilterState {
703
+ /** How to combine groups at the top level */
704
+ combinator: FilterCombinator;
705
+ /** List of filter groups */
706
+ groups: FilterGroup[];
707
+ }
708
+ /**
709
+ * Type guard to check if a filter state is advanced
710
+ */
711
+ declare function isAdvancedFilterState(state: FilterState | AdvancedFilterState): state is AdvancedFilterState;
712
+ /**
713
+ * Convert simple filter state to advanced filter state
714
+ */
715
+ declare function toAdvancedFilterState(state: FilterState): AdvancedFilterState;
716
+ /**
717
+ * Convert advanced filter state to simple filter state (flattens groups)
718
+ * Note: This loses group structure - use with caution
719
+ */
720
+ declare function toSimpleFilterState(state: AdvancedFilterState): FilterState;
721
+ /** Sort direction */
722
+ type SortDirection = "asc" | "desc";
723
+ /** A single sort rule */
724
+ interface SortRule {
725
+ /** Attribute name to sort by */
726
+ attribute: string;
727
+ /** Sort direction */
728
+ direction: SortDirection;
729
+ }
730
+ /** Complete query state with search, filters, sorts, and pagination */
731
+ interface QueryState {
732
+ /** Full-text search query */
733
+ search?: string;
734
+ /** Filter configuration (simple mode) */
735
+ filters?: FilterState;
736
+ /** Advanced filter configuration (grouped mode) */
737
+ advancedFilters?: AdvancedFilterState;
738
+ /** Sort configuration (multiple sorts supported) */
739
+ sorts?: SortRule[];
740
+ /** Pagination */
741
+ limit?: number;
742
+ offset?: number;
743
+ }
744
+ /** Mapping of attribute types to their valid operators */
745
+ declare const OPERATORS_BY_TYPE: Record<AttributeType, readonly FilterOperator[]>;
746
+ /** Check if an operator requires a value */
747
+ type NoValueOperator = "is_empty" | "is_not_empty" | "is_checked" | "is_not_checked";
748
+ /** Operators that don't require a value */
749
+ declare const NO_VALUE_OPERATORS: readonly NoValueOperator[];
750
+ /**
751
+ * Check if an operator requires a value
752
+ */
753
+ declare function isNoValueOperator(operator: FilterOperator): operator is NoValueOperator;
754
+
755
+ /**
756
+ * Autocomplete suggestion from geocoding service
757
+ */
758
+ interface GeocodingSuggestion {
759
+ /** Unique identifier from the provider */
760
+ id: string;
761
+ /** Human-readable label for display */
762
+ label: string;
763
+ /** Structured location data */
764
+ location: Location;
765
+ /** Optional match score/confidence (0-1) */
766
+ score?: number;
767
+ /** Optional additional metadata from the provider */
768
+ metadata?: Record<string, unknown>;
769
+ }
770
+ /**
771
+ * Parameters for autocomplete search
772
+ */
773
+ interface GeocodingAutocompleteParams {
774
+ /** Search query */
775
+ query: string;
776
+ /** Restrict results to specific countries */
777
+ countries?: CountryIso3[];
778
+ /** Preferred language for results (ISO 639-1) */
779
+ language?: string;
780
+ /** Maximum number of results */
781
+ limit?: number;
782
+ /** Bias results toward a specific location */
783
+ biasLocation?: {
784
+ latitude: number;
785
+ longitude: number;
786
+ };
787
+ /** Session token for billing optimization (some providers) */
788
+ sessionToken?: string;
789
+ /**
790
+ * Level of address detail expected
791
+ * Adapters can use this to filter/optimize results
792
+ * - "full": Full address with street, city, postal code, etc.
793
+ * - "address": Street address without postal code
794
+ * - "city": City level only
795
+ * - "state": State/region level only
796
+ * - "country": Country level only
797
+ * - "coordinates": Only coordinates (lat/lng)
798
+ */
799
+ granularity?: LocationGranularity;
800
+ }
801
+ /**
802
+ * Parameters for reverse geocoding
803
+ */
804
+ interface ReverseGeocodingParams {
805
+ /** Latitude */
806
+ latitude: number;
807
+ /** Longitude */
808
+ longitude: number;
809
+ /** Preferred language for results */
810
+ language?: string;
811
+ }
812
+ /**
813
+ * Parameters for geocoding a structured address
814
+ */
815
+ interface GeocodingParams {
816
+ /** Structured address components */
817
+ address: Partial<Location>;
818
+ /** Preferred language for results */
819
+ language?: string;
820
+ }
821
+ /**
822
+ * Abstract geocoding adapter interface
823
+ * Implement this interface for different geocoding providers
824
+ *
825
+ * @example Google Maps implementation
826
+ * ```typescript
827
+ * class GoogleMapsGeocodingAdapter implements GeocodingAdapter {
828
+ * async autocomplete(params) {
829
+ * const response = await fetch(
830
+ * `https://maps.googleapis.com/maps/api/place/autocomplete/json?input=${params.query}&key=${this.apiKey}`
831
+ * );
832
+ * // Transform response to GeocodingSuggestion[]
833
+ * }
834
+ * }
835
+ * ```
836
+ *
837
+ * @example Mapbox implementation
838
+ * ```typescript
839
+ * class MapboxGeocodingAdapter implements GeocodingAdapter {
840
+ * async autocomplete(params) {
841
+ * const response = await fetch(
842
+ * `https://api.mapbox.com/geocoding/v5/mapbox.places/${params.query}.json?access_token=${this.token}`
843
+ * );
844
+ * // Transform response to GeocodingSuggestion[]
845
+ * }
846
+ * }
847
+ * ```
848
+ */
849
+ interface GeocodingAdapter {
850
+ /**
851
+ * Autocomplete address search
852
+ * Returns suggestions as the user types
853
+ */
854
+ autocomplete(params: GeocodingAutocompleteParams): Promise<GeocodingSuggestion[]>;
855
+ /**
856
+ * Reverse geocode coordinates to an address
857
+ * Optional - some providers may not support this
858
+ */
859
+ reverse?(params: ReverseGeocodingParams): Promise<GeocodingSuggestion | null>;
860
+ /**
861
+ * Geocode a structured address to coordinates
862
+ * Optional - some providers may not support this
863
+ */
864
+ geocode?(params: GeocodingParams): Promise<GeocodingSuggestion | null>;
865
+ }
866
+ /**
867
+ * No-op geocoding adapter
868
+ * Returns empty results when no geocoding provider is configured
869
+ */
870
+ declare class NoopGeocodingAdapter implements GeocodingAdapter {
871
+ autocomplete(): Promise<GeocodingSuggestion[]>;
872
+ reverse(): Promise<null>;
873
+ geocode(): Promise<null>;
874
+ }
875
+
876
+ /**
877
+ * Scope of a permission rule.
878
+ * - `object`: Controls access to an entire object type (business data)
879
+ * - `system`: Controls access to platform resources (users, schema, roles)
880
+ * - `attribute`: Controls access to specific fields (Phase 2)
881
+ * - `record`: Controls access to specific records via filters (Phase 3)
882
+ */
883
+ type PermissionScope = "object" | "system" | "attribute" | "record";
884
+ /**
885
+ * Actions that can be performed on an object.
886
+ */
887
+ type ObjectAction = "read" | "create" | "update" | "delete";
888
+ /**
889
+ * System resources that can be managed.
890
+ * - `users`: User profiles and invitations
891
+ * - `schema`: Object definitions and attributes
892
+ * - `roles`: Roles and permissions
893
+ * - `settings`: Tenant settings and configuration
894
+ */
895
+ type SystemResource = "users" | "schema" | "roles" | "settings" | "audit";
896
+ /**
897
+ * Actions that can be performed on system resources.
898
+ * Same as ObjectAction for consistency.
899
+ */
900
+ type SystemAction = "read" | "create" | "update" | "delete";
901
+ /**
902
+ * Role definition - Groups permissions together.
903
+ *
904
+ * Roles are tenant-scoped and can be system-defined (immutable) or custom.
905
+ *
906
+ * @example
907
+ * ```typescript
908
+ * const adminRole: Role = {
909
+ * id: "role-123",
910
+ * tenantId: "tenant-456",
911
+ * name: "admin",
912
+ * label: "Administrator",
913
+ * description: "Full access to all objects",
914
+ * system: true,
915
+ * createdAt: new Date(),
916
+ * updatedAt: new Date(),
917
+ * };
918
+ * ```
919
+ */
920
+ interface Role extends Timestamps {
921
+ id: Uuid;
922
+ tenantId: Uuid;
923
+ /**
924
+ * Technical name (unique per tenant, used in code).
925
+ * Examples: "admin", "member", "sales_manager"
926
+ */
927
+ name: string;
928
+ /**
929
+ * Display name shown in UI.
930
+ */
931
+ label: string;
932
+ /**
933
+ * Optional description of the role's purpose.
934
+ */
935
+ description?: string;
936
+ /**
937
+ * If true, this role cannot be modified or deleted.
938
+ * Used for built-in roles like "admin".
939
+ * Follows the same naming convention as ObjectDefinition.system.
940
+ */
941
+ system: boolean;
942
+ }
943
+ /**
944
+ * Permission definition - Grants specific actions on a target.
945
+ *
946
+ * A permission belongs to a role and defines what actions are allowed
947
+ * on a specific target (object, attribute, or record filter).
948
+ *
949
+ * @example
950
+ * ```typescript
951
+ * // Allow read and update on companies object
952
+ * const permission: Permission = {
953
+ * id: "perm-123",
954
+ * roleId: "role-456",
955
+ * scope: "object",
956
+ * target: "companies",
957
+ * actions: ["read", "update"],
958
+ * createdAt: new Date(),
959
+ * };
960
+ *
961
+ * // Wildcard permission for all objects
962
+ * const wildcardPerm: Permission = {
963
+ * id: "perm-789",
964
+ * roleId: "role-456",
965
+ * scope: "object",
966
+ * target: "*",
967
+ * actions: ["read", "create", "update", "delete"],
968
+ * createdAt: new Date(),
969
+ * };
970
+ * ```
971
+ */
972
+ interface Permission {
973
+ id: Uuid;
974
+ roleId: Uuid;
975
+ /**
976
+ * Scope of this permission.
977
+ */
978
+ scope: PermissionScope;
979
+ /**
980
+ * Target of the permission.
981
+ * - For `object` scope: object name (e.g., "companies") or "*" for all
982
+ * - For `system` scope: system resource (e.g., "users", "schema") or "*" for all
983
+ * - For `attribute` scope: "objectName.attributeName"
984
+ * - For `record` scope: object name (filter is in `filter` field)
985
+ */
986
+ target: string;
987
+ /**
988
+ * Actions allowed on the target.
989
+ * For object/system scopes: "read", "create", "update", "delete"
990
+ */
991
+ actions: ObjectAction[] | SystemAction[];
992
+ /**
993
+ * JSON filter for record-level permissions (Phase 3).
994
+ * Supports template variables like `{{ currentUser.id }}`.
995
+ */
996
+ filter?: Record<string, unknown>;
997
+ createdAt: Date;
998
+ }
999
+ /**
1000
+ * Links a user profile to a role within a tenant.
1001
+ *
1002
+ * A user can have multiple roles, and their permissions are additive (union).
1003
+ *
1004
+ * Note: `userProfileId` references UserProfile.id from the user_profiles table,
1005
+ * NOT the auth provider ID (authId). This keeps permissions tied to the
1006
+ * application's user management, not the authentication layer.
1007
+ */
1008
+ interface UserRoleAssignment {
1009
+ id: Uuid;
1010
+ /**
1011
+ * User profile ID (UserProfile.id from user_profiles table).
1012
+ * This links to the application's user management system,
1013
+ * not the auth provider's user ID.
1014
+ */
1015
+ userProfileId: Uuid;
1016
+ roleId: Uuid;
1017
+ tenantId: Uuid;
1018
+ /**
1019
+ * When the role was assigned.
1020
+ */
1021
+ assignedAt: Date;
1022
+ /**
1023
+ * User profile ID of who assigned this role (for audit trail).
1024
+ */
1025
+ assignedBy?: Uuid;
1026
+ }
1027
+ /**
1028
+ * Computed permissions for a user.
1029
+ *
1030
+ * This is the merged result of all roles assigned to a user.
1031
+ * Returned by the API for permission checks.
1032
+ */
1033
+ interface EffectivePermissions {
1034
+ /**
1035
+ * If true, the user has full admin access and bypasses all permission checks.
1036
+ */
1037
+ isAdmin: boolean;
1038
+ /**
1039
+ * Object-level permissions.
1040
+ * Key is the object name, value is array of allowed actions.
1041
+ * Special key "*" means permission applies to all objects.
1042
+ */
1043
+ objectPermissions: Record<string, ObjectAction[]>;
1044
+ /**
1045
+ * System-level permissions.
1046
+ * Key is the system resource (users, schema, roles, settings), value is array of allowed actions.
1047
+ * Special key "*" means permission applies to all system resources.
1048
+ */
1049
+ systemPermissions: Record<string, SystemAction[]>;
1050
+ }
1051
+ /**
1052
+ * Permissions for a specific object.
1053
+ * Convenience type for frontend use.
1054
+ */
1055
+ interface ObjectPermissions {
1056
+ canRead: boolean;
1057
+ canCreate: boolean;
1058
+ canUpdate: boolean;
1059
+ canDelete: boolean;
1060
+ }
1061
+ /**
1062
+ * Permissions for a specific system resource.
1063
+ * Convenience type for frontend use.
1064
+ */
1065
+ interface SystemPermissions {
1066
+ canRead: boolean;
1067
+ canCreate: boolean;
1068
+ canUpdate: boolean;
1069
+ canDelete: boolean;
1070
+ }
1071
+ /**
1072
+ * Input for creating a new role.
1073
+ */
1074
+ interface CreateRoleInput {
1075
+ tenantId: Uuid;
1076
+ name: string;
1077
+ label: string;
1078
+ description?: string;
1079
+ }
1080
+ /**
1081
+ * Input for updating an existing role.
1082
+ */
1083
+ interface UpdateRoleInput {
1084
+ label?: string;
1085
+ description?: string;
1086
+ }
1087
+ /**
1088
+ * Input for creating a permission.
1089
+ */
1090
+ interface CreatePermissionInput {
1091
+ scope: PermissionScope;
1092
+ target: string;
1093
+ /**
1094
+ * Actions allowed on the target.
1095
+ * For object/system scopes: "read", "create", "update", "delete"
1096
+ */
1097
+ actions: ObjectAction[] | SystemAction[];
1098
+ filter?: Record<string, unknown>;
1099
+ }
1100
+ /**
1101
+ * Input for assigning a role to a user.
1102
+ */
1103
+ interface AssignRoleInput {
1104
+ /**
1105
+ * User profile ID (UserProfile.id).
1106
+ */
1107
+ userProfileId: Uuid;
1108
+ roleId: Uuid;
1109
+ tenantId: Uuid;
1110
+ assignedBy?: Uuid;
1111
+ }
1112
+
1113
+ /**
1114
+ * Simple user role for basic authorization.
1115
+ *
1116
+ * COEXISTENCE WITH RBAC:
1117
+ * This simple role system coexists with the granular RBAC system (see `permissions.ts`).
1118
+ *
1119
+ * - **UserProfile.role**: Simple string-based role for quick access checks.
1120
+ * Used for high-level authorization (e.g., "is this user an admin?").
1121
+ * Values: "admin" | "member" | "guest" | custom strings.
1122
+ *
1123
+ * - **RBAC System (Role, Permission, UserRoleAssignment)**: Fine-grained permissions
1124
+ * for object-level access control (read/create/update/delete per object type).
1125
+ * Used for detailed authorization (e.g., "can this user update companies?").
1126
+ *
1127
+ * MIGRATION STRATEGY:
1128
+ * 1. For simple apps: Use only `UserProfile.role` - sufficient for basic admin/member checks.
1129
+ * 2. For complex apps: Use RBAC for granular permissions, `UserProfile.role` becomes informational.
1130
+ * 3. The `PermissionService.getEffectivePermissions()` automatically grants admin bypass
1131
+ * when a user has a role named "admin" in the RBAC system.
1132
+ *
1133
+ * @see permissions.ts for the full RBAC implementation
1134
+ */
1135
+ type UserRole = "admin" | "member" | "guest" | string;
1136
+ /**
1137
+ * User status for account management
1138
+ */
1139
+ type UserStatus = "active" | "pending" | "inactive" | "suspended";
1140
+ /**
1141
+ * User Profile - Represents application user data (authorization)
1142
+ *
1143
+ * ARCHITECTURE:
1144
+ * - authId links to external auth provider (Supabase, Clerk, Auth0, etc.)
1145
+ * - email is denormalized from auth provider for performance
1146
+ * - Auth provider handles authentication (passwords, sessions, OAuth)
1147
+ * - This type handles authorization (roles, permissions, tenant membership)
1148
+ *
1149
+ * RELATIONSHIP WITH RBAC:
1150
+ * - `UserProfile.id` is used as `userProfileId` in `UserRoleAssignment`
1151
+ * - The RBAC system assigns multiple roles to a UserProfile
1152
+ * - For simple use cases, `UserProfile.role` is sufficient
1153
+ * - For granular permissions, use the RBAC system via `PermissionService`
1154
+ *
1155
+ * @example
1156
+ * ```typescript
1157
+ * const profile: UserProfile = {
1158
+ * id: "profile-123",
1159
+ * tenantId: "tenant-456",
1160
+ * authId: "supabase-auth-uuid-789",
1161
+ * email: "john@example.com",
1162
+ * firstName: "John",
1163
+ * lastName: "Doe",
1164
+ * role: "admin",
1165
+ * status: "active",
1166
+ * createdAt: new Date(),
1167
+ * updatedAt: new Date(),
1168
+ * };
1169
+ * ```
1170
+ */
1171
+ interface UserProfile extends Timestamps {
1172
+ id: Uuid;
1173
+ tenantId: Uuid;
1174
+ /**
1175
+ * Link to external auth provider (Supabase auth.users.id, Clerk user ID, etc.)
1176
+ * This is the bridge between authentication (provider) and authorization (your app)
1177
+ */
1178
+ authId: string;
1179
+ /**
1180
+ * Email address (denormalized from auth provider)
1181
+ * Allows efficient querying and filtering without hitting auth provider API
1182
+ */
1183
+ email: string;
1184
+ /**
1185
+ * User's first name
1186
+ */
1187
+ firstName?: string;
1188
+ /**
1189
+ * User's last name
1190
+ */
1191
+ lastName?: string;
1192
+ /**
1193
+ * Avatar/profile picture URL
1194
+ */
1195
+ avatarUrl?: string;
1196
+ /**
1197
+ * Simple role for basic authorization checks.
1198
+ *
1199
+ * Common values: "admin", "member", "guest"
1200
+ *
1201
+ * NOTE: For granular object-level permissions, use the RBAC system
1202
+ * (Role, Permission, UserRoleAssignment) via PermissionService.
1203
+ * This field is kept for backward compatibility and simple use cases.
1204
+ *
1205
+ * @see UserRoleAssignment for the RBAC relationship
1206
+ */
1207
+ role: UserRole;
1208
+ /**
1209
+ * Account status
1210
+ * - active: Normal user, full access
1211
+ * - pending: Awaiting activation/approval
1212
+ * - inactive: Deactivated account
1213
+ * - suspended: Temporarily blocked
1214
+ */
1215
+ status: UserStatus;
1216
+ /**
1217
+ * Last login timestamp (updated on each successful auth)
1218
+ */
1219
+ lastLoginAt?: Date;
1220
+ }
1221
+ /**
1222
+ * Data required to create a new user profile
1223
+ */
1224
+ interface CreateUserProfile {
1225
+ tenantId: Uuid;
1226
+ authId: string;
1227
+ email: string;
1228
+ firstName?: string;
1229
+ lastName?: string;
1230
+ avatarUrl?: string;
1231
+ role?: UserRole;
1232
+ status?: UserStatus;
1233
+ }
1234
+ /**
1235
+ * Data for updating an existing user profile
1236
+ */
1237
+ interface UpdateUserProfile {
1238
+ firstName?: string;
1239
+ lastName?: string;
1240
+ avatarUrl?: string;
1241
+ role?: UserRole;
1242
+ status?: UserStatus;
1243
+ lastLoginAt?: Date;
1244
+ }
1245
+
1246
+ /**
1247
+ * Inline attribute group configuration
1248
+ * Groups multiple attributes into a single composite field with dropdown editing
1249
+ */
1250
+ interface AttributeGroupField {
1251
+ /** Unique identifier for the group */
1252
+ id: string;
1253
+ /** Display label for the composite field */
1254
+ label: string;
1255
+ /** Description shown in the dropdown */
1256
+ description?: string;
1257
+ /** Attribute names to include in this group */
1258
+ attributes: string[];
1259
+ /**
1260
+ * Template for the display value
1261
+ * Uses {attributeName} syntax for interpolation
1262
+ * @example "{billing_street}, {billing_city} {billing_postal_code}"
1263
+ */
1264
+ displayTemplate?: string;
1265
+ }
1266
+ /**
1267
+ * Field definition within a form group
1268
+ * Can be either a single attribute or an inline attribute group
1269
+ */
1270
+ interface Field {
1271
+ /** Attribute name to display (for single attribute fields) */
1272
+ attribute?: string;
1273
+ /** Inline attribute group (groups multiple attributes into one composite field) */
1274
+ attributeGroup?: AttributeGroupField;
1275
+ /** Grid span (1-12 columns) */
1276
+ span?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
1277
+ /** Override label for this view (only for single attribute fields) */
1278
+ label?: string;
1279
+ /** Force read-only display */
1280
+ readOnly?: boolean;
1281
+ }
1282
+ /**
1283
+ * Group of fields for organizing forms
1284
+ */
1285
+ interface Group {
1286
+ id: string;
1287
+ label: string;
1288
+ description?: string;
1289
+ fields: Field[];
1290
+ collapsible?: boolean;
1291
+ collapsed?: boolean;
1292
+ order?: number;
1293
+ }
1294
+ type TabType = "form" | "table" | "custom";
1295
+ /**
1296
+ * Base properties shared by all tab types
1297
+ */
1298
+ interface BaseTab {
1299
+ id: string;
1300
+ name: string;
1301
+ label: string;
1302
+ icon?: IconName;
1303
+ order?: number;
1304
+ /** If true, tab is defined by developer (protected) */
1305
+ system?: boolean;
1306
+ }
1307
+ /**
1308
+ * Form tab - displays attributes organized in groups
1309
+ */
1310
+ interface FormTab extends BaseTab {
1311
+ type: "form";
1312
+ groups: Group[];
1313
+ }
1314
+ /**
1315
+ * Table tab - displays records from a relation attribute
1316
+ */
1317
+ interface TableTab extends BaseTab {
1318
+ type: "table";
1319
+ /** Name of the relation attribute */
1320
+ relation: string;
1321
+ /** Columns to display (attribute names from target object) */
1322
+ columns: string[];
1323
+ /** Allow creating new records */
1324
+ allowCreate?: boolean;
1325
+ /** Allow inline editing */
1326
+ allowEdit?: boolean;
1327
+ /** Allow deleting records */
1328
+ allowDelete?: boolean;
1329
+ /** Default filters */
1330
+ filters?: Record<string, unknown>;
1331
+ /** Default sort */
1332
+ sort?: {
1333
+ field: string;
1334
+ order: "asc" | "desc";
1335
+ };
1336
+ }
1337
+ /**
1338
+ * Custom tab - renders a developer-defined component
1339
+ */
1340
+ interface CustomTab extends BaseTab {
1341
+ type: "custom";
1342
+ /** Component identifier to render */
1343
+ component: string;
1344
+ /** Props to pass to the component */
1345
+ props?: Record<string, unknown>;
1346
+ }
1347
+ /**
1348
+ * Union of all tab types
1349
+ */
1350
+ type Tab = FormTab | TableTab | CustomTab;
1351
+ /**
1352
+ * View definition - organizes object attributes into tabs/pages
1353
+ *
1354
+ * @example
1355
+ * ```typescript
1356
+ * const companyView: ViewDefinition = {
1357
+ * name: "detail",
1358
+ * label: "Company Detail",
1359
+ * object: "companies",
1360
+ * tabs: [
1361
+ * { type: "form", name: "general", label: "Info", groups: [...] },
1362
+ * { type: "table", name: "contacts", label: "Contacts", relation: "contacts", columns: [...] }
1363
+ * ],
1364
+ * default: true,
1365
+ * system: true
1366
+ * };
1367
+ * ```
1368
+ */
1369
+ interface ViewDefinition {
1370
+ id?: Uuid;
1371
+ /** Technical name (kebab-case) */
1372
+ name: string;
1373
+ /** Display label */
1374
+ label: string;
1375
+ /** Description */
1376
+ description?: string;
1377
+ /** Icon */
1378
+ icon?: IconName;
1379
+ /** Object this view belongs to (object name) */
1380
+ object: string;
1381
+ /** Tabs in this view */
1382
+ tabs: Tab[];
1383
+ /** Default view for this object */
1384
+ default?: boolean;
1385
+ /** System view (defined by developer, protected) */
1386
+ system?: boolean;
1387
+ /** Extensible metadata */
1388
+ metadata?: Record<string, unknown>;
1389
+ }
1390
+ /**
1391
+ * Check if a tab is a form tab
1392
+ */
1393
+ declare function isFormTab(tab: Tab): tab is FormTab;
1394
+ /**
1395
+ * Check if a tab is a table tab
1396
+ */
1397
+ declare function isTableTab(tab: Tab): tab is TableTab;
1398
+ /**
1399
+ * Check if a tab is a custom tab
1400
+ */
1401
+ declare function isCustomTab(tab: Tab): tab is CustomTab;
1402
+
1403
+ /**
1404
+ * Registry for native objects defined in code
1405
+ * Native objects are system objects that cannot be deleted/modified by clients
1406
+ * They are defined by developers and synced to the database at startup
1407
+ */
1408
+ declare class NativeObjectRegistryClass {
1409
+ private objects;
1410
+ /**
1411
+ * Register one or more native objects
1412
+ * Auto-builds if ObjectBuilder is passed instead of ObjectDefinition
1413
+ * @param objects - Single object/builder or array of objects/builders to register
1414
+ * @returns this (for chaining)
1415
+ * @throws Error if object is invalid or already registered
1416
+ *
1417
+ * @example
1418
+ * ```typescript
1419
+ * // No .build() needed - registry auto-builds!
1420
+ * const PRODUCT = object({ id: "obj-product", name: "products", label: "Product" })
1421
+ * .system()
1422
+ * .attribute(text({ id: "attr-name", name: "name", label: "Name" }));
1423
+ *
1424
+ * registry.register(PRODUCT); // ← Auto-builds here
1425
+ *
1426
+ * // Can still explicitly .build() if you want
1427
+ * registry.register(PRODUCT.build());
1428
+ *
1429
+ * // Register multiple
1430
+ * registry.register([PRODUCT, ORDER, CUSTOMER]);
1431
+ * ```
1432
+ */
1433
+ register(objects: ObjectDefinition | ObjectDefinition[] | {
1434
+ build: () => ObjectDefinition;
1435
+ } | Array<{
1436
+ build: () => ObjectDefinition;
1437
+ }>): this;
1438
+ /**
1439
+ * Validate and register a single object
1440
+ * @internal
1441
+ */
1442
+ private validateAndRegister;
1443
+ /**
1444
+ * Get a native object by its name
1445
+ * @param name - The object name
1446
+ * @returns The object definition or undefined if not found
1447
+ *
1448
+ * @example
1449
+ * ```typescript
1450
+ * const product = registry.getByName("products");
1451
+ * if (product) {
1452
+ * console.log(product.label); // "Product"
1453
+ * }
1454
+ * ```
1455
+ */
1456
+ getByName(name: string): ObjectDefinition | undefined;
1457
+ /**
1458
+ * Get a native object or throw if not found
1459
+ * @param name - The object name
1460
+ * @returns The object definition
1461
+ * @throws Error if not found
1462
+ */
1463
+ getByNameOrThrow(name: string): ObjectDefinition;
1464
+ /**
1465
+ * Get all registered native objects
1466
+ * @returns Array of all registered object definitions
1467
+ */
1468
+ getAll(): ObjectDefinition[];
1469
+ /**
1470
+ * Get all registered native object names
1471
+ * @returns Array of object names
1472
+ */
1473
+ listNames(): string[];
1474
+ /**
1475
+ * Check if a native object is registered
1476
+ * @param name - The object name
1477
+ * @returns true if registered, false otherwise
1478
+ */
1479
+ has(name: string): boolean;
1480
+ /**
1481
+ * Get the number of registered objects
1482
+ */
1483
+ get size(): number;
1484
+ /**
1485
+ * Clear all registered objects (useful for testing)
1486
+ * @internal
1487
+ */
1488
+ clear(): void;
1489
+ /**
1490
+ * Get registry summary for debugging
1491
+ */
1492
+ summary(): string;
1493
+ /**
1494
+ * Log registry summary to console
1495
+ */
1496
+ debug(): void;
1497
+ }
1498
+ /**
1499
+ * Singleton instance of the registry
1500
+ * Use this to register and retrieve native objects
1501
+ *
1502
+ * @example
1503
+ * ```typescript
1504
+ * import { registry } from "@stndrds/schema";
1505
+ *
1506
+ * // Register objects
1507
+ * registry.register([PRODUCT, ORDER, CUSTOMER]);
1508
+ *
1509
+ * // Get object by name
1510
+ * const product = registry.getByName("products");
1511
+ *
1512
+ * // List all
1513
+ * console.log(registry.listNames()); // ["products", "orders", ...]
1514
+ *
1515
+ * // Debug
1516
+ * registry.debug();
1517
+ * ```
1518
+ */
1519
+ declare const registry: NativeObjectRegistryClass;
1520
+
1521
+ /**
1522
+ * Registry for native view definitions
1523
+ *
1524
+ * Views registered here are considered "system" views, defined by the developer.
1525
+ * They are protected from modification by end-users and synced to the database
1526
+ * at application startup.
1527
+ *
1528
+ * @example
1529
+ * ```typescript
1530
+ * import { view, group, viewRegistry } from "@stndrds/schema";
1531
+ *
1532
+ * const COMPANY_DETAIL = view("detail", "Detail")
1533
+ * .for("companies")
1534
+ * .system()
1535
+ * .tab("general", "Info")
1536
+ * .form(group("main", "Main").fields("name", "status"))
1537
+ * .build();
1538
+ *
1539
+ * viewRegistry.register(COMPANY_DETAIL);
1540
+ * ```
1541
+ */
1542
+ declare class ViewRegistry {
1543
+ private views;
1544
+ private byObject;
1545
+ /**
1546
+ * Register a native view
1547
+ * @param viewOrViews - Single view or array of views
1548
+ * @throws Error if view is not marked as system
1549
+ * @throws Error if view with same name already exists for the object
1550
+ */
1551
+ register(viewOrViews: ViewDefinition | ViewDefinition[]): this;
1552
+ private registerSingle;
1553
+ /**
1554
+ * Get all views for an object
1555
+ */
1556
+ getByObjectName(objectName: string): ViewDefinition[];
1557
+ /**
1558
+ * Get a specific view by object and view name
1559
+ */
1560
+ get(objectName: string, viewName: string): ViewDefinition | undefined;
1561
+ /**
1562
+ * Get a view or throw if not found
1563
+ */
1564
+ getOrThrow(objectName: string, viewName: string): ViewDefinition;
1565
+ /**
1566
+ * Get all registered views
1567
+ */
1568
+ getAll(): ViewDefinition[];
1569
+ /**
1570
+ * Check if a view exists
1571
+ */
1572
+ has(objectName: string, viewName: string): boolean;
1573
+ /**
1574
+ * Check if any views exist for an object
1575
+ */
1576
+ hasObject(objectName: string): boolean;
1577
+ /**
1578
+ * Get number of registered views
1579
+ */
1580
+ get size(): number;
1581
+ /**
1582
+ * List all object names that have views
1583
+ */
1584
+ listObjectNames(): string[];
1585
+ /**
1586
+ * Get default view for an object (if any)
1587
+ */
1588
+ getDefault(objectName: string): ViewDefinition | undefined;
1589
+ /**
1590
+ * Clear all registered views (for testing)
1591
+ */
1592
+ clear(): void;
1593
+ /**
1594
+ * Generate summary string for debugging
1595
+ */
1596
+ summary(): string;
1597
+ /**
1598
+ * Log summary to console
1599
+ */
1600
+ debug(): void;
1601
+ private makeKey;
1602
+ }
1603
+ /**
1604
+ * Global registry for native views
1605
+ */
1606
+ declare const viewRegistry: ViewRegistry;
1607
+
1608
+ /**
1609
+ * Text attribute config schema
1610
+ */
1611
+ declare const textConfigSchema: z.ZodObject<{
1612
+ disabled: z.ZodOptional<z.ZodBoolean>;
1613
+ placeholder: z.ZodOptional<z.ZodString>;
1614
+ description: z.ZodOptional<z.ZodString>;
1615
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1616
+ icon: z.ZodOptional<z.ZodString>;
1617
+ order: z.ZodOptional<z.ZodNumber>;
1618
+ hidden: z.ZodOptional<z.ZodBoolean>;
1619
+ archived: z.ZodOptional<z.ZodBoolean>;
1620
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1621
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1622
+ minLength: z.ZodOptional<z.ZodNumber>;
1623
+ maxLength: z.ZodOptional<z.ZodNumber>;
1624
+ pattern: z.ZodOptional<z.ZodString>;
1625
+ }, z.core.$strip>;
1626
+ /**
1627
+ * Textarea attribute config schema
1628
+ */
1629
+ declare const textareaConfigSchema: z.ZodObject<{
1630
+ disabled: z.ZodOptional<z.ZodBoolean>;
1631
+ placeholder: z.ZodOptional<z.ZodString>;
1632
+ description: z.ZodOptional<z.ZodString>;
1633
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1634
+ icon: z.ZodOptional<z.ZodString>;
1635
+ order: z.ZodOptional<z.ZodNumber>;
1636
+ hidden: z.ZodOptional<z.ZodBoolean>;
1637
+ archived: z.ZodOptional<z.ZodBoolean>;
1638
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1639
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1640
+ }, z.core.$strip>;
1641
+ /**
1642
+ * Number attribute config schema
1643
+ */
1644
+ declare const numberConfigSchema: z.ZodObject<{
1645
+ disabled: z.ZodOptional<z.ZodBoolean>;
1646
+ placeholder: z.ZodOptional<z.ZodString>;
1647
+ description: z.ZodOptional<z.ZodString>;
1648
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1649
+ icon: z.ZodOptional<z.ZodString>;
1650
+ order: z.ZodOptional<z.ZodNumber>;
1651
+ hidden: z.ZodOptional<z.ZodBoolean>;
1652
+ archived: z.ZodOptional<z.ZodBoolean>;
1653
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1654
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1655
+ min: z.ZodOptional<z.ZodNumber>;
1656
+ max: z.ZodOptional<z.ZodNumber>;
1657
+ unit: z.ZodOptional<z.ZodEnum<{
1658
+ percentage: "percentage";
1659
+ integer: "integer";
1660
+ decimal: "decimal";
1661
+ }>>;
1662
+ decimals: z.ZodOptional<z.ZodNumber>;
1663
+ }, z.core.$strip>;
1664
+ /**
1665
+ * Checkbox attribute config schema
1666
+ */
1667
+ declare const checkboxConfigSchema: z.ZodObject<{
1668
+ disabled: z.ZodOptional<z.ZodBoolean>;
1669
+ placeholder: z.ZodOptional<z.ZodString>;
1670
+ description: z.ZodOptional<z.ZodString>;
1671
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1672
+ icon: z.ZodOptional<z.ZodString>;
1673
+ order: z.ZodOptional<z.ZodNumber>;
1674
+ hidden: z.ZodOptional<z.ZodBoolean>;
1675
+ archived: z.ZodOptional<z.ZodBoolean>;
1676
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1677
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1678
+ }, z.core.$strip>;
1679
+ /**
1680
+ * Date attribute config schema
1681
+ */
1682
+ declare const dateConfigSchema: z.ZodObject<{
1683
+ disabled: z.ZodOptional<z.ZodBoolean>;
1684
+ placeholder: z.ZodOptional<z.ZodString>;
1685
+ description: z.ZodOptional<z.ZodString>;
1686
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1687
+ icon: z.ZodOptional<z.ZodString>;
1688
+ order: z.ZodOptional<z.ZodNumber>;
1689
+ hidden: z.ZodOptional<z.ZodBoolean>;
1690
+ archived: z.ZodOptional<z.ZodBoolean>;
1691
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1692
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1693
+ dateFormat: z.ZodOptional<z.ZodEnum<{
1694
+ short: "short";
1695
+ long: "long";
1696
+ full: "full";
1697
+ relative: "relative";
1698
+ }>>;
1699
+ minDate: z.ZodOptional<z.ZodString>;
1700
+ maxDate: z.ZodOptional<z.ZodString>;
1701
+ }, z.core.$strip>;
1702
+ /**
1703
+ * Phone attribute config schema
1704
+ */
1705
+ declare const phoneConfigSchema: z.ZodObject<{
1706
+ disabled: z.ZodOptional<z.ZodBoolean>;
1707
+ placeholder: z.ZodOptional<z.ZodString>;
1708
+ description: z.ZodOptional<z.ZodString>;
1709
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1710
+ icon: z.ZodOptional<z.ZodString>;
1711
+ order: z.ZodOptional<z.ZodNumber>;
1712
+ hidden: z.ZodOptional<z.ZodBoolean>;
1713
+ archived: z.ZodOptional<z.ZodBoolean>;
1714
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1715
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1716
+ defaultCountryCode: z.ZodOptional<z.ZodString>;
1717
+ }, z.core.$strip>;
1718
+ /**
1719
+ * Currency attribute config schema
1720
+ */
1721
+ declare const currencyConfigSchema: z.ZodObject<{
1722
+ disabled: z.ZodOptional<z.ZodBoolean>;
1723
+ placeholder: z.ZodOptional<z.ZodString>;
1724
+ description: z.ZodOptional<z.ZodString>;
1725
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1726
+ icon: z.ZodOptional<z.ZodString>;
1727
+ order: z.ZodOptional<z.ZodNumber>;
1728
+ hidden: z.ZodOptional<z.ZodBoolean>;
1729
+ archived: z.ZodOptional<z.ZodBoolean>;
1730
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1731
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1732
+ defaultCurrency: z.ZodOptional<z.ZodString>;
1733
+ allowedCurrencies: z.ZodOptional<z.ZodArray<z.ZodString>>;
1734
+ }, z.core.$strip>;
1735
+ /**
1736
+ * Status attribute config schema
1737
+ */
1738
+ declare const statusConfigSchema: z.ZodObject<{
1739
+ disabled: z.ZodOptional<z.ZodBoolean>;
1740
+ placeholder: z.ZodOptional<z.ZodString>;
1741
+ description: z.ZodOptional<z.ZodString>;
1742
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1743
+ icon: z.ZodOptional<z.ZodString>;
1744
+ order: z.ZodOptional<z.ZodNumber>;
1745
+ hidden: z.ZodOptional<z.ZodBoolean>;
1746
+ archived: z.ZodOptional<z.ZodBoolean>;
1747
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1748
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1749
+ options: z.ZodArray<z.ZodObject<{
1750
+ id: z.ZodString;
1751
+ label: z.ZodString;
1752
+ value: z.ZodString;
1753
+ color: z.ZodOptional<z.ZodString>;
1754
+ icon: z.ZodOptional<z.ZodString>;
1755
+ description: z.ZodOptional<z.ZodString>;
1756
+ group: z.ZodOptional<z.ZodEnum<{
1757
+ idle: "idle";
1758
+ in_progress: "in_progress";
1759
+ finished: "finished";
1760
+ }>>;
1761
+ }, z.core.$strip>>;
1762
+ }, z.core.$strip>;
1763
+ /**
1764
+ * Location attribute config schema
1765
+ */
1766
+ declare const locationConfigSchema: z.ZodObject<{
1767
+ disabled: z.ZodOptional<z.ZodBoolean>;
1768
+ placeholder: z.ZodOptional<z.ZodString>;
1769
+ description: z.ZodOptional<z.ZodString>;
1770
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1771
+ icon: z.ZodOptional<z.ZodString>;
1772
+ order: z.ZodOptional<z.ZodNumber>;
1773
+ hidden: z.ZodOptional<z.ZodBoolean>;
1774
+ archived: z.ZodOptional<z.ZodBoolean>;
1775
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1776
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1777
+ granularity: z.ZodEnum<{
1778
+ full: "full";
1779
+ address: "address";
1780
+ city: "city";
1781
+ state: "state";
1782
+ country: "country";
1783
+ coordinates: "coordinates";
1784
+ }>;
1785
+ enableAutocomplete: z.ZodOptional<z.ZodBoolean>;
1786
+ enableMap: z.ZodOptional<z.ZodBoolean>;
1787
+ defaultCountry: z.ZodOptional<z.ZodString>;
1788
+ allowedCountries: z.ZodOptional<z.ZodArray<z.ZodString>>;
1789
+ displayFormat: z.ZodOptional<z.ZodEnum<{
1790
+ single_line: "single_line";
1791
+ multi_line: "multi_line";
1792
+ compact: "compact";
1793
+ }>>;
1794
+ }, z.core.$strip>;
1795
+ /**
1796
+ * Timestamp attribute config schema
1797
+ */
1798
+ declare const timestampConfigSchema: z.ZodObject<{
1799
+ disabled: z.ZodOptional<z.ZodBoolean>;
1800
+ placeholder: z.ZodOptional<z.ZodString>;
1801
+ description: z.ZodOptional<z.ZodString>;
1802
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1803
+ icon: z.ZodOptional<z.ZodString>;
1804
+ order: z.ZodOptional<z.ZodNumber>;
1805
+ hidden: z.ZodOptional<z.ZodBoolean>;
1806
+ archived: z.ZodOptional<z.ZodBoolean>;
1807
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1808
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1809
+ autoUpdate: z.ZodOptional<z.ZodBoolean>;
1810
+ }, z.core.$strip>;
1811
+ /**
1812
+ * Select attribute config schema
1813
+ */
1814
+ declare const selectConfigSchema: z.ZodObject<{
1815
+ disabled: z.ZodOptional<z.ZodBoolean>;
1816
+ placeholder: z.ZodOptional<z.ZodString>;
1817
+ description: z.ZodOptional<z.ZodString>;
1818
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1819
+ icon: z.ZodOptional<z.ZodString>;
1820
+ order: z.ZodOptional<z.ZodNumber>;
1821
+ hidden: z.ZodOptional<z.ZodBoolean>;
1822
+ archived: z.ZodOptional<z.ZodBoolean>;
1823
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1824
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1825
+ options: z.ZodArray<z.ZodObject<{
1826
+ id: z.ZodString;
1827
+ label: z.ZodString;
1828
+ value: z.ZodString;
1829
+ color: z.ZodOptional<z.ZodString>;
1830
+ icon: z.ZodOptional<z.ZodString>;
1831
+ description: z.ZodOptional<z.ZodString>;
1832
+ group: z.ZodOptional<z.ZodEnum<{
1833
+ idle: "idle";
1834
+ in_progress: "in_progress";
1835
+ finished: "finished";
1836
+ }>>;
1837
+ }, z.core.$strip>>;
1838
+ }, z.core.$strip>;
1839
+ /**
1840
+ * Multiselect attribute config schema
1841
+ */
1842
+ declare const multiselectConfigSchema: z.ZodObject<{
1843
+ disabled: z.ZodOptional<z.ZodBoolean>;
1844
+ placeholder: z.ZodOptional<z.ZodString>;
1845
+ description: z.ZodOptional<z.ZodString>;
1846
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1847
+ icon: z.ZodOptional<z.ZodString>;
1848
+ order: z.ZodOptional<z.ZodNumber>;
1849
+ hidden: z.ZodOptional<z.ZodBoolean>;
1850
+ archived: z.ZodOptional<z.ZodBoolean>;
1851
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1852
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1853
+ options: z.ZodArray<z.ZodObject<{
1854
+ id: z.ZodString;
1855
+ label: z.ZodString;
1856
+ value: z.ZodString;
1857
+ color: z.ZodOptional<z.ZodString>;
1858
+ icon: z.ZodOptional<z.ZodString>;
1859
+ description: z.ZodOptional<z.ZodString>;
1860
+ group: z.ZodOptional<z.ZodEnum<{
1861
+ idle: "idle";
1862
+ in_progress: "in_progress";
1863
+ finished: "finished";
1864
+ }>>;
1865
+ }, z.core.$strip>>;
1866
+ }, z.core.$strip>;
1867
+ /**
1868
+ * File attribute config schema
1869
+ */
1870
+ declare const fileConfigSchema: z.ZodObject<{
1871
+ disabled: z.ZodOptional<z.ZodBoolean>;
1872
+ placeholder: z.ZodOptional<z.ZodString>;
1873
+ description: z.ZodOptional<z.ZodString>;
1874
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1875
+ icon: z.ZodOptional<z.ZodString>;
1876
+ order: z.ZodOptional<z.ZodNumber>;
1877
+ hidden: z.ZodOptional<z.ZodBoolean>;
1878
+ archived: z.ZodOptional<z.ZodBoolean>;
1879
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1880
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1881
+ maxFiles: z.ZodOptional<z.ZodNumber>;
1882
+ maxSize: z.ZodOptional<z.ZodNumber>;
1883
+ allowedTypes: z.ZodOptional<z.ZodArray<z.ZodString>>;
1884
+ verification: z.ZodOptional<z.ZodObject<{
1885
+ enabled: z.ZodBoolean;
1886
+ documentTypes: z.ZodArray<z.ZodObject<{
1887
+ type: z.ZodEnum<{
1888
+ id_card: "id_card";
1889
+ passport: "passport";
1890
+ incorporation_certificate: "incorporation_certificate";
1891
+ driver_license: "driver_license";
1892
+ birth_certificate: "birth_certificate";
1893
+ residence_permit: "residence_permit";
1894
+ bank_statement: "bank_statement";
1895
+ proof_of_address: "proof_of_address";
1896
+ custom: "custom";
1897
+ }>;
1898
+ label: z.ZodString;
1899
+ faces: z.ZodArray<z.ZodEnum<{
1900
+ front: "front";
1901
+ back: "back";
1902
+ single: "single";
1903
+ }>>;
1904
+ attributeMapping: z.ZodOptional<z.ZodArray<z.ZodObject<{
1905
+ attributeId: z.ZodString;
1906
+ extractedKey: z.ZodString;
1907
+ face: z.ZodOptional<z.ZodEnum<{
1908
+ front: "front";
1909
+ back: "back";
1910
+ single: "single";
1911
+ }>>;
1912
+ required: z.ZodOptional<z.ZodBoolean>;
1913
+ }, z.core.$strip>>>;
1914
+ }, z.core.$strip>>;
1915
+ autoExtract: z.ZodOptional<z.ZodBoolean>;
1916
+ autoValidate: z.ZodOptional<z.ZodBoolean>;
1917
+ }, z.core.$strip>>;
1918
+ }, z.core.$strip>;
1919
+ /**
1920
+ * User attribute config schema
1921
+ */
1922
+ declare const userConfigSchema: z.ZodObject<{
1923
+ disabled: z.ZodOptional<z.ZodBoolean>;
1924
+ placeholder: z.ZodOptional<z.ZodString>;
1925
+ description: z.ZodOptional<z.ZodString>;
1926
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1927
+ icon: z.ZodOptional<z.ZodString>;
1928
+ order: z.ZodOptional<z.ZodNumber>;
1929
+ hidden: z.ZodOptional<z.ZodBoolean>;
1930
+ archived: z.ZodOptional<z.ZodBoolean>;
1931
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1932
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1933
+ allowedRoles: z.ZodOptional<z.ZodArray<z.ZodString>>;
1934
+ }, z.core.$strip>;
1935
+ /**
1936
+ * Relation attribute config schema
1937
+ */
1938
+ declare const relationConfigSchema: z.ZodObject<{
1939
+ disabled: z.ZodOptional<z.ZodBoolean>;
1940
+ placeholder: z.ZodOptional<z.ZodString>;
1941
+ description: z.ZodOptional<z.ZodString>;
1942
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1943
+ icon: z.ZodOptional<z.ZodString>;
1944
+ order: z.ZodOptional<z.ZodNumber>;
1945
+ hidden: z.ZodOptional<z.ZodBoolean>;
1946
+ archived: z.ZodOptional<z.ZodBoolean>;
1947
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1948
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1949
+ targets: z.ZodArray<z.ZodObject<{
1950
+ object: z.ZodString;
1951
+ displayTemplate: z.ZodOptional<z.ZodString>;
1952
+ filter: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1953
+ }, z.core.$strip>>;
1954
+ cardinality: z.ZodEnum<{
1955
+ one: "one";
1956
+ many: "many";
1957
+ }>;
1958
+ minItems: z.ZodOptional<z.ZodNumber>;
1959
+ maxItems: z.ZodOptional<z.ZodNumber>;
1960
+ }, z.core.$strip>;
1961
+ /**
1962
+ * Rating attribute config schema
1963
+ */
1964
+ declare const ratingConfigSchema: z.ZodObject<{
1965
+ disabled: z.ZodOptional<z.ZodBoolean>;
1966
+ placeholder: z.ZodOptional<z.ZodString>;
1967
+ description: z.ZodOptional<z.ZodString>;
1968
+ defaultValue: z.ZodOptional<z.ZodUnknown>;
1969
+ icon: z.ZodOptional<z.ZodString>;
1970
+ order: z.ZodOptional<z.ZodNumber>;
1971
+ hidden: z.ZodOptional<z.ZodBoolean>;
1972
+ archived: z.ZodOptional<z.ZodBoolean>;
1973
+ deprecated: z.ZodOptional<z.ZodBoolean>;
1974
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1975
+ max: z.ZodOptional<z.ZodNumber>;
1976
+ iconType: z.ZodOptional<z.ZodEnum<{
1977
+ number: "number";
1978
+ heart: "heart";
1979
+ star: "star";
1980
+ thumbs: "thumbs";
1981
+ }>>;
1982
+ }, z.core.$strip>;
1983
+ /**
1984
+ * Map of attribute type to config schema
1985
+ */
1986
+ declare const attributeConfigSchemas: Record<AttributeType, z.ZodObject<z.ZodRawShape>>;
1987
+ /**
1988
+ * Get the config schema for a specific attribute type
1989
+ */
1990
+ declare function getAttributeConfigSchema(type: AttributeType): z.ZodObject<z.ZodRawShape>;
1991
+ /**
1992
+ * Validate attribute config for a specific type
1993
+ * Returns the validated config with only allowed properties
1994
+ */
1995
+ declare function validateAttributeConfig(type: AttributeType, config: Record<string, unknown>): {
1996
+ success: true;
1997
+ data: Record<string, unknown>;
1998
+ } | {
1999
+ success: false;
2000
+ errors: string[];
2001
+ };
2002
+ /**
2003
+ * Validate and strip unknown properties from attribute config
2004
+ * This ensures only allowed properties are stored in the database
2005
+ */
2006
+ declare function parseAttributeConfig(type: AttributeType, config: Record<string, unknown>): Record<string, unknown>;
2007
+ /**
2008
+ * Safely parse attribute config, returning undefined for invalid configs
2009
+ */
2010
+ declare function safeParseAttributeConfig(type: AttributeType, config: Record<string, unknown>): Record<string, unknown> | undefined;
2011
+ /**
2012
+ * Create a Zod schema for a text attribute
2013
+ */
2014
+ declare function createTextValidator(attr: TextAttribute): z.ZodString;
2015
+ /**
2016
+ * Create a Zod schema for a number attribute
2017
+ */
2018
+ declare function createNumberValidator(attr: NumberAttribute): z.ZodNumber;
2019
+ /**
2020
+ * Create a Zod schema for a checkbox attribute
2021
+ */
2022
+ declare function createCheckboxValidator(_attr: CheckboxAttribute): z.ZodBoolean;
2023
+ /**
2024
+ * Create a Zod schema for a date attribute
2025
+ */
2026
+ declare function createDateValidator(attr: DateAttribute): z.ZodString;
2027
+ /**
2028
+ * Create a Zod schema for a phone attribute
2029
+ */
2030
+ declare function createPhoneValidator(_attr: PhoneAttribute): z.ZodType<{
2031
+ countryCode: string;
2032
+ phoneNumber: string;
2033
+ }>;
2034
+ /**
2035
+ * Create a Zod schema for a currency attribute
2036
+ */
2037
+ declare function createCurrencyValidator(_attr: CurrencyAttribute): z.ZodType<{
2038
+ code: string;
2039
+ value: number;
2040
+ }>;
2041
+ /**
2042
+ * Create a Zod schema for a status attribute
2043
+ */
2044
+ declare function createStatusValidator(attr: StatusAttribute): z.ZodEnum<Readonly<Record<string, string>>>;
2045
+ /**
2046
+ * Create a Zod schema for a select attribute
2047
+ */
2048
+ declare function createSelectValidator(attr: SelectAttribute): z.ZodEnum<Readonly<Record<string, string>>>;
2049
+ /**
2050
+ * Create a Zod schema for a multiselect attribute
2051
+ */
2052
+ declare function createMultiselectValidator(attr: MultiselectAttribute): z.ZodArray<z.ZodEnum<Readonly<Record<string, string>>>>;
2053
+ /**
2054
+ * Create a Zod schema for a location attribute
2055
+ */
2056
+ type LocationShape = {
2057
+ address?: string;
2058
+ address2?: string;
2059
+ city?: string;
2060
+ state?: string;
2061
+ postalCode?: string;
2062
+ country?: string;
2063
+ latitude?: number;
2064
+ longitude?: number;
2065
+ };
2066
+ declare function createLocationValidator(_attr: LocationAttribute): z.ZodType<LocationShape>;
2067
+ /**
2068
+ * Create a Zod schema for a timestamp attribute
2069
+ */
2070
+ declare function createTimestampValidator(_attr: TimestampAttribute): z.ZodNumber;
2071
+ /**
2072
+ * Create a Zod schema for a file attribute
2073
+ */
2074
+ declare function createFileValidator(_attr: FileAttribute): z.ZodString;
2075
+ /**
2076
+ * Create a Zod schema for a user attribute
2077
+ */
2078
+ declare function createUserValidator(_attr: UserAttribute): z.ZodString;
2079
+ /**
2080
+ * Create a Zod schema for a single relation attribute (cardinality: "one")
2081
+ */
2082
+ declare function createSingleRelationValidator(attr: SingleRelationAttribute): z.ZodUnion<[z.ZodString, z.ZodNull]>;
2083
+ /**
2084
+ * Create a Zod schema for a multi relation attribute (cardinality: "many")
2085
+ */
2086
+ declare function createMultiRelationValidator(attr: MultiRelationAttribute): z.ZodArray<z.ZodString>;
2087
+ /**
2088
+ * Create a Zod schema for a relation attribute
2089
+ * Dispatches to single or multi validator based on cardinality
2090
+ */
2091
+ declare function createRelationValidator(attr: RelationAttribute): z.ZodUnion<[z.ZodString, z.ZodNull]> | z.ZodArray<z.ZodString>;
2092
+ /**
2093
+ * Create a Zod schema for a rating attribute
2094
+ */
2095
+ declare function createRatingValidator(attr: RatingAttribute): z.ZodNumber;
2096
+ /**
2097
+ * Create a Zod schema for any attribute type
2098
+ */
2099
+ declare function createAttributeValidator(attr: Attribute): z.ZodTypeAny;
2100
+ /**
2101
+ * Create a Zod schema for an entire object
2102
+ */
2103
+ declare function createObjectValidator(objectDef: ObjectDefinition): z.ZodType<Record<string, unknown>>;
2104
+ /**
2105
+ * Validation result
2106
+ */
2107
+ interface ValidationResult {
2108
+ success: boolean;
2109
+ data?: Record<string, unknown>;
2110
+ errors?: Array<{
2111
+ path: string[];
2112
+ message: string;
2113
+ }>;
2114
+ }
2115
+ /**
2116
+ * Validate data against an attribute schema
2117
+ */
2118
+ declare function validateAttribute(attr: Attribute, value: unknown): ValidationResult;
2119
+ /**
2120
+ * Validate data against an object schema
2121
+ */
2122
+ declare function validateObject(objectDef: ObjectDefinition, data: Record<string, unknown>): ValidationResult;
2123
+ /**
2124
+ * Validate and throw if invalid
2125
+ */
2126
+ declare function validateObjectOrThrow(objectDef: ObjectDefinition, data: Record<string, unknown>): Record<string, unknown>;
2127
+ /**
2128
+ * Create a Zod schema for draft validation.
2129
+ * All attributes become optional, but provided values are still validated.
2130
+ */
2131
+ declare function createDraftValidator(objectDef: ObjectDefinition): z.ZodType<Record<string, unknown>>;
2132
+ /**
2133
+ * Validate data in draft mode.
2134
+ * - All attributes are treated as optional (no required validation)
2135
+ * - Provided values are still validated for format/type correctness
2136
+ *
2137
+ * Use this when creating records that may be incomplete (drafts).
2138
+ *
2139
+ * @example
2140
+ * ```typescript
2141
+ * const result = validateDraft(PRODUCT, { name: "Draft" });
2142
+ * // → success even if "price" is required but missing
2143
+ *
2144
+ * const result2 = validateDraft(PRODUCT, { price: -10 });
2145
+ * // → fails because price must be >= 0 (format validation still applies)
2146
+ * ```
2147
+ */
2148
+ declare function validateDraft(objectDef: ObjectDefinition, data: Record<string, unknown>): ValidationResult;
2149
+ /**
2150
+ * Validate draft data and throw if format validation fails.
2151
+ */
2152
+ declare function validateDraftOrThrow(objectDef: ObjectDefinition, data: Record<string, unknown>): Record<string, unknown>;
2153
+ /**
2154
+ * Get the list of required attributes that are missing values.
2155
+ *
2156
+ * @example
2157
+ * ```typescript
2158
+ * const missing = getMissingRequiredAttributes(PRODUCT, { name: "Test" });
2159
+ * // → [priceAttribute, statusAttribute] if price and status are required but missing
2160
+ * ```
2161
+ */
2162
+ declare function getMissingRequiredAttributes(objectDef: ObjectDefinition, data: Record<string, unknown>): Attribute[];
2163
+ /**
2164
+ * Check if a record is complete (all required attributes have valid values).
2165
+ *
2166
+ * @returns `true` if all required values are present and valid, `false` otherwise
2167
+ */
2168
+ declare function isRecordComplete(objectDef: ObjectDefinition, data: Record<string, unknown>): boolean;
2169
+ /**
2170
+ * Compute the completion status of a record based on its data.
2171
+ *
2172
+ * - `"complete"`: All required values are present and valid
2173
+ * - `"draft"`: One or more required values are missing or invalid
2174
+ *
2175
+ * This function is used to dynamically determine the status when
2176
+ * creating or updating records.
2177
+ *
2178
+ * @example
2179
+ * ```typescript
2180
+ * const status = computeRecordStatus(PRODUCT, {
2181
+ * name: "Nike Air Max",
2182
+ * price: 129.99,
2183
+ * status: "active"
2184
+ * });
2185
+ * // → "complete"
2186
+ *
2187
+ * const status2 = computeRecordStatus(PRODUCT, { name: "Draft Product" });
2188
+ * // → "draft" (missing required fields)
2189
+ * ```
2190
+ */
2191
+ declare function computeRecordStatus(objectDef: ObjectDefinition, data: Record<string, unknown>): CompletionStatus;
2192
+
2193
+ /**
2194
+ * Object as stored in database (metadata)
2195
+ */
2196
+ interface DBObject extends Timestamps {
2197
+ id: Uuid;
2198
+ tenantId: TenantId;
2199
+ name: string;
2200
+ label: string;
2201
+ pluralLabel?: string;
2202
+ description?: string;
2203
+ icon?: IconName;
2204
+ labelExpression: string;
2205
+ system: boolean;
2206
+ metadata?: Record<string, unknown>;
2207
+ }
2208
+ interface CreateDBObject {
2209
+ tenantId: TenantId;
2210
+ name: string;
2211
+ label: string;
2212
+ pluralLabel?: string;
2213
+ description?: string;
2214
+ icon?: IconName;
2215
+ labelExpression: string;
2216
+ system?: boolean;
2217
+ metadata?: Record<string, unknown>;
2218
+ }
2219
+ interface UpdateDBObject {
2220
+ label?: string;
2221
+ pluralLabel?: string;
2222
+ description?: string;
2223
+ icon?: IconName;
2224
+ labelExpression?: string;
2225
+ metadata?: Record<string, unknown>;
2226
+ }
2227
+ interface UpsertDBObject extends CreateDBObject {
2228
+ system: boolean;
2229
+ }
2230
+ /**
2231
+ * Attribute as stored in database (metadata)
2232
+ */
2233
+ interface DBAttribute extends Timestamps {
2234
+ id: Uuid;
2235
+ objectId: Uuid;
2236
+ name: string;
2237
+ label: string;
2238
+ type: AttributeType;
2239
+ description?: string;
2240
+ icon?: IconName;
2241
+ system: boolean;
2242
+ required: boolean;
2243
+ unique: boolean;
2244
+ order: number;
2245
+ hidden?: boolean;
2246
+ archived?: boolean;
2247
+ deprecated?: boolean;
2248
+ defaultValue?: unknown;
2249
+ config: Record<string, unknown>;
2250
+ }
2251
+ interface CreateDBAttribute {
2252
+ objectId: Uuid;
2253
+ name: string;
2254
+ label: string;
2255
+ type: AttributeType;
2256
+ description?: string;
2257
+ icon?: IconName;
2258
+ system?: boolean;
2259
+ required?: boolean;
2260
+ unique?: boolean;
2261
+ order?: number;
2262
+ hidden?: boolean;
2263
+ archived?: boolean;
2264
+ deprecated?: boolean;
2265
+ defaultValue?: unknown;
2266
+ config?: Record<string, unknown>;
2267
+ }
2268
+ interface UpdateDBAttribute {
2269
+ label?: string;
2270
+ type?: AttributeType;
2271
+ description?: string;
2272
+ icon?: IconName;
2273
+ required?: boolean;
2274
+ order?: number;
2275
+ hidden?: boolean;
2276
+ archived?: boolean;
2277
+ deprecated?: boolean;
2278
+ defaultValue?: unknown;
2279
+ config?: Record<string, unknown>;
2280
+ }
2281
+ interface UpsertDBAttribute extends CreateDBAttribute {
2282
+ objectId: Uuid;
2283
+ name: string;
2284
+ }
2285
+
2286
+ /**
2287
+ * Object record creation data
2288
+ */
2289
+ interface CreateObjectRecord {
2290
+ tenantId: TenantId;
2291
+ objectId: Uuid;
2292
+ data: Record<string, unknown>;
2293
+ /**
2294
+ * Computed display label from labelExpression.
2295
+ * Stored for performance - avoids recomputation on every read.
2296
+ */
2297
+ label: string;
2298
+ /**
2299
+ * Completion status of the record.
2300
+ * - `draft`: Record is incomplete (missing required values)
2301
+ * - `complete`: All required values are present and valid
2302
+ */
2303
+ completionStatus: "draft" | "complete";
2304
+ }
2305
+ /**
2306
+ * Generic list options for pagination and sorting
2307
+ */
2308
+ interface ListOptions {
2309
+ limit?: number;
2310
+ offset?: number;
2311
+ /** @deprecated Use sorts instead */
2312
+ orderBy?: string;
2313
+ /** @deprecated Use sorts instead */
2314
+ orderDirection?: "asc" | "desc";
2315
+ /** Multiple sort rules */
2316
+ sorts?: SortRule[];
2317
+ /** Filter state */
2318
+ filters?: FilterState;
2319
+ /**
2320
+ * Include soft-deleted records in results.
2321
+ * By default, soft-deleted records are excluded.
2322
+ * @default false
2323
+ */
2324
+ includeDeleted?: boolean;
2325
+ }
2326
+ /**
2327
+ * Search options for full-text search
2328
+ */
2329
+ interface SearchOptions extends ListOptions {
2330
+ highlight?: boolean;
2331
+ }
2332
+ /**
2333
+ * Global search options
2334
+ */
2335
+ interface GlobalSearchOptions extends ListOptions {
2336
+ /**
2337
+ * Limit results to specific object names
2338
+ */
2339
+ objectNames?: string[];
2340
+ /**
2341
+ * Whether to include object metadata in results
2342
+ */
2343
+ includeObjectInfo?: boolean;
2344
+ }
2345
+ /**
2346
+ * Global search result item
2347
+ */
2348
+ interface GlobalSearchResultItem {
2349
+ objectId: string;
2350
+ objectName: string;
2351
+ objectLabel: string;
2352
+ /** Computed label from labelExpression */
2353
+ label: string;
2354
+ recordId: string;
2355
+ values: Record<string, unknown>;
2356
+ completionStatus: "draft" | "complete";
2357
+ createdAt: Date;
2358
+ updatedAt: Date;
2359
+ }
2360
+ /**
2361
+ * File-specific list options
2362
+ */
2363
+ interface FileListOptions extends ListOptions {
2364
+ folderPath?: string;
2365
+ mimeType?: string;
2366
+ uploadedBy?: Uuid;
2367
+ }
2368
+ /**
2369
+ * View as stored in database
2370
+ */
2371
+ interface DBView extends Timestamps {
2372
+ id: Uuid;
2373
+ tenantId: TenantId;
2374
+ objectId?: Uuid;
2375
+ objectName: string;
2376
+ name: string;
2377
+ label: string;
2378
+ description?: string;
2379
+ icon?: IconName;
2380
+ tabs: Tab[];
2381
+ default: boolean;
2382
+ system: boolean;
2383
+ metadata?: Record<string, unknown>;
2384
+ }
2385
+ interface CreateDBView {
2386
+ tenantId: TenantId;
2387
+ objectId?: Uuid;
2388
+ objectName: string;
2389
+ name: string;
2390
+ label: string;
2391
+ description?: string;
2392
+ icon?: IconName;
2393
+ tabs: Tab[];
2394
+ default?: boolean;
2395
+ system?: boolean;
2396
+ metadata?: Record<string, unknown>;
2397
+ }
2398
+ interface UpdateDBView {
2399
+ label?: string;
2400
+ description?: string;
2401
+ icon?: IconName;
2402
+ tabs?: Tab[];
2403
+ default?: boolean;
2404
+ metadata?: Record<string, unknown>;
2405
+ }
2406
+ interface UpsertDBView {
2407
+ tenantId: TenantId;
2408
+ objectName: string;
2409
+ name: string;
2410
+ label: string;
2411
+ description?: string;
2412
+ icon?: IconName;
2413
+ tabs: Tab[];
2414
+ default?: boolean;
2415
+ system?: boolean;
2416
+ metadata?: Record<string, unknown>;
2417
+ }
2418
+ /**
2419
+ * Result of an operation
2420
+ */
2421
+ interface OperationResult<T = unknown> {
2422
+ success: boolean;
2423
+ data?: T;
2424
+ error?: {
2425
+ code: string;
2426
+ message: string;
2427
+ details?: Record<string, unknown>;
2428
+ };
2429
+ }
2430
+
2431
+ /**
2432
+ * Repository for objects table (metadata)
2433
+ */
2434
+ interface ObjectsRepository {
2435
+ /**
2436
+ * Find object by ID
2437
+ */
2438
+ findById(id: Uuid): Promise<DBObject | null>;
2439
+ /**
2440
+ * Find object by name and tenant
2441
+ */
2442
+ findByName(tenantId: TenantId, name: string): Promise<DBObject | null>;
2443
+ /**
2444
+ * Find system/native object by name (system=true, for sync)
2445
+ */
2446
+ findSystemByName(name: string): Promise<DBObject | null>;
2447
+ /**
2448
+ * Create object
2449
+ */
2450
+ create(data: CreateDBObject): Promise<DBObject>;
2451
+ /**
2452
+ * Update object
2453
+ */
2454
+ update(id: Uuid, data: Partial<UpdateDBObject>): Promise<DBObject>;
2455
+ /**
2456
+ * Delete object
2457
+ */
2458
+ delete(id: Uuid): Promise<void>;
2459
+ /**
2460
+ * List all objects for a tenant
2461
+ */
2462
+ list(tenantId: TenantId): Promise<DBObject[]>;
2463
+ /**
2464
+ * Upsert object (create or update based on nativeObjectId)
2465
+ */
2466
+ upsert(data: UpsertDBObject): Promise<DBObject>;
2467
+ }
2468
+ /**
2469
+ * Repository for attributes table (metadata)
2470
+ */
2471
+ interface AttributesRepository {
2472
+ /**
2473
+ * Find attribute by ID
2474
+ */
2475
+ findById(id: Uuid): Promise<DBAttribute | null>;
2476
+ /**
2477
+ * Find attributes by object ID
2478
+ */
2479
+ findByObjectId(objectId: Uuid): Promise<DBAttribute[]>;
2480
+ /**
2481
+ * Create attribute
2482
+ */
2483
+ create(data: CreateDBAttribute): Promise<DBAttribute>;
2484
+ /**
2485
+ * Update attribute
2486
+ */
2487
+ update(id: Uuid, data: Partial<UpdateDBAttribute>): Promise<DBAttribute>;
2488
+ /**
2489
+ * Delete attribute
2490
+ */
2491
+ delete(id: Uuid): Promise<void>;
2492
+ /**
2493
+ * Delete multiple attributes by names (for sync cleanup)
2494
+ */
2495
+ deleteByNames(objectId: Uuid, excludeNames: string[]): Promise<number>;
2496
+ /**
2497
+ * Upsert attribute (create or update based on objectId + name)
2498
+ */
2499
+ upsert(data: UpsertDBAttribute): Promise<DBAttribute>;
2500
+ }
2501
+ /**
2502
+ * Repository for user_profiles table
2503
+ */
2504
+ interface UserProfilesRepository {
2505
+ /**
2506
+ * Find user profile by ID
2507
+ */
2508
+ findById(id: Uuid): Promise<UserProfile | null>;
2509
+ /**
2510
+ * Find user profile by auth ID (external auth provider)
2511
+ */
2512
+ findByAuthId(authId: string): Promise<UserProfile | null>;
2513
+ /**
2514
+ * Find user profile by email
2515
+ */
2516
+ findByEmail(tenantId: TenantId, email: string): Promise<UserProfile | null>;
2517
+ /**
2518
+ * Create user profile
2519
+ */
2520
+ create(data: CreateUserProfile): Promise<UserProfile>;
2521
+ /**
2522
+ * Update user profile
2523
+ */
2524
+ update(id: Uuid, data: UpdateUserProfile): Promise<UserProfile>;
2525
+ /**
2526
+ * Delete user profile
2527
+ */
2528
+ delete(id: Uuid): Promise<void>;
2529
+ /**
2530
+ * List all user profiles for a tenant
2531
+ */
2532
+ list(tenantId: TenantId, options?: ListOptions): Promise<UserProfile[]>;
2533
+ /**
2534
+ * Count user profiles by role within a tenant.
2535
+ * Useful for checking if last admin before deletion.
2536
+ */
2537
+ countByRole(tenantId: TenantId, role: string): Promise<number>;
2538
+ /**
2539
+ * Update last login timestamp
2540
+ */
2541
+ updateLastLogin(id: Uuid): Promise<void>;
2542
+ }
2543
+ /**
2544
+ * Repository for files table
2545
+ */
2546
+ interface FilesRepository {
2547
+ /**
2548
+ * Find file by ID
2549
+ */
2550
+ findById(id: Uuid): Promise<File | null>;
2551
+ /**
2552
+ * Create file
2553
+ */
2554
+ create(data: CreateFile): Promise<File>;
2555
+ /**
2556
+ * Update file
2557
+ */
2558
+ update(id: Uuid, data: UpdateFile): Promise<File>;
2559
+ /**
2560
+ * Delete file (soft delete)
2561
+ */
2562
+ delete(id: Uuid): Promise<void>;
2563
+ /**
2564
+ * Hard delete file (permanent)
2565
+ */
2566
+ hardDelete(id: Uuid): Promise<void>;
2567
+ /**
2568
+ * List files for a tenant
2569
+ */
2570
+ list(tenantId: TenantId, options?: FileListOptions): Promise<File[]>;
2571
+ /**
2572
+ * Find files by folder path
2573
+ */
2574
+ findByFolder(tenantId: TenantId, folderPath: string): Promise<File[]>;
2575
+ /**
2576
+ * Find files by uploader
2577
+ */
2578
+ findByUploader(uploadedBy: Uuid): Promise<File[]>;
2579
+ }
2580
+ /**
2581
+ * Repository for object_records table (unified JSONB)
2582
+ */
2583
+ interface ObjectRecordsRepository {
2584
+ /**
2585
+ * Find record by ID
2586
+ */
2587
+ findById(id: Uuid): Promise<ObjectRecord | null>;
2588
+ /**
2589
+ * Find multiple records by IDs (batch fetch)
2590
+ * Returns records in no particular order. Missing IDs are simply not included.
2591
+ */
2592
+ findByIds(ids: Uuid[]): Promise<ObjectRecord[]>;
2593
+ /**
2594
+ * Create record
2595
+ */
2596
+ create(data: CreateObjectRecord): Promise<ObjectRecord>;
2597
+ /**
2598
+ * Update record
2599
+ */
2600
+ update(id: Uuid, data: Partial<Record<string, unknown>>): Promise<ObjectRecord>;
2601
+ /**
2602
+ * Soft delete record (sets deletedAt timestamp).
2603
+ * Record can be restored with restore().
2604
+ */
2605
+ delete(id: Uuid): Promise<void>;
2606
+ /**
2607
+ * Hard delete record (permanent, cannot be restored).
2608
+ * Use with caution - prefer soft delete for data safety.
2609
+ */
2610
+ hardDelete(id: Uuid): Promise<void>;
2611
+ /**
2612
+ * Restore a soft-deleted record (clears deletedAt).
2613
+ * @throws Error if record is not found or not deleted
2614
+ */
2615
+ restore(id: Uuid): Promise<ObjectRecord>;
2616
+ /**
2617
+ * List records for an object
2618
+ */
2619
+ list(tenantId: TenantId, objectId: Uuid, options?: ListOptions): Promise<{
2620
+ records: ObjectRecord[];
2621
+ total: number;
2622
+ }>;
2623
+ /**
2624
+ * Search records using PostgreSQL full-text search
2625
+ */
2626
+ search(tenantId: TenantId, objectId: Uuid, query: string, options?: SearchOptions): Promise<{
2627
+ records: ObjectRecord[];
2628
+ total: number;
2629
+ }>;
2630
+ /**
2631
+ * Global search across all object records for a tenant
2632
+ * Searches in all objects or a subset specified by objectNames
2633
+ */
2634
+ globalSearch(tenantId: TenantId, query: string, options?: GlobalSearchOptions): Promise<{
2635
+ results: GlobalSearchResultItem[];
2636
+ total: number;
2637
+ }>;
2638
+ /**
2639
+ * Count records that reference a given record ID in any relation attribute.
2640
+ * Used to implement the "Restrict" delete behavior.
2641
+ *
2642
+ * This method scans all relation attributes across all objects to find
2643
+ * records that contain the target ID in their relation values.
2644
+ *
2645
+ * **Important:** This method should exclude soft-deleted records from the count.
2646
+ *
2647
+ * @param targetId - The record ID being checked for references
2648
+ * @returns Array of objects with reference counts, grouped by object
2649
+ *
2650
+ * @example
2651
+ * ```typescript
2652
+ * const refs = await repo.countRecordsReferencingId("rec-123");
2653
+ * // [{ objectName: "contacts", objectLabel: "Contacts", count: 3 }]
2654
+ * ```
2655
+ */
2656
+ countRecordsReferencingId(targetId: Uuid): Promise<Array<{
2657
+ objectName: string;
2658
+ objectLabel: string;
2659
+ count: number;
2660
+ }>>;
2661
+ /**
2662
+ * Remove an attribute's data from all records of an object.
2663
+ * Used after attribute deletion to clean up orphaned data.
2664
+ *
2665
+ * This efficiently removes the key from the JSONB values column
2666
+ * for all records belonging to the specified object.
2667
+ *
2668
+ * @param objectId - Object UUID
2669
+ * @param attributeName - Name of the attribute to remove
2670
+ * @returns Number of records that were updated
2671
+ *
2672
+ * @example
2673
+ * ```typescript
2674
+ * const updated = await repo.removeAttributeData("obj-123", "oldField");
2675
+ * console.log(`Cleaned up ${updated} records`);
2676
+ * ```
2677
+ */
2678
+ removeAttributeData(objectId: Uuid, attributeName: string): Promise<number>;
2679
+ /**
2680
+ * Batch update labels for all records of an object.
2681
+ * Used after labelExpression changes to refresh all record labels.
2682
+ *
2683
+ * @param objectId - Object UUID
2684
+ * @param computeLabel - Function to compute label from record values
2685
+ * @returns Number of records that were updated
2686
+ *
2687
+ * @example
2688
+ * ```typescript
2689
+ * const updated = await repo.batchRefreshLabels("obj-123", (values) => {
2690
+ * return `${values.sku} - ${values.name}`;
2691
+ * });
2692
+ * ```
2693
+ */
2694
+ batchRefreshLabels(objectId: Uuid, computeLabel: (values: Record<string, unknown>) => string): Promise<number>;
2695
+ /**
2696
+ * Batch update completion status for all records of an object.
2697
+ * Used after required attribute changes to refresh status.
2698
+ *
2699
+ * @param objectId - Object UUID
2700
+ * @param computeStatus - Function to compute status from record values
2701
+ * @returns Number of records that were updated
2702
+ */
2703
+ batchRefreshStatus(objectId: Uuid, computeStatus: (values: Record<string, unknown>) => "draft" | "complete"): Promise<number>;
2704
+ }
2705
+ /**
2706
+ * Repository for views table
2707
+ */
2708
+ interface ViewsRepository {
2709
+ /**
2710
+ * Find view by ID
2711
+ */
2712
+ findById(id: Uuid): Promise<DBView | null>;
2713
+ /**
2714
+ * Find view by name for an object and tenant
2715
+ */
2716
+ findByName(tenantId: TenantId, objectName: string, viewName: string): Promise<DBView | null>;
2717
+ /**
2718
+ * Find all views for an object and tenant
2719
+ */
2720
+ findByObjectName(tenantId: TenantId, objectName: string): Promise<DBView[]>;
2721
+ /**
2722
+ * Find all views for a tenant
2723
+ */
2724
+ findAllForTenant(tenantId: TenantId): Promise<DBView[]>;
2725
+ /**
2726
+ * Find system view by name (for sync)
2727
+ */
2728
+ findSystemByName(objectName: string, viewName: string): Promise<DBView | null>;
2729
+ /**
2730
+ * Find all system views for an object
2731
+ */
2732
+ findSystemByObjectName(objectName: string): Promise<DBView[]>;
2733
+ /**
2734
+ * Create view
2735
+ */
2736
+ create(data: CreateDBView): Promise<DBView>;
2737
+ /**
2738
+ * Update view
2739
+ */
2740
+ update(id: Uuid, data: Partial<UpdateDBView>): Promise<DBView>;
2741
+ /**
2742
+ * Delete view
2743
+ */
2744
+ delete(id: Uuid): Promise<void>;
2745
+ /**
2746
+ * Delete views not in the list (for sync cleanup)
2747
+ * @returns Number of deleted views
2748
+ */
2749
+ deleteNotIn(objectName: string, keepViewNames: string[]): Promise<number>;
2750
+ /**
2751
+ * Upsert view (create or update based on objectName + name)
2752
+ */
2753
+ upsert(data: UpsertDBView): Promise<DBView>;
2754
+ }
2755
+ /**
2756
+ * Repository for audit logs.
2757
+ *
2758
+ * This repository is optional - if not provided in the DatabaseAdapter,
2759
+ * audit logging is disabled (backward compatible behavior).
2760
+ */
2761
+ interface AuditRepository {
2762
+ /**
2763
+ * Create a new audit log entry
2764
+ */
2765
+ create(entry: CreateAuditLogInput): Promise<AuditLogEntry>;
2766
+ /**
2767
+ * Create multiple audit log entries (batch insert for performance)
2768
+ */
2769
+ createMany(entries: CreateAuditLogInput[]): Promise<void>;
2770
+ /**
2771
+ * List audit logs with filtering and pagination
2772
+ */
2773
+ list(tenantId: TenantId, options?: AuditListOptions): Promise<{
2774
+ entries: AuditLogEntry[];
2775
+ total: number;
2776
+ }>;
2777
+ /**
2778
+ * Get audit logs for a specific resource
2779
+ */
2780
+ getByResource(tenantId: TenantId, resourceType: AuditResourceType, resourceId: Uuid, options?: AuditListOptions): Promise<AuditLogEntry[]>;
2781
+ /**
2782
+ * Get audit logs by actor
2783
+ */
2784
+ getByActor(tenantId: TenantId, actorId: Uuid, options?: AuditListOptions): Promise<AuditLogEntry[]>;
2785
+ /**
2786
+ * Delete old audit logs (for retention policy)
2787
+ * @returns Number of deleted entries
2788
+ */
2789
+ deleteOlderThan(tenantId: TenantId, date: Date): Promise<number>;
2790
+ }
2791
+ /**
2792
+ * Repository for roles, permissions, and user role assignments.
2793
+ *
2794
+ * This repository is optional - if not provided in the DatabaseAdapter,
2795
+ * permission checks are disabled (backward compatible behavior).
2796
+ */
2797
+ interface PermissionsRepository {
2798
+ /**
2799
+ * Get all roles for a tenant
2800
+ */
2801
+ getRolesByTenant(tenantId: TenantId): Promise<Role[]>;
2802
+ /**
2803
+ * Get a role by ID
2804
+ */
2805
+ getRoleById(roleId: Uuid): Promise<Role | null>;
2806
+ /**
2807
+ * Get a role by name for a tenant
2808
+ */
2809
+ getRoleByName(tenantId: TenantId, name: string): Promise<Role | null>;
2810
+ /**
2811
+ * Create a new role
2812
+ */
2813
+ createRole(input: CreateRoleInput): Promise<Role>;
2814
+ /**
2815
+ * Update an existing role
2816
+ */
2817
+ updateRole(roleId: Uuid, updates: UpdateRoleInput): Promise<Role>;
2818
+ /**
2819
+ * Delete a role (fails if role is system role)
2820
+ */
2821
+ deleteRole(roleId: Uuid): Promise<void>;
2822
+ /**
2823
+ * Get all permissions for a role
2824
+ */
2825
+ getPermissionsByRole(roleId: Uuid): Promise<Permission[]>;
2826
+ /**
2827
+ * Set permissions for a role (replaces existing permissions)
2828
+ */
2829
+ setPermissions(roleId: Uuid, permissions: CreatePermissionInput[]): Promise<void>;
2830
+ /**
2831
+ * Get all roles assigned to a user profile within a tenant
2832
+ */
2833
+ getUserRoles(userProfileId: Uuid, tenantId: TenantId): Promise<Role[]>;
2834
+ /**
2835
+ * Assign a role to a user profile
2836
+ */
2837
+ assignRole(input: AssignRoleInput): Promise<UserRoleAssignment>;
2838
+ /**
2839
+ * Revoke a role from a user profile
2840
+ */
2841
+ revokeRole(userProfileId: Uuid, roleId: Uuid, tenantId: TenantId): Promise<void>;
2842
+ /**
2843
+ * Get effective permissions for a user profile (merged from all assigned roles)
2844
+ *
2845
+ * This method should:
2846
+ * 1. Get all roles assigned to the user profile
2847
+ * 2. Get all permissions for those roles
2848
+ * 3. Merge permissions (union of actions per object)
2849
+ * 4. Determine if user has admin privileges
2850
+ */
2851
+ getEffectivePermissions(userProfileId: Uuid, tenantId: TenantId): Promise<EffectivePermissions>;
2852
+ }
2853
+
2854
+ /**
2855
+ * Generic database adapter interface
2856
+ * Any ORM (Drizzle, Prisma, Kysely, TypeORM) can implement this interface
2857
+ * to work with @stndrds/schema runtime services
2858
+ *
2859
+ * @example
2860
+ * ```typescript
2861
+ * // Drizzle implementation
2862
+ * import { createDrizzleAdapter } from "./adapters/drizzle";
2863
+ * import { db } from "./db";
2864
+ *
2865
+ * const adapter = createDrizzleAdapter(db);
2866
+ *
2867
+ * // Prisma implementation
2868
+ * import { createPrismaAdapter } from "./adapters/prisma";
2869
+ * import { prisma } from "./db";
2870
+ *
2871
+ * const adapter = createPrismaAdapter(prisma);
2872
+ * ```
2873
+ */
2874
+ interface DatabaseAdapter {
2875
+ objects: ObjectsRepository;
2876
+ attributes: AttributesRepository;
2877
+ views: ViewsRepository;
2878
+ userProfiles: UserProfilesRepository;
2879
+ files: FilesRepository;
2880
+ objectRecords: ObjectRecordsRepository;
2881
+ permissions?: PermissionsRepository;
2882
+ audit?: AuditRepository;
2883
+ transaction<T>(callback: (adapter: DatabaseAdapter) => Promise<T>): Promise<T>;
2884
+ }
2885
+
2886
+ /**
2887
+ * Types of hooks supported by the system
2888
+ */
2889
+ type HookType = "beforeCreate" | "afterCreate" | "beforeUpdate" | "afterUpdate" | "beforeDelete" | "afterDelete" | "beforeRestore" | "afterRestore";
2890
+ /**
2891
+ * Change information for a specific attribute
2892
+ */
2893
+ interface AttributeChange<T = unknown> {
2894
+ oldValue: T | undefined;
2895
+ newValue: T | undefined;
2896
+ changed: boolean;
2897
+ }
2898
+ /**
2899
+ * Context passed to hooks - abstract, no framework dependencies
2900
+ *
2901
+ * This interface is designed to be extended by framework-specific implementations
2902
+ * (e.g., TriggerContext in schema-nestjs adds NestJS services)
2903
+ */
2904
+ interface HookContext<T = unknown> {
2905
+ objectId: string;
2906
+ objectName: string;
2907
+ recordId: string;
2908
+ tenantId: string;
2909
+ record: ObjectRecord;
2910
+ oldValues: Record<string, unknown>;
2911
+ newValues: Record<string, unknown>;
2912
+ changedAttributes: string[];
2913
+ /**
2914
+ * Get change information for a specific attribute
2915
+ * @param attributeName - The name of the attribute to check
2916
+ * @returns Change information including old value, new value, and whether it changed
2917
+ */
2918
+ getChange<V = T>(attributeName: string): AttributeChange<V>;
2919
+ metadata: Record<string, unknown>;
2920
+ timestamp: Date;
2921
+ }
2922
+ /**
2923
+ * Generic hook handler function
2924
+ */
2925
+ type HookHandler = (ctx: HookContext) => Promise<void> | void;
2926
+ /**
2927
+ * Definition of a hook for registration
2928
+ */
2929
+ interface HookDefinition {
2930
+ type: HookType;
2931
+ objectName: string;
2932
+ /** Attribute name to watch, or '*' for all attributes */
2933
+ attributeName?: string | "*";
2934
+ /** Execution priority (lower = earlier), default 0 */
2935
+ priority?: number;
2936
+ handler: HookHandler;
2937
+ }
2938
+
2939
+ /**
2940
+ * Abstract interface for hook registry
2941
+ *
2942
+ * This interface is implemented by TriggerRegistry in schema-nestjs
2943
+ * to provide NestJS-specific functionality like dependency injection
2944
+ * and automatic discovery of trigger classes.
2945
+ */
2946
+ interface HookRegistry {
2947
+ /**
2948
+ * Execute all hooks of a given type for an object
2949
+ *
2950
+ * @param type - The type of hook to execute (beforeUpdate, afterUpdate, etc.)
2951
+ * @param objectName - The name of the object being modified
2952
+ * @param ctx - The hook context containing record data and changes
2953
+ */
2954
+ execute(type: HookType, objectName: string, ctx: HookContext): Promise<void>;
2955
+ /**
2956
+ * Register a hook manually (optional)
2957
+ * Not all implementations need to support this (e.g., decorator-based discovery)
2958
+ */
2959
+ register?(hook: HookDefinition): void;
2960
+ }
2961
+ /**
2962
+ * No-op implementation of HookRegistry
2963
+ *
2964
+ * Used as default when no registry is provided to RecordService.
2965
+ * This allows the schema package to work independently without triggers.
2966
+ */
2967
+ declare class NoopHookRegistry implements HookRegistry {
2968
+ execute(): Promise<void>;
2969
+ }
2970
+
2971
+ interface InternalObjectRecord extends ObjectRecord {
2972
+ tenantId: TenantId;
2973
+ }
2974
+ interface MockStores {
2975
+ objects: Map<Uuid, DBObject>;
2976
+ attributes: Map<Uuid, DBAttribute>;
2977
+ userProfiles: Map<Uuid, UserProfile>;
2978
+ files: Map<Uuid, File>;
2979
+ objectRecords: Map<Uuid, InternalObjectRecord>;
2980
+ views: Map<Uuid, DBView>;
2981
+ roles: Map<Uuid, Role>;
2982
+ permissions: Map<Uuid, Permission>;
2983
+ userRoles: Map<Uuid, UserRoleAssignment>;
2984
+ }
2985
+ /**
2986
+ * Create an in-memory mock adapter for testing and development
2987
+ *
2988
+ * This adapter stores all data in memory and is perfect for:
2989
+ * - Unit tests
2990
+ * - Integration tests
2991
+ * - Local development without a database
2992
+ * - Understanding the adapter interface
2993
+ *
2994
+ * @returns A DatabaseAdapter implementation using in-memory storage
2995
+ *
2996
+ * @example
2997
+ * ```typescript
2998
+ * import { createMockAdapter, RecordService } from "@stndrds/schema";
2999
+ *
3000
+ * // Create adapter
3001
+ * const adapter = createMockAdapter();
3002
+ *
3003
+ * // Use with services
3004
+ * const recordService = new RecordService(adapter, "tenant-123");
3005
+ *
3006
+ * // Create a record
3007
+ * const product = await recordService.createRecord("obj-product", {
3008
+ * name: "Nike Air Max",
3009
+ * price: 129.99
3010
+ * });
3011
+ *
3012
+ * // List records
3013
+ * const { records, total } = await recordService.listRecords("obj-product");
3014
+ * ```
3015
+ */
3016
+ declare function createMockAdapter(): DatabaseAdapter & {
3017
+ _stores: MockStores;
3018
+ reset(): void;
3019
+ };
3020
+
3021
+ /**
3022
+ * Build audit changes by comparing old and new values.
3023
+ * Only includes fields that have actually changed.
3024
+ *
3025
+ * @param oldValues - Original values before update
3026
+ * @param newValues - New values from update input
3027
+ * @param fieldsToCheck - List of field names to compare
3028
+ * @returns Array of AuditChange objects for changed fields
3029
+ *
3030
+ * @example
3031
+ * ```typescript
3032
+ * const changes = buildAuditChanges(
3033
+ * { label: "Old", required: false },
3034
+ * { label: "New" },
3035
+ * ["label", "required"]
3036
+ * );
3037
+ * // Result: [{ field: "label", oldValue: "Old", newValue: "New" }]
3038
+ * ```
3039
+ */
3040
+ declare function buildAuditChanges<T extends Record<string, unknown>>(oldValues: T, newValues: Partial<T>, fieldsToCheck: (keyof T)[]): AuditChange[];
3041
+
3042
+ /**
3043
+ * Service for audit logging.
3044
+ *
3045
+ * Provides methods to log actions and query audit history.
3046
+ * Automatically redacts sensitive fields from changes.
3047
+ *
3048
+ * @example
3049
+ * ```typescript
3050
+ * const auditService = new AuditService(adapter, tenantId);
3051
+ *
3052
+ * // Log a record creation
3053
+ * await auditService.logRecordAction({
3054
+ * action: "record.created",
3055
+ * actorId: userId,
3056
+ * actorEmail: userEmail,
3057
+ * objectName: "companies",
3058
+ * objectId: schemaId,
3059
+ * recordId: record.id,
3060
+ * recordLabel: record.label,
3061
+ * });
3062
+ *
3063
+ * // Query record history
3064
+ * const history = await auditService.getRecordHistory(recordId);
3065
+ * ```
3066
+ */
3067
+ declare class AuditService {
3068
+ private readonly adapter;
3069
+ private readonly tenantId;
3070
+ private readonly options?;
3071
+ private buffer;
3072
+ private flushTimer;
3073
+ constructor(adapter: DatabaseAdapter, tenantId: string, options?: AuditServiceOptions | undefined);
3074
+ /**
3075
+ * Log a record action (create, update, delete, restore)
3076
+ */
3077
+ logRecordAction(params: {
3078
+ action: "record.created" | "record.updated" | "record.deleted" | "record.restored";
3079
+ actorId: string;
3080
+ actorEmail?: string;
3081
+ objectName: string;
3082
+ objectId: string;
3083
+ recordId: string;
3084
+ recordLabel?: string;
3085
+ changes?: AuditChange[];
3086
+ metadata?: Record<string, unknown>;
3087
+ }): Promise<void>;
3088
+ /**
3089
+ * Log a schema action (object/attribute changes)
3090
+ */
3091
+ logSchemaAction(params: {
3092
+ action: "object.created" | "object.updated" | "object.deleted" | "attribute.created" | "attribute.updated" | "attribute.deleted";
3093
+ actorId: string;
3094
+ actorEmail?: string;
3095
+ resourceType: "object" | "attribute";
3096
+ resourceId: string;
3097
+ resourceLabel: string;
3098
+ objectName?: string;
3099
+ objectId?: string;
3100
+ changes?: AuditChange[];
3101
+ metadata?: Record<string, unknown>;
3102
+ }): Promise<void>;
3103
+ /**
3104
+ * Log a user action
3105
+ */
3106
+ logUserAction(params: {
3107
+ action: "user.created" | "user.updated" | "user.deleted" | "user.login" | "user.logout";
3108
+ actorId: string;
3109
+ actorEmail?: string;
3110
+ targetUserId: string;
3111
+ targetUserEmail: string;
3112
+ changes?: AuditChange[];
3113
+ metadata?: Record<string, unknown>;
3114
+ }): Promise<void>;
3115
+ /**
3116
+ * Log a role action
3117
+ */
3118
+ logRoleAction(params: {
3119
+ action: "role.created" | "role.updated" | "role.deleted" | "role.assigned" | "role.revoked";
3120
+ actorId: string;
3121
+ actorEmail?: string;
3122
+ roleId: string;
3123
+ roleLabel: string;
3124
+ targetUserId?: string;
3125
+ changes?: AuditChange[];
3126
+ metadata?: Record<string, unknown>;
3127
+ }): Promise<void>;
3128
+ /**
3129
+ * Log a system action (no actor)
3130
+ */
3131
+ logSystemAction(params: {
3132
+ action: AuditAction;
3133
+ resourceType: AuditResourceType;
3134
+ resourceId: string;
3135
+ resourceLabel?: string;
3136
+ objectName?: string;
3137
+ objectId?: string;
3138
+ changes?: AuditChange[];
3139
+ metadata?: Record<string, unknown>;
3140
+ }): Promise<void>;
3141
+ /**
3142
+ * List audit logs with filtering and pagination
3143
+ */
3144
+ list(options?: AuditListOptions): Promise<{
3145
+ entries: AuditLogEntry[];
3146
+ total: number;
3147
+ }>;
3148
+ /**
3149
+ * Get audit history for a specific record
3150
+ */
3151
+ getRecordHistory(recordId: string): Promise<AuditLogEntry[]>;
3152
+ /**
3153
+ * Get activity for a specific user
3154
+ */
3155
+ getUserActivity(userId: string, options?: AuditListOptions): Promise<AuditLogEntry[]>;
3156
+ /**
3157
+ * Get audit logs for a specific resource
3158
+ */
3159
+ getResourceHistory(resourceType: AuditResourceType, resourceId: string): Promise<AuditLogEntry[]>;
3160
+ /**
3161
+ * Flush buffered logs to the database
3162
+ */
3163
+ flush(): Promise<void>;
3164
+ /**
3165
+ * Clean up resources (stop timer, flush remaining logs)
3166
+ */
3167
+ dispose(): Promise<void>;
3168
+ /**
3169
+ * Log an entry (sync or async based on options)
3170
+ */
3171
+ private log;
3172
+ /**
3173
+ * Start the flush timer for async mode
3174
+ */
3175
+ private startFlushTimer;
3176
+ /**
3177
+ * Redact sensitive fields from changes
3178
+ */
3179
+ private redactSensitiveFields;
3180
+ }
3181
+
3182
+ /**
3183
+ * Service for managing files
3184
+ * Handles file metadata CRUD, permissions, and soft delete
3185
+ */
3186
+ declare class FileService {
3187
+ private adapter;
3188
+ private tenantId;
3189
+ constructor(adapter: DatabaseAdapter, tenantId: string);
3190
+ /**
3191
+ * Create a new file record (after upload to storage)
3192
+ *
3193
+ * @param data - File metadata
3194
+ * @returns Created file record
3195
+ *
3196
+ * @example
3197
+ * ```typescript
3198
+ * const service = new FileService(adapter, "tenant-123");
3199
+ *
3200
+ * // After uploading to S3
3201
+ * const file = await service.createFile({
3202
+ * tenantId: "tenant-123",
3203
+ * name: "contract-2025.pdf",
3204
+ * originalName: "Contract Acme Corp 2025.pdf",
3205
+ * mimeType: "application/pdf",
3206
+ * size: 2458624,
3207
+ * storageProvider: "s3",
3208
+ * storagePath: "tenants/123/files/2025/contract.pdf",
3209
+ * storageBucket: "my-app-files",
3210
+ * url: "https://cdn.example.com/files/file-123",
3211
+ * uploadedBy: "profile-456",
3212
+ * visibility: "private"
3213
+ * });
3214
+ * ```
3215
+ */
3216
+ createFile(data: CreateFile): Promise<File>;
3217
+ /**
3218
+ * Get file by ID
3219
+ */
3220
+ getFile(fileId: string): Promise<File | null>;
3221
+ /**
3222
+ * Get file by ID or throw
3223
+ */
3224
+ getFileOrThrow(fileId: string): Promise<File>;
3225
+ /**
3226
+ * Update file metadata
3227
+ *
3228
+ * @param fileId - File UUID
3229
+ * @param data - Data to update
3230
+ * @returns Updated file
3231
+ */
3232
+ updateFile(fileId: string, data: UpdateFile): Promise<File>;
3233
+ /**
3234
+ * Delete file (soft delete)
3235
+ *
3236
+ * @param fileId - File UUID
3237
+ * @param options - Delete options
3238
+ */
3239
+ deleteFile(fileId: string, options?: {
3240
+ hard?: boolean;
3241
+ checkOwnership?: boolean;
3242
+ userId?: string;
3243
+ }): Promise<void>;
3244
+ /**
3245
+ * List files for the tenant
3246
+ */
3247
+ listFiles(options?: FileListOptions): Promise<File[]>;
3248
+ /**
3249
+ * List files by folder
3250
+ */
3251
+ listFilesByFolder(folderPath: string): Promise<File[]>;
3252
+ /**
3253
+ * List files uploaded by a specific user
3254
+ */
3255
+ listFilesByUploader(uploadedBy: string): Promise<File[]>;
3256
+ /**
3257
+ * Change file visibility
3258
+ *
3259
+ * @param fileId - File UUID
3260
+ * @param visibility - New visibility level
3261
+ * @param allowedUsers - Users allowed to access (if restricted)
3262
+ */
3263
+ changeVisibility(fileId: string, visibility: "public" | "private" | "restricted", allowedUsers?: string[]): Promise<File>;
3264
+ /**
3265
+ * Grant access to a file for specific users
3266
+ *
3267
+ * @param fileId - File UUID
3268
+ * @param userIds - User IDs to grant access
3269
+ */
3270
+ grantAccess(fileId: string, userIds: string[]): Promise<File>;
3271
+ /**
3272
+ * Revoke access to a file for specific users
3273
+ *
3274
+ * @param fileId - File UUID
3275
+ * @param userIds - User IDs to revoke access
3276
+ */
3277
+ revokeAccess(fileId: string, userIds: string[]): Promise<File>;
3278
+ /**
3279
+ * Check if user has access to a file
3280
+ *
3281
+ * @param fileId - File UUID
3282
+ * @param userId - User ID to check
3283
+ * @returns true if user can access the file
3284
+ */
3285
+ canAccess(fileId: string, userId: string): Promise<boolean>;
3286
+ /**
3287
+ * Move file to different folder
3288
+ */
3289
+ moveToFolder(fileId: string, newFolderPath: string): Promise<File>;
3290
+ /**
3291
+ * Add tags to file
3292
+ */
3293
+ addTags(fileId: string, tags: string[]): Promise<File>;
3294
+ /**
3295
+ * Remove tags from file
3296
+ */
3297
+ removeTags(fileId: string, tags: string[]): Promise<File>;
3298
+ }
3299
+
3300
+ /**
3301
+ * Geocoding service for address autocomplete and geocoding operations
3302
+ *
3303
+ * @example
3304
+ * ```typescript
3305
+ * import { GeocodingService } from "@stndrds/schema";
3306
+ * import { GoogleMapsAdapter } from "./adapters/google-maps";
3307
+ *
3308
+ * const geocoding = new GeocodingService(new GoogleMapsAdapter({ apiKey: "..." }));
3309
+ *
3310
+ * const suggestions = await geocoding.autocomplete({
3311
+ * query: "40 Quai des Belges",
3312
+ * countries: ["FRA"],
3313
+ * limit: 5,
3314
+ * });
3315
+ * ```
3316
+ */
3317
+ declare class GeocodingService {
3318
+ private readonly adapter;
3319
+ constructor(adapter: GeocodingAdapter);
3320
+ /**
3321
+ * Search for address suggestions as the user types
3322
+ */
3323
+ autocomplete(params: GeocodingAutocompleteParams): Promise<GeocodingSuggestion[]>;
3324
+ /**
3325
+ * Reverse geocode coordinates to an address
3326
+ */
3327
+ reverse(params: ReverseGeocodingParams): Promise<GeocodingSuggestion | null>;
3328
+ /**
3329
+ * Geocode a structured address to coordinates
3330
+ */
3331
+ geocode(params: GeocodingParams): Promise<GeocodingSuggestion | null>;
3332
+ }
3333
+
3334
+ /**
3335
+ * Service for global search across all object records
3336
+ * Allows searching across all objects or a subset of objects for a tenant
3337
+ *
3338
+ * @example
3339
+ * ```typescript
3340
+ * const service = new GlobalSearchService(adapter, "tenant-123");
3341
+ *
3342
+ * // Search across all objects
3343
+ * const { results, total } = await service.search("nike");
3344
+ *
3345
+ * // Search only in specific objects
3346
+ * const { results, total } = await service.search("nike", {
3347
+ * objectNames: ["products", "orders"],
3348
+ * limit: 20
3349
+ * });
3350
+ * ```
3351
+ */
3352
+ declare class GlobalSearchService {
3353
+ private adapter;
3354
+ private tenantId;
3355
+ constructor(adapter: DatabaseAdapter, tenantId: string);
3356
+ /**
3357
+ * Search across all object records for the tenant
3358
+ *
3359
+ * @param query - Search query string
3360
+ * @param options - Search options (pagination, object filters)
3361
+ * @returns Matching records with object metadata and total count
3362
+ *
3363
+ * @example
3364
+ * ```typescript
3365
+ * // Basic search
3366
+ * const { results, total } = await service.search("nike air");
3367
+ *
3368
+ * // With pagination
3369
+ * const { results, total } = await service.search("nike", {
3370
+ * limit: 10,
3371
+ * offset: 20
3372
+ * });
3373
+ *
3374
+ * // Filter by object types
3375
+ * const { results, total } = await service.search("nike", {
3376
+ * objectNames: ["products", "orders"]
3377
+ * });
3378
+ * ```
3379
+ */
3380
+ search(query: string, options?: GlobalSearchOptions): Promise<{
3381
+ results: GlobalSearchResultItem[];
3382
+ total: number;
3383
+ }>;
3384
+ /**
3385
+ * Search and group results by object type
3386
+ *
3387
+ * @param query - Search query string
3388
+ * @param options - Search options
3389
+ * @returns Results grouped by object name
3390
+ */
3391
+ searchGrouped(query: string, options?: Omit<GlobalSearchOptions, "limit" | "offset">): Promise<{
3392
+ groups: Array<{
3393
+ objectName: string;
3394
+ objectLabel: string;
3395
+ results: GlobalSearchResultItem[];
3396
+ count: number;
3397
+ }>;
3398
+ total: number;
3399
+ }>;
3400
+ }
3401
+
3402
+ /**
3403
+ * Input for creating a custom object
3404
+ */
3405
+ interface CreateCustomObjectInput {
3406
+ name: string;
3407
+ label: string;
3408
+ /**
3409
+ * Plural form of the label (e.g., "Products" for "Product")
3410
+ * Used in UI when displaying collections
3411
+ */
3412
+ pluralLabel?: string;
3413
+ /**
3414
+ * Template expression for computing record display labels.
3415
+ * Example: "{{ name }}" or "{{ firstName }} {{ lastName }}"
3416
+ */
3417
+ labelExpression: string;
3418
+ description?: string;
3419
+ icon?: IconName;
3420
+ attributes?: (Attribute | {
3421
+ build: () => Attribute;
3422
+ })[];
3423
+ metadata?: Record<string, unknown>;
3424
+ }
3425
+ /**
3426
+ * Input for adding an attribute to an object
3427
+ */
3428
+ interface AddAttributeInput {
3429
+ name: string;
3430
+ label: string;
3431
+ type: AttributeType;
3432
+ required?: boolean;
3433
+ unique?: boolean;
3434
+ description?: string;
3435
+ placeholder?: string;
3436
+ icon?: IconName;
3437
+ defaultValue?: unknown;
3438
+ metadata?: Record<string, unknown>;
3439
+ [key: string]: unknown;
3440
+ }
3441
+ /**
3442
+ * Input for updating an object
3443
+ * Only custom objects (system=false) can be updated
3444
+ * Note: `name` cannot be changed after creation
3445
+ */
3446
+ interface UpdateObjectInput {
3447
+ label?: string;
3448
+ pluralLabel?: string;
3449
+ description?: string;
3450
+ icon?: IconName;
3451
+ labelExpression?: string;
3452
+ metadata?: Record<string, unknown>;
3453
+ }
3454
+ /**
3455
+ * Options for ObjectSchemaService constructor
3456
+ */
3457
+ interface ObjectSchemaServiceOptions {
3458
+ /**
3459
+ * Audit service for logging schema changes.
3460
+ * If provided along with userId, audit logging is enabled.
3461
+ */
3462
+ auditService?: AuditService;
3463
+ /**
3464
+ * Current user ID for audit logs.
3465
+ */
3466
+ userId?: string;
3467
+ /**
3468
+ * Current user email for audit log denormalization.
3469
+ */
3470
+ userEmail?: string;
3471
+ }
3472
+ /**
3473
+ * Service for managing object schemas
3474
+ * Handles fusion of native objects (from registry) and custom objects (from database)
3475
+ */
3476
+ declare class ObjectSchemaService {
3477
+ private adapter;
3478
+ private nativeRegistry;
3479
+ private auditService?;
3480
+ private userId?;
3481
+ private userEmail?;
3482
+ constructor(adapter: DatabaseAdapter, nativeRegistry: typeof registry, options?: ObjectSchemaServiceOptions);
3483
+ /**
3484
+ * Create a new custom object
3485
+ * Validates name format and ensures system=false
3486
+ *
3487
+ * @param definition - Object definition (attributes can be raw definitions or builders)
3488
+ * @param tenantId - Tenant ID owning the object
3489
+ * @returns Created ObjectDefinition
3490
+ */
3491
+ createCustomObject(definition: CreateCustomObjectInput, tenantId: string): Promise<ObjectDefinition>;
3492
+ /**
3493
+ * Add a custom attribute to an existing object
3494
+ * Can be used on both custom and native objects
3495
+ * Attributes added via API are always custom (system=false)
3496
+ *
3497
+ * @param objectId - Object UUID from database
3498
+ * @param attribute - Attribute definition
3499
+ * @param tenantId - Tenant ID (required for relation validation)
3500
+ * @returns Created Attribute
3501
+ */
3502
+ addAttributeToObject(objectId: string, attribute: AddAttributeInput, tenantId?: string): Promise<Attribute>;
3503
+ /**
3504
+ * Update an attribute
3505
+ * Can only update custom attributes (system=false)
3506
+ *
3507
+ * @param attributeId - Attribute UUID
3508
+ * @param updates - Partial attribute updates
3509
+ * @param tenantId - Tenant ID (required for relation validation)
3510
+ * @returns Updated Attribute
3511
+ */
3512
+ updateAttribute(attributeId: string, updates: Partial<AddAttributeInput>, tenantId?: string): Promise<Attribute>;
3513
+ /**
3514
+ * Delete an attribute
3515
+ * Can only delete custom attributes (system=false)
3516
+ *
3517
+ * @param attributeId - Attribute UUID
3518
+ */
3519
+ deleteAttribute(attributeId: string): Promise<void>;
3520
+ /**
3521
+ * List attributes for an object
3522
+ *
3523
+ * @param objectId - Object UUID
3524
+ * @param options - Filter options
3525
+ * @returns List of attributes
3526
+ */
3527
+ listAttributes(objectId: string, options?: {
3528
+ systemOnly?: boolean;
3529
+ customOnly?: boolean;
3530
+ }): Promise<Attribute[]>;
3531
+ /**
3532
+ * Update a custom object
3533
+ * Can only update custom objects (system=false)
3534
+ *
3535
+ * @param objectId - Object UUID
3536
+ * @param updates - Partial object updates
3537
+ * @returns Updated ObjectDefinition
3538
+ *
3539
+ * @example
3540
+ * ```typescript
3541
+ * const updated = await service.updateObject("obj-123", {
3542
+ * label: "Updated Label",
3543
+ * description: "New description",
3544
+ * });
3545
+ * ```
3546
+ */
3547
+ updateObject(objectId: string, updates: UpdateObjectInput): Promise<ObjectDefinition>;
3548
+ /**
3549
+ * Delete a custom object
3550
+ * Can only delete custom objects (system=false)
3551
+ * This will also delete all associated attributes and records
3552
+ *
3553
+ * @param objectId - Object UUID
3554
+ *
3555
+ * @example
3556
+ * ```typescript
3557
+ * await service.deleteObject("obj-123");
3558
+ * ```
3559
+ */
3560
+ deleteObject(objectId: string, tenantId?: string): Promise<void>;
3561
+ /**
3562
+ * Validate attribute name format (variable identifier)
3563
+ * @internal
3564
+ */
3565
+ private validateAttributeName;
3566
+ /**
3567
+ * Get complete object schema (system + custom attributes)
3568
+ * If object is native, merges registry definition with DB custom attributes
3569
+ * If object is custom, returns DB definition only
3570
+ *
3571
+ * @param objectId - Object UUID from database
3572
+ * @returns Complete ObjectDefinition with all attributes
3573
+ *
3574
+ * @example
3575
+ * ```typescript
3576
+ * const service = new ObjectSchemaService(adapter, registry);
3577
+ * const productSchema = await service.getObjectSchema("obj-123");
3578
+ *
3579
+ * console.log(productSchema.attributes); // System + custom attributes
3580
+ * ```
3581
+ */
3582
+ getObjectSchema(objectId: string): Promise<ObjectDefinition>;
3583
+ /**
3584
+ * Get object schema by name (for system/native objects)
3585
+ * @deprecated Use getObjectSchemaByNameForTenant for custom objects support
3586
+ */
3587
+ getObjectSchemaByName(name: string): Promise<ObjectDefinition>;
3588
+ /**
3589
+ * Get object schema by name for a tenant (supports both native and custom objects)
3590
+ */
3591
+ getObjectSchemaByNameForTenant(name: string, tenantId: string): Promise<ObjectDefinition>;
3592
+ /**
3593
+ * List all object schemas for a tenant
3594
+ */
3595
+ listObjectSchemas(tenantId: string): Promise<ObjectDefinition[]>;
3596
+ /**
3597
+ * Merge native object from registry with custom attributes from DB
3598
+ * @internal
3599
+ */
3600
+ private mergeNativeObject;
3601
+ /**
3602
+ * Convert DB object + attributes to ObjectDefinition
3603
+ * @internal
3604
+ */
3605
+ private convertDBObjectToDefinition;
3606
+ /**
3607
+ * Validate attribute input and return parsed config
3608
+ * Validates: name format, config schema, relation targets
3609
+ * @internal
3610
+ */
3611
+ private validateAttributeInput;
3612
+ /**
3613
+ * Build and validate attribute config from input
3614
+ * Uses Zod schema to validate and strip unknown properties
3615
+ * @internal
3616
+ */
3617
+ private buildAttributeConfig;
3618
+ /**
3619
+ * Check if an object exists (in tenant, system, or native registry)
3620
+ * @internal
3621
+ */
3622
+ private objectExists;
3623
+ /**
3624
+ * Validate that relation target objects exist
3625
+ * @internal
3626
+ */
3627
+ private validateRelationTargets;
3628
+ /**
3629
+ * Find objects that have a relation attribute targeting the given object.
3630
+ * Used to prevent deletion of objects that are relation targets.
3631
+ * @internal
3632
+ */
3633
+ private findObjectsWithRelationTo;
3634
+ /**
3635
+ * Convert DB attribute to Attribute type
3636
+ * @internal
3637
+ */
3638
+ private convertDBAttributeToAttribute;
3639
+ }
3640
+
3641
+ /**
3642
+ * Options for PermissionService constructor
3643
+ */
3644
+ interface PermissionServiceOptions {
3645
+ /**
3646
+ * Cache TTL in milliseconds.
3647
+ * @default 60000 (1 minute)
3648
+ */
3649
+ cacheTtlMs?: number;
3650
+ /**
3651
+ * Maximum number of entries in the cache.
3652
+ * Oldest entries are evicted when limit is reached (LRU-like).
3653
+ * @default 10000
3654
+ */
3655
+ maxCacheSize?: number;
3656
+ /**
3657
+ * Audit service for logging role changes.
3658
+ * If provided along with userId, audit logging is enabled.
3659
+ */
3660
+ auditService?: AuditService;
3661
+ /**
3662
+ * Current user ID for audit logs (the actor).
3663
+ */
3664
+ userId?: string;
3665
+ /**
3666
+ * Current user email for audit log denormalization.
3667
+ */
3668
+ userEmail?: string;
3669
+ }
3670
+ /**
3671
+ * Service for checking and managing permissions.
3672
+ *
3673
+ * Features:
3674
+ * - In-memory cache with TTL and size limit for effective permissions
3675
+ * - Admin bypass for full access
3676
+ * - Wildcard permission support (target: "*")
3677
+ * - Object-level permission checks
3678
+ *
3679
+ * @example
3680
+ * ```typescript
3681
+ * const permissionService = new PermissionService(adapter, "tenant-123");
3682
+ *
3683
+ * // Check if user can read companies
3684
+ * const canRead = await permissionService.canAccessObject(userId, "companies", "read");
3685
+ *
3686
+ * // Throw if user cannot update
3687
+ * await permissionService.checkObjectAccess(userId, "companies", "update");
3688
+ * ```
3689
+ */
3690
+ declare class PermissionService {
3691
+ private readonly adapter;
3692
+ private readonly tenantId;
3693
+ private cache;
3694
+ private readonly cacheTtlMs;
3695
+ private readonly maxCacheSize;
3696
+ private readonly permissionsRepo;
3697
+ private readonly auditService?;
3698
+ private readonly userId?;
3699
+ private readonly userEmail?;
3700
+ constructor(adapter: DatabaseAdapter, tenantId: string, options?: PermissionServiceOptions);
3701
+ /**
3702
+ * Check if user can perform an action on an object.
3703
+ *
3704
+ * @param userProfileId - User profile ID to check
3705
+ * @param objectName - Object name to check access for
3706
+ * @param action - Action to check (read, create, update, delete)
3707
+ * @returns true if user has permission, false otherwise
3708
+ */
3709
+ canAccessObject(userProfileId: string, objectName: string, action: ObjectAction): Promise<boolean>;
3710
+ /**
3711
+ * Check if user can access an object, throw ForbiddenError if not.
3712
+ *
3713
+ * @param userProfileId - User profile ID to check
3714
+ * @param objectName - Object name to check access for
3715
+ * @param action - Action to check
3716
+ * @throws ForbiddenError if user lacks permission
3717
+ */
3718
+ checkObjectAccess(userProfileId: string, objectName: string, action: ObjectAction): Promise<void>;
3719
+ /**
3720
+ * Get permissions for a specific object as boolean flags.
3721
+ * Optimized to fetch permissions once and compute all flags.
3722
+ *
3723
+ * @param userProfileId - User profile ID to check
3724
+ * @param objectName - Object name to check
3725
+ * @returns Object with canRead, canCreate, canUpdate, canDelete flags
3726
+ */
3727
+ getObjectPermissions(userProfileId: string, objectName: string): Promise<ObjectPermissions>;
3728
+ /**
3729
+ * Check if user can perform an action on a system resource.
3730
+ *
3731
+ * @param userProfileId - User profile ID to check
3732
+ * @param resource - System resource (users, schema, roles, settings)
3733
+ * @param action - Action to check (read, create, update, delete)
3734
+ * @returns true if user has permission, false otherwise
3735
+ */
3736
+ canAccessSystem(userProfileId: string, resource: SystemResource, action: SystemAction): Promise<boolean>;
3737
+ /**
3738
+ * Check if user can access a system resource, throw ForbiddenError if not.
3739
+ *
3740
+ * @param userProfileId - User profile ID to check
3741
+ * @param resource - System resource to check access for
3742
+ * @param action - Action to check
3743
+ * @throws ForbiddenError if user lacks permission
3744
+ */
3745
+ checkSystemAccess(userProfileId: string, resource: SystemResource, action: SystemAction): Promise<void>;
3746
+ /**
3747
+ * Get permissions for a specific system resource as boolean flags.
3748
+ * Optimized to fetch permissions once and compute all flags.
3749
+ *
3750
+ * @param userProfileId - User profile ID to check
3751
+ * @param resource - System resource to check
3752
+ * @returns Object with canRead, canCreate, canUpdate, canDelete flags
3753
+ */
3754
+ getSystemPermissions(userProfileId: string, resource: SystemResource): Promise<SystemPermissions>;
3755
+ /**
3756
+ * Get effective permissions for a user.
3757
+ * Results are cached with TTL and size limit.
3758
+ *
3759
+ * @param userProfileId - User profile ID
3760
+ * @returns Merged permissions from all user's roles
3761
+ */
3762
+ getEffectivePermissions(userProfileId: string): Promise<EffectivePermissions>;
3763
+ /**
3764
+ * Invalidate cached permissions for a specific user.
3765
+ * Call this after role/permission changes.
3766
+ */
3767
+ invalidateCache(userProfileId: string): void;
3768
+ /**
3769
+ * Invalidate all cached permissions.
3770
+ * Call this after bulk role/permission changes.
3771
+ */
3772
+ invalidateAllCache(): void;
3773
+ /**
3774
+ * Get current cache size (for monitoring).
3775
+ */
3776
+ getCacheSize(): number;
3777
+ private getCacheKey;
3778
+ /**
3779
+ * Get all roles for the current tenant.
3780
+ */
3781
+ getRoles(): Promise<Role[]>;
3782
+ /**
3783
+ * Get a role by ID.
3784
+ */
3785
+ getRoleById(roleId: string): Promise<Role | null>;
3786
+ /**
3787
+ * Create a new role.
3788
+ */
3789
+ createRole(input: Omit<CreateRoleInput, "tenantId">): Promise<Role>;
3790
+ /**
3791
+ * Update a role.
3792
+ */
3793
+ updateRole(roleId: string, updates: UpdateRoleInput): Promise<Role>;
3794
+ /**
3795
+ * Delete a role.
3796
+ */
3797
+ deleteRole(roleId: string): Promise<void>;
3798
+ /**
3799
+ * Get all permissions for a role.
3800
+ */
3801
+ getPermissionsByRole(roleId: string): Promise<Permission[]>;
3802
+ /**
3803
+ * Set permissions for a role (replaces existing).
3804
+ */
3805
+ setPermissions(roleId: string, permissions: CreatePermissionInput[]): Promise<void>;
3806
+ /**
3807
+ * Get all roles assigned to a user.
3808
+ */
3809
+ getUserRoles(userProfileId: string): Promise<Role[]>;
3810
+ /**
3811
+ * Assign a role to a user.
3812
+ */
3813
+ assignRole(userProfileId: string, roleId: string, assignedBy?: string): Promise<UserRoleAssignment>;
3814
+ /**
3815
+ * Revoke a role from a user.
3816
+ */
3817
+ revokeRole(userProfileId: string, roleId: string): Promise<void>;
3818
+ /**
3819
+ * Initialize default roles for the tenant if they don't exist.
3820
+ *
3821
+ * Creates the following roles with their default permissions:
3822
+ * - **admin**: Full access to all system resources and objects
3823
+ * - **member**: No system access, can read/create/update objects (no delete)
3824
+ * - **guest**: No system access, read-only access to objects
3825
+ *
3826
+ * This method is idempotent - it only creates roles that don't already exist.
3827
+ * Should be called during tenant bootstrap or on first admin login.
3828
+ *
3829
+ * @example
3830
+ * ```typescript
3831
+ * const service = new PermissionService(adapter, tenantId);
3832
+ * await service.initializeDefaultRoles();
3833
+ * ```
3834
+ */
3835
+ initializeDefaultRoles(): Promise<void>;
3836
+ }
3837
+
3838
+ /**
3839
+ * Options for RecordService constructor
3840
+ */
3841
+ interface RecordServiceOptions {
3842
+ /**
3843
+ * Hook registry for executing triggers before/after operations.
3844
+ * If not provided, a no-op registry is used (no hooks executed).
3845
+ */
3846
+ hookRegistry?: HookRegistry;
3847
+ /**
3848
+ * Permission service for access control.
3849
+ * If provided along with userId, permission checks are enforced.
3850
+ * If not provided, no permission checks are performed (backward compatible).
3851
+ */
3852
+ permissionService?: PermissionService;
3853
+ /**
3854
+ * Current user ID for permission checks.
3855
+ * Required when permissionService is provided.
3856
+ */
3857
+ userId?: string;
3858
+ /**
3859
+ * Audit service for logging CRUD operations.
3860
+ * If provided along with userId, audit logging is enabled.
3861
+ * If not provided, no audit logs are created (backward compatible).
3862
+ */
3863
+ auditService?: AuditService;
3864
+ /**
3865
+ * Current user email for audit logs denormalization.
3866
+ * Optional but recommended for audit trail readability.
3867
+ */
3868
+ userEmail?: string;
3869
+ }
3870
+ /**
3871
+ * Service for managing object records (CRUD operations)
3872
+ * Handles validation, dispatch to correct table, and data consistency
3873
+ *
3874
+ * Supports optional hook system for triggers on attribute changes.
3875
+ * Supports optional permission checks when permissionService and userId are provided.
3876
+ */
3877
+ declare class RecordService {
3878
+ private adapter;
3879
+ private tenantId;
3880
+ private schemaService;
3881
+ private relationService;
3882
+ private hookRegistry;
3883
+ private permissionService?;
3884
+ private auditService?;
3885
+ private userId?;
3886
+ private userEmail?;
3887
+ constructor(adapter: DatabaseAdapter, tenantId: string, options?: RecordServiceOptions);
3888
+ /**
3889
+ * Check permission for an action on an object.
3890
+ * Only checks if permissionService and userId are configured.
3891
+ * @internal
3892
+ */
3893
+ private checkPermission;
3894
+ /**
3895
+ * Resolve relation IDs to their display labels
3896
+ * @internal
3897
+ */
3898
+ private resolveRelationLabels;
3899
+ /**
3900
+ * Extract relation IDs from a value (string or array)
3901
+ * @internal
3902
+ */
3903
+ private extractRelationIds;
3904
+ /**
3905
+ * Compute display label from schema expression
3906
+ * Automatically resolves relation attribute values to their labels
3907
+ * @internal
3908
+ */
3909
+ private computeLabel;
3910
+ /**
3911
+ * Create a new record with validation
3912
+ *
3913
+ * Triggers beforeCreate and afterCreate hooks if a HookRegistry is configured.
3914
+ *
3915
+ * @param objectId - Object UUID
3916
+ * @param data - Record data (attribute values)
3917
+ * @param options - Creation options
3918
+ * @returns Created record with computed completionStatus
3919
+ *
3920
+ * @example
3921
+ * ```typescript
3922
+ * const service = new RecordService(adapter, "tenant-123");
3923
+ *
3924
+ * // Create a complete record (strict validation)
3925
+ * const product = await service.createRecord("obj-product", {
3926
+ * name: "Nike Air Max",
3927
+ * price: 129.99,
3928
+ * status: "active"
3929
+ * });
3930
+ * // → product.completionStatus = "complete"
3931
+ *
3932
+ * // Create a draft record (allows missing required fields)
3933
+ * const draft = await service.createRecord("obj-product", {
3934
+ * name: "Draft Product"
3935
+ * }, { allowDraft: true });
3936
+ * // → draft.completionStatus = "draft"
3937
+ * ```
3938
+ */
3939
+ createRecord(objectId: string, data: Record<string, unknown>, options?: {
3940
+ /**
3941
+ * Allow creating records with missing required fields.
3942
+ * Format validation still applies to provided values.
3943
+ * @default false
3944
+ */
3945
+ allowDraft?: boolean;
3946
+ /**
3947
+ * Skip all validation (format + relations).
3948
+ * @default true
3949
+ */
3950
+ validate?: boolean;
3951
+ /**
3952
+ * Skip relation validation only.
3953
+ * Useful for bulk imports where relations are validated separately.
3954
+ * @default false
3955
+ */
3956
+ skipRelationValidation?: boolean;
3957
+ skipSystemCheck?: boolean;
3958
+ /**
3959
+ * Skip hook execution.
3960
+ * @default false
3961
+ */
3962
+ skipHooks?: boolean;
3963
+ /**
3964
+ * Additional metadata to pass to hooks (e.g., userId, requestId).
3965
+ */
3966
+ hookMetadata?: Record<string, unknown>;
3967
+ }): Promise<ObjectRecord>;
3968
+ /**
3969
+ * Get a record by ID
3970
+ *
3971
+ * @param recordId - Record UUID
3972
+ * @param options - Query options
3973
+ * @returns Record or null if not found
3974
+ */
3975
+ getRecord(recordId: string, options?: {
3976
+ includeSchema?: boolean;
3977
+ }): Promise<ObjectRecord | null>;
3978
+ /**
3979
+ * Get a record by ID or throw if not found
3980
+ */
3981
+ getRecordOrThrow(recordId: string): Promise<ObjectRecord>;
3982
+ /**
3983
+ * Update a record with validation
3984
+ *
3985
+ * The completion status is automatically recalculated after each update.
3986
+ * A draft record becomes complete when all required fields are filled.
3987
+ *
3988
+ * Triggers beforeUpdate and afterUpdate hooks if a HookRegistry is configured.
3989
+ *
3990
+ * @param recordId - Record UUID
3991
+ * @param data - Partial data to update
3992
+ * @param options - Update options
3993
+ * @returns Updated record with recalculated completionStatus
3994
+ *
3995
+ * @example
3996
+ * ```typescript
3997
+ * // Update a draft record to make it complete
3998
+ * const updated = await service.updateRecord(draftId, {
3999
+ * price: 99.99,
4000
+ * status: "active"
4001
+ * });
4002
+ * // → updated.completionStatus = "complete" if all required fields now present
4003
+ * ```
4004
+ */
4005
+ updateRecord(recordId: string, data: Partial<Record<string, unknown>>, options?: {
4006
+ /**
4007
+ * Skip validation entirely (format + relations).
4008
+ * @default true
4009
+ */
4010
+ validate?: boolean;
4011
+ /**
4012
+ * Allow partial updates without strict validation.
4013
+ * Format validation still applies to provided values.
4014
+ * @default false
4015
+ */
4016
+ partial?: boolean;
4017
+ /**
4018
+ * Skip relation validation only.
4019
+ * @default false
4020
+ */
4021
+ skipRelationValidation?: boolean;
4022
+ /**
4023
+ * Skip hook execution.
4024
+ * @default false
4025
+ */
4026
+ skipHooks?: boolean;
4027
+ /**
4028
+ * Additional metadata to pass to hooks (e.g., userId, requestId).
4029
+ */
4030
+ hookMetadata?: Record<string, unknown>;
4031
+ }): Promise<ObjectRecord>;
4032
+ /**
4033
+ * Build hook context for update operations
4034
+ * @internal
4035
+ */
4036
+ private buildHookContext;
4037
+ /**
4038
+ * Build hook context for create operations (no existing record)
4039
+ * @internal
4040
+ */
4041
+ private buildCreateHookContext;
4042
+ /**
4043
+ * Build hook context for delete operations
4044
+ * @internal
4045
+ */
4046
+ private buildDeleteHookContext;
4047
+ /**
4048
+ * Delete a record
4049
+ *
4050
+ * Triggers beforeDelete and afterDelete hooks if a HookRegistry is configured.
4051
+ *
4052
+ * @param recordId - Record UUID
4053
+ * @param options - Delete options
4054
+ */
4055
+ deleteRecord(recordId: string, options?: {
4056
+ checkSystem?: boolean;
4057
+ /**
4058
+ * Skip hook execution.
4059
+ * @default false
4060
+ */
4061
+ skipHooks?: boolean;
4062
+ /**
4063
+ * Skip relation reference check (use with caution).
4064
+ * @default false
4065
+ */
4066
+ skipReferenceCheck?: boolean;
4067
+ /**
4068
+ * Additional metadata to pass to hooks (e.g., userId, requestId).
4069
+ */
4070
+ hookMetadata?: Record<string, unknown>;
4071
+ }): Promise<void>;
4072
+ /**
4073
+ * Restore a soft-deleted record
4074
+ *
4075
+ * Triggers beforeRestore and afterRestore hooks if a HookRegistry is configured.
4076
+ *
4077
+ * @param recordId - Record UUID
4078
+ * @param options - Restore options
4079
+ * @returns Restored record
4080
+ *
4081
+ * @example
4082
+ * ```typescript
4083
+ * // Restore a deleted record
4084
+ * const restored = await service.restoreRecord("rec-123");
4085
+ * console.log(restored.deletedAt); // null
4086
+ * ```
4087
+ */
4088
+ restoreRecord(recordId: string, options?: {
4089
+ /**
4090
+ * Skip hook execution.
4091
+ * @default false
4092
+ */
4093
+ skipHooks?: boolean;
4094
+ /**
4095
+ * Additional metadata to pass to hooks (e.g., userId, requestId).
4096
+ */
4097
+ hookMetadata?: Record<string, unknown>;
4098
+ }): Promise<ObjectRecord>;
4099
+ /**
4100
+ * Build hook context for restore operations
4101
+ * @internal
4102
+ */
4103
+ private buildRestoreHookContext;
4104
+ /**
4105
+ * Permanently delete a record (hard delete)
4106
+ *
4107
+ * This cannot be undone. Use with caution - prefer soft delete for data safety.
4108
+ * Does NOT trigger delete hooks (already triggered on soft delete).
4109
+ *
4110
+ * @param recordId - Record UUID
4111
+ *
4112
+ * @example
4113
+ * ```typescript
4114
+ * // Permanently delete a record
4115
+ * await service.hardDeleteRecord("rec-123");
4116
+ * ```
4117
+ */
4118
+ hardDeleteRecord(recordId: string): Promise<void>;
4119
+ /**
4120
+ * List records for an object with pagination
4121
+ *
4122
+ * @param objectId - Object UUID
4123
+ * @param options - List options
4124
+ * @returns Records and total count
4125
+ */
4126
+ listRecords(objectId: string, options?: ListOptions): Promise<{
4127
+ records: ObjectRecord[];
4128
+ total: number;
4129
+ }>;
4130
+ /**
4131
+ * Search records using full-text search
4132
+ *
4133
+ * @param objectId - Object UUID
4134
+ * @param query - Search query
4135
+ * @param options - Search options
4136
+ * @returns Matching records and total count
4137
+ */
4138
+ searchRecords(objectId: string, query: string, options?: SearchOptions): Promise<{
4139
+ records: ObjectRecord[];
4140
+ total: number;
4141
+ }>;
4142
+ /**
4143
+ * Validate data against object schema without saving
4144
+ *
4145
+ * @param objectId - Object UUID
4146
+ * @param data - Data to validate
4147
+ * @returns Validation result
4148
+ */
4149
+ validateData(objectId: string, data: Record<string, unknown>): Promise<ValidationResult>;
4150
+ /**
4151
+ * Compute the completion status for given data without saving.
4152
+ * Useful for UI to show draft/complete status before submitting.
4153
+ *
4154
+ * @param objectId - Object UUID
4155
+ * @param data - Data to check
4156
+ * @returns Computed completion status
4157
+ */
4158
+ computeStatus(objectId: string, data: Record<string, unknown>): Promise<CompletionStatus>;
4159
+ /**
4160
+ * Refresh the completion status of an existing record.
4161
+ * Useful when schema changes and you need to recompute statuses.
4162
+ *
4163
+ * @param recordId - Record UUID
4164
+ * @returns Updated completion status
4165
+ */
4166
+ refreshRecordStatus(recordId: string): Promise<CompletionStatus>;
4167
+ }
4168
+
4169
+ /**
4170
+ * Result of relation validation
4171
+ */
4172
+ interface RelationValidationResult {
4173
+ valid: boolean;
4174
+ errors: RelationValidationError[];
4175
+ }
4176
+ /**
4177
+ * Individual relation validation error
4178
+ */
4179
+ interface RelationValidationError {
4180
+ /** Attribute name */
4181
+ attribute: string;
4182
+ /** Error message */
4183
+ message: string;
4184
+ /** Invalid record IDs */
4185
+ invalidIds?: string[];
4186
+ }
4187
+ /**
4188
+ * Resolved relation option
4189
+ *
4190
+ * SECURITY: This type intentionally excludes raw record data.
4191
+ * Only the computed label is exposed to prevent unauthorized data access
4192
+ * through relation lookups. Users must have explicit read permissions
4193
+ * on an object to access its record data.
4194
+ */
4195
+ interface RelationOption {
4196
+ /** Record ID */
4197
+ id: string;
4198
+ /** Object ID */
4199
+ objectId: string;
4200
+ /** Object name (technical name) */
4201
+ objectName: string;
4202
+ /** Object label (display name) */
4203
+ objectLabel: string;
4204
+ /** Object icon */
4205
+ objectIcon?: string;
4206
+ /** Display label (computed from labelExpression) */
4207
+ label: string;
4208
+ }
4209
+ /**
4210
+ * Response for relation options
4211
+ */
4212
+ interface RelationOptionsResponse {
4213
+ options: RelationOption[];
4214
+ hasMore: boolean;
4215
+ total: number;
4216
+ }
4217
+ /**
4218
+ * Parameters for fetching relation options
4219
+ */
4220
+ interface GetRelationOptionsParams {
4221
+ /** Search query */
4222
+ query?: string;
4223
+ /** Page number (1-based) */
4224
+ page?: number;
4225
+ /** Page size */
4226
+ pageSize?: number;
4227
+ /** Filter by specific target object */
4228
+ targetObject?: string;
4229
+ }
4230
+ /**
4231
+ * Service for validating relation attributes
4232
+ * Ensures referenced records exist and belong to valid target objects
4233
+ */
4234
+ declare class RelationService {
4235
+ private adapter;
4236
+ private schemaService;
4237
+ constructor(adapter: DatabaseAdapter, nativeRegistry: typeof registry);
4238
+ /**
4239
+ * Validate all relation attributes in the data
4240
+ *
4241
+ * @param schema - Object schema containing attribute definitions
4242
+ * @param data - Record data to validate
4243
+ * @returns Validation result with errors if any
4244
+ *
4245
+ * @example
4246
+ * ```typescript
4247
+ * const result = await relationService.validateRelations(schema, {
4248
+ * company: "rec-123",
4249
+ * contacts: ["rec-456", "rec-789"]
4250
+ * });
4251
+ *
4252
+ * if (!result.valid) {
4253
+ * console.log(result.errors);
4254
+ * // [{ attribute: "company", message: "Record not found", invalidIds: ["rec-123"] }]
4255
+ * }
4256
+ * ```
4257
+ */
4258
+ validateRelations(schema: ObjectDefinition, data: Record<string, unknown>): Promise<RelationValidationResult>;
4259
+ /**
4260
+ * Validate a single relation attribute value
4261
+ */
4262
+ private validateRelationAttribute;
4263
+ /**
4264
+ * Extract IDs from relation value based on cardinality
4265
+ */
4266
+ private extractIds;
4267
+ /**
4268
+ * Get valid object IDs from relation targets
4269
+ */
4270
+ private getValidObjectIds;
4271
+ /**
4272
+ * Validate relations and throw if invalid
4273
+ */
4274
+ validateRelationsOrThrow(schema: ObjectDefinition, data: Record<string, unknown>): Promise<void>;
4275
+ /**
4276
+ * Get available options for a relation attribute
4277
+ * Searches across all target objects defined in the relation
4278
+ *
4279
+ * @param attribute - Relation attribute definition
4280
+ * @param tenantId - Tenant ID for multi-tenant isolation
4281
+ * @param params - Query parameters
4282
+ *
4283
+ * @example
4284
+ * ```typescript
4285
+ * const options = await relationService.getOptions(attribute, "tenant-1", {
4286
+ * query: "nike",
4287
+ * page: 1,
4288
+ * pageSize: 20
4289
+ * });
4290
+ * ```
4291
+ */
4292
+ getOptions(attribute: RelationAttribute, tenantId: string, params?: GetRelationOptionsParams): Promise<RelationOptionsResponse>;
4293
+ /**
4294
+ * Resolve record IDs to their display labels
4295
+ * Useful for displaying current values in the UI
4296
+ *
4297
+ * Uses batch fetching for performance - fetches all records in one query,
4298
+ * then groups by objectId to minimize schema lookups.
4299
+ *
4300
+ * @param ids - Record IDs to resolve
4301
+ * @param _tenantId - Tenant ID for multi-tenant isolation (unused but kept for API compatibility)
4302
+ * @param attributeId - Relation attribute ID to use its displayTemplate for label rendering
4303
+ *
4304
+ * @example
4305
+ * ```typescript
4306
+ * const resolved = await relationService.resolveIds(["rec-1", "rec-2"], "tenant-1", "attr-123");
4307
+ * // [{ id: "rec-1", label: "Nike Air Max", objectName: "products", ... }]
4308
+ * ```
4309
+ */
4310
+ resolveIds(ids: string[], _tenantId: string, attributeId: string): Promise<RelationOption[]>;
4311
+ /**
4312
+ * Find a relation attribute by ID
4313
+ */
4314
+ findAttributeById(attributeId: string): Promise<RelationAttribute | null>;
4315
+ }
4316
+
4317
+ /**
4318
+ * Options for UserProfileService constructor
4319
+ */
4320
+ interface UserProfileServiceOptions {
4321
+ /**
4322
+ * Audit service for logging user changes.
4323
+ * If provided along with userId, audit logging is enabled.
4324
+ */
4325
+ auditService?: AuditService;
4326
+ /**
4327
+ * Current user ID for audit logs (the actor).
4328
+ */
4329
+ userId?: string;
4330
+ /**
4331
+ * Current user email for audit log denormalization.
4332
+ */
4333
+ userEmail?: string;
4334
+ }
4335
+ /**
4336
+ * Service for managing user profiles
4337
+ * Handles user profile CRUD, auth provider sync, and role management
4338
+ */
4339
+ declare class UserProfileService {
4340
+ private adapter;
4341
+ private tenantId;
4342
+ private auditService?;
4343
+ private userId?;
4344
+ private userEmail?;
4345
+ constructor(adapter: DatabaseAdapter, tenantId: string, options?: UserProfileServiceOptions);
4346
+ /**
4347
+ * Create a new user profile (typically after first auth)
4348
+ *
4349
+ * @param data - User profile creation data
4350
+ * @returns Created user profile
4351
+ *
4352
+ * @example
4353
+ * ```typescript
4354
+ * const service = new UserProfileService(adapter, "tenant-123");
4355
+ *
4356
+ * // After Supabase auth
4357
+ * const profile = await service.createProfile({
4358
+ * tenantId: "tenant-123",
4359
+ * authId: authUser.id,
4360
+ * email: authUser.email,
4361
+ * firstName: authUser.user_metadata.first_name,
4362
+ * lastName: authUser.user_metadata.last_name,
4363
+ * role: "member",
4364
+ * status: "active"
4365
+ * });
4366
+ * ```
4367
+ */
4368
+ createProfile(data: CreateUserProfile): Promise<UserProfile>;
4369
+ /**
4370
+ * Get user profile by ID
4371
+ */
4372
+ getProfile(profileId: string): Promise<UserProfile | null>;
4373
+ /**
4374
+ * Get user profile by ID or throw
4375
+ */
4376
+ getProfileOrThrow(profileId: string): Promise<UserProfile>;
4377
+ /**
4378
+ * Get user profile by auth provider ID
4379
+ *
4380
+ * @param authId - Auth provider user ID (Supabase, Clerk, etc.)
4381
+ * @returns User profile or null
4382
+ */
4383
+ getProfileByAuthId(authId: string): Promise<UserProfile | null>;
4384
+ /**
4385
+ * Get or create user profile (idempotent operation)
4386
+ * Useful for auth callbacks - ensures profile exists
4387
+ *
4388
+ * @param authId - Auth provider user ID
4389
+ * @param data - Profile data to create if doesn't exist
4390
+ * @returns Existing or newly created profile
4391
+ *
4392
+ * @example
4393
+ * ```typescript
4394
+ * // In Supabase auth callback
4395
+ * const profile = await service.getOrCreateProfile(
4396
+ * authUser.id,
4397
+ * {
4398
+ * tenantId: "tenant-123",
4399
+ * authId: authUser.id,
4400
+ * email: authUser.email,
4401
+ * role: "member",
4402
+ * status: "active"
4403
+ * }
4404
+ * );
4405
+ * ```
4406
+ */
4407
+ getOrCreateProfile(authId: string, data: CreateUserProfile): Promise<UserProfile>;
4408
+ /**
4409
+ * Update user profile
4410
+ */
4411
+ updateProfile(profileId: string, data: UpdateUserProfile): Promise<UserProfile>;
4412
+ /**
4413
+ * Delete user profile
4414
+ *
4415
+ * @param profileId - Profile UUID
4416
+ * @param options - Delete options
4417
+ */
4418
+ deleteProfile(profileId: string, options?: {
4419
+ checkAdmin?: boolean;
4420
+ }): Promise<void>;
4421
+ /**
4422
+ * List all user profiles for the tenant
4423
+ */
4424
+ listProfiles(options?: ListOptions): Promise<UserProfile[]>;
4425
+ /**
4426
+ * Update last login timestamp
4427
+ *
4428
+ * @param profileId - Profile UUID
4429
+ *
4430
+ * @example
4431
+ * ```typescript
4432
+ * // After successful auth
4433
+ * await service.updateLastLogin(profile.id);
4434
+ * ```
4435
+ */
4436
+ updateLastLogin(profileId: string): Promise<void>;
4437
+ /**
4438
+ * Change user role
4439
+ *
4440
+ * @param profileId - Profile UUID
4441
+ * @param newRole - New role
4442
+ */
4443
+ changeRole(profileId: string, newRole: string): Promise<UserProfile>;
4444
+ /**
4445
+ * Change user status
4446
+ *
4447
+ * @param profileId - Profile UUID
4448
+ * @param newStatus - New status
4449
+ */
4450
+ changeStatus(profileId: string, newStatus: "active" | "pending" | "inactive" | "suspended"): Promise<UserProfile>;
4451
+ /**
4452
+ * Get user by email
4453
+ */
4454
+ getProfileByEmail(email: string): Promise<UserProfile | null>;
4455
+ /**
4456
+ * Check if user has role
4457
+ */
4458
+ hasRole(profileId: string, role: string): Promise<boolean>;
4459
+ /**
4460
+ * Check if user is admin
4461
+ */
4462
+ isAdmin(profileId: string): Promise<boolean>;
4463
+ }
4464
+
4465
+ /**
4466
+ * Input for creating a custom view
4467
+ */
4468
+ interface CreateViewInput {
4469
+ name: string;
4470
+ label: string;
4471
+ objectName: string;
4472
+ description?: string;
4473
+ icon?: IconName;
4474
+ tabs: Tab[];
4475
+ default?: boolean;
4476
+ metadata?: Record<string, unknown>;
4477
+ }
4478
+ /**
4479
+ * Input for updating a view
4480
+ */
4481
+ interface UpdateViewInput {
4482
+ label?: string;
4483
+ description?: string;
4484
+ icon?: IconName;
4485
+ tabs?: Tab[];
4486
+ default?: boolean;
4487
+ metadata?: Record<string, unknown>;
4488
+ }
4489
+ /**
4490
+ * Service for managing views
4491
+ * Handles fusion of native views (from registry) and custom views (from database)
4492
+ */
4493
+ declare class ViewService {
4494
+ private adapter;
4495
+ private nativeViews;
4496
+ constructor(adapter: DatabaseAdapter, nativeViews: typeof viewRegistry);
4497
+ /**
4498
+ * Get all views for an object (native + custom)
4499
+ *
4500
+ * @param objectName - Object name
4501
+ * @param tenantId - Tenant ID
4502
+ * @returns All views for the object
4503
+ */
4504
+ getViewsForObject(objectName: string, tenantId: string): Promise<ViewDefinition[]>;
4505
+ /**
4506
+ * Get a specific view by name
4507
+ *
4508
+ * @param objectName - Object name
4509
+ * @param viewName - View name
4510
+ * @param tenantId - Tenant ID
4511
+ * @returns View definition or null
4512
+ */
4513
+ getView(objectName: string, viewName: string, tenantId: string): Promise<ViewDefinition | null>;
4514
+ /**
4515
+ * Get the default view for an object
4516
+ *
4517
+ * Priority:
4518
+ * 1. Custom view marked as default
4519
+ * 2. Native view marked as default
4520
+ * 3. First available view
4521
+ *
4522
+ * @param objectName - Object name
4523
+ * @param tenantId - Tenant ID
4524
+ * @returns Default view or null
4525
+ */
4526
+ getDefaultView(objectName: string, tenantId: string): Promise<ViewDefinition | null>;
4527
+ /**
4528
+ * Create a custom view
4529
+ *
4530
+ * @param input - View definition
4531
+ * @param tenantId - Tenant ID
4532
+ * @returns Created view
4533
+ */
4534
+ createView(input: CreateViewInput, tenantId: string): Promise<ViewDefinition>;
4535
+ /**
4536
+ * Update a custom view
4537
+ *
4538
+ * @param viewId - View ID
4539
+ * @param input - Update data
4540
+ * @returns Updated view
4541
+ */
4542
+ updateView(viewId: string, input: UpdateViewInput): Promise<ViewDefinition>;
4543
+ /**
4544
+ * Delete a custom view
4545
+ *
4546
+ * @param viewId - View ID
4547
+ */
4548
+ deleteView(viewId: string): Promise<void>;
4549
+ /**
4550
+ * Set a view as default for its object
4551
+ *
4552
+ * @param viewId - View ID
4553
+ * @param tenantId - Tenant ID
4554
+ * @returns Updated view
4555
+ */
4556
+ setDefaultView(viewId: string, tenantId: string): Promise<ViewDefinition>;
4557
+ /**
4558
+ * Validate view name format (kebab-case)
4559
+ */
4560
+ private validateViewName;
4561
+ /**
4562
+ * Convert database view to ViewDefinition
4563
+ */
4564
+ private convertDBViewToDefinition;
4565
+ }
4566
+
4567
+ /**
4568
+ * Result of view sync operation
4569
+ */
4570
+ interface ViewSyncResult {
4571
+ success: boolean;
4572
+ viewsSynced: number;
4573
+ viewsCreated: number;
4574
+ viewsUpdated: number;
4575
+ viewsDeleted: number;
4576
+ errors: Array<{
4577
+ viewName: string;
4578
+ objectName: string;
4579
+ error: string;
4580
+ }>;
4581
+ }
4582
+ /**
4583
+ * Options for view sync
4584
+ */
4585
+ interface ViewSyncOptions {
4586
+ dryRun?: boolean;
4587
+ verbose?: boolean;
4588
+ tenantId?: string;
4589
+ }
4590
+ /**
4591
+ * Sync native views from registry to database
4592
+ *
4593
+ * This function:
4594
+ * 1. Reads all registered native views from the registry
4595
+ * 2. Upserts them into the database (views table)
4596
+ * 3. Marks them as system=true for protection
4597
+ * 4. Removes views that were deleted from code
4598
+ *
4599
+ * @param adapter - Database adapter implementing DatabaseAdapter interface
4600
+ * @param nativeViewRegistry - Registry containing native views
4601
+ * @param options - Sync options (dryRun, verbose, tenantId)
4602
+ * @returns Sync result with statistics
4603
+ *
4604
+ * @example
4605
+ * ```typescript
4606
+ * import { syncNativeViews, viewRegistry } from "@stndrds/schema";
4607
+ * import { drizzleAdapter } from "./db/adapter";
4608
+ *
4609
+ * const result = await syncNativeViews(drizzleAdapter, viewRegistry, {
4610
+ * verbose: true,
4611
+ * tenantId: "default"
4612
+ * });
4613
+ *
4614
+ * if (result.success) {
4615
+ * console.log(`✓ Synced ${result.viewsSynced} views`);
4616
+ * }
4617
+ * ```
4618
+ */
4619
+ declare function syncNativeViews(adapter: DatabaseAdapter, nativeViewRegistry: typeof viewRegistry, options?: ViewSyncOptions): Promise<ViewSyncResult>;
4620
+ /**
4621
+ * Verify that all native views are synced to database
4622
+ *
4623
+ * @param adapter - Database adapter
4624
+ * @param nativeViewRegistry - Registry containing native views
4625
+ * @returns true if all views are synced, false otherwise
4626
+ *
4627
+ * @example
4628
+ * ```typescript
4629
+ * const isSynced = await verifyNativeViewsSync(adapter, viewRegistry);
4630
+ * if (!isSynced) {
4631
+ * console.warn("Native views not synced, running sync...");
4632
+ * await syncNativeViews(adapter, viewRegistry);
4633
+ * }
4634
+ * ```
4635
+ */
4636
+ declare function verifyNativeViewsSync(adapter: DatabaseAdapter, nativeViewRegistry: typeof viewRegistry): Promise<boolean>;
4637
+ /**
4638
+ * Get sync preview without modifying database
4639
+ *
4640
+ * @param adapter - Database adapter
4641
+ * @param nativeViewRegistry - Registry containing native views
4642
+ * @returns Sync result (dry run)
4643
+ */
4644
+ declare function getViewSyncPreview(adapter: DatabaseAdapter, nativeViewRegistry: typeof viewRegistry): Promise<ViewSyncResult>;
4645
+
4646
+ /**
4647
+ * Result of sync operation
4648
+ */
4649
+ interface SyncResult {
4650
+ success: boolean;
4651
+ objectsSynced: number;
4652
+ attributesSynced: number;
4653
+ objectsCreated: number;
4654
+ objectsUpdated: number;
4655
+ attributesCreated: number;
4656
+ attributesUpdated: number;
4657
+ attributesDeleted: number;
4658
+ errors: Array<{
4659
+ objectName: string;
4660
+ error: string;
4661
+ }>;
4662
+ }
4663
+ /**
4664
+ * Sync options
4665
+ */
4666
+ interface SyncOptions {
4667
+ dryRun?: boolean;
4668
+ verbose?: boolean;
4669
+ tenantId?: string;
4670
+ }
4671
+ /**
4672
+ * Sync native objects from registry to database
4673
+ *
4674
+ * This function:
4675
+ * 1. Reads all registered native objects from the registry
4676
+ * 2. Upserts them into the database (objects + attributes tables)
4677
+ * 3. Marks them as system=true for protection
4678
+ * 4. Removes attributes that were deleted from code
4679
+ *
4680
+ * @param adapter - Database adapter implementing DatabaseAdapter interface
4681
+ * @param nativeRegistry - Registry containing native objects
4682
+ * @param options - Sync options (dryRun, verbose, tenantId)
4683
+ * @returns Sync result with statistics
4684
+ *
4685
+ * @example
4686
+ * ```typescript
4687
+ * import { syncNativeObjects } from "@stndrds/schema/runtime";
4688
+ * import { registry } from "./objects/native";
4689
+ * import { drizzleAdapter } from "./db/adapter";
4690
+ *
4691
+ * const result = await syncNativeObjects(drizzleAdapter, registry, {
4692
+ * verbose: true,
4693
+ * tenantId: "default"
4694
+ * });
4695
+ *
4696
+ * if (result.success) {
4697
+ * console.log(`✓ Synced ${result.objectsSynced} objects`);
4698
+ * } else {
4699
+ * console.error("Sync errors:", result.errors);
4700
+ * }
4701
+ * ```
4702
+ */
4703
+ declare function syncNativeObjects(adapter: DatabaseAdapter, nativeRegistry: typeof registry, options?: SyncOptions): Promise<SyncResult>;
4704
+ /**
4705
+ * Verify that all native objects are synced to database
4706
+ *
4707
+ * @param adapter - Database adapter
4708
+ * @param nativeRegistry - Registry containing native objects
4709
+ * @returns true if all objects are synced, false otherwise
4710
+ *
4711
+ * @example
4712
+ * ```typescript
4713
+ * const isSynced = await verifyNativeObjectsSync(adapter, registry);
4714
+ * if (!isSynced) {
4715
+ * console.warn("Native objects not synced, running sync...");
4716
+ * await syncNativeObjects(adapter, registry);
4717
+ * }
4718
+ * ```
4719
+ */
4720
+ declare function verifyNativeObjectsSync(adapter: DatabaseAdapter, nativeRegistry: typeof registry): Promise<boolean>;
4721
+ /**
4722
+ * Get sync statistics without modifying database
4723
+ *
4724
+ * @param adapter - Database adapter
4725
+ * @param nativeRegistry - Registry containing native objects
4726
+ * @returns Sync result (dry run)
4727
+ */
4728
+ declare function getSyncPreview(adapter: DatabaseAdapter, nativeRegistry: typeof registry): Promise<SyncResult>;
4729
+ /**
4730
+ * Result of full sync operation (objects + views)
4731
+ */
4732
+ interface FullSyncResult {
4733
+ success: boolean;
4734
+ objects: SyncResult;
4735
+ views: ViewSyncResult;
4736
+ }
4737
+ /**
4738
+ * Options for full sync
4739
+ */
4740
+ interface FullSyncOptions extends SyncOptions, ViewSyncOptions {
4741
+ }
4742
+ /**
4743
+ * Sync all native objects and views from registries to database
4744
+ *
4745
+ * This is a convenience function that:
4746
+ * 1. Syncs native objects first (views may depend on objects)
4747
+ * 2. Syncs native views
4748
+ *
4749
+ * @param adapter - Database adapter
4750
+ * @param objectRegistry - Registry containing native objects
4751
+ * @param nativeViewRegistry - Registry containing native views
4752
+ * @param options - Sync options
4753
+ * @returns Combined sync result
4754
+ *
4755
+ * @example
4756
+ * ```typescript
4757
+ * import { syncAll, registry, viewRegistry } from "@stndrds/schema";
4758
+ * import { adapter } from "./db";
4759
+ *
4760
+ * const result = await syncAll(adapter, registry, viewRegistry, {
4761
+ * verbose: true,
4762
+ * tenantId: "default"
4763
+ * });
4764
+ *
4765
+ * if (result.success) {
4766
+ * console.log(`✓ Synced ${result.objects.objectsSynced} objects, ${result.views.viewsSynced} views`);
4767
+ * }
4768
+ * ```
4769
+ */
4770
+ declare function syncAll(adapter: DatabaseAdapter, objectRegistry: typeof registry, nativeViewRegistry: typeof viewRegistry, options?: FullSyncOptions): Promise<FullSyncResult>;
4771
+
4772
+ /**
4773
+ * Default fallback value when expression resolves to empty string
4774
+ */
4775
+ declare const DEFAULT_LABEL_FALLBACK = "(Untitled)";
4776
+ /**
4777
+ * Render a label expression template with values
4778
+ *
4779
+ * Supports:
4780
+ * - Variable interpolation: `{{ fieldName }}`
4781
+ * - Dot notation: `{{ user.firstName }}`
4782
+ * - Pipes: `{{ name | UPPER }}`, `{{ name | capitalize | trim }}`
4783
+ *
4784
+ * @param template - The label expression template (e.g., "{{ firstName }} {{ lastName }}")
4785
+ * @param values - Record values to interpolate
4786
+ * @param fallback - Fallback value if result is empty (default: "(Untitled)")
4787
+ * @returns The rendered label string
4788
+ *
4789
+ * @example
4790
+ * ```typescript
4791
+ * const label = renderLabelExpression(
4792
+ * "{{ firstName }} {{ lastName | UPPER }}",
4793
+ * { firstName: "John", lastName: "Doe" }
4794
+ * );
4795
+ * // → "John DOE"
4796
+ *
4797
+ * // With missing values
4798
+ * const label = renderLabelExpression(
4799
+ * "{{ name }}",
4800
+ * { }
4801
+ * );
4802
+ * // → "(Untitled)"
4803
+ * ```
4804
+ */
4805
+ declare function renderLabelExpression(template: string, values: Record<string, unknown>, fallback?: string): string;
4806
+ /**
4807
+ * Check if a string is a valid label expression template
4808
+ * A valid template contains at least one {{ variable }} block with a non-empty variable
4809
+ */
4810
+ declare function isLabelExpression(value: string): boolean;
4811
+ /**
4812
+ * Extract attribute names referenced in a label expression
4813
+ * Useful for validation or dependency tracking
4814
+ *
4815
+ * @example
4816
+ * extractAttributeNames("{{ firstName }} {{ lastName | UPPER }}")
4817
+ * // → ["firstName", "lastName"]
4818
+ */
4819
+ declare function extractAttributeNames(template: string): string[];
4820
+
4821
+ export { type StorageProvider as $, type Attribute as A, type BaseAttribute as B, type Currency as C, type DateAttribute as D, type DocumentTypeConfig as E, type FileAttribute as F, type Group as G, type FileVerificationConfig as H, type RelationAttribute as I, type AuditResourceType as J, type AuditAction as K, type Location as L, type MultiselectAttribute as M, type NumberAttribute as N, type ObjectAction as O, type Phone as P, type AuditActorType as Q, type RelationTarget as R, type StatusAttribute as S, type TextAttribute as T, type UserAttribute as U, type ViewDefinition as V, type AuditChange as W, type AuditLogEntry as X, type CreateAuditLogInput as Y, type AuditListOptions as Z, type AuditServiceOptions as _, type SelectAttribute as a, isCustomTab as a$, type FileVisibility as a0, type File as a1, type CreateFile as a2, type UpdateFile as a3, type TextFilterOperator as a4, type NumberFilterOperator as a5, type CheckboxFilterOperator as a6, type DateFilterOperator as a7, type SelectFilterOperator as a8, type MultiselectFilterOperator as a9, NoopGeocodingAdapter as aA, type Timestamps as aB, type ObjectAttribute as aC, type CompletionStatus as aD, type ObjectRecord as aE, type PermissionScope as aF, type Role as aG, type Permission as aH, type UserRoleAssignment as aI, type EffectivePermissions as aJ, type ObjectPermissions as aK, type SystemPermissions as aL, type CreateRoleInput as aM, type UpdateRoleInput as aN, type CreatePermissionInput as aO, type AssignRoleInput as aP, type UserRole as aQ, type UserStatus as aR, type UserProfile as aS, type CreateUserProfile as aT, type UpdateUserProfile as aU, type TabType as aV, type FormTab as aW, type TableTab as aX, type CustomTab as aY, isFormTab as aZ, isTableTab as a_, type RelationFilterOperator as aa, type FilterOperator as ab, type RelativeDateValue as ac, type CurrencyFilterValue as ad, type PhoneFilterValue as ae, type FilterValue as af, type FilterRule as ag, type FilterCombinator as ah, type FilterState as ai, type FilterGroup as aj, type AdvancedFilterState as ak, isAdvancedFilterState as al, toAdvancedFilterState as am, toSimpleFilterState as an, type SortDirection as ao, type SortRule as ap, type QueryState as aq, OPERATORS_BY_TYPE as ar, type NoValueOperator as as, NO_VALUE_OPERATORS as at, isNoValueOperator as au, type GeocodingSuggestion as av, type GeocodingAutocompleteParams as aw, type ReverseGeocodingParams as ax, type GeocodingParams as ay, type GeocodingAdapter as az, type SystemResource as b, type HookRegistry as b$, type Uuid as b0, type TenantId as b1, generateId as b2, generatePrefixedId as b3, registry as b4, viewRegistry as b5, textConfigSchema as b6, textareaConfigSchema as b7, numberConfigSchema as b8, checkboxConfigSchema as b9, createLocationValidator as bA, createTimestampValidator as bB, createFileValidator as bC, createUserValidator as bD, createSingleRelationValidator as bE, createMultiRelationValidator as bF, createRelationValidator as bG, createRatingValidator as bH, createAttributeValidator as bI, createObjectValidator as bJ, type ValidationResult as bK, validateAttribute as bL, validateObject as bM, validateObjectOrThrow as bN, createDraftValidator as bO, validateDraft as bP, validateDraftOrThrow as bQ, getMissingRequiredAttributes as bR, isRecordComplete as bS, computeRecordStatus as bT, type DatabaseAdapter as bU, type AttributeChange as bV, type HookContext as bW, type HookDefinition as bX, type HookHandler as bY, type HookType as bZ, NoopHookRegistry as b_, dateConfigSchema as ba, phoneConfigSchema as bb, currencyConfigSchema as bc, statusConfigSchema as bd, locationConfigSchema as be, timestampConfigSchema as bf, selectConfigSchema as bg, multiselectConfigSchema as bh, fileConfigSchema as bi, userConfigSchema as bj, relationConfigSchema as bk, ratingConfigSchema as bl, attributeConfigSchemas as bm, getAttributeConfigSchema as bn, validateAttributeConfig as bo, parseAttributeConfig as bp, safeParseAttributeConfig as bq, createTextValidator as br, createNumberValidator as bs, createCheckboxValidator as bt, createDateValidator as bu, createPhoneValidator as bv, createCurrencyValidator as bw, createStatusValidator as bx, createSelectValidator as by, createMultiselectValidator as bz, type SystemAction as c, type UpsertDBView as c$, createMockAdapter as c0, type ObjectsRepository as c1, type AttributesRepository as c2, type UserProfilesRepository as c3, type FilesRepository as c4, type ObjectRecordsRepository as c5, type ViewsRepository as c6, type AuditRepository as c7, type PermissionsRepository as c8, buildAuditChanges as c9, syncNativeObjects as cA, verifyNativeObjectsSync as cB, getSyncPreview as cC, type FullSyncResult as cD, type FullSyncOptions as cE, syncAll as cF, DEFAULT_LABEL_FALLBACK as cG, renderLabelExpression as cH, isLabelExpression as cI, extractAttributeNames as cJ, type DBObject as cK, type CreateDBObject as cL, type UpdateDBObject as cM, type UpsertDBObject as cN, type DBAttribute as cO, type CreateDBAttribute as cP, type UpdateDBAttribute as cQ, type UpsertDBAttribute as cR, type CreateObjectRecord as cS, type ListOptions as cT, type SearchOptions as cU, type GlobalSearchOptions as cV, type GlobalSearchResultItem as cW, type FileListOptions as cX, type DBView as cY, type CreateDBView as cZ, type UpdateDBView as c_, AuditService as ca, FileService as cb, GeocodingService as cc, GlobalSearchService as cd, type CreateCustomObjectInput as ce, type AddAttributeInput as cf, type UpdateObjectInput as cg, type ObjectSchemaServiceOptions as ch, ObjectSchemaService as ci, type PermissionServiceOptions as cj, PermissionService as ck, type RecordServiceOptions as cl, RecordService as cm, type RelationValidationResult as cn, type RelationValidationError as co, type RelationOption as cp, type RelationOptionsResponse as cq, type GetRelationOptionsParams as cr, RelationService as cs, type UserProfileServiceOptions as ct, UserProfileService as cu, type CreateViewInput as cv, type UpdateViewInput as cw, ViewService as cx, type SyncResult as cy, type SyncOptions as cz, type TextAreaAttribute as d, type OperationResult as d0, type ViewSyncResult as d1, type ViewSyncOptions as d2, syncNativeViews as d3, verifyNativeViewsSync as d4, getViewSyncPreview as d5, type CheckboxAttribute as e, type PhoneAttribute as f, type CurrencyAttribute as g, type Option as h, type LocationAttribute as i, type TimestampAttribute as j, type SingleRelationAttribute as k, type MultiRelationAttribute as l, type RatingAttribute as m, type ObjectDefinition as n, type Field as o, type AttributeGroupField as p, type Tab as q, type AttributeType as r, type StatusGroup as s, type AttributeGroup as t, type NumberUnit as u, type DateFormat as v, type DateValue as w, type LocationGranularity as x, type DocumentType as y, type DocumentFace as z };