@loadbare/app 0.5.5 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -4
- package/dist/build/assemble.d.ts +1 -1
- package/dist/build/assemble.d.ts.map +1 -1
- package/dist/build/assemble.js +80 -7
- package/dist/build/cli.d.ts +2 -2
- package/dist/build/cli.js +2 -2
- package/dist/build/expand.d.ts.map +1 -1
- package/dist/build/expand.js +30 -34
- package/dist/build/locations.d.ts +3 -3
- package/dist/build/locations.d.ts.map +1 -1
- package/dist/build/locations.js +15 -3
- package/dist/build/origins.d.ts +0 -13
- package/dist/build/origins.d.ts.map +1 -1
- package/dist/build/origins.js +32 -8
- package/dist/build/pages.d.ts +3 -3
- package/dist/build/pages.d.ts.map +1 -1
- package/dist/build/pages.js +9 -7
- package/dist/core/lb-constants.d.ts +17 -12
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +106 -43
- package/dist/core/lb-types.d.ts +103 -62
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js +11 -3
- package/dist/hub/lb-apply.d.ts +18 -4
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +184 -34
- package/dist/hub/lb-hub.browser.d.ts +1 -1
- package/dist/hub/lb-hub.browser.d.ts.map +1 -1
- package/dist/hub/lb-hub.browser.js +165 -83
- package/dist/server/lb-express.d.ts +7 -4
- package/dist/server/lb-express.d.ts.map +1 -1
- package/dist/server/lb-express.js +45 -33
- package/dist/server/lb-server.d.ts +67 -45
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +55 -18
- package/dist/tests/assemble.test.js +154 -2
- package/dist/tests/expand.test.js +30 -3
- package/dist/tests/helpers/hub.d.ts +73 -0
- package/dist/tests/helpers/hub.d.ts.map +1 -0
- package/dist/tests/helpers/hub.js +151 -0
- package/dist/tests/lb-apply.test.js +86 -62
- package/dist/tests/lb-express.test.d.ts +1 -1
- package/dist/tests/lb-express.test.js +43 -38
- package/dist/tests/lb-hub.test.d.ts +14 -0
- package/dist/tests/lb-hub.test.d.ts.map +1 -0
- package/dist/tests/lb-hub.test.js +319 -0
- package/dist/tests/{lb-rows.test.d.ts → lb-list.test.d.ts} +1 -1
- package/dist/tests/lb-list.test.d.ts.map +1 -0
- package/dist/tests/{lb-rows.test.js → lb-list.test.js} +109 -106
- package/dist/tests/lb-server.test.js +151 -100
- package/dist/tests/origins.test.js +19 -1
- package/dist/tests/pages.test.d.ts +1 -1
- package/dist/tests/pages.test.js +64 -14
- package/docs/TECHREF-1.0.md +1000 -0
- package/docs/reference/builder.md +4 -4
- package/docs/reference/chrome.md +56 -5
- package/docs/reference/custom-elements.md +59 -36
- package/docs/reference/data-binding.md +142 -84
- package/docs/reference/overview.md +1 -1
- package/docs/reference/page-files.md +64 -49
- package/docs/reference/server.md +7 -6
- package/docs/reference/widgets.md +43 -32
- package/docs/roadmap.md +68 -22
- package/docs/testing.md +47 -17
- package/docs/theory.md +2 -2
- package/docs/tutorials/010-pages-and-navigation.md +14 -8
- package/docs/tutorials/040-displaying-data.md +9 -9
- package/docs/tutorials/050-actions.md +5 -5
- package/docs/tutorials/060-custom-element-code.md +1 -1
- package/docs/tutorials/065-conditional-rendering.md +4 -4
- package/docs/tutorials/070-displaying-a-list.md +24 -47
- package/docs/tutorials/072-inserting-into-a-list.md +17 -17
- package/docs/tutorials/074-deleting-from-a-list.md +15 -17
- package/docs/tutorials/076-updating-a-list-item.md +20 -22
- package/docs/tutorials/080-widget-requests.md +27 -23
- package/docs/tutorials/090-using-widget-libraries.md +23 -1
- package/package.json +1 -2
- package/dist/hub/lb-rows.d.ts +0 -18
- package/dist/hub/lb-rows.d.ts.map +0 -1
- package/dist/hub/lb-rows.js +0 -106
- package/dist/tests/lb-rows.test.d.ts.map +0 -1
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import assert from "node:assert/strict";
|
|
9
9
|
import { describe, it } from "node:test";
|
|
10
|
-
import { createHub, patch,
|
|
11
|
-
import { isProjection } from "../core/lb-types";
|
|
10
|
+
import { createHub, list, patch, row, } from "../server/lb-server";
|
|
12
11
|
import { captured } from "./helpers/console";
|
|
13
12
|
/**
|
|
14
13
|
* A context to compare by identity.
|
|
@@ -21,25 +20,25 @@ import { captured } from "./helpers/console";
|
|
|
21
20
|
function context() {
|
|
22
21
|
return {};
|
|
23
22
|
}
|
|
24
|
-
/** A query that answers a fixed
|
|
23
|
+
/** A query that answers a fixed row and records that it ran. */
|
|
25
24
|
function stub(log, name, answer) {
|
|
26
|
-
return (ctx) => {
|
|
25
|
+
return row((ctx) => {
|
|
27
26
|
log.push(name);
|
|
28
27
|
seen.set(name, ctx);
|
|
29
28
|
return answer;
|
|
30
|
-
};
|
|
29
|
+
});
|
|
31
30
|
}
|
|
32
31
|
const seen = new Map();
|
|
33
32
|
function pages(entries) {
|
|
34
33
|
const map = {};
|
|
35
34
|
for (const [name, entry] of Object.entries(entries)) {
|
|
36
|
-
map[name] = { queries: entry.queries,
|
|
35
|
+
map[name] = { queries: entry.queries, requests: entry.requests ?? {} };
|
|
37
36
|
}
|
|
38
37
|
return map;
|
|
39
38
|
}
|
|
40
|
-
// ---
|
|
41
|
-
describe("a page
|
|
42
|
-
it("runs
|
|
39
|
+
// --- Entering a page ----------------------------------------------------
|
|
40
|
+
describe("entering a page", () => {
|
|
41
|
+
it("runs onPageEnter before any query, then the whole query set", async () => {
|
|
43
42
|
const log = [];
|
|
44
43
|
const hub = createHub(pages({
|
|
45
44
|
report: {
|
|
@@ -47,16 +46,16 @@ describe("a page GET", () => {
|
|
|
47
46
|
head: stub(log, "head", { title: "Q1" }),
|
|
48
47
|
body: stub(log, "body", { line: "one" }),
|
|
49
48
|
},
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
requests: {
|
|
50
|
+
onPageEnter: () => void log.push("onPageEnter"),
|
|
52
51
|
},
|
|
53
52
|
},
|
|
54
53
|
}));
|
|
55
54
|
const data = await hub.dataForPage("report", context());
|
|
56
|
-
assert.deepEqual(log, ["
|
|
55
|
+
assert.deepEqual(log, ["onPageEnter", "head", "body"]);
|
|
57
56
|
assert.deepEqual(data, { head: { title: "Q1" }, body: { line: "one" } });
|
|
58
57
|
});
|
|
59
|
-
it("runs the query set when the page declares no
|
|
58
|
+
it("runs the query set when the page declares no requests", async () => {
|
|
60
59
|
const log = [];
|
|
61
60
|
const hub = createHub(pages({ report: { queries: { head: stub(log, "head", { a: "1" }) } } }));
|
|
62
61
|
assert.deepEqual(await hub.dataForPage("report", context()), {
|
|
@@ -89,7 +88,7 @@ describe("a page GET", () => {
|
|
|
89
88
|
});
|
|
90
89
|
it("awaits a query that returns a promise", async () => {
|
|
91
90
|
const hub = createHub(pages({
|
|
92
|
-
report: { queries: { head: async () => ({ a: "1" }) } },
|
|
91
|
+
report: { queries: { head: row(async () => ({ a: "1" })) } },
|
|
93
92
|
}));
|
|
94
93
|
assert.deepEqual(await hub.dataForPage("report", context()), {
|
|
95
94
|
head: { a: "1" },
|
|
@@ -97,6 +96,28 @@ describe("a page GET", () => {
|
|
|
97
96
|
});
|
|
98
97
|
});
|
|
99
98
|
// --- Actions -------------------------------------------------------------
|
|
99
|
+
describe("a reserved name", () => {
|
|
100
|
+
// Names beginning with lb- are Loadbare's: the hub's own query, the CRUD
|
|
101
|
+
// operations. See LB_RESERVED_PREFIX in core/lb-constants.ts.
|
|
102
|
+
it("is refused as a query, at startup", () => {
|
|
103
|
+
assert.throws(() => createHub({
|
|
104
|
+
home: {
|
|
105
|
+
queries: { "lb-navigation": row(async () => ({})) },
|
|
106
|
+
requests: {},
|
|
107
|
+
},
|
|
108
|
+
}), /page 'home' declares 'lb-navigation', but names beginning with 'lb-' are reserved/);
|
|
109
|
+
});
|
|
110
|
+
it("is refused as an action, at startup", () => {
|
|
111
|
+
assert.throws(() => createHub({
|
|
112
|
+
home: {
|
|
113
|
+
queries: {},
|
|
114
|
+
requests: {
|
|
115
|
+
actions: { "lb-delete": { run: () => { }, refresh: [] } },
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
}), /declares 'lb-delete', but names beginning with 'lb-' are reserved/);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
100
121
|
describe("an action", () => {
|
|
101
122
|
it("is refused unless the page declared it", async () => {
|
|
102
123
|
// The rule that keeps this from being an RPC endpoint: the wire cannot
|
|
@@ -105,7 +126,7 @@ describe("an action", () => {
|
|
|
105
126
|
const hub = createHub(pages({
|
|
106
127
|
counter: {
|
|
107
128
|
queries: { count: stub(log, "count", { n: "1" }) },
|
|
108
|
-
|
|
129
|
+
requests: {
|
|
109
130
|
actions: {
|
|
110
131
|
bump: { run: () => void log.push("bump"), refresh: ["count"] },
|
|
111
132
|
},
|
|
@@ -131,7 +152,7 @@ describe("an action", () => {
|
|
|
131
152
|
count: stub(log, "count", { n: "1" }),
|
|
132
153
|
unrelated: stub(log, "unrelated", { x: "9" }),
|
|
133
154
|
},
|
|
134
|
-
|
|
155
|
+
requests: {
|
|
135
156
|
actions: {
|
|
136
157
|
bump: { run: () => void log.push("bump"), refresh: ["count"] },
|
|
137
158
|
},
|
|
@@ -147,16 +168,16 @@ describe("an action", () => {
|
|
|
147
168
|
const hub = createHub(pages({
|
|
148
169
|
roster: {
|
|
149
170
|
queries: {},
|
|
150
|
-
|
|
171
|
+
requests: {
|
|
151
172
|
actions: {
|
|
152
173
|
pick: { run: (_ctx, w) => void (where = w), refresh: [] },
|
|
153
174
|
},
|
|
154
175
|
},
|
|
155
176
|
},
|
|
156
177
|
}));
|
|
157
|
-
await hub.runAction("roster", "pick", {
|
|
178
|
+
await hub.runAction("roster", "pick", { list: "members", key: "7", cell: "role", value: "lead" }, context());
|
|
158
179
|
assert.deepEqual(where, {
|
|
159
|
-
|
|
180
|
+
list: "members",
|
|
160
181
|
key: "7",
|
|
161
182
|
cell: "role",
|
|
162
183
|
value: "lead",
|
|
@@ -169,7 +190,7 @@ describe("an action", () => {
|
|
|
169
190
|
const hub = createHub(pages({
|
|
170
191
|
counter: {
|
|
171
192
|
queries: { count: stub(log, "count", {}) },
|
|
172
|
-
|
|
193
|
+
requests: {
|
|
173
194
|
actions: {
|
|
174
195
|
bump: {
|
|
175
196
|
run: (ctx) => void (actionCtx = ctx),
|
|
@@ -190,8 +211,8 @@ describe("an action", () => {
|
|
|
190
211
|
const store = { n: 1 };
|
|
191
212
|
const hub = createHub(pages({
|
|
192
213
|
counter: {
|
|
193
|
-
queries: { count: () => ({ n: String(store.n) }) },
|
|
194
|
-
|
|
214
|
+
queries: { count: row(() => ({ n: String(store.n) })) },
|
|
215
|
+
requests: {
|
|
195
216
|
actions: {
|
|
196
217
|
bump: { run: () => void (store.n += 1), refresh: ["count"] },
|
|
197
218
|
},
|
|
@@ -208,8 +229,8 @@ describe("an action", () => {
|
|
|
208
229
|
// what it changed and may say only that. The narrower answer wins.
|
|
209
230
|
const hub = createHub(pages({
|
|
210
231
|
roster: {
|
|
211
|
-
queries: { members: () =>
|
|
212
|
-
|
|
232
|
+
queries: { members: list(() => [{ id: "1" }, { id: "2" }]) },
|
|
233
|
+
requests: {
|
|
213
234
|
actions: {
|
|
214
235
|
rename: {
|
|
215
236
|
run: () => ({ members: patch({ rows: [{ id: "2" }] }) }),
|
|
@@ -220,13 +241,15 @@ describe("an action", () => {
|
|
|
220
241
|
},
|
|
221
242
|
}));
|
|
222
243
|
const data = await hub.runAction("roster", "rename", {}, context());
|
|
223
|
-
assert.deepEqual(data, { members: {
|
|
244
|
+
assert.deepEqual(data, { members: { rows: [{ id: "2" }] } });
|
|
224
245
|
});
|
|
225
246
|
it("answers with the refresh set alone when it states nothing", async () => {
|
|
226
247
|
const hub = createHub(pages({
|
|
227
248
|
counter: {
|
|
228
|
-
queries: { count: () => ({ n: "1" }) },
|
|
229
|
-
|
|
249
|
+
queries: { count: row(() => ({ n: "1" })) },
|
|
250
|
+
requests: {
|
|
251
|
+
actions: { bump: { run: () => { }, refresh: ["count"] } },
|
|
252
|
+
},
|
|
230
253
|
},
|
|
231
254
|
}));
|
|
232
255
|
assert.deepEqual(await hub.runAction("counter", "bump", {}, context()), {
|
|
@@ -236,8 +259,8 @@ describe("an action", () => {
|
|
|
236
259
|
it("skips a refresh name the page does not declare, and runs its siblings", async () => {
|
|
237
260
|
const hub = createHub(pages({
|
|
238
261
|
counter: {
|
|
239
|
-
queries: { count: () => ({ n: "1" }) },
|
|
240
|
-
|
|
262
|
+
queries: { count: row(() => ({ n: "1" })) },
|
|
263
|
+
requests: {
|
|
241
264
|
actions: {
|
|
242
265
|
bump: { run: () => { }, refresh: ["ghost", "count"] },
|
|
243
266
|
},
|
|
@@ -253,7 +276,9 @@ describe("an action", () => {
|
|
|
253
276
|
run: async () => ({ count: { n: "5" } }),
|
|
254
277
|
refresh: [],
|
|
255
278
|
};
|
|
256
|
-
const hub = createHub(pages({
|
|
279
|
+
const hub = createHub(pages({
|
|
280
|
+
counter: { queries: {}, requests: { actions: { bump: action } } },
|
|
281
|
+
}));
|
|
257
282
|
assert.deepEqual(await hub.runAction("counter", "bump", {}, context()), {
|
|
258
283
|
count: { n: "5" },
|
|
259
284
|
});
|
|
@@ -262,10 +287,10 @@ describe("an action", () => {
|
|
|
262
287
|
// --- Cell change -----------------------------------------------------------
|
|
263
288
|
describe("a cell-change", () => {
|
|
264
289
|
it("is refused unless the query declared it", async () => {
|
|
265
|
-
const hub = createHub(pages({ roster: { queries: { members: () =>
|
|
266
|
-
const { result, warn } = await captured(() => hub.runCellChange("roster", {
|
|
290
|
+
const hub = createHub(pages({ roster: { queries: { members: list(() => []) } } }));
|
|
291
|
+
const { result, warn } = await captured(() => hub.runCellChange("roster", { list: "members", key: "1", cell: "name", value: "Ada" }, context()));
|
|
267
292
|
assert.deepEqual(result, {});
|
|
268
|
-
assert.match(warn.join("\n"), /page 'roster' declares no
|
|
293
|
+
assert.match(warn.join("\n"), /page 'roster' declares no cellChange for 'members'/);
|
|
269
294
|
});
|
|
270
295
|
it("hands the operation its key, cell and value, then runs its refresh set", async () => {
|
|
271
296
|
let where;
|
|
@@ -273,7 +298,7 @@ describe("a cell-change", () => {
|
|
|
273
298
|
const hub = createHub(pages({
|
|
274
299
|
roster: {
|
|
275
300
|
queries: { members: stub(log, "members", { n: "1" }) },
|
|
276
|
-
|
|
301
|
+
requests: {
|
|
277
302
|
crud: {
|
|
278
303
|
members: {
|
|
279
304
|
cellChange: {
|
|
@@ -285,7 +310,7 @@ describe("a cell-change", () => {
|
|
|
285
310
|
},
|
|
286
311
|
},
|
|
287
312
|
}));
|
|
288
|
-
const data = await hub.runCellChange("roster", {
|
|
313
|
+
const data = await hub.runCellChange("roster", { list: "members", key: "7", cell: "role", value: "lead" }, context());
|
|
289
314
|
assert.deepEqual(where, { key: "7", cell: "role", value: "lead" });
|
|
290
315
|
assert.deepEqual(log, ["members"]);
|
|
291
316
|
assert.deepEqual(data, { members: { n: "1" } });
|
|
@@ -293,8 +318,8 @@ describe("a cell-change", () => {
|
|
|
293
318
|
it("lays what it stated over what the refresh produced", async () => {
|
|
294
319
|
const hub = createHub(pages({
|
|
295
320
|
roster: {
|
|
296
|
-
queries: { members: () =>
|
|
297
|
-
|
|
321
|
+
queries: { members: list(() => [{ id: "1" }, { id: "2" }]) },
|
|
322
|
+
requests: {
|
|
298
323
|
crud: {
|
|
299
324
|
members: {
|
|
300
325
|
cellChange: {
|
|
@@ -308,29 +333,29 @@ describe("a cell-change", () => {
|
|
|
308
333
|
},
|
|
309
334
|
},
|
|
310
335
|
}));
|
|
311
|
-
const data = await hub.runCellChange("roster", {
|
|
336
|
+
const data = await hub.runCellChange("roster", { list: "members", key: "2", cell: "name", value: "Grace" }, context());
|
|
312
337
|
assert.deepEqual(data, {
|
|
313
|
-
members: {
|
|
338
|
+
members: { rows: [{ id: "2", name: "Grace" }] },
|
|
314
339
|
});
|
|
315
340
|
});
|
|
316
341
|
});
|
|
317
|
-
// ---
|
|
318
|
-
describe("a
|
|
342
|
+
// --- Row delete ------------------------------------------------------
|
|
343
|
+
describe("a row-delete", () => {
|
|
319
344
|
it("is refused unless the query declared it", async () => {
|
|
320
|
-
const hub = createHub(pages({ roster: { queries: { members: () =>
|
|
321
|
-
const { result, warn } = await captured(() => hub.
|
|
345
|
+
const hub = createHub(pages({ roster: { queries: { members: list(() => []) } } }));
|
|
346
|
+
const { result, warn } = await captured(() => hub.runRowDelete("roster", { list: "members", key: "1" }, context()));
|
|
322
347
|
assert.deepEqual(result, {});
|
|
323
|
-
assert.match(warn.join("\n"), /page 'roster' declares no
|
|
348
|
+
assert.match(warn.join("\n"), /page 'roster' declares no rowDelete for 'members'/);
|
|
324
349
|
});
|
|
325
350
|
it("hands the operation its key, then runs its refresh set", async () => {
|
|
326
351
|
let where;
|
|
327
352
|
const hub = createHub(pages({
|
|
328
353
|
roster: {
|
|
329
|
-
queries: { members: () =>
|
|
330
|
-
|
|
354
|
+
queries: { members: list(() => []) },
|
|
355
|
+
requests: {
|
|
331
356
|
crud: {
|
|
332
357
|
members: {
|
|
333
|
-
|
|
358
|
+
rowDelete: {
|
|
334
359
|
run: (_ctx, w) => void (where = w),
|
|
335
360
|
refresh: [],
|
|
336
361
|
},
|
|
@@ -339,17 +364,17 @@ describe("a tuple-delete", () => {
|
|
|
339
364
|
},
|
|
340
365
|
},
|
|
341
366
|
}));
|
|
342
|
-
await hub.
|
|
367
|
+
await hub.runRowDelete("roster", { list: "members", key: "7" }, context());
|
|
343
368
|
assert.deepEqual(where, { key: "7" });
|
|
344
369
|
});
|
|
345
370
|
it("answers with the drop it stated", async () => {
|
|
346
371
|
const hub = createHub(pages({
|
|
347
372
|
roster: {
|
|
348
|
-
queries: { members: () =>
|
|
349
|
-
|
|
373
|
+
queries: { members: list(() => [{ id: "1" }, { id: "2" }]) },
|
|
374
|
+
requests: {
|
|
350
375
|
crud: {
|
|
351
376
|
members: {
|
|
352
|
-
|
|
377
|
+
rowDelete: {
|
|
353
378
|
run: (_ctx, w) => ({
|
|
354
379
|
members: patch({ drop: [w.key] }),
|
|
355
380
|
}),
|
|
@@ -360,27 +385,27 @@ describe("a tuple-delete", () => {
|
|
|
360
385
|
},
|
|
361
386
|
},
|
|
362
387
|
}));
|
|
363
|
-
const data = await hub.
|
|
364
|
-
assert.deepEqual(data, { members: {
|
|
388
|
+
const data = await hub.runRowDelete("roster", { list: "members", key: "1" }, context());
|
|
389
|
+
assert.deepEqual(data, { members: { drop: ["1"] } });
|
|
365
390
|
});
|
|
366
391
|
});
|
|
367
|
-
// ---
|
|
368
|
-
describe("a
|
|
392
|
+
// --- Row insert --------------------------------------------------------
|
|
393
|
+
describe("a row-insert", () => {
|
|
369
394
|
it("is refused unless the query declared it", async () => {
|
|
370
|
-
const hub = createHub(pages({ roster: { queries: { members: () =>
|
|
371
|
-
const { result, warn } = await captured(() => hub.
|
|
395
|
+
const hub = createHub(pages({ roster: { queries: { members: list(() => []) } } }));
|
|
396
|
+
const { result, warn } = await captured(() => hub.runRowInsert("roster", { list: "members", values: { name: "Ada" } }, context()));
|
|
372
397
|
assert.deepEqual(result, {});
|
|
373
|
-
assert.match(warn.join("\n"), /page 'roster' declares no
|
|
398
|
+
assert.match(warn.join("\n"), /page 'roster' declares no rowInsert for 'members'/);
|
|
374
399
|
});
|
|
375
400
|
it("hands the operation the gathered values, then runs its refresh set", async () => {
|
|
376
401
|
let where;
|
|
377
402
|
const hub = createHub(pages({
|
|
378
403
|
roster: {
|
|
379
|
-
queries: { members: () =>
|
|
380
|
-
|
|
404
|
+
queries: { members: list(() => []) },
|
|
405
|
+
requests: {
|
|
381
406
|
crud: {
|
|
382
407
|
members: {
|
|
383
|
-
|
|
408
|
+
rowInsert: {
|
|
384
409
|
run: (_ctx, w) => void (where = w),
|
|
385
410
|
refresh: [],
|
|
386
411
|
},
|
|
@@ -389,17 +414,17 @@ describe("a tuple-insert", () => {
|
|
|
389
414
|
},
|
|
390
415
|
},
|
|
391
416
|
}));
|
|
392
|
-
await hub.
|
|
417
|
+
await hub.runRowInsert("roster", { list: "members", values: { name: "Ada", role: "Analyst" } }, context());
|
|
393
418
|
assert.deepEqual(where, { values: { name: "Ada", role: "Analyst" } });
|
|
394
419
|
});
|
|
395
420
|
it("answers with the row it stated", async () => {
|
|
396
421
|
const hub = createHub(pages({
|
|
397
422
|
roster: {
|
|
398
|
-
queries: { members: () =>
|
|
399
|
-
|
|
423
|
+
queries: { members: list(() => [{ id: "1" }]) },
|
|
424
|
+
requests: {
|
|
400
425
|
crud: {
|
|
401
426
|
members: {
|
|
402
|
-
|
|
427
|
+
rowInsert: {
|
|
403
428
|
run: (_ctx, w) => ({
|
|
404
429
|
members: patch({ rows: [{ id: "2", ...w.values }] }),
|
|
405
430
|
}),
|
|
@@ -410,29 +435,29 @@ describe("a tuple-insert", () => {
|
|
|
410
435
|
},
|
|
411
436
|
},
|
|
412
437
|
}));
|
|
413
|
-
const data = await hub.
|
|
438
|
+
const data = await hub.runRowInsert("roster", { list: "members", values: { name: "Ada" } }, context());
|
|
414
439
|
assert.deepEqual(data, {
|
|
415
|
-
members: {
|
|
440
|
+
members: { rows: [{ id: "2", name: "Ada" }] },
|
|
416
441
|
});
|
|
417
442
|
});
|
|
418
443
|
});
|
|
419
|
-
// ---
|
|
420
|
-
describe("a
|
|
444
|
+
// --- Row update ----------------------------------------------------------
|
|
445
|
+
describe("a row-update", () => {
|
|
421
446
|
it("is refused unless the query declared it", async () => {
|
|
422
|
-
const hub = createHub(pages({ roster: { queries: { members: () =>
|
|
423
|
-
const { result, warn } = await captured(() => hub.
|
|
447
|
+
const hub = createHub(pages({ roster: { queries: { members: list(() => []) } } }));
|
|
448
|
+
const { result, warn } = await captured(() => hub.runRowUpdate("roster", { list: "members", key: "1", values: { name: "Ada" } }, context()));
|
|
424
449
|
assert.deepEqual(result, {});
|
|
425
|
-
assert.match(warn.join("\n"), /page 'roster' declares no
|
|
450
|
+
assert.match(warn.join("\n"), /page 'roster' declares no rowUpdate for 'members'/);
|
|
426
451
|
});
|
|
427
452
|
it("hands the operation the key and the gathered values, then runs its refresh set", async () => {
|
|
428
453
|
let where;
|
|
429
454
|
const hub = createHub(pages({
|
|
430
455
|
roster: {
|
|
431
|
-
queries: { members: () =>
|
|
432
|
-
|
|
456
|
+
queries: { members: list(() => []) },
|
|
457
|
+
requests: {
|
|
433
458
|
crud: {
|
|
434
459
|
members: {
|
|
435
|
-
|
|
460
|
+
rowUpdate: {
|
|
436
461
|
run: (_ctx, w) => void (where = w),
|
|
437
462
|
refresh: [],
|
|
438
463
|
},
|
|
@@ -441,7 +466,7 @@ describe("a tuple-update", () => {
|
|
|
441
466
|
},
|
|
442
467
|
},
|
|
443
468
|
}));
|
|
444
|
-
await hub.
|
|
469
|
+
await hub.runRowUpdate("roster", { list: "members", key: "7", values: { name: "Ada", role: "Analyst" } }, context());
|
|
445
470
|
assert.deepEqual(where, {
|
|
446
471
|
key: "7",
|
|
447
472
|
values: { name: "Ada", role: "Analyst" },
|
|
@@ -450,11 +475,11 @@ describe("a tuple-update", () => {
|
|
|
450
475
|
it("answers with the row it stated", async () => {
|
|
451
476
|
const hub = createHub(pages({
|
|
452
477
|
roster: {
|
|
453
|
-
queries: { members: () =>
|
|
454
|
-
|
|
478
|
+
queries: { members: list(() => [{ id: "1", name: "old" }]) },
|
|
479
|
+
requests: {
|
|
455
480
|
crud: {
|
|
456
481
|
members: {
|
|
457
|
-
|
|
482
|
+
rowUpdate: {
|
|
458
483
|
run: (_ctx, w) => ({
|
|
459
484
|
members: patch({ rows: [{ id: w.key, ...w.values }] }),
|
|
460
485
|
}),
|
|
@@ -465,31 +490,57 @@ describe("a tuple-update", () => {
|
|
|
465
490
|
},
|
|
466
491
|
},
|
|
467
492
|
}));
|
|
468
|
-
const data = await hub.
|
|
493
|
+
const data = await hub.runRowUpdate("roster", { list: "members", key: "1", values: { name: "Ada" } }, context());
|
|
469
494
|
assert.deepEqual(data, {
|
|
470
|
-
members: {
|
|
495
|
+
members: { rows: [{ id: "1", name: "Ada" }] },
|
|
471
496
|
});
|
|
472
497
|
});
|
|
473
498
|
});
|
|
474
499
|
// --- The wire vocabulary -------------------------------------------------
|
|
475
|
-
describe("
|
|
476
|
-
it("
|
|
477
|
-
assert.
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
500
|
+
describe("cardinality is declared, not inspected", () => {
|
|
501
|
+
it("marks a list query and a row query apart", () => {
|
|
502
|
+
assert.equal(list(() => []).kind, "list");
|
|
503
|
+
assert.equal(row(() => ({})).kind, "row");
|
|
504
|
+
});
|
|
505
|
+
it("carries a whole set as the array itself, with no wrapper", async () => {
|
|
506
|
+
const set = [{ id: "1" }, { id: "2" }];
|
|
507
|
+
const hub = createHub(pages({ roster: { queries: { members: list(() => set) } } }));
|
|
508
|
+
assert.deepEqual(await hub.dataForPage("roster", context()), {
|
|
509
|
+
members: set,
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
it("carries only what changed as an object, and adds nothing", () => {
|
|
513
|
+
assert.deepEqual(patch({ drop: ["1"] }), { drop: ["1"] });
|
|
514
|
+
assert.deepEqual(patch({}), {});
|
|
515
|
+
});
|
|
516
|
+
it("tells a whole set from a patch by Array.isArray, so no column is reserved", () => {
|
|
517
|
+
// `op` used to be a reserved column name, because the hub read it to
|
|
518
|
+
// decide which shape had arrived. Nothing inspects a value now.
|
|
519
|
+
assert.equal(Array.isArray(patch({ rows: [] })), false);
|
|
520
|
+
assert.equal(Array.isArray([{ op: "rows" }]), true);
|
|
521
|
+
});
|
|
522
|
+
it("refuses a list query that answers with one row", async () => {
|
|
523
|
+
const hub = createHub(pages({
|
|
524
|
+
roster: {
|
|
525
|
+
queries: {
|
|
526
|
+
members: list(() => ({})),
|
|
527
|
+
},
|
|
528
|
+
},
|
|
529
|
+
}));
|
|
530
|
+
const { result, warn } = await captured(() => hub.dataForPage("roster", context()));
|
|
531
|
+
assert.deepEqual(result, {});
|
|
532
|
+
assert.match(warn.join("\n"), /query 'members' is declared list but answered with one row/);
|
|
533
|
+
});
|
|
534
|
+
it("refuses a row query that answers with rows", async () => {
|
|
535
|
+
const hub = createHub(pages({
|
|
536
|
+
roster: {
|
|
537
|
+
queries: {
|
|
538
|
+
chosen: row(() => []),
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
}));
|
|
542
|
+
const { result, warn } = await captured(() => hub.dataForPage("roster", context()));
|
|
543
|
+
assert.deepEqual(result, {});
|
|
544
|
+
assert.match(warn.join("\n"), /query 'chosen' is declared row but answered with rows/);
|
|
494
545
|
});
|
|
495
546
|
});
|
|
@@ -91,7 +91,7 @@ describe("scanOrigin", () => {
|
|
|
91
91
|
const dir = await tree({
|
|
92
92
|
"chrome.html": "<lb-hub><main></main></lb-hub>",
|
|
93
93
|
"about.page.html": "<h1>About</h1>",
|
|
94
|
-
"about.
|
|
94
|
+
"about.requests.ts": "export const requests = {};",
|
|
95
95
|
"imports.ts": "export default {};",
|
|
96
96
|
});
|
|
97
97
|
try {
|
|
@@ -118,6 +118,24 @@ describe("scanOrigin", () => {
|
|
|
118
118
|
await rm(dir, { recursive: true, force: true });
|
|
119
119
|
}
|
|
120
120
|
});
|
|
121
|
+
it("throws when a definition filename carries capitals", async () => {
|
|
122
|
+
const dir = await tree({ "App-Box.html": "<div></div>" });
|
|
123
|
+
try {
|
|
124
|
+
await assert.rejects(() => scanOrigin({ name: "src", dirs: [dir] }), /carries capitals.*must be named 'app-box\.html'/);
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
await rm(dir, { recursive: true, force: true });
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
it("throws when a widget script filename carries capitals", async () => {
|
|
131
|
+
const dir = await tree({ "App-Box.browser.ts": "export {};" });
|
|
132
|
+
try {
|
|
133
|
+
await assert.rejects(() => scanOrigin({ name: "src", dirs: [dir] }), /must be named 'app-box\.browser\.ts'/);
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
await rm(dir, { recursive: true, force: true });
|
|
137
|
+
}
|
|
138
|
+
});
|
|
121
139
|
it("throws when one origin has two definitions for the same tag", async () => {
|
|
122
140
|
const dir = await tree({
|
|
123
141
|
"a/app-box.html": "<div></div>",
|