@stndrds/schema 0.1.0-alpha.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/dist/chunk-3ABIOLKO.mjs +4624 -0
  2. package/dist/chunk-3IRX2BDY.mjs +3970 -0
  3. package/dist/chunk-4OTLPWSP.mjs +4448 -0
  4. package/dist/chunk-54ODQBUN.mjs +3867 -0
  5. package/dist/chunk-6HIA3FR2.mjs +4230 -0
  6. package/dist/chunk-6P3XHNLD.mjs +4171 -0
  7. package/dist/chunk-7LJECNAS.mjs +4167 -0
  8. package/dist/chunk-A2WNLUKV.mjs +4475 -0
  9. package/dist/chunk-AA3234RM.mjs +4475 -0
  10. package/dist/chunk-ADNXDLLD.mjs +3136 -0
  11. package/dist/chunk-B4DCDQUI.mjs +4695 -0
  12. package/dist/chunk-B4PSXY6I.mjs +4527 -0
  13. package/dist/chunk-BJHJBZCM.mjs +4228 -0
  14. package/dist/chunk-C3ZWTO6G.mjs +4474 -0
  15. package/dist/chunk-CLOGNKDD.mjs +4040 -0
  16. package/dist/chunk-EW4V63PA.mjs +3053 -0
  17. package/dist/chunk-FR2LCO6R.mjs +2485 -0
  18. package/dist/chunk-GJSF2RKL.mjs +2786 -0
  19. package/dist/chunk-GO5NPP2X.mjs +4726 -0
  20. package/dist/chunk-H3ZYJUDS.mjs +3050 -0
  21. package/dist/chunk-IDDLSFSK.mjs +4230 -0
  22. package/dist/chunk-JRADUJQ7.mjs +4726 -0
  23. package/dist/chunk-NEDR6N7R.mjs +3118 -0
  24. package/dist/chunk-NESPRMDF.mjs +2482 -0
  25. package/dist/chunk-NWFGRJBW.mjs +4612 -0
  26. package/dist/chunk-NXJ57GSN.mjs +2918 -0
  27. package/dist/chunk-O4C5FCTS.mjs +3961 -0
  28. package/dist/chunk-O4PRB27Q.mjs +2513 -0
  29. package/dist/chunk-OJDFNUCR.mjs +4463 -0
  30. package/dist/chunk-OSEKVBHC.mjs +4627 -0
  31. package/dist/chunk-QX4U6U6K.mjs +2484 -0
  32. package/dist/chunk-RMDQ6LKV.mjs +3046 -0
  33. package/dist/chunk-SBXKDATH.mjs +2673 -0
  34. package/dist/chunk-SKMP3AP3.mjs +2918 -0
  35. package/dist/chunk-STMS7WZD.mjs +2623 -0
  36. package/dist/chunk-T3T6PTFG.mjs +3057 -0
  37. package/dist/chunk-TPW72RAH.mjs +4624 -0
  38. package/dist/chunk-TUQEGNGV.mjs +2534 -0
  39. package/dist/chunk-TVKFS3YN.mjs +2449 -0
  40. package/dist/chunk-TZTH3BHG.mjs +3110 -0
  41. package/dist/chunk-W6MU3XPG.mjs +4612 -0
  42. package/dist/chunk-WL7Z3YFR.mjs +4603 -0
  43. package/dist/chunk-XNVVYHH3.mjs +4602 -0
  44. package/dist/chunk-XTA3WY64.mjs +2918 -0
  45. package/dist/chunk-YV7DEZFZ.mjs +2528 -0
  46. package/dist/chunk-ZLJPJND6.mjs +4474 -0
  47. package/dist/chunk-ZNZXTCEX.mjs +4234 -0
  48. package/dist/index.d.mts +1222 -0
  49. package/dist/index.d.ts +1222 -0
  50. package/dist/index.js +5320 -0
  51. package/dist/index.mjs +635 -0
  52. package/dist/runtime-2iKarXtl.d.mts +1924 -0
  53. package/dist/runtime-2iKarXtl.d.ts +1924 -0
  54. package/dist/runtime-4l2ddHZ7.d.mts +2242 -0
  55. package/dist/runtime-4l2ddHZ7.d.ts +2243 -0
  56. package/dist/runtime-8hKS6zAv.d.mts +2226 -0
  57. package/dist/runtime-8hKS6zAv.d.ts +2226 -0
  58. package/dist/runtime-B6yjtoR3.d.mts +4142 -0
  59. package/dist/runtime-B6yjtoR3.d.ts +4142 -0
  60. package/dist/runtime-BGCShrZB.d.mts +2001 -0
  61. package/dist/runtime-BGCShrZB.d.ts +2001 -0
  62. package/dist/runtime-BRW4NGAk.d.mts +1668 -0
  63. package/dist/runtime-BRW4NGAk.d.ts +1668 -0
  64. package/dist/runtime-BnfBr314.d.mts +1683 -0
  65. package/dist/runtime-BnfBr314.d.ts +1683 -0
  66. package/dist/runtime-BrqhiJUF.d.mts +4151 -0
  67. package/dist/runtime-BrqhiJUF.d.ts +4151 -0
  68. package/dist/runtime-Bvj8c-BE.d.mts +4277 -0
  69. package/dist/runtime-Bvj8c-BE.d.ts +4277 -0
  70. package/dist/runtime-C03qT4qU.d.mts +4267 -0
  71. package/dist/runtime-C03qT4qU.d.ts +4267 -0
  72. package/dist/runtime-CaCZ7mSf.d.mts +2974 -0
  73. package/dist/runtime-CaCZ7mSf.d.ts +2974 -0
  74. package/dist/runtime-CaIzNX0Z.d.mts +4091 -0
  75. package/dist/runtime-CaIzNX0Z.d.ts +4091 -0
  76. package/dist/runtime-ChPTgmLP.d.mts +4320 -0
  77. package/dist/runtime-ChPTgmLP.d.ts +4320 -0
  78. package/dist/runtime-CnImZ1Vv.d.mts +4267 -0
  79. package/dist/runtime-CnImZ1Vv.d.ts +4267 -0
  80. package/dist/runtime-Cp-B26Nj.d.mts +2890 -0
  81. package/dist/runtime-Cp-B26Nj.d.ts +2890 -0
  82. package/dist/runtime-Cq8jfk3c.d.mts +4277 -0
  83. package/dist/runtime-Cq8jfk3c.d.ts +4277 -0
  84. package/dist/runtime-CqDFXhhP.d.mts +1685 -0
  85. package/dist/runtime-CqDFXhhP.d.ts +1685 -0
  86. package/dist/runtime-CqtpZLdL.d.mts +4280 -0
  87. package/dist/runtime-CqtpZLdL.d.ts +4280 -0
  88. package/dist/runtime-D-4DblaZ.d.mts +4147 -0
  89. package/dist/runtime-D-4DblaZ.d.ts +4147 -0
  90. package/dist/runtime-D59sSqNl.d.mts +4146 -0
  91. package/dist/runtime-D59sSqNl.d.ts +4146 -0
  92. package/dist/runtime-DEfPk3wT.d.mts +1840 -0
  93. package/dist/runtime-DEfPk3wT.d.ts +1840 -0
  94. package/dist/runtime-DGV-7vES.d.mts +1685 -0
  95. package/dist/runtime-DGV-7vES.d.ts +1685 -0
  96. package/dist/runtime-DNacghqc.d.mts +3971 -0
  97. package/dist/runtime-DNacghqc.d.ts +3971 -0
  98. package/dist/runtime-DPLsUHYK.d.mts +3977 -0
  99. package/dist/runtime-DPLsUHYK.d.ts +3977 -0
  100. package/dist/runtime-DTxCw60F.d.mts +2973 -0
  101. package/dist/runtime-DTxCw60F.d.ts +2973 -0
  102. package/dist/runtime-DUXJ-zNS.d.mts +2788 -0
  103. package/dist/runtime-DUXJ-zNS.d.ts +2788 -0
  104. package/dist/runtime-DbljInhV.d.mts +2237 -0
  105. package/dist/runtime-DbljInhV.d.ts +2237 -0
  106. package/dist/runtime-DcH3y6Yu.d.mts +4272 -0
  107. package/dist/runtime-DcH3y6Yu.d.ts +4272 -0
  108. package/dist/runtime-DdBFz85g.d.mts +4229 -0
  109. package/dist/runtime-DdBFz85g.d.ts +4229 -0
  110. package/dist/runtime-DjMWhjkm.d.mts +4321 -0
  111. package/dist/runtime-DjMWhjkm.d.ts +4321 -0
  112. package/dist/runtime-Djz3fOBE.d.mts +2078 -0
  113. package/dist/runtime-Djz3fOBE.d.ts +2078 -0
  114. package/dist/runtime-Dli8g3Aq.d.mts +2082 -0
  115. package/dist/runtime-Dli8g3Aq.d.ts +2082 -0
  116. package/dist/runtime-a8J378im.d.mts +4123 -0
  117. package/dist/runtime-a8J378im.d.ts +4123 -0
  118. package/dist/runtime-dp3rb3Lf.d.mts +2968 -0
  119. package/dist/runtime-dp3rb3Lf.d.ts +2968 -0
  120. package/dist/runtime-iWXlSN_H.d.mts +1683 -0
  121. package/dist/runtime-iWXlSN_H.d.ts +1683 -0
  122. package/dist/runtime-yMql0npY.d.mts +4321 -0
  123. package/dist/runtime-yMql0npY.d.ts +4321 -0
  124. package/dist/runtime.d.mts +3 -0
  125. package/dist/runtime.d.ts +3 -0
  126. package/dist/runtime.js +3626 -0
  127. package/dist/runtime.mjs +48 -0
  128. package/package.json +58 -0
