@adminiumjs/manifest 0.3.27 → 0.3.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/app-grants.d.ts +134 -0
  2. package/dist/app-grants.d.ts.map +1 -0
  3. package/dist/app-grants.js +263 -0
  4. package/dist/app-grants.js.map +1 -0
  5. package/dist/automations.d.ts +2255 -58
  6. package/dist/automations.d.ts.map +1 -1
  7. package/dist/automations.js +450 -25
  8. package/dist/automations.js.map +1 -1
  9. package/dist/compose.d.ts.map +1 -1
  10. package/dist/compose.js +3 -0
  11. package/dist/compose.js.map +1 -1
  12. package/dist/documents.d.ts +1 -0
  13. package/dist/documents.d.ts.map +1 -1
  14. package/dist/documents.js +2 -0
  15. package/dist/documents.js.map +1 -1
  16. package/dist/index.d.ts +11 -5
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +10 -4
  19. package/dist/index.js.map +1 -1
  20. package/dist/ledgers.d.ts.map +1 -1
  21. package/dist/ledgers.js +2 -0
  22. package/dist/ledgers.js.map +1 -1
  23. package/dist/modules.d.ts +93 -0
  24. package/dist/modules.d.ts.map +1 -0
  25. package/dist/modules.js +489 -0
  26. package/dist/modules.js.map +1 -0
  27. package/dist/occupies.d.ts +50 -0
  28. package/dist/occupies.d.ts.map +1 -0
  29. package/dist/occupies.js +123 -0
  30. package/dist/occupies.js.map +1 -0
  31. package/dist/outbox.d.ts +223 -17
  32. package/dist/outbox.d.ts.map +1 -1
  33. package/dist/outbox.js +125 -13
  34. package/dist/outbox.js.map +1 -1
  35. package/dist/page-board.d.ts +144 -0
  36. package/dist/page-board.d.ts.map +1 -0
  37. package/dist/page-board.js +280 -0
  38. package/dist/page-board.js.map +1 -0
  39. package/dist/plan-context.d.ts.map +1 -1
  40. package/dist/plan-context.js +3 -0
  41. package/dist/plan-context.js.map +1 -1
  42. package/dist/plan-model.d.ts +7 -0
  43. package/dist/plan-model.d.ts.map +1 -1
  44. package/dist/public-access.d.ts +6 -0
  45. package/dist/public-access.d.ts.map +1 -1
  46. package/dist/public-access.js +75 -1
  47. package/dist/public-access.js.map +1 -1
  48. package/dist/rail.d.ts +50 -0
  49. package/dist/rail.d.ts.map +1 -0
  50. package/dist/rail.js +122 -0
  51. package/dist/rail.js.map +1 -0
  52. package/dist/roles.d.ts +647 -1
  53. package/dist/roles.d.ts.map +1 -1
  54. package/dist/roles.js +459 -3
  55. package/dist/roles.js.map +1 -1
  56. package/dist/sample.d.ts +62 -0
  57. package/dist/sample.d.ts.map +1 -1
  58. package/dist/sample.js +203 -4
  59. package/dist/sample.js.map +1 -1
  60. package/dist/schema.d.ts +4349 -169
  61. package/dist/schema.d.ts.map +1 -1
  62. package/dist/schema.js +540 -41
  63. package/dist/schema.js.map +1 -1
  64. package/dist/shapes.d.ts +3 -2
  65. package/dist/shapes.d.ts.map +1 -1
  66. package/dist/shapes.js +76 -7
  67. package/dist/shapes.js.map +1 -1
  68. package/dist/state-actions.d.ts +4 -0
  69. package/dist/state-actions.d.ts.map +1 -1
  70. package/dist/state-actions.js +7 -0
  71. package/dist/state-actions.js.map +1 -1
  72. package/dist/states.d.ts +20 -1
  73. package/dist/states.d.ts.map +1 -1
  74. package/dist/states.js +47 -5
  75. package/dist/states.js.map +1 -1
  76. package/dist/validate.d.ts.map +1 -1
  77. package/dist/validate.js +110 -3
  78. package/dist/validate.js.map +1 -1
  79. package/dist/words.d.ts +1 -1
  80. package/dist/words.d.ts.map +1 -1
  81. package/dist/words.js +332 -1
  82. package/dist/words.js.map +1 -1
  83. package/package.json +2 -2
package/dist/sample.d.ts CHANGED
@@ -21,6 +21,12 @@
21
21
  * `{"@table": "<table>"}` that table's stored name, for a column that
22
22
  * holds one (`rules.tableRef`): the name it has
23
23
  * on this install, whichever app made the table
24
+ * `{"@person": "<key>"}` one of the manifest's sample people
25
+ * (`sampleData.people`): the staff member made
26
+ * for that key. In a staff column (`rules.staff`)
27
+ * or another text column that holds a staff id,
28
+ * and anywhere inside a json value (a saved
29
+ * filter that names a rep)
24
30
  *
