@fieldia/core 0.28.0 → 0.30.0

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 (251) hide show
  1. package/README.md +125 -1
  2. package/package.json +1 -1
  3. package/schema/page.schema.json +279 -6
  4. package/src/index.d.mts +8 -2
  5. package/src/index.d.ts +8 -2
  6. package/src/index.js +29 -2
  7. package/src/index.js.map +1 -1
  8. package/src/index.mjs +11 -2
  9. package/src/index.mjs.map +1 -1
  10. package/src/lib/format/approval-checks.d.mts +8 -0
  11. package/src/lib/format/approval-checks.d.ts +8 -0
  12. package/src/lib/format/approval-checks.js +29 -0
  13. package/src/lib/format/approval-checks.js.map +1 -0
  14. package/src/lib/format/approval-checks.mjs +26 -0
  15. package/src/lib/format/approval-checks.mjs.map +1 -0
  16. package/src/lib/format/approval.d.mts +20 -0
  17. package/src/lib/format/approval.d.ts +20 -0
  18. package/src/lib/format/approval.js +25 -0
  19. package/src/lib/format/approval.js.map +1 -0
  20. package/src/lib/format/approval.mjs +22 -0
  21. package/src/lib/format/approval.mjs.map +1 -0
  22. package/src/lib/format/assist-checks.d.mts +14 -0
  23. package/src/lib/format/assist-checks.d.ts +14 -0
  24. package/src/lib/format/assist-checks.js +68 -0
  25. package/src/lib/format/assist-checks.js.map +1 -0
  26. package/src/lib/format/assist-checks.mjs +64 -0
  27. package/src/lib/format/assist-checks.mjs.map +1 -0
  28. package/src/lib/format/assist.d.mts +41 -0
  29. package/src/lib/format/assist.d.ts +41 -0
  30. package/src/lib/format/assist.js +29 -0
  31. package/src/lib/format/assist.js.map +1 -0
  32. package/src/lib/format/assist.mjs +26 -0
  33. package/src/lib/format/assist.mjs.map +1 -0
  34. package/src/lib/format/editable-checks.d.mts +8 -0
  35. package/src/lib/format/editable-checks.d.ts +8 -0
  36. package/src/lib/format/editable-checks.js +38 -0
  37. package/src/lib/format/editable-checks.js.map +1 -0
  38. package/src/lib/format/editable-checks.mjs +35 -0
  39. package/src/lib/format/editable-checks.mjs.map +1 -0
  40. package/src/lib/format/field.d.mts +86 -0
  41. package/src/lib/format/field.d.ts +86 -0
  42. package/src/lib/format/field.js +56 -10
  43. package/src/lib/format/field.js.map +1 -1
  44. package/src/lib/format/field.mjs +55 -9
  45. package/src/lib/format/field.mjs.map +1 -1
  46. package/src/lib/format/head-checks.d.mts +10 -2
  47. package/src/lib/format/head-checks.d.ts +10 -2
  48. package/src/lib/format/head-checks.js +9 -2
  49. package/src/lib/format/head-checks.js.map +1 -1
  50. package/src/lib/format/head-checks.mjs +10 -4
  51. package/src/lib/format/head-checks.mjs.map +1 -1
  52. package/src/lib/format/languages-checks.d.mts +8 -0
  53. package/src/lib/format/languages-checks.d.ts +8 -0
  54. package/src/lib/format/languages-checks.js +40 -0
  55. package/src/lib/format/languages-checks.js.map +1 -0
  56. package/src/lib/format/languages-checks.mjs +37 -0
  57. package/src/lib/format/languages-checks.mjs.map +1 -0
  58. package/src/lib/format/later-checks.d.mts +4 -3
  59. package/src/lib/format/later-checks.d.ts +4 -3
  60. package/src/lib/format/later-checks.js +4 -3
  61. package/src/lib/format/later-checks.js.map +1 -1
  62. package/src/lib/format/later-checks.mjs +4 -3
  63. package/src/lib/format/later-checks.mjs.map +1 -1
  64. package/src/lib/format/layout.d.mts +129 -26
  65. package/src/lib/format/layout.d.ts +129 -26
  66. package/src/lib/format/layout.js +24 -3
  67. package/src/lib/format/layout.js.map +1 -1
  68. package/src/lib/format/layout.mjs +22 -1
  69. package/src/lib/format/layout.mjs.map +1 -1
  70. package/src/lib/format/layout.types.d.mts +2 -1
  71. package/src/lib/format/layout.types.d.ts +2 -1
  72. package/src/lib/format/list-editable.d.mts +20 -0
  73. package/src/lib/format/list-editable.d.ts +20 -0
  74. package/src/lib/format/list-editable.js +33 -0
  75. package/src/lib/format/list-editable.js.map +1 -0
  76. package/src/lib/format/list-editable.mjs +28 -0
  77. package/src/lib/format/list-editable.mjs.map +1 -0
  78. package/src/lib/format/page.d.mts +88 -9
  79. package/src/lib/format/page.d.ts +88 -9
  80. package/src/lib/format/page.js +4 -0
  81. package/src/lib/format/page.js.map +1 -1
  82. package/src/lib/format/page.mjs +4 -0
  83. package/src/lib/format/page.mjs.map +1 -1
  84. package/src/lib/format/photos-options.d.mts +29 -0
  85. package/src/lib/format/photos-options.d.ts +29 -0
  86. package/src/lib/format/photos-options.js +49 -0
  87. package/src/lib/format/photos-options.js.map +1 -0
  88. package/src/lib/format/photos-options.mjs +45 -0
  89. package/src/lib/format/photos-options.mjs.map +1 -0
  90. package/src/lib/format/references.d.mts +3 -1
  91. package/src/lib/format/references.d.ts +3 -1
  92. package/src/lib/format/references.js +41 -14
  93. package/src/lib/format/references.js.map +1 -1
  94. package/src/lib/format/references.mjs +42 -15
  95. package/src/lib/format/references.mjs.map +1 -1
  96. package/src/lib/format/scan-checks.d.mts +12 -0
  97. package/src/lib/format/scan-checks.d.ts +12 -0
  98. package/src/lib/format/scan-checks.js +89 -0
  99. package/src/lib/format/scan-checks.js.map +1 -0
  100. package/src/lib/format/scan-checks.mjs +86 -0
  101. package/src/lib/format/scan-checks.mjs.map +1 -0
  102. package/src/lib/format/scan-options.d.mts +84 -0
  103. package/src/lib/format/scan-options.d.ts +84 -0
  104. package/src/lib/format/scan-options.js +58 -0
  105. package/src/lib/format/scan-options.js.map +1 -0
  106. package/src/lib/format/scan-options.mjs +54 -0
  107. package/src/lib/format/scan-options.mjs.map +1 -0
  108. package/src/lib/format/sheet-head-checks.d.mts +10 -2
  109. package/src/lib/format/sheet-head-checks.d.ts +10 -2
  110. package/src/lib/format/sheet-head-checks.js +24 -0
  111. package/src/lib/format/sheet-head-checks.js.map +1 -1
  112. package/src/lib/format/sheet-head-checks.mjs +23 -0
  113. package/src/lib/format/sheet-head-checks.mjs.map +1 -1
  114. package/src/lib/format/upgrade.d.mts +6 -2
  115. package/src/lib/format/upgrade.d.ts +6 -2
  116. package/src/lib/format/upgrade.js +6 -2
  117. package/src/lib/format/upgrade.js.map +1 -1
  118. package/src/lib/format/upgrade.mjs +6 -2
  119. package/src/lib/format/upgrade.mjs.map +1 -1
  120. package/src/lib/format/version.d.mts +2 -2
  121. package/src/lib/format/version.d.ts +2 -2
  122. package/src/lib/format/version.js +2 -2
  123. package/src/lib/format/version.js.map +1 -1
  124. package/src/lib/format/version.mjs +2 -2
  125. package/src/lib/format/version.mjs.map +1 -1
  126. package/src/lib/record/approvals-part.d.mts +6 -0
  127. package/src/lib/record/approvals-part.d.ts +6 -0
  128. package/src/lib/record/approvals-part.js +14 -0
  129. package/src/lib/record/approvals-part.js.map +1 -0
  130. package/src/lib/record/approvals-part.mjs +7 -0
  131. package/src/lib/record/approvals-part.mjs.map +1 -0
  132. package/src/lib/record/approving.d.mts +45 -0
  133. package/src/lib/record/approving.d.ts +45 -0
  134. package/src/lib/record/approving.js +10 -0
  135. package/src/lib/record/approving.js.map +1 -0
  136. package/src/lib/record/approving.mjs +7 -0
  137. package/src/lib/record/approving.mjs.map +1 -0
  138. package/src/lib/record/check.js +4 -0
  139. package/src/lib/record/check.js.map +1 -1
  140. package/src/lib/record/check.mjs +4 -0
  141. package/src/lib/record/check.mjs.map +1 -1
  142. package/src/lib/record/data-source.d.mts +270 -0
  143. package/src/lib/record/data-source.d.ts +270 -0
  144. package/src/lib/record/data-source.js.map +1 -1
  145. package/src/lib/record/data-source.mjs.map +1 -1
  146. package/src/lib/record/files-part.d.mts +7 -0
  147. package/src/lib/record/files-part.d.ts +7 -0
  148. package/src/lib/record/files-part.js +167 -0
  149. package/src/lib/record/files-part.js.map +1 -0
  150. package/src/lib/record/files-part.mjs +163 -0
  151. package/src/lib/record/files-part.mjs.map +1 -0
  152. package/src/lib/record/filter.d.mts +5 -0
  153. package/src/lib/record/filter.d.ts +5 -0
  154. package/src/lib/record/filter.js +1 -0
  155. package/src/lib/record/filter.js.map +1 -1
  156. package/src/lib/record/filter.mjs +1 -1
  157. package/src/lib/record/filter.mjs.map +1 -1
  158. package/src/lib/record/form-approvals.d.mts +47 -0
  159. package/src/lib/record/form-approvals.d.ts +47 -0
  160. package/src/lib/record/form-approvals.js +186 -0
  161. package/src/lib/record/form-approvals.js.map +1 -0
  162. package/src/lib/record/form-approvals.mjs +180 -0
  163. package/src/lib/record/form-approvals.mjs.map +1 -0
  164. package/src/lib/record/form.d.mts +32 -1
  165. package/src/lib/record/form.d.ts +32 -1
  166. package/src/lib/record/form.js +60 -15
  167. package/src/lib/record/form.js.map +1 -1
  168. package/src/lib/record/form.mjs +60 -15
  169. package/src/lib/record/form.mjs.map +1 -1
  170. package/src/lib/record/languages-part.d.mts +32 -0
  171. package/src/lib/record/languages-part.d.ts +32 -0
  172. package/src/lib/record/languages-part.js +104 -0
  173. package/src/lib/record/languages-part.js.map +1 -0
  174. package/src/lib/record/languages-part.mjs +96 -0
  175. package/src/lib/record/languages-part.mjs.map +1 -0
  176. package/src/lib/record/languages.d.mts +26 -0
  177. package/src/lib/record/languages.d.ts +26 -0
  178. package/src/lib/record/languages.js +20 -0
  179. package/src/lib/record/languages.js.map +1 -0
  180. package/src/lib/record/languages.mjs +15 -0
  181. package/src/lib/record/languages.mjs.map +1 -0
  182. package/src/lib/record/locales/ar.js +5 -0
  183. package/src/lib/record/locales/ar.js.map +1 -1
  184. package/src/lib/record/locales/ar.mjs +5 -0
  185. package/src/lib/record/locales/ar.mjs.map +1 -1
  186. package/src/lib/record/locales/de.js +5 -0
  187. package/src/lib/record/locales/de.js.map +1 -1
  188. package/src/lib/record/locales/de.mjs +5 -0
  189. package/src/lib/record/locales/de.mjs.map +1 -1
  190. package/src/lib/record/locales/fr.js +5 -0
  191. package/src/lib/record/locales/fr.js.map +1 -1
  192. package/src/lib/record/locales/fr.mjs +5 -0
  193. package/src/lib/record/locales/fr.mjs.map +1 -1
  194. package/src/lib/record/memory-approvals.d.mts +47 -0
  195. package/src/lib/record/memory-approvals.d.ts +47 -0
  196. package/src/lib/record/memory-approvals.js +78 -0
  197. package/src/lib/record/memory-approvals.js.map +1 -0
  198. package/src/lib/record/memory-approvals.mjs +75 -0
  199. package/src/lib/record/memory-approvals.mjs.map +1 -0
  200. package/src/lib/record/memory-assist.d.mts +53 -0
  201. package/src/lib/record/memory-assist.d.ts +53 -0
  202. package/src/lib/record/memory-assist.js +519 -0
  203. package/src/lib/record/memory-assist.js.map +1 -0
  204. package/src/lib/record/memory-assist.mjs +513 -0
  205. package/src/lib/record/memory-assist.mjs.map +1 -0
  206. package/src/lib/record/memory-clusters.d.mts +18 -0
  207. package/src/lib/record/memory-clusters.d.ts +18 -0
  208. package/src/lib/record/memory-clusters.js +83 -0
  209. package/src/lib/record/memory-clusters.js.map +1 -0
  210. package/src/lib/record/memory-clusters.mjs +80 -0
  211. package/src/lib/record/memory-clusters.mjs.map +1 -0
  212. package/src/lib/record/memory-data-source.d.mts +43 -3
  213. package/src/lib/record/memory-data-source.d.ts +43 -3
  214. package/src/lib/record/memory-data-source.js +105 -19
  215. package/src/lib/record/memory-data-source.js.map +1 -1
  216. package/src/lib/record/memory-data-source.mjs +105 -19
  217. package/src/lib/record/memory-data-source.mjs.map +1 -1
  218. package/src/lib/record/memory-later.d.mts +1 -1
  219. package/src/lib/record/memory-later.d.ts +1 -1
  220. package/src/lib/record/memory-later.js +4 -1
  221. package/src/lib/record/memory-later.js.map +1 -1
  222. package/src/lib/record/memory-later.mjs +4 -1
  223. package/src/lib/record/memory-later.mjs.map +1 -1
  224. package/src/lib/record/messages.d.mts +14 -0
  225. package/src/lib/record/messages.d.ts +14 -0
  226. package/src/lib/record/messages.js +5 -0
  227. package/src/lib/record/messages.js.map +1 -1
  228. package/src/lib/record/messages.mjs +5 -0
  229. package/src/lib/record/messages.mjs.map +1 -1
  230. package/src/lib/record/run.d.mts +6 -1
  231. package/src/lib/record/run.d.ts +6 -1
  232. package/src/lib/record/run.js.map +1 -1
  233. package/src/lib/record/run.mjs.map +1 -1
  234. package/src/lib/record/uploads.d.mts +35 -9
  235. package/src/lib/record/uploads.d.ts +35 -9
  236. package/src/lib/record/uploads.js +49 -169
  237. package/src/lib/record/uploads.js.map +1 -1
  238. package/src/lib/record/uploads.mjs +42 -164
  239. package/src/lib/record/uploads.mjs.map +1 -1
  240. package/src/lib/record/values.d.mts +23 -0
  241. package/src/lib/record/values.d.ts +23 -0
  242. package/src/lib/record/values.js +4 -0
  243. package/src/lib/record/values.js.map +1 -1
  244. package/src/lib/record/values.mjs +4 -0
  245. package/src/lib/record/values.mjs.map +1 -1
  246. package/src/runtime.d.mts +12 -5
  247. package/src/runtime.d.ts +12 -5
  248. package/src/runtime.js +18 -3
  249. package/src/runtime.js.map +1 -1
  250. package/src/runtime.mjs +7 -1
  251. package/src/runtime.mjs.map +1 -1
