on-zero 0.4.26 → 0.4.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/createZeroClient.cjs +14 -1
- package/dist/cjs/createZeroClient.native.js +15 -1
- package/dist/cjs/createZeroClient.native.js.map +1 -1
- package/dist/cjs/httpPull/auth.test.cjs +197 -0
- package/dist/cjs/httpPull/auth.test.native.js +279 -0
- package/dist/cjs/httpPull/auth.test.native.js.map +1 -0
- package/dist/cjs/httpPull/churn.test.cjs +132 -0
- package/dist/cjs/httpPull/churn.test.native.js +155 -0
- package/dist/cjs/httpPull/churn.test.native.js.map +1 -0
- package/dist/cjs/httpPull/fixtureSchema.cjs +76 -0
- package/dist/cjs/httpPull/fixtureSchema.native.js +82 -0
- package/dist/cjs/httpPull/fixtureSchema.native.js.map +1 -0
- package/dist/cjs/httpPull/fixtureServer.cjs +340 -0
- package/dist/cjs/httpPull/fixtureServer.native.js +534 -0
- package/dist/cjs/httpPull/fixtureServer.native.js.map +1 -0
- package/dist/cjs/httpPull/integration.test.cjs +53 -0
- package/dist/cjs/httpPull/integration.test.native.js +60 -0
- package/dist/cjs/httpPull/integration.test.native.js.map +1 -0
- package/dist/cjs/httpPull/rebase.test.cjs +360 -0
- package/dist/cjs/httpPull/rebase.test.native.js +420 -0
- package/dist/cjs/httpPull/rebase.test.native.js.map +1 -0
- package/dist/cjs/httpPull/relations.test.cjs +107 -0
- package/dist/cjs/httpPull/relations.test.native.js +119 -0
- package/dist/cjs/httpPull/relations.test.native.js.map +1 -0
- package/dist/cjs/httpPull/testHarness.cjs +100 -0
- package/dist/cjs/httpPull/testHarness.native.js +112 -0
- package/dist/cjs/httpPull/testHarness.native.js.map +1 -0
- package/dist/cjs/httpPull/transport.test.cjs +588 -0
- package/dist/cjs/httpPull/transport.test.native.js +677 -0
- package/dist/cjs/httpPull/transport.test.native.js.map +1 -0
- package/dist/cjs/httpPullTransport.cjs +447 -0
- package/dist/cjs/httpPullTransport.native.js +710 -0
- package/dist/cjs/httpPullTransport.native.js.map +1 -0
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.native.js +1 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/esm/createZeroClient.mjs +14 -1
- package/dist/esm/createZeroClient.mjs.map +1 -1
- package/dist/esm/createZeroClient.native.js +15 -1
- package/dist/esm/createZeroClient.native.js.map +1 -1
- package/dist/esm/httpPull/auth.test.mjs +198 -0
- package/dist/esm/httpPull/auth.test.mjs.map +1 -0
- package/dist/esm/httpPull/auth.test.native.js +277 -0
- package/dist/esm/httpPull/auth.test.native.js.map +1 -0
- package/dist/esm/httpPull/churn.test.mjs +133 -0
- package/dist/esm/httpPull/churn.test.mjs.map +1 -0
- package/dist/esm/httpPull/churn.test.native.js +153 -0
- package/dist/esm/httpPull/churn.test.native.js.map +1 -0
- package/dist/esm/httpPull/fixtureSchema.mjs +50 -0
- package/dist/esm/httpPull/fixtureSchema.mjs.map +1 -0
- package/dist/esm/httpPull/fixtureSchema.native.js +53 -0
- package/dist/esm/httpPull/fixtureSchema.native.js.map +1 -0
- package/dist/esm/httpPull/fixtureServer.mjs +315 -0
- package/dist/esm/httpPull/fixtureServer.mjs.map +1 -0
- package/dist/esm/httpPull/fixtureServer.native.js +506 -0
- package/dist/esm/httpPull/fixtureServer.native.js.map +1 -0
- package/dist/esm/httpPull/integration.test.mjs +54 -0
- package/dist/esm/httpPull/integration.test.mjs.map +1 -0
- package/dist/esm/httpPull/integration.test.native.js +58 -0
- package/dist/esm/httpPull/integration.test.native.js.map +1 -0
- package/dist/esm/httpPull/rebase.test.mjs +361 -0
- package/dist/esm/httpPull/rebase.test.mjs.map +1 -0
- package/dist/esm/httpPull/rebase.test.native.js +418 -0
- package/dist/esm/httpPull/rebase.test.native.js.map +1 -0
- package/dist/esm/httpPull/relations.test.mjs +108 -0
- package/dist/esm/httpPull/relations.test.mjs.map +1 -0
- package/dist/esm/httpPull/relations.test.native.js +117 -0
- package/dist/esm/httpPull/relations.test.native.js.map +1 -0
- package/dist/esm/httpPull/testHarness.mjs +72 -0
- package/dist/esm/httpPull/testHarness.mjs.map +1 -0
- package/dist/esm/httpPull/testHarness.native.js +81 -0
- package/dist/esm/httpPull/testHarness.native.js.map +1 -0
- package/dist/esm/httpPull/transport.test.mjs +589 -0
- package/dist/esm/httpPull/transport.test.mjs.map +1 -0
- package/dist/esm/httpPull/transport.test.native.js +675 -0
- package/dist/esm/httpPull/transport.test.native.js.map +1 -0
- package/dist/esm/httpPullTransport.mjs +421 -0
- package/dist/esm/httpPullTransport.mjs.map +1 -0
- package/dist/esm/httpPullTransport.native.js +681 -0
- package/dist/esm/httpPullTransport.native.js.map +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.mjs +1 -0
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +1 -0
- package/dist/esm/index.native.js.map +1 -1
- package/package.json +2 -2
- package/src/createZeroClient.tsx +22 -0
- package/src/httpPull/auth.test.ts +208 -0
- package/src/httpPull/churn.test.ts +147 -0
- package/src/httpPull/fixtureSchema.ts +82 -0
- package/src/httpPull/fixtureServer.ts +391 -0
- package/src/httpPull/integration.test.ts +57 -0
- package/src/httpPull/rebase.test.ts +368 -0
- package/src/httpPull/relations.test.ts +135 -0
- package/src/httpPull/testHarness.ts +95 -0
- package/src/httpPull/transport.test.ts +603 -0
- package/src/httpPullTransport.ts +587 -0
- package/src/index.ts +1 -0
- package/types/createZeroClient.d.ts +3 -1
- package/types/createZeroClient.d.ts.map +1 -1
- package/types/httpPull/auth.test.d.ts +2 -0
- package/types/httpPull/auth.test.d.ts.map +1 -0
- package/types/httpPull/churn.test.d.ts +2 -0
- package/types/httpPull/churn.test.d.ts.map +1 -0
- package/types/httpPull/fixtureSchema.d.ts +111 -0
- package/types/httpPull/fixtureSchema.d.ts.map +1 -0
- package/types/httpPull/fixtureServer.d.ts +14 -0
- package/types/httpPull/fixtureServer.d.ts.map +1 -0
- package/types/httpPull/integration.test.d.ts +2 -0
- package/types/httpPull/integration.test.d.ts.map +1 -0
- package/types/httpPull/rebase.test.d.ts +2 -0
- package/types/httpPull/rebase.test.d.ts.map +1 -0
- package/types/httpPull/relations.test.d.ts +2 -0
- package/types/httpPull/relations.test.d.ts.map +1 -0
- package/types/httpPull/testHarness.d.ts +32 -0
- package/types/httpPull/testHarness.d.ts.map +1 -0
- package/types/httpPull/transport.test.d.ts +2 -0
- package/types/httpPull/transport.test.d.ts.map +1 -0
- package/types/httpPullTransport.d.ts +13 -0
- package/types/httpPullTransport.d.ts.map +1 -0
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all) __defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
14
|
+
get: () => from[key],
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
|
|
21
|
+
value: true
|
|
22
|
+
}), mod);
|
|
23
|
+
var fixtureServer_exports = {};
|
|
24
|
+
__export(fixtureServer_exports, {
|
|
25
|
+
startZeroHttpServer: () => startZeroHttpServer
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(fixtureServer_exports);
|
|
28
|
+
var import_node_http = require("node:http");
|
|
29
|
+
const tableNames = ["user", "project", "member"];
|
|
30
|
+
async function startZeroHttpServer(opts) {
|
|
31
|
+
const tables = seedTables(opts?.seed);
|
|
32
|
+
const lmids = /* @__PURE__ */new Map();
|
|
33
|
+
const mutationResults = /* @__PURE__ */new Map();
|
|
34
|
+
const clientGroupUsers = /* @__PURE__ */new Map();
|
|
35
|
+
let cookie = 1;
|
|
36
|
+
const server = (0, import_node_http.createServer)(async (req, res) => {
|
|
37
|
+
try {
|
|
38
|
+
if (req.method !== "POST") {
|
|
39
|
+
sendJSON(res, 404, {
|
|
40
|
+
error: "not found"
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const path = new URL(req.url || "/", "http://127.0.0.1").pathname;
|
|
45
|
+
const userID = authenticate(req, tables);
|
|
46
|
+
if (!userID) {
|
|
47
|
+
sendJSON(res, 401, {
|
|
48
|
+
error: "unauthorized"
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (path === "/pull") {
|
|
53
|
+
const body = await readJSON(req);
|
|
54
|
+
if (!bindClientGroup(clientGroupUsers, body.clientGroupID, userID)) {
|
|
55
|
+
sendJSON(res, 403, {
|
|
56
|
+
error: "client group belongs to a different user"
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (body.cookie === cookie) {
|
|
61
|
+
sendJSON(res, 200, {
|
|
62
|
+
cookie,
|
|
63
|
+
unchanged: true
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (typeof body.cookie === "number" && body.cookie > cookie) {
|
|
68
|
+
sendJSON(res, 409, {
|
|
69
|
+
error: `future cookie ${body.cookie} is ahead of server cookie ${cookie}`
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
sendJSON(res, 200, {
|
|
74
|
+
cookie,
|
|
75
|
+
lastMutationIDChanges: lastMutationIDChanges(lmids, body.clientGroupID),
|
|
76
|
+
rowsPatch: [{
|
|
77
|
+
op: "clear"
|
|
78
|
+
}, ...visibleRowsPatch(tables, userID)]
|
|
79
|
+
});
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (path === "/push") {
|
|
83
|
+
const body = await readJSON(req);
|
|
84
|
+
if (!bindClientGroup(clientGroupUsers, body.clientGroupID, userID)) {
|
|
85
|
+
sendJSON(res, 403, {
|
|
86
|
+
error: "client group belongs to a different user"
|
|
87
|
+
});
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const mutations = Array.isArray(body.mutations) ? body.mutations : [];
|
|
91
|
+
const gap = findMutationGap(lmids, body.clientGroupID, mutations);
|
|
92
|
+
if (gap) {
|
|
93
|
+
sendJSON(res, 500, {
|
|
94
|
+
error: gap
|
|
95
|
+
});
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const pushResults = [];
|
|
99
|
+
let processedNewMutation = false;
|
|
100
|
+
for (const mutation of mutations) {
|
|
101
|
+
const current = lmidFor(lmids, body.clientGroupID, mutation.clientID);
|
|
102
|
+
if (mutation.id <= current) {
|
|
103
|
+
pushResults.push({
|
|
104
|
+
id: {
|
|
105
|
+
clientID: mutation.clientID,
|
|
106
|
+
id: mutation.id
|
|
107
|
+
},
|
|
108
|
+
result: resultForMutation(mutationResults, body.clientGroupID, mutation.clientID, mutation.id) || {}
|
|
109
|
+
});
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const result = applyMutation(tables, userID, mutation);
|
|
113
|
+
setLMID(lmids, body.clientGroupID, mutation.clientID, mutation.id);
|
|
114
|
+
setMutationResult(mutationResults, body.clientGroupID, mutation.clientID, mutation.id, result);
|
|
115
|
+
processedNewMutation = true;
|
|
116
|
+
pushResults.push({
|
|
117
|
+
id: {
|
|
118
|
+
clientID: mutation.clientID,
|
|
119
|
+
id: mutation.id
|
|
120
|
+
},
|
|
121
|
+
result
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (processedNewMutation) cookie += 1;
|
|
125
|
+
sendJSON(res, 200, {
|
|
126
|
+
pushResponse: {
|
|
127
|
+
mutations: pushResults
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
sendJSON(res, 404, {
|
|
133
|
+
error: "not found"
|
|
134
|
+
});
|
|
135
|
+
} catch (err) {
|
|
136
|
+
sendJSON(res, 500, {
|
|
137
|
+
error: err instanceof Error ? err.message : String(err)
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
await new Promise((resolve, reject) => {
|
|
142
|
+
server.once("error", reject);
|
|
143
|
+
server.listen(0, "127.0.0.1", () => {
|
|
144
|
+
server.off("error", reject);
|
|
145
|
+
resolve();
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
const address = server.address();
|
|
149
|
+
return {
|
|
150
|
+
url: `http://127.0.0.1:${address.port}`,
|
|
151
|
+
version: () => cookie,
|
|
152
|
+
rows: table => rowsForTable(tables, table),
|
|
153
|
+
close: () => new Promise((resolve, reject) => {
|
|
154
|
+
server.close(err => err ? reject(err) : resolve());
|
|
155
|
+
})
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function seedTables(seed) {
|
|
159
|
+
const tables = {
|
|
160
|
+
user: /* @__PURE__ */new Map(),
|
|
161
|
+
project: /* @__PURE__ */new Map(),
|
|
162
|
+
member: /* @__PURE__ */new Map()
|
|
163
|
+
};
|
|
164
|
+
for (const table of tableNames) {
|
|
165
|
+
for (const row of seed?.[table] || []) {
|
|
166
|
+
if (typeof row.id === "string") tables[table].set(row.id, cloneRow(row));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return tables;
|
|
170
|
+
}
|
|
171
|
+
function authenticate(req, tables) {
|
|
172
|
+
const header = req.headers.authorization;
|
|
173
|
+
if (!header?.startsWith("Bearer token-")) return null;
|
|
174
|
+
const userID = header.slice("Bearer token-".length);
|
|
175
|
+
return tables.user.has(userID) ? userID : null;
|
|
176
|
+
}
|
|
177
|
+
function bindClientGroup(clientGroupUsers, clientGroupID, userID) {
|
|
178
|
+
const owner = clientGroupUsers.get(clientGroupID);
|
|
179
|
+
if (owner) return owner === userID;
|
|
180
|
+
clientGroupUsers.set(clientGroupID, userID);
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
function visibleRowsPatch(tables, userID) {
|
|
184
|
+
const visibleProjectIDs = visibleProjectIDSet(tables, userID);
|
|
185
|
+
const rows = [];
|
|
186
|
+
const user = tables.user.get(userID);
|
|
187
|
+
if (user) rows.push({
|
|
188
|
+
op: "put",
|
|
189
|
+
tableName: "user",
|
|
190
|
+
value: cloneRow(user)
|
|
191
|
+
});
|
|
192
|
+
for (const project of tables.project.values()) {
|
|
193
|
+
if (visibleProjectIDs.has(project.id)) {
|
|
194
|
+
rows.push({
|
|
195
|
+
op: "put",
|
|
196
|
+
tableName: "project",
|
|
197
|
+
value: cloneRow(project)
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
for (const member of tables.member.values()) {
|
|
202
|
+
if (visibleProjectIDs.has(member.projectId)) {
|
|
203
|
+
rows.push({
|
|
204
|
+
op: "put",
|
|
205
|
+
tableName: "member",
|
|
206
|
+
value: cloneRow(member)
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return rows;
|
|
211
|
+
}
|
|
212
|
+
function visibleProjectIDSet(tables, userID) {
|
|
213
|
+
const projectIDs = /* @__PURE__ */new Set();
|
|
214
|
+
for (const project of tables.project.values()) {
|
|
215
|
+
if (project.ownerId === userID) projectIDs.add(project.id);
|
|
216
|
+
}
|
|
217
|
+
for (const member of tables.member.values()) {
|
|
218
|
+
if (member.userId === userID && tables.project.has(member.projectId)) {
|
|
219
|
+
projectIDs.add(member.projectId);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return projectIDs;
|
|
223
|
+
}
|
|
224
|
+
function findMutationGap(lmids, clientGroupID, mutations) {
|
|
225
|
+
const nextLMIDs = /* @__PURE__ */new Map();
|
|
226
|
+
for (const mutation of mutations) {
|
|
227
|
+
const current = nextLMIDs.get(mutation.clientID) ?? lmidFor(lmids, clientGroupID, mutation.clientID);
|
|
228
|
+
if (mutation.id <= current) continue;
|
|
229
|
+
if (mutation.id !== current + 1) {
|
|
230
|
+
return `mutation id gap for ${mutation.clientID}: got ${mutation.id}, expected ${current + 1}`;
|
|
231
|
+
}
|
|
232
|
+
nextLMIDs.set(mutation.clientID, mutation.id);
|
|
233
|
+
}
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
function applyMutation(tables, userID, mutation) {
|
|
237
|
+
if (mutation.type !== "custom") return appError("unsupported");
|
|
238
|
+
const args = mutation.args[0] || {};
|
|
239
|
+
if (mutation.name === "project|create") {
|
|
240
|
+
if (tables.project.has(args.id)) return appError("exists");
|
|
241
|
+
if (args.ownerId !== userID) return appError("forbidden");
|
|
242
|
+
tables.project.set(args.id, {
|
|
243
|
+
id: args.id,
|
|
244
|
+
ownerId: args.ownerId,
|
|
245
|
+
name: args.name
|
|
246
|
+
});
|
|
247
|
+
return {};
|
|
248
|
+
}
|
|
249
|
+
if (mutation.name === "project|rename") {
|
|
250
|
+
const project = tables.project.get(args.id);
|
|
251
|
+
if (!project) return appError("not-found");
|
|
252
|
+
if (project.ownerId !== userID) return appError("forbidden");
|
|
253
|
+
tables.project.set(args.id, {
|
|
254
|
+
...project,
|
|
255
|
+
name: args.name
|
|
256
|
+
});
|
|
257
|
+
return {};
|
|
258
|
+
}
|
|
259
|
+
if (mutation.name === "member|add") {
|
|
260
|
+
const project = tables.project.get(args.projectId);
|
|
261
|
+
if (!project) return appError("not-found");
|
|
262
|
+
if (project.ownerId !== userID) return appError("forbidden");
|
|
263
|
+
if (tables.member.has(args.id)) return appError("exists");
|
|
264
|
+
tables.member.set(args.id, {
|
|
265
|
+
id: args.id,
|
|
266
|
+
projectId: args.projectId,
|
|
267
|
+
userId: args.userId
|
|
268
|
+
});
|
|
269
|
+
return {};
|
|
270
|
+
}
|
|
271
|
+
if (mutation.name === "member|remove") {
|
|
272
|
+
const member = tables.member.get(args.id);
|
|
273
|
+
if (!member) return appError("not-found");
|
|
274
|
+
const project = tables.project.get(member.projectId);
|
|
275
|
+
if (!project) return appError("not-found");
|
|
276
|
+
if (project.ownerId !== userID) return appError("forbidden");
|
|
277
|
+
tables.member.delete(args.id);
|
|
278
|
+
return {};
|
|
279
|
+
}
|
|
280
|
+
return appError("unsupported");
|
|
281
|
+
}
|
|
282
|
+
function appError(details) {
|
|
283
|
+
return {
|
|
284
|
+
error: "app",
|
|
285
|
+
details
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function lastMutationIDChanges(lmids, clientGroupID) {
|
|
289
|
+
return Object.fromEntries(lmids.get(clientGroupID) || []);
|
|
290
|
+
}
|
|
291
|
+
function lmidFor(lmids, clientGroupID, clientID) {
|
|
292
|
+
return lmids.get(clientGroupID)?.get(clientID) || 0;
|
|
293
|
+
}
|
|
294
|
+
function setLMID(lmids, clientGroupID, clientID, id) {
|
|
295
|
+
let group = lmids.get(clientGroupID);
|
|
296
|
+
if (!group) {
|
|
297
|
+
group = /* @__PURE__ */new Map();
|
|
298
|
+
lmids.set(clientGroupID, group);
|
|
299
|
+
}
|
|
300
|
+
group.set(clientID, id);
|
|
301
|
+
}
|
|
302
|
+
function resultForMutation(results, clientGroupID, clientID, id) {
|
|
303
|
+
return results.get(clientGroupID)?.get(clientID)?.get(id);
|
|
304
|
+
}
|
|
305
|
+
function setMutationResult(results, clientGroupID, clientID, id, result) {
|
|
306
|
+
let group = results.get(clientGroupID);
|
|
307
|
+
if (!group) {
|
|
308
|
+
group = /* @__PURE__ */new Map();
|
|
309
|
+
results.set(clientGroupID, group);
|
|
310
|
+
}
|
|
311
|
+
let client = group.get(clientID);
|
|
312
|
+
if (!client) {
|
|
313
|
+
client = /* @__PURE__ */new Map();
|
|
314
|
+
group.set(clientID, client);
|
|
315
|
+
}
|
|
316
|
+
client.set(id, result);
|
|
317
|
+
}
|
|
318
|
+
function rowsForTable(tables, table) {
|
|
319
|
+
if (!isTableName(table)) return [];
|
|
320
|
+
return [...tables[table].values()].map(cloneRow);
|
|
321
|
+
}
|
|
322
|
+
function isTableName(table) {
|
|
323
|
+
return tableNames.includes(table);
|
|
324
|
+
}
|
|
325
|
+
function cloneRow(row) {
|
|
326
|
+
return {
|
|
327
|
+
...row
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
async function readJSON(req) {
|
|
331
|
+
let body = "";
|
|
332
|
+
for await (const chunk of req) body += chunk;
|
|
333
|
+
return body ? JSON.parse(body) : {};
|
|
334
|
+
}
|
|
335
|
+
function sendJSON(res, status, body) {
|
|
336
|
+
res.writeHead(status, {
|
|
337
|
+
"content-type": "application/json"
|
|
338
|
+
});
|
|
339
|
+
res.end(JSON.stringify(body));
|
|
340
|
+
}
|