@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.
- package/dist/chunk-3ABIOLKO.mjs +4624 -0
- package/dist/chunk-3IRX2BDY.mjs +3970 -0
- package/dist/chunk-4OTLPWSP.mjs +4448 -0
- package/dist/chunk-54ODQBUN.mjs +3867 -0
- package/dist/chunk-6HIA3FR2.mjs +4230 -0
- package/dist/chunk-6P3XHNLD.mjs +4171 -0
- package/dist/chunk-7LJECNAS.mjs +4167 -0
- package/dist/chunk-A2WNLUKV.mjs +4475 -0
- package/dist/chunk-AA3234RM.mjs +4475 -0
- package/dist/chunk-ADNXDLLD.mjs +3136 -0
- package/dist/chunk-B4DCDQUI.mjs +4695 -0
- package/dist/chunk-B4PSXY6I.mjs +4527 -0
- package/dist/chunk-BJHJBZCM.mjs +4228 -0
- package/dist/chunk-C3ZWTO6G.mjs +4474 -0
- package/dist/chunk-CLOGNKDD.mjs +4040 -0
- package/dist/chunk-EW4V63PA.mjs +3053 -0
- package/dist/chunk-FR2LCO6R.mjs +2485 -0
- package/dist/chunk-GJSF2RKL.mjs +2786 -0
- package/dist/chunk-GO5NPP2X.mjs +4726 -0
- package/dist/chunk-H3ZYJUDS.mjs +3050 -0
- package/dist/chunk-IDDLSFSK.mjs +4230 -0
- package/dist/chunk-JRADUJQ7.mjs +4726 -0
- package/dist/chunk-NEDR6N7R.mjs +3118 -0
- package/dist/chunk-NESPRMDF.mjs +2482 -0
- package/dist/chunk-NWFGRJBW.mjs +4612 -0
- package/dist/chunk-NXJ57GSN.mjs +2918 -0
- package/dist/chunk-O4C5FCTS.mjs +3961 -0
- package/dist/chunk-O4PRB27Q.mjs +2513 -0
- package/dist/chunk-OJDFNUCR.mjs +4463 -0
- package/dist/chunk-OSEKVBHC.mjs +4627 -0
- package/dist/chunk-QX4U6U6K.mjs +2484 -0
- package/dist/chunk-RMDQ6LKV.mjs +3046 -0
- package/dist/chunk-SBXKDATH.mjs +2673 -0
- package/dist/chunk-SKMP3AP3.mjs +2918 -0
- package/dist/chunk-STMS7WZD.mjs +2623 -0
- package/dist/chunk-T3T6PTFG.mjs +3057 -0
- package/dist/chunk-TPW72RAH.mjs +4624 -0
- package/dist/chunk-TUQEGNGV.mjs +2534 -0
- package/dist/chunk-TVKFS3YN.mjs +2449 -0
- package/dist/chunk-TZTH3BHG.mjs +3110 -0
- package/dist/chunk-W6MU3XPG.mjs +4612 -0
- package/dist/chunk-WL7Z3YFR.mjs +4603 -0
- package/dist/chunk-XNVVYHH3.mjs +4602 -0
- package/dist/chunk-XTA3WY64.mjs +2918 -0
- package/dist/chunk-YV7DEZFZ.mjs +2528 -0
- package/dist/chunk-ZLJPJND6.mjs +4474 -0
- package/dist/chunk-ZNZXTCEX.mjs +4234 -0
- package/dist/index.d.mts +1222 -0
- package/dist/index.d.ts +1222 -0
- package/dist/index.js +5320 -0
- package/dist/index.mjs +635 -0
- package/dist/runtime-2iKarXtl.d.mts +1924 -0
- package/dist/runtime-2iKarXtl.d.ts +1924 -0
- package/dist/runtime-4l2ddHZ7.d.mts +2242 -0
- package/dist/runtime-4l2ddHZ7.d.ts +2243 -0
- package/dist/runtime-8hKS6zAv.d.mts +2226 -0
- package/dist/runtime-8hKS6zAv.d.ts +2226 -0
- package/dist/runtime-B6yjtoR3.d.mts +4142 -0
- package/dist/runtime-B6yjtoR3.d.ts +4142 -0
- package/dist/runtime-BGCShrZB.d.mts +2001 -0
- package/dist/runtime-BGCShrZB.d.ts +2001 -0
- package/dist/runtime-BRW4NGAk.d.mts +1668 -0
- package/dist/runtime-BRW4NGAk.d.ts +1668 -0
- package/dist/runtime-BnfBr314.d.mts +1683 -0
- package/dist/runtime-BnfBr314.d.ts +1683 -0
- package/dist/runtime-BrqhiJUF.d.mts +4151 -0
- package/dist/runtime-BrqhiJUF.d.ts +4151 -0
- package/dist/runtime-Bvj8c-BE.d.mts +4277 -0
- package/dist/runtime-Bvj8c-BE.d.ts +4277 -0
- package/dist/runtime-C03qT4qU.d.mts +4267 -0
- package/dist/runtime-C03qT4qU.d.ts +4267 -0
- package/dist/runtime-CaCZ7mSf.d.mts +2974 -0
- package/dist/runtime-CaCZ7mSf.d.ts +2974 -0
- package/dist/runtime-CaIzNX0Z.d.mts +4091 -0
- package/dist/runtime-CaIzNX0Z.d.ts +4091 -0
- package/dist/runtime-ChPTgmLP.d.mts +4320 -0
- package/dist/runtime-ChPTgmLP.d.ts +4320 -0
- package/dist/runtime-CnImZ1Vv.d.mts +4267 -0
- package/dist/runtime-CnImZ1Vv.d.ts +4267 -0
- package/dist/runtime-Cp-B26Nj.d.mts +2890 -0
- package/dist/runtime-Cp-B26Nj.d.ts +2890 -0
- package/dist/runtime-Cq8jfk3c.d.mts +4277 -0
- package/dist/runtime-Cq8jfk3c.d.ts +4277 -0
- package/dist/runtime-CqDFXhhP.d.mts +1685 -0
- package/dist/runtime-CqDFXhhP.d.ts +1685 -0
- package/dist/runtime-CqtpZLdL.d.mts +4280 -0
- package/dist/runtime-CqtpZLdL.d.ts +4280 -0
- package/dist/runtime-D-4DblaZ.d.mts +4147 -0
- package/dist/runtime-D-4DblaZ.d.ts +4147 -0
- package/dist/runtime-D59sSqNl.d.mts +4146 -0
- package/dist/runtime-D59sSqNl.d.ts +4146 -0
- package/dist/runtime-DEfPk3wT.d.mts +1840 -0
- package/dist/runtime-DEfPk3wT.d.ts +1840 -0
- package/dist/runtime-DGV-7vES.d.mts +1685 -0
- package/dist/runtime-DGV-7vES.d.ts +1685 -0
- package/dist/runtime-DNacghqc.d.mts +3971 -0
- package/dist/runtime-DNacghqc.d.ts +3971 -0
- package/dist/runtime-DPLsUHYK.d.mts +3977 -0
- package/dist/runtime-DPLsUHYK.d.ts +3977 -0
- package/dist/runtime-DTxCw60F.d.mts +2973 -0
- package/dist/runtime-DTxCw60F.d.ts +2973 -0
- package/dist/runtime-DUXJ-zNS.d.mts +2788 -0
- package/dist/runtime-DUXJ-zNS.d.ts +2788 -0
- package/dist/runtime-DbljInhV.d.mts +2237 -0
- package/dist/runtime-DbljInhV.d.ts +2237 -0
- package/dist/runtime-DcH3y6Yu.d.mts +4272 -0
- package/dist/runtime-DcH3y6Yu.d.ts +4272 -0
- package/dist/runtime-DdBFz85g.d.mts +4229 -0
- package/dist/runtime-DdBFz85g.d.ts +4229 -0
- package/dist/runtime-DjMWhjkm.d.mts +4321 -0
- package/dist/runtime-DjMWhjkm.d.ts +4321 -0
- package/dist/runtime-Djz3fOBE.d.mts +2078 -0
- package/dist/runtime-Djz3fOBE.d.ts +2078 -0
- package/dist/runtime-Dli8g3Aq.d.mts +2082 -0
- package/dist/runtime-Dli8g3Aq.d.ts +2082 -0
- package/dist/runtime-a8J378im.d.mts +4123 -0
- package/dist/runtime-a8J378im.d.ts +4123 -0
- package/dist/runtime-dp3rb3Lf.d.mts +2968 -0
- package/dist/runtime-dp3rb3Lf.d.ts +2968 -0
- package/dist/runtime-iWXlSN_H.d.mts +1683 -0
- package/dist/runtime-iWXlSN_H.d.ts +1683 -0
- package/dist/runtime-yMql0npY.d.mts +4321 -0
- package/dist/runtime-yMql0npY.d.ts +4321 -0
- package/dist/runtime.d.mts +3 -0
- package/dist/runtime.d.ts +3 -0
- package/dist/runtime.js +3626 -0
- package/dist/runtime.mjs +48 -0
- package/package.json +58 -0
|
@@ -0,0 +1,1668 @@
|
|
|
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
|
+
* RelationAttribute links to other objects/records
|
|
212
|
+
* relation: array of object IDs that can be linked
|
|
213
|
+
* limit: maximum number of relations that can be selected
|
|
214
|
+
*/
|
|
215
|
+
interface RelationAttribute extends BaseAttribute<string> {
|
|
216
|
+
type: "relation";
|
|
217
|
+
relation: string[];
|
|
218
|
+
limit?: number;
|
|
219
|
+
cascadeDelete?: boolean;
|
|
220
|
+
}
|
|
221
|
+
interface TextAreaAttribute extends BaseAttribute<string> {
|
|
222
|
+
type: "textarea";
|
|
223
|
+
}
|
|
224
|
+
interface RatingAttribute extends BaseAttribute<number> {
|
|
225
|
+
type: "rating";
|
|
226
|
+
max?: number;
|
|
227
|
+
iconType?: "star" | "heart" | "thumbs" | "number";
|
|
228
|
+
}
|
|
229
|
+
type Attribute = TextAttribute | TextAreaAttribute | NumberAttribute | CheckboxAttribute | DateAttribute | PhoneAttribute | CurrencyAttribute | StatusAttribute | LocationAttribute | TimestampAttribute | SelectAttribute | MultiselectAttribute | FileAttribute | UserAttribute | RelationAttribute | RatingAttribute;
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Timestamps for tracking creation and updates
|
|
233
|
+
*/
|
|
234
|
+
interface Timestamps {
|
|
235
|
+
createdAt: Date;
|
|
236
|
+
updatedAt: Date;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Object definition - Represents a database table/entity
|
|
240
|
+
*/
|
|
241
|
+
interface ObjectDefinition {
|
|
242
|
+
id?: Uuid;
|
|
243
|
+
name: string;
|
|
244
|
+
label: string;
|
|
245
|
+
description?: string;
|
|
246
|
+
icon?: IconName;
|
|
247
|
+
attributes: Attribute[];
|
|
248
|
+
system?: boolean;
|
|
249
|
+
metadata?: Record<string, unknown>;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Links an attribute to an object
|
|
253
|
+
*/
|
|
254
|
+
interface ObjectAttribute {
|
|
255
|
+
objectId: Uuid;
|
|
256
|
+
attributeId: Uuid;
|
|
257
|
+
order?: number;
|
|
258
|
+
required?: boolean;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Record - Instance of an Object (a row in the database)
|
|
262
|
+
*/
|
|
263
|
+
interface ObjectRecord extends Timestamps {
|
|
264
|
+
id: Uuid;
|
|
265
|
+
objectId: Uuid;
|
|
266
|
+
values: Record<string, unknown>;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Storage provider type
|
|
271
|
+
*/
|
|
272
|
+
type StorageProvider = "s3" | "gcs" | "azure" | "local" | "cloudflare-r2" | string;
|
|
273
|
+
/**
|
|
274
|
+
* File visibility level
|
|
275
|
+
*/
|
|
276
|
+
type FileVisibility = "public" | "private" | "restricted";
|
|
277
|
+
/**
|
|
278
|
+
* File - Represents uploaded file metadata and storage info
|
|
279
|
+
*
|
|
280
|
+
* ARCHITECTURE:
|
|
281
|
+
* - Fixed table (no custom attributes)
|
|
282
|
+
* - Manages file storage, permissions, and metadata
|
|
283
|
+
* - uploadedBy links to user_profiles table
|
|
284
|
+
* - Supports soft delete via deletedAt
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* ```typescript
|
|
288
|
+
* const file: File = {
|
|
289
|
+
* id: "file-123",
|
|
290
|
+
* tenantId: "tenant-456",
|
|
291
|
+
* name: "contract-2025.pdf",
|
|
292
|
+
* originalName: "Contract Acme Corp 2025.pdf",
|
|
293
|
+
* mimeType: "application/pdf",
|
|
294
|
+
* size: 2458624,
|
|
295
|
+
* storageProvider: "s3",
|
|
296
|
+
* storagePath: "tenants/456/files/2025/11/contract-2025.pdf",
|
|
297
|
+
* storageBucket: "my-app-files",
|
|
298
|
+
* url: "https://cdn.example.com/files/file-123",
|
|
299
|
+
* uploadedBy: "profile-789",
|
|
300
|
+
* folderPath: "/contracts/2025",
|
|
301
|
+
* tags: ["contract", "legal"],
|
|
302
|
+
* visibility: "restricted",
|
|
303
|
+
* allowedUsers: ["profile-789", "profile-456"],
|
|
304
|
+
* createdAt: new Date(),
|
|
305
|
+
* updatedAt: new Date(),
|
|
306
|
+
* };
|
|
307
|
+
* ```
|
|
308
|
+
*/
|
|
309
|
+
interface File extends Timestamps {
|
|
310
|
+
id: Uuid;
|
|
311
|
+
tenantId: Uuid;
|
|
312
|
+
/**
|
|
313
|
+
* File name (sanitized for storage)
|
|
314
|
+
*/
|
|
315
|
+
name: string;
|
|
316
|
+
/**
|
|
317
|
+
* Original file name (as uploaded by user)
|
|
318
|
+
*/
|
|
319
|
+
originalName: string;
|
|
320
|
+
/**
|
|
321
|
+
* MIME type (e.g., "application/pdf", "image/jpeg")
|
|
322
|
+
*/
|
|
323
|
+
mimeType: MimeType | string;
|
|
324
|
+
/**
|
|
325
|
+
* File size in bytes
|
|
326
|
+
*/
|
|
327
|
+
size: number;
|
|
328
|
+
/**
|
|
329
|
+
* Storage provider (s3, gcs, azure, local, etc.)
|
|
330
|
+
*/
|
|
331
|
+
storageProvider: StorageProvider;
|
|
332
|
+
/**
|
|
333
|
+
* Path in the storage bucket
|
|
334
|
+
*/
|
|
335
|
+
storagePath: string;
|
|
336
|
+
/**
|
|
337
|
+
* Storage bucket name (if applicable)
|
|
338
|
+
*/
|
|
339
|
+
storageBucket?: string;
|
|
340
|
+
/**
|
|
341
|
+
* Public or signed URL to access the file
|
|
342
|
+
*/
|
|
343
|
+
url: string;
|
|
344
|
+
/**
|
|
345
|
+
* User who uploaded the file (FK to user_profiles)
|
|
346
|
+
*/
|
|
347
|
+
uploadedBy: Uuid;
|
|
348
|
+
/**
|
|
349
|
+
* Folder path for organization (e.g., "/contracts/2025")
|
|
350
|
+
*/
|
|
351
|
+
folderPath?: string;
|
|
352
|
+
/**
|
|
353
|
+
* Tags for search and categorization
|
|
354
|
+
*/
|
|
355
|
+
tags?: string[];
|
|
356
|
+
/**
|
|
357
|
+
* File visibility level
|
|
358
|
+
* - public: Anyone with the URL can access
|
|
359
|
+
* - private: Only uploadedBy can access
|
|
360
|
+
* - restricted: Only specific users (allowedUsers) can access
|
|
361
|
+
*/
|
|
362
|
+
visibility: FileVisibility;
|
|
363
|
+
/**
|
|
364
|
+
* List of user IDs authorized to access this file (if visibility = "restricted")
|
|
365
|
+
*/
|
|
366
|
+
allowedUsers?: Uuid[];
|
|
367
|
+
/**
|
|
368
|
+
* Soft delete timestamp (null = not deleted)
|
|
369
|
+
*/
|
|
370
|
+
deletedAt?: Date;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Data required to create a new file record
|
|
374
|
+
*/
|
|
375
|
+
interface CreateFile {
|
|
376
|
+
tenantId: Uuid;
|
|
377
|
+
name: string;
|
|
378
|
+
originalName: string;
|
|
379
|
+
mimeType: MimeType | string;
|
|
380
|
+
size: number;
|
|
381
|
+
storageProvider: StorageProvider;
|
|
382
|
+
storagePath: string;
|
|
383
|
+
storageBucket?: string;
|
|
384
|
+
url: string;
|
|
385
|
+
uploadedBy: Uuid;
|
|
386
|
+
folderPath?: string;
|
|
387
|
+
tags?: string[];
|
|
388
|
+
visibility?: FileVisibility;
|
|
389
|
+
allowedUsers?: Uuid[];
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Data for updating an existing file record
|
|
393
|
+
*/
|
|
394
|
+
interface UpdateFile {
|
|
395
|
+
name?: string;
|
|
396
|
+
folderPath?: string;
|
|
397
|
+
tags?: string[];
|
|
398
|
+
visibility?: FileVisibility;
|
|
399
|
+
allowedUsers?: Uuid[];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* User role in the application
|
|
404
|
+
* Can be extended with custom roles as needed
|
|
405
|
+
*/
|
|
406
|
+
type UserRole = "admin" | "member" | "guest" | string;
|
|
407
|
+
/**
|
|
408
|
+
* User status for account management
|
|
409
|
+
*/
|
|
410
|
+
type UserStatus = "active" | "pending" | "inactive" | "suspended";
|
|
411
|
+
/**
|
|
412
|
+
* User Profile - Represents application user data (authorization)
|
|
413
|
+
*
|
|
414
|
+
* ARCHITECTURE:
|
|
415
|
+
* - authId links to external auth provider (Supabase, Clerk, Auth0, etc.)
|
|
416
|
+
* - email is denormalized from auth provider for performance
|
|
417
|
+
* - Auth provider handles authentication (passwords, sessions, OAuth)
|
|
418
|
+
* - This type handles authorization (roles, permissions, tenant membership)
|
|
419
|
+
*
|
|
420
|
+
* @example
|
|
421
|
+
* ```typescript
|
|
422
|
+
* const profile: UserProfile = {
|
|
423
|
+
* id: "profile-123",
|
|
424
|
+
* tenantId: "tenant-456",
|
|
425
|
+
* authId: "supabase-auth-uuid-789",
|
|
426
|
+
* email: "john@example.com",
|
|
427
|
+
* firstName: "John",
|
|
428
|
+
* lastName: "Doe",
|
|
429
|
+
* role: "admin",
|
|
430
|
+
* status: "active",
|
|
431
|
+
* createdAt: new Date(),
|
|
432
|
+
* updatedAt: new Date(),
|
|
433
|
+
* };
|
|
434
|
+
* ```
|
|
435
|
+
*/
|
|
436
|
+
interface UserProfile extends Timestamps {
|
|
437
|
+
id: Uuid;
|
|
438
|
+
tenantId: Uuid;
|
|
439
|
+
/**
|
|
440
|
+
* Link to external auth provider (Supabase auth.users.id, Clerk user ID, etc.)
|
|
441
|
+
* This is the bridge between authentication (provider) and authorization (your app)
|
|
442
|
+
*/
|
|
443
|
+
authId: string;
|
|
444
|
+
/**
|
|
445
|
+
* Email address (denormalized from auth provider)
|
|
446
|
+
* Allows efficient querying and filtering without hitting auth provider API
|
|
447
|
+
*/
|
|
448
|
+
email: string;
|
|
449
|
+
/**
|
|
450
|
+
* User's first name
|
|
451
|
+
*/
|
|
452
|
+
firstName?: string;
|
|
453
|
+
/**
|
|
454
|
+
* User's last name
|
|
455
|
+
*/
|
|
456
|
+
lastName?: string;
|
|
457
|
+
/**
|
|
458
|
+
* Avatar/profile picture URL
|
|
459
|
+
*/
|
|
460
|
+
avatarUrl?: string;
|
|
461
|
+
/**
|
|
462
|
+
* User role for authorization
|
|
463
|
+
* Common values: "admin", "member", "guest"
|
|
464
|
+
* Can be extended with custom roles
|
|
465
|
+
*/
|
|
466
|
+
role: UserRole;
|
|
467
|
+
/**
|
|
468
|
+
* Account status
|
|
469
|
+
* - active: Normal user, full access
|
|
470
|
+
* - pending: Awaiting activation/approval
|
|
471
|
+
* - inactive: Deactivated account
|
|
472
|
+
* - suspended: Temporarily blocked
|
|
473
|
+
*/
|
|
474
|
+
status: UserStatus;
|
|
475
|
+
/**
|
|
476
|
+
* Last login timestamp (updated on each successful auth)
|
|
477
|
+
*/
|
|
478
|
+
lastLoginAt?: Date;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Data required to create a new user profile
|
|
482
|
+
*/
|
|
483
|
+
interface CreateUserProfile {
|
|
484
|
+
tenantId: Uuid;
|
|
485
|
+
authId: string;
|
|
486
|
+
email: string;
|
|
487
|
+
firstName?: string;
|
|
488
|
+
lastName?: string;
|
|
489
|
+
avatarUrl?: string;
|
|
490
|
+
role?: UserRole;
|
|
491
|
+
status?: UserStatus;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Data for updating an existing user profile
|
|
495
|
+
*/
|
|
496
|
+
interface UpdateUserProfile {
|
|
497
|
+
firstName?: string;
|
|
498
|
+
lastName?: string;
|
|
499
|
+
avatarUrl?: string;
|
|
500
|
+
role?: UserRole;
|
|
501
|
+
status?: UserStatus;
|
|
502
|
+
lastLoginAt?: Date;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Registry for native objects defined in code
|
|
507
|
+
* Native objects are system objects that cannot be deleted/modified by clients
|
|
508
|
+
* They are defined by developers and synced to the database at startup
|
|
509
|
+
*/
|
|
510
|
+
declare class NativeObjectRegistryClass {
|
|
511
|
+
private objects;
|
|
512
|
+
/**
|
|
513
|
+
* Register one or more native objects
|
|
514
|
+
* Auto-builds if ObjectBuilder is passed instead of ObjectDefinition
|
|
515
|
+
* @param objects - Single object/builder or array of objects/builders to register
|
|
516
|
+
* @returns this (for chaining)
|
|
517
|
+
* @throws Error if object is invalid or already registered
|
|
518
|
+
*
|
|
519
|
+
* @example
|
|
520
|
+
* ```typescript
|
|
521
|
+
* // No .build() needed - registry auto-builds!
|
|
522
|
+
* const PRODUCT = object({ id: "obj-product", name: "products", label: "Product" })
|
|
523
|
+
* .system()
|
|
524
|
+
* .attribute(text({ id: "attr-name", name: "name", label: "Name" }));
|
|
525
|
+
*
|
|
526
|
+
* registry.register(PRODUCT); // ← Auto-builds here
|
|
527
|
+
*
|
|
528
|
+
* // Can still explicitly .build() if you want
|
|
529
|
+
* registry.register(PRODUCT.build());
|
|
530
|
+
*
|
|
531
|
+
* // Register multiple
|
|
532
|
+
* registry.register([PRODUCT, ORDER, CUSTOMER]);
|
|
533
|
+
* ```
|
|
534
|
+
*/
|
|
535
|
+
register(objects: ObjectDefinition | ObjectDefinition[] | {
|
|
536
|
+
build: () => ObjectDefinition;
|
|
537
|
+
} | Array<{
|
|
538
|
+
build: () => ObjectDefinition;
|
|
539
|
+
}>): this;
|
|
540
|
+
/**
|
|
541
|
+
* Validate and register a single object
|
|
542
|
+
* @internal
|
|
543
|
+
*/
|
|
544
|
+
private validateAndRegister;
|
|
545
|
+
/**
|
|
546
|
+
* Get a native object by its name
|
|
547
|
+
* @param name - The object name
|
|
548
|
+
* @returns The object definition or undefined if not found
|
|
549
|
+
*
|
|
550
|
+
* @example
|
|
551
|
+
* ```typescript
|
|
552
|
+
* const product = registry.getByName("products");
|
|
553
|
+
* if (product) {
|
|
554
|
+
* console.log(product.label); // "Product"
|
|
555
|
+
* }
|
|
556
|
+
* ```
|
|
557
|
+
*/
|
|
558
|
+
getByName(name: string): ObjectDefinition | undefined;
|
|
559
|
+
/**
|
|
560
|
+
* Get a native object or throw if not found
|
|
561
|
+
* @param name - The object name
|
|
562
|
+
* @returns The object definition
|
|
563
|
+
* @throws Error if not found
|
|
564
|
+
*/
|
|
565
|
+
getByNameOrThrow(name: string): ObjectDefinition;
|
|
566
|
+
/**
|
|
567
|
+
* Get all registered native objects
|
|
568
|
+
* @returns Array of all registered object definitions
|
|
569
|
+
*/
|
|
570
|
+
getAll(): ObjectDefinition[];
|
|
571
|
+
/**
|
|
572
|
+
* Get all registered native object names
|
|
573
|
+
* @returns Array of object names
|
|
574
|
+
*/
|
|
575
|
+
listNames(): string[];
|
|
576
|
+
/**
|
|
577
|
+
* Check if a native object is registered
|
|
578
|
+
* @param name - The object name
|
|
579
|
+
* @returns true if registered, false otherwise
|
|
580
|
+
*/
|
|
581
|
+
has(name: string): boolean;
|
|
582
|
+
/**
|
|
583
|
+
* Get the number of registered objects
|
|
584
|
+
*/
|
|
585
|
+
get size(): number;
|
|
586
|
+
/**
|
|
587
|
+
* Clear all registered objects (useful for testing)
|
|
588
|
+
* @internal
|
|
589
|
+
*/
|
|
590
|
+
clear(): void;
|
|
591
|
+
/**
|
|
592
|
+
* Get registry summary for debugging
|
|
593
|
+
*/
|
|
594
|
+
summary(): string;
|
|
595
|
+
/**
|
|
596
|
+
* Log registry summary to console
|
|
597
|
+
*/
|
|
598
|
+
debug(): void;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Singleton instance of the registry
|
|
602
|
+
* Use this to register and retrieve native objects
|
|
603
|
+
*
|
|
604
|
+
* @example
|
|
605
|
+
* ```typescript
|
|
606
|
+
* import { registry } from "@stndrds/schema";
|
|
607
|
+
*
|
|
608
|
+
* // Register objects
|
|
609
|
+
* registry.register([PRODUCT, ORDER, CUSTOMER]);
|
|
610
|
+
*
|
|
611
|
+
* // Get object by name
|
|
612
|
+
* const product = registry.getByName("products");
|
|
613
|
+
*
|
|
614
|
+
* // List all
|
|
615
|
+
* console.log(registry.listNames()); // ["products", "orders", ...]
|
|
616
|
+
*
|
|
617
|
+
* // Debug
|
|
618
|
+
* registry.debug();
|
|
619
|
+
* ```
|
|
620
|
+
*/
|
|
621
|
+
declare const registry: NativeObjectRegistryClass;
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Create a Zod schema for a text attribute
|
|
625
|
+
*/
|
|
626
|
+
declare function createTextValidator(attr: TextAttribute): z.ZodString;
|
|
627
|
+
/**
|
|
628
|
+
* Create a Zod schema for a number attribute
|
|
629
|
+
*/
|
|
630
|
+
declare function createNumberValidator(attr: NumberAttribute): z.ZodNumber;
|
|
631
|
+
/**
|
|
632
|
+
* Create a Zod schema for a checkbox attribute
|
|
633
|
+
*/
|
|
634
|
+
declare function createCheckboxValidator(_attr: CheckboxAttribute): z.ZodBoolean;
|
|
635
|
+
/**
|
|
636
|
+
* Create a Zod schema for a date attribute
|
|
637
|
+
*/
|
|
638
|
+
declare function createDateValidator(attr: DateAttribute): z.ZodString;
|
|
639
|
+
/**
|
|
640
|
+
* Create a Zod schema for a phone attribute
|
|
641
|
+
*/
|
|
642
|
+
declare function createPhoneValidator(_attr: PhoneAttribute): z.ZodType<{
|
|
643
|
+
countryCode: string;
|
|
644
|
+
phoneNumber: string;
|
|
645
|
+
}>;
|
|
646
|
+
/**
|
|
647
|
+
* Create a Zod schema for a currency attribute
|
|
648
|
+
*/
|
|
649
|
+
declare function createCurrencyValidator(_attr: CurrencyAttribute): z.ZodType<{
|
|
650
|
+
code: string;
|
|
651
|
+
value: number;
|
|
652
|
+
}>;
|
|
653
|
+
/**
|
|
654
|
+
* Create a Zod schema for a status attribute
|
|
655
|
+
*/
|
|
656
|
+
declare function createStatusValidator(attr: StatusAttribute): z.ZodEnum<[string, ...string[]]>;
|
|
657
|
+
/**
|
|
658
|
+
* Create a Zod schema for a select attribute
|
|
659
|
+
*/
|
|
660
|
+
declare function createSelectValidator(attr: SelectAttribute): z.ZodEnum<[string, ...string[]]>;
|
|
661
|
+
/**
|
|
662
|
+
* Create a Zod schema for a multiselect attribute
|
|
663
|
+
*/
|
|
664
|
+
declare function createMultiselectValidator(attr: MultiselectAttribute): z.ZodArray<z.ZodEnum<[string, ...string[]]>>;
|
|
665
|
+
/**
|
|
666
|
+
* Create a Zod schema for a location attribute
|
|
667
|
+
*/
|
|
668
|
+
type LocationShape = {
|
|
669
|
+
address?: string;
|
|
670
|
+
address2?: string;
|
|
671
|
+
city?: string;
|
|
672
|
+
state?: string;
|
|
673
|
+
postalCode?: string;
|
|
674
|
+
country?: string;
|
|
675
|
+
latitude?: number;
|
|
676
|
+
longitude?: number;
|
|
677
|
+
};
|
|
678
|
+
declare function createLocationValidator(_attr: LocationAttribute): z.ZodType<LocationShape>;
|
|
679
|
+
/**
|
|
680
|
+
* Create a Zod schema for a timestamp attribute
|
|
681
|
+
*/
|
|
682
|
+
declare function createTimestampValidator(_attr: TimestampAttribute): z.ZodNumber;
|
|
683
|
+
/**
|
|
684
|
+
* Create a Zod schema for a file attribute
|
|
685
|
+
*/
|
|
686
|
+
declare function createFileValidator(_attr: FileAttribute): z.ZodString;
|
|
687
|
+
/**
|
|
688
|
+
* Create a Zod schema for a user attribute
|
|
689
|
+
*/
|
|
690
|
+
declare function createUserValidator(_attr: UserAttribute): z.ZodString;
|
|
691
|
+
/**
|
|
692
|
+
* Create a Zod schema for a relation attribute
|
|
693
|
+
*/
|
|
694
|
+
declare function createRelationValidator(_attr: RelationAttribute): z.ZodString;
|
|
695
|
+
/**
|
|
696
|
+
* Create a Zod schema for a rating attribute
|
|
697
|
+
*/
|
|
698
|
+
declare function createRatingValidator(attr: RatingAttribute): z.ZodNumber;
|
|
699
|
+
/**
|
|
700
|
+
* Create a Zod schema for any attribute type
|
|
701
|
+
*/
|
|
702
|
+
declare function createAttributeValidator(attr: Attribute): z.ZodTypeAny;
|
|
703
|
+
/**
|
|
704
|
+
* Create a Zod schema for an entire object
|
|
705
|
+
*/
|
|
706
|
+
declare function createObjectValidator(objectDef: ObjectDefinition): z.ZodType<Record<string, unknown>>;
|
|
707
|
+
/**
|
|
708
|
+
* Validation result
|
|
709
|
+
*/
|
|
710
|
+
interface ValidationResult {
|
|
711
|
+
success: boolean;
|
|
712
|
+
data?: Record<string, unknown>;
|
|
713
|
+
errors?: Array<{
|
|
714
|
+
path: string[];
|
|
715
|
+
message: string;
|
|
716
|
+
}>;
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Validate data against an attribute schema
|
|
720
|
+
*/
|
|
721
|
+
declare function validateAttribute(attr: Attribute, value: unknown): ValidationResult;
|
|
722
|
+
/**
|
|
723
|
+
* Validate data against an object schema
|
|
724
|
+
*/
|
|
725
|
+
declare function validateObject(objectDef: ObjectDefinition, data: Record<string, unknown>): ValidationResult;
|
|
726
|
+
/**
|
|
727
|
+
* Validate and throw if invalid
|
|
728
|
+
*/
|
|
729
|
+
declare function validateObjectOrThrow(objectDef: ObjectDefinition, data: Record<string, unknown>): Record<string, unknown>;
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Object as stored in database (metadata)
|
|
733
|
+
*/
|
|
734
|
+
interface DBObject {
|
|
735
|
+
id: Uuid;
|
|
736
|
+
tenantId: TenantId;
|
|
737
|
+
name: string;
|
|
738
|
+
label: string;
|
|
739
|
+
description?: string;
|
|
740
|
+
system: boolean;
|
|
741
|
+
metadata?: Record<string, unknown>;
|
|
742
|
+
createdAt: Date;
|
|
743
|
+
updatedAt: Date;
|
|
744
|
+
}
|
|
745
|
+
interface CreateDBObject {
|
|
746
|
+
tenantId: TenantId;
|
|
747
|
+
name: string;
|
|
748
|
+
label: string;
|
|
749
|
+
description?: string;
|
|
750
|
+
system?: boolean;
|
|
751
|
+
metadata?: Record<string, unknown>;
|
|
752
|
+
}
|
|
753
|
+
interface UpdateDBObject {
|
|
754
|
+
label?: string;
|
|
755
|
+
description?: string;
|
|
756
|
+
metadata?: Record<string, unknown>;
|
|
757
|
+
}
|
|
758
|
+
interface UpsertDBObject extends CreateDBObject {
|
|
759
|
+
system: boolean;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Attribute as stored in database (metadata)
|
|
763
|
+
*/
|
|
764
|
+
interface DBAttribute {
|
|
765
|
+
id: Uuid;
|
|
766
|
+
objectId: Uuid;
|
|
767
|
+
name: string;
|
|
768
|
+
label: string;
|
|
769
|
+
type: string;
|
|
770
|
+
system: boolean;
|
|
771
|
+
required: boolean;
|
|
772
|
+
unique: boolean;
|
|
773
|
+
orderIndex: number;
|
|
774
|
+
config: Record<string, unknown>;
|
|
775
|
+
createdAt: Date;
|
|
776
|
+
updatedAt: Date;
|
|
777
|
+
}
|
|
778
|
+
interface CreateDBAttribute {
|
|
779
|
+
objectId: Uuid;
|
|
780
|
+
name: string;
|
|
781
|
+
label: string;
|
|
782
|
+
type: string;
|
|
783
|
+
system?: boolean;
|
|
784
|
+
required?: boolean;
|
|
785
|
+
unique?: boolean;
|
|
786
|
+
orderIndex?: number;
|
|
787
|
+
config?: Record<string, unknown>;
|
|
788
|
+
}
|
|
789
|
+
interface UpdateDBAttribute {
|
|
790
|
+
label?: string;
|
|
791
|
+
required?: boolean;
|
|
792
|
+
orderIndex?: number;
|
|
793
|
+
config?: Record<string, unknown>;
|
|
794
|
+
}
|
|
795
|
+
interface UpsertDBAttribute extends CreateDBAttribute {
|
|
796
|
+
objectId: Uuid;
|
|
797
|
+
name: string;
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Object record creation data
|
|
801
|
+
*/
|
|
802
|
+
interface CreateObjectRecord {
|
|
803
|
+
tenantId: TenantId;
|
|
804
|
+
objectId: Uuid;
|
|
805
|
+
data: Record<string, unknown>;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Generic list options for pagination and sorting
|
|
809
|
+
*/
|
|
810
|
+
interface ListOptions {
|
|
811
|
+
limit?: number;
|
|
812
|
+
offset?: number;
|
|
813
|
+
orderBy?: string;
|
|
814
|
+
orderDirection?: "asc" | "desc";
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Search options for full-text search
|
|
818
|
+
*/
|
|
819
|
+
interface SearchOptions extends ListOptions {
|
|
820
|
+
filters?: Record<string, unknown>;
|
|
821
|
+
highlight?: boolean;
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* File-specific list options
|
|
825
|
+
*/
|
|
826
|
+
interface FileListOptions extends ListOptions {
|
|
827
|
+
folderPath?: string;
|
|
828
|
+
mimeType?: string;
|
|
829
|
+
uploadedBy?: Uuid;
|
|
830
|
+
includeDeleted?: boolean;
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Result of an operation
|
|
834
|
+
*/
|
|
835
|
+
interface OperationResult<T = unknown> {
|
|
836
|
+
success: boolean;
|
|
837
|
+
data?: T;
|
|
838
|
+
error?: {
|
|
839
|
+
code: string;
|
|
840
|
+
message: string;
|
|
841
|
+
details?: Record<string, unknown>;
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Repository for objects table (metadata)
|
|
847
|
+
*/
|
|
848
|
+
interface ObjectsRepository {
|
|
849
|
+
/**
|
|
850
|
+
* Find object by ID
|
|
851
|
+
*/
|
|
852
|
+
findById(id: Uuid): Promise<DBObject | null>;
|
|
853
|
+
/**
|
|
854
|
+
* Find object by name and tenant
|
|
855
|
+
*/
|
|
856
|
+
findByName(tenantId: TenantId, name: string): Promise<DBObject | null>;
|
|
857
|
+
/**
|
|
858
|
+
* Find system/native object by name (system=true, for sync)
|
|
859
|
+
*/
|
|
860
|
+
findSystemByName(name: string): Promise<DBObject | null>;
|
|
861
|
+
/**
|
|
862
|
+
* Create object
|
|
863
|
+
*/
|
|
864
|
+
create(data: CreateDBObject): Promise<DBObject>;
|
|
865
|
+
/**
|
|
866
|
+
* Update object
|
|
867
|
+
*/
|
|
868
|
+
update(id: Uuid, data: Partial<UpdateDBObject>): Promise<DBObject>;
|
|
869
|
+
/**
|
|
870
|
+
* Delete object
|
|
871
|
+
*/
|
|
872
|
+
delete(id: Uuid): Promise<void>;
|
|
873
|
+
/**
|
|
874
|
+
* List all objects for a tenant
|
|
875
|
+
*/
|
|
876
|
+
list(tenantId: TenantId): Promise<DBObject[]>;
|
|
877
|
+
/**
|
|
878
|
+
* Upsert object (create or update based on nativeObjectId)
|
|
879
|
+
*/
|
|
880
|
+
upsert(data: UpsertDBObject): Promise<DBObject>;
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Repository for attributes table (metadata)
|
|
884
|
+
*/
|
|
885
|
+
interface AttributesRepository {
|
|
886
|
+
/**
|
|
887
|
+
* Find attribute by ID
|
|
888
|
+
*/
|
|
889
|
+
findById(id: Uuid): Promise<DBAttribute | null>;
|
|
890
|
+
/**
|
|
891
|
+
* Find attributes by object ID
|
|
892
|
+
*/
|
|
893
|
+
findByObjectId(objectId: Uuid): Promise<DBAttribute[]>;
|
|
894
|
+
/**
|
|
895
|
+
* Create attribute
|
|
896
|
+
*/
|
|
897
|
+
create(data: CreateDBAttribute): Promise<DBAttribute>;
|
|
898
|
+
/**
|
|
899
|
+
* Update attribute
|
|
900
|
+
*/
|
|
901
|
+
update(id: Uuid, data: Partial<UpdateDBAttribute>): Promise<DBAttribute>;
|
|
902
|
+
/**
|
|
903
|
+
* Delete attribute
|
|
904
|
+
*/
|
|
905
|
+
delete(id: Uuid): Promise<void>;
|
|
906
|
+
/**
|
|
907
|
+
* Delete multiple attributes by names (for sync cleanup)
|
|
908
|
+
*/
|
|
909
|
+
deleteByNames(objectId: Uuid, excludeNames: string[]): Promise<number>;
|
|
910
|
+
/**
|
|
911
|
+
* Upsert attribute (create or update based on objectId + name)
|
|
912
|
+
*/
|
|
913
|
+
upsert(data: UpsertDBAttribute): Promise<DBAttribute>;
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Repository for user_profiles table
|
|
917
|
+
*/
|
|
918
|
+
interface UserProfilesRepository {
|
|
919
|
+
/**
|
|
920
|
+
* Find user profile by ID
|
|
921
|
+
*/
|
|
922
|
+
findById(id: Uuid): Promise<UserProfile | null>;
|
|
923
|
+
/**
|
|
924
|
+
* Find user profile by auth ID (external auth provider)
|
|
925
|
+
*/
|
|
926
|
+
findByAuthId(authId: string): Promise<UserProfile | null>;
|
|
927
|
+
/**
|
|
928
|
+
* Find user profile by email
|
|
929
|
+
*/
|
|
930
|
+
findByEmail(tenantId: TenantId, email: string): Promise<UserProfile | null>;
|
|
931
|
+
/**
|
|
932
|
+
* Create user profile
|
|
933
|
+
*/
|
|
934
|
+
create(data: CreateUserProfile): Promise<UserProfile>;
|
|
935
|
+
/**
|
|
936
|
+
* Update user profile
|
|
937
|
+
*/
|
|
938
|
+
update(id: Uuid, data: UpdateUserProfile): Promise<UserProfile>;
|
|
939
|
+
/**
|
|
940
|
+
* Delete user profile
|
|
941
|
+
*/
|
|
942
|
+
delete(id: Uuid): Promise<void>;
|
|
943
|
+
/**
|
|
944
|
+
* List all user profiles for a tenant
|
|
945
|
+
*/
|
|
946
|
+
list(tenantId: TenantId, options?: ListOptions): Promise<UserProfile[]>;
|
|
947
|
+
/**
|
|
948
|
+
* Update last login timestamp
|
|
949
|
+
*/
|
|
950
|
+
updateLastLogin(id: Uuid): Promise<void>;
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Repository for files table
|
|
954
|
+
*/
|
|
955
|
+
interface FilesRepository {
|
|
956
|
+
/**
|
|
957
|
+
* Find file by ID
|
|
958
|
+
*/
|
|
959
|
+
findById(id: Uuid): Promise<File | null>;
|
|
960
|
+
/**
|
|
961
|
+
* Create file
|
|
962
|
+
*/
|
|
963
|
+
create(data: CreateFile): Promise<File>;
|
|
964
|
+
/**
|
|
965
|
+
* Update file
|
|
966
|
+
*/
|
|
967
|
+
update(id: Uuid, data: UpdateFile): Promise<File>;
|
|
968
|
+
/**
|
|
969
|
+
* Delete file (soft delete)
|
|
970
|
+
*/
|
|
971
|
+
delete(id: Uuid): Promise<void>;
|
|
972
|
+
/**
|
|
973
|
+
* Hard delete file (permanent)
|
|
974
|
+
*/
|
|
975
|
+
hardDelete(id: Uuid): Promise<void>;
|
|
976
|
+
/**
|
|
977
|
+
* List files for a tenant
|
|
978
|
+
*/
|
|
979
|
+
list(tenantId: TenantId, options?: FileListOptions): Promise<File[]>;
|
|
980
|
+
/**
|
|
981
|
+
* Find files by folder path
|
|
982
|
+
*/
|
|
983
|
+
findByFolder(tenantId: TenantId, folderPath: string): Promise<File[]>;
|
|
984
|
+
/**
|
|
985
|
+
* Find files by uploader
|
|
986
|
+
*/
|
|
987
|
+
findByUploader(uploadedBy: Uuid): Promise<File[]>;
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Repository for object_records table (unified JSONB)
|
|
991
|
+
*/
|
|
992
|
+
interface ObjectRecordsRepository {
|
|
993
|
+
/**
|
|
994
|
+
* Find record by ID
|
|
995
|
+
*/
|
|
996
|
+
findById(id: Uuid): Promise<ObjectRecord | null>;
|
|
997
|
+
/**
|
|
998
|
+
* Create record
|
|
999
|
+
*/
|
|
1000
|
+
create(data: CreateObjectRecord): Promise<ObjectRecord>;
|
|
1001
|
+
/**
|
|
1002
|
+
* Update record
|
|
1003
|
+
*/
|
|
1004
|
+
update(id: Uuid, data: Partial<Record<string, unknown>>): Promise<ObjectRecord>;
|
|
1005
|
+
/**
|
|
1006
|
+
* Delete record
|
|
1007
|
+
*/
|
|
1008
|
+
delete(id: Uuid): Promise<void>;
|
|
1009
|
+
/**
|
|
1010
|
+
* List records for an object
|
|
1011
|
+
*/
|
|
1012
|
+
list(tenantId: TenantId, objectId: Uuid, options?: ListOptions): Promise<{
|
|
1013
|
+
records: ObjectRecord[];
|
|
1014
|
+
total: number;
|
|
1015
|
+
}>;
|
|
1016
|
+
/**
|
|
1017
|
+
* Search records using PostgreSQL full-text search
|
|
1018
|
+
*/
|
|
1019
|
+
search(tenantId: TenantId, objectId: Uuid, query: string, options?: SearchOptions): Promise<{
|
|
1020
|
+
records: ObjectRecord[];
|
|
1021
|
+
total: number;
|
|
1022
|
+
}>;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Generic database adapter interface
|
|
1027
|
+
* Any ORM (Drizzle, Prisma, Kysely, TypeORM) can implement this interface
|
|
1028
|
+
* to work with @stndrds/schema runtime services
|
|
1029
|
+
*
|
|
1030
|
+
* @example
|
|
1031
|
+
* ```typescript
|
|
1032
|
+
* // Drizzle implementation
|
|
1033
|
+
* import { createDrizzleAdapter } from "./adapters/drizzle";
|
|
1034
|
+
* import { db } from "./db";
|
|
1035
|
+
*
|
|
1036
|
+
* const adapter = createDrizzleAdapter(db);
|
|
1037
|
+
*
|
|
1038
|
+
* // Prisma implementation
|
|
1039
|
+
* import { createPrismaAdapter } from "./adapters/prisma";
|
|
1040
|
+
* import { prisma } from "./db";
|
|
1041
|
+
*
|
|
1042
|
+
* const adapter = createPrismaAdapter(prisma);
|
|
1043
|
+
* ```
|
|
1044
|
+
*/
|
|
1045
|
+
interface DatabaseAdapter {
|
|
1046
|
+
objects: ObjectsRepository;
|
|
1047
|
+
attributes: AttributesRepository;
|
|
1048
|
+
userProfiles: UserProfilesRepository;
|
|
1049
|
+
files: FilesRepository;
|
|
1050
|
+
objectRecords: ObjectRecordsRepository;
|
|
1051
|
+
transaction<T>(callback: (adapter: DatabaseAdapter) => Promise<T>): Promise<T>;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
interface InternalObjectRecord extends ObjectRecord {
|
|
1055
|
+
tenantId: TenantId;
|
|
1056
|
+
}
|
|
1057
|
+
interface MockStores {
|
|
1058
|
+
objects: Map<Uuid, DBObject>;
|
|
1059
|
+
attributes: Map<Uuid, DBAttribute>;
|
|
1060
|
+
userProfiles: Map<Uuid, UserProfile>;
|
|
1061
|
+
files: Map<Uuid, File>;
|
|
1062
|
+
objectRecords: Map<Uuid, InternalObjectRecord>;
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Create an in-memory mock adapter for testing and development
|
|
1066
|
+
*
|
|
1067
|
+
* This adapter stores all data in memory and is perfect for:
|
|
1068
|
+
* - Unit tests
|
|
1069
|
+
* - Integration tests
|
|
1070
|
+
* - Local development without a database
|
|
1071
|
+
* - Understanding the adapter interface
|
|
1072
|
+
*
|
|
1073
|
+
* @returns A DatabaseAdapter implementation using in-memory storage
|
|
1074
|
+
*
|
|
1075
|
+
* @example
|
|
1076
|
+
* ```typescript
|
|
1077
|
+
* import { createMockAdapter, RecordService } from "@stndrds/schema";
|
|
1078
|
+
*
|
|
1079
|
+
* // Create adapter
|
|
1080
|
+
* const adapter = createMockAdapter();
|
|
1081
|
+
*
|
|
1082
|
+
* // Use with services
|
|
1083
|
+
* const recordService = new RecordService(adapter, "tenant-123");
|
|
1084
|
+
*
|
|
1085
|
+
* // Create a record
|
|
1086
|
+
* const product = await recordService.createRecord("obj-product", {
|
|
1087
|
+
* name: "Nike Air Max",
|
|
1088
|
+
* price: 129.99
|
|
1089
|
+
* });
|
|
1090
|
+
*
|
|
1091
|
+
* // List records
|
|
1092
|
+
* const { records, total } = await recordService.listRecords("obj-product");
|
|
1093
|
+
* ```
|
|
1094
|
+
*/
|
|
1095
|
+
declare function createMockAdapter(): DatabaseAdapter & {
|
|
1096
|
+
_stores: MockStores;
|
|
1097
|
+
reset(): void;
|
|
1098
|
+
};
|
|
1099
|
+
|
|
1100
|
+
/**
|
|
1101
|
+
* Service for managing files
|
|
1102
|
+
* Handles file metadata CRUD, permissions, and soft delete
|
|
1103
|
+
*/
|
|
1104
|
+
declare class FileService {
|
|
1105
|
+
private adapter;
|
|
1106
|
+
private tenantId;
|
|
1107
|
+
constructor(adapter: DatabaseAdapter, tenantId: string);
|
|
1108
|
+
/**
|
|
1109
|
+
* Create a new file record (after upload to storage)
|
|
1110
|
+
*
|
|
1111
|
+
* @param data - File metadata
|
|
1112
|
+
* @returns Created file record
|
|
1113
|
+
*
|
|
1114
|
+
* @example
|
|
1115
|
+
* ```typescript
|
|
1116
|
+
* const service = new FileService(adapter, "tenant-123");
|
|
1117
|
+
*
|
|
1118
|
+
* // After uploading to S3
|
|
1119
|
+
* const file = await service.createFile({
|
|
1120
|
+
* tenantId: "tenant-123",
|
|
1121
|
+
* name: "contract-2025.pdf",
|
|
1122
|
+
* originalName: "Contract Acme Corp 2025.pdf",
|
|
1123
|
+
* mimeType: "application/pdf",
|
|
1124
|
+
* size: 2458624,
|
|
1125
|
+
* storageProvider: "s3",
|
|
1126
|
+
* storagePath: "tenants/123/files/2025/contract.pdf",
|
|
1127
|
+
* storageBucket: "my-app-files",
|
|
1128
|
+
* url: "https://cdn.example.com/files/file-123",
|
|
1129
|
+
* uploadedBy: "profile-456",
|
|
1130
|
+
* visibility: "private"
|
|
1131
|
+
* });
|
|
1132
|
+
* ```
|
|
1133
|
+
*/
|
|
1134
|
+
createFile(data: CreateFile): Promise<File>;
|
|
1135
|
+
/**
|
|
1136
|
+
* Get file by ID
|
|
1137
|
+
*/
|
|
1138
|
+
getFile(fileId: string): Promise<File | null>;
|
|
1139
|
+
/**
|
|
1140
|
+
* Get file by ID or throw
|
|
1141
|
+
*/
|
|
1142
|
+
getFileOrThrow(fileId: string): Promise<File>;
|
|
1143
|
+
/**
|
|
1144
|
+
* Update file metadata
|
|
1145
|
+
*
|
|
1146
|
+
* @param fileId - File UUID
|
|
1147
|
+
* @param data - Data to update
|
|
1148
|
+
* @returns Updated file
|
|
1149
|
+
*/
|
|
1150
|
+
updateFile(fileId: string, data: UpdateFile): Promise<File>;
|
|
1151
|
+
/**
|
|
1152
|
+
* Delete file (soft delete)
|
|
1153
|
+
*
|
|
1154
|
+
* @param fileId - File UUID
|
|
1155
|
+
* @param options - Delete options
|
|
1156
|
+
*/
|
|
1157
|
+
deleteFile(fileId: string, options?: {
|
|
1158
|
+
hard?: boolean;
|
|
1159
|
+
checkOwnership?: boolean;
|
|
1160
|
+
userId?: string;
|
|
1161
|
+
}): Promise<void>;
|
|
1162
|
+
/**
|
|
1163
|
+
* List files for the tenant
|
|
1164
|
+
*/
|
|
1165
|
+
listFiles(options?: FileListOptions): Promise<File[]>;
|
|
1166
|
+
/**
|
|
1167
|
+
* List files by folder
|
|
1168
|
+
*/
|
|
1169
|
+
listFilesByFolder(folderPath: string): Promise<File[]>;
|
|
1170
|
+
/**
|
|
1171
|
+
* List files uploaded by a specific user
|
|
1172
|
+
*/
|
|
1173
|
+
listFilesByUploader(uploadedBy: string): Promise<File[]>;
|
|
1174
|
+
/**
|
|
1175
|
+
* Change file visibility
|
|
1176
|
+
*
|
|
1177
|
+
* @param fileId - File UUID
|
|
1178
|
+
* @param visibility - New visibility level
|
|
1179
|
+
* @param allowedUsers - Users allowed to access (if restricted)
|
|
1180
|
+
*/
|
|
1181
|
+
changeVisibility(fileId: string, visibility: "public" | "private" | "restricted", allowedUsers?: string[]): Promise<File>;
|
|
1182
|
+
/**
|
|
1183
|
+
* Grant access to a file for specific users
|
|
1184
|
+
*
|
|
1185
|
+
* @param fileId - File UUID
|
|
1186
|
+
* @param userIds - User IDs to grant access
|
|
1187
|
+
*/
|
|
1188
|
+
grantAccess(fileId: string, userIds: string[]): Promise<File>;
|
|
1189
|
+
/**
|
|
1190
|
+
* Revoke access to a file for specific users
|
|
1191
|
+
*
|
|
1192
|
+
* @param fileId - File UUID
|
|
1193
|
+
* @param userIds - User IDs to revoke access
|
|
1194
|
+
*/
|
|
1195
|
+
revokeAccess(fileId: string, userIds: string[]): Promise<File>;
|
|
1196
|
+
/**
|
|
1197
|
+
* Check if user has access to a file
|
|
1198
|
+
*
|
|
1199
|
+
* @param fileId - File UUID
|
|
1200
|
+
* @param userId - User ID to check
|
|
1201
|
+
* @returns true if user can access the file
|
|
1202
|
+
*/
|
|
1203
|
+
canAccess(fileId: string, userId: string): Promise<boolean>;
|
|
1204
|
+
/**
|
|
1205
|
+
* Move file to different folder
|
|
1206
|
+
*/
|
|
1207
|
+
moveToFolder(fileId: string, newFolderPath: string): Promise<File>;
|
|
1208
|
+
/**
|
|
1209
|
+
* Add tags to file
|
|
1210
|
+
*/
|
|
1211
|
+
addTags(fileId: string, tags: string[]): Promise<File>;
|
|
1212
|
+
/**
|
|
1213
|
+
* Remove tags from file
|
|
1214
|
+
*/
|
|
1215
|
+
removeTags(fileId: string, tags: string[]): Promise<File>;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
/**
|
|
1219
|
+
* Input for creating a custom object
|
|
1220
|
+
*/
|
|
1221
|
+
interface CreateCustomObjectInput {
|
|
1222
|
+
name: string;
|
|
1223
|
+
label: string;
|
|
1224
|
+
description?: string;
|
|
1225
|
+
icon?: IconName;
|
|
1226
|
+
attributes?: (Attribute | {
|
|
1227
|
+
build: () => Attribute;
|
|
1228
|
+
})[];
|
|
1229
|
+
metadata?: Record<string, unknown>;
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Input for adding an attribute to an object
|
|
1233
|
+
*/
|
|
1234
|
+
interface AddAttributeInput {
|
|
1235
|
+
name: string;
|
|
1236
|
+
label: string;
|
|
1237
|
+
type: AttributeType;
|
|
1238
|
+
required?: boolean;
|
|
1239
|
+
unique?: boolean;
|
|
1240
|
+
description?: string;
|
|
1241
|
+
placeholder?: string;
|
|
1242
|
+
icon?: IconName;
|
|
1243
|
+
defaultValue?: unknown;
|
|
1244
|
+
metadata?: Record<string, unknown>;
|
|
1245
|
+
[key: string]: unknown;
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Service for managing object schemas
|
|
1249
|
+
* Handles fusion of native objects (from registry) and custom objects (from database)
|
|
1250
|
+
*/
|
|
1251
|
+
declare class ObjectSchemaService {
|
|
1252
|
+
private adapter;
|
|
1253
|
+
private nativeRegistry;
|
|
1254
|
+
constructor(adapter: DatabaseAdapter, nativeRegistry: typeof registry);
|
|
1255
|
+
/**
|
|
1256
|
+
* Create a new custom object
|
|
1257
|
+
* Validates name format and ensures system=false
|
|
1258
|
+
*
|
|
1259
|
+
* @param definition - Object definition (attributes can be raw definitions or builders)
|
|
1260
|
+
* @param tenantId - Tenant ID owning the object
|
|
1261
|
+
* @returns Created ObjectDefinition
|
|
1262
|
+
*/
|
|
1263
|
+
createCustomObject(definition: CreateCustomObjectInput, tenantId: string): Promise<ObjectDefinition>;
|
|
1264
|
+
/**
|
|
1265
|
+
* Add a custom attribute to an existing object
|
|
1266
|
+
* Can be used on both custom and native objects
|
|
1267
|
+
* Attributes added via API are always custom (system=false)
|
|
1268
|
+
*
|
|
1269
|
+
* @param objectId - Object UUID from database
|
|
1270
|
+
* @param attribute - Attribute definition
|
|
1271
|
+
* @returns Created Attribute
|
|
1272
|
+
*/
|
|
1273
|
+
addAttributeToObject(objectId: string, attribute: AddAttributeInput): Promise<Attribute>;
|
|
1274
|
+
/**
|
|
1275
|
+
* Update an attribute
|
|
1276
|
+
* Can only update custom attributes (system=false)
|
|
1277
|
+
*
|
|
1278
|
+
* @param attributeId - Attribute UUID
|
|
1279
|
+
* @param updates - Partial attribute updates
|
|
1280
|
+
* @returns Updated Attribute
|
|
1281
|
+
*/
|
|
1282
|
+
updateAttribute(attributeId: string, updates: Partial<AddAttributeInput>): Promise<Attribute>;
|
|
1283
|
+
/**
|
|
1284
|
+
* Delete an attribute
|
|
1285
|
+
* Can only delete custom attributes (system=false)
|
|
1286
|
+
*
|
|
1287
|
+
* @param attributeId - Attribute UUID
|
|
1288
|
+
*/
|
|
1289
|
+
deleteAttribute(attributeId: string): Promise<void>;
|
|
1290
|
+
/**
|
|
1291
|
+
* List attributes for an object
|
|
1292
|
+
*
|
|
1293
|
+
* @param objectId - Object UUID
|
|
1294
|
+
* @param options - Filter options
|
|
1295
|
+
* @returns List of attributes
|
|
1296
|
+
*/
|
|
1297
|
+
listAttributes(objectId: string, options?: {
|
|
1298
|
+
systemOnly?: boolean;
|
|
1299
|
+
customOnly?: boolean;
|
|
1300
|
+
}): Promise<Attribute[]>;
|
|
1301
|
+
/**
|
|
1302
|
+
* Validate attribute name format (variable identifier)
|
|
1303
|
+
* @internal
|
|
1304
|
+
*/
|
|
1305
|
+
private validateAttributeName;
|
|
1306
|
+
/**
|
|
1307
|
+
* Get complete object schema (system + custom attributes)
|
|
1308
|
+
* If object is native, merges registry definition with DB custom attributes
|
|
1309
|
+
* If object is custom, returns DB definition only
|
|
1310
|
+
*
|
|
1311
|
+
* @param objectId - Object UUID from database
|
|
1312
|
+
* @returns Complete ObjectDefinition with all attributes
|
|
1313
|
+
*
|
|
1314
|
+
* @example
|
|
1315
|
+
* ```typescript
|
|
1316
|
+
* const service = new ObjectSchemaService(adapter, registry);
|
|
1317
|
+
* const productSchema = await service.getObjectSchema("obj-123");
|
|
1318
|
+
*
|
|
1319
|
+
* console.log(productSchema.attributes); // System + custom attributes
|
|
1320
|
+
* ```
|
|
1321
|
+
*/
|
|
1322
|
+
getObjectSchema(objectId: string): Promise<ObjectDefinition>;
|
|
1323
|
+
/**
|
|
1324
|
+
* Get object schema by name (for system/native objects)
|
|
1325
|
+
*/
|
|
1326
|
+
getObjectSchemaByName(name: string): Promise<ObjectDefinition>;
|
|
1327
|
+
/**
|
|
1328
|
+
* List all object schemas for a tenant
|
|
1329
|
+
*/
|
|
1330
|
+
listObjectSchemas(tenantId: string): Promise<ObjectDefinition[]>;
|
|
1331
|
+
/**
|
|
1332
|
+
* Merge native object from registry with custom attributes from DB
|
|
1333
|
+
* @internal
|
|
1334
|
+
*/
|
|
1335
|
+
private mergeNativeObject;
|
|
1336
|
+
/**
|
|
1337
|
+
* Convert DB object + attributes to ObjectDefinition
|
|
1338
|
+
* @internal
|
|
1339
|
+
*/
|
|
1340
|
+
private convertDBObjectToDefinition;
|
|
1341
|
+
/**
|
|
1342
|
+
* Convert DB attribute to Attribute type
|
|
1343
|
+
* @internal
|
|
1344
|
+
*/
|
|
1345
|
+
private convertDBAttributeToAttribute;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* Service for managing object records (CRUD operations)
|
|
1350
|
+
* Handles validation, dispatch to correct table, and data consistency
|
|
1351
|
+
*/
|
|
1352
|
+
declare class RecordService {
|
|
1353
|
+
private adapter;
|
|
1354
|
+
private tenantId;
|
|
1355
|
+
private schemaService;
|
|
1356
|
+
constructor(adapter: DatabaseAdapter, tenantId: string);
|
|
1357
|
+
/**
|
|
1358
|
+
* Create a new record with validation
|
|
1359
|
+
*
|
|
1360
|
+
* @param objectId - Object UUID
|
|
1361
|
+
* @param data - Record data (attribute values)
|
|
1362
|
+
* @param options - Creation options
|
|
1363
|
+
* @returns Created record
|
|
1364
|
+
*
|
|
1365
|
+
* @example
|
|
1366
|
+
* ```typescript
|
|
1367
|
+
* const service = new RecordService(adapter, "tenant-123");
|
|
1368
|
+
*
|
|
1369
|
+
* const product = await service.createRecord("obj-product", {
|
|
1370
|
+
* name: "Nike Air Max",
|
|
1371
|
+
* price: 129.99,
|
|
1372
|
+
* sku: "NIKE-AM-001"
|
|
1373
|
+
* });
|
|
1374
|
+
* ```
|
|
1375
|
+
*/
|
|
1376
|
+
createRecord(objectId: string, data: Record<string, unknown>, options?: {
|
|
1377
|
+
validate?: boolean;
|
|
1378
|
+
skipSystemCheck?: boolean;
|
|
1379
|
+
}): Promise<ObjectRecord>;
|
|
1380
|
+
/**
|
|
1381
|
+
* Get a record by ID
|
|
1382
|
+
*
|
|
1383
|
+
* @param recordId - Record UUID
|
|
1384
|
+
* @param options - Query options
|
|
1385
|
+
* @returns Record or null if not found
|
|
1386
|
+
*/
|
|
1387
|
+
getRecord(recordId: string, options?: {
|
|
1388
|
+
includeSchema?: boolean;
|
|
1389
|
+
}): Promise<ObjectRecord | null>;
|
|
1390
|
+
/**
|
|
1391
|
+
* Get a record by ID or throw if not found
|
|
1392
|
+
*/
|
|
1393
|
+
getRecordOrThrow(recordId: string): Promise<ObjectRecord>;
|
|
1394
|
+
/**
|
|
1395
|
+
* Update a record with validation
|
|
1396
|
+
*
|
|
1397
|
+
* @param recordId - Record UUID
|
|
1398
|
+
* @param data - Partial data to update
|
|
1399
|
+
* @param options - Update options
|
|
1400
|
+
* @returns Updated record
|
|
1401
|
+
*/
|
|
1402
|
+
updateRecord(recordId: string, data: Partial<Record<string, unknown>>, options?: {
|
|
1403
|
+
validate?: boolean;
|
|
1404
|
+
partial?: boolean;
|
|
1405
|
+
}): Promise<ObjectRecord>;
|
|
1406
|
+
/**
|
|
1407
|
+
* Delete a record
|
|
1408
|
+
*
|
|
1409
|
+
* @param recordId - Record UUID
|
|
1410
|
+
* @param options - Delete options
|
|
1411
|
+
*/
|
|
1412
|
+
deleteRecord(recordId: string, options?: {
|
|
1413
|
+
checkSystem?: boolean;
|
|
1414
|
+
}): Promise<void>;
|
|
1415
|
+
/**
|
|
1416
|
+
* List records for an object with pagination
|
|
1417
|
+
*
|
|
1418
|
+
* @param objectId - Object UUID
|
|
1419
|
+
* @param options - List options
|
|
1420
|
+
* @returns Records and total count
|
|
1421
|
+
*/
|
|
1422
|
+
listRecords(objectId: string, options?: {
|
|
1423
|
+
limit?: number;
|
|
1424
|
+
offset?: number;
|
|
1425
|
+
orderBy?: string;
|
|
1426
|
+
orderDirection?: "asc" | "desc";
|
|
1427
|
+
}): Promise<{
|
|
1428
|
+
records: ObjectRecord[];
|
|
1429
|
+
total: number;
|
|
1430
|
+
}>;
|
|
1431
|
+
/**
|
|
1432
|
+
* Search records using full-text search
|
|
1433
|
+
*
|
|
1434
|
+
* @param objectId - Object UUID
|
|
1435
|
+
* @param query - Search query
|
|
1436
|
+
* @param options - Search options
|
|
1437
|
+
* @returns Matching records and total count
|
|
1438
|
+
*/
|
|
1439
|
+
searchRecords(objectId: string, query: string, options?: {
|
|
1440
|
+
limit?: number;
|
|
1441
|
+
offset?: number;
|
|
1442
|
+
filters?: Record<string, unknown>;
|
|
1443
|
+
}): Promise<{
|
|
1444
|
+
records: ObjectRecord[];
|
|
1445
|
+
total: number;
|
|
1446
|
+
}>;
|
|
1447
|
+
/**
|
|
1448
|
+
* Validate data against object schema without saving
|
|
1449
|
+
*
|
|
1450
|
+
* @param objectId - Object UUID
|
|
1451
|
+
* @param data - Data to validate
|
|
1452
|
+
* @returns Validation result
|
|
1453
|
+
*/
|
|
1454
|
+
validateData(objectId: string, data: Record<string, unknown>): Promise<ValidationResult>;
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
/**
|
|
1458
|
+
* Service for managing user profiles
|
|
1459
|
+
* Handles user profile CRUD, auth provider sync, and role management
|
|
1460
|
+
*/
|
|
1461
|
+
declare class UserProfileService {
|
|
1462
|
+
private adapter;
|
|
1463
|
+
private tenantId;
|
|
1464
|
+
constructor(adapter: DatabaseAdapter, tenantId: string);
|
|
1465
|
+
/**
|
|
1466
|
+
* Create a new user profile (typically after first auth)
|
|
1467
|
+
*
|
|
1468
|
+
* @param data - User profile creation data
|
|
1469
|
+
* @returns Created user profile
|
|
1470
|
+
*
|
|
1471
|
+
* @example
|
|
1472
|
+
* ```typescript
|
|
1473
|
+
* const service = new UserProfileService(adapter, "tenant-123");
|
|
1474
|
+
*
|
|
1475
|
+
* // After Supabase auth
|
|
1476
|
+
* const profile = await service.createProfile({
|
|
1477
|
+
* tenantId: "tenant-123",
|
|
1478
|
+
* authId: authUser.id,
|
|
1479
|
+
* email: authUser.email,
|
|
1480
|
+
* firstName: authUser.user_metadata.first_name,
|
|
1481
|
+
* lastName: authUser.user_metadata.last_name,
|
|
1482
|
+
* role: "member",
|
|
1483
|
+
* status: "active"
|
|
1484
|
+
* });
|
|
1485
|
+
* ```
|
|
1486
|
+
*/
|
|
1487
|
+
createProfile(data: CreateUserProfile): Promise<UserProfile>;
|
|
1488
|
+
/**
|
|
1489
|
+
* Get user profile by ID
|
|
1490
|
+
*/
|
|
1491
|
+
getProfile(profileId: string): Promise<UserProfile | null>;
|
|
1492
|
+
/**
|
|
1493
|
+
* Get user profile by ID or throw
|
|
1494
|
+
*/
|
|
1495
|
+
getProfileOrThrow(profileId: string): Promise<UserProfile>;
|
|
1496
|
+
/**
|
|
1497
|
+
* Get user profile by auth provider ID
|
|
1498
|
+
*
|
|
1499
|
+
* @param authId - Auth provider user ID (Supabase, Clerk, etc.)
|
|
1500
|
+
* @returns User profile or null
|
|
1501
|
+
*/
|
|
1502
|
+
getProfileByAuthId(authId: string): Promise<UserProfile | null>;
|
|
1503
|
+
/**
|
|
1504
|
+
* Get or create user profile (idempotent operation)
|
|
1505
|
+
* Useful for auth callbacks - ensures profile exists
|
|
1506
|
+
*
|
|
1507
|
+
* @param authId - Auth provider user ID
|
|
1508
|
+
* @param data - Profile data to create if doesn't exist
|
|
1509
|
+
* @returns Existing or newly created profile
|
|
1510
|
+
*
|
|
1511
|
+
* @example
|
|
1512
|
+
* ```typescript
|
|
1513
|
+
* // In Supabase auth callback
|
|
1514
|
+
* const profile = await service.getOrCreateProfile(
|
|
1515
|
+
* authUser.id,
|
|
1516
|
+
* {
|
|
1517
|
+
* tenantId: "tenant-123",
|
|
1518
|
+
* authId: authUser.id,
|
|
1519
|
+
* email: authUser.email,
|
|
1520
|
+
* role: "member",
|
|
1521
|
+
* status: "active"
|
|
1522
|
+
* }
|
|
1523
|
+
* );
|
|
1524
|
+
* ```
|
|
1525
|
+
*/
|
|
1526
|
+
getOrCreateProfile(authId: string, data: CreateUserProfile): Promise<UserProfile>;
|
|
1527
|
+
/**
|
|
1528
|
+
* Update user profile
|
|
1529
|
+
*/
|
|
1530
|
+
updateProfile(profileId: string, data: UpdateUserProfile): Promise<UserProfile>;
|
|
1531
|
+
/**
|
|
1532
|
+
* Delete user profile
|
|
1533
|
+
*
|
|
1534
|
+
* @param profileId - Profile UUID
|
|
1535
|
+
* @param options - Delete options
|
|
1536
|
+
*/
|
|
1537
|
+
deleteProfile(profileId: string, options?: {
|
|
1538
|
+
checkAdmin?: boolean;
|
|
1539
|
+
}): Promise<void>;
|
|
1540
|
+
/**
|
|
1541
|
+
* List all user profiles for the tenant
|
|
1542
|
+
*/
|
|
1543
|
+
listProfiles(options?: ListOptions): Promise<UserProfile[]>;
|
|
1544
|
+
/**
|
|
1545
|
+
* Update last login timestamp
|
|
1546
|
+
*
|
|
1547
|
+
* @param profileId - Profile UUID
|
|
1548
|
+
*
|
|
1549
|
+
* @example
|
|
1550
|
+
* ```typescript
|
|
1551
|
+
* // After successful auth
|
|
1552
|
+
* await service.updateLastLogin(profile.id);
|
|
1553
|
+
* ```
|
|
1554
|
+
*/
|
|
1555
|
+
updateLastLogin(profileId: string): Promise<void>;
|
|
1556
|
+
/**
|
|
1557
|
+
* Change user role
|
|
1558
|
+
*
|
|
1559
|
+
* @param profileId - Profile UUID
|
|
1560
|
+
* @param newRole - New role
|
|
1561
|
+
*/
|
|
1562
|
+
changeRole(profileId: string, newRole: string): Promise<UserProfile>;
|
|
1563
|
+
/**
|
|
1564
|
+
* Change user status
|
|
1565
|
+
*
|
|
1566
|
+
* @param profileId - Profile UUID
|
|
1567
|
+
* @param newStatus - New status
|
|
1568
|
+
*/
|
|
1569
|
+
changeStatus(profileId: string, newStatus: "active" | "pending" | "inactive" | "suspended"): Promise<UserProfile>;
|
|
1570
|
+
/**
|
|
1571
|
+
* Get user by email
|
|
1572
|
+
*/
|
|
1573
|
+
getProfileByEmail(email: string): Promise<UserProfile | null>;
|
|
1574
|
+
/**
|
|
1575
|
+
* Check if user has role
|
|
1576
|
+
*/
|
|
1577
|
+
hasRole(profileId: string, role: string): Promise<boolean>;
|
|
1578
|
+
/**
|
|
1579
|
+
* Check if user is admin
|
|
1580
|
+
*/
|
|
1581
|
+
isAdmin(profileId: string): Promise<boolean>;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
/**
|
|
1585
|
+
* Result of sync operation
|
|
1586
|
+
*/
|
|
1587
|
+
interface SyncResult {
|
|
1588
|
+
success: boolean;
|
|
1589
|
+
objectsSynced: number;
|
|
1590
|
+
attributesSynced: number;
|
|
1591
|
+
objectsCreated: number;
|
|
1592
|
+
objectsUpdated: number;
|
|
1593
|
+
attributesCreated: number;
|
|
1594
|
+
attributesUpdated: number;
|
|
1595
|
+
attributesDeleted: number;
|
|
1596
|
+
errors: Array<{
|
|
1597
|
+
objectName: string;
|
|
1598
|
+
error: string;
|
|
1599
|
+
}>;
|
|
1600
|
+
}
|
|
1601
|
+
/**
|
|
1602
|
+
* Sync options
|
|
1603
|
+
*/
|
|
1604
|
+
interface SyncOptions {
|
|
1605
|
+
dryRun?: boolean;
|
|
1606
|
+
verbose?: boolean;
|
|
1607
|
+
tenantId?: string;
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* Sync native objects from registry to database
|
|
1611
|
+
*
|
|
1612
|
+
* This function:
|
|
1613
|
+
* 1. Reads all registered native objects from the registry
|
|
1614
|
+
* 2. Upserts them into the database (objects + attributes tables)
|
|
1615
|
+
* 3. Marks them as system=true for protection
|
|
1616
|
+
* 4. Removes attributes that were deleted from code
|
|
1617
|
+
*
|
|
1618
|
+
* @param adapter - Database adapter implementing DatabaseAdapter interface
|
|
1619
|
+
* @param nativeRegistry - Registry containing native objects
|
|
1620
|
+
* @param options - Sync options (dryRun, verbose, tenantId)
|
|
1621
|
+
* @returns Sync result with statistics
|
|
1622
|
+
*
|
|
1623
|
+
* @example
|
|
1624
|
+
* ```typescript
|
|
1625
|
+
* import { syncNativeObjects } from "@stndrds/schema/runtime";
|
|
1626
|
+
* import { registry } from "./objects/native";
|
|
1627
|
+
* import { drizzleAdapter } from "./db/adapter";
|
|
1628
|
+
*
|
|
1629
|
+
* const result = await syncNativeObjects(drizzleAdapter, registry, {
|
|
1630
|
+
* verbose: true,
|
|
1631
|
+
* tenantId: "default"
|
|
1632
|
+
* });
|
|
1633
|
+
*
|
|
1634
|
+
* if (result.success) {
|
|
1635
|
+
* console.log(`✓ Synced ${result.objectsSynced} objects`);
|
|
1636
|
+
* } else {
|
|
1637
|
+
* console.error("Sync errors:", result.errors);
|
|
1638
|
+
* }
|
|
1639
|
+
* ```
|
|
1640
|
+
*/
|
|
1641
|
+
declare function syncNativeObjects(adapter: DatabaseAdapter, nativeRegistry: typeof registry, options?: SyncOptions): Promise<SyncResult>;
|
|
1642
|
+
/**
|
|
1643
|
+
* Verify that all native objects are synced to database
|
|
1644
|
+
*
|
|
1645
|
+
* @param adapter - Database adapter
|
|
1646
|
+
* @param nativeRegistry - Registry containing native objects
|
|
1647
|
+
* @returns true if all objects are synced, false otherwise
|
|
1648
|
+
*
|
|
1649
|
+
* @example
|
|
1650
|
+
* ```typescript
|
|
1651
|
+
* const isSynced = await verifyNativeObjectsSync(adapter, registry);
|
|
1652
|
+
* if (!isSynced) {
|
|
1653
|
+
* console.warn("Native objects not synced, running sync...");
|
|
1654
|
+
* await syncNativeObjects(adapter, registry);
|
|
1655
|
+
* }
|
|
1656
|
+
* ```
|
|
1657
|
+
*/
|
|
1658
|
+
declare function verifyNativeObjectsSync(adapter: DatabaseAdapter, nativeRegistry: typeof registry): Promise<boolean>;
|
|
1659
|
+
/**
|
|
1660
|
+
* Get sync statistics without modifying database
|
|
1661
|
+
*
|
|
1662
|
+
* @param adapter - Database adapter
|
|
1663
|
+
* @param nativeRegistry - Registry containing native objects
|
|
1664
|
+
* @returns Sync result (dry run)
|
|
1665
|
+
*/
|
|
1666
|
+
declare function getSyncPreview(adapter: DatabaseAdapter, nativeRegistry: typeof registry): Promise<SyncResult>;
|
|
1667
|
+
|
|
1668
|
+
export { createNumberValidator as $, type Attribute as A, type BaseAttribute as B, type CheckboxAttribute as C, type DateAttribute as D, type ObjectAttribute as E, type FileAttribute as F, type ObjectRecord as G, type UserRole as H, type UserStatus as I, type UserProfile as J, type CreateUserProfile as K, type LocationAttribute as L, type MultiselectAttribute as M, type NumberAttribute as N, type Option as O, type PhoneAttribute as P, type UpdateUserProfile as Q, type RelationAttribute as R, type StatusAttribute as S, type TextAttribute as T, type UserAttribute as U, type Uuid as V, type TenantId as W, generateId as X, generatePrefixedId as Y, registry as Z, createTextValidator as _, type TextAreaAttribute as a, createCheckboxValidator as a0, createDateValidator as a1, createPhoneValidator as a2, createCurrencyValidator as a3, createStatusValidator as a4, createSelectValidator as a5, createMultiselectValidator as a6, createLocationValidator as a7, createTimestampValidator as a8, createFileValidator as a9, getSyncPreview as aA, type DBObject as aB, type CreateDBObject as aC, type UpdateDBObject as aD, type UpsertDBObject as aE, type DBAttribute as aF, type CreateDBAttribute as aG, type UpdateDBAttribute as aH, type UpsertDBAttribute as aI, type CreateObjectRecord as aJ, type ListOptions as aK, type SearchOptions as aL, type FileListOptions as aM, type OperationResult as aN, createUserValidator as aa, createRelationValidator as ab, createRatingValidator as ac, createAttributeValidator as ad, createObjectValidator as ae, type ValidationResult as af, validateAttribute as ag, validateObject as ah, validateObjectOrThrow as ai, type DatabaseAdapter as aj, createMockAdapter as ak, type ObjectsRepository as al, type AttributesRepository as am, type UserProfilesRepository as an, type FilesRepository as ao, type ObjectRecordsRepository as ap, FileService as aq, type CreateCustomObjectInput as ar, type AddAttributeInput as as, ObjectSchemaService as at, RecordService as au, UserProfileService as av, type SyncResult as aw, type SyncOptions as ax, syncNativeObjects as ay, verifyNativeObjectsSync as az, type Phone as b, type CurrencyAttribute as c, type Currency as d, type SelectAttribute as e, type Location as f, type TimestampAttribute as g, type RatingAttribute as h, type ObjectDefinition as i, type AttributeType as j, type StatusGroup as k, type AttributeGroup as l, type NumberUnit as m, type DateFormat as n, type DateValue as o, type LocationGranularity as p, type DocumentType as q, type DocumentFace as r, type DocumentTypeConfig as s, type FileVerificationConfig as t, type StorageProvider as u, type FileVisibility as v, type File as w, type CreateFile as x, type UpdateFile as y, type Timestamps as z };
|