@stndrds/schema 1.0.0-alpha.192 → 1.0.0-alpha.194
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/attributes-DP08iPCh.d.mts +889 -0
- package/dist/attributes-Ddhps_AU.d.ts +889 -0
- package/dist/{chunk-MAKSIK3P.js → chunk-5WATIVCA.js} +2 -2
- package/dist/{chunk-4KRLCWJM.mjs → chunk-6I2R22CX.mjs} +1 -1
- package/dist/{chunk-DC7YYE3U.js → chunk-ACKMC6FL.js} +2 -2
- package/dist/{chunk-TEQNVO7W.mjs → chunk-AYVHMVKQ.mjs} +1 -1
- package/dist/{chunk-P2BE2A2G.js → chunk-BV3IUMMW.js} +4 -7
- package/dist/{chunk-CCB4OY2O.js → chunk-E6JUYGJX.js} +2 -2
- package/dist/{chunk-7A3Y6O32.js → chunk-EBGRZUIH.js} +2 -2
- package/dist/{chunk-QWZHR2OO.js → chunk-EW5XR2X3.js} +2 -2
- package/dist/{chunk-UQVW4KPP.mjs → chunk-HRQEZUAA.mjs} +0 -1
- package/dist/{chunk-OAQWRMTP.js → chunk-HTSTPTNZ.js} +2 -2
- package/dist/{chunk-U4JC7P6D.mjs → chunk-HXDDJLHD.mjs} +37 -52
- package/dist/{chunk-Z75P63VF.mjs → chunk-JUKLL5RJ.mjs} +1 -1
- package/dist/{chunk-YQROI4IE.js → chunk-M5QIBMIH.js} +37 -52
- package/dist/{chunk-FQ2ZYOVS.js → chunk-MZOEL7FN.js} +47 -41
- package/dist/{chunk-KWVCGQ7N.mjs → chunk-NOMHDOVE.mjs} +2 -5
- package/dist/{chunk-OLJLCVNY.mjs → chunk-NS63SPNN.mjs} +1 -1
- package/dist/{chunk-J5HRK3WD.js → chunk-O44XVGHE.js} +2 -2
- package/dist/{chunk-TMYBEXBT.mjs → chunk-OSSGN43B.mjs} +1 -1
- package/dist/{chunk-LTHSU4NF.mjs → chunk-PMKCDRRO.mjs} +26 -21
- package/dist/{chunk-4TC27SMF.mjs → chunk-QEZUDVTN.mjs} +1 -1
- package/dist/{chunk-BLXQGQLK.mjs → chunk-QVLQPW3O.mjs} +1 -1
- package/dist/{chunk-EZHMVZQ4.js → chunk-QWKLQRBX.js} +4 -4
- package/dist/{chunk-ROAZLZA2.js → chunk-ROLWVTU3.js} +2 -2
- package/dist/{chunk-3JINFUN3.js → chunk-SGSGREPG.js} +2 -6
- package/dist/{chunk-VXOTYFEW.js → chunk-T6L2MUG2.js} +2 -2
- package/dist/{chunk-KNYZH2WD.mjs → chunk-UANQJ2GH.mjs} +8 -1
- package/dist/{chunk-IRTRJH37.mjs → chunk-UXAHLSWC.mjs} +1 -1
- package/dist/{chunk-ACQ3TUFK.js → chunk-V2UA2D4E.js} +2 -2
- package/dist/{chunk-R4CJHCC4.mjs → chunk-XZ5RG2OG.mjs} +1 -4
- package/dist/{chunk-TKN73433.js → chunk-YIP5FJ5H.js} +9 -2
- package/dist/{chunk-T225DB55.js → chunk-YKWSHBT5.js} +0 -1
- package/dist/{chunk-TBYXYSGA.mjs → chunk-YUFTHPOE.mjs} +1 -1
- package/dist/{chunk-ITTQ4FGR.mjs → chunk-ZDMOXF3U.mjs} +1 -1
- package/dist/{chunk-SMOHDR6M.mjs → chunk-ZDQ5AP26.mjs} +1 -1
- package/dist/{helpers-Y9rNQ6sz.d.ts → helpers-Bph8AoY7.d.mts} +15 -3
- package/dist/{helpers-HrKmSkLi.d.mts → helpers-D9klos6x.d.ts} +15 -3
- package/dist/index.d.mts +1121 -235
- package/dist/index.d.ts +1121 -235
- package/dist/index.js +2369 -854
- package/dist/index.mjs +2264 -774
- package/dist/{types-Bdlhgrf7.d.ts → types-D38kriFw.d.mts} +1 -2
- package/dist/{types-C5DittlR.d.mts → types-iKvYBe6v.d.ts} +1 -2
- package/dist/validation/all.d.mts +3 -3
- package/dist/validation/all.d.ts +3 -3
- package/dist/validation/all.js +30 -27
- package/dist/validation/all.mjs +17 -18
- package/dist/validation/complex/currency.d.mts +2 -2
- package/dist/validation/complex/currency.d.ts +2 -2
- package/dist/validation/complex/currency.js +3 -3
- package/dist/validation/complex/currency.mjs +2 -2
- package/dist/validation/complex/file.d.mts +2 -2
- package/dist/validation/complex/file.d.ts +2 -2
- package/dist/validation/complex/file.js +3 -3
- package/dist/validation/complex/file.mjs +2 -2
- package/dist/validation/complex/location.d.mts +2 -2
- package/dist/validation/complex/location.d.ts +2 -2
- package/dist/validation/complex/location.js +3 -3
- package/dist/validation/complex/location.mjs +2 -2
- package/dist/validation/complex/phone.d.mts +2 -2
- package/dist/validation/complex/phone.d.ts +2 -2
- package/dist/validation/complex/phone.js +3 -3
- package/dist/validation/complex/phone.mjs +2 -2
- package/dist/validation/complex/relation.d.mts +2 -2
- package/dist/validation/complex/relation.d.ts +2 -2
- package/dist/validation/complex/relation.js +5 -5
- package/dist/validation/complex/relation.mjs +2 -2
- package/dist/validation/complex/richtext.d.mts +2 -2
- package/dist/validation/complex/richtext.d.ts +2 -2
- package/dist/validation/complex/richtext.js +3 -3
- package/dist/validation/complex/richtext.mjs +2 -2
- package/dist/validation/complex/select.d.mts +2 -2
- package/dist/validation/complex/select.d.ts +2 -2
- package/dist/validation/complex/select.js +5 -5
- package/dist/validation/complex/select.mjs +2 -2
- package/dist/validation/complex/user.d.mts +2 -2
- package/dist/validation/complex/user.d.ts +2 -2
- package/dist/validation/complex/user.js +3 -3
- package/dist/validation/complex/user.mjs +2 -2
- package/dist/validation/computed/formula.d.mts +2 -2
- package/dist/validation/computed/formula.d.ts +2 -2
- package/dist/validation/computed/formula.js +3 -3
- package/dist/validation/computed/formula.mjs +2 -2
- package/dist/validation/computed/rollup.d.mts +2 -2
- package/dist/validation/computed/rollup.d.ts +2 -2
- package/dist/validation/computed/rollup.js +3 -3
- package/dist/validation/computed/rollup.mjs +2 -2
- package/dist/validation/config/index.d.mts +1 -1
- package/dist/validation/config/index.d.ts +1 -1
- package/dist/validation/config/index.js +4 -4
- package/dist/validation/config/index.mjs +1 -1
- package/dist/validation/core/index.d.mts +3 -3
- package/dist/validation/core/index.d.ts +3 -3
- package/dist/validation/core/index.js +3 -3
- package/dist/validation/core/index.mjs +1 -1
- package/dist/validation/object/index.d.mts +3 -3
- package/dist/validation/object/index.d.ts +3 -3
- package/dist/validation/object/index.js +32 -29
- package/dist/validation/object/index.mjs +16 -17
- package/dist/validation/primitives/checkbox.d.mts +2 -2
- package/dist/validation/primitives/checkbox.d.ts +2 -2
- package/dist/validation/primitives/checkbox.js +3 -3
- package/dist/validation/primitives/checkbox.mjs +2 -2
- package/dist/validation/primitives/date.d.mts +2 -2
- package/dist/validation/primitives/date.d.ts +2 -2
- package/dist/validation/primitives/date.js +3 -3
- package/dist/validation/primitives/date.mjs +2 -2
- package/dist/validation/primitives/number.d.mts +2 -2
- package/dist/validation/primitives/number.d.ts +2 -2
- package/dist/validation/primitives/number.js +3 -3
- package/dist/validation/primitives/number.mjs +2 -2
- package/dist/validation/primitives/text.d.mts +3 -8
- package/dist/validation/primitives/text.d.ts +3 -8
- package/dist/validation/primitives/text.js +3 -7
- package/dist/validation/primitives/text.mjs +2 -2
- package/package.json +2 -13
- package/dist/chunk-D7WFIGXW.js +0 -14
- package/dist/chunk-RUYUFXNW.mjs +0 -12
- package/dist/filters-DVBn5tov.d.ts +0 -1630
- package/dist/filters-HDG4kLoo.d.mts +0 -1630
- package/dist/validation/primitives/rating.d.mts +0 -12
- package/dist/validation/primitives/rating.d.ts +0 -12
- package/dist/validation/primitives/rating.js +0 -11
- package/dist/validation/primitives/rating.mjs +0 -2
|
@@ -0,0 +1,889 @@
|
|
|
1
|
+
import { IconName, CountryIso3, CurrencyCode, ColorId, MimeType } from '@stndrds/constants';
|
|
2
|
+
import { Uuid } from './utils.mjs';
|
|
3
|
+
|
|
4
|
+
type ComputedReturnType = "text" | "number" | "boolean" | "date" | "select" | "multiselect";
|
|
5
|
+
type ComputedFieldKind = "formula" | "rollup";
|
|
6
|
+
type ComputedValueType = {
|
|
7
|
+
kind: "scalar";
|
|
8
|
+
type: Exclude<ComputedReturnType, "multiselect">;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "collection";
|
|
11
|
+
itemType: "text" | "number" | "boolean" | "date" | "select";
|
|
12
|
+
};
|
|
13
|
+
type ComputedStateStatus = "pending" | "fresh" | "stale" | "failed";
|
|
14
|
+
interface ComputedOptionsSource {
|
|
15
|
+
objectName: string;
|
|
16
|
+
attributeName: string;
|
|
17
|
+
attributeId?: string;
|
|
18
|
+
}
|
|
19
|
+
interface ComputedAttributeState {
|
|
20
|
+
attributeName: string;
|
|
21
|
+
status: ComputedStateStatus;
|
|
22
|
+
computedAt: Date | null;
|
|
23
|
+
staleSince: Date | null;
|
|
24
|
+
dependenciesHash: string | null;
|
|
25
|
+
jobId: string | null;
|
|
26
|
+
error: string | null;
|
|
27
|
+
updatedAt: Date;
|
|
28
|
+
}
|
|
29
|
+
type ComputedDependency = {
|
|
30
|
+
kind: "local";
|
|
31
|
+
attributeName: string;
|
|
32
|
+
} | {
|
|
33
|
+
kind: "relation";
|
|
34
|
+
relationName: string;
|
|
35
|
+
targetAttributeName?: string;
|
|
36
|
+
cardinality?: "one" | "many";
|
|
37
|
+
} | {
|
|
38
|
+
kind: "computed";
|
|
39
|
+
attributeName: string;
|
|
40
|
+
} | {
|
|
41
|
+
kind: "options";
|
|
42
|
+
source: ComputedOptionsSource;
|
|
43
|
+
};
|
|
44
|
+
interface ComputedPlan {
|
|
45
|
+
kind: ComputedFieldKind;
|
|
46
|
+
attributeName: string;
|
|
47
|
+
expression: string;
|
|
48
|
+
returnType: ComputedReturnType;
|
|
49
|
+
valueType: ComputedValueType;
|
|
50
|
+
dependencies: ComputedDependency[];
|
|
51
|
+
optionsSource?: ComputedOptionsSource;
|
|
52
|
+
targetAttributeType?: AttributeType;
|
|
53
|
+
dependenciesHash: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface DocumentLayout {
|
|
57
|
+
/** Named sub-folders auto-created at the top of the record's drive when first used via attach --variant. */
|
|
58
|
+
variants?: Record<string, DocumentLayoutVariant>;
|
|
59
|
+
/** Dev-declared folder structures the user can instantiate via the UI inside a record's drive. */
|
|
60
|
+
presets?: FolderPreset[];
|
|
61
|
+
}
|
|
62
|
+
interface DocumentLayoutVariant {
|
|
63
|
+
/** Display title for the auto-created sub-folder. */
|
|
64
|
+
title: string;
|
|
65
|
+
description?: string;
|
|
66
|
+
}
|
|
67
|
+
interface FolderPreset {
|
|
68
|
+
id: string;
|
|
69
|
+
label: string;
|
|
70
|
+
icon?: IconName;
|
|
71
|
+
description?: string;
|
|
72
|
+
structure: PresetNode[];
|
|
73
|
+
}
|
|
74
|
+
interface PresetNode {
|
|
75
|
+
title: string;
|
|
76
|
+
description?: string;
|
|
77
|
+
children?: PresetNode[];
|
|
78
|
+
}
|
|
79
|
+
/** Max depth allowed for preset.structure to keep instantiation predictable. */
|
|
80
|
+
declare const MAX_PRESET_DEPTH = 5;
|
|
81
|
+
|
|
82
|
+
type BuiltInTransform = "toString" | "toNumber" | "toDate" | "toBoolean" | "toISOString";
|
|
83
|
+
type SchemaOperation = {
|
|
84
|
+
type: "add_attribute";
|
|
85
|
+
attribute: Attribute;
|
|
86
|
+
} | {
|
|
87
|
+
type: "remove_attribute";
|
|
88
|
+
name: string;
|
|
89
|
+
backup_config: Attribute;
|
|
90
|
+
} | {
|
|
91
|
+
type: "rename_attribute";
|
|
92
|
+
from: string;
|
|
93
|
+
to: string;
|
|
94
|
+
} | {
|
|
95
|
+
type: "change_type";
|
|
96
|
+
name: string;
|
|
97
|
+
from: AttributeType;
|
|
98
|
+
to: AttributeType;
|
|
99
|
+
transform?: BuiltInTransform;
|
|
100
|
+
} | {
|
|
101
|
+
type: "update_config";
|
|
102
|
+
name: string;
|
|
103
|
+
from: Partial<Record<string, unknown>>;
|
|
104
|
+
to: Partial<Record<string, unknown>>;
|
|
105
|
+
} | {
|
|
106
|
+
type: "remove_object";
|
|
107
|
+
backup: Record<string, unknown>;
|
|
108
|
+
} | {
|
|
109
|
+
type: "rename_object";
|
|
110
|
+
from: string;
|
|
111
|
+
to: string;
|
|
112
|
+
};
|
|
113
|
+
interface MigrationDefinition {
|
|
114
|
+
version: number;
|
|
115
|
+
operations: SchemaOperation[];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Timestamps for tracking creation and updates
|
|
120
|
+
*/
|
|
121
|
+
interface Timestamps {
|
|
122
|
+
createdAt: Date;
|
|
123
|
+
updatedAt: Date;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Object definition - Represents a database table/entity
|
|
127
|
+
*/
|
|
128
|
+
interface ObjectDefinition {
|
|
129
|
+
id?: Uuid;
|
|
130
|
+
name: string;
|
|
131
|
+
label: string;
|
|
132
|
+
pluralLabel?: string;
|
|
133
|
+
description?: string;
|
|
134
|
+
icon?: IconName;
|
|
135
|
+
/**
|
|
136
|
+
* Template expression used to compute the object's display label.
|
|
137
|
+
* Supports variable interpolation and pipes for formatting.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* // Simple attribute reference
|
|
142
|
+
* labelExpression: "{{ name }}"
|
|
143
|
+
*
|
|
144
|
+
* // Multiple attributes
|
|
145
|
+
* labelExpression: "{{ firstName }} {{ lastName }}"
|
|
146
|
+
*
|
|
147
|
+
* // With pipes for formatting
|
|
148
|
+
* labelExpression: "{{ code | UPPER }} - {{ name | capitalize }}"
|
|
149
|
+
* ```
|
|
150
|
+
*
|
|
151
|
+
* Available pipes: UPPER, LOWER, capitalize, trim
|
|
152
|
+
*/
|
|
153
|
+
labelExpression: string;
|
|
154
|
+
attributes: Attribute[];
|
|
155
|
+
system?: boolean;
|
|
156
|
+
/**
|
|
157
|
+
* If true, no custom (user-created) attributes are allowed on this object.
|
|
158
|
+
* `standards diff` will report an error if any custom attribute is found.
|
|
159
|
+
* Use .sealed() on the ObjectBuilder to set this.
|
|
160
|
+
*/
|
|
161
|
+
sealed?: boolean;
|
|
162
|
+
/**
|
|
163
|
+
* List of custom attribute names explicitly acknowledged by the developer.
|
|
164
|
+
* On a sealed object, these names will NOT trigger a CI failure.
|
|
165
|
+
* On an extensible object, this is purely documentary.
|
|
166
|
+
* Use .tolerate(["name"]) on the ObjectBuilder to set this.
|
|
167
|
+
*/
|
|
168
|
+
toleratedAttributes?: string[];
|
|
169
|
+
metadata?: Record<string, unknown>;
|
|
170
|
+
/** Current schema version (incremented with each migration) */
|
|
171
|
+
schema_version: number;
|
|
172
|
+
/** Ordered list of migrations applied to this object's schema */
|
|
173
|
+
migrations: MigrationDefinition[];
|
|
174
|
+
/** Smart-folder layout convention for documents attached to records of this object. */
|
|
175
|
+
documentLayout?: DocumentLayout;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Links an attribute to an object
|
|
179
|
+
*/
|
|
180
|
+
interface ObjectAttribute {
|
|
181
|
+
objectId: Uuid;
|
|
182
|
+
attributeId: Uuid;
|
|
183
|
+
order?: number;
|
|
184
|
+
required?: boolean;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Completion status of a record based on data completeness.
|
|
188
|
+
*
|
|
189
|
+
* - `draft`: Record is missing one or more required attribute values.
|
|
190
|
+
* Can be saved but is considered incomplete.
|
|
191
|
+
* - `complete`: All required attribute values are present and valid.
|
|
192
|
+
* Record is ready for use.
|
|
193
|
+
*
|
|
194
|
+
* This is different from workflow status (e.g., "pending", "approved").
|
|
195
|
+
* Completion status is computed dynamically based on the object schema.
|
|
196
|
+
*/
|
|
197
|
+
type CompletionStatus = "draft" | "complete";
|
|
198
|
+
/**
|
|
199
|
+
* Record - Instance of an Object (a row in the database)
|
|
200
|
+
*/
|
|
201
|
+
interface ObjectRecord extends Timestamps {
|
|
202
|
+
id: Uuid;
|
|
203
|
+
objectId: Uuid;
|
|
204
|
+
/**
|
|
205
|
+
* Display label computed from the object's labelExpression.
|
|
206
|
+
* Computed dynamically based on record values.
|
|
207
|
+
*
|
|
208
|
+
* @example "John Doe" (from "{{ firstName }} {{ lastName }}")
|
|
209
|
+
*/
|
|
210
|
+
label: string;
|
|
211
|
+
/**
|
|
212
|
+
* Completion status of the record.
|
|
213
|
+
* - `draft`: Missing required values, record is incomplete
|
|
214
|
+
* - `complete`: All required values present and valid
|
|
215
|
+
*
|
|
216
|
+
* Computed dynamically based on the object's schema.
|
|
217
|
+
*/
|
|
218
|
+
completionStatus: CompletionStatus;
|
|
219
|
+
values: Record<string, unknown>;
|
|
220
|
+
/** Per-computed-attribute state keyed by attribute name. */
|
|
221
|
+
computedStates?: Record<string, ComputedAttributeState>;
|
|
222
|
+
/**
|
|
223
|
+
* Custom metadata for the record.
|
|
224
|
+
* Use this for UI/UX state, feature flags, or any application-specific data.
|
|
225
|
+
* Unlike system fields (id, createdAt, updatedAt), metadata can be updated.
|
|
226
|
+
*/
|
|
227
|
+
metadata?: Record<string, unknown>;
|
|
228
|
+
/**
|
|
229
|
+
* Soft delete timestamp.
|
|
230
|
+
* If set, the record is considered deleted but can be restored.
|
|
231
|
+
* Queries exclude soft-deleted records by default.
|
|
232
|
+
*/
|
|
233
|
+
deletedAt?: Date | null;
|
|
234
|
+
/**
|
|
235
|
+
* Actor ID who soft-deleted this record.
|
|
236
|
+
* Set alongside deletedAt when a record is soft-deleted.
|
|
237
|
+
*/
|
|
238
|
+
deletedBy?: string | null;
|
|
239
|
+
/**
|
|
240
|
+
* Actor ID who created this record.
|
|
241
|
+
* Automatically set by RecordService when actorId is configured.
|
|
242
|
+
* Optional for backward compatibility with existing records.
|
|
243
|
+
*/
|
|
244
|
+
createdBy?: string;
|
|
245
|
+
/**
|
|
246
|
+
* Actor ID who last updated this record.
|
|
247
|
+
* Automatically set by RecordService when actorId is configured.
|
|
248
|
+
* Optional for backward compatibility with existing records.
|
|
249
|
+
*/
|
|
250
|
+
lastUpdatedBy?: string;
|
|
251
|
+
/** Schema version at the time this record was last migrated */
|
|
252
|
+
schemaVersion: number;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* System-managed field names on ObjectRecord.
|
|
256
|
+
* These are stored as SQL columns (not in JSONB `values`).
|
|
257
|
+
*
|
|
258
|
+
* Use this in adapters to determine if a filter/sort attribute is a table column
|
|
259
|
+
* vs. a JSONB value field.
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* ```typescript
|
|
263
|
+
* if (SYSTEM_FIELD_NAMES.includes(filter.attribute)) {
|
|
264
|
+
* // Filter on SQL column (e.g., WHERE created_at > ...)
|
|
265
|
+
* } else {
|
|
266
|
+
* // Filter on JSONB field (e.g., WHERE values->>'name' = ...)
|
|
267
|
+
* }
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
declare const SYSTEM_FIELD_NAMES: readonly ["id", "createdAt", "updatedAt", "createdBy", "lastUpdatedBy"];
|
|
271
|
+
/**
|
|
272
|
+
* Type for system field names
|
|
273
|
+
*/
|
|
274
|
+
type SystemFieldName = (typeof SYSTEM_FIELD_NAMES)[number];
|
|
275
|
+
/**
|
|
276
|
+
* Reserved attribute names that cannot be used for custom attributes.
|
|
277
|
+
* These names conflict with ObjectRecord properties.
|
|
278
|
+
*
|
|
279
|
+
* Includes:
|
|
280
|
+
* - System fields (id, createdAt, updatedAt, createdBy, lastUpdatedBy)
|
|
281
|
+
* - Other ObjectRecord properties (objectId, label, completionStatus, values, metadata, deletedAt)
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* ```typescript
|
|
285
|
+
* if (RESERVED_ATTRIBUTE_NAMES.includes(attributeName)) {
|
|
286
|
+
* throw new Error(`"${attributeName}" is a reserved name`);
|
|
287
|
+
* }
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
declare const RESERVED_ATTRIBUTE_NAMES: readonly ["id", "createdAt", "updatedAt", "createdBy", "lastUpdatedBy", "objectId", "label", "completionStatus", "values", "metadata", "deletedAt", "deletedBy", "schemaVersion"];
|
|
291
|
+
/**
|
|
292
|
+
* Type for reserved attribute names
|
|
293
|
+
*/
|
|
294
|
+
type ReservedAttributeName = (typeof RESERVED_ATTRIBUTE_NAMES)[number];
|
|
295
|
+
|
|
296
|
+
type DocumentKind = "file" | "folder";
|
|
297
|
+
interface Document extends Timestamps {
|
|
298
|
+
id: Uuid;
|
|
299
|
+
tenantId: Uuid;
|
|
300
|
+
title: string;
|
|
301
|
+
kind: DocumentKind;
|
|
302
|
+
parentId?: Uuid | null;
|
|
303
|
+
description?: string;
|
|
304
|
+
contentHash?: string;
|
|
305
|
+
createdBy?: Uuid;
|
|
306
|
+
updatedBy?: Uuid;
|
|
307
|
+
deletedAt?: Date | null;
|
|
308
|
+
/**
|
|
309
|
+
* Fractional ordering position within `(tenantId, parentId)`. Lower values
|
|
310
|
+
* sort first. New rows default to `max(siblings) + 1024` so reorderings can
|
|
311
|
+
* insert between neighbours without renumbering. The server may rebalance
|
|
312
|
+
* positions in the background to keep the spacing bounded.
|
|
313
|
+
*/
|
|
314
|
+
position: number;
|
|
315
|
+
}
|
|
316
|
+
interface DocumentWithSubCount extends Document {
|
|
317
|
+
subCount: number | null;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Configuration for a single named slot on a DocumentAttribute.
|
|
321
|
+
* Slots are placeholders for files within a Document; declared at design-time
|
|
322
|
+
* on the attribute and validated at attach time.
|
|
323
|
+
*/
|
|
324
|
+
interface DocumentSlotConfig {
|
|
325
|
+
/** Stable identifier referenced in DB. */
|
|
326
|
+
name: string;
|
|
327
|
+
/** Display label in UI. Falls back to `name` if absent. */
|
|
328
|
+
label?: string;
|
|
329
|
+
/** Description shown to users and to the AI in tool context. */
|
|
330
|
+
description?: string;
|
|
331
|
+
/** Surfaced to UI/AI but not enforced by the system. */
|
|
332
|
+
required?: boolean;
|
|
333
|
+
/** MIME prefixes or globs (e.g. "image/*", "application/pdf"). */
|
|
334
|
+
acceptedMimeTypes?: string[];
|
|
335
|
+
/** Hard ceiling enforced by the service before upload. */
|
|
336
|
+
maxSizeBytes?: number;
|
|
337
|
+
}
|
|
338
|
+
/** Default applied by the document() builder when no slots are configured. */
|
|
339
|
+
declare const DEFAULT_DOCUMENT_SLOT: DocumentSlotConfig;
|
|
340
|
+
interface DocumentSlot extends Timestamps {
|
|
341
|
+
id: Uuid;
|
|
342
|
+
tenantId: Uuid;
|
|
343
|
+
documentId: Uuid;
|
|
344
|
+
slotName: string;
|
|
345
|
+
isAdditional: boolean;
|
|
346
|
+
fileId: Uuid;
|
|
347
|
+
status: SlotStatus;
|
|
348
|
+
ocrText?: string;
|
|
349
|
+
ocrConfidence?: number;
|
|
350
|
+
processedAt?: Date;
|
|
351
|
+
}
|
|
352
|
+
type SlotStatus = "uploaded" | "processing" | "completed" | "failed";
|
|
353
|
+
interface CreateDocument {
|
|
354
|
+
title: string;
|
|
355
|
+
kind?: DocumentKind;
|
|
356
|
+
parentId?: Uuid | null;
|
|
357
|
+
description?: string;
|
|
358
|
+
position?: number;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Reference to a record attribute that should atomically attach a freshly
|
|
362
|
+
* created Document. Passed alongside `CreateDocument` to make creation
|
|
363
|
+
* link-aware end-to-end (client → REST → service → repo).
|
|
364
|
+
*
|
|
365
|
+
* The server requires this — orphan Documents are not creatable through the
|
|
366
|
+
* REST API.
|
|
367
|
+
*/
|
|
368
|
+
interface CreateDocumentLink {
|
|
369
|
+
objectName: string;
|
|
370
|
+
sourceRecordId: string;
|
|
371
|
+
attributeName: string;
|
|
372
|
+
/**
|
|
373
|
+
* Optional qualifyWith properties on the record→Document edge. Currently
|
|
374
|
+
* accepted by the controller DTO but silently ignored by the service (see
|
|
375
|
+
* `DocumentService.createDocument` — there's a TODO to support this).
|
|
376
|
+
*/
|
|
377
|
+
properties?: Record<string, unknown>;
|
|
378
|
+
}
|
|
379
|
+
interface UpdateDocument {
|
|
380
|
+
title?: string;
|
|
381
|
+
parentId?: Uuid | null;
|
|
382
|
+
position?: number;
|
|
383
|
+
}
|
|
384
|
+
interface CreateDocumentSlot {
|
|
385
|
+
documentId: Uuid;
|
|
386
|
+
slotName: string;
|
|
387
|
+
fileId: Uuid;
|
|
388
|
+
isAdditional?: boolean;
|
|
389
|
+
}
|
|
390
|
+
interface UpdateDocumentSlot {
|
|
391
|
+
status?: SlotStatus;
|
|
392
|
+
ocrText?: string;
|
|
393
|
+
ocrConfidence?: number;
|
|
394
|
+
}
|
|
395
|
+
interface DocumentListOptions {
|
|
396
|
+
limit?: number;
|
|
397
|
+
offset?: number;
|
|
398
|
+
}
|
|
399
|
+
interface DocumentWithSlots extends Document {
|
|
400
|
+
slots: DocumentSlot[];
|
|
401
|
+
}
|
|
402
|
+
interface RecordDocuments {
|
|
403
|
+
/** Documents grouped by attribute name (includes system 'attachments' attribute) */
|
|
404
|
+
byAttribute: Record<string, DocumentWithSlots[]>;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Allowed attribute types in .qualifyWith()
|
|
409
|
+
*
|
|
410
|
+
* IMPORTANT: Complex types (formula, rollup, relation, file, user, document, richtext)
|
|
411
|
+
* are NOT supported to avoid duplicating backend behavior.
|
|
412
|
+
*/
|
|
413
|
+
type PropertyType = "text" | "number" | "checkbox" | "date" | "phone" | "currency" | "status" | "select" | "multiselect" | "location";
|
|
414
|
+
/**
|
|
415
|
+
* Union of attribute types allowed as qualified relation properties.
|
|
416
|
+
*
|
|
417
|
+
* These are the same Attribute types used for object attributes,
|
|
418
|
+
* restricted to simple types that don't require complex backend duplication.
|
|
419
|
+
*/
|
|
420
|
+
type PropertyAttribute = TextAttribute | NumberAttribute | CheckboxAttribute | DateAttribute | PhoneAttribute | CurrencyAttribute | StatusAttribute | SelectAttribute | MultiselectAttribute | LocationAttribute;
|
|
421
|
+
/**
|
|
422
|
+
* Schema defining properties for a qualified relation.
|
|
423
|
+
*
|
|
424
|
+
* Uses the same Attribute types as object attributes, enabling DRY builders:
|
|
425
|
+
*
|
|
426
|
+
* @example
|
|
427
|
+
* ```typescript
|
|
428
|
+
* relation({ name: "companies", label: "Companies" })
|
|
429
|
+
* .to("companies").many()
|
|
430
|
+
* .qualifyWith(
|
|
431
|
+
* select({ name: "role", label: "Role" }).options([...]).required(),
|
|
432
|
+
* number({ name: "shares", label: "Shares" }).min(0),
|
|
433
|
+
* )
|
|
434
|
+
* ```
|
|
435
|
+
*/
|
|
436
|
+
interface PropertySchema {
|
|
437
|
+
definitions: PropertyAttribute[];
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Property attribute types that have an `options` array.
|
|
441
|
+
*/
|
|
442
|
+
type OptionPropertyAttribute = SelectAttribute | StatusAttribute | MultiselectAttribute;
|
|
443
|
+
/**
|
|
444
|
+
* Type guard to check if a property attribute has options.
|
|
445
|
+
*
|
|
446
|
+
* @param attr - The property attribute to check
|
|
447
|
+
* @returns true if the attribute is a select, status, or multiselect type with options
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* ```typescript
|
|
451
|
+
* for (const def of definitions) {
|
|
452
|
+
* if (hasOptions(def)) {
|
|
453
|
+
* // TypeScript knows def.options exists and is Option[]
|
|
454
|
+
* for (const option of def.options) {
|
|
455
|
+
* console.log(option.value);
|
|
456
|
+
* }
|
|
457
|
+
* }
|
|
458
|
+
* }
|
|
459
|
+
* ```
|
|
460
|
+
*/
|
|
461
|
+
declare function hasOptions(attr: PropertyAttribute): attr is OptionPropertyAttribute;
|
|
462
|
+
|
|
463
|
+
type AttributeType = "text" | "richtext" | "number" | "checkbox" | "date" | "phone" | "currency" | "status" | "location" | "select" | "multiselect" | "file" | "user" | "relation" | "formula" | "rollup" | "document";
|
|
464
|
+
/**
|
|
465
|
+
* Status group categorization
|
|
466
|
+
*/
|
|
467
|
+
type StatusGroup = "idle" | "in_progress" | "finished";
|
|
468
|
+
/**
|
|
469
|
+
* Unified option type for select-like fields
|
|
470
|
+
*/
|
|
471
|
+
interface Option {
|
|
472
|
+
value: string;
|
|
473
|
+
label: string;
|
|
474
|
+
color?: ColorId;
|
|
475
|
+
description?: string;
|
|
476
|
+
group?: StatusGroup;
|
|
477
|
+
/** Value of the inverse option for bilateral relations (e.g. "parent" → "child") */
|
|
478
|
+
inverse?: string;
|
|
479
|
+
archived?: boolean;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Attribute grouping for UI organization
|
|
483
|
+
*/
|
|
484
|
+
interface AttributeGroup {
|
|
485
|
+
id: string;
|
|
486
|
+
label: string;
|
|
487
|
+
description?: string;
|
|
488
|
+
attributeIds: string[];
|
|
489
|
+
collapsible?: boolean;
|
|
490
|
+
collapsed?: boolean;
|
|
491
|
+
order?: number;
|
|
492
|
+
}
|
|
493
|
+
interface BaseAttribute<DefaultValueType = unknown> {
|
|
494
|
+
id?: Uuid;
|
|
495
|
+
objectId?: Uuid;
|
|
496
|
+
name: string;
|
|
497
|
+
label: string;
|
|
498
|
+
type: AttributeType;
|
|
499
|
+
required?: boolean;
|
|
500
|
+
placeholder?: string;
|
|
501
|
+
description?: string;
|
|
502
|
+
defaultValue?: DefaultValueType;
|
|
503
|
+
icon?: IconName;
|
|
504
|
+
order?: number;
|
|
505
|
+
hidden?: boolean;
|
|
506
|
+
archived?: boolean;
|
|
507
|
+
deprecated?: boolean;
|
|
508
|
+
system?: boolean;
|
|
509
|
+
unique?: boolean;
|
|
510
|
+
metadata?: Record<string, unknown>;
|
|
511
|
+
}
|
|
512
|
+
interface TextAttribute extends BaseAttribute<string> {
|
|
513
|
+
type: "text";
|
|
514
|
+
multiline?: boolean;
|
|
515
|
+
minLength?: number;
|
|
516
|
+
maxLength?: number;
|
|
517
|
+
pattern?: string;
|
|
518
|
+
format?: "email" | "url" | "slug";
|
|
519
|
+
}
|
|
520
|
+
type NumberUnit = "integer" | "decimal" | "percentage";
|
|
521
|
+
interface NumberAttribute extends BaseAttribute<number> {
|
|
522
|
+
type: "number";
|
|
523
|
+
min?: number;
|
|
524
|
+
max?: number;
|
|
525
|
+
unit?: NumberUnit;
|
|
526
|
+
decimals?: number;
|
|
527
|
+
renderAs?: "number" | "rating";
|
|
528
|
+
}
|
|
529
|
+
interface CheckboxAttribute extends BaseAttribute<boolean> {
|
|
530
|
+
type: "checkbox";
|
|
531
|
+
}
|
|
532
|
+
type DateFormat = "short" | "long" | "full" | "relative";
|
|
533
|
+
type DateValue = string | "today";
|
|
534
|
+
interface DateAttribute extends BaseAttribute<string> {
|
|
535
|
+
type: "date";
|
|
536
|
+
dateFormat?: DateFormat;
|
|
537
|
+
minDate?: DateValue;
|
|
538
|
+
maxDate?: DateValue;
|
|
539
|
+
}
|
|
540
|
+
interface Phone {
|
|
541
|
+
countryCode: CountryIso3;
|
|
542
|
+
phoneNumber: string;
|
|
543
|
+
}
|
|
544
|
+
interface PhoneAttribute extends BaseAttribute<Phone> {
|
|
545
|
+
type: "phone";
|
|
546
|
+
defaultCountryCode?: CountryIso3;
|
|
547
|
+
}
|
|
548
|
+
interface Currency {
|
|
549
|
+
code: CurrencyCode;
|
|
550
|
+
value: number;
|
|
551
|
+
}
|
|
552
|
+
interface CurrencyAttribute extends BaseAttribute<Currency> {
|
|
553
|
+
type: "currency";
|
|
554
|
+
defaultCurrency?: CurrencyCode;
|
|
555
|
+
allowedCurrencies?: CurrencyCode[];
|
|
556
|
+
/** Allow negative currency values (e.g. refunds, credits). Defaults to false. */
|
|
557
|
+
allowNegative?: boolean;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* StatusAttribute - For workflow states with semantic grouping (idle/in_progress/finished)
|
|
561
|
+
* Use this for: Task status, Order status, Project phases, Process states
|
|
562
|
+
* Use SelectAttribute for: Categories, Types, simple choices without workflow
|
|
563
|
+
*/
|
|
564
|
+
interface StatusAttribute extends BaseAttribute<string> {
|
|
565
|
+
type: "status";
|
|
566
|
+
options: Option[];
|
|
567
|
+
}
|
|
568
|
+
interface Location {
|
|
569
|
+
address?: string;
|
|
570
|
+
address2?: string;
|
|
571
|
+
city?: string;
|
|
572
|
+
state?: string;
|
|
573
|
+
postalCode?: string;
|
|
574
|
+
country?: CountryIso3;
|
|
575
|
+
latitude?: number;
|
|
576
|
+
longitude?: number;
|
|
577
|
+
}
|
|
578
|
+
type LocationGranularity = "full" | "address" | "city" | "state" | "country" | "coordinates";
|
|
579
|
+
interface LocationAttribute extends BaseAttribute<Location> {
|
|
580
|
+
type: "location";
|
|
581
|
+
granularity?: LocationGranularity;
|
|
582
|
+
defaultCountry?: CountryIso3;
|
|
583
|
+
allowedCountries?: CountryIso3[];
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* SelectAttribute - For simple single-choice selection
|
|
587
|
+
* Use this for: Categories, Document types, Departments, Priorities
|
|
588
|
+
* Options can be grouped (e.g., countries by continent) but no workflow logic
|
|
589
|
+
*/
|
|
590
|
+
interface SelectAttribute extends BaseAttribute<string> {
|
|
591
|
+
type: "select";
|
|
592
|
+
options: Option[];
|
|
593
|
+
}
|
|
594
|
+
interface MultiselectAttribute extends BaseAttribute<string[]> {
|
|
595
|
+
type: "multiselect";
|
|
596
|
+
options: Option[];
|
|
597
|
+
}
|
|
598
|
+
interface FileAttribute extends BaseAttribute<string> {
|
|
599
|
+
type: "file";
|
|
600
|
+
maxFiles?: number;
|
|
601
|
+
maxSize?: number;
|
|
602
|
+
allowedTypes?: MimeType[] | readonly MimeType[];
|
|
603
|
+
multiple?: boolean;
|
|
604
|
+
}
|
|
605
|
+
type UserReferenceType = "user" | "agent";
|
|
606
|
+
interface UserAttribute extends BaseAttribute<string | string[]> {
|
|
607
|
+
type: "user";
|
|
608
|
+
types?: UserReferenceType[];
|
|
609
|
+
multiple?: boolean;
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Wildcard marker for universal relations (can link to any object)
|
|
613
|
+
* Use with `.toAny()` builder method
|
|
614
|
+
*/
|
|
615
|
+
declare const RELATION_TARGET_ANY: "*";
|
|
616
|
+
/**
|
|
617
|
+
* Configuration for bilateral synchronization (bidirectional relations)
|
|
618
|
+
*/
|
|
619
|
+
interface BilateralConfig {
|
|
620
|
+
/** Target object containing the inverse attribute */
|
|
621
|
+
object: string;
|
|
622
|
+
/** Name of the inverse attribute */
|
|
623
|
+
attribute: string;
|
|
624
|
+
/** Optional cardinality override (inferred by default) */
|
|
625
|
+
cardinality?: "one" | "many";
|
|
626
|
+
/** When true, this side owns the storage direction for qualified properties.
|
|
627
|
+
* Set to false on the inverse side (enriched at read time). */
|
|
628
|
+
storageOwner?: boolean;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Target object for a relation - defines which objects can be linked
|
|
632
|
+
*/
|
|
633
|
+
interface RelationTarget {
|
|
634
|
+
/** Object name (e.g., "companies", "contacts") or "*" for any object */
|
|
635
|
+
object: string;
|
|
636
|
+
/**
|
|
637
|
+
* Display template for the label using mustache-like syntax
|
|
638
|
+
* @example "{name}" or "{firstName} {lastName} — {email}"
|
|
639
|
+
*/
|
|
640
|
+
displayTemplate?: string;
|
|
641
|
+
/**
|
|
642
|
+
* Optional filter to restrict available records
|
|
643
|
+
* @example { status: "active" }
|
|
644
|
+
*/
|
|
645
|
+
filter?: Record<string, unknown>;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Base properties shared by both single and multi relation attributes
|
|
649
|
+
*
|
|
650
|
+
* Note: Deletion behavior is always "restrict" - if a record is referenced
|
|
651
|
+
* by other records, it cannot be deleted until those references are removed.
|
|
652
|
+
* This is enforced by RecordService.deleteRecord() which throws
|
|
653
|
+
* RecordReferencedError when attempting to delete a referenced record.
|
|
654
|
+
*/
|
|
655
|
+
interface RelationAttributeBase extends Omit<BaseAttribute<unknown>, "defaultValue"> {
|
|
656
|
+
type: "relation";
|
|
657
|
+
/** Target objects that can be linked */
|
|
658
|
+
targets: RelationTarget[];
|
|
659
|
+
/** Optional properties schema for qualified relations */
|
|
660
|
+
properties?: PropertySchema;
|
|
661
|
+
/** Configuration for bilateral synchronization (opt-in) */
|
|
662
|
+
bilateral?: BilateralConfig;
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Single relation attribute (one-to-one or many-to-one)
|
|
666
|
+
* Stores a single record ID or null
|
|
667
|
+
*/
|
|
668
|
+
interface SingleRelationAttribute extends RelationAttributeBase {
|
|
669
|
+
cardinality: "one";
|
|
670
|
+
defaultValue?: string | null;
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Multi relation attribute (one-to-many or many-to-many)
|
|
674
|
+
* Stores an array of record IDs
|
|
675
|
+
*/
|
|
676
|
+
interface MultiRelationAttribute extends RelationAttributeBase {
|
|
677
|
+
cardinality: "many";
|
|
678
|
+
defaultValue?: string[];
|
|
679
|
+
/** Maximum number of relations allowed */
|
|
680
|
+
maxItems?: number;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* RelationAttribute links to other objects/records
|
|
684
|
+
* Discriminated union by cardinality for type-safe value handling
|
|
685
|
+
*
|
|
686
|
+
* @example Single relation (many-to-one)
|
|
687
|
+
* ```typescript
|
|
688
|
+
* relation({ name: "company", label: "Company" })
|
|
689
|
+
* .to("companies")
|
|
690
|
+
* .required()
|
|
691
|
+
* // → Value: "rec-uuid-123" | null
|
|
692
|
+
* ```
|
|
693
|
+
*
|
|
694
|
+
* @example Multi relation (many-to-many)
|
|
695
|
+
* ```typescript
|
|
696
|
+
* relation({ name: "contacts", label: "Contacts" })
|
|
697
|
+
* .to("contacts", { displayTemplate: "{firstName} {lastName}" })
|
|
698
|
+
* .many()
|
|
699
|
+
* .maxItems(5)
|
|
700
|
+
* // → Value: ["rec-1", "rec-2", ...]
|
|
701
|
+
* ```
|
|
702
|
+
*
|
|
703
|
+
* @example Polymorphic relation (multiple target objects)
|
|
704
|
+
* ```typescript
|
|
705
|
+
* relation({ name: "linked", label: "Linked Items" })
|
|
706
|
+
* .to("companies")
|
|
707
|
+
* .to("contacts")
|
|
708
|
+
* .to("deals")
|
|
709
|
+
* .many()
|
|
710
|
+
* // → Can link to records from any of these objects
|
|
711
|
+
* ```
|
|
712
|
+
*/
|
|
713
|
+
type RelationAttribute = SingleRelationAttribute | MultiRelationAttribute;
|
|
714
|
+
/**
|
|
715
|
+
* Check if a relation attribute is universal (can link to any object)
|
|
716
|
+
* Universal relations have `targets: [{ object: "*" }]`
|
|
717
|
+
*/
|
|
718
|
+
declare function isUniversalRelation(attr: RelationAttribute): boolean;
|
|
719
|
+
/**
|
|
720
|
+
* Check if a relation attribute has bilateral synchronization enabled
|
|
721
|
+
*/
|
|
722
|
+
declare function isBilateralRelation(attr: RelationAttribute): attr is RelationAttribute & {
|
|
723
|
+
bilateral: BilateralConfig;
|
|
724
|
+
};
|
|
725
|
+
/**
|
|
726
|
+
* Infer the cardinality of the inverse relation
|
|
727
|
+
* - one → many (contact.company ↔ company.contacts)
|
|
728
|
+
* - many → many (contact.tags ↔ tag.contacts)
|
|
729
|
+
*/
|
|
730
|
+
declare function inferInverseCardinality(cardinality: "one" | "many"): "one" | "many";
|
|
731
|
+
/**
|
|
732
|
+
* RichtextAttribute - Rich text content using semantic markdown
|
|
733
|
+
*
|
|
734
|
+
* Stores content as semantic markdown string (with directives like :::callout).
|
|
735
|
+
* Parsed at runtime to Tiptap JSON for editing.
|
|
736
|
+
* Use this for: Notes, articles, descriptions, long-form content.
|
|
737
|
+
*
|
|
738
|
+
* @example
|
|
739
|
+
* ```typescript
|
|
740
|
+
* richtext({ name: "content", label: "Content" }).required()
|
|
741
|
+
* ```
|
|
742
|
+
*/
|
|
743
|
+
interface RichtextAttribute extends BaseAttribute<string> {
|
|
744
|
+
type: "richtext";
|
|
745
|
+
}
|
|
746
|
+
type RichTextAttribute = RichtextAttribute;
|
|
747
|
+
/**
|
|
748
|
+
* Return type for formula expressions
|
|
749
|
+
*/
|
|
750
|
+
type FormulaReturnType = ComputedReturnType;
|
|
751
|
+
/**
|
|
752
|
+
* FormulaAttribute - Computed value based on other attributes
|
|
753
|
+
*
|
|
754
|
+
* Formulas are calculated at read-time and are always read-only.
|
|
755
|
+
* Users cannot directly edit formula values.
|
|
756
|
+
*
|
|
757
|
+
* @example Simple calculation
|
|
758
|
+
* ```typescript
|
|
759
|
+
* formula({ name: "total", label: "Total" })
|
|
760
|
+
* .expression("price * quantity")
|
|
761
|
+
* .returns("number")
|
|
762
|
+
* .decimals(2)
|
|
763
|
+
* ```
|
|
764
|
+
*
|
|
765
|
+
* @example With functions
|
|
766
|
+
* ```typescript
|
|
767
|
+
* formula({ name: "fullName", label: "Full Name" })
|
|
768
|
+
* .expression("CONCAT(firstName, ' ', lastName)")
|
|
769
|
+
* .returns("text")
|
|
770
|
+
* ```
|
|
771
|
+
*/
|
|
772
|
+
interface FormulaAttribute extends Omit<BaseAttribute<never>, "defaultValue" | "required"> {
|
|
773
|
+
type: "formula";
|
|
774
|
+
/** Expression to evaluate (e.g., "price * quantity") */
|
|
775
|
+
expression: string;
|
|
776
|
+
/** Expected return type for formatting */
|
|
777
|
+
returnType: FormulaReturnType;
|
|
778
|
+
/** Decimal places for number results */
|
|
779
|
+
decimals?: number;
|
|
780
|
+
/** Source attribute for select-like computed options */
|
|
781
|
+
optionsSource?: ComputedOptionsSource;
|
|
782
|
+
/** Whether to allow relation references in the expression (e.g., "company.name") */
|
|
783
|
+
allowRelations?: boolean;
|
|
784
|
+
/** Formula is always not required (read-only) */
|
|
785
|
+
required: false;
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Aggregation functions for rollup attributes
|
|
789
|
+
*
|
|
790
|
+
* Categories:
|
|
791
|
+
* - Numeric (sum, avg): Only for number and currency types
|
|
792
|
+
* - Date (earliest, latest): Only for date type
|
|
793
|
+
* - Count (count, countValues, countUniqueValues, countEmpty): Universal
|
|
794
|
+
* - Percent (percentEmpty, percentNotEmpty): Universal
|
|
795
|
+
* - Lookup (original): Returns all values as array, rendered as target type
|
|
796
|
+
*/
|
|
797
|
+
type RollupFunction = "sum" | "avg" | "earliest" | "latest" | "count" | "countValues" | "countUniqueValues" | "countEmpty" | "percentEmpty" | "percentNotEmpty" | "original";
|
|
798
|
+
/**
|
|
799
|
+
* RollupAttribute - Aggregates values from related records
|
|
800
|
+
*
|
|
801
|
+
* Rollups are calculated and stored (denormalized) for performance.
|
|
802
|
+
* They are automatically recalculated when related records change.
|
|
803
|
+
* Users cannot directly edit rollup values.
|
|
804
|
+
*
|
|
805
|
+
* @example Sum of related amounts
|
|
806
|
+
* ```typescript
|
|
807
|
+
* rollup({ name: "totalOrders", label: "Total Orders" })
|
|
808
|
+
* .from("orders") // relation attribute name
|
|
809
|
+
* .aggregate("amount") // target attribute to sum
|
|
810
|
+
* .using("sum")
|
|
811
|
+
* .decimals(2)
|
|
812
|
+
* ```
|
|
813
|
+
*
|
|
814
|
+
* @example Count of related records
|
|
815
|
+
* ```typescript
|
|
816
|
+
* rollup({ name: "orderCount", label: "Number of Orders" })
|
|
817
|
+
* .from("orders")
|
|
818
|
+
* .using("count")
|
|
819
|
+
* ```
|
|
820
|
+
*/
|
|
821
|
+
interface RollupAttribute extends Omit<BaseAttribute<never>, "defaultValue" | "required"> {
|
|
822
|
+
type: "rollup";
|
|
823
|
+
/** Name of the relation attribute on this object */
|
|
824
|
+
relationAttribute: string;
|
|
825
|
+
/**
|
|
826
|
+
* Dot notation path for multi-level traversal (Phase 4+)
|
|
827
|
+
* @example "orders.items" - traverse through orders to items
|
|
828
|
+
*/
|
|
829
|
+
relationPath?: string;
|
|
830
|
+
/** Attribute name on the target object to aggregate */
|
|
831
|
+
targetAttribute: string;
|
|
832
|
+
/** Aggregation function to apply */
|
|
833
|
+
function: RollupFunction;
|
|
834
|
+
/** Decimal places for numeric results */
|
|
835
|
+
decimals?: number;
|
|
836
|
+
/** Rollup is always not required (read-only) */
|
|
837
|
+
required: false;
|
|
838
|
+
/**
|
|
839
|
+
* Cached type of the target attribute for display purposes
|
|
840
|
+
* Used when function="original" to render values as the target type
|
|
841
|
+
*/
|
|
842
|
+
targetAttributeType?: AttributeType;
|
|
843
|
+
/**
|
|
844
|
+
* Cached options from target attribute (for select/status/multiselect display)
|
|
845
|
+
* Required when function="original" and target is a select-like type
|
|
846
|
+
*/
|
|
847
|
+
targetAttributeOptions?: Option[];
|
|
848
|
+
/** Source attribute for select-like computed options */
|
|
849
|
+
optionsSource?: ComputedOptionsSource;
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* DocumentAttribute - References structured documents.
|
|
853
|
+
*
|
|
854
|
+
* Unlike FileAttribute which stores raw file references, DocumentAttribute
|
|
855
|
+
* provides structured document handling with typed edge properties.
|
|
856
|
+
*
|
|
857
|
+
* Every document attribute is multi by construction — the underlying
|
|
858
|
+
* storage (documents + document_slots + files) supports 1 Doc → N slot
|
|
859
|
+
* files uniformly. Single-value file fields use `file()` instead.
|
|
860
|
+
*
|
|
861
|
+
* @example
|
|
862
|
+
* ```typescript
|
|
863
|
+
* document({ name: "contracts", label: "Contrats" })
|
|
864
|
+
* .slots([{ name: "signed" }])
|
|
865
|
+
* ```
|
|
866
|
+
*/
|
|
867
|
+
interface DocumentAttribute extends BaseAttribute<string[]> {
|
|
868
|
+
type: "document";
|
|
869
|
+
/**
|
|
870
|
+
* Schema for typed properties on the record→document edge.
|
|
871
|
+
* Symmetric with `RelationAttribute.properties`; populated by `.qualifyWith(...)`.
|
|
872
|
+
* Stored on `record_reference_edges.properties`, not on the document itself.
|
|
873
|
+
*/
|
|
874
|
+
properties?: PropertySchema;
|
|
875
|
+
/**
|
|
876
|
+
* File slots declared on this attribute. Builder injects
|
|
877
|
+
* [DEFAULT_DOCUMENT_SLOT] when omitted. Always non-empty in practice.
|
|
878
|
+
*/
|
|
879
|
+
slots?: DocumentSlotConfig[];
|
|
880
|
+
}
|
|
881
|
+
type Attribute = TextAttribute | RichtextAttribute | NumberAttribute | CheckboxAttribute | DateAttribute | PhoneAttribute | CurrencyAttribute | StatusAttribute | LocationAttribute | SelectAttribute | MultiselectAttribute | FileAttribute | UserAttribute | RelationAttribute | FormulaAttribute | RollupAttribute | DocumentAttribute;
|
|
882
|
+
/**
|
|
883
|
+
* Check if an attribute supports sorting based on its type.
|
|
884
|
+
*/
|
|
885
|
+
declare function isAttributeSortable(attr: {
|
|
886
|
+
type: AttributeType;
|
|
887
|
+
}): boolean;
|
|
888
|
+
|
|
889
|
+
export { DEFAULT_DOCUMENT_SLOT as $, type Attribute as A, type BilateralConfig as B, type CurrencyAttribute as C, type DateAttribute as D, type RelationTarget as E, type FileAttribute as F, type RollupFunction as G, type UserReferenceType as H, type MigrationDefinition as I, type AttributeGroup as J, type BaseAttribute as K, type LocationAttribute as L, type MultiRelationAttribute as M, type NumberAttribute as N, type ObjectDefinition as O, type PhoneAttribute as P, type BuiltInTransform as Q, type RelationAttribute as R, type SingleRelationAttribute as S, type TextAttribute as T, type UserAttribute as U, type ComputedAttributeState as V, type ComputedFieldKind as W, type ComputedStateStatus as X, type CreateDocument as Y, type CreateDocumentLink as Z, type CreateDocumentSlot as _, type RichtextAttribute as a, type DateFormat as a0, type DateValue as a1, type Document as a2, type DocumentKind as a3, type DocumentLayoutVariant as a4, type DocumentListOptions as a5, type DocumentSlot as a6, type DocumentWithSlots as a7, type DocumentWithSubCount as a8, type FolderPreset as a9, MAX_PRESET_DEPTH as aa, type NumberUnit as ab, type ObjectAttribute as ac, type ObjectRecord as ad, type OptionPropertyAttribute as ae, type PresetNode as af, type PropertyAttribute as ag, type PropertyType as ah, RELATION_TARGET_ANY as ai, RESERVED_ATTRIBUTE_NAMES as aj, type RecordDocuments as ak, type ReservedAttributeName as al, type RichTextAttribute as am, SYSTEM_FIELD_NAMES as an, type SlotStatus as ao, type StatusGroup as ap, type SystemFieldName as aq, type UpdateDocument as ar, type UpdateDocumentSlot as as, hasOptions as at, inferInverseCardinality as au, isAttributeSortable as av, isBilateralRelation as aw, isUniversalRelation as ax, type MultiselectAttribute as b, type SelectAttribute as c, type StatusAttribute as d, type FormulaAttribute as e, type RollupAttribute as f, type CheckboxAttribute as g, type CompletionStatus as h, type ComputedValueType as i, type ComputedReturnType as j, type AttributeType as k, type ComputedOptionsSource as l, type ComputedDependency as m, type ComputedPlan as n, type Timestamps as o, type DocumentLayout as p, type SchemaOperation as q, type LocationGranularity as r, type Location as s, type DocumentAttribute as t, type Phone as u, type Currency as v, type DocumentSlotConfig as w, type PropertySchema as x, type Option as y, type FormulaReturnType as z };
|