@stoker-platform/types 0.5.69 → 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,147 +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
266
  IndividualEntityParentFilter | ParentEntityParentFilter | ParentPropertyEntityParentFilter
180
267
 
268
+ /** Define which roles can perform which CRUD operations for the collection */
181
269
  export interface AccessOperations {
270
+ /** Set to `true` or an array of user roles to allow disabling of access in the user's profile */
182
271
  assignable?: boolean | StokerRole[]
272
+ /** Roles that can read records in the collection */
183
273
  read?: StokerRole[]
274
+ /** Roles that can create records in the collection */
184
275
  create?: StokerRole[]
276
+ /** Roles that can update records in the collection */
185
277
  update?: StokerRole[]
278
+ /** Roles that can delete records in the collection */
186
279
  delete?: StokerRole[]
187
280
  }
188
281
 
282
+ /** The roles that must be granted each file operation */
189
283
  export interface AccessFilesAssignmentRoles {
284
+ /** Roles for read access to the file */
190
285
  read?: StokerRole[]
286
+ /** Roles for update access to the file */
191
287
  update?: StokerRole[]
288
+ /** Roles for delete access to the file */
192
289
  delete?: StokerRole[]
193
290
  }
291
+ /** File access assignment rules for a user role */
194
292
  export interface AccessFilesAssignment {
293
+ /** Access assignments the user may optionally grant */
195
294
  optional?: AccessFilesAssignmentRoles
295
+ /** Access assignments the user must grant */
196
296
  required?: AccessFilesAssignmentRoles
197
297
  }
298
+ /** Access rules for file uploads */
198
299
  export interface AccessFiles {
300
+ /** Define the user roles that the user must assign access to for each file */
199
301
  assignment?: {
200
302
  [role: StokerRole]: AccessFilesAssignment
201
303
  }
304
+ /** Enforce Firebase Storage metadata constraints, i.e. `{ size: " <= (5 * 1024 * 1024)" }` */
202
305
  metadata?: {
203
306
  [key: string]: string
204
307
  }
308
+ /** Enforce custom metadata constraints */
205
309
  customMetadata?: {
206
310
  [key: string]: string
207
311
  }
208
312
  }
209
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) */
210
315
  export interface EntityRestrictions {
316
+ /** User roles for which entity restrictions can be disabled for individual users */
211
317
  assignable?: StokerRole[]
318
+ /** The entity restrictions to apply */
212
319
  restrictions?: EntityRestriction[]
320
+ /** Apply entity restrictions from a parent collection onto this collection, i.e. "All Jobs on Sites for Company X" */
213
321
  parentFilters?: EntityParentFilter[]
214
322
  }
323
+ /** Access control config for the collection */
215
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
+ */
216
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
+ */
217
336
  serverWriteOnly?: boolean
337
+ /** Set to `true` to write custom Firestore Security Rules for the collection, at `firebase-rules/firestore.custom.rules` */
218
338
  customSecurityRules?: boolean
339
+ /** Set to `true` to write custom Firebase Storage Rules for the collection */
219
340
  customStorageRules?: boolean
341
+ /** Restrict a user's access to records with certain attributes */
220
342
  attributeRestrictions?: AttributeRestriction[]
343
+ /** Explicitly define which specific records or groups of records can be accessed by a user */
221
344
  entityRestrictions?: EntityRestrictions
345
+ /** Restrict which permissions a user role can assign to other user roles */
222
346
  permissionWriteRestrictions?: PermissionWriteRestriction[]
347
+ /** Define which roles can perform which CRUD operations for the collection */
223
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
+ */
224
355
  auth?: { roles: StokerRole[]; assignable?: StokerRole[] }
356
+ /** Define access rules for file uploads */
225
357
  files?: AccessFiles
226
358
  }
227
359
 
