@stoker-platform/types 0.5.68 → 0.5.70

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.
@@ -5,7 +5,9 @@ import { CalendarOptions } from "@fullcalendar/core"
5
5
  import { SearchOptions } from "minisearch"
6
6
  import { UserRecord } from "firebase-admin/auth"
7
7
 
8
+ /** An access role in the app, i.e. "Manager". Each role has its own permissions */
8
9
  export type StokerRole = string
10
+ /** The name of a collection in the app, i.e. "Clients" */
9
11
  export type StokerCollection = string
10
12
 
11
13
  export type FirestoreTimestamp = Timestamp | AdminTimestamp
@@ -13,64 +15,96 @@ export type InputTimestamp = Timestamp | FieldValue
13
15
 
14
16
  /* eslint-disable @typescript-eslint/no-explicit-any */
15
17
 
18
+ /** A relation value stored on a record, containing the related record's collection path and any denormalized include fields */
16
19
  export interface StokerRelation {
17
20
  Collection_Path: string[]
18
21
  [key: string]: any
19
22
  }
20
23
 
24
+ /** A map of related record IDs to relation values, as stored on relation fields */
21
25
  export interface StokerRelationObject {
22
26
  [id: string]: StokerRelation
23
27
  }
28
+ /** An array of related record IDs, as stored on relation fields */
24
29
  export type StokerRelationArray = string[]
25
30
 
31
+ /** System fields automatically maintained on every record */
26
32
  export interface SystemFields {
33
+ /** The Firestore path segments for the record's collection */
27
34
  Collection_Path: string[]
35
+ /** When the record was last written. Set on the client, so it may not be reliable. Useful for logging when offline writes occurred */
28
36
  Last_Write_At: Timestamp | FieldValue
37
+ /** When the record was last saved. Safely generated on the server */
29
38
  Last_Save_At: Timestamp | FieldValue
39
+ /** The ID of the user who last wrote the record */
30
40
  Last_Write_By: string
41
+ /** The app that made the last write */
31
42
  Last_Write_App: string
43
+ /** Whether the last write was made while online or offline */
32
44
  Last_Write_Connection_Status: "Online" | "Offline"
45
+ /** The schema version at the time of the last write */
33
46
  Last_Write_Version: number
47
+ /** When the record was created. Set on the client, so it may not be reliable. Useful for logging when offline writes occurred */
34
48
  Created_At: Timestamp | FieldValue
49
+ /** When the record was first saved. Safely generated on the server */
35
50
  Saved_At: Timestamp | FieldValue
51
+ /** The ID of the user who created the record */
36
52
  Created_By: string
37
53
  }
38
54
 
55
+ /** A record in a Stoker collection, including system fields */
39
56
  export interface StokerRecord extends SystemFields {
40
57
  [key: string]: any
41
58
  }
42
59
  /* eslint-enable @typescript-eslint/no-explicit-any */
43
60
 
61
+ /** A user's permissions for a single collection, as stored in their permissions record */
44
62
  export interface CollectionPermissions {
63
+ /** Whether the user has been granted auth (credential assignment) access for this collection */
45
64
  auth?: boolean
65
+ /** The CRUD operations the user can perform on the collection */
46
66
  operations: ("Read" | "Create" | "Update" | "Delete")[]
67
+ /** Whether the Record Owner restriction is active for this user */
47
68
  recordOwner?: {
48
69
  active: boolean
49
70
  }
71
+ /** Whether the Record User restriction is active for this user */
50
72
  recordUser?: {
51
73
  active: boolean
52
74
  }
75
+ /** Whether the Record Property restriction is active for this user */
53
76
  recordProperty?: {
54
77
  active: boolean
55
78
  }
79
+ /** Whether entity restrictions are active for this user */
56
80
  restrictEntities?: boolean
81
+ /** IDs of individual records assigned to the user */
57
82
  individualEntities?: string[]
83
+ /** IDs of parent records whose child records are assigned to the user */
58
84
  parentEntities?: string[]
85
+ /** IDs of parent records mapped to the property values assigned to the user */
59
86
  parentPropertyEntities?: Record<string, string[]>
60
87
  }
61
88
 
89
+ /** A user's permissions record, defining their role and per-collection access */
62
90
  export interface StokerPermissions {
63
- Tenant_ID?: string
91
+ /** The ID of the auth user these permissions apply to */
64
92
  User_ID?: string
93
+ /** The ID of the record in the auth collection that the user is linked to */
65
94
  Doc_ID?: string
95
+ /** The auth collection the user belongs to */
66
96
  Collection?: StokerCollection
97
+ /** The user's access role */
67
98
  Role?: StokerRole
99
+ /** Whether the user's access is enabled */
68
100
  Enabled?: boolean
101
+ /** Per-collection permissions for the user */
69
102
  collections?: {
70
103
  [collection: string]: CollectionPermissions
71
104
  }
72
105
  }
73
106
 
107
+ /** The names of the system fields automatically maintained on every record */
74
108
  export type SystemField =
75
109
  | "id"
76
110
  | "Collection_Path"
@@ -84,149 +118,246 @@ export type SystemField =
84
118
  | "Saved_At"
85
119
  | "Created_By"
86
120
 
121
+ /** The names for a collection. Names must start with a capital letter and contain only letters, digits, and underscores. Provide user-friendly labels in admin.titles */
87
122
  export interface CollectionLabels {
123
+ /** The name for the collection, i.e. "Clients" */
88
124
  collection: string
125
+ /** The name for a record in the collection, i.e. "Client" */
89
126
  record: string
90
127
  }
91
128
 
92
129
  export type OperationType = "Read" | "Create" | "Update" | "Delete"
93
130
  export type OperationTypeLower = "read" | "create" | "update" | "delete"
94
131
 
132
+ /** Defines which operations and restrictions can be assigned for a collection when writing permissions */
95
133
  export interface PermissionWriteCollection {
134
+ /** The collection these permission write restrictions apply to */
96
135
  collection: StokerCollection
136
+ /** The operations that can be granted for the collection */
97
137
  operations: OperationType[]
138
+ /** Attribute restrictions that must be applied when granting access to the collection */
98
139
  attributeRestrictions?: AttributeRestriction["type"][]
140
+ /** Whether entity restrictions must be applied when granting access to the collection */
99
141
  restrictEntities?: boolean
142
+ /** Whether auth access can be granted for the collection */
100
143
  auth?: boolean
101
144
  }
102
145
 
146
+ /** Restricts which permissions a user role can assign to other user roles, allowing a flexible yet secure hierarchy of access assignment */
103
147
  export interface PermissionWriteRestriction {
148
+ /** The user role you are applying restrictions to */
104
149
  userRole: StokerRole
150
+ /** A role that the user above can assign access to */
105
151
  recordRole: StokerRole
152
+ /** Define which operations and restrictions are applied for each collection */
106
153
  collections: PermissionWriteCollection[]
107
154
  }
108
155
 
156
+ /** A role that an attribute restriction applies to */
109
157
  export interface AttributeRestrictionRole {
158
+ /** The role that this restriction applies to */
110
159
  role: StokerRole
160
+ /** If `true`, this restriction can be removed for individual users */
111
161
  assignable?: boolean
162
+ /** For Record Property restrictions, the property values this role can access */
112
163
  values?: string[]
113
164
  }
165
+ /** A role that an entity restriction applies to */
114
166
  export interface EntityRestrictionRole {
167
+ /** The role that this restriction applies to */
115
168
  role: StokerRole
116
169
  }
117
170
 
118
171
  export type AccessRole = AttributeRestrictionRole | EntityRestrictionRole
119
172
 
173
+ /** Assign individual records to a user in their profile */
120
174
  export interface IndividualEntityRestriction {
121
175
  type: "Individual"
176
+ /** The roles that this restriction applies to */
122
177
  roles: EntityRestrictionRole[]
178
+ /** Advanced. Force read operations to get all records in a single API call */
123
179
  singleQuery?: number
124
180
  }
181
+ /** Assign all records for a parent record to a user in their profile, i.e. "All Sites for Company X" */
125
182
  export interface ParentEntityRestriction {
126
183
  type: "Parent"
184
+ /** The roles that this restriction applies to */
127
185
  roles: EntityRestrictionRole[]
186
+ /** The field that parent records can be selected from. Must be a relational field */
128
187
  collectionField: string
188
+ /** Advanced. Force read operations to get all records in a single API call */
129
189
  singleQuery?: number
130
190
  }
191
+ /** Assign all records for a parent record to a user in their profile, by attribute, i.e. "All Sites for Company X in State NY" */
131
192
  export interface ParentPropertyEntityRestriction {
132
193
  type: "Parent_Property"
194
+ /** The roles that this restriction applies to */
133
195
  roles: EntityRestrictionRole[]
196
+ /** The field that parent records can be selected from. Must be a relational field */
134
197
  collectionField: string
198
+ /** The field that defines the attribute */
135
199
  propertyField: string
136
200
  }
137
201
  export type AttributeRestriction = RecordUserRestriction | RecordOwnerRestriction | RecordPropertyRestriction
138
202
 
203
+ /** Users will only be able to access records that they have been assigned to, i.e. via an "Assigned To" field */
139
204
  export interface RecordUserRestriction {
140
205
  type: "Record_User"
206
+ /** The roles that this restriction applies to. If `assignable` is `true`, this restriction can be removed for individual users */
141
207
  roles: AttributeRestrictionRole[]
208
+ /** The field used to assign access. Must be a relational field linked to an auth collection */
142
209
  collectionField: string
210
+ /** If provided, the restriction only applies to the listed operations */
143
211
  operations?: ("Read" | "Create" | "Update" | "Delete")[]
144
212
  }
213
+ /** Users will only be able to access records that they created themselves */
145
214
  export interface RecordOwnerRestriction {
146
215
  type: "Record_Owner"
216
+ /** The roles that this restriction applies to. If `assignable` is `true`, this restriction can be removed for individual users */
147
217
  roles: AttributeRestrictionRole[]
218
+ /** If provided, the restriction only applies to the listed operations */
148
219
  operations?: ("Read" | "Create" | "Update" | "Delete")[]
149
220
  }
221
+ /** Users will only be able to access records that have specified values for a selected field, i.e. only "Not Started" and "In Progress" records */
150
222
  export interface RecordPropertyRestriction {
151
223
  type: "Record_Property"
224
+ /** The roles this restriction applies to, and which property values they can access. If `assignable` is `true`, this restriction can be removed for individual users */
152
225
  roles: AttributeRestrictionRole[]
226
+ /** The field that defines the property. Must be a String field with `values` set */
153
227
  propertyField: string
228
+ /** If provided, the restriction only applies to the listed operations */
154
229
  operations?: ("Read" | "Create" | "Update" | "Delete")[]
155
230
  }
156
231
 
157
232
  export type EntityRestriction = IndividualEntityRestriction | ParentEntityRestriction | ParentPropertyEntityRestriction
158
233
  export type AccessRestriction = AttributeRestriction | EntityRestriction