25
31
  * A `@day` may add `"@workdays": true`: its days count Monday to Friday, and
26
32
  * day 0 on a weekend is the Monday after — so "today's" busy day is never a
@@ -61,6 +67,10 @@
61
67
  * `"@onlyIfEmpty": true` is for a table that holds one row, the app's own
62
68
  * settings: the row is added only when the table has none, and otherwise its
63
69
  * `@label` names the row already there — the operator's own settings stay.
70
+ * With `"@fillEmpty": true` beside it, the row already there takes the
71
+ * sample's values in the columns it holds nothing in (a depot's name on a
72
+ * settings row the install wrote with its defaults): never over a value, and
73
+ * emptied again with the sample where nobody changed them since.
64
74
  *
65
75
  * AN APP'S ROWS FOR AN ADD-ON. An app that names an add-on may ship a second
66
76
  * file of the same format with a top-level `addOn`: rows of that add-on's
@@ -102,6 +112,8 @@ export declare const sampleValueSchema: z.ZodUnion<readonly [z.ZodUnion<readonly
102
112
  '@asset': z.ZodString;
103
113
  }, z.core.$strict>, z.ZodObject<{
104
114
  '@table': z.ZodString;
115
+ }, z.core.$strict>, z.ZodObject<{
116
+ '@person': z.ZodString;
105
117
  }, z.core.$strict>]>, z.ZodArray<z.ZodUnknown>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>;
106
118
  export declare const sampleRowSchema: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>, z.ZodUnion<readonly [z.ZodObject<{
107
119
  '@ref': z.ZodString;
@@ -130,6 +142,8 @@ export declare const sampleRowSchema: z.ZodRecord<z.ZodString, z.ZodUnion<readon
130
142
  '@asset': z.ZodString;
131
143
  }, z.core.$strict>, z.ZodObject<{
132
144
  '@table': z.ZodString;
145
+ }, z.core.$strict>, z.ZodObject<{
146
+ '@person': z.ZodString;
133
147
  }, z.core.$strict>]>, z.ZodArray<z.ZodUnknown>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
134
148
  /** `@byClock`: a row's columns by where its time falls against the adding moment. */
135
149
  export declare const byClockSchema: z.ZodObject<{
@@ -160,6 +174,8 @@ export declare const byClockSchema: z.ZodObject<{
160
174
  '@asset': z.ZodString;
161
175
  }, z.core.$strict>, z.ZodObject<{
162
176
  '@table': z.ZodString;
177
+ }, z.core.$strict>, z.ZodObject<{
178
+ '@person': z.ZodString;
163
179
  }, z.core.$strict>]>]>;
164
180
  before: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>, z.ZodUnion<readonly [z.ZodObject<{
165
181
  '@ref': z.ZodString;
@@ -188,6 +204,8 @@ export declare const byClockSchema: z.ZodObject<{
188
204
  '@asset': z.ZodString;
189
205
  }, z.core.$strict>, z.ZodObject<{
190
206
  '@table': z.ZodString;
207
+ }, z.core.$strict>, z.ZodObject<{
208
+ '@person': z.ZodString;
191
209
  }, z.core.$strict>]>, z.ZodArray<z.ZodUnknown>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>>;
192
210
  around: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>, z.ZodUnion<readonly [z.ZodObject<{
193
211
  '@ref': z.ZodString;
@@ -216,6 +234,8 @@ export declare const byClockSchema: z.ZodObject<{
216
234
  '@asset': z.ZodString;
217
235
  }, z.core.$strict>, z.ZodObject<{
218
236
  '@table': z.ZodString;
237
+ }, z.core.$strict>, z.ZodObject<{
238
+ '@person': z.ZodString;
219
239
  }, z.core.$strict>]>, z.ZodArray<z.ZodUnknown>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>>;
220
240
  after: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>, z.ZodUnion<readonly [z.ZodObject<{
221
241
  '@ref': z.ZodString;
@@ -244,6 +264,8 @@ export declare const byClockSchema: z.ZodObject<{
244
264
  '@asset': z.ZodString;
245
265
  }, z.core.$strict>, z.ZodObject<{
246
266
  '@table': z.ZodString;
267
+ }, z.core.$strict>, z.ZodObject<{
268
+ '@person': z.ZodString;
247
269
  }, z.core.$strict>]>, z.ZodArray<z.ZodUnknown>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>>;
248
270
  }, z.core.$strict>;
249
271
  export type ByClock = z.infer<typeof byClockSchema>;
@@ -276,6 +298,8 @@ export declare const byStaySchema: z.ZodObject<{
276
298
  '@asset': z.ZodString;
277
299
  }, z.core.$strict>, z.ZodObject<{
278
300
  '@table': z.ZodString;
301
+ }, z.core.$strict>, z.ZodObject<{
302
+ '@person': z.ZodString;
279
303
  }, z.core.$strict>]>]>;