360
+ /** Arguments for the preOperation hook, which fires before a read or write operation */
228
361
  export type PreOperationHookArgs = {
229
362
  operation: "read" | "create" | "update" | "delete"
230
363
  data?: StokerRecord
@@ -234,6 +367,7 @@ export type PreOperationHookArgs = {
234
367
  batch?: WriteBatch
235
368
  originalRecord?: StokerRecord
236
369
  }
370
+ /** Arguments for the preRead hook, which fires before a read operation */
237
371
  export type PreReadHookArgs = {
238
372
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
239
373
  context: any
@@ -241,6 +375,7 @@ export type PreReadHookArgs = {
241
375
  multiple?: boolean
242
376
  listener?: boolean
243
377
  }
378
+ /** Arguments for the postRead hook, which fires after a read operation */
244
379
  export type PostReadHookArgs = {
245
380
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
246
381
  context: any
@@ -248,7 +383,9 @@ export type PostReadHookArgs = {
248
383
  record?: StokerRecord
249
384
  listener?: boolean
250
385
  }
386
+ /** Arguments for the preDuplicate hook, which fires before a duplicate operation in the Admin UI */
251
387
  export type PreDuplicateHookArgs = { data: Partial<StokerRecord> }
388
+ /** Arguments for the preValidate hook, which fires at write validation time */
252
389
  export type PreValidateHookArgs = {
253
390
  operation: "create" | "update"
254
391
  data: StokerRecord
@@ -257,6 +394,7 @@ export type PreValidateHookArgs = {
257
394
  batch?: WriteBatch
258
395
  originalRecord?: StokerRecord
259
396
  }
397
+ /** Arguments for the preWrite hook, which fires before a write operation */
260
398
  export type PreWriteHookArgs = {
261
399
  operation: "create" | "update" | "delete"
262
400
  data: StokerRecord
@@ -266,6 +404,7 @@ export type PreWriteHookArgs = {
266
404
  batch?: WriteBatch
267
405
  originalRecord?: StokerRecord
268
406
  }
407
+ /** Arguments for the postWrite hook, which fires after a write operation */
269
408
  export type PostWriteHookArgs = {
270
409
  operation: "create" | "update" | "delete"
271
410
  data: StokerRecord
@@ -275,6 +414,7 @@ export type PostWriteHookArgs = {
275
414
  retry?: boolean
276
415
  originalRecord?: StokerRecord
277
416
  }
417
+ /** Arguments for the postWriteError hook, which fires when a write operation encounters an error */
278
418
  export type PostWriteErrorHookArgs = {
279
419
  operation: "create" | "update" | "delete"
280
420
  data: StokerRecord
@@ -287,6 +427,7 @@ export type PostWriteErrorHookArgs = {
287
427
  retries?: number
288
428
  originalRecord?: StokerRecord
289
429
  }
430
+ /** Arguments for the postOperation hook, which fires after a read or write operation */
290
431
  export type PostOperationHookArgs = {
291
432
  operation: "read" | "create" | "update" | "delete"
292
433
  data?: StokerRecord
@@ -297,12 +438,14 @@ export type PostOperationHookArgs = {
297
438
  originalRecord?: StokerRecord
298
439
  }
299
440
 
441
+ /** The access permissions assigned to an uploaded file */
300
442
  export type FilePermissions = {
301
443
  read?: string
302
444
  update?: string
303
445
  delete?: string
304
446
  }
305
447
 
448
+ /** Arguments for the preFileAdd hook, which fires before a file is uploaded */
306
449
  export type PreFileAddHookArgs = {
307
450
  record: StokerRecord
308
451
  fullPath: string
@@ -310,6 +453,7 @@ export type PreFileAddHookArgs = {
310
453
  permissions: FilePermissions
311
454
  }
312
455
 
456
+ /** Arguments for the preFileUpdate hook, which fires before a file is updated */
313
457
  export type PreFileUpdateHookArgs = {
314
458
  record: StokerRecord
315
459
  update:
@@ -322,6 +466,7 @@ export type PreFileUpdateHookArgs = {
322
466
  }
323
467
  }
324
468
 
469
+ /** Arguments for the postFileAdd hook, which fires after a file is uploaded */
325
470
  export type PostFileAddHookArgs = {
326
471
  record: StokerRecord
327
472
  fullPath: string
@@ -329,6 +474,7 @@ export type PostFileAddHookArgs = {
329
474
  permissions: FilePermissions
330
475
  }
331
476
 
477
+ /** Arguments for the postFileUpdate hook, which fires after a file is updated */
332
478
  export type PostFileUpdateHookArgs = {
333
479
  record: StokerRecord
334
480
  update:
@@ -341,10 +487,12 @@ export type PostFileUpdateHookArgs = {
341
487
  }
342
488
  }
343
489
 
490
+ /** Arguments for the postFileAddError hook, which fires when a file upload fails */
344
491
  export type PostFileAddErrorHookArgs = PostFileAddHookArgs & {
345
492
  error: unknown
346
493
  }
347
494
 
495
+ /** Arguments for the setEmbedding hook, which calculates an embedding value for the record */
348
496
  export type SetEmbeddingHookArgs = { record: StokerRecord }
349
497
 
350
498
  export type HookArgs =
@@ -364,24 +512,43 @@ export type HookArgs =
364
512
  | PostFileUpdateHookArgs
365
513
  | SetEmbeddingHookArgs
366
514
 
515
+ /** Fires before a read or write operation. Return `false` to cancel the operation */
367
516
  export type PreOperationHook = (args: PreOperationHookArgs) => boolean | void | Promise<boolean | void>
517
+ /** Fires before a read operation */
368
518
  export type PreReadHook = (args: PreReadHookArgs) => void | Promise<void>
519
+ /** Fires after a read operation */
369
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 */
370
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
+ */
371
528
  export type PreValidateHook = (
372
529
  args: PreValidateHookArgs,
373
530
  ) => { valid: boolean; message?: string } | Promise<{ valid: boolean; message?: string }>
531
+ /** Fires before a write operation. Return `false` to cancel the operation */
374
532
  export type PreWriteHook = (args: PreWriteHookArgs) => boolean | void | Promise<boolean | void>
533
+ /** Fires after a write operation */
375
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 */
376
536
  export type PostWriteErrorHook = (args: PostWriteErrorHookArgs) => boolean | void | Promise<boolean | void>
537
+ /** Fires after a read or write operation */
377
538
  export type PostOperationHook = (args: PostOperationHookArgs) => boolean | void | Promise<boolean | void>
378
539
 
540
+ /** Fires before a file is uploaded. Return `false` to cancel the operation */
379
541
  export type PreFileAddHook = (args: PreFileAddHookArgs) => boolean | void | Promise<boolean | void>
542
+ /** Fires before a file is updated. Return `false` to cancel the operation */
380
543
  export type PreFileUpdateHook = (args: PreFileUpdateHookArgs) => boolean | void | Promise<boolean | void>
544
+ /** Fires after a file is uploaded */
381
545
  export type PostFileAddHook = (args: PostFileAddHookArgs) => boolean | void | Promise<boolean | void>
546
+ /** Fires after a file is updated */
382
547
  export type PostFileUpdateHook = (args: PostFileUpdateHookArgs) => boolean | void | Promise<boolean | void>
548
+ /** Fires when a file upload fails */
383
549
  export type PostFileAddErrorHook = (args: PostFileAddErrorHookArgs) => void | Promise<void>
384
550
 
551
+ /** Calculate an embedding value for the record */
385
552
  export type SetEmbeddingHook = (args: SetEmbeddingHookArgs) => string | Promise<string>
386
553
 
387
554
  export type Hook =
@@ -402,45 +569,85 @@ export type Hook =
402
569
  | SetEmbeddingHook
403
570
 
404
571
  export type Hooks = {
572
+ /** Fires before a read or write operation. Return `false` to cancel the operation */
405
573
  preOperation?: PreOperationHook
574
+ /** Fires before a read operation */
406
575
  preRead?: PreReadHook
576
+ /** Fires after a read operation */
407
577
  postRead?: PostReadHook
578
+ /** Fires before a duplicate operation in the Admin UI. Return `false` to cancel the operation */
408
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
+ */
409
585
  preValidate?: PreValidateHook
586
+ /** Fires before a write operation. Return `false` to cancel the operation */
410
587
  preWrite?: PreWriteHook
588
+ /** Fires after a write operation */
411
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 */
412
591
  postWriteError?: PostWriteErrorHook
592
+ /** Fires after a read or write operation */
413
593
  postOperation?: PostOperationHook
594
+ /** Fires before a file is uploaded. Return `false` to cancel the operation */
414
595
  preFileAdd?: PreFileAddHook
596
+ /** Fires before a file is updated. Return `false` to cancel the operation */
415
597
  preFileUpdate?: PreFileUpdateHook
598
+ /** Fires after a file is uploaded */
416
599
  postFileAdd?: PostFileAddHook
600
+ /** Fires when a file upload fails */
417
601
  postFileAddError?: PostFileAddErrorHook
602
+ /** Fires after a file is updated */
418
603
  postFileUpdate?: PostFileUpdateHook
604
+ /** Calculate an embedding value for the record. Required for AI chat embeddings */
419
605
  setEmbedding?: SetEmbeddingHook
420
606
  }
421
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 */
422
609
  export interface PreloadCacheRange {
610
+ /** Timestamp fields the user can preload by */
423
611
  fields: string[]
612
+ /** Ranges of fields to preload, for example `["Start", "End"]`. Fields must also be listed in `fields` */
424
613
  ranges?: [string, string][]
614
+ /** Human-readable labels for the fields listed in `fields` */
425
615
  labels?: string[]
616
+ /** The default start date for the preloaded range */
426
617
  start: "Today" | "Week" | "Month" | "Year" | Date | number
618
+ /** Offset the default start date by this many days */
427
619
  startOffsetDays?: number
620
+ /** Offset the default start date by this many hours */
428
621
  startOffsetHours?: number
622
+ /** The default end date for the preloaded range */
429
623
  end?: Date | number
624
+ /** Offset the default end date by this many days */
430
625
  endOffsetDays?: number
626
+ /** Offset the default end date by this many hours */
431
627
  endOffsetHours?: number
628
+ /** Which selectors to show in the range picker */
432
629
  selector?: "range" | "week" | "month" | ("range" | "week" | "month")[]
433
630
  }
434
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
+ */
435
637
  export interface PreloadCache {
638
+ /** The user roles that will use the preload cache */
436
639
  roles: StokerRole[]
640
+ /** Whether to wait for related collections to load before signalling to the app that the collection is loaded */
437
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 */
438
643
  range?: PreloadCacheRange
644
+ /** Advanced. Additional Firestore constraints to apply to the preload cache */
439
645
  constraints?:
440
646
  | [string, WhereFilterOp, unknown][]
441
647
  | (() => [string, WhereFilterOp, unknown][] | Promise<[string, WhereFilterOp, unknown][]>)
442
648
  }
443
649
 
650
+ /** The initial preload cache state for each collection */
444
651
  export interface PreloadCacheInitial {
445
652
  [collection: string]: {
446
653
  roles: StokerRole[]
@@ -450,7 +657,13 @@ export interface PreloadCacheInitial {
450
657
  }
451
658
  }
452
659
 
660
+ /** Custom code config for the collection, including hooks and server access control */
453
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
+ */
454
667
  serverAccess?: {
455
668
  read?: (permissions: StokerPermissions, user: UserRecord, record?: StokerRecord) => boolean | Promise<boolean>
456
669
  create?: (permissions: StokerPermissions, user: UserRecord, record: StokerRecord) => boolean | Promise<boolean>
@@ -462,15 +675,21 @@ export interface CollectionCustom extends Hooks {
462
675
  ) => boolean | Promise<boolean>
463
676
  delete?: (permissions: StokerPermissions, user: UserRecord, record: StokerRecord) => boolean | Promise<boolean>
464
677
  }
678
+ /** Advanced. Additional Firestore constraints to apply to the preload cache */
465
679
  preloadCacheConstraints?:
466
680
  | [string, WhereFilterOp, unknown][]
467
681
  | (() => [string, WhereFilterOp, unknown][] | Promise<[string, WhereFilterOp, unknown][]>)
682
+ /** Advanced. Firestore OR query constraints to apply to the preload cache */
468
683
  preloadCacheOrQueries?:
469
684
  | [string, WhereFilterOp, unknown][]
470
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 */
471
687
  autoCorrectUnique?: boolean | (() => boolean | Promise<boolean>)
688
+ /** Return `true` to disable adding new records while offline */
472
689
  disableOfflineCreate?: boolean | (() => boolean | Promise<boolean>)
690
+ /** Return `true` to disable updating records while offline */
473
691
  disableOfflineUpdate?: boolean | (() => boolean | Promise<boolean>)
692
+ /** Return `true` to disable deleting records while offline */
474
693
  disableOfflineDelete?: boolean | (() => boolean | Promise<boolean>)
475
694
  }
476
695
  export interface CollectionCustomCache {
@@ -482,37 +701,64 @@ export interface CollectionCustomCache {
482
701
  disableOfflineDelete?: boolean
483
702
  }
484
703
 
704
+ /** Config for the list view */
485
705
  export interface ListConfig {
706
+ /** Limit which user roles can view the list */
486
707
  roles?: StokerRole[]
708
+ /** Customise the title for the list tab. Defaults to `"List"` */
487
709
  title?: string
488
710
  }
489
711
 
712
+ /** Show a board view with drag and drop and infinite scroll */
490
713
  export interface CardsConfig {
714
+ /** Limit which user roles can view the board */
491
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 */
492
717
  statusField?: string
718
+ /** Exclude status values from the board */
493
719
  excludeValues?: string[] | number[]
720
+ /** The sub-heading shown on cards */
494
721
  headerField: string
722
+ /** The number of lines for the header field text */
495
723
  maxHeaderLines?: 1 | 2
724
+ /** Sections to display on cards */
496
725
  sections: {
726
+ /** The title for the section */
497
727
  title?: string
728
+ /** The fields to display in the section */
498
729
  fields: string[]
730
+ /** Show multiple columns of fields, rather than listing fields down the card vertically */
499
731
  blocks?: boolean
732
+ /** Show a large field value */
500
733
  large?: boolean
734
+ /** The number of lines for field text */
501
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 */
502
737
  collapse?: "sm" | "md" | "lg" | "xl" | "2xl" | ((record?: StokerRecord) => "sm" | "md" | "lg" | "xl" | "2xl")
503
738
  }[]
739
+ /** The footer field shown on cards */
504
740
  footerField?: string
741
+ /** The number of lines for the footer field text */
505
742
  maxFooterLines?: 1 | 2
743
+ /** Customise the title for the board tab. Defaults to `"Board"` */
506
744
  title?: string
745
+ /** Tailwind classes to apply to the card component */
507
746
  cardClass?: string
508
747
  }
509
748
 
749
+ /** Show a list of image cards with infinite scroll */
510
750
  export interface ImagesConfig {
751
+ /** Limit which user roles can view the images page */
511
752
  roles?: StokerRole[]
753
+ /** The field that contains the image URL for the record. Must be a String field */
512
754
  imageField: string
755
+ /** The image size */
513
756
  size: "sm" | "md" | "lg" | "xl"
757
+ /** The number of lines for the header field text */
514
758
  maxHeaderLines?: 1 | 2
759
+ /** Customise the title for the images tab. Defaults to `"Pics"` */
515
760
  title?: string
761
+ /** An optional custom component shown above each image */
516
762
  customComponent?: {
517
763
  component: React.FC<{
518
764
  record: StokerRecord | undefined
@@ -533,55 +779,88 @@ export interface ImagesConfig {
533
779
  }
534
780
  }
535
781
 
782
+ /** Show a map view */
536
783
  export interface MapConfig {
784
+ /** Limit which user roles can view the map page */
537
785
  roles?: StokerRole[]
786
+ /** The field containing coordinates. Must be an Array field */
538
787
  coordinatesField?: string
788
+ /** Alternatively, provide a String field containing an address */
539
789
  addressField?: string
790
+ /** The starting coordinates for the map */
540
791
  center: {
541
792
  lat: number
542
793
  lng: number
543
794
  }
795
+ /** The starting zoom value for the map */
544
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) */
545
798
  noLocation?: {
546
799
  title: string
547
800
  }
801
+ /** Customise the title for the map tab. Defaults to `"Map"` */
548
802
  title?: string
549
803
  }
550
804
 
805
+ /** Show a calendar view. Requires a Fullcalendar license */
551
806
  export interface CalendarConfig {
807
+ /** Limit which user roles can view the calendar page */
552
808
  roles?: StokerRole[]
809
+ /** Timestamp field specifying the start date of the event */
553
810
  startField: string
811
+ /** Timestamp field specifying the end date of the event. If omitted, the start date is used */
554
812
  endField?: string
813
+ /** Additional Timestamp fields to include as all-day events */
555
814
  additionalFields?: string[]
815
+ /** Boolean field indicating whether records are all-day */
556
816
  allDayField?: string
817
+ /** Fullcalendar options for desktop screen sizes */
557
818
  fullCalendarLarge?: CalendarOptions
819
+ /** Fullcalendar options for mobile screen sizes */
558
820
  fullCalendarSmall?: CalendarOptions
821
+ /** Relational field specifying a parent resource. Used for Fullcalendar features that require "resources" */
559
822
  resourceField?: string
823
+ /** Field in the `resourceField` collection that acts as the resource title */
560
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 */
561
826
  unscheduled?: {
562
827
  title: string
563
828
  roles?: StokerRole[]
564
829
  }
830
+ /** Customise the title for the calendar tab. Defaults to `"Calendar"` */
565
831
  title?: string
832
+ /** How far into the past to load records for */
566
833
  dataStart?: { days: number } | { weeks: number } | { months: number } | { years: number }
834
+ /** How far into the future to load records for */
567
835
  dataEnd?: { days: number } | { weeks: number } | { months: number } | { years: number }
836
+ /** Threshold at which more past records will be loaded */
568
837
  dataStartOffset?: { days: number } | { weeks: number } | { months: number } | { years: number }
838
+ /** Threshold at which more future records will be loaded */
569
839
  dataEndOffset?: { days: number } | { weeks: number } | { months: number } | { years: number }
840
+ /** The color for the provided record's event on the calendar */
570
841
  color?: string | ((record: StokerRecord) => string)
842
+ /** A custom title for the provided record's event on the calendar */
571
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 */
572
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 */
573
847
  additionalCollections?: StokerCollection[]
574
848
  }
575
849
 
850
+ /** Filter the list by the collection's status field */
576
851
  export type StatusFilter = {
577
852
  type: "status"
578
853
  value?: string | number
854
+ /** The roles that can see this filter */
579
855
  roles?: StokerRole[]
580
856
  }
581
857
 
858
+ /** Filter the list by a Timestamp field */
582
859
  export type RangeFilter = {
583
860
  type: "range"
861
+ /** The Timestamp field to filter by */
584
862
  field: string
863
+ /** Which selectors to show in the range picker */
585
864
  selector?:
586
865
  | "range"
587
866
  | "week"
@@ -589,29 +868,40 @@ export type RangeFilter = {
589
868
  | ("range" | "week" | "month")[]
590
869
  | (() => "range" | "week" | "month" | ("range" | "week" | "month")[])
591
870
  value?: string
871
+ /** Offset the default start date by this many days */
592
872
  startOffsetDays?: number
873
+ /** Offset the default start date by this many hours */
593
874
  startOffsetHours?: number
875
+ /** Offset the default end date by this many days */
594
876
  endOffsetDays?: number
877
+ /** Offset the default end date by this many hours */
595
878
  endOffsetHours?: number
596
879
  }
597
880
 
881
+ /** Filter the list by a field with `values` set */
598
882
  export type SelectFilter = {
599
883
  type: "select"
884
+ /** The field to filter by. Must have `values` set */
600
885
  field: string
886
+ /** The title for the filter */
601
887
  title?: string | (() => string)
888
+ /** The roles that can see this filter */
602
889
  roles?: StokerRole[]
890
+ /** Modify the titles shown for filter values */
603
891
  titles?: (
604
892
  value: string,
605
893
  relationCollection?: CollectionSchema,
606
894
  relationParent?: StokerRecord,
607
895
  isAssigning?: boolean,
608
896
  ) => string
897
+ /** Filter which values are shown in the filter */
609
898
  filterValues?: (
610
899
  value: boolean | string | number | undefined,
611
900
  relationCollection?: CollectionSchema,
612
901
  relationParent?: StokerRecord,
613
902
  isAssigning?: boolean,
614
903
  ) => boolean
904
+ /** The default value for the filter */
615
905
  defaultValue?:
616
906
  | string
617
907
  | number
@@ -620,88 +910,147 @@ export type SelectFilter = {
620
910
  parentRecord?: StokerRecord,
621
911
  isAssigning?: boolean,
622
912
  ) => string | number | undefined)
913
+ /** Show or hide the filter */
623
914
  condition?: (parentCollection?: CollectionSchema, parentRecord?: StokerRecord, isAssigning?: boolean) => boolean
624
915
  value?: string | number
916
+ /** The style of the filter input */
625
917
  style?: "select" | "radio" | "buttons"
626
918
  }
627
919
 
920
+ /** Filter the list by a related field */
628
921
  export type RelationFilter = {
629
922
  type: "relation"
923
+ /** The relational field to filter by */
630
924
  field: string
925
+ /** The title for the filter */
631
926
  title?: string | (() => string)
927
+ /** The roles that can see this filter */
632
928
  roles?: StokerRole[]
929
+ /** Filter the list of related values using a Firestore where() query */
633
930
  constraints?: [string, "==" | "in", unknown][]
634
931
  value?: string
635
932
  }
636
933
 
934
+ /** A filter shown in the right-hand-side filter drawer on the list page */
637
935
  export type Filter = StatusFilter | RangeFilter | SelectFilter | RelationFilter
638
936
 
937
+ /** Show a metric (numerical counter) at the top of the list page */
639
938
  export interface Metric {
939
+ /** The metric type. For "custom" metrics, use the `formula` method to calculate the value to display */
640
940
  type: "sum" | "average" | "count" | "custom"
941
+ /** The field to aggregate. Not required for `count` or `custom` */
641
942
  field?: string
943
+ /** Limit which user roles can view the metric */
642
944
  roles?: StokerRole[]
945
+ /** The title shown above the metric */
643
946
  title?: string
947
+ /** Maximum decimal places to display */
644
948
  decimal?: number
949
+ /** Prefix text, for example a currency symbol */
645
950
  prefix?: string
951
+ /** Suffix text, for example units */
646
952
  suffix?: string
953
+ /** Tailwind text size for the metric value */
647
954
  textSize?: "text-xl" | "text-2xl" | "text-3xl"
955
+ /** Compact the metric vertically */
648
956
  compact?: boolean
957
+ /** Custom metric calculation */
649
958
  formula?: (records: StokerRecord[]) => number | string
650
959
  }
960
+ /** Show a chart at the top of the list page */
651
961
  export interface Chart {
962
+ /** The chart type */
652
963
  type: "area"
964
+ /** The date field used to group points */
653
965
  dateField: string
966
+ /** First metric field */
654
967
  metricField1?: string
968
+ /** Optional second metric field */
655
969
  metricField2?: string
970
+ /** Default chart date range */
656
971
  defaultRange: "90d" | "30d" | "7d"
972
+ /** Limit which user roles can view the chart */
657
973
  roles?: StokerRole[]
974
+ /** Title shown above the chart */
658
975
  title?: string
976
+ /** Currency symbol to display */
659
977
  currency?: string | (() => string)
660
978
  }
979
+ /** A custom meta title and description for the collection's pages */
661
980
  export interface CollectionMeta {
662
981
  title?: string
663
982
  description?: string
664
983
  }
984
+ /** Highlight rows in the list view */
665
985
  export interface RowHighlight {
986
+ /** Return `true` to highlight the row for the given record */
666
987
  condition: (record: StokerRecord) => boolean
988
+ /** The Tailwind classes to apply to the highlighted row */
667
989
  className: string
990
+ /** The user roles to highlight rows for */
668
991
  roles?: StokerRole[]
669
992
  }
993
+ /** Converting a record creates a new record in the target collection and keeps the original */
670
994
  export interface Convert {
995
+ /** The collection to convert records to */
671
996
  collection: string
997
+ /** A function that modifies the record before conversion */
672
998
  convert: (record: StokerRecord) => Partial<StokerRecord> | Promise<Partial<StokerRecord>>
999
+ /** The roles that can perform this conversion */
673
1000
  roles?: StokerRole[]
674
1001
  }
1002
+ /** A custom form field component */
675
1003
  export interface CustomField {
1004
+ /** The position of the custom component in the form */
676
1005
  position?: number | ((record?: StokerRecord) => number)
1006
+ /** The React component */
677
1007
  component?: React.FC
1008
+ /** Props to pass to the React component */
678
1009
  props?: Record<string, unknown>
1010
+ /** Show or hide the custom component */
679
1011
  condition?: (operation: "create" | "update" | "update-many", record?: StokerRecord) => boolean
680
1012
  }
1013
+ /** Show a relation list directly on the edit record form page */
681
1014
  export interface FormList {
1015
+ /** The collection to show the relation list for */
682
1016
  collection: StokerCollection
1017
+ /** Which columns to show in the list */
683
1018
  fields: string[]
1019
+ /** The field to sort records by */
684
1020
  sortField?: string
1021
+ /** The direction to sort records by */
685
1022
  sortDirection?: "asc" | "desc"
1023
+ /** The title for the relation list */
686
1024
  label?: string
687
1025
  }
688
1026
 
1027
+ /** A custom button shown at the bottom of the edit record form */
689
1028
  export interface FormButton {
1029
+ /** The title text for the custom button */
690
1030
  title: string
1031
+ /** The icon shown on the button */
691
1032
  icon?: React.FC<{ className?: string }>
1033
+ /** The style of the button */
692
1034
  variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link"
1035
+ /** The function that fires when the button is clicked */
693
1036
  action: (
694
1037
  operation: "create" | "update" | "update-many",
695
1038
  formValues: StokerRecord,
696
1039
  originalRecord?: StokerRecord,
697
1040
  ) => void | Promise<void>
1041
+ /** Show or hide the button */
698
1042
  condition?: boolean | ((operation: "create" | "update" | "update-many", record?: StokerRecord) => boolean)
1043
+ /** A loading callback that will be called when the button is pressed */
699
1044
  setIsLoading?: (isLoading: boolean) => void
700
1045
  }
701
1046
 
1047
+ /** A custom page for the collection, shown in the record page sidebar */
702
1048
  export interface CustomRecordPage {
1049
+ /** The title for the custom page in the sidebar */
703
1050
  title: string
1051
+ /** The URL segment that the page will load on */
704
1052
  url: string
1053
+ /** The custom component */
705
1054
  component: React.FC<{
706
1055
  record: StokerRecord | undefined
707
1056
  collection: CollectionSchema
@@ -712,8 +1061,11 @@ export interface CustomRecordPage {
712
1061
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
713
1062
  utils: any
714
1063
  }>
1064
+ /** Props to pass to the custom component */
715
1065
  props?: Record<string, unknown>
1066
+ /** Show or hide the custom page */
716
1067
  condition?: (record: StokerRecord | undefined) => boolean
1068
+ /** The icon shown in the sidebar */
717
1069
  icon?: React.FC<{ className?: string }>
718
1070
  }
719
1071
 
@@ -729,21 +1081,33 @@ export interface Assignable {
729
1081
  }[]
730
1082
  }
731
1083
 
1084
+ /** A custom action shown in a dropdown menu on the list page */
732
1085
  export interface CustomListAction {
1086
+ /** The title for the action */
733
1087
  title: string
1088
+ /** The icon shown for the action */
734
1089
  icon?: React.FC<{ className?: string }>
1090
+ /** The function that fires when the action is clicked */
735
1091
  action: () => void | Promise<void>
1092
+ /** Show or hide the action */
736
1093
  condition?: () => boolean
737
1094
  }
738
1095
 
1096
+ /** Options for file uploads */
739
1097
  export interface FileOptions {
1098
+ /** The maximum width for uploaded image files. Images above this size will be downscaled */
740
1099
  maxImageWidth?: number
1100
+ /** Set to `true` to show thumbnails for images in the files list */
741
1101
  thumbnails?: boolean
742
1102
  }
743
1103
 
1104
+ /** Admin UI config for the collection */
744
1105
  export interface CollectionAdmin {
1106
+ /** Return `true` to hide the collection in the Admin UI */
745
1107
  hidden?: boolean | (() => boolean | Promise<boolean>)
1108
+ /** The collection's position in the navbar */
746
1109
  navbarPosition?: number | (() => number)
1110
+ /** Human-readable labels for the collection. Only necessary if the root `labels` are not human-readable */
747
1111
  titles?:
748
1112
  | {
749
1113
  collection: string
@@ -754,15 +1118,31 @@ export interface CollectionAdmin {
754
1118
  parentCollection?: CollectionSchema,
755
1119
  parentRecord?: StokerRecord,
756
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 */
757
1122
  icon?: React.FC | (() => React.FC | Promise<React.FC>)
1123
+ /** Return `true` to show the "Duplicate" button on the form page */
758
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 */
759
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
+ */
760
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
+ */
761
1137
  statusField?: {
1138
+ /** The field that defines the active / archived status */
762
1139
  field: string
1140
+ /** Values considered active */
763
1141
  active?: unknown[]
1142
+ /** Values considered archived */
764
1143
  archived?: unknown[]
765
1144
  }
1145
+ /** The default view for the collection */
766
1146
  defaultView?:
767
1147
  | "list"
768
1148
  | "cards"
@@ -773,7 +1153,9 @@ export interface CollectionAdmin {
773
1153
  parentCollection: CollectionSchema,
774
1154
  parentRecord?: StokerRecord,
775
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 */
776
1157
  defaultRoute?: string | (() => string)
1158
+ /** The default field to sort the list by */
777
1159
  defaultSort?:
778
1160
  | {
779
1161
  field: string
@@ -788,6 +1170,7 @@ export interface CollectionAdmin {
788
1170
  field: string
789
1171
  direction?: "asc" | "desc"
790
1172
  }>)
1173
+ /** The secondary field to sort the list by. Only works for collections with `preloadCache` or `access.serverReadOnly` enabled */
791
1174
  secondarySort?:
792
1175
  | {
793
1176
  field: string
@@ -802,52 +1185,93 @@ export interface CollectionAdmin {
802
1185
  field: string
803
1186
  direction?: "asc" | "desc"
804
1187
  }>)
1188
+ /** The number of items to show per page */
805
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
+ */
806
1195
  searchOptions?: SearchOptions & { hitsPerPage?: number }
1196
+ /** Config for the list view */
807
1197
  list?: ListConfig | (() => ListConfig | Promise<ListConfig>)
1198
+ /** Show a board view with drag and drop and infinite scroll */
808
1199
  cards?: CardsConfig | (() => CardsConfig | Promise<CardsConfig>)
1200
+ /** Show a list of image cards with infinite scroll */
809
1201
  images?: ImagesConfig | (() => ImagesConfig | Promise<ImagesConfig>)
1202
+ /** Show a map view */
810
1203
  map?: MapConfig | (() => MapConfig | Promise<MapConfig>)
1204
+ /** Show a calendar view. Requires a Fullcalendar license */
811
1205
  calendar?: CalendarConfig | (() => CalendarConfig | Promise<CalendarConfig>)
1206
+ /** Filters that will appear in the right-hand-side filter drawer on the list page */
812
1207
  filters?: Filter[]
1208
+ /** The date range selector options to be shown to the user. Only relevant when `preloadCache.range` is present */
813
1209
  rangeSelectorValues?:
814
1210
  | "range"
815
1211
  | "week"
816
1212
  | "month"
817
1213
  | ("range" | "week" | "month")[]
818
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 */
819
1216
  defaultRangeSelector?: "range" | "week" | "month" | (() => "range" | "week" | "month")
1217
+ /** Restrict CSV export to the defined roles */
820
1218
  restrictExport?: StokerRole[] | (() => StokerRole[] | Promise<StokerRole[]>)
1219
+ /** Display a counter in the title bar showing the number of items in the list */
821
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 */
822
1222
  metrics?: (Metric | Chart)[] | (() => (Metric | Chart)[] | Promise<(Metric | Chart)[]>)
1223
+ /** Define a custom meta title and description for the collection's pages */
823
1224
  meta?: CollectionMeta | (() => CollectionMeta | Promise<CollectionMeta>)
1225
+ /** Highlight rows in the list view */
824
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 */
825
1228
  breadcrumbs?: string[] | (() => string[] | Promise<string[]>)
1229
+ /** Create custom form field components */
826
1230
  customFields?: CustomField[] | (() => CustomField[] | Promise<CustomField[]>)
1231
+ /** Create custom pages for the collection */
827
1232
  customRecordPages?: CustomRecordPage[] | (() => CustomRecordPage[] | Promise<CustomRecordPage[]>)
1233
+ /** Show custom buttons at the bottom of the edit record form */
828
1234
  formButtons?: FormButton[] | (() => FormButton[] | Promise<FormButton[]>)
1235
+ /** Show a file upload button on the add record form */
829
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 */
830
1238
  formImages?: boolean | (() => boolean | Promise<boolean>)
1239
+ /** Show relation lists directly on the edit record form page */
831
1240
  formLists?: FormList[] | (() => FormList[] | Promise<FormList[]>)
1241
+ /** Hide the add record button */
832
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
+ */
833
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
+ */
834
1252
  onFormOpen?: (
835
1253
  operation: "create" | "update",
836
1254
  record: StokerRecord,
837
1255
  parentCollection?: StokerCollection,
838
1256
  parentRecord?: StokerRecord,
839
1257
  ) => void | Promise<void>
1258
+ /** A hook that fires whenever the form is updated. Optionally return an object with field updates */
840
1259
  onChange?: (
841
1260
  operation: "create" | "update",
842
1261
  record: StokerRecord,
843
1262
  originalRecord: StokerRecord,
844
1263
  ) => Partial<StokerRecord> | void | Promise<Partial<StokerRecord> | void>
1264
+ /** Override the default behaviour of opening the add record form */
845
1265
  addRecordButtonOverride?: (record?: StokerRecord) => void | Promise<void>
1266
+ /** Disable the date range selector for the user */
846
1267
  disableRangeSelector?: boolean | (() => boolean)
1268
+ /** Load data for use in your computed fields, once per query */
847
1269
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
848
1270
  retriever?: () => any | Promise<any>
849
1271
  assignable?: Assignable[] | (() => Assignable[] | Promise<Assignable[]>)
1272
+ /** Custom actions that will be shown in a dropdown menu on the list page */
850
1273
  customListActions?: CustomListAction[] | (() => CustomListAction[] | Promise<CustomListAction[]>)
1274
+ /** Options for file uploads */
851
1275
  fileOptions?: FileOptions | (() => FileOptions | Promise<FileOptions>)
852
1276
  }
853
1277
  export interface CollectionAdminCache {
@@ -905,8 +1329,15 @@ export interface CollectionAdminCache {
905
1329
  fileOptions?: FileOptions
906
1330
  }
907
1331
 
1332
+ /** Custom code config for the field, including hooks and server access control */
908
1333
  export interface FieldCustom extends Hooks {
1334
+ /** Calculate an initial value for this field when creating the record */
909
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
+ */
910
1341
  serverAccess?: {
911
1342
  read?: (permissions: StokerPermissions, user: UserRecord, record?: StokerRecord) => boolean | Promise<boolean>
912
1343
  create?: (permissions: StokerPermissions, user: UserRecord, record: StokerRecord) => boolean | Promise<boolean>
@@ -919,68 +1350,121 @@ export interface FieldCustom extends Hooks {
919
1350
  }
920
1351
  }
921
1352
 
1353
+ /** Display a conditional description message under the field in the Admin UI */
922
1354
  export interface FieldDescription {
1355
+ /** The description message to display */
923
1356
  message: string | ((record?: StokerRecord) => string | Promise<string>)
1357
+ /** Show or hide the description */
924
1358
  condition?: boolean | ((record?: StokerRecord) => boolean | Promise<boolean>)
925
1359
  }
926
1360
 
1361
+ /** Config for a coordinates field, providing the starting location and zoom for the map */
927
1362
  export interface LocationFieldAdmin {
1363
+ /** The starting coordinates for the map */
928
1364
  center: {
929
1365
  lat: number
930
1366
  lng: number
931
1367
  }
1368
+ /** The starting zoom value for the map */
932
1369
  zoom: number
933
1370
  }
934
1371
 
1372
+ /** An icon shown for the field on the form page */
935
1373
  export interface FormFieldIcon {
1374
+ /** The icon component */
936
1375
  component: React.FC
1376
+ /** Additional Tailwind classes for the icon */
937
1377
  className?: string
938
1378
  }
939
1379
 
1380
+ /** Admin UI config for the field */
940
1381
  export interface FieldAdmin {
1382
+ /** A human-readable name for the field. Only necessary if `name` is not human-readable */
941
1383
  label?: string | (() => string)
1384
+ /** Override the field name shown in the list view */
942
1385
  listLabel?: string | (() => string)
1386
+ /** An icon that will be shown for the field on the form page */
943
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
+ */
944
1393
  condition?: {
945
1394
  list?: boolean | ((parentCollection?: CollectionSchema, parentRecord?: StokerRecord) => boolean)
946
1395
  form?: boolean | ((operation?: "create" | "update", record?: StokerRecord, isExport?: boolean) => boolean)
947
1396
  }
1397
+ /** Return `true` to set this as a read-only field in the Admin UI */
948
1398
  readOnly?: boolean | ((operation?: "create" | "update", record?: StokerRecord) => boolean)
1399
+ /** Display a conditional description message under the field in the Admin UI */
949
1400
  description?: FieldDescription
1401
+ /** Set to `true` on a String field to make it a textarea */
950
1402
  textarea?: boolean | (() => boolean | Promise<boolean>)
1403
+ /** Set to `true` on a String field with `values` to make it a radio group */
951
1404
  radio?: boolean | (() => boolean | Promise<boolean>)
1405
+ /** Set to `true` on a String field with `values` to make it a button group */
952
1406
  buttonGroup?: boolean | (() => boolean | Promise<boolean>)
1407
+ /** Set to `true` on a Boolean field to make it a switch */
953
1408
  switch?: boolean | (() => boolean | Promise<boolean>)
1409
+ /** Set to `true` on a Timestamp field to make it a month picker */
954
1410
  month?: boolean | (() => boolean | Promise<boolean>)
1411
+ /** Set to `true` on a Number field to make it a slider */
955
1412
  slider?: boolean | (() => boolean | Promise<boolean>)
1413
+ /** Set to `true` on a Map field to make it a rich text field */
956
1414
  richText?: boolean | (() => boolean | Promise<boolean>)
1415
+ /** Set on an Array field to make it a coordinates field. Provide starting location coordinates and zoom */
957
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 */
958
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 */
959
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 */
960
1422
  tags?: string[] | (() => string[])
1423
+ /** Set to `true` to have the field live-update on the form page when the record is updated remotely */
961
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 */
962
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 */
963
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 */
964
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 */
965
1432
  italic?: boolean | ((record?: StokerRecord) => boolean)
1433
+ /** Set to a currency symbol to make the field a currency */
966
1434
  currency?: string | ((record?: StokerRecord) => string)
1435
+ /** The returned value will be used for sorting in the list view */
967
1436
  sort?: (record?: StokerRecord) => unknown
1437
+ /** Set to `true` to exclude this field from CSV export */
968
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 `", "` */
969
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
+ */
970
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 */
971
1447
  overrideFormRequiredValidation?: (
972
1448
  operation: "create" | "update",
973
1449
  record?: StokerRecord,
974
1450
  originalRecord?: StokerRecord,
975
1451
  ) => boolean
1452
+ /** Filter the options in the dropdown for String or Number fields with `values` set */
976
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 */
977
1455
  filterResults?: (result: StokerRecord, parentCollection: CollectionSchema, parentRecord?: StokerRecord) => boolean
1456
+ /** Modify the results in the dropdown for relational fields */
978
1457
  modifyResultTitle?: (
979
1458
  record: StokerRecord,
980
1459
  parentCollection: CollectionSchema,
981
1460
  parentRecord?: StokerRecord,
982
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 */
983
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
+ */
984
1468
  customListView?: (
985
1469
  record?: StokerRecord,
986
1470
  parentCollection?: CollectionSchema,
@@ -992,18 +1476,34 @@ export interface FieldAdmin {
992
1476
  receiveClick?: boolean
993
1477
  }
994
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) */
995
1480
  queryFullRecord?: boolean | (() => boolean)
1481
+ /** Display a Computed field value as rich text */
996
1482
  asRichText?: boolean | (() => boolean)
997
1483
  }
998
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
+ */
999
1489
  export interface DependencyField {
1490
+ /** The field in the related collection to grant access to */
1000
1491
  field: string
1492
+ /** The roles to grant access to */
1001
1493
  roles: StokerRole[]
1002
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" */
1003
1496
  export interface EnforceHierarchy {
1497
+ /** Another relational field in the collection that is above the current field in the relational hierarchy */
1004
1498
  field: string
1499
+ /** The field in the related collection above that links to the same collection as the current field */
1005
1500
  recordLinkField: string
1006
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
+ */
1007
1507
  export interface SingleFieldExemption {
1008
1508
  queryScope: "COLLECTION" | "COLLECTION_GROUP"
1009
1509
  order?: "ASCENDING" | "DESCENDING"
@@ -1046,11 +1546,23 @@ export interface FieldAccessGroupReference {
1046
1546
  group: string
1047
1547
  }
1048
1548
 
1549
+ /** Properties that can be set on any field type */
1049
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` */
1050
1552
  name: string
1553
+ /** A description for the field for LLMs. Only relevant if `ai` is configured */
1051
1554
  description?: string | (() => string | Promise<string>)
1052
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
+ */
1053
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
+ */
1054
1566
  sorting?:
1055
1567
  | boolean
1056
1568
  | {
@@ -1058,17 +1570,45 @@ export interface StandardField {
1058
1570
  roles?: StokerRole[]
1059
1571
  }
1060
1572
 
1573
+ /** Set to `true` if the field is a required field */
1061
1574
  required?: boolean
1575
+ /** Set to `true` if the field is a nullable field */
1062
1576
  nullable?: boolean
1063
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
+ */
1064
1582
  saveToAuthToken?: boolean
1065
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
+ */
1066
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
+ */
1067
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
+ */
1068
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
+ */
1069
1607
  skipRulesValidation?: boolean
1070
1608
 
1609
+ /** Custom code config for the field, including hooks and server access control */
1071
1610
  custom?: FieldCustom
1611
+ /** Admin UI config for the field */
1072
1612
  admin?: FieldAdmin
1073
1613
  }
1074
1614
  export interface BooleanField extends StandardField {
@@ -1076,44 +1616,79 @@ export interface BooleanField extends StandardField {
1076
1616
  }
1077
1617
  export interface StringField extends StandardField {
1078
1618
  type: "String"
1619
+ /** An optional list of values. This will result in a dropdown list being shown in the Admin UI */
1079
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
+ */
1080
1626
  unique?: boolean
1081
1627
 
1628
+ /** Specify a fixed length for the field */
1082
1629
  length?: number
1630
+ /** Specify a minimum length for the field */
1083
1631
  minlength?: number
1632
+ /** Specify a maximum length for the field */
1084
1633
  maxlength?: number
1634
+ /** Specify a regex pattern for the field */
1085
1635
  pattern?: string
1086
1636
 
1637
+ /** Set to `true` if the field is an email address. Only validated server-side if `access.serverWriteOnly` is `true` */
1087
1638
  email?: boolean
1639
+ /** Set to `true` if the field is a url. Only validated server-side if `access.serverWriteOnly` is `true` */
1088
1640
  url?: boolean
1641
+ /** Set to `true` if the field is an emoji. Only validated server-side if `access.serverWriteOnly` is `true` */
1089
1642
  emoji?: boolean
1643
+ /** Set to `true` if the field is a UUID. Only validated server-side if `access.serverWriteOnly` is `true` */
1090
1644
  uuid?: boolean
1645
+ /** Set to `true` if the field is an IP address. Only validated server-side if `access.serverWriteOnly` is `true` */
1091
1646
  ip?: boolean
1092
1647
  }
1093
1648
  export interface NumberField extends StandardField {
1094
1649
  type: "Number"
1650
+ /** An optional list of values. This will result in a dropdown list being shown in the Admin UI */
1095
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
+ */
1096
1656
  unique?: boolean
1097
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
+ */
1098
1663
  autoIncrement?: boolean
1664
+ /** Set the maximum number of decimal places for this field */
1099
1665
  decimal?: number
1100
1666
 
1667
+ /** Set the maximum number value for this field */
1101
1668
  max?: number
1669
+ /** Set the minimum number value for this field */
1102
1670
  min?: number
1103
1671
  }
1104
1672
  export interface TimestampField extends StandardField {
1105
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 */
1106
1675
  values?: number[]
1107
1676
 
1677
+ /** Set the maximum milliseconds value for this field */
1108
1678
  max?: number
1679
+ /** Set the minimum milliseconds value for this field */
1109
1680
  min?: number
1110
1681
  }
1111
1682
  export interface ArrayField extends StandardField {
1112
1683
  type: "Array"
1684
+ /** A list of values. A dropdown selector will be shown in the Admin UI */
1113
1685
  values?: string[]
1114
1686
 
1687
+ /** Set the exact required length for this field */
1115
1688
  length?: number
1689
+ /** Set the minimum length for this field */
1116
1690
  minlength?: number
1691
+ /** Set the maximum length for this field */
1117
1692
  maxlength?: number
1118
1693
  }
1119
1694
  export interface MapField extends StandardField {
@@ -1121,17 +1696,42 @@ export interface MapField extends StandardField {
1121
1696
  }
1122
1697
  export interface RelationField extends StandardField {
1123
1698
  type: "OneToOne" | "OneToMany" | "ManyToOne" | "ManyToMany"
1699
+ /** The collection for the relational field */
1124
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
+ */
1125
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
+ */
1126
1711
  includeFields?: string[]
1712
+ /** Choose one of the `includeFields` values to act as the title field for the related record */
1127
1713
  titleField?: string
1714
+ /** Set to `true` to preserve relation data when the related record is deleted. Warning: this may have privacy implications */
1128
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
+ */
1129
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
+ */
1130
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" */
1131
1727
  enforceHierarchy?: EnforceHierarchy
1728
+ /** Set the minimum number of relations for this field */
1132
1729
  min?: number
1730
+ /** Set the maximum number of relations for this field */
1133
1731
  max?: number
1732
+ /** Set the exact number of relations required for this field */
1134
1733
  length?: number
1734
+ /** Firestore where() query constraints that will be applied when retrieving records for the dropdown selector */
1135
1735
  constraints?: [string, "==" | "in", unknown][]
1136
1736
  }
1137
1737
  export interface EmbeddingField extends StandardField {
@@ -1139,9 +1739,15 @@ export interface EmbeddingField extends StandardField {
1139
1739
  }
1140
1740
  export interface ComputedField extends StandardField {
1141
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
+ */
1142
1747
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1143
1748
  formula: (record: StokerRecord, retrieverData?: any) => string | number | Promise<string | number>
1144
1749
  }
1750
+ /** A field in a Stoker collection */
1145
1751
  export type CollectionField =
1146
1752
  | BooleanField
1147
1753
  | StringField
@@ -1153,11 +1759,15 @@ export type CollectionField =
1153
1759
  | EmbeddingField
1154
1760
  | ComputedField
1155
1761
 
1762
+ /** Make a system field (i.e. `Created_At`, `Last_Write_By`) accessible to the given roles */
1156
1763
  export interface RoleSystemField {
1764
+ /** The name of the system field */
1157
1765
  field: string
1766
+ /** The roles that can access the field */
1158
1767
  roles?: StokerRole[]
1159
1768
  }
1160
1769
 
1770
+ /** The customization (custom code and Admin UI config) for a collection */
1161
1771
  export interface CollectionCustomization {
1162
1772
  custom?: CollectionCustom
1163
1773
  admin?: CollectionAdmin
@@ -1169,31 +1779,61 @@ export interface CollectionCustomization {
1169
1779
  }[]
1170
1780
  }
1171
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
+ */
1172
1786
  export interface Query {
1787
+ /** The name of the field */
1173
1788
  field: string
1789
+ /** Set to `true` if the field is a Timestamp field */
1174
1790
  range?: boolean
1791
+ /** Set to `true` if the field needs to be indexed independently of sorting */
1175
1792
  standalone?: boolean
1793
+ /** The user roles that will run the query */
1176
1794
  roles?: StokerRole[]
1177
1795
  }
1178
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 */
1179
1798
  export interface RelationList {
1799
+ /** The collection for the relation list */
1180
1800
  collection: StokerCollection
1801
+ /** The field in the current collection that relates to the collection above */
1181
1802
  field: string
1803
+ /** The roles that can see this relation list */
1182
1804
  roles?: StokerRole[]
1805
+ /** Firestore constraints to apply to the relation list query */
1183
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 */
1184
1808
  loadAll?: boolean
1809
+ /** Show metrics above the list in the Admin UI */
1185
1810
  showMetrics?: boolean
1811
+ /** A list of filters to show in the LHS sidebar when the relation list is active */
1186
1812
  showFilters?: string[]
1187
1813
  }
1188
1814
 
1815
+ /** The schema for a Stoker collection. Each collection represents a collection in Cloud Firestore, and a page in the Admin UI */
1189
1816
  export interface CollectionSchema {
1817
+ /** The names for the collection. Names must start with a capital letter and contain only letters, digits, and underscores */
1190
1818
  labels: CollectionLabels
1819
+ /** Access control config for the collection */
1191
1820
  access: CollectionAccess
1821
+ /** The fields for the collection */
1192
1822
  fields: (CollectionField | RelationField)[]
1823
+ /** The field in the collection that will be used as the record's title, i.e. "Name" */
1193
1824
  recordTitleField: string
1194
1825
 
1826
+ /** Set to `true` if this collection is a "users" collection. Records in the collection can then be assigned access credentials */
1195
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
+ */
1196
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
+ */
1197
1837
  parentCollection?: StokerCollection
1198
1838
 
1199
1839
  /**
@@ -1202,34 +1842,80 @@ export interface CollectionSchema {
1202
1842
  */
1203
1843
  fieldAccessGroups?: Record<string, FieldAccessCondition>
1204
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
+ */
1205
1849
  preloadCache?: PreloadCache
1850
+ /** Enable soft-delete for this collection */
1206
1851
  softDelete?: {
1852
+ /** The name of a Boolean field. This field will be set to `true` when the record is soft-deleted */
1207
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 */
1208
1855
  timestampField: string
1856
+ /** The number of days after which soft-deleted records will be permanently deleted */
1209
1857
  retentionPeriod: number
1210
1858
  }
1211
1859
 
1860
+ /** Advanced. Define fields that will be indexed for querying. Only relevant if you are using Stoker as a headless CMS */
1212
1861
  queries?: Query[]
1862
+ /** Define "child lists" that will appear in the Admin UI for records in this collection */
1213
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 */
1214
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
+ */
1215
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
+ */
1216
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
+ */
1217
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
+ */
1218
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
+ */
1219
1891
  indexExemption?: boolean
1892
+ /** Make system fields (i.e. `Created_At`, `Last_Write_By`) accessible to your app's users */
1220
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
+ */
1221
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 */
1222
1901
  ai?: {
1902
+ /** Set to `true` to save embeddings for records in this collection. Requires the `custom.setEmbedding` hook */
1223
1903
  embedding?: boolean
1224
1904
  chat?: {
1905
+ /** The name for the chat bot */
1225
1906
  name: string
1907
+ /** The number of records the LLM should retrieve for context */
1226
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 */
1227
1910
  roles: StokerRole[]
1228
1911
  }
1229
1912
  }
1913
+ /** The priority of this collection when seeding test data using `stoker seed-data` */
1230
1914
  seedOrder?: number
1231
1915
 
1916
+ /** Custom code config for the collection, including hooks and server access control */
1232
1917
  custom?: CollectionCustom
1918
+ /** Admin UI config for the collection */
1233
1919
  admin?: CollectionAdmin
1234
1920
  }
1235
1921