159
234
 
235
+ /** Apply an individual entity restriction from a parent collection onto this collection */
160
236
  export interface IndividualEntityParentFilter {
161
237
  type: "Individual"
238
+ /** The relational field that links to the collection that the individual entity restriction is on */
162
239
  collectionField: string
240
+ /** The roles that this parent filter applies to */
163
241
  roles: EntityRestrictionRole[]
164
242
  }
243
+ /** Apply a parent entity restriction from a parent collection onto this collection */
165
244
  export interface ParentEntityParentFilter {
166
245
  type: "Parent"
246
+ /** The relational field that links to the collection that the parent entity restriction is on */
167
247
  collectionField: string
248
+ /** The relational field that matches the parent entity restriction's collection field */
168
249
  parentCollectionField: string
250
+ /** The roles that this parent filter applies to */
169
251
  roles: EntityRestrictionRole[]
170
252
  }
253
+ /** Apply a parent property entity restriction from a parent collection onto this collection */
171
254
  export interface ParentPropertyEntityParentFilter {
172
255
  type: "Parent_Property"
256
+ /** The relational field that links to the collection that the parent property entity restriction is on */
173
257
  collectionField: string
258
+ /** The relational field that matches the parent entity restriction's collection field */
174
259
  parentCollectionField: string
260
+ /** The field that matches the parent entity restriction's property field */
175
261
  parentPropertyField: string
262
+ /** The roles that this parent filter applies to */
176
263
  roles: EntityRestrictionRole[]
177
264
  }
178
265
  export type EntityParentFilter =
179
- | IndividualEntityParentFilter
180
- | ParentEntityParentFilter
181
- | ParentPropertyEntityParentFilter
266
+ IndividualEntityParentFilter | ParentEntityParentFilter | ParentPropertyEntityParentFilter
182
267
 
268
+ /** Define which roles can perform which CRUD operations for the collection */
183
269
  export interface AccessOperations {
270
+ /** Set to `true` or an array of user roles to allow disabling of access in the user's profile */
184
271
  assignable?: boolean | StokerRole[]
272
+ /** Roles that can read records in the collection */
185
273
  read?: StokerRole[]
274
+ /** Roles that can create records in the collection */
186
275
  create?: StokerRole[]
276
+ /** Roles that can update records in the collection */
187
277
  update?: StokerRole[]
278
+ /** Roles that can delete records in the collection */
188
279
  delete?: StokerRole[]
189
280
  }
190
281
 
282
+ /** The roles that must be granted each file operation */
191
283
  export interface AccessFilesAssignmentRoles {
284
+ /** Roles for read access to the file */
192
285
  read?: StokerRole[]
286
+ /** Roles for update access to the file */
193
287
  update?: StokerRole[]
288
+ /** Roles for delete access to the file */
194
289
  delete?: StokerRole[]
195
290
  }
291
+ /** File access assignment rules for a user role */
196
292
  export interface AccessFilesAssignment {
293
+ /** Access assignments the user may optionally grant */
197
294
  optional?: AccessFilesAssignmentRoles
295
+ /** Access assignments the user must grant */
198
296
  required?: AccessFilesAssignmentRoles
199
297
  }
298
+ /** Access rules for file uploads */
200
299
  export interface AccessFiles {
300
+ /** Define the user roles that the user must assign access to for each file */
201
301
  assignment?: {
202
302
  [role: StokerRole]: AccessFilesAssignment
203
303
  }
304
+ /** Enforce Firebase Storage metadata constraints, i.e. `{ size: " <= (5 * 1024 * 1024)" }` */
204
305
  metadata?: {
205
306
  [key: string]: string
206
307
  }
308
+ /** Enforce custom metadata constraints */
207
309
  customMetadata?: {
208
310
  [key: string]: string
209
311
  }
210
312
  }
211
313
 
314
+ /** Explicitly define which specific records or groups of records can be accessed by a user. Assignment is done in the user's profile (by an Admin) */
212
315
  export interface EntityRestrictions {
316
+ /** User roles for which entity restrictions can be disabled for individual users */
213
317
  assignable?: StokerRole[]
318
+ /** The entity restrictions to apply */
214
319
  restrictions?: EntityRestriction[]
320
+ /** Apply entity restrictions from a parent collection onto this collection, i.e. "All Jobs on Sites for Company X" */
215
321
  parentFilters?: EntityParentFilter[]
216
322
  }
323
+ /** Access control config for the collection */
217
324
  export interface CollectionAccess {
325
+ /**
326
+ * Roles that must read data via the server. This allows more granular access control (specified in
327
+ * `custom.serverAccess` at the collection or field level). Warning: slows performance and removes
328
+ * offline and realtime capabilities
329
+ */
218
330
  serverReadOnly?: StokerRole[]
331
+ /**
332
+ * Set to `true` to force writes through the server. Required for two-way relation writes,
333
+ * and automatically enabled for collections with `auth` set to `true`. Removes offline write
334
+ * capabilities, but can greatly reduce the amount of Firestore Security Rules used by the collection
335
+ */
219
336
  serverWriteOnly?: boolean
337
+ /** Set to `true` to write custom Firestore Security Rules for the collection, at `firebase-rules/firestore.custom.rules` */
220
338
  customSecurityRules?: boolean
339
+ /** Set to `true` to write custom Firebase Storage Rules for the collection */
221
340
  customStorageRules?: boolean
341
+ /** Restrict a user's access to records with certain attributes */
222
342
  attributeRestrictions?: AttributeRestriction[]
343
+ /** Explicitly define which specific records or groups of records can be accessed by a user */
223
344
  entityRestrictions?: EntityRestrictions
345
+ /** Restrict which permissions a user role can assign to other user roles */
224
346
  permissionWriteRestrictions?: PermissionWriteRestriction[]
347
+ /** Define which roles can perform which CRUD operations for the collection */
225
348
  operations: AccessOperations
349
+ /**
350
+ * Only relevant when `auth` is set to `true` in the root collection config.
351
+ * `roles`: Roles that can be granted the ability to assign access credentials for this collection.
352
+ * `assignable`: Optional subset of `roles` for which auth can be enabled or disabled per user.
353
+ * Roles listed in `roles` but not in `assignable` are granted auth access automatically
354
+ */
226
355
  auth?: { roles: StokerRole[]; assignable?: StokerRole[] }
356
+ /** Define access rules for file uploads */
227
357
  files?: AccessFiles
228
358
  }
229
359
 