280
304
  to: z.ZodUnion<readonly [z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
281
305
  '@ref': z.ZodString;
@@ -304,6 +328,8 @@ export declare const byStaySchema: z.ZodObject<{
304
328
  '@asset': z.ZodString;
305
329
  }, z.core.$strict>, z.ZodObject<{
306
330
  '@table': z.ZodString;
331
+ }, z.core.$strict>, z.ZodObject<{
332
+ '@person': z.ZodString;
307
333
  }, z.core.$strict>]>]>;
308
334
  times: z.ZodOptional<z.ZodObject<{
309
335
  from: z.ZodOptional<z.ZodString>;
@@ -336,6 +362,8 @@ export declare const byStaySchema: z.ZodObject<{
336
362
  '@asset': z.ZodString;
337
363
  }, z.core.$strict>, z.ZodObject<{
338
364
  '@table': z.ZodString;
365
+ }, z.core.$strict>, z.ZodObject<{
366
+ '@person': z.ZodString;
339
367
  }, z.core.$strict>]>, z.ZodArray<z.ZodUnknown>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>>;
340
368
  during: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>, z.ZodUnion<readonly [z.ZodObject<{
341
369
  '@ref': z.ZodString;
@@ -364,6 +392,8 @@ export declare const byStaySchema: z.ZodObject<{
364
392
  '@asset': z.ZodString;
365
393
  }, z.core.$strict>, z.ZodObject<{
366
394
  '@table': z.ZodString;
395
+ }, z.core.$strict>, z.ZodObject<{
396
+ '@person': z.ZodString;
367
397
  }, z.core.$strict>]>, z.ZodArray<z.ZodUnknown>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>>;
368
398
  after: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>, z.ZodUnion<readonly [z.ZodObject<{
369
399
  '@ref': z.ZodString;
@@ -392,11 +422,15 @@ export declare const byStaySchema: z.ZodObject<{
392
422
  '@asset': z.ZodString;
393
423
  }, z.core.$strict>, z.ZodObject<{
394
424
  '@table': z.ZodString;
425
+ }, z.core.$strict>, z.ZodObject<{
426
+ '@person': z.ZodString;
395
427
  }, z.core.$strict>]>, z.ZodArray<z.ZodUnknown>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>>;
396
428
  }, z.core.$strict>;
397
429
  export type ByStay = z.infer<typeof byStaySchema>;
398
430
  /** The keys of a row that are directives about the row, not columns. */
399
431
  export declare const ROW_DIRECTIVES: ReadonlySet<string>;
432
+ /** The most moves one file asks for. */
433
+ export declare const SAMPLE_MOVES_MAX = 50;
400
434
  /** The weekdays a bundle's week anchor names. */
401
435
  export declare const SAMPLE_WEEKDAYS: readonly ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];
402
436
  export declare const sampleBundleSchema: z.ZodObject<{
@@ -447,8 +481,15 @@ export declare const sampleBundleSchema: z.ZodObject<{
447
481
  '@asset': z.ZodString;
448
482
  }, z.core.$strict>, z.ZodObject<{
449
483
  '@table': z.ZodString;
484
+ }, z.core.$strict>, z.ZodObject<{
485
+ '@person': z.ZodString;
450
486
  }, z.core.$strict>]>, z.ZodArray<z.ZodUnknown>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>>;
451
487
  }, z.core.$strict>>;
488
+ moves: z.ZodOptional<z.ZodArray<z.ZodObject<{
489
+ ref: z.ZodOptional<z.ZodString>;
490
+ table: z.ZodOptional<z.ZodString>;
491
+ to: z.ZodString;
492
+ }, z.core.$strict>>>;
452
493
  }, z.core.$strict>;
453
494
  export type SampleBundle = z.infer<typeof sampleBundleSchema>;
454
495
  export type SampleValue = z.infer<typeof sampleValueSchema>;
@@ -489,7 +530,15 @@ export declare function sampleDirective(value: unknown): {
489
530
  } | {
490
531
  kind: 'table';
491
532
  ref: string;
533
+ } | {
534
+ kind: 'person';
535
+ key: string;
492
536
  } | null;
537
+ /**
538
+ * Every sample person a value names: itself, or anywhere inside it when it
539
+ * is a json value (an object or a list that is no directive of its own).
540
+ */
541
+ export declare function samplePeopleIn(value: unknown): string[];
493
542
  /** An ISO-8601 duration in milliseconds. */
494
543
  export declare function isoDurationMs(duration: string): number;
