@wovin/core 0.0.0-ciao-mobx-955482e8

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 (180) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +3 -0
  3. package/dist/applog/applog-helpers.d.ts +47 -0
  4. package/dist/applog/applog-helpers.d.ts.map +1 -0
  5. package/dist/applog/applog-utils.d.ts +57 -0
  6. package/dist/applog/applog-utils.d.ts.map +1 -0
  7. package/dist/applog/datom-types.d.ts +128 -0
  8. package/dist/applog/datom-types.d.ts.map +1 -0
  9. package/dist/applog.d.ts +4 -0
  10. package/dist/applog.d.ts.map +1 -0
  11. package/dist/applog.js +101 -0
  12. package/dist/applog.js.map +1 -0
  13. package/dist/blockstore/index.d.ts +21 -0
  14. package/dist/blockstore/index.d.ts.map +1 -0
  15. package/dist/blockstore.d.ts +2 -0
  16. package/dist/blockstore.d.ts.map +1 -0
  17. package/dist/blockstore.js +24 -0
  18. package/dist/blockstore.js.map +1 -0
  19. package/dist/chunk-6MQKRL6W.js +86 -0
  20. package/dist/chunk-6MQKRL6W.js.map +1 -0
  21. package/dist/chunk-7MW34UEO.js +40 -0
  22. package/dist/chunk-7MW34UEO.js.map +1 -0
  23. package/dist/chunk-7Z5YDQKK.js +1 -0
  24. package/dist/chunk-7Z5YDQKK.js.map +1 -0
  25. package/dist/chunk-CY4NLISM.js +144 -0
  26. package/dist/chunk-CY4NLISM.js.map +1 -0
  27. package/dist/chunk-E46VTKTZ.js +1 -0
  28. package/dist/chunk-E46VTKTZ.js.map +1 -0
  29. package/dist/chunk-O43W7UW6.js +434 -0
  30. package/dist/chunk-O43W7UW6.js.map +1 -0
  31. package/dist/chunk-XIQSYEV3.js +1604 -0
  32. package/dist/chunk-XIQSYEV3.js.map +1 -0
  33. package/dist/chunk-XVGW4QC3.js +55 -0
  34. package/dist/chunk-XVGW4QC3.js.map +1 -0
  35. package/dist/chunk-YDAKBU6Q.js +9 -0
  36. package/dist/chunk-YDAKBU6Q.js.map +1 -0
  37. package/dist/chunk-ZAADLBSB.js +36 -0
  38. package/dist/chunk-ZAADLBSB.js.map +1 -0
  39. package/dist/chunk-ZXCJRYD7.js +883 -0
  40. package/dist/chunk-ZXCJRYD7.js.map +1 -0
  41. package/dist/index.d.ts +8 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +354 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/ipfs/car.d.ts +59 -0
  46. package/dist/ipfs/car.d.ts.map +1 -0
  47. package/dist/ipfs/fetch-snapshot-chain.d.ts +32 -0
  48. package/dist/ipfs/fetch-snapshot-chain.d.ts.map +1 -0
  49. package/dist/ipfs/ipfs-utils.d.ts +35 -0
  50. package/dist/ipfs/ipfs-utils.d.ts.map +1 -0
  51. package/dist/ipfs.d.ts +4 -0
  52. package/dist/ipfs.d.ts.map +1 -0
  53. package/dist/ipfs.js +60 -0
  54. package/dist/ipfs.js.map +1 -0
  55. package/dist/ipns/ipns-record.d.ts +34 -0
  56. package/dist/ipns/ipns-record.d.ts.map +1 -0
  57. package/dist/ipns.d.ts +2 -0
  58. package/dist/ipns.d.ts.map +1 -0
  59. package/dist/ipns.js +64 -0
  60. package/dist/ipns.js.map +1 -0
  61. package/dist/pubsub/connector.d.ts +9 -0
  62. package/dist/pubsub/connector.d.ts.map +1 -0
  63. package/dist/pubsub/pub-pull.d.ts +14 -0
  64. package/dist/pubsub/pub-pull.d.ts.map +1 -0
  65. package/dist/pubsub/pubsub-types.d.ts +72 -0
  66. package/dist/pubsub/pubsub-types.d.ts.map +1 -0
  67. package/dist/pubsub/snap-push.d.ts +41 -0
  68. package/dist/pubsub/snap-push.d.ts.map +1 -0
  69. package/dist/pubsub/ucan-example.d.ts +3 -0
  70. package/dist/pubsub/ucan-example.d.ts.map +1 -0
  71. package/dist/pubsub/ucan.d.ts +16 -0
  72. package/dist/pubsub/ucan.d.ts.map +1 -0
  73. package/dist/pubsub.d.ts +5 -0
  74. package/dist/pubsub.d.ts.map +1 -0
  75. package/dist/pubsub.js +31 -0
  76. package/dist/pubsub.js.map +1 -0
  77. package/dist/query/basic.d.ts +105 -0
  78. package/dist/query/basic.d.ts.map +1 -0
  79. package/dist/query/divergences.d.ts +12 -0
  80. package/dist/query/divergences.d.ts.map +1 -0
  81. package/dist/query/matchers.d.ts +4 -0
  82. package/dist/query/matchers.d.ts.map +1 -0
  83. package/dist/query/memoized.d.ts +66 -0
  84. package/dist/query/memoized.d.ts.map +1 -0
  85. package/dist/query/query-steps.d.ts +4 -0
  86. package/dist/query/query-steps.d.ts.map +1 -0
  87. package/dist/query/situations.d.ts +80 -0
  88. package/dist/query/situations.d.ts.map +1 -0
  89. package/dist/query/subscribable.d.ts +102 -0
  90. package/dist/query/subscribable.d.ts.map +1 -0
  91. package/dist/query/types.d.ts +70 -0
  92. package/dist/query/types.d.ts.map +1 -0
  93. package/dist/query.d.ts +8 -0
  94. package/dist/query.d.ts.map +1 -0
  95. package/dist/query.js +108 -0
  96. package/dist/query.js.map +1 -0
  97. package/dist/retrieve/index.d.ts +2 -0
  98. package/dist/retrieve/index.d.ts.map +1 -0
  99. package/dist/retrieve/update-thread.d.ts +64 -0
  100. package/dist/retrieve/update-thread.d.ts.map +1 -0
  101. package/dist/retrieve.d.ts +2 -0
  102. package/dist/retrieve.d.ts.map +1 -0
  103. package/dist/retrieve.js +14 -0
  104. package/dist/retrieve.js.map +1 -0
  105. package/dist/thread/basic.d.ts +60 -0
  106. package/dist/thread/basic.d.ts.map +1 -0
  107. package/dist/thread/filters.d.ts +47 -0
  108. package/dist/thread/filters.d.ts.map +1 -0
  109. package/dist/thread/mapped.d.ts +31 -0
  110. package/dist/thread/mapped.d.ts.map +1 -0
  111. package/dist/thread/utils.d.ts +23 -0
  112. package/dist/thread/utils.d.ts.map +1 -0
  113. package/dist/thread/writeable.d.ts +41 -0
  114. package/dist/thread/writeable.d.ts.map +1 -0
  115. package/dist/thread.d.ts +6 -0
  116. package/dist/thread.d.ts.map +1 -0
  117. package/dist/thread.js +54 -0
  118. package/dist/thread.js.map +1 -0
  119. package/dist/types/typescript-utils.d.ts +34 -0
  120. package/dist/types/typescript-utils.d.ts.map +1 -0
  121. package/dist/types.d.ts +2 -0
  122. package/dist/types.d.ts.map +1 -0
  123. package/dist/types.js +26 -0
  124. package/dist/types.js.map +1 -0
  125. package/dist/utils/debug-name.d.ts +13 -0
  126. package/dist/utils/debug-name.d.ts.map +1 -0
  127. package/dist/utils.d.ts +4 -0
  128. package/dist/utils.d.ts.map +1 -0
  129. package/dist/utils.js +9 -0
  130. package/dist/utils.js.map +1 -0
  131. package/package.json +110 -0
  132. package/src/applog/applog-helpers.ts +150 -0
  133. package/src/applog/applog-utils.ts +398 -0
  134. package/src/applog/datom-types.ts +148 -0
  135. package/src/applog.ts +3 -0
  136. package/src/blockstore/index.ts +36 -0
  137. package/src/blockstore.ts +1 -0
  138. package/src/index.ts +8 -0
  139. package/src/ipfs/car.ts +291 -0
  140. package/src/ipfs/fetch-snapshot-chain.ts +135 -0
  141. package/src/ipfs/ipfs-utils.ts +132 -0
  142. package/src/ipfs.ts +3 -0
  143. package/src/ipns/ipns-record.ts +115 -0
  144. package/src/ipns.ts +1 -0
  145. package/src/pubsub/UCAN Specs Overview.md +217 -0
  146. package/src/pubsub/connector.ts +9 -0
  147. package/src/pubsub/pub-pull.ts +31 -0
  148. package/src/pubsub/pubsub-types.ts +90 -0
  149. package/src/pubsub/snap-push.ts +277 -0
  150. package/src/pubsub/ucan-example.ts +61 -0
  151. package/src/pubsub/ucan.ts +56 -0
  152. package/src/pubsub.ts +4 -0
  153. package/src/query/basic.ts +1061 -0
  154. package/src/query/divergences.ts +50 -0
  155. package/src/query/matchers.ts +8 -0
  156. package/src/query/memoized.test.ts +151 -0
  157. package/src/query/memoized.ts +180 -0
  158. package/src/query/query-steps.ts +4 -0
  159. package/src/query/query.test.ts +536 -0
  160. package/src/query/situations.ts +261 -0
  161. package/src/query/subscribable.test.ts +245 -0
  162. package/src/query/subscribable.ts +225 -0
  163. package/src/query/types.ts +155 -0
  164. package/src/query.ts +7 -0
  165. package/src/retrieve/index.ts +1 -0
  166. package/src/retrieve/update-thread.ts +248 -0
  167. package/src/retrieve.ts +1 -0
  168. package/src/test/perf/query.1m.perf.test.ts +94 -0
  169. package/src/test/perf/query.perf.test.ts +389 -0
  170. package/src/test/perf/query.realdata.perf.test.ts +175 -0
  171. package/src/thread/basic.ts +209 -0
  172. package/src/thread/filters.ts +234 -0
  173. package/src/thread/mapped.ts +166 -0
  174. package/src/thread/utils.ts +146 -0
  175. package/src/thread/writeable.ts +163 -0
  176. package/src/thread.ts +5 -0
  177. package/src/types/typescript-utils.ts +64 -0
  178. package/src/types.ts +1 -0
  179. package/src/utils/debug-name.ts +54 -0
  180. package/src/utils.ts +4 -0