360
+ /** Arguments for the preOperation hook, which fires before a read or write operation */
230
361
  export type PreOperationHookArgs = {
231
362
  operation: "read" | "create" | "update" | "delete"
232
363
  data?: StokerRecord
@@ -236,6 +367,7 @@ export type PreOperationHookArgs = {
236
367
  batch?: WriteBatch
237
368
  originalRecord?: StokerRecord
238
369
  }
370
+ /** Arguments for the preRead hook, which fires before a read operation */
239
371
  export type PreReadHookArgs = {
240
372
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
241
373
  context: any
@@ -243,6 +375,7 @@ export type PreReadHookArgs = {
243
375
  multiple?: boolean
244
376
  listener?: boolean
245
377
  }
378
+ /** Arguments for the postRead hook, which fires after a read operation */
246
379
  export type PostReadHookArgs = {
247
380
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
248
381
  context: any
@@ -250,7 +383,9 @@ export type PostReadHookArgs = {
250
383
  record?: StokerRecord
251
384
  listener?: boolean
252
385
  }
386
+ /** Arguments for the preDuplicate hook, which fires before a duplicate operation in the Admin UI */
253
387
  export type PreDuplicateHookArgs = { data: Partial<StokerRecord> }
388
+ /** Arguments for the preValidate hook, which fires at write validation time */
254
389
  export type PreValidateHookArgs = {
255
390
  operation: "create" | "update"
256
391
  data: StokerRecord
@@ -259,6 +394,7 @@ export type PreValidateHookArgs = {
259
394
  batch?: WriteBatch
260
395
  originalRecord?: StokerRecord
261
396
  }
397
+ /** Arguments for the preWrite hook, which fires before a write operation */
262
398
  export type PreWriteHookArgs = {
263
399
  operation: "create" | "update" | "delete"
264
400
  data: StokerRecord
@@ -268,6 +404,7 @@ export type PreWriteHookArgs = {
268
404
  batch?: WriteBatch
269
405
  originalRecord?: StokerRecord
270
406
  }
407
+ /** Arguments for the postWrite hook, which fires after a write operation */
271
408
  export type PostWriteHookArgs = {
272
409
  operation: "create" | "update" | "delete"
273
410
  data: StokerRecord
@@ -277,6 +414,7 @@ export type PostWriteHookArgs = {
277
414
  retry?: boolean
278
415
  originalRecord?: StokerRecord
279
416
  }
417
+ /** Arguments for the postWriteError hook, which fires when a write operation encounters an error */
280
418
  export type PostWriteErrorHookArgs = {
281
419
  operation: "create" | "update" | "delete"
282
420
  data: StokerRecord
@@ -289,6 +427,7 @@ export type PostWriteErrorHookArgs = {
289
427
  retries?: number
290
428
  originalRecord?: StokerRecord
291
429
  }
430
+ /** Arguments for the postOperation hook, which fires after a read or write operation */
292
431
  export type PostOperationHookArgs = {
293
432
  operation: "read" | "create" | "update" | "delete"
294
433
  data?: StokerRecord
@@ -299,12 +438,14 @@ export type PostOperationHookArgs = {
299
438
  originalRecord?: StokerRecord
300
439
  }
301
440
 
441
+ /** The access permissions assigned to an uploaded file */
302
442
  export type FilePermissions = {
303
443
  read?: string
304
444
  update?: string
305
445
  delete?: string
306
446
  }
307
447
 
448
+ /** Arguments for the preFileAdd hook, which fires before a file is uploaded */
308
449
  export type PreFileAddHookArgs = {
309
450
  record: StokerRecord
310
451
  fullPath: string
@@ -312,6 +453,7 @@ export type PreFileAddHookArgs = {
312
453
  permissions: FilePermissions
313
454
  }
314
455
 
456
+ /** Arguments for the preFileUpdate hook, which fires before a file is updated */
315
457
  export type PreFileUpdateHookArgs = {
316
458
  record: StokerRecord
317
459
  update:
@@ -324,6 +466,7 @@ export type PreFileUpdateHookArgs = {
324
466
  }
325
467
  }
326
468
 
469
+ /** Arguments for the postFileAdd hook, which fires after a file is uploaded */
327
470
  export type PostFileAddHookArgs = {
328
471
  record: StokerRecord
329
472
  fullPath: string
@@ -331,6 +474,7 @@ export type PostFileAddHookArgs = {
331
474
  permissions: FilePermissions
332
475
  }
333
476
 
477
+ /** Arguments for the postFileUpdate hook, which fires after a file is updated */
334
478
  export type PostFileUpdateHookArgs = {
335
479
  record: StokerRecord
336
480
  update:
@@ -343,10 +487,12 @@ export type PostFileUpdateHookArgs = {
343
487
  }
344
488
  }
345
489
 
490
+ /** Arguments for the postFileAddError hook, which fires when a file upload fails */
346
491
  export type PostFileAddErrorHookArgs = PostFileAddHookArgs & {
347
492
  error: unknown
348
493
  }
349
494
 
495
+ /** Arguments for the setEmbedding hook, which calculates an embedding value for the record */
350
496
  export type SetEmbeddingHookArgs = { record: StokerRecord }
351
497
 
352
498
  export type HookArgs =
@@ -366,24 +512,43 @@ export type HookArgs =
366
512
  | PostFileUpdateHookArgs
367
513
  | SetEmbeddingHookArgs
368
514
 
515
+ /** Fires before a read or write operation. Return `false` to cancel the operation */
369
516
  export type PreOperationHook = (args: PreOperationHookArgs) => boolean | void | Promise<boolean | void>
517
+ /** Fires before a read operation */
370
518
  export type PreReadHook = (args: PreReadHookArgs) => void | Promise<void>
519
+ /** Fires after a read operation */
371
520
  export type PostReadHook = (args: PostReadHookArgs) => void | Promise<void>
521
+ /** Fires before a duplicate operation in the Admin UI. Return `false` to cancel the operation */
372
522
  export type PreDuplicateHook = (args: PreDuplicateHookArgs) => boolean | void | Promise<boolean | void>
523
+ /**
524
+ * Fires at write validation time. This is where you can define custom validation logic.
525
+ * Return an object with a boolean indicating whether validation passed, and a message to
526
+ * display to the user if validation has failed
527
+ */
373
528
  export type PreValidateHook = (
374
529
  args: PreValidateHookArgs,
375
530
  ) => { valid: boolean; message?: string } | Promise<{ valid: boolean; message?: string }>
531
+ /** Fires before a write operation. Return `false` to cancel the operation */
376
532
  export type PreWriteHook = (args: PreWriteHookArgs) => boolean | void | Promise<boolean | void>
533
+ /** Fires after a write operation */
377
534
  export type PostWriteHook = (args: PostWriteHookArgs) => boolean | void | Promise<boolean | void>
535
+ /** Fires when a write operation encounters an error. May fire multiple times per write, so be sure to write idempotent code */
378
536
  export type PostWriteErrorHook = (args: PostWriteErrorHookArgs) => boolean | void | Promise<boolean | void>
537
+ /** Fires after a read or write operation */
379
538
  export type PostOperationHook = (args: PostOperationHookArgs) => boolean | void | Promise<boolean | void>
380
539
 
540
+ /** Fires before a file is uploaded. Return `false` to cancel the operation */
381
541
  export type PreFileAddHook = (args: PreFileAddHookArgs) => boolean | void | Promise<boolean | void>
542
+ /** Fires before a file is updated. Return `false` to cancel the operation */
382
543
  export type PreFileUpdateHook = (args: PreFileUpdateHookArgs) => boolean | void | Promise<boolean | void>
544
+ /** Fires after a file is uploaded */
383
545
  export type PostFileAddHook = (args: PostFileAddHookArgs) => boolean | void | Promise<boolean | void>
546
+ /** Fires after a file is updated */
384
547
  export type PostFileUpdateHook = (args: PostFileUpdateHookArgs) => boolean | void | Promise<boolean | void>
548
+ /** Fires when a file upload fails */
385
549
  export type PostFileAddErrorHook = (args: PostFileAddErrorHookArgs) => void | Promise<void>
386
550
 
551
+ /** Calculate an embedding value for the record */
387
552
  export type SetEmbeddingHook = (args: SetEmbeddingHookArgs) => string | Promise<string>
388
553
 
389
554
  export type Hook =
@@ -404,45 +569,85 @@ export type Hook =
404
569
  | SetEmbeddingHook
405
570
 
406
571
  export type Hooks = {
572
+ /** Fires before a read or write operation. Return `false` to cancel the operation */
407
573
  preOperation?: PreOperationHook
574
+ /** Fires before a read operation */
408
575
  preRead?: PreReadHook
576
+ /** Fires after a read operation */
409
577
  postRead?: PostReadHook
578
+ /** Fires before a duplicate operation in the Admin UI. Return `false` to cancel the operation */
410
579
  preDuplicate?: PreDuplicateHook
580
+ /**
581
+ * Fires at write validation time. This is where you can define custom validation logic.
582
+ * Return an object with a boolean indicating whether validation passed, and a message to
583
+ * display to the user if validation has failed
584
+ */
411
585
  preValidate?: PreValidateHook
586
+ /** Fires before a write operation. Return `false` to cancel the operation */
412
587
  preWrite?: PreWriteHook
588
+ /** Fires after a write operation */
413
589
  postWrite?: PostWriteHook
590
+ /** Fires when a write operation encounters an error. May fire multiple times per write, so be sure to write idempotent code */
414
591
  postWriteError?: PostWriteErrorHook
592
+ /** Fires after a read or write operation */
415
593
  postOperation?: PostOperationHook
594
+ /** Fires before a file is uploaded. Return `false` to cancel the operation */
416
595
  preFileAdd?: PreFileAddHook
596
+ /** Fires before a file is updated. Return `false` to cancel the operation */
417
597
  preFileUpdate?: PreFileUpdateHook
598
+ /** Fires after a file is uploaded */
418
599
  postFileAdd?: PostFileAddHook
600
+ /** Fires when a file upload fails */
419
601
  postFileAddError?: PostFileAddErrorHook
602
+ /** Fires after a file is updated */
420
603
  postFileUpdate?: PostFileUpdateHook
604
+ /** Calculate an embedding value for the record. Required for AI chat embeddings */
421
605
  setEmbedding?: SetEmbeddingHook
422
606
  }
423
607
 
608
+ /** Preload a range of time-series data. The user will be able to update the preloaded range using a date picker in the Admin UI */
424
609
  export interface PreloadCacheRange {
610
+ /** Timestamp fields the user can preload by */
425
611
  fields: string[]
612
+ /** Ranges of fields to preload, for example `["Start", "End"]`. Fields must also be listed in `fields` */
426
613
  ranges?: [string, string][]
614
+ /** Human-readable labels for the fields listed in `fields` */
427
615
  labels?: string[]
616
+ /** The default start date for the preloaded range */
428
617
  start: "Today" | "Week" | "Month" | "Year" | Date | number
618
+ /** Offset the default start date by this many days */
429
619
  startOffsetDays?: number
620
+ /** Offset the default start date by this many hours */
430
621
  startOffsetHours?: number
622
+ /** The default end date for the preloaded range */
431
623
  end?: Date | number
624
+ /** Offset the default end date by this many days */
432
625
  endOffsetDays?: number
626
+ /** Offset the default end date by this many hours */
433
627
  endOffsetHours?: number
628
+ /** Which selectors to show in the range picker */
434
629
  selector?: "range" | "week" | "month" | ("range" | "week" | "month")[]
435
630
  }
436
631
 
632
+ /**
633
+ * Preload data for the collection on app startup. Preloaded data is cached and is available
634
+ * for the lifetime of the session, resulting in a snappy application that works offline.
635
+ * Highly recommended for time series data
636
+ */
437
637
  export interface PreloadCache {
638
+ /** The user roles that will use the preload cache */
438
639
  roles: StokerRole[]
640
+ /** Whether to wait for related collections to load before signalling to the app that the collection is loaded */
439
641
  relationCollections?: boolean | (() => boolean | Promise<boolean>)
642
+ /** Preload a range of time-series data. The user can update the preloaded range using a date picker in the Admin UI */
440
643
  range?: PreloadCacheRange
644
+ /** Advanced. Additional Firestore constraints to apply to the preload cache */
441
645
  constraints?:
442
646
  | [string, WhereFilterOp, unknown][]
443
647
  | (() => [string, WhereFilterOp, unknown][] | Promise<[string, WhereFilterOp, unknown][]>)
444
648
  }
445
649
 
650
+ /** The initial preload cache state for each collection */
446
651
  export interface PreloadCacheInitial {
447
652
  [collection: string]: {
448
653
  roles: StokerRole[]
@@ -452,7 +657,13 @@ export interface PreloadCacheInitial {
452
657
  }
453
658
  }
454
659
 
660
+ /** Custom code config for the collection, including hooks and server access control */
455
661
  export interface CollectionCustom extends Hooks {
662
+ /**
663
+ * Define additional access control using code on the server. Only relevant if
664
+ * `access.serverWriteOnly` is set to `true`. Return a boolean indicating whether or not
665
+ * the access check passed. This code is not sent to the client
666
+ */
456
667
  serverAccess?: {
457
668
  read?: (permissions: StokerPermissions, user: UserRecord, record?: StokerRecord) => boolean | Promise<boolean>
458
669
  create?: (permissions: StokerPermissions, user: UserRecord, record: StokerRecord) => boolean | Promise<boolean>
@@ -464,15 +675,21 @@ export interface CollectionCustom extends Hooks {
464
675
  ) => boolean | Promise<boolean>
465
676
  delete?: (permissions: StokerPermissions, user: UserRecord, record: StokerRecord) => boolean | Promise<boolean>
466
677
  }
678
+ /** Advanced. Additional Firestore constraints to apply to the preload cache */
467
679
  preloadCacheConstraints?:
468
680
  | [string, WhereFilterOp, unknown][]
469
681
  | (() => [string, WhereFilterOp, unknown][] | Promise<[string, WhereFilterOp, unknown][]>)
682
+ /** Advanced. Firestore OR query constraints to apply to the preload cache */
470
683
  preloadCacheOrQueries?:
471
684
  | [string, WhereFilterOp, unknown][]
472
685
  | (() => [string, WhereFilterOp, unknown][] | Promise<[string, WhereFilterOp, unknown][]>)
686
+ /** Return `true` to automatically rename duplicate records rather than throwing an error. Only relevant when `access.serverWriteOnly` is falsy */
473
687
  autoCorrectUnique?: boolean | (() => boolean | Promise<boolean>)
688
+ /** Return `true` to disable adding new records while offline */
474
689
  disableOfflineCreate?: boolean | (() => boolean | Promise<boolean>)
690
+ /** Return `true` to disable updating records while offline */
475
691
  disableOfflineUpdate?: boolean | (() => boolean | Promise<boolean>)
692
+ /** Return `true` to disable deleting records while offline */
476
693
  disableOfflineDelete?: boolean | (() => boolean | Promise<boolean>)
477
694
  }
478
695
  export interface CollectionCustomCache {
@@ -484,37 +701,64 @@ export interface CollectionCustomCache {
484
701
  disableOfflineDelete?: boolean
485
702
  }
486
703
 
704
+ /** Config for the list view */
487
705
  export interface ListConfig {
706
+ /** Limit which user roles can view the list */
488
707
  roles?: StokerRole[]
708
+ /** Customise the title for the list tab. Defaults to `"List"` */
489
709
  title?: string
490
710
  }
491
711
 
712
+ /** Show a board view with drag and drop and infinite scroll */
492
713
  export interface CardsConfig {
714
+ /** Limit which user roles can view the board */
493
715
  roles?: StokerRole[]
716
+ /** The field that defines the board columns. Must be a String or Number field with `values`, or a Boolean field. Not required if `admin.statusField` has already been set */
494
717
  statusField?: string
718
+ /** Exclude status values from the board */
495
719
  excludeValues?: string[] | number[]
720
+ /** The sub-heading shown on cards */
496
721
  headerField: string
722
+ /** The number of lines for the header field text */
497
723
  maxHeaderLines?: 1 | 2
724
+ /** Sections to display on cards */
498
725
  sections: {
726
+ /** The title for the section */
499
727
  title?: string
728
+ /** The fields to display in the section */
500
729
  fields: string[]
730
+ /** Show multiple columns of fields, rather than listing fields down the card vertically */
501
731
  blocks?: boolean
732
+ /** Show a large field value */
502
733
  large?: boolean
734
+ /** The number of lines for field text */
503
735
  maxSectionLines?: 1 | 2 | 3 | 4
736
+ /** Only relevant when `blocks` is set to `true`. Hide the outermost block at this screen size. Helps with responsiveness */
504
737
  collapse?: "sm" | "md" | "lg" | "xl" | "2xl" | ((record?: StokerRecord) => "sm" | "md" | "lg" | "xl" | "2xl")
505
738
  }[]
739
+ /** The footer field shown on cards */
506
740
  footerField?: string
741
+ /** The number of lines for the footer field text */
507
742
  maxFooterLines?: 1 | 2
743
+ /** Customise the title for the board tab. Defaults to `"Board"` */
508
744
  title?: string
745
+ /** Tailwind classes to apply to the card component */
509
746
  cardClass?: string
510
747
  }
511
748
 
749
+ /** Show a list of image cards with infinite scroll */
512
750
  export interface ImagesConfig {
751
+ /** Limit which user roles can view the images page */
513
752
  roles?: StokerRole[]
753
+ /** The field that contains the image URL for the record. Must be a String field */
514
754
  imageField: string
755
+ /** The image size */
515
756
  size: "sm" | "md" | "lg" | "xl"
757
+ /** The number of lines for the header field text */
516
758
  maxHeaderLines?: 1 | 2
759
+ /** Customise the title for the images tab. Defaults to `"Pics"` */
517
760
  title?: string
761
+ /** An optional custom component shown above each image */
518
762
  customComponent?: {
519
763
  component: React.FC<{
520
764
  record: StokerRecord | undefined
@@ -535,55 +779,88 @@ export interface ImagesConfig {
535
779
  }
536
780
  }
537
781
 
782
+ /** Show a map view */
538
783
  export interface MapConfig {
784
+ /** Limit which user roles can view the map page */
539
785
  roles?: StokerRole[]
786
+ /** The field containing coordinates. Must be an Array field */
540
787
  coordinatesField?: string
788
+ /** Alternatively, provide a String field containing an address */
541
789
  addressField?: string
790
+ /** The starting coordinates for the map */
542
791
  center: {
543
792
  lat: number
544
793
  lng: number
545
794
  }
795
+ /** The starting zoom value for the map */
546
796
  zoom: number
797
+ /** Show a column of records without coordinates or an address. Records can be dragged onto the map (only when `coordinatesField` is provided) */
547
798
  noLocation?: {
548
799
  title: string
549
800
  }
801
+ /** Customise the title for the map tab. Defaults to `"Map"` */
550
802
  title?: string
551
803
  }
552
804
 
805
+ /** Show a calendar view. Requires a Fullcalendar license */
553
806
  export interface CalendarConfig {
807
+ /** Limit which user roles can view the calendar page */
554
808
  roles?: StokerRole[]
809
+ /** Timestamp field specifying the start date of the event */
555
810
  startField: string
811
+ /** Timestamp field specifying the end date of the event. If omitted, the start date is used */
556
812
  endField?: string
813
+ /** Additional Timestamp fields to include as all-day events */
557
814
  additionalFields?: string[]
815
+ /** Boolean field indicating whether records are all-day */
558
816
  allDayField?: string
817
+ /** Fullcalendar options for desktop screen sizes */
559
818
  fullCalendarLarge?: CalendarOptions
819
+ /** Fullcalendar options for mobile screen sizes */
560
820
  fullCalendarSmall?: CalendarOptions
821
+ /** Relational field specifying a parent resource. Used for Fullcalendar features that require "resources" */
561
822
  resourceField?: string
823
+ /** Field in the `resourceField` collection that acts as the resource title */
562
824
  resourceTitleField?: string
825
+ /** Show a column of unscheduled records. Only relevant if `preloadCache.range` is present for the user's role. Records can be dragged onto the calendar */
563
826
  unscheduled?: {
564
827
  title: string
565
828
  roles?: StokerRole[]
566
829
  }
830
+ /** Customise the title for the calendar tab. Defaults to `"Calendar"` */
567
831
  title?: string
832
+ /** How far into the past to load records for */
568
833
  dataStart?: { days: number } | { weeks: number } | { months: number } | { years: number }
834
+ /** How far into the future to load records for */
569
835
  dataEnd?: { days: number } | { weeks: number } | { months: number } | { years: number }
836
+ /** Threshold at which more past records will be loaded */
570
837
  dataStartOffset?: { days: number } | { weeks: number } | { months: number } | { years: number }
838
+ /** Threshold at which more future records will be loaded */
571
839
  dataEndOffset?: { days: number } | { weeks: number } | { months: number } | { years: number }
840
+ /** The color for the provided record's event on the calendar */
572
841
  color?: string | ((record: StokerRecord) => string)
842
+ /** A custom title for the provided record's event on the calendar */
573
843
  eventTitle?: (record: StokerRecord) => string
844
+ /** Determines whether a record should be displayed on the calendar. This filter only runs in the client, so it should not be used for access control purposes */
574
845
  filterRecords?: (record: StokerRecord) => boolean
846
+ /** Additional collections to show on the calendar. Only works when the preload cache is enabled for the user's role for the given collection */
575
847
  additionalCollections?: StokerCollection[]
576
848
  }
577
849
 
850
+ /** Filter the list by the collection's status field */
578
851
  export type StatusFilter = {
579
852
  type: "status"
580
853
  value?: string | number
854
+ /** The roles that can see this filter */
581
855
  roles?: StokerRole[]
582
856
  }
583
857
 
858
+ /** Filter the list by a Timestamp field */
584
859
  export type RangeFilter = {
585
860
  type: "range"
861
+ /** The Timestamp field to filter by */
586
862
  field: string
863
+ /** Which selectors to show in the range picker */
587
864
  selector?:
588
865
  | "range"
589
866
  | "week"
@@ -591,29 +868,40 @@ export type RangeFilter = {
591
868
  | ("range" | "week" | "month")[]
592
869
  | (() => "range" | "week" | "month" | ("range" | "week" | "month")[])
593
870
  value?: string
871
+ /** Offset the default start date by this many days */
594
872
  startOffsetDays?: number
873
+ /** Offset the default start date by this many hours */
595
874
  startOffsetHours?: number
875
+ /** Offset the default end date by this many days */
596
876
  endOffsetDays?: number
877
+ /** Offset the default end date by this many hours */
597
878
  endOffsetHours?: number
598
879
  }
599
880
 
881
+ /** Filter the list by a field with `values` set */
600
882
  export type SelectFilter = {
601
883
  type: "select"
884
+ /** The field to filter by. Must have `values` set */
602
885
  field: string
886
+ /** The title for the filter */
603
887
  title?: string | (() => string)
888
+ /** The roles that can see this filter */
604
889
  roles?: StokerRole[]
890
+ /** Modify the titles shown for filter values */
605
891
  titles?: (
606
892
  value: string,
607
893
  relationCollection?: CollectionSchema,
608
894
  relationParent?: StokerRecord,
609
895
  isAssigning?: boolean,
610
896
  ) => string
897
+ /** Filter which values are shown in the filter */
611
898
  filterValues?: (
612
899
  value: boolean | string | number | undefined,
613
900
  relationCollection?: CollectionSchema,
614
901
  relationParent?: StokerRecord,
615
902
  isAssigning?: boolean,
616
903
  ) => boolean
904
+ /** The default value for the filter */
617
905
  defaultValue?:
618
906
  | string
619
907
  | number
@@ -622,88 +910,147 @@ export type SelectFilter = {
622
910
  parentRecord?: StokerRecord,
623
911
  isAssigning?: boolean,
624
912
  ) => string | number | undefined)
913
+ /** Show or hide the filter */
625
914
  condition?: (parentCollection?: CollectionSchema, parentRecord?: StokerRecord, isAssigning?: boolean) => boolean
626
915
  value?: string | number
916
+ /** The style of the filter input */
627
917
  style?: "select" | "radio" | "buttons"
628
918
  }
629
919
 
920
+ /** Filter the list by a related field */
630
921
  export type RelationFilter = {
631
922
  type: "relation"
923
+ /** The relational field to filter by */
632
924
  field: string
925
+ /** The title for the filter */
633
926
  title?: string | (() => string)
927
+ /** The roles that can see this filter */
634
928
  roles?: StokerRole[]
929
+ /** Filter the list of related values using a Firestore where() query */
635
930
  constraints?: [string, "==" | "in", unknown][]
636
931
  value?: string
637
932
  }
638
933
 
934
+ /** A filter shown in the right-hand-side filter drawer on the list page */
639
935
  export type Filter = StatusFilter | RangeFilter | SelectFilter | RelationFilter
640
936
 
937
+ /** Show a metric (numerical counter) at the top of the list page */
641
938
  export interface Metric {
939
+ /** The metric type. For "custom" metrics, use the `formula` method to calculate the value to display */
642
940
  type: "sum" | "average" | "count" | "custom"
941
+ /** The field to aggregate. Not required for `count` or `custom` */
643
942
  field?: string
943
+ /** Limit which user roles can view the metric */
644
944
  roles?: StokerRole[]
945
+ /** The title shown above the metric */
645
946
  title?: string
947
+ /** Maximum decimal places to display */
646
948
  decimal?: number
949
+ /** Prefix text, for example a currency symbol */
647
950
  prefix?: string
951
+ /** Suffix text, for example units */
648
952
  suffix?: string
953
+ /** Tailwind text size for the metric value */
649
954
  textSize?: "text-xl" | "text-2xl" | "text-3xl"
955
+ /** Compact the metric vertically */
650
956
  compact?: boolean
957
+ /** Custom metric calculation */
651
958
  formula?: (records: StokerRecord[]) => number | string
652
959
  }
960
+ /** Show a chart at the top of the list page */
653
961
  export interface Chart {
962
+ /** The chart type */
654
963
  type: "area"
964
+ /** The date field used to group points */
655
965
  dateField: string
966
+ /** First metric field */
656
967
  metricField1?: string
968
+ /** Optional second metric field */
657
969
  metricField2?: string
970
+ /** Default chart date range */
658
971
  defaultRange: "90d" | "30d" | "7d"
972
+ /** Limit which user roles can view the chart */
659
973
  roles?: StokerRole[]
974
+ /** Title shown above the chart */
660
975
  title?: string
976
+ /** Currency symbol to display */
661
977
  currency?: string | (() => string)
662
978
  }
979
+ /** A custom meta title and description for the collection's pages */
663
980
  export interface CollectionMeta {
664
981
  title?: string
665
982
  description?: string
666
983
  }
984
+ /** Highlight rows in the list view */
667
985
  export interface RowHighlight {
986
+ /** Return `true` to highlight the row for the given record */
668
987
  condition: (record: StokerRecord) => boolean
988
+ /** The Tailwind classes to apply to the highlighted row */
669
989
  className: string
990
+ /** The user roles to highlight rows for */
670
991
  roles?: StokerRole[]
671
992
  }
993
+ /** Converting a record creates a new record in the target collection and keeps the original */
672
994
  export interface Convert {
995
+ /** The collection to convert records to */
673
996
  collection: string
997
+ /** A function that modifies the record before conversion */
674
998
  convert: (record: StokerRecord) => Partial<StokerRecord> | Promise<Partial<StokerRecord>>
999
+ /** The roles that can perform this conversion */
675
1000
  roles?: StokerRole[]
676
1001
  }
1002
+ /** A custom form field component */
677
1003
  export interface CustomField {
1004
+ /** The position of the custom component in the form */
678
1005
  position?: number | ((record?: StokerRecord) => number)
1006
+ /** The React component */
679
1007
  component?: React.FC
1008
+ /** Props to pass to the React component */
680
1009
  props?: Record<string, unknown>
1010
+ /** Show or hide the custom component */
681
1011
  condition?: (operation: "create" | "update" | "update-many", record?: StokerRecord) => boolean
682
1012
  }
1013
+ /** Show a relation list directly on the edit record form page */
683
1014
  export interface FormList {
1015
+ /** The collection to show the relation list for */
684
1016
  collection: StokerCollection
1017
+ /** Which columns to show in the list */
685
1018
  fields: string[]
1019
+ /** The field to sort records by */
686
1020
  sortField?: string
1021
+ /** The direction to sort records by */
687
1022
  sortDirection?: "asc" | "desc"
1023
+ /** The title for the relation list */
688
1024
  label?: string
689
1025
  }
690
1026
 
1027
+ /** A custom button shown at the bottom of the edit record form */
691
1028
  export interface FormButton {
1029
+ /** The title text for the custom button */
692
1030
  title: string
1031
+ /** The icon shown on the button */
693
1032
  icon?: React.FC<{ className?: string }>
1033
+ /** The style of the button */
694
1034
  variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link"
1035
+ /** The function that fires when the button is clicked */
695
1036
  action: (
696
1037
  operation: "create" | "update" | "update-many",
697
1038
  formValues: StokerRecord,
698
1039
  originalRecord?: StokerRecord,
699
1040
  ) => void | Promise<void>
1041
+ /** Show or hide the button */
700
1042
  condition?: boolean | ((operation: "create" | "update" | "update-many", record?: StokerRecord) => boolean)
1043
+ /** A loading callback that will be called when the button is pressed */
701
1044
  setIsLoading?: (isLoading: boolean) => void
702
1045
  }
703
1046
 
1047
+ /** A custom page for the collection, shown in the record page sidebar */
704
1048
  export interface CustomRecordPage {
1049
+ /** The title for the custom page in the sidebar */
705
1050
  title: string
1051
+ /** The URL segment that the page will load on */
706
1052
  url: string
1053
+ /** The custom component */
707
1054
  component: React.FC<{
708
1055
  record: StokerRecord | undefined
709
1056
  collection: CollectionSchema
@@ -714,8 +1061,11 @@ export interface CustomRecordPage {
714
1061
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
715
1062
  utils: any
716
1063
  }>
1064
+ /** Props to pass to the custom component */
717
1065
  props?: Record<string, unknown>
1066
+ /** Show or hide the custom page */
718
1067
  condition?: (record: StokerRecord | undefined) => boolean
1068
+ /** The icon shown in the sidebar */
719
1069
  icon?: React.FC<{ className?: string }>
720
1070
  }
721
1071
 
@@ -731,21 +1081,33 @@ export interface Assignable {
731
1081
  }[]
732
1082
  }
733
1083
 
1084
+ /** A custom action shown in a dropdown menu on the list page */
734
1085
  export interface CustomListAction {
1086
+ /** The title for the action */
735
1087
  title: string
1088
+ /** The icon shown for the action */
736
1089
  icon?: React.FC<{ className?: string }>
1090
+ /** The function that fires when the action is clicked */
737
1091
  action: () => void | Promise<void>
1092
+ /** Show or hide the action */
738
1093
  condition?: () => boolean
739
1094
  }
740
1095
 
1096
+ /** Options for file uploads */
741
1097
  export interface FileOptions {
1098
+ /** The maximum width for uploaded image files. Images above this size will be downscaled */
742
1099
  maxImageWidth?: number
1100
+ /** Set to `true` to show thumbnails for images in the files list */
743
1101
  thumbnails?: boolean
744
1102
  }
745
1103
 
1104
+ /** Admin UI config for the collection */
746
1105
  export interface CollectionAdmin {
1106
+ /** Return `true` to hide the collection in the Admin UI */
747
1107
  hidden?: boolean | (() => boolean | Promise<boolean>)
1108
+ /** The collection's position in the navbar */
748
1109
  navbarPosition?: number | (() => number)
1110
+ /** Human-readable labels for the collection. Only necessary if the root `labels` are not human-readable */
749
1111
  titles?:
750
1112
  | {
751
1113
  collection: string
@@ -756,15 +1118,31 @@ export interface CollectionAdmin {
756
1118
  parentCollection?: CollectionSchema,
757
1119
  parentRecord?: StokerRecord,
758
1120
  ) => { collection: string; record: string } | Promise<{ collection: string; record: string }>)
1121
+ /** An icon component for the collection. We recommend using Lucide icons, which are bundled with Stoker */
759
1122
  icon?: React.FC | (() => React.FC | Promise<React.FC>)
1123
+ /** Return `true` to show the "Duplicate" button on the form page */
760
1124
  duplicate?: boolean | (() => boolean | Promise<boolean>)
1125
+ /** Define which collections records can be converted to. A "Convert" button will be shown on the form page */
761
1126
  convert?: Convert[] | (() => Convert[] | Promise<Convert[]>)
1127
+ /**
1128
+ * Set to `true` to have the form page live-update when the record is updated remotely.
1129
+ * Can also be configured at the field-level. Note: on a live form remote updates will
1130
+ * overwrite local changes, introducing a risk of data loss
1131
+ */
762
1132
  live?: boolean | (() => boolean | Promise<boolean>)
1133
+ /**
1134
+ * Define a field that will be used to sort records into "Active" and "Archived" lists,
1135
+ * i.e. `{ field: "Status", active: ["Not Started", "In Progress"], archived: ["Completed"] }`
1136
+ */
763
1137
  statusField?: {
1138
+ /** The field that defines the active / archived status */
764
1139
  field: string
1140
+ /** Values considered active */
765
1141
  active?: unknown[]
1142
+ /** Values considered archived */
766
1143
  archived?: unknown[]
767
1144
  }
1145
+ /** The default view for the collection */
768
1146
  defaultView?:
769
1147
  | "list"
770
1148
  | "cards"
@@ -775,7 +1153,9 @@ export interface CollectionAdmin {
775
1153
  parentCollection: CollectionSchema,
776
1154
  parentRecord?: StokerRecord,
777
1155
  ) => "list" | "cards" | "images" | "map" | "calendar")
1156
+ /** The default route for the record page. Can be "edit", "files", a relation list collection name or a custom record page url */
778
1157
  defaultRoute?: string | (() => string)
1158
+ /** The default field to sort the list by */
779
1159
  defaultSort?:
780
1160
  | {
781
1161
  field: string
@@ -790,6 +1170,7 @@ export interface CollectionAdmin {
790
1170
  field: string
791
1171
  direction?: "asc" | "desc"
792
1172
  }>)
1173
+ /** The secondary field to sort the list by. Only works for collections with `preloadCache` or `access.serverReadOnly` enabled */
793
1174
  secondarySort?:
794
1175
  | {
795
1176
  field: string
@@ -804,52 +1185,93 @@ export interface CollectionAdmin {
804
1185
  field: string
805
1186
  direction?: "asc" | "desc"
806
1187
  }>)
1188
+ /** The number of items to show per page */
807
1189
  itemsPerPage?: number | (() => number | Promise<number>)
1190
+ /**
1191
+ * Full text search options. For roles with the preload cache or `access.serverReadOnly`
1192
+ * enabled, provide MiniSearch settings. For other roles, provide `{ hitsPerPage?: number }`
1193
+ * to specify the maximum number of results to retrieve from Algolia
1194
+ */
808
1195
  searchOptions?: SearchOptions & { hitsPerPage?: number }
1196
+ /** Config for the list view */
809
1197
  list?: ListConfig | (() => ListConfig | Promise<ListConfig>)
1198
+ /** Show a board view with drag and drop and infinite scroll */
810
1199
  cards?: CardsConfig | (() => CardsConfig | Promise<CardsConfig>)
1200
+ /** Show a list of image cards with infinite scroll */
811
1201
  images?: ImagesConfig | (() => ImagesConfig | Promise<ImagesConfig>)
1202
+ /** Show a map view */
812
1203
  map?: MapConfig | (() => MapConfig | Promise<MapConfig>)
1204
+ /** Show a calendar view. Requires a Fullcalendar license */
813
1205
  calendar?: CalendarConfig | (() => CalendarConfig | Promise<CalendarConfig>)
1206
+ /** Filters that will appear in the right-hand-side filter drawer on the list page */
814
1207
  filters?: Filter[]
1208
+ /** The date range selector options to be shown to the user. Only relevant when `preloadCache.range` is present */
815
1209
  rangeSelectorValues?:
816
1210
  | "range"
817
1211
  | "week"
818
1212
  | "month"
819
1213
  | ("range" | "week" | "month")[]
820
1214
  | (() => "range" | "week" | "month" | ("range" | "week" | "month")[])
1215
+ /** The default date range selector to be shown to the user. Only relevant when `preloadCache.range` is present or a range filter has been applied */
821
1216
  defaultRangeSelector?: "range" | "week" | "month" | (() => "range" | "week" | "month")
1217
+ /** Restrict CSV export to the defined roles */
822
1218
  restrictExport?: StokerRole[] | (() => StokerRole[] | Promise<StokerRole[]>)
1219
+ /** Display a counter in the title bar showing the number of items in the list */
823
1220
  titleCount?: boolean | (() => boolean | Promise<boolean>)
1221
+ /** Show metrics (numerical counters) and a chart at the top of the list page. We recommend 1-2 metrics and a chart */
824
1222
  metrics?: (Metric | Chart)[] | (() => (Metric | Chart)[] | Promise<(Metric | Chart)[]>)
1223
+ /** Define a custom meta title and description for the collection's pages */
825
1224
  meta?: CollectionMeta | (() => CollectionMeta | Promise<CollectionMeta>)
1225
+ /** Highlight rows in the list view */
826
1226
  rowHighlight?: RowHighlight[] | (() => RowHighlight[])
1227
+ /** An array of relational field names that will be used to show breadcrumbs at the top of the record page */
827
1228
  breadcrumbs?: string[] | (() => string[] | Promise<string[]>)
1229
+ /** Create custom form field components */
828
1230
  customFields?: CustomField[] | (() => CustomField[] | Promise<CustomField[]>)
1231
+ /** Create custom pages for the collection */
829
1232
  customRecordPages?: CustomRecordPage[] | (() => CustomRecordPage[] | Promise<CustomRecordPage[]>)
1233
+ /** Show custom buttons at the bottom of the edit record form */
830
1234
  formButtons?: FormButton[] | (() => FormButton[] | Promise<FormButton[]>)
1235
+ /** Show a file upload button on the add record form */
831
1236
  formUpload?: boolean | (() => boolean | Promise<boolean>)
1237
+ /** Show an image carousel at the top of the edit record form. All image files uploaded to the record will be displayed */
832
1238
  formImages?: boolean | (() => boolean | Promise<boolean>)
1239
+ /** Show relation lists directly on the edit record form page */
833
1240
  formLists?: FormList[] | (() => FormList[] | Promise<FormList[]>)
1241
+ /** Hide the add record button */
834
1242
  hideCreate?: boolean | ((relationList?: StokerCollection) => boolean | Promise<boolean>)
1243
+ /**
1244
+ * Disable the edit record form. Warning: this only disables editing client side.
1245
+ * Use `restrictUpdate` or `access.operations` to securely block record updates
1246
+ */
835
1247
  disableUpdate?: boolean | ((operation: "create" | "update", record: StokerRecord) => boolean | Promise<boolean>)
1248
+ /**
1249
+ * A hook that fires when the record form is opened. When the "create" form is opened from
1250
+ * within another record's relation list, the parent collection and parent record are provided
1251
+ */
836
1252
  onFormOpen?: (
837
1253
  operation: "create" | "update",
838
1254
  record: StokerRecord,
839
1255
  parentCollection?: StokerCollection,
840
1256
  parentRecord?: StokerRecord,
841
1257
  ) => void | Promise<void>
1258
+ /** A hook that fires whenever the form is updated. Optionally return an object with field updates */
842
1259
  onChange?: (
843
1260
  operation: "create" | "update",
844
1261
  record: StokerRecord,
845
1262
  originalRecord: StokerRecord,
846
1263
  ) => Partial<StokerRecord> | void | Promise<Partial<StokerRecord> | void>
1264
+ /** Override the default behaviour of opening the add record form */
847
1265
  addRecordButtonOverride?: (record?: StokerRecord) => void | Promise<void>
1266
+ /** Disable the date range selector for the user */
848
1267
  disableRangeSelector?: boolean | (() => boolean)
1268
+ /** Load data for use in your computed fields, once per query */
849
1269
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
850
1270
  retriever?: () => any | Promise<any>
851
1271
  assignable?: Assignable[] | (() => Assignable[] | Promise<Assignable[]>)
1272
+ /** Custom actions that will be shown in a dropdown menu on the list page */
852
1273
  customListActions?: CustomListAction[] | (() => CustomListAction[] | Promise<CustomListAction[]>)
1274
+ /** Options for file uploads */
853
1275
  fileOptions?: FileOptions | (() => FileOptions | Promise<FileOptions>)
854
1276
  }
855
1277
  export interface CollectionAdminCache {
@@ -907,8 +1329,15 @@ export interface CollectionAdminCache {
907
1329
  fileOptions?: FileOptions
908
1330
  }
909
1331
 
1332
+ /** Custom code config for the field, including hooks and server access control */
910
1333
  export interface FieldCustom extends Hooks {
1334
+ /** Calculate an initial value for this field when creating the record */
911
1335
  initialValue?: unknown | ((record?: StokerRecord) => unknown | Promise<unknown>)
1336
+ /**
1337
+ * Define additional access control using code on the server. Only relevant if
1338
+ * `access.serverWriteOnly` is set to `true`. Return a boolean indicating whether or not
1339
+ * the access check passed. This code is not sent to the client
1340
+ */
912
1341
  serverAccess?: {
913
1342
  read?: (permissions: StokerPermissions, user: UserRecord, record?: StokerRecord) => boolean | Promise<boolean>
914
1343
  create?: (permissions: StokerPermissions, user: UserRecord, record: StokerRecord) => boolean | Promise<boolean>
@@ -921,68 +1350,121 @@ export interface FieldCustom extends Hooks {
921
1350
  }
922
1351
  }
923
1352
 
1353
+ /** Display a conditional description message under the field in the Admin UI */
924
1354
  export interface FieldDescription {
1355
+ /** The description message to display */
925
1356
  message: string | ((record?: StokerRecord) => string | Promise<string>)
1357
+ /** Show or hide the description */
926
1358
  condition?: boolean | ((record?: StokerRecord) => boolean | Promise<boolean>)
927
1359
  }
928
1360
 
1361
+ /** Config for a coordinates field, providing the starting location and zoom for the map */
929
1362
  export interface LocationFieldAdmin {
1363
+ /** The starting coordinates for the map */
930
1364
  center: {
931
1365
  lat: number
932
1366
  lng: number
933
1367
  }
1368
+ /** The starting zoom value for the map */
934
1369
  zoom: number
935
1370
  }
936
1371
 
1372
+ /** An icon shown for the field on the form page */
937
1373
  export interface FormFieldIcon {
1374
+ /** The icon component */
938
1375
  component: React.FC
1376
+ /** Additional Tailwind classes for the icon */
939
1377
  className?: string
940
1378
  }
941
1379
 
1380
+ /** Admin UI config for the field */
942
1381
  export interface FieldAdmin {
1382
+ /** A human-readable name for the field. Only necessary if `name` is not human-readable */
943
1383
  label?: string | (() => string)
1384
+ /** Override the field name shown in the list view */
944
1385
  listLabel?: string | (() => string)
1386
+ /** An icon that will be shown for the field on the form page */
945
1387
  icon?: FormFieldIcon | (() => FormFieldIcon | Promise<FormFieldIcon>)
1388
+ /**
1389
+ * Show or hide the field in the list view and on the form page. The list method receives
1390
+ * the parent collection and parent record when shown on a relation list page. The form
1391
+ * method receives `isExport` as `true` during CSV export operations
1392
+ */
946
1393
  condition?: {
947
1394
  list?: boolean | ((parentCollection?: CollectionSchema, parentRecord?: StokerRecord) => boolean)
948
1395
  form?: boolean | ((operation?: "create" | "update", record?: StokerRecord, isExport?: boolean) => boolean)
949
1396
  }
1397
+ /** Return `true` to set this as a read-only field in the Admin UI */
950
1398
  readOnly?: boolean | ((operation?: "create" | "update", record?: StokerRecord) => boolean)
1399
+ /** Display a conditional description message under the field in the Admin UI */
951
1400
  description?: FieldDescription
1401
+ /** Set to `true` on a String field to make it a textarea */
952
1402
  textarea?: boolean | (() => boolean | Promise<boolean>)
1403
+ /** Set to `true` on a String field with `values` to make it a radio group */
953
1404
  radio?: boolean | (() => boolean | Promise<boolean>)
1405
+ /** Set to `true` on a String field with `values` to make it a button group */
954
1406
  buttonGroup?: boolean | (() => boolean | Promise<boolean>)
1407
+ /** Set to `true` on a Boolean field to make it a switch */
955
1408
  switch?: boolean | (() => boolean | Promise<boolean>)
1409
+ /** Set to `true` on a Timestamp field to make it a month picker */
956
1410
  month?: boolean | (() => boolean | Promise<boolean>)
1411
+ /** Set to `true` on a Number field to make it a slider */
957
1412
  slider?: boolean | (() => boolean | Promise<boolean>)
1413
+ /** Set to `true` on a Map field to make it a rich text field */
958
1414
  richText?: boolean | (() => boolean | Promise<boolean>)
1415
+ /** Set on an Array field to make it a coordinates field. Provide starting location coordinates and zoom */
959
1416
  location?: LocationFieldAdmin | (() => LocationFieldAdmin | Promise<LocationFieldAdmin>)
1417
+ /** Set to `true` on a String field to make it a time field, or on a Timestamp field to make it a datetime field */
960
1418
  time?: boolean | (() => boolean)
1419
+ /** Set on a String field to make it an image field. Images can be uploaded, or selected from files uploaded to the record */
961
1420
  image?: boolean | (() => boolean)
1421
+ /** For Array fields. Provide an array of Tailwind classes to have values appear as colored badges. The order must match the field's `values` array */
962
1422
  tags?: string[] | (() => string[])
1423
+ /** Set to `true` to have the field live-update on the form page when the record is updated remotely */
963
1424
  live?: boolean | (() => boolean | Promise<boolean>)
1425
+ /** Set the position of the field in the list and the form. Defaults to the position of the field in the fields array */
964
1426
  column?: boolean | number | (() => boolean | number)
1427
+ /** Set on a String field to make it a badge. Return a Tailwind class specifying the color for the badge */
965
1428
  badge?: boolean | string | ((record?: StokerRecord) => boolean | string)
1429
+ /** The screen size at which the field should be hidden from the list view. This is useful for responsiveness */
966
1430
  hidden?: "sm" | "md" | "lg" | "xl" | "2xl" | ((record?: StokerRecord) => "sm" | "md" | "lg" | "xl" | "2xl")
1431
+ /** Set to `true` on a String field to make it italic */
967
1432
  italic?: boolean | ((record?: StokerRecord) => boolean)
1433
+ /** Set to a currency symbol to make the field a currency */
968
1434
  currency?: string | ((record?: StokerRecord) => string)
1435
+ /** The returned value will be used for sorting in the list view */
969
1436
  sort?: (record?: StokerRecord) => unknown
1437
+ /** Set to `true` to exclude this field from CSV export */
970
1438
  noExport?: boolean | (() => boolean)
1439
+ /** Set on an Array or relational field to specify the separator to be used between items in CSV export. Defaults to `", "` */
971
1440
  exportSeparator?: string | (() => string)
1441
+ /**
1442
+ * Set to `true` to skip required validation on the form page. This is useful if the required
1443
+ * field value will be set after the form has been submitted, for example in `custom.initialValue` or a hook
1444
+ */
972
1445
  skipFormRequiredValidation?: boolean | (() => boolean)
1446
+ /** Conditionally set a field to "required". This overrides `required`, however `required` will still be enforced on the server if present */
973
1447
  overrideFormRequiredValidation?: (
974
1448
  operation: "create" | "update",
975
1449
  record?: StokerRecord,
976
1450
  originalRecord?: StokerRecord,
977
1451
  ) => boolean
1452
+ /** Filter the options in the dropdown for String or Number fields with `values` set */
978
1453
  filterValues?: (value: string | number, parentCollection: CollectionSchema, parentRecord?: StokerRecord) => boolean
1454
+ /** Filter the options in the dropdown for relational fields. Only works when `preloadCache` is enabled for the user's role */
979
1455
  filterResults?: (result: StokerRecord, parentCollection: CollectionSchema, parentRecord?: StokerRecord) => boolean
1456
+ /** Modify the results in the dropdown for relational fields */
980
1457
  modifyResultTitle?: (
981
1458
  record: StokerRecord,
982
1459
  parentCollection: CollectionSchema,
983
1460
  parentRecord?: StokerRecord,
984
1461
  ) => string
1462
+ /** Modify the displayed value for the field, for example when shown in the list view or when read-only on a form */
985
1463
  modifyDisplayValue?: (record?: StokerRecord, context?: "card" | "form" | "list" | "export") => unknown
1464
+ /**
1465
+ * Return a custom component to be used in the list view. Set `receiveClick` to `true` to have the
1466
+ * component receive the click and override the default behaviour of navigating to the record page
1467
+ */
986
1468
  customListView?: (
987
1469
  record?: StokerRecord,
988
1470
  parentCollection?: CollectionSchema,
@@ -994,18 +1476,34 @@ export interface FieldAdmin {
994
1476
  receiveClick?: boolean
995
1477
  }
996
1478
  | undefined
1479
+ /** When using include fields on a relation field, return `true` to force the full relation record to be loaded (on the form page only) */
997
1480
  queryFullRecord?: boolean | (() => boolean)
1481
+ /** Display a Computed field value as rich text */
998
1482
  asRichText?: boolean | (() => boolean)
999
1483
  }
1000
1484
 
1485
+ /**
1486
+ * Grant users access to the specified field in the related collection. This lets users select
1487
+ * values from a dropdown without giving them full access to the related collection
1488
+ */
1001
1489
  export interface DependencyField {
1490
+ /** The field in the related collection to grant access to */
1002
1491
  field: string
1492
+ /** The roles to grant access to */
1003
1493
  roles: StokerRole[]
1004
1494
  }
1495
+ /** Enforce the relational integrity of the field. For example, ensure that the record's "Site" is actually related to the record's "Company" */
1005
1496
  export interface EnforceHierarchy {
1497
+ /** Another relational field in the collection that is above the current field in the relational hierarchy */
1006
1498
  field: string
1499
+ /** The field in the related collection above that links to the same collection as the current field */
1007
1500
  recordLinkField: string
1008
1501
  }
1502
+ /**
1503
+ * Place a single field exemption on the field in Firestore. If `indexExemption` is set at the
1504
+ * collection level, this option will re-enable indexing for the field. Consider exempting
1505
+ * incrementally increasing monotonic fields, large String fields, Map fields and Array fields
1506
+ */
1009
1507
  export interface SingleFieldExemption {
1010
1508
  queryScope: "COLLECTION" | "COLLECTION_GROUP"
1011
1509
  order?: "ASCENDING" | "DESCENDING"
@@ -1048,11 +1546,23 @@ export interface FieldAccessGroupReference {
1048
1546
  group: string
1049
1547
  }
1050
1548
 
1549
+ /** Properties that can be set on any field type */
1051
1550
  export interface StandardField {
1551
+ /** The name of the field. It must not have spaces (use underscores). You can set a human-readable name in `admin.label` */
1052
1552
  name: string
1553
+ /** A description for the field for LLMs. Only relevant if `ai` is configured */
1053
1554
  description?: string | (() => string | Promise<string>)
1054
1555
 
1556
+ /**
1557
+ * Place a single field exemption on the field in Firestore. If `indexExemption` is set at
1558
+ * the collection level, this option will re-enable indexing for the field
1559
+ */
1055
1560
  singleFieldExemption?: SingleFieldExemption[] | boolean
1561
+ /**
1562
+ * Specifies that this field will be used for sorting. Not required if all user roles have
1563
+ * `preloadCache` or `access.serverReadOnly` enabled (sorting is automatic in these cases).
1564
+ * Set to `true` to sort by "asc" and "desc" for all user roles, or provide more granular config
1565
+ */
1056
1566
  sorting?:
1057
1567
  | boolean
1058
1568
  | {
@@ -1060,17 +1570,45 @@ export interface StandardField {
1060
1570
  roles?: StokerRole[]
1061
1571
  }
1062
1572
 
1573
+ /** Set to `true` if the field is a required field */
1063
1574
  required?: boolean
1575
+ /** Set to `true` if the field is a nullable field */
1064
1576
  nullable?: boolean
1065
1577
 
1578
+ /**
1579
+ * In auth collections, set to `true` to add the field to the linked user's auth token.
1580
+ * Be sure to control access to auth token fields using `restrictCreate` and `restrictUpdate`
1581
+ */
1066
1582
  saveToAuthToken?: boolean
1067
1583
 
1584
+ /**
1585
+ * Controls which users can access the field. Provide an array of user roles for static access,
1586
+ * or reference a field access group with `{ group }` for conditional access.
1587
+ * Warning: omitting the access property altogether allows access by ALL roles
1588
+ */
1068
1589
  access?: StokerRole[] | FieldAccessGroupReference
1590
+ /**
1591
+ * Set to `true` to prevent this field from being included when the record is created.
1592
+ * Alternatively, provide an array of user roles that CAN provide the field when creating
1593
+ * a record, or a FieldAccessCondition for even more granular access control
1594
+ */
1069
1595
  restrictCreate?: StokerRole[] | boolean | FieldAccessCondition
1596
+ /**
1597
+ * Set to `true` to prevent this field from being changed when the record is updated.
1598
+ * Alternatively, provide an array of user roles that CAN change the field when updating
1599
+ * a record, or a FieldAccessCondition for even more granular access control
1600
+ */
1070
1601
  restrictUpdate?: StokerRole[] | boolean | FieldAccessCondition
1602
+ /**
1603
+ * Skip Firestore Security Rules validation for this field. This can help to keep the size of
1604
+ * the security ruleset down. Validation will be performed post-write in a Cloud Function, and
1605
+ * you will receive an email if invalid data has been submitted
1606
+ */
1071
1607
  skipRulesValidation?: boolean
1072
1608
 
1609
+ /** Custom code config for the field, including hooks and server access control */
1073
1610
  custom?: FieldCustom
1611
+ /** Admin UI config for the field */
1074
1612
  admin?: FieldAdmin
1075
1613
  }
1076
1614
  export interface BooleanField extends StandardField {
@@ -1078,44 +1616,79 @@ export interface BooleanField extends StandardField {
1078
1616
  }
1079
1617
  export interface StringField extends StandardField {
1080
1618
  type: "String"
1619
+ /** An optional list of values. This will result in a dropdown list being shown in the Admin UI */
1081
1620
  values?: string[]
1621
+ /**
1622
+ * Set to `true` to make the field a unique field. Be sparing with this option unless
1623
+ * `access.serverWriteOnly` is set to `true`. Case is ignored when determining whether or not
1624
+ * a value is a duplicate. Unique field values are NOT freed up when a record is soft-deleted
1625
+ */
1082
1626
  unique?: boolean
1083
1627
 
1628
+ /** Specify a fixed length for the field */
1084
1629
  length?: number
1630
+ /** Specify a minimum length for the field */
1085
1631
  minlength?: number
1632
+ /** Specify a maximum length for the field */
1086
1633
  maxlength?: number
1634
+ /** Specify a regex pattern for the field */
1087
1635
  pattern?: string
1088
1636
 
1637
+ /** Set to `true` if the field is an email address. Only validated server-side if `access.serverWriteOnly` is `true` */
1089
1638
  email?: boolean
1639
+ /** Set to `true` if the field is a url. Only validated server-side if `access.serverWriteOnly` is `true` */
1090
1640
  url?: boolean
1641
+ /** Set to `true` if the field is an emoji. Only validated server-side if `access.serverWriteOnly` is `true` */
1091
1642
  emoji?: boolean
1643
+ /** Set to `true` if the field is a UUID. Only validated server-side if `access.serverWriteOnly` is `true` */
1092
1644
  uuid?: boolean
1645
+ /** Set to `true` if the field is an IP address. Only validated server-side if `access.serverWriteOnly` is `true` */
1093
1646
  ip?: boolean
1094
1647
  }
1095
1648
  export interface NumberField extends StandardField {
1096
1649
  type: "Number"
1650
+ /** An optional list of values. This will result in a dropdown list being shown in the Admin UI */
1097
1651
  values?: number[]
1652
+ /**
1653
+ * Set to `true` to make the field a unique field. Be sparing with this option unless
1654
+ * `access.serverWriteOnly` is set to `true`. Unique field values are NOT freed up when a record is soft-deleted
1655
+ */
1098
1656
  unique?: boolean
1099
1657
 
1658
+ /**
1659
+ * Set to `true` to make the field an auto-incremented number. Auto-incremented numbers are
1660
+ * written by a Cloud Function after the record has been saved to the server, so numbers for
1661
+ * offline writes won't appear until the user has reconnected
1662
+ */
1100
1663
  autoIncrement?: boolean
1664
+ /** Set the maximum number of decimal places for this field */
1101
1665
  decimal?: number
1102
1666
 
1667
+ /** Set the maximum number value for this field */
1103
1668
  max?: number
1669
+ /** Set the minimum number value for this field */
1104
1670
  min?: number
1105
1671
  }
1106
1672
  export interface TimestampField extends StandardField {
1107
1673
  type: "Timestamp"
1674
+ /** An optional list of dates in milliseconds format. This will result in a dropdown list being shown in the Admin UI */
1108
1675
  values?: number[]
1109
1676
 
1677
+ /** Set the maximum milliseconds value for this field */
1110
1678
  max?: number
1679
+ /** Set the minimum milliseconds value for this field */
1111
1680
  min?: number
1112
1681
  }
1113
1682
  export interface ArrayField extends StandardField {
1114
1683
  type: "Array"
1684
+ /** A list of values. A dropdown selector will be shown in the Admin UI */
1115
1685
  values?: string[]
1116
1686
 
1687
+ /** Set the exact required length for this field */
1117
1688
  length?: number
1689
+ /** Set the minimum length for this field */
1118
1690
  minlength?: number
1691
+ /** Set the maximum length for this field */
1119
1692
  maxlength?: number
1120
1693
  }
1121
1694
  export interface MapField extends StandardField {
@@ -1123,17 +1696,42 @@ export interface MapField extends StandardField {
1123
1696
  }
1124
1697
  export interface RelationField extends StandardField {
1125
1698
  type: "OneToOne" | "OneToMany" | "ManyToOne" | "ManyToMany"
1699
+ /** The collection for the relational field */
1126
1700
  collection: StokerCollection
1701
+ /**
1702
+ * Enable a two-way relation. Provide the name of a relational field in the target collection
1703
+ * to link with. Requires `access.serverWriteOnly`
1704
+ */
1127
1705
  twoWay?: string
1706
+ /**
1707
+ * Save fields from the related record to the target record. Field values will be denormalized
1708
+ * and will automatically update when the source record is updated. Updates are written by a
1709
+ * Cloud Function, so updates made in offline mode won't appear until the user has reconnected
1710
+ */
1128
1711
  includeFields?: string[]
1712
+ /** Choose one of the `includeFields` values to act as the title field for the related record */
1129
1713
  titleField?: string
1714
+ /** Set to `true` to preserve relation data when the related record is deleted. Warning: this may have privacy implications */
1130
1715
  preserve?: boolean
1716
+ /**
1717
+ * Set to `true` to allow users to write any value to the relational field. By default, users
1718
+ * can only write values for records that they have access to. Warning: this may have security implications
1719
+ */
1131
1720
  writeAny?: boolean
1721
+ /**
1722
+ * Grant users access to the specified fields in the related collection. This lets users select
1723
+ * values from a dropdown without giving them full access to the related collection
1724
+ */
1132
1725
  dependencyFields?: DependencyField[]
1726
+ /** Enforce the relational integrity of the field. For example, ensure that the record's "Site" is actually related to the record's "Company" */
1133
1727
  enforceHierarchy?: EnforceHierarchy
1728
+ /** Set the minimum number of relations for this field */
1134
1729
  min?: number
1730
+ /** Set the maximum number of relations for this field */
1135
1731
  max?: number
1732
+ /** Set the exact number of relations required for this field */
1136
1733
  length?: number
1734
+ /** Firestore where() query constraints that will be applied when retrieving records for the dropdown selector */
1137
1735
  constraints?: [string, "==" | "in", unknown][]
1138
1736
  }
1139
1737
  export interface EmbeddingField extends StandardField {
@@ -1141,9 +1739,15 @@ export interface EmbeddingField extends StandardField {
1141
1739
  }
1142
1740
  export interface ComputedField extends StandardField {
1143
1741
  type: "Computed"
1742
+ /**
1743
+ * Calculates the value for the field. When using `getSome` or `subscribeMany`, `retrieverData`
1744
+ * will return the data provided by the collection's `retriever` function. This lets you load
1745
+ * data sets for your computed field formulas once per query
1746
+ */
1144
1747
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1145
1748
  formula: (record: StokerRecord, retrieverData?: any) => string | number | Promise<string | number>
1146
1749
  }
1750
+ /** A field in a Stoker collection */
1147
1751
  export type CollectionField =
1148
1752
  | BooleanField
1149
1753
  | StringField
@@ -1155,11 +1759,15 @@ export type CollectionField =
1155
1759
  | EmbeddingField
1156
1760
  | ComputedField
1157
1761
 
1762
+ /** Make a system field (i.e. `Created_At`, `Last_Write_By`) accessible to the given roles */
1158
1763
  export interface RoleSystemField {
1764
+ /** The name of the system field */
1159
1765
  field: string
1766
+ /** The roles that can access the field */
1160
1767
  roles?: StokerRole[]
1161
1768
  }
1162
1769
 
1770
+ /** The customization (custom code and Admin UI config) for a collection */
1163
1771
  export interface CollectionCustomization {
1164
1772
  custom?: CollectionCustom
1165
1773
  admin?: CollectionAdmin
@@ -1171,31 +1779,61 @@ export interface CollectionCustomization {
1171
1779
  }[]
1172
1780
  }
1173
1781
 
1782
+ /**
1783
+ * Advanced. Define fields that will be indexed for querying. Only relevant if you are using
1784
+ * Stoker as a headless CMS. This option is handled automatically by the Admin UI
1785
+ */
1174
1786
  export interface Query {
1787
+ /** The name of the field */
1175
1788
  field: string
1789
+ /** Set to `true` if the field is a Timestamp field */
1176
1790
  range?: boolean
1791
+ /** Set to `true` if the field needs to be indexed independently of sorting */
1177
1792
  standalone?: boolean
1793
+ /** The user roles that will run the query */
1178
1794
  roles?: StokerRole[]
1179
1795
  }
1180
1796
 
1797
+ /** A "child list" that will appear in the Admin UI for records in this collection, i.e. lists of related "Sites" on a "Clients" record page */
1181
1798
  export interface RelationList {
1799
+ /** The collection for the relation list */
1182
1800
  collection: StokerCollection
1801
+ /** The field in the current collection that relates to the collection above */
1183
1802
  field: string
1803
+ /** The roles that can see this relation list */
1184
1804
  roles?: StokerRole[]
1805
+ /** Firestore constraints to apply to the relation list query */
1185
1806
  constraints?: [string, "==" | "in", unknown][]
1807
+ /** When `preloadCache.range` is enabled, setting this option will ignore the range restrictions and load all records available for the relation list */
1186
1808
  loadAll?: boolean
1809
+ /** Show metrics above the list in the Admin UI */
1187
1810
  showMetrics?: boolean
1811
+ /** A list of filters to show in the LHS sidebar when the relation list is active */
1188
1812
  showFilters?: string[]
1189
1813
  }
1190
1814
 
1815
+ /** The schema for a Stoker collection. Each collection represents a collection in Cloud Firestore, and a page in the Admin UI */
1191
1816
  export interface CollectionSchema {
1817
+ /** The names for the collection. Names must start with a capital letter and contain only letters, digits, and underscores */
1192
1818
  labels: CollectionLabels
1819
+ /** Access control config for the collection */
1193
1820
  access: CollectionAccess
1821
+ /** The fields for the collection */
1194
1822
  fields: (CollectionField | RelationField)[]
1823
+ /** The field in the collection that will be used as the record's title, i.e. "Name" */
1195
1824
  recordTitleField: string
1196
1825
 
1826
+ /** Set to `true` if this collection is a "users" collection. Records in the collection can then be assigned access credentials */
1197
1827
  auth?: boolean
1828
+ /**
1829
+ * Set to `true` if the collection will only have one record / page, i.e. "Settings".
1830
+ * No list page will be shown in the Admin UI; the user will be sent straight to the form page
1831
+ */
1198
1832
  singleton?: boolean
1833
+ /**
1834
+ * Advanced. Set to the collection's parent collection if this collection will be a subcollection
1835
+ * in Firestore. Subcollections are not currently supported in the Admin UI
1836
+ */
1199
1837
  parentCollection?: StokerCollection
1200
1838
 
1201
1839
  /**
@@ -1204,34 +1842,80 @@ export interface CollectionSchema {
1204
1842
  */
1205
1843
  fieldAccessGroups?: Record<string, FieldAccessCondition>
1206
1844
 
1845
+ /**
1846
+ * Preload data for the collection on app startup. Preloaded data is cached and is available
1847
+ * for the lifetime of the session. Highly recommended for time series data
1848
+ */
1207
1849
  preloadCache?: PreloadCache
1850
+ /** Enable soft-delete for this collection */
1208
1851
  softDelete?: {
1852
+ /** The name of a Boolean field. This field will be set to `true` when the record is soft-deleted */
1209
1853
  archivedField: string
1854
+ /** The name of a Timestamp field. This field will be set to the current time when the record is soft-deleted */
1210
1855
  timestampField: string
1856
+ /** The number of days after which soft-deleted records will be permanently deleted */
1211
1857
  retentionPeriod: number
1212
1858
  }
1213
1859
 
1860
+ /** Advanced. Define fields that will be indexed for querying. Only relevant if you are using Stoker as a headless CMS */
1214
1861
  queries?: Query[]
1862
+ /** Define "child lists" that will appear in the Admin UI for records in this collection */
1215
1863
  relationLists?: RelationList[]
1864
+ /** Set to `true` to allow fields that are not defined in the schema to be written to records in the collection */
1216
1865
  allowSchemalessFields?: boolean
1866
+ /**
1867
+ * Set to `true` to enable the write log for this collection. Every write to a record will be
1868
+ * logged in Firestore, creating a history that can be used for data recovery and audit purposes
1869
+ */
1217
1870
  enableWriteLog?: boolean
1871
+ /**
1872
+ * Set to `true` to preserve write log entries for deleted records. If not enabled, all write
1873
+ * log entries for a record will be deleted on record delete
1874
+ */
1218
1875
  preserveWriteLog?: boolean
1876
+ /**
1877
+ * An array of field names. These fields will be searchable. For collections without
1878
+ * `preloadCache` or `serverReadOnly` set to `true`, you will need to set up Algolia
1879
+ */
1219
1880
  fullTextSearch?: string[]
1881
+ /**
1882
+ * The name of a Timestamp field containing an automatic deletion date for the record,
1883
+ * i.e. "Expires_At". Warning: denormalized data is not currently deleted by TTL policies
1884
+ */
1220
1885
  ttl?: string
1886
+ /**
1887
+ * Set to `true` to exempt this collection from Firestore indexing. Indexes that are required
1888
+ * for your app to function will be re-added automatically. Improves performance and reduces
1889
+ * costs, but may prevent queries outside of the standard queries used by your app
1890
+ */
1221
1891
  indexExemption?: boolean
1892
+ /** Make system fields (i.e. `Created_At`, `Last_Write_By`) accessible to your app's users */
1222
1893
  roleSystemFields?: RoleSystemField[]
1894
+ /**
1895
+ * Set to `true` to skip Firestore Security Rules validation of writes, for collections that hit
1896
+ * the limit of 1000 expressions per request. Validation will be run post-write in a Cloud
1897
+ * Function, and you will get an email if invalid data is submitted
1898
+ */
1223
1899
  skipRulesValidation?: boolean
1900
+ /** Enable an AI chat bot for the collection. The chat bot uses Retrieval Augmented Generation (RAG) to converse with the user about the data in the collection */
1224
1901
  ai?: {
1902
+ /** Set to `true` to save embeddings for records in this collection. Requires the `custom.setEmbedding` hook */
1225
1903
  embedding?: boolean
1226
1904
  chat?: {
1905
+ /** The name for the chat bot */
1227
1906
  name: string
1907
+ /** The number of records the LLM should retrieve for context */
1228
1908
  defaultQueryLimit?: number
1909
+ /** The roles that can view the chat bot. Warning: Only assign AI chat access to roles that have access to ALL fields used to calculate embeddings */
1229
1910
  roles: StokerRole[]
1230
1911
  }
1231
1912
  }
1913
+ /** The priority of this collection when seeding test data using `stoker seed-data` */
1232
1914
  seedOrder?: number
1233
1915
 
1916
+ /** Custom code config for the collection, including hooks and server access control */
1234
1917
  custom?: CollectionCustom
1918
+ /** Admin UI config for the collection */
1235
1919
  admin?: CollectionAdmin
1236
1920
  }
1237
1921