@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,2788 @@
1
+ import { ColorId, IconName, CountryIso3, CurrencyCode, MimeType } from '@stndrds/constants';
2
+ import { z } from 'zod';
3
+
4
+ /**
5
+ * UUID string type for all identifiers
6
+ */
7
+ type Uuid = string;
8
+ /**
9
+ * Tenant identifier for multi-tenant isolation
10
+ * Phase 1: Type defined but not yet used in interfaces
11
+ * Phase 2+: Will be added to ObjectRecord and services for SaaS mode
12
+ */
13
+ type TenantId = string;
14
+ /**
15
+ * Generate a unique UUID v4
16
+ * Uses crypto.randomUUID() when available (Node.js 19+, modern browsers)
17
+ * Falls back to a manual implementation for older environments
18
+ *
19
+ * @returns A UUID v4 string
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const id = generateId();
24
+ * // "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
25
+ * ```
26
+ */
27
+ declare function generateId(): Uuid;
28
+ /**
29
+ * Generate a prefixed ID for better debugging and readability
30
+ *
31
+ * @param prefix - Prefix for the ID (e.g., "obj", "attr", "rec")
32
+ * @returns A prefixed UUID string
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const objectId = generatePrefixedId("obj");
37
+ * // "obj_a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
38
+ *
39
+ * const attrId = generatePrefixedId("attr");
40
+ * // "attr_a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
41
+ * ```
42
+ */
43
+ declare function generatePrefixedId(prefix: string): Uuid;
44
+
45
+ type AttributeType = "text" | "textarea" | "number" | "checkbox" | "date" | "phone" | "currency" | "status" | "location" | "timestamp" | "select" | "multiselect" | "file" | "user" | "relation" | "rating";
46
+ /**
47
+ * Status group categorization
48
+ */
49
+ type StatusGroup = "idle" | "in_progress" | "finished";
50
+ /**
51
+ * Unified option type for select-like fields
52
+ */
53
+ interface Option {
54
+ id: string;
55
+ label: string;
56
+ value: string;
57
+ color?: ColorId;
58
+ icon?: IconName;
59
+ description?: string;
60
+ group?: StatusGroup;
61
+ }
62
+ /**
63
+ * Attribute grouping for UI organization
64
+ */
65
+ interface AttributeGroup {
66
+ id: string;
67
+ label: string;
68
+ description?: string;
69
+ attributeIds: string[];
70
+ collapsible?: boolean;
71
+ collapsed?: boolean;
72
+ order?: number;
73
+ }
74
+ interface BaseAttribute<DefaultValueType = unknown> {
75
+ id: Uuid;
76
+ name: string;
77
+ label: string;
78
+ type: AttributeType;
79
+ required: boolean;
80
+ disabled?: boolean;
81
+ placeholder?: string;
82
+ description?: string;
83
+ defaultValue?: DefaultValueType;
84
+ icon?: IconName;
85
+ order?: number;
86
+ hidden?: boolean;
87
+ archived?: boolean;
88
+ deprecated?: boolean;
89
+ system?: boolean;
90
+ metadata?: Record<string, unknown>;
91
+ }
92
+ interface TextAttribute extends BaseAttribute<string> {
93
+ type: "text";
94
+ minLength?: number;
95
+ maxLength?: number;
96
+ pattern?: string;
97
+ }
98
+ type NumberUnit = "integer" | "decimal" | "percentage";
99
+ interface NumberAttribute extends BaseAttribute<number> {
100
+ type: "number";
101
+ min?: number;
102
+ max?: number;
103
+ unit?: NumberUnit;
104
+ decimals?: number;
105
+ }
106
+ interface CheckboxAttribute extends BaseAttribute<boolean> {
107
+ type: "checkbox";
108
+ }
109
+ type DateFormat = "short" | "long" | "full" | "relative";
110
+ type DateValue = string | "today";
111
+ interface DateAttribute extends BaseAttribute<string> {
112
+ type: "date";
113
+ dateFormat?: DateFormat;
114
+ minDate?: DateValue;
115
+ maxDate?: DateValue;
116
+ }
117
+ interface Phone {
118
+ countryCode: CountryIso3;
119
+ phoneNumber: string;
120
+ }
121
+ interface PhoneAttribute extends BaseAttribute<Phone> {
122
+ type: "phone";
123
+ defaultCountryCode?: CountryIso3;
124
+ }
125
+ interface Currency {
126
+ code: CurrencyCode;
127
+ value: number;
128
+ }
129
+ interface CurrencyAttribute extends BaseAttribute<Currency> {
130
+ type: "currency";
131
+ defaultCurrency?: CurrencyCode;
132
+ allowedCurrencies?: CurrencyCode[];
133
+ }
134
+ /**
135
+ * StatusAttribute - For workflow states with semantic grouping (idle/in_progress/finished)
136
+ * Use this for: Task status, Order status, Project phases, Process states
137
+ * Use SelectAttribute for: Categories, Types, simple choices without workflow
138
+ */
139
+ interface StatusAttribute extends BaseAttribute<string> {
140
+ type: "status";
141
+ options: Option[];
142
+ }
143
+ interface Location {
144
+ address?: string;
145
+ address2?: string;
146
+ city?: string;
147
+ state?: string;
148
+ postalCode?: string;
149
+ country?: CountryIso3;
150
+ latitude?: number;
151
+ longitude?: number;
152
+ }
153
+ type LocationGranularity = "full" | "address" | "city" | "state" | "country" | "coordinates";
154
+ interface LocationAttribute extends BaseAttribute<Location> {
155
+ type: "location";
156
+ granularity: LocationGranularity;
157
+ enableAutocomplete?: boolean;
158
+ enableMap?: boolean;
159
+ defaultCountry?: CountryIso3;
160
+ allowedCountries?: CountryIso3[];
161
+ displayFormat?: "single_line" | "multi_line" | "compact";
162
+ }
163
+ interface TimestampAttribute extends BaseAttribute<number> {
164
+ type: "timestamp";
165
+ autoUpdate?: boolean;
166
+ }
167
+ /**
168
+ * SelectAttribute - For simple single-choice selection
169
+ * Use this for: Categories, Document types, Departments, Priorities
170
+ * Options can be grouped (e.g., countries by continent) but no workflow logic
171
+ */
172
+ interface SelectAttribute extends BaseAttribute<string> {
173
+ type: "select";
174
+ options: Option[];
175
+ }
176
+ interface MultiselectAttribute extends BaseAttribute<string[]> {
177
+ type: "multiselect";
178
+ options: Option[];
179
+ }
180
+ type DocumentType = "id_card" | "passport" | "incorporation_certificate" | "driver_license" | "birth_certificate" | "residence_permit" | "bank_statement" | "proof_of_address" | "custom";
181
+ type DocumentFace = "front" | "back" | "single";
182
+ interface DocumentTypeConfig {
183
+ type: DocumentType;
184
+ label: string;
185
+ faces: DocumentFace[];
186
+ attributeMapping?: {
187
+ attributeId: string;
188
+ extractedKey: string;
189
+ face?: DocumentFace;
190
+ required?: boolean;
191
+ }[];
192
+ }
193
+ interface FileVerificationConfig {
194
+ enabled: boolean;
195
+ documentTypes: DocumentTypeConfig[];
196
+ autoExtract?: boolean;
197
+ autoValidate?: boolean;
198
+ }
199
+ interface FileAttribute extends BaseAttribute<string> {
200
+ type: "file";
201
+ maxFiles?: number;
202
+ maxSize?: number;
203
+ allowedTypes?: MimeType[] | readonly MimeType[];
204
+ verification?: FileVerificationConfig;
205
+ }
206
+ interface UserAttribute extends BaseAttribute<string> {
207
+ type: "user";
208
+ allowedRoles?: string[];
209
+ }
210
+ /**
211
+ * Target object for a relation - defines which objects can be linked
212
+ */
213
+ interface RelationTarget {
214
+ /** Object name (e.g., "companies", "contacts") */
215
+ object: string;
216
+ /**
217
+ * Display template for the label using mustache-like syntax
218
+ * @example "{name}" or "{firstName} {lastName} — {email}"
219
+ */
220
+ displayTemplate?: string;
221
+ /**
222
+ * Optional filter to restrict available records
223
+ * @example { status: "active" }
224
+ */
225
+ filter?: Record<string, unknown>;
226
+ }
227
+ /**
228
+ * 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
+ * Field definition within a form group
729
+ */
730
+ interface Field {
731
+ /** Attribute name to display */
732
+ attribute: string;
733
+ /** Grid span (1-12 columns) */
734
+ span?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
735
+ /** Override label for this view */
736
+ label?: string;
737
+ /** Force read-only display */
738
+ readOnly?: boolean;
739
+ }
740
+ /**
741
+ * Group of fields for organizing forms
742
+ */
743
+ interface Group {
744
+ id: string;
745
+ label: string;
746
+ description?: string;
747
+ fields: Field[];
748
+ collapsible?: boolean;
749
+ collapsed?: boolean;
750
+ order?: number;
751
+ }
752
+ type TabType = "form" | "table" | "custom";
753
+ /**
754
+ * Base properties shared by all tab types
755
+ */
756
+ interface BaseTab {
757
+ id: string;
758
+ name: string;
759
+ label: string;
760
+ icon?: IconName;
761
+ order?: number;
762
+ /** If true, tab is defined by developer (protected) */
763
+ system?: boolean;
764
+ }
765
+ /**
766
+ * Form tab - displays attributes organized in groups
767
+ */
768
+ interface FormTab extends BaseTab {
769
+ type: "form";
770
+ groups: Group[];
771
+ }
772
+ /**
773
+ * Table tab - displays records from a relation attribute
774
+ */
775
+ interface TableTab extends BaseTab {
776
+ type: "table";
777
+ /** Name of the relation attribute */
778
+ relation: string;
779
+ /** Columns to display (attribute names from target object) */
780
+ columns: string[];
781
+ /** Allow creating new records */
782
+ allowCreate?: boolean;
783
+ /** Allow inline editing */
784
+ allowEdit?: boolean;
785
+ /** Allow deleting records */
786
+ allowDelete?: boolean;
787
+ /** Default filters */
788
+ filters?: Record<string, unknown>;
789
+ /** Default sort */
790
+ sort?: {
791
+ field: string;
792
+ order: "asc" | "desc";
793
+ };
794
+ }
795
+ /**
796
+ * Custom tab - renders a developer-defined component
797
+ */
798
+ interface CustomTab extends BaseTab {
799
+ type: "custom";
800
+ /** Component identifier to render */
801
+ component: string;
802
+ /** Props to pass to the component */
803
+ props?: Record<string, unknown>;
804
+ }
805
+ /**
806
+ * Union of all tab types
807
+ */
808
+ type Tab = FormTab | TableTab | CustomTab;
809
+ /**
810
+ * View definition - organizes object attributes into tabs/pages
811
+ *
812
+ * @example
813
+ * ```typescript
814
+ * const companyView: ViewDefinition = {
815
+ * name: "detail",
816
+ * label: "Company Detail",
817
+ * object: "companies",
818
+ * tabs: [
819
+ * { type: "form", name: "general", label: "Info", groups: [...] },
820
+ * { type: "table", name: "contacts", label: "Contacts", relation: "contacts", columns: [...] }
821
+ * ],
822
+ * default: true,
823
+ * system: true
824
+ * };
825
+ * ```
826
+ */
827
+ interface ViewDefinition {
828
+ id?: Uuid;
829
+ /** Technical name (kebab-case) */
830
+ name: string;
831
+ /** Display label */
832
+ label: string;
833
+ /** Description */
834
+ description?: string;
835
+ /** Icon */
836
+ icon?: IconName;
837
+ /** Object this view belongs to (object name) */
838
+ object: string;
839
+ /** Tabs in this view */
840
+ tabs: Tab[];
841
+ /** Default view for this object */
842
+ default?: boolean;
843
+ /** System view (defined by developer, protected) */
844
+ system?: boolean;
845
+ /** Extensible metadata */
846
+ metadata?: Record<string, unknown>;
847
+ }
848
+ /**
849
+ * Check if a tab is a form tab
850
+ */
851
+ declare function isFormTab(tab: Tab): tab is FormTab;
852
+ /**
853
+ * Check if a tab is a table tab
854
+ */
855
+ declare function isTableTab(tab: Tab): tab is TableTab;
856
+ /**
857
+ * Check if a tab is a custom tab
858
+ */
859
+ declare function isCustomTab(tab: Tab): tab is CustomTab;
860
+
861
+ /**
862
+ * Registry for native objects defined in code
863
+ * Native objects are system objects that cannot be deleted/modified by clients
864
+ * They are defined by developers and synced to the database at startup
865
+ */
866
+ declare class NativeObjectRegistryClass {
867
+ private objects;
868
+ /**
869
+ * Register one or more native objects
870
+ * Auto-builds if ObjectBuilder is passed instead of ObjectDefinition
871
+ * @param objects - Single object/builder or array of objects/builders to register
872
+ * @returns this (for chaining)
873
+ * @throws Error if object is invalid or already registered
874
+ *
875
+ * @example
876
+ * ```typescript
877
+ * // No .build() needed - registry auto-builds!
878
+ * const PRODUCT = object({ id: "obj-product", name: "products", label: "Product" })
879
+ * .system()
880
+ * .attribute(text({ id: "attr-name", name: "name", label: "Name" }));
881
+ *
882
+ * registry.register(PRODUCT); // ← Auto-builds here
883
+ *
884
+ * // Can still explicitly .build() if you want
885
+ * registry.register(PRODUCT.build());
886
+ *
887
+ * // Register multiple
888
+ * registry.register([PRODUCT, ORDER, CUSTOMER]);
889
+ * ```
890
+ */
891
+ register(objects: ObjectDefinition | ObjectDefinition[] | {
892
+ build: () => ObjectDefinition;
893
+ } | Array<{
894
+ build: () => ObjectDefinition;
895
+ }>): this;
896
+ /**
897
+ * Validate and register a single object
898
+ * @internal
899
+ */
900
+ private validateAndRegister;
901
+ /**
902
+ * Get a native object by its name
903
+ * @param name - The object name
904
+ * @returns The object definition or undefined if not found
905
+ *
906
+ * @example
907
+ * ```typescript
908
+ * const product = registry.getByName("products");
909
+ * if (product) {
910
+ * console.log(product.label); // "Product"
911
+ * }
912
+ * ```
913
+ */
914
+ getByName(name: string): ObjectDefinition | undefined;
915
+ /**
916
+ * Get a native object or throw if not found
917
+ * @param name - The object name
918
+ * @returns The object definition
919
+ * @throws Error if not found
920
+ */
921
+ getByNameOrThrow(name: string): ObjectDefinition;
922
+ /**
923
+ * Get all registered native objects
924
+ * @returns Array of all registered object definitions
925
+ */
926
+ getAll(): ObjectDefinition[];
927
+ /**
928
+ * Get all registered native object names
929
+ * @returns Array of object names
930
+ */
931
+ listNames(): string[];
932
+ /**
933
+ * Check if a native object is registered
934
+ * @param name - The object name
935
+ * @returns true if registered, false otherwise
936
+ */
937
+ has(name: string): boolean;
938
+ /**
939
+ * Get the number of registered objects
940
+ */
941
+ get size(): number;
942
+ /**
943
+ * Clear all registered objects (useful for testing)
944
+ * @internal
945
+ */
946
+ clear(): void;
947
+ /**
948
+ * Get registry summary for debugging
949
+ */
950
+ summary(): string;
951
+ /**
952
+ * Log registry summary to console
953
+ */
954
+ debug(): void;
955
+ }
956
+ /**
957
+ * Singleton instance of the registry
958
+ * Use this to register and retrieve native objects
959
+ *
960
+ * @example
961
+ * ```typescript
962
+ * import { registry } from "@stndrds/schema";
963
+ *
964
+ * // Register objects
965
+ * registry.register([PRODUCT, ORDER, CUSTOMER]);
966
+ *
967
+ * // Get object by name
968
+ * const product = registry.getByName("products");
969
+ *
970
+ * // List all
971
+ * console.log(registry.listNames()); // ["products", "orders", ...]
972
+ *
973
+ * // Debug
974
+ * registry.debug();
975
+ * ```
976
+ */
977
+ declare const registry: NativeObjectRegistryClass;
978
+
979
+ /**
980
+ * Registry for native view definitions
981
+ *
982
+ * Views registered here are considered "system" views, defined by the developer.
983
+ * They are protected from modification by end-users and synced to the database
984
+ * at application startup.
985
+ *
986
+ * @example
987
+ * ```typescript
988
+ * import { view, group, viewRegistry } from "@stndrds/schema";
989
+ *
990
+ * const COMPANY_DETAIL = view("detail", "Detail")
991
+ * .for("companies")
992
+ * .system()
993
+ * .tab("general", "Info")
994
+ * .form(group("main", "Main").fields("name", "status"))
995
+ * .build();
996
+ *
997
+ * viewRegistry.register(COMPANY_DETAIL);
998
+ * ```
999
+ */
1000
+ declare class ViewRegistry {
1001
+ private views;
1002
+ private byObject;
1003
+ /**
1004
+ * Register a native view
1005
+ * @param viewOrViews - Single view or array of views
1006
+ * @throws Error if view is not marked as system
1007
+ * @throws Error if view with same name already exists for the object
1008
+ */
1009
+ register(viewOrViews: ViewDefinition | ViewDefinition[]): this;
1010
+ private registerSingle;
1011
+ /**
1012
+ * Get all views for an object
1013
+ */
1014
+ getByObjectName(objectName: string): ViewDefinition[];
1015
+ /**
1016
+ * Get a specific view by object and view name
1017
+ */
1018
+ get(objectName: string, viewName: string): ViewDefinition | undefined;
1019
+ /**
1020
+ * Get a view or throw if not found
1021
+ */
1022
+ getOrThrow(objectName: string, viewName: string): ViewDefinition;
1023
+ /**
1024
+ * Get all registered views
1025
+ */
1026
+ getAll(): ViewDefinition[];
1027
+ /**
1028
+ * Check if a view exists
1029
+ */
1030
+ has(objectName: string, viewName: string): boolean;
1031
+ /**
1032
+ * Check if any views exist for an object
1033
+ */
1034
+ hasObject(objectName: string): boolean;
1035
+ /**
1036
+ * Get number of registered views
1037
+ */
1038
+ get size(): number;
1039
+ /**
1040
+ * List all object names that have views
1041
+ */
1042
+ listObjectNames(): string[];
1043
+ /**
1044
+ * Get default view for an object (if any)
1045
+ */
1046
+ getDefault(objectName: string): ViewDefinition | undefined;
1047
+ /**
1048
+ * Clear all registered views (for testing)
1049
+ */
1050
+ clear(): void;
1051
+ /**
1052
+ * Generate summary string for debugging
1053
+ */
1054
+ summary(): string;
1055
+ /**
1056
+ * Log summary to console
1057
+ */
1058
+ debug(): void;
1059
+ private makeKey;
1060
+ }
1061
+ /**
1062
+ * Global registry for native views
1063
+ */
1064
+ declare const viewRegistry: ViewRegistry;
1065
+
1066
+ /**
1067
+ * Create a Zod schema for a text attribute
1068
+ */
1069
+ declare function createTextValidator(attr: TextAttribute): z.ZodString;
1070
+ /**
1071
+ * Create a Zod schema for a number attribute
1072
+ */
1073
+ declare function createNumberValidator(attr: NumberAttribute): z.ZodNumber;
1074
+ /**
1075
+ * Create a Zod schema for a checkbox attribute
1076
+ */
1077
+ declare function createCheckboxValidator(_attr: CheckboxAttribute): z.ZodBoolean;
1078
+ /**
1079
+ * Create a Zod schema for a date attribute
1080
+ */
1081
+ declare function createDateValidator(attr: DateAttribute): z.ZodString;
1082
+ /**
1083
+ * Create a Zod schema for a phone attribute
1084
+ */
1085
+ declare function createPhoneValidator(_attr: PhoneAttribute): z.ZodType<{
1086
+ countryCode: string;
1087
+ phoneNumber: string;
1088
+ }>;
1089
+ /**
1090
+ * Create a Zod schema for a currency attribute
1091
+ */
1092
+ declare function createCurrencyValidator(_attr: CurrencyAttribute): z.ZodType<{
1093
+ code: string;
1094
+ value: number;
1095
+ }>;
1096
+ /**
1097
+ * Create a Zod schema for a status attribute
1098
+ */
1099
+ declare function createStatusValidator(attr: StatusAttribute): z.ZodEnum<[string, ...string[]]>;
1100
+ /**
1101
+ * Create a Zod schema for a select attribute
1102
+ */
1103
+ declare function createSelectValidator(attr: SelectAttribute): z.ZodEnum<[string, ...string[]]>;
1104
+ /**
1105
+ * Create a Zod schema for a multiselect attribute
1106
+ */
1107
+ declare function createMultiselectValidator(attr: MultiselectAttribute): z.ZodArray<z.ZodEnum<[string, ...string[]]>>;
1108
+ /**
1109
+ * Create a Zod schema for a location attribute
1110
+ */
1111
+ type LocationShape = {
1112
+ address?: string;
1113
+ address2?: string;
1114
+ city?: string;
1115
+ state?: string;
1116
+ postalCode?: string;
1117
+ country?: string;
1118
+ latitude?: number;
1119
+ longitude?: number;
1120
+ };
1121
+ declare function createLocationValidator(_attr: LocationAttribute): z.ZodType<LocationShape>;
1122
+ /**
1123
+ * Create a Zod schema for a timestamp attribute
1124
+ */
1125
+ declare function createTimestampValidator(_attr: TimestampAttribute): z.ZodNumber;
1126
+ /**
1127
+ * Create a Zod schema for a file attribute
1128
+ */
1129
+ declare function createFileValidator(_attr: FileAttribute): z.ZodString;
1130
+ /**
1131
+ * Create a Zod schema for a user attribute
1132
+ */
1133
+ declare function createUserValidator(_attr: UserAttribute): z.ZodString;
1134
+ /**
1135
+ * Create a Zod schema for a single relation attribute (cardinality: "one")
1136
+ */
1137
+ declare function createSingleRelationValidator(attr: SingleRelationAttribute): z.ZodUnion<[z.ZodString, z.ZodNull]>;
1138
+ /**
1139
+ * Create a Zod schema for a multi relation attribute (cardinality: "many")
1140
+ */
1141
+ declare function createMultiRelationValidator(attr: MultiRelationAttribute): z.ZodArray<z.ZodString>;
1142
+ /**
1143
+ * Create a Zod schema for a relation attribute
1144
+ * Dispatches to single or multi validator based on cardinality
1145
+ */
1146
+ declare function createRelationValidator(attr: RelationAttribute): z.ZodUnion<[z.ZodString, z.ZodNull]> | z.ZodArray<z.ZodString>;
1147
+ /**
1148
+ * Create a Zod schema for a rating attribute
1149
+ */
1150
+ declare function createRatingValidator(attr: RatingAttribute): z.ZodNumber;
1151
+ /**
1152
+ * Create a Zod schema for any attribute type
1153
+ */
1154
+ declare function createAttributeValidator(attr: Attribute): z.ZodTypeAny;
1155
+ /**
1156
+ * Create a Zod schema for an entire object
1157
+ */
1158
+ declare function createObjectValidator(objectDef: ObjectDefinition): z.ZodType<Record<string, unknown>>;
1159
+ /**
1160
+ * Validation result
1161
+ */
1162
+ interface ValidationResult {
1163
+ success: boolean;
1164
+ data?: Record<string, unknown>;
1165
+ errors?: Array<{
1166
+ path: string[];
1167
+ message: string;
1168
+ }>;
1169
+ }
1170
+ /**
1171
+ * Validate data against an attribute schema
1172
+ */
1173
+ declare function validateAttribute(attr: Attribute, value: unknown): ValidationResult;
1174
+ /**
1175
+ * Validate data against an object schema
1176
+ */
1177
+ declare function validateObject(objectDef: ObjectDefinition, data: Record<string, unknown>): ValidationResult;
1178
+ /**
1179
+ * Validate and throw if invalid
1180
+ */
1181
+ declare function validateObjectOrThrow(objectDef: ObjectDefinition, data: Record<string, unknown>): Record<string, unknown>;
1182
+ /**
1183
+ * Create a Zod schema for draft validation.
1184
+ * All attributes become optional, but provided values are still validated.
1185
+ */
1186
+ declare function createDraftValidator(objectDef: ObjectDefinition): z.ZodType<Record<string, unknown>>;
1187
+ /**
1188
+ * Validate data in draft mode.
1189
+ * - All attributes are treated as optional (no required validation)
1190
+ * - Provided values are still validated for format/type correctness
1191
+ *
1192
+ * Use this when creating records that may be incomplete (drafts).
1193
+ *
1194
+ * @example
1195
+ * ```typescript
1196
+ * const result = validateDraft(PRODUCT, { name: "Draft" });
1197
+ * // → success even if "price" is required but missing
1198
+ *
1199
+ * const result2 = validateDraft(PRODUCT, { price: -10 });
1200
+ * // → fails because price must be >= 0 (format validation still applies)
1201
+ * ```
1202
+ */
1203
+ declare function validateDraft(objectDef: ObjectDefinition, data: Record<string, unknown>): ValidationResult;
1204
+ /**
1205
+ * Validate draft data and throw if format validation fails.
1206
+ */
1207
+ declare function validateDraftOrThrow(objectDef: ObjectDefinition, data: Record<string, unknown>): Record<string, unknown>;
1208
+ /**
1209
+ * Get the list of required attributes that are missing values.
1210
+ *
1211
+ * @example
1212
+ * ```typescript
1213
+ * const missing = getMissingRequiredAttributes(PRODUCT, { name: "Test" });
1214
+ * // → [priceAttribute, statusAttribute] if price and status are required but missing
1215
+ * ```
1216
+ */
1217
+ declare function getMissingRequiredAttributes(objectDef: ObjectDefinition, data: Record<string, unknown>): Attribute[];
1218
+ /**
1219
+ * Check if a record is complete (all required attributes have valid values).
1220
+ *
1221
+ * @returns `true` if all required values are present and valid, `false` otherwise
1222
+ */
1223
+ declare function isRecordComplete(objectDef: ObjectDefinition, data: Record<string, unknown>): boolean;
1224
+ /**
1225
+ * Compute the completion status of a record based on its data.
1226
+ *
1227
+ * - `"complete"`: All required values are present and valid
1228
+ * - `"draft"`: One or more required values are missing or invalid
1229
+ *
1230
+ * This function is used to dynamically determine the status when
1231
+ * creating or updating records.
1232
+ *
1233
+ * @example
1234
+ * ```typescript
1235
+ * const status = computeRecordStatus(PRODUCT, {
1236
+ * name: "Nike Air Max",
1237
+ * price: 129.99,
1238
+ * status: "active"
1239
+ * });
1240
+ * // → "complete"
1241
+ *
1242
+ * const status2 = computeRecordStatus(PRODUCT, { name: "Draft Product" });
1243
+ * // → "draft" (missing required fields)
1244
+ * ```
1245
+ */
1246
+ declare function computeRecordStatus(objectDef: ObjectDefinition, data: Record<string, unknown>): CompletionStatus;
1247
+
1248
+ /**
1249
+ * Object as stored in database (metadata)
1250
+ */
1251
+ interface DBObject extends Timestamps {
1252
+ id: Uuid;
1253
+ tenantId: TenantId;
1254
+ name: string;
1255
+ label: string;
1256
+ description?: string;
1257
+ icon?: IconName;
1258
+ system: boolean;
1259
+ metadata?: Record<string, unknown>;
1260
+ }
1261
+ interface CreateDBObject {
1262
+ tenantId: TenantId;
1263
+ name: string;
1264
+ label: string;
1265
+ description?: string;
1266
+ icon?: IconName;
1267
+ system?: boolean;
1268
+ metadata?: Record<string, unknown>;
1269
+ }
1270
+ interface UpdateDBObject {
1271
+ label?: string;
1272
+ description?: string;
1273
+ icon?: IconName;
1274
+ metadata?: Record<string, unknown>;
1275
+ }
1276
+ interface UpsertDBObject extends CreateDBObject {
1277
+ system: boolean;
1278
+ }
1279
+ /**
1280
+ * Attribute as stored in database (metadata)
1281
+ */
1282
+ interface DBAttribute extends Timestamps {
1283
+ id: Uuid;
1284
+ objectId: Uuid;
1285
+ name: string;
1286
+ label: string;
1287
+ type: AttributeType;
1288
+ description?: string;
1289
+ icon?: IconName;
1290
+ system: boolean;
1291
+ required: boolean;
1292
+ unique: boolean;
1293
+ order: number;
1294
+ hidden?: boolean;
1295
+ archived?: boolean;
1296
+ deprecated?: boolean;
1297
+ defaultValue?: unknown;
1298
+ config: Record<string, unknown>;
1299
+ }
1300
+ interface CreateDBAttribute {
1301
+ objectId: Uuid;
1302
+ name: string;
1303
+ label: string;
1304
+ type: AttributeType;
1305
+ description?: string;
1306
+ icon?: IconName;
1307
+ system?: boolean;
1308
+ required?: boolean;
1309
+ unique?: boolean;
1310
+ order?: number;
1311
+ hidden?: boolean;
1312
+ archived?: boolean;
1313
+ deprecated?: boolean;
1314
+ defaultValue?: unknown;
1315
+ config?: Record<string, unknown>;
1316
+ }
1317
+ interface UpdateDBAttribute {
1318
+ label?: string;
1319
+ description?: string;
1320
+ icon?: IconName;
1321
+ required?: boolean;
1322
+ order?: number;
1323
+ hidden?: boolean;
1324
+ archived?: boolean;
1325
+ deprecated?: boolean;
1326
+ defaultValue?: unknown;
1327
+ config?: Record<string, unknown>;
1328
+ }
1329
+ interface UpsertDBAttribute extends CreateDBAttribute {
1330
+ objectId: Uuid;
1331
+ name: string;
1332
+ }
1333
+
1334
+ /**
1335
+ * Object record creation data
1336
+ */
1337
+ interface CreateObjectRecord {
1338
+ tenantId: TenantId;
1339
+ objectId: Uuid;
1340
+ data: Record<string, unknown>;
1341
+ /**
1342
+ * Completion status of the record.
1343
+ * - `draft`: Record is incomplete (missing required values)
1344
+ * - `complete`: All required values are present and valid
1345
+ */
1346
+ completionStatus: "draft" | "complete";
1347
+ }
1348
+ /**
1349
+ * Generic list options for pagination and sorting
1350
+ */
1351
+ interface ListOptions {
1352
+ limit?: number;
1353
+ offset?: number;
1354
+ orderBy?: string;
1355
+ orderDirection?: "asc" | "desc";
1356
+ }
1357
+ /**
1358
+ * Search options for full-text search
1359
+ */
1360
+ interface SearchOptions extends ListOptions {
1361
+ filters?: Record<string, unknown>;
1362
+ highlight?: boolean;
1363
+ }
1364
+ /**
1365
+ * File-specific list options
1366
+ */
1367
+ interface FileListOptions extends ListOptions {
1368
+ folderPath?: string;
1369
+ mimeType?: string;
1370
+ uploadedBy?: Uuid;
1371
+ includeDeleted?: boolean;
1372
+ }
1373
+ /**
1374
+ * View as stored in database
1375
+ */
1376
+ interface DBView extends Timestamps {
1377
+ id: Uuid;
1378
+ tenantId: TenantId;
1379
+ objectId?: Uuid;
1380
+ objectName: string;
1381
+ name: string;
1382
+ label: string;
1383
+ description?: string;
1384
+ icon?: IconName;
1385
+ tabs: Tab[];
1386
+ default: boolean;
1387
+ system: boolean;
1388
+ metadata?: Record<string, unknown>;
1389
+ }
1390
+ interface CreateDBView {
1391
+ tenantId: TenantId;
1392
+ objectId?: Uuid;
1393
+ objectName: string;
1394
+ name: string;
1395
+ label: string;
1396
+ description?: string;
1397
+ icon?: IconName;
1398
+ tabs: Tab[];
1399
+ default?: boolean;
1400
+ system?: boolean;
1401
+ metadata?: Record<string, unknown>;
1402
+ }
1403
+ interface UpdateDBView {
1404
+ label?: string;
1405
+ description?: string;
1406
+ icon?: IconName;
1407
+ tabs?: Tab[];
1408
+ default?: boolean;
1409
+ metadata?: Record<string, unknown>;
1410
+ }
1411
+ interface UpsertDBView {
1412
+ tenantId: TenantId;
1413
+ objectName: string;
1414
+ name: string;
1415
+ label: string;
1416
+ description?: string;
1417
+ icon?: IconName;
1418
+ tabs: Tab[];
1419
+ default?: boolean;
1420
+ system?: boolean;
1421
+ metadata?: Record<string, unknown>;
1422
+ }
1423
+ /**
1424
+ * Result of an operation
1425
+ */
1426
+ interface OperationResult<T = unknown> {
1427
+ success: boolean;
1428
+ data?: T;
1429
+ error?: {
1430
+ code: string;
1431
+ message: string;
1432
+ details?: Record<string, unknown>;
1433
+ };
1434
+ }
1435
+
1436
+ /**
1437
+ * Repository for objects table (metadata)
1438
+ */
1439
+ interface ObjectsRepository {
1440
+ /**
1441
+ * Find object by ID
1442
+ */
1443
+ findById(id: Uuid): Promise<DBObject | null>;
1444
+ /**
1445
+ * Find object by name and tenant
1446
+ */
1447
+ findByName(tenantId: TenantId, name: string): Promise<DBObject | null>;
1448
+ /**
1449
+ * Find system/native object by name (system=true, for sync)
1450
+ */
1451
+ findSystemByName(name: string): Promise<DBObject | null>;
1452
+ /**
1453
+ * Create object
1454
+ */
1455
+ create(data: CreateDBObject): Promise<DBObject>;
1456
+ /**
1457
+ * Update object
1458
+ */
1459
+ update(id: Uuid, data: Partial<UpdateDBObject>): Promise<DBObject>;
1460
+ /**
1461
+ * Delete object
1462
+ */
1463
+ delete(id: Uuid): Promise<void>;
1464
+ /**
1465
+ * List all objects for a tenant
1466
+ */
1467
+ list(tenantId: TenantId): Promise<DBObject[]>;
1468
+ /**
1469
+ * Upsert object (create or update based on nativeObjectId)
1470
+ */
1471
+ upsert(data: UpsertDBObject): Promise<DBObject>;
1472
+ }
1473
+ /**
1474
+ * Repository for attributes table (metadata)
1475
+ */
1476
+ interface AttributesRepository {
1477
+ /**
1478
+ * Find attribute by ID
1479
+ */
1480
+ findById(id: Uuid): Promise<DBAttribute | null>;
1481
+ /**
1482
+ * Find attributes by object ID
1483
+ */
1484
+ findByObjectId(objectId: Uuid): Promise<DBAttribute[]>;
1485
+ /**
1486
+ * Create attribute
1487
+ */
1488
+ create(data: CreateDBAttribute): Promise<DBAttribute>;
1489
+ /**
1490
+ * Update attribute
1491
+ */
1492
+ update(id: Uuid, data: Partial<UpdateDBAttribute>): Promise<DBAttribute>;
1493
+ /**
1494
+ * Delete attribute
1495
+ */
1496
+ delete(id: Uuid): Promise<void>;
1497
+ /**
1498
+ * Delete multiple attributes by names (for sync cleanup)
1499
+ */
1500
+ deleteByNames(objectId: Uuid, excludeNames: string[]): Promise<number>;
1501
+ /**
1502
+ * Upsert attribute (create or update based on objectId + name)
1503
+ */
1504
+ upsert(data: UpsertDBAttribute): Promise<DBAttribute>;
1505
+ }
1506
+ /**
1507
+ * Repository for user_profiles table
1508
+ */
1509
+ interface UserProfilesRepository {
1510
+ /**
1511
+ * Find user profile by ID
1512
+ */
1513
+ findById(id: Uuid): Promise<UserProfile | null>;
1514
+ /**
1515
+ * Find user profile by auth ID (external auth provider)
1516
+ */
1517
+ findByAuthId(authId: string): Promise<UserProfile | null>;
1518
+ /**
1519
+ * Find user profile by email
1520
+ */
1521
+ findByEmail(tenantId: TenantId, email: string): Promise<UserProfile | null>;
1522
+ /**
1523
+ * Create user profile
1524
+ */
1525
+ create(data: CreateUserProfile): Promise<UserProfile>;
1526
+ /**
1527
+ * Update user profile
1528
+ */
1529
+ update(id: Uuid, data: UpdateUserProfile): Promise<UserProfile>;
1530
+ /**
1531
+ * Delete user profile
1532
+ */
1533
+ delete(id: Uuid): Promise<void>;
1534
+ /**
1535
+ * List all user profiles for a tenant
1536
+ */
1537
+ list(tenantId: TenantId, options?: ListOptions): Promise<UserProfile[]>;
1538
+ /**
1539
+ * Update last login timestamp
1540
+ */
1541
+ updateLastLogin(id: Uuid): Promise<void>;
1542
+ }
1543
+ /**
1544
+ * Repository for files table
1545
+ */
1546
+ interface FilesRepository {
1547
+ /**
1548
+ * Find file by ID
1549
+ */
1550
+ findById(id: Uuid): Promise<File | null>;
1551
+ /**
1552
+ * Create file
1553
+ */
1554
+ create(data: CreateFile): Promise<File>;
1555
+ /**
1556
+ * Update file
1557
+ */
1558
+ update(id: Uuid, data: UpdateFile): Promise<File>;
1559
+ /**
1560
+ * Delete file (soft delete)
1561
+ */
1562
+ delete(id: Uuid): Promise<void>;
1563
+ /**
1564
+ * Hard delete file (permanent)
1565
+ */
1566
+ hardDelete(id: Uuid): Promise<void>;
1567
+ /**
1568
+ * List files for a tenant
1569
+ */
1570
+ list(tenantId: TenantId, options?: FileListOptions): Promise<File[]>;
1571
+ /**
1572
+ * Find files by folder path
1573
+ */
1574
+ findByFolder(tenantId: TenantId, folderPath: string): Promise<File[]>;
1575
+ /**
1576
+ * Find files by uploader
1577
+ */
1578
+ findByUploader(uploadedBy: Uuid): Promise<File[]>;
1579
+ }
1580
+ /**
1581
+ * Repository for object_records table (unified JSONB)
1582
+ */
1583
+ interface ObjectRecordsRepository {
1584
+ /**
1585
+ * Find record by ID
1586
+ */
1587
+ findById(id: Uuid): Promise<ObjectRecord | null>;
1588
+ /**
1589
+ * Create record
1590
+ */
1591
+ create(data: CreateObjectRecord): Promise<ObjectRecord>;
1592
+ /**
1593
+ * Update record
1594
+ */
1595
+ update(id: Uuid, data: Partial<Record<string, unknown>>): Promise<ObjectRecord>;
1596
+ /**
1597
+ * Delete record
1598
+ */
1599
+ delete(id: Uuid): Promise<void>;
1600
+ /**
1601
+ * List records for an object
1602
+ */
1603
+ list(tenantId: TenantId, objectId: Uuid, options?: ListOptions): Promise<{
1604
+ records: ObjectRecord[];
1605
+ total: number;
1606
+ }>;
1607
+ /**
1608
+ * Search records using PostgreSQL full-text search
1609
+ */
1610
+ search(tenantId: TenantId, objectId: Uuid, query: string, options?: SearchOptions): Promise<{
1611
+ records: ObjectRecord[];
1612
+ total: number;
1613
+ }>;
1614
+ }
1615
+ /**
1616
+ * Repository for views table
1617
+ */
1618
+ interface ViewsRepository {
1619
+ /**
1620
+ * Find view by ID
1621
+ */
1622
+ findById(id: Uuid): Promise<DBView | null>;
1623
+ /**
1624
+ * Find view by name for an object and tenant
1625
+ */
1626
+ findByName(tenantId: TenantId, objectName: string, viewName: string): Promise<DBView | null>;
1627
+ /**
1628
+ * Find all views for an object and tenant
1629
+ */
1630
+ findByObjectName(tenantId: TenantId, objectName: string): Promise<DBView[]>;
1631
+ /**
1632
+ * Find all views for a tenant
1633
+ */
1634
+ findAllForTenant(tenantId: TenantId): Promise<DBView[]>;
1635
+ /**
1636
+ * Find system view by name (for sync)
1637
+ */
1638
+ findSystemByName(objectName: string, viewName: string): Promise<DBView | null>;
1639
+ /**
1640
+ * Find all system views for an object
1641
+ */
1642
+ findSystemByObjectName(objectName: string): Promise<DBView[]>;
1643
+ /**
1644
+ * Create view
1645
+ */
1646
+ create(data: CreateDBView): Promise<DBView>;
1647
+ /**
1648
+ * Update view
1649
+ */
1650
+ update(id: Uuid, data: Partial<UpdateDBView>): Promise<DBView>;
1651
+ /**
1652
+ * Delete view
1653
+ */
1654
+ delete(id: Uuid): Promise<void>;
1655
+ /**
1656
+ * Delete views not in the list (for sync cleanup)
1657
+ * @returns Number of deleted views
1658
+ */
1659
+ deleteNotIn(objectName: string, keepViewNames: string[]): Promise<number>;
1660
+ /**
1661
+ * Upsert view (create or update based on objectName + name)
1662
+ */
1663
+ upsert(data: UpsertDBView): Promise<DBView>;
1664
+ }
1665
+
1666
+ /**
1667
+ * Generic database adapter interface
1668
+ * Any ORM (Drizzle, Prisma, Kysely, TypeORM) can implement this interface
1669
+ * to work with @stndrds/schema runtime services
1670
+ *
1671
+ * @example
1672
+ * ```typescript
1673
+ * // Drizzle implementation
1674
+ * import { createDrizzleAdapter } from "./adapters/drizzle";
1675
+ * import { db } from "./db";
1676
+ *
1677
+ * const adapter = createDrizzleAdapter(db);
1678
+ *
1679
+ * // Prisma implementation
1680
+ * import { createPrismaAdapter } from "./adapters/prisma";
1681
+ * import { prisma } from "./db";
1682
+ *
1683
+ * const adapter = createPrismaAdapter(prisma);
1684
+ * ```
1685
+ */
1686
+ interface DatabaseAdapter {
1687
+ objects: ObjectsRepository;
1688
+ attributes: AttributesRepository;
1689
+ views: ViewsRepository;
1690
+ userProfiles: UserProfilesRepository;
1691
+ files: FilesRepository;
1692
+ objectRecords: ObjectRecordsRepository;
1693
+ transaction<T>(callback: (adapter: DatabaseAdapter) => Promise<T>): Promise<T>;
1694
+ }
1695
+
1696
+ interface InternalObjectRecord extends ObjectRecord {
1697
+ tenantId: TenantId;
1698
+ }
1699
+ interface MockStores {
1700
+ objects: Map<Uuid, DBObject>;
1701
+ attributes: Map<Uuid, DBAttribute>;
1702
+ userProfiles: Map<Uuid, UserProfile>;
1703
+ files: Map<Uuid, File>;
1704
+ objectRecords: Map<Uuid, InternalObjectRecord>;
1705
+ views: Map<Uuid, DBView>;
1706
+ }
1707
+ /**
1708
+ * Create an in-memory mock adapter for testing and development
1709
+ *
1710
+ * This adapter stores all data in memory and is perfect for:
1711
+ * - Unit tests
1712
+ * - Integration tests
1713
+ * - Local development without a database
1714
+ * - Understanding the adapter interface
1715
+ *
1716
+ * @returns A DatabaseAdapter implementation using in-memory storage
1717
+ *
1718
+ * @example
1719
+ * ```typescript
1720
+ * import { createMockAdapter, RecordService } from "@stndrds/schema";
1721
+ *
1722
+ * // Create adapter
1723
+ * const adapter = createMockAdapter();
1724
+ *
1725
+ * // Use with services
1726
+ * const recordService = new RecordService(adapter, "tenant-123");
1727
+ *
1728
+ * // Create a record
1729
+ * const product = await recordService.createRecord("obj-product", {
1730
+ * name: "Nike Air Max",
1731
+ * price: 129.99
1732
+ * });
1733
+ *
1734
+ * // List records
1735
+ * const { records, total } = await recordService.listRecords("obj-product");
1736
+ * ```
1737
+ */
1738
+ declare function createMockAdapter(): DatabaseAdapter & {
1739
+ _stores: MockStores;
1740
+ reset(): void;
1741
+ };
1742
+
1743
+ /**
1744
+ * Service for managing files
1745
+ * Handles file metadata CRUD, permissions, and soft delete
1746
+ */
1747
+ declare class FileService {
1748
+ private adapter;
1749
+ private tenantId;
1750
+ constructor(adapter: DatabaseAdapter, tenantId: string);
1751
+ /**
1752
+ * Create a new file record (after upload to storage)
1753
+ *
1754
+ * @param data - File metadata
1755
+ * @returns Created file record
1756
+ *
1757
+ * @example
1758
+ * ```typescript
1759
+ * const service = new FileService(adapter, "tenant-123");
1760
+ *
1761
+ * // After uploading to S3
1762
+ * const file = await service.createFile({
1763
+ * tenantId: "tenant-123",
1764
+ * name: "contract-2025.pdf",
1765
+ * originalName: "Contract Acme Corp 2025.pdf",
1766
+ * mimeType: "application/pdf",
1767
+ * size: 2458624,
1768
+ * storageProvider: "s3",
1769
+ * storagePath: "tenants/123/files/2025/contract.pdf",
1770
+ * storageBucket: "my-app-files",
1771
+ * url: "https://cdn.example.com/files/file-123",
1772
+ * uploadedBy: "profile-456",
1773
+ * visibility: "private"
1774
+ * });
1775
+ * ```
1776
+ */
1777
+ createFile(data: CreateFile): Promise<File>;
1778
+ /**
1779
+ * Get file by ID
1780
+ */
1781
+ getFile(fileId: string): Promise<File | null>;
1782
+ /**
1783
+ * Get file by ID or throw
1784
+ */
1785
+ getFileOrThrow(fileId: string): Promise<File>;
1786
+ /**
1787
+ * Update file metadata
1788
+ *
1789
+ * @param fileId - File UUID
1790
+ * @param data - Data to update
1791
+ * @returns Updated file
1792
+ */
1793
+ updateFile(fileId: string, data: UpdateFile): Promise<File>;
1794
+ /**
1795
+ * Delete file (soft delete)
1796
+ *
1797
+ * @param fileId - File UUID
1798
+ * @param options - Delete options
1799
+ */
1800
+ deleteFile(fileId: string, options?: {
1801
+ hard?: boolean;
1802
+ checkOwnership?: boolean;
1803
+ userId?: string;
1804
+ }): Promise<void>;
1805
+ /**
1806
+ * List files for the tenant
1807
+ */
1808
+ listFiles(options?: FileListOptions): Promise<File[]>;
1809
+ /**
1810
+ * List files by folder
1811
+ */
1812
+ listFilesByFolder(folderPath: string): Promise<File[]>;
1813
+ /**
1814
+ * List files uploaded by a specific user
1815
+ */
1816
+ listFilesByUploader(uploadedBy: string): Promise<File[]>;
1817
+ /**
1818
+ * Change file visibility
1819
+ *
1820
+ * @param fileId - File UUID
1821
+ * @param visibility - New visibility level
1822
+ * @param allowedUsers - Users allowed to access (if restricted)
1823
+ */
1824
+ changeVisibility(fileId: string, visibility: "public" | "private" | "restricted", allowedUsers?: string[]): Promise<File>;
1825
+ /**
1826
+ * Grant access to a file for specific users
1827
+ *
1828
+ * @param fileId - File UUID
1829
+ * @param userIds - User IDs to grant access
1830
+ */
1831
+ grantAccess(fileId: string, userIds: string[]): Promise<File>;
1832
+ /**
1833
+ * Revoke access to a file for specific users
1834
+ *
1835
+ * @param fileId - File UUID
1836
+ * @param userIds - User IDs to revoke access
1837
+ */
1838
+ revokeAccess(fileId: string, userIds: string[]): Promise<File>;
1839
+ /**
1840
+ * Check if user has access to a file
1841
+ *
1842
+ * @param fileId - File UUID
1843
+ * @param userId - User ID to check
1844
+ * @returns true if user can access the file
1845
+ */
1846
+ canAccess(fileId: string, userId: string): Promise<boolean>;
1847
+ /**
1848
+ * Move file to different folder
1849
+ */
1850
+ moveToFolder(fileId: string, newFolderPath: string): Promise<File>;
1851
+ /**
1852
+ * Add tags to file
1853
+ */
1854
+ addTags(fileId: string, tags: string[]): Promise<File>;
1855
+ /**
1856
+ * Remove tags from file
1857
+ */
1858
+ removeTags(fileId: string, tags: string[]): Promise<File>;
1859
+ }
1860
+
1861
+ /**
1862
+ * Geocoding service for address autocomplete and geocoding operations
1863
+ *
1864
+ * @example
1865
+ * ```typescript
1866
+ * import { GeocodingService } from "@stndrds/schema";
1867
+ * import { GoogleMapsAdapter } from "./adapters/google-maps";
1868
+ *
1869
+ * const geocoding = new GeocodingService(new GoogleMapsAdapter({ apiKey: "..." }));
1870
+ *
1871
+ * const suggestions = await geocoding.autocomplete({
1872
+ * query: "40 Quai des Belges",
1873
+ * countries: ["FRA"],
1874
+ * limit: 5,
1875
+ * });
1876
+ * ```
1877
+ */
1878
+ declare class GeocodingService {
1879
+ private readonly adapter;
1880
+ constructor(adapter: GeocodingAdapter);
1881
+ /**
1882
+ * Search for address suggestions as the user types
1883
+ */
1884
+ autocomplete(params: GeocodingAutocompleteParams): Promise<GeocodingSuggestion[]>;
1885
+ /**
1886
+ * Reverse geocode coordinates to an address
1887
+ */
1888
+ reverse(params: ReverseGeocodingParams): Promise<GeocodingSuggestion | null>;
1889
+ /**
1890
+ * Geocode a structured address to coordinates
1891
+ */
1892
+ geocode(params: GeocodingParams): Promise<GeocodingSuggestion | null>;
1893
+ }
1894
+
1895
+ /**
1896
+ * Input for creating a custom object
1897
+ */
1898
+ interface CreateCustomObjectInput {
1899
+ name: string;
1900
+ label: string;
1901
+ description?: string;
1902
+ icon?: IconName;
1903
+ attributes?: (Attribute | {
1904
+ build: () => Attribute;
1905
+ })[];
1906
+ metadata?: Record<string, unknown>;
1907
+ }
1908
+ /**
1909
+ * Input for adding an attribute to an object
1910
+ */
1911
+ interface AddAttributeInput {
1912
+ name: string;
1913
+ label: string;
1914
+ type: AttributeType;
1915
+ required?: boolean;
1916
+ unique?: boolean;
1917
+ description?: string;
1918
+ placeholder?: string;
1919
+ icon?: IconName;
1920
+ defaultValue?: unknown;
1921
+ metadata?: Record<string, unknown>;
1922
+ [key: string]: unknown;
1923
+ }
1924
+ /**
1925
+ * Service for managing object schemas
1926
+ * Handles fusion of native objects (from registry) and custom objects (from database)
1927
+ */
1928
+ declare class ObjectSchemaService {
1929
+ private adapter;
1930
+ private nativeRegistry;
1931
+ constructor(adapter: DatabaseAdapter, nativeRegistry: typeof registry);
1932
+ /**
1933
+ * Create a new custom object
1934
+ * Validates name format and ensures system=false
1935
+ *
1936
+ * @param definition - Object definition (attributes can be raw definitions or builders)
1937
+ * @param tenantId - Tenant ID owning the object
1938
+ * @returns Created ObjectDefinition
1939
+ */
1940
+ createCustomObject(definition: CreateCustomObjectInput, tenantId: string): Promise<ObjectDefinition>;
1941
+ /**
1942
+ * Add a custom attribute to an existing object
1943
+ * Can be used on both custom and native objects
1944
+ * Attributes added via API are always custom (system=false)
1945
+ *
1946
+ * @param objectId - Object UUID from database
1947
+ * @param attribute - Attribute definition
1948
+ * @returns Created Attribute
1949
+ */
1950
+ addAttributeToObject(objectId: string, attribute: AddAttributeInput): Promise<Attribute>;
1951
+ /**
1952
+ * Update an attribute
1953
+ * Can only update custom attributes (system=false)
1954
+ *
1955
+ * @param attributeId - Attribute UUID
1956
+ * @param updates - Partial attribute updates
1957
+ * @returns Updated Attribute
1958
+ */
1959
+ updateAttribute(attributeId: string, updates: Partial<AddAttributeInput>): Promise<Attribute>;
1960
+ /**
1961
+ * Delete an attribute
1962
+ * Can only delete custom attributes (system=false)
1963
+ *
1964
+ * @param attributeId - Attribute UUID
1965
+ */
1966
+ deleteAttribute(attributeId: string): Promise<void>;
1967
+ /**
1968
+ * List attributes for an object
1969
+ *
1970
+ * @param objectId - Object UUID
1971
+ * @param options - Filter options
1972
+ * @returns List of attributes
1973
+ */
1974
+ listAttributes(objectId: string, options?: {
1975
+ systemOnly?: boolean;
1976
+ customOnly?: boolean;
1977
+ }): Promise<Attribute[]>;
1978
+ /**
1979
+ * Validate attribute name format (variable identifier)
1980
+ * @internal
1981
+ */
1982
+ private validateAttributeName;
1983
+ /**
1984
+ * Get complete object schema (system + custom attributes)
1985
+ * If object is native, merges registry definition with DB custom attributes
1986
+ * If object is custom, returns DB definition only
1987
+ *
1988
+ * @param objectId - Object UUID from database
1989
+ * @returns Complete ObjectDefinition with all attributes
1990
+ *
1991
+ * @example
1992
+ * ```typescript
1993
+ * const service = new ObjectSchemaService(adapter, registry);
1994
+ * const productSchema = await service.getObjectSchema("obj-123");
1995
+ *
1996
+ * console.log(productSchema.attributes); // System + custom attributes
1997
+ * ```
1998
+ */
1999
+ getObjectSchema(objectId: string): Promise<ObjectDefinition>;
2000
+ /**
2001
+ * Get object schema by name (for system/native objects)
2002
+ * @deprecated Use getObjectSchemaByNameForTenant for custom objects support
2003
+ */
2004
+ getObjectSchemaByName(name: string): Promise<ObjectDefinition>;
2005
+ /**
2006
+ * Get object schema by name for a tenant (supports both native and custom objects)
2007
+ */
2008
+ getObjectSchemaByNameForTenant(name: string, tenantId: string): Promise<ObjectDefinition>;
2009
+ /**
2010
+ * List all object schemas for a tenant
2011
+ */
2012
+ listObjectSchemas(tenantId: string): Promise<ObjectDefinition[]>;
2013
+ /**
2014
+ * Merge native object from registry with custom attributes from DB
2015
+ * @internal
2016
+ */
2017
+ private mergeNativeObject;
2018
+ /**
2019
+ * Convert DB object + attributes to ObjectDefinition
2020
+ * @internal
2021
+ */
2022
+ private convertDBObjectToDefinition;
2023
+ /**
2024
+ * Convert DB attribute to Attribute type
2025
+ * @internal
2026
+ */
2027
+ private convertDBAttributeToAttribute;
2028
+ }
2029
+
2030
+ /**
2031
+ * Service for managing object records (CRUD operations)
2032
+ * Handles validation, dispatch to correct table, and data consistency
2033
+ */
2034
+ declare class RecordService {
2035
+ private adapter;
2036
+ private tenantId;
2037
+ private schemaService;
2038
+ private relationService;
2039
+ constructor(adapter: DatabaseAdapter, tenantId: string);
2040
+ /**
2041
+ * Create a new record with validation
2042
+ *
2043
+ * @param objectId - Object UUID
2044
+ * @param data - Record data (attribute values)
2045
+ * @param options - Creation options
2046
+ * @returns Created record with computed completionStatus
2047
+ *
2048
+ * @example
2049
+ * ```typescript
2050
+ * const service = new RecordService(adapter, "tenant-123");
2051
+ *
2052
+ * // Create a complete record (strict validation)
2053
+ * const product = await service.createRecord("obj-product", {
2054
+ * name: "Nike Air Max",
2055
+ * price: 129.99,
2056
+ * status: "active"
2057
+ * });
2058
+ * // → product.completionStatus = "complete"
2059
+ *
2060
+ * // Create a draft record (allows missing required fields)
2061
+ * const draft = await service.createRecord("obj-product", {
2062
+ * name: "Draft Product"
2063
+ * }, { allowDraft: true });
2064
+ * // → draft.completionStatus = "draft"
2065
+ * ```
2066
+ */
2067
+ createRecord(objectId: string, data: Record<string, unknown>, options?: {
2068
+ /**
2069
+ * Allow creating records with missing required fields.
2070
+ * Format validation still applies to provided values.
2071
+ * @default false
2072
+ */
2073
+ allowDraft?: boolean;
2074
+ /**
2075
+ * Skip all validation (format + relations).
2076
+ * @default true
2077
+ */
2078
+ validate?: boolean;
2079
+ /**
2080
+ * Skip relation validation only.
2081
+ * Useful for bulk imports where relations are validated separately.
2082
+ * @default false
2083
+ */
2084
+ skipRelationValidation?: boolean;
2085
+ skipSystemCheck?: boolean;
2086
+ }): Promise<ObjectRecord>;
2087
+ /**
2088
+ * Get a record by ID
2089
+ *
2090
+ * @param recordId - Record UUID
2091
+ * @param options - Query options
2092
+ * @returns Record or null if not found
2093
+ */
2094
+ getRecord(recordId: string, options?: {
2095
+ includeSchema?: boolean;
2096
+ }): Promise<ObjectRecord | null>;
2097
+ /**
2098
+ * Get a record by ID or throw if not found
2099
+ */
2100
+ getRecordOrThrow(recordId: string): Promise<ObjectRecord>;
2101
+ /**
2102
+ * Update a record with validation
2103
+ *
2104
+ * The completion status is automatically recalculated after each update.
2105
+ * A draft record becomes complete when all required fields are filled.
2106
+ *
2107
+ * @param recordId - Record UUID
2108
+ * @param data - Partial data to update
2109
+ * @param options - Update options
2110
+ * @returns Updated record with recalculated completionStatus
2111
+ *
2112
+ * @example
2113
+ * ```typescript
2114
+ * // Update a draft record to make it complete
2115
+ * const updated = await service.updateRecord(draftId, {
2116
+ * price: 99.99,
2117
+ * status: "active"
2118
+ * });
2119
+ * // → updated.completionStatus = "complete" if all required fields now present
2120
+ * ```
2121
+ */
2122
+ updateRecord(recordId: string, data: Partial<Record<string, unknown>>, options?: {
2123
+ /**
2124
+ * Skip validation entirely (format + relations).
2125
+ * @default true
2126
+ */
2127
+ validate?: boolean;
2128
+ /**
2129
+ * Allow partial updates without strict validation.
2130
+ * Format validation still applies to provided values.
2131
+ * @default false
2132
+ */
2133
+ partial?: boolean;
2134
+ /**
2135
+ * Skip relation validation only.
2136
+ * @default false
2137
+ */
2138
+ skipRelationValidation?: boolean;
2139
+ }): Promise<ObjectRecord>;
2140
+ /**
2141
+ * Delete a record
2142
+ *
2143
+ * @param recordId - Record UUID
2144
+ * @param options - Delete options
2145
+ */
2146
+ deleteRecord(recordId: string, options?: {
2147
+ checkSystem?: boolean;
2148
+ }): Promise<void>;
2149
+ /**
2150
+ * List records for an object with pagination
2151
+ *
2152
+ * @param objectId - Object UUID
2153
+ * @param options - List options
2154
+ * @returns Records and total count
2155
+ */
2156
+ listRecords(objectId: string, options?: {
2157
+ limit?: number;
2158
+ offset?: number;
2159
+ orderBy?: string;
2160
+ orderDirection?: "asc" | "desc";
2161
+ }): Promise<{
2162
+ records: ObjectRecord[];
2163
+ total: number;
2164
+ }>;
2165
+ /**
2166
+ * Search records using full-text search
2167
+ *
2168
+ * @param objectId - Object UUID
2169
+ * @param query - Search query
2170
+ * @param options - Search options
2171
+ * @returns Matching records and total count
2172
+ */
2173
+ searchRecords(objectId: string, query: string, options?: {
2174
+ limit?: number;
2175
+ offset?: number;
2176
+ filters?: Record<string, unknown>;
2177
+ }): Promise<{
2178
+ records: ObjectRecord[];
2179
+ total: number;
2180
+ }>;
2181
+ /**
2182
+ * Validate data against object schema without saving
2183
+ *
2184
+ * @param objectId - Object UUID
2185
+ * @param data - Data to validate
2186
+ * @returns Validation result
2187
+ */
2188
+ validateData(objectId: string, data: Record<string, unknown>): Promise<ValidationResult>;
2189
+ /**
2190
+ * Compute the completion status for given data without saving.
2191
+ * Useful for UI to show draft/complete status before submitting.
2192
+ *
2193
+ * @param objectId - Object UUID
2194
+ * @param data - Data to check
2195
+ * @returns Computed completion status
2196
+ */
2197
+ computeStatus(objectId: string, data: Record<string, unknown>): Promise<CompletionStatus>;
2198
+ /**
2199
+ * Refresh the completion status of an existing record.
2200
+ * Useful when schema changes and you need to recompute statuses.
2201
+ *
2202
+ * @param recordId - Record UUID
2203
+ * @returns Updated completion status
2204
+ */
2205
+ refreshRecordStatus(recordId: string): Promise<CompletionStatus>;
2206
+ }
2207
+
2208
+ /**
2209
+ * Result of relation validation
2210
+ */
2211
+ interface RelationValidationResult {
2212
+ valid: boolean;
2213
+ errors: RelationValidationError[];
2214
+ }
2215
+ /**
2216
+ * Individual relation validation error
2217
+ */
2218
+ interface RelationValidationError {
2219
+ /** Attribute name */
2220
+ attribute: string;
2221
+ /** Error message */
2222
+ message: string;
2223
+ /** Invalid record IDs */
2224
+ invalidIds?: string[];
2225
+ }
2226
+ /**
2227
+ * Resolved relation option
2228
+ */
2229
+ interface RelationOption {
2230
+ /** Record ID */
2231
+ id: string;
2232
+ /** Object ID */
2233
+ objectId: string;
2234
+ /** Object name (technical name) */
2235
+ objectName: string;
2236
+ /** Object label (display name) */
2237
+ objectLabel: string;
2238
+ /** Object icon */
2239
+ objectIcon?: string;
2240
+ /** Display label (resolved from titleAttribute) */
2241
+ label: string;
2242
+ /** Additional record data */
2243
+ data?: Record<string, unknown>;
2244
+ }
2245
+ /**
2246
+ * Response for relation options
2247
+ */
2248
+ interface RelationOptionsResponse {
2249
+ options: RelationOption[];
2250
+ hasMore: boolean;
2251
+ total: number;
2252
+ }
2253
+ /**
2254
+ * Parameters for fetching relation options
2255
+ */
2256
+ interface GetRelationOptionsParams {
2257
+ /** Search query */
2258
+ query?: string;
2259
+ /** Page number (1-based) */
2260
+ page?: number;
2261
+ /** Page size */
2262
+ pageSize?: number;
2263
+ /** Filter by specific target object */
2264
+ targetObject?: string;
2265
+ }
2266
+ /**
2267
+ * Service for validating relation attributes
2268
+ * Ensures referenced records exist and belong to valid target objects
2269
+ */
2270
+ declare class RelationService {
2271
+ private adapter;
2272
+ private schemaService;
2273
+ constructor(adapter: DatabaseAdapter, nativeRegistry: typeof registry);
2274
+ /**
2275
+ * Validate all relation attributes in the data
2276
+ *
2277
+ * @param schema - Object schema containing attribute definitions
2278
+ * @param data - Record data to validate
2279
+ * @returns Validation result with errors if any
2280
+ *
2281
+ * @example
2282
+ * ```typescript
2283
+ * const result = await relationService.validateRelations(schema, {
2284
+ * company: "rec-123",
2285
+ * contacts: ["rec-456", "rec-789"]
2286
+ * });
2287
+ *
2288
+ * if (!result.valid) {
2289
+ * console.log(result.errors);
2290
+ * // [{ attribute: "company", message: "Record not found", invalidIds: ["rec-123"] }]
2291
+ * }
2292
+ * ```
2293
+ */
2294
+ validateRelations(schema: ObjectDefinition, data: Record<string, unknown>): Promise<RelationValidationResult>;
2295
+ /**
2296
+ * Validate a single relation attribute value
2297
+ */
2298
+ private validateRelationAttribute;
2299
+ /**
2300
+ * Extract IDs from relation value based on cardinality
2301
+ */
2302
+ private extractIds;
2303
+ /**
2304
+ * Get valid object IDs from relation targets
2305
+ */
2306
+ private getValidObjectIds;
2307
+ /**
2308
+ * Validate relations and throw if invalid
2309
+ */
2310
+ validateRelationsOrThrow(schema: ObjectDefinition, data: Record<string, unknown>): Promise<void>;
2311
+ /**
2312
+ * Get available options for a relation attribute
2313
+ * Searches across all target objects defined in the relation
2314
+ *
2315
+ * @param attribute - Relation attribute definition
2316
+ * @param tenantId - Tenant ID for multi-tenant isolation
2317
+ * @param params - Query parameters
2318
+ *
2319
+ * @example
2320
+ * ```typescript
2321
+ * const options = await relationService.getOptions(attribute, "tenant-1", {
2322
+ * query: "nike",
2323
+ * page: 1,
2324
+ * pageSize: 20
2325
+ * });
2326
+ * ```
2327
+ */
2328
+ getOptions(attribute: RelationAttribute, tenantId: string, params?: GetRelationOptionsParams): Promise<RelationOptionsResponse>;
2329
+ /**
2330
+ * Resolve record IDs to their display labels
2331
+ * Useful for displaying current values in the UI
2332
+ *
2333
+ * @param ids - Record IDs to resolve
2334
+ * @param tenantId - Tenant ID for multi-tenant isolation
2335
+ *
2336
+ * @example
2337
+ * ```typescript
2338
+ * const resolved = await relationService.resolveIds(["rec-1", "rec-2"], "tenant-1");
2339
+ * // [{ id: "rec-1", label: "Nike Air Max", objectName: "products", ... }]
2340
+ * ```
2341
+ */
2342
+ resolveIds(ids: string[], tenantId: string): Promise<RelationOption[]>;
2343
+ /**
2344
+ * Find a relation attribute by ID
2345
+ */
2346
+ findAttributeById(attributeId: string): Promise<RelationAttribute | null>;
2347
+ /**
2348
+ * Resolve display label from record values
2349
+ * Uses displayTemplate if provided, otherwise falls back to titleAttribute
2350
+ */
2351
+ private resolveLabel;
2352
+ }
2353
+
2354
+ /**
2355
+ * Service for managing user profiles
2356
+ * Handles user profile CRUD, auth provider sync, and role management
2357
+ */
2358
+ declare class UserProfileService {
2359
+ private adapter;
2360
+ private tenantId;
2361
+ constructor(adapter: DatabaseAdapter, tenantId: string);
2362
+ /**
2363
+ * Create a new user profile (typically after first auth)
2364
+ *
2365
+ * @param data - User profile creation data
2366
+ * @returns Created user profile
2367
+ *
2368
+ * @example
2369
+ * ```typescript
2370
+ * const service = new UserProfileService(adapter, "tenant-123");
2371
+ *
2372
+ * // After Supabase auth
2373
+ * const profile = await service.createProfile({
2374
+ * tenantId: "tenant-123",
2375
+ * authId: authUser.id,
2376
+ * email: authUser.email,
2377
+ * firstName: authUser.user_metadata.first_name,
2378
+ * lastName: authUser.user_metadata.last_name,
2379
+ * role: "member",
2380
+ * status: "active"
2381
+ * });
2382
+ * ```
2383
+ */
2384
+ createProfile(data: CreateUserProfile): Promise<UserProfile>;
2385
+ /**
2386
+ * Get user profile by ID
2387
+ */
2388
+ getProfile(profileId: string): Promise<UserProfile | null>;
2389
+ /**
2390
+ * Get user profile by ID or throw
2391
+ */
2392
+ getProfileOrThrow(profileId: string): Promise<UserProfile>;
2393
+ /**
2394
+ * Get user profile by auth provider ID
2395
+ *
2396
+ * @param authId - Auth provider user ID (Supabase, Clerk, etc.)
2397
+ * @returns User profile or null
2398
+ */
2399
+ getProfileByAuthId(authId: string): Promise<UserProfile | null>;
2400
+ /**
2401
+ * Get or create user profile (idempotent operation)
2402
+ * Useful for auth callbacks - ensures profile exists
2403
+ *
2404
+ * @param authId - Auth provider user ID
2405
+ * @param data - Profile data to create if doesn't exist
2406
+ * @returns Existing or newly created profile
2407
+ *
2408
+ * @example
2409
+ * ```typescript
2410
+ * // In Supabase auth callback
2411
+ * const profile = await service.getOrCreateProfile(
2412
+ * authUser.id,
2413
+ * {
2414
+ * tenantId: "tenant-123",
2415
+ * authId: authUser.id,
2416
+ * email: authUser.email,
2417
+ * role: "member",
2418
+ * status: "active"
2419
+ * }
2420
+ * );
2421
+ * ```
2422
+ */
2423
+ getOrCreateProfile(authId: string, data: CreateUserProfile): Promise<UserProfile>;
2424
+ /**
2425
+ * Update user profile
2426
+ */
2427
+ updateProfile(profileId: string, data: UpdateUserProfile): Promise<UserProfile>;
2428
+ /**
2429
+ * Delete user profile
2430
+ *
2431
+ * @param profileId - Profile UUID
2432
+ * @param options - Delete options
2433
+ */
2434
+ deleteProfile(profileId: string, options?: {
2435
+ checkAdmin?: boolean;
2436
+ }): Promise<void>;
2437
+ /**
2438
+ * List all user profiles for the tenant
2439
+ */
2440
+ listProfiles(options?: ListOptions): Promise<UserProfile[]>;
2441
+ /**
2442
+ * Update last login timestamp
2443
+ *
2444
+ * @param profileId - Profile UUID
2445
+ *
2446
+ * @example
2447
+ * ```typescript
2448
+ * // After successful auth
2449
+ * await service.updateLastLogin(profile.id);
2450
+ * ```
2451
+ */
2452
+ updateLastLogin(profileId: string): Promise<void>;
2453
+ /**
2454
+ * Change user role
2455
+ *
2456
+ * @param profileId - Profile UUID
2457
+ * @param newRole - New role
2458
+ */
2459
+ changeRole(profileId: string, newRole: string): Promise<UserProfile>;
2460
+ /**
2461
+ * Change user status
2462
+ *
2463
+ * @param profileId - Profile UUID
2464
+ * @param newStatus - New status
2465
+ */
2466
+ changeStatus(profileId: string, newStatus: "active" | "pending" | "inactive" | "suspended"): Promise<UserProfile>;
2467
+ /**
2468
+ * Get user by email
2469
+ */
2470
+ getProfileByEmail(email: string): Promise<UserProfile | null>;
2471
+ /**
2472
+ * Check if user has role
2473
+ */
2474
+ hasRole(profileId: string, role: string): Promise<boolean>;
2475
+ /**
2476
+ * Check if user is admin
2477
+ */
2478
+ isAdmin(profileId: string): Promise<boolean>;
2479
+ }
2480
+
2481
+ /**
2482
+ * Input for creating a custom view
2483
+ */
2484
+ interface CreateViewInput {
2485
+ name: string;
2486
+ label: string;
2487
+ objectName: string;
2488
+ description?: string;
2489
+ icon?: IconName;
2490
+ tabs: Tab[];
2491
+ default?: boolean;
2492
+ metadata?: Record<string, unknown>;
2493
+ }
2494
+ /**
2495
+ * Input for updating a view
2496
+ */
2497
+ interface UpdateViewInput {
2498
+ label?: string;
2499
+ description?: string;
2500
+ icon?: IconName;
2501
+ tabs?: Tab[];
2502
+ default?: boolean;
2503
+ metadata?: Record<string, unknown>;
2504
+ }
2505
+ /**
2506
+ * Service for managing views
2507
+ * Handles fusion of native views (from registry) and custom views (from database)
2508
+ */
2509
+ declare class ViewService {
2510
+ private adapter;
2511
+ private nativeViews;
2512
+ constructor(adapter: DatabaseAdapter, nativeViews: typeof viewRegistry);
2513
+ /**
2514
+ * Get all views for an object (native + custom)
2515
+ *
2516
+ * @param objectName - Object name
2517
+ * @param tenantId - Tenant ID
2518
+ * @returns All views for the object
2519
+ */
2520
+ getViewsForObject(objectName: string, tenantId: string): Promise<ViewDefinition[]>;
2521
+ /**
2522
+ * Get a specific view by name
2523
+ *
2524
+ * @param objectName - Object name
2525
+ * @param viewName - View name
2526
+ * @param tenantId - Tenant ID
2527
+ * @returns View definition or null
2528
+ */
2529
+ getView(objectName: string, viewName: string, tenantId: string): Promise<ViewDefinition | null>;
2530
+ /**
2531
+ * Get the default view for an object
2532
+ *
2533
+ * Priority:
2534
+ * 1. Custom view marked as default
2535
+ * 2. Native view marked as default
2536
+ * 3. First available view
2537
+ *
2538
+ * @param objectName - Object name
2539
+ * @param tenantId - Tenant ID
2540
+ * @returns Default view or null
2541
+ */
2542
+ getDefaultView(objectName: string, tenantId: string): Promise<ViewDefinition | null>;
2543
+ /**
2544
+ * Create a custom view
2545
+ *
2546
+ * @param input - View definition
2547
+ * @param tenantId - Tenant ID
2548
+ * @returns Created view
2549
+ */
2550
+ createView(input: CreateViewInput, tenantId: string): Promise<ViewDefinition>;
2551
+ /**
2552
+ * Update a custom view
2553
+ *
2554
+ * @param viewId - View ID
2555
+ * @param input - Update data
2556
+ * @returns Updated view
2557
+ */
2558
+ updateView(viewId: string, input: UpdateViewInput): Promise<ViewDefinition>;
2559
+ /**
2560
+ * Delete a custom view
2561
+ *
2562
+ * @param viewId - View ID
2563
+ */
2564
+ deleteView(viewId: string): Promise<void>;
2565
+ /**
2566
+ * Set a view as default for its object
2567
+ *
2568
+ * @param viewId - View ID
2569
+ * @param tenantId - Tenant ID
2570
+ * @returns Updated view
2571
+ */
2572
+ setDefaultView(viewId: string, tenantId: string): Promise<ViewDefinition>;
2573
+ /**
2574
+ * Validate view name format (kebab-case)
2575
+ */
2576
+ private validateViewName;
2577
+ /**
2578
+ * Convert database view to ViewDefinition
2579
+ */
2580
+ private convertDBViewToDefinition;
2581
+ }
2582
+
2583
+ /**
2584
+ * Result of view sync operation
2585
+ */
2586
+ interface ViewSyncResult {
2587
+ success: boolean;
2588
+ viewsSynced: number;
2589
+ viewsCreated: number;
2590
+ viewsUpdated: number;
2591
+ viewsDeleted: number;
2592
+ errors: Array<{
2593
+ viewName: string;
2594
+ objectName: string;
2595
+ error: string;
2596
+ }>;
2597
+ }
2598
+ /**
2599
+ * Options for view sync
2600
+ */
2601
+ interface ViewSyncOptions {
2602
+ dryRun?: boolean;
2603
+ verbose?: boolean;
2604
+ tenantId?: string;
2605
+ }
2606
+ /**
2607
+ * Sync native views from registry to database
2608
+ *
2609
+ * This function:
2610
+ * 1. Reads all registered native views from the registry
2611
+ * 2. Upserts them into the database (views table)
2612
+ * 3. Marks them as system=true for protection
2613
+ * 4. Removes views that were deleted from code
2614
+ *
2615
+ * @param adapter - Database adapter implementing DatabaseAdapter interface
2616
+ * @param nativeViewRegistry - Registry containing native views
2617
+ * @param options - Sync options (dryRun, verbose, tenantId)
2618
+ * @returns Sync result with statistics
2619
+ *
2620
+ * @example
2621
+ * ```typescript
2622
+ * import { syncNativeViews, viewRegistry } from "@stndrds/schema";
2623
+ * import { drizzleAdapter } from "./db/adapter";
2624
+ *
2625
+ * const result = await syncNativeViews(drizzleAdapter, viewRegistry, {
2626
+ * verbose: true,
2627
+ * tenantId: "default"
2628
+ * });
2629
+ *
2630
+ * if (result.success) {
2631
+ * console.log(`✓ Synced ${result.viewsSynced} views`);
2632
+ * }
2633
+ * ```
2634
+ */
2635
+ declare function syncNativeViews(adapter: DatabaseAdapter, nativeViewRegistry: typeof viewRegistry, options?: ViewSyncOptions): Promise<ViewSyncResult>;
2636
+ /**
2637
+ * Verify that all native views are synced to database
2638
+ *
2639
+ * @param adapter - Database adapter
2640
+ * @param nativeViewRegistry - Registry containing native views
2641
+ * @returns true if all views are synced, false otherwise
2642
+ *
2643
+ * @example
2644
+ * ```typescript
2645
+ * const isSynced = await verifyNativeViewsSync(adapter, viewRegistry);
2646
+ * if (!isSynced) {
2647
+ * console.warn("Native views not synced, running sync...");
2648
+ * await syncNativeViews(adapter, viewRegistry);
2649
+ * }
2650
+ * ```
2651
+ */
2652
+ declare function verifyNativeViewsSync(adapter: DatabaseAdapter, nativeViewRegistry: typeof viewRegistry): Promise<boolean>;
2653
+ /**
2654
+ * Get sync preview without modifying database
2655
+ *
2656
+ * @param adapter - Database adapter
2657
+ * @param nativeViewRegistry - Registry containing native views
2658
+ * @returns Sync result (dry run)
2659
+ */
2660
+ declare function getViewSyncPreview(adapter: DatabaseAdapter, nativeViewRegistry: typeof viewRegistry): Promise<ViewSyncResult>;
2661
+
2662
+ /**
2663
+ * Result of sync operation
2664
+ */
2665
+ interface SyncResult {
2666
+ success: boolean;
2667
+ objectsSynced: number;
2668
+ attributesSynced: number;
2669
+ objectsCreated: number;
2670
+ objectsUpdated: number;
2671
+ attributesCreated: number;
2672
+ attributesUpdated: number;
2673
+ attributesDeleted: number;
2674
+ errors: Array<{
2675
+ objectName: string;
2676
+ error: string;
2677
+ }>;
2678
+ }
2679
+ /**
2680
+ * Sync options
2681
+ */
2682
+ interface SyncOptions {
2683
+ dryRun?: boolean;
2684
+ verbose?: boolean;
2685
+ tenantId?: string;
2686
+ }
2687
+ /**
2688
+ * Sync native objects from registry to database
2689
+ *
2690
+ * This function:
2691
+ * 1. Reads all registered native objects from the registry
2692
+ * 2. Upserts them into the database (objects + attributes tables)
2693
+ * 3. Marks them as system=true for protection
2694
+ * 4. Removes attributes that were deleted from code
2695
+ *
2696
+ * @param adapter - Database adapter implementing DatabaseAdapter interface
2697
+ * @param nativeRegistry - Registry containing native objects
2698
+ * @param options - Sync options (dryRun, verbose, tenantId)
2699
+ * @returns Sync result with statistics
2700
+ *
2701
+ * @example
2702
+ * ```typescript
2703
+ * import { syncNativeObjects } from "@stndrds/schema/runtime";
2704
+ * import { registry } from "./objects/native";
2705
+ * import { drizzleAdapter } from "./db/adapter";
2706
+ *
2707
+ * const result = await syncNativeObjects(drizzleAdapter, registry, {
2708
+ * verbose: true,
2709
+ * tenantId: "default"
2710
+ * });
2711
+ *
2712
+ * if (result.success) {
2713
+ * console.log(`✓ Synced ${result.objectsSynced} objects`);
2714
+ * } else {
2715
+ * console.error("Sync errors:", result.errors);
2716
+ * }
2717
+ * ```
2718
+ */
2719
+ declare function syncNativeObjects(adapter: DatabaseAdapter, nativeRegistry: typeof registry, options?: SyncOptions): Promise<SyncResult>;
2720
+ /**
2721
+ * Verify that all native objects are synced to database
2722
+ *
2723
+ * @param adapter - Database adapter
2724
+ * @param nativeRegistry - Registry containing native objects
2725
+ * @returns true if all objects are synced, false otherwise
2726
+ *
2727
+ * @example
2728
+ * ```typescript
2729
+ * const isSynced = await verifyNativeObjectsSync(adapter, registry);
2730
+ * if (!isSynced) {
2731
+ * console.warn("Native objects not synced, running sync...");
2732
+ * await syncNativeObjects(adapter, registry);
2733
+ * }
2734
+ * ```
2735
+ */
2736
+ declare function verifyNativeObjectsSync(adapter: DatabaseAdapter, nativeRegistry: typeof registry): Promise<boolean>;
2737
+ /**
2738
+ * Get sync statistics without modifying database
2739
+ *
2740
+ * @param adapter - Database adapter
2741
+ * @param nativeRegistry - Registry containing native objects
2742
+ * @returns Sync result (dry run)
2743
+ */
2744
+ declare function getSyncPreview(adapter: DatabaseAdapter, nativeRegistry: typeof registry): Promise<SyncResult>;
2745
+ /**
2746
+ * Result of full sync operation (objects + views)
2747
+ */
2748
+ interface FullSyncResult {
2749
+ success: boolean;
2750
+ objects: SyncResult;
2751
+ views: ViewSyncResult;
2752
+ }
2753
+ /**
2754
+ * Options for full sync
2755
+ */
2756
+ interface FullSyncOptions extends SyncOptions, ViewSyncOptions {
2757
+ }
2758
+ /**
2759
+ * Sync all native objects and views from registries to database
2760
+ *
2761
+ * This is a convenience function that:
2762
+ * 1. Syncs native objects first (views may depend on objects)
2763
+ * 2. Syncs native views
2764
+ *
2765
+ * @param adapter - Database adapter
2766
+ * @param objectRegistry - Registry containing native objects
2767
+ * @param nativeViewRegistry - Registry containing native views
2768
+ * @param options - Sync options
2769
+ * @returns Combined sync result
2770
+ *
2771
+ * @example
2772
+ * ```typescript
2773
+ * import { syncAll, registry, viewRegistry } from "@stndrds/schema";
2774
+ * import { adapter } from "./db";
2775
+ *
2776
+ * const result = await syncAll(adapter, registry, viewRegistry, {
2777
+ * verbose: true,
2778
+ * tenantId: "default"
2779
+ * });
2780
+ *
2781
+ * if (result.success) {
2782
+ * console.log(`✓ Synced ${result.objects.objectsSynced} objects, ${result.views.viewsSynced} views`);
2783
+ * }
2784
+ * ```
2785
+ */
2786
+ declare function syncAll(adapter: DatabaseAdapter, objectRegistry: typeof registry, nativeViewRegistry: typeof viewRegistry, options?: FullSyncOptions): Promise<FullSyncResult>;
2787
+
2788
+ export { type Timestamps as $, type Attribute as A, type BaseAttribute as B, type Currency as C, type DateAttribute as D, type StorageProvider as E, type FileAttribute as F, type Group as G, type FileVisibility as H, type File as I, type CreateFile as J, type UpdateFile as K, type Location as L, type MultiselectAttribute as M, type NumberAttribute as N, type Option as O, type Phone as P, type GeocodingSuggestion as Q, type RelationOnDelete as R, type StatusAttribute as S, type TextAttribute as T, type UserAttribute as U, type ViewDefinition as V, type GeocodingAutocompleteParams as W, type ReverseGeocodingParams as X, type GeocodingParams as Y, type GeocodingAdapter as Z, NoopGeocodingAdapter as _, type SelectAttribute as a, RecordService as a$, type ObjectAttribute as a0, type CompletionStatus as a1, type ObjectRecord as a2, type UserRole as a3, type UserStatus as a4, type UserProfile as a5, type CreateUserProfile as a6, type UpdateUserProfile as a7, type TabType as a8, type FormTab as a9, createRelationValidator as aA, createRatingValidator as aB, createAttributeValidator as aC, createObjectValidator as aD, type ValidationResult as aE, validateAttribute as aF, validateObject as aG, validateObjectOrThrow as aH, createDraftValidator as aI, validateDraft as aJ, validateDraftOrThrow as aK, getMissingRequiredAttributes as aL, isRecordComplete as aM, computeRecordStatus as aN, type DatabaseAdapter as aO, createMockAdapter as aP, type ObjectsRepository as aQ, type AttributesRepository as aR, type UserProfilesRepository as aS, type FilesRepository as aT, type ObjectRecordsRepository as aU, type ViewsRepository as aV, FileService as aW, GeocodingService as aX, type CreateCustomObjectInput as aY, type AddAttributeInput as aZ, ObjectSchemaService as a_, type TableTab as aa, type CustomTab as ab, isFormTab as ac, isTableTab as ad, isCustomTab as ae, type Uuid as af, type TenantId as ag, generateId as ah, generatePrefixedId as ai, registry as aj, viewRegistry as ak, createTextValidator as al, createNumberValidator as am, createCheckboxValidator as an, createDateValidator as ao, createPhoneValidator as ap, createCurrencyValidator as aq, createStatusValidator as ar, createSelectValidator as as, createMultiselectValidator as at, createLocationValidator as au, createTimestampValidator as av, createFileValidator as aw, createUserValidator as ax, createSingleRelationValidator as ay, createMultiRelationValidator as az, type TextAreaAttribute as b, type RelationValidationResult as b0, type RelationValidationError as b1, type RelationOption as b2, type RelationOptionsResponse as b3, type GetRelationOptionsParams as b4, RelationService as b5, UserProfileService as b6, type CreateViewInput as b7, type UpdateViewInput as b8, ViewService as b9, type ViewSyncOptions as bA, syncNativeViews as bB, verifyNativeViewsSync as bC, getViewSyncPreview as bD, type SyncResult as ba, type SyncOptions as bb, syncNativeObjects as bc, verifyNativeObjectsSync as bd, getSyncPreview as be, type FullSyncResult as bf, type FullSyncOptions as bg, syncAll as bh, type DBObject as bi, type CreateDBObject as bj, type UpdateDBObject as bk, type UpsertDBObject as bl, type DBAttribute as bm, type CreateDBAttribute as bn, type UpdateDBAttribute as bo, type UpsertDBAttribute as bp, type CreateObjectRecord as bq, type ListOptions as br, type SearchOptions as bs, type FileListOptions as bt, type DBView as bu, type CreateDBView as bv, type UpdateDBView as bw, type UpsertDBView as bx, type OperationResult as by, type ViewSyncResult as bz, 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 Field as m, type Tab as n, type AttributeType as o, type StatusGroup as p, type AttributeGroup as q, type NumberUnit as r, type DateFormat as s, type DateValue as t, type LocationGranularity as u, type DocumentType as v, type DocumentFace as w, type DocumentTypeConfig as x, type FileVerificationConfig as y, type RelationAttribute as z };