@comapeo/core 5.5.0 → 6.0.1
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-api.d.ts.map +1 -1
- package/dist/blob-store/downloader.d.ts.map +1 -1
- package/dist/blob-store/hyperdrive-index.d.ts.map +1 -1
- package/dist/blob-store/index.d.ts.map +1 -1
- package/dist/core-manager/bitfield-rle.d.ts.map +1 -1
- package/dist/core-manager/core-index.d.ts.map +1 -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.map +1 -1
- package/dist/datatype/index.d.ts +7 -0
- package/dist/datatype/index.d.ts.map +1 -1
- package/dist/discovery/local-discovery.d.ts.map +1 -1
- package/dist/errors.d.ts +437 -35
- package/dist/errors.d.ts.map +1 -1
- package/dist/fastify-plugins/blobs.d.ts.map +1 -1
- package/dist/fastify-plugins/icons.d.ts.map +1 -1
- package/dist/fastify-plugins/maps.d.ts.map +1 -1
- package/dist/generated/rpc.d.ts +1 -0
- package/dist/generated/rpc.d.ts.map +1 -1
- package/dist/icon-api.d.ts +0 -1
- package/dist/icon-api.d.ts.map +1 -1
- package/dist/import-categories.d.ts.map +1 -1
- package/dist/index-writer/index.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/intl/parse-bcp-47.d.ts.map +1 -1
- package/dist/invite/invite-api.d.ts.map +1 -1
- package/dist/lib/drizzle-helpers.d.ts.map +1 -1
- package/dist/lib/hypercore-helpers.d.ts.map +1 -1
- package/dist/lib/key-by.d.ts.map +1 -1
- package/dist/local-peers.d.ts +0 -14
- package/dist/local-peers.d.ts.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/mapeo-manager.d.ts +2 -1
- package/dist/mapeo-manager.d.ts.map +1 -1
- package/dist/mapeo-project.d.ts +1 -3
- package/dist/mapeo-project.d.ts.map +1 -1
- package/dist/member-api.d.ts +42 -7
- package/dist/member-api.d.ts.map +1 -1
- package/dist/roles.d.ts.map +1 -1
- package/dist/schema/json-schema-to-drizzle.d.ts.map +1 -1
- package/dist/schema.d.ts +2 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/sync/core-sync-state.d.ts.map +1 -1
- package/dist/sync/peer-sync-controller.d.ts.map +1 -1
- package/dist/sync/sync-api.d.ts.map +1 -1
- package/dist/utils.d.ts +8 -10
- package/dist/utils.d.ts.map +1 -1
- package/package.json +19 -3
- package/src/blob-api.js +24 -4
- package/src/blob-store/downloader.js +7 -6
- package/src/blob-store/entries-stream.js +1 -1
- package/src/blob-store/hyperdrive-index.js +3 -5
- package/src/blob-store/index.js +15 -20
- package/src/core-manager/bitfield-rle.js +2 -1
- package/src/core-manager/core-index.js +2 -1
- package/src/core-manager/index.js +9 -10
- package/src/core-ownership.js +7 -3
- package/src/datastore/index.js +13 -9
- package/src/datatype/index.js +28 -5
- package/src/discovery/local-discovery.js +8 -7
- package/src/errors.js +530 -62
- package/src/fastify-controller.js +3 -3
- package/src/fastify-plugins/blobs.js +21 -14
- package/src/fastify-plugins/icons.js +18 -9
- package/src/fastify-plugins/maps.js +6 -5
- package/src/generated/rpc.d.ts +1 -0
- package/src/generated/rpc.js +12 -1
- package/src/generated/rpc.ts +13 -0
- package/src/icon-api.js +15 -7
- package/src/import-categories.js +6 -7
- package/src/index-writer/index.js +3 -2
- package/src/index.js +1 -0
- package/src/intl/parse-bcp-47.js +2 -1
- package/src/invite/invite-api.js +26 -20
- package/src/lib/drizzle-helpers.js +54 -39
- package/src/lib/hypercore-helpers.js +4 -2
- package/src/lib/key-by.js +3 -1
- package/src/local-peers.js +39 -46
- package/src/logger.js +2 -1
- package/src/mapeo-manager.js +36 -23
- package/src/mapeo-project.js +68 -61
- package/src/member-api.js +177 -96
- package/src/roles.js +11 -10
- package/src/schema/json-schema-to-drizzle.js +13 -4
- package/src/schema.js +1 -0
- package/src/sync/core-sync-state.js +2 -1
- package/src/sync/peer-sync-controller.js +4 -3
- package/src/sync/sync-api.js +9 -9
- package/src/translation-api.js +2 -2
- package/src/utils.js +56 -41
- package/dist/lib/error.d.ts +0 -51
- package/dist/lib/error.d.ts.map +0 -1
- package/src/lib/error.js +0 -71
|
@@ -5,7 +5,13 @@ import { Type as T } from '@sinclair/typebox'
|
|
|
5
5
|
|
|
6
6
|
import { SUPPORTED_BLOB_VARIANTS } from '../blob-store/index.js'
|
|
7
7
|
import { HEX_REGEX_32_BYTES, Z_BASE_32_REGEX_32_BYTES } from './constants.js'
|
|
8
|
-
import {
|
|
8
|
+
import { ensureKnownError } from '../errors.js'
|
|
9
|
+
import {
|
|
10
|
+
BlobNotFoundError,
|
|
11
|
+
BlobStoreEntryNotFoundError,
|
|
12
|
+
UnsupportedVariantError,
|
|
13
|
+
} from '../errors.js'
|
|
14
|
+
import ensureError from 'ensure-error'
|
|
9
15
|
|
|
10
16
|
/** @import { BlobId } from '../types.js' */
|
|
11
17
|
|
|
@@ -45,7 +51,7 @@ const PARAMS_JSON_SCHEMA = T.Object({
|
|
|
45
51
|
|
|
46
52
|
/** @type {import('fastify').FastifyPluginAsync<import('fastify').RegisterOptions & BlobServerPluginOpts>} */
|
|
47
53
|
async function blobServerPlugin(fastify, options) {
|
|
48
|
-
if (!options.getBlobStore) throw new
|
|
54
|
+
if (!options.getBlobStore) throw new TypeError('Missing getBlobStore')
|
|
49
55
|
|
|
50
56
|
// We call register here so that the `prefix` option can work if desired
|
|
51
57
|
// https://fastify.dev/docs/latest/Reference/Routes#route-prefixing-and-fastify-plugin
|
|
@@ -64,9 +70,10 @@ async function routes(fastify, options) {
|
|
|
64
70
|
|
|
65
71
|
if (!isValidBlobId(blobId)) {
|
|
66
72
|
reply.code(400)
|
|
67
|
-
throw new
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
throw new UnsupportedVariantError({
|
|
74
|
+
variant: blobId.variant,
|
|
75
|
+
type: blobId.type,
|
|
76
|
+
})
|
|
70
77
|
}
|
|
71
78
|
const { driveId } = blobId
|
|
72
79
|
|
|
@@ -75,7 +82,7 @@ async function routes(fastify, options) {
|
|
|
75
82
|
blobStore = await getBlobStore(projectPublicId)
|
|
76
83
|
} catch (e) {
|
|
77
84
|
reply.code(404)
|
|
78
|
-
throw e
|
|
85
|
+
throw ensureKnownError(e)
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
let entry
|
|
@@ -83,12 +90,12 @@ async function routes(fastify, options) {
|
|
|
83
90
|
entry = await blobStore.entry(blobId, { wait: false })
|
|
84
91
|
} catch (e) {
|
|
85
92
|
reply.code(404)
|
|
86
|
-
throw e
|
|
93
|
+
throw ensureKnownError(e)
|
|
87
94
|
}
|
|
88
95
|
|
|
89
96
|
if (!entry) {
|
|
90
97
|
reply.code(404)
|
|
91
|
-
throw new
|
|
98
|
+
throw new BlobStoreEntryNotFoundError()
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
const { metadata } = entry.value
|
|
@@ -98,19 +105,19 @@ async function routes(fastify, options) {
|
|
|
98
105
|
blobStream = await blobStore.createReadStreamFromEntry(driveId, entry)
|
|
99
106
|
} catch (e) {
|
|
100
107
|
reply.code(404)
|
|
101
|
-
throw e
|
|
108
|
+
throw ensureKnownError(e)
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
try {
|
|
105
112
|
await pEvent(blobStream, 'readable', { rejectionEvents: ['error'] })
|
|
106
|
-
} catch (
|
|
113
|
+
} catch (e) {
|
|
107
114
|
// This matches [how Hyperblobs checks if a blob is unavailable][0].
|
|
108
115
|
// [0]: https://github.com/holepunchto/hyperblobs/blob/518088d2b828082fd70a276fa2c8848a2cf2a56b/index.js#L49
|
|
109
|
-
if (
|
|
116
|
+
if (ensureError(e).message === 'Block not available') {
|
|
110
117
|
reply.code(404)
|
|
111
|
-
throw new
|
|
118
|
+
throw new BlobNotFoundError()
|
|
112
119
|
} else {
|
|
113
|
-
throw
|
|
120
|
+
throw ensureKnownError(e)
|
|
114
121
|
}
|
|
115
122
|
}
|
|
116
123
|
|
|
@@ -130,7 +137,7 @@ async function routes(fastify, options) {
|
|
|
130
137
|
|
|
131
138
|
if (!blobSlice) {
|
|
132
139
|
reply.code(404)
|
|
133
|
-
throw new
|
|
140
|
+
throw new BlobNotFoundError()
|
|
134
141
|
}
|
|
135
142
|
|
|
136
143
|
const [guessedMime] = filetypemime(blobSlice)
|
|
@@ -4,7 +4,12 @@ import { docSchemas } from '@comapeo/schema'
|
|
|
4
4
|
|
|
5
5
|
import { kGetIconBlob } from '../icon-api.js'
|
|
6
6
|
import { HEX_REGEX_32_BYTES, Z_BASE_32_REGEX_32_BYTES } from './constants.js'
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
ensureKnownError,
|
|
9
|
+
InvalidIconPixelDensityError,
|
|
10
|
+
InvalidIconSizeError,
|
|
11
|
+
ExhaustivenessError,
|
|
12
|
+
} from '../errors.js'
|
|
8
13
|
|
|
9
14
|
export default fp(iconServerPlugin, {
|
|
10
15
|
fastify: '4.x',
|
|
@@ -42,7 +47,7 @@ const PARAMS_JSON_SCHEMA = T.Object({
|
|
|
42
47
|
case 'image/svg+xml':
|
|
43
48
|
return T.Literal('svg')
|
|
44
49
|
default:
|
|
45
|
-
throw new ExhaustivenessError(mimeType)
|
|
50
|
+
throw new ExhaustivenessError({ value: mimeType })
|
|
46
51
|
}
|
|
47
52
|
})
|
|
48
53
|
),
|
|
@@ -56,7 +61,7 @@ const PARAMS_JSON_SCHEMA = T.Object({
|
|
|
56
61
|
|
|
57
62
|
/** @type {import('fastify').FastifyPluginAsync<import('fastify').RegisterOptions & IconServerPluginOpts>} */
|
|
58
63
|
async function iconServerPlugin(fastify, options) {
|
|
59
|
-
if (!options.getProject) throw new
|
|
64
|
+
if (!options.getProject) throw new TypeError('Missing getProject')
|
|
60
65
|
fastify.register(routes, options)
|
|
61
66
|
}
|
|
62
67
|
|
|
@@ -95,9 +100,9 @@ async function routes(fastify, options) {
|
|
|
95
100
|
|
|
96
101
|
res.header('Content-Type', mimeType)
|
|
97
102
|
return res.send(icon)
|
|
98
|
-
} catch (
|
|
103
|
+
} catch (e) {
|
|
99
104
|
res.code(404)
|
|
100
|
-
throw
|
|
105
|
+
throw ensureKnownError(e)
|
|
101
106
|
}
|
|
102
107
|
}
|
|
103
108
|
)
|
|
@@ -143,16 +148,20 @@ function assertValidSize(value) {
|
|
|
143
148
|
value
|
|
144
149
|
)
|
|
145
150
|
) {
|
|
146
|
-
throw new
|
|
151
|
+
throw new InvalidIconSizeError({ value })
|
|
147
152
|
}
|
|
148
153
|
}
|
|
149
154
|
|
|
150
155
|
/**
|
|
151
|
-
* @param {
|
|
156
|
+
* @param {number} value
|
|
152
157
|
* @returns {asserts value is import('../icon-api.js').BitmapOpts['pixelDensity']}
|
|
153
158
|
*/
|
|
154
159
|
function assertValidPixelDensity(value) {
|
|
155
|
-
if (
|
|
156
|
-
|
|
160
|
+
if (
|
|
161
|
+
!VALID_PIXEL_DENSITIES.includes(
|
|
162
|
+
/** @type {import('../icon-api.js').BitmapOpts['pixelDensity']} */ (value)
|
|
163
|
+
)
|
|
164
|
+
) {
|
|
165
|
+
throw new InvalidIconPixelDensityError({ density: value })
|
|
157
166
|
}
|
|
158
167
|
}
|
|
@@ -5,7 +5,8 @@ import { ReaderWatch, createServer } 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 '../
|
|
8
|
+
import { ensureKnownError, getErrorCode } from '../errors.js'
|
|
9
|
+
import { FailedToGetStyleError } from '../errors.js'
|
|
9
10
|
|
|
10
11
|
/** @import { FastifyPluginAsync } from 'fastify' */
|
|
11
12
|
/** @import { Stats } from 'node:fs' */
|
|
@@ -48,7 +49,7 @@ export async function plugin(fastify, opts) {
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
if (!response.ok) {
|
|
51
|
-
throw new
|
|
52
|
+
throw new FailedToGetStyleError(customStyleJsonUrl)
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
/** @type {Stats | undefined} */
|
|
@@ -56,12 +57,12 @@ export async function plugin(fastify, opts) {
|
|
|
56
57
|
|
|
57
58
|
try {
|
|
58
59
|
stats = await fs.stat(customMapPath)
|
|
59
|
-
} catch (
|
|
60
|
-
if (getErrorCode(
|
|
60
|
+
} catch (e) {
|
|
61
|
+
if (getErrorCode(e) === 'ENOENT') {
|
|
61
62
|
throw new ENOENTError(customMapPath)
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
throw
|
|
65
|
+
throw ensureKnownError(e)
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
const style = await response.json()
|
package/src/generated/rpc.d.ts
CHANGED
package/src/generated/rpc.js
CHANGED
|
@@ -122,6 +122,7 @@ function createBaseInvite() {
|
|
|
122
122
|
projectName: "",
|
|
123
123
|
invitorName: "",
|
|
124
124
|
sendStats: false,
|
|
125
|
+
invitorWroteDeviceInfo: false,
|
|
125
126
|
};
|
|
126
127
|
}
|
|
127
128
|
export var Invite = {
|
|
@@ -154,6 +155,9 @@ export var Invite = {
|
|
|
154
155
|
if (message.sendStats === true) {
|
|
155
156
|
writer.uint32(72).bool(message.sendStats);
|
|
156
157
|
}
|
|
158
|
+
if (message.invitorWroteDeviceInfo === true) {
|
|
159
|
+
writer.uint32(80).bool(message.invitorWroteDeviceInfo);
|
|
160
|
+
}
|
|
157
161
|
return writer;
|
|
158
162
|
},
|
|
159
163
|
decode: function (input, length) {
|
|
@@ -217,6 +221,12 @@ export var Invite = {
|
|
|
217
221
|
}
|
|
218
222
|
message.sendStats = reader.bool();
|
|
219
223
|
continue;
|
|
224
|
+
case 10:
|
|
225
|
+
if (tag !== 80) {
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
message.invitorWroteDeviceInfo = reader.bool();
|
|
229
|
+
continue;
|
|
220
230
|
}
|
|
221
231
|
if ((tag & 7) === 4 || tag === 0) {
|
|
222
232
|
break;
|
|
@@ -229,7 +239,7 @@ export var Invite = {
|
|
|
229
239
|
return Invite.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
230
240
|
},
|
|
231
241
|
fromPartial: function (object) {
|
|
232
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
242
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
233
243
|
var message = createBaseInvite();
|
|
234
244
|
message.inviteId = (_a = object.inviteId) !== null && _a !== void 0 ? _a : Buffer.alloc(0);
|
|
235
245
|
message.projectInviteId = (_b = object.projectInviteId) !== null && _b !== void 0 ? _b : Buffer.alloc(0);
|
|
@@ -240,6 +250,7 @@ export var Invite = {
|
|
|
240
250
|
message.projectColor = (_g = object.projectColor) !== null && _g !== void 0 ? _g : undefined;
|
|
241
251
|
message.projectDescription = (_h = object.projectDescription) !== null && _h !== void 0 ? _h : undefined;
|
|
242
252
|
message.sendStats = (_j = object.sendStats) !== null && _j !== void 0 ? _j : false;
|
|
253
|
+
message.invitorWroteDeviceInfo = (_k = object.invitorWroteDeviceInfo) !== null && _k !== void 0 ? _k : false;
|
|
243
254
|
return message;
|
|
244
255
|
},
|
|
245
256
|
};
|
package/src/generated/rpc.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface Invite {
|
|
|
12
12
|
projectColor?: string | undefined;
|
|
13
13
|
projectDescription?: string | undefined;
|
|
14
14
|
sendStats: boolean;
|
|
15
|
+
invitorWroteDeviceInfo: boolean;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export interface InviteCancel {
|
|
@@ -187,6 +188,7 @@ function createBaseInvite(): Invite {
|
|
|
187
188
|
projectName: "",
|
|
188
189
|
invitorName: "",
|
|
189
190
|
sendStats: false,
|
|
191
|
+
invitorWroteDeviceInfo: false,
|
|
190
192
|
};
|
|
191
193
|
}
|
|
192
194
|
|
|
@@ -219,6 +221,9 @@ export const Invite = {
|
|
|
219
221
|
if (message.sendStats === true) {
|
|
220
222
|
writer.uint32(72).bool(message.sendStats);
|
|
221
223
|
}
|
|
224
|
+
if (message.invitorWroteDeviceInfo === true) {
|
|
225
|
+
writer.uint32(80).bool(message.invitorWroteDeviceInfo);
|
|
226
|
+
}
|
|
222
227
|
return writer;
|
|
223
228
|
},
|
|
224
229
|
|
|
@@ -292,6 +297,13 @@ export const Invite = {
|
|
|
292
297
|
|
|
293
298
|
message.sendStats = reader.bool();
|
|
294
299
|
continue;
|
|
300
|
+
case 10:
|
|
301
|
+
if (tag !== 80) {
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
message.invitorWroteDeviceInfo = reader.bool();
|
|
306
|
+
continue;
|
|
295
307
|
}
|
|
296
308
|
if ((tag & 7) === 4 || tag === 0) {
|
|
297
309
|
break;
|
|
@@ -315,6 +327,7 @@ export const Invite = {
|
|
|
315
327
|
message.projectColor = object.projectColor ?? undefined;
|
|
316
328
|
message.projectDescription = object.projectDescription ?? undefined;
|
|
317
329
|
message.sendStats = object.sendStats ?? false;
|
|
330
|
+
message.invitorWroteDeviceInfo = object.invitorWroteDeviceInfo ?? false;
|
|
318
331
|
return message;
|
|
319
332
|
},
|
|
320
333
|
};
|
package/src/icon-api.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
/** @import { PresetValue, IconValue } from '@comapeo/schema' */
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
EmptyIconPathError,
|
|
5
|
+
EmptyVariantsArrayError,
|
|
6
|
+
InvalidPixelDensityError,
|
|
7
|
+
NoVariantsExistError,
|
|
8
|
+
NoVariantsForMimeTypeError,
|
|
9
|
+
} from './errors.js'
|
|
10
|
+
|
|
3
11
|
export const kGetIconBlob = Symbol('getIcon')
|
|
4
12
|
|
|
5
13
|
/** @typedef {PresetValue['iconRef']} IconRef */
|
|
@@ -59,7 +67,7 @@ export class IconApi {
|
|
|
59
67
|
*/
|
|
60
68
|
async create(icon) {
|
|
61
69
|
if (icon.variants.length < 1) {
|
|
62
|
-
throw new
|
|
70
|
+
throw new EmptyVariantsArrayError()
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
const savedVariants = await Promise.all(
|
|
@@ -158,15 +166,13 @@ export function getBestVariant(variants, opts) {
|
|
|
158
166
|
wantedPixelDensity = opts.pixelDensity
|
|
159
167
|
}
|
|
160
168
|
if (variants.length === 0) {
|
|
161
|
-
throw new
|
|
169
|
+
throw new NoVariantsExistError()
|
|
162
170
|
}
|
|
163
171
|
|
|
164
172
|
const matchingMime = variants.filter((v) => v.mimeType === wantedMimeType)
|
|
165
173
|
|
|
166
174
|
if (matchingMime.length === 0) {
|
|
167
|
-
throw new
|
|
168
|
-
`No variants with desired mime type ${wantedMimeType} exist`
|
|
169
|
-
)
|
|
175
|
+
throw new NoVariantsForMimeTypeError({ wantedMimeType })
|
|
170
176
|
}
|
|
171
177
|
const wantedSizeNum = SIZE_AS_NUMERIC[wantedSize]
|
|
172
178
|
|
|
@@ -264,14 +270,16 @@ function determineSortValue(target, a, b) {
|
|
|
264
270
|
*/
|
|
265
271
|
export function constructIconPath({ size, pixelDensity, iconId, extension }) {
|
|
266
272
|
if (iconId.length === 0 || size.length === 0 || extension.length === 0) {
|
|
267
|
-
throw new
|
|
273
|
+
throw new EmptyIconPathError()
|
|
268
274
|
}
|
|
269
275
|
|
|
270
276
|
let result = `${iconId}/${size}`
|
|
271
277
|
|
|
272
278
|
if (typeof pixelDensity === 'number') {
|
|
273
279
|
if (pixelDensity < 1) {
|
|
274
|
-
throw new
|
|
280
|
+
throw new InvalidPixelDensityError({
|
|
281
|
+
pixelDensity,
|
|
282
|
+
})
|
|
275
283
|
}
|
|
276
284
|
result += `@${pixelDensity}x`
|
|
277
285
|
}
|
package/src/import-categories.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Reader } from 'comapeocat/reader.js'
|
|
|
3
3
|
import { typedEntries } from './utils.js'
|
|
4
4
|
import { parseBcp47 } from './intl/parse-bcp-47.js'
|
|
5
5
|
import ensureError from 'ensure-error'
|
|
6
|
+
import { IconNotFoundError, KeyNotFoundError } from './errors.js'
|
|
6
7
|
|
|
7
8
|
// The main reason for the concurrency limit is to avoid run-away memory usage.
|
|
8
9
|
// The comapeocat Reader limits icon size to 2Mb (as-per the specification), and
|
|
@@ -75,7 +76,7 @@ export async function importCategories(project, { filePath, logger }) {
|
|
|
75
76
|
const iconXml = await reader.getIcon(iconName)
|
|
76
77
|
if (!iconXml) {
|
|
77
78
|
// This should never happen because of the validate() call above
|
|
78
|
-
throw new
|
|
79
|
+
throw new IconNotFoundError(iconName)
|
|
79
80
|
}
|
|
80
81
|
/** @type {Parameters<typeof project.$icons.create>[0]} */
|
|
81
82
|
const icon = {
|
|
@@ -308,7 +309,7 @@ export async function importCategories(project, { filePath, logger }) {
|
|
|
308
309
|
// flow statements (return, throw, break, continue) in the finally block
|
|
309
310
|
// will "mask" any completion value of the try block or catch block)
|
|
310
311
|
await reader.close().catch((e) => {
|
|
311
|
-
logger.log('
|
|
312
|
+
logger.log('ERROR: closing import file reader', e)
|
|
312
313
|
})
|
|
313
314
|
}
|
|
314
315
|
}
|
|
@@ -349,19 +350,17 @@ function appliesToToGeometry(appliesTo) {
|
|
|
349
350
|
/**
|
|
350
351
|
* Get a value from a Map, or throw an error if the key is not present
|
|
351
352
|
*
|
|
352
|
-
* @template K, V
|
|
353
|
+
* @template {string} K, V
|
|
353
354
|
* @param {Map<K, V>} map
|
|
354
355
|
* @param {K} key
|
|
355
|
-
* @param {string | Error} [msgOrError]
|
|
356
356
|
* @returns {V}
|
|
357
357
|
* @throws {TypeError} if `map` is not a Map
|
|
358
358
|
* @throws {Error} if `key` is not in `map` (with `msgOrError` as message or the default message)
|
|
359
359
|
*/
|
|
360
|
-
function getOrThrow(map, key
|
|
360
|
+
function getOrThrow(map, key) {
|
|
361
361
|
if (!(map instanceof Map)) throw new TypeError('map must be a Map')
|
|
362
362
|
if (!map.has(key)) {
|
|
363
|
-
|
|
364
|
-
throw new Error(msgOrError ?? `key ${key} not found in map`)
|
|
363
|
+
throw new KeyNotFoundError(key)
|
|
365
364
|
}
|
|
366
365
|
return /** @type {V} */ (map.get(key))
|
|
367
366
|
}
|
|
@@ -4,6 +4,7 @@ import { getTableConfig } from 'drizzle-orm/sqlite-core'
|
|
|
4
4
|
import { getBacklinkTableName } from '../schema/comapeo-to-drizzle.js'
|
|
5
5
|
import { discoveryKey } from 'hypercore-crypto'
|
|
6
6
|
import { Logger } from '../logger.js'
|
|
7
|
+
import { UnknownSchemaError } from '../errors.js'
|
|
7
8
|
/** @import { MapeoDoc, VersionIdObject } from '@comapeo/schema' */
|
|
8
9
|
/** @import { MapeoDocTables } from '../datatype/index.js' */
|
|
9
10
|
|
|
@@ -76,7 +77,7 @@ export class IndexWriter {
|
|
|
76
77
|
try {
|
|
77
78
|
const version = { coreDiscoveryKey: discoveryKey(key), index }
|
|
78
79
|
doc = this.#mapDoc(decode(block, version), version)
|
|
79
|
-
} catch
|
|
80
|
+
} catch {
|
|
80
81
|
this.#l.log('Could not decode entry %d of %h', index, key)
|
|
81
82
|
// Unknown or invalid entry - silently ignore
|
|
82
83
|
continue
|
|
@@ -119,7 +120,7 @@ export class IndexWriter {
|
|
|
119
120
|
deleteSchema(schemaName) {
|
|
120
121
|
const indexer = this.#indexers.get(schemaName)
|
|
121
122
|
if (!indexer) {
|
|
122
|
-
throw new
|
|
123
|
+
throw new UnknownSchemaError({ schemaName })
|
|
123
124
|
}
|
|
124
125
|
indexer.deleteAll()
|
|
125
126
|
}
|
package/src/index.js
CHANGED
|
@@ -34,6 +34,7 @@ export { MapeoManager } from './mapeo-manager.js'
|
|
|
34
34
|
/**
|
|
35
35
|
* @namespace MemberApi
|
|
36
36
|
* @typedef {import('./member-api.js').MemberInfo} MemberApi.MemberInfo
|
|
37
|
+
* @typedef {import('./member-api.js').ActiveMemberInfo} MemberApi.ActiveMemberInfo
|
|
37
38
|
* @typedef {import('./roles.js').RoleId} MemberApi.RoleId
|
|
38
39
|
* @typedef {import('./roles.js').RoleIdForNewInvite} MemberApi.RoleIdForNewInvite
|
|
39
40
|
* @typedef {import('./roles.js').RoleIdAssignableToOthers} MemberApi.RoleIdAssignableToOthers
|
package/src/intl/parse-bcp-47.js
CHANGED
|
@@ -4,6 +4,7 @@ import { iso6391To6393, iso6393 } from './iso639.js'
|
|
|
4
4
|
import { iso31661 } from 'iso-3166'
|
|
5
5
|
import { iso31661Alpha3ToAlpha2 } from 'iso-3166'
|
|
6
6
|
import { unM49 as unM49Array } from 'un-m49'
|
|
7
|
+
import { InvalidLanguageTagError } from '../errors.js'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Map of UN M.49 country codes to their corresponding ISO 3166-1 alpha-2 country codes.
|
|
@@ -82,7 +83,7 @@ export function parseBcp47(languageTag) {
|
|
|
82
83
|
const normalized = bcp47Normalize(languageTag)
|
|
83
84
|
const { language, region } = simpleParseBcp47(normalized)
|
|
84
85
|
if (!language) {
|
|
85
|
-
throw new
|
|
86
|
+
throw new InvalidLanguageTagError({ languageTag })
|
|
86
87
|
}
|
|
87
88
|
return {
|
|
88
89
|
language: normalizePrimaryLanguageSubtag(language),
|
package/src/invite/invite-api.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TypedEmitter } from 'tiny-typed-emitter'
|
|
2
2
|
import { InviteResponse_Decision } from '../generated/rpc.js'
|
|
3
|
-
import {
|
|
3
|
+
import { keyToId, noop } from '../utils.js'
|
|
4
4
|
import HashMap from '../lib/hashmap.js'
|
|
5
5
|
import timingSafeEqual from 'string-timing-safe-equal'
|
|
6
6
|
import { Logger } from '../logger.js'
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
NotFoundError,
|
|
11
11
|
AlreadyJoinedError,
|
|
12
12
|
InviteSendError,
|
|
13
|
+
ensureKnownError,
|
|
14
|
+
InviteNotFoundError,
|
|
13
15
|
} from '../errors.js'
|
|
14
16
|
|
|
15
17
|
/** @import { ProjectToAddDetails } from '../mapeo-manager.js' */
|
|
@@ -79,24 +81,24 @@ export class InviteApi extends TypedEmitter {
|
|
|
79
81
|
this.rpc.on('invite', (...args) => {
|
|
80
82
|
try {
|
|
81
83
|
this.#handleNewInvite(...args)
|
|
82
|
-
} catch (
|
|
83
|
-
|
|
84
|
+
} catch (e) {
|
|
85
|
+
this.#l.log('ERROR: could not handle invite', e)
|
|
84
86
|
}
|
|
85
87
|
})
|
|
86
88
|
|
|
87
89
|
this.rpc.on('invite-cancel', (_peerId, inviteCancel) => {
|
|
88
90
|
try {
|
|
89
91
|
this.#handleInviteCancel(inviteCancel)
|
|
90
|
-
} catch (
|
|
91
|
-
|
|
92
|
+
} catch (e) {
|
|
93
|
+
this.#l.log('ERROR: could not handle invite cancel', e)
|
|
92
94
|
}
|
|
93
95
|
})
|
|
94
96
|
|
|
95
97
|
this.rpc.on('got-project-details', (peerId, projectJoinDetails) => {
|
|
96
98
|
try {
|
|
97
99
|
this.#handleGotProjectDetails(peerId, projectJoinDetails)
|
|
98
|
-
} catch (
|
|
99
|
-
|
|
100
|
+
} catch (e) {
|
|
101
|
+
this.#l.log('ERROR: could not handle got-project-details', e)
|
|
100
102
|
}
|
|
101
103
|
})
|
|
102
104
|
}
|
|
@@ -127,6 +129,7 @@ export class InviteApi extends TypedEmitter {
|
|
|
127
129
|
projectColor,
|
|
128
130
|
projectDescription,
|
|
129
131
|
sendStats,
|
|
132
|
+
invitorWroteDeviceInfo,
|
|
130
133
|
} = inviteRpcMessage
|
|
131
134
|
const invite = { ...inviteRpcMessage, receivedAt: Date.now() }
|
|
132
135
|
|
|
@@ -167,6 +170,7 @@ export class InviteApi extends TypedEmitter {
|
|
|
167
170
|
projectColor,
|
|
168
171
|
projectDescription,
|
|
169
172
|
sendStats,
|
|
173
|
+
invitorWroteDeviceInfo,
|
|
170
174
|
})
|
|
171
175
|
}),
|
|
172
176
|
},
|
|
@@ -202,7 +206,9 @@ export class InviteApi extends TypedEmitter {
|
|
|
202
206
|
this.#l.log('Received invite cancel for invite ID %h', inviteId)
|
|
203
207
|
|
|
204
208
|
const invite = this.#invites.get(inviteId)
|
|
205
|
-
|
|
209
|
+
if (!invite) {
|
|
210
|
+
throw new InviteNotFoundError({ inviteId: inviteId.toString('hex') })
|
|
211
|
+
}
|
|
206
212
|
|
|
207
213
|
// TODO: Move this logging to the state machine
|
|
208
214
|
const state = invite.actor.getSnapshot()
|
|
@@ -292,10 +298,9 @@ export class InviteApi extends TypedEmitter {
|
|
|
292
298
|
const inviteId = Buffer.from(inviteIdString, 'hex')
|
|
293
299
|
|
|
294
300
|
const invite = this.#invites.get(inviteId)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
)
|
|
301
|
+
if (!invite) {
|
|
302
|
+
throw new InviteNotFoundError({ inviteId: inviteId.toString('hex') })
|
|
303
|
+
}
|
|
299
304
|
assertCanSend(invite.actor, { type: 'ACCEPT_INVITE' })
|
|
300
305
|
|
|
301
306
|
this.#l.log('Accepting invite %h', inviteId)
|
|
@@ -321,9 +326,9 @@ export class InviteApi extends TypedEmitter {
|
|
|
321
326
|
}
|
|
322
327
|
|
|
323
328
|
return projectPublicId
|
|
324
|
-
} catch (
|
|
325
|
-
this.#l.log('ERROR: Unable to accept invite',
|
|
326
|
-
throw
|
|
329
|
+
} catch (e) {
|
|
330
|
+
this.#l.log('ERROR: Unable to accept invite', e)
|
|
331
|
+
throw ensureKnownError(e)
|
|
327
332
|
}
|
|
328
333
|
}
|
|
329
334
|
|
|
@@ -335,7 +340,9 @@ export class InviteApi extends TypedEmitter {
|
|
|
335
340
|
const inviteId = Buffer.from(inviteIdString, 'hex')
|
|
336
341
|
|
|
337
342
|
const invite = this.#invites.get(inviteId)
|
|
338
|
-
|
|
343
|
+
if (!invite) {
|
|
344
|
+
throw new InviteNotFoundError({ inviteId: inviteId.toString('hex') })
|
|
345
|
+
}
|
|
339
346
|
assertCanSend(invite.actor, { type: 'REJECT_INVITE' })
|
|
340
347
|
|
|
341
348
|
this.#l.log('Rejecting invite %h', inviteId)
|
|
@@ -381,14 +388,13 @@ function toInvite(internal, snapshot, invitorDeviceId) {
|
|
|
381
388
|
*/
|
|
382
389
|
function assertCanSend(actor, eventType) {
|
|
383
390
|
const state = actor.getSnapshot()
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
new InviteSendError(
|
|
391
|
+
if (!state.can(eventType)) {
|
|
392
|
+
throw new InviteSendError(
|
|
387
393
|
`Cannot send ${JSON.stringify(eventType)} in state ${toStateString(
|
|
388
394
|
state.value
|
|
389
395
|
)}`
|
|
390
396
|
)
|
|
391
|
-
|
|
397
|
+
}
|
|
392
398
|
}
|
|
393
399
|
|
|
394
400
|
/**
|