@comapeo/core 5.4.1 → 6.0.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.
Files changed (100) hide show
  1. package/dist/blob-api.d.ts.map +1 -1
  2. package/dist/blob-store/downloader.d.ts.map +1 -1
  3. package/dist/blob-store/hyperdrive-index.d.ts.map +1 -1
  4. package/dist/blob-store/index.d.ts.map +1 -1
  5. package/dist/core-manager/bitfield-rle.d.ts.map +1 -1
  6. package/dist/core-manager/core-index.d.ts.map +1 -1
  7. package/dist/core-manager/index.d.ts +1 -2
  8. package/dist/core-manager/index.d.ts.map +1 -1
  9. package/dist/core-ownership.d.ts.map +1 -1
  10. package/dist/datastore/index.d.ts.map +1 -1
  11. package/dist/datatype/index.d.ts +7 -0
  12. package/dist/datatype/index.d.ts.map +1 -1
  13. package/dist/discovery/local-discovery.d.ts.map +1 -1
  14. package/dist/errors.d.ts +437 -35
  15. package/dist/errors.d.ts.map +1 -1
  16. package/dist/fastify-plugins/blobs.d.ts.map +1 -1
  17. package/dist/fastify-plugins/icons.d.ts.map +1 -1
  18. package/dist/fastify-plugins/maps.d.ts.map +1 -1
  19. package/dist/generated/extensions.d.ts +1 -1
  20. package/dist/generated/extensions.d.ts.map +1 -1
  21. package/dist/generated/rpc.d.ts +1 -0
  22. package/dist/generated/rpc.d.ts.map +1 -1
  23. package/dist/icon-api.d.ts +0 -1
  24. package/dist/icon-api.d.ts.map +1 -1
  25. package/dist/import-categories.d.ts.map +1 -1
  26. package/dist/index-writer/index.d.ts.map +1 -1
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/intl/parse-bcp-47.d.ts.map +1 -1
  30. package/dist/invite/invite-api.d.ts.map +1 -1
  31. package/dist/lib/drizzle-helpers.d.ts.map +1 -1
  32. package/dist/lib/hypercore-helpers.d.ts.map +1 -1
  33. package/dist/lib/key-by.d.ts.map +1 -1
  34. package/dist/local-peers.d.ts +0 -14
  35. package/dist/local-peers.d.ts.map +1 -1
  36. package/dist/logger.d.ts.map +1 -1
  37. package/dist/mapeo-manager.d.ts +2 -1
  38. package/dist/mapeo-manager.d.ts.map +1 -1
  39. package/dist/mapeo-project.d.ts +15 -8
  40. package/dist/mapeo-project.d.ts.map +1 -1
  41. package/dist/member-api.d.ts +42 -7
  42. package/dist/member-api.d.ts.map +1 -1
  43. package/dist/roles.d.ts.map +1 -1
  44. package/dist/schema/json-schema-to-drizzle.d.ts.map +1 -1
  45. package/dist/schema.d.ts +2 -0
  46. package/dist/schema.d.ts.map +1 -0
  47. package/dist/sync/core-sync-state.d.ts.map +1 -1
  48. package/dist/sync/peer-sync-controller.d.ts.map +1 -1
  49. package/dist/sync/sync-api.d.ts.map +1 -1
  50. package/dist/utils.d.ts +8 -10
  51. package/dist/utils.d.ts.map +1 -1
  52. package/package.json +18 -2
  53. package/src/blob-api.js +24 -4
  54. package/src/blob-store/downloader.js +7 -6
  55. package/src/blob-store/entries-stream.js +1 -1
  56. package/src/blob-store/hyperdrive-index.js +3 -5
  57. package/src/blob-store/index.js +15 -20
  58. package/src/core-manager/bitfield-rle.js +2 -1
  59. package/src/core-manager/core-index.js +2 -1
  60. package/src/core-manager/index.js +12 -13
  61. package/src/core-ownership.js +7 -3
  62. package/src/datastore/index.js +13 -9
  63. package/src/datatype/index.js +28 -5
  64. package/src/discovery/local-discovery.js +8 -7
  65. package/src/errors.js +530 -62
  66. package/src/fastify-controller.js +3 -3
  67. package/src/fastify-plugins/blobs.js +21 -14
  68. package/src/fastify-plugins/icons.js +18 -9
  69. package/src/fastify-plugins/maps.js +6 -5
  70. package/src/generated/extensions.d.ts +1 -1
  71. package/src/generated/extensions.js +5 -5
  72. package/src/generated/extensions.ts +6 -6
  73. package/src/generated/rpc.d.ts +1 -0
  74. package/src/generated/rpc.js +12 -1
  75. package/src/generated/rpc.ts +13 -0
  76. package/src/icon-api.js +15 -7
  77. package/src/import-categories.js +6 -7
  78. package/src/index-writer/index.js +3 -2
  79. package/src/index.js +1 -0
  80. package/src/intl/parse-bcp-47.js +2 -1
  81. package/src/invite/invite-api.js +26 -20
  82. package/src/lib/drizzle-helpers.js +54 -39
  83. package/src/lib/hypercore-helpers.js +4 -2
  84. package/src/lib/key-by.js +3 -1
  85. package/src/local-peers.js +39 -46
  86. package/src/logger.js +2 -1
  87. package/src/mapeo-manager.js +36 -23
  88. package/src/mapeo-project.js +96 -67
  89. package/src/member-api.js +177 -96
  90. package/src/roles.js +11 -10
  91. package/src/schema/json-schema-to-drizzle.js +13 -4
  92. package/src/schema.js +1 -0
  93. package/src/sync/core-sync-state.js +2 -1
  94. package/src/sync/peer-sync-controller.js +4 -3
  95. package/src/sync/sync-api.js +9 -9
  96. package/src/translation-api.js +2 -2
  97. package/src/utils.js +58 -43
  98. package/dist/lib/error.d.ts +0 -51
  99. package/dist/lib/error.d.ts.map +0 -1
  100. 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 { getErrorMessage } from '../lib/error.js'
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 Error('Missing getBlobStore')
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 Error(
68
- `Unsupported variant "${blobId.variant}" for ${blobId.type}`
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 Error('Entry not found')
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 (err) {
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 (getErrorMessage(err) === 'Block not available') {
116
+ if (ensureError(e).message === 'Block not available') {
110
117
  reply.code(404)
111
- throw new Error('Blob not found')
118
+ throw new BlobNotFoundError()
112
119
  } else {
113
- throw err
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 Error('Blob not found')
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 { ExhaustivenessError } from '../utils.js'
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 Error('Missing getProject')
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 (err) {
103
+ } catch (e) {
99
104
  res.code(404)
100
- throw err
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 Error(`'${value}' is not a valid icon size`)
151
+ throw new InvalidIconSizeError({ value })
147
152
  }
148
153
  }
149
154
 
150
155
  /**
151
- * @param {unknown} value
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 (!VALID_PIXEL_DENSITIES.includes(/** @type {any} */ (value))) {
156
- throw new Error(`${value} is not a valid icon pixel density`)
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 '../lib/error.js'
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 Error(`Failed to get style from ${customStyleJsonUrl.href}`)
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 (err) {
60
- if (getErrorCode(err) === 'ENOENT') {
60
+ } catch (e) {
61
+ if (getErrorCode(e) === 'ENOENT') {
61
62
  throw new ENOENTError(customMapPath)
62
63
  }
63
64
 
64
- throw err
65
+ throw ensureKnownError(e)
65
66
  }
66
67
 
67
68
  const style = await response.json()
@@ -43,7 +43,7 @@ export interface MapShareExtension {
43
43
  /** URLs to map share */
44
44
  mapShareUrls: string[];
45
45
  /** ID of peer that can receive the map share (each map share is linked to a specific device ID) */
46
- receiverDeviceId: string;
46
+ receiverDeviceKey: Buffer;
47
47
  /** The ID of the map share */
48
48
  shareId: string;
49
49
  /** The name of the map being shared */
@@ -332,7 +332,7 @@ export var DownloadIntentExtension_DownloadIntentsEntry = {
332
332
  function createBaseMapShareExtension() {
333
333
  return {
334
334
  mapShareUrls: [],
335
- receiverDeviceId: "",
335
+ receiverDeviceKey: Buffer.alloc(0),
336
336
  shareId: "",
337
337
  mapName: "",
338
338
  mapId: "",
@@ -351,8 +351,8 @@ export var MapShareExtension = {
351
351
  var v = _a[_i];
352
352
  writer.uint32(10).string(v);
353
353
  }
354
- if (message.receiverDeviceId !== "") {
355
- writer.uint32(18).string(message.receiverDeviceId);
354
+ if (message.receiverDeviceKey.length !== 0) {
355
+ writer.uint32(18).bytes(message.receiverDeviceKey);
356
356
  }
357
357
  if (message.shareId !== "") {
358
358
  writer.uint32(26).string(message.shareId);
@@ -403,7 +403,7 @@ export var MapShareExtension = {
403
403
  if (tag !== 18) {
404
404
  break;
405
405
  }
406
- message.receiverDeviceId = reader.string();
406
+ message.receiverDeviceKey = reader.bytes();
407
407
  continue;
408
408
  case 3:
409
409
  if (tag !== 26) {
@@ -481,7 +481,7 @@ export var MapShareExtension = {
481
481
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
482
482
  var message = createBaseMapShareExtension();
483
483
  message.mapShareUrls = ((_a = object.mapShareUrls) === null || _a === void 0 ? void 0 : _a.map(function (e) { return e; })) || [];
484
- message.receiverDeviceId = (_b = object.receiverDeviceId) !== null && _b !== void 0 ? _b : "";
484
+ message.receiverDeviceKey = (_b = object.receiverDeviceKey) !== null && _b !== void 0 ? _b : Buffer.alloc(0);
485
485
  message.shareId = (_c = object.shareId) !== null && _c !== void 0 ? _c : "";
486
486
  message.mapName = (_d = object.mapName) !== null && _d !== void 0 ? _d : "";
487
487
  message.mapId = (_e = object.mapId) !== null && _e !== void 0 ? _e : "";
@@ -91,7 +91,7 @@ export interface MapShareExtension {
91
91
  /** URLs to map share */
92
92
  mapShareUrls: string[];
93
93
  /** ID of peer that can receive the map share (each map share is linked to a specific device ID) */
94
- receiverDeviceId: string;
94
+ receiverDeviceKey: Buffer;
95
95
  /** The ID of the map share */
96
96
  shareId: string;
97
97
  /** The name of the map being shared */
@@ -421,7 +421,7 @@ export const DownloadIntentExtension_DownloadIntentsEntry = {
421
421
  function createBaseMapShareExtension(): MapShareExtension {
422
422
  return {
423
423
  mapShareUrls: [],
424
- receiverDeviceId: "",
424
+ receiverDeviceKey: Buffer.alloc(0),
425
425
  shareId: "",
426
426
  mapName: "",
427
427
  mapId: "",
@@ -439,8 +439,8 @@ export const MapShareExtension = {
439
439
  for (const v of message.mapShareUrls) {
440
440
  writer.uint32(10).string(v!);
441
441
  }
442
- if (message.receiverDeviceId !== "") {
443
- writer.uint32(18).string(message.receiverDeviceId);
442
+ if (message.receiverDeviceKey.length !== 0) {
443
+ writer.uint32(18).bytes(message.receiverDeviceKey);
444
444
  }
445
445
  if (message.shareId !== "") {
446
446
  writer.uint32(26).string(message.shareId);
@@ -493,7 +493,7 @@ export const MapShareExtension = {
493
493
  break;
494
494
  }
495
495
 
496
- message.receiverDeviceId = reader.string();
496
+ message.receiverDeviceKey = reader.bytes() as Buffer;
497
497
  continue;
498
498
  case 3:
499
499
  if (tag !== 26) {
@@ -583,7 +583,7 @@ export const MapShareExtension = {
583
583
  fromPartial<I extends Exact<DeepPartial<MapShareExtension>, I>>(object: I): MapShareExtension {
584
584
  const message = createBaseMapShareExtension();
585
585
  message.mapShareUrls = object.mapShareUrls?.map((e) => e) || [];
586
- message.receiverDeviceId = object.receiverDeviceId ?? "";
586
+ message.receiverDeviceKey = object.receiverDeviceKey ?? Buffer.alloc(0);
587
587
  message.shareId = object.shareId ?? "";
588
588
  message.mapName = object.mapName ?? "";
589
589
  message.mapId = object.mapId ?? "";
@@ -10,6 +10,7 @@ export interface Invite {
10
10
  projectColor?: string | undefined;
11
11
  projectDescription?: string | undefined;
12
12
  sendStats: boolean;
13
+ invitorWroteDeviceInfo: boolean;
13
14
  }
14
15
  export interface InviteCancel {
15
16
  inviteId: Buffer;
@@ -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
  };
@@ -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 Error('empty variants array')
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 Error('No variants exist')
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 Error(
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 Error('iconId, size, and extension cannot be empty strings')
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 Error('pixelDensity must be a positive number')
280
+ throw new InvalidPixelDensityError({
281
+ pixelDensity,
282
+ })
275
283
  }
276
284
  result += `@${pixelDensity}x`
277
285
  }
@@ -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 Error(`Icon ${iconName} not found in import file`)
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('error closing import file reader', e)
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, msgOrError) {
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
- if (msgOrError instanceof Error) throw msgOrError
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 (e) {
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 Error(`IndexWriter doesn't know a schema named "${schemaName}"`)
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
@@ -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 Error(`Invalid BCP 47 language tag: ${languageTag}`)
86
+ throw new InvalidLanguageTagError({ languageTag })
86
87
  }
87
88
  return {
88
89
  language: normalizePrimaryLanguageSubtag(language),