495
544
  export interface SampleIssue {
@@ -535,6 +584,19 @@ export declare function seedRowIdentity(manifest: Manifest, table: string, row:
535
584
  * is written only by actions that count nothing. The receipt table never is.
536
585
  */
537
586
  export declare function ledgerCatalogueTables(addOn: AddOnManifest): ReadonlySet<string>;
587
+ /**
588
+ * The tables a ledger writes that an app's file may still bring ROWS WITH NO
589
+ * HISTORY of: an item with no stock, an order still a draft. A table is one
590
+ * when a person may make its rows by hand (a role of the add-on is granted
591
+ * `create` on it), and it is not the receipt table. The row then leaves out
592
+ * every column the ledger keeps (`ledgerKeptColumns`) that it can be made
593
+ * without, and names no receipt.
594
+ * What only the ledger writes (a movement, a level, a reservation) comes only
595
+ * from the ledger: the file asks for it with `moves`, and the add-on writes it.
596
+ */
597
+ export declare function ledgerBringableTables(addOn: AddOnManifest): ReadonlySet<string>;
598
+ /** The columns of a table that an add-on's ledgers keep: what a plan may change of a row that is there (`update.set`). */
599
+ export declare function ledgerKeptColumns(addOn: AddOnManifest, ref: string): ReadonlySet<string>;
538
600
  /**
539
601
  * An app's rows for an add-on it names (`sampleData.addOns.<key>.file`):
540
602
  * the file is for this app and that add-on, the app names the add-on, a
@@ -1 +1 @@
1
- {"version":3,"file":"sample.d.ts","sourceRoot":"","sources":["../src/sample.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sample.d.ts","sourceRoot":"","sources":["../src/sample.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkFG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAa,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE7F,eAAO,MAAM,aAAa,sBAAsB,CAAC;AA8CjD,gGAAgG;AAChG,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wFAAuF,CAAC;AAEtH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yFAA0C,CAAC;AAKvE,qFAAqF;AACrF,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAQf,CAAC;AACZ,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAIpD,4HAA4H;AAC5H,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAWd,CAAC;AACZ,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAElD,wEAAwE;AACxE,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,MAAM,CAA4E,CAAC;AAE5H,wCAAwC;AACxC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC,iDAAiD;AACjD,eAAO,MAAM,eAAe,4DAA6D,CAAC;AAE1F,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAyCpB,CAAC;AAEZ,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,2CAA2C;AAC3C,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAC1C;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC1E;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC7E;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC/D;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACnE;IAAE,IAAI,EAAE,GAAG,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC/B,IAAI,CA2BP;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAMvD;AAED,4CAA4C;AAC5C,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUtD;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AA2BD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,GAAG,WAAW,EAAE,CAiB1F;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,CAM5D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAAA;CAAE,EAAE,CAgB5J;AAwDD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,aAAa,GAAG,WAAW,CAAC,MAAM,CAAC,CA8B/E;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,aAAa,GAAG,WAAW,CAAC,MAAM,CAAC,CAY/E;AAED,0HAA0H;AAC1H,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAIxF;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,WAAW,EAAE,CAkEhH"}
package/dist/sample.js CHANGED
@@ -22,6 +22,12 @@
22
22
  * `{"@table": "<table>"}` that table's stored name, for a column that
23
23
  * holds one (`rules.tableRef`): the name it has
24
24
  * on this install, whichever app made the table
25
+ * `{"@person": "<key>"}` one of the manifest's sample people
26
+ * (`sampleData.people`): the staff member made
27
+ * for that key. In a staff column (`rules.staff`)
28
+ * or another text column that holds a staff id,
29
+ * and anywhere inside a json value (a saved
30
+ * filter that names a rep)
25
31
  *
26
32
  * A `@day` may add `"@workdays": true`: its days count Monday to Friday, and
27
33
  * day 0 on a weekend is the Monday after — so "today's" busy day is never a
@@ -62,6 +68,10 @@
62
68
  * `"@onlyIfEmpty": true` is for a table that holds one row, the app's own
63
69
  * settings: the row is added only when the table has none, and otherwise its
64
70
  * `@label` names the row already there — the operator's own settings stay.
71
+ * With `"@fillEmpty": true` beside it, the row already there takes the
72
+ * sample's values in the columns it holds nothing in (a depot's name on a
73
+ * settings row the install wrote with its defaults): never over a value, and
74
+ * emptied again with the sample where nobody changed them since.
65
75
  *
66
76
  * AN APP'S ROWS FOR AN ADD-ON. An app that names an add-on may ship a second
67
77
  * file of the same format with a top-level `addOn`: rows of that add-on's
@@ -112,6 +122,7 @@ const directive = z.union([
112
122
  z.object({ '@t': z.record(z.string().regex(/^[a-z]{2}(-[A-Z]{2})?$/), z.string()).refine((m) => Object.keys(m).length > 0) }).strict(),
113
123
  z.object({ '@asset': label }).strict(),
114
124
  z.object({ '@table': z.string().regex(/^[a-z][a-z0-9_]*$/, 'a table') }).strict(),
125
+ z.object({ '@person': z.string().regex(/^[a-z][a-z0-9_-]{0,39}$/, 'a sample person\'s key') }).strict(),
115
126
  ]);
116
127
  const plain = z.union([z.string(), z.number(), z.boolean(), z.null()]);
117
128
  /** A column's value: a plain value, a directive, or (for a json column) any object or array. */
@@ -144,7 +155,9 @@ export const byStaySchema = z
144
155
  })
