@agentshouse/mdruntime 0.1.0-c069136dcb4e8 → 0.1.0-c09bf842921ff
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/README.md +1 -1
- package/dist/adapter.d.ts +51 -22
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +3 -10
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +21 -56
- package/dist/bootstrap.d.ts +2 -2
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +9 -8
- package/dist/chain.d.ts +2 -1
- package/dist/chain.d.ts.map +1 -1
- package/dist/client.d.ts +9 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -1
- package/dist/collection.d.ts +2 -1
- package/dist/collection.d.ts.map +1 -1
- package/dist/compile.d.ts +1 -6
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +90 -277
- package/dist/connection.d.ts +8 -7
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +362 -313
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +7 -0
- package/dist/conversation.d.ts +14 -8
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +85 -67
- package/dist/durability.d.ts +6 -0
- package/dist/durability.d.ts.map +1 -1
- package/dist/entry.d.ts +2 -2
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +16 -7
- package/dist/host.d.ts +3 -6
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +16 -23
- package/dist/identity.d.ts +0 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +0 -3
- package/dist/index.d.ts +10 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/job.d.ts +14 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +28 -0
- package/dist/live.d.ts +2 -1
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +10 -7
- package/dist/memory.d.ts +8 -4
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +116 -66
- package/dist/messages.d.ts +2 -2
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +15 -16
- package/dist/model.d.ts +2 -7
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +0 -3
- package/dist/observation.d.ts +2 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +7 -2
- package/dist/query.d.ts +4 -209
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -505
- package/dist/reach.d.ts +7 -7
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +33 -44
- package/dist/react.d.ts +3 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/readiness.d.ts +0 -2
- package/dist/readiness.d.ts.map +1 -1
- package/dist/refresh.d.ts +2 -1
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +25 -1
- package/dist/replica.d.ts +8 -5
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +27 -9
- package/dist/room-draft.d.ts +4 -3
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +65 -52
- package/dist/rows.d.ts +14 -21
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +48 -184
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +1 -1
- package/dist/search.d.ts +12 -63
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +9 -52
- package/dist/store.d.ts +5 -7
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +11 -15
- package/dist/stored.d.ts +3 -4
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +90 -126
- package/dist/tables.d.ts +8 -6
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +22 -13
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +7 -4
- package/dist/windows.d.ts +1 -12
- package/dist/windows.d.ts.map +1 -1
- package/dist/windows.js +2 -75
- package/dist/wire.d.ts +9 -4
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +3 -1
- package/package.json +2 -2
- package/dist/fulltext.d.ts +0 -27
- package/dist/fulltext.d.ts.map +0 -1
- package/dist/fulltext.js +0 -370
- package/dist/words.d.ts +0 -19
- package/dist/words.d.ts.map +0 -1
- package/dist/words.js +0 -156
package/dist/compile.d.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import type { Plan, QueryRows } from './query.js';
|
|
2
|
-
import type { Connection
|
|
2
|
+
import type { Connection } from './tables.js';
|
|
3
3
|
export type QueryRoom = {
|
|
4
4
|
readonly locator: string;
|
|
5
5
|
readonly key: number;
|
|
6
6
|
};
|
|
7
|
-
export type Fragment = {
|
|
8
|
-
readonly sql: string;
|
|
9
|
-
readonly bind: readonly Value[];
|
|
10
|
-
};
|
|
11
7
|
export type QueryAnswer = QueryRows & {
|
|
12
8
|
readonly more?: string;
|
|
13
9
|
};
|
|
14
|
-
export declare function selected(connection: Connection, plan: Plan, rooms: readonly QueryRoom[]): Fragment;
|
|
15
10
|
export declare function answerQuery(connection: Connection, plan: Plan, rooms: readonly QueryRoom[], budget?: number | null): QueryAnswer;
|
|
16
11
|
//# sourceMappingURL=compile.d.ts.map
|
package/dist/compile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,IAAI,EAAuC,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvF,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,aAAa,CAAC;AAG1D,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AA+a3E,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG;IAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAkUjE,wBAAgB,WAAW,CACzB,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,MAAM,GAAE,MAAM,GAAG,IAAW,GAC3B,WAAW,CAUb"}
|
package/dist/compile.js
CHANGED
|
@@ -1,125 +1,36 @@
|
|
|
1
|
-
import { ARRAY_INDEX, parse, pointerSegments } from '@agentshouse/mdmodel';
|
|
1
|
+
import { ARRAY_INDEX, bodied, byCodePoint, compared, cursorOf, heldKind, parse, pointerSegments, sql, } from '@agentshouse/mdmodel';
|
|
2
2
|
import { binaryRecord } from './binary.js';
|
|
3
|
-
import { documentKey,
|
|
4
|
-
import {
|
|
3
|
+
import { documentKey, pathOfKey, roomOfKey } from './rows.js';
|
|
4
|
+
import { FROM_START, MAXIMUM_WINDOW } from './windows.js';
|
|
5
|
+
const { all, any, exists, fragment, grouped, joined, listed } = sql;
|
|
5
6
|
const VISIBLE = (alias) => `${alias}.visible = 1 AND ${alias}.present = 1 AND ${alias}.queried = 1`;
|
|
6
|
-
function
|
|
7
|
-
return
|
|
7
|
+
function located(alias) {
|
|
8
|
+
return fragment(`(SELECT k.locator FROM replica_keys k WHERE k.key = ${alias}.key)`);
|
|
8
9
|
}
|
|
9
|
-
function
|
|
10
|
-
return {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
function balanced(parts, separator) {
|
|
16
|
-
if (parts.length === 1)
|
|
17
|
-
return grouped(parts[0]);
|
|
18
|
-
const half = Math.ceil(parts.length / 2);
|
|
19
|
-
return grouped(joined([balanced(parts.slice(0, half), separator), balanced(parts.slice(half), separator)], separator));
|
|
20
|
-
}
|
|
21
|
-
function all(parts) {
|
|
22
|
-
return parts.length === 0 ? fragment('1') : balanced(parts, ' AND ');
|
|
23
|
-
}
|
|
24
|
-
function any(parts) {
|
|
25
|
-
return parts.length === 0 ? fragment('0') : balanced(parts, ' OR ');
|
|
26
|
-
}
|
|
27
|
-
function grouped(part) {
|
|
28
|
-
return { sql: `(${part.sql})`, bind: part.bind };
|
|
29
|
-
}
|
|
30
|
-
function list(values) {
|
|
31
|
-
return fragment(`(${values.map(() => '?').join(', ')})`, values);
|
|
32
|
-
}
|
|
33
|
-
function sqlValue(value) {
|
|
34
|
-
if (value === null)
|
|
35
|
-
return { kind: 'null', value: null };
|
|
36
|
-
if (typeof value === 'boolean')
|
|
37
|
-
return { kind: 'boolean', value: value ? 1 : 0 };
|
|
38
|
-
if (typeof value === 'number')
|
|
39
|
-
return { kind: 'number', value };
|
|
40
|
-
return { kind: 'string', value };
|
|
41
|
-
}
|
|
42
|
-
function same(alias, value) {
|
|
43
|
-
const held = sqlValue(value);
|
|
44
|
-
return held.kind === 'null'
|
|
45
|
-
? fragment(`${alias}.kind = 'null'`)
|
|
46
|
-
: fragment(`${alias}.kind = ? AND ${alias}.value = ?`, [held.kind, held.value]);
|
|
47
|
-
}
|
|
48
|
-
const BOUNDS = { gt: '>', gte: '>=', lt: '<', lte: '<=' };
|
|
49
|
-
function pointed(alias, field, reach) {
|
|
50
|
-
const { keys, places, glob } = placedOf(field, reach);
|
|
51
|
-
return fragment(`${alias}.keys = ? AND ${alias}.places ${glob ? 'GLOB' : '='} ?`, [keys, places]);
|
|
52
|
-
}
|
|
53
|
-
function heldValues(doc, field, alias, where) {
|
|
54
|
-
return joined([
|
|
55
|
-
fragment(`SELECT 1 FROM replica_values ${alias} WHERE ${alias}.doc = ${doc}.id AND `),
|
|
56
|
-
grouped(pointed(alias, field, 'value')),
|
|
57
|
-
fragment(' AND '),
|
|
58
|
-
grouped(where),
|
|
59
|
-
], '');
|
|
60
|
-
}
|
|
61
|
-
function exists(part) {
|
|
62
|
-
return { sql: `EXISTS (${part.sql})`, bind: part.bind };
|
|
63
|
-
}
|
|
64
|
-
function itemOf(held, where) {
|
|
65
|
-
return exists(joined([
|
|
66
|
-
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 `),
|
|
67
|
-
grouped(where),
|
|
68
|
-
], ''));
|
|
69
|
-
}
|
|
70
|
-
function matching(held, values) {
|
|
71
|
-
return any(values.map((value) => any([
|
|
72
|
-
same(held, value),
|
|
73
|
-
all([fragment(`${held}.kind = 'array'`), itemOf(held, same('i', value))]),
|
|
74
|
-
])));
|
|
75
|
-
}
|
|
76
|
-
function tested(doc, field, test) {
|
|
77
|
-
const reached = exists(heldValues(doc, field, 'r', fragment('r.item = -1')));
|
|
78
|
-
if (test.op === 'exists')
|
|
79
|
-
return test.exists ? reached : fragment(`NOT ${reached.sql}`, reached.bind);
|
|
80
|
-
if (test.op === 'eq' || test.op === 'in') {
|
|
81
|
-
const values = test.op === 'eq' ? [test.value] : test.values;
|
|
82
|
-
return exists(heldValues(doc, field, 'v', any(values.map((value) => same('v', value)))));
|
|
83
|
-
}
|
|
84
|
-
if (test.op === 'ne' || test.op === 'nin') {
|
|
85
|
-
const values = test.op === 'ne' ? [test.value] : test.values;
|
|
86
|
-
const unmatched = matching('h', values);
|
|
87
|
-
return any([
|
|
88
|
-
fragment(`NOT ${reached.sql}`, reached.bind),
|
|
89
|
-
exists(heldValues(doc, field, 'h', all([fragment('h.item = -1'), fragment(`NOT (${unmatched.sql})`, unmatched.bind)]))),
|
|
90
|
-
]);
|
|
91
|
-
}
|
|
92
|
-
if (test.op === 'range') {
|
|
93
|
-
return exists(heldValues(doc, field, 'v', all([
|
|
94
|
-
fragment('v.item = -1'),
|
|
95
|
-
...test.bounds.map(([operator, bound]) => {
|
|
96
|
-
const held = sqlValue(bound);
|
|
97
|
-
return fragment(`v.kind = ? AND v.value ${BOUNDS[operator]} ?`, [
|
|
98
|
-
held.kind,
|
|
99
|
-
held.value,
|
|
100
|
-
]);
|
|
101
|
-
}),
|
|
102
|
-
])));
|
|
103
|
-
}
|
|
104
|
-
if (test.op === 'all') {
|
|
105
|
-
return exists(heldValues(doc, field, 'h', all([
|
|
106
|
-
fragment("h.item = -1 AND h.kind = 'array'"),
|
|
107
|
-
...test.values.map((value) => itemOf('h', same('i', value))),
|
|
108
|
-
])));
|
|
109
|
-
}
|
|
110
|
-
return exists(heldValues(doc, field, 'v', fragment("v.item = -1 AND v.kind = 'array' AND v.size = ?", [test.size])));
|
|
10
|
+
function within(alias, path) {
|
|
11
|
+
return fragment(`(${alias}.path = ? OR (${alias}.path >= ? AND ${alias}.path < ?))`, [
|
|
12
|
+
path,
|
|
13
|
+
`${path}/`,
|
|
14
|
+
`${path}0`,
|
|
15
|
+
]);
|
|
111
16
|
}
|
|
112
|
-
function
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return
|
|
117
|
-
|
|
17
|
+
function ancestors(area) {
|
|
18
|
+
const depth = area.path.split('/').length;
|
|
19
|
+
return area.open.flatMap((open) => {
|
|
20
|
+
const segments = open.split('/');
|
|
21
|
+
return segments.slice(depth - 1, -1).map((_, at) => segments.slice(0, depth + at).join('/'));
|
|
22
|
+
});
|
|
118
23
|
}
|
|
119
|
-
function
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
24
|
+
function fenced(alias, key, area, folders) {
|
|
25
|
+
const opened = any([
|
|
26
|
+
...area.open.map((open) => within(alias, open)),
|
|
27
|
+
...(folders ? ancestors(area).map((path) => fragment(`${alias}.path = ?`, [path])) : []),
|
|
28
|
+
]);
|
|
29
|
+
return all([
|
|
30
|
+
fragment(`${alias}.key = ?`, [key]),
|
|
31
|
+
within(alias, area.path),
|
|
32
|
+
fragment(`NOT ${grouped(opened).sql}`, opened.bind),
|
|
33
|
+
]);
|
|
123
34
|
}
|
|
124
35
|
function reached(bound, alias) {
|
|
125
36
|
const reach = bound.reach;
|
|
@@ -133,44 +44,20 @@ function reached(bound, alias) {
|
|
|
133
44
|
const key = bound.keys.get(roomOfKey(held));
|
|
134
45
|
return key === undefined ? [] : [[key, pathOfKey(held)]];
|
|
135
46
|
});
|
|
136
|
-
const
|
|
47
|
+
const outside = any(reach.areas.flatMap((area) => {
|
|
137
48
|
const key = bound.keys.get(area.room);
|
|
138
|
-
return key === undefined ? [] : [
|
|
139
|
-
});
|
|
140
|
-
const beneath = (area) => fragment(`${alias}.key = ? AND (${alias}.path = ? OR (${alias}.path >= ? AND ${alias}.path < ?))`, [area.key, area.path, `${area.path}/`, `${area.path}0`]);
|
|
141
|
-
const typed = (types) => fragment(`${alias}.type IN ${list(types).sql}`, types);
|
|
142
|
-
const outside = any(areas.map((area) => area.types.length === 0
|
|
143
|
-
? beneath(area)
|
|
144
|
-
: all([beneath(area), fragment(`NOT ${grouped(typed(area.types)).sql}`, area.types)])));
|
|
49
|
+
return key === undefined ? [] : [fenced(alias, key, area, false)];
|
|
50
|
+
}));
|
|
145
51
|
return all([
|
|
146
52
|
any([
|
|
147
|
-
...(rooms.length === 0 ? [] : [fragment(`${alias}.key IN ${
|
|
53
|
+
...(rooms.length === 0 ? [] : [fragment(`${alias}.key IN ${listed(rooms).sql}`, rooms)]),
|
|
148
54
|
...(documents.length === 0
|
|
149
55
|
? []
|
|
150
56
|
: [
|
|
151
57
|
fragment(`EXISTS (SELECT 1 FROM json_each(?) j WHERE j.value ->> 0 = ${alias}.key AND j.value ->> 1 = ${alias}.path)`, [JSON.stringify(documents)]),
|
|
152
58
|
]),
|
|
153
|
-
...areas.flatMap((area) => area.types.length === 0 ? [] : [all([beneath(area), typed(area.types)])]),
|
|
154
59
|
]),
|
|
155
|
-
...(areas.length === 0 ? [] : [fragment(`NOT ${grouped(outside).sql}`, outside.bind)]),
|
|
156
|
-
]);
|
|
157
|
-
}
|
|
158
|
-
function unindexed(column) {
|
|
159
|
-
return `+${column}`;
|
|
160
|
-
}
|
|
161
|
-
function scoped(alias, scope, byPaths = false) {
|
|
162
|
-
const type = byPaths ? unindexed(`${alias}.type`) : `${alias}.type`;
|
|
163
|
-
return all([
|
|
164
|
-
...(scope.types === null ? [] : [fragment(`${type} IN ${list(scope.types).sql}`, scope.types)]),
|
|
165
|
-
...(scope.pathPrefix === null || scope.pathPrefix === ''
|
|
166
|
-
? []
|
|
167
|
-
: [
|
|
168
|
-
fragment(`${alias}.path >= ? AND ${alias}.path < ?`, [
|
|
169
|
-
`${scope.pathPrefix}/`,
|
|
170
|
-
`${scope.pathPrefix}0`,
|
|
171
|
-
]),
|
|
172
|
-
]),
|
|
173
|
-
...(scope.where === null ? [] : [conditioned(alias, scope.where)]),
|
|
60
|
+
...(reach.areas.length === 0 ? [] : [fragment(`NOT ${grouped(outside).sql}`, outside.bind)]),
|
|
174
61
|
]);
|
|
175
62
|
}
|
|
176
63
|
function related(bound, plan) {
|
|
@@ -178,17 +65,17 @@ function related(bound, plan) {
|
|
|
178
65
|
if (plan.hop !== null) {
|
|
179
66
|
parts.push(exists(joined([
|
|
180
67
|
fragment(`SELECT 1 FROM replica_links l JOIN replica_entries m ON m.key = d.key AND m.path = l.target AND ${VISIBLE('m')} WHERE l.doc = d.id AND l.kind = 'field' AND`),
|
|
181
|
-
grouped(
|
|
68
|
+
grouped(sql.placed('l', plan.hop.field, 'link')),
|
|
182
69
|
fragment('AND'),
|
|
183
|
-
grouped(all([scoped('m', plan.hop), reached(bound, 'm')])),
|
|
70
|
+
grouped(all([sql.scoped('m', plan.hop), reached(bound, 'm')])),
|
|
184
71
|
], ' ')));
|
|
185
72
|
}
|
|
186
73
|
if (plan.backlinks !== null) {
|
|
187
74
|
const field = plan.backlinks.field;
|
|
188
75
|
parts.push(exists(joined([
|
|
189
76
|
fragment(`SELECT 1 FROM replica_links l JOIN replica_entries s ON s.id = l.doc AND ${VISIBLE('s')} WHERE l.key = d.key AND l.target = d.path AND l.kind = 'field' AND`),
|
|
190
|
-
...(field === null ? [] : [grouped(
|
|
191
|
-
grouped(all([scoped('s', plan.backlinks), reached(bound, 's')])),
|
|
77
|
+
...(field === null ? [] : [grouped(sql.placed('l', field, 'link')), fragment('AND')]),
|
|
78
|
+
grouped(all([sql.scoped('s', plan.backlinks), reached(bound, 's')])),
|
|
192
79
|
], ' ')));
|
|
193
80
|
}
|
|
194
81
|
return parts;
|
|
@@ -196,11 +83,9 @@ function related(bound, plan) {
|
|
|
196
83
|
function filtered(bound, plan) {
|
|
197
84
|
const keys = bound.rooms.map(({ key }) => key);
|
|
198
85
|
return all([
|
|
199
|
-
fragment(`d.key IN ${
|
|
200
|
-
...(plan.paths === null ? [] : [fragment(`d.path IN ${list(plan.paths).sql}`, plan.paths)]),
|
|
201
|
-
...(plan.folder === null ? [] : [fragment('d.folder = ?', [plan.folder])]),
|
|
86
|
+
fragment(`d.key IN ${listed(keys).sql} AND ${VISIBLE('d')}`, keys),
|
|
202
87
|
reached(bound, 'd'),
|
|
203
|
-
|
|
88
|
+
sql.tests('d', plan),
|
|
204
89
|
...related(bound, plan),
|
|
205
90
|
]);
|
|
206
91
|
}
|
|
@@ -231,103 +116,25 @@ const GROUP_TIES = {
|
|
|
231
116
|
locale: undefined,
|
|
232
117
|
localeOptions: undefined,
|
|
233
118
|
};
|
|
234
|
-
function
|
|
235
|
-
if (value === null || value === undefined) {
|
|
236
|
-
return { placement: ordering.nulls === 'first' ? 0 : 2, klass: 0, scalar: null };
|
|
237
|
-
}
|
|
238
|
-
if (typeof value === 'boolean')
|
|
239
|
-
return { placement: 1, klass: 1, scalar: value ? 1 : 0 };
|
|
240
|
-
if (typeof value === 'number')
|
|
241
|
-
return { placement: 1, klass: 1, scalar: value };
|
|
242
|
-
if (typeof value === 'string')
|
|
243
|
-
return { placement: 1, klass: 2, scalar: value };
|
|
244
|
-
return { placement: 1, klass: 3, scalar: JSON.stringify(value) };
|
|
245
|
-
}
|
|
246
|
-
function placement(term) {
|
|
247
|
-
const nulls = term.ordering.nulls === 'first' ? 0 : 2;
|
|
248
|
-
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]);
|
|
249
|
-
}
|
|
250
|
-
function klass(term) {
|
|
251
|
-
return fragment(`CASE ${term.kind.sql} WHEN 'boolean' THEN 1 WHEN 'number' THEN 1 WHEN 'string' THEN 2 WHEN 'array' THEN 3 ELSE 4 END`, term.kind.bind);
|
|
252
|
-
}
|
|
253
|
-
function classed(term, which) {
|
|
254
|
-
const kinds = which === 1 ? "('boolean', 'number')" : which === 2 ? "('string')" : "('array')";
|
|
255
|
-
return fragment(`(CASE WHEN ${term.kind.sql} IN ${kinds} THEN ${term.value.sql} END)`, [
|
|
256
|
-
...term.kind.bind,
|
|
257
|
-
...term.value.bind,
|
|
258
|
-
]);
|
|
259
|
-
}
|
|
260
|
-
function collated(bound, term, which) {
|
|
261
|
-
if (which === 1)
|
|
262
|
-
return '';
|
|
263
|
-
const name = bound.collate(term.ordering, which === 3, term.total === true);
|
|
264
|
-
return name === null ? '' : ` COLLATE ${name}`;
|
|
265
|
-
}
|
|
266
|
-
function sorted(bound, term) {
|
|
267
|
-
const direction = term.ordering.direction === 'desc' ? 'DESC' : 'ASC';
|
|
268
|
-
return joined([
|
|
269
|
-
placement(term),
|
|
270
|
-
fragment(`${klass(term).sql} ${direction}`, klass(term).bind),
|
|
271
|
-
...[1, 2, 3].map((which) => {
|
|
272
|
-
const value = classed(term, which);
|
|
273
|
-
return fragment(`${value.sql}${collated(bound, term, which)} ${direction}`, value.bind);
|
|
274
|
-
}),
|
|
275
|
-
], ', ');
|
|
276
|
-
}
|
|
277
|
-
function beyond(bound, term, value) {
|
|
278
|
-
const held = classOf(value, term.ordering);
|
|
279
|
-
const place = placement(term);
|
|
280
|
-
if (held.placement !== 1) {
|
|
281
|
-
return {
|
|
282
|
-
beyond: fragment(`${place.sql} > ${held.placement}`, place.bind),
|
|
283
|
-
equal: fragment(`${place.sql} = ${held.placement}`, place.bind),
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
const after = term.ordering.direction === 'desc' ? '<' : '>';
|
|
287
|
-
const kind = klass(term);
|
|
288
|
-
const scalar = classed(term, held.klass);
|
|
289
|
-
const collation = collated(bound, term, held.klass);
|
|
290
|
-
return {
|
|
291
|
-
beyond: any([
|
|
292
|
-
fragment(`${place.sql} > 1`, place.bind),
|
|
293
|
-
all([
|
|
294
|
-
fragment(`${place.sql} = 1`, place.bind),
|
|
295
|
-
any([
|
|
296
|
-
fragment(`${kind.sql} ${after} ${held.klass}`, kind.bind),
|
|
297
|
-
all([
|
|
298
|
-
fragment(`${kind.sql} = ${held.klass}`, kind.bind),
|
|
299
|
-
fragment(`${scalar.sql} ${after} ?${collation}`, [...scalar.bind, held.scalar]),
|
|
300
|
-
]),
|
|
301
|
-
]),
|
|
302
|
-
]),
|
|
303
|
-
]),
|
|
304
|
-
equal: all([
|
|
305
|
-
fragment(`${place.sql} = 1 AND ${kind.sql} = ${held.klass}`, [...place.bind, ...kind.bind]),
|
|
306
|
-
fragment(`${scalar.sql} = ?${collation}`, [...scalar.bind, held.scalar]),
|
|
307
|
-
]),
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
function following(parts, tie) {
|
|
311
|
-
let condition = tie;
|
|
312
|
-
for (const part of [...parts].reverse()) {
|
|
313
|
-
condition = any([part.beyond, all([part.equal, condition])]);
|
|
314
|
-
}
|
|
315
|
-
return condition;
|
|
316
|
-
}
|
|
317
|
-
function keyTerm(bound, key, ordering, index) {
|
|
119
|
+
function keyTerm(key, ordering, index) {
|
|
318
120
|
if (key.of === 'field') {
|
|
319
121
|
const alias = `o${index}`;
|
|
320
122
|
return {
|
|
321
|
-
term: {
|
|
123
|
+
term: {
|
|
124
|
+
ordering,
|
|
125
|
+
kind: fragment(`${alias}.kind`),
|
|
126
|
+
value: fragment(`${alias}.value`),
|
|
127
|
+
instant: fragment(`${alias}.instant`),
|
|
128
|
+
},
|
|
322
129
|
join: joined([
|
|
323
130
|
fragment(`LEFT JOIN replica_values ${alias} ON ${alias}.doc = d.id AND `),
|
|
324
|
-
grouped(
|
|
131
|
+
grouped(sql.placed(alias, key.field, 'exact')),
|
|
325
132
|
fragment(` AND ${alias}.item = -1 AND ${alias}.kind NOT IN ('object', 'undefined')`),
|
|
326
133
|
], ''),
|
|
327
134
|
};
|
|
328
135
|
}
|
|
329
136
|
const expression = key.of === 'room'
|
|
330
|
-
? located(
|
|
137
|
+
? located('d')
|
|
331
138
|
: key.of === 'path'
|
|
332
139
|
? fragment('d.path')
|
|
333
140
|
: key.of === 'type'
|
|
@@ -353,13 +160,20 @@ function decoded(kind, value) {
|
|
|
353
160
|
return JSON.parse(value);
|
|
354
161
|
return null;
|
|
355
162
|
}
|
|
163
|
+
function sortedValue(row, index) {
|
|
164
|
+
const kind = row[`k${index}`];
|
|
165
|
+
return sql.sortValue(kind, decoded(kind, row[`v${index}`]), row[`i${index}`]);
|
|
166
|
+
}
|
|
356
167
|
function decodedKey(key, row, index) {
|
|
357
168
|
if (key.of === 'field') {
|
|
358
169
|
const kind = row[`k${index}`];
|
|
359
|
-
return kind === null || kind === undefined ? undefined :
|
|
170
|
+
return kind === null || kind === undefined ? undefined : sortedValue(row, index);
|
|
360
171
|
}
|
|
361
172
|
return row[`v${index}`] ?? null;
|
|
362
173
|
}
|
|
174
|
+
function instantOf(term) {
|
|
175
|
+
return term.instant ?? fragment('NULL');
|
|
176
|
+
}
|
|
363
177
|
const registered = new WeakMap();
|
|
364
178
|
function hashed(text) {
|
|
365
179
|
let hash = 0x811c9dc5;
|
|
@@ -409,9 +223,6 @@ function collating(connection) {
|
|
|
409
223
|
return name;
|
|
410
224
|
};
|
|
411
225
|
}
|
|
412
|
-
function bodied(shape) {
|
|
413
|
-
return shape.sections !== null || shape.preamble || shape.markdown;
|
|
414
|
-
}
|
|
415
226
|
function masked(pointers) {
|
|
416
227
|
const root = { whole: false, members: new Map() };
|
|
417
228
|
for (const pointer of pointers) {
|
|
@@ -536,10 +347,10 @@ function backlinksOf(connection, bound, rows) {
|
|
|
536
347
|
return linked;
|
|
537
348
|
}
|
|
538
349
|
function documents(connection, bound, plan, shape, budget) {
|
|
539
|
-
const terms = shape.orderBy.map(({ key, options }, index) => keyTerm(
|
|
350
|
+
const terms = shape.orderBy.map(({ key, options }, index) => keyTerm(key, orderingOf(options), index));
|
|
540
351
|
const filter = filtered(bound, plan);
|
|
541
352
|
const cursor = plan.after?.values ?? null;
|
|
542
|
-
const room = located(
|
|
353
|
+
const room = located('d');
|
|
543
354
|
const tie = cursor === null
|
|
544
355
|
? null
|
|
545
356
|
: (() => {
|
|
@@ -555,19 +366,20 @@ function documents(connection, bound, plan, shape, budget) {
|
|
|
555
366
|
})();
|
|
556
367
|
const after = cursor === null || tie === null
|
|
557
368
|
? null
|
|
558
|
-
: following(terms.map(({ term }, index) => beyond(bound, term, cursor[index])), tie);
|
|
369
|
+
: sql.following(terms.map(({ term }, index) => sql.beyond(bound.collate, term, cursor[index])), tie);
|
|
559
370
|
const selected = joined([
|
|
560
371
|
fragment('SELECT d.id, d.key, d.path, d.type, d.binary, d.revision, d.fields, d.change, d.file_id'),
|
|
561
372
|
...terms.map(({ term }, index) => joined([
|
|
562
373
|
fragment(`, ${term.kind.sql} AS k${index}`, term.kind.bind),
|
|
563
374
|
fragment(`, ${term.value.sql} AS v${index}`, term.value.bind),
|
|
375
|
+
fragment(`, ${instantOf(term).sql} AS i${index}`, instantOf(term).bind),
|
|
564
376
|
], '')),
|
|
565
377
|
fragment(' FROM replica_entries d'),
|
|
566
378
|
...terms.flatMap(({ join }) => (join === null ? [] : [fragment(' '), join])),
|
|
567
379
|
fragment(' WHERE '),
|
|
568
380
|
after === null ? filter : all([filter, after]),
|
|
569
381
|
fragment(' ORDER BY '),
|
|
570
|
-
joined([...terms.map(({ term }) => sorted(bound, term)), room, fragment('d.path')], ', '),
|
|
382
|
+
joined([...terms.map(({ term }) => sql.sorted(bound.collate, term)), room, fragment('d.path')], ', '),
|
|
571
383
|
...(plan.limit === null ? [] : [fragment(' LIMIT ?', [plan.limit + 1])]),
|
|
572
384
|
], '');
|
|
573
385
|
const rows = connection.rows(selected.sql, selected.bind);
|
|
@@ -606,7 +418,7 @@ function withContent(connection, rows, budget) {
|
|
|
606
418
|
return rows;
|
|
607
419
|
const ids = rows.map((row) => row.id);
|
|
608
420
|
const sizes = new Map(connection
|
|
609
|
-
.rows(`SELECT id, octet_length(content) AS size FROM replica_entries WHERE id IN ${
|
|
421
|
+
.rows(`SELECT id, octet_length(content) AS size FROM replica_entries WHERE id IN ${listed(ids).sql}`, ids)
|
|
610
422
|
.map((row) => [row.id, Number(row.size ?? 0)]));
|
|
611
423
|
let cut = rows.length;
|
|
612
424
|
let spent = 0;
|
|
@@ -621,34 +433,30 @@ function withContent(connection, rows, budget) {
|
|
|
621
433
|
const kept = rows.slice(0, cut);
|
|
622
434
|
const read = kept.map((row) => row.id);
|
|
623
435
|
const held = new Map(connection
|
|
624
|
-
.rows(`SELECT id, content FROM replica_entries WHERE id IN ${
|
|
436
|
+
.rows(`SELECT id, content FROM replica_entries WHERE id IN ${listed(read).sql}`, read)
|
|
625
437
|
.map((row) => [row.id, row.content]));
|
|
626
438
|
return kept.map((row) => ({ ...row, content: held.get(row.id) ?? null }));
|
|
627
439
|
}
|
|
628
440
|
function storedFolders(bound, plan) {
|
|
629
|
-
if (plan.paths !== null || plan.
|
|
441
|
+
if (plan.paths !== null || plan.fileIds !== null)
|
|
442
|
+
return null;
|
|
443
|
+
if (plan.hop !== null || plan.backlinks !== null)
|
|
630
444
|
return null;
|
|
631
445
|
const whole = bound.rooms.filter(({ locator }) => bound.reach === null || bound.reach.rooms.includes(locator));
|
|
632
446
|
if (whole.length === 0)
|
|
633
447
|
return null;
|
|
634
448
|
const keys = whole.map(({ key }) => key);
|
|
635
449
|
const prefix = plan.root.pathPrefix;
|
|
636
|
-
const
|
|
450
|
+
const outside = (bound.reach?.areas ?? []).flatMap((area) => {
|
|
637
451
|
const key = bound.keys.get(area.room);
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
key,
|
|
643
|
-
area.path,
|
|
644
|
-
`${area.path}/`,
|
|
645
|
-
`${area.path}0`,
|
|
646
|
-
]),
|
|
647
|
-
];
|
|
452
|
+
if (key === undefined)
|
|
453
|
+
return [];
|
|
454
|
+
const fence = fenced('f', key, area, true);
|
|
455
|
+
return [fragment(`NOT ${grouped(fence).sql}`, fence.bind)];
|
|
648
456
|
});
|
|
649
457
|
return all([
|
|
650
|
-
fragment(`f.key IN ${
|
|
651
|
-
...
|
|
458
|
+
fragment(`f.key IN ${listed(keys).sql} AND f.layer = 0 AND f.present = 1`, keys),
|
|
459
|
+
...outside,
|
|
652
460
|
...(prefix === null || prefix === ''
|
|
653
461
|
? []
|
|
654
462
|
: [
|
|
@@ -663,7 +471,7 @@ function storedFolders(bound, plan) {
|
|
|
663
471
|
}
|
|
664
472
|
function groups(connection, bound, plan, shape) {
|
|
665
473
|
const keyed = shape.groupBy.map((key, index) => {
|
|
666
|
-
const { term, join } = keyTerm(
|
|
474
|
+
const { term, join } = keyTerm(key, GROUP_ORDER, index);
|
|
667
475
|
const kind = fragment(`COALESCE(${term.kind.sql}, 'null')`, term.kind.bind);
|
|
668
476
|
return { term: { ...term, kind }, join };
|
|
669
477
|
});
|
|
@@ -673,6 +481,7 @@ function groups(connection, bound, plan, shape) {
|
|
|
673
481
|
joined(keyed.map(({ term }, index) => joined([
|
|
674
482
|
fragment(`${term.kind.sql} AS k${index}`, term.kind.bind),
|
|
675
483
|
fragment(`, ${term.value.sql} AS v${index}`, term.value.bind),
|
|
484
|
+
fragment(`, ${instantOf(term).sql} AS i${index}`, instantOf(term).bind),
|
|
676
485
|
], '')), ', '),
|
|
677
486
|
fragment(', 1 AS n FROM replica_entries d'),
|
|
678
487
|
...keyed.flatMap(({ join }) => (join === null ? [] : [fragment(' '), join])),
|
|
@@ -686,10 +495,15 @@ function groups(connection, bound, plan, shape) {
|
|
|
686
495
|
? inner
|
|
687
496
|
: joined([
|
|
688
497
|
inner,
|
|
689
|
-
fragment(" UNION ALL SELECT 'string' AS k0, f.path AS v0, 0 AS n FROM replica_folders f WHERE "),
|
|
498
|
+
fragment(" UNION ALL SELECT 'string' AS k0, f.path AS v0, NULL AS i0, 0 AS n FROM replica_folders f WHERE "),
|
|
690
499
|
folders,
|
|
691
500
|
], '');
|
|
692
|
-
const columns = keyed
|
|
501
|
+
const columns = keyed
|
|
502
|
+
.map((_, index) => `k${index}, min(v${index}) AS v${index}, i${index}`)
|
|
503
|
+
.join(', ');
|
|
504
|
+
const held = keyed
|
|
505
|
+
.map((_, index) => `k${index}, i${index}, CASE WHEN i${index} IS NULL THEN v${index} END`)
|
|
506
|
+
.join(', ');
|
|
693
507
|
const counted = {
|
|
694
508
|
ordering: { ...GROUP_ORDER },
|
|
695
509
|
kind: fragment("'number'"),
|
|
@@ -702,7 +516,7 @@ function groups(connection, bound, plan, shape) {
|
|
|
702
516
|
});
|
|
703
517
|
const ordered = shape.orderBy.map(({ by, direction }) => by === 'count'
|
|
704
518
|
? { ...counted, ordering: { ...GROUP_ORDER, direction } }
|
|
705
|
-
: outer(by, { ...GROUP_ORDER, direction }));
|
|
519
|
+
: { ...outer(by, { ...GROUP_ORDER, direction }), instant: fragment(`i${by}`) });
|
|
706
520
|
const ties = keyed.flatMap((_, index) => [
|
|
707
521
|
{ ...outer(index, GROUP_TIES), total: true },
|
|
708
522
|
{ ordering: GROUP_TIES, kind: fragment("'string'"), value: fragment(`k${index}`) },
|
|
@@ -712,19 +526,21 @@ function groups(connection, bound, plan, shape) {
|
|
|
712
526
|
? null
|
|
713
527
|
: cursor[shape.orderBy.length].flatMap((value) => [
|
|
714
528
|
value,
|
|
715
|
-
|
|
529
|
+
heldKind(value ?? null),
|
|
716
530
|
]);
|
|
717
531
|
const after = cursor === null || tieValues === null
|
|
718
532
|
? null
|
|
719
|
-
: following(ordered.map((term, index) => beyond(bound, term, cursor[index])), following(ties
|
|
720
|
-
|
|
533
|
+
: sql.following(ordered.map((term, index) => sql.beyond(bound.collate, term, cursor[index])), sql.following(ties
|
|
534
|
+
.slice(0, -1)
|
|
535
|
+
.map((term, index) => sql.beyond(bound.collate, term, tieValues[index])), sql.beyond(bound.collate, ties.at(-1), tieValues.at(-1)).beyond));
|
|
536
|
+
const grouping = joined([fragment(`SELECT ${columns}, sum(n) AS n FROM (`), source, fragment(`) GROUP BY ${held}`)], '');
|
|
721
537
|
const windowed = joined([
|
|
722
538
|
fragment('SELECT * FROM ('),
|
|
723
539
|
grouping,
|
|
724
540
|
fragment(')'),
|
|
725
541
|
...(after === null ? [] : [fragment(' WHERE '), after]),
|
|
726
542
|
fragment(' ORDER BY '),
|
|
727
|
-
joined([...ordered, ...ties].map((term) => sorted(bound, term)), ', '),
|
|
543
|
+
joined([...ordered, ...ties].map((term) => sql.sorted(bound.collate, term)), ', '),
|
|
728
544
|
...(plan.limit === null ? [] : [fragment(' LIMIT ?', [plan.limit + 1])]),
|
|
729
545
|
], '');
|
|
730
546
|
const rows = connection.rows(windowed.sql, windowed.bind);
|
|
@@ -737,7 +553,7 @@ function groups(connection, bound, plan, shape) {
|
|
|
737
553
|
: last === undefined
|
|
738
554
|
? (plan.after?.text ?? FROM_START)
|
|
739
555
|
: cursorOf([
|
|
740
|
-
...shape.orderBy.map(({ by }) => by === 'count' ? Number(last.n) :
|
|
556
|
+
...shape.orderBy.map(({ by }) => by === 'count' ? Number(last.n) : sortedValue(last, by)),
|
|
741
557
|
keyOf(last),
|
|
742
558
|
]);
|
|
743
559
|
const total = plan.total
|
|
@@ -757,9 +573,6 @@ function boundOf(connection, plan, rooms) {
|
|
|
757
573
|
collate: collating(connection),
|
|
758
574
|
};
|
|
759
575
|
}
|
|
760
|
-
export function selected(connection, plan, rooms) {
|
|
761
|
-
return filtered(boundOf(connection, plan, rooms), plan);
|
|
762
|
-
}
|
|
763
576
|
export function answerQuery(connection, plan, rooms, budget = null) {
|
|
764
577
|
const bound = boundOf(connection, plan, rooms);
|
|
765
578
|
if (rooms.length === 0) {
|
package/dist/connection.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { type DocumentQuery, type GroupQuery } from '@agentshouse/mdmodel';
|
|
1
2
|
import { type TypesResult } from './model.js';
|
|
2
3
|
import { type Observation } from './observation.js';
|
|
3
|
-
import { type
|
|
4
|
+
import { type DocumentResult, type GroupResult, type PendingResult } from './query.js';
|
|
4
5
|
import { type EntrySeed } from './seed.js';
|
|
5
6
|
import { type SearchRequest, type SearchResult } from './search.js';
|
|
6
7
|
import { type Adapter, type Authorization, type Member, type ProviderRefusal, type ServiceMode } from './adapter.js';
|
|
@@ -12,8 +13,8 @@ import { type Recovery } from './recovery.js';
|
|
|
12
13
|
import { type SaveRefusal } from './save.js';
|
|
13
14
|
import { type RoomDraft, type RoomDraftParts, type RoomDraftWrite } from './room-draft.js';
|
|
14
15
|
import { type SyncStatus } from './status.js';
|
|
15
|
-
import { type RuntimeShell } from './durability.js';
|
|
16
|
-
import { type CodeAvailability, type Persistence, type Readiness
|
|
16
|
+
import { type RuntimeShell, type UnsentWork } from './durability.js';
|
|
17
|
+
import { type CodeAvailability, type Persistence, type Readiness } from './readiness.js';
|
|
17
18
|
import type { RuntimeDatabase } from './tables.js';
|
|
18
19
|
import { type NarrowedView, type PortReach, type RuntimeView } from './reach.js';
|
|
19
20
|
export type AppTypedSelection = {
|
|
@@ -116,6 +117,8 @@ export type MdRuntime = {
|
|
|
116
117
|
persistence(): boolean;
|
|
117
118
|
retry(): Promise<void>;
|
|
118
119
|
cleanUp(): Promise<number>;
|
|
120
|
+
unsent(): Promise<readonly UnsentWork[]>;
|
|
121
|
+
reset(): Promise<void>;
|
|
119
122
|
observe: {
|
|
120
123
|
(topic: 'rooms'): Promise<Observation<readonly AppRoom[]>>;
|
|
121
124
|
(topic: 'readiness'): Promise<Observation<Readiness>>;
|
|
@@ -124,6 +127,7 @@ export type MdRuntime = {
|
|
|
124
127
|
(topic: 'sync-status'): Promise<Observation<SyncStatus>>;
|
|
125
128
|
(topic: 'service-mode'): Promise<Observation<ServiceMode>>;
|
|
126
129
|
(topic: 'member'): Promise<Observation<Member | null>>;
|
|
130
|
+
(topic: 'authority-changes'): Promise<Observation<number>>;
|
|
127
131
|
(topic: 'query', query: DocumentQuery): Promise<Observation<DocumentResult | PendingResult>>;
|
|
128
132
|
(topic: 'query', query: GroupQuery): Promise<Observation<GroupResult | PendingResult>>;
|
|
129
133
|
(topic: 'queue', locator: string): Promise<Observation<readonly QueueEntry[]>>;
|
|
@@ -135,17 +139,14 @@ export type MdRuntime = {
|
|
|
135
139
|
};
|
|
136
140
|
reauthorize(): Promise<void>;
|
|
137
141
|
};
|
|
138
|
-
export type RuntimeTopic = 'rooms' | 'readiness' | 'connection-refusal' | 'storage-loss' | 'sync-status' | 'service-mode' | 'member' | 'query' | 'queue' | 'room-drafts' | 'presentation' | 'conversation' | 'conversations' | 'agents';
|
|
142
|
+
export type RuntimeTopic = 'rooms' | 'readiness' | 'connection-refusal' | 'storage-loss' | 'sync-status' | 'service-mode' | 'member' | 'authority-changes' | 'query' | 'queue' | 'room-drafts' | 'presentation' | 'conversation' | 'conversations' | 'agents';
|
|
139
143
|
export type ReadinessPort = {
|
|
140
144
|
readonly target: string;
|
|
141
145
|
readonly code: CodeAvailability;
|
|
142
|
-
readonly space: Space | null;
|
|
143
|
-
readonly search: SearchObligation;
|
|
144
146
|
};
|
|
145
147
|
export type ComposedRuntime = RuntimeView & {
|
|
146
148
|
seed(entry: EntrySeed): void;
|
|
147
149
|
stop(): Promise<void>;
|
|
148
|
-
lose(locators: readonly string[]): Promise<void>;
|
|
149
150
|
persisted(answer: Persistence): void;
|
|
150
151
|
within(reach: PortReach | null, port: ReadinessPort): NarrowedView;
|
|
151
152
|
};
|