@dittolive/ditto 4.5.1-experimental.aarch64-linux.1.aarch64 → 4.5.2-rc.2

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 (152) hide show
  1. package/DittoReactNative.podspec +25 -0
  2. package/README.md +2 -2
  3. package/node/ditto.cjs.js +1 -1
  4. package/node/ditto.cjs.js.map +1 -0
  5. package/node/ditto.cjs.pretty.js +9655 -0
  6. package/node/ditto.cjs.pretty.js.map +1 -0
  7. package/node/ditto.darwin-arm64.node +0 -0
  8. package/node/ditto.darwin-x64.node +0 -0
  9. package/node/{ditto.linux-arm64.node → ditto.linux-x64.node} +0 -0
  10. package/node/transports.darwin-arm64.node +0 -0
  11. package/node/transports.darwin-x64.node +0 -0
  12. package/package.json +2 -1
  13. package/react-native/android/CMakeLists.txt +37 -0
  14. package/react-native/android/build.gradle +186 -0
  15. package/react-native/android/cpp-adapter.cpp +254 -0
  16. package/react-native/android/gradle.properties +5 -0
  17. package/react-native/android/src/main/AndroidManifest.xml +4 -0
  18. package/react-native/android/src/main/java/com/dittolive/rnsdk/DittoRNSDKModule.java +85 -0
  19. package/react-native/android/src/main/java/com/dittolive/rnsdk/DittoRNSDKPackage.java +28 -0
  20. package/react-native/cpp/include/Arc.hpp +141 -0
  21. package/react-native/cpp/include/Attachment.h +16 -0
  22. package/react-native/cpp/include/Authentication.h +23 -0
  23. package/react-native/cpp/include/Collection.h +13 -0
  24. package/react-native/cpp/include/DQL.h +21 -0
  25. package/react-native/cpp/include/Document.h +17 -0
  26. package/react-native/cpp/include/Identity.h +17 -0
  27. package/react-native/cpp/include/Lifecycle.h +17 -0
  28. package/react-native/cpp/include/LiveQuery.h +17 -0
  29. package/react-native/cpp/include/Logger.h +22 -0
  30. package/react-native/cpp/include/Misc.h +27 -0
  31. package/react-native/cpp/include/Presence.h +14 -0
  32. package/react-native/cpp/include/SmallPeerInfo.h +19 -0
  33. package/react-native/cpp/include/Transports.h +25 -0
  34. package/react-native/cpp/include/TypedArray.hpp +167 -0
  35. package/react-native/cpp/include/Utils.h +61 -0
  36. package/react-native/cpp/include/main.h +10 -0
  37. package/react-native/cpp/src/Attachment.cpp +86 -0
  38. package/react-native/cpp/src/Authentication.cpp +227 -0
  39. package/react-native/cpp/src/Collection.cpp +54 -0
  40. package/react-native/cpp/src/DQL.cpp +256 -0
  41. package/react-native/cpp/src/Document.cpp +146 -0
  42. package/react-native/cpp/src/Identity.cpp +123 -0
  43. package/react-native/cpp/src/Lifecycle.cpp +110 -0
  44. package/react-native/cpp/src/LiveQuery.cpp +63 -0
  45. package/react-native/cpp/src/Logger.cpp +200 -0
  46. package/react-native/cpp/src/Misc.cpp +283 -0
  47. package/react-native/cpp/src/Presence.cpp +79 -0
  48. package/react-native/cpp/src/SmallPeerInfo.cpp +142 -0
  49. package/react-native/cpp/src/Transports.cpp +270 -0
  50. package/react-native/cpp/src/TypedArray.cpp +303 -0
  51. package/react-native/cpp/src/Utils.cpp +138 -0
  52. package/react-native/cpp/src/main.cpp +152 -0
  53. package/react-native/dittoffi/dittoffi.h +4700 -0
  54. package/react-native/dittoffi/ifaddrs.cpp +385 -0
  55. package/react-native/dittoffi/ifaddrs.h +206 -0
  56. package/react-native/ios/DittoRNSDK.h +7 -0
  57. package/react-native/ios/DittoRNSDK.mm +107 -0
  58. package/react-native/ios/YeetJSIUtils.h +60 -0
  59. package/react-native/ios/YeetJSIUtils.mm +196 -0
  60. package/react-native/lib/commonjs/ditto.rn.js +92 -0
  61. package/react-native/lib/commonjs/ditto.rn.js.map +1 -0
  62. package/react-native/lib/commonjs/index.js +61 -0
  63. package/react-native/lib/commonjs/index.js.map +1 -0
  64. package/react-native/lib/module/ditto.rn.js +88 -0
  65. package/react-native/lib/module/ditto.rn.js.map +1 -0
  66. package/react-native/lib/module/index.js +27 -0
  67. package/react-native/lib/module/index.js.map +1 -0
  68. package/react-native/lib/typescript/ditto.rn.d.ts +15 -0
  69. package/react-native/lib/typescript/ditto.rn.d.ts.map +1 -0
  70. package/react-native/lib/typescript/index.d.ts +1 -0
  71. package/react-native/lib/typescript/index.d.ts.map +1 -0
  72. package/react-native/src/ditto.rn.ts +91 -0
  73. package/react-native/src/environment/environment.fallback.ts +4 -0
  74. package/react-native/src/index.ts +26 -0
  75. package/react-native/src/sources/@cbor-redux.ts +2 -0
  76. package/react-native/src/sources/@ditto.core.ts +1 -0
  77. package/react-native/src/sources/@environment.ts +1 -0
  78. package/react-native/src/sources/attachment-fetch-event.ts +54 -0
  79. package/react-native/src/sources/attachment-fetcher-manager.ts +144 -0
  80. package/react-native/src/sources/attachment-fetcher.ts +134 -0
  81. package/react-native/src/sources/attachment-token.ts +48 -0
  82. package/react-native/src/sources/attachment.ts +74 -0
  83. package/react-native/src/sources/augment.ts +101 -0
  84. package/react-native/src/sources/authenticator.ts +314 -0
  85. package/react-native/src/sources/base-pending-cursor-operation.ts +239 -0
  86. package/react-native/src/sources/base-pending-id-specific-operation.ts +109 -0
  87. package/react-native/src/sources/bridge.ts +553 -0
  88. package/react-native/src/sources/build-time-constants.ts +8 -0
  89. package/react-native/src/sources/cbor.ts +35 -0
  90. package/react-native/src/sources/collection-interface.ts +67 -0
  91. package/react-native/src/sources/collection.ts +212 -0
  92. package/react-native/src/sources/collections-event.ts +99 -0
  93. package/react-native/src/sources/counter.ts +82 -0
  94. package/react-native/src/sources/ditto.ts +979 -0
  95. package/react-native/src/sources/document-id.ts +159 -0
  96. package/react-native/src/sources/document-path.ts +306 -0
  97. package/react-native/src/sources/document.ts +193 -0
  98. package/react-native/src/sources/epilogue.ts +30 -0
  99. package/react-native/src/sources/error-codes.ts +52 -0
  100. package/react-native/src/sources/error.ts +208 -0
  101. package/react-native/src/sources/essentials.ts +53 -0
  102. package/react-native/src/sources/ffi-error.ts +122 -0
  103. package/react-native/src/sources/ffi.ts +2012 -0
  104. package/react-native/src/sources/identity.ts +163 -0
  105. package/react-native/src/sources/init.ts +71 -0
  106. package/react-native/src/sources/internal.ts +109 -0
  107. package/react-native/src/sources/keep-alive.ts +73 -0
  108. package/react-native/src/sources/key-path.ts +198 -0
  109. package/react-native/src/sources/live-query-event.ts +208 -0
  110. package/react-native/src/sources/live-query-manager.ts +102 -0
  111. package/react-native/src/sources/live-query.ts +166 -0
  112. package/react-native/src/sources/logger.ts +196 -0
  113. package/react-native/src/sources/main.ts +60 -0
  114. package/react-native/src/sources/observer-manager.ts +178 -0
  115. package/react-native/src/sources/observer.ts +79 -0
  116. package/react-native/src/sources/pending-collections-operation.ts +232 -0
  117. package/react-native/src/sources/pending-cursor-operation.ts +218 -0
  118. package/react-native/src/sources/pending-id-specific-operation.ts +218 -0
  119. package/react-native/src/sources/presence-manager.ts +161 -0
  120. package/react-native/src/sources/presence.ts +233 -0
  121. package/react-native/src/sources/query-result-item.ts +116 -0
  122. package/react-native/src/sources/query-result.ts +55 -0
  123. package/react-native/src/sources/register.ts +95 -0
  124. package/react-native/src/sources/small-peer-info.ts +177 -0
  125. package/react-native/src/sources/static-tcp-client.ts +6 -0
  126. package/react-native/src/sources/store-observer.ts +177 -0
  127. package/react-native/src/sources/store.ts +385 -0
  128. package/react-native/src/sources/subscription-manager.ts +99 -0
  129. package/react-native/src/sources/subscription.ts +89 -0
  130. package/react-native/src/sources/sync-subscription.ts +90 -0
  131. package/react-native/src/sources/sync.ts +559 -0
  132. package/react-native/src/sources/test-helpers.ts +24 -0
  133. package/react-native/src/sources/transport-conditions-manager.ts +104 -0
  134. package/react-native/src/sources/transport-config.ts +430 -0
  135. package/react-native/src/sources/update-result.ts +66 -0
  136. package/react-native/src/sources/update-results-map.ts +57 -0
  137. package/react-native/src/sources/websocket-client.ts +7 -0
  138. package/react-native/src/sources/write-transaction-collection.ts +122 -0
  139. package/react-native/src/sources/write-transaction-pending-cursor-operation.ts +101 -0
  140. package/react-native/src/sources/write-transaction-pending-id-specific-operation.ts +74 -0
  141. package/react-native/src/sources/write-transaction.ts +121 -0
  142. package/react-native.config.js +9 -0
  143. package/types/ditto.d.ts.map +1 -0
  144. package/web/ditto.es6.js +1 -1
  145. package/web/ditto.es6.js.map +1 -0
  146. package/web/ditto.es6.pretty.js +12600 -0
  147. package/web/ditto.es6.pretty.js.map +1 -0
  148. package/web/ditto.umd.js +1 -1
  149. package/web/ditto.umd.js.map +1 -0
  150. package/web/ditto.umd.pretty.js +12669 -0
  151. package/web/ditto.umd.pretty.js.map +1 -0
  152. package/web/ditto.wasm +0 -0
