@wovin/core 0.1.35 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/README.md +0 -12
  2. package/dist/applog/applog-helpers.d.ts +12 -12
  3. package/dist/applog/applog-helpers.d.ts.map +1 -1
  4. package/dist/applog/applog-utils.d.ts +25 -6
  5. package/dist/applog/applog-utils.d.ts.map +1 -1
  6. package/dist/applog/datom-types.d.ts +4 -5
  7. package/dist/applog/datom-types.d.ts.map +1 -1
  8. package/dist/applog.d.ts +3 -3
  9. package/dist/applog.d.ts.map +1 -1
  10. package/dist/{applog.min.js → applog.js} +6 -7
  11. package/dist/blockstore.d.ts +1 -1
  12. package/dist/blockstore.d.ts.map +1 -1
  13. package/dist/{blockstore.min.js → blockstore.js} +1 -3
  14. package/dist/{blockstore.min.js.map → blockstore.js.map} +1 -1
  15. package/dist/{chunk-KXMTKPF4.min.js → chunk-3JZMOEOD.js} +8 -8
  16. package/dist/chunk-3JZMOEOD.js.map +1 -0
  17. package/dist/chunk-3WZVG277.js +434 -0
  18. package/dist/chunk-3WZVG277.js.map +1 -0
  19. package/dist/chunk-7Z5YDQKK.js +1 -0
  20. package/dist/chunk-CPSDKFBG.js +147 -0
  21. package/dist/chunk-CPSDKFBG.js.map +1 -0
  22. package/dist/chunk-E46VTKTZ.js +1 -0
  23. package/dist/{chunk-H3VQJP56.min.js → chunk-J2FDHGOZ.js} +9 -9
  24. package/dist/chunk-J2FDHGOZ.js.map +1 -0
  25. package/dist/chunk-L5EEEGE6.js +1862 -0
  26. package/dist/chunk-L5EEEGE6.js.map +1 -0
  27. package/dist/{chunk-BRC7LSM6.min.js → chunk-PD3C7XUM.js} +5 -5
  28. package/dist/chunk-PD3C7XUM.js.map +1 -0
  29. package/dist/chunk-QZXKQCAY.js +1026 -0
  30. package/dist/chunk-QZXKQCAY.js.map +1 -0
  31. package/dist/{chunk-QPGEBDMJ.min.js → chunk-YDAKBU6Q.js} +1 -1
  32. package/dist/chunk-YDAKBU6Q.js.map +1 -0
  33. package/dist/chunk-ZAADLBSB.js +36 -0
  34. package/dist/chunk-ZAADLBSB.js.map +1 -0
  35. package/dist/index.d.ts +7 -7
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/{index.min.js → index.js} +73 -46
  38. package/dist/ipfs/car.d.ts +11 -11
  39. package/dist/ipfs/car.d.ts.map +1 -1
  40. package/dist/ipfs/ipfs-utils.d.ts +2 -2
  41. package/dist/ipfs/ipfs-utils.d.ts.map +1 -1
  42. package/dist/ipfs.d.ts +3 -3
  43. package/dist/ipfs.d.ts.map +1 -1
  44. package/dist/{ipfs.min.js → ipfs.js} +7 -10
  45. package/dist/ipns.d.ts +1 -1
  46. package/dist/ipns.d.ts.map +1 -1
  47. package/dist/ipns.js +64 -0
  48. package/dist/ipns.js.map +1 -0
  49. package/dist/pubsub/pub-pull.d.ts +3 -3
  50. package/dist/pubsub/pub-pull.d.ts.map +1 -1
  51. package/dist/pubsub/pubsub-types.d.ts +3 -3
  52. package/dist/pubsub/pubsub-types.d.ts.map +1 -1
  53. package/dist/pubsub/snap-push.d.ts +4 -4
  54. package/dist/pubsub/snap-push.d.ts.map +1 -1
  55. package/dist/pubsub/ucan.d.ts +1 -1
  56. package/dist/pubsub/ucan.d.ts.map +1 -1
  57. package/dist/pubsub.d.ts +4 -4
  58. package/dist/pubsub.d.ts.map +1 -1
  59. package/dist/{pubsub.min.js → pubsub.js} +7 -10
  60. package/dist/query/attr-helpers.d.ts +5 -0
  61. package/dist/query/attr-helpers.d.ts.map +1 -0
  62. package/dist/query/basic.d.ts +85 -21
  63. package/dist/query/basic.d.ts.map +1 -1
  64. package/dist/query/divergences.d.ts +5 -5
  65. package/dist/query/divergences.d.ts.map +1 -1
  66. package/dist/query/entity-collection.d.ts +19 -0
  67. package/dist/query/entity-collection.d.ts.map +1 -0
  68. package/dist/query/matchers.d.ts +1 -1
  69. package/dist/query/matchers.d.ts.map +1 -1
  70. package/dist/query/memoized.d.ts +66 -0
  71. package/dist/query/memoized.d.ts.map +1 -0
  72. package/dist/query/situations.d.ts +2 -1
  73. package/dist/query/situations.d.ts.map +1 -1
  74. package/dist/query/subscribable.d.ts +111 -0
  75. package/dist/query/subscribable.d.ts.map +1 -0
  76. package/dist/query/types.d.ts +54 -14
  77. package/dist/query/types.d.ts.map +1 -1
  78. package/dist/query.d.ts +9 -5
  79. package/dist/query.d.ts.map +1 -1
  80. package/dist/{query.min.js → query.js} +51 -32
  81. package/dist/retrieve/index.d.ts +1 -1
  82. package/dist/retrieve/index.d.ts.map +1 -1
  83. package/dist/retrieve/update-thread.d.ts +3 -3
  84. package/dist/retrieve/update-thread.d.ts.map +1 -1
  85. package/dist/retrieve.d.ts +1 -1
  86. package/dist/retrieve.d.ts.map +1 -1
  87. package/dist/retrieve.js +14 -0
  88. package/dist/thread/basic.d.ts +15 -19
  89. package/dist/thread/basic.d.ts.map +1 -1
  90. package/dist/thread/filters.d.ts +8 -10
  91. package/dist/thread/filters.d.ts.map +1 -1
  92. package/dist/thread/indexes.d.ts +56 -0
  93. package/dist/thread/indexes.d.ts.map +1 -0
  94. package/dist/thread/mapped.d.ts +40 -11
  95. package/dist/thread/mapped.d.ts.map +1 -1
  96. package/dist/thread/utils.d.ts +5 -5
  97. package/dist/thread/utils.d.ts.map +1 -1
  98. package/dist/thread/writeable.d.ts +2 -2
  99. package/dist/thread/writeable.d.ts.map +1 -1
  100. package/dist/thread.d.ts +6 -5
  101. package/dist/thread.d.ts.map +1 -1
  102. package/dist/{thread.min.js → thread.js} +9 -6
  103. package/dist/types/typescript-utils.d.ts +6 -5
  104. package/dist/types/typescript-utils.d.ts.map +1 -1
  105. package/dist/types.d.ts +1 -1
  106. package/dist/types.d.ts.map +1 -1
  107. package/dist/{types.min.js → types.js} +3 -4
  108. package/dist/utils/debug-name.d.ts +13 -0
  109. package/dist/utils/debug-name.d.ts.map +1 -0
  110. package/dist/utils.d.ts +1 -1
  111. package/dist/utils.d.ts.map +1 -1
  112. package/dist/utils.js +9 -0
  113. package/package.json +32 -23
  114. package/src/applog/applog-helpers.ts +155 -0
  115. package/src/applog/applog-utils.test.ts +108 -0
  116. package/src/applog/applog-utils.ts +507 -0
  117. package/src/applog/datom-types.ts +148 -0
  118. package/src/applog.ts +3 -0
  119. package/src/blockstore/index.ts +36 -0
  120. package/src/blockstore.ts +1 -0
  121. package/src/index.ts +8 -0
  122. package/src/ipfs/car.ts +291 -0
  123. package/src/ipfs/fetch-snapshot-chain.ts +135 -0
  124. package/src/ipfs/ipfs-utils.ts +132 -0
  125. package/src/ipfs.ts +3 -0
  126. package/src/ipns/ipns-record.ts +115 -0
  127. package/src/ipns.ts +1 -0
  128. package/src/pubsub/UCAN Specs Overview.md +217 -0
  129. package/src/pubsub/connector.ts +9 -0
  130. package/src/pubsub/pub-pull.ts +31 -0
  131. package/src/pubsub/pubsub-types.ts +90 -0
  132. package/src/pubsub/snap-push.ts +277 -0
  133. package/src/pubsub/ucan-example.ts +61 -0
  134. package/src/pubsub/ucan.ts +56 -0
  135. package/src/pubsub.ts +4 -0
  136. package/src/query/attr-helpers.ts +5 -0
  137. package/src/query/basic.ts +1245 -0
  138. package/src/query/divergences.ts +50 -0
  139. package/src/query/entity-collection.ts +131 -0
  140. package/src/query/liveFilterAndMap.test.ts +102 -0
  141. package/src/query/matchers.ts +8 -0
  142. package/src/query/memoized.test.ts +151 -0
  143. package/src/query/memoized.ts +180 -0
  144. package/src/query/query-steps.ts +4 -0
  145. package/src/query/query.test.ts +538 -0
  146. package/src/query/situations.ts +261 -0
  147. package/src/query/subscribable.test.ts +245 -0
  148. package/src/query/subscribable.ts +234 -0
  149. package/src/query/types.ts +155 -0
  150. package/src/query/withoutDeleted.test.ts +204 -0
  151. package/src/query.ts +9 -0
  152. package/src/retrieve/index.ts +1 -0
  153. package/src/retrieve/update-thread.ts +248 -0
  154. package/src/retrieve.ts +1 -0
  155. package/src/test/perf/query.1m.perf.test.ts +94 -0
  156. package/src/test/perf/query.perf.test.ts +389 -0
  157. package/src/test/perf/query.realdata.perf.test.ts +182 -0
  158. package/src/thread/basic.ts +209 -0
  159. package/src/thread/filters.ts +227 -0
  160. package/src/thread/indexes.ts +250 -0
  161. package/src/thread/joinThreads.test.ts +304 -0
  162. package/src/thread/mapped.ts +226 -0
  163. package/src/thread/utils.ts +144 -0
  164. package/src/thread/writeable.ts +163 -0
  165. package/src/thread.ts +6 -0
  166. package/src/types/typescript-utils.ts +64 -0
  167. package/src/types.ts +1 -0
  168. package/src/utils/debug-name.ts +54 -0
  169. package/src/utils.ts +4 -0
  170. package/dist/chunk-2Y2PYHGR.min.js +0 -65
  171. package/dist/chunk-2Y2PYHGR.min.js.map +0 -1
  172. package/dist/chunk-5MMGBK2U.min.js +0 -1
  173. package/dist/chunk-7IDQIMQO.min.js +0 -1
  174. package/dist/chunk-BRC7LSM6.min.js.map +0 -1
  175. package/dist/chunk-COXXILXC.min.js +0 -512
  176. package/dist/chunk-COXXILXC.min.js.map +0 -1
  177. package/dist/chunk-GDX2OO7L.min.js +0 -9080
  178. package/dist/chunk-GDX2OO7L.min.js.map +0 -1
  179. package/dist/chunk-H3VQJP56.min.js.map +0 -1
  180. package/dist/chunk-HYMC7W6S.min.js +0 -1549
  181. package/dist/chunk-HYMC7W6S.min.js.map +0 -1
  182. package/dist/chunk-KEHU7HGZ.min.js +0 -5216
  183. package/dist/chunk-KEHU7HGZ.min.js.map +0 -1
  184. package/dist/chunk-KXMTKPF4.min.js.map +0 -1
  185. package/dist/chunk-PHITDXZT.min.js +0 -36
  186. package/dist/chunk-QO2KMGDN.min.js +0 -3771
  187. package/dist/chunk-QO2KMGDN.min.js.map +0 -1
  188. package/dist/chunk-QPGEBDMJ.min.js.map +0 -1
  189. package/dist/chunk-WXLCBTHX.min.js +0 -1606
  190. package/dist/chunk-WXLCBTHX.min.js.map +0 -1
  191. package/dist/ipns.min.js +0 -6419
  192. package/dist/ipns.min.js.map +0 -1
  193. package/dist/mobx/mobx-utils.d.ts +0 -82
  194. package/dist/mobx/mobx-utils.d.ts.map +0 -1
  195. package/dist/mobx.d.ts +0 -2
  196. package/dist/mobx.d.ts.map +0 -1
  197. package/dist/mobx.min.js +0 -141
  198. package/dist/retrieve.min.js +0 -17
  199. package/dist/types.min.js.map +0 -1
  200. package/dist/utils.min.js +0 -10
  201. package/dist/utils.min.js.map +0 -1
  202. /package/dist/{applog.min.js.map → applog.js.map} +0 -0
  203. /package/dist/{chunk-5MMGBK2U.min.js.map → chunk-7Z5YDQKK.js.map} +0 -0
  204. /package/dist/{chunk-7IDQIMQO.min.js.map → chunk-E46VTKTZ.js.map} +0 -0
  205. /package/dist/{chunk-PHITDXZT.min.js.map → index.js.map} +0 -0
  206. /package/dist/{index.min.js.map → ipfs.js.map} +0 -0
  207. /package/dist/{ipfs.min.js.map → pubsub.js.map} +0 -0
  208. /package/dist/{mobx.min.js.map → query.js.map} +0 -0
  209. /package/dist/{pubsub.min.js.map → retrieve.js.map} +0 -0
  210. /package/dist/{query.min.js.map → thread.js.map} +0 -0
  211. /package/dist/{retrieve.min.js.map → types.js.map} +0 -0
  212. /package/dist/{thread.min.js.map → utils.js.map} +0 -0
