@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,2242 @@
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
+ * Autocomplete suggestion from geocoding service
505
+ */
506
+ interface GeocodingSuggestion {
507
+ /** Unique identifier from the provider */
508
+ id: string;
509
+ /** Human-readable label for display */
510
+ label: string;
511
+ /** Structured location data */
512
+ location: Location;
513
+ /** Optional match score/confidence (0-1) */
514
+ score?: number;
515
+ /** Optional additional metadata from the provider */
516
+ metadata?: Record<string, unknown>;
517
+ }
518
+ /**
519
+ * Parameters for autocomplete search
520
+ */
521
+ interface GeocodingAutocompleteParams {
522
+ /** Search query */
523
+ query: string;
524
+ /** Restrict results to specific countries */
525
+ countries?: CountryIso3[];
526
+ /** Preferred language for results (ISO 639-1) */
527
+ language?: string;
528
+ /** Maximum number of results */
529
+ limit?: number;
530
+ /** Bias results toward a specific location */
531
+ biasLocation?: {
532
+ latitude: number;
533
+ longitude: number;
534
+ };
535
+ /** Session token for billing optimization (some providers) */
536
+ sessionToken?: string;
537
+ /**
538
+ * Level of address detail expected
539
+ * Adapters can use this to filter/optimize results
540
+ * - "full": Full address with street, city, postal code, etc.
541
+ * - "address": Street address without postal code
542
+ * - "city": City level only
543
+ * - "state": State/region level only
544
+ * - "country": Country level only
545
+ * - "coordinates": Only coordinates (lat/lng)
546
+ */
547
+ granularity?: LocationGranularity;
548
+ }
549
+ /**
550
+ * Parameters for reverse geocoding
551
+ */
552
+ interface ReverseGeocodingParams {
553
+ /** Latitude */
554
+ latitude: number;
555
+ /** Longitude */
556
+ longitude: number;
557
+ /** Preferred language for results */
558
+ language?: string;
559
+ }
560
+ /**
561
+ * Parameters for geocoding a structured address
562
+ */
563
+ interface GeocodingParams {
564
+ /** Structured address components */
565
+ address: Partial<Location>;
566
+ /** Preferred language for results */
567
+ language?: string;
568
+ }
569
+ /**
570
+ * Abstract geocoding adapter interface
571
+ * Implement this interface for different geocoding providers
572
+ *
573
+ * @example Google Maps implementation
574
+ * ```typescript
575
+ * class GoogleMapsGeocodingAdapter implements GeocodingAdapter {
576
+ * async autocomplete(params) {
577
+ * const response = await fetch(
578
+ * `https://maps.googleapis.com/maps/api/place/autocomplete/json?input=${params.query}&key=${this.apiKey}`
579
+ * );
580
+ * // Transform response to GeocodingSuggestion[]
581
+ * }
582
+ * }
583
+ * ```
584
+ *
585
+ * @example Mapbox implementation
586
+ * ```typescript
587
+ * class MapboxGeocodingAdapter implements GeocodingAdapter {
588
+ * async autocomplete(params) {
589
+ * const response = await fetch(
590
+ * `https://api.mapbox.com/geocoding/v5/mapbox.places/${params.query}.json?access_token=${this.token}`
591
+ * );
592
+ * // Transform response to GeocodingSuggestion[]
593
+ * }
594
+ * }
595
+ * ```
596
+ */
597
+ interface GeocodingAdapter {
598
+ /**
599
+ * Autocomplete address search
600
+ * Returns suggestions as the user types
601
+ */
602
+ autocomplete(params: GeocodingAutocompleteParams): Promise<GeocodingSuggestion[]>;
603
+ /**
604
+ * Reverse geocode coordinates to an address
605
+ * Optional - some providers may not support this
606
+ */
607
+ reverse?(params: ReverseGeocodingParams): Promise<GeocodingSuggestion | null>;
608
+ /**
609
+ * Geocode a structured address to coordinates
610
+ * Optional - some providers may not support this
611
+ */
612
+ geocode?(params: GeocodingParams): Promise<GeocodingSuggestion | null>;
613
+ }
614
+ /**
615
+ * No-op geocoding adapter
616
+ * Returns empty results when no geocoding provider is configured
617
+ */
618
+ declare class NoopGeocodingAdapter implements GeocodingAdapter {
619
+ autocomplete(): Promise<GeocodingSuggestion[]>;
620
+ reverse(): Promise<null>;
621
+ geocode(): Promise<null>;
622
+ }
623
+
624
+ /**
625
+ * User role in the application
626
+ * Can be extended with custom roles as needed
627
+ */
628
+ type UserRole = "admin" | "member" | "guest" | string;
629
+ /**
630
+ * User status for account management
631
+ */
632
+ type UserStatus = "active" | "pending" | "inactive" | "suspended";
633
+ /**
634
+ * User Profile - Represents application user data (authorization)
635
+ *
636
+ * ARCHITECTURE:
637
+ * - authId links to external auth provider (Supabase, Clerk, Auth0, etc.)
638
+ * - email is denormalized from auth provider for performance
639
+ * - Auth provider handles authentication (passwords, sessions, OAuth)
640
+ * - This type handles authorization (roles, permissions, tenant membership)
641
+ *
642
+ * @example
643
+ * ```typescript
644
+ * const profile: UserProfile = {
645
+ * id: "profile-123",
646
+ * tenantId: "tenant-456",
647
+ * authId: "supabase-auth-uuid-789",
648
+ * email: "john@example.com",
649
+ * firstName: "John",
650
+ * lastName: "Doe",
651
+ * role: "admin",
652
+ * status: "active",
653
+ * createdAt: new Date(),
654
+ * updatedAt: new Date(),
655
+ * };
656
+ * ```
657
+ */
658
+ interface UserProfile extends Timestamps {
659
+ id: Uuid;
660
+ tenantId: Uuid;
661
+ /**
662
+ * Link to external auth provider (Supabase auth.users.id, Clerk user ID, etc.)
663
+ * This is the bridge between authentication (provider) and authorization (your app)
664
+ */
665
+ authId: string;
666
+ /**
667
+ * Email address (denormalized from auth provider)
668
+ * Allows efficient querying and filtering without hitting auth provider API
669
+ */
670
+ email: string;
671
+ /**
672
+ * User's first name
673
+ */
674
+ firstName?: string;
675
+ /**
676
+ * User's last name
677
+ */
678
+ lastName?: string;
679
+ /**
680
+ * Avatar/profile picture URL
681
+ */
682
+ avatarUrl?: string;
683
+ /**
684
+ * User role for authorization
685
+ * Common values: "admin", "member", "guest"
686
+ * Can be extended with custom roles
687
+ */
688
+ role: UserRole;
689
+ /**
690
+ * Account status
691
+ * - active: Normal user, full access
692
+ * - pending: Awaiting activation/approval
693
+ * - inactive: Deactivated account
694
+ * - suspended: Temporarily blocked
695
+ */
696
+ status: UserStatus;
697
+ /**
698
+ * Last login timestamp (updated on each successful auth)
699
+ */
700
+ lastLoginAt?: Date;
701
+ }
702
+ /**
703
+ * Data required to create a new user profile
704
+ */
705
+ interface CreateUserProfile {
706
+ tenantId: Uuid;
707
+ authId: string;
708
+ email: string;
709
+ firstName?: string;
710
+ lastName?: string;
711
+ avatarUrl?: string;
712
+ role?: UserRole;
713
+ status?: UserStatus;
714
+ }
715
+ /**
716
+ * Data for updating an existing user profile
717
+ */
718
+ interface UpdateUserProfile {
719
+ firstName?: string;
720
+ lastName?: string;
721
+ avatarUrl?: string;
722
+ role?: UserRole;
723
+ status?: UserStatus;
724
+ lastLoginAt?: Date;
725
+ }
726
+
727
+ /**
728
+ * Registry for native objects defined in code
729
+ * Native objects are system objects that cannot be deleted/modified by clients
730
+ * They are defined by developers and synced to the database at startup
731
+ */
732
+ declare class NativeObjectRegistryClass {
733
+ private objects;
734
+ /**
735
+ * Register one or more native objects
736
+ * Auto-builds if ObjectBuilder is passed instead of ObjectDefinition
737
+ * @param objects - Single object/builder or array of objects/builders to register
738
+ * @returns this (for chaining)
739
+ * @throws Error if object is invalid or already registered
740
+ *
741
+ * @example
742
+ * ```typescript
743
+ * // No .build() needed - registry auto-builds!
744
+ * const PRODUCT = object({ id: "obj-product", name: "products", label: "Product" })
745
+ * .system()
746
+ * .attribute(text({ id: "attr-name", name: "name", label: "Name" }));
747
+ *
748
+ * registry.register(PRODUCT); // ← Auto-builds here
749
+ *
750
+ * // Can still explicitly .build() if you want
751
+ * registry.register(PRODUCT.build());
752
+ *
753
+ * // Register multiple
754
+ * registry.register([PRODUCT, ORDER, CUSTOMER]);
755
+ * ```
756
+ */
757
+ register(objects: ObjectDefinition | ObjectDefinition[] | {
758
+ build: () => ObjectDefinition;
759
+ } | Array<{
760
+ build: () => ObjectDefinition;
761
+ }>): this;
762
+ /**
763
+ * Validate and register a single object
764
+ * @internal
765
+ */
766
+ private validateAndRegister;
767
+ /**
768
+ * Get a native object by its name
769
+ * @param name - The object name
770
+ * @returns The object definition or undefined if not found
771
+ *
772
+ * @example
773
+ * ```typescript
774
+ * const product = registry.getByName("products");
775
+ * if (product) {
776
+ * console.log(product.label); // "Product"
777
+ * }
778
+ * ```
779
+ */
780
+ getByName(name: string): ObjectDefinition | undefined;
781
+ /**
782
+ * Get a native object or throw if not found
783
+ * @param name - The object name
784
+ * @returns The object definition
785
+ * @throws Error if not found
786
+ */
787
+ getByNameOrThrow(name: string): ObjectDefinition;
788
+ /**
789
+ * Get all registered native objects
790
+ * @returns Array of all registered object definitions
791
+ */
792
+ getAll(): ObjectDefinition[];
793
+ /**
794
+ * Get all registered native object names
795
+ * @returns Array of object names
796
+ */
797
+ listNames(): string[];
798
+ /**
799
+ * Check if a native object is registered
800
+ * @param name - The object name
801
+ * @returns true if registered, false otherwise
802
+ */
803
+ has(name: string): boolean;
804
+ /**
805
+ * Get the number of registered objects
806
+ */
807
+ get size(): number;
808
+ /**
809
+ * Clear all registered objects (useful for testing)
810
+ * @internal
811
+ */
812
+ clear(): void;
813
+ /**
814
+ * Get registry summary for debugging
815
+ */
816
+ summary(): string;
817
+ /**
818
+ * Log registry summary to console
819
+ */
820
+ debug(): void;
821
+ }
822
+ /**
823
+ * Singleton instance of the registry
824
+ * Use this to register and retrieve native objects
825
+ *
826
+ * @example
827
+ * ```typescript
828
+ * import { registry } from "@andyoucreate/schema";
829
+ *
830
+ * // Register objects
831
+ * registry.register([PRODUCT, ORDER, CUSTOMER]);
832
+ *
833
+ * // Get object by name
834
+ * const product = registry.getByName("products");
835
+ *
836
+ * // List all
837
+ * console.log(registry.listNames()); // ["products", "orders", ...]
838
+ *
839
+ * // Debug
840
+ * registry.debug();
841
+ * ```
842
+ */
843
+ declare const registry: NativeObjectRegistryClass;
844
+
845
+ /**
846
+ * Create a Zod schema for a text attribute
847
+ */
848
+ declare function createTextValidator(attr: TextAttribute): z.ZodString;
849
+ /**
850
+ * Create a Zod schema for a number attribute
851
+ */
852
+ declare function createNumberValidator(attr: NumberAttribute): z.ZodNumber;
853
+ /**
854
+ * Create a Zod schema for a checkbox attribute
855
+ */
856
+ declare function createCheckboxValidator(_attr: CheckboxAttribute): z.ZodBoolean;
857
+ /**
858
+ * Create a Zod schema for a date attribute
859
+ */
860
+ declare function createDateValidator(attr: DateAttribute): z.ZodString;
861
+ /**
862
+ * Create a Zod schema for a phone attribute
863
+ */
864
+ declare function createPhoneValidator(_attr: PhoneAttribute): z.ZodType<{
865
+ countryCode: string;
866
+ phoneNumber: string;
867
+ }>;
868
+ /**
869
+ * Create a Zod schema for a currency attribute
870
+ */
871
+ declare function createCurrencyValidator(_attr: CurrencyAttribute): z.ZodType<{
872
+ code: string;
873
+ value: number;
874
+ }>;
875
+ /**
876
+ * Create a Zod schema for a status attribute
877
+ */
878
+ declare function createStatusValidator(attr: StatusAttribute): z.ZodEnum<[string, ...string[]]>;
879
+ /**
880
+ * Create a Zod schema for a select attribute
881
+ */
882
+ declare function createSelectValidator(attr: SelectAttribute): z.ZodEnum<[string, ...string[]]>;
883
+ /**
884
+ * Create a Zod schema for a multiselect attribute
885
+ */
886
+ declare function createMultiselectValidator(attr: MultiselectAttribute): z.ZodArray<z.ZodEnum<[string, ...string[]]>>;
887
+ /**
888
+ * Create a Zod schema for a location attribute
889
+ */
890
+ type LocationShape = {
891
+ address?: string;
892
+ address2?: string;
893
+ city?: string;
894
+ state?: string;
895
+ postalCode?: string;
896
+ country?: string;
897
+ latitude?: number;
898
+ longitude?: number;
899
+ };
900
+ declare function createLocationValidator(_attr: LocationAttribute): z.ZodType<LocationShape>;
901
+ /**
902
+ * Create a Zod schema for a timestamp attribute
903
+ */
904
+ declare function createTimestampValidator(_attr: TimestampAttribute): z.ZodNumber;
905
+ /**
906
+ * Create a Zod schema for a file attribute
907
+ */
908
+ declare function createFileValidator(_attr: FileAttribute): z.ZodString;
909
+ /**
910
+ * Create a Zod schema for a user attribute
911
+ */
912
+ declare function createUserValidator(_attr: UserAttribute): z.ZodString;
913
+ /**
914
+ * Create a Zod schema for a single relation attribute (cardinality: "one")
915
+ */
916
+ declare function createSingleRelationValidator(attr: SingleRelationAttribute): z.ZodUnion<[z.ZodString, z.ZodNull]>;
917
+ /**
918
+ * Create a Zod schema for a multi relation attribute (cardinality: "many")
919
+ */
920
+ declare function createMultiRelationValidator(attr: MultiRelationAttribute): z.ZodArray<z.ZodString>;
921
+ /**
922
+ * Create a Zod schema for a relation attribute
923
+ * Dispatches to single or multi validator based on cardinality
924
+ */
925
+ declare function createRelationValidator(attr: RelationAttribute): z.ZodUnion<[z.ZodString, z.ZodNull]> | z.ZodArray<z.ZodString>;
926
+ /**
927
+ * Create a Zod schema for a rating attribute
928
+ */
929
+ declare function createRatingValidator(attr: RatingAttribute): z.ZodNumber;
930
+ /**
931
+ * Create a Zod schema for any attribute type
932
+ */
933
+ declare function createAttributeValidator(attr: Attribute): z.ZodTypeAny;
934
+ /**
935
+ * Create a Zod schema for an entire object
936
+ */
937
+ declare function createObjectValidator(objectDef: ObjectDefinition): z.ZodType<Record<string, unknown>>;
938
+ /**
939
+ * Validation result
940
+ */
941
+ interface ValidationResult {
942
+ success: boolean;
943
+ data?: Record<string, unknown>;
944
+ errors?: Array<{
945
+ path: string[];
946
+ message: string;
947
+ }>;
948
+ }
949
+ /**
950
+ * Validate data against an attribute schema
951
+ */
952
+ declare function validateAttribute(attr: Attribute, value: unknown): ValidationResult;
953
+ /**
954
+ * Validate data against an object schema
955
+ */
956
+ declare function validateObject(objectDef: ObjectDefinition, data: Record<string, unknown>): ValidationResult;
957
+ /**
958
+ * Validate and throw if invalid
959
+ */
960
+ declare function validateObjectOrThrow(objectDef: ObjectDefinition, data: Record<string, unknown>): Record<string, unknown>;
961
+ /**
962
+ * Create a Zod schema for draft validation.
963
+ * All attributes become optional, but provided values are still validated.
964
+ */
965
+ declare function createDraftValidator(objectDef: ObjectDefinition): z.ZodType<Record<string, unknown>>;
966
+ /**
967
+ * Validate data in draft mode.
968
+ * - All attributes are treated as optional (no required validation)
969
+ * - Provided values are still validated for format/type correctness
970
+ *
971
+ * Use this when creating records that may be incomplete (drafts).
972
+ *
973
+ * @example
974
+ * ```typescript
975
+ * const result = validateDraft(PRODUCT, { name: "Draft" });
976
+ * // → success even if "price" is required but missing
977
+ *
978
+ * const result2 = validateDraft(PRODUCT, { price: -10 });
979
+ * // → fails because price must be >= 0 (format validation still applies)
980
+ * ```
981
+ */
982
+ declare function validateDraft(objectDef: ObjectDefinition, data: Record<string, unknown>): ValidationResult;
983
+ /**
984
+ * Validate draft data and throw if format validation fails.
985
+ */
986
+ declare function validateDraftOrThrow(objectDef: ObjectDefinition, data: Record<string, unknown>): Record<string, unknown>;
987
+ /**
988
+ * Get the list of required attributes that are missing values.
989
+ *
990
+ * @example
991
+ * ```typescript
992
+ * const missing = getMissingRequiredAttributes(PRODUCT, { name: "Test" });
993
+ * // → [priceAttribute, statusAttribute] if price and status are required but missing
994
+ * ```
995
+ */
996
+ declare function getMissingRequiredAttributes(objectDef: ObjectDefinition, data: Record<string, unknown>): Attribute[];
997
+ /**
998
+ * Check if a record is complete (all required attributes have valid values).
999
+ *
1000
+ * @returns `true` if all required values are present and valid, `false` otherwise
1001
+ */
1002
+ declare function isRecordComplete(objectDef: ObjectDefinition, data: Record<string, unknown>): boolean;
1003
+ /**
1004
+ * Compute the completion status of a record based on its data.
1005
+ *
1006
+ * - `"complete"`: All required values are present and valid
1007
+ * - `"draft"`: One or more required values are missing or invalid
1008
+ *
1009
+ * This function is used to dynamically determine the status when
1010
+ * creating or updating records.
1011
+ *
1012
+ * @example
1013
+ * ```typescript
1014
+ * const status = computeRecordStatus(PRODUCT, {
1015
+ * name: "Nike Air Max",
1016
+ * price: 129.99,
1017
+ * status: "active"
1018
+ * });
1019
+ * // → "complete"
1020
+ *
1021
+ * const status2 = computeRecordStatus(PRODUCT, { name: "Draft Product" });
1022
+ * // → "draft" (missing required fields)
1023
+ * ```
1024
+ */
1025
+ declare function computeRecordStatus(objectDef: ObjectDefinition, data: Record<string, unknown>): CompletionStatus;
1026
+
1027
+ /**
1028
+ * Object as stored in database (metadata)
1029
+ */
1030
+ interface DBObject extends Timestamps {
1031
+ id: Uuid;
1032
+ tenantId: TenantId;
1033
+ name: string;
1034
+ label: string;
1035
+ description?: string;
1036
+ icon?: IconName;
1037
+ system: boolean;
1038
+ metadata?: Record<string, unknown>;
1039
+ }
1040
+ interface CreateDBObject {
1041
+ tenantId: TenantId;
1042
+ name: string;
1043
+ label: string;
1044
+ description?: string;
1045
+ icon?: IconName;
1046
+ system?: boolean;
1047
+ metadata?: Record<string, unknown>;
1048
+ }
1049
+ interface UpdateDBObject {
1050
+ label?: string;
1051
+ description?: string;
1052
+ icon?: IconName;
1053
+ metadata?: Record<string, unknown>;
1054
+ }
1055
+ interface UpsertDBObject extends CreateDBObject {
1056
+ system: boolean;
1057
+ }
1058
+ /**
1059
+ * Attribute as stored in database (metadata)
1060
+ */
1061
+ interface DBAttribute extends Timestamps {
1062
+ id: Uuid;
1063
+ objectId: Uuid;
1064
+ name: string;
1065
+ label: string;
1066
+ type: AttributeType;
1067
+ description?: string;
1068
+ icon?: IconName;
1069
+ system: boolean;
1070
+ required: boolean;
1071
+ unique: boolean;
1072
+ order: number;
1073
+ hidden?: boolean;
1074
+ archived?: boolean;
1075
+ deprecated?: boolean;
1076
+ defaultValue?: unknown;
1077
+ config: Record<string, unknown>;
1078
+ }
1079
+ interface CreateDBAttribute {
1080
+ objectId: Uuid;
1081
+ name: string;
1082
+ label: string;
1083
+ type: AttributeType;
1084
+ description?: string;
1085
+ icon?: IconName;
1086
+ system?: boolean;
1087
+ required?: boolean;
1088
+ unique?: boolean;
1089
+ order?: number;
1090
+ hidden?: boolean;
1091
+ archived?: boolean;
1092
+ deprecated?: boolean;
1093
+ defaultValue?: unknown;
1094
+ config?: Record<string, unknown>;
1095
+ }
1096
+ interface UpdateDBAttribute {
1097
+ label?: string;
1098
+ description?: string;
1099
+ icon?: IconName;
1100
+ required?: boolean;
1101
+ order?: number;
1102
+ hidden?: boolean;
1103
+ archived?: boolean;
1104
+ deprecated?: boolean;
1105
+ defaultValue?: unknown;
1106
+ config?: Record<string, unknown>;
1107
+ }
1108
+ interface UpsertDBAttribute extends CreateDBAttribute {
1109
+ objectId: Uuid;
1110
+ name: string;
1111
+ }
1112
+
1113
+ /**
1114
+ * Object record creation data
1115
+ */
1116
+ interface CreateObjectRecord {
1117
+ tenantId: TenantId;
1118
+ objectId: Uuid;
1119
+ data: Record<string, unknown>;
1120
+ /**
1121
+ * Completion status of the record.
1122
+ * - `draft`: Record is incomplete (missing required values)
1123
+ * - `complete`: All required values are present and valid
1124
+ */
1125
+ completionStatus: "draft" | "complete";
1126
+ }
1127
+ /**
1128
+ * Generic list options for pagination and sorting
1129
+ */
1130
+ interface ListOptions {
1131
+ limit?: number;
1132
+ offset?: number;
1133
+ orderBy?: string;
1134
+ orderDirection?: "asc" | "desc";
1135
+ }
1136
+ /**
1137
+ * Search options for full-text search
1138
+ */
1139
+ interface SearchOptions extends ListOptions {
1140
+ filters?: Record<string, unknown>;
1141
+ highlight?: boolean;
1142
+ }
1143
+ /**
1144
+ * File-specific list options
1145
+ */
1146
+ interface FileListOptions extends ListOptions {
1147
+ folderPath?: string;
1148
+ mimeType?: string;
1149
+ uploadedBy?: Uuid;
1150
+ includeDeleted?: boolean;
1151
+ }
1152
+ /**
1153
+ * Result of an operation
1154
+ */
1155
+ interface OperationResult<T = unknown> {
1156
+ success: boolean;
1157
+ data?: T;
1158
+ error?: {
1159
+ code: string;
1160
+ message: string;
1161
+ details?: Record<string, unknown>;
1162
+ };
1163
+ }
1164
+
1165
+ /**
1166
+ * Repository for objects table (metadata)
1167
+ */
1168
+ interface ObjectsRepository {
1169
+ /**
1170
+ * Find object by ID
1171
+ */
1172
+ findById(id: Uuid): Promise<DBObject | null>;
1173
+ /**
1174
+ * Find object by name and tenant
1175
+ */
1176
+ findByName(tenantId: TenantId, name: string): Promise<DBObject | null>;
1177
+ /**
1178
+ * Find system/native object by name (system=true, for sync)
1179
+ */
1180
+ findSystemByName(name: string): Promise<DBObject | null>;
1181
+ /**
1182
+ * Create object
1183
+ */
1184
+ create(data: CreateDBObject): Promise<DBObject>;
1185
+ /**
1186
+ * Update object
1187
+ */
1188
+ update(id: Uuid, data: Partial<UpdateDBObject>): Promise<DBObject>;
1189
+ /**
1190
+ * Delete object
1191
+ */
1192
+ delete(id: Uuid): Promise<void>;
1193
+ /**
1194
+ * List all objects for a tenant
1195
+ */
1196
+ list(tenantId: TenantId): Promise<DBObject[]>;
1197
+ /**
1198
+ * Upsert object (create or update based on nativeObjectId)
1199
+ */
1200
+ upsert(data: UpsertDBObject): Promise<DBObject>;
1201
+ }
1202
+ /**
1203
+ * Repository for attributes table (metadata)
1204
+ */
1205
+ interface AttributesRepository {
1206
+ /**
1207
+ * Find attribute by ID
1208
+ */
1209
+ findById(id: Uuid): Promise<DBAttribute | null>;
1210
+ /**
1211
+ * Find attributes by object ID
1212
+ */
1213
+ findByObjectId(objectId: Uuid): Promise<DBAttribute[]>;
1214
+ /**
1215
+ * Create attribute
1216
+ */
1217
+ create(data: CreateDBAttribute): Promise<DBAttribute>;
1218
+ /**
1219
+ * Update attribute
1220
+ */
1221
+ update(id: Uuid, data: Partial<UpdateDBAttribute>): Promise<DBAttribute>;
1222
+ /**
1223
+ * Delete attribute
1224
+ */
1225
+ delete(id: Uuid): Promise<void>;
1226
+ /**
1227
+ * Delete multiple attributes by names (for sync cleanup)
1228
+ */
1229
+ deleteByNames(objectId: Uuid, excludeNames: string[]): Promise<number>;
1230
+ /**
1231
+ * Upsert attribute (create or update based on objectId + name)
1232
+ */
1233
+ upsert(data: UpsertDBAttribute): Promise<DBAttribute>;
1234
+ }
1235
+ /**
1236
+ * Repository for user_profiles table
1237
+ */
1238
+ interface UserProfilesRepository {
1239
+ /**
1240
+ * Find user profile by ID
1241
+ */
1242
+ findById(id: Uuid): Promise<UserProfile | null>;
1243
+ /**
1244
+ * Find user profile by auth ID (external auth provider)
1245
+ */
1246
+ findByAuthId(authId: string): Promise<UserProfile | null>;
1247
+ /**
1248
+ * Find user profile by email
1249
+ */
1250
+ findByEmail(tenantId: TenantId, email: string): Promise<UserProfile | null>;
1251
+ /**
1252
+ * Create user profile
1253
+ */
1254
+ create(data: CreateUserProfile): Promise<UserProfile>;
1255
+ /**
1256
+ * Update user profile
1257
+ */
1258
+ update(id: Uuid, data: UpdateUserProfile): Promise<UserProfile>;
1259
+ /**
1260
+ * Delete user profile
1261
+ */
1262
+ delete(id: Uuid): Promise<void>;
1263
+ /**
1264
+ * List all user profiles for a tenant
1265
+ */
1266
+ list(tenantId: TenantId, options?: ListOptions): Promise<UserProfile[]>;
1267
+ /**
1268
+ * Update last login timestamp
1269
+ */
1270
+ updateLastLogin(id: Uuid): Promise<void>;
1271
+ }
1272
+ /**
1273
+ * Repository for files table
1274
+ */
1275
+ interface FilesRepository {
1276
+ /**
1277
+ * Find file by ID
1278
+ */
1279
+ findById(id: Uuid): Promise<File | null>;
1280
+ /**
1281
+ * Create file
1282
+ */
1283
+ create(data: CreateFile): Promise<File>;
1284
+ /**
1285
+ * Update file
1286
+ */
1287
+ update(id: Uuid, data: UpdateFile): Promise<File>;
1288
+ /**
1289
+ * Delete file (soft delete)
1290
+ */
1291
+ delete(id: Uuid): Promise<void>;
1292
+ /**
1293
+ * Hard delete file (permanent)
1294
+ */
1295
+ hardDelete(id: Uuid): Promise<void>;
1296
+ /**
1297
+ * List files for a tenant
1298
+ */
1299
+ list(tenantId: TenantId, options?: FileListOptions): Promise<File[]>;
1300
+ /**
1301
+ * Find files by folder path
1302
+ */
1303
+ findByFolder(tenantId: TenantId, folderPath: string): Promise<File[]>;
1304
+ /**
1305
+ * Find files by uploader
1306
+ */
1307
+ findByUploader(uploadedBy: Uuid): Promise<File[]>;
1308
+ }
1309
+ /**
1310
+ * Repository for object_records table (unified JSONB)
1311
+ */
1312
+ interface ObjectRecordsRepository {
1313
+ /**
1314
+ * Find record by ID
1315
+ */
1316
+ findById(id: Uuid): Promise<ObjectRecord | null>;
1317
+ /**
1318
+ * Create record
1319
+ */
1320
+ create(data: CreateObjectRecord): Promise<ObjectRecord>;
1321
+ /**
1322
+ * Update record
1323
+ */
1324
+ update(id: Uuid, data: Partial<Record<string, unknown>>): Promise<ObjectRecord>;
1325
+ /**
1326
+ * Delete record
1327
+ */
1328
+ delete(id: Uuid): Promise<void>;
1329
+ /**
1330
+ * List records for an object
1331
+ */
1332
+ list(tenantId: TenantId, objectId: Uuid, options?: ListOptions): Promise<{
1333
+ records: ObjectRecord[];
1334
+ total: number;
1335
+ }>;
1336
+ /**
1337
+ * Search records using PostgreSQL full-text search
1338
+ */
1339
+ search(tenantId: TenantId, objectId: Uuid, query: string, options?: SearchOptions): Promise<{
1340
+ records: ObjectRecord[];
1341
+ total: number;
1342
+ }>;
1343
+ }
1344
+
1345
+ /**
1346
+ * Generic database adapter interface
1347
+ * Any ORM (Drizzle, Prisma, Kysely, TypeORM) can implement this interface
1348
+ * to work with @andyoucreate/schema runtime services
1349
+ *
1350
+ * @example
1351
+ * ```typescript
1352
+ * // Drizzle implementation
1353
+ * import { createDrizzleAdapter } from "./adapters/drizzle";
1354
+ * import { db } from "./db";
1355
+ *
1356
+ * const adapter = createDrizzleAdapter(db);
1357
+ *
1358
+ * // Prisma implementation
1359
+ * import { createPrismaAdapter } from "./adapters/prisma";
1360
+ * import { prisma } from "./db";
1361
+ *
1362
+ * const adapter = createPrismaAdapter(prisma);
1363
+ * ```
1364
+ */
1365
+ interface DatabaseAdapter {
1366
+ objects: ObjectsRepository;
1367
+ attributes: AttributesRepository;
1368
+ userProfiles: UserProfilesRepository;
1369
+ files: FilesRepository;
1370
+ objectRecords: ObjectRecordsRepository;
1371
+ transaction<T>(callback: (adapter: DatabaseAdapter) => Promise<T>): Promise<T>;
1372
+ }
1373
+
1374
+ interface InternalObjectRecord extends ObjectRecord {
1375
+ tenantId: TenantId;
1376
+ }
1377
+ interface MockStores {
1378
+ objects: Map<Uuid, DBObject>;
1379
+ attributes: Map<Uuid, DBAttribute>;
1380
+ userProfiles: Map<Uuid, UserProfile>;
1381
+ files: Map<Uuid, File>;
1382
+ objectRecords: Map<Uuid, InternalObjectRecord>;
1383
+ }
1384
+ /**
1385
+ * Create an in-memory mock adapter for testing and development
1386
+ *
1387
+ * This adapter stores all data in memory and is perfect for:
1388
+ * - Unit tests
1389
+ * - Integration tests
1390
+ * - Local development without a database
1391
+ * - Understanding the adapter interface
1392
+ *
1393
+ * @returns A DatabaseAdapter implementation using in-memory storage
1394
+ *
1395
+ * @example
1396
+ * ```typescript
1397
+ * import { createMockAdapter, RecordService } from "@andyoucreate/schema";
1398
+ *
1399
+ * // Create adapter
1400
+ * const adapter = createMockAdapter();
1401
+ *
1402
+ * // Use with services
1403
+ * const recordService = new RecordService(adapter, "tenant-123");
1404
+ *
1405
+ * // Create a record
1406
+ * const product = await recordService.createRecord("obj-product", {
1407
+ * name: "Nike Air Max",
1408
+ * price: 129.99
1409
+ * });
1410
+ *
1411
+ * // List records
1412
+ * const { records, total } = await recordService.listRecords("obj-product");
1413
+ * ```
1414
+ */
1415
+ declare function createMockAdapter(): DatabaseAdapter & {
1416
+ _stores: MockStores;
1417
+ reset(): void;
1418
+ };
1419
+
1420
+ /**
1421
+ * Service for managing files
1422
+ * Handles file metadata CRUD, permissions, and soft delete
1423
+ */
1424
+ declare class FileService {
1425
+ private adapter;
1426
+ private tenantId;
1427
+ constructor(adapter: DatabaseAdapter, tenantId: string);
1428
+ /**
1429
+ * Create a new file record (after upload to storage)
1430
+ *
1431
+ * @param data - File metadata
1432
+ * @returns Created file record
1433
+ *
1434
+ * @example
1435
+ * ```typescript
1436
+ * const service = new FileService(adapter, "tenant-123");
1437
+ *
1438
+ * // After uploading to S3
1439
+ * const file = await service.createFile({
1440
+ * tenantId: "tenant-123",
1441
+ * name: "contract-2025.pdf",
1442
+ * originalName: "Contract Acme Corp 2025.pdf",
1443
+ * mimeType: "application/pdf",
1444
+ * size: 2458624,
1445
+ * storageProvider: "s3",
1446
+ * storagePath: "tenants/123/files/2025/contract.pdf",
1447
+ * storageBucket: "my-app-files",
1448
+ * url: "https://cdn.example.com/files/file-123",
1449
+ * uploadedBy: "profile-456",
1450
+ * visibility: "private"
1451
+ * });
1452
+ * ```
1453
+ */
1454
+ createFile(data: CreateFile): Promise<File>;
1455
+ /**
1456
+ * Get file by ID
1457
+ */
1458
+ getFile(fileId: string): Promise<File | null>;
1459
+ /**
1460
+ * Get file by ID or throw
1461
+ */
1462
+ getFileOrThrow(fileId: string): Promise<File>;
1463
+ /**
1464
+ * Update file metadata
1465
+ *
1466
+ * @param fileId - File UUID
1467
+ * @param data - Data to update
1468
+ * @returns Updated file
1469
+ */
1470
+ updateFile(fileId: string, data: UpdateFile): Promise<File>;
1471
+ /**
1472
+ * Delete file (soft delete)
1473
+ *
1474
+ * @param fileId - File UUID
1475
+ * @param options - Delete options
1476
+ */
1477
+ deleteFile(fileId: string, options?: {
1478
+ hard?: boolean;
1479
+ checkOwnership?: boolean;
1480
+ userId?: string;
1481
+ }): Promise<void>;
1482
+ /**
1483
+ * List files for the tenant
1484
+ */
1485
+ listFiles(options?: FileListOptions): Promise<File[]>;
1486
+ /**
1487
+ * List files by folder
1488
+ */
1489
+ listFilesByFolder(folderPath: string): Promise<File[]>;
1490
+ /**
1491
+ * List files uploaded by a specific user
1492
+ */
1493
+ listFilesByUploader(uploadedBy: string): Promise<File[]>;
1494
+ /**
1495
+ * Change file visibility
1496
+ *
1497
+ * @param fileId - File UUID
1498
+ * @param visibility - New visibility level
1499
+ * @param allowedUsers - Users allowed to access (if restricted)
1500
+ */
1501
+ changeVisibility(fileId: string, visibility: "public" | "private" | "restricted", allowedUsers?: string[]): Promise<File>;
1502
+ /**
1503
+ * Grant access to a file for specific users
1504
+ *
1505
+ * @param fileId - File UUID
1506
+ * @param userIds - User IDs to grant access
1507
+ */
1508
+ grantAccess(fileId: string, userIds: string[]): Promise<File>;
1509
+ /**
1510
+ * Revoke access to a file for specific users
1511
+ *
1512
+ * @param fileId - File UUID
1513
+ * @param userIds - User IDs to revoke access
1514
+ */
1515
+ revokeAccess(fileId: string, userIds: string[]): Promise<File>;
1516
+ /**
1517
+ * Check if user has access to a file
1518
+ *
1519
+ * @param fileId - File UUID
1520
+ * @param userId - User ID to check
1521
+ * @returns true if user can access the file
1522
+ */
1523
+ canAccess(fileId: string, userId: string): Promise<boolean>;
1524
+ /**
1525
+ * Move file to different folder
1526
+ */
1527
+ moveToFolder(fileId: string, newFolderPath: string): Promise<File>;
1528
+ /**
1529
+ * Add tags to file
1530
+ */
1531
+ addTags(fileId: string, tags: string[]): Promise<File>;
1532
+ /**
1533
+ * Remove tags from file
1534
+ */
1535
+ removeTags(fileId: string, tags: string[]): Promise<File>;
1536
+ }
1537
+
1538
+ /**
1539
+ * Geocoding service for address autocomplete and geocoding operations
1540
+ *
1541
+ * @example
1542
+ * ```typescript
1543
+ * import { GeocodingService } from "@andyoucreate/schema";
1544
+ * import { GoogleMapsAdapter } from "./adapters/google-maps";
1545
+ *
1546
+ * const geocoding = new GeocodingService(new GoogleMapsAdapter({ apiKey: "..." }));
1547
+ *
1548
+ * const suggestions = await geocoding.autocomplete({
1549
+ * query: "40 Quai des Belges",
1550
+ * countries: ["FRA"],
1551
+ * limit: 5,
1552
+ * });
1553
+ * ```
1554
+ */
1555
+ declare class GeocodingService {
1556
+ private readonly adapter;
1557
+ constructor(adapter: GeocodingAdapter);
1558
+ /**
1559
+ * Search for address suggestions as the user types
1560
+ */
1561
+ autocomplete(params: GeocodingAutocompleteParams): Promise<GeocodingSuggestion[]>;
1562
+ /**
1563
+ * Reverse geocode coordinates to an address
1564
+ */
1565
+ reverse(params: ReverseGeocodingParams): Promise<GeocodingSuggestion | null>;
1566
+ /**
1567
+ * Geocode a structured address to coordinates
1568
+ */
1569
+ geocode(params: GeocodingParams): Promise<GeocodingSuggestion | null>;
1570
+ }
1571
+
1572
+ /**
1573
+ * Input for creating a custom object
1574
+ */
1575
+ interface CreateCustomObjectInput {
1576
+ name: string;
1577
+ label: string;
1578
+ description?: string;
1579
+ icon?: IconName;
1580
+ attributes?: (Attribute | {
1581
+ build: () => Attribute;
1582
+ })[];
1583
+ metadata?: Record<string, unknown>;
1584
+ }
1585
+ /**
1586
+ * Input for adding an attribute to an object
1587
+ */
1588
+ interface AddAttributeInput {
1589
+ name: string;
1590
+ label: string;
1591
+ type: AttributeType;
1592
+ required?: boolean;
1593
+ unique?: boolean;
1594
+ description?: string;
1595
+ placeholder?: string;
1596
+ icon?: IconName;
1597
+ defaultValue?: unknown;
1598
+ metadata?: Record<string, unknown>;
1599
+ [key: string]: unknown;
1600
+ }
1601
+ /**
1602
+ * Service for managing object schemas
1603
+ * Handles fusion of native objects (from registry) and custom objects (from database)
1604
+ */
1605
+ declare class ObjectSchemaService {
1606
+ private adapter;
1607
+ private nativeRegistry;
1608
+ constructor(adapter: DatabaseAdapter, nativeRegistry: typeof registry);
1609
+ /**
1610
+ * Create a new custom object
1611
+ * Validates name format and ensures system=false
1612
+ *
1613
+ * @param definition - Object definition (attributes can be raw definitions or builders)
1614
+ * @param tenantId - Tenant ID owning the object
1615
+ * @returns Created ObjectDefinition
1616
+ */
1617
+ createCustomObject(definition: CreateCustomObjectInput, tenantId: string): Promise<ObjectDefinition>;
1618
+ /**
1619
+ * Add a custom attribute to an existing object
1620
+ * Can be used on both custom and native objects
1621
+ * Attributes added via API are always custom (system=false)
1622
+ *
1623
+ * @param objectId - Object UUID from database
1624
+ * @param attribute - Attribute definition
1625
+ * @returns Created Attribute
1626
+ */
1627
+ addAttributeToObject(objectId: string, attribute: AddAttributeInput): Promise<Attribute>;
1628
+ /**
1629
+ * Update an attribute
1630
+ * Can only update custom attributes (system=false)
1631
+ *
1632
+ * @param attributeId - Attribute UUID
1633
+ * @param updates - Partial attribute updates
1634
+ * @returns Updated Attribute
1635
+ */
1636
+ updateAttribute(attributeId: string, updates: Partial<AddAttributeInput>): Promise<Attribute>;
1637
+ /**
1638
+ * Delete an attribute
1639
+ * Can only delete custom attributes (system=false)
1640
+ *
1641
+ * @param attributeId - Attribute UUID
1642
+ */
1643
+ deleteAttribute(attributeId: string): Promise<void>;
1644
+ /**
1645
+ * List attributes for an object
1646
+ *
1647
+ * @param objectId - Object UUID
1648
+ * @param options - Filter options
1649
+ * @returns List of attributes
1650
+ */
1651
+ listAttributes(objectId: string, options?: {
1652
+ systemOnly?: boolean;
1653
+ customOnly?: boolean;
1654
+ }): Promise<Attribute[]>;
1655
+ /**
1656
+ * Validate attribute name format (variable identifier)
1657
+ * @internal
1658
+ */
1659
+ private validateAttributeName;
1660
+ /**
1661
+ * Get complete object schema (system + custom attributes)
1662
+ * If object is native, merges registry definition with DB custom attributes
1663
+ * If object is custom, returns DB definition only
1664
+ *
1665
+ * @param objectId - Object UUID from database
1666
+ * @returns Complete ObjectDefinition with all attributes
1667
+ *
1668
+ * @example
1669
+ * ```typescript
1670
+ * const service = new ObjectSchemaService(adapter, registry);
1671
+ * const productSchema = await service.getObjectSchema("obj-123");
1672
+ *
1673
+ * console.log(productSchema.attributes); // System + custom attributes
1674
+ * ```
1675
+ */
1676
+ getObjectSchema(objectId: string): Promise<ObjectDefinition>;
1677
+ /**
1678
+ * Get object schema by name (for system/native objects)
1679
+ * @deprecated Use getObjectSchemaByNameForTenant for custom objects support
1680
+ */
1681
+ getObjectSchemaByName(name: string): Promise<ObjectDefinition>;
1682
+ /**
1683
+ * Get object schema by name for a tenant (supports both native and custom objects)
1684
+ */
1685
+ getObjectSchemaByNameForTenant(name: string, tenantId: string): Promise<ObjectDefinition>;
1686
+ /**
1687
+ * List all object schemas for a tenant
1688
+ */
1689
+ listObjectSchemas(tenantId: string): Promise<ObjectDefinition[]>;
1690
+ /**
1691
+ * Merge native object from registry with custom attributes from DB
1692
+ * @internal
1693
+ */
1694
+ private mergeNativeObject;
1695
+ /**
1696
+ * Convert DB object + attributes to ObjectDefinition
1697
+ * @internal
1698
+ */
1699
+ private convertDBObjectToDefinition;
1700
+ /**
1701
+ * Convert DB attribute to Attribute type
1702
+ * @internal
1703
+ */
1704
+ private convertDBAttributeToAttribute;
1705
+ }
1706
+
1707
+ /**
1708
+ * Service for managing object records (CRUD operations)
1709
+ * Handles validation, dispatch to correct table, and data consistency
1710
+ */
1711
+ declare class RecordService {
1712
+ private adapter;
1713
+ private tenantId;
1714
+ private schemaService;
1715
+ private relationService;
1716
+ constructor(adapter: DatabaseAdapter, tenantId: string);
1717
+ /**
1718
+ * Create a new record with validation
1719
+ *
1720
+ * @param objectId - Object UUID
1721
+ * @param data - Record data (attribute values)
1722
+ * @param options - Creation options
1723
+ * @returns Created record with computed completionStatus
1724
+ *
1725
+ * @example
1726
+ * ```typescript
1727
+ * const service = new RecordService(adapter, "tenant-123");
1728
+ *
1729
+ * // Create a complete record (strict validation)
1730
+ * const product = await service.createRecord("obj-product", {
1731
+ * name: "Nike Air Max",
1732
+ * price: 129.99,
1733
+ * status: "active"
1734
+ * });
1735
+ * // → product.completionStatus = "complete"
1736
+ *
1737
+ * // Create a draft record (allows missing required fields)
1738
+ * const draft = await service.createRecord("obj-product", {
1739
+ * name: "Draft Product"
1740
+ * }, { allowDraft: true });
1741
+ * // → draft.completionStatus = "draft"
1742
+ * ```
1743
+ */
1744
+ createRecord(objectId: string, data: Record<string, unknown>, options?: {
1745
+ /**
1746
+ * Allow creating records with missing required fields.
1747
+ * Format validation still applies to provided values.
1748
+ * @default false
1749
+ */
1750
+ allowDraft?: boolean;
1751
+ /**
1752
+ * Skip all validation (format + relations).
1753
+ * @default true
1754
+ */
1755
+ validate?: boolean;
1756
+ /**
1757
+ * Skip relation validation only.
1758
+ * Useful for bulk imports where relations are validated separately.
1759
+ * @default false
1760
+ */
1761
+ skipRelationValidation?: boolean;
1762
+ skipSystemCheck?: boolean;
1763
+ }): Promise<ObjectRecord>;
1764
+ /**
1765
+ * Get a record by ID
1766
+ *
1767
+ * @param recordId - Record UUID
1768
+ * @param options - Query options
1769
+ * @returns Record or null if not found
1770
+ */
1771
+ getRecord(recordId: string, options?: {
1772
+ includeSchema?: boolean;
1773
+ }): Promise<ObjectRecord | null>;
1774
+ /**
1775
+ * Get a record by ID or throw if not found
1776
+ */
1777
+ getRecordOrThrow(recordId: string): Promise<ObjectRecord>;
1778
+ /**
1779
+ * Update a record with validation
1780
+ *
1781
+ * The completion status is automatically recalculated after each update.
1782
+ * A draft record becomes complete when all required fields are filled.
1783
+ *
1784
+ * @param recordId - Record UUID
1785
+ * @param data - Partial data to update
1786
+ * @param options - Update options
1787
+ * @returns Updated record with recalculated completionStatus
1788
+ *
1789
+ * @example
1790
+ * ```typescript
1791
+ * // Update a draft record to make it complete
1792
+ * const updated = await service.updateRecord(draftId, {
1793
+ * price: 99.99,
1794
+ * status: "active"
1795
+ * });
1796
+ * // → updated.completionStatus = "complete" if all required fields now present
1797
+ * ```
1798
+ */
1799
+ updateRecord(recordId: string, data: Partial<Record<string, unknown>>, options?: {
1800
+ /**
1801
+ * Skip validation entirely (format + relations).
1802
+ * @default true
1803
+ */
1804
+ validate?: boolean;
1805
+ /**
1806
+ * Allow partial updates without strict validation.
1807
+ * Format validation still applies to provided values.
1808
+ * @default false
1809
+ */
1810
+ partial?: boolean;
1811
+ /**
1812
+ * Skip relation validation only.
1813
+ * @default false
1814
+ */
1815
+ skipRelationValidation?: boolean;
1816
+ }): Promise<ObjectRecord>;
1817
+ /**
1818
+ * Delete a record
1819
+ *
1820
+ * @param recordId - Record UUID
1821
+ * @param options - Delete options
1822
+ */
1823
+ deleteRecord(recordId: string, options?: {
1824
+ checkSystem?: boolean;
1825
+ }): Promise<void>;
1826
+ /**
1827
+ * List records for an object with pagination
1828
+ *
1829
+ * @param objectId - Object UUID
1830
+ * @param options - List options
1831
+ * @returns Records and total count
1832
+ */
1833
+ listRecords(objectId: string, options?: {
1834
+ limit?: number;
1835
+ offset?: number;
1836
+ orderBy?: string;
1837
+ orderDirection?: "asc" | "desc";
1838
+ }): Promise<{
1839
+ records: ObjectRecord[];
1840
+ total: number;
1841
+ }>;
1842
+ /**
1843
+ * Search records using full-text search
1844
+ *
1845
+ * @param objectId - Object UUID
1846
+ * @param query - Search query
1847
+ * @param options - Search options
1848
+ * @returns Matching records and total count
1849
+ */
1850
+ searchRecords(objectId: string, query: string, options?: {
1851
+ limit?: number;
1852
+ offset?: number;
1853
+ filters?: Record<string, unknown>;
1854
+ }): Promise<{
1855
+ records: ObjectRecord[];
1856
+ total: number;
1857
+ }>;
1858
+ /**
1859
+ * Validate data against object schema without saving
1860
+ *
1861
+ * @param objectId - Object UUID
1862
+ * @param data - Data to validate
1863
+ * @returns Validation result
1864
+ */
1865
+ validateData(objectId: string, data: Record<string, unknown>): Promise<ValidationResult>;
1866
+ /**
1867
+ * Compute the completion status for given data without saving.
1868
+ * Useful for UI to show draft/complete status before submitting.
1869
+ *
1870
+ * @param objectId - Object UUID
1871
+ * @param data - Data to check
1872
+ * @returns Computed completion status
1873
+ */
1874
+ computeStatus(objectId: string, data: Record<string, unknown>): Promise<CompletionStatus>;
1875
+ /**
1876
+ * Refresh the completion status of an existing record.
1877
+ * Useful when schema changes and you need to recompute statuses.
1878
+ *
1879
+ * @param recordId - Record UUID
1880
+ * @returns Updated completion status
1881
+ */
1882
+ refreshRecordStatus(recordId: string): Promise<CompletionStatus>;
1883
+ }
1884
+
1885
+ /**
1886
+ * Result of relation validation
1887
+ */
1888
+ interface RelationValidationResult {
1889
+ valid: boolean;
1890
+ errors: RelationValidationError[];
1891
+ }
1892
+ /**
1893
+ * Individual relation validation error
1894
+ */
1895
+ interface RelationValidationError {
1896
+ /** Attribute name */
1897
+ attribute: string;
1898
+ /** Error message */
1899
+ message: string;
1900
+ /** Invalid record IDs */
1901
+ invalidIds?: string[];
1902
+ }
1903
+ /**
1904
+ * Resolved relation option
1905
+ */
1906
+ interface RelationOption {
1907
+ /** Record ID */
1908
+ id: string;
1909
+ /** Object ID */
1910
+ objectId: string;
1911
+ /** Object name (technical name) */
1912
+ objectName: string;
1913
+ /** Object label (display name) */
1914
+ objectLabel: string;
1915
+ /** Object icon */
1916
+ objectIcon?: string;
1917
+ /** Display label (resolved from titleAttribute) */
1918
+ label: string;
1919
+ /** Additional record data */
1920
+ data?: Record<string, unknown>;
1921
+ }
1922
+ /**
1923
+ * Response for relation options
1924
+ */
1925
+ interface RelationOptionsResponse {
1926
+ options: RelationOption[];
1927
+ hasMore: boolean;
1928
+ total: number;
1929
+ }
1930
+ /**
1931
+ * Parameters for fetching relation options
1932
+ */
1933
+ interface GetRelationOptionsParams {
1934
+ /** Search query */
1935
+ query?: string;
1936
+ /** Page number (1-based) */
1937
+ page?: number;
1938
+ /** Page size */
1939
+ pageSize?: number;
1940
+ /** Filter by specific target object */
1941
+ targetObject?: string;
1942
+ }
1943
+ /**
1944
+ * Service for validating relation attributes
1945
+ * Ensures referenced records exist and belong to valid target objects
1946
+ */
1947
+ declare class RelationService {
1948
+ private adapter;
1949
+ private schemaService;
1950
+ constructor(adapter: DatabaseAdapter, nativeRegistry: typeof registry);
1951
+ /**
1952
+ * Validate all relation attributes in the data
1953
+ *
1954
+ * @param schema - Object schema containing attribute definitions
1955
+ * @param data - Record data to validate
1956
+ * @returns Validation result with errors if any
1957
+ *
1958
+ * @example
1959
+ * ```typescript
1960
+ * const result = await relationService.validateRelations(schema, {
1961
+ * company: "rec-123",
1962
+ * contacts: ["rec-456", "rec-789"]
1963
+ * });
1964
+ *
1965
+ * if (!result.valid) {
1966
+ * console.log(result.errors);
1967
+ * // [{ attribute: "company", message: "Record not found", invalidIds: ["rec-123"] }]
1968
+ * }
1969
+ * ```
1970
+ */
1971
+ validateRelations(schema: ObjectDefinition, data: Record<string, unknown>): Promise<RelationValidationResult>;
1972
+ /**
1973
+ * Validate a single relation attribute value
1974
+ */
1975
+ private validateRelationAttribute;
1976
+ /**
1977
+ * Extract IDs from relation value based on cardinality
1978
+ */
1979
+ private extractIds;
1980
+ /**
1981
+ * Get valid object IDs from relation targets
1982
+ */
1983
+ private getValidObjectIds;
1984
+ /**
1985
+ * Validate relations and throw if invalid
1986
+ */
1987
+ validateRelationsOrThrow(schema: ObjectDefinition, data: Record<string, unknown>): Promise<void>;
1988
+ /**
1989
+ * Get available options for a relation attribute
1990
+ * Searches across all target objects defined in the relation
1991
+ *
1992
+ * @param attribute - Relation attribute definition
1993
+ * @param tenantId - Tenant ID for multi-tenant isolation
1994
+ * @param params - Query parameters
1995
+ *
1996
+ * @example
1997
+ * ```typescript
1998
+ * const options = await relationService.getOptions(attribute, "tenant-1", {
1999
+ * query: "nike",
2000
+ * page: 1,
2001
+ * pageSize: 20
2002
+ * });
2003
+ * ```
2004
+ */
2005
+ getOptions(attribute: RelationAttribute, tenantId: string, params?: GetRelationOptionsParams): Promise<RelationOptionsResponse>;
2006
+ /**
2007
+ * Resolve record IDs to their display labels
2008
+ * Useful for displaying current values in the UI
2009
+ *
2010
+ * @param ids - Record IDs to resolve
2011
+ * @param tenantId - Tenant ID for multi-tenant isolation
2012
+ *
2013
+ * @example
2014
+ * ```typescript
2015
+ * const resolved = await relationService.resolveIds(["rec-1", "rec-2"], "tenant-1");
2016
+ * // [{ id: "rec-1", label: "Nike Air Max", objectName: "products", ... }]
2017
+ * ```
2018
+ */
2019
+ resolveIds(ids: string[], tenantId: string): Promise<RelationOption[]>;
2020
+ /**
2021
+ * Find a relation attribute by ID
2022
+ */
2023
+ findAttributeById(attributeId: string): Promise<RelationAttribute | null>;
2024
+ /**
2025
+ * Resolve display label from record values
2026
+ * Uses displayTemplate if provided, otherwise falls back to titleAttribute
2027
+ */
2028
+ private resolveLabel;
2029
+ }
2030
+
2031
+ /**
2032
+ * Service for managing user profiles
2033
+ * Handles user profile CRUD, auth provider sync, and role management
2034
+ */
2035
+ declare class UserProfileService {
2036
+ private adapter;
2037
+ private tenantId;
2038
+ constructor(adapter: DatabaseAdapter, tenantId: string);
2039
+ /**
2040
+ * Create a new user profile (typically after first auth)
2041
+ *
2042
+ * @param data - User profile creation data
2043
+ * @returns Created user profile
2044
+ *
2045
+ * @example
2046
+ * ```typescript
2047
+ * const service = new UserProfileService(adapter, "tenant-123");
2048
+ *
2049
+ * // After Supabase auth
2050
+ * const profile = await service.createProfile({
2051
+ * tenantId: "tenant-123",
2052
+ * authId: authUser.id,
2053
+ * email: authUser.email,
2054
+ * firstName: authUser.user_metadata.first_name,
2055
+ * lastName: authUser.user_metadata.last_name,
2056
+ * role: "member",
2057
+ * status: "active"
2058
+ * });
2059
+ * ```
2060
+ */
2061
+ createProfile(data: CreateUserProfile): Promise<UserProfile>;
2062
+ /**
2063
+ * Get user profile by ID
2064
+ */
2065
+ getProfile(profileId: string): Promise<UserProfile | null>;
2066
+ /**
2067
+ * Get user profile by ID or throw
2068
+ */
2069
+ getProfileOrThrow(profileId: string): Promise<UserProfile>;
2070
+ /**
2071
+ * Get user profile by auth provider ID
2072
+ *
2073
+ * @param authId - Auth provider user ID (Supabase, Clerk, etc.)
2074
+ * @returns User profile or null
2075
+ */
2076
+ getProfileByAuthId(authId: string): Promise<UserProfile | null>;
2077
+ /**
2078
+ * Get or create user profile (idempotent operation)
2079
+ * Useful for auth callbacks - ensures profile exists
2080
+ *
2081
+ * @param authId - Auth provider user ID
2082
+ * @param data - Profile data to create if doesn't exist
2083
+ * @returns Existing or newly created profile
2084
+ *
2085
+ * @example
2086
+ * ```typescript
2087
+ * // In Supabase auth callback
2088
+ * const profile = await service.getOrCreateProfile(
2089
+ * authUser.id,
2090
+ * {
2091
+ * tenantId: "tenant-123",
2092
+ * authId: authUser.id,
2093
+ * email: authUser.email,
2094
+ * role: "member",
2095
+ * status: "active"
2096
+ * }
2097
+ * );
2098
+ * ```
2099
+ */
2100
+ getOrCreateProfile(authId: string, data: CreateUserProfile): Promise<UserProfile>;
2101
+ /**
2102
+ * Update user profile
2103
+ */
2104
+ updateProfile(profileId: string, data: UpdateUserProfile): Promise<UserProfile>;
2105
+ /**
2106
+ * Delete user profile
2107
+ *
2108
+ * @param profileId - Profile UUID
2109
+ * @param options - Delete options
2110
+ */
2111
+ deleteProfile(profileId: string, options?: {
2112
+ checkAdmin?: boolean;
2113
+ }): Promise<void>;
2114
+ /**
2115
+ * List all user profiles for the tenant
2116
+ */
2117
+ listProfiles(options?: ListOptions): Promise<UserProfile[]>;
2118
+ /**
2119
+ * Update last login timestamp
2120
+ *
2121
+ * @param profileId - Profile UUID
2122
+ *
2123
+ * @example
2124
+ * ```typescript
2125
+ * // After successful auth
2126
+ * await service.updateLastLogin(profile.id);
2127
+ * ```
2128
+ */
2129
+ updateLastLogin(profileId: string): Promise<void>;
2130
+ /**
2131
+ * Change user role
2132
+ *
2133
+ * @param profileId - Profile UUID
2134
+ * @param newRole - New role
2135
+ */
2136
+ changeRole(profileId: string, newRole: string): Promise<UserProfile>;
2137
+ /**
2138
+ * Change user status
2139
+ *
2140
+ * @param profileId - Profile UUID
2141
+ * @param newStatus - New status
2142
+ */
2143
+ changeStatus(profileId: string, newStatus: "active" | "pending" | "inactive" | "suspended"): Promise<UserProfile>;
2144
+ /**
2145
+ * Get user by email
2146
+ */
2147
+ getProfileByEmail(email: string): Promise<UserProfile | null>;
2148
+ /**
2149
+ * Check if user has role
2150
+ */
2151
+ hasRole(profileId: string, role: string): Promise<boolean>;
2152
+ /**
2153
+ * Check if user is admin
2154
+ */
2155
+ isAdmin(profileId: string): Promise<boolean>;
2156
+ }
2157
+
2158
+ /**
2159
+ * Result of sync operation
2160
+ */
2161
+ interface SyncResult {
2162
+ success: boolean;
2163
+ objectsSynced: number;
2164
+ attributesSynced: number;
2165
+ objectsCreated: number;
2166
+ objectsUpdated: number;
2167
+ attributesCreated: number;
2168
+ attributesUpdated: number;
2169
+ attributesDeleted: number;
2170
+ errors: Array<{
2171
+ objectName: string;
2172
+ error: string;
2173
+ }>;
2174
+ }
2175
+ /**
2176
+ * Sync options
2177
+ */
2178
+ interface SyncOptions {
2179
+ dryRun?: boolean;
2180
+ verbose?: boolean;
2181
+ tenantId?: string;
2182
+ }
2183
+ /**
2184
+ * Sync native objects from registry to database
2185
+ *
2186
+ * This function:
2187
+ * 1. Reads all registered native objects from the registry
2188
+ * 2. Upserts them into the database (objects + attributes tables)
2189
+ * 3. Marks them as system=true for protection
2190
+ * 4. Removes attributes that were deleted from code
2191
+ *
2192
+ * @param adapter - Database adapter implementing DatabaseAdapter interface
2193
+ * @param nativeRegistry - Registry containing native objects
2194
+ * @param options - Sync options (dryRun, verbose, tenantId)
2195
+ * @returns Sync result with statistics
2196
+ *
2197
+ * @example
2198
+ * ```typescript
2199
+ * import { syncNativeObjects } from "@andyoucreate/schema/runtime";
2200
+ * import { registry } from "./objects/native";
2201
+ * import { drizzleAdapter } from "./db/adapter";
2202
+ *
2203
+ * const result = await syncNativeObjects(drizzleAdapter, registry, {
2204
+ * verbose: true,
2205
+ * tenantId: "default"
2206
+ * });
2207
+ *
2208
+ * if (result.success) {
2209
+ * console.log(`✓ Synced ${result.objectsSynced} objects`);
2210
+ * } else {
2211
+ * console.error("Sync errors:", result.errors);
2212
+ * }
2213
+ * ```
2214
+ */
2215
+ declare function syncNativeObjects(adapter: DatabaseAdapter, nativeRegistry: typeof registry, options?: SyncOptions): Promise<SyncResult>;
2216
+ /**
2217
+ * Verify that all native objects are synced to database
2218
+ *
2219
+ * @param adapter - Database adapter
2220
+ * @param nativeRegistry - Registry containing native objects
2221
+ * @returns true if all objects are synced, false otherwise
2222
+ *
2223
+ * @example
2224
+ * ```typescript
2225
+ * const isSynced = await verifyNativeObjectsSync(adapter, registry);
2226
+ * if (!isSynced) {
2227
+ * console.warn("Native objects not synced, running sync...");
2228
+ * await syncNativeObjects(adapter, registry);
2229
+ * }
2230
+ * ```
2231
+ */
2232
+ declare function verifyNativeObjectsSync(adapter: DatabaseAdapter, nativeRegistry: typeof registry): Promise<boolean>;
2233
+ /**
2234
+ * Get sync statistics without modifying database
2235
+ *
2236
+ * @param adapter - Database adapter
2237
+ * @param nativeRegistry - Registry containing native objects
2238
+ * @returns Sync result (dry run)
2239
+ */
2240
+ declare function getSyncPreview(adapter: DatabaseAdapter, nativeRegistry: typeof registry): Promise<SyncResult>;
2241
+
2242
+ export { type UserRole 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 GeocodingSuggestion as I, type GeocodingAutocompleteParams as J, type ReverseGeocodingParams as K, type Location as L, type MultiselectAttribute as M, type NumberAttribute as N, type Option as O, type Phone as P, type GeocodingParams as Q, type RelationOnDelete as R, type StatusAttribute as S, type TextAttribute as T, type UserAttribute as U, type GeocodingAdapter as V, NoopGeocodingAdapter as W, type Timestamps as X, type ObjectAttribute as Y, type CompletionStatus as Z, type ObjectRecord as _, type SelectAttribute as a, type DBObject as a$, type UserStatus as a0, type UserProfile as a1, type CreateUserProfile as a2, type UpdateUserProfile as a3, type Uuid as a4, type TenantId as a5, generateId as a6, generatePrefixedId as a7, registry as a8, createTextValidator as a9, isRecordComplete as aA, computeRecordStatus as aB, type DatabaseAdapter as aC, createMockAdapter as aD, type ObjectsRepository as aE, type AttributesRepository as aF, type UserProfilesRepository as aG, type FilesRepository as aH, type ObjectRecordsRepository as aI, FileService as aJ, GeocodingService as aK, type CreateCustomObjectInput as aL, type AddAttributeInput as aM, ObjectSchemaService as aN, RecordService as aO, type RelationValidationResult as aP, type RelationValidationError as aQ, type RelationOption as aR, type RelationOptionsResponse as aS, type GetRelationOptionsParams as aT, RelationService as aU, UserProfileService as aV, type SyncResult as aW, type SyncOptions as aX, syncNativeObjects as aY, verifyNativeObjectsSync as aZ, getSyncPreview as a_, createNumberValidator as aa, createCheckboxValidator as ab, createDateValidator as ac, createPhoneValidator as ad, createCurrencyValidator as ae, createStatusValidator as af, createSelectValidator as ag, createMultiselectValidator as ah, createLocationValidator as ai, createTimestampValidator as aj, createFileValidator as ak, createUserValidator as al, createSingleRelationValidator as am, createMultiRelationValidator as an, createRelationValidator as ao, createRatingValidator as ap, createAttributeValidator as aq, createObjectValidator as ar, type ValidationResult as as, validateAttribute as at, validateObject as au, validateObjectOrThrow as av, createDraftValidator as aw, validateDraft as ax, validateDraftOrThrow as ay, getMissingRequiredAttributes as az, type TextAreaAttribute as b, type CreateDBObject as b0, type UpdateDBObject as b1, type UpsertDBObject as b2, type DBAttribute as b3, type CreateDBAttribute as b4, type UpdateDBAttribute as b5, type UpsertDBAttribute as b6, type CreateObjectRecord as b7, type ListOptions as b8, type SearchOptions as b9, type FileListOptions as ba, type OperationResult as bb, 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 };