@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,2237 @@
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
+ */
1680
+ getObjectSchemaByName(name: string): Promise<ObjectDefinition>;
1681
+ /**
1682
+ * List all object schemas for a tenant
1683
+ */
1684
+ listObjectSchemas(tenantId: string): Promise<ObjectDefinition[]>;
1685
+ /**
1686
+ * Merge native object from registry with custom attributes from DB
1687
+ * @internal
1688
+ */
1689
+ private mergeNativeObject;
1690
+ /**
1691
+ * Convert DB object + attributes to ObjectDefinition
1692
+ * @internal
1693
+ */
1694
+ private convertDBObjectToDefinition;
1695
+ /**
1696
+ * Convert DB attribute to Attribute type
1697
+ * @internal
1698
+ */
1699
+ private convertDBAttributeToAttribute;
1700
+ }
1701
+
1702
+ /**
1703
+ * Service for managing object records (CRUD operations)
1704
+ * Handles validation, dispatch to correct table, and data consistency
1705
+ */
1706
+ declare class RecordService {
1707
+ private adapter;
1708
+ private tenantId;
1709
+ private schemaService;
1710
+ private relationService;
1711
+ constructor(adapter: DatabaseAdapter, tenantId: string);
1712
+ /**
1713
+ * Create a new record with validation
1714
+ *
1715
+ * @param objectId - Object UUID
1716
+ * @param data - Record data (attribute values)
1717
+ * @param options - Creation options
1718
+ * @returns Created record with computed completionStatus
1719
+ *
1720
+ * @example
1721
+ * ```typescript
1722
+ * const service = new RecordService(adapter, "tenant-123");
1723
+ *
1724
+ * // Create a complete record (strict validation)
1725
+ * const product = await service.createRecord("obj-product", {
1726
+ * name: "Nike Air Max",
1727
+ * price: 129.99,
1728
+ * status: "active"
1729
+ * });
1730
+ * // → product.completionStatus = "complete"
1731
+ *
1732
+ * // Create a draft record (allows missing required fields)
1733
+ * const draft = await service.createRecord("obj-product", {
1734
+ * name: "Draft Product"
1735
+ * }, { allowDraft: true });
1736
+ * // → draft.completionStatus = "draft"
1737
+ * ```
1738
+ */
1739
+ createRecord(objectId: string, data: Record<string, unknown>, options?: {
1740
+ /**
1741
+ * Allow creating records with missing required fields.
1742
+ * Format validation still applies to provided values.
1743
+ * @default false
1744
+ */
1745
+ allowDraft?: boolean;
1746
+ /**
1747
+ * Skip all validation (format + relations).
1748
+ * @default true
1749
+ */
1750
+ validate?: boolean;
1751
+ /**
1752
+ * Skip relation validation only.
1753
+ * Useful for bulk imports where relations are validated separately.
1754
+ * @default false
1755
+ */
1756
+ skipRelationValidation?: boolean;
1757
+ skipSystemCheck?: boolean;
1758
+ }): Promise<ObjectRecord>;
1759
+ /**
1760
+ * Get a record by ID
1761
+ *
1762
+ * @param recordId - Record UUID
1763
+ * @param options - Query options
1764
+ * @returns Record or null if not found
1765
+ */
1766
+ getRecord(recordId: string, options?: {
1767
+ includeSchema?: boolean;
1768
+ }): Promise<ObjectRecord | null>;
1769
+ /**
1770
+ * Get a record by ID or throw if not found
1771
+ */
1772
+ getRecordOrThrow(recordId: string): Promise<ObjectRecord>;
1773
+ /**
1774
+ * Update a record with validation
1775
+ *
1776
+ * The completion status is automatically recalculated after each update.
1777
+ * A draft record becomes complete when all required fields are filled.
1778
+ *
1779
+ * @param recordId - Record UUID
1780
+ * @param data - Partial data to update
1781
+ * @param options - Update options
1782
+ * @returns Updated record with recalculated completionStatus
1783
+ *
1784
+ * @example
1785
+ * ```typescript
1786
+ * // Update a draft record to make it complete
1787
+ * const updated = await service.updateRecord(draftId, {
1788
+ * price: 99.99,
1789
+ * status: "active"
1790
+ * });
1791
+ * // → updated.completionStatus = "complete" if all required fields now present
1792
+ * ```
1793
+ */
1794
+ updateRecord(recordId: string, data: Partial<Record<string, unknown>>, options?: {
1795
+ /**
1796
+ * Skip validation entirely (format + relations).
1797
+ * @default true
1798
+ */
1799
+ validate?: boolean;
1800
+ /**
1801
+ * Allow partial updates without strict validation.
1802
+ * Format validation still applies to provided values.
1803
+ * @default false
1804
+ */
1805
+ partial?: boolean;
1806
+ /**
1807
+ * Skip relation validation only.
1808
+ * @default false
1809
+ */
1810
+ skipRelationValidation?: boolean;
1811
+ }): Promise<ObjectRecord>;
1812
+ /**
1813
+ * Delete a record
1814
+ *
1815
+ * @param recordId - Record UUID
1816
+ * @param options - Delete options
1817
+ */
1818
+ deleteRecord(recordId: string, options?: {
1819
+ checkSystem?: boolean;
1820
+ }): Promise<void>;
1821
+ /**
1822
+ * List records for an object with pagination
1823
+ *
1824
+ * @param objectId - Object UUID
1825
+ * @param options - List options
1826
+ * @returns Records and total count
1827
+ */
1828
+ listRecords(objectId: string, options?: {
1829
+ limit?: number;
1830
+ offset?: number;
1831
+ orderBy?: string;
1832
+ orderDirection?: "asc" | "desc";
1833
+ }): Promise<{
1834
+ records: ObjectRecord[];
1835
+ total: number;
1836
+ }>;
1837
+ /**
1838
+ * Search records using full-text search
1839
+ *
1840
+ * @param objectId - Object UUID
1841
+ * @param query - Search query
1842
+ * @param options - Search options
1843
+ * @returns Matching records and total count
1844
+ */
1845
+ searchRecords(objectId: string, query: string, options?: {
1846
+ limit?: number;
1847
+ offset?: number;
1848
+ filters?: Record<string, unknown>;
1849
+ }): Promise<{
1850
+ records: ObjectRecord[];
1851
+ total: number;
1852
+ }>;
1853
+ /**
1854
+ * Validate data against object schema without saving
1855
+ *
1856
+ * @param objectId - Object UUID
1857
+ * @param data - Data to validate
1858
+ * @returns Validation result
1859
+ */
1860
+ validateData(objectId: string, data: Record<string, unknown>): Promise<ValidationResult>;
1861
+ /**
1862
+ * Compute the completion status for given data without saving.
1863
+ * Useful for UI to show draft/complete status before submitting.
1864
+ *
1865
+ * @param objectId - Object UUID
1866
+ * @param data - Data to check
1867
+ * @returns Computed completion status
1868
+ */
1869
+ computeStatus(objectId: string, data: Record<string, unknown>): Promise<CompletionStatus>;
1870
+ /**
1871
+ * Refresh the completion status of an existing record.
1872
+ * Useful when schema changes and you need to recompute statuses.
1873
+ *
1874
+ * @param recordId - Record UUID
1875
+ * @returns Updated completion status
1876
+ */
1877
+ refreshRecordStatus(recordId: string): Promise<CompletionStatus>;
1878
+ }
1879
+
1880
+ /**
1881
+ * Result of relation validation
1882
+ */
1883
+ interface RelationValidationResult {
1884
+ valid: boolean;
1885
+ errors: RelationValidationError[];
1886
+ }
1887
+ /**
1888
+ * Individual relation validation error
1889
+ */
1890
+ interface RelationValidationError {
1891
+ /** Attribute name */
1892
+ attribute: string;
1893
+ /** Error message */
1894
+ message: string;
1895
+ /** Invalid record IDs */
1896
+ invalidIds?: string[];
1897
+ }
1898
+ /**
1899
+ * Resolved relation option
1900
+ */
1901
+ interface RelationOption {
1902
+ /** Record ID */
1903
+ id: string;
1904
+ /** Object ID */
1905
+ objectId: string;
1906
+ /** Object name (technical name) */
1907
+ objectName: string;
1908
+ /** Object label (display name) */
1909
+ objectLabel: string;
1910
+ /** Object icon */
1911
+ objectIcon?: string;
1912
+ /** Display label (resolved from titleAttribute) */
1913
+ label: string;
1914
+ /** Additional record data */
1915
+ data?: Record<string, unknown>;
1916
+ }
1917
+ /**
1918
+ * Response for relation options
1919
+ */
1920
+ interface RelationOptionsResponse {
1921
+ options: RelationOption[];
1922
+ hasMore: boolean;
1923
+ total: number;
1924
+ }
1925
+ /**
1926
+ * Parameters for fetching relation options
1927
+ */
1928
+ interface GetRelationOptionsParams {
1929
+ /** Search query */
1930
+ query?: string;
1931
+ /** Page number (1-based) */
1932
+ page?: number;
1933
+ /** Page size */
1934
+ pageSize?: number;
1935
+ /** Filter by specific target object */
1936
+ targetObject?: string;
1937
+ }
1938
+ /**
1939
+ * Service for validating relation attributes
1940
+ * Ensures referenced records exist and belong to valid target objects
1941
+ */
1942
+ declare class RelationService {
1943
+ private adapter;
1944
+ private schemaService;
1945
+ constructor(adapter: DatabaseAdapter, nativeRegistry: typeof registry);
1946
+ /**
1947
+ * Validate all relation attributes in the data
1948
+ *
1949
+ * @param schema - Object schema containing attribute definitions
1950
+ * @param data - Record data to validate
1951
+ * @returns Validation result with errors if any
1952
+ *
1953
+ * @example
1954
+ * ```typescript
1955
+ * const result = await relationService.validateRelations(schema, {
1956
+ * company: "rec-123",
1957
+ * contacts: ["rec-456", "rec-789"]
1958
+ * });
1959
+ *
1960
+ * if (!result.valid) {
1961
+ * console.log(result.errors);
1962
+ * // [{ attribute: "company", message: "Record not found", invalidIds: ["rec-123"] }]
1963
+ * }
1964
+ * ```
1965
+ */
1966
+ validateRelations(schema: ObjectDefinition, data: Record<string, unknown>): Promise<RelationValidationResult>;
1967
+ /**
1968
+ * Validate a single relation attribute value
1969
+ */
1970
+ private validateRelationAttribute;
1971
+ /**
1972
+ * Extract IDs from relation value based on cardinality
1973
+ */
1974
+ private extractIds;
1975
+ /**
1976
+ * Get valid object IDs from relation targets
1977
+ */
1978
+ private getValidObjectIds;
1979
+ /**
1980
+ * Validate relations and throw if invalid
1981
+ */
1982
+ validateRelationsOrThrow(schema: ObjectDefinition, data: Record<string, unknown>): Promise<void>;
1983
+ /**
1984
+ * Get available options for a relation attribute
1985
+ * Searches across all target objects defined in the relation
1986
+ *
1987
+ * @param attribute - Relation attribute definition
1988
+ * @param tenantId - Tenant ID for multi-tenant isolation
1989
+ * @param params - Query parameters
1990
+ *
1991
+ * @example
1992
+ * ```typescript
1993
+ * const options = await relationService.getOptions(attribute, "tenant-1", {
1994
+ * query: "nike",
1995
+ * page: 1,
1996
+ * pageSize: 20
1997
+ * });
1998
+ * ```
1999
+ */
2000
+ getOptions(attribute: RelationAttribute, tenantId: string, params?: GetRelationOptionsParams): Promise<RelationOptionsResponse>;
2001
+ /**
2002
+ * Resolve record IDs to their display labels
2003
+ * Useful for displaying current values in the UI
2004
+ *
2005
+ * @param ids - Record IDs to resolve
2006
+ * @param tenantId - Tenant ID for multi-tenant isolation
2007
+ *
2008
+ * @example
2009
+ * ```typescript
2010
+ * const resolved = await relationService.resolveIds(["rec-1", "rec-2"], "tenant-1");
2011
+ * // [{ id: "rec-1", label: "Nike Air Max", objectName: "products", ... }]
2012
+ * ```
2013
+ */
2014
+ resolveIds(ids: string[], tenantId: string): Promise<RelationOption[]>;
2015
+ /**
2016
+ * Find a relation attribute by ID
2017
+ */
2018
+ findAttributeById(attributeId: string): Promise<RelationAttribute | null>;
2019
+ /**
2020
+ * Resolve display label from record values
2021
+ * Uses displayTemplate if provided, otherwise falls back to titleAttribute
2022
+ */
2023
+ private resolveLabel;
2024
+ }
2025
+
2026
+ /**
2027
+ * Service for managing user profiles
2028
+ * Handles user profile CRUD, auth provider sync, and role management
2029
+ */
2030
+ declare class UserProfileService {
2031
+ private adapter;
2032
+ private tenantId;
2033
+ constructor(adapter: DatabaseAdapter, tenantId: string);
2034
+ /**
2035
+ * Create a new user profile (typically after first auth)
2036
+ *
2037
+ * @param data - User profile creation data
2038
+ * @returns Created user profile
2039
+ *
2040
+ * @example
2041
+ * ```typescript
2042
+ * const service = new UserProfileService(adapter, "tenant-123");
2043
+ *
2044
+ * // After Supabase auth
2045
+ * const profile = await service.createProfile({
2046
+ * tenantId: "tenant-123",
2047
+ * authId: authUser.id,
2048
+ * email: authUser.email,
2049
+ * firstName: authUser.user_metadata.first_name,
2050
+ * lastName: authUser.user_metadata.last_name,
2051
+ * role: "member",
2052
+ * status: "active"
2053
+ * });
2054
+ * ```
2055
+ */
2056
+ createProfile(data: CreateUserProfile): Promise<UserProfile>;
2057
+ /**
2058
+ * Get user profile by ID
2059
+ */
2060
+ getProfile(profileId: string): Promise<UserProfile | null>;
2061
+ /**
2062
+ * Get user profile by ID or throw
2063
+ */
2064
+ getProfileOrThrow(profileId: string): Promise<UserProfile>;
2065
+ /**
2066
+ * Get user profile by auth provider ID
2067
+ *
2068
+ * @param authId - Auth provider user ID (Supabase, Clerk, etc.)
2069
+ * @returns User profile or null
2070
+ */
2071
+ getProfileByAuthId(authId: string): Promise<UserProfile | null>;
2072
+ /**
2073
+ * Get or create user profile (idempotent operation)
2074
+ * Useful for auth callbacks - ensures profile exists
2075
+ *
2076
+ * @param authId - Auth provider user ID
2077
+ * @param data - Profile data to create if doesn't exist
2078
+ * @returns Existing or newly created profile
2079
+ *
2080
+ * @example
2081
+ * ```typescript
2082
+ * // In Supabase auth callback
2083
+ * const profile = await service.getOrCreateProfile(
2084
+ * authUser.id,
2085
+ * {
2086
+ * tenantId: "tenant-123",
2087
+ * authId: authUser.id,
2088
+ * email: authUser.email,
2089
+ * role: "member",
2090
+ * status: "active"
2091
+ * }
2092
+ * );
2093
+ * ```
2094
+ */
2095
+ getOrCreateProfile(authId: string, data: CreateUserProfile): Promise<UserProfile>;
2096
+ /**
2097
+ * Update user profile
2098
+ */
2099
+ updateProfile(profileId: string, data: UpdateUserProfile): Promise<UserProfile>;
2100
+ /**
2101
+ * Delete user profile
2102
+ *
2103
+ * @param profileId - Profile UUID
2104
+ * @param options - Delete options
2105
+ */
2106
+ deleteProfile(profileId: string, options?: {
2107
+ checkAdmin?: boolean;
2108
+ }): Promise<void>;
2109
+ /**
2110
+ * List all user profiles for the tenant
2111
+ */
2112
+ listProfiles(options?: ListOptions): Promise<UserProfile[]>;
2113
+ /**
2114
+ * Update last login timestamp
2115
+ *
2116
+ * @param profileId - Profile UUID
2117
+ *
2118
+ * @example
2119
+ * ```typescript
2120
+ * // After successful auth
2121
+ * await service.updateLastLogin(profile.id);
2122
+ * ```
2123
+ */
2124
+ updateLastLogin(profileId: string): Promise<void>;
2125
+ /**
2126
+ * Change user role
2127
+ *
2128
+ * @param profileId - Profile UUID
2129
+ * @param newRole - New role
2130
+ */
2131
+ changeRole(profileId: string, newRole: string): Promise<UserProfile>;
2132
+ /**
2133
+ * Change user status
2134
+ *
2135
+ * @param profileId - Profile UUID
2136
+ * @param newStatus - New status
2137
+ */
2138
+ changeStatus(profileId: string, newStatus: "active" | "pending" | "inactive" | "suspended"): Promise<UserProfile>;
2139
+ /**
2140
+ * Get user by email
2141
+ */
2142
+ getProfileByEmail(email: string): Promise<UserProfile | null>;
2143
+ /**
2144
+ * Check if user has role
2145
+ */
2146
+ hasRole(profileId: string, role: string): Promise<boolean>;
2147
+ /**
2148
+ * Check if user is admin
2149
+ */
2150
+ isAdmin(profileId: string): Promise<boolean>;
2151
+ }
2152
+
2153
+ /**
2154
+ * Result of sync operation
2155
+ */
2156
+ interface SyncResult {
2157
+ success: boolean;
2158
+ objectsSynced: number;
2159
+ attributesSynced: number;
2160
+ objectsCreated: number;
2161
+ objectsUpdated: number;
2162
+ attributesCreated: number;
2163
+ attributesUpdated: number;
2164
+ attributesDeleted: number;
2165
+ errors: Array<{
2166
+ objectName: string;
2167
+ error: string;
2168
+ }>;
2169
+ }
2170
+ /**
2171
+ * Sync options
2172
+ */
2173
+ interface SyncOptions {
2174
+ dryRun?: boolean;
2175
+ verbose?: boolean;
2176
+ tenantId?: string;
2177
+ }
2178
+ /**
2179
+ * Sync native objects from registry to database
2180
+ *
2181
+ * This function:
2182
+ * 1. Reads all registered native objects from the registry
2183
+ * 2. Upserts them into the database (objects + attributes tables)
2184
+ * 3. Marks them as system=true for protection
2185
+ * 4. Removes attributes that were deleted from code
2186
+ *
2187
+ * @param adapter - Database adapter implementing DatabaseAdapter interface
2188
+ * @param nativeRegistry - Registry containing native objects
2189
+ * @param options - Sync options (dryRun, verbose, tenantId)
2190
+ * @returns Sync result with statistics
2191
+ *
2192
+ * @example
2193
+ * ```typescript
2194
+ * import { syncNativeObjects } from "@andyoucreate/schema/runtime";
2195
+ * import { registry } from "./objects/native";
2196
+ * import { drizzleAdapter } from "./db/adapter";
2197
+ *
2198
+ * const result = await syncNativeObjects(drizzleAdapter, registry, {
2199
+ * verbose: true,
2200
+ * tenantId: "default"
2201
+ * });
2202
+ *
2203
+ * if (result.success) {
2204
+ * console.log(`✓ Synced ${result.objectsSynced} objects`);
2205
+ * } else {
2206
+ * console.error("Sync errors:", result.errors);
2207
+ * }
2208
+ * ```
2209
+ */
2210
+ declare function syncNativeObjects(adapter: DatabaseAdapter, nativeRegistry: typeof registry, options?: SyncOptions): Promise<SyncResult>;
2211
+ /**
2212
+ * Verify that all native objects are synced to database
2213
+ *
2214
+ * @param adapter - Database adapter
2215
+ * @param nativeRegistry - Registry containing native objects
2216
+ * @returns true if all objects are synced, false otherwise
2217
+ *
2218
+ * @example
2219
+ * ```typescript
2220
+ * const isSynced = await verifyNativeObjectsSync(adapter, registry);
2221
+ * if (!isSynced) {
2222
+ * console.warn("Native objects not synced, running sync...");
2223
+ * await syncNativeObjects(adapter, registry);
2224
+ * }
2225
+ * ```
2226
+ */
2227
+ declare function verifyNativeObjectsSync(adapter: DatabaseAdapter, nativeRegistry: typeof registry): Promise<boolean>;
2228
+ /**
2229
+ * Get sync statistics without modifying database
2230
+ *
2231
+ * @param adapter - Database adapter
2232
+ * @param nativeRegistry - Registry containing native objects
2233
+ * @returns Sync result (dry run)
2234
+ */
2235
+ declare function getSyncPreview(adapter: DatabaseAdapter, nativeRegistry: typeof registry): Promise<SyncResult>;
2236
+
2237
+ 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 };