@@ -0,0 +1,398 @@
1
+ import { Logger } from 'besonders-logger'
2
+ import { isBefore } from 'date-fns'
3
+ import { partial, pick } from 'lodash-es'
4
+ import { isEqual } from 'lodash-es'
5
+ import stringify from 'safe-stable-stringify'
6
+ import type {
7
+ Applog,
8
+ ApplogForInsert,
9
+ ApplogValue,
10
+ DatalogQueryPattern,
11
+ DatalogQueryResultEntry,
12
+ ResultContext,
13
+ SearchContext,
14
+ ValueOrMatcher,
15
+ } from './datom-types.ts'
16
+
17
+ const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line no-unused-vars
18
+
19
+ export const isoDateStrCompare = (strA: string, strB: string, dir: 'asc' | 'desc' = 'asc') =>
20
+ dir === 'asc'
21
+ ? strA.localeCompare(strB, 'en-US')
22
+ : strB.localeCompare(strA, 'en-US')
23
+ export const objEqualByKeys = (keys: string[], objA: object, objB: object) => {
24
+ return isEqual(pick(objA, keys), pick(objB, keys))
25
+ }
26
+
27
+ export const compareApplogsByTs = (logA: Applog, logB: Applog, dir: 'asc' | 'desc' = 'asc') => isoDateStrCompare(logA.ts, logB.ts, dir)
28
+ export const compareApplogsByEnAt = partial(objEqualByKeys, ['en', 'at'])
29
+
30
+ /** Sort by TS (modifies the array, but also returns for ease of use) */
31
+ export function sortApplogsByTs(appLogArray: Applog[], dir: 'asc' | 'desc' = 'asc') {
32
+ return appLogArray.sort((a, b) => compareApplogsByTs(a, b, dir))
33
+ }
34
+ export const isTsBefore = (log: Applog, logToCompare: Applog) => isBefore(new Date(log.ts), new Date(logToCompare.ts))
35
+ export const uniqueEnFromAppLogs = (appLogArray: Applog[]) => [...new Set(appLogArray.map(eachLog => eachLog.en))]
36
+ export const areApplogsEqual = (logA: Applog, logB: Applog) => isEqual(logA, logB)
37
+
38
+ export type RemoveDuplicateAppLogsMode = 'safety' | 'cleanup'
39
+
40
+ const warnMissingRemoveDuplicateMode = () => {
41
+ WARN(`[removeDuplicateAppLogs] mode not set; pass 'safety' or 'cleanup' for optimal behavior`)
42
+ }
43
+
44
+ const removeDuplicateAppLogsCleanup = (appLogArray: Applog[]) => {
45
+ const logMap = new Map<string, Applog>()
46
+ const verboseEnabled = VERBOSE.isEnabled
47
+ for (const eachLog of appLogArray) {
48
+ if (!eachLog) {
49
+ ERROR(`falsy entry in applogs`, appLogArray)
50
+ throw new Error(`falsy entry in applogs`)
51
+ }
52
+ if (!eachLog.cid) {
53
+ ERROR(`applog with missing CID`, eachLog)
54
+ throw new Error(`applog with missing CID`)
55
+ }
56
+ const key = eachLog.cid
57
+ const existing = logMap.get(key)
58
+ if (existing) {
59
+ if (verboseEnabled) VERBOSE(`Skipping duplicate applog:`, [existing, eachLog])
60
+ } else {
61
+ logMap.set(key, eachLog)
62
+ }
63
+ }
64
+ return Array.from(logMap.values())
65
+ }
66
+
67
+ const removeDuplicateAppLogsSafety = (appLogArray: Applog[]) => {
68
+ const seen = new Set<string>()
69
+ const verboseEnabled = VERBOSE.isEnabled
70
+ const existingByCid = verboseEnabled ? new Map<string, Applog>() : null
71
+ let result: Applog[] | null = null
72
+ let index = 0
73
+ for (const eachLog of appLogArray) {
74
+ if (!eachLog) {
75
+ ERROR(`falsy entry in applogs`, appLogArray)
76
+ throw new Error(`falsy entry in applogs`)
77
+ }
78
+ if (!eachLog.cid) {
79
+ ERROR(`applog with missing CID`, eachLog)
80
+ throw new Error(`applog with missing CID`)
81
+ }
82
+ const key = eachLog.cid
83
+ if (seen.has(key)) {
84
+ if (!result) {
85
+ result = appLogArray.slice(0, index)
86
+ }
87
+ if (verboseEnabled) VERBOSE(`Skipping duplicate applog:`, [existingByCid?.get(key), eachLog])
88
+ } else {
89
+ seen.add(key)
90
+ if (existingByCid) existingByCid.set(key, eachLog)
91
+ if (result) result.push(eachLog)
92
+ }
93
+ index++
94
+ }
95
+ return result ?? appLogArray
96
+ }
97
+
98
+ /**
99
+ * Deduplicate applogs by CID.
100
+ * - safety: fast duplicate check; returns original array if no duplicates found.
101
+ * - cleanup: optimized for merged arrays with likely duplicates.
102
+ */
103
+ export const removeDuplicateAppLogs = (appLogArray: Applog[], mode?: RemoveDuplicateAppLogsMode) => {
104
+ if (!mode) {
105
+ warnMissingRemoveDuplicateMode()
106
+ return removeDuplicateAppLogsCleanup(appLogArray)
107
+ }
108
+ return mode === 'safety'
109
+ ? removeDuplicateAppLogsSafety(appLogArray)
110
+ : removeDuplicateAppLogsCleanup(appLogArray)
111
+ }
112
+
113
+ // export const removeDuplicateAndMaybeDeletedAppLogs = (ds: Thread, appLogArray: Applog[], removeDeletedEntities = true) => {
114
+ // const logMap = new Map()
115
+ // for (const eachLog of appLogArray) {
116
+ // if (!removeDeletedEntities || ds.entityIsDeleted(eachLog.en))
117
+ // logMap.set(stringify(eachLog), eachLog)
118
+ // }
119
+ // return Array.from(logMap.values())
120
+ // }
121
+
122
+ export const getHashID = (stringifiable: any, lngth = 8) => cyrb53hash(stringify(stringifiable), 31, lngth) as string
123
+
124
+ export function isVariable(x: any): x is string {
125
+ return typeof x === 'string' && x.startsWith('?')
126
+ }
127
+ export function variableNameWithoutQuestionmark(str: string) {
128
+ return str.slice(1)
129
+ }
130
+ // export function isMatcher(x: any): x is string {
131
+ // return
132
+ // }
133
+ export function isStaticPattern(x: any): x is ApplogValue {
134
+ if (!['string', 'boolean', 'number', 'function'].includes(typeof x)) WARN(`Unhandled pattern value type:`, typeof x, x)
135
+ return !isVariable(x) && ['string', 'boolean', 'number'].includes(typeof x)
136
+ }
137
+ // export function isIgnorePattern(x: any): boolean {
138
+ // return x === '_'
139
+ // }
140
+
141
+ /*
142
+ * In a pattern from a Query:
143
+ * - variables that don't have a value in the search context:
144
+ * - remove from pattern
145
+ * - add to variableToFill as: { en: 'movieID' } (useful for mapTo)
146
+ * - variables that have a value set:
147
+ * - replace placeholder with actual value
148
+ */
149
+ export function resolveOrRemoveVariables(pattern: DatalogQueryPattern, candidate: SearchContext) {
150
+ let variablesToFill = {} as Partial<{ [key in keyof Applog]: string }>
151
+ const newPattern: DatalogQueryPattern = {}
152
+ for (const [patternKey, patternValue] of Object.entries(pattern)) {
153
+ if (isVariable(patternValue)) {
154
+ const varName = variableNameWithoutQuestionmark(patternValue)
155
+ const candidateValue = candidate[varName]
156
+ if (candidateValue) {
157
+ newPattern[patternKey] = candidateValue
158
+ // & not adding it to newPattern
159
+ } else {
160
+ variablesToFill[patternKey] = varName
161
+ }
162
+ } else {
163
+ newPattern[patternKey] = patternValue // keep static value
164
+ }
165
+ }
166
+
167
+ return [newPattern, variablesToFill] as const
168
+ }
169
+
170
+ function matchVariable(variable: string, triplePart: ApplogValue, context: SearchContext): SearchContext {
171
+ if (context.hasOwnProperty(variable)) {
172
+ // TODO: fix lint error with: if (Object.hasOwnProperty.call(context, variable)) {
173
+ const bound = context[variable]
174
+ const match = matchPart(bound, triplePart, context)
175
+ // if (VERBOSE.isEnabled) VERBOSE('[matchVariable] match?', variable, bound, match)
176
+ return match
177
+ }
178
+ // if (VERBOSE.isEnabled) VERBOSE('[matchVariable] initializing variable', variable, 'to', triplePart)
179
+ return { ...context, [variable]: triplePart }
180
+ }
181
+
182
+ export function matchPartStatic(field: keyof Applog, patternPart: ValueOrMatcher<ApplogValue>, atomPart: ApplogValue): boolean {
183
+ // if (VERBOSE.isEnabled) VERBOSE('[matchPartStatic]', field, patternPart, patternPart === atomPart ? '===' : '!==', atomPart)
184
+ let result
185
+ if (patternPart) {
186
+ const typ = typeof patternPart
187
+ if (typ === 'string') {
188
+ result = patternPart === atomPart // shortcut for most common use-case
189
+ } else if (typ === 'function') {
190
+ result = (patternPart as Function)(atomPart)
191
+ } else if (typeof (patternPart as any).has === 'function') {
192
+ result = (patternPart as Set<any>).has(atomPart)
193
+ } else if (Array.isArray(patternPart) && !Array.isArray(atomPart) /* ? how to handle array values */) {
194
+ result = patternPart.includes(atomPart)
195
+ } // if (field === 'at' && typ === 'string' && patternPart.endsWith('*')) {
196
+ // return typeof atomPart === 'string' && atomPart.startsWith(patternPart.slice(0, -1))
197
+ // }
198
+ else {
199
+ result = patternPart === atomPart
200
+ }
201
+ } else {
202
+ result = patternPart === atomPart
203
+ }
204
+
205
+ // if (VERBOSE.isEnabled) VERBOSE('[matchPartStatic] =>', field.startsWith('!') ? '!' : '', result)
206
+ if (field.charAt(0) === '!') {
207
+ return !result
208
+ } else {
209
+ return result
210
+ }
211
+ }
212
+ export function matchPart(patternPart: ValueOrMatcher<ApplogValue>, atomPart: ApplogValue, context: SearchContext): ResultContext {
213
+ if (!context) {
214
+ // if (VERBOSE.isEnabled) VERBOSE('[matchPart] no context')
215
+ return null
216
+ }
217
+ if (typeof patternPart === 'string') {
218
+ if (isVariable(patternPart)) {
219
+ return matchVariable(patternPart, atomPart, context)
220
+ } /* TODO: else if (isIgnorePattern(patternPart)) {
221
+ return matchVariable(patternPart, atomPart, context)
222
+ } */
223
+ }
224
+ // if (VERBOSE.isEnabled) VERBOSE('[matchPart]', patternPart, patternPart === atomPart ? '===' : '!==', atomPart)
225
+ if (typeof patternPart === 'function') {
226
+ return patternPart(atomPart) ? context : null
227
+ }
228
+ return patternPart === atomPart ? context : null
229
+ }
230
+
231
+ /**
232
+ * Check if pattern matches triple with context substitutions
233
+ */
234
+ export function matchPattern(pattern: DatalogQueryPattern, applog: Applog, context: SearchContext): ResultContext {
235
+ return Object.entries(pattern).reduce((context, [field, patternValue]) => {
236
+ const applogValue = applog[field]
237
+ // @ts-expect-error wtf no idea //HACK: ts weird
238
+ const patternValT: ValueOrMatcher<ApplogValue> = patternValue
239
+ return matchPart(patternValT, applogValue, context)
240
+ }, context)
241
+ }
242
+
243
+ export function actualize<SELECT extends string>(context: ResultContext, find: readonly SELECT[]): DatalogQueryResultEntry<SELECT> {
244
+ return Object.fromEntries(find.map((findField) => {
245
+ if (context === null) {
246
+ throw new Error(`actualize context is null ${find}`)
247
+ }
248
+ return [
249
+ isVariable(findField) ? findField.replace(/^\?/, '') : findField,
250
+ isVariable(findField) ? context[findField] : findField,
251
+ ]
252
+ })) as DatalogQueryResultEntry<SELECT>
253
+ }
254
+ const sum = function sum(array: number[]) {
255
+ var num = 0
256
+ for (var i = 0, l = array.length; i < l; i++) num += array[i]
257
+ return num
258
+ }
259
+ const mean = function mean(array: number[]) {
260
+ return sum(array) / array.length
261
+ }
262
+ export const arrStats = {
263
+ max: function(array: number[]) {
264
+ return Math.max.apply(null, array)
265
+ },
266
+
267
+ min: function(array: number[]) {
268
+ return Math.min.apply(null, array)
269
+ },
270
+
271
+ range: function(array: number[]) {
272
+ return arrStats.max(array) - arrStats.min(array)
273
+ },
274
+
275
+ midrange: function(array: number[]) {
276
+ return arrStats.range(array) / 2
277
+ },
278
+
279
+ sum,
280
+
281
+ mean,
282
+
283
+ average: mean,
284
+
285
+ median: function(array: number[]) {
286
+ array.sort(function(a, b) {
287
+ return a - b
288
+ })
289
+ var mid = array.length / 2
290
+ return mid % 1 ? array[mid - 0.5] : (array[mid - 1] + array[mid]) / 2
291
+ },
292
+
293
+ modes: function(array: number[]) {
294
+ if (!array.length) return []
295
+ var modeMap = {},
296
+ maxCount = 0,
297
+ modes = []
298
+
299
+ array.forEach(function(val) {
300
+ if (!modeMap[val]) modeMap[val] = 1
301
+ else modeMap[val]++
302
+
303
+ if (modeMap[val] > maxCount) {
304
+ modes = [val]
305
+ maxCount = modeMap[val]
306
+ } else if (modeMap[val] === maxCount) {
307
+ modes.push(val)
308
+ maxCount = modeMap[val]
309
+ }
310
+ })
311
+ return modes
312
+ },
313
+
314
+ variance: function(array: number[]) {
315
+ var mean = arrStats.mean(array)
316
+ return arrStats.mean(array.map(function(num) {
317
+ return Math.pow(num - mean, 2)
318
+ }))
319
+ },
320
+
321
+ standardDeviation: function(array: number[]) {
322
+ return Math.sqrt(arrStats.variance(array))
323
+ },
324
+
325
+ meanAbsoluteDeviation: function(array: number[]) {
326
+ var mean = arrStats.mean(array)
327
+ return arrStats.mean(array.map(function(num) {
328
+ return Math.abs(num - mean)
329
+ }))
330
+ },
331
+
332
+ zScores: function(array: number[]) {
333
+ var mean = arrStats.mean(array)
334
+ var standardDeviation = arrStats.standardDeviation(array)
335
+ return array.map(function(num) {
336
+ return (num - mean) / standardDeviation
337
+ })
338
+ },
339
+ }
340
+
341
+ // Function aliases:
342
+ arrStats.average = arrStats.mean
343
+
344
+ export const tsNearlySame = (timeA: string, timeB: string) => timeB.startsWith(timeA.slice(0, timeA.length - 4)) // HACK: to quickly check if same second
345
+
346
+ /*
347
+ cyrb53 (c) 2018 bryc (github.com/bryc)
348
+ A fast and simple hash function with decent collision resistance.
349
+ Largely inspired by MurmurHash2/3, but with a focus on speed/simplicity.
350
+ Public domain. Attribution appreciated.
351
+
352
+ ripped from https://github.com/bryc/code/blob/mast`er/jshash/experimental/cyrb53.js
353
+ */
354
+ export const cyrb53hash = function(
355
+ str: string,
356
+ seed = 13,
357
+ strLength: number, /* = 0 */
358
+ ) {
359
+ if (!str?.length) {
360
+ throw new Error(`Empty string: ${str}`)
361
+ }
362
+
363
+ let h1 = 0xdeadbeef ^ seed
364
+ let h2 = 0x41c6ce57 ^ seed
365
+ for (let i = 0, ch; i < str.length; i++) {
366
+ ch = str.charCodeAt(i)
367
+ h1 = Math.imul(h1 ^ ch, 2654435761)
368
+ h2 = Math.imul(h2 ^ ch, 1597334677)
369
+ }
370
+ h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909)
371
+ h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909)
372
+ // if (strLength) {
373
+ const asHex = (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(16)
374
+ return asHex.slice(-strLength).padStart(strLength, '0')
375
+ // }
376
+ // // if not specified return as 16 digit integer
377
+ // return 4294967296 * (2097151 & h2) + (h1 >>> 0)
378
+ }
379
+ export function arraysContainSameElements(arr1, arr2) {
380
+ if (arr1.length !== arr2.length) {
381
+ return false
382
+ }
383
+
384
+ const sortedArr1 = [...arr1].sort()
385
+ const sortedArr2 = [...arr2].sort()
386
+
387
+ for (let i = 0; i < sortedArr1.length; i++) {
388
+ if (sortedArr1[i] !== sortedArr2[i]) {
389
+ return false
390
+ }
391
+ }
392
+
393
+ return true
394
+ }
395
+ export function dateNowIso(): string {
396
+ const now = new Date()
397
+ return now.toISOString()
398
+ }
@@ -0,0 +1,148 @@
1
+ // import type { AgentHash } from '../pubsub/pubsub-types.ts'
2
+ // import type { CID } from '@oddjs/odd'
3
+ import { FormatRegistry, Static, TSchema, Type } from '@sinclair/typebox'
4
+ import { TypeCompiler } from '@sinclair/typebox/compiler'
5
+ import { CID } from 'multiformats/cid'
6
+ import type { PartialBy, Tagged } from '../types/typescript-utils.ts'
7
+
8
+ export const Nullable = <T extends TSchema>(schema: T) => Type.Union([schema, Type.Null()])
9
+ export const EntityID_LENGTH = 7
10
+ // const bagu = 'baguqeerav3h4b46j2pyxikqhtm5si5vhzsyrba2duhrtltfutrlmj42anmvq'
11
+ // const k51q = 'k51qzi5uqu5dhe1bxxjxj144bj2a225o1681yobevns26xlxtsfidjgnpwknfd'
12
+ const isCID = /^(k51qz|baguq)[0-9a-z]{56,57}$/ // FIXME: k51 is not really a CID, is it?
13
+ const isShortHash = /^[0-9A-Fa-f]{7,8}$/g // TODO awkward why are some 7 and some 8 long
14
+ // engine level: min 6 (lenient within reason)
15
+ // note3 TBD... either fixed for all entity types VS 7 for pub/sub, 8 for tags, 9 for blocks, 10 for relations etc...
16
+
17
+ FormatRegistry.Set('EntityID', (value) => !!value.match(isShortHash) || !!value.match(isCID))
18
+ export const EntityID = Type.String() // HACK how to configure ID format?
19
+ /*{ format: 'EntityID' }*/
20
+ export type EntityID = Static<typeof EntityID>
21
+
22
+ export type AgentHash = Tagged<string, 'AgentHash'>
23
+ export type DatomPart = string // TODO refactor
24
+ export type CidString = Tagged<string, CID>
25
+ export type IpnsString = Tagged<CidString, 'IPNS'>
26
+ export type AgentID = EntityID
27
+ export type Attribute = string
28
+ export type ApplogValue = string | boolean | number | null // TODO: use Tagged types
29
+ // ? allow objects? or just as serialized strings? Or serialize everything anyways?
30
+
31
+ export interface Atom {
32
+ en: EntityID
33
+ at: Attribute
34
+ vl: ApplogValue
35
+ }
36
+
37
+ export type Timestamp = string
38
+ export interface Applog extends Atom {
39
+ cid: CidString
40
+ pv: CidString | null // ? | CID
41
+ ts: Timestamp
42
+ ag: AgentHash
43
+ }
44
+ export type ApplogNoCid = Omit<Applog, 'cid'>
45
+ export type ApplogOptionalCid = PartialBy<Applog, 'cid'>
46
+ export type ApplogForInsert = PartialBy<ApplogNoCid, 'ts' | 'pv'>
47
+ export type ApplogForInsertOptionalAgent = PartialBy<ApplogForInsert, 'ag'>
48
+
49
+ export interface ApplogEnc {
50
+ cid: CidString
51
+ enc: Uint8Array
52
+ iv?: Uint8Array // iv may be needed if we prefer a different strategy for transmitting iv (odd appends a random iv to the payload via keystoreAES.(en|de)cryptBytes)
53
+ }
54
+ export type ApplogEncNoCid = Omit<ApplogEnc, 'cid'>
55
+
56
+ export type ApplogArrayMaybeEncrypted = (Applog | ApplogEnc)[]
57
+ export type ApplogArrayMaybeEncryptedRO = readonly (Applog | ApplogEnc)[]
58
+ export type ApplogArrayNoCIDMaybeEncryptedRO = readonly (ApplogNoCid | ApplogEncNoCid)[]
59
+
60
+ export type ApplogOfSomeSort = Applog | ApplogEnc | ApplogNoCid | ApplogEncNoCid | ApplogForInsert
61
+
62
+ export const isEncryptedApplog = (l: ApplogOfSomeSort): l is ApplogEnc => (l as ApplogEnc)?.enc instanceof Uint8Array
63
+
64
+ export type AtomPattern = Atom | Applog
65
+
66
+ export interface DatalogStateIdentifier {
67
+ lastTS: Timestamp
68
+ }
69
+
70
+ // New generic type for fields that can be a value, an array of that, or a function
71
+ export type ValueOrMatcher<T> = T | readonly T[] | ReadonlySet<T> | ((value: T) => boolean)
72
+ // Generic type that applies ValueOrMatcher to each field of T
73
+ export type WithMatchers<T extends Record<string, any>> = {
74
+ [K in keyof T & string as `${K}` | `!${K}`]?: ValueOrMatcher<T[K]>
75
+ }
76
+
77
+ export type DatalogQueryPattern = Partial<WithMatchers<Applog>>
78
+ export type DatalogQueryPatternArray = DatalogQueryPattern[]
79
+ export interface DatalogQuery<SELECT extends string> {
80
+ find: readonly SELECT[] // see: https://github.com/microsoft/TypeScript/issues/20965#issuecomment-868981458
81
+ where: DatalogQueryPatternArray
82
+ onlyLatest?: boolean
83
+ }
84
+ export type DatalogQueryResultEntry<SELECT extends string> = Record<
85
+ // SELECT,
86
+ StripPrefix<'?', SELECT>,
87
+ DatomPart
88
+ >
89
+ export type DatalogQueryResultRows<SELECT extends string> = DatalogQueryResultEntry<SELECT>[]
90
+ // export type StripTest = StripPrefix<'?', '?A' | '?B'>
91
+ // export type DatalogQueryResultEntryTEST = DatalogQueryResultEntry<'?A' | '?B'>
92
+ // export type DatalogQueryResultEntryTESTX = MapKeysStripPrefix<'?A' | '?B', '?'>
93
+
94
+ export interface SearchContext {
95
+ [key: string]: ApplogValue
96
+ }
97
+ export interface SearchContextWithLog {
98
+ context: SearchContext
99
+ applog?: Applog
100
+ }
101
+
102
+ export type ResultContext = SearchContext | null
103
+
104
+ /* https://stackoverflow.com/a/72497461 */
105
+ type StripPrefix<
106
+ TPrefix extends string,
107
+ T extends string,
108
+ > = T extends `${TPrefix}${infer R}` ? R : never
109
+
110
+ type MapKeysStripPrefix<SELECT extends string, TPrefix extends string> = {
111
+ [K in SELECT as StripPrefix<TPrefix, K>]: DatomPart
112
+ }
113
+
114
+ FormatRegistry.Set('CID', (value) => !!value.match(isCID))
115
+ export const CIDTB = Type.String({ format: 'CID' })
116
+ export type CIDTB = Static<typeof EntityID>
117
+
118
+ const isURL = /^http([s]?):\/\/.*\..*/
119
+ FormatRegistry.Set('URL', (value) => !!value.match(isURL))
120
+ export const URL = Type.String({ format: 'URL' })
121
+ export type URL = Static<typeof URL>
122
+
123
+ export const AppLogNoCidTB = Type.Object({
124
+ en: EntityID, // EntityID
125
+ at: Type.String(), // Attribute
126
+ vl: Nullable(Type.Union([Type.String(), Type.Boolean(), Type.Number()])), // TODO refactor to semantic typesafe ApplogValue
127
+ ts: Type.String(), // Timestamp
128
+ ag: Type.String(), // AgentHash
129
+ pv: Nullable(CIDTB), // CidString
130
+ })
131
+ export type AppLogNoCidTB = Static<typeof AppLogNoCidTB> // type T = {
132
+
133
+ export const AppLogNoCidTBC = TypeCompiler.Compile(AppLogNoCidTB)
134
+ export const getApplogNoCidTypeErrors = (obj: any) => Array.from(AppLogNoCidTBC.Errors(obj))
135
+ export const isValidApplogNoCid = AppLogNoCidTBC.Check.bind(AppLogNoCidTBC) // ? Include CID
136
+
137
+ export const AppLogTB = Type.Composite([
138
+ Type.Object({
139
+ cid: CIDTB,
140
+ }),
141
+ AppLogNoCidTB,
142
+ ])
143
+ export type AppLogTB = Static<typeof AppLogTB> // type T = {
144
+
145
+ export const AppLogTBC = TypeCompiler.Compile(AppLogTB)
146
+ export const getApplogTypeErrors = (obj: any) => Array.from(AppLogTBC.Errors(obj))
147
+ export const isValidApplog = AppLogTBC.Check.bind(AppLogTBC) // ? Include CID
148
+ // maybe useful for defaulting https://github.com/sinclairzx81/typebox#cast
package/src/applog.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './applog/applog-helpers.ts'
2
+ export * from './applog/applog-utils.ts'
3
+ export * from './applog/datom-types.ts'
@@ -0,0 +1,36 @@
1
+ import type { CID } from 'multiformats/cid'
2
+
3
+ /** Minimal async block store interface — get/put/has. */
4
+ export interface BlockStore {
5
+ get(cid: CID): Promise<Uint8Array>
6
+ put(cid: CID, bytes: Uint8Array): Promise<void>
7
+ has(cid: CID): Promise<boolean>
8
+ }
9
+
10
+ /**
11
+ * A block store that reads locally first, with optional remote fallback.
12
+ * On get: local hit → return; local miss + remote → fetch, write-back, return.
13
+ * put/has always operate on local only.
14
+ */
15
+ export class LocalFirstBlockStore implements BlockStore {
16
+ constructor(
17
+ private local: BlockStore,
18
+ private remote?: Pick<BlockStore, 'get'>,
19
+ ) {}
20
+
21
+ async get(cid: CID): Promise<Uint8Array> {
22
+ if (await this.local.has(cid)) return this.local.get(cid)
23
+ if (!this.remote) throw new Error(`Block not found: ${cid}`)
24
+ const bytes = await this.remote.get(cid)
25
+ await this.local.put(cid, bytes)
26
+ return bytes
27
+ }
28
+
29
+ put(cid: CID, bytes: Uint8Array): Promise<void> {
30
+ return this.local.put(cid, bytes)
31
+ }
32
+
33
+ has(cid: CID): Promise<boolean> {
34
+ return this.local.has(cid)
35
+ }
36
+ }
@@ -0,0 +1 @@
1
+ export * from './blockstore/index.ts'
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ export * from './applog.ts'
2
+ export * from './ipfs.ts'
3
+ export * from './pubsub.ts'
4
+ export * from './query.ts'
5
+ export * from './retrieve.ts'
6
+ export * from './thread.ts'
7
+ export * from './types.ts'
8
+ // export * from './utils.ts'