@agentshouse/mdmodel 0.1.0-cabd61587647f → 0.1.0-caefdbe3e3d42
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/API.md +161 -116
- package/README.md +6 -4
- package/dist/blocks.d.ts.map +1 -1
- package/dist/blocks.js +29 -7
- package/dist/condition.d.ts +43 -13
- package/dist/condition.d.ts.map +1 -1
- package/dist/condition.js +225 -127
- package/dist/definition.d.ts +3 -1
- package/dist/definition.d.ts.map +1 -1
- package/dist/definition.js +105 -21
- package/dist/divide.js +9 -9
- package/dist/document.d.ts +4 -4
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +41 -41
- package/dist/index.d.ts +20 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -6
- package/dist/kinds.d.ts +18 -0
- package/dist/kinds.d.ts.map +1 -0
- package/dist/kinds.js +141 -0
- package/dist/links.d.ts +1 -0
- package/dist/links.d.ts.map +1 -1
- package/dist/links.js +85 -39
- package/dist/paths.d.ts +9 -1
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +28 -2
- package/dist/pattern.d.ts +10 -0
- package/dist/pattern.d.ts.map +1 -0
- package/dist/pattern.js +355 -0
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +7 -7
- package/dist/query/language.d.ts +218 -0
- package/dist/query/language.d.ts.map +1 -0
- package/dist/query/language.js +526 -0
- package/dist/query/match.d.ts +12 -0
- package/dist/query/match.d.ts.map +1 -0
- package/dist/query/match.js +118 -0
- package/dist/query/rows.d.ts +32 -0
- package/dist/query/rows.d.ts.map +1 -0
- package/dist/query/rows.js +161 -0
- package/dist/query/sql.d.ts +39 -0
- package/dist/query/sql.d.ts.map +1 -0
- package/dist/query/sql.js +279 -0
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +6 -1
- package/dist/reserved.d.ts +5 -0
- package/dist/reserved.d.ts.map +1 -0
- package/dist/reserved.js +20 -0
- package/dist/schema.d.ts +1 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +55 -4
- package/dist/types.d.ts +34 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/validate.d.ts +28 -4
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +302 -75
- package/dist/yaml.d.ts.map +1 -1
- package/dist/yaml.js +36 -3
- package/fixtures/README.md +26 -6
- package/fixtures/cases/conditional-section/context.json +1 -0
- package/fixtures/cases/conditional-section/expected.json +6 -0
- package/fixtures/cases/conditional-section/library/questions/hosting.md +9 -0
- package/fixtures/cases/conditional-section/library/questions/payments.md +12 -0
- package/fixtures/cases/conditional-section/schema.yaml +14 -0
- package/fixtures/cases/cross-room-link/context.json +1 -0
- package/fixtures/cases/cross-room-link/expected.json +1 -0
- package/fixtures/cases/cross-room-link/library/clients/acme.md +6 -0
- package/fixtures/cases/cross-room-link/schema.yaml +3 -0
- package/fixtures/cases/cross-room-reference/context.json +1 -0
- package/fixtures/cases/cross-room-reference/expected.json +1 -0
- package/fixtures/cases/cross-room-reference/library/clients/acme.md +9 -0
- package/fixtures/cases/cross-room-reference/schema.yaml +9 -0
- package/fixtures/cases/link-into-a-reserved-name/context.json +1 -0
- package/fixtures/cases/link-into-a-reserved-name/expected.json +5 -0
- package/fixtures/cases/link-into-a-reserved-name/library/notes/a.md +6 -0
- package/fixtures/cases/link-into-a-reserved-name/schema.yaml +3 -0
- package/fixtures/cases/link-into-a-reserved-name-from-an-untyped-document/context.json +1 -0
- package/fixtures/cases/link-into-a-reserved-name-from-an-untyped-document/expected.json +5 -0
- package/fixtures/cases/link-into-a-reserved-name-from-an-untyped-document/library/notes/b.md +5 -0
- package/fixtures/cases/member-on-an-untyped-document/context.json +1 -0
- package/fixtures/cases/member-on-an-untyped-document/expected.json +6 -0
- package/fixtures/cases/member-on-an-untyped-document/library/note.md +4 -0
- package/fixtures/cases/member-on-another-type/context.json +1 -0
- package/fixtures/cases/member-on-another-type/expected.json +6 -0
- package/fixtures/cases/member-on-another-type/library/note.md +4 -0
- package/fixtures/cases/member-on-another-type/schema.yaml +7 -0
- package/fixtures/cases/reference-into-a-reserved-name/context.json +1 -0
- package/fixtures/cases/reference-into-a-reserved-name/expected.json +6 -0
- package/fixtures/cases/reference-into-a-reserved-name/library/notes/a.md +4 -0
- package/fixtures/cases/reference-into-a-reserved-name/library/settings/brief.md +1 -0
- package/fixtures/cases/reference-into-a-reserved-name/schema.yaml +3 -0
- package/fixtures/cases/room-md-declares-a-type/context.json +1 -0
- package/fixtures/cases/room-md-declares-a-type/expected.json +6 -0
- package/fixtures/cases/room-md-declares-a-type/library/ROOM.md +5 -0
- package/fixtures/cases/typed-path-names-another-type/context.json +11 -0
- package/fixtures/cases/typed-path-names-another-type/expected.json +6 -0
- package/fixtures/cases/typed-path-names-another-type/library/settings/prefs/one.md +3 -0
- package/fixtures/cases/typed-path-names-another-type/schema.yaml +6 -0
- package/fixtures/cases/typed-path-names-its-type/context.json +11 -0
- package/fixtures/cases/typed-path-names-its-type/expected.json +3 -0
- package/fixtures/cases/typed-path-names-its-type/library/settings/prefs/deeper/two.md +1 -0
- package/fixtures/cases/typed-path-names-its-type/library/settings/prefs/one.md +4 -0
- package/fixtures/cases/typed-path-names-its-type/schema.yaml +6 -0
- package/fixtures/cases/typed-path-names-no-type/context.json +11 -0
- package/fixtures/cases/typed-path-names-no-type/expected.json +6 -0
- package/fixtures/cases/typed-path-names-no-type/library/settings/prefs/one.md +1 -0
- package/fixtures/cases/typed-path-names-no-type/schema.yaml +6 -0
- package/fixtures/cases/unnamed-typed-path-checks-its-fields/context.json +11 -0
- package/fixtures/cases/unnamed-typed-path-checks-its-fields/expected.json +6 -0
- package/fixtures/cases/unnamed-typed-path-checks-its-fields/library/rooms/team/tasks/plan.md +4 -0
- package/fixtures/cases/unnamed-typed-path-checks-its-fields/schema.yaml +5 -0
- package/fixtures/cases/unnamed-typed-path-names-a-type/context.json +11 -0
- package/fixtures/cases/unnamed-typed-path-names-a-type/expected.json +6 -0
- package/fixtures/cases/unnamed-typed-path-names-a-type/library/rooms/team/tasks/plan.md +6 -0
- package/fixtures/cases/unnamed-typed-path-names-a-type/schema.yaml +5 -0
- package/fixtures/cases/unnamed-typed-path-reads-its-type/context.json +11 -0
- package/fixtures/cases/unnamed-typed-path-reads-its-type/expected.json +3 -0
- package/fixtures/cases/unnamed-typed-path-reads-its-type/library/rooms/team/plan.md +1 -0
- package/fixtures/cases/unnamed-typed-path-reads-its-type/library/rooms/team/tasks/plan.md +5 -0
- package/fixtures/cases/unnamed-typed-path-reads-its-type/schema.yaml +5 -0
- package/fixtures/cases/unnamed-typed-path-without-a-body/context.json +11 -0
- package/fixtures/cases/unnamed-typed-path-without-a-body/expected.json +5 -0
- package/fixtures/cases/unnamed-typed-path-without-a-body/library/rooms/team/tasks/plan.md +5 -0
- package/fixtures/cases/unnamed-typed-path-without-a-body/schema.yaml +5 -0
- package/fixtures/division.json +26 -26
- package/fixtures/queries.json +913 -0
- package/package.json +7 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../../src/query/rows.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,UAAU,EAEhB,MAAM,kBAAkB,CAAC;AAI1B,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEnG,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/C,MAAM,MAAM,MAAM,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAahG,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAuB9D;AA4BD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAMjD;AAoGD,wBAAgB,OAAO,CACrB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,UAAU,EAAE,UAAU,GAAG,SAAS,GACjC,OAAO,CAcT"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { ARRAY_INDEX, escapePointer, itemFacts, memberFacts, } from '../definition.js';
|
|
2
|
+
import { instantKey, kindOf } from '../kinds.js';
|
|
3
|
+
import { target } from '../paths.js';
|
|
4
|
+
export function placedOf(pointer, reach) {
|
|
5
|
+
const [first = '', ...rest] = pointer.split('/').slice(1);
|
|
6
|
+
const keys = [first];
|
|
7
|
+
const gaps = [[]];
|
|
8
|
+
for (const segment of rest) {
|
|
9
|
+
if (ARRAY_INDEX.test(segment)) {
|
|
10
|
+
gaps.at(-1).push(segment);
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
keys.push(segment);
|
|
14
|
+
gaps.push([]);
|
|
15
|
+
}
|
|
16
|
+
const places = gaps
|
|
17
|
+
.map((gap, at) => {
|
|
18
|
+
const open = at < gaps.length - 1 ? reach !== 'exact' : reach === 'link';
|
|
19
|
+
return `/${gap.map((number) => `[.:]${number}`).join('')}.${open ? '*' : ''}`;
|
|
20
|
+
})
|
|
21
|
+
.join('');
|
|
22
|
+
return {
|
|
23
|
+
keys: keys.map((key) => `/${key}`).join(''),
|
|
24
|
+
places,
|
|
25
|
+
glob: /[[*]/u.test(places),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function placesOf({ done, head, tail }) {
|
|
29
|
+
return `${done}/${head}${tail}.`;
|
|
30
|
+
}
|
|
31
|
+
function throughOf(keys, places) {
|
|
32
|
+
const gaps = places.split('/').slice(1);
|
|
33
|
+
return keys
|
|
34
|
+
.split('/')
|
|
35
|
+
.slice(1)
|
|
36
|
+
.map((key, at) => `/${key}${gaps[at].replace(/\.\d*/gu, '').replaceAll(':', '/')}`)
|
|
37
|
+
.join('');
|
|
38
|
+
}
|
|
39
|
+
function plainObject(value) {
|
|
40
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
41
|
+
}
|
|
42
|
+
function referred(facts, value) {
|
|
43
|
+
return typeof value === 'string' && facts?.reference !== undefined;
|
|
44
|
+
}
|
|
45
|
+
function resolved(reading, facts, value) {
|
|
46
|
+
if (!referred(facts, value))
|
|
47
|
+
return value;
|
|
48
|
+
return target(reading.path, value) ?? undefined;
|
|
49
|
+
}
|
|
50
|
+
export function heldKind(value) {
|
|
51
|
+
if (value === undefined)
|
|
52
|
+
return 'undefined';
|
|
53
|
+
if (value === null)
|
|
54
|
+
return 'null';
|
|
55
|
+
if (Array.isArray(value))
|
|
56
|
+
return 'array';
|
|
57
|
+
if (typeof value === 'object')
|
|
58
|
+
return 'object';
|
|
59
|
+
return typeof value;
|
|
60
|
+
}
|
|
61
|
+
function scalarOf(value) {
|
|
62
|
+
if (typeof value === 'string' || typeof value === 'number')
|
|
63
|
+
return value;
|
|
64
|
+
if (typeof value === 'boolean')
|
|
65
|
+
return value ? 1 : 0;
|
|
66
|
+
if (Array.isArray(value))
|
|
67
|
+
return JSON.stringify(value);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
function instantOf(facts, value) {
|
|
71
|
+
return facts !== undefined && kindOf(facts) === 'date-time' ? instantKey(value) : null;
|
|
72
|
+
}
|
|
73
|
+
function held(reading, at, places, value, items) {
|
|
74
|
+
reading.values.push({
|
|
75
|
+
keys: at.keys,
|
|
76
|
+
places,
|
|
77
|
+
item: -1,
|
|
78
|
+
kind: heldKind(value),
|
|
79
|
+
value: scalarOf(value),
|
|
80
|
+
size: Array.isArray(value) ? value.length : null,
|
|
81
|
+
instant: instantOf(at.facts, value),
|
|
82
|
+
});
|
|
83
|
+
if (!Array.isArray(value))
|
|
84
|
+
return;
|
|
85
|
+
value.forEach((item, index) => {
|
|
86
|
+
const itemKind = heldKind(item);
|
|
87
|
+
reading.values.push({
|
|
88
|
+
keys: at.keys,
|
|
89
|
+
places,
|
|
90
|
+
item: index,
|
|
91
|
+
kind: itemKind,
|
|
92
|
+
value: itemKind === 'array' ? null : scalarOf(item),
|
|
93
|
+
size: null,
|
|
94
|
+
instant: instantOf(items[index], item),
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function itemsOf(facts, node) {
|
|
99
|
+
return node.map((_, index) => (facts === undefined ? undefined : itemFacts(facts, index)));
|
|
100
|
+
}
|
|
101
|
+
function record(reading, node, at) {
|
|
102
|
+
const items = Array.isArray(node) ? itemsOf(at.facts, node) : [];
|
|
103
|
+
const value = Array.isArray(node)
|
|
104
|
+
? node.map((item, index) => resolved(reading, items[index], item))
|
|
105
|
+
: resolved(reading, at.facts, node);
|
|
106
|
+
const places = placesOf(at);
|
|
107
|
+
held(reading, at, places, value, items);
|
|
108
|
+
if (typeof value !== 'string' || !referred(at.facts, node))
|
|
109
|
+
return;
|
|
110
|
+
reading.links.push({
|
|
111
|
+
kind: 'field',
|
|
112
|
+
keys: at.keys,
|
|
113
|
+
places,
|
|
114
|
+
target: value,
|
|
115
|
+
field: throughOf(at.keys, places),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
function walk(reading, node, at) {
|
|
119
|
+
record(reading, node, at);
|
|
120
|
+
if (Array.isArray(node)) {
|
|
121
|
+
const items = itemsOf(at.facts, node);
|
|
122
|
+
node.forEach((item, index) => walk(reading, item, {
|
|
123
|
+
...at,
|
|
124
|
+
tail: `${at.tail}.${index}`,
|
|
125
|
+
exact: `${at.exact}/${index}`,
|
|
126
|
+
facts: items[index],
|
|
127
|
+
}));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (!plainObject(node))
|
|
131
|
+
return;
|
|
132
|
+
for (const [key, value] of Object.entries(node)) {
|
|
133
|
+
const segment = escapePointer(key);
|
|
134
|
+
const exact = `${at.exact}/${segment}`;
|
|
135
|
+
const facts = at.facts === undefined ? undefined : memberFacts(at.facts, key);
|
|
136
|
+
walk(reading, value, ARRAY_INDEX.test(segment)
|
|
137
|
+
? {
|
|
138
|
+
...at,
|
|
139
|
+
head: `${at.head}${at.tail.replaceAll('.', ':')}:${segment}`,
|
|
140
|
+
tail: '',
|
|
141
|
+
exact,
|
|
142
|
+
facts,
|
|
143
|
+
}
|
|
144
|
+
: { keys: `${at.keys}/${segment}`, done: placesOf(at), head: '', tail: '', exact, facts });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
export function indexed(path, fields, definition) {
|
|
148
|
+
const reading = { path, values: [], links: [] };
|
|
149
|
+
for (const [name, value] of Object.entries(fields)) {
|
|
150
|
+
const segment = escapePointer(name);
|
|
151
|
+
walk(reading, value, {
|
|
152
|
+
keys: `/${segment}`,
|
|
153
|
+
done: '',
|
|
154
|
+
head: '',
|
|
155
|
+
tail: '',
|
|
156
|
+
exact: `/${segment}`,
|
|
157
|
+
facts: definition === undefined ? undefined : memberFacts(definition.frontmatter, name),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return { values: reading.values, links: reading.links };
|
|
161
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type Ordering } from '../kinds.js';
|
|
2
|
+
import type { QueryPlan, Scope } from './language.js';
|
|
3
|
+
import { type HeldValue, type Reach } from './rows.js';
|
|
4
|
+
export type SqlValue = string | number | bigint | Uint8Array | null;
|
|
5
|
+
export type Fragment = {
|
|
6
|
+
readonly sql: string;
|
|
7
|
+
readonly bind: readonly SqlValue[];
|
|
8
|
+
};
|
|
9
|
+
export type Collate = (ordering: Ordering, lists: boolean, total: boolean) => string | null;
|
|
10
|
+
export type Term = {
|
|
11
|
+
readonly ordering: Ordering;
|
|
12
|
+
readonly kind: Fragment;
|
|
13
|
+
readonly value: Fragment;
|
|
14
|
+
readonly instant?: Fragment;
|
|
15
|
+
readonly total?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type Following = {
|
|
18
|
+
readonly beyond: Fragment;
|
|
19
|
+
readonly equal: Fragment;
|
|
20
|
+
};
|
|
21
|
+
export declare const VALUE_TABLE = "CREATE TABLE replica_values (doc INTEGER NOT NULL, keys TEXT NOT NULL, places TEXT NOT NULL, item INTEGER NOT NULL, kind TEXT NOT NULL, value, size INTEGER, instant TEXT, PRIMARY KEY (doc, keys, places, item)) WITHOUT ROWID";
|
|
22
|
+
export declare const HOLDS_PHRASE = "holds_phrase";
|
|
23
|
+
export declare function holdsPhrase(_context: number, text: unknown, phrase: unknown): number;
|
|
24
|
+
export declare function fragment(sql: string, bind?: readonly SqlValue[]): Fragment;
|
|
25
|
+
export declare function joined(parts: readonly Fragment[], separator: string): Fragment;
|
|
26
|
+
export declare function all(parts: readonly Fragment[]): Fragment;
|
|
27
|
+
export declare function any(parts: readonly Fragment[]): Fragment;
|
|
28
|
+
export declare function grouped(part: Fragment): Fragment;
|
|
29
|
+
export declare function listed(values: readonly SqlValue[]): Fragment;
|
|
30
|
+
export declare function exists(part: Fragment): Fragment;
|
|
31
|
+
export declare function insertedValues(doc: number, values: readonly HeldValue[]): Fragment;
|
|
32
|
+
export declare function placed(alias: string, field: string, reach: Reach): Fragment;
|
|
33
|
+
export declare function scoped(alias: string, scope: Scope, byPaths?: boolean): Fragment;
|
|
34
|
+
export declare function tests(alias: string, plan: QueryPlan): Fragment;
|
|
35
|
+
export declare function sortValue(kind: SqlValue | undefined, value: unknown, instant: SqlValue | undefined): unknown;
|
|
36
|
+
export declare function sorted(collate: Collate, term: Term): Fragment;
|
|
37
|
+
export declare function beyond(collate: Collate, term: Term, value: unknown): Following;
|
|
38
|
+
export declare function following(parts: readonly Following[], tie: Fragment): Fragment;
|
|
39
|
+
//# sourceMappingURL=sql.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/query/sql.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { instantKey } from '../kinds.js';
|
|
2
|
+
import { placedOf } from './rows.js';
|
|
3
|
+
export const VALUE_TABLE = 'CREATE TABLE replica_values (doc INTEGER NOT NULL, keys TEXT NOT NULL, places TEXT NOT NULL, item INTEGER NOT NULL, kind TEXT NOT NULL, value, size INTEGER, instant TEXT, PRIMARY KEY (doc, keys, places, item)) WITHOUT ROWID';
|
|
4
|
+
export const HOLDS_PHRASE = 'holds_phrase';
|
|
5
|
+
const BOUNDS = { gt: '>', gte: '>=', lt: '<', lte: '<=' };
|
|
6
|
+
const NUMBERS = 1;
|
|
7
|
+
const INSTANTS = 2;
|
|
8
|
+
const TEXTS = 3;
|
|
9
|
+
const LISTS = 4;
|
|
10
|
+
export function holdsPhrase(_context, text, phrase) {
|
|
11
|
+
return typeof text === 'string' &&
|
|
12
|
+
typeof phrase === 'string' &&
|
|
13
|
+
text.toLowerCase().includes(phrase)
|
|
14
|
+
? 1
|
|
15
|
+
: 0;
|
|
16
|
+
}
|
|
17
|
+
export function fragment(sql, bind = []) {
|
|
18
|
+
return { sql, bind };
|
|
19
|
+
}
|
|
20
|
+
export function joined(parts, separator) {
|
|
21
|
+
return {
|
|
22
|
+
sql: parts.map((part) => part.sql).join(separator),
|
|
23
|
+
bind: parts.flatMap((part) => part.bind),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function balanced(parts, separator) {
|
|
27
|
+
if (parts.length === 1)
|
|
28
|
+
return grouped(parts[0]);
|
|
29
|
+
const half = Math.ceil(parts.length / 2);
|
|
30
|
+
return grouped(joined([balanced(parts.slice(0, half), separator), balanced(parts.slice(half), separator)], separator));
|
|
31
|
+
}
|
|
32
|
+
export function all(parts) {
|
|
33
|
+
return parts.length === 0 ? fragment('1') : balanced(parts, ' AND ');
|
|
34
|
+
}
|
|
35
|
+
export function any(parts) {
|
|
36
|
+
return parts.length === 0 ? fragment('0') : balanced(parts, ' OR ');
|
|
37
|
+
}
|
|
38
|
+
export function grouped(part) {
|
|
39
|
+
return { sql: `(${part.sql})`, bind: part.bind };
|
|
40
|
+
}
|
|
41
|
+
export function listed(values) {
|
|
42
|
+
return fragment(`(${values.map(() => '?').join(', ')})`, values);
|
|
43
|
+
}
|
|
44
|
+
export function exists(part) {
|
|
45
|
+
return { sql: `EXISTS (${part.sql})`, bind: part.bind };
|
|
46
|
+
}
|
|
47
|
+
function not(part) {
|
|
48
|
+
return { sql: `NOT ${part.sql}`, bind: part.bind };
|
|
49
|
+
}
|
|
50
|
+
function scalarText(value) {
|
|
51
|
+
if (value === Infinity)
|
|
52
|
+
return 'Infinity';
|
|
53
|
+
if (value === -Infinity)
|
|
54
|
+
return '-Infinity';
|
|
55
|
+
return JSON.stringify(value);
|
|
56
|
+
}
|
|
57
|
+
export function insertedValues(doc, values) {
|
|
58
|
+
const rows = values.map((value) => `[${JSON.stringify(value.keys)},${JSON.stringify(value.places)},${value.item},${JSON.stringify(value.kind)},${scalarText(value.value)},${JSON.stringify(value.size)},${JSON.stringify(value.instant)}]`);
|
|
59
|
+
return fragment('INSERT INTO replica_values (doc, keys, places, item, kind, value, size, instant) SELECT ?, value ->> 0, value ->> 1, value ->> 2, value ->> 3, value ->> 4, value ->> 5, value ->> 6 FROM json_each(?)', [doc, `[${rows.join(',')}]`]);
|
|
60
|
+
}
|
|
61
|
+
export function placed(alias, field, reach) {
|
|
62
|
+
const { keys, places, glob } = placedOf(field, reach);
|
|
63
|
+
return fragment(`${alias}.keys = ? AND ${alias}.places ${glob ? 'GLOB' : '='} ?`, [keys, places]);
|
|
64
|
+
}
|
|
65
|
+
function plainText(alias, operator, text) {
|
|
66
|
+
return fragment(`${alias}.instant IS NULL AND ${alias}.kind = 'string' AND ${alias}.value ${operator} ?`, [text]);
|
|
67
|
+
}
|
|
68
|
+
function compares(alias, operator, value) {
|
|
69
|
+
if (typeof value !== 'string') {
|
|
70
|
+
return fragment(`${alias}.kind = ? AND ${alias}.value ${operator} ?`, [
|
|
71
|
+
typeof value,
|
|
72
|
+
typeof value === 'boolean' ? (value ? 1 : 0) : value,
|
|
73
|
+
]);
|
|
74
|
+
}
|
|
75
|
+
const key = instantKey(value);
|
|
76
|
+
if (key === null)
|
|
77
|
+
return plainText(alias, operator, value);
|
|
78
|
+
return any([
|
|
79
|
+
fragment(`${alias}.instant IS NOT NULL AND ${alias}.instant ${operator} ?`, [key]),
|
|
80
|
+
plainText(alias, operator, value),
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
83
|
+
function same(alias, value) {
|
|
84
|
+
return value === null ? fragment(`${alias}.kind = 'null'`) : compares(alias, '=', value);
|
|
85
|
+
}
|
|
86
|
+
function heldValues(doc, field, alias, where) {
|
|
87
|
+
return joined([
|
|
88
|
+
fragment(`SELECT 1 FROM replica_values ${alias} WHERE ${alias}.doc = ${doc}.id AND `),
|
|
89
|
+
grouped(placed(alias, field, 'value')),
|
|
90
|
+
fragment(' AND '),
|
|
91
|
+
grouped(where),
|
|
92
|
+
], '');
|
|
93
|
+
}
|
|
94
|
+
function itemOf(held, where) {
|
|
95
|
+
return exists(joined([
|
|
96
|
+
fragment(`SELECT 1 FROM replica_values i WHERE i.doc = ${held}.doc AND i.keys = ${held}.keys AND i.places = ${held}.places AND i.item >= 0 AND `),
|
|
97
|
+
grouped(where),
|
|
98
|
+
], ''));
|
|
99
|
+
}
|
|
100
|
+
function matching(held, values) {
|
|
101
|
+
return any(values.map((value) => any([
|
|
102
|
+
same(held, value),
|
|
103
|
+
all([fragment(`${held}.kind = 'array'`), itemOf(held, same('i', value))]),
|
|
104
|
+
])));
|
|
105
|
+
}
|
|
106
|
+
function tested(doc, field, test) {
|
|
107
|
+
const reached = exists(heldValues(doc, field, 'r', fragment('r.item = -1')));
|
|
108
|
+
if (test.op === 'exists')
|
|
109
|
+
return test.exists ? reached : not(reached);
|
|
110
|
+
if (test.op === 'eq' || test.op === 'in') {
|
|
111
|
+
const values = test.op === 'eq' ? [test.value] : test.values;
|
|
112
|
+
return exists(heldValues(doc, field, 'v', any(values.map((value) => same('v', value)))));
|
|
113
|
+
}
|
|
114
|
+
if (test.op === 'ne' || test.op === 'nin') {
|
|
115
|
+
const values = test.op === 'ne' ? [test.value] : test.values;
|
|
116
|
+
return any([
|
|
117
|
+
not(reached),
|
|
118
|
+
exists(heldValues(doc, field, 'h', all([fragment('h.item = -1'), not(grouped(matching('h', values)))]))),
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
121
|
+
if (test.op === 'range') {
|
|
122
|
+
return exists(heldValues(doc, field, 'v', all([
|
|
123
|
+
fragment('v.item = -1'),
|
|
124
|
+
...test.bounds.map(([operator, bound]) => compares('v', BOUNDS[operator], bound)),
|
|
125
|
+
])));
|
|
126
|
+
}
|
|
127
|
+
if (test.op === 'prefix') {
|
|
128
|
+
return exists(heldValues(doc, field, 'v', fragment("v.kind = 'string' AND substr(v.value, 1, length(?)) = ?", [
|
|
129
|
+
test.prefix,
|
|
130
|
+
test.prefix,
|
|
131
|
+
])));
|
|
132
|
+
}
|
|
133
|
+
if (test.op === 'all') {
|
|
134
|
+
return exists(heldValues(doc, field, 'h', all([
|
|
135
|
+
fragment("h.item = -1 AND h.kind = 'array'"),
|
|
136
|
+
...test.values.map((value) => itemOf('h', same('i', value))),
|
|
137
|
+
])));
|
|
138
|
+
}
|
|
139
|
+
return exists(heldValues(doc, field, 'v', fragment("v.item = -1 AND v.kind = 'array' AND v.size = ?", [test.size])));
|
|
140
|
+
}
|
|
141
|
+
function conditioned(doc, condition) {
|
|
142
|
+
if ('every' in condition)
|
|
143
|
+
return all(condition.every.map((one) => conditioned(doc, one)));
|
|
144
|
+
if ('some' in condition)
|
|
145
|
+
return any(condition.some.map((one) => conditioned(doc, one)));
|
|
146
|
+
return tested(doc, condition.field, condition.test);
|
|
147
|
+
}
|
|
148
|
+
export function scoped(alias, scope, byPaths = false) {
|
|
149
|
+
const type = byPaths ? `+${alias}.type` : `${alias}.type`;
|
|
150
|
+
return all([
|
|
151
|
+
...(scope.types === null
|
|
152
|
+
? []
|
|
153
|
+
: [fragment(`${type} IN ${listed(scope.types).sql}`, scope.types)]),
|
|
154
|
+
...(scope.pathPrefix === null || scope.pathPrefix === ''
|
|
155
|
+
? []
|
|
156
|
+
: [
|
|
157
|
+
fragment(`${alias}.path >= ? AND ${alias}.path < ?`, [
|
|
158
|
+
`${scope.pathPrefix}/`,
|
|
159
|
+
`${scope.pathPrefix}0`,
|
|
160
|
+
]),
|
|
161
|
+
]),
|
|
162
|
+
...(scope.where === null ? [] : [conditioned(alias, scope.where)]),
|
|
163
|
+
]);
|
|
164
|
+
}
|
|
165
|
+
export function tests(alias, plan) {
|
|
166
|
+
return all([
|
|
167
|
+
...(plan.paths === null
|
|
168
|
+
? []
|
|
169
|
+
: [fragment(`${alias}.path IN ${listed(plan.paths).sql}`, plan.paths)]),
|
|
170
|
+
...(plan.fileIds === null
|
|
171
|
+
? []
|
|
172
|
+
: [fragment(`${alias}.file_id IN ${listed(plan.fileIds).sql}`, plan.fileIds)]),
|
|
173
|
+
...(plan.folder === null ? [] : [fragment(`${alias}.folder = ?`, [plan.folder])]),
|
|
174
|
+
scoped(alias, plan.root, plan.paths !== null),
|
|
175
|
+
...(plan.text === null
|
|
176
|
+
? []
|
|
177
|
+
: [fragment(`${HOLDS_PHRASE}(${alias}.content, ?)`, [plan.text.toLowerCase()])]),
|
|
178
|
+
]);
|
|
179
|
+
}
|
|
180
|
+
function instantMark(value) {
|
|
181
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
182
|
+
return null;
|
|
183
|
+
const { instant } = value;
|
|
184
|
+
return typeof instant === 'string' && Object.keys(value).length === 1 ? instant : null;
|
|
185
|
+
}
|
|
186
|
+
export function sortValue(kind, value, instant) {
|
|
187
|
+
return typeof instant === 'string' && kind === 'string' ? { instant } : value;
|
|
188
|
+
}
|
|
189
|
+
function classOf(value, ordering) {
|
|
190
|
+
if (value === null || value === undefined) {
|
|
191
|
+
return { placement: ordering.nulls === 'first' ? 0 : 2, klass: 0, scalar: null };
|
|
192
|
+
}
|
|
193
|
+
const mark = instantMark(value);
|
|
194
|
+
if (mark !== null)
|
|
195
|
+
return { placement: 1, klass: INSTANTS, scalar: mark };
|
|
196
|
+
if (typeof value === 'boolean')
|
|
197
|
+
return { placement: 1, klass: NUMBERS, scalar: value ? 1 : 0 };
|
|
198
|
+
if (typeof value === 'number')
|
|
199
|
+
return { placement: 1, klass: NUMBERS, scalar: value };
|
|
200
|
+
if (typeof value === 'string')
|
|
201
|
+
return { placement: 1, klass: TEXTS, scalar: value };
|
|
202
|
+
return { placement: 1, klass: LISTS, scalar: JSON.stringify(value) };
|
|
203
|
+
}
|
|
204
|
+
function placement(term) {
|
|
205
|
+
const nulls = term.ordering.nulls === 'first' ? 0 : 2;
|
|
206
|
+
return fragment(`CASE WHEN ${term.kind.sql} IS NULL OR ${term.kind.sql} = 'null' THEN ${nulls} ELSE 1 END`, [...term.kind.bind, ...term.kind.bind]);
|
|
207
|
+
}
|
|
208
|
+
function klass(term) {
|
|
209
|
+
const kinds = fragment(`CASE ${term.kind.sql} WHEN 'boolean' THEN ${NUMBERS} WHEN 'number' THEN ${NUMBERS} WHEN 'string' THEN ${TEXTS} WHEN 'array' THEN ${LISTS} ELSE 5 END`, term.kind.bind);
|
|
210
|
+
if (term.instant === undefined)
|
|
211
|
+
return kinds;
|
|
212
|
+
return fragment(`CASE WHEN ${term.instant.sql} IS NOT NULL THEN ${INSTANTS} ELSE ${kinds.sql} END`, [...term.instant.bind, ...kinds.bind]);
|
|
213
|
+
}
|
|
214
|
+
function classed(term, which) {
|
|
215
|
+
if (which === INSTANTS)
|
|
216
|
+
return term.instant ?? fragment('NULL');
|
|
217
|
+
const kinds = which === NUMBERS ? "('boolean', 'number')" : which === TEXTS ? "('string')" : "('array')";
|
|
218
|
+
const plain = which === TEXTS && term.instant !== undefined
|
|
219
|
+
? fragment(` AND ${term.instant.sql} IS NULL`, term.instant.bind)
|
|
220
|
+
: fragment('');
|
|
221
|
+
return fragment(`(CASE WHEN ${term.kind.sql} IN ${kinds}${plain.sql} THEN ${term.value.sql} END)`, [...term.kind.bind, ...plain.bind, ...term.value.bind]);
|
|
222
|
+
}
|
|
223
|
+
function collated(collate, term, which) {
|
|
224
|
+
if (which === NUMBERS || which === INSTANTS)
|
|
225
|
+
return '';
|
|
226
|
+
const name = collate(term.ordering, which === LISTS, term.total === true);
|
|
227
|
+
return name === null ? '' : ` COLLATE ${name}`;
|
|
228
|
+
}
|
|
229
|
+
export function sorted(collate, term) {
|
|
230
|
+
const direction = term.ordering.direction === 'desc' ? 'DESC' : 'ASC';
|
|
231
|
+
return joined([
|
|
232
|
+
placement(term),
|
|
233
|
+
fragment(`${klass(term).sql} ${direction}`, klass(term).bind),
|
|
234
|
+
...[NUMBERS, INSTANTS, TEXTS, LISTS].map((which) => {
|
|
235
|
+
const value = classed(term, which);
|
|
236
|
+
return fragment(`${value.sql}${collated(collate, term, which)} ${direction}`, value.bind);
|
|
237
|
+
}),
|
|
238
|
+
], ', ');
|
|
239
|
+
}
|
|
240
|
+
export function beyond(collate, term, value) {
|
|
241
|
+
const held = classOf(value, term.ordering);
|
|
242
|
+
const place = placement(term);
|
|
243
|
+
if (held.placement !== 1) {
|
|
244
|
+
return {
|
|
245
|
+
beyond: fragment(`${place.sql} > ${held.placement}`, place.bind),
|
|
246
|
+
equal: fragment(`${place.sql} = ${held.placement}`, place.bind),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
const after = term.ordering.direction === 'desc' ? '<' : '>';
|
|
250
|
+
const kind = klass(term);
|
|
251
|
+
const scalar = classed(term, held.klass);
|
|
252
|
+
const collation = collated(collate, term, held.klass);
|
|
253
|
+
return {
|
|
254
|
+
beyond: any([
|
|
255
|
+
fragment(`${place.sql} > 1`, place.bind),
|
|
256
|
+
all([
|
|
257
|
+
fragment(`${place.sql} = 1`, place.bind),
|
|
258
|
+
any([
|
|
259
|
+
fragment(`${kind.sql} ${after} ${held.klass}`, kind.bind),
|
|
260
|
+
all([
|
|
261
|
+
fragment(`${kind.sql} = ${held.klass}`, kind.bind),
|
|
262
|
+
fragment(`${scalar.sql} ${after} ?${collation}`, [...scalar.bind, held.scalar]),
|
|
263
|
+
]),
|
|
264
|
+
]),
|
|
265
|
+
]),
|
|
266
|
+
]),
|
|
267
|
+
equal: all([
|
|
268
|
+
fragment(`${place.sql} = 1 AND ${kind.sql} = ${held.klass}`, [...place.bind, ...kind.bind]),
|
|
269
|
+
fragment(`${scalar.sql} = ?${collation}`, [...scalar.bind, held.scalar]),
|
|
270
|
+
]),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
export function following(parts, tie) {
|
|
274
|
+
let condition = tie;
|
|
275
|
+
for (const part of [...parts].reverse()) {
|
|
276
|
+
condition = any([part.beyond, all([part.equal, condition])]);
|
|
277
|
+
}
|
|
278
|
+
return condition;
|
|
279
|
+
}
|
package/dist/references.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"references.d.ts","sourceRoot":"","sources":["../src/references.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,UAAU,EAEhB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"references.d.ts","sourceRoot":"","sources":["../src/references.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,UAAU,EAEhB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA8BzD,wBAAgB,UAAU,CACxB,QAAQ,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,EACrC,UAAU,EAAE,UAAU,GACrB,SAAS,SAAS,EAAE,CAItB"}
|
package/dist/references.js
CHANGED
|
@@ -5,7 +5,12 @@ function collect(value, facts, pointer, found) {
|
|
|
5
5
|
return;
|
|
6
6
|
if (typeof value === 'string') {
|
|
7
7
|
if (facts.reference !== undefined)
|
|
8
|
-
found.push({
|
|
8
|
+
found.push({
|
|
9
|
+
pointer,
|
|
10
|
+
value,
|
|
11
|
+
to: facts.reference,
|
|
12
|
+
...(facts.crossRoom === true ? { crossRoom: true } : {}),
|
|
13
|
+
});
|
|
9
14
|
return;
|
|
10
15
|
}
|
|
11
16
|
if (Array.isArray(value)) {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const ROOM_DESCRIPTION = "ROOM.md";
|
|
2
|
+
export declare const RESERVED_NAMES: readonly string[];
|
|
3
|
+
export declare function isReservedName(name: string): boolean;
|
|
4
|
+
export declare function isDocumentPath(path: string): boolean;
|
|
5
|
+
//# sourceMappingURL=reserved.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reserved.d.ts","sourceRoot":"","sources":["../src/reserved.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAE1C,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAO3C,CAAC;AAQF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGpD"}
|
package/dist/reserved.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const ROOM_DESCRIPTION = 'ROOM.md';
|
|
2
|
+
export const RESERVED_NAMES = [
|
|
3
|
+
ROOM_DESCRIPTION,
|
|
4
|
+
'capture',
|
|
5
|
+
'settings',
|
|
6
|
+
'rooms',
|
|
7
|
+
'agents',
|
|
8
|
+
'house',
|
|
9
|
+
];
|
|
10
|
+
function folded(name) {
|
|
11
|
+
return name.replace(/[A-Z]/g, (letter) => letter.toLowerCase());
|
|
12
|
+
}
|
|
13
|
+
const FOLDED_RESERVED_NAMES = new Set(RESERVED_NAMES.map(folded));
|
|
14
|
+
export function isReservedName(name) {
|
|
15
|
+
return FOLDED_RESERVED_NAMES.has(folded(name));
|
|
16
|
+
}
|
|
17
|
+
export function isDocumentPath(path) {
|
|
18
|
+
const first = path.split('/', 1)[0];
|
|
19
|
+
return first !== '' && !isReservedName(first);
|
|
20
|
+
}
|
package/dist/schema.d.ts
CHANGED
|
@@ -4,5 +4,6 @@ export type SchemaRefusal = {
|
|
|
4
4
|
readonly location: string;
|
|
5
5
|
};
|
|
6
6
|
export declare function frontmatterRefusal(definition: Definition, fields: Readonly<Record<string, unknown>>): SchemaRefusal | undefined;
|
|
7
|
+
export declare function conditionedSections(definition: Definition, fields: Readonly<Record<string, unknown>>): readonly string[];
|
|
7
8
|
export declare function fitsItem(definition: Definition, section: string, text: string): boolean;
|
|
8
9
|
//# sourceMappingURL=schema.d.ts.map
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA0B5E,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AA6NjF,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,aAAa,GAAG,SAAS,CAI3B;AAYD,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,SAAS,MAAM,EAAE,CAGnB;AAED,wBAAgB,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAMvF"}
|
package/dist/schema.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Validator } from '@cfworker/json-schema';
|
|
2
2
|
import { escapePointer, refTarget } from './definition.js';
|
|
3
3
|
import { MEMBER_FIELD, RECEIVED_FIELD, SITE_FIELD, TYPE_FIELD } from './fields.js';
|
|
4
|
+
import { withLinearRegExp } from './pattern.js';
|
|
4
5
|
import { interpretPlain, isRecord } from './yaml.js';
|
|
5
6
|
const STANDING_MAPS = ['properties', 'patternProperties'];
|
|
6
7
|
const STANDING = [
|
|
@@ -95,10 +96,49 @@ function closed(node, inPlace, defs, always = false) {
|
|
|
95
96
|
function copied(value) {
|
|
96
97
|
return JSON.parse(JSON.stringify(value));
|
|
97
98
|
}
|
|
99
|
+
function asks(asked) {
|
|
100
|
+
return asked.sections.length > 0 || asked.conditions.length > 0;
|
|
101
|
+
}
|
|
102
|
+
function branchOf(node, defs, closedDefs) {
|
|
103
|
+
if (!isRecord(node))
|
|
104
|
+
return { schema: node, asked: { sections: [], conditions: [] } };
|
|
105
|
+
const { sections, ...rest } = node;
|
|
106
|
+
const read = unconditioned(rest, defs, closedDefs);
|
|
107
|
+
const required = Object.entries(isRecord(sections) ? sections : {}).flatMap(([name, spec]) => isRecord(spec) && spec.required === true ? [name] : []);
|
|
108
|
+
return { schema: read.schema, asked: { sections: required, conditions: read.conditions } };
|
|
109
|
+
}
|
|
110
|
+
function unconditioned(node, defs, closedDefs) {
|
|
111
|
+
if (!isRecord(node))
|
|
112
|
+
return { schema: node, conditions: [] };
|
|
113
|
+
const schema = { ...node };
|
|
114
|
+
const conditions = [];
|
|
115
|
+
if (Array.isArray(node.allOf))
|
|
116
|
+
schema.allOf = node.allOf.map((entry) => {
|
|
117
|
+
const read = unconditioned(entry, defs, closedDefs);
|
|
118
|
+
conditions.push(...read.conditions);
|
|
119
|
+
return read.schema;
|
|
120
|
+
});
|
|
121
|
+
if (node.if === undefined)
|
|
122
|
+
return { schema, conditions };
|
|
123
|
+
const met = branchOf(node.then, defs, closedDefs);
|
|
124
|
+
const unmet = branchOf(node.else, defs, closedDefs);
|
|
125
|
+
if (node.then !== undefined)
|
|
126
|
+
schema.then = met.schema;
|
|
127
|
+
if (node.else !== undefined)
|
|
128
|
+
schema.else = unmet.schema;
|
|
129
|
+
if (!asks(met.asked) && !asks(unmet.asked))
|
|
130
|
+
return { schema, conditions };
|
|
131
|
+
const test = new Validator(isRecord(node.if)
|
|
132
|
+
? { ...closed(node.if, true, defs), $defs: closedDefs }
|
|
133
|
+
: node.if === true, '2020-12', false);
|
|
134
|
+
return { schema, conditions: [...conditions, { test, met: met.asked, unmet: unmet.asked }] };
|
|
135
|
+
}
|
|
98
136
|
function compile(definition) {
|
|
99
137
|
const defs = copied(definition.shape.defs);
|
|
100
|
-
const { sections, member_document: _mark, ...
|
|
138
|
+
const { sections, member_document: _mark, ...written } = copied(definition.shape.schema);
|
|
101
139
|
const closedDefs = mapped(defs, (held) => closed(held, true, defs));
|
|
140
|
+
const read = unconditioned(written, defs, closedDefs);
|
|
141
|
+
const root = read.schema;
|
|
102
142
|
const declared = isRecord(root.properties) ? root.properties : {};
|
|
103
143
|
const properties = {
|
|
104
144
|
[TYPE_FIELD]: true,
|
|
@@ -114,7 +154,7 @@ function compile(definition) {
|
|
|
114
154
|
continue;
|
|
115
155
|
lists.set(name, new Validator({ ...closed(spec.items, false, defs), $defs: closedDefs }, '2020-12', false));
|
|
116
156
|
}
|
|
117
|
-
return { frontmatter, sections: lists };
|
|
157
|
+
return { frontmatter, sections: lists, conditions: read.conditions };
|
|
118
158
|
}
|
|
119
159
|
function compiledOf(definition) {
|
|
120
160
|
const held = compiled.get(definition);
|
|
@@ -150,14 +190,25 @@ function refusalOf(errors) {
|
|
|
150
190
|
return { rule: 'schema', location: '' };
|
|
151
191
|
}
|
|
152
192
|
export function frontmatterRefusal(definition, fields) {
|
|
153
|
-
const
|
|
193
|
+
const { frontmatter } = compiledOf(definition);
|
|
194
|
+
const result = withLinearRegExp(() => frontmatter.validate(fields));
|
|
154
195
|
return result.valid ? undefined : refusalOf(result.errors);
|
|
155
196
|
}
|
|
197
|
+
function askedOf(conditions, fields) {
|
|
198
|
+
return conditions.flatMap((condition) => {
|
|
199
|
+
const asked = condition.test.validate(fields).valid ? condition.met : condition.unmet;
|
|
200
|
+
return [...asked.sections, ...askedOf(asked.conditions, fields)];
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
export function conditionedSections(definition, fields) {
|
|
204
|
+
const { conditions } = compiledOf(definition);
|
|
205
|
+
return withLinearRegExp(() => askedOf(conditions, fields));
|
|
206
|
+
}
|
|
156
207
|
export function fitsItem(definition, section, text) {
|
|
157
208
|
const spec = definition.sections[section];
|
|
158
209
|
const validator = compiledOf(definition).sections.get(section);
|
|
159
210
|
if (spec?.kind !== 'list' || validator === undefined)
|
|
160
211
|
return false;
|
|
161
212
|
const typed = spec.items.type === 'string' ? text : interpretPlain(text);
|
|
162
|
-
return validator.validate(typed).valid;
|
|
213
|
+
return withLinearRegExp(() => validator.validate(typed).valid);
|
|
163
214
|
}
|