@@ -0,0 +1,47 @@
1
+ import type { ApprovalEntry, Approvals, ApprovalsRequest, DecideRequest, RecordVersion } from './data-source';
2
+ import { type RecordId, type Values } from './values';
3
+ /**
4
+ * The way a record is approved, as a memory source's `approvals` option works it out from the record: its levels in
5
+ * order — or all at once (`together`) — each with who may approve it there and whether any one of them or all must;
6
+ * whether it is sent for approval now (`asked`: a draft is not); why, in words; and what the record holds once every
7
+ * level approved it, or once one rejected it (its state moved on).
8
+ */
9
+ export interface MemoryApprovalRoute {
10
+ levels: {
11
+ id: string;
12
+ name: string;
13
+ approvers: {
14
+ id?: RecordId;
15
+ name: string;
16
+ }[];
17
+ needs?: 'any' | 'all';
18
+ }[];
19
+ together?: boolean;
20
+ /** Sent for approval now: its levels are asked. Until then they wait for it, and the record "needs approval". */
21
+ asked: boolean;
22
+ why?: string;
23
+ /** Values the record takes once every level approved it: `{ state: 'purchase' }`. */
24
+ approved?: Values;
25
+ /** Values it takes once a level rejected it: `{ state: 'rejected' }`. */
26
+ rejected?: Values;
27
+ }
28
+ /** What the memory source's approvals work on: its options, its records, and a record's version moved on. */
29
+ export interface MemoryApprovalsUse {
30
+ routes: Record<string, (values: Values, id: RecordId) => MemoryApprovalRoute | null | undefined> | undefined;
31
+ history: Record<string, ApprovalEntry[]> | undefined;
32
+ table(model: string): Record<string, Values>;
33
+ moved(model: string, id: RecordId, by: string): RecordVersion | undefined;
34
+ now(): string;
35
+ }
36
+ /**
37
+ * A memory source's approvals, as an approval engine keeps them: each decision kept, oldest first — a round of asking
38
+ * starting each time the record is sent for approval again (`requested`) — and where each level stands worked out from
39
+ * the route and the round: in turn unless the route says together; met by one approver, or by every one where it needs
40
+ * all; a rejection ending the round. A decision is refused where the level is not waiting, the person is none of its
41
+ * approvers or decided already, or a rejection says no reason; the last approval writes the route's `approved` values on
42
+ * the record, a rejection its `rejected` ones.
43
+ */
44
+ export declare function memoryApprovals(use: MemoryApprovalsUse): {
45
+ approvals(request: ApprovalsRequest): Approvals;
46
+ decide(request: DecideRequest): Approvals;
47
+ };
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.memoryApprovals = memoryApprovals;
4
+ const data_source_1 = require("./data-source");
5
+ const values_1 = require("./values");
6
+ /** The same person: by their ids where both have one, else by their names. */
7
+ const same = (approver, person) => !!person && (approver.id !== undefined && person.id !== null && person.id !== undefined ? String(approver.id) === String(person.id) : !!person.name && approver.name === person.name);
8
+ /**
9
+ * A memory source's approvals, as an approval engine keeps them: each decision kept, oldest first — a round of asking
10
+ * starting each time the record is sent for approval again (`requested`) — and where each level stands worked out from
11
+ * the route and the round: in turn unless the route says together; met by one approver, or by every one where it needs
12
+ * all; a rejection ending the round. A decision is refused where the level is not waiting, the person is none of its
13
+ * approvers or decided already, or a rejection says no reason; the last approval writes the route's `approved` values on
14
+ * the record, a rejection its `rejected` ones.
15
+ */
16
+ function memoryApprovals(use) {
17
+ const histories = (0, values_1.structuredCopy)(use.history ?? {});
18
+ /** Whether each record was sent for approval when last asked: sent again since, a round starts. */
19
+ const sent = new Map();
20
+ function routed(model, id, person) {
21
+ const values = use.table(model)[String(id)];
22
+ if (!values)
23
+ throw new Error(`No ${model} record with id ${id}`);
24
+ const route = use.routes?.[model]?.((0, values_1.structuredCopy)(values), id) ?? null;
25
+ const key = `${model}:${id}`;
26
+ const history = (histories[key] ??= []);
27
+ if (!route?.levels.length)
28
+ return { levels: [], history: (0, values_1.structuredCopy)(history) };
29
+ if (route.asked && sent.get(key) === false)
30
+ history.push({ decision: 'requested', by: person?.name || 'Someone', at: use.now() });
31
+ sent.set(key, route.asked);
32
+ const round = history.slice(history.map((entry) => entry.decision).lastIndexOf('requested') + 1);
33
+ const ended = round.some((entry) => entry.decision === 'rejected');
34
+ let before = true;
35
+ const levels = route.levels.map((level) => {
36
+ const approvers = level.approvers.map((approver) => {
37
+ const said = round.find((entry) => entry.level === level.id && entry.by === approver.name && entry.decision !== 'requested');
38
+ return { ...approver, ...(said ? { decision: said.decision, at: said.at } : {}) };
39
+ });
40
+ const needs = level.needs ?? 'any';
41
+ const approved = needs === 'all' ? approvers.every((one) => one.decision === 'approved') : approvers.some((one) => one.decision === 'approved');
42
+ const state = approvers.some((one) => one.decision === 'rejected') ? 'rejected' : approved ? 'approved' : route.asked && !ended && (route.together || before) ? 'waiting' : 'later';
43
+ if (state !== 'approved')
44
+ before = false;
45
+ return { id: level.id, name: level.name, approvers, needs, state };
46
+ });
47
+ const mine = levels.filter((level) => level.state === 'waiting' && level.approvers.some((one) => !one.decision && same(one, person))).map((level) => level.id);
48
+ return { levels, ...(route.together ? { together: true } : {}), history: (0, values_1.structuredCopy)(history), mine, ...(route.why ? { why: route.why } : {}) };
49
+ }
50
+ return {
51
+ approvals: (request) => routed(request.model, request.recordId, request.user),
52
+ decide(request) {
53
+ const { model, recordId } = request;
54
+ const now = routed(model, recordId, request.user);
55
+ const level = now.levels.find((one) => one.id === request.level);
56
+ if (!level || level.state !== 'waiting')
57
+ throw (0, data_source_1.saveRefused)({ kind: 'rule', message: `${level?.name ?? request.level} is not waiting for a decision` });
58
+ const approver = level.approvers.find((one) => same(one, request.user));
59
+ if (!approver || approver.decision)
60
+ throw (0, data_source_1.saveRefused)({ kind: 'rule', message: `${request.user?.name || 'This person'} may not decide for ${level.name} now` });
61
+ const reason = request.reason?.trim();
62
+ if (request.decision === 'rejected' && !reason)
63
+ throw (0, data_source_1.saveRefused)({ kind: 'rule', message: 'A rejection says why' });
64
+ (histories[`${model}:${recordId}`] ??= []).push({ decision: request.decision, level: level.id, by: approver.name, at: use.now(), ...(reason ? { reason } : {}) });
65
+ const after = routed(model, recordId, request.user);
66
+ const asked = after.levels.filter((one) => one.state !== 'skipped');
67
+ const route = use.routes?.[model]?.((0, values_1.structuredCopy)(use.table(model)[String(recordId)]), recordId);
68
+ // The last approval moves the record on, as a rejection does: its state, as the route says.
69
+ const ending = asked.some((one) => one.state === 'rejected') ? route?.rejected : asked.length && asked.every((one) => one.state === 'approved') ? route?.approved : undefined;
70
+ if (!ending)
71
+ return after;
72
+ Object.assign(use.table(model)[String(recordId)], (0, values_1.structuredCopy)(ending));
73
+ use.moved(model, recordId, approver.name);
74
+ return routed(model, recordId, request.user);
75
+ },
76
+ };
77
+ }
78
+ //# sourceMappingURL=memory-approvals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-approvals.js","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/record/memory-approvals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import { saveRefused } from './data-source.mjs';
2
+ import { structuredCopy } from './values.mjs';
3
+ /** The same person: by their ids where both have one, else by their names. */
4
+ const same = (approver, person) => !!person && (approver.id !== undefined && person.id !== null && person.id !== undefined ? String(approver.id) === String(person.id) : !!person.name && approver.name === person.name);
5
+ /**
6
+ * A memory source's approvals, as an approval engine keeps them: each decision kept, oldest first — a round of asking
7
+ * starting each time the record is sent for approval again (`requested`) — and where each level stands worked out from
8
+ * the route and the round: in turn unless the route says together; met by one approver, or by every one where it needs
9
+ * all; a rejection ending the round. A decision is refused where the level is not waiting, the person is none of its
10
+ * approvers or decided already, or a rejection says no reason; the last approval writes the route's `approved` values on
11
+ * the record, a rejection its `rejected` ones.
12
+ */
13
+ export function memoryApprovals(use) {
14
+ const histories = structuredCopy(use.history ?? {});
15
+ /** Whether each record was sent for approval when last asked: sent again since, a round starts. */
16
+ const sent = new Map();
17
+ function routed(model, id, person) {
18
+ const values = use.table(model)[String(id)];
19
+ if (!values)
20
+ throw new Error(`No ${model} record with id ${id}`);
21
+ const route = use.routes?.[model]?.(structuredCopy(values), id) ?? null;
22
+ const key = `${model}:${id}`;
23
+ const history = (histories[key] ??= []);
24
+ if (!route?.levels.length)
25
+ return { levels: [], history: structuredCopy(history) };
26
+ if (route.asked && sent.get(key) === false)
27
+ history.push({ decision: 'requested', by: person?.name || 'Someone', at: use.now() });
28
+ sent.set(key, route.asked);
29
+ const round = history.slice(history.map((entry) => entry.decision).lastIndexOf('requested') + 1);
30
+ const ended = round.some((entry) => entry.decision === 'rejected');
31
+ let before = true;
32
+ const levels = route.levels.map((level) => {
33
+ const approvers = level.approvers.map((approver) => {
34
+ const said = round.find((entry) => entry.level === level.id && entry.by === approver.name && entry.decision !== 'requested');
35
+ return { ...approver, ...(said ? { decision: said.decision, at: said.at } : {}) };
36
+ });
37
+ const needs = level.needs ?? 'any';
38
+ const approved = needs === 'all' ? approvers.every((one) => one.decision === 'approved') : approvers.some((one) => one.decision === 'approved');
39
+ const state = approvers.some((one) => one.decision === 'rejected') ? 'rejected' : approved ? 'approved' : route.asked && !ended && (route.together || before) ? 'waiting' : 'later';
40
+ if (state !== 'approved')
41
+ before = false;
42
+ return { id: level.id, name: level.name, approvers, needs, state };
43
+ });
44
+ const mine = levels.filter((level) => level.state === 'waiting' && level.approvers.some((one) => !one.decision && same(one, person))).map((level) => level.id);
45
+ return { levels, ...(route.together ? { together: true } : {}), history: structuredCopy(history), mine, ...(route.why ? { why: route.why } : {}) };
46
+ }
47
+ return {
48
+ approvals: (request) => routed(request.model, request.recordId, request.user),
49
+ decide(request) {
50
+ const { model, recordId } = request;
51
+ const now = routed(model, recordId, request.user);
52
+ const level = now.levels.find((one) => one.id === request.level);
53
+ if (!level || level.state !== 'waiting')
54
+ throw saveRefused({ kind: 'rule', message: `${level?.name ?? request.level} is not waiting for a decision` });
55
+ const approver = level.approvers.find((one) => same(one, request.user));
56
+ if (!approver || approver.decision)
57
+ throw saveRefused({ kind: 'rule', message: `${request.user?.name || 'This person'} may not decide for ${level.name} now` });
58
+ const reason = request.reason?.trim();
59
+ if (request.decision === 'rejected' && !reason)
60
+ throw saveRefused({ kind: 'rule', message: 'A rejection says why' });
61
+ (histories[`${model}:${recordId}`] ??= []).push({ decision: request.decision, level: level.id, by: approver.name, at: use.now(), ...(reason ? { reason } : {}) });
62
+ const after = routed(model, recordId, request.user);
63
+ const asked = after.levels.filter((one) => one.state !== 'skipped');
64
+ const route = use.routes?.[model]?.(structuredCopy(use.table(model)[String(recordId)]), recordId);
65
+ // The last approval moves the record on, as a rejection does: its state, as the route says.
66
+ const ending = asked.some((one) => one.state === 'rejected') ? route?.rejected : asked.length && asked.every((one) => one.state === 'approved') ? route?.approved : undefined;
67
+ if (!ending)
68
+ return after;
69
+ Object.assign(use.table(model)[String(recordId)], structuredCopy(ending));
70
+ use.moved(model, recordId, approver.name);
71
+ return routed(model, recordId, request.user);
72
+ },
73
+ };
74
+ }
75
+ //# sourceMappingURL=memory-approvals.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-approvals.mjs","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/record/memory-approvals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { RewriteRequest } from './data-source.mjs';
2
+ import type { MemoryAssist } from './memory-data-source.mjs';
3
+ import type { Values } from './values.mjs';
4
+ /**
5
+ * A stand-in for an app's AI, for demos and tests: no model, no keys, nothing sent anywhere — words read by their
6
+ * patterns, the same every time. `createMemoryDataSource({ assist: assistStandIn() })` answers AI help with it:
7
+ *
8
+ * - **suggest**: an e-mail signature or a message read line by line — a person's name (two to four words with
9
+ * capitals), a job title and a company (by the words they hold: "Director", "Ltd", "Traders"), e-mails, phones (a
10
+ * mobile by its "M:" or "Mobile"), a website, a street, a city and a country (the country among the records a link
11
+ * can point to), a tax id — each given to the field whose name or label says it holds one; a choice by an option's
12
+ * words in the text, a number by the words of its label beside it ("3 bedrooms"); a table's lines as the person
13
+ * found (a name with an e-mail, a phone or a job), or as lines of "2 x Dental chair @ 4,500". Text files are read;
14
+ * any other file is said not to be.
15
+ * - **rewrite**: a draft from a template of the app's (`drafts`, by `model.field`), else a short reply that greets the
16
+ * person the record names; polish puts capitals and full stops in, writes out "u", "pls", "asap" and the like, and
17
+ * tidies the spaces.
18
+ * - **searchToFilter**: "villas in New Cairo under 15M" — a choice's options and a link's records by their names
19
+ * (singular or plural, the longest first), a number by "under", "over", "between … and …", "3+" and the words of a
20
+ * field's label or unit beside it ("3 bedrooms", "200 m²"), else the first amount; "15M", "2.5 million", "800k".
21
+ *
22
+ * Every answer says so in its `why`: "Stand-in AI, no model: …". An app answers with its own model instead.
23
+ */
24
+ export interface StandInOptions {
25
+ /** A draft's words, by the text field's `model.field` (or `pageId.field`), from the record: a template of the app's. */
26
+ drafts?: Record<string, (values: Values, request: RewriteRequest) => string>;
27
+ }
28
+ /** A number as people write it in a search or a message: "15M", "2.5 million", "800k", "15,000,000", "1.5". */
29
+ export declare function standInNumber(digits: string, unit?: string): number | null;
30
+ /** What a signature or a message holds, as a stand-in reads it. */
31
+ export interface StandInFound {
32
+ person?: string;
33
+ title?: string;
34
+ company?: string;
35
+ emails: string[];
36
+ phones: string[];
37
+ mobile?: string;
38
+ website?: string;
39
+ street?: string;
40
+ city?: string;
41
+ /** The last part of an address: a country's name, looked for among the records later. */
42
+ place?: string;
43
+ tax?: string;
44
+ }
45
+ /** A signature or a message, read line by line by its patterns. */
46
+ export declare function readSignature(text: string): StandInFound;
47
+ /** Words made clearer, the same every time: what it did, in words. */
48
+ export declare function polishText(text: string): {
49
+ text: string;
50
+ did: string[];
51
+ };
52
+ /** The stand-in's AI help, for a memory source's `assist`: see above. */
53
+ export declare function assistStandIn(options?: StandInOptions): Required<MemoryAssist>;
@@ -0,0 +1,53 @@
1
+ import type { RewriteRequest } from './data-source';
2
+ import type { MemoryAssist } from './memory-data-source';
3
+ import type { Values } from './values';
4
+ /**
5
+ * A stand-in for an app's AI, for demos and tests: no model, no keys, nothing sent anywhere — words read by their
6
+ * patterns, the same every time. `createMemoryDataSource({ assist: assistStandIn() })` answers AI help with it:
7
+ *
8
+ * - **suggest**: an e-mail signature or a message read line by line — a person's name (two to four words with
9
+ * capitals), a job title and a company (by the words they hold: "Director", "Ltd", "Traders"), e-mails, phones (a
10
+ * mobile by its "M:" or "Mobile"), a website, a street, a city and a country (the country among the records a link
11
+ * can point to), a tax id — each given to the field whose name or label says it holds one; a choice by an option's
12
+ * words in the text, a number by the words of its label beside it ("3 bedrooms"); a table's lines as the person
13
+ * found (a name with an e-mail, a phone or a job), or as lines of "2 x Dental chair @ 4,500". Text files are read;
14
+ * any other file is said not to be.
15
+ * - **rewrite**: a draft from a template of the app's (`drafts`, by `model.field`), else a short reply that greets the
16
+ * person the record names; polish puts capitals and full stops in, writes out "u", "pls", "asap" and the like, and
17
+ * tidies the spaces.
18
+ * - **searchToFilter**: "villas in New Cairo under 15M" — a choice's options and a link's records by their names
19
+ * (singular or plural, the longest first), a number by "under", "over", "between … and …", "3+" and the words of a
20
+ * field's label or unit beside it ("3 bedrooms", "200 m²"), else the first amount; "15M", "2.5 million", "800k".
21
+ *
22
+ * Every answer says so in its `why`: "Stand-in AI, no model: …". An app answers with its own model instead.
23
+ */
24
+ export interface StandInOptions {
25
+ /** A draft's words, by the text field's `model.field` (or `pageId.field`), from the record: a template of the app's. */
26
+ drafts?: Record<string, (values: Values, request: RewriteRequest) => string>;
27
+ }
28
+ /** A number as people write it in a search or a message: "15M", "2.5 million", "800k", "15,000,000", "1.5". */
29
+ export declare function standInNumber(digits: string, unit?: string): number | null;
30
+ /** What a signature or a message holds, as a stand-in reads it. */
31
+ export interface StandInFound {
32
+ person?: string;
33
+ title?: string;
34
+ company?: string;
35
+ emails: string[];
36
+ phones: string[];
37
+ mobile?: string;
38
+ website?: string;
39
+ street?: string;
40
+ city?: string;
41
+ /** The last part of an address: a country's name, looked for among the records later. */
42
+ place?: string;
43
+ tax?: string;
44
+ }
45
+ /** A signature or a message, read line by line by its patterns. */
46
+ export declare function readSignature(text: string): StandInFound;
47
+ /** Words made clearer, the same every time: what it did, in words. */
48
+ export declare function polishText(text: string): {
49
+ text: string;
50
+ did: string[];
51
+ };
52
+ /** The stand-in's AI help, for a memory source's `assist`: see above. */
53
+ export declare function assistStandIn(options?: StandInOptions): Required<MemoryAssist>;