145
156
  .strict();
146
157
  /** The keys of a row that are directives about the row, not columns. */
147
- export const ROW_DIRECTIVES = new Set(['@label', '@byClock', '@byStay', '@onlyIfEmpty']);
158
+ export const ROW_DIRECTIVES = new Set(['@label', '@byClock', '@byStay', '@onlyIfEmpty', '@fillEmpty']);
159
+ /** The most moves one file asks for. */
160
+ export const SAMPLE_MOVES_MAX = 50;
148
161
  /** The weekdays a bundle's week anchor names. */
149
162
  export const SAMPLE_WEEKDAYS = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];
150
163
  export const sampleBundleSchema = z
@@ -170,6 +183,19 @@ export const sampleBundleSchema = z
170
183
  })
171
184
  .strict())
172
185
  .min(1),
186
+ /**
187
+ * In an app's rows for an add-on: changes of state made once the rows are in, each as a save of its own, in
188
+ * this order. Such a save is a real one, so what the add-on's ledger hands to a move (a receipt posted: stock
189
+ * in) is written by the add-on itself. One row by its label (`ref`), or every row this file wrote of a table.
190
+ */
191
+ moves: z
192
+ .array(z
193
+ .object({ ref: label.optional(), table: z.string().min(1).optional(), to: z.string().min(1).max(80) })
194
+ .strict()
195
+ .refine((move) => (move.ref === undefined) !== (move.table === undefined), { message: 'a move names one row (`ref`) or one table (`table`), not both' }))
196
+ .min(1)
197
+ .max(SAMPLE_MOVES_MAX)
198
+ .optional(),
173
199
  })
174
200
  .strict();
175
201
  /** A value's directive, when it is one. */
@@ -204,8 +230,24 @@ export function sampleDirective(value) {
204
230
  return { kind: 'asset', label: record['@asset'] };
205
231
  if (typeof record['@table'] === 'string')
206
232
  return { kind: 'table', ref: record['@table'] };
233
+ if (typeof record['@person'] === 'string')
234
+ return { kind: 'person', key: record['@person'] };
207
235
  return null;
208
236
  }
237
+ /**
238
+ * Every sample person a value names: itself, or anywhere inside it when it
239
+ * is a json value (an object or a list that is no directive of its own).
240
+ */
241
+ export function samplePeopleIn(value) {
242
+ const found = sampleDirective(value);
243
+ if (found !== null)
244
+ return found.kind === 'person' ? [found.key] : [];
245
+ if (Array.isArray(value))
246
+ return value.flatMap(samplePeopleIn);
247
+ if (typeof value === 'object' && value !== null)
248
+ return Object.values(value).flatMap(samplePeopleIn);
249
+ return [];
250
+ }
209
251
  /** An ISO-8601 duration in milliseconds. */
210
252
  export function isoDurationMs(duration) {
211
253
  const match = ISO_DURATION.exec(duration);
@@ -257,8 +299,11 @@ export function sampleBundleIssues(bundle, manifest) {
257
299
  if (table.own === true)
258
300
  issues.push({ path: `tables.${String(t)}.own`, message: '"own" marks a table of the app in its rows for an add-on; a manifest\'s own sample file takes none.' });
259
301
  });
302
+ if (bundle.moves !== undefined)
303
+ issues.push({ path: 'moves', message: '"moves" are for an app\'s rows for an add-on (sampleData.addOns); a manifest\'s own sample file writes its rows as they should stand.' });
260
304
  const declared = new Map((manifest.requiredSchema?.tables ?? []).map((table) => [table.ref, table]));
261
- issues.push(...rowIssues(bundle, declared, { names: new Set(declared.keys()), what: manifest.kind === 'add-on' ? 'add-on' : 'app', otherLabels: false, known: seedLabelsOf(manifest) }));
305
+ const people = new Set(((manifest.kind === 'app' ? manifest.sampleData?.people : undefined) ?? []).map((person) => person.key));
306
+ issues.push(...rowIssues(bundle, declared, { names: new Set(declared.keys()), what: manifest.kind === 'add-on' ? 'add-on' : 'app', otherLabels: false, known: seedLabelsOf(manifest), people }));
262
307
  issues.push(...ledgerOrderIssues(bundle, manifest, declared));
263
308
  return issues;
264
309
  }