@@ -0,0 +1,277 @@
1
+ import * as dagJson from '@ipld/dag-json'
2
+ import { Logger } from 'besonders-logger'
3
+ import { CID } from 'multiformats/cid'
4
+ import stringify from 'safe-stable-stringify'
5
+ import { ensureTsPvAndFinalizeApplog } from '../applog/applog-helpers.ts'
6
+ import type {
7
+ Applog,
8
+ ApplogArrayMaybeEncrypted,
9
+ ApplogArrayMaybeEncryptedRO,
10
+ ApplogArrayNoCIDMaybeEncryptedRO,
11
+ ApplogEnc,
12
+ ApplogEncNoCid,
13
+ CidString,
14
+ } from '../applog/datom-types.ts'
15
+ import { BlockStoreish, DecodedCar, getDecodedBlock, makeCarBlob } from '../ipfs/car.ts'
16
+ import { encodeBlockOriginal, prepareForPub } from '../ipfs/ipfs-utils.ts'
17
+ import { lastWriteWins } from './../query/basic.ts'
18
+ import { ApplogsOrThread, getLogsFromThread, Thread } from '../thread.ts'
19
+ import { rollingFilter } from '../thread/filters.ts'
20
+ import { keepTruthy } from '../utils.ts'
21
+ import type { AppAgent, IShare, SnapBlockChunks, SnapBlockLogs, SnapBlockLogsOrChunks } from './pubsub-types.ts'
22
+
23
+ const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line no-unused-vars
24
+
25
+ // export const neverEncryptAttrs = [
26
+ // 'agent/jwkd',
27
+ // 'agent/appAgent',
28
+ // 'pub/encryptedFor',
29
+ // 'pub/sharedKey',
30
+ // ]
31
+
32
+ // export interface WovinPublicationInfo {
33
+ // id: string
34
+ // }
35
+
36
+ export async function prepareSnapshotForPush(
37
+ agent: AppAgent,
38
+ appThread: Thread,
39
+ threadToPublish: ApplogsOrThread,
40
+ share: IShare,
41
+ prevSnapCID: CID | null,
42
+ prevCounter: number | null,
43
+ ) {
44
+ if (prevCounter !== null && !prevSnapCID) {
45
+ throw ERROR('[prepareSnapshotForPush] prevCounter provided without prevSnapCID')
46
+ }
47
+ // TODO prevent publish if there is no new info
48
+ let logsToPublish = getLogsFromThread(threadToPublish)
49
+
50
+ // const logsFromLastPush = await getLogsFromPub(publication)
51
+ // logsToPublish = logsToPublish.filter(eachLog => !logsFromLastPush.includes(eachLog.cid)) // TODO deep compare includes
52
+ // const prevPushCIDs = [
53
+ // publication.lastCID,
54
+ // //TODO add this one and update the publication data after push
55
+ // ]
56
+ // const includedLogCIDs = [
57
+ // 'full array of CIDS from all previous pushes'
58
+ // ]
59
+
60
+ DEBUG(`[preparePubForPush] Collected ${logsToPublish.length} logs :`, {
61
+ logsToPublish,
62
+ threadOrLogsCount: (threadToPublish as any).nameAndSizeUntracked || (`[${(threadToPublish as any).length}]`),
63
+ })
64
+
65
+ const { sharedAgents, sharedKeyMap, sharedKey, pubCounter } = share ?? {}
66
+
67
+ const getExistingOrNewLog = (thread: Thread, share: IShare, ag: string, at: string, vl) => {
68
+ let logInQuestion = rollingFilter(lastWriteWins(thread), { en: share.id, at }).latestLog
69
+ if (!logInQuestion && vl !== undefined) {
70
+ logInQuestion = ensureTsPvAndFinalizeApplog({ ag, en: share.id, at, vl }, thread)
71
+ }
72
+ return logInQuestion // can be undefined if the passed vl is undefined and the log is not found
73
+ }
74
+ const shareNameLog = getExistingOrNewLog(appThread, share, agent.ag, 'share/name', share.name)
75
+
76
+ // ? using did as it is derived from the same ecdh in note3 and part of the minimal AppAgent type required here in wovin core
77
+ const shareCounterLog = getExistingOrNewLog(appThread, share, agent.ag, 'share/counter', `${agent.did}<::>${pubCounter}`)
78
+ // ? discuss if this works to bind the counter to a specific derivation key - did is not necessarily derived from the same key by all lib users
79
+
80
+ const encryptApplog = async (applog: Applog, keyToUse: CryptoKey): Promise<Uint8Array> => {
81
+ const { log: eachLog, cid } = prepareForPub(applog) // without cid
82
+ const enc = new TextEncoder()
83
+ const stringified = stringify(eachLog)
84
+ const stringifiedEncodedAppLogPayload = enc.encode(stringified) // TODO: consider encodeToDagJson instead
85
+ VERBOSE('[odd]', { eachLog, stringified, stringifiedEncodedAppLogPayload })
86
+
87
+ try {
88
+ // @ts-expect-error
89
+ const encPayload = await agent.crypto?.aes.encrypt(stringifiedEncodedAppLogPayload, keyToUse, 'AES-GCM')
90
+ // TODO get rid of odd down here
91
+ VERBOSE('[odd] encrypted length:', stringifiedEncodedAppLogPayload.length, { encPayload })
92
+ return encPayload
93
+ } catch (err) {
94
+ throw ERROR('FAILED TO ENC payload length:', stringifiedEncodedAppLogPayload.length, { err })
95
+ }
96
+
97
+ // const decrypted = await decryptWithAesSharedKey(encPayload, keyToUse, 'string')
98
+ }
99
+
100
+ let maybeEncryptedApplogs: ApplogEncNoCid[] | readonly Applog[]
101
+ const encryptedApplogs = [] as { enc: Uint8Array }[]
102
+ const agentSharedKeyLogs = []
103
+ if (sharedAgents) { // encrypt all Applogs
104
+ if (!sharedKey || !sharedKeyMap) {
105
+ throw ERROR('sharedAgents but no Keys/Map', { sharedAgents, sharedKeyMap, sharedKey })
106
+ }
107
+ VERBOSE('encrypting', { sharedAgents, sharedKeyMap })
108
+
109
+ for (const [eachAgent, eachEncKey] of Array.from(sharedKeyMap.entries())) {
110
+ VERBOSE('adding key', { eachAgent, eachEncKey })
111
+ agentSharedKeyLogs.push({
112
+ ag: agent.ag,
113
+ en: eachAgent,
114
+ at: 'share/sharedKey',
115
+ vl: eachEncKey, // these are encrypted with the derived key from the local agent private and remote agent public keys
116
+ })
117
+ }
118
+ // const encryptedForLogs = await insertApplogsInAppDB(agentSharedKeyLogs)
119
+ // DEBUG(`[publish] adding agentSharedKeyLogs:`, encryptedForLogs)
120
+ const CIDlist: { cid: CidString; encCID?: CidString }[] = []
121
+ const pubCIDmap: Record<CidString, typeof CIDlist> = {}
122
+ // TODO ensure that all needed keys are in
123
+ for (const eachLog of logsToPublish) {
124
+ VERBOSE('[crypto] encrypting ', { eachLog, sharedKey })
125
+ // try {
126
+ const encPayload = await encryptApplog(eachLog, sharedKey)
127
+ DEBUG('[crypto] encrypted ', { eachLog, encPayload, sharedKey })
128
+ // } catch (err) {
129
+ // // its already traced in encryptAndTestDecrypt
130
+ // // continue
131
+ // }
132
+ encryptedApplogs.push({ enc: encPayload })
133
+ }
134
+ maybeEncryptedApplogs = encryptedApplogs
135
+ } else {
136
+ maybeEncryptedApplogs = logsToPublish // publish nonEncrypted
137
+ }
138
+
139
+ DEBUG('adding all agent info and shareAtoms', {
140
+ share,
141
+ agent,
142
+ logsToPublish,
143
+ // threadToPublish, - very verbose
144
+ agentSharedKeyLogs,
145
+ })
146
+ const infoLogs = [
147
+ ...rollingFilter(lastWriteWins(appThread), { // TODO: use static filter for performance
148
+ en: agent.ag,
149
+ at: ['agent/ecdh', 'agent/jwkd', 'agent/appAgent'],
150
+ }).applogs,
151
+ ...(shareNameLog ? [shareNameLog] : []),
152
+ ...(shareCounterLog ? [shareCounterLog] : []),
153
+ ...agentSharedKeyLogs,
154
+ ]
155
+ DEBUG(`[prepareSnapshotForPush] info logs:`, infoLogs)
156
+ if (!infoLogs.find(({ at }) => at === 'agent/appAgent')) throw ERROR(`[prepareSnapshotForPush] appThread missing agent/appAgent log`)
157
+
158
+ const applogsToEncode = keepTruthy(maybeEncryptedApplogs)
159
+ const infologsToEncode = keepTruthy(infoLogs)
160
+ if (!applogsToEncode.length) {
161
+ throw ERROR('no valid applogs', { agent, maybeEncryptedApplogs, infoLogs, applogsToEncode, infologsToEncode, prevSnapCID })
162
+ }
163
+ if (!infologsToEncode.length) {
164
+ throw ERROR('no valid infologs', { agent, maybeEncryptedApplogs, infoLogs, applogsToEncode, infologsToEncode, prevSnapCID })
165
+ }
166
+ const encodedSnapshot = await encodeSnapshotAsCar(agent, applogsToEncode, infologsToEncode, prevSnapCID, prevCounter)
167
+ DEBUG('inPrepareSnapshotForPush', { encodedSnapshot })
168
+ return encodedSnapshot
169
+ }
170
+
171
+ /**
172
+ * @param applogs Encrypted or plain applogs
173
+ * @returns Car file
174
+ */
175
+ export async function encodeSnapshotAsCar(
176
+ agent: AppAgent,
177
+ applogs: ApplogArrayNoCIDMaybeEncryptedRO,
178
+ infoLogs: readonly Applog[],
179
+ prevSnapCID: CID | null,
180
+ prevCounter: number | null,
181
+ ) {
182
+ DEBUG(`[encodeSnapshotAsCar] encoding`, { agent, applogs, infoLogs })
183
+ const { cids: infoLogCids, encodedApplogs: encodedInfoLogs } = await encodeApplogsAsIPLD(infoLogs)
184
+ const { cids: applogCids, encodedApplogs } = await encodeApplogsAsIPLD(applogs)
185
+ let blocks = encodedApplogs.concat(encodedInfoLogs)
186
+ // We need to wrap the array to get a CID
187
+ const infoLogsWrap = await encodeBlockOriginal({ logs: infoLogCids })
188
+ blocks.push(infoLogsWrap)
189
+ const { rootCID: chunkRootCID, blocks: chunkBlocks } = await chunkApplogs(applogCids)
190
+ blocks = blocks.concat(chunkBlocks) // (i) concat bc. https://stackoverflow.com/a/51860949
191
+ const infoSignature = await agent.sign(infoLogsWrap.cid.bytes)
192
+ const applogsSignature = await agent.sign(chunkRootCID.bytes)
193
+ const root = {
194
+ info: infoLogsWrap.cid,
195
+ applogs: chunkRootCID,
196
+ infoSignature,
197
+ applogsSignature,
198
+ prev: prevSnapCID,
199
+ prevCounter: !prevSnapCID ? 0 : prevCounter !== null ? prevCounter + 1 : null,
200
+ }
201
+ DEBUG('[encodeSnapshotAsCar] encoding root', { root, logCids: applogCids, infoLogCids })
202
+ const encodedRoot = await encodeBlockOriginal(root)
203
+ blocks.push(encodedRoot)
204
+ DEBUG('[encodeSnapshotAsCar] => root', { encodedRoot })
205
+
206
+ return {
207
+ cid: encodedRoot.cid,
208
+ blob: await makeCarBlob(encodedRoot.cid, blocks), // TODO: create CarBuilder (incl .encodeAndAdd({...}))
209
+ blocks,
210
+ infoLogCids,
211
+ applogCids,
212
+ }
213
+ }
214
+
215
+ /** (i) IPFS has a block size limit of 1MB - which is about 15K CIDs */
216
+ export async function chunkApplogs(applogCids: CID<unknown, 297, 18, 1>[], size = 10000) {
217
+ if (!applogCids.length) throw ERROR(`[chunkApplogs] called with empty array`)
218
+ const chunks = []
219
+ // TODO: chunk by stable btree based on size or something like that
220
+ for (let i = 0; i < applogCids.length; i += size) {
221
+ const chunk = await encodeBlockOriginal({ logs: applogCids.slice(i, Math.min(i + applogCids.length, i + size)) })
222
+ chunks.push(chunk)
223
+ }
224
+ if (chunks.length === 1) return { rootCID: chunks[0].cid, blocks: chunks }
225
+ const root = await encodeBlockOriginal({ chunks: chunks.map(chunk => chunk.cid) })
226
+ const blocks = [root, ...chunks]
227
+ DEBUG(`[chunkApplogs] ${applogCids.length} logs chunked into ${chunks.length}`, { applogCids, root, blocks, chunks, dagJson })
228
+ return { rootCID: root.cid, blocks, chunks }
229
+ }
230
+ export async function unchunkApplogsBlock(block: SnapBlockLogsOrChunks, blockStore: BlockStoreish): Promise<CID[]> {
231
+ if (isSnapBlockChunks(block)) {
232
+ return (await Promise.all(
233
+ block.chunks.map(async (chunkCid) => {
234
+ const block = (await getDecodedBlock(blockStore, chunkCid)) as SnapBlockLogs
235
+ if (!block.logs) throw ERROR(`Weird chunk`, block)
236
+ return block.logs
237
+ }),
238
+ )).flat()
239
+ } else {
240
+ return block.logs
241
+ }
242
+ }
243
+ export function isSnapBlockChunks(block: SnapBlockLogsOrChunks): block is SnapBlockChunks {
244
+ return (block as any).chunks
245
+ }
246
+ /**
247
+ * @param applogs Encrypted or plain applogs
248
+ * @returns Car file
249
+ */
250
+ export async function encodeSnapshotApplogsAsCar(
251
+ applogs: ApplogArrayMaybeEncryptedRO,
252
+ ) {
253
+ const encoded = await encodeApplogsAsIPLD(applogs)
254
+ if (!encoded) throw ERROR('invalid applogs cannot continue', { applogs, encoded })
255
+ const { cids, encodedApplogs } = encoded
256
+ const root = { applogs: cids }
257
+ const encodedRoot = await encodeBlockOriginal(root)
258
+ DEBUG('[encodeSnapshotApplogsAsCar] encoded root', { cids, encodedRoot })
259
+
260
+ return await makeCarBlob(encodedRoot.cid, [encodedRoot, ...encodedApplogs])
261
+ }
262
+
263
+ async function encodeApplogsAsIPLD(applogs: ApplogArrayNoCIDMaybeEncryptedRO) {
264
+ DEBUG({ applogs })
265
+ const validApplogs = applogs.filter(eachLog => !!eachLog)
266
+ DEBUG({ validApplogs })
267
+ if (!validApplogs.length) throw ERROR('no valid applogs')
268
+ const preppedLogs = validApplogs.map(log => prepareForPub(log as Applog).log)
269
+ const encodedApplogs = await Promise.all(preppedLogs.map(encodeBlockOriginal))
270
+ DEBUG('[encodeApplogsAsIpld] encoded applogs', { preppedLogs, encodedApplogs })
271
+
272
+ const cids = encodedApplogs.map(b => {
273
+ if (!b.cid) throw ERROR(`[publish] no cid for encoded log:`, b)
274
+ return b.cid
275
+ })
276
+ return { cids, encodedApplogs }
277
+ }
@@ -0,0 +1,61 @@
1
+ import { Capability } from 'iso-ucan/capability'
2
+ import { Store } from 'iso-ucan/store'
3
+ import { MemoryDriver } from 'iso-kv/drivers/memory.js'
4
+ import { EdDSASigner } from 'iso-signatures/signers/eddsa.js'
5
+ import { z } from 'zod'
6
+ import { verify } from 'iso-signatures/verifiers/eddsa.js'
7
+ import { Resolver } from 'iso-signatures/verifiers/resolver.js'
8
+
9
+ export const verifierResolver = new Resolver({
10
+ Ed25519: verify,
11
+ })
12
+ // from:
13
+ //https://ucan.xyz/guides/examples/
14
+
15
+ // Initialize delegation store for tracking capability chains
16
+ // In production, this might be backed by a database or persistent storage
17
+ const store = new Store(new MemoryDriver())
18
+
19
+ // Define file read capability with path validation schema
20
+ // The schema ensures all invocations include a valid file path
21
+ const FileReadCap = Capability.from({
22
+ schema: z.object({
23
+ path: z.string(), // Required: file path to read
24
+ }),
25
+ cmd: '/file/read', // UCAN v1 command identifier
26
+ verifierResolver,
27
+ })
28
+
29
+ // Create cryptographic identities for resource owner and accessor
30
+ // In a real system, these would be persistent identity keypairs
31
+ const alice = await EdDSASigner.generate() // Resource owner
32
+ const bob = await EdDSASigner.generate() // Requesting access
33
+
34
+ const nowInSeconds = Math.floor(Date.now() / 1000)
35
+
36
+ // Alice grants Bob permission to read files
37
+ // This delegation can be stored, transmitted, or embedded in applications
38
+ const delegation = await FileReadCap.delegate({
39
+ iss: alice, // Alice issues this capability
40
+ aud: bob, // Bob is authorized to use it
41
+ sub: alice, // Alice's resources are the subject
42
+ pol: [], // No additional policy constraints
43
+ exp: nowInSeconds + 3600, // Expires in 1 hour for security
44
+ store,
45
+ })
46
+
47
+ // Store delegation to enable later invocation validation
48
+ // The store enables automatic delegation chain resolution
49
+ // await store.set(delegation)
50
+
51
+ // Bob exercises the delegated capability to read a specific file
52
+ // This creates a cryptographically verifiable access request
53
+ const invocation = await FileReadCap.invoke({
54
+ iss: bob, // Bob is invoking the capability
55
+ sub: alice, // Alice's system will process the request
56
+ args: {
57
+ path: '/documents/report.pdf' // Specific file Bob wants to read
58
+ },
59
+ store, // Store containing the delegation proof
60
+ exp: nowInSeconds + 300, // Invocation expires in 5 minutes
61
+ })
@@ -0,0 +1,56 @@
1
+ import type { DelegationOptions, DelegationPayload, PayloadBase, StandardSchemaV1 } from 'iso-ucan/types'
2
+ import { Delegation } from 'iso-ucan/delegation'
3
+ import { AgentID } from '../applog/datom-types.ts'
4
+ import { Capability } from 'iso-ucan/capability'
5
+ import { z } from 'zod/mini'
6
+ import { verify } from 'iso-signatures/verifiers/eddsa.js'
7
+ import { Resolver } from 'iso-signatures/verifiers/resolver.js'
8
+
9
+ export const verifierResolver = new Resolver({
10
+ Ed25519: verify,// TODO review if iso-signatures is better than web crypto (or if it even uses it)
11
+ })
12
+
13
+
14
+ const WovinRootCap = Capability.from({
15
+ cmd: '/wovin',
16
+ schema: z.object({
17
+ agentString: z.string(),
18
+ agentID: z.string(),
19
+ type: z.string(),
20
+ }),
21
+ verifierResolver,
22
+ })
23
+
24
+ const WovinPublishCap = Capability.from({
25
+ cmd: '/wovin/publish',
26
+ schema: z.object({
27
+ agentString: z.string(),
28
+ agentID: z.string(),
29
+ type: z.string(),
30
+ }),
31
+ verifierResolver,
32
+ })
33
+ const WovinDelegateCap = Capability.from({
34
+ cmd: '/wovin/delegate',
35
+ schema: z.object({
36
+ agentString: z.string(),
37
+ agentID: z.string(),
38
+ type: z.string(),
39
+ }),
40
+ verifierResolver,
41
+ })
42
+ export enum KnownCaps {
43
+ publish = 'WovinPublishCap',
44
+ delegate = 'cap_delegate',
45
+ }
46
+
47
+ type DelegationPayloadSchema = StandardSchemaV1<unknown, DelegationPayload<unknown>>
48
+ export type DelegationOptionsStandard = DelegationOptions<DelegationPayloadSchema>
49
+ export type UCANCapMap = Partial<Record<KnownCaps, DelegationOptionsStandard[]>>
50
+
51
+ export type AgentCapMap = Record<AgentID, UCANCapMap>
52
+
53
+ export const createDelegation = (delegationPayload:DelegationOptionsStandard) => {
54
+ // delegationPayload.
55
+ return Delegation.create(delegationPayload)
56
+ }
package/src/pubsub.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './pubsub/connector.ts'
2
+ export * from './pubsub/pub-pull.ts'
3
+ export * from './pubsub/snap-push.ts'
4
+ export * from './pubsub/pubsub-types.ts'
@@ -0,0 +1,5 @@
1
+ /** Strip everything up to and including the first `/` */
2
+ export type StripFirstPrefix<S extends string> = S extends `${string}/${infer Rest}` ? Rest : S
3
+
4
+ /** Strip a specific prefix `P/` from attribute string */
5
+ export type StripExplicitPrefix<S extends string, P extends string> = S extends `${P}/${infer Rest}` ? Rest : S