@@ -0,0 +1,2078 @@
1
+ import { ColorId, IconName, CountryIso3, CurrencyCode, MimeType } from '@andyoucreate/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
+ * Strategy when a target record is deleted
229
+ * - "restrict": Prevent deletion if relations exist (default)
230
+ * - "cascade": Delete this record too
231
+ * - "set_null": Set the relation value to null
232
+ */
233
+ type RelationOnDelete = "restrict" | "cascade" | "set_null";
234
+ /**
235
+ * Base properties shared by both single and multi relation attributes
236
+ */
237
+ interface RelationAttributeBase extends Omit<BaseAttribute<unknown>, "defaultValue"> {
238
+ type: "relation";
239
+ /** Target objects that can be linked */
240
+ targets: RelationTarget[];
241
+ /** Deletion strategy when target record is deleted */
242
+ onDelete?: RelationOnDelete;
243
+ }
244
+ /**
245
+ * Single relation attribute (one-to-one or many-to-one)
246
+ * Stores a single record ID or null
247
+ */
248
+ interface SingleRelationAttribute extends RelationAttributeBase {
249
+ cardinality: "one";
250
+ defaultValue?: string | null;
251
+ }
252
+ /**
253
+ * Multi relation attribute (one-to-many or many-to-many)
254
+ * Stores an array of record IDs
255
+ */
256
+ interface MultiRelationAttribute extends RelationAttributeBase {
257
+ cardinality: "many";
258
+ defaultValue?: string[];
259
+ /** Minimum number of relations required */
260
+ minItems?: number;
261
+ /** Maximum number of relations allowed */
262
+ maxItems?: number;
263
+ }
264
+ /**
265
+ * RelationAttribute links to other objects/records
266
+ * Discriminated union by cardinality for type-safe value handling
267
+ *
268
+ * @example Single relation (many-to-one)
269
+ * ```typescript
270
+ * relation({ name: "company", label: "Company" })
271
+ * .to("companies")
272
+ * .required()
273
+ * // → Value: "rec-uuid-123" | null
274
+ * ```
275
+ *
276
+ * @example Multi relation (many-to-many)
277
+ * ```typescript
278
+ * relation({ name: "contacts", label: "Contacts" })
279
+ * .to("contacts", { displayTemplate: "{firstName} {lastName}" })
280
+ * .many()
281
+ * .maxItems(5)
282
+ * // → Value: ["rec-1", "rec-2", ...]
283
+ * ```
284
+ *
285
+ * @example Polymorphic relation (multiple target objects)
286
+ * ```typescript
287
+ * relation({ name: "linked", label: "Linked Items" })
288
+ * .to("companies")
289
+ * .to("contacts")
290
+ * .to("deals")
291
+ * .many()
292
+ * // → Can link to records from any of these objects
293
+ * ```
294
+ */
295
+ type RelationAttribute = SingleRelationAttribute | MultiRelationAttribute;
296
+ interface TextAreaAttribute extends BaseAttribute<string> {
297
+ type: "textarea";
298
+ }
299
+ interface RatingAttribute extends BaseAttribute<number> {
300
+ type: "rating";
301
+ max?: number;
302
+ iconType?: "star" | "heart" | "thumbs" | "number";
303
+ }
304
+ type Attribute = TextAttribute | TextAreaAttribute | NumberAttribute | CheckboxAttribute | DateAttribute | PhoneAttribute | CurrencyAttribute | StatusAttribute | LocationAttribute | TimestampAttribute | SelectAttribute | MultiselectAttribute | FileAttribute | UserAttribute | RelationAttribute | RatingAttribute;
305
+
306
+ /**
307
+ * Timestamps for tracking creation and updates
308
+ */
309
+ interface Timestamps {
310
+ createdAt: Date;
311
+ updatedAt: Date;
312
+ }
313
+ /**
314
+ * Object definition - Represents a database table/entity
315
+ */
316
+ interface ObjectDefinition {
317
+ id?: Uuid;
318
+ name: string;
319
+ label: string;
320
+ description?: string;
321
+ icon?: IconName;
322
+ /**
323
+ * Name of the text attribute used as the object's title/display name.
324
+ * This attribute is used to represent the record in lists, relations, etc.
325
+ * Must reference an existing text attribute.
326
+ */
327
+ titleAttribute: string;
328
+ attributes: Attribute[];
329
+ system?: boolean;
330
+ metadata?: Record<string, unknown>;
331
+ }
332
+ /**
333
+ * Links an attribute to an object
334
+ */
335
+ interface ObjectAttribute {
336
+ objectId: Uuid;
337
+ attributeId: Uuid;
338
+ order?: number;
339
+ required?: boolean;
340
+ }
341
+ /**
342
+ * Completion status of a record based on data completeness.
343
+ *
344
+ * - `draft`: Record is missing one or more required attribute values.
345
+ * Can be saved but is considered incomplete.
346
+ * - `complete`: All required attribute values are present and valid.
347
+ * Record is ready for use.
348
+ *
349
+ * This is different from workflow status (e.g., "pending", "approved").
350
+ * Completion status is computed dynamically based on the object schema.
351
+ */
352
+ type CompletionStatus = "draft" | "complete";
353
+ /**
354
+ * Record - Instance of an Object (a row in the database)
355
+ */
356
+ interface ObjectRecord extends Timestamps {
357
+ id: Uuid;
358
+ objectId: Uuid;
359
+ /**
360
+ * Completion status of the record.
361
+ * - `draft`: Missing required values, record is incomplete
362
+ * - `complete`: All required values present and valid
363
+ *
364
+ * Computed dynamically based on the object's schema.
365
+ */
366
+ completionStatus: CompletionStatus;
367
+ values: Record<string, unknown>;
368
+ }
369
+
370
+ /**
371
+ * Storage provider type
372
+ */
373
+ type StorageProvider = "s3" | "gcs" | "azure" | "local" | "cloudflare-r2" | string;
374
+ /**
375
+ * File visibility level
376
+ */
377
+ type FileVisibility = "public" | "private" | "restricted";
378
+ /**
379
+ * File - Represents uploaded file metadata and storage info
380
+ *
381
+ * ARCHITECTURE:
382
+ * - Fixed table (no custom attributes)
383
+ * - Manages file storage, permissions, and metadata
384
+ * - uploadedBy links to user_profiles table
385
+ * - Supports soft delete via deletedAt
386
+ *
387
+ * @example
388
+ * ```typescript
389
+ * const file: File = {
390
+ * id: "file-123",
391
+ * tenantId: "tenant-456",
392
+ * name: "contract-2025.pdf",
393
+ * originalName: "Contract Acme Corp 2025.pdf",
394
+ * mimeType: "application/pdf",
395
+ * size: 2458624,
396
+ * storageProvider: "s3",
397
+ * storagePath: "tenants/456/files/2025/11/contract-2025.pdf",
398
+ * storageBucket: "my-app-files",
399
+ * url: "https://cdn.example.com/files/file-123",
400
+ * uploadedBy: "profile-789",
401
+ * folderPath: "/contracts/2025",
402
+ * tags: ["contract", "legal"],
403
+ * visibility: "restricted",
404
+ * allowedUsers: ["profile-789", "profile-456"],
405
+ * createdAt: new Date(),
406
+ * updatedAt: new Date(),
407
+ * };
408
+ * ```
409
+ */
410
+ interface File extends Timestamps {
411
+ id: Uuid;
412
+ tenantId: Uuid;
413
+ /**
414
+ * File name (sanitized for storage)
415
+ */
416
+ name: string;
417
+ /**
418
+ * Original file name (as uploaded by user)
419
+ */
420
+ originalName: string;
421
+ /**
422
+ * MIME type (e.g., "application/pdf", "image/jpeg")
423
+ */
424
+ mimeType: MimeType | string;
425
+ /**
426
+ * File size in bytes
427
+ */
428
+ size: number;
429
+ /**
430
+ * Storage provider (s3, gcs, azure, local, etc.)
431
+ */
432
+ storageProvider: StorageProvider;
433
+ /**
434
+ * Path in the storage bucket
435
+ */
436
+ storagePath: string;
437
+ /**
438
+ * Storage bucket name (if applicable)
439
+ */
440
+ storageBucket?: string;
441
+ /**
442
+ * Public or signed URL to access the file
443
+ */
444
+ url: string;
445
+ /**
446
+ * User who uploaded the file (FK to user_profiles)
447
+ */
448
+ uploadedBy: Uuid;
449
+ /**
450
+ * Folder path for organization (e.g., "/contracts/2025")
451
+ */
452
+ folderPath?: string;
453
+ /**
454
+ * Tags for search and categorization
455
+ */
456
+ tags?: string[];
457
+ /**
458
+ * File visibility level
459
+ * - public: Anyone with the URL can access
460
+ * - private: Only uploadedBy can access
461
+ * - restricted: Only specific users (allowedUsers) can access
462
+ */
463
+ visibility: FileVisibility;
464
+ /**
465
+ * List of user IDs authorized to access this file (if visibility = "restricted")
466
+ */
467
+ allowedUsers?: Uuid[];
468
+ /**
469
+ * Soft delete timestamp (null = not deleted)
470
+ */
471
+ deletedAt?: Date;
472
+ }
473
+ /**
474
+ * Data required to create a new file record
475
+ */
476
+ interface CreateFile {
477
+ tenantId: Uuid;
478
+ name: string;
479
+ originalName: string;
480
+ mimeType: MimeType | string;
481
+ size: number;
482
+ storageProvider: StorageProvider;
483
+ storagePath: string;
484
+ storageBucket?: string;
485
+ url: string;
486
+ uploadedBy: Uuid;
487
+ folderPath?: string;
488
+ tags?: string[];
489
+ visibility?: FileVisibility;
490
+ allowedUsers?: Uuid[];
491
+ }
492
+ /**
493
+ * Data for updating an existing file record
494
+ */
495
+ interface UpdateFile {
496
+ name?: string;
497
+ folderPath?: string;
498
+ tags?: string[];
499
+ visibility?: FileVisibility;
500
+ allowedUsers?: Uuid[];
501
+ }
502
+
503
+ /**
504
+ * User role in the application
505
+ * Can be extended with custom roles as needed
506
+ */
507
+ type UserRole = "admin" | "member" | "guest" | string;
508
+ /**
509
+ * User status for account management
510
+ */
511
+ type UserStatus = "active" | "pending" | "inactive" | "suspended";
512
+ /**
513
+ * User Profile - Represents application user data (authorization)
514
+ *
515
+ * ARCHITECTURE:
516
+ * - authId links to external auth provider (Supabase, Clerk, Auth0, etc.)
517
+ * - email is denormalized from auth provider for performance
518
+ * - Auth provider handles authentication (passwords, sessions, OAuth)
519
+ * - This type handles authorization (roles, permissions, tenant membership)
520
+ *
521
+ * @example
522
+ * ```typescript
523
+ * const profile: UserProfile = {
524
+ * id: "profile-123",
525
+ * tenantId: "tenant-456",
526
+ * authId: "supabase-auth-uuid-789",
527
+ * email: "john@example.com",
528
+ * firstName: "John",
529
+ * lastName: "Doe",
530
+ * role: "admin",
531
+ * status: "active",
532
+ * createdAt: new Date(),
533
+ * updatedAt: new Date(),
534
+ * };
535
+ * ```
536
+ */
537
+ interface UserProfile extends Timestamps {
538
+ id: Uuid;
539
+ tenantId: Uuid;
540
+ /**
541
+ * Link to external auth provider (Supabase auth.users.id, Clerk user ID, etc.)
542
+ * This is the bridge between authentication (provider) and authorization (your app)
543
+ */
544
+ authId: string;
545
+ /**
546
+ * Email address (denormalized from auth provider)
547
+ * Allows efficient querying and filtering without hitting auth provider API
548
+ */
549
+ email: string;
550
+ /**
551
+ * User's first name
552
+ */
553
+ firstName?: string;
554
+ /**
555
+ * User's last name
556
+ */
557
+ lastName?: string;
558
+ /**
559
+ * Avatar/profile picture URL
560
+ */
561
+ avatarUrl?: string;
562
+ /**
563
+ * User role for authorization
564
+ * Common values: "admin", "member", "guest"
565
+ * Can be extended with custom roles
566
+ */
567
+ role: UserRole;
568
+ /**
569
+ * Account status
570
+ * - active: Normal user, full access
571
+ * - pending: Awaiting activation/approval
572
+ * - inactive: Deactivated account
573
+ * - suspended: Temporarily blocked
574
+ */
575
+ status: UserStatus;
576
+ /**
577
+ * Last login timestamp (updated on each successful auth)
578
+ */
579
+ lastLoginAt?: Date;
580
+ }
581
+ /**
582
+ * Data required to create a new user profile
583
+ */
584
+ interface CreateUserProfile {
585
+ tenantId: Uuid;
586
+ authId: string;
587
+ email: string;
588
+ firstName?: string;
589
+ lastName?: string;
590
+ avatarUrl?: string;
591
+ role?: UserRole;
592
+ status?: UserStatus;
593
+ }
594
+ /**
595
+ * Data for updating an existing user profile
596
+ */
597
+ interface UpdateUserProfile {
598
+ firstName?: string;
599
+ lastName?: string;
600
+ avatarUrl?: string;
601
+ role?: UserRole;
602
+ status?: UserStatus;
603
+ lastLoginAt?: Date;
604
+ }
605
+
606
+ /**
607
+ * Registry for native objects defined in code
608
+ * Native objects are system objects that cannot be deleted/modified by clients
609
+ * They are defined by developers and synced to the database at startup
610
+ */
611
+ declare class NativeObjectRegistryClass {
612
+ private objects;
613
+ /**
614
+ * Register one or more native objects
615
+ * Auto-builds if ObjectBuilder is passed instead of ObjectDefinition
616
+ * @param objects - Single object/builder or array of objects/builders to register
617
+ * @returns this (for chaining)
618
+ * @throws Error if object is invalid or already registered
619
+ *
620
+ * @example
621
+ * ```typescript
622
+ * // No .build() needed - registry auto-builds!
623
+ * const PRODUCT = object({ id: "obj-product", name: "products", label: "Product" })
624
+ * .system()
625
+ * .attribute(text({ id: "attr-name", name: "name", label: "Name" }));
626
+ *
627
+ * registry.register(PRODUCT); // ← Auto-builds here
628
+ *
629
+ * // Can still explicitly .build() if you want
630
+ * registry.register(PRODUCT.build());
631
+ *
632
+ * // Register multiple
633
+ * registry.register([PRODUCT, ORDER, CUSTOMER]);
634
+ * ```
635
+ */
636
+ register(objects: ObjectDefinition | ObjectDefinition[] | {
637
+ build: () => ObjectDefinition;
638
+ } | Array<{
639
+ build: () => ObjectDefinition;
640
+ }>): this;
641
+ /**
642
+ * Validate and register a single object
643
+ * @internal
644
+ */
645
+ private validateAndRegister;
646
+ /**
647
+ * Get a native object by its name
648
+ * @param name - The object name
649
+ * @returns The object definition or undefined if not found
650
+ *
651
+ * @example
652
+ * ```typescript
653
+ * const product = registry.getByName("products");
654
+ * if (product) {
655
+ * console.log(product.label); // "Product"
656
+ * }
657
+ * ```
658
+ */
659
+ getByName(name: string): ObjectDefinition | undefined;
660
+ /**
661
+ * Get a native object or throw if not found
662
+ * @param name - The object name
663
+ * @returns The object definition
664
+ * @throws Error if not found
665
+ */
666
+ getByNameOrThrow(name: string): ObjectDefinition;
667
+ /**
668
+ * Get all registered native objects
669
+ * @returns Array of all registered object definitions
670
+ */
671
+ getAll(): ObjectDefinition[];
672
+ /**
673
+ * Get all registered native object names
674
+ * @returns Array of object names
675
+ */
676
+ listNames(): string[];
677
+ /**
678
+ * Check if a native object is registered
679
+ * @param name - The object name
680
+ * @returns true if registered, false otherwise
681
+ */
682
+ has(name: string): boolean;
683
+ /**
684
+ * Get the number of registered objects
685
+ */
686
+ get size(): number;
687
+ /**
688
+ * Clear all registered objects (useful for testing)
689
+ * @internal
690
+ */
691
+ clear(): void;
692
+ /**
693
+ * Get registry summary for debugging
694
+ */
695
+ summary(): string;
696
+ /**
697
+ * Log registry summary to console
698
+ */
699
+ debug(): void;
700
+ }
701
+ /**
702
+ * Singleton instance of the registry
703
+ * Use this to register and retrieve native objects
704
+ *
705
+ * @example
706
+ * ```typescript
707
+ * import { registry } from "@andyoucreate/schema";
708
+ *
709
+ * // Register objects
710
+ * registry.register([PRODUCT, ORDER, CUSTOMER]);
711
+ *
712
+ * // Get object by name
713
+ * const product = registry.getByName("products");
714
+ *
715
+ * // List all
716
+ * console.log(registry.listNames()); // ["products", "orders", ...]
717
+ *
718
+ * // Debug
719
+ * registry.debug();
720
+ * ```
721
+ */
722
+ declare const registry: NativeObjectRegistryClass;
723
+
724
+ /**
725
+ * Create a Zod schema for a text attribute
726
+ */
727
+ declare function createTextValidator(attr: TextAttribute): z.ZodString;
728
+ /**
729
+ * Create a Zod schema for a number attribute
730
+ */
731
+ declare function createNumberValidator(attr: NumberAttribute): z.ZodNumber;
732
+ /**
733
+ * Create a Zod schema for a checkbox attribute
734
+ */
735
+ declare function createCheckboxValidator(_attr: CheckboxAttribute): z.ZodBoolean;
736
+ /**
737
+ * Create a Zod schema for a date attribute
738
+ */
739
+ declare function createDateValidator(attr: DateAttribute): z.ZodString;
740
+ /**
741
+ * Create a Zod schema for a phone attribute
742
+ */
743
+ declare function createPhoneValidator(_attr: PhoneAttribute): z.ZodType<{
744
+ countryCode: string;
745
+ phoneNumber: string;
746
+ }>;
747
+ /**
748
+ * Create a Zod schema for a currency attribute
749
+ */
750
+ declare function createCurrencyValidator(_attr: CurrencyAttribute): z.ZodType<{
751
+ code: string;
752
+ value: number;
753
+ }>;
754
+ /**
755
+ * Create a Zod schema for a status attribute
756
+ */
757
+ declare function createStatusValidator(attr: StatusAttribute): z.ZodEnum<[string, ...string[]]>;
758
+ /**
759
+ * Create a Zod schema for a select attribute
760
+ */
761
+ declare function createSelectValidator(attr: SelectAttribute): z.ZodEnum<[string, ...string[]]>;
762
+ /**
763
+ * Create a Zod schema for a multiselect attribute
764
+ */
765
+ declare function createMultiselectValidator(attr: MultiselectAttribute): z.ZodArray<z.ZodEnum<[string, ...string[]]>>;
766
+ /**
767
+ * Create a Zod schema for a location attribute
768
+ */
769
+ type LocationShape = {
770
+ address?: string;
771
+ address2?: string;
772
+ city?: string;
773
+ state?: string;
774
+ postalCode?: string;
775
+ country?: string;
776
+ latitude?: number;
777
+ longitude?: number;
778
+ };
779
+ declare function createLocationValidator(_attr: LocationAttribute): z.ZodType<LocationShape>;
780
+ /**
781
+ * Create a Zod schema for a timestamp attribute
782
+ */
783
+ declare function createTimestampValidator(_attr: TimestampAttribute): z.ZodNumber;
784
+ /**
785
+ * Create a Zod schema for a file attribute
786
+ */
787
+ declare function createFileValidator(_attr: FileAttribute): z.ZodString;
788
+ /**
789
+ * Create a Zod schema for a user attribute
790
+ */
791
+ declare function createUserValidator(_attr: UserAttribute): z.ZodString;
792
+ /**
793
+ * Create a Zod schema for a single relation attribute (cardinality: "one")
794
+ */
795
+ declare function createSingleRelationValidator(attr: SingleRelationAttribute): z.ZodUnion<[z.ZodString, z.ZodNull]>;
796
+ /**
797
+ * Create a Zod schema for a multi relation attribute (cardinality: "many")
798
+ */
799
+ declare function createMultiRelationValidator(attr: MultiRelationAttribute): z.ZodArray<z.ZodString>;
800
+ /**
801
+ * Create a Zod schema for a relation attribute
802
+ * Dispatches to single or multi validator based on cardinality
803
+ */
804
+ declare function createRelationValidator(attr: RelationAttribute): z.ZodUnion<[z.ZodString, z.ZodNull]> | z.ZodArray<z.ZodString>;
805
+ /**
806
+ * Create a Zod schema for a rating attribute
807
+ */
808
+ declare function createRatingValidator(attr: RatingAttribute): z.ZodNumber;
809
+ /**
810
+ * Create a Zod schema for any attribute type
811
+ */
812
+ declare function createAttributeValidator(attr: Attribute): z.ZodTypeAny;
813
+ /**
814
+ * Create a Zod schema for an entire object
815
+ */
816
+ declare function createObjectValidator(objectDef: ObjectDefinition): z.ZodType<Record<string, unknown>>;
817
+ /**
818
+ * Validation result
819
+ */
820
+ interface ValidationResult {
821
+ success: boolean;
822
+ data?: Record<string, unknown>;
823
+ errors?: Array<{
824
+ path: string[];
825
+ message: string;
826
+ }>;
827
+ }
828
+ /**
829
+ * Validate data against an attribute schema
830
+ */
831
+ declare function validateAttribute(attr: Attribute, value: unknown): ValidationResult;
832
+ /**
833
+ * Validate data against an object schema
834
+ */
835
+ declare function validateObject(objectDef: ObjectDefinition, data: Record<string, unknown>): ValidationResult;
836
+ /**
837
+ * Validate and throw if invalid
838
+ */
839
+ declare function validateObjectOrThrow(objectDef: ObjectDefinition, data: Record<string, unknown>): Record<string, unknown>;
840
+ /**
841
+ * Create a Zod schema for draft validation.
842
+ * All attributes become optional, but provided values are still validated.
843
+ */
844
+ declare function createDraftValidator(objectDef: ObjectDefinition): z.ZodType<Record<string, unknown>>;
845
+ /**
846
+ * Validate data in draft mode.
847
+ * - All attributes are treated as optional (no required validation)
848
+ * - Provided values are still validated for format/type correctness
849
+ *
850
+ * Use this when creating records that may be incomplete (drafts).
851
+ *
852
+ * @example
853
+ * ```typescript
854
+ * const result = validateDraft(PRODUCT, { name: "Draft" });
855
+ * // → success even if "price" is required but missing
856
+ *
857
+ * const result2 = validateDraft(PRODUCT, { price: -10 });
858
+ * // → fails because price must be >= 0 (format validation still applies)
859
+ * ```
860
+ */
861
+ declare function validateDraft(objectDef: ObjectDefinition, data: Record<string, unknown>): ValidationResult;
862
+ /**
863
+ * Validate draft data and throw if format validation fails.
864
+ */
865
+ declare function validateDraftOrThrow(objectDef: ObjectDefinition, data: Record<string, unknown>): Record<string, unknown>;
866
+ /**
867
+ * Get the list of required attributes that are missing values.
868
+ *
869
+ * @example
870
+ * ```typescript
871
+ * const missing = getMissingRequiredAttributes(PRODUCT, { name: "Test" });
872
+ * // → [priceAttribute, statusAttribute] if price and status are required but missing
873
+ * ```
874
+ */
875
+ declare function getMissingRequiredAttributes(objectDef: ObjectDefinition, data: Record<string, unknown>): Attribute[];
876
+ /**
877
+ * Check if a record is complete (all required attributes have valid values).
878
+ *
879
+ * @returns `true` if all required values are present and valid, `false` otherwise
880
+ */
881
+ declare function isRecordComplete(objectDef: ObjectDefinition, data: Record<string, unknown>): boolean;
882
+ /**
883
+ * Compute the completion status of a record based on its data.
884
+ *
885
+ * - `"complete"`: All required values are present and valid
886
+ * - `"draft"`: One or more required values are missing or invalid
887
+ *
888
+ * This function is used to dynamically determine the status when
889
+ * creating or updating records.
890
+ *
891
+ * @example
892
+ * ```typescript
893
+ * const status = computeRecordStatus(PRODUCT, {
894
+ * name: "Nike Air Max",
895
+ * price: 129.99,
896
+ * status: "active"
897
+ * });
898
+ * // → "complete"
899
+ *
900
+ * const status2 = computeRecordStatus(PRODUCT, { name: "Draft Product" });
901
+ * // → "draft" (missing required fields)
902
+ * ```
903
+ */
904
+ declare function computeRecordStatus(objectDef: ObjectDefinition, data: Record<string, unknown>): CompletionStatus;
905
+
906
+ /**
907
+ * Object as stored in database (metadata)
908
+ */
909
+ interface DBObject extends Timestamps {
910
+ id: Uuid;
911
+ tenantId: TenantId;
912
+ name: string;
913
+ label: string;
914
+ description?: string;
915
+ icon?: IconName;
916
+ system: boolean;
917
+ metadata?: Record<string, unknown>;
918
+ }
919
+ interface CreateDBObject {
920
+ tenantId: TenantId;
921
+ name: string;
922
+ label: string;
923
+ description?: string;
924
+ icon?: IconName;
925
+ system?: boolean;
926
+ metadata?: Record<string, unknown>;
927
+ }
928
+ interface UpdateDBObject {
929
+ label?: string;
930
+ description?: string;
931
+ icon?: IconName;
932
+ metadata?: Record<string, unknown>;
933
+ }
934
+ interface UpsertDBObject extends CreateDBObject {
935
+ system: boolean;
936
+ }
937
+ /**
938
+ * Attribute as stored in database (metadata)
939
+ */
940
+ interface DBAttribute extends Timestamps {
941
+ id: Uuid;
942
+ objectId: Uuid;
943
+ name: string;
944
+ label: string;
945
+ type: AttributeType;
946
+ description?: string;
947
+ icon?: IconName;
948
+ system: boolean;
949
+ required: boolean;
950
+ unique: boolean;
951
+ order: number;
952
+ hidden?: boolean;
953
+ archived?: boolean;
954
+ deprecated?: boolean;
955
+ defaultValue?: unknown;
956
+ config: Record<string, unknown>;
957
+ }
958
+ interface CreateDBAttribute {
959
+ objectId: Uuid;
960
+ name: string;
961
+ label: string;
962
+ type: AttributeType;
963
+ description?: string;
964
+ icon?: IconName;
965
+ system?: boolean;
966
+ required?: boolean;
967
+ unique?: boolean;
968
+ order?: number;
969
+ hidden?: boolean;
970
+ archived?: boolean;
971
+ deprecated?: boolean;
972
+ defaultValue?: unknown;
973
+ config?: Record<string, unknown>;
974
+ }
975
+ interface UpdateDBAttribute {
976
+ label?: string;
977
+ description?: string;
978
+ icon?: IconName;
979
+ required?: boolean;
980
+ order?: number;
981
+ hidden?: boolean;
982
+ archived?: boolean;
983
+ deprecated?: boolean;
984
+ defaultValue?: unknown;
985
+ config?: Record<string, unknown>;
986
+ }
987
+ interface UpsertDBAttribute extends CreateDBAttribute {
988
+ objectId: Uuid;
989
+ name: string;
990
+ }
991
+
992
+ /**
993
+ * Object record creation data
994
+ */
995
+ interface CreateObjectRecord {
996
+ tenantId: TenantId;
997
+ objectId: Uuid;
998
+ data: Record<string, unknown>;
999
+ /**
1000
+ * Completion status of the record.
1001
+ * - `draft`: Record is incomplete (missing required values)
1002
+ * - `complete`: All required values are present and valid
1003
+ */
1004
+ completionStatus: "draft" | "complete";
1005
+ }
1006
+ /**
1007
+ * Generic list options for pagination and sorting
1008
+ */
1009
+ interface ListOptions {
1010
+ limit?: number;
1011
+ offset?: number;
1012
+ orderBy?: string;
1013
+ orderDirection?: "asc" | "desc";
1014
+ }
1015
+ /**
1016
+ * Search options for full-text search
1017
+ */
1018
+ interface SearchOptions extends ListOptions {
1019
+ filters?: Record<string, unknown>;
1020
+ highlight?: boolean;
1021
+ }
1022
+ /**
1023
+ * File-specific list options
1024
+ */
1025
+ interface FileListOptions extends ListOptions {
1026
+ folderPath?: string;
1027
+ mimeType?: string;
1028
+ uploadedBy?: Uuid;
1029
+ includeDeleted?: boolean;
1030
+ }
1031
+ /**
1032
+ * Result of an operation
1033
+ */
1034
+ interface OperationResult<T = unknown> {
1035
+ success: boolean;
1036
+ data?: T;
1037
+ error?: {
1038
+ code: string;
1039
+ message: string;
1040
+ details?: Record<string, unknown>;
1041
+ };
1042
+ }
1043
+
1044
+ /**
1045
+ * Repository for objects table (metadata)
1046
+ */
1047
+ interface ObjectsRepository {
1048
+ /**
1049
+ * Find object by ID
1050
+ */
1051
+ findById(id: Uuid): Promise<DBObject | null>;
1052
+ /**
1053
+ * Find object by name and tenant
1054
+ */
1055
+ findByName(tenantId: TenantId, name: string): Promise<DBObject | null>;
1056
+ /**
1057
+ * Find system/native object by name (system=true, for sync)
1058
+ */
1059
+ findSystemByName(name: string): Promise<DBObject | null>;
1060
+ /**
1061
+ * Create object
1062
+ */
1063
+ create(data: CreateDBObject): Promise<DBObject>;
1064
+ /**
1065
+ * Update object
1066
+ */
1067
+ update(id: Uuid, data: Partial<UpdateDBObject>): Promise<DBObject>;
1068
+ /**
1069
+ * Delete object
1070
+ */
1071
+ delete(id: Uuid): Promise<void>;
1072
+ /**
1073
+ * List all objects for a tenant
1074
+ */
1075
+ list(tenantId: TenantId): Promise<DBObject[]>;
1076
+ /**
1077
+ * Upsert object (create or update based on nativeObjectId)
1078
+ */
1079
+ upsert(data: UpsertDBObject): Promise<DBObject>;
1080
+ }
1081
+ /**
1082
+ * Repository for attributes table (metadata)
1083
+ */
1084
+ interface AttributesRepository {
1085
+ /**
1086
+ * Find attribute by ID
1087
+ */
1088
+ findById(id: Uuid): Promise<DBAttribute | null>;
1089
+ /**
1090
+ * Find attributes by object ID
1091
+ */
1092
+ findByObjectId(objectId: Uuid): Promise<DBAttribute[]>;
1093
+ /**
1094
+ * Create attribute
1095
+ */
1096
+ create(data: CreateDBAttribute): Promise<DBAttribute>;
1097
+ /**
1098
+ * Update attribute
1099
+ */
1100
+ update(id: Uuid, data: Partial<UpdateDBAttribute>): Promise<DBAttribute>;
1101
+ /**
1102
+ * Delete attribute
1103
+ */
1104
+ delete(id: Uuid): Promise<void>;
1105
+ /**
1106
+ * Delete multiple attributes by names (for sync cleanup)
1107
+ */
1108
+ deleteByNames(objectId: Uuid, excludeNames: string[]): Promise<number>;
1109
+ /**
1110
+ * Upsert attribute (create or update based on objectId + name)
1111
+ */
1112
+ upsert(data: UpsertDBAttribute): Promise<DBAttribute>;
1113
+ }
1114
+ /**
1115
+ * Repository for user_profiles table
1116
+ */
1117
+ interface UserProfilesRepository {
1118
+ /**
1119
+ * Find user profile by ID
1120
+ */
1121
+ findById(id: Uuid): Promise<UserProfile | null>;
1122
+ /**
1123
+ * Find user profile by auth ID (external auth provider)
1124
+ */
1125
+ findByAuthId(authId: string): Promise<UserProfile | null>;
1126
+ /**
1127
+ * Find user profile by email
1128
+ */
1129
+ findByEmail(tenantId: TenantId, email: string): Promise<UserProfile | null>;
1130
+ /**
1131
+ * Create user profile
1132
+ */
1133
+ create(data: CreateUserProfile): Promise<UserProfile>;
1134
+ /**
1135
+ * Update user profile
1136
+ */
1137
+ update(id: Uuid, data: UpdateUserProfile): Promise<UserProfile>;
1138
+ /**
1139
+ * Delete user profile
1140
+ */
1141
+ delete(id: Uuid): Promise<void>;
1142
+ /**
1143
+ * List all user profiles for a tenant
1144
+ */
1145
+ list(tenantId: TenantId, options?: ListOptions): Promise<UserProfile[]>;
1146
+ /**
1147
+ * Update last login timestamp
1148
+ */
1149
+ updateLastLogin(id: Uuid): Promise<void>;
1150
+ }
1151
+ /**
1152
+ * Repository for files table
1153
+ */
1154
+ interface FilesRepository {
1155
+ /**
1156
+ * Find file by ID
1157
+ */
1158
+ findById(id: Uuid): Promise<File | null>;
1159
+ /**
1160
+ * Create file
1161
+ */
1162
+ create(data: CreateFile): Promise<File>;
1163
+ /**
1164
+ * Update file
1165
+ */
1166
+ update(id: Uuid, data: UpdateFile): Promise<File>;
1167
+ /**
1168
+ * Delete file (soft delete)
1169
+ */
1170
+ delete(id: Uuid): Promise<void>;
1171
+ /**
1172
+ * Hard delete file (permanent)
1173
+ */
1174
+ hardDelete(id: Uuid): Promise<void>;
1175
+ /**
1176
+ * List files for a tenant
1177
+ */
1178
+ list(tenantId: TenantId, options?: FileListOptions): Promise<File[]>;
1179
+ /**
1180
+ * Find files by folder path
1181
+ */
1182
+ findByFolder(tenantId: TenantId, folderPath: string): Promise<File[]>;
1183
+ /**
1184
+ * Find files by uploader
1185
+ */
1186
+ findByUploader(uploadedBy: Uuid): Promise<File[]>;
1187
+ }
1188
+ /**
1189
+ * Repository for object_records table (unified JSONB)
1190
+ */
1191
+ interface ObjectRecordsRepository {
1192
+ /**
1193
+ * Find record by ID
1194
+ */
1195
+ findById(id: Uuid): Promise<ObjectRecord | null>;
1196
+ /**
1197
+ * Create record
1198
+ */
1199
+ create(data: CreateObjectRecord): Promise<ObjectRecord>;
1200
+ /**
1201
+ * Update record
1202
+ */
1203
+ update(id: Uuid, data: Partial<Record<string, unknown>>): Promise<ObjectRecord>;
1204
+ /**
1205
+ * Delete record
1206
+ */
1207
+ delete(id: Uuid): Promise<void>;
1208
+ /**
1209
+ * List records for an object
1210
+ */
1211
+ list(tenantId: TenantId, objectId: Uuid, options?: ListOptions): Promise<{
1212
+ records: ObjectRecord[];
1213
+ total: number;
1214
+ }>;
1215
+ /**
1216
+ * Search records using PostgreSQL full-text search
1217
+ */
1218
+ search(tenantId: TenantId, objectId: Uuid, query: string, options?: SearchOptions): Promise<{
1219
+ records: ObjectRecord[];
1220
+ total: number;
1221
+ }>;
1222
+ }
1223
+
1224
+ /**
1225
+ * Generic database adapter interface
1226
+ * Any ORM (Drizzle, Prisma, Kysely, TypeORM) can implement this interface
1227
+ * to work with @andyoucreate/schema runtime services
1228
+ *
1229
+ * @example
1230
+ * ```typescript
1231
+ * // Drizzle implementation
1232
+ * import { createDrizzleAdapter } from "./adapters/drizzle";
1233
+ * import { db } from "./db";
1234
+ *
1235
+ * const adapter = createDrizzleAdapter(db);
1236
+ *
1237
+ * // Prisma implementation
1238
+ * import { createPrismaAdapter } from "./adapters/prisma";
1239
+ * import { prisma } from "./db";
1240
+ *
1241
+ * const adapter = createPrismaAdapter(prisma);
1242
+ * ```
1243
+ */
1244
+ interface DatabaseAdapter {
1245
+ objects: ObjectsRepository;
1246
+ attributes: AttributesRepository;
1247
+ userProfiles: UserProfilesRepository;
1248
+ files: FilesRepository;
1249
+ objectRecords: ObjectRecordsRepository;
1250
+ transaction<T>(callback: (adapter: DatabaseAdapter) => Promise<T>): Promise<T>;
1251
+ }
1252
+
1253
+ interface InternalObjectRecord extends ObjectRecord {
1254
+ tenantId: TenantId;
1255
+ }
1256
+ interface MockStores {
1257
+ objects: Map<Uuid, DBObject>;
1258
+ attributes: Map<Uuid, DBAttribute>;
1259
+ userProfiles: Map<Uuid, UserProfile>;
1260
+ files: Map<Uuid, File>;
1261
+ objectRecords: Map<Uuid, InternalObjectRecord>;
1262
+ }
1263
+ /**
1264
+ * Create an in-memory mock adapter for testing and development
1265
+ *
1266
+ * This adapter stores all data in memory and is perfect for:
1267
+ * - Unit tests
1268
+ * - Integration tests
1269
+ * - Local development without a database
1270
+ * - Understanding the adapter interface
1271
+ *
1272
+ * @returns A DatabaseAdapter implementation using in-memory storage
1273
+ *
1274
+ * @example
1275
+ * ```typescript
1276
+ * import { createMockAdapter, RecordService } from "@andyoucreate/schema";
1277
+ *
1278
+ * // Create adapter
1279
+ * const adapter = createMockAdapter();
1280
+ *
1281
+ * // Use with services
1282
+ * const recordService = new RecordService(adapter, "tenant-123");
1283
+ *
1284
+ * // Create a record
1285
+ * const product = await recordService.createRecord("obj-product", {
1286
+ * name: "Nike Air Max",
1287
+ * price: 129.99
1288
+ * });
1289
+ *
1290
+ * // List records
1291
+ * const { records, total } = await recordService.listRecords("obj-product");
1292
+ * ```
1293
+ */
1294
+ declare function createMockAdapter(): DatabaseAdapter & {
1295
+ _stores: MockStores;
1296
+ reset(): void;
1297
+ };
1298
+
1299
+ /**
1300
+ * Service for managing files
1301
+ * Handles file metadata CRUD, permissions, and soft delete
1302
+ */
1303
+ declare class FileService {
1304
+ private adapter;
1305
+ private tenantId;
1306
+ constructor(adapter: DatabaseAdapter, tenantId: string);
1307
+ /**
1308
+ * Create a new file record (after upload to storage)
1309
+ *
1310
+ * @param data - File metadata
1311
+ * @returns Created file record
1312
+ *
1313
+ * @example
1314
+ * ```typescript
1315
+ * const service = new FileService(adapter, "tenant-123");
1316
+ *
1317
+ * // After uploading to S3
1318
+ * const file = await service.createFile({
1319
+ * tenantId: "tenant-123",
1320
+ * name: "contract-2025.pdf",
1321
+ * originalName: "Contract Acme Corp 2025.pdf",
1322
+ * mimeType: "application/pdf",
1323
+ * size: 2458624,
1324
+ * storageProvider: "s3",
1325
+ * storagePath: "tenants/123/files/2025/contract.pdf",
1326
+ * storageBucket: "my-app-files",
1327
+ * url: "https://cdn.example.com/files/file-123",
1328
+ * uploadedBy: "profile-456",
1329
+ * visibility: "private"
1330
+ * });
1331
+ * ```
1332
+ */
1333
+ createFile(data: CreateFile): Promise<File>;
1334
+ /**
1335
+ * Get file by ID
1336
+ */
1337
+ getFile(fileId: string): Promise<File | null>;
1338
+ /**
1339
+ * Get file by ID or throw
1340
+ */
1341
+ getFileOrThrow(fileId: string): Promise<File>;
1342
+ /**
1343
+ * Update file metadata
1344
+ *
1345
+ * @param fileId - File UUID
1346
+ * @param data - Data to update
1347
+ * @returns Updated file
1348
+ */
1349
+ updateFile(fileId: string, data: UpdateFile): Promise<File>;
1350
+ /**
1351
+ * Delete file (soft delete)
1352
+ *
1353
+ * @param fileId - File UUID
1354
+ * @param options - Delete options
1355
+ */
1356
+ deleteFile(fileId: string, options?: {
1357
+ hard?: boolean;
1358
+ checkOwnership?: boolean;
1359
+ userId?: string;
1360
+ }): Promise<void>;
1361
+ /**
1362
+ * List files for the tenant
1363
+ */
1364
+ listFiles(options?: FileListOptions): Promise<File[]>;
1365
+ /**
1366
+ * List files by folder
1367
+ */
1368
+ listFilesByFolder(folderPath: string): Promise<File[]>;
1369
+ /**
1370
+ * List files uploaded by a specific user
1371
+ */
1372
+ listFilesByUploader(uploadedBy: string): Promise<File[]>;
1373
+ /**
1374
+ * Change file visibility
1375
+ *
1376
+ * @param fileId - File UUID
1377
+ * @param visibility - New visibility level
1378
+ * @param allowedUsers - Users allowed to access (if restricted)
1379
+ */
1380
+ changeVisibility(fileId: string, visibility: "public" | "private" | "restricted", allowedUsers?: string[]): Promise<File>;
1381
+ /**
1382
+ * Grant access to a file for specific users
1383
+ *
1384
+ * @param fileId - File UUID
1385
+ * @param userIds - User IDs to grant access
1386
+ */
1387
+ grantAccess(fileId: string, userIds: string[]): Promise<File>;
1388
+ /**
1389
+ * Revoke access to a file for specific users
1390
+ *
1391
+ * @param fileId - File UUID
1392
+ * @param userIds - User IDs to revoke access
1393
+ */
1394
+ revokeAccess(fileId: string, userIds: string[]): Promise<File>;
1395
+ /**
1396
+ * Check if user has access to a file
1397
+ *
1398
+ * @param fileId - File UUID
1399
+ * @param userId - User ID to check
1400
+ * @returns true if user can access the file
1401
+ */
1402
+ canAccess(fileId: string, userId: string): Promise<boolean>;
1403
+ /**
1404
+ * Move file to different folder
1405
+ */
1406
+ moveToFolder(fileId: string, newFolderPath: string): Promise<File>;
1407
+ /**
1408
+ * Add tags to file
1409
+ */
1410
+ addTags(fileId: string, tags: string[]): Promise<File>;
1411
+ /**
1412
+ * Remove tags from file
1413
+ */
1414
+ removeTags(fileId: string, tags: string[]): Promise<File>;
1415
+ }
1416
+
1417
+ /**
1418
+ * Input for creating a custom object
1419
+ */
1420
+ interface CreateCustomObjectInput {
1421
+ name: string;
1422
+ label: string;
1423
+ description?: string;
1424
+ icon?: IconName;
1425
+ attributes?: (Attribute | {
1426
+ build: () => Attribute;
1427
+ })[];
1428
+ metadata?: Record<string, unknown>;
1429
+ }
1430
+ /**
1431
+ * Input for adding an attribute to an object
1432
+ */
1433
+ interface AddAttributeInput {
1434
+ name: string;
1435
+ label: string;
1436
+ type: AttributeType;
1437
+ required?: boolean;
1438
+ unique?: boolean;
1439
+ description?: string;
1440
+ placeholder?: string;
1441
+ icon?: IconName;
1442
+ defaultValue?: unknown;
1443
+ metadata?: Record<string, unknown>;
1444
+ [key: string]: unknown;
1445
+ }
1446
+ /**
1447
+ * Service for managing object schemas
1448
+ * Handles fusion of native objects (from registry) and custom objects (from database)
1449
+ */
1450
+ declare class ObjectSchemaService {
1451
+ private adapter;
1452
+ private nativeRegistry;
1453
+ constructor(adapter: DatabaseAdapter, nativeRegistry: typeof registry);
1454
+ /**
1455
+ * Create a new custom object
1456
+ * Validates name format and ensures system=false
1457
+ *
1458
+ * @param definition - Object definition (attributes can be raw definitions or builders)
1459
+ * @param tenantId - Tenant ID owning the object
1460
+ * @returns Created ObjectDefinition
1461
+ */
1462
+ createCustomObject(definition: CreateCustomObjectInput, tenantId: string): Promise<ObjectDefinition>;
1463
+ /**
1464
+ * Add a custom attribute to an existing object
1465
+ * Can be used on both custom and native objects
1466
+ * Attributes added via API are always custom (system=false)
1467
+ *
1468
+ * @param objectId - Object UUID from database
1469
+ * @param attribute - Attribute definition
1470
+ * @returns Created Attribute
1471
+ */
1472
+ addAttributeToObject(objectId: string, attribute: AddAttributeInput): Promise<Attribute>;
1473
+ /**
1474
+ * Update an attribute
1475
+ * Can only update custom attributes (system=false)
1476
+ *
1477
+ * @param attributeId - Attribute UUID
1478
+ * @param updates - Partial attribute updates
1479
+ * @returns Updated Attribute
1480
+ */
1481
+ updateAttribute(attributeId: string, updates: Partial<AddAttributeInput>): Promise<Attribute>;
1482
+ /**
1483
+ * Delete an attribute
1484
+ * Can only delete custom attributes (system=false)
1485
+ *
1486
+ * @param attributeId - Attribute UUID
1487
+ */
1488
+ deleteAttribute(attributeId: string): Promise<void>;
1489
+ /**
1490
+ * List attributes for an object
1491
+ *
1492
+ * @param objectId - Object UUID
1493
+ * @param options - Filter options
1494
+ * @returns List of attributes
1495
+ */
1496
+ listAttributes(objectId: string, options?: {
1497
+ systemOnly?: boolean;
1498
+ customOnly?: boolean;
1499
+ }): Promise<Attribute[]>;
1500
+ /**
1501
+ * Validate attribute name format (variable identifier)
1502
+ * @internal
1503
+ */
1504
+ private validateAttributeName;
1505
+ /**
1506
+ * Get complete object schema (system + custom attributes)
1507
+ * If object is native, merges registry definition with DB custom attributes
1508
+ * If object is custom, returns DB definition only
1509
+ *
1510
+ * @param objectId - Object UUID from database
1511
+ * @returns Complete ObjectDefinition with all attributes
1512
+ *
1513
+ * @example
1514
+ * ```typescript
1515
+ * const service = new ObjectSchemaService(adapter, registry);
1516
+ * const productSchema = await service.getObjectSchema("obj-123");
1517
+ *
1518
+ * console.log(productSchema.attributes); // System + custom attributes
1519
+ * ```
1520
+ */
1521
+ getObjectSchema(objectId: string): Promise<ObjectDefinition>;
1522
+ /**
1523
+ * Get object schema by name (for system/native objects)
1524
+ */
1525
+ getObjectSchemaByName(name: string): Promise<ObjectDefinition>;
1526
+ /**
1527
+ * List all object schemas for a tenant
1528
+ */
1529
+ listObjectSchemas(tenantId: string): Promise<ObjectDefinition[]>;
1530
+ /**
1531
+ * Merge native object from registry with custom attributes from DB
1532
+ * @internal
1533
+ */
1534
+ private mergeNativeObject;
1535
+ /**
1536
+ * Convert DB object + attributes to ObjectDefinition
1537
+ * @internal
1538
+ */
1539
+ private convertDBObjectToDefinition;
1540
+ /**
1541
+ * Convert DB attribute to Attribute type
1542
+ * @internal
1543
+ */
1544
+ private convertDBAttributeToAttribute;
1545
+ }
1546
+
1547
+ /**
1548
+ * Service for managing object records (CRUD operations)
1549
+ * Handles validation, dispatch to correct table, and data consistency
1550
+ */
1551
+ declare class RecordService {
1552
+ private adapter;
1553
+ private tenantId;
1554
+ private schemaService;
1555
+ private relationService;
1556
+ constructor(adapter: DatabaseAdapter, tenantId: string);
1557
+ /**
1558
+ * Create a new record with validation
1559
+ *
1560
+ * @param objectId - Object UUID
1561
+ * @param data - Record data (attribute values)
1562
+ * @param options - Creation options
1563
+ * @returns Created record with computed completionStatus
1564
+ *
1565
+ * @example
1566
+ * ```typescript
1567
+ * const service = new RecordService(adapter, "tenant-123");
1568
+ *
1569
+ * // Create a complete record (strict validation)
1570
+ * const product = await service.createRecord("obj-product", {
1571
+ * name: "Nike Air Max",
1572
+ * price: 129.99,
1573
+ * status: "active"
1574
+ * });
1575
+ * // → product.completionStatus = "complete"
1576
+ *
1577
+ * // Create a draft record (allows missing required fields)
1578
+ * const draft = await service.createRecord("obj-product", {
1579
+ * name: "Draft Product"
1580
+ * }, { allowDraft: true });
1581
+ * // → draft.completionStatus = "draft"
1582
+ * ```
1583
+ */
1584
+ createRecord(objectId: string, data: Record<string, unknown>, options?: {
1585
+ /**
1586
+ * Allow creating records with missing required fields.
1587
+ * Format validation still applies to provided values.
1588
+ * @default false
1589
+ */
1590
+ allowDraft?: boolean;
1591
+ /**
1592
+ * Skip all validation (format + relations).
1593
+ * @default true
1594
+ */
1595
+ validate?: boolean;
1596
+ /**
1597
+ * Skip relation validation only.
1598
+ * Useful for bulk imports where relations are validated separately.
1599
+ * @default false
1600
+ */
1601
+ skipRelationValidation?: boolean;
1602
+ skipSystemCheck?: boolean;
1603
+ }): Promise<ObjectRecord>;
1604
+ /**
1605
+ * Get a record by ID
1606
+ *
1607
+ * @param recordId - Record UUID
1608
+ * @param options - Query options
1609
+ * @returns Record or null if not found
1610
+ */
1611
+ getRecord(recordId: string, options?: {
1612
+ includeSchema?: boolean;
1613
+ }): Promise<ObjectRecord | null>;
1614
+ /**
1615
+ * Get a record by ID or throw if not found
1616
+ */
1617
+ getRecordOrThrow(recordId: string): Promise<ObjectRecord>;
1618
+ /**
1619
+ * Update a record with validation
1620
+ *
1621
+ * The completion status is automatically recalculated after each update.
1622
+ * A draft record becomes complete when all required fields are filled.
1623
+ *
1624
+ * @param recordId - Record UUID
1625
+ * @param data - Partial data to update
1626
+ * @param options - Update options
1627
+ * @returns Updated record with recalculated completionStatus
1628
+ *
1629
+ * @example
1630
+ * ```typescript
1631
+ * // Update a draft record to make it complete
1632
+ * const updated = await service.updateRecord(draftId, {
1633
+ * price: 99.99,
1634
+ * status: "active"
1635
+ * });
1636
+ * // → updated.completionStatus = "complete" if all required fields now present
1637
+ * ```
1638
+ */
1639
+ updateRecord(recordId: string, data: Partial<Record<string, unknown>>, options?: {
1640
+ /**
1641
+ * Skip validation entirely (format + relations).
1642
+ * @default true
1643
+ */
1644
+ validate?: boolean;
1645
+ /**
1646
+ * Allow partial updates without strict validation.
1647
+ * Format validation still applies to provided values.
1648
+ * @default false
1649
+ */
1650
+ partial?: boolean;
1651
+ /**
1652
+ * Skip relation validation only.
1653
+ * @default false
1654
+ */
1655
+ skipRelationValidation?: boolean;
1656
+ }): Promise<ObjectRecord>;
1657
+ /**
1658
+ * Delete a record
1659
+ *
1660
+ * @param recordId - Record UUID
1661
+ * @param options - Delete options
1662
+ */
1663
+ deleteRecord(recordId: string, options?: {
1664
+ checkSystem?: boolean;
1665
+ }): Promise<void>;
1666
+ /**
1667
+ * List records for an object with pagination
1668
+ *
1669
+ * @param objectId - Object UUID
1670
+ * @param options - List options
1671
+ * @returns Records and total count
1672
+ */
1673
+ listRecords(objectId: string, options?: {
1674
+ limit?: number;
1675
+ offset?: number;
1676
+ orderBy?: string;
1677
+ orderDirection?: "asc" | "desc";
1678
+ }): Promise<{
1679
+ records: ObjectRecord[];
1680
+ total: number;
1681
+ }>;
1682
+ /**
1683
+ * Search records using full-text search
1684
+ *
1685
+ * @param objectId - Object UUID
1686
+ * @param query - Search query
1687
+ * @param options - Search options
1688
+ * @returns Matching records and total count
1689
+ */
1690
+ searchRecords(objectId: string, query: string, options?: {
1691
+ limit?: number;
1692
+ offset?: number;
1693
+ filters?: Record<string, unknown>;
1694
+ }): Promise<{
1695
+ records: ObjectRecord[];
1696
+ total: number;
1697
+ }>;
1698
+ /**
1699
+ * Validate data against object schema without saving
1700
+ *
1701
+ * @param objectId - Object UUID
1702
+ * @param data - Data to validate
1703
+ * @returns Validation result
1704
+ */
1705
+ validateData(objectId: string, data: Record<string, unknown>): Promise<ValidationResult>;
1706
+ /**
1707
+ * Compute the completion status for given data without saving.
1708
+ * Useful for UI to show draft/complete status before submitting.
1709
+ *
1710
+ * @param objectId - Object UUID
1711
+ * @param data - Data to check
1712
+ * @returns Computed completion status
1713
+ */
1714
+ computeStatus(objectId: string, data: Record<string, unknown>): Promise<CompletionStatus>;
1715
+ /**
1716
+ * Refresh the completion status of an existing record.
1717
+ * Useful when schema changes and you need to recompute statuses.
1718
+ *
1719
+ * @param recordId - Record UUID
1720
+ * @returns Updated completion status
1721
+ */
1722
+ refreshRecordStatus(recordId: string): Promise<CompletionStatus>;
1723
+ }
1724
+
1725
+ /**
1726
+ * Result of relation validation
1727
+ */
1728
+ interface RelationValidationResult {
1729
+ valid: boolean;
1730
+ errors: RelationValidationError[];
1731
+ }
1732
+ /**
1733
+ * Individual relation validation error
1734
+ */
1735
+ interface RelationValidationError {
1736
+ /** Attribute name */
1737
+ attribute: string;
1738
+ /** Error message */
1739
+ message: string;
1740
+ /** Invalid record IDs */
1741
+ invalidIds?: string[];
1742
+ }
1743
+ /**
1744
+ * Resolved relation option
1745
+ */
1746
+ interface RelationOption {
1747
+ /** Record ID */
1748
+ id: string;
1749
+ /** Object ID */
1750
+ objectId: string;
1751
+ /** Object name */
1752
+ objectName: string;
1753
+ /** Display label (resolved from titleAttribute) */
1754
+ label: string;
1755
+ /** Additional record data */
1756
+ data?: Record<string, unknown>;
1757
+ }
1758
+ /**
1759
+ * Response for relation options
1760
+ */
1761
+ interface RelationOptionsResponse {
1762
+ options: RelationOption[];
1763
+ hasMore: boolean;
1764
+ total: number;
1765
+ }
1766
+ /**
1767
+ * Parameters for fetching relation options
1768
+ */
1769
+ interface GetRelationOptionsParams {
1770
+ /** Search query */
1771
+ query?: string;
1772
+ /** Page number (1-based) */
1773
+ page?: number;
1774
+ /** Page size */
1775
+ pageSize?: number;
1776
+ /** Filter by specific target object */
1777
+ targetObject?: string;
1778
+ }
1779
+ /**
1780
+ * Service for validating relation attributes
1781
+ * Ensures referenced records exist and belong to valid target objects
1782
+ */
1783
+ declare class RelationService {
1784
+ private adapter;
1785
+ private schemaService;
1786
+ constructor(adapter: DatabaseAdapter, nativeRegistry: typeof registry);
1787
+ /**
1788
+ * Validate all relation attributes in the data
1789
+ *
1790
+ * @param schema - Object schema containing attribute definitions
1791
+ * @param data - Record data to validate
1792
+ * @returns Validation result with errors if any
1793
+ *
1794
+ * @example
1795
+ * ```typescript
1796
+ * const result = await relationService.validateRelations(schema, {
1797
+ * company: "rec-123",
1798
+ * contacts: ["rec-456", "rec-789"]
1799
+ * });
1800
+ *
1801
+ * if (!result.valid) {
1802
+ * console.log(result.errors);
1803
+ * // [{ attribute: "company", message: "Record not found", invalidIds: ["rec-123"] }]
1804
+ * }
1805
+ * ```
1806
+ */
1807
+ validateRelations(schema: ObjectDefinition, data: Record<string, unknown>): Promise<RelationValidationResult>;
1808
+ /**
1809
+ * Validate a single relation attribute value
1810
+ */
1811
+ private validateRelationAttribute;
1812
+ /**
1813
+ * Extract IDs from relation value based on cardinality
1814
+ */
1815
+ private extractIds;
1816
+ /**
1817
+ * Get valid object IDs from relation targets
1818
+ */
1819
+ private getValidObjectIds;
1820
+ /**
1821
+ * Validate relations and throw if invalid
1822
+ */
1823
+ validateRelationsOrThrow(schema: ObjectDefinition, data: Record<string, unknown>): Promise<void>;
1824
+ /**
1825
+ * Get available options for a relation attribute
1826
+ * Searches across all target objects defined in the relation
1827
+ *
1828
+ * @param attribute - Relation attribute definition
1829
+ * @param tenantId - Tenant ID for multi-tenant isolation
1830
+ * @param params - Query parameters
1831
+ *
1832
+ * @example
1833
+ * ```typescript
1834
+ * const options = await relationService.getOptions(attribute, "tenant-1", {
1835
+ * query: "nike",
1836
+ * page: 1,
1837
+ * pageSize: 20
1838
+ * });
1839
+ * ```
1840
+ */
1841
+ getOptions(attribute: RelationAttribute, tenantId: string, params?: GetRelationOptionsParams): Promise<RelationOptionsResponse>;
1842
+ /**
1843
+ * Resolve record IDs to their display labels
1844
+ * Useful for displaying current values in the UI
1845
+ *
1846
+ * @param ids - Record IDs to resolve
1847
+ * @param tenantId - Tenant ID for multi-tenant isolation
1848
+ *
1849
+ * @example
1850
+ * ```typescript
1851
+ * const resolved = await relationService.resolveIds(["rec-1", "rec-2"], "tenant-1");
1852
+ * // [{ id: "rec-1", label: "Nike Air Max", objectName: "products", ... }]
1853
+ * ```
1854
+ */
1855
+ resolveIds(ids: string[], tenantId: string): Promise<RelationOption[]>;
1856
+ /**
1857
+ * Find a relation attribute by ID
1858
+ */
1859
+ findAttributeById(attributeId: string): Promise<RelationAttribute | null>;
1860
+ /**
1861
+ * Resolve display label from record values
1862
+ * Uses displayTemplate if provided, otherwise falls back to titleAttribute
1863
+ */
1864
+ private resolveLabel;
1865
+ }
1866
+
1867
+ /**
1868
+ * Service for managing user profiles
1869
+ * Handles user profile CRUD, auth provider sync, and role management
1870
+ */
1871
+ declare class UserProfileService {
1872
+ private adapter;
1873
+ private tenantId;
1874
+ constructor(adapter: DatabaseAdapter, tenantId: string);
1875
+ /**
1876
+ * Create a new user profile (typically after first auth)
1877
+ *
1878
+ * @param data - User profile creation data
1879
+ * @returns Created user profile
1880
+ *
1881
+ * @example
1882
+ * ```typescript
1883
+ * const service = new UserProfileService(adapter, "tenant-123");
1884
+ *
1885
+ * // After Supabase auth
1886
+ * const profile = await service.createProfile({
1887
+ * tenantId: "tenant-123",
1888
+ * authId: authUser.id,
1889
+ * email: authUser.email,
1890
+ * firstName: authUser.user_metadata.first_name,
1891
+ * lastName: authUser.user_metadata.last_name,
1892
+ * role: "member",
1893
+ * status: "active"
1894
+ * });
1895
+ * ```
1896
+ */
1897
+ createProfile(data: CreateUserProfile): Promise<UserProfile>;
1898
+ /**
1899
+ * Get user profile by ID
1900
+ */
1901
+ getProfile(profileId: string): Promise<UserProfile | null>;
1902
+ /**
1903
+ * Get user profile by ID or throw
1904
+ */
1905
+ getProfileOrThrow(profileId: string): Promise<UserProfile>;
1906
+ /**
1907
+ * Get user profile by auth provider ID
1908
+ *
1909
+ * @param authId - Auth provider user ID (Supabase, Clerk, etc.)
1910
+ * @returns User profile or null
1911
+ */
1912
+ getProfileByAuthId(authId: string): Promise<UserProfile | null>;
1913
+ /**
1914
+ * Get or create user profile (idempotent operation)
1915
+ * Useful for auth callbacks - ensures profile exists
1916
+ *
1917
+ * @param authId - Auth provider user ID
1918
+ * @param data - Profile data to create if doesn't exist
1919
+ * @returns Existing or newly created profile
1920
+ *
1921
+ * @example
1922
+ * ```typescript
1923
+ * // In Supabase auth callback
1924
+ * const profile = await service.getOrCreateProfile(
1925
+ * authUser.id,
1926
+ * {
1927
+ * tenantId: "tenant-123",
1928
+ * authId: authUser.id,
1929
+ * email: authUser.email,
1930
+ * role: "member",
1931
+ * status: "active"
1932
+ * }
1933
+ * );
1934
+ * ```
1935
+ */
1936
+ getOrCreateProfile(authId: string, data: CreateUserProfile): Promise<UserProfile>;
1937
+ /**
1938
+ * Update user profile
1939
+ */
1940
+ updateProfile(profileId: string, data: UpdateUserProfile): Promise<UserProfile>;
1941
+ /**
1942
+ * Delete user profile
1943
+ *
1944
+ * @param profileId - Profile UUID
1945
+ * @param options - Delete options
1946
+ */
1947
+ deleteProfile(profileId: string, options?: {
1948
+ checkAdmin?: boolean;
1949
+ }): Promise<void>;
1950
+ /**
1951
+ * List all user profiles for the tenant
1952
+ */
1953
+ listProfiles(options?: ListOptions): Promise<UserProfile[]>;
1954
+ /**
1955
+ * Update last login timestamp
1956
+ *
1957
+ * @param profileId - Profile UUID
1958
+ *
1959
+ * @example
1960
+ * ```typescript
1961
+ * // After successful auth
1962
+ * await service.updateLastLogin(profile.id);
1963
+ * ```
1964
+ */
1965
+ updateLastLogin(profileId: string): Promise<void>;
1966
+ /**
1967
+ * Change user role
1968
+ *
1969
+ * @param profileId - Profile UUID
1970
+ * @param newRole - New role
1971
+ */
1972
+ changeRole(profileId: string, newRole: string): Promise<UserProfile>;
1973
+ /**
1974
+ * Change user status
1975
+ *
1976
+ * @param profileId - Profile UUID
1977
+ * @param newStatus - New status
1978
+ */
1979
+ changeStatus(profileId: string, newStatus: "active" | "pending" | "inactive" | "suspended"): Promise<UserProfile>;
1980
+ /**
1981
+ * Get user by email
1982
+ */
1983
+ getProfileByEmail(email: string): Promise<UserProfile | null>;
1984
+ /**
1985
+ * Check if user has role
1986
+ */
1987
+ hasRole(profileId: string, role: string): Promise<boolean>;
1988
+ /**
1989
+ * Check if user is admin
1990
+ */
1991
+ isAdmin(profileId: string): Promise<boolean>;
1992
+ }
1993
+
1994
+ /**
1995
+ * Result of sync operation
1996
+ */
1997
+ interface SyncResult {
1998
+ success: boolean;
1999
+ objectsSynced: number;
2000
+ attributesSynced: number;
2001
+ objectsCreated: number;
2002
+ objectsUpdated: number;
2003
+ attributesCreated: number;
2004
+ attributesUpdated: number;
2005
+ attributesDeleted: number;
2006
+ errors: Array<{
2007
+ objectName: string;
2008
+ error: string;
2009
+ }>;
2010
+ }
2011
+ /**
2012
+ * Sync options
2013
+ */
2014
+ interface SyncOptions {
2015
+ dryRun?: boolean;
2016
+ verbose?: boolean;
2017
+ tenantId?: string;
2018
+ }
2019
+ /**
2020
+ * Sync native objects from registry to database
2021
+ *
2022
+ * This function:
2023
+ * 1. Reads all registered native objects from the registry
2024
+ * 2. Upserts them into the database (objects + attributes tables)
2025
+ * 3. Marks them as system=true for protection
2026
+ * 4. Removes attributes that were deleted from code
2027
+ *
2028
+ * @param adapter - Database adapter implementing DatabaseAdapter interface
2029
+ * @param nativeRegistry - Registry containing native objects
2030
+ * @param options - Sync options (dryRun, verbose, tenantId)
2031
+ * @returns Sync result with statistics
2032
+ *
2033
+ * @example
2034
+ * ```typescript
2035
+ * import { syncNativeObjects } from "@andyoucreate/schema/runtime";
2036
+ * import { registry } from "./objects/native";
2037
+ * import { drizzleAdapter } from "./db/adapter";
2038
+ *
2039
+ * const result = await syncNativeObjects(drizzleAdapter, registry, {
2040
+ * verbose: true,
2041
+ * tenantId: "default"
2042
+ * });
2043
+ *
2044
+ * if (result.success) {
2045
+ * console.log(`✓ Synced ${result.objectsSynced} objects`);
2046
+ * } else {
2047
+ * console.error("Sync errors:", result.errors);
2048
+ * }
2049
+ * ```
2050
+ */
2051
+ declare function syncNativeObjects(adapter: DatabaseAdapter, nativeRegistry: typeof registry, options?: SyncOptions): Promise<SyncResult>;
2052
+ /**
2053
+ * Verify that all native objects are synced to database
2054
+ *
2055
+ * @param adapter - Database adapter
2056
+ * @param nativeRegistry - Registry containing native objects
2057
+ * @returns true if all objects are synced, false otherwise
2058
+ *
2059
+ * @example
2060
+ * ```typescript
2061
+ * const isSynced = await verifyNativeObjectsSync(adapter, registry);
2062
+ * if (!isSynced) {
2063
+ * console.warn("Native objects not synced, running sync...");
2064
+ * await syncNativeObjects(adapter, registry);
2065
+ * }
2066
+ * ```
2067
+ */
2068
+ declare function verifyNativeObjectsSync(adapter: DatabaseAdapter, nativeRegistry: typeof registry): Promise<boolean>;
2069
+ /**
2070
+ * Get sync statistics without modifying database
2071
+ *
2072
+ * @param adapter - Database adapter
2073
+ * @param nativeRegistry - Registry containing native objects
2074
+ * @returns Sync result (dry run)
2075
+ */
2076
+ declare function getSyncPreview(adapter: DatabaseAdapter, nativeRegistry: typeof registry): Promise<SyncResult>;
2077
+
2078
+ export { type TenantId as $, type Attribute as A, type BaseAttribute as B, type Currency as C, type DateAttribute as D, type File as E, type FileAttribute as F, type CreateFile as G, type UpdateFile as H, type Timestamps as I, type ObjectAttribute as J, type CompletionStatus as K, type Location as L, type MultiselectAttribute as M, type NumberAttribute as N, type Option as O, type Phone as P, type ObjectRecord as Q, type RelationOnDelete as R, type StatusAttribute as S, type TextAttribute as T, type UserAttribute as U, type UserRole as V, type UserStatus as W, type UserProfile as X, type CreateUserProfile as Y, type UpdateUserProfile as Z, type Uuid as _, type SelectAttribute as a, type UpsertDBAttribute as a$, generateId as a0, generatePrefixedId as a1, registry as a2, createTextValidator as a3, createNumberValidator as a4, createCheckboxValidator as a5, createDateValidator as a6, createPhoneValidator as a7, createCurrencyValidator as a8, createStatusValidator as a9, type UserProfilesRepository as aA, type FilesRepository as aB, type ObjectRecordsRepository as aC, FileService as aD, type CreateCustomObjectInput as aE, type AddAttributeInput as aF, ObjectSchemaService as aG, RecordService as aH, type RelationValidationResult as aI, type RelationValidationError as aJ, type RelationOption as aK, type RelationOptionsResponse as aL, type GetRelationOptionsParams as aM, RelationService as aN, UserProfileService as aO, type SyncResult as aP, type SyncOptions as aQ, syncNativeObjects as aR, verifyNativeObjectsSync as aS, getSyncPreview as aT, type DBObject as aU, type CreateDBObject as aV, type UpdateDBObject as aW, type UpsertDBObject as aX, type DBAttribute as aY, type CreateDBAttribute as aZ, type UpdateDBAttribute as a_, createSelectValidator as aa, createMultiselectValidator as ab, createLocationValidator as ac, createTimestampValidator as ad, createFileValidator as ae, createUserValidator as af, createSingleRelationValidator as ag, createMultiRelationValidator as ah, createRelationValidator as ai, createRatingValidator as aj, createAttributeValidator as ak, createObjectValidator as al, type ValidationResult as am, validateAttribute as an, validateObject as ao, validateObjectOrThrow as ap, createDraftValidator as aq, validateDraft as ar, validateDraftOrThrow as as, getMissingRequiredAttributes as at, isRecordComplete as au, computeRecordStatus as av, type DatabaseAdapter as aw, createMockAdapter as ax, type ObjectsRepository as ay, type AttributesRepository as az, type TextAreaAttribute as b, type CreateObjectRecord as b0, type ListOptions as b1, type SearchOptions as b2, type FileListOptions as b3, type OperationResult as b4, type CheckboxAttribute as c, type PhoneAttribute as d, type CurrencyAttribute as e, type LocationAttribute as f, type TimestampAttribute as g, type SingleRelationAttribute as h, type MultiRelationAttribute as i, type RelationTarget as j, type RatingAttribute as k, type ObjectDefinition as l, type AttributeType as m, type StatusGroup as n, type AttributeGroup as o, type NumberUnit as p, type DateFormat as q, type DateValue as r, type LocationGranularity as s, type DocumentType as t, type DocumentFace as u, type DocumentTypeConfig as v, type FileVerificationConfig as w, type RelationAttribute as x, type StorageProvider as y, type FileVisibility as z };