@@ -0,0 +1,159 @@
1
+ //
2
+ // Copyright © 2021 DittoLive Incorporated. All rights reserved.
3
+ //
4
+
5
+ import * as FFI from './ffi'
6
+ import { CBOR } from './cbor'
7
+
8
+ /** Represents a unique identifier for a {@link Document}. */
9
+ export type DocumentIDValue = any
10
+
11
+ /** Represents a unique identifier for a {@link Document}. */
12
+ export class DocumentID {
13
+ /**
14
+ * Returns the value of the receiver, lazily decoded from its CBOR
15
+ * representation if needed.
16
+ */
17
+ get value(): any {
18
+ let value = this['@ditto.value']
19
+ if (typeof value === 'undefined') {
20
+ value = CBOR.decode(this['@ditto.cbor'])
21
+ this['@ditto.value'] = value
22
+ }
23
+ return value
24
+ }
25
+
26
+ /**
27
+ * Returns `false` if validation has been skipped at construction time,
28
+ * otherwise returns `true`. This is mostly for internal use only, you
29
+ * shouldn't need this in client code.
30
+ */
31
+ readonly isValidated: boolean
32
+
33
+ /**
34
+ * Creates a new `DocumentID`.
35
+ *
36
+ * A document ID can be created from any of the following:
37
+ *
38
+ * - `string`
39
+ * - `number` (integer)
40
+ * - `boolean`
41
+ * - `null`
42
+ * - raw data in the form of a JS [Typed Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays)
43
+ * - `Array` (containing any of the items in this list)
44
+ * - Map (a raw JS `object`, where the keys must be strings and the values
45
+ * can be made up of any of the items in this list)
46
+ *
47
+ * Note that you cannot use floats or other custom types to create a document
48
+ * ID.
49
+ *
50
+ * Document IDs are also limited in size, based on their serialized
51
+ * representation, to 256 bytes. You will receive an error if you try to
52
+ * create a document ID that exceeds the size limit.
53
+ *
54
+ * @param value The value that represents the document identifier.
55
+ * @param skipCBOREncoding If `true, skips CBOR encoding and assumes
56
+ * the passed in `value` is already CBOR encoded. You shouldn't need to ever
57
+ * pass this parameter, it's only used internally for certain edge cases.
58
+ * @param skipValidation If `true, skips validation of the passed in value or
59
+ * CBOR. You shouldn't need to ever pass this parameter, it's only used
60
+ * internally for certain edge cases.
61
+ */
62
+ constructor(value: any, skipCBOREncoding: boolean = false, skipValidation: boolean = false) {
63
+ const cbor = skipCBOREncoding ? value : CBOR.encode(value)
64
+ const validatedCBOR = skipValidation ? cbor : validateDocumentIDCBOR(cbor)
65
+
66
+ if (!validatedCBOR) {
67
+ throw new Error(`Can't create DocumentID, passed in value is not valid: ${value}`)
68
+ }
69
+
70
+ this.isValidated = !skipValidation
71
+ this['@ditto.cbor'] = validatedCBOR
72
+ }
73
+
74
+ /**
75
+ * Returns `true` if passed in `documentID` is equal to the receiver,
76
+ * otherwise returns `false`.
77
+ */
78
+ equals(documentID: DocumentID): boolean {
79
+ const left = this['@ditto.cbor']
80
+ const right = documentID['@ditto.cbor']
81
+
82
+ if (left === right) {
83
+ return true
84
+ }
85
+
86
+ if (!(left instanceof Uint8Array)) {
87
+ return false
88
+ }
89
+
90
+ if (!(right instanceof Uint8Array)) {
91
+ return false
92
+ }
93
+
94
+ if (left.length !== right.length) {
95
+ return false
96
+ }
97
+
98
+ for (let i = 0; i < left.length; i += 1) {
99
+ if (left[i] !== right[i]) return false
100
+ }
101
+
102
+ return true
103
+ }
104
+
105
+ /**
106
+ * Returns a string representation of the receiver.
107
+ *
108
+ * If you need a string representation to be used directly in a query,
109
+ * please use `toQueryCompatibleString()` instead.
110
+ */
111
+ toString(): string {
112
+ return FFI.documentIDQueryCompatible(this['@ditto.cbor'], 'WithoutQuotes')
113
+ }
114
+
115
+ /**
116
+ * Returns a byte representation of the document ID value as base64 string.
117
+ */
118
+ toBase64String(): string {
119
+ const bytes = this['@ditto.cbor']
120
+ return btoa(String.fromCharCode.apply(null, bytes))
121
+ }
122
+
123
+ /**
124
+ * Returns a query compatible string representation of the receiver.
125
+ *
126
+ * The returned string can be used directly in queries that you use with
127
+ * other Ditto functions. For example you could create a query that was like
128
+ * this:
129
+ *
130
+ * ``` TypeScript
131
+ * collection.find(`_id == ${documentID.toQueryCompatibleString()}`)
132
+ * ```
133
+ */
134
+ toQueryCompatibleString(): string {
135
+ return FFI.documentIDQueryCompatible(this['@ditto.cbor'], 'WithQuotes')
136
+ }
137
+
138
+ /** @internal */
139
+ toCBOR(): Uint8Array {
140
+ return this['@ditto.cbor']
141
+ }
142
+ }
143
+
144
+ // -----------------------------------------------------------------------------
145
+
146
+ /** @internal */
147
+ export function validateDocumentIDValue(id: DocumentIDValue): DocumentIDValue {
148
+ if (typeof id === 'undefined') {
149
+ throw new Error(`Invalid document ID: ${id}`)
150
+ }
151
+
152
+ return id
153
+ }
154
+
155
+ /** @internal */
156
+ export function validateDocumentIDCBOR(idCBOR: Uint8Array): Uint8Array {
157
+ const validatedIDCBOROrNull = FFI.validateDocumentID(idCBOR)
158
+ return validatedIDCBOROrNull ?? idCBOR
159
+ }
@@ -0,0 +1,306 @@
1
+ //
2
+ // Copyright © 2021 DittoLive Incorporated. All rights reserved.
3
+ //
4
+
5
+ import * as FFI from './ffi'
6
+ import { CBOR } from './cbor'
7
+ import { Bridge } from './bridge'
8
+ import { Counter, MutableCounter } from './counter'
9
+ import { Register, MutableRegister } from './register'
10
+ import { AttachmentToken } from './attachment-token'
11
+ import { UpdateResult } from './update-result'
12
+ import { desugarJSObject, augmentJSONValue } from './augment'
13
+ import { validateNumber } from './internal'
14
+ import { KeyPath } from './key-path'
15
+ import type { Document, MutableDocument } from './document'
16
+
17
+ // -----------------------------------------------------------------------------
18
+
19
+ /**
20
+ * Represents a portion of the document at a specific key-path.
21
+ *
22
+ * Provides an interface to specify a path to a key in a document that you can
23
+ * then call a function on to get the value at the specified key as a specific
24
+ * type. You don't create a `DocumentPath` directly but obtain one via the
25
+ * {@link Document.path | path} property or the {@link Document.at | at()}
26
+ * method of {@link Document}.
27
+ */
28
+ export class DocumentPath {
29
+ /** The document this path belongs to. */
30
+ readonly document: Document
31
+
32
+ /** The full document path so far. */
33
+ readonly path: string
34
+
35
+ /**
36
+ * Returns a new document path instance with the passed in key-path or
37
+ * index appended.
38
+ *
39
+ * A key-path can be a single property name or multiple property names
40
+ * separated by a dot. Indexes can also be specified as part of the key
41
+ * path using the square bracket syntax. The empty string returns a document
42
+ * path representing the same portion of the document as the receiver. If a
43
+ * key-path starts with a property name and is prefixed by a dot, the dot is
44
+ * ignored.
45
+ *
46
+ * Examples:
47
+ *
48
+ * - `documentPath.at('mileage')`
49
+ * - `documentPath.at('driver.name')`
50
+ * - `documentPath.at('passengers[2]')`
51
+ * - `documentPath.at('passengers[2].belongings[1].kind')`
52
+ * - `documentPath.at('.mileage')`
53
+ */
54
+ at(keyPathOrIndex: string | number): DocumentPath {
55
+ if (typeof keyPathOrIndex === 'string') {
56
+ const keyPath: string = keyPathOrIndex
57
+ const validatedKeyPath = KeyPath.validate(keyPath)
58
+ // this.path can be an empty string.
59
+ const absoluteKeyPath = KeyPath.withoutLeadingDot(`${this.path}${validatedKeyPath}`)
60
+ return new DocumentPath(this.document, absoluteKeyPath, false)
61
+ }
62
+
63
+ if (typeof keyPathOrIndex === 'number') {
64
+ const index: number = keyPathOrIndex
65
+ const validatedIndex = validateNumber(index, { integer: true, min: 0, errorMessagePrefix: 'DocumentPath.at() validation failed index:' })
66
+ return new DocumentPath(this.document, `${this.path}[${validatedIndex.toString()}]`, false)
67
+ }
68
+
69
+ throw new Error(`Can't return document path at key-path or index, string or number expected but got ${typeof keyPathOrIndex}: ${keyPathOrIndex}`)
70
+ }
71
+
72
+ /**
73
+ * Traverses the document with the key-path represented by the receiver and
74
+ * returns the corresponding object or value.
75
+ */
76
+ get value(): any | undefined {
77
+ return this.underlyingValueForPathType('Any')
78
+ }
79
+
80
+ /**
81
+ * Returns the value at the previously specified key in the document as a
82
+ * {@link Counter} if possible, otherwise returns `null`.
83
+ */
84
+ get counter(): Counter | null {
85
+ const underlyingValue = this.underlyingValueForPathType('Counter')
86
+ return typeof underlyingValue !== 'undefined' ? Counter['@ditto.create'](null, this.path, underlyingValue) : null
87
+ }
88
+
89
+ /**
90
+ * Returns the value at the previously specified key in the document as a
91
+ * {@link Register} if possible, otherwise returns `null`.
92
+ */
93
+ get register(): Register | null {
94
+ const underlyingValue = this.underlyingValueForPathType('Register')
95
+ return typeof underlyingValue !== 'undefined' ? Register['@ditto.create'](null, this.path, underlyingValue) : null
96
+ }
97
+
98
+ /**
99
+ * Returns the value at the previously specified key in the document as an
100
+ * {@link AttachmentToken} if possible, otherwise returns `null`.
101
+ */
102
+ get attachmentToken(): AttachmentToken | null {
103
+ const underlyingValue = this.underlyingValueForPathType('Attachment')
104
+ return typeof underlyingValue !== 'undefined' ? new AttachmentToken(underlyingValue) : null
105
+ }
106
+
107
+ /** @internal */
108
+ constructor(document: Document, path: string, validate: boolean) {
109
+ this.document = document
110
+ this.path = validate ? KeyPath.validate(path, { isInitial: true }) : path
111
+ }
112
+
113
+ /** @internal */
114
+ underlyingValueForPathType(pathType: FFI.PathAccessorType): any | undefined {
115
+ const path = this.path
116
+ const document = this.document
117
+ const documentHandle = Bridge.document.handleFor(document)
118
+ const cborPathResult = FFI.documentGetCBORWithPathType(documentHandle.deref(), path, pathType)
119
+ return cborPathResult.cbor !== null ? CBOR.decode(cborPathResult.cbor) : undefined
120
+ }
121
+ }
122
+
123
+ // -----------------------------------------------------------------------------
124
+
125
+ /**
126
+ * Mutable version of {@link DocumentPath} allowing you to mutate a document at
127
+ * a specific key-path. You don't create a `MutableDocumentPath` directly but
128
+ * obtain one via the {@link MutableDocument.path | path} property or the
129
+ * {@link MutableDocument.at | at()} method of {@link MutableDocument}.
130
+ */
131
+ export class MutableDocumentPath {
132
+ /** The (mutable) document this path belongs to. */
133
+ readonly mutableDocument: MutableDocument
134
+
135
+ /** The full document path so far. */
136
+ readonly path: string
137
+
138
+ /**
139
+ * Returns a new mutable document path instance with the passed in key-path or
140
+ * index appended.
141
+ *
142
+ * A key-path can be a single property name or multiple property names
143
+ * separated by a dot. Indexes can also be specified as part of the key
144
+ * path using square brackets syntax. The empty string returns a document path
145
+ * representing the same portion of the document as the receiver. If a key
146
+ * path starts with a property name and is prefixed by a dot, the dot is
147
+ * ignored.
148
+ *
149
+ * Examples:
150
+ *
151
+ * - `mutableDocumentPath.at('mileage')`
152
+ * - `mutableDocumentPath.at('driver.name')`
153
+ * - `mutableDocumentPath.at('passengers[2]')`
154
+ * - `mutableDocumentPath.at('passengers[2].belongings[1].kind')`
155
+ * - `mutableDocumentPath.at('.mileage')`
156
+ */
157
+ at(keyPathOrIndex: string | number): MutableDocumentPath {
158
+ if (typeof keyPathOrIndex === 'string') {
159
+ const keyPath: string = keyPathOrIndex
160
+ const validatedKeyPath = KeyPath.validate(keyPath)
161
+ // this.path can be an empty string.
162
+ const absoluteKeyPath = KeyPath.withoutLeadingDot(`${this.path}${validatedKeyPath}`)
163
+ return new MutableDocumentPath(this.mutableDocument, absoluteKeyPath, false)
164
+ }
165
+
166
+ if (typeof keyPathOrIndex === 'number') {
167
+ const index: number = keyPathOrIndex
168
+ const validatedIndex = validateNumber(index, { integer: true, min: 0, errorMessagePrefix: 'MutableDocumentPath.at() validation failed index:' })
169
+ return new MutableDocumentPath(this.mutableDocument, `${this.path}[${validatedIndex.toString()}]`, false)
170
+ }
171
+
172
+ throw new Error(`Can't return mutable document path at key-path or index, string or number expected but got ${typeof keyPathOrIndex}: ${keyPathOrIndex}`)
173
+ }
174
+
175
+ /**
176
+ * Traverses the document with the key-path represented by the receiver and
177
+ * returns the corresponding object or value.
178
+ */
179
+ get value(): any | undefined {
180
+ return this.underlyingValueForPathType('Any')
181
+ }
182
+
183
+ /**
184
+ * Returns the value at the previously specified key in the document as a
185
+ * {@link MutableCounter} if possible, otherwise returns `null`.
186
+ */
187
+ get counter(): MutableCounter | null {
188
+ const underlyingValue = this.underlyingValueForPathType('Counter')
189
+ return typeof underlyingValue !== 'undefined' ? Counter['@ditto.create'](this.mutableDocument, this.path, underlyingValue) : null
190
+ }
191
+
192
+ /**
193
+ * Returns the value at the previously specified key in the document as a
194
+ * {@link MutableRegister} if possible, otherwise returns `null`.
195
+ */
196
+ get register(): MutableRegister | null {
197
+ const underlyingValue = this.underlyingValueForPathType('Register')
198
+ return typeof underlyingValue !== 'undefined' ? Register['@ditto.create'](this.mutableDocument, this.path, underlyingValue) : null
199
+ }
200
+
201
+ /**
202
+ * Returns the value at the previously specified key in the document as a
203
+ * {@link AttachmentToken} if possible, otherwise returns `null`.
204
+ */
205
+ get attachmentToken(): AttachmentToken | null {
206
+ const underlyingValue = this.underlyingValueForPathType('Attachment')
207
+ return typeof underlyingValue !== 'undefined' ? new AttachmentToken(underlyingValue) : null
208
+ }
209
+
210
+ /**
211
+ * Sets a value at the document's key-path defined by the receiver.
212
+ *
213
+ * @param isDefault Represents whether or not the value should be set as a
214
+ * default value. Set this to `true` if you want to set a default value that
215
+ * you expect to be overwritten by other devices in the network. The default
216
+ * value is `false`.
217
+ */
218
+ set(value: any, isDefault?: boolean) {
219
+ return this['@ditto.set'](value, isDefault)
220
+ }
221
+
222
+ /**
223
+ * Removes a value at the document's key-path defined by the receiver.
224
+ */
225
+ remove() {
226
+ return this['@ditto.remove']()
227
+ }
228
+
229
+ /** @internal */
230
+ constructor(mutableDocument: MutableDocument, path: string, validate: boolean) {
231
+ this.mutableDocument = mutableDocument
232
+ this.path = validate ? KeyPath.validate(path, { isInitial: true }) : path
233
+ }
234
+
235
+ /** @internal */
236
+ underlyingValueForPathType(pathType: FFI.PathAccessorType): any | null {
237
+ const path = this.path
238
+ const document = this.mutableDocument
239
+ const documentHandle = Bridge.mutableDocument.handleFor(document)
240
+ const cborPathResult = FFI.documentGetCBORWithPathType(documentHandle.deref(), path, pathType)
241
+ return cborPathResult.cbor !== null ? CBOR.decode(cborPathResult.cbor) : undefined
242
+ }
243
+
244
+ /** @internal */
245
+ '@ditto.increment'(amount: number) {
246
+
247
+ const documentHandle = Bridge.mutableDocument.handleFor(this.mutableDocument)
248
+ FFI.documentIncrementCounter(documentHandle.deref(), this.path, amount)
249
+
250
+ const updateResult = UpdateResult.incremented(this.mutableDocument.id, this.path, amount)
251
+ this.recordUpdateResult(updateResult)
252
+ }
253
+
254
+ /** @internal */
255
+ '@ditto.set'(value: any, isDefault?: boolean) {
256
+ const documentHandle = Bridge.mutableDocument.handleFor(this.mutableDocument)
257
+ const valueJSON = desugarJSObject(value, false)
258
+ const valueCBOR = CBOR.encode(valueJSON)
259
+
260
+ if (isDefault) {
261
+ FFI.documentSetCBORWithTimestamp(documentHandle.deref(), this.path, valueCBOR, 0)
262
+ } else {
263
+ FFI.documentSetCBOR(documentHandle.deref(), this.path, valueCBOR)
264
+ }
265
+
266
+ const valueJSONCopy = CBOR.decode(valueCBOR)
267
+ const valueCopy = augmentJSONValue(valueJSONCopy, this.mutableDocument, this.path)
268
+
269
+ const updateResult = UpdateResult.set(this.mutableDocument.id, this.path, valueCopy)
270
+ this.recordUpdateResult(updateResult)
271
+ }
272
+
273
+ /** @internal */
274
+ '@ditto.remove'() {
275
+ const documentHandle = Bridge.mutableDocument.handleFor(this.mutableDocument)
276
+ FFI.documentRemove(documentHandle.deref(), this.path)
277
+
278
+ this.updateInMemory((container, lastPathComponent) => {
279
+ if (Array.isArray(container) && typeof lastPathComponent === 'number') {
280
+ container.splice(lastPathComponent, 1)
281
+ } else {
282
+ delete container[lastPathComponent]
283
+ }
284
+ })
285
+
286
+ const updateResult = UpdateResult.removed(this.mutableDocument.id, this.path)
287
+ this.recordUpdateResult(updateResult)
288
+ }
289
+
290
+ /** @private */
291
+ private updateInMemory(block: (container: any, lastPathComponent: any) => void) {
292
+ const mutableDocumentValue = this.mutableDocument.value
293
+ const evaluationResult = KeyPath.evaluate(this.path, mutableDocumentValue, { stopAtLastContainer: true })
294
+ block(evaluationResult.value, evaluationResult.nextPathComponent)
295
+ }
296
+
297
+ /** @private */
298
+ private recordUpdateResult(updateResult: UpdateResult) {
299
+ // OPTIMIZE: not sure how much of a performance hit this
300
+ // clone-modify-freeze dance implies. Investigate and fix.
301
+ const updateResults = this.mutableDocument['@ditto.updateResults'].slice()
302
+ updateResults.push(updateResult)
303
+ Object.freeze(updateResults)
304
+ this.mutableDocument['@ditto.updateResults' as any] = updateResults
305
+ }
306
+ }
@@ -0,0 +1,193 @@
1
+ //
2
+ // Copyright © 2021 DittoLive Incorporated. All rights reserved.
3
+ //
4
+
5
+ import * as FFI from './ffi'
6
+ import { Bridge } from './bridge'
7
+ import { DocumentID } from './document-id'
8
+ import { validateDocumentIDCBOR } from './document-id'
9
+ import { DocumentPath, MutableDocumentPath } from './document-path'
10
+
11
+ import type { UpdateResult } from './update-result'
12
+
13
+ // -----------------------------------------------------------------------------
14
+
15
+ /**
16
+ * A document value is a JavaScript object containing values for keys that
17
+ * can be serialized via CBOR.
18
+ */
19
+ export type DocumentValue = Record<string, any>
20
+
21
+ // -----------------------------------------------------------------------------
22
+
23
+ /** A document belonging to a {@link Collection} with an inner value. */
24
+ export class Document {
25
+ /**
26
+ * Returns a hash that represents the passed in document(s).
27
+ */
28
+ static hash(documentOrMany: Document | Document[]): BigInt {
29
+ const documents = documentsFrom(documentOrMany)
30
+ const documentHandles = documents.map((doc) => Bridge.document.handleFor(doc))
31
+ return FFI.documentsHash(documentHandles.map((handle) => handle.deref()))
32
+ }
33
+
34
+ /**
35
+ * Returns a pattern of words that together create a mnemonic, which
36
+ * represents the passed in document(s).
37
+ */
38
+ static hashMnemonic(documentOrMany: Document | Document[]): string {
39
+ const documents = documentsFrom(documentOrMany)
40
+ const documentHandles = Bridge.document.handlesFor(documents)
41
+ return FFI.documentsHashMnemonic(documentHandles.deref())
42
+ }
43
+
44
+ /**
45
+ * Returns the document ID.
46
+ */
47
+ get id(): DocumentID {
48
+ let id = this['@ditto.id']
49
+ if (typeof id === 'undefined') {
50
+ const documentHandle = Bridge.document.handleFor(this)
51
+ const documentIDCBOR = FFI.documentID(documentHandle.deref())
52
+ id = new DocumentID(documentIDCBOR, true)
53
+ this['@ditto.id'] = id
54
+ }
55
+ return id
56
+ }
57
+
58
+ /**
59
+ * Returns the document path at the root of the document.
60
+ */
61
+ get path(): DocumentPath {
62
+ return new DocumentPath(this, '', false)
63
+ }
64
+
65
+ /**
66
+ * Convenience property, same as calling `path.value`. The value is cached on
67
+ * first access and returned on subsequent calls without calling `path.value`
68
+ * again.
69
+ */
70
+ get value(): DocumentValue {
71
+ let value = this['@ditto.value']
72
+ if (typeof value === 'undefined') {
73
+ value = this.path.value
74
+ this['@ditto.value'] = value
75
+ }
76
+ return value
77
+ }
78
+
79
+ /**
80
+ * Convenience method, same as calling `path.at()`.
81
+ */
82
+ at(keyPathOrIndex: string | number): DocumentPath {
83
+ return this.path.at(keyPathOrIndex)
84
+ }
85
+
86
+ /** @internal */
87
+ constructor() {}
88
+
89
+ // TEMPORARY: helpers to deal with non-canonical IDs.
90
+
91
+ /** @internal */
92
+ static idCBOR(document: Document): Uint8Array {
93
+ const documentHandle = Bridge.document.handleFor(document)
94
+ return FFI.documentID(documentHandle.deref())
95
+ }
96
+
97
+ /** @internal */
98
+ static canonicalizedIDCBOR(idCBOR: Uint8Array): Uint8Array {
99
+ return validateDocumentIDCBOR(idCBOR)
100
+ }
101
+
102
+ /** @internal */
103
+ static isIDCBORCanonical(idCBOR: Uint8Array): boolean {
104
+ const canonicalIDCBOR = this.canonicalizedIDCBOR(idCBOR)
105
+ return idCBOR === canonicalIDCBOR
106
+ }
107
+ }
108
+
109
+ // -----------------------------------------------------------------------------
110
+
111
+ /**
112
+ * A representation of a {@link Document} that can be mutated via
113
+ * {@link MutableDocumentPath}. You don't create or interact with
114
+ * a `MutableDocument` directly but rather through our proxy-based
115
+ * subscripting API exposed within the `update()` methods of
116
+ * {@link PendingCursorOperation.update | PendingCursorOperation} and
117
+ * {@link PendingIDSpecificOperation.update | PendingIDSpecificOperation}.
118
+ */
119
+ export class MutableDocument {
120
+ /**
121
+ * Returns the ID of the document.
122
+ */
123
+ get id(): DocumentID {
124
+ let id = this['@ditto.id']
125
+ if (typeof id === 'undefined') {
126
+ const documentHandle = Bridge.mutableDocument.handleFor(this)
127
+ const documentIDCBOR = FFI.documentID(documentHandle.deref())
128
+ id = new DocumentID(documentIDCBOR, true)
129
+ this['@ditto.id'] = id
130
+ }
131
+ return id
132
+ }
133
+
134
+ /**
135
+ * Returns the document path at the root of the document.
136
+ */
137
+ get path(): MutableDocumentPath {
138
+ return new MutableDocumentPath(this, '', false)
139
+ }
140
+
141
+ /**
142
+ * Convenience property, same as `path.value`.
143
+ */
144
+ get value(): DocumentValue {
145
+ return this.path.value
146
+ }
147
+
148
+ /**
149
+ * Convenience method, same as calling `path.at()`.
150
+ */
151
+ at(keyPathOrIndex: string | number): MutableDocumentPath {
152
+ return this.path.at(keyPathOrIndex)
153
+ }
154
+
155
+ /** @internal */
156
+ constructor() {}
157
+
158
+ /** @internal */
159
+ readonly '@ditto.updateResults': UpdateResult[] = []
160
+
161
+ // TEMPORARY: helpers to deal with non-canonical IDs.
162
+
163
+ /** @internal */
164
+ static idCBOR(mutableDocument: MutableDocument): Uint8Array {
165
+ const documentHandle = Bridge.mutableDocument.handleFor(mutableDocument)
166
+ return FFI.documentID(documentHandle.deref())
167
+ }
168
+
169
+ /** @internal */
170
+ static canonicalizedIDCBOR = Document.canonicalizedIDCBOR
171
+
172
+ /** @internal */
173
+ static isIDCBORCanonical = Document.isIDCBORCanonical
174
+ }
175
+
176
+ // -----------------------------------------------------------------------------
177
+
178
+ /** @private */
179
+ function documentsFrom(documentOrMany: Document | Document[] | null): Document[] {
180
+ if (!documentOrMany) {
181
+ return []
182
+ }
183
+
184
+ if (documentOrMany instanceof Document) {
185
+ return [documentOrMany]
186
+ }
187
+
188
+ if (documentOrMany instanceof Array) {
189
+ return documentOrMany as Document[]
190
+ }
191
+
192
+ throw new Error(`Expected null, a single document, or an array of documents but got value of type ${typeof documentOrMany}: ${documentOrMany}`)
193
+ }
@@ -0,0 +1,30 @@
1
+ //
2
+ // Copyright © 2021 DittoLive Incorporated. All rights reserved.
3
+ //
4
+
5
+ // NOTE: this is the last file to be rolled up. Typically, you'll use this to
6
+ // tie up certain associations that would otherwise lead to import cycles. The
7
+ // bridges are a good example of this: the type constructors for the bridged
8
+ // types need to be registered with it, but can't do it at creation time of the
9
+ // bridges, because anything that would require a certain bridge would
10
+ // immediately require the bridged type, oftentimes leading to an import cycle.
11
+
12
+ import { Attachment } from './attachment'
13
+ import { Document, MutableDocument } from './document'
14
+
15
+ import { StaticTCPClient } from './static-tcp-client'
16
+ import { WebsocketClient } from './websocket-client'
17
+
18
+ import { Ditto } from './ditto'
19
+ import { Bridge } from './bridge'
20
+ import { QueryResult } from './query-result'
21
+ import { QueryResultItem } from './query-result-item'
22
+
23
+ Bridge.attachment.registerType(Attachment)
24
+ Bridge.document.registerType(Document)
25
+ Bridge.dqlResult.registerType(QueryResultItem)
26
+ Bridge.dqlResponse.registerType(QueryResult)
27
+ Bridge.mutableDocument.registerType(MutableDocument)
28
+ Bridge.staticTCPClient.registerType(StaticTCPClient)
29
+ Bridge.websocketClient.registerType(WebsocketClient)
30
+ Bridge.ditto.registerType(Ditto)