@adminiumjs/manifest 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/add-ons.d.ts +63 -0
- package/dist/add-ons.d.ts.map +1 -0
- package/dist/add-ons.js +100 -0
- package/dist/add-ons.js.map +1 -0
- package/dist/documents.d.ts +115 -0
- package/dist/documents.d.ts.map +1 -0
- package/dist/documents.js +171 -0
- package/dist/documents.js.map +1 -0
- package/dist/formula.d.ts +124 -0
- package/dist/formula.d.ts.map +1 -0
- package/dist/formula.js +428 -0
- package/dist/formula.js.map +1 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -4
- package/dist/index.js.map +1 -1
- package/dist/outbox.d.ts +354 -9
- package/dist/outbox.d.ts.map +1 -1
- package/dist/outbox.js +223 -9
- package/dist/outbox.js.map +1 -1
- package/dist/plan-model.d.ts +1 -1
- package/dist/plan-model.d.ts.map +1 -1
- package/dist/public-access.d.ts +48 -7
- package/dist/public-access.d.ts.map +1 -1
- package/dist/public-access.js +235 -28
- package/dist/public-access.js.map +1 -1
- package/dist/refs.d.ts +17 -0
- package/dist/refs.d.ts.map +1 -1
- package/dist/refs.js +12 -0
- package/dist/refs.js.map +1 -1
- package/dist/sample.d.ts +49 -1
- package/dist/sample.d.ts.map +1 -1
- package/dist/sample.js +39 -2
- package/dist/sample.js.map +1 -1
- package/dist/schema.d.ts +2501 -158
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +518 -26
- package/dist/schema.js.map +1 -1
- package/dist/shapes.d.ts +97 -0
- package/dist/shapes.d.ts.map +1 -0
- package/dist/shapes.js +190 -0
- package/dist/shapes.js.map +1 -0
- package/dist/states.d.ts +134 -0
- package/dist/states.d.ts.map +1 -0
- package/dist/states.js +244 -0
- package/dist/states.js.map +1 -0
- package/dist/validate.d.ts +14 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +29 -2
- package/dist/validate.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `addOns` — the add-ons an app needs, and the ones it works better with.
|
|
3
|
+
*
|
|
4
|
+
* ```json
|
|
5
|
+
* "addOns": {
|
|
6
|
+
* "requires": [{ "key": "invoices", "range": ">=1.1.0",
|
|
7
|
+
* "reason": { "en-US": "Proposals, invoices and receipts are made by this add-on." } }],
|
|
8
|
+
* "suggests": [{ "key": "holiday-calendars", "range": ">=1.1.0", "checked": true,
|
|
9
|
+
* "reason": { "en-US": "Marks public holidays as days off." } }],
|
|
10
|
+
* "features": [{ "id": "capacity-holidays", "label": { "en-US": "Holidays in Capacity" },
|
|
11
|
+
* "requires": ["holiday-calendars"] }]
|
|
12
|
+
* }
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Installing the app installs (or connects) every add-on it `requires`,
|
|
16
|
+
* before its own tables; one that is missing and cannot be had refuses the
|
|
17
|
+
* install before anything is written, and an add-on an installed app
|
|
18
|
+
* requires cannot be removed. A `suggests` entry is offered with the app,
|
|
19
|
+
* ticked when `checked`. A `feature` names what stops working without its
|
|
20
|
+
* add-ons, so a page may say `"feature": "<id>"` and stay hidden until they
|
|
21
|
+
* are there.
|
|
22
|
+
*
|
|
23
|
+
* Every `range` is a semver range over the add-on's version (`>=1.1.0`,
|
|
24
|
+
* `^1.1.0`); the reasons and labels are in every language the app speaks,
|
|
25
|
+
* US English always among them.
|
|
26
|
+
*/
|
|
27
|
+
import { z } from 'zod';
|
|
28
|
+
import { type ReferenceIssue } from './refs.js';
|
|
29
|
+
export declare const addOnsSchema: z.ZodObject<{
|
|
30
|
+
requires: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
31
|
+
key: z.ZodString;
|
|
32
|
+
range: z.ZodString;
|
|
33
|
+
reason: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
34
|
+
}, z.core.$strict>>>;
|
|
35
|
+
suggests: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
36
|
+
key: z.ZodString;
|
|
37
|
+
range: z.ZodString;
|
|
38
|
+
reason: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
39
|
+
checked: z.ZodOptional<z.ZodBoolean>;
|
|
40
|
+
}, z.core.$strict>>>;
|
|
41
|
+
features: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
42
|
+
id: z.ZodString;
|
|
43
|
+
label: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
44
|
+
requires: z.ZodArray<z.ZodString>;
|
|
45
|
+
}, z.core.$strict>>>;
|
|
46
|
+
}, z.core.$strict>;
|
|
47
|
+
export type AddOnNeeds = z.infer<typeof addOnsSchema>;
|
|
48
|
+
/** The add-ons an app names at all: required first, then suggested. */
|
|
49
|
+
export declare function namedAddOns(needs: AddOnNeeds | undefined): {
|
|
50
|
+
key: string;
|
|
51
|
+
need: 'requires' | 'suggests';
|
|
52
|
+
}[];
|
|
53
|
+
/** Whether the app requires this add-on (so it is always there when the app runs). */
|
|
54
|
+
export declare function requiresAddOn(needs: AddOnNeeds | undefined, key: string): boolean;
|
|
55
|
+
/** Everything wrong with an app's `addOns` and the pages that name a feature. */
|
|
56
|
+
export declare function addOnNeedsIssues(m: {
|
|
57
|
+
key: string;
|
|
58
|
+
addOns?: AddOnNeeds | undefined;
|
|
59
|
+
pages?: readonly {
|
|
60
|
+
feature?: string | undefined;
|
|
61
|
+
}[] | undefined;
|
|
62
|
+
}): ReferenceIssue[];
|
|
63
|
+
//# sourceMappingURL=add-ons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-ons.d.ts","sourceRoot":"","sources":["../src/add-ons.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAa9D,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;kBAkBd,CAAC;AACZ,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAEtD,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAAA;CAAE,EAAE,CAK3G;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAEjF;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,CAAC,EAAE;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,KAAK,CAAC,EAAE,SAAS;QAAE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,EAAE,GAAG,SAAS,CAAC;CACjE,GAAG,cAAc,EAAE,CA6BnB"}
|
package/dist/add-ons.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
/**
|
|
3
|
+
* `addOns` — the add-ons an app needs, and the ones it works better with.
|
|
4
|
+
*
|
|
5
|
+
* ```json
|
|
6
|
+
* "addOns": {
|
|
7
|
+
* "requires": [{ "key": "invoices", "range": ">=1.1.0",
|
|
8
|
+
* "reason": { "en-US": "Proposals, invoices and receipts are made by this add-on." } }],
|
|
9
|
+
* "suggests": [{ "key": "holiday-calendars", "range": ">=1.1.0", "checked": true,
|
|
10
|
+
* "reason": { "en-US": "Marks public holidays as days off." } }],
|
|
11
|
+
* "features": [{ "id": "capacity-holidays", "label": { "en-US": "Holidays in Capacity" },
|
|
12
|
+
* "requires": ["holiday-calendars"] }]
|
|
13
|
+
* }
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Installing the app installs (or connects) every add-on it `requires`,
|
|
17
|
+
* before its own tables; one that is missing and cannot be had refuses the
|
|
18
|
+
* install before anything is written, and an add-on an installed app
|
|
19
|
+
* requires cannot be removed. A `suggests` entry is offered with the app,
|
|
20
|
+
* ticked when `checked`. A `feature` names what stops working without its
|
|
21
|
+
* add-ons, so a page may say `"feature": "<id>"` and stay hidden until they
|
|
22
|
+
* are there.
|
|
23
|
+
*
|
|
24
|
+
* Every `range` is a semver range over the add-on's version (`>=1.1.0`,
|
|
25
|
+
* `^1.1.0`); the reasons and labels are in every language the app speaks,
|
|
26
|
+
* US English always among them.
|
|
27
|
+
*/
|
|
28
|
+
import { z } from 'zod';
|
|
29
|
+
import { labelsSchema } from './refs.js';
|
|
30
|
+
import { parseSemverRange } from './semver.js';
|
|
31
|
+
const addOnKeySchema = z.string().regex(/^[a-z][a-z0-9-]{1,79}$/, 'an add-on key');
|
|
32
|
+
const rangeSchema = z
|
|
33
|
+
.string()
|
|
34
|
+
.min(1)
|
|
35
|
+
.max(120)
|
|
36
|
+
.refine((range) => parseSemverRange(range) !== null, { message: 'a semver range such as ">=1.1.0" or "^1.1.0"' });
|
|
37
|
+
const needSchema = z.object({ key: addOnKeySchema, range: rangeSchema, reason: labelsSchema }).strict();
|
|
38
|
+
export const addOnsSchema = z
|
|
39
|
+
.object({
|
|
40
|
+
requires: z.array(needSchema).min(1).max(8).optional(),
|
|
41
|
+
suggests: z.array(needSchema.extend({ checked: z.boolean().optional() }).strict()).min(1).max(8).optional(),
|
|
42
|
+
features: z
|
|
43
|
+
.array(z
|
|
44
|
+
.object({
|
|
45
|
+
id: z.string().regex(/^[a-z][a-z0-9-]{0,39}$/, 'a feature id is kebab-case'),
|
|
46
|
+
label: labelsSchema,
|
|
47
|
+
requires: z.array(addOnKeySchema).min(1).max(4),
|
|
48
|
+
})
|
|
49
|
+
.strict())
|
|
50
|
+
.min(1)
|
|
51
|
+
.max(16)
|
|
52
|
+
.optional(),
|
|
53
|
+
})
|
|
54
|
+
.strict();
|
|
55
|
+
/** The add-ons an app names at all: required first, then suggested. */
|
|
56
|
+
export function namedAddOns(needs) {
|
|
57
|
+
return [
|
|
58
|
+
...(needs?.requires ?? []).map((n) => ({ key: n.key, need: 'requires' })),
|
|
59
|
+
...(needs?.suggests ?? []).map((n) => ({ key: n.key, need: 'suggests' })),
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
/** Whether the app requires this add-on (so it is always there when the app runs). */
|
|
63
|
+
export function requiresAddOn(needs, key) {
|
|
64
|
+
return (needs?.requires ?? []).some((n) => n.key === key);
|
|
65
|
+
}
|
|
66
|
+
/** Everything wrong with an app's `addOns` and the pages that name a feature. */
|
|
67
|
+
export function addOnNeedsIssues(m) {
|
|
68
|
+
const out = [];
|
|
69
|
+
const needs = m.addOns;
|
|
70
|
+
const seen = new Map();
|
|
71
|
+
for (const list of ['requires', 'suggests']) {
|
|
72
|
+
(needs?.[list] ?? []).forEach((need, i) => {
|
|
73
|
+
const at = ['addOns', list, i, 'key'];
|
|
74
|
+
if (need.key === m.key)
|
|
75
|
+
out.push({ path: at, message: 'an app does not need itself' });
|
|
76
|
+
const earlier = seen.get(need.key);
|
|
77
|
+
if (earlier !== undefined)
|
|
78
|
+
out.push({ path: at, message: `"${need.key}" is already named in ${earlier}` });
|
|
79
|
+
seen.set(need.key, list);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const features = new Set();
|
|
83
|
+
(needs?.features ?? []).forEach((feature, f) => {
|
|
84
|
+
if (features.has(feature.id))
|
|
85
|
+
out.push({ path: ['addOns', 'features', f, 'id'], message: `"${feature.id}" is declared twice` });
|
|
86
|
+
features.add(feature.id);
|
|
87
|
+
feature.requires.forEach((key, k) => {
|
|
88
|
+
if (!seen.has(key)) {
|
|
89
|
+
out.push({ path: ['addOns', 'features', f, 'requires', k], message: `"${key}" is neither required nor suggested by the app` });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
(m.pages ?? []).forEach((page, p) => {
|
|
94
|
+
if (page.feature !== undefined && !features.has(page.feature)) {
|
|
95
|
+
out.push({ path: ['pages', p, 'feature'], message: `"${page.feature}" is not one of the app's addOns.features` });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=add-ons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-ons.js","sourceRoot":"","sources":["../src/add-ons.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document profiles an app ships: which of its columns make a printed
|
|
3
|
+
* document (an invoice, a receipt, a statement), drawn by an add-on that
|
|
4
|
+
* renders documents.
|
|
5
|
+
*
|
|
6
|
+
* ```json
|
|
7
|
+
* "documents": [{
|
|
8
|
+
* "kind": "receipt", "addOn": "invoices", "table": "sales",
|
|
9
|
+
* "name": { "en-US": "Till receipt" },
|
|
10
|
+
* "mapping": {
|
|
11
|
+
* "number": { "column": "number" },
|
|
12
|
+
* "customerName": { "via": "customer_id", "column": "name" },
|
|
13
|
+
* "lines": { "collection": { "table": "sale_lines", "via": "sale_id", "orderBy": "position",
|
|
14
|
+
* "columns": { "description": "name", "qty": "qty", "amount": "amount" } } }
|
|
15
|
+
* }
|
|
16
|
+
* }]
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* A slot reads a column of the row, a column of a row a foreign key points at
|
|
20
|
+
* (`via`), or a list of child rows in order. A list may leave rows out by
|
|
21
|
+
* their own columns, the way a statement's sources do: `where` keeps only the
|
|
22
|
+
* rows whose column holds one of its values, and `unless` drops a row whose
|
|
23
|
+
* column is true or set (a voided line: `"unless": "voided"`). The slots
|
|
24
|
+
* themselves are the add-on's words; Adminium checks the columns here and the
|
|
25
|
+
* slots against the add-on when the profile is made at install. A profile is
|
|
26
|
+
* the app's: made with real table names when the app is installed, removed
|
|
27
|
+
* with it.
|
|
28
|
+
*
|
|
29
|
+
* A statement is a document over one row (a client) and a period: it lists
|
|
30
|
+
* the documents and payments that point at that row, so it names them
|
|
31
|
+
* instead of one child list.
|
|
32
|
+
*/
|
|
33
|
+
import { z } from 'zod';
|
|
34
|
+
import { type ReferenceIssue, type TableIndex } from './refs.js';
|
|
35
|
+
export declare const slotMappingSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
36
|
+
column: z.ZodString;
|
|
37
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
38
|
+
via: z.ZodString;
|
|
39
|
+
column: z.ZodString;
|
|
40
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
41
|
+
collection: z.ZodObject<{
|
|
42
|
+
table: z.ZodString;
|
|
43
|
+
via: z.ZodString;
|
|
44
|
+
orderBy: z.ZodOptional<z.ZodString>;
|
|
45
|
+
columns: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
46
|
+
where: z.ZodOptional<z.ZodObject<{
|
|
47
|
+
column: z.ZodString;
|
|
48
|
+
in: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
49
|
+
}, z.core.$strict>>;
|
|
50
|
+
unless: z.ZodOptional<z.ZodString>;
|
|
51
|
+
}, z.core.$strict>;
|
|
52
|
+
}, z.core.$strict>]>;
|
|
53
|
+
export type SlotMapping = z.infer<typeof slotMappingSchema>;
|
|
54
|
+
export declare const appDocumentSchema: z.ZodObject<{
|
|
55
|
+
kind: z.ZodString;
|
|
56
|
+
addOn: z.ZodString;
|
|
57
|
+
table: z.ZodString;
|
|
58
|
+
name: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
|
|
59
|
+
mapping: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
|
|
60
|
+
column: z.ZodString;
|
|
61
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
62
|
+
via: z.ZodString;
|
|
63
|
+
column: z.ZodString;
|
|
64
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
65
|
+
collection: z.ZodObject<{
|
|
66
|
+
table: z.ZodString;
|
|
67
|
+
via: z.ZodString;
|
|
68
|
+
orderBy: z.ZodOptional<z.ZodString>;
|
|
69
|
+
columns: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
70
|
+
where: z.ZodOptional<z.ZodObject<{
|
|
71
|
+
column: z.ZodString;
|
|
72
|
+
in: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
73
|
+
}, z.core.$strict>>;
|
|
74
|
+
unless: z.ZodOptional<z.ZodString>;
|
|
75
|
+
}, z.core.$strict>;
|
|
76
|
+
}, z.core.$strict>]>>;
|
|
77
|
+
statement: z.ZodOptional<z.ZodObject<{
|
|
78
|
+
documents: z.ZodObject<{
|
|
79
|
+
table: z.ZodString;
|
|
80
|
+
via: z.ZodString;
|
|
81
|
+
date: z.ZodString;
|
|
82
|
+
amount: z.ZodString;
|
|
83
|
+
number: z.ZodOptional<z.ZodString>;
|
|
84
|
+
where: z.ZodOptional<z.ZodObject<{
|
|
85
|
+
column: z.ZodString;
|
|
86
|
+
in: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
87
|
+
}, z.core.$strict>>;
|
|
88
|
+
unless: z.ZodOptional<z.ZodString>;
|
|
89
|
+
}, z.core.$strict>;
|
|
90
|
+
payments: z.ZodObject<{
|
|
91
|
+
table: z.ZodString;
|
|
92
|
+
via: z.ZodString;
|
|
93
|
+
date: z.ZodString;
|
|
94
|
+
amount: z.ZodString;
|
|
95
|
+
number: z.ZodOptional<z.ZodString>;
|
|
96
|
+
where: z.ZodOptional<z.ZodObject<{
|
|
97
|
+
column: z.ZodString;
|
|
98
|
+
in: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
99
|
+
}, z.core.$strict>>;
|
|
100
|
+
unless: z.ZodOptional<z.ZodString>;
|
|
101
|
+
}, z.core.$strict>;
|
|
102
|
+
}, z.core.$strict>>;
|
|
103
|
+
feature: z.ZodOptional<z.ZodString>;
|
|
104
|
+
requestValues: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
105
|
+
}, z.core.$strict>;
|
|
106
|
+
export type AppDocument = z.infer<typeof appDocumentSchema>;
|
|
107
|
+
/** Every column a mapping names, against the manifest's tables. */
|
|
108
|
+
export declare function mappingIssues(table: string, mapping: Readonly<Record<string, SlotMapping>>, index: TableIndex, at: (...rest: (string | number)[]) => (string | number)[]): ReferenceIssue[];
|
|
109
|
+
/** Everything wrong with an app's `documents` against its tables and add-on needs. */
|
|
110
|
+
export declare function appDocumentIssues(documents: readonly AppDocument[], ctx: {
|
|
111
|
+
index: TableIndex;
|
|
112
|
+
addOns: ReadonlySet<string>;
|
|
113
|
+
features: ReadonlySet<string>;
|
|
114
|
+
}): ReferenceIssue[];
|
|
115
|
+
//# sourceMappingURL=documents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../src/documents.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAA2B,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAO1F,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;oBAmB5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAgB5D,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAkBnB,CAAC;AACZ,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,mEAAmE;AACnE,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,EAC9C,KAAK,EAAE,UAAU,EACjB,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GACxD,cAAc,EAAE,CA2BlB;AAED,sFAAsF;AACtF,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,SAAS,WAAW,EAAE,EACjC,GAAG,EAAE;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAAE,GACrF,cAAc,EAAE,CAqClB"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
/**
|
|
3
|
+
* Document profiles an app ships: which of its columns make a printed
|
|
4
|
+
* document (an invoice, a receipt, a statement), drawn by an add-on that
|
|
5
|
+
* renders documents.
|
|
6
|
+
*
|
|
7
|
+
* ```json
|
|
8
|
+
* "documents": [{
|
|
9
|
+
* "kind": "receipt", "addOn": "invoices", "table": "sales",
|
|
10
|
+
* "name": { "en-US": "Till receipt" },
|
|
11
|
+
* "mapping": {
|
|
12
|
+
* "number": { "column": "number" },
|
|
13
|
+
* "customerName": { "via": "customer_id", "column": "name" },
|
|
14
|
+
* "lines": { "collection": { "table": "sale_lines", "via": "sale_id", "orderBy": "position",
|
|
15
|
+
* "columns": { "description": "name", "qty": "qty", "amount": "amount" } } }
|
|
16
|
+
* }
|
|
17
|
+
* }]
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* A slot reads a column of the row, a column of a row a foreign key points at
|
|
21
|
+
* (`via`), or a list of child rows in order. A list may leave rows out by
|
|
22
|
+
* their own columns, the way a statement's sources do: `where` keeps only the
|
|
23
|
+
* rows whose column holds one of its values, and `unless` drops a row whose
|
|
24
|
+
* column is true or set (a voided line: `"unless": "voided"`). The slots
|
|
25
|
+
* themselves are the add-on's words; Adminium checks the columns here and the
|
|
26
|
+
* slots against the add-on when the profile is made at install. A profile is
|
|
27
|
+
* the app's: made with real table names when the app is installed, removed
|
|
28
|
+
* with it.
|
|
29
|
+
*
|
|
30
|
+
* A statement is a document over one row (a client) and a period: it lists
|
|
31
|
+
* the documents and payments that point at that row, so it names them
|
|
32
|
+
* instead of one child list.
|
|
33
|
+
*/
|
|
34
|
+
import { z } from 'zod';
|
|
35
|
+
import { refSchema, textOrLabels } from './refs.js';
|
|
36
|
+
const slotId = z.string().regex(/^[A-Za-z][A-Za-z0-9_]*$/, 'a slot id');
|
|
37
|
+
/** Only rows whose column holds one of the values (sent invoices, not drafts). */
|
|
38
|
+
const whereSchema = z.object({ column: refSchema, in: z.array(z.union([z.string(), z.number(), z.boolean()])).min(1).max(16) }).strict();
|
|
39
|
+
export const slotMappingSchema = z.union([
|
|
40
|
+
z.object({ column: refSchema }).strict(),
|
|
41
|
+
z.object({ via: refSchema, column: refSchema }).strict(),
|
|
42
|
+
z
|
|
43
|
+
.object({
|
|
44
|
+
collection: z
|
|
45
|
+
.object({
|
|
46
|
+
table: refSchema,
|
|
47
|
+
via: refSchema,
|
|
48
|
+
orderBy: refSchema.optional(),
|
|
49
|
+
columns: z.record(slotId, refSchema),
|
|
50
|
+
/** Only the child rows whose column holds one of the values. */
|
|
51
|
+
where: whereSchema.optional(),
|
|
52
|
+
/** A child row whose column is true (or set) is left out: a voided line. */
|
|
53
|
+
unless: refSchema.optional(),
|
|
54
|
+
})
|
|
55
|
+
.strict(),
|
|
56
|
+
})
|
|
57
|
+
.strict(),
|
|
58
|
+
]);
|
|
59
|
+
/** The rows a statement lists for its one row, and the columns it reads of them. */
|
|
60
|
+
const statementSourceSchema = z
|
|
61
|
+
.object({
|
|
62
|
+
table: refSchema,
|
|
63
|
+
via: refSchema,
|
|
64
|
+
date: refSchema,
|
|
65
|
+
amount: refSchema,
|
|
66
|
+
number: refSchema.optional(),
|
|
67
|
+
where: whereSchema.optional(),
|
|
68
|
+
/** A row whose column is true (or set) is left out: a voided payment. */
|
|
69
|
+
unless: refSchema.optional(),
|
|
70
|
+
})
|
|
71
|
+
.strict();
|
|
72
|
+
export const appDocumentSchema = z
|
|
73
|
+
.object({
|
|
74
|
+
kind: z.string().regex(/^[a-z][a-z0-9-]*$/, 'a document kind'),
|
|
75
|
+
addOn: z.string().regex(/^[a-z][a-z0-9-]{1,79}$/, 'an add-on key'),
|
|
76
|
+
table: refSchema,
|
|
77
|
+
name: textOrLabels,
|
|
78
|
+
mapping: z.record(slotId, slotMappingSchema),
|
|
79
|
+
statement: z.object({ documents: statementSourceSchema, payments: statementSourceSchema }).strict().optional(),
|
|
80
|
+
/** The feature this document belongs to (see `addOns.features`). */
|
|
81
|
+
feature: z.string().regex(/^[a-z][a-z0-9-]{0,39}$/).optional(),
|
|
82
|
+
/**
|
|
83
|
+
* The slots the app's own screen may fill when it asks for the document
|
|
84
|
+
* (a label sheet's `count`), and no others: a slot nothing maps, with no
|
|
85
|
+
* default, holding a number or a text. Anything a request sends for a slot
|
|
86
|
+
* not listed here is refused.
|
|
87
|
+
*/
|
|
88
|
+
requestValues: z.array(slotId).min(1).max(8).optional(),
|
|
89
|
+
})
|
|
90
|
+
.strict();
|
|
91
|
+
/** Every column a mapping names, against the manifest's tables. */
|
|
92
|
+
export function mappingIssues(table, mapping, index, at) {
|
|
93
|
+
const out = [];
|
|
94
|
+
for (const [slot, source] of Object.entries(mapping)) {
|
|
95
|
+
const here = (...rest) => at('mapping', slot, ...rest);
|
|
96
|
+
if ('collection' in source) {
|
|
97
|
+
const c = source.collection;
|
|
98
|
+
if (index.table(c.table) === undefined) {
|
|
99
|
+
out.push({ path: here('collection', 'table'), message: `"${c.table}" is not a table of this manifest` });
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const via = index.column(c.table, c.via);
|
|
103
|
+
if (via?.type !== 'fk' || via.references !== table)
|
|
104
|
+
out.push({ path: here('collection', 'via'), message: `"${c.table}.${c.via}" does not point at "${table}"` });
|
|
105
|
+
for (const ref of [...Object.values(c.columns), ...[c.orderBy, c.where?.column, c.unless].filter((r) => r !== undefined)]) {
|
|
106
|
+
if (!index.has(c.table, ref))
|
|
107
|
+
out.push({ path: here('collection'), message: `"${c.table}" has no column "${ref}"` });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else if ('via' in source) {
|
|
111
|
+
const via = index.column(table, source.via);
|
|
112
|
+
if (via?.type !== 'fk' || via.references === undefined) {
|
|
113
|
+
out.push({ path: here('via'), message: `"${source.via}" is not a foreign key of "${table}"` });
|
|
114
|
+
}
|
|
115
|
+
else if (!index.has(via.references, source.column)) {
|
|
116
|
+
out.push({ path: here('column'), message: `"${via.references}" has no column "${source.column}"` });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else if (!index.has(table, source.column)) {
|
|
120
|
+
out.push({ path: here('column'), message: `"${table}" has no column "${source.column}"` });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
/** Everything wrong with an app's `documents` against its tables and add-on needs. */
|
|
126
|
+
export function appDocumentIssues(documents, ctx) {
|
|
127
|
+
const out = [];
|
|
128
|
+
const kinds = new Set();
|
|
129
|
+
documents.forEach((doc, d) => {
|
|
130
|
+
const at = (...rest) => ['documents', d, ...rest];
|
|
131
|
+
const key = `${doc.table}|${doc.kind}`;
|
|
132
|
+
if (kinds.has(key))
|
|
133
|
+
out.push({ path: at('kind'), message: `"${doc.table}" already has a "${doc.kind}" document` });
|
|
134
|
+
kinds.add(key);
|
|
135
|
+
if (!ctx.addOns.has(doc.addOn))
|
|
136
|
+
out.push({ path: at('addOn'), message: `"${doc.addOn}" is neither required nor suggested by the app` });
|
|
137
|
+
if (doc.feature !== undefined && !ctx.features.has(doc.feature))
|
|
138
|
+
out.push({ path: at('feature'), message: `"${doc.feature}" is not one of the app's addOns.features` });
|
|
139
|
+
if (ctx.index.table(doc.table) === undefined) {
|
|
140
|
+
out.push({ path: at('table'), message: `"${doc.table}" is not a table of this app` });
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
out.push(...mappingIssues(doc.table, doc.mapping, ctx.index, at));
|
|
144
|
+
(doc.requestValues ?? []).forEach((slot, n) => {
|
|
145
|
+
if (doc.requestValues.indexOf(slot) !== n)
|
|
146
|
+
out.push({ path: at('requestValues', n), message: `"${slot}" is listed twice` });
|
|
147
|
+
// A mapped slot prints the row; a value sent for it would print something the row does not say.
|
|
148
|
+
else if (doc.mapping[slot] !== undefined)
|
|
149
|
+
out.push({ path: at('requestValues', n), message: `"${slot}" is mapped, so a request may not fill it` });
|
|
150
|
+
});
|
|
151
|
+
if (doc.statement !== undefined) {
|
|
152
|
+
for (const part of ['documents', 'payments']) {
|
|
153
|
+
const s = doc.statement[part];
|
|
154
|
+
const here = (...rest) => at('statement', part, ...rest);
|
|
155
|
+
if (ctx.index.table(s.table) === undefined) {
|
|
156
|
+
out.push({ path: here('table'), message: `"${s.table}" is not a table of this app` });
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const via = ctx.index.column(s.table, s.via);
|
|
160
|
+
if (via?.type !== 'fk' || via.references !== doc.table)
|
|
161
|
+
out.push({ path: here('via'), message: `"${s.table}.${s.via}" does not point at "${doc.table}"` });
|
|
162
|
+
for (const ref of [s.date, s.amount, s.number, s.unless, s.where?.column]) {
|
|
163
|
+
if (ref !== undefined && !ctx.index.has(s.table, ref))
|
|
164
|
+
out.push({ path: here(), message: `"${s.table}" has no column "${ref}"` });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=documents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documents.js","sourceRoot":"","sources":["../src/documents.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `rules.formula` — a column Adminium works out from the other columns of the
|
|
3
|
+
* same row, on every write: a line's amount, a document's tax and total.
|
|
4
|
+
*
|
|
5
|
+
* ```json
|
|
6
|
+
* { "ref": "amount", "type": "decimal", "scale": "currency",
|
|
7
|
+
* "rules": { "formula": { "max": [0, { "sub": [{ "mul": ["qty", "rate"] }, { "coalesce": ["discount", 0] }] }] } } }
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* The grammar is small on purpose: numbers, the row's own columns, the four
|
|
11
|
+
* operations, `min`, `max`, `round`, `coalesce` and one `if`. Anything that
|
|
12
|
+
* reads another row is a `copy` or a `rollup`, which already know how to keep
|
|
13
|
+
* in step when that other row changes; a formula that could read across rows
|
|
14
|
+
* would need the same machinery again.
|
|
15
|
+
*
|
|
16
|
+
* THE ARITHMETIC IS EXACT. Every value becomes a fraction of two big
|
|
17
|
+
* integers, parsed from its decimal text (Postgres and MySQL hand a decimal
|
|
18
|
+
* back as `"12.5000"`, SQLite as the number `12.5`), and nothing goes through
|
|
19
|
+
* a float. Division stays exact; the result is rounded ONCE, half away from
|
|
20
|
+
* zero, to the column's scale. So `1 / 3 × 3` is exactly 1, and a total is the
|
|
21
|
+
* same on all three databases to the last minor unit.
|
|
22
|
+
*
|
|
23
|
+
* An empty column makes the result empty, unless `coalesce` says what to read
|
|
24
|
+
* instead: a draft line with no rate yet has no amount, not an amount of 0
|
|
25
|
+
* that looks like a price. Division by zero is empty too.
|
|
26
|
+
*
|
|
27
|
+
* Pure: a browser (an app's demo) evaluates the same formula the server does.
|
|
28
|
+
*/
|
|
29
|
+
import { z } from 'zod';
|
|
30
|
+
import { type ColumnShape, type ReferenceIssue } from './refs.js';
|
|
31
|
+
export type FormulaExpr = number | string | {
|
|
32
|
+
add: FormulaExpr[];
|
|
33
|
+
} | {
|
|
34
|
+
sub: [FormulaExpr, FormulaExpr];
|
|
35
|
+
} | {
|
|
36
|
+
mul: FormulaExpr[];
|
|
37
|
+
} | {
|
|
38
|
+
div: [FormulaExpr, FormulaExpr];
|
|
39
|
+
} | {
|
|
40
|
+
min: FormulaExpr[];
|
|
41
|
+
} | {
|
|
42
|
+
max: FormulaExpr[];
|
|
43
|
+
} | {
|
|
44
|
+
round: FormulaExpr | [FormulaExpr, number];
|
|
45
|
+
} | {
|
|
46
|
+
coalesce: [FormulaExpr, FormulaExpr];
|
|
47
|
+
} | {
|
|
48
|
+
if: [FormulaCondition, FormulaExpr, FormulaExpr];
|
|
49
|
+
};
|
|
50
|
+
export type FormulaCondition = {
|
|
51
|
+
eq: [string, string | number | boolean];
|
|
52
|
+
} | {
|
|
53
|
+
neq: [string, string | number | boolean];
|
|
54
|
+
} | {
|
|
55
|
+
gt: [FormulaExpr, FormulaExpr];
|
|
56
|
+
} | {
|
|
57
|
+
gte: [FormulaExpr, FormulaExpr];
|
|
58
|
+
} | {
|
|
59
|
+
lt: [FormulaExpr, FormulaExpr];
|
|
60
|
+
} | {
|
|
61
|
+
lte: [FormulaExpr, FormulaExpr];
|
|
62
|
+
} | {
|
|
63
|
+
isNull: string;
|
|
64
|
+
} | {
|
|
65
|
+
and: FormulaCondition[];
|
|
66
|
+
} | {
|
|
67
|
+
or: FormulaCondition[];
|
|
68
|
+
};
|
|
69
|
+
export declare const formulaExprSchema: z.ZodType<FormulaExpr>;
|
|
70
|
+
export declare const formulaConditionSchema: z.ZodType<FormulaCondition>;
|
|
71
|
+
/** How deep a formula may nest: deeper is a formula nobody can read back. */
|
|
72
|
+
export declare const FORMULA_MAX_DEPTH = 8;
|
|
73
|
+
/** Every column a formula reads, in the order it first names them. */
|
|
74
|
+
export declare function formulaColumns(expr: FormulaExpr | FormulaCondition): string[];
|
|
75
|
+
/**
|
|
76
|
+
* What is wrong with one column's formula against its table: a column it
|
|
77
|
+
* names that the table lacks, a column used in arithmetic that holds no
|
|
78
|
+
* number, a formula deeper than {@link FORMULA_MAX_DEPTH}, one that reads
|
|
79
|
+
* itself. Cycles across a table's formulas are {@link formulaCycle}'s.
|
|
80
|
+
*/
|
|
81
|
+
export declare function formulaIssues(expr: FormulaExpr, own: string, column: (ref: string) => ColumnShape | undefined): string[];
|
|
82
|
+
/**
|
|
83
|
+
* The first cycle among a table's formula columns (`total` reads `tax`, which
|
|
84
|
+
* reads `total`), as the columns in order, or `null`. A cycle has no value to
|
|
85
|
+
* settle on, so it is refused before it is ever stored.
|
|
86
|
+
*/
|
|
87
|
+
export declare function formulaCycle(formulas: ReadonlyMap<string, FormulaExpr>): string[] | null;
|
|
88
|
+
/**
|
|
89
|
+
* A table's formula columns in the order they can be worked out: each after
|
|
90
|
+
* every formula column it reads. Assumes no cycle (checked at validation).
|
|
91
|
+
*/
|
|
92
|
+
export declare function formulaOrder(formulas: ReadonlyMap<string, FormulaExpr>): string[];
|
|
93
|
+
/** A fraction of two big integers; `d` is always positive. */
|
|
94
|
+
interface Ratio {
|
|
95
|
+
n: bigint;
|
|
96
|
+
d: bigint;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* A stored value as an exact fraction: the decimal text of a number (never a
|
|
100
|
+
* float product), a decimal string as a driver returns it, a bigint, or a
|
|
101
|
+
* boolean (1 / 0, as MySQL and SQLite store one). `null` for an empty value or
|
|
102
|
+
* text that is not a number.
|
|
103
|
+
*/
|
|
104
|
+
export declare function toRatio(value: unknown): Ratio | null;
|
|
105
|
+
/** A fraction as decimal text with exactly `scale` places (`12.50`, `-3`, `0.125`). */
|
|
106
|
+
export declare function ratioText(value: Ratio, scale: number): string;
|
|
107
|
+
/**
|
|
108
|
+
* Work a formula out over a row, exactly: the result as decimal text at
|
|
109
|
+
* `scale` places, or `null` when an input it needs is empty or a divisor is
|
|
110
|
+
* zero. `row` holds the stored row with the new values over it.
|
|
111
|
+
*/
|
|
112
|
+
export declare function evaluateFormula(expr: FormulaExpr, row: Readonly<Record<string, unknown>>, scale: number): string | null;
|
|
113
|
+
/** Whether a condition holds for the row; a comparison with an empty side does not. */
|
|
114
|
+
export declare function holds(condition: FormulaCondition, row: Readonly<Record<string, unknown>>, scale: number): boolean;
|
|
115
|
+
/** The decimals a currency is written with (ISO 4217): JPY 0, most 2, KWD 3. Unknown → 2. */
|
|
116
|
+
export declare function currencyScale(code: string | null | undefined): number;
|
|
117
|
+
/** For the checks in `schema.ts`: every issue of a table's formulas, cycles included. */
|
|
118
|
+
export declare function tableFormulaIssues(columns: readonly (ColumnShape & {
|
|
119
|
+
rules?: {
|
|
120
|
+
formula?: FormulaExpr | undefined;
|
|
121
|
+
} | undefined;
|
|
122
|
+
})[], at: (column: number, ...rest: (string | number)[]) => (string | number)[]): ReferenceIssue[];
|
|
123
|
+
export {};
|
|
124
|
+
//# sourceMappingURL=formula.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formula.d.ts","sourceRoot":"","sources":["../src/formula.ts"],"names":[],"mappings":"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"}
|