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