@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,234 @@
|
|
|
1
|
+
import { TypedEmitter } from 'tiny-typed-emitter'
|
|
2
|
+
import { encode, decode, getVersionId, parseVersionId } from '@comapeo/schema'
|
|
3
|
+
import MultiCoreIndexer from 'multi-core-indexer'
|
|
4
|
+
import pDefer from 'p-defer'
|
|
5
|
+
import { discoveryKey } from 'hypercore-crypto'
|
|
6
|
+
import { NAMESPACE_SCHEMAS } from '../constants.js'
|
|
7
|
+
import { createMap } from '../utils.js'
|
|
8
|
+
/** @import { MapeoDoc } from '@comapeo/schema' */
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* For each schemaName in this dataStore, emits an array of docIds that have
|
|
12
|
+
* been indexed whenever indexing finishes (becomes idle). `projectSettings`
|
|
13
|
+
* currently not supported.
|
|
14
|
+
*
|
|
15
|
+
* @template {MapeoDoc['schemaName']} TSchemaName
|
|
16
|
+
* @typedef {{
|
|
17
|
+
* [S in Exclude<TSchemaName, 'projectSettings'>]: (docIds: Set<string>) => void
|
|
18
|
+
* }} DataStoreEvents
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* @template T
|
|
22
|
+
* @template {keyof any} K
|
|
23
|
+
* @typedef {T extends any ? Omit<T, K> : never} OmitUnion
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {typeof NAMESPACE_SCHEMAS} NamespaceSchemas
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @template {keyof NamespaceSchemas} [TNamespace=keyof NamespaceSchemas]
|
|
32
|
+
* @template {NamespaceSchemas[TNamespace][number]} [TSchemaName=NamespaceSchemas[TNamespace][number]]
|
|
33
|
+
* @extends {TypedEmitter<DataStoreEvents<TSchemaName>>}
|
|
34
|
+
*/
|
|
35
|
+
export class DataStore extends TypedEmitter {
|
|
36
|
+
#coreManager
|
|
37
|
+
#namespace
|
|
38
|
+
#batch
|
|
39
|
+
#writerCore
|
|
40
|
+
#coreIndexer
|
|
41
|
+
/** @type {Map<string, import('p-defer').DeferredPromise<void>>} */
|
|
42
|
+
#pendingIndex = new Map()
|
|
43
|
+
/** @type {Set<import('p-defer').DeferredPromise<void>['promise']>} */
|
|
44
|
+
#pendingAppends = new Set()
|
|
45
|
+
/** @type {Record<MapeoDoc['schemaName'], Set<string>>} */
|
|
46
|
+
#pendingEmits
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {object} opts
|
|
50
|
+
* @param {import('../core-manager/index.js').CoreManager} opts.coreManager
|
|
51
|
+
* @param {TNamespace} opts.namespace
|
|
52
|
+
* @param {(entries: MultiCoreIndexer.Entry<'binary'>[]) => Promise<import('../index-writer/index.js').IndexedDocIds>} opts.batch
|
|
53
|
+
* @param {MultiCoreIndexer.StorageParam} opts.storage
|
|
54
|
+
*/
|
|
55
|
+
constructor({ coreManager, namespace, batch, storage }) {
|
|
56
|
+
super()
|
|
57
|
+
this.#coreManager = coreManager
|
|
58
|
+
this.#namespace = namespace
|
|
59
|
+
this.#batch = batch
|
|
60
|
+
this.#pendingEmits = createMap(
|
|
61
|
+
NAMESPACE_SCHEMAS[namespace],
|
|
62
|
+
() => new Set()
|
|
63
|
+
)
|
|
64
|
+
this.#writerCore = coreManager.getWriterCore(namespace).core
|
|
65
|
+
const cores = coreManager.getCores(namespace).map((cr) => cr.core)
|
|
66
|
+
this.#coreIndexer = new MultiCoreIndexer(cores, {
|
|
67
|
+
storage,
|
|
68
|
+
batch: (entries) => this.#handleEntries(entries),
|
|
69
|
+
})
|
|
70
|
+
coreManager.on('add-core', (coreRecord) => {
|
|
71
|
+
if (coreRecord.namespace !== namespace) return
|
|
72
|
+
this.#coreIndexer.addCore(coreRecord.core)
|
|
73
|
+
})
|
|
74
|
+
this.#coreIndexer.on('idle', this.#handleIndexerIdle)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
get indexer() {
|
|
78
|
+
return this.#coreIndexer
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
get namespace() {
|
|
82
|
+
return this.#namespace
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get schemas() {
|
|
86
|
+
// Return a shallow copy (slice(0)) to avoid mutation bugs
|
|
87
|
+
return NAMESPACE_SCHEMAS[this.#namespace].slice(0)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get writerCore() {
|
|
91
|
+
return this.#writerCore
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
getIndexState() {
|
|
95
|
+
return this.#coreIndexer.state
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
*
|
|
100
|
+
* @param {MultiCoreIndexer.Entry<'binary'>[]} entries
|
|
101
|
+
*/
|
|
102
|
+
async #handleEntries(entries) {
|
|
103
|
+
const indexed = await this.#batch(entries)
|
|
104
|
+
await Promise.all(this.#pendingAppends)
|
|
105
|
+
// Writes to the writerCore need to wait until the entry is indexed before
|
|
106
|
+
// returning, so we check if any incoming entry has a pending promise
|
|
107
|
+
for (const entry of entries) {
|
|
108
|
+
if (!entry.key.equals(this.#writerCore.key)) continue
|
|
109
|
+
const versionId = getVersionId({
|
|
110
|
+
coreDiscoveryKey: discoveryKey(entry.key),
|
|
111
|
+
index: entry.index,
|
|
112
|
+
})
|
|
113
|
+
const pending = this.#pendingIndex.get(versionId)
|
|
114
|
+
if (!pending) continue
|
|
115
|
+
pending.resolve()
|
|
116
|
+
}
|
|
117
|
+
for (const schemaName of this.schemas) {
|
|
118
|
+
// Unsupported initially
|
|
119
|
+
if (schemaName === 'projectSettings') continue
|
|
120
|
+
const docIds = indexed[schemaName]
|
|
121
|
+
if (!docIds) continue
|
|
122
|
+
for (const docId of docIds) {
|
|
123
|
+
// We'll only emit these once the indexer is idle - a particular docId
|
|
124
|
+
// could have many updates, and we only emit it once after indexing
|
|
125
|
+
this.#pendingEmits[schemaName].add(docId)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* UNSAFE: Does not check links: [] refer to a valid doc - should only be used
|
|
132
|
+
* internally.
|
|
133
|
+
*
|
|
134
|
+
* Write a doc, must be one of the schema types supported by the namespace of
|
|
135
|
+
* this DataStore.
|
|
136
|
+
* @template {Extract<Parameters<encode>[0], { schemaName: TSchemaName }>} TDoc
|
|
137
|
+
* @param {TDoc} doc
|
|
138
|
+
* @returns {Promise<Extract<MapeoDoc, TDoc>>}
|
|
139
|
+
*/
|
|
140
|
+
async write(doc) {
|
|
141
|
+
// @ts-ignore
|
|
142
|
+
if (!NAMESPACE_SCHEMAS[this.#namespace].includes(doc.schemaName)) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Schema '${doc.schemaName}' is not allowed in namespace '${
|
|
145
|
+
this.#namespace
|
|
146
|
+
}'`
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
const block = encode(doc)
|
|
150
|
+
// The indexer batch can sometimes complete before the append below
|
|
151
|
+
// resolves, so in the batch function we await any pending appends. We can't
|
|
152
|
+
// know the versionId before the append, because docs can be written in the
|
|
153
|
+
// same tick, so we can't know their index before append resolves.
|
|
154
|
+
const deferredAppend = pDefer()
|
|
155
|
+
this.#pendingAppends.add(deferredAppend.promise)
|
|
156
|
+
const { length } = await this.#writerCore.append(block)
|
|
157
|
+
deferredAppend.resolve()
|
|
158
|
+
this.#pendingAppends.delete(deferredAppend.promise)
|
|
159
|
+
|
|
160
|
+
const index = length - 1
|
|
161
|
+
const coreDiscoveryKey = this.#writerCore.discoveryKey
|
|
162
|
+
if (!coreDiscoveryKey) {
|
|
163
|
+
throw new Error('Writer core is not ready')
|
|
164
|
+
}
|
|
165
|
+
const versionId = getVersionId({ coreDiscoveryKey, index })
|
|
166
|
+
/** @type {import('p-defer').DeferredPromise<void>} */
|
|
167
|
+
const deferred = pDefer()
|
|
168
|
+
this.#pendingIndex.set(versionId, deferred)
|
|
169
|
+
await deferred.promise
|
|
170
|
+
|
|
171
|
+
return /** @type {Extract<MapeoDoc, TDoc>} */ (
|
|
172
|
+
decode(block, { coreDiscoveryKey, index })
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
*
|
|
178
|
+
* @param {string} versionId
|
|
179
|
+
* @returns {Promise<MapeoDoc>}
|
|
180
|
+
*/
|
|
181
|
+
async read(versionId) {
|
|
182
|
+
const { coreDiscoveryKey, index } = parseVersionId(versionId)
|
|
183
|
+
const coreRecord = this.#coreManager.getCoreByDiscoveryKey(coreDiscoveryKey)
|
|
184
|
+
if (!coreRecord) throw new Error('Invalid versionId')
|
|
185
|
+
const block = await coreRecord.core.get(index, { wait: false })
|
|
186
|
+
if (!block) throw new Error('Not Found')
|
|
187
|
+
return decode(block, { coreDiscoveryKey, index })
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** @param {Buffer} buf */
|
|
191
|
+
async writeRaw(buf) {
|
|
192
|
+
const { length } = await this.#writerCore.append(buf)
|
|
193
|
+
const index = length - 1
|
|
194
|
+
const coreDiscoveryKey = this.#writerCore.discoveryKey
|
|
195
|
+
if (!coreDiscoveryKey) {
|
|
196
|
+
throw new Error('Writer core is not ready')
|
|
197
|
+
}
|
|
198
|
+
const versionId = getVersionId({ coreDiscoveryKey, index })
|
|
199
|
+
return versionId
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** @param {string} versionId */
|
|
203
|
+
async readRaw(versionId) {
|
|
204
|
+
const { coreDiscoveryKey, index } = parseVersionId(versionId)
|
|
205
|
+
const coreRecord = this.#coreManager.getCoreByDiscoveryKey(coreDiscoveryKey)
|
|
206
|
+
if (!coreRecord) throw new Error('core not found')
|
|
207
|
+
const block = await coreRecord.core.get(index, { wait: false })
|
|
208
|
+
if (!block) throw new Error('Not Found')
|
|
209
|
+
return block
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async close() {
|
|
213
|
+
await this.#coreIndexer.close()
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Unlink all index files. This should only be called after `close()` has resolved.
|
|
218
|
+
*/
|
|
219
|
+
async unlink() {
|
|
220
|
+
await this.#coreIndexer.unlink()
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
#handleIndexerIdle = () => {
|
|
224
|
+
for (const eventName of this.eventNames()) {
|
|
225
|
+
if (!(eventName in this.#pendingEmits)) continue
|
|
226
|
+
const docIds = this.#pendingEmits[eventName]
|
|
227
|
+
if (!docIds.size) continue
|
|
228
|
+
// @ts-ignore - I'm pretty sure TS is just not smart enough here, it's not me!
|
|
229
|
+
this.emit(eventName, docIds)
|
|
230
|
+
}
|
|
231
|
+
// Make sure we reset this, otherwise we'd get a memory leak of this growing without bounds.
|
|
232
|
+
this.#pendingEmits = createMap(this.schemas, () => new Set())
|
|
233
|
+
}
|
|
234
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# DataType
|
|
2
|
+
|
|
3
|
+
> Create, read, update, delete, and query data.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
The `DataType` class implements CRUD methods for a particular Mapeo data type. Reads and queries are done from the SQLite indexes / materialized views. Historical data read from a `versionId` is read from the associated `DataStore`.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
`DataType` is exposed directly on the client API for data types that can be read/written directly by the client. It serves as an abstraction for reading indexed data and creating/updating documents for all data types stored in Mapeo core namespaces.
|
|
12
|
+
|
|
13
|
+
A usage example:
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
const dataType = new DataType({
|
|
17
|
+
table: observationTable, // Drizzle table schema definition
|
|
18
|
+
db, // Drizzle instance
|
|
19
|
+
dataStore, // DataStore instance
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const observation = await observation.getByDocId(id)
|
|
23
|
+
const updated = await observation.update(observation.versionId, newValue)
|
|
24
|
+
const allObservations = await observation.getMany()
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## API docs
|
|
28
|
+
|
|
29
|
+
TODO!
|
|
30
|
+
|
|
31
|
+
## Tests
|
|
32
|
+
|
|
33
|
+
Tests for this module are in [tests/datatype.js](../../tests/datatype.js)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Typescript was incorrectly compiling declaration files for this, so the
|
|
2
|
+
// declaration for DataType is written manually below, and copied into the
|
|
3
|
+
// `dist` folder at build-time. The types are checked in `test-types/data-types.ts`
|
|
4
|
+
|
|
5
|
+
import { type MapeoDoc, type MapeoValue } from '@comapeo/schema'
|
|
6
|
+
import {
|
|
7
|
+
type MapeoDocMap,
|
|
8
|
+
type MapeoValueMap,
|
|
9
|
+
type CoreOwnershipWithSignaturesValue,
|
|
10
|
+
} from '../types.js'
|
|
11
|
+
import { type BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'
|
|
12
|
+
import { SQLiteSelectBase } from 'drizzle-orm/sqlite-core'
|
|
13
|
+
import { RunResult } from 'better-sqlite3'
|
|
14
|
+
import type Hypercore from 'hypercore'
|
|
15
|
+
import { TypedEmitter } from 'tiny-typed-emitter'
|
|
16
|
+
import TranslationApi from '../translation-api.js'
|
|
17
|
+
|
|
18
|
+
type MapeoDocTableName = `${MapeoDoc['schemaName']}Table`
|
|
19
|
+
type GetMapeoDocTables<T> = T[keyof T & MapeoDocTableName]
|
|
20
|
+
/** Union of Drizzle schema tables that correspond to MapeoDoc types (e.g. excluding backlink tables and other utility tables) */
|
|
21
|
+
type MapeoDocTables =
|
|
22
|
+
| GetMapeoDocTables<typeof import('../schema/project.js')>
|
|
23
|
+
| GetMapeoDocTables<typeof import('../schema/client.js')>
|
|
24
|
+
type MapeoDocTablesMap = {
|
|
25
|
+
[K in MapeoDocTables['_']['name']]: Extract<
|
|
26
|
+
MapeoDocTables,
|
|
27
|
+
{ _: { name: K } }
|
|
28
|
+
>
|
|
29
|
+
}
|
|
30
|
+
export interface DataTypeEvents<TDoc extends MapeoDoc> {
|
|
31
|
+
'updated-docs': (docs: TDoc[]) => void
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const kCreateWithDocId: unique symbol
|
|
35
|
+
export const kSelect: unique symbol
|
|
36
|
+
export const kTable: unique symbol
|
|
37
|
+
export const kDataStore: unique symbol
|
|
38
|
+
|
|
39
|
+
type OmitUnion<T, K extends keyof any> = T extends any ? Omit<T, K> : never
|
|
40
|
+
type ExcludeSchema<
|
|
41
|
+
T extends MapeoValue,
|
|
42
|
+
S extends MapeoValue['schemaName']
|
|
43
|
+
> = Exclude<T, { schemaName: S }>
|
|
44
|
+
|
|
45
|
+
export class DataType<
|
|
46
|
+
TDataStore extends import('../datastore/index.js').DataStore,
|
|
47
|
+
TTable extends MapeoDocTables,
|
|
48
|
+
TSchemaName extends TTable['_']['name'],
|
|
49
|
+
TDoc extends MapeoDocMap[TSchemaName],
|
|
50
|
+
TValue extends MapeoValueMap[TSchemaName]
|
|
51
|
+
> extends TypedEmitter<DataTypeEvents<TDoc>> {
|
|
52
|
+
constructor({
|
|
53
|
+
dataStore,
|
|
54
|
+
table,
|
|
55
|
+
db,
|
|
56
|
+
getTranslations,
|
|
57
|
+
}: {
|
|
58
|
+
table: TTable
|
|
59
|
+
dataStore: TDataStore
|
|
60
|
+
db: import('drizzle-orm/better-sqlite3').BetterSQLite3Database
|
|
61
|
+
getTranslations: TranslationApi['get']
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
get [kTable](): TTable
|
|
65
|
+
|
|
66
|
+
get [kDataStore](): TDataStore
|
|
67
|
+
|
|
68
|
+
get schemaName(): TSchemaName
|
|
69
|
+
|
|
70
|
+
get namespace(): TDataStore.namespace
|
|
71
|
+
|
|
72
|
+
[kCreateWithDocId](
|
|
73
|
+
docId: string,
|
|
74
|
+
value:
|
|
75
|
+
| ExcludeSchema<TValue, 'coreOwnership'>
|
|
76
|
+
| CoreOwnershipWithSignaturesValue
|
|
77
|
+
): Promise<TDoc & { forks: string[] }>
|
|
78
|
+
|
|
79
|
+
[kSelect](): Promise<SQLiteSelectBase<TTable, 'sync', RunResult>>
|
|
80
|
+
|
|
81
|
+
create<
|
|
82
|
+
T extends import('type-fest').Exact<
|
|
83
|
+
ExcludeSchema<TValue, 'coreOwnership'>,
|
|
84
|
+
T
|
|
85
|
+
>
|
|
86
|
+
>(value: T): Promise<TDoc & { forks: string[] }>
|
|
87
|
+
|
|
88
|
+
getByDocId(
|
|
89
|
+
docId: string,
|
|
90
|
+
opts?: { lang?: string }
|
|
91
|
+
): Promise<TDoc & { forks: string[] }>
|
|
92
|
+
|
|
93
|
+
getByVersionId(versionId: string, opts?: { lang?: string }): Promise<TDoc>
|
|
94
|
+
|
|
95
|
+
getMany(opts?: {
|
|
96
|
+
includeDeleted?: boolean
|
|
97
|
+
lang?: string
|
|
98
|
+
}): Promise<Array<TDoc & { forks: string[] }>>
|
|
99
|
+
|
|
100
|
+
update<
|
|
101
|
+
T extends import('type-fest').Exact<
|
|
102
|
+
ExcludeSchema<TValue, 'coreOwnership'>,
|
|
103
|
+
T
|
|
104
|
+
>
|
|
105
|
+
>(versionId: string | string[], value: T): Promise<TDoc & { forks: string[] }>
|
|
106
|
+
|
|
107
|
+
delete(docId: string): Promise<TDoc & { forks: string[] }>
|
|
108
|
+
}
|