@comapeo/core 1.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.
- package/LICENSE.md +9 -0
- package/README.md +31 -0
- package/dist/blob-api.d.ts +92 -0
- package/dist/blob-api.d.ts.map +1 -0
- package/dist/blob-store/index.d.ts +163 -0
- package/dist/blob-store/index.d.ts.map +1 -0
- package/dist/blob-store/live-download.d.ts +107 -0
- package/dist/blob-store/live-download.d.ts.map +1 -0
- package/dist/config-import.d.ts +74 -0
- package/dist/config-import.d.ts.map +1 -0
- package/dist/constants.d.ts +14 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/core-manager/bitfield-rle.d.ts +25 -0
- package/dist/core-manager/bitfield-rle.d.ts.map +1 -0
- package/dist/core-manager/core-index.d.ts +56 -0
- package/dist/core-manager/core-index.d.ts.map +1 -0
- package/dist/core-manager/index.d.ts +125 -0
- package/dist/core-manager/index.d.ts.map +1 -0
- package/dist/core-manager/random-access-file-pool.d.ts +17 -0
- package/dist/core-manager/random-access-file-pool.d.ts.map +1 -0
- package/dist/core-manager/remote-bitfield.d.ts +146 -0
- package/dist/core-manager/remote-bitfield.d.ts.map +1 -0
- package/dist/core-ownership.d.ts +112 -0
- package/dist/core-ownership.d.ts.map +1 -0
- package/dist/datastore/index.d.ts +91 -0
- package/dist/datastore/index.d.ts.map +1 -0
- package/dist/datatype/index.d.ts +108 -0
- package/dist/discovery/local-discovery.d.ts +64 -0
- package/dist/discovery/local-discovery.d.ts.map +1 -0
- package/dist/errors.d.ts +4 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/fastify-controller.d.ts +27 -0
- package/dist/fastify-controller.d.ts.map +1 -0
- package/dist/fastify-plugins/blobs.d.ts +6 -0
- package/dist/fastify-plugins/blobs.d.ts.map +1 -0
- package/dist/fastify-plugins/constants.d.ts +3 -0
- package/dist/fastify-plugins/constants.d.ts.map +1 -0
- package/dist/fastify-plugins/icons.d.ts +6 -0
- package/dist/fastify-plugins/icons.d.ts.map +1 -0
- package/dist/fastify-plugins/maps/index.d.ts +11 -0
- package/dist/fastify-plugins/maps/index.d.ts.map +1 -0
- package/dist/fastify-plugins/maps/offline-fallback-map.d.ts +12 -0
- package/dist/fastify-plugins/maps/offline-fallback-map.d.ts.map +1 -0
- package/dist/fastify-plugins/maps/static-maps.d.ts +11 -0
- package/dist/fastify-plugins/maps/static-maps.d.ts.map +1 -0
- package/dist/fastify-plugins/utils.d.ts +23 -0
- package/dist/fastify-plugins/utils.d.ts.map +1 -0
- package/dist/generated/extensions.d.ts +44 -0
- package/dist/generated/extensions.d.ts.map +1 -0
- package/dist/generated/keys.d.ts +36 -0
- package/dist/generated/keys.d.ts.map +1 -0
- package/dist/generated/rpc.d.ts +87 -0
- package/dist/generated/rpc.d.ts.map +1 -0
- package/dist/icon-api.d.ts +109 -0
- package/dist/icon-api.d.ts.map +1 -0
- package/dist/index-writer/index.d.ts +51 -0
- package/dist/index-writer/index.d.ts.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/invite-api.d.ts +70 -0
- package/dist/invite-api.d.ts.map +1 -0
- package/dist/lib/hashmap.d.ts +62 -0
- package/dist/lib/hashmap.d.ts.map +1 -0
- package/dist/lib/hypercore-helpers.d.ts +6 -0
- package/dist/lib/hypercore-helpers.d.ts.map +1 -0
- package/dist/lib/noise-secret-stream-helpers.d.ts +45 -0
- package/dist/lib/noise-secret-stream-helpers.d.ts.map +1 -0
- package/dist/lib/ponyfills.d.ts +10 -0
- package/dist/lib/ponyfills.d.ts.map +1 -0
- package/dist/lib/string.d.ts +2 -0
- package/dist/lib/string.d.ts.map +1 -0
- package/dist/lib/timing-safe-equal.d.ts +15 -0
- package/dist/lib/timing-safe-equal.d.ts.map +1 -0
- package/dist/local-peers.d.ts +151 -0
- package/dist/local-peers.d.ts.map +1 -0
- package/dist/logger.d.ts +32 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/mapeo-manager.d.ts +178 -0
- package/dist/mapeo-manager.d.ts.map +1 -0
- package/dist/mapeo-project.d.ts +3233 -0
- package/dist/mapeo-project.d.ts.map +1 -0
- package/dist/member-api.d.ts +114 -0
- package/dist/member-api.d.ts.map +1 -0
- package/dist/roles.d.ts +157 -0
- package/dist/roles.d.ts.map +1 -0
- package/dist/schema/client.d.ts +284 -0
- package/dist/schema/client.d.ts.map +1 -0
- package/dist/schema/project.d.ts +1812 -0
- package/dist/schema/project.d.ts.map +1 -0
- package/dist/schema/schema-to-drizzle.d.ts +20 -0
- package/dist/schema/schema-to-drizzle.d.ts.map +1 -0
- package/dist/schema/types.d.ts +98 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/utils.d.ts +55 -0
- package/dist/schema/utils.d.ts.map +1 -0
- package/dist/sync/core-sync-state.d.ts +252 -0
- package/dist/sync/core-sync-state.d.ts.map +1 -0
- package/dist/sync/namespace-sync-state.d.ts +47 -0
- package/dist/sync/namespace-sync-state.d.ts.map +1 -0
- package/dist/sync/peer-sync-controller.d.ts +44 -0
- package/dist/sync/peer-sync-controller.d.ts.map +1 -0
- package/dist/sync/sync-api.d.ts +158 -0
- package/dist/sync/sync-api.d.ts.map +1 -0
- package/dist/sync/sync-state.d.ts +40 -0
- package/dist/sync/sync-state.d.ts.map +1 -0
- package/dist/translation-api.d.ts +288 -0
- package/dist/translation-api.d.ts.map +1 -0
- package/dist/types.d.ts +115 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils.d.ts +115 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils_types.d.ts +14 -0
- package/drizzle/client/0000_bumpy_carnage.sql +33 -0
- package/drizzle/client/meta/0000_snapshot.json +199 -0
- package/drizzle/client/meta/_journal.json +13 -0
- package/drizzle/project/0000_spooky_lady_ursula.sql +192 -0
- package/drizzle/project/meta/0000_snapshot.json +1137 -0
- package/drizzle/project/meta/_journal.json +13 -0
- package/package.json +202 -0
- package/src/blob-api.js +139 -0
- package/src/blob-store/index.js +325 -0
- package/src/blob-store/live-download.js +373 -0
- package/src/config-import.js +604 -0
- package/src/constants.js +34 -0
- package/src/core-manager/bitfield-rle.js +235 -0
- package/src/core-manager/core-index.js +87 -0
- package/src/core-manager/index.js +504 -0
- package/src/core-manager/random-access-file-pool.js +30 -0
- package/src/core-manager/remote-bitfield.js +416 -0
- package/src/core-ownership.js +235 -0
- package/src/datastore/README.md +46 -0
- package/src/datastore/index.js +234 -0
- package/src/datatype/README.md +33 -0
- package/src/datatype/index.d.ts +108 -0
- package/src/datatype/index.js +358 -0
- package/src/discovery/local-discovery.js +303 -0
- package/src/errors.js +5 -0
- package/src/fastify-controller.js +84 -0
- package/src/fastify-plugins/blobs.js +139 -0
- package/src/fastify-plugins/constants.js +5 -0
- package/src/fastify-plugins/icons.js +158 -0
- package/src/fastify-plugins/maps/index.js +173 -0
- package/src/fastify-plugins/maps/offline-fallback-map.js +114 -0
- package/src/fastify-plugins/maps/static-maps.js +271 -0
- package/src/fastify-plugins/utils.js +52 -0
- package/src/generated/README.md +3 -0
- package/src/generated/extensions.d.ts +44 -0
- package/src/generated/extensions.js +196 -0
- package/src/generated/extensions.ts +237 -0
- package/src/generated/keys.d.ts +36 -0
- package/src/generated/keys.js +148 -0
- package/src/generated/keys.ts +185 -0
- package/src/generated/rpc.d.ts +87 -0
- package/src/generated/rpc.js +389 -0
- package/src/generated/rpc.ts +463 -0
- package/src/icon-api.js +282 -0
- package/src/index-writer/README.md +38 -0
- package/src/index-writer/index.js +124 -0
- package/src/index.js +16 -0
- package/src/invite-api.js +450 -0
- package/src/lib/hashmap.js +91 -0
- package/src/lib/hypercore-helpers.js +18 -0
- package/src/lib/noise-secret-stream-helpers.js +37 -0
- package/src/lib/ponyfills.js +25 -0
- package/src/lib/string.js +7 -0
- package/src/lib/timing-safe-equal.js +34 -0
- package/src/local-peers.js +737 -0
- package/src/logger.js +99 -0
- package/src/mapeo-manager.js +914 -0
- package/src/mapeo-project.js +980 -0
- package/src/member-api.js +319 -0
- package/src/roles.js +412 -0
- package/src/schema/client.js +55 -0
- package/src/schema/project.js +44 -0
- package/src/schema/schema-to-drizzle.js +118 -0
- package/src/schema/types.ts +153 -0
- package/src/schema/utils.js +51 -0
- package/src/sync/core-sync-state.js +440 -0
- package/src/sync/namespace-sync-state.js +193 -0
- package/src/sync/peer-sync-controller.js +332 -0
- package/src/sync/sync-api.js +588 -0
- package/src/sync/sync-state.js +63 -0
- package/src/translation-api.js +141 -0
- package/src/types.ts +149 -0
- package/src/utils.js +210 -0
- package/src/utils_types.d.ts +14 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { and, sql } from 'drizzle-orm'
|
|
2
|
+
import { kCreateWithDocId, kSelect } from './datatype/index.js'
|
|
3
|
+
import { hashObject } from './utils.js'
|
|
4
|
+
import { NotFoundError } from './errors.js'
|
|
5
|
+
/** @import { Translation, TranslationValue } from '@comapeo/schema' */
|
|
6
|
+
/** @import { SetOptional } from 'type-fest' */
|
|
7
|
+
|
|
8
|
+
export const ktranslatedLanguageCodeToSchemaNames = Symbol(
|
|
9
|
+
'translatedLanguageCodeToSchemaNames'
|
|
10
|
+
)
|
|
11
|
+
export default class TranslationApi {
|
|
12
|
+
/** @type {Map<
|
|
13
|
+
* TranslationValue['languageCode'],
|
|
14
|
+
* Set<import('@comapeo/schema/dist/types.js').SchemaName>>} */
|
|
15
|
+
#translatedLanguageCodeToSchemaNames = new Map()
|
|
16
|
+
#dataType
|
|
17
|
+
#table
|
|
18
|
+
#indexPromise
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {Object} opts
|
|
22
|
+
* @param {import('./datatype/index.js').DataType<
|
|
23
|
+
* import('./datastore/index.js').DataStore<'config'>,
|
|
24
|
+
* typeof import('./schema/project.js').translationTable,
|
|
25
|
+
* 'translation',
|
|
26
|
+
* Translation,
|
|
27
|
+
* TranslationValue
|
|
28
|
+
* >} opts.dataType
|
|
29
|
+
* @param {typeof import('./schema/project.js').translationTable} opts.table
|
|
30
|
+
*/
|
|
31
|
+
constructor({ dataType, table }) {
|
|
32
|
+
this.#dataType = dataType
|
|
33
|
+
this.#table = table
|
|
34
|
+
this.#indexPromise = this.#dataType
|
|
35
|
+
.getMany()
|
|
36
|
+
.then((docs) => {
|
|
37
|
+
docs.map((doc) => this.index(doc))
|
|
38
|
+
})
|
|
39
|
+
.catch((err) => {
|
|
40
|
+
throw new Error(`error loading Translation cache: ${err}`)
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** @returns {Promise<void>} */
|
|
45
|
+
ready() {
|
|
46
|
+
return this.#indexPromise
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {TranslationValue} value
|
|
51
|
+
*/
|
|
52
|
+
async put(value) {
|
|
53
|
+
/* eslint-disable no-unused-vars */
|
|
54
|
+
const { message, ...identifiers } = value
|
|
55
|
+
const docId = hashObject(identifiers)
|
|
56
|
+
try {
|
|
57
|
+
const doc = await this.#dataType.getByDocId(docId)
|
|
58
|
+
return await this.#dataType.update(doc.versionId, value)
|
|
59
|
+
} catch (e) {
|
|
60
|
+
if (e instanceof NotFoundError) {
|
|
61
|
+
return await this.#dataType[kCreateWithDocId](docId, value)
|
|
62
|
+
} else {
|
|
63
|
+
throw new Error(`Error on translation ${e}`)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** @typedef {SetOptional<TranslationValue['docRef'], 'versionId'>} DocRefWithOptionalVersionId */
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param {SetOptional<
|
|
72
|
+
* Omit<TranslationValue,'schemaName' | 'message' | 'docRef'>,
|
|
73
|
+
* 'propertyRef' | 'regionCode'> & {docRef: DocRefWithOptionalVersionId}} value
|
|
74
|
+
* @returns {Promise<import('@comapeo/schema').Translation[]>}
|
|
75
|
+
*/
|
|
76
|
+
async get(value) {
|
|
77
|
+
await this.ready()
|
|
78
|
+
|
|
79
|
+
const docTypeIsTranslatedToLanguage =
|
|
80
|
+
this.#translatedLanguageCodeToSchemaNames
|
|
81
|
+
.get(value.languageCode)
|
|
82
|
+
?.has(
|
|
83
|
+
/** @type {import('@comapeo/schema/dist/types.js').SchemaName} */ (
|
|
84
|
+
value.docRefType
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
if (!docTypeIsTranslatedToLanguage) return []
|
|
88
|
+
|
|
89
|
+
const filters = [
|
|
90
|
+
sql`docRefType = ${value.docRefType}`,
|
|
91
|
+
sql`languageCode = ${value.languageCode}`,
|
|
92
|
+
sql`json_extract(docRef, '$.docId') = ${value.docRef.docId}`,
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
if (value.docRef?.versionId) {
|
|
96
|
+
filters.push(
|
|
97
|
+
sql`json_extract(docRef,'$.versionId') = ${value.docRef.versionId}`
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
if (value.propertyRef) {
|
|
101
|
+
filters.push(sql`propertyRef = ${value.propertyRef}`)
|
|
102
|
+
}
|
|
103
|
+
if (value.regionCode) {
|
|
104
|
+
filters.push(sql`regionCode = ${value.regionCode}`)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return (await this.#dataType[kSelect]())
|
|
108
|
+
.where(and.apply(null, filters))
|
|
109
|
+
.prepare()
|
|
110
|
+
.all()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {TranslationValue} doc
|
|
115
|
+
*/
|
|
116
|
+
index(doc) {
|
|
117
|
+
let translatedSchemas = this.#translatedLanguageCodeToSchemaNames.get(
|
|
118
|
+
doc.languageCode
|
|
119
|
+
)
|
|
120
|
+
if (!translatedSchemas) {
|
|
121
|
+
translatedSchemas = new Set()
|
|
122
|
+
this.#translatedLanguageCodeToSchemaNames.set(
|
|
123
|
+
doc.languageCode,
|
|
124
|
+
translatedSchemas
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
translatedSchemas.add(
|
|
128
|
+
/** @type {import('@comapeo/schema/dist/types.js').SchemaName} */ (
|
|
129
|
+
doc.docRefType
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// This should only be used by tests.
|
|
135
|
+
get [ktranslatedLanguageCodeToSchemaNames]() {
|
|
136
|
+
return this.#translatedLanguageCodeToSchemaNames
|
|
137
|
+
}
|
|
138
|
+
get dataType() {
|
|
139
|
+
return this.#dataType
|
|
140
|
+
}
|
|
141
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Simplify,
|
|
3
|
+
TupleToUnion,
|
|
4
|
+
ValueOf,
|
|
5
|
+
RequireAtLeastOne,
|
|
6
|
+
SetOptional,
|
|
7
|
+
} from 'type-fest'
|
|
8
|
+
import { SUPPORTED_BLOB_VARIANTS } from './blob-store/index.js'
|
|
9
|
+
import { MapeoCommon, MapeoDoc, MapeoValue, decode } from '@comapeo/schema'
|
|
10
|
+
import type BigSparseArray from 'big-sparse-array'
|
|
11
|
+
import type Protomux from 'protomux'
|
|
12
|
+
import type NoiseStream from '@hyperswarm/secret-stream'
|
|
13
|
+
import { Duplex } from 'streamx'
|
|
14
|
+
import RandomAccessStorage from 'random-access-storage'
|
|
15
|
+
import { DefaultListener, ListenerSignature } from 'tiny-typed-emitter'
|
|
16
|
+
import type { NAMESPACES } from './constants.js'
|
|
17
|
+
|
|
18
|
+
export type Namespace = (typeof NAMESPACES)[number]
|
|
19
|
+
|
|
20
|
+
type SupportedBlobVariants = typeof SUPPORTED_BLOB_VARIANTS
|
|
21
|
+
export type BlobType = keyof SupportedBlobVariants
|
|
22
|
+
export type BlobVariant<TBlobType extends BlobType> = TupleToUnion<
|
|
23
|
+
SupportedBlobVariants[TBlobType]
|
|
24
|
+
>
|
|
25
|
+
|
|
26
|
+
type BlobIdBase<T extends BlobType> = {
|
|
27
|
+
/** Type of blob */
|
|
28
|
+
type: T
|
|
29
|
+
/** Blob variant (some blob types have smaller previews and thumbnails available) */
|
|
30
|
+
variant: BlobVariant<T>
|
|
31
|
+
/** unique identifier for blob (e.g. hash of content) */
|
|
32
|
+
name: string
|
|
33
|
+
/** discovery key as hex string of hyperdrive where blob is stored */
|
|
34
|
+
driveId: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Ugly, but the only way I could figure out how to get what I wanted
|
|
38
|
+
export type BlobId = Simplify<
|
|
39
|
+
ValueOf<{
|
|
40
|
+
[KeyType in BlobType]: BlobIdBase<KeyType>
|
|
41
|
+
}>
|
|
42
|
+
>
|
|
43
|
+
|
|
44
|
+
type ArrayAtLeastOne<T> = [T, ...T[]]
|
|
45
|
+
|
|
46
|
+
export type BlobFilter = RequireAtLeastOne<{
|
|
47
|
+
[KeyType in BlobType]: ArrayAtLeastOne<BlobVariant<KeyType>>
|
|
48
|
+
}>
|
|
49
|
+
|
|
50
|
+
export type MapeoDocMap = {
|
|
51
|
+
[K in MapeoDoc['schemaName']]: Extract<MapeoDoc, { schemaName: K }>
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type MapeoValueMap = {
|
|
55
|
+
[K in MapeoValue['schemaName']]: Extract<MapeoValue, { schemaName: K }>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// TODO: Replace this with exports from @comapeo/schema
|
|
59
|
+
export type CoreOwnershipWithSignatures = Extract<
|
|
60
|
+
ReturnType<typeof decode>,
|
|
61
|
+
{ schemaName: 'coreOwnership' }
|
|
62
|
+
>
|
|
63
|
+
export type CoreOwnershipWithSignaturesValue = Omit<
|
|
64
|
+
CoreOwnershipWithSignatures,
|
|
65
|
+
Exclude<keyof MapeoCommon, 'schemaName'>
|
|
66
|
+
>
|
|
67
|
+
|
|
68
|
+
type NullToOptional<T> = SetOptional<T, NullKeys<T>>
|
|
69
|
+
type RemoveNull<T> = {
|
|
70
|
+
[K in keyof T]: Exclude<T[K], null>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
type NullKeys<Base> = NonNullable<
|
|
74
|
+
// Wrap in `NonNullable` to strip away the `undefined` type from the produced union.
|
|
75
|
+
{
|
|
76
|
+
// Map through all the keys of the given base type.
|
|
77
|
+
[Key in keyof Base]: null extends Base[Key] // Pick only keys with types extending the given `Condition` type.
|
|
78
|
+
? // Retain this key since the condition passes.
|
|
79
|
+
Key
|
|
80
|
+
: // Discard this key since the condition fails.
|
|
81
|
+
never
|
|
82
|
+
|
|
83
|
+
// Convert the produced object into a union type of the keys which passed the conditional test.
|
|
84
|
+
}[keyof Base]
|
|
85
|
+
>
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Replace an object's `Buffer` values with `string`s. Useful for serialization.
|
|
89
|
+
*/
|
|
90
|
+
export type MapBuffers<T> = {
|
|
91
|
+
[K in keyof T]: T[K] extends Buffer ? string : T[K]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Make any properties whose value include `null` optional, and remove `null`
|
|
96
|
+
* from the type. This converts the types returned from SQLite (which have all
|
|
97
|
+
* top-level optional props set to `null`) to the original types in
|
|
98
|
+
* @comapeo/schema
|
|
99
|
+
*/
|
|
100
|
+
export type NullableToOptional<T> = Simplify<RemoveNull<NullToOptional<T>>>
|
|
101
|
+
export type KeyPair = {
|
|
102
|
+
publicKey: PublicKey
|
|
103
|
+
secretKey: SecretKey
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** 32 byte buffer */
|
|
107
|
+
export type PublicKey = Buffer
|
|
108
|
+
/** 32 byte buffer */
|
|
109
|
+
export type SecretKey = Buffer
|
|
110
|
+
export type IdentityKeyPair = KeyPair
|
|
111
|
+
|
|
112
|
+
type HypercoreRemoteBitfieldPage = {
|
|
113
|
+
bitfield: Uint32Array
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* A subset of Hypercore's `RemoteBitfield` class that we use.
|
|
118
|
+
*/
|
|
119
|
+
export type HypercoreRemoteBitfield = {
|
|
120
|
+
_pages: BigSparseArray<HypercoreRemoteBitfieldPage>
|
|
121
|
+
get(index: number): boolean
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* A subset of Hypercore's `Peer` class that we use.
|
|
126
|
+
* TODO: Contribute these types upstream.
|
|
127
|
+
*/
|
|
128
|
+
export type HypercorePeer = {
|
|
129
|
+
protomux: Protomux
|
|
130
|
+
remotePublicKey: Buffer
|
|
131
|
+
remoteBitfield: HypercoreRemoteBitfield
|
|
132
|
+
onbitfield: (options: { start: number; bitfield: Buffer }) => void
|
|
133
|
+
onrange: (options: { drop: boolean; start: number; length: number }) => void
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export { NoiseStream }
|
|
137
|
+
type ProtocolStream = Omit<NoiseStream, 'userData'> & {
|
|
138
|
+
userData: Protomux
|
|
139
|
+
}
|
|
140
|
+
export type ReplicationStream = Duplex & { noiseStream: ProtocolStream }
|
|
141
|
+
|
|
142
|
+
export type CoreStorage = (name: string) => RandomAccessStorage
|
|
143
|
+
|
|
144
|
+
export type DefaultEmitterEvents<
|
|
145
|
+
L extends ListenerSignature<L> = DefaultListener
|
|
146
|
+
> = {
|
|
147
|
+
newListener: (event: keyof L, listener: L[keyof L]) => void
|
|
148
|
+
removeListener: (event: keyof L, listener: L[keyof L]) => void
|
|
149
|
+
}
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import b4a from 'b4a'
|
|
2
|
+
import sodium from 'sodium-universal'
|
|
3
|
+
import { keyToPublicId } from '@mapeo/crypto'
|
|
4
|
+
import { createHash } from 'node:crypto'
|
|
5
|
+
import stableStringify from 'json-stable-stringify'
|
|
6
|
+
|
|
7
|
+
const PROJECT_INVITE_ID_SALT = Buffer.from('mapeo project invite id', 'ascii')
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {String|Buffer} id
|
|
11
|
+
* @returns {Buffer | Uint8Array}
|
|
12
|
+
*/
|
|
13
|
+
export function idToKey(id) {
|
|
14
|
+
if (b4a.isBuffer(id)) {
|
|
15
|
+
return /** @type {Buffer} */ (id)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return b4a.from(/** @type {String} */ (id), 'hex')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
*
|
|
23
|
+
* @param {Buffer|String} key
|
|
24
|
+
* @returns {String}
|
|
25
|
+
*/
|
|
26
|
+
export function keyToId(key) {
|
|
27
|
+
if (typeof key === 'string') {
|
|
28
|
+
return key
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return key.toString('hex')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {String} version
|
|
36
|
+
* @returns {{coreId: String, blockIndex: Number}}
|
|
37
|
+
*/
|
|
38
|
+
export function parseVersion(version) {
|
|
39
|
+
const [coreId, blockIndex] = version.split('@')
|
|
40
|
+
return {
|
|
41
|
+
coreId,
|
|
42
|
+
blockIndex: Number(blockIndex),
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class ExhaustivenessError extends Error {
|
|
47
|
+
/** @param {never} value */
|
|
48
|
+
constructor(value) {
|
|
49
|
+
super(`Exhaustiveness check failed. ${value} should be impossible`)
|
|
50
|
+
this.name = 'ExhaustivenessError'
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @returns {void}
|
|
56
|
+
*/
|
|
57
|
+
export function noop() {}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {unknown} condition
|
|
61
|
+
* @param {string} message
|
|
62
|
+
* @returns {asserts condition}
|
|
63
|
+
*/
|
|
64
|
+
export function assert(condition, message) {
|
|
65
|
+
if (!condition) throw new Error(message)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Return a function that itself returns whether a value is part of the set.
|
|
70
|
+
*
|
|
71
|
+
* Similar to binding `Set.prototype.has`, but (1) is shorter (2) refines the type.
|
|
72
|
+
*
|
|
73
|
+
* @template T
|
|
74
|
+
* @param {Readonly<Set<T>>} set
|
|
75
|
+
* @example
|
|
76
|
+
* const mySet = new Set([1, 2, 3])
|
|
77
|
+
* const isInMySet = setHas(mySet)
|
|
78
|
+
*
|
|
79
|
+
* console.log(isInMySet(2))
|
|
80
|
+
* // => true
|
|
81
|
+
*/
|
|
82
|
+
export function setHas(set) {
|
|
83
|
+
/**
|
|
84
|
+
* @param {unknown} value
|
|
85
|
+
* @returns {value is T}
|
|
86
|
+
*/
|
|
87
|
+
return (value) => set.has(/** @type {*} */ (value))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @template T
|
|
92
|
+
* @param {undefined | T} value
|
|
93
|
+
* @returns {value is T}
|
|
94
|
+
*/
|
|
95
|
+
export function isDefined(value) {
|
|
96
|
+
return value !== undefined
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* When reading from SQLite, any optional properties are set to `null`. This
|
|
101
|
+
* converts `null` back to `undefined` to match the input types (e.g. the types
|
|
102
|
+
* defined in @comapeo/schema)
|
|
103
|
+
* @template {{}} T
|
|
104
|
+
* @param {T} obj
|
|
105
|
+
* @returns {import('./types.js').NullableToOptional<T>}
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
export function deNullify(obj) {
|
|
109
|
+
/** @type {Record<string, any>} */
|
|
110
|
+
const objNoNulls = {}
|
|
111
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
112
|
+
objNoNulls[key] = value === null ? undefined : value
|
|
113
|
+
}
|
|
114
|
+
return /** @type {import('./types.js').NullableToOptional<T>} */ (objNoNulls)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @template {import('@comapeo/schema').MapeoDoc & { forks?: string[] }} T
|
|
119
|
+
* @param {T} doc
|
|
120
|
+
* @returns {Omit<T, 'docId' | 'versionId' | 'originalVersionId' | 'links' | 'forks' | 'createdAt' | 'updatedAt' | 'deleted'>}
|
|
121
|
+
*/
|
|
122
|
+
export function valueOf(doc) {
|
|
123
|
+
/* eslint-disable no-unused-vars */
|
|
124
|
+
const {
|
|
125
|
+
docId,
|
|
126
|
+
versionId,
|
|
127
|
+
originalVersionId,
|
|
128
|
+
links,
|
|
129
|
+
forks,
|
|
130
|
+
createdAt,
|
|
131
|
+
updatedAt,
|
|
132
|
+
deleted,
|
|
133
|
+
...rest
|
|
134
|
+
} = doc
|
|
135
|
+
/* eslint-enable no-unused-vars */
|
|
136
|
+
return rest
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Create an internal ID from a project key
|
|
141
|
+
* @param {Buffer} projectKey
|
|
142
|
+
* @returns {string}
|
|
143
|
+
*/
|
|
144
|
+
export function projectKeyToId(projectKey) {
|
|
145
|
+
return projectKey.toString('hex')
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Create a public ID from a project key
|
|
150
|
+
* @param {Buffer} projectKey
|
|
151
|
+
* @returns {string}
|
|
152
|
+
*/
|
|
153
|
+
export function projectKeyToPublicId(projectKey) {
|
|
154
|
+
return keyToPublicId(projectKey)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Generate an invite ID from a project key
|
|
159
|
+
* @param {Readonly<Buffer>} projectKey
|
|
160
|
+
* @returns {Buffer}
|
|
161
|
+
*/
|
|
162
|
+
export function projectKeyToProjectInviteId(projectKey) {
|
|
163
|
+
const result = Buffer.allocUnsafe(32)
|
|
164
|
+
sodium.crypto_generichash(result, PROJECT_INVITE_ID_SALT, projectKey)
|
|
165
|
+
return result
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @param {string} projectId Project internal ID
|
|
170
|
+
* @returns {Buffer} 24-byte nonce (same length as sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)
|
|
171
|
+
*/
|
|
172
|
+
export function projectIdToNonce(projectId) {
|
|
173
|
+
return Buffer.from(projectId, 'hex').subarray(0, 24)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @param {import('@mapeo/crypto').KeyManager} keyManager
|
|
178
|
+
* @returns {string}
|
|
179
|
+
*/
|
|
180
|
+
export function getDeviceId(keyManager) {
|
|
181
|
+
return keyManager.getIdentityKeypair().publicKey.toString('hex')
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Small helper to create a typed map
|
|
186
|
+
*
|
|
187
|
+
* @template {string} K
|
|
188
|
+
* @template {any} V
|
|
189
|
+
* @param {ReadonlyArray<K>} keys
|
|
190
|
+
* @param {V} value
|
|
191
|
+
* @returns {Record<K, V extends () => infer T ? T : V>} */
|
|
192
|
+
export function createMap(keys, value) {
|
|
193
|
+
const map = /** @type {Record<K, V extends () => infer T ? T : V>} */ ({})
|
|
194
|
+
for (const key of keys) {
|
|
195
|
+
map[key] = typeof value === 'function' ? value() : value
|
|
196
|
+
}
|
|
197
|
+
return map
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* create a sha256 hash of an object using json-stable-stringify for deterministic results
|
|
202
|
+
* @param {Object} obj
|
|
203
|
+
* @returns {String} hash of the object
|
|
204
|
+
*/
|
|
205
|
+
export function hashObject(obj) {
|
|
206
|
+
return createHash('sha256')
|
|
207
|
+
.update(stableStringify(obj))
|
|
208
|
+
.digest()
|
|
209
|
+
.toString('hex')
|
|
210
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TypedEmitter } from 'tiny-typed-emitter'
|
|
2
|
+
|
|
3
|
+
export type TypedEvents<T extends TypedEmitter<any>> = T extends TypedEmitter<
|
|
4
|
+
infer Result
|
|
5
|
+
>
|
|
6
|
+
? Result
|
|
7
|
+
: never
|
|
8
|
+
|
|
9
|
+
export type TypedEventsFor<T extends TypedEmitter<any>> = keyof TypedEvents<T>
|
|
10
|
+
|
|
11
|
+
export type TypedEventArgs<
|
|
12
|
+
Emitter extends TypedEmitter<any>,
|
|
13
|
+
Event extends TypedEventsFor<Emitter>
|
|
14
|
+
> = Parameters<TypedEvents<Emitter>[Event]>
|