@@ -403,6 +448,42 @@ export function ledgerCatalogueTables(addOn) {
403
448
  verdicts.set(ledger.receipts, false);
404
449
  return new Set([...verdicts].filter(([, catalogue]) => catalogue).map(([ref]) => ref));
405
450
  }
451
+ /**
452
+ * The tables a ledger writes that an app's file may still bring ROWS WITH NO
453
+ * HISTORY of: an item with no stock, an order still a draft. A table is one
454
+ * when a person may make its rows by hand (a role of the add-on is granted
455
+ * `create` on it), and it is not the receipt table. The row then leaves out
456
+ * every column the ledger keeps (`ledgerKeptColumns`) that it can be made
457
+ * without, and names no receipt.
458
+ * What only the ledger writes (a movement, a level, a reservation) comes only
459
+ * from the ledger: the file asks for it with `moves`, and the add-on writes it.
460
+ */
461
+ export function ledgerBringableTables(addOn) {
462
+ const byHand = new Set();
463
+ for (const role of addOn.roles ?? []) {
464
+ for (const permission of role.permissions ?? []) {
465
+ const [kind, table, action] = permission.split(':');
466
+ if (kind === 'table' && action === 'create' && table !== undefined)
467
+ byHand.add(table.replace(/^@/, ''));
468
+ }
469
+ }
470
+ const out = new Set();
471
+ for (const ledger of ledgersOf(addOn))
472
+ for (const ref of Object.keys(ledger.writes))
473
+ if (byHand.has(ref))
474
+ out.add(ref);
475
+ for (const ledger of ledgersOf(addOn))
476
+ out.delete(ledger.receipts);
477
+ return out;
478
+ }
479
+ /** The columns of a table that an add-on's ledgers keep: what a plan may change of a row that is there (`update.set`). */
480
+ export function ledgerKeptColumns(addOn, ref) {
481
+ const out = new Set();
482
+ for (const ledger of ledgersOf(addOn))
483
+ for (const column of ledger.writes[ref]?.update?.set ?? [])
484
+ out.add(column);
485
+ return out;
486
+ }
406
487
  /**
407
488
  * An app's rows for an add-on it names (`sampleData.addOns.<key>.file`):
408
489
  * the file is for this app and that add-on, the app names the add-on, a
@@ -429,6 +510,7 @@ export function sampleSectionIssues(bundle, app, addOn) {
429
510
  const theirs = addOn === undefined ? null : new Map((addOn.requiredSchema?.tables ?? []).map((table) => [table.ref, table]));
430
511
  const declared = new Map();
431
512
  const catalogue = addOn === undefined ? new Set() : ledgerCatalogueTables(addOn);
513
+ const bringable = addOn === undefined ? new Set() : ledgerBringableTables(addOn);
432
514
  const receipts = new Set(addOn === undefined ? [] : ledgersOf(addOn).map((ledger) => ledger.receipts));
433
515
  bundle.tables.forEach((table, t) => {
434
516
  const at = `tables.${String(t)}`;
@@ -450,7 +532,7 @@ export function sampleSectionIssues(bundle, app, addOn) {
450
532
  const shape = theirs.get(table.ref);
451
533
  // An add-on's history is its own to write: an app brings what a ledger counts, never what it counted.
452
534
  // What a ledger writes beside its books (which item a dish uses) is no history, and an app may bring it.
453
- const ledger = addOn === undefined || catalogue.has(table.ref) ? undefined : ledgersOf(addOn).find((candidate) => candidate.receipts === table.ref || candidate.writes[table.ref] !== undefined);
535
+ const ledger = addOn === undefined || catalogue.has(table.ref) || bringable.has(table.ref) ? undefined : ledgersOf(addOn).find((candidate) => candidate.receipts === table.ref || candidate.writes[table.ref] !== undefined);
454
536
  if (shape === undefined)
455
537
  issues.push({ path: `${at}.ref`, message: `"${table.ref}" is not a table of "${key}". A table of the app itself is marked "own": true.` });
456
538
  else if (ledger !== undefined)
@@ -466,8 +548,21 @@ export function sampleSectionIssues(bundle, app, addOn) {
466
548
  issues.push({ path: `${at}.rows.${String(r)}.${column.ref}`, message: `"${table.ref}.${column.ref}" names the receipt of a posting: an app's rows for an add-on are made by no posting, so leave it out.` });
467
549
  });
468
550
  }
551
+ // A row with no history: what the ledger keeps of it (an average cost, a status it moves) is the ledger's to write.
552
+ // But for a column a row cannot be made without (an order line's quantity, which a receipt later adjusts): that one a person types.
553
+ const needed = new Set(shape.columns.filter((column) => column.nullable !== true && column.default === undefined).map((column) => column.ref));
554
+ const kept = addOn === undefined || catalogue.has(table.ref) ? new Set() : new Set([...ledgerKeptColumns(addOn, table.ref)].filter((column) => !needed.has(column)));
555
+ if (kept.size > 0) {
556
+ table.rows.forEach((row, r) => {
557
+ for (const column of kept) {
558
+ if (row[column] !== undefined)
559
+ issues.push({ path: `${at}.rows.${String(r)}.${column}`, message: `"${table.ref}.${column}" is kept by "${key}"'s ledger: an app's row brings no history, so leave it out (a move asks the add-on to write it).` });
560
+ }
561
+ });
562
+ }
469
563
  }
470
564
  });
565
+ issues.push(...sampleMoveIssues(bundle, theirs));
471
566
  const twice = bundle.tables.map((table) => table.ref).filter((ref, i, all) => all.indexOf(ref) !== i);
472
567
  for (const ref of new Set(twice))
473
568
  issues.push({ path: 'tables', message: `"${ref}" is listed twice.` });
@@ -475,6 +570,51 @@ export function sampleSectionIssues(bundle, app, addOn) {
475
570
  issues.push(...rowIssues(bundle, declared, { names: new Set(own.keys()), what: 'app', otherLabels: true }));
476
571
  return issues;
477
572
  }
573
+ /**
574
+ * A section's `moves`: each names a labelled row, or a table, of the add-on's
575
+ * that this file itself writes, and a state that table has. Whether the move
576
+ * is one the table allows from where the row stands is judged when it is
577
+ * made, as for anybody's save.
578
+ */
579
+ function sampleMoveIssues(bundle, theirs) {
580
+ const issues = [];
581
+ const tableOfLabel = new Map();
582
+ const written = new Map();
583
+ for (const table of bundle.tables) {
584
+ written.set(table.ref, table.own === true);
585
+ for (const row of table.rows)
586
+ if (typeof row['@label'] === 'string')
587
+ tableOfLabel.set(row['@label'], table.ref);
588
+ }
589
+ (bundle.moves ?? []).forEach((move, m) => {
590
+ const at = `moves.${String(m)}`;
591
+ const ref = move.table ?? (move.ref === undefined ? undefined : tableOfLabel.get(move.ref));
592
+ if (ref === undefined) {
593
+ issues.push({ path: `${at}.ref`, message: `No row of this file carries the label "${String(move.ref)}".` });
594
+ return;
595
+ }
596
+ if (!written.has(ref)) {
597
+ issues.push({ path: `${at}.table`, message: `This file writes no row of "${ref}": a move is of a row the file itself brings.` });
598
+ return;
599
+ }
600
+ if (written.get(ref) === true) {
601
+ issues.push({ path: at, message: `"${ref}" is the app's own table: a move is of a row of the add-on's, whose ledger then writes what follows.` });
602
+ return;
603
+ }
604
+ const shape = theirs?.get(ref);
605
+ if (shape === undefined)
606
+ return; // checked when the add-on's manifest is at hand
607
+ const states = shape.states;
608
+ if (states === undefined) {
609
+ issues.push({ path: at, message: `"${ref}" has no states, so its rows make no move.` });
610
+ return;
611
+ }
612
+ const values = shape.columns.find((column) => column.ref === states.column)?.enum ?? [];
613
+ if (!values.includes(move.to))
614
+ issues.push({ path: `${at}.to`, message: `"${ref}" has no state "${move.to}".` });
615
+ });
616
+ return issues;
617
+ }
478
618
  /**
479
619
  * The checks every row of a file gets, over the tables `declared` knows. A
480
620
  * table it does not know is skipped: the caller has said why, or cannot see it.
@@ -511,9 +651,44 @@ function rowIssues(bundle, declared, opts) {
511
651
  }
512
652
  }
513
653
  // A number Adminium gives without gaps: a sample row names it, empty, or the load numbers it into the real series.
514
- const gapless = shape.columns.filter((column) => column.rules?.sequence?.gapless === true).map((column) => column.ref);
654
+ // One the app opened to its sample (`sequence.sample`) may instead be given its number: checked below, with the text written from it.
655
+ const gapless = shape.columns.filter((column) => column.rules?.sequence?.gapless === true && column.rules.sequence.sample !== true).map((column) => column.ref);
656
+ const numberedBySample = shape.columns.filter((column) => column.rules?.sequence?.gapless === true && column.rules.sequence.sample === true);
657
+ const taken = new Map();
515
658
  for (const [r, row] of table.rows.entries()) {
516
659
  const at = `tables.${String(t)}.rows.${String(r)}`;
660
+ for (const numbered of numberedBySample) {
661
+ const column = numbered.ref;
662
+ const given = row[column];
663
+ if (given === null)
664
+ continue;
665
+ if (typeof given !== 'number' || !Number.isInteger(given) || given < 1) {
666
+ const now = given === undefined ? 'this row leaves it out' : 'this row gives it something else';
667
+ issues.push({ path: `${at}.${column}`, message: `"${column}" is a running number, and ${now}. Write the row's number (1, 2, 3 …), or "${column}": null to give it none.` });
668
+ continue;
669
+ }
670
+ // One series per parent row when the number is scoped: the same number under two parents is two numbers.
671
+ const scope = numbered.rules?.sequence?.scope;
672
+ const key = `${column}\u0000${scope === undefined ? '' : JSON.stringify(row[scope] ?? null)}\u0000${String(given)}`;
673
+ const before = taken.get(key);
674
+ if (before !== undefined)
675
+ issues.push({ path: `${at}.${column}`, message: `"${column}" ${String(given)} is row ${String(before)}'s number already.` });
676
+ else
677
+ taken.set(key, r);
678
+ // The text people read is written with it: nothing writes it for a sample row.
679
+ const text = shape.columns.find((other) => other.rules?.format?.from === column);
680
+ const format = text?.rules?.format;
681
+ if (text === undefined || format === undefined)
682
+ continue;
683
+ const written = row[text.ref];
684
+ const want = format.prefixSetting === undefined ? `${format.prefix ?? ''}${String(given).padStart(format.pad ?? 0, '0')}` : null;
685
+ if (typeof written !== 'string' || written === '') {
686
+ issues.push({ path: `${at}.${text.ref}`, message: `"${text.ref}" is the number as people read it${want === null ? '' : ` ("${want}")`}: a sample row that gives "${column}" writes it too.` });
687
+ }
688
+ else if (want !== null && written !== want) {
689
+ issues.push({ path: `${at}.${text.ref}`, message: `"${text.ref}" is written from "${column}": for ${String(given)} it reads "${want}", not "${written}".` });
690
+ }
691
+ }
517
692
  for (const column of gapless) {
518
693
  if (row[column] !== null) {
519
694
  // Said as what to write: a model given only the reason left the key out, eight times running.
@@ -541,6 +716,13 @@ function rowIssues(bundle, declared, opts) {
541
716
  issues.push({ path: `${at}.@onlyIfEmpty`, message: `"${table.ref}" is only for an empty table as a whole; a row of it says nothing more.` });
542
717
  continue;
543
718
  }
719
+ if (column === '@fillEmpty') {
720
+ if (value !== true)
721
+ issues.push({ path: `${at}.@fillEmpty`, message: '"@fillEmpty" is true, or absent.' });
722
+ else if (row['@onlyIfEmpty'] !== true)
723
+ issues.push({ path: `${at}.@fillEmpty`, message: '"@fillEmpty" goes with "@onlyIfEmpty": it says what to do with the row the table already holds.' });
724
+ continue;
725
+ }
544
726
  if (column === '@byStay') {
545
727
  if (row['@byClock'] !== undefined)
546
728
  issues.push({ path: `${at}.@byStay`, message: 'A row takes @byClock or @byStay, not both.' });
@@ -609,6 +791,11 @@ function rowIssues(bundle, declared, opts) {
609
791
  issues.push({ path: `${at}.${column}`, message: `"${table.ref}" has no column "${column}".` });
610
792
  continue;
611
793
  }
794
+ // A file is uploaded by a signed-in person: no sample row brings one.
795
+ if (value !== null && !(Array.isArray(value) && value.length === 0) && shape.columns.find((c) => c.ref === column)?.rules?.file !== undefined) {
796
+ issues.push({ path: `${at}.${column}`, message: `"${table.ref}.${column}" holds files a signed-in person uploads, so a sample row leaves it empty.` });
797
+ continue;
798
+ }
612
799
  // An object that looks like a directive must BE one: a malformed
613
800
  // `@ago` would otherwise be written into the column as JSON.
614
801
  if (typeof value === 'object' &&
@@ -620,6 +807,18 @@ function rowIssues(bundle, declared, opts) {
620
807
  continue;
621
808
  }
622
809
  const found = sampleDirective(value);
810
+ // A sample person: one the manifest lists, in a column that can hold a staff member's id.
811
+ for (const key of samplePeopleIn(value)) {
812
+ if (opts.people === undefined)
813
+ issues.push({ path: `${at}.${column}`, message: 'A sample person is named in the app\'s own sample file, not in its rows for an add-on.' });
814
+ else if (!opts.people.has(key))
815
+ issues.push({ path: `${at}.${column}`, message: `"${key}" is not one of the manifest's sample people (sampleData.people).` });
816
+ }
817
+ if (found?.kind === 'person') {
818
+ const holds = shape.columns.find((c) => c.ref === column);
819
+ if (holds?.type !== 'text')
820
+ issues.push({ path: `${at}.${column}`, message: `"${table.ref}.${column}" is not a text column, so it cannot hold a staff member.` });
821
+ }
623
822
  if (found?.kind === 'table') {
624
823
  const holds = shape.columns.find((c) => c.ref === column);
625
824
  if (!opts.names.has(found.ref))