@comapeo/core 2.0.1 → 2.2.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/dist/blob-store/downloader.d.ts +43 -0
- package/dist/blob-store/downloader.d.ts.map +1 -0
- package/dist/blob-store/entries-stream.d.ts +13 -0
- package/dist/blob-store/entries-stream.d.ts.map +1 -0
- package/dist/blob-store/hyperdrive-index.d.ts +20 -0
- package/dist/blob-store/hyperdrive-index.d.ts.map +1 -0
- package/dist/blob-store/index.d.ts +34 -29
- package/dist/blob-store/index.d.ts.map +1 -1
- package/dist/blob-store/utils.d.ts +27 -0
- package/dist/blob-store/utils.d.ts.map +1 -0
- package/dist/constants.d.ts +2 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/core-manager/index.d.ts +11 -1
- package/dist/core-manager/index.d.ts.map +1 -1
- package/dist/core-ownership.d.ts.map +1 -1
- package/dist/datastore/index.d.ts +5 -4
- package/dist/datastore/index.d.ts.map +1 -1
- package/dist/datatype/index.d.ts +5 -1
- package/dist/discovery/local-discovery.d.ts.map +1 -1
- package/dist/errors.d.ts +6 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/fastify-plugins/blobs.d.ts.map +1 -1
- package/dist/fastify-plugins/maps.d.ts.map +1 -1
- package/dist/generated/extensions.d.ts +31 -0
- package/dist/generated/extensions.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/drizzle-helpers.d.ts +6 -0
- package/dist/lib/drizzle-helpers.d.ts.map +1 -0
- package/dist/lib/error.d.ts +51 -0
- package/dist/lib/error.d.ts.map +1 -0
- package/dist/lib/get-own.d.ts +9 -0
- package/dist/lib/get-own.d.ts.map +1 -0
- package/dist/lib/is-hostname-ip-address.d.ts +17 -0
- package/dist/lib/is-hostname-ip-address.d.ts.map +1 -0
- package/dist/lib/ws-core-replicator.d.ts +11 -0
- package/dist/lib/ws-core-replicator.d.ts.map +1 -0
- package/dist/mapeo-manager.d.ts +18 -22
- package/dist/mapeo-manager.d.ts.map +1 -1
- package/dist/mapeo-project.d.ts +459 -26
- package/dist/mapeo-project.d.ts.map +1 -1
- package/dist/member-api.d.ts +44 -1
- package/dist/member-api.d.ts.map +1 -1
- package/dist/roles.d.ts.map +1 -1
- package/dist/schema/client.d.ts +17 -5
- package/dist/schema/client.d.ts.map +1 -1
- package/dist/schema/project.d.ts +212 -2
- package/dist/schema/project.d.ts.map +1 -1
- package/dist/sync/core-sync-state.d.ts +20 -15
- package/dist/sync/core-sync-state.d.ts.map +1 -1
- package/dist/sync/namespace-sync-state.d.ts +13 -1
- package/dist/sync/namespace-sync-state.d.ts.map +1 -1
- package/dist/sync/peer-sync-controller.d.ts +1 -1
- package/dist/sync/peer-sync-controller.d.ts.map +1 -1
- package/dist/sync/sync-api.d.ts +47 -2
- package/dist/sync/sync-api.d.ts.map +1 -1
- package/dist/sync/sync-state.d.ts +12 -0
- package/dist/sync/sync-state.d.ts.map +1 -1
- package/dist/translation-api.d.ts +2 -2
- package/dist/translation-api.d.ts.map +1 -1
- package/dist/types.d.ts +10 -2
- package/dist/types.d.ts.map +1 -1
- package/drizzle/client/0001_chubby_cargill.sql +12 -0
- package/drizzle/client/meta/0001_snapshot.json +208 -0
- package/drizzle/client/meta/_journal.json +7 -0
- package/drizzle/project/0001_medical_wendell_rand.sql +22 -0
- package/drizzle/project/meta/0001_snapshot.json +1267 -0
- package/drizzle/project/meta/_journal.json +7 -0
- package/package.json +14 -5
- package/src/blob-store/downloader.js +130 -0
- package/src/blob-store/entries-stream.js +81 -0
- package/src/blob-store/hyperdrive-index.js +122 -0
- package/src/blob-store/index.js +59 -117
- package/src/blob-store/utils.js +54 -0
- package/src/constants.js +4 -1
- package/src/core-manager/index.js +60 -3
- package/src/core-ownership.js +2 -4
- package/src/datastore/README.md +1 -2
- package/src/datastore/index.js +8 -8
- package/src/datatype/index.d.ts +5 -1
- package/src/datatype/index.js +22 -9
- package/src/discovery/local-discovery.js +2 -1
- package/src/errors.js +11 -2
- package/src/fastify-plugins/blobs.js +17 -1
- package/src/fastify-plugins/maps.js +2 -1
- package/src/generated/extensions.d.ts +31 -0
- package/src/generated/extensions.js +150 -0
- package/src/generated/extensions.ts +181 -0
- package/src/index.js +10 -0
- package/src/invite-api.js +1 -1
- package/src/lib/drizzle-helpers.js +79 -0
- package/src/lib/error.js +71 -0
- package/src/lib/get-own.js +10 -0
- package/src/lib/is-hostname-ip-address.js +26 -0
- package/src/lib/ws-core-replicator.js +47 -0
- package/src/mapeo-manager.js +74 -45
- package/src/mapeo-project.js +238 -58
- package/src/member-api.js +295 -2
- package/src/roles.js +38 -32
- package/src/schema/client.js +4 -3
- package/src/schema/project.js +7 -0
- package/src/sync/core-sync-state.js +39 -23
- package/src/sync/namespace-sync-state.js +22 -0
- package/src/sync/peer-sync-controller.js +1 -0
- package/src/sync/sync-api.js +197 -3
- package/src/sync/sync-state.js +18 -0
- package/src/translation-api.js +5 -9
- package/src/types.ts +12 -3
- package/dist/blob-store/live-download.d.ts +0 -107
- package/dist/blob-store/live-download.d.ts.map +0 -1
- package/dist/lib/timing-safe-equal.d.ts +0 -15
- package/dist/lib/timing-safe-equal.d.ts.map +0 -1
- package/src/blob-store/live-download.js +0 -373
- package/src/lib/timing-safe-equal.js +0 -34
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import fp from 'fastify-plugin'
|
|
2
2
|
import { filetypemime } from 'magic-bytes.js'
|
|
3
|
+
import { pEvent } from 'p-event'
|
|
3
4
|
import { Type as T } from '@sinclair/typebox'
|
|
4
5
|
|
|
5
6
|
import { SUPPORTED_BLOB_VARIANTS } from '../blob-store/index.js'
|
|
6
7
|
import { HEX_REGEX_32_BYTES, Z_BASE_32_REGEX_32_BYTES } from './constants.js'
|
|
8
|
+
import { getErrorMessage } from '../lib/error.js'
|
|
7
9
|
|
|
8
10
|
/** @import { BlobId } from '../types.js' */
|
|
9
11
|
|
|
@@ -93,15 +95,29 @@ async function routes(fastify, options) {
|
|
|
93
95
|
|
|
94
96
|
let blobStream
|
|
95
97
|
try {
|
|
96
|
-
blobStream = await blobStore.
|
|
98
|
+
blobStream = await blobStore.createReadStreamFromEntry(driveId, entry)
|
|
97
99
|
} catch (e) {
|
|
98
100
|
reply.code(404)
|
|
99
101
|
throw e
|
|
100
102
|
}
|
|
101
103
|
|
|
104
|
+
try {
|
|
105
|
+
await pEvent(blobStream, 'readable', { rejectionEvents: ['error'] })
|
|
106
|
+
} catch (err) {
|
|
107
|
+
// This matches [how Hyperblobs checks if a blob is unavailable][0].
|
|
108
|
+
// [0]: https://github.com/holepunchto/hyperblobs/blob/518088d2b828082fd70a276fa2c8848a2cf2a56b/index.js#L49
|
|
109
|
+
if (getErrorMessage(err) === 'Block not available') {
|
|
110
|
+
reply.code(404)
|
|
111
|
+
throw new Error('Blob not found')
|
|
112
|
+
} else {
|
|
113
|
+
throw err
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
102
117
|
// Extract the 'mimeType' property of the metadata and use it for the response header if found
|
|
103
118
|
if (
|
|
104
119
|
metadata &&
|
|
120
|
+
typeof metadata === 'object' &&
|
|
105
121
|
'mimeType' in metadata &&
|
|
106
122
|
typeof metadata.mimeType === 'string'
|
|
107
123
|
) {
|
|
@@ -5,6 +5,7 @@ import { ReaderWatch, Server as SMPServerPlugin } from 'styled-map-package'
|
|
|
5
5
|
|
|
6
6
|
import { noop } from '../utils.js'
|
|
7
7
|
import { NotFoundError, ENOENTError } from './utils.js'
|
|
8
|
+
import { getErrorCode } from '../lib/error.js'
|
|
8
9
|
|
|
9
10
|
/** @import { FastifyPluginAsync } from 'fastify' */
|
|
10
11
|
/** @import { Stats } from 'node:fs' */
|
|
@@ -56,7 +57,7 @@ export async function plugin(fastify, opts) {
|
|
|
56
57
|
try {
|
|
57
58
|
stats = await fs.stat(customMapPath)
|
|
58
59
|
} catch (err) {
|
|
59
|
-
if (err
|
|
60
|
+
if (getErrorCode(err) === 'ENOENT') {
|
|
60
61
|
throw new ENOENTError(customMapPath)
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -19,6 +19,19 @@ export declare const HaveExtension_Namespace: {
|
|
|
19
19
|
export type HaveExtension_Namespace = typeof HaveExtension_Namespace[keyof typeof HaveExtension_Namespace];
|
|
20
20
|
export declare function haveExtension_NamespaceFromJSON(object: any): HaveExtension_Namespace;
|
|
21
21
|
export declare function haveExtension_NamespaceToNumber(object: HaveExtension_Namespace): number;
|
|
22
|
+
/** A map of blob types and variants that a peer intends to download */
|
|
23
|
+
export interface DownloadIntentExtension {
|
|
24
|
+
downloadIntents: {
|
|
25
|
+
[key: string]: DownloadIntentExtension_DownloadIntent;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export interface DownloadIntentExtension_DownloadIntent {
|
|
29
|
+
variants: string[];
|
|
30
|
+
}
|
|
31
|
+
export interface DownloadIntentExtension_DownloadIntentsEntry {
|
|
32
|
+
key: string;
|
|
33
|
+
value: DownloadIntentExtension_DownloadIntent | undefined;
|
|
34
|
+
}
|
|
22
35
|
export declare const ProjectExtension: {
|
|
23
36
|
encode(message: ProjectExtension, writer?: _m0.Writer): _m0.Writer;
|
|
24
37
|
decode(input: _m0.Reader | Uint8Array, length?: number): ProjectExtension;
|
|
@@ -31,6 +44,24 @@ export declare const HaveExtension: {
|
|
|
31
44
|
create<I extends Exact<DeepPartial<HaveExtension>, I>>(base?: I): HaveExtension;
|
|
32
45
|
fromPartial<I extends Exact<DeepPartial<HaveExtension>, I>>(object: I): HaveExtension;
|
|
33
46
|
};
|
|
47
|
+
export declare const DownloadIntentExtension: {
|
|
48
|
+
encode(message: DownloadIntentExtension, writer?: _m0.Writer): _m0.Writer;
|
|
49
|
+
decode(input: _m0.Reader | Uint8Array, length?: number): DownloadIntentExtension;
|
|
50
|
+
create<I extends Exact<DeepPartial<DownloadIntentExtension>, I>>(base?: I): DownloadIntentExtension;
|
|
51
|
+
fromPartial<I extends Exact<DeepPartial<DownloadIntentExtension>, I>>(object: I): DownloadIntentExtension;
|
|
52
|
+
};
|
|
53
|
+
export declare const DownloadIntentExtension_DownloadIntent: {
|
|
54
|
+
encode(message: DownloadIntentExtension_DownloadIntent, writer?: _m0.Writer): _m0.Writer;
|
|
55
|
+
decode(input: _m0.Reader | Uint8Array, length?: number): DownloadIntentExtension_DownloadIntent;
|
|
56
|
+
create<I extends Exact<DeepPartial<DownloadIntentExtension_DownloadIntent>, I>>(base?: I): DownloadIntentExtension_DownloadIntent;
|
|
57
|
+
fromPartial<I extends Exact<DeepPartial<DownloadIntentExtension_DownloadIntent>, I>>(object: I): DownloadIntentExtension_DownloadIntent;
|
|
58
|
+
};
|
|
59
|
+
export declare const DownloadIntentExtension_DownloadIntentsEntry: {
|
|
60
|
+
encode(message: DownloadIntentExtension_DownloadIntentsEntry, writer?: _m0.Writer): _m0.Writer;
|
|
61
|
+
decode(input: _m0.Reader | Uint8Array, length?: number): DownloadIntentExtension_DownloadIntentsEntry;
|
|
62
|
+
create<I extends Exact<DeepPartial<DownloadIntentExtension_DownloadIntentsEntry>, I>>(base?: I): DownloadIntentExtension_DownloadIntentsEntry;
|
|
63
|
+
fromPartial<I extends Exact<DeepPartial<DownloadIntentExtension_DownloadIntentsEntry>, I>>(object: I): DownloadIntentExtension_DownloadIntentsEntry;
|
|
64
|
+
};
|
|
34
65
|
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
|
|
35
66
|
type DeepPartial<T> = T extends Builtin ? T : T extends Array<infer U> ? Array<DeepPartial<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T extends {} ? {
|
|
36
67
|
[K in keyof T]?: DeepPartial<T[K]>;
|
|
@@ -169,6 +169,156 @@ export var HaveExtension = {
|
|
|
169
169
|
return message;
|
|
170
170
|
},
|
|
171
171
|
};
|
|
172
|
+
function createBaseDownloadIntentExtension() {
|
|
173
|
+
return { downloadIntents: {} };
|
|
174
|
+
}
|
|
175
|
+
export var DownloadIntentExtension = {
|
|
176
|
+
encode: function (message, writer) {
|
|
177
|
+
if (writer === void 0) { writer = _m0.Writer.create(); }
|
|
178
|
+
Object.entries(message.downloadIntents).forEach(function (_a) {
|
|
179
|
+
var key = _a[0], value = _a[1];
|
|
180
|
+
DownloadIntentExtension_DownloadIntentsEntry.encode({ key: key, value: value }, writer.uint32(10).fork())
|
|
181
|
+
.ldelim();
|
|
182
|
+
});
|
|
183
|
+
return writer;
|
|
184
|
+
},
|
|
185
|
+
decode: function (input, length) {
|
|
186
|
+
var reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input);
|
|
187
|
+
var end = length === undefined ? reader.len : reader.pos + length;
|
|
188
|
+
var message = createBaseDownloadIntentExtension();
|
|
189
|
+
while (reader.pos < end) {
|
|
190
|
+
var tag = reader.uint32();
|
|
191
|
+
switch (tag >>> 3) {
|
|
192
|
+
case 1:
|
|
193
|
+
if (tag !== 10) {
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
var entry1 = DownloadIntentExtension_DownloadIntentsEntry.decode(reader, reader.uint32());
|
|
197
|
+
if (entry1.value !== undefined) {
|
|
198
|
+
message.downloadIntents[entry1.key] = entry1.value;
|
|
199
|
+
}
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if ((tag & 7) === 4 || tag === 0) {
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
reader.skipType(tag & 7);
|
|
206
|
+
}
|
|
207
|
+
return message;
|
|
208
|
+
},
|
|
209
|
+
create: function (base) {
|
|
210
|
+
return DownloadIntentExtension.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
211
|
+
},
|
|
212
|
+
fromPartial: function (object) {
|
|
213
|
+
var _a;
|
|
214
|
+
var message = createBaseDownloadIntentExtension();
|
|
215
|
+
message.downloadIntents = Object.entries((_a = object.downloadIntents) !== null && _a !== void 0 ? _a : {}).reduce(function (acc, _a) {
|
|
216
|
+
var key = _a[0], value = _a[1];
|
|
217
|
+
if (value !== undefined) {
|
|
218
|
+
acc[key] = DownloadIntentExtension_DownloadIntent.fromPartial(value);
|
|
219
|
+
}
|
|
220
|
+
return acc;
|
|
221
|
+
}, {});
|
|
222
|
+
return message;
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
function createBaseDownloadIntentExtension_DownloadIntent() {
|
|
226
|
+
return { variants: [] };
|
|
227
|
+
}
|
|
228
|
+
export var DownloadIntentExtension_DownloadIntent = {
|
|
229
|
+
encode: function (message, writer) {
|
|
230
|
+
if (writer === void 0) { writer = _m0.Writer.create(); }
|
|
231
|
+
for (var _i = 0, _a = message.variants; _i < _a.length; _i++) {
|
|
232
|
+
var v = _a[_i];
|
|
233
|
+
writer.uint32(10).string(v);
|
|
234
|
+
}
|
|
235
|
+
return writer;
|
|
236
|
+
},
|
|
237
|
+
decode: function (input, length) {
|
|
238
|
+
var reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input);
|
|
239
|
+
var end = length === undefined ? reader.len : reader.pos + length;
|
|
240
|
+
var message = createBaseDownloadIntentExtension_DownloadIntent();
|
|
241
|
+
while (reader.pos < end) {
|
|
242
|
+
var tag = reader.uint32();
|
|
243
|
+
switch (tag >>> 3) {
|
|
244
|
+
case 1:
|
|
245
|
+
if (tag !== 10) {
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
message.variants.push(reader.string());
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if ((tag & 7) === 4 || tag === 0) {
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
reader.skipType(tag & 7);
|
|
255
|
+
}
|
|
256
|
+
return message;
|
|
257
|
+
},
|
|
258
|
+
create: function (base) {
|
|
259
|
+
return DownloadIntentExtension_DownloadIntent.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
260
|
+
},
|
|
261
|
+
fromPartial: function (object) {
|
|
262
|
+
var _a;
|
|
263
|
+
var message = createBaseDownloadIntentExtension_DownloadIntent();
|
|
264
|
+
message.variants = ((_a = object.variants) === null || _a === void 0 ? void 0 : _a.map(function (e) { return e; })) || [];
|
|
265
|
+
return message;
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
function createBaseDownloadIntentExtension_DownloadIntentsEntry() {
|
|
269
|
+
return { key: "", value: undefined };
|
|
270
|
+
}
|
|
271
|
+
export var DownloadIntentExtension_DownloadIntentsEntry = {
|
|
272
|
+
encode: function (message, writer) {
|
|
273
|
+
if (writer === void 0) { writer = _m0.Writer.create(); }
|
|
274
|
+
if (message.key !== "") {
|
|
275
|
+
writer.uint32(10).string(message.key);
|
|
276
|
+
}
|
|
277
|
+
if (message.value !== undefined) {
|
|
278
|
+
DownloadIntentExtension_DownloadIntent.encode(message.value, writer.uint32(18).fork()).ldelim();
|
|
279
|
+
}
|
|
280
|
+
return writer;
|
|
281
|
+
},
|
|
282
|
+
decode: function (input, length) {
|
|
283
|
+
var reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input);
|
|
284
|
+
var end = length === undefined ? reader.len : reader.pos + length;
|
|
285
|
+
var message = createBaseDownloadIntentExtension_DownloadIntentsEntry();
|
|
286
|
+
while (reader.pos < end) {
|
|
287
|
+
var tag = reader.uint32();
|
|
288
|
+
switch (tag >>> 3) {
|
|
289
|
+
case 1:
|
|
290
|
+
if (tag !== 10) {
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
message.key = reader.string();
|
|
294
|
+
continue;
|
|
295
|
+
case 2:
|
|
296
|
+
if (tag !== 18) {
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
message.value = DownloadIntentExtension_DownloadIntent.decode(reader, reader.uint32());
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
if ((tag & 7) === 4 || tag === 0) {
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
reader.skipType(tag & 7);
|
|
306
|
+
}
|
|
307
|
+
return message;
|
|
308
|
+
},
|
|
309
|
+
create: function (base) {
|
|
310
|
+
return DownloadIntentExtension_DownloadIntentsEntry.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
311
|
+
},
|
|
312
|
+
fromPartial: function (object) {
|
|
313
|
+
var _a;
|
|
314
|
+
var message = createBaseDownloadIntentExtension_DownloadIntentsEntry();
|
|
315
|
+
message.key = (_a = object.key) !== null && _a !== void 0 ? _a : "";
|
|
316
|
+
message.value = (object.value !== undefined && object.value !== null)
|
|
317
|
+
? DownloadIntentExtension_DownloadIntent.fromPartial(object.value)
|
|
318
|
+
: undefined;
|
|
319
|
+
return message;
|
|
320
|
+
},
|
|
321
|
+
};
|
|
172
322
|
var tsProtoGlobalThis = (function () {
|
|
173
323
|
if (typeof globalThis !== "undefined") {
|
|
174
324
|
return globalThis;
|
|
@@ -66,6 +66,20 @@ export function haveExtension_NamespaceToNumber(object: HaveExtension_Namespace)
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/** A map of blob types and variants that a peer intends to download */
|
|
70
|
+
export interface DownloadIntentExtension {
|
|
71
|
+
downloadIntents: { [key: string]: DownloadIntentExtension_DownloadIntent };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface DownloadIntentExtension_DownloadIntent {
|
|
75
|
+
variants: string[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface DownloadIntentExtension_DownloadIntentsEntry {
|
|
79
|
+
key: string;
|
|
80
|
+
value: DownloadIntentExtension_DownloadIntent | undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
69
83
|
function createBaseProjectExtension(): ProjectExtension {
|
|
70
84
|
return { authCoreKeys: [] };
|
|
71
85
|
}
|
|
@@ -194,6 +208,173 @@ export const HaveExtension = {
|
|
|
194
208
|
},
|
|
195
209
|
};
|
|
196
210
|
|
|
211
|
+
function createBaseDownloadIntentExtension(): DownloadIntentExtension {
|
|
212
|
+
return { downloadIntents: {} };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export const DownloadIntentExtension = {
|
|
216
|
+
encode(message: DownloadIntentExtension, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
|
|
217
|
+
Object.entries(message.downloadIntents).forEach(([key, value]) => {
|
|
218
|
+
DownloadIntentExtension_DownloadIntentsEntry.encode({ key: key as any, value }, writer.uint32(10).fork())
|
|
219
|
+
.ldelim();
|
|
220
|
+
});
|
|
221
|
+
return writer;
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
decode(input: _m0.Reader | Uint8Array, length?: number): DownloadIntentExtension {
|
|
225
|
+
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input);
|
|
226
|
+
let end = length === undefined ? reader.len : reader.pos + length;
|
|
227
|
+
const message = createBaseDownloadIntentExtension();
|
|
228
|
+
while (reader.pos < end) {
|
|
229
|
+
const tag = reader.uint32();
|
|
230
|
+
switch (tag >>> 3) {
|
|
231
|
+
case 1:
|
|
232
|
+
if (tag !== 10) {
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const entry1 = DownloadIntentExtension_DownloadIntentsEntry.decode(reader, reader.uint32());
|
|
237
|
+
if (entry1.value !== undefined) {
|
|
238
|
+
message.downloadIntents[entry1.key] = entry1.value;
|
|
239
|
+
}
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if ((tag & 7) === 4 || tag === 0) {
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
reader.skipType(tag & 7);
|
|
246
|
+
}
|
|
247
|
+
return message;
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
create<I extends Exact<DeepPartial<DownloadIntentExtension>, I>>(base?: I): DownloadIntentExtension {
|
|
251
|
+
return DownloadIntentExtension.fromPartial(base ?? ({} as any));
|
|
252
|
+
},
|
|
253
|
+
fromPartial<I extends Exact<DeepPartial<DownloadIntentExtension>, I>>(object: I): DownloadIntentExtension {
|
|
254
|
+
const message = createBaseDownloadIntentExtension();
|
|
255
|
+
message.downloadIntents = Object.entries(object.downloadIntents ?? {}).reduce<
|
|
256
|
+
{ [key: string]: DownloadIntentExtension_DownloadIntent }
|
|
257
|
+
>((acc, [key, value]) => {
|
|
258
|
+
if (value !== undefined) {
|
|
259
|
+
acc[key] = DownloadIntentExtension_DownloadIntent.fromPartial(value);
|
|
260
|
+
}
|
|
261
|
+
return acc;
|
|
262
|
+
}, {});
|
|
263
|
+
return message;
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
function createBaseDownloadIntentExtension_DownloadIntent(): DownloadIntentExtension_DownloadIntent {
|
|
268
|
+
return { variants: [] };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export const DownloadIntentExtension_DownloadIntent = {
|
|
272
|
+
encode(message: DownloadIntentExtension_DownloadIntent, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
|
|
273
|
+
for (const v of message.variants) {
|
|
274
|
+
writer.uint32(10).string(v!);
|
|
275
|
+
}
|
|
276
|
+
return writer;
|
|
277
|
+
},
|
|
278
|
+
|
|
279
|
+
decode(input: _m0.Reader | Uint8Array, length?: number): DownloadIntentExtension_DownloadIntent {
|
|
280
|
+
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input);
|
|
281
|
+
let end = length === undefined ? reader.len : reader.pos + length;
|
|
282
|
+
const message = createBaseDownloadIntentExtension_DownloadIntent();
|
|
283
|
+
while (reader.pos < end) {
|
|
284
|
+
const tag = reader.uint32();
|
|
285
|
+
switch (tag >>> 3) {
|
|
286
|
+
case 1:
|
|
287
|
+
if (tag !== 10) {
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
message.variants.push(reader.string());
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if ((tag & 7) === 4 || tag === 0) {
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
reader.skipType(tag & 7);
|
|
298
|
+
}
|
|
299
|
+
return message;
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
create<I extends Exact<DeepPartial<DownloadIntentExtension_DownloadIntent>, I>>(
|
|
303
|
+
base?: I,
|
|
304
|
+
): DownloadIntentExtension_DownloadIntent {
|
|
305
|
+
return DownloadIntentExtension_DownloadIntent.fromPartial(base ?? ({} as any));
|
|
306
|
+
},
|
|
307
|
+
fromPartial<I extends Exact<DeepPartial<DownloadIntentExtension_DownloadIntent>, I>>(
|
|
308
|
+
object: I,
|
|
309
|
+
): DownloadIntentExtension_DownloadIntent {
|
|
310
|
+
const message = createBaseDownloadIntentExtension_DownloadIntent();
|
|
311
|
+
message.variants = object.variants?.map((e) => e) || [];
|
|
312
|
+
return message;
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
function createBaseDownloadIntentExtension_DownloadIntentsEntry(): DownloadIntentExtension_DownloadIntentsEntry {
|
|
317
|
+
return { key: "", value: undefined };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export const DownloadIntentExtension_DownloadIntentsEntry = {
|
|
321
|
+
encode(message: DownloadIntentExtension_DownloadIntentsEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
|
|
322
|
+
if (message.key !== "") {
|
|
323
|
+
writer.uint32(10).string(message.key);
|
|
324
|
+
}
|
|
325
|
+
if (message.value !== undefined) {
|
|
326
|
+
DownloadIntentExtension_DownloadIntent.encode(message.value, writer.uint32(18).fork()).ldelim();
|
|
327
|
+
}
|
|
328
|
+
return writer;
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
decode(input: _m0.Reader | Uint8Array, length?: number): DownloadIntentExtension_DownloadIntentsEntry {
|
|
332
|
+
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input);
|
|
333
|
+
let end = length === undefined ? reader.len : reader.pos + length;
|
|
334
|
+
const message = createBaseDownloadIntentExtension_DownloadIntentsEntry();
|
|
335
|
+
while (reader.pos < end) {
|
|
336
|
+
const tag = reader.uint32();
|
|
337
|
+
switch (tag >>> 3) {
|
|
338
|
+
case 1:
|
|
339
|
+
if (tag !== 10) {
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
message.key = reader.string();
|
|
344
|
+
continue;
|
|
345
|
+
case 2:
|
|
346
|
+
if (tag !== 18) {
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
message.value = DownloadIntentExtension_DownloadIntent.decode(reader, reader.uint32());
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if ((tag & 7) === 4 || tag === 0) {
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
reader.skipType(tag & 7);
|
|
357
|
+
}
|
|
358
|
+
return message;
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
create<I extends Exact<DeepPartial<DownloadIntentExtension_DownloadIntentsEntry>, I>>(
|
|
362
|
+
base?: I,
|
|
363
|
+
): DownloadIntentExtension_DownloadIntentsEntry {
|
|
364
|
+
return DownloadIntentExtension_DownloadIntentsEntry.fromPartial(base ?? ({} as any));
|
|
365
|
+
},
|
|
366
|
+
fromPartial<I extends Exact<DeepPartial<DownloadIntentExtension_DownloadIntentsEntry>, I>>(
|
|
367
|
+
object: I,
|
|
368
|
+
): DownloadIntentExtension_DownloadIntentsEntry {
|
|
369
|
+
const message = createBaseDownloadIntentExtension_DownloadIntentsEntry();
|
|
370
|
+
message.key = object.key ?? "";
|
|
371
|
+
message.value = (object.value !== undefined && object.value !== null)
|
|
372
|
+
? DownloadIntentExtension_DownloadIntent.fromPartial(object.value)
|
|
373
|
+
: undefined;
|
|
374
|
+
return message;
|
|
375
|
+
},
|
|
376
|
+
};
|
|
377
|
+
|
|
197
378
|
declare const self: any | undefined;
|
|
198
379
|
declare const window: any | undefined;
|
|
199
380
|
declare const global: any | undefined;
|
package/src/index.js
CHANGED
|
@@ -3,9 +3,19 @@ import {
|
|
|
3
3
|
COORDINATOR_ROLE_ID,
|
|
4
4
|
MEMBER_ROLE_ID,
|
|
5
5
|
} from './roles.js'
|
|
6
|
+
import { kProjectReplicate } from './mapeo-project.js'
|
|
6
7
|
export { plugin as CoMapeoMapsFastifyPlugin } from './fastify-plugins/maps.js'
|
|
7
8
|
export { FastifyController } from './fastify-controller.js'
|
|
8
9
|
export { MapeoManager } from './mapeo-manager.js'
|
|
10
|
+
/** @import { MapeoProject } from './mapeo-project.js' */
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {MapeoProject} project
|
|
14
|
+
* @param {Parameters<MapeoProject.prototype[kProjectReplicate]>} args
|
|
15
|
+
* @returns {ReturnType<MapeoProject.prototype[kProjectReplicate]>}
|
|
16
|
+
*/
|
|
17
|
+
export const replicateProject = (project, ...args) =>
|
|
18
|
+
project[kProjectReplicate](...args)
|
|
9
19
|
|
|
10
20
|
export const roles = /** @type {const} */ ({
|
|
11
21
|
CREATOR_ROLE_ID,
|
package/src/invite-api.js
CHANGED
|
@@ -3,7 +3,7 @@ import { pEvent } from 'p-event'
|
|
|
3
3
|
import { InviteResponse_Decision } from './generated/rpc.js'
|
|
4
4
|
import { assert, keyToId, noop } from './utils.js'
|
|
5
5
|
import HashMap from './lib/hashmap.js'
|
|
6
|
-
import timingSafeEqual from '
|
|
6
|
+
import timingSafeEqual from 'string-timing-safe-equal'
|
|
7
7
|
import { Logger } from './logger.js'
|
|
8
8
|
/** @import { MapBuffers } from './types.js' */
|
|
9
9
|
/**
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { sql } from 'drizzle-orm'
|
|
2
|
+
import { assert } from '../utils.js'
|
|
3
|
+
import { migrate as drizzleMigrate } from 'drizzle-orm/better-sqlite3/migrator'
|
|
4
|
+
import { DRIZZLE_MIGRATIONS_TABLE } from '../constants.js'
|
|
5
|
+
/** @import { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3' */
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {unknown} queryResult
|
|
9
|
+
* @returns {number}
|
|
10
|
+
*/
|
|
11
|
+
const getNumberResult = (queryResult) => {
|
|
12
|
+
assert(
|
|
13
|
+
queryResult &&
|
|
14
|
+
typeof queryResult === 'object' &&
|
|
15
|
+
'result' in queryResult &&
|
|
16
|
+
typeof queryResult.result === 'number',
|
|
17
|
+
'expected query to return proper result'
|
|
18
|
+
)
|
|
19
|
+
return queryResult.result
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get the number of rows in a table using `SELECT COUNT(*)`.
|
|
24
|
+
* Returns 0 if the table doesn't exist.
|
|
25
|
+
*
|
|
26
|
+
* @param {BetterSQLite3Database} db
|
|
27
|
+
* @param {string} tableName
|
|
28
|
+
* @returns {number}
|
|
29
|
+
*/
|
|
30
|
+
const safeCountTableRows = (db, tableName) =>
|
|
31
|
+
db.transaction((tx) => {
|
|
32
|
+
const existsQuery = sql`
|
|
33
|
+
SELECT EXISTS (
|
|
34
|
+
SELECT 1
|
|
35
|
+
FROM sqlite_schema
|
|
36
|
+
WHERE type IS 'table'
|
|
37
|
+
AND name IS ${tableName}
|
|
38
|
+
) AS result
|
|
39
|
+
`
|
|
40
|
+
const existsResult = tx.get(existsQuery)
|
|
41
|
+
const exists = getNumberResult(existsResult)
|
|
42
|
+
if (!exists) return 0
|
|
43
|
+
|
|
44
|
+
const countQuery = sql`
|
|
45
|
+
SELECT COUNT(*) AS result
|
|
46
|
+
FROM ${sql.identifier(tableName)}
|
|
47
|
+
`
|
|
48
|
+
const countResult = tx.get(countQuery)
|
|
49
|
+
return getNumberResult(countResult)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @internal
|
|
54
|
+
* @typedef {'initialized database' | 'migrated' | 'no migration'} MigrationResult
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Wrapper around Drizzle's migration function. Returns what happened during
|
|
59
|
+
* migration; did a migration occur?
|
|
60
|
+
*
|
|
61
|
+
* @param {BetterSQLite3Database} db
|
|
62
|
+
* @param {object} options
|
|
63
|
+
* @param {string} options.migrationsFolder
|
|
64
|
+
* @returns {MigrationResult}
|
|
65
|
+
*/
|
|
66
|
+
export const migrate = (db, { migrationsFolder }) => {
|
|
67
|
+
const migrationsBefore = safeCountTableRows(db, DRIZZLE_MIGRATIONS_TABLE)
|
|
68
|
+
drizzleMigrate(db, {
|
|
69
|
+
migrationsFolder,
|
|
70
|
+
migrationsTable: DRIZZLE_MIGRATIONS_TABLE,
|
|
71
|
+
})
|
|
72
|
+
const migrationsAfter = safeCountTableRows(db, DRIZZLE_MIGRATIONS_TABLE)
|
|
73
|
+
|
|
74
|
+
if (migrationsAfter === migrationsBefore) return 'no migration'
|
|
75
|
+
|
|
76
|
+
if (migrationsBefore === 0) return 'initialized database'
|
|
77
|
+
|
|
78
|
+
return 'migrated'
|
|
79
|
+
}
|
package/src/lib/error.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create an `Error` with a `code` property.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* const err = new ErrorWithCode('INVALID_DATA', 'data was invalid')
|
|
6
|
+
* err.message
|
|
7
|
+
* // => 'data was invalid'
|
|
8
|
+
* err.code
|
|
9
|
+
* // => 'INVALID_DATA'
|
|
10
|
+
*/
|
|
11
|
+
export class ErrorWithCode extends Error {
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} code
|
|
14
|
+
* @param {string} message
|
|
15
|
+
* @param {object} [options]
|
|
16
|
+
* @param {unknown} [options.cause]
|
|
17
|
+
*/
|
|
18
|
+
constructor(code, message, options) {
|
|
19
|
+
super(message, options)
|
|
20
|
+
/** @readonly */ this.code = code
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* If the argument is an `Error` instance, return its `code` property if it is a string.
|
|
26
|
+
* Otherwise, returns `undefined`.
|
|
27
|
+
*
|
|
28
|
+
* @param {unknown} maybeError
|
|
29
|
+
* @returns {undefined | string}
|
|
30
|
+
* @example
|
|
31
|
+
* try {
|
|
32
|
+
* // do something
|
|
33
|
+
* } catch (err) {
|
|
34
|
+
* console.error(getErrorCode(err))
|
|
35
|
+
* }
|
|
36
|
+
*/
|
|
37
|
+
export function getErrorCode(maybeError) {
|
|
38
|
+
if (
|
|
39
|
+
maybeError instanceof Error &&
|
|
40
|
+
'code' in maybeError &&
|
|
41
|
+
typeof maybeError.code === 'string'
|
|
42
|
+
) {
|
|
43
|
+
return maybeError.code
|
|
44
|
+
}
|
|
45
|
+
return undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get the error message from an object if possible.
|
|
50
|
+
* Otherwise, stringify the argument.
|
|
51
|
+
*
|
|
52
|
+
* @param {unknown} maybeError
|
|
53
|
+
* @returns {string}
|
|
54
|
+
* @example
|
|
55
|
+
* try {
|
|
56
|
+
* // do something
|
|
57
|
+
* } catch (err) {
|
|
58
|
+
* console.error(getErrorMessage(err))
|
|
59
|
+
* }
|
|
60
|
+
*/
|
|
61
|
+
export function getErrorMessage(maybeError) {
|
|
62
|
+
if (maybeError && typeof maybeError === 'object' && 'message' in maybeError) {
|
|
63
|
+
try {
|
|
64
|
+
const { message } = maybeError
|
|
65
|
+
if (typeof message === 'string') return message
|
|
66
|
+
} catch (_err) {
|
|
67
|
+
// Ignored
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return 'unknown error'
|
|
71
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { isIPv4, isIPv6 } from 'node:net'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Is this hostname an IP address?
|
|
5
|
+
*
|
|
6
|
+
* @param {string} hostname
|
|
7
|
+
* @returns {boolean}
|
|
8
|
+
* @example
|
|
9
|
+
* isHostnameIpAddress('100.64.0.42')
|
|
10
|
+
* // => false
|
|
11
|
+
*
|
|
12
|
+
* isHostnameIpAddress('[2001:0db8:85a3:0000:0000:8a2e:0370:7334]')
|
|
13
|
+
* // => true
|
|
14
|
+
*
|
|
15
|
+
* isHostnameIpAddress('example.com')
|
|
16
|
+
* // => false
|
|
17
|
+
*/
|
|
18
|
+
export function isHostnameIpAddress(hostname) {
|
|
19
|
+
if (isIPv4(hostname)) return true
|
|
20
|
+
|
|
21
|
+
if (hostname.startsWith('[') && hostname.endsWith(']')) {
|
|
22
|
+
return isIPv6(hostname.slice(1, -1))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return false
|
|
26
|
+
}
|