@ibgib/core-gib 0.0.8 → 0.0.11

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 (154) hide show
  1. package/README.md +5 -10
  2. package/dist/assumptions.respec.d.mts +2 -0
  3. package/dist/assumptions.respec.d.mts.map +1 -0
  4. package/dist/assumptions.respec.mjs +41 -0
  5. package/dist/assumptions.respec.mjs.map +1 -0
  6. package/dist/core-constants.d.mts +0 -2
  7. package/dist/core-constants.d.mts.map +1 -1
  8. package/dist/core-constants.mjs +0 -2
  9. package/dist/core-constants.mjs.map +1 -1
  10. package/dist/core-helper.respec.d.mts +2 -0
  11. package/dist/core-helper.respec.d.mts.map +1 -0
  12. package/dist/core-helper.respec.mjs +53 -0
  13. package/dist/core-helper.respec.mjs.map +1 -0
  14. package/dist/core-types.d.mts +22 -0
  15. package/dist/core-types.d.mts.map +1 -1
  16. package/dist/respec-gib.node.d.mts +2 -0
  17. package/dist/respec-gib.node.d.mts.map +1 -0
  18. package/dist/respec-gib.node.mjs +211 -0
  19. package/dist/respec-gib.node.mjs.map +1 -0
  20. package/dist/spec-helper.node.respec.d.mts +12 -0
  21. package/dist/spec-helper.node.respec.d.mts.map +1 -0
  22. package/dist/spec-helper.node.respec.mjs +43 -0
  23. package/dist/spec-helper.node.respec.mjs.map +1 -0
  24. package/dist/witness/app/app-base-v1.d.mts +2 -2
  25. package/dist/witness/app/app-base-v1.d.mts.map +1 -1
  26. package/dist/witness/app/app-base-v1.mjs +23 -141
  27. package/dist/witness/app/app-base-v1.mjs.map +1 -1
  28. package/dist/witness/app/app-constants.d.mts +2 -0
  29. package/dist/witness/app/app-constants.d.mts.map +1 -1
  30. package/dist/witness/app/app-constants.mjs +2 -0
  31. package/dist/witness/app/app-constants.mjs.map +1 -1
  32. package/dist/witness/app/app-helper.d.mts.map +1 -1
  33. package/dist/witness/app/app-helper.mjs +2 -1
  34. package/dist/witness/app/app-helper.mjs.map +1 -1
  35. package/dist/witness/app/app-types.d.mts +2 -1
  36. package/dist/witness/app/app-types.d.mts.map +1 -1
  37. package/dist/witness/robbot/robbot-base-v1.d.mts +12 -164
  38. package/dist/witness/robbot/robbot-base-v1.d.mts.map +1 -1
  39. package/dist/witness/robbot/robbot-base-v1.mjs +41 -657
  40. package/dist/witness/robbot/robbot-base-v1.mjs.map +1 -1
  41. package/dist/witness/robbot/robbot-helper.mjs +3 -3
  42. package/dist/witness/robbot/robbot-helper.mjs.map +1 -1
  43. package/dist/witness/robbot/robbot-helper.respec.d.mts +2 -0
  44. package/dist/witness/robbot/robbot-helper.respec.d.mts.map +1 -0
  45. package/dist/witness/robbot/robbot-helper.respec.mjs +106 -0
  46. package/dist/witness/robbot/robbot-helper.respec.mjs.map +1 -0
  47. package/dist/witness/robbot/robbot-types.d.mts +6 -3
  48. package/dist/witness/robbot/robbot-types.d.mts.map +1 -1
  49. package/dist/witness/robbot/robbot-types.mjs +1 -0
  50. package/dist/witness/robbot/robbot-types.mjs.map +1 -1
  51. package/dist/witness/space/filesystem-space/filesystem-space-v1.d.mts +1 -1
  52. package/dist/witness/space/filesystem-space/filesystem-space-v1.d.mts.map +1 -1
  53. package/dist/witness/space/filesystem-space/filesystem-space-v1.respec.d.mts +2 -0
  54. package/dist/witness/space/filesystem-space/filesystem-space-v1.respec.d.mts.map +1 -0
  55. package/dist/witness/space/filesystem-space/filesystem-space-v1.respec.mjs +107 -0
  56. package/dist/witness/space/filesystem-space/filesystem-space-v1.respec.mjs.map +1 -0
  57. package/dist/witness/space/filesystem-space/node-filesystem-space-v1.d.mts.map +1 -1
  58. package/dist/witness/space/filesystem-space/node-filesystem-space-v1.mjs +1 -1
  59. package/dist/witness/space/filesystem-space/node-filesystem-space-v1.mjs.map +1 -1
  60. package/dist/witness/space/filesystem-space/node-filesystem-space-v1.node.respec.d.mts +2 -0
  61. package/dist/witness/space/filesystem-space/node-filesystem-space-v1.node.respec.d.mts.map +1 -0
  62. package/dist/witness/space/filesystem-space/node-filesystem-space-v1.node.respec.mjs +129 -0
  63. package/dist/witness/space/filesystem-space/node-filesystem-space-v1.node.respec.mjs.map +1 -0
  64. package/dist/witness/space/inner-space/inner-space-v1.respec.d.mts +2 -0
  65. package/dist/witness/space/inner-space/inner-space-v1.respec.d.mts.map +1 -0
  66. package/dist/witness/space/inner-space/inner-space-v1.respec.mjs +56 -0
  67. package/dist/witness/space/inner-space/inner-space-v1.respec.mjs.map +1 -0
  68. package/dist/witness/space/metaspace/metaspace-base.d.mts +795 -0
  69. package/dist/witness/space/metaspace/metaspace-base.d.mts.map +1 -0
  70. package/dist/witness/space/metaspace/metaspace-base.mjs +3251 -0
  71. package/dist/witness/space/metaspace/metaspace-base.mjs.map +1 -0
  72. package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace-helper.d.mts +4 -0
  73. package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace-helper.d.mts.map +1 -0
  74. package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace-helper.mjs +117 -0
  75. package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace-helper.mjs.map +1 -0
  76. package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.d.mts +34 -0
  77. package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.d.mts.map +1 -0
  78. package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs +76 -0
  79. package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs.map +1 -0
  80. package/dist/witness/space/metaspace/metaspace-types.d.mts +580 -0
  81. package/dist/witness/space/metaspace/metaspace-types.d.mts.map +1 -0
  82. package/dist/witness/space/metaspace/metaspace-types.mjs +6 -0
  83. package/dist/witness/space/metaspace/metaspace-types.mjs.map +1 -0
  84. package/dist/witness/space/space-helper.d.mts.map +1 -1
  85. package/dist/witness/space/space-helper.mjs +2 -1
  86. package/dist/witness/space/space-helper.mjs.map +1 -1
  87. package/dist/witness/space/{space-spec-helper.d.mts → space-respec-helper.d.mts} +15 -5
  88. package/dist/witness/space/space-respec-helper.d.mts.map +1 -0
  89. package/dist/witness/space/space-respec-helper.mjs +186 -0
  90. package/dist/witness/space/space-respec-helper.mjs.map +1 -0
  91. package/dist/witness/space/space-types.d.mts +2 -1
  92. package/dist/witness/space/space-types.d.mts.map +1 -1
  93. package/dist/witness/space/space-types.mjs.map +1 -1
  94. package/dist/witness/witness-base-v1.d.mts.map +1 -1
  95. package/dist/witness/witness-base-v1.mjs +7 -7
  96. package/dist/witness/witness-base-v1.mjs.map +1 -1
  97. package/dist/witness/witness-cmd/witness-cmd-types.d.mts +31 -0
  98. package/dist/witness/witness-cmd/witness-cmd-types.d.mts.map +1 -0
  99. package/dist/witness/witness-cmd/witness-cmd-types.mjs +2 -0
  100. package/dist/witness/witness-cmd/witness-cmd-types.mjs.map +1 -0
  101. package/dist/witness/witness-helper.d.mts +8 -0
  102. package/dist/witness/witness-helper.d.mts.map +1 -1
  103. package/dist/witness/witness-helper.mjs +30 -0
  104. package/dist/witness/witness-helper.mjs.map +1 -1
  105. package/dist/witness/witness-types.d.mts +26 -36
  106. package/dist/witness/witness-types.d.mts.map +1 -1
  107. package/dist/witness/witness-with-context/witness-with-context-base-v1.d.mts +235 -0
  108. package/dist/witness/witness-with-context/witness-with-context-base-v1.d.mts.map +1 -0
  109. package/dist/witness/witness-with-context/witness-with-context-base-v1.mjs +742 -0
  110. package/dist/witness/witness-with-context/witness-with-context-base-v1.mjs.map +1 -0
  111. package/dist/witness/witness-with-context/witness-with-context-types.d.mts +17 -0
  112. package/dist/witness/witness-with-context/witness-with-context-types.d.mts.map +1 -0
  113. package/dist/witness/witness-with-context/witness-with-context-types.mjs +2 -0
  114. package/dist/witness/witness-with-context/witness-with-context-types.mjs.map +1 -0
  115. package/package.json +12 -15
  116. package/src/{assumptions.spec.mts → assumptions.respec.mts} +11 -5
  117. package/src/core-constants.mts +0 -2
  118. package/src/core-helper.respec.mts +71 -0
  119. package/src/core-types.mts +20 -0
  120. package/src/respec-gib.node.mts +199 -0
  121. package/src/witness/app/app-base-v1.mts +19 -212
  122. package/src/witness/app/app-constants.mts +3 -0
  123. package/src/witness/app/app-helper.mts +2 -1
  124. package/src/witness/app/app-types.mts +1 -1
  125. package/src/witness/robbot/robbot-base-v1.mts +39 -690
  126. package/src/witness/robbot/robbot-helper.mts +1 -1
  127. package/src/witness/robbot/{robbot-helper.spec.mts → robbot-helper.respec.mts} +39 -33
  128. package/src/witness/robbot/robbot-types.mts +5 -3
  129. package/src/witness/space/filesystem-space/filesystem-space-v1.mts +1 -1
  130. package/src/witness/space/filesystem-space/{filesystem-space-v1.spec.mts → filesystem-space-v1.respec.mts} +17 -8
  131. package/src/witness/space/filesystem-space/node-filesystem-space-v1.mts +3 -11
  132. package/src/witness/space/filesystem-space/{node-filesystem-space-v1.node.spec.mts → node-filesystem-space-v1.node.respec.mts} +18 -14
  133. package/src/witness/space/inner-space/{inner-space-v1.spec.mts → inner-space-v1.respec.mts} +17 -8
  134. package/src/witness/space/metaspace/metaspace-base.mts +3702 -0
  135. package/src/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace-helper.mts +116 -0
  136. package/src/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mts +88 -0
  137. package/src/witness/space/metaspace/metaspace-types.mts +614 -0
  138. package/src/witness/space/space-helper.mts +2 -1
  139. package/src/witness/space/{space-spec-helper.mts → space-respec-helper.mts} +76 -57
  140. package/src/witness/space/space-types.mts +2 -1
  141. package/src/witness/witness-base-v1.mts +8 -6
  142. package/src/witness/witness-cmd/witness-cmd-types.mts +38 -0
  143. package/src/witness/witness-helper.mts +31 -0
  144. package/src/witness/witness-types.mts +31 -41
  145. package/src/witness/witness-with-context/witness-with-context-base-v1.mts +809 -0
  146. package/src/witness/witness-with-context/witness-with-context-types.mts +24 -0
  147. package/tsconfig.json +1 -0
  148. package/dist/witness/space/space-spec-helper.d.mts.map +0 -1
  149. package/dist/witness/space/space-spec-helper.mjs +0 -179
  150. package/dist/witness/space/space-spec-helper.mjs.map +0 -1
  151. package/jasmine-browser.json +0 -18
  152. package/jasmine.json +0 -6
  153. package/src/core-helper.spec.mts +0 -64
  154. /package/src/{spec-helper.node.spec.mts → spec-helper.node.respec.mts} +0 -0
@@ -0,0 +1,3702 @@
1
+ import { Observable, ReplaySubject, Subscription, } from 'rxjs';
2
+ import { concatMap, } from 'rxjs/operators';
3
+
4
+ import { delay, getUUID, groupBy, pretty } from '@ibgib/helper-gib';
5
+
6
+ import { encrypt, decrypt, } from '@ibgib/encrypt-gib';
7
+ import { IbGib_V1, GIB, GIB_DELIMITER, } from '@ibgib/ts-gib/dist/V1/index.mjs';
8
+ import { IbGibAddr, TjpIbGibAddr, TransformResult, getIbGibAddr, } from '@ibgib/ts-gib';
9
+ import { Factory_V1 as factory } from '@ibgib/ts-gib/dist/V1/index.mjs';
10
+ import { getGib, getGibInfo, isPrimitive } from '@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs';
11
+
12
+ import {
13
+ DeleteIbGibOpts, DeleteIbGibResult,
14
+ GetIbGibOpts, GetIbGibResult,
15
+ PutIbGibOpts, PutIbGibResult
16
+ } from '../../../common/other/other-types.mjs';
17
+ import { TagIbGib_V1 } from '../../../common/tag/tag-types.mjs';
18
+ import {
19
+ ParticipantInfo, StatusCode, SyncSagaInfo, SyncSpaceOptionsData,
20
+ SyncSpaceOptionsIbGib, SyncSpaceResultIbGib, SyncStatusIbGib,
21
+ } from '../../../witness/space/outer-space/outer-space-types.mjs';
22
+ import { IbGibSpaceData, IbGibSpaceRel8ns, SpaceId } from '../../../witness/space/space-types.mjs';
23
+ import { IbGibSpaceAny } from '../../../witness/space/space-base-v1.mjs';
24
+ import { BootstrapIbGib } from '../../../witness/space/bootstrap/bootstrap-types.mjs';
25
+ import { IbGibTimelineUpdateInfo, SpecialIbGibType } from '../../../common/other/other-types.mjs';
26
+ import { RootData } from '../../../common/root/root-types.mjs';
27
+ import {
28
+ CiphertextData, CiphertextIbGib_V1, CiphertextRel8ns,
29
+ EncryptionData_V1, EncryptionIbGib, EncryptionInfo_EncryptGib,
30
+ SecretData_V1, SecretIbGib_V1, SecretInfo_Password
31
+ } from '../../../common/encrypt/encrypt-types.mjs';
32
+ import { RobbotIbGib_V1 } from '../../../witness/robbot/robbot-types.mjs';
33
+ import { AppIbGib_V1 } from '../../../witness/app/app-types.mjs';
34
+ import {
35
+ getTimelinesGroupedByTjp, getTjpAddr, hasTjp
36
+ } from '../../../common/other/ibgib-helper.mjs';
37
+ // import {
38
+ // getFnPrompt, getFnAlert, getFnPromptPassword
39
+ // } from '../../../common/prompt-functions.mjs';
40
+ import {
41
+ getValidatedBootstrapIbGib, getLocalSpace, execInSpaceWithLocking,
42
+ updateBootstrapIbGib, getSpaceArgMetadata, createTagIbGibAndSundry,
43
+ getConfigAddr, setConfigAddr, setCurrentRoot, rel8ToCurrentRoot,
44
+ rel8ToSpecialIbGib, registerNewIbGib, persistTransformResult, getFromSpace,
45
+ putInSpace, deleteFromSpace, getLatestAddrs, getTjpIbGib,
46
+ getSpecialIbGib, getSpecialRel8dIbGibs, /*createRobbotIbGib, */
47
+ trash, archive, parseSpaceIb,
48
+ } from '../../../witness/space/space-helper.mjs';
49
+ import { getDependencyGraph, GetDependencyGraphOptions } from '../../../common/other/graph-helper.mjs';
50
+ import { spaceNameIsValid, } from '../../../witness/space/space-helper.mjs';
51
+ import { RobbotPromptResult } from '../../../witness/robbot/robbot-types.mjs';
52
+ import { createNewRobbot, validateCommonRobbotData, validateCommonRobbotIbGib } from '../../../witness/robbot/robbot-helper.mjs';
53
+ import { AppPromptResult } from '../../../witness/app/app-types.mjs';
54
+ import { createNewApp, documentLocationIsAtWelcomePage } from '../../../witness/app/app-helper.mjs';
55
+ import { CommentIbGib_V1, UpdateCommentPromptResult } from '../../../common/comment/comment-types.mjs';
56
+ import { IbGibCacheService } from '../../../common/cache/cache-types.mjs';
57
+ import { BOOTSTRAP_DATA_KNOWN_SPACE_IDS_KEY, BOOTSTRAP_IBGIB_ADDR } from '../../../witness/space/bootstrap/bootstrap-constants.mjs';
58
+ import { DEFAULT_LOCAL_SPACE_DESCRIPTION, DEFAULT_LOCAL_SPACE_POLLING_INTERVAL_MS, DEFAULT_MAX_RETRIES_GET_DEPENDENCY_GRAPH_OUTERSPACE, DEFAULT_MS_BETWEEN_RETRIES_GET_DEPENDENCY_GRAPH_OUTERSPACE, DEFAULT_SECONDS_VALID_LOCAL, PERSIST_OPTS_AND_RESULTS_IBGIBS_DEFAULT, SYNC_SPACE_REL8N_NAME } from '../../../witness/space/space-constants.mjs';
59
+ import { IBGIB_BASE_DIR, IBGIB_BASE_SUBPATH, IBGIB_BIN_SUBPATH, IBGIB_DNA_SUBPATH, IBGIB_ENCODING, IBGIB_IBGIBS_SUBPATH, IBGIB_META_SUBPATH } from '../../../witness/space/filesystem-space/filesystem-constants.mjs';
60
+ import { DEFAULT_ENCRYPTION_HASH_ALGORITHM, DEFAULT_ENCRYPTION_INITIAL_RECURSIONS, DEFAULT_ENCRYPTION_RECURSIONS_PER_HASH, DEFAULT_ENCRYPTION_SALT_STRATEGY, ENCRYPTION_REL8N_NAME, SECRET_REL8N_NAME } from '../../../common/encrypt/encrypt-constants.mjs';
61
+ import { ROBBOT_REL8N_NAME } from '../../../witness/robbot/robbot-constants.mjs';
62
+ import { APP_REL8N_NAME } from '../../../witness/app/app-constants.mjs';
63
+ import { AUTOSYNC_ALWAYS_REL8N_NAME } from '../../../common/other/other-constants.mjs';
64
+ import {
65
+ MetaspaceInitializeOptions, CreateLocalSpaceOptions, DtoToSpaceFunction,
66
+ LocalSpaceFactoryFunction, ZeroSpaceFactoryFunction, MetaspaceFactory
67
+ } from './metaspace-types.mjs';
68
+ import { MetaspaceService, TempCacheEntry } from './metaspace-types.mjs';
69
+ import { InnerSpace_V1 } from '../../../witness/space/inner-space/inner-space-v1.mjs';
70
+ import { GLOBAL_LOG_A_LOT, GLOBAL_TIMER_NAME } from '../../../core-constants.mjs';
71
+
72
+ const logalot = GLOBAL_LOG_A_LOT || true;
73
+
74
+ /**
75
+ * All-purpose mega service (todo: which we'll need to break up!) to interact
76
+ * with ibgibs at the app/device level.
77
+ *
78
+ * This works with the local app user space.
79
+ *
80
+ * ## regarding special ibgibs
81
+ *
82
+ * Special ibgibs' behaviors are what hold in other apps configuration data.
83
+ * Of course the difference is that most special ibgibs can leverage the "on-chain"
84
+ * functionality of "regular" ibgibs.
85
+ *
86
+ * There are a couple meta ibgibs (which I also call "special"):
87
+ * * roots^gib
88
+ * * tracks other special root^gib ibgibs, which are like local app-level indexes.
89
+ * * tags^gib
90
+ * * tracks other special tag^gib ibgibs, which you can apply to any ibgib
91
+ * * latest^gib
92
+ * * tracks mappings between tjp -> latest ib^gib address
93
+ * * ephemeral (deletes past rel8ns and past ibGib frames)
94
+ * * ...
95
+ *
96
+ * ## regarding latest ibgibs
97
+ *
98
+ * The tjp (temporal junction point) defines atow the beginning of an ibGib
99
+ * timeline. it's like the birthday for an ibGib. (Or you can think if it as
100
+ * the id for the stream of ibgib frames in a given timeline.)
101
+ *
102
+ * The latest ibGib in that timeline is also special, because it's often what
103
+ * you want to work with.
104
+ *
105
+ * So ideally, when an ibgib, A, has a tjp A1, and it is updated to A2, A3, An
106
+ * via `mut8` and/or `rel8` transforms, that ibgib creates a single timeline.
107
+ * This service attempts to track the relationship between that starting tjp
108
+ * address and its corresponding latest frame in that timeline, i.e., A1 -> An.
109
+ *
110
+ * ### mapping persistence implementation details
111
+ *
112
+ * The latest ibGib service is backed by a special ibgib that maintains the
113
+ * mapping index. It does this by rel8-ing that special backing ibgib via the
114
+ * tjp pointer, e.g. [special latest ibgib^XXX000].rel8ns[A^TJP123] ===
115
+ * [A^N12345] . It does this via the ib^gib content address pointer, so this
116
+ * becomes a mapping from A^TJP123 to A^N12345.
117
+ *
118
+ * This backing ibGib is special (even for special ibGibs) in that:
119
+ * * it does not relate itself with the current root of the application
120
+ * * it does not maintain references to its past (i.e. rel8ns['past'] === [])
121
+ * * it DELETES its previous incarnation from the files service
122
+ *
123
+ * In other words, this service is meant to be as ephemeral as possible. I am
124
+ * keeping it as an ibGib and not some other data format (like straight in
125
+ * storage/some other db) because I've found this is often useful and what I end
126
+ * up doing anyway to leverage other ibgib behavior. For example, in the future
127
+ * it may be good to take snapshots, which is a simple copy operation of the
128
+ * file persistence.
129
+ *
130
+ * ### current naive implementation notes
131
+ *
132
+ * questions:
133
+ * * What do we want to do if we can't locate an ibGib record?
134
+ * * How/when do we want to alert the user/our own code that we've found
135
+ * multiple timelines for an ibGib with a tjp (usually a thing we want to
136
+ * avoid)?
137
+ * * Who do we want to notify when new ibGibs arrive?
138
+ * * How often do we want to check external sources for latest?
139
+ * * When do we get to merging ibGib timelines?
140
+ *
141
+ * This is behavior that is somewhat taken care of, e.g. in git, with the HEAD
142
+ * pointer for a repo. But we're talking about here basically as a metarepo or
143
+ * "repo of repos", and unlike git, we don't want our HEAD metadata living "off
144
+ * chain" (outside of the DLT itself that it's modifying). So eventually, what
145
+ * we want is just like what we want with ALL ibGibs: perspective. From "the
146
+ * app"'s perspective, the latest is mapped. But really, apps can't view slices
147
+ * of ibGib graphs in all sorts of interesting ways and still be productive &
148
+ * beneficial to the ecosystem as a whole.
149
+ */
150
+ export abstract class MetaspaceBase implements MetaspaceService {
151
+
152
+ // we won't get an object back, only a DTO ibGib essentially
153
+ protected lc: string = `[${MetaspaceBase.name}]`;
154
+
155
+ instanceId: string = (new Date()).toUTCString();
156
+
157
+ protected _initialized: boolean = false;
158
+ get initialized(): boolean { return this._initialized; }
159
+
160
+ protected _initializedSubj = new ReplaySubject<void>();
161
+ initialized$ = this._initializedSubj.asObservable();
162
+
163
+ protected _initializing: boolean = false;
164
+ get initializing(): boolean { return this._initializing; }
165
+
166
+ protected _latestSubj = new ReplaySubject<IbGibTimelineUpdateInfo>();
167
+ latestObs = this._latestSubj.asObservable();
168
+
169
+ /**
170
+ * gets the current local user space
171
+ */
172
+ async getLocalUserSpace({
173
+ lock = true,
174
+ localSpaceId,
175
+ }: {
176
+ /**
177
+ * If true, then we lock by bootstrap/spaceId before trying to retrieve.
178
+ *
179
+ * @default true
180
+ */
181
+ lock?: boolean,
182
+ /**
183
+ * If provided, will look for the space via this id in the bootstrap ibgib.
184
+ * If not provided, will use the bootstrap ibgib's default spaceId.
185
+ */
186
+ localSpaceId?: SpaceId,
187
+ }): Promise<IbGibSpaceAny | undefined> {
188
+ const lc = `${this.lc}[${this.getLocalUserSpace.name}]`;
189
+ try {
190
+ if (logalot) { console.log(`${lc} starting...`); }
191
+ debugger;
192
+
193
+ // if we're not explicit with skipLock, go by platform
194
+ // we only need to lock when doing the web, because we could
195
+ // have multiple tabs open.
196
+ lock ??= true;
197
+ // if (lock === undefined) {
198
+ // if (!this._platform) { this._platform = Capacitor.getPlatform(); }
199
+ // if (logalot) { console.log(`${this.lc} platform: ${this._platform}`); }
200
+ // lock = this._platform === 'web';
201
+ // }
202
+
203
+ const bootstrapIbGib =
204
+ await getValidatedBootstrapIbGib({ zeroSpace: this.zeroSpace }) ?? undefined;
205
+
206
+ const localSpace =
207
+ await getLocalSpace<IbGibSpaceAny>({
208
+ zeroSpace: this.zeroSpace,
209
+ bootstrapIbGib,
210
+ lock,
211
+ callerInstanceId: this.instanceId,
212
+ localSpaceId,
213
+ fnDtoToSpace: (spaceDto: IbGib_V1) => this.metaspaceFactory.fnDtoToSpace!(spaceDto),
214
+ localSpaceCacheSvc: this.cacheSvc,
215
+ });
216
+
217
+ return localSpace;
218
+ } catch (error) {
219
+ console.error(`${lc} ${error.message}`);
220
+ throw error;
221
+ } finally {
222
+ if (logalot) { console.log(`${lc} complete.`); }
223
+ }
224
+ }
225
+
226
+ /**
227
+ * gets all local user spaces known in bootstrap ibgib, according to
228
+ * spaceIds property
229
+ *
230
+ * (`bootstrapIbGib.data[BOOTSTRAP_DATA_KNOWN_SPACE_IDS_KEY]` atow)
231
+ *
232
+ * ## example bootstrap ibgib atow
233
+ *
234
+ ```json
235
+ {
236
+ "ib":"bootstrap",
237
+ "gib":"gib",
238
+ "data":{
239
+ "defaultSpaceId":"d455d9a72807617634ccbf1e532b71037c45762f824ec85fcd9a4c2275562f33",
240
+ "spaceIds":["d455d9a72807617634ccbf1e532b71037c45762f824ec85fcd9a4c2275562f33"]
241
+ },
242
+ "rel8ns":{
243
+ "d455d9a72807617634ccbf1e532b71037c45762f824ec85fcd9a4c2275562f33":[
244
+ "witness space IonicSpace_V1 oij d455d9a72807617634ccbf1e532b71037c45762f824ec85fcd9a4c2275562f33^B336251655E8C56B38E9E86F20E0E42E6C153785F1A0A798ADE6916E71CF055B"
245
+ ]
246
+ }
247
+ }
248
+ ```
249
+ *
250
+ * so this enumerates `data.spaceIds` and gets the corresponding addrs in the `rel8ns`.
251
+ * it then gets the space ibgibs themselves via the local zero space.
252
+ *
253
+ * @returns array of known local user spaces
254
+ *
255
+ * @throws if no local user spaces found (there should be at least one atow i think)
256
+ */
257
+ async getLocalUserSpaces({
258
+ lock = true,
259
+ }: {
260
+ /**
261
+ * If true, then we lock by bootstrap/spaceId before trying to retrieve.
262
+ *
263
+ * @default true
264
+ */
265
+ lock?: boolean,
266
+ }): Promise<IbGibSpaceAny[]> {
267
+ const lc = `${this.lc}[${this.getLocalUserSpaces.name}]`;
268
+ try {
269
+ if (logalot) { console.log(`${lc} starting...`); }
270
+
271
+ const localSpaces: IbGibSpaceAny[] = [];
272
+
273
+ // if we're not explicit with skipLock, go by platform
274
+ // we only need to lock when doing the web, because we could
275
+ // have multiple tabs open.
276
+ lock ??= true;
277
+ // if (lock === undefined) {
278
+ // if (!this._platform) { this._platform = Capacitor.getPlatform(); }
279
+ // if (logalot) { console.log(`${this.lc} platform: ${this._platform}`); }
280
+ // lock = this._platform === 'web';
281
+ // }
282
+
283
+ const bootstrapIbGib =
284
+ await getValidatedBootstrapIbGib({ zeroSpace: this.zeroSpace });
285
+
286
+ // #region validate bootstrapIbGib
287
+ if (!bootstrapIbGib) { throw new Error(`(UNEXPECTED) bootstrapIbGib falsy (E: fff2de921a3ee56a1d70e4ac320e4122)`); }
288
+ if (!bootstrapIbGib.data) { throw new Error(`(UNEXPECTED) bootstrapIbGib data falsy (E: 7f9d08050f214078b2f85dd2ed47e005)`); }
289
+ if (!bootstrapIbGib.data[BOOTSTRAP_DATA_KNOWN_SPACE_IDS_KEY]) { throw new Error(`(UNEXPECTED) bootstrapIbGib data invalid. data[${BOOTSTRAP_DATA_KNOWN_SPACE_IDS_KEY}] falsy (E: 37614e2e8f914d6ab7c605cf064a80d2)`); }
290
+ if (bootstrapIbGib.data[BOOTSTRAP_DATA_KNOWN_SPACE_IDS_KEY].length === 0) { throw new Error(`(UNEXPECTED) bootstrapIbGib data invalid. data[${BOOTSTRAP_DATA_KNOWN_SPACE_IDS_KEY}] length === 0 (E: d57691d17e6b4d7ea3a8fed36c0f47e5)`); }
291
+ // #endregion validate bootstrapIbGib
292
+
293
+ const localSpaceIds = bootstrapIbGib.data[BOOTSTRAP_DATA_KNOWN_SPACE_IDS_KEY];
294
+
295
+ for (let i = 0; i < localSpaceIds.length; i++) {
296
+ const localSpaceId = localSpaceIds[i];
297
+ if (!localSpaceId) { throw new Error(`(UNEXPECTED) localSpaceId falsy (E: 582c520897f5355d642229eae122ac22)`); }
298
+
299
+ const localSpace =
300
+ await getLocalSpace<IbGibSpaceAny>({
301
+ zeroSpace: this.zeroSpace,
302
+ bootstrapIbGib,
303
+ lock,
304
+ localSpaceId,
305
+ callerInstanceId: this.instanceId,
306
+ fnDtoToSpace: (spaceDto: IbGib_V1) => this.metaspaceFactory.fnDtoToSpace!(spaceDto),
307
+ // fnDtoToSpace: (spaceDto: IbGib_V1) => {
308
+ // return Promise.resolve(
309
+ // NodeFilesystemSpace_V1.createFromDto(spaceDto as IbGib_V1<NodeFilesystemSpaceData_V1, NodeFilesystemSpaceRel8ns_V1>)
310
+ // );
311
+ // },
312
+ localSpaceCacheSvc: this.cacheSvc,
313
+ });
314
+
315
+ localSpaces.push(localSpace);
316
+ }
317
+
318
+ return localSpaces;
319
+ } catch (error) {
320
+ console.error(`${lc} ${error.message}`);
321
+ throw error;
322
+ } finally {
323
+ if (logalot) { console.log(`${lc} complete.`); }
324
+ }
325
+ }
326
+
327
+ abstract get zeroSpace(): IbGibSpaceAny;
328
+ // {
329
+ // const lc = `${this.lc}[get zeroSpace]`;
330
+ // if (!this._zeroSpace) { throw new Error(`this._zeroSpace falsy. should have been created on this.initialize (E: e723177fd2a74e25eb25e2299dfa4d23)`); }
331
+ // return this._zeroSpace;
332
+ // }
333
+
334
+ // protected _localUserSpaceCurrentRoot: IbGib_V1<RootData> | undefined;
335
+
336
+ /**
337
+ * Just to prevent plaintext passwords from just sitting in memory,
338
+ * this is a slight layer of indirection for caching.
339
+ */
340
+ protected passwordCache: { [addr: string]: TempCacheEntry } = {};
341
+
342
+ // protected fnPromptSecret: (space: IbGibSpaceAny) => Promise<IbGib_V1 | undefined>;
343
+ // protected fnPromptEncryption: (space: IbGibSpaceAny) => Promise<IbGib_V1 | undefined>;
344
+ // protected fnPromptOuterSpace: (space: IbGibSpaceAny) => Promise<IbGib_V1 | undefined>;
345
+ // protected fnPromptUpdatePic: (space: IbGibSpaceAny, picIbGib: PicIbGib_V1) => Promise<UpdatePicPromptResult | undefined>;
346
+ // protected fnPromptUpdateComment: (space: IbGibSpaceAny, commentIbGib: CommentIbGib_V1) => Promise<UpdateCommentPromptResult | undefined>;
347
+ // fnPromptRobbot: ((space: IbGibSpaceAny, ibGib: RobbotIbGib_V1) => Promise<RobbotPromptResult | undefined>) | undefined = undefined;
348
+ // fnPromptApp: ((space: IbGibSpaceAny, ibGib: AppIbGib_V1) => Promise<AppPromptResult | undefined>) | undefined = undefined;
349
+
350
+ protected _syncing: boolean = false;
351
+ /**
352
+ * should only syncIbGibs when not already syncing.
353
+ */
354
+ get syncing(): boolean {
355
+ return this._syncing;
356
+ }
357
+
358
+ protected sagaInfoMap: { [sagaId: string]: SyncSagaInfo } = {};
359
+
360
+ /**
361
+ * unique set of tjp addresses that will auto sync to sync spaces.
362
+ */
363
+ protected _alwaysAutosyncTjpAddrsCache = new Set<TjpIbGibAddr>();
364
+
365
+ // /**
366
+ // * Cached platform value via `Capacitor.getPlatform()`.
367
+ // */
368
+ // protected _platform: string;
369
+
370
+ /**
371
+ * Hmm, hacky way of tracking if we're prompt the user for something.
372
+ * We don't want to prompt while we're already prompting, and this
373
+ * is a straight-forward approach.
374
+ *
375
+ * @see {@link getPasswordForSecrets}
376
+ */
377
+ protected isPrompting: boolean = false;
378
+
379
+ // #region sync uuids
380
+ /**
381
+ * getUUID is async. there are many times we want a throwaway id
382
+ * synchronously. So we'll prepopulate them here.
383
+ */
384
+ protected _precalculatedIds: string[] = [];
385
+ public getUUIDSync(): string {
386
+ const lc = `${this.lc}[${this.getUUIDSync.name}]`;
387
+ try {
388
+ if (logalot) { console.log(`${lc} starting...`); }
389
+ const id = this._precalculatedIds.pop();
390
+ if (!id) { throw new Error(`no precalculated ids? (E: 14915489b21d031ee9ca695737ac6923)`); }
391
+ if (this._precalculatedIds.length < 100) {
392
+ this.precalculateSomeUUIDsPlease(); // spins off
393
+ // only a race condition if we're consuming sync ids
394
+ // in a tight loop...
395
+ }
396
+ return id;
397
+ } catch (error) {
398
+ console.error(`${lc} ${error.message}`);
399
+ throw error;
400
+ } finally {
401
+ if (logalot) { console.log(`${lc} complete.`); }
402
+ }
403
+ }
404
+ async precalculateSomeUUIDsPlease(): Promise<void> {
405
+ const lc = `${this.lc}[${this.precalculateSomeUUIDsPlease.name}]`;
406
+ try {
407
+ if (logalot) { console.log(`${lc} starting...`); }
408
+ const ids: string[] = [];
409
+ for (let i = 0; i < 500; i++) {
410
+ const id = await getUUID();
411
+ ids.push(id);
412
+ }
413
+ this._precalculatedIds = ids;
414
+ } catch (error) {
415
+ console.error(`${lc} ${error.message}`);
416
+ throw error;
417
+ } finally {
418
+ if (logalot) { console.log(`${lc} complete.`); }
419
+ }
420
+ }
421
+ // #endregion sync uuids
422
+
423
+ protected metaspaceFactory: MetaspaceFactory = {};
424
+
425
+ /**
426
+ * used in alerting user of things... carryover of mvp. should be in initialize
427
+ */
428
+ protected getFnAlert: (() => (arg: { title: string, msg: string, }) => Promise<void>) | undefined;
429
+ /**
430
+ * used in prompting user for passwords... carryover of mvp. should be in initialize
431
+ */
432
+ protected getFnPrompt: (() => ((arg: { title: string, msg: string }) => Promise<string>)) | undefined;
433
+ /**
434
+ * used in prompting user for passwords... carryover of mvp. should be in initialize
435
+ */
436
+ protected getFnPromptPassword: (() => (title: string, msg: string) => Promise<string | null>) | undefined;
437
+
438
+ constructor(
439
+ protected cacheSvc: IbGibCacheService | undefined,
440
+ ) {
441
+ const lc = `${this.lc}[ctor]`;
442
+ if (logalot) { console.log(`${lc}${GLOBAL_TIMER_NAME}`); console.timeLog(GLOBAL_TIMER_NAME); }
443
+ console.log(`${lc} created.`);
444
+ }
445
+
446
+ protected async initializeMetaspaceFactory({ metaspaceFactory }: { metaspaceFactory: MetaspaceFactory }): Promise<void> {
447
+ const lc = `[${this.initializeMetaspaceFactory.name}]`;
448
+ try {
449
+ if (logalot) { console.log(`${lc} starting... (I: 0b84dfb68c1e89e0af880d4461fe4b23)`); }
450
+ this.metaspaceFactory = metaspaceFactory;
451
+ } catch (error) {
452
+ console.error(`${lc} ${error.message}`);
453
+ throw error;
454
+ } finally {
455
+ if (logalot) { console.log(`${lc} complete.`); }
456
+ }
457
+ }
458
+
459
+ async initialize({
460
+ spaceName,
461
+ metaspaceFactory,
462
+ getFnAlert,
463
+ getFnPrompt,
464
+ getFnPromptPassword,
465
+ }: MetaspaceInitializeOptions): Promise<void> {
466
+ const lc = `${this.lc}[${this.initialize.name}]`;
467
+ try {
468
+ this.instanceId = await getUUID();
469
+
470
+ if (!getFnAlert) { throw new Error(`getFnAlert required (E: c7ded289d9b6265b6d1294988faba923)`); }
471
+ if (!getFnPrompt) { throw new Error(`getFnPrompt required (E: cce7733db0abbf9a41fa45b6b9bf9523)`); }
472
+ if (!getFnPromptPassword) { throw new Error(`getFnPromptPassword required (E: cadae9f5df9a7c310e76b63cb6afb623)`); }
473
+ this.getFnAlert = getFnAlert;
474
+ this.getFnPrompt = getFnPrompt;
475
+ this.getFnPromptPassword = getFnPromptPassword;
476
+
477
+ let timerName: string = `${lc}[timer]`;
478
+ if (logalot) {
479
+ timerName = lc + '[timer 71cbfa]';
480
+ console.log(`${lc} starting timer ${timerName}`);
481
+ console.time(timerName);
482
+ }
483
+
484
+ await this.precalculateSomeUUIDsPlease();
485
+ if (logalot) { console.timeLog(timerName); }
486
+ await this.initializeMetaspaceFactory({ metaspaceFactory: metaspaceFactory ??= {} });
487
+
488
+ // local space(s)
489
+ await this.initializeLocalSpaces({ spaceName });
490
+ if (logalot) { console.timeLog(timerName); }
491
+
492
+ // flexibility for descending classes
493
+ await this.initializeSpecialIbGibs_required({ timerName });
494
+ await this.initializeSpecialIbGibs_outerspaces({ timerName });
495
+ await this.initializeSpecialIbGibs_other({ timerName });
496
+
497
+ if (logalot) {
498
+ console.timeEnd(timerName);
499
+ console.log(`${lc} timer ${timerName} complete.`);
500
+ }
501
+
502
+ this._initialized = true;
503
+ this._initializedSubj.next();
504
+
505
+ // always log when this is initialized (not just logalot)
506
+ console.log(`${lc} initialized (I: e3b3a9652c09445cb055013166ff089c)`);
507
+ } catch (error) {
508
+ console.error(`${lc} ${error.message}`);
509
+ throw error;
510
+ }
511
+ }
512
+
513
+ protected async initializeSpecialIbGibs_required({ timerName }: { timerName: string }): Promise<void> {
514
+ const lc = `[${this.initializeSpecialIbGibs_required.name}]`;
515
+ try {
516
+ if (logalot) { console.log(`${lc} starting... (I: 8cb7f63d766ab38885dc63d8da9cd323)`); }
517
+
518
+ await this.getSpecialIbGib({ type: "latest", initialize: true });
519
+ if (logalot) { console.timeLog(timerName); }
520
+
521
+ await this.getSpecialIbGib({ type: "roots", initialize: true });
522
+ if (logalot) { console.timeLog(timerName); }
523
+
524
+ await this.getSpecialIbGib({ type: "tags", initialize: true });
525
+ if (logalot) { console.timeLog(timerName); }
526
+
527
+ await this.getSpecialIbGib({ type: "robbots", initialize: true });
528
+ if (logalot) { console.timeLog(timerName); }
529
+
530
+ await this.getSpecialIbGib({ type: "apps", initialize: true });
531
+ if (logalot) { console.timeLog(timerName); }
532
+
533
+ await this.getSpecialIbGib({ type: "secrets", initialize: true });
534
+ if (logalot) { console.timeLog(timerName); }
535
+ } catch (error) {
536
+ console.error(`${lc} ${error.message}`);
537
+ throw error;
538
+ } finally {
539
+ if (logalot) { console.log(`${lc} complete.`); }
540
+ }
541
+ }
542
+
543
+ protected async initializeSpecialIbGibs_outerspaces({ timerName }: { timerName: string }): Promise<void> {
544
+ const lc = `[${this.initializeSpecialIbGibs_outerspaces.name}]`;
545
+ try {
546
+ if (logalot) { console.log(`${lc} starting... (I: 4f5ca25899384ceabd65e30cdf58f889)`); }
547
+
548
+ await this.getSpecialIbGib({ type: "encryptions", initialize: true });
549
+ if (logalot) { console.timeLog(timerName); }
550
+
551
+ await this.getSpecialIbGib({ type: "outerspaces", initialize: true });
552
+ if (logalot) { console.timeLog(timerName); }
553
+
554
+ await this.getSpecialIbGib({ type: "autosyncs", initialize: true });
555
+ if (logalot) { console.timeLog(timerName); }
556
+ await this.loadAutoSyncs();
557
+ } catch (error) {
558
+ console.error(`${lc} ${error.message}`);
559
+ throw error;
560
+ } finally {
561
+ if (logalot) { console.log(`${lc} complete.`); }
562
+ }
563
+ }
564
+
565
+ protected async initializeSpecialIbGibs_other({ timerName }: { timerName: string }): Promise<void> {
566
+ const lc = `[${this.initializeSpecialIbGibs_other.name}]`;
567
+ try {
568
+ if (logalot) { console.log(`${lc} starting... (I: 3cad0b16f5dac3686929dd1923ee3623)`); }
569
+ } catch (error) {
570
+ console.error(`${lc} ${error.message}`);
571
+ throw error;
572
+ } finally {
573
+ if (logalot) { console.log(`${lc} complete.`); }
574
+ }
575
+ }
576
+
577
+ /**
578
+ * Make sure we have a bootstrapped space. This is like a node...kinda in peer
579
+ * to peer systems.
580
+ *
581
+ * For all intents and purposes here to begin with, I'm using this initially
582
+ * as where to put the settings ibGib that will contain pointers for this app, be it an
583
+ * ionic android/web app or a browser extension. This is because the initial
584
+ * intent of doing the spaces, besides it being necessary for the distributed
585
+ * nature of the architecture, is to obviate the use of ionic Storage.
586
+ * That is currently where we are storing things like the pointers to special
587
+ * ibGibs like tags^ibgib, roots ibgibs, etc.
588
+ */
589
+ protected async initializeLocalSpaces({
590
+ spaceName,
591
+ }: {
592
+ spaceName: string | undefined,
593
+ }): Promise<void> {
594
+ const lc = `${this.lc}[${this.initializeLocalSpaces.name}]`;
595
+ try {
596
+ if (logalot) { console.log(`${lc} starting...`); }
597
+
598
+ // we're going to use the default space first to find/load the actual user's space (if it exists)
599
+ let zeroSpace = this.zeroSpace;
600
+ if (!zeroSpace.gib) {
601
+ zeroSpace.gib = await getGib({ ibGib: zeroSpace, hasTjp: false });
602
+ }
603
+
604
+ if (logalot) { console.log(`${lc} getting bootstrap ibgib (I: a4ad1cfd5c6f895e879d9e6a5f607b22)`); }
605
+ // first call without locking, because we just want to see if it exists.
606
+ // note that because of race conditions, this may be out of date though.
607
+ const bootstrapAddr = BOOTSTRAP_IBGIB_ADDR;
608
+ let bootstrapIbGib = await getValidatedBootstrapIbGib({ zeroSpace });
609
+ if (bootstrapIbGib) {
610
+ // re-get it using locking to ensure we've gotten the correct one.
611
+ if (logalot) { console.log(`${lc} getting bootstrap ibgib with locking from zero space...`) }
612
+ bootstrapIbGib = await execInSpaceWithLocking<BootstrapIbGib>({
613
+ space: zeroSpace,
614
+ scope: bootstrapAddr,
615
+ secondsValid: DEFAULT_SECONDS_VALID_LOCAL,
616
+ fn: async () => {
617
+ const bootstrapIbGib = await getValidatedBootstrapIbGib({ zeroSpace });
618
+ if (!bootstrapIbGib) { throw new Error(`failed to get validated bootstrapIbGib (E: a4318a8183c3d8979981c29bd4ecb223)`); }
619
+ return bootstrapIbGib;
620
+ },
621
+ callerInstanceId: this.instanceId,
622
+ });
623
+ if (logalot) { console.log(`${lc} got bootstrap ibgib with locking from zero space.`) }
624
+ } else {
625
+ if (logalot) { console.log(`${lc} getting from default space...not found. bootstrap space not found.`); }
626
+ // bootstrap space ibgib not found, so first run probably for user.
627
+ // so create a new bootstrapGib and user space
628
+ await this.createLocalSpaceAndUpdateBootstrap({
629
+ zeroSpace,
630
+ allowCancel: false,
631
+ createBootstrap: true,
632
+ spaceName,
633
+ });
634
+ }
635
+
636
+ } catch (error) {
637
+ if (logalot) { console.log(`${lc} getting from default space...not found. bootstrap space not found.`); }
638
+ console.error(`${lc} ${error.message}`);
639
+ throw error;
640
+ } finally {
641
+ if (logalot) { console.log(`${lc} complete.`); }
642
+ }
643
+ }
644
+
645
+ // #region create functions
646
+
647
+ /**
648
+ * wrapping dispatch factory function for creating local spaces. If a factory
649
+ * functionis provided, will use that. If not, then will use
650
+ * this.fnDefaultLocalSpaceFactory function. Ifthat is also falsy, then this
651
+ * will default to the NodeFSSpace factory function
652
+ * `fnCreateNewLocalSpace_NodeFSSpace`.
653
+ *
654
+ * @returns newly created local space, or undefined if there was a problem.
655
+ */
656
+ createNewLocalSpace({
657
+ opts,
658
+ fnLocalSpaceFactory,
659
+ }: {
660
+ opts: CreateLocalSpaceOptions;
661
+ fnLocalSpaceFactory?: LocalSpaceFactoryFunction;
662
+ }): Promise<IbGibSpaceAny | undefined> {
663
+ const lc = `[${this.createNewLocalSpace.name}]`;
664
+ try {
665
+ if (logalot) { console.log(`${lc} starting... (I: ac652df9d652459d98832be8483544d1)`); }
666
+ if (!opts) { throw new Error(`opts required (E: f1d50207b35c96985ba6c2e1a18a4e23)`); }
667
+ fnLocalSpaceFactory ??= this.metaspaceFactory.fnDefaultLocalSpaceFactory;
668
+ if (!fnLocalSpaceFactory) {
669
+ throw new Error(`neither fnLocalSpaceFactory nor this.fnDefaultLocalSpaceFactory provided (E: 5142df9dbae63fd0ffef8165782dc423)`);
670
+ }
671
+ return fnLocalSpaceFactory(opts);
672
+ } catch (error) {
673
+ console.error(`${lc} ${error.message}`);
674
+ throw error;
675
+ } finally {
676
+ if (logalot) { console.log(`${lc} complete.`); }
677
+ }
678
+ }
679
+
680
+ async createLocalSpaceAndUpdateBootstrap({
681
+ zeroSpace,
682
+ allowCancel,
683
+ createBootstrap,
684
+ spaceName,
685
+ }: {
686
+ zeroSpace: IbGibSpaceAny,
687
+ allowCancel: boolean,
688
+ createBootstrap: boolean,
689
+ spaceName: string | undefined,
690
+ }): Promise<IbGibSpaceAny | undefined> {
691
+ const lc = `${this.lc}[${this.createLocalSpaceAndUpdateBootstrap.name}]`;
692
+ try {
693
+ let newLocalSpace: IbGibSpaceAny | undefined;
694
+
695
+ if (allowCancel) {
696
+ newLocalSpace = await this.createNewLocalSpace({
697
+ opts: {
698
+ allowCancel, spaceName, logalot, getFnPrompt: this.getFnPrompt!,
699
+ },
700
+ });
701
+ if (!newLocalSpace) {
702
+ // cancelled
703
+ return undefined; /* <<<< returns early */
704
+ }
705
+ } else {
706
+ // can't cancel (probably first run of app)
707
+ do {
708
+ newLocalSpace = await this.createNewLocalSpace({
709
+ opts: {
710
+ allowCancel, spaceName, logalot,
711
+ getFnPrompt: this.getFnPrompt!,
712
+ }
713
+ });
714
+ } while (!newLocalSpace)
715
+ }
716
+
717
+ // create bootstrap if needed
718
+ if (createBootstrap) {
719
+ if (logalot) { console.log(`${lc} creating new bootstrap ibgib (I: ecc58dd4af21a0c69a16b3d71dad9c22)`); }
720
+ await updateBootstrapIbGib({
721
+ space: newLocalSpace,
722
+ zeroSpace,
723
+ setSpaceAsDefault: true,
724
+ createIfNotFound: true,
725
+ });
726
+ }
727
+
728
+ /** put arg used in both put calls to zero space and new local space. */
729
+ const argPutUserSpace = await zeroSpace.argy({
730
+ ibMetadata: getSpaceArgMetadata({ space: newLocalSpace }),
731
+ argData: {
732
+ cmd: 'put',
733
+ isMeta: true,
734
+ ibGibAddrs: [getIbGibAddr({ ibGib: newLocalSpace })],
735
+ },
736
+ ibGibs: [newLocalSpace],
737
+ });
738
+
739
+ // save the localSpace in default space
740
+ if (logalot) { console.log(`${lc} save the localSpace in default zero space (I: 1b34fb9f548042ab9d9ae0619377e370)`); }
741
+ const resZeroSpace = await zeroSpace.witness(argPutUserSpace);
742
+ if (resZeroSpace?.data?.success) {
743
+ if (logalot) { console.log(`${lc} default zero space witnessed the user space (I: b6314ea887d34609904db3bb76ae2f9b)`); }
744
+ } else {
745
+ throw new Error(`${resZeroSpace?.data?.errors?.join('|') || "There was a problem with zeroSpace witnessing the new localSpace (E: b952e888a4954cc5b671034b8384e750)"}`);
746
+ }
747
+
748
+ // save the localSpace in its own space(?)
749
+ if (logalot) { console.log(`${lc} save the new localSpace in its own space (I: e1f520f2cc614b1fa9c1ebd1c4dd6957)`); }
750
+ const resPutUserSpaceInUserSpace = await newLocalSpace.witness(argPutUserSpace);
751
+ if (resPutUserSpaceInUserSpace?.data?.success) {
752
+ // we now have saved the localSpace ibgib "in" its own space.
753
+ // but atow, this does NOT change the space's gib hash, so no
754
+ // need to update the space
755
+ if (logalot) { console.log(`${lc} user space witnessed itself`); }
756
+ } else {
757
+ throw new Error(`${resPutUserSpaceInUserSpace?.data?.errors?.join('|') || "There was a problem with localSpace witnessing itself. (E: 33d4b1ffcca64160afe67046531958b5)"}`);
758
+ }
759
+
760
+ // update the bootstrap ibgib to point to the new local space
761
+ await updateBootstrapIbGib({
762
+ space: newLocalSpace,
763
+ zeroSpace: this.zeroSpace,
764
+ });
765
+
766
+ return newLocalSpace;
767
+ } catch (error) {
768
+ console.error(`${lc} ${error.message}`);
769
+ const alert = this.getFnAlert!();
770
+ alert({ title: 'failed', msg: `failed to initialize the local space. error: ${error.message}` });
771
+ throw error;
772
+ }
773
+ }
774
+
775
+ async createTagIbGib({
776
+ text,
777
+ icon,
778
+ description,
779
+ space,
780
+ }: {
781
+ text: string,
782
+ icon?: string,
783
+ description?: string,
784
+ space?: IbGibSpaceAny,
785
+ }): Promise<{ newTagIbGib: TagIbGib_V1, newTagsAddr: string }> {
786
+ const lc = `${this.lc}[${this.createTagIbGib.name}]`;
787
+ try {
788
+ space = space ?? await this.getLocalUserSpace({});
789
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (E: 18f846b645124210a2ff1611641a8daf)`); }
790
+
791
+ return createTagIbGibAndSundry({
792
+ text,
793
+ icon,
794
+ description,
795
+ space,
796
+ zeroSpace: this.zeroSpace,
797
+ fnBroadcast: (x) => this.fnBroadcast(x),
798
+ fnUpdateBootstrap: (x) => this.fnUpdateBootstrap(x),
799
+ });
800
+ } catch (error) {
801
+ console.error(`${lc} ${error.message}`);
802
+ throw error;
803
+ }
804
+ }
805
+
806
+ async trash({
807
+ ibGib_Context,
808
+ rel8nName_Context,
809
+ addr,
810
+ space,
811
+ }: {
812
+ ibGib_Context: IbGib_V1,
813
+ rel8nName_Context: IbGibAddr,
814
+ addr: IbGibAddr,
815
+ space?: IbGibSpaceAny,
816
+ }): Promise<void> {
817
+ const lc = `${this.lc}[${this.trash.name}]`;
818
+ try {
819
+ if (logalot) { console.log(`${lc} starting... (I: 4cf73be6e3294124a78a4a45368bfbcc)`); }
820
+ if (!ibGib_Context) { throw new Error(`ibGib_Context required (E: 09dd5e5a9f784953a42d70b1827ba442)`); }
821
+ if (!rel8nName_Context) { throw new Error(`rel8nName_Context required (E: cb6edaaac586773b08fd18855fc11322)`); }
822
+ if (!addr) { throw new Error(`addr required (E: 14f27805749c499098cebc0b1b11bc57)`); }
823
+
824
+ space = space ?? await this.getLocalUserSpace({});
825
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (E: f81574e3437a4b88acd044b2abdc1ae4)`); }
826
+
827
+ await trash({
828
+ ibGib_Context,
829
+ rel8nName_Context,
830
+ addr,
831
+ space,
832
+ zeroSpace: this.zeroSpace,
833
+ fnBroadcast: (x) => this.fnBroadcast(x),
834
+ fnUpdateBootstrap: (x) => this.fnUpdateBootstrap(x),
835
+ });
836
+ // Toast.show({ text: `trashed '${addr.slice(0, 32)}...'`, duration: "long" }); // spins off...
837
+ } catch (error) {
838
+ console.error(`${lc} ${error.message}`);
839
+ throw error;
840
+ } finally {
841
+ if (logalot) { console.log(`${lc} complete.`); }
842
+ }
843
+ }
844
+
845
+ async archive({
846
+ ibGib_Context,
847
+ rel8nName_Context,
848
+ addr,
849
+ space,
850
+ }: {
851
+ ibGib_Context: IbGib_V1,
852
+ rel8nName_Context: IbGibAddr,
853
+ addr: IbGibAddr,
854
+ space?: IbGibSpaceAny,
855
+ }): Promise<void> {
856
+ const lc = `${this.lc}[${this.archive.name}]`;
857
+ try {
858
+ if (logalot) { console.log(`${lc} starting... (I: 785b44dbb7b048cb8c210a907b73b4c8)`); }
859
+ if (!ibGib_Context) { throw new Error(`ibGib_Context required (E: dc277b47aa4f42118ad14bfc817bf1d5)`); }
860
+ if (!rel8nName_Context) { throw new Error(`rel8nName_Context required (E: 87bd7ffa820645e9bdfb17fc726249ba)`); }
861
+ if (!addr) { throw new Error(`addr required (E: f6ce4e1325c84aa9b3da31c5b9dcc787)`); }
862
+
863
+ space = space ?? await this.getLocalUserSpace({});
864
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (E: d6e0b1618eec400e820de6ac37491d39)`); }
865
+
866
+ await archive({
867
+ ibGib_Context,
868
+ rel8nName_Context,
869
+ addr,
870
+ space,
871
+ zeroSpace: this.zeroSpace,
872
+ fnBroadcast: (x) => this.fnBroadcast(x),
873
+ fnUpdateBootstrap: (x) => this.fnUpdateBootstrap(x),
874
+ });
875
+
876
+ // Toast.show({ text: `archived '${addr.slice(0, 32)}...'`, duration: "long" }); // spins off...
877
+ if (logalot) { console.log(`${lc} archived '${addr.slice(0, 32)}...' (I: 767b06c8cadd58c618924f0cd98a1723)`); }
878
+ } catch (error) {
879
+ console.error(`${lc} ${error.message}`);
880
+ throw error;
881
+ } finally {
882
+ if (logalot) { console.log(`${lc} complete.`); }
883
+ }
884
+ }
885
+
886
+ // #endregion
887
+
888
+ /**
889
+ * Gets a config addr from the current space via the given key
890
+ * as the space's rel8n name.
891
+ *
892
+ * For example, for `key = tags`, a space may look like:
893
+ *
894
+ * ```json
895
+ * {
896
+ * ib: space xyz,
897
+ * gib: e89ff8a1c4954db28432007615a78952,
898
+ * rel8ns: {
899
+ * past: [space xyz^21cb29cc353f45a491d2b49ff2f130db],
900
+ * ancestor: [space^gib],
901
+ * tags: [tags^99b388355f8f4a979ca30ba284d3a686], // <<< rel8n with name specified by key
902
+ * }
903
+ * }
904
+ * ```
905
+ *
906
+ * @param key config key
907
+ * @returns addr in config if exists, else undefined
908
+ */
909
+ async getConfigAddr({
910
+ key,
911
+ space,
912
+ }: {
913
+ key: string,
914
+ space?: IbGibSpaceAny,
915
+ }): Promise<string | undefined> {
916
+ const lc = `${this.lc}[${this.getConfigAddr.name}](${key})`;
917
+ try {
918
+ if (logalot) { console.log(`${lc} getting...`) }
919
+
920
+ space = space ?? await this.getLocalUserSpace({});
921
+ if (!space) { throw new Error(`space falsy and could not get local space. (E: b6e7fcc09c6244b99b2e6e6ece398db0)`); }
922
+
923
+ return getConfigAddr({ key, space });
924
+
925
+ } catch (error) {
926
+ console.error(`${lc} ${error.message}`);
927
+ return undefined;
928
+ }
929
+ }
930
+
931
+ fnUpdateBootstrap = async (newSpace: IbGibSpaceAny) => {
932
+ const space = await this.getLocalUserSpace({});
933
+ await updateBootstrapIbGib({ space: newSpace, zeroSpace: this.zeroSpace });
934
+ }
935
+
936
+ fnBroadcast = (info: IbGibTimelineUpdateInfo) => {
937
+ // if (!getIbAndGib({ibGibAddr: info.latestAddr}).gib?.includes('.')) {
938
+ // need to know why tjp's continue to get published as the latest addrs
939
+ // }
940
+ // this._latestSubj.next({tjpAddr, latestAddr: ibGibAddr, latestIbGib: ibGib});
941
+ // let gib = getIbAndGib({ibGibAddr: info.latestAddr}).gib;
942
+ this._latestSubj.next(info);
943
+ }
944
+
945
+ /**
946
+ * Takes the given `key`, which should be unique in the given space (or
947
+ * zerospace), and uses that to persist the given `addr` in that space.
948
+ *
949
+ * # notes
950
+ *
951
+ * configuration is stored in "special" ibgibs, and so we need only to persist
952
+ * the address of that configuration.
953
+ */
954
+ async setConfigAddr({
955
+ key,
956
+ addr,
957
+ space,
958
+ }: {
959
+ key: string,
960
+ addr: string,
961
+ space?: IbGibSpaceAny,
962
+ }): Promise<IbGibSpaceAny> {
963
+ const lc = `${this.lc}[${this.setConfigAddr.name}]`;
964
+ try {
965
+ space = space ?? await this.getLocalUserSpace({});
966
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: 2149c59ebd584a118304dd54656aa800)`); }
967
+
968
+ return await setConfigAddr({
969
+ key, addr, space, zeroSpace: this.zeroSpace,
970
+ fnUpdateBootstrap: (x) => this.fnUpdateBootstrap(x),
971
+ });
972
+
973
+ } catch (error) {
974
+ console.error(`${lc} ${error.message}`);
975
+ throw error;
976
+ }
977
+ }
978
+
979
+ async getCurrentRoot({
980
+ space,
981
+ }: {
982
+ space?: IbGibSpaceAny,
983
+ }): Promise<IbGib_V1<RootData> | undefined> {
984
+ const lc = `${this.lc}[${this.getCurrentRoot.name}]`;
985
+
986
+ try {
987
+ space = space ?? await this.getLocalUserSpace({});
988
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: 1f4dc5e9560341a993bf0b28accd75fe)`); }
989
+
990
+ while (this.initializing) {
991
+ if (logalot) { console.log(`${lc} hacky wait while initializing ibgibs service (I: 5fd759510e584cb69b232259b891cca1)`); }
992
+ await delay(100);
993
+ }
994
+ const roots = await this.getSpecialIbGib({ type: "roots", space });
995
+ if (!roots) { throw new Error(`Roots not initialized. (E: e7712dc3d183487e98cd44a2b4324bc2)`); }
996
+ if (!roots.rel8ns) { throw new Error(`Roots not initialized properly. No rel8ns. (E: 689f47f5d1da4a868d1c1ddd2ff13e17)`); }
997
+ if (!roots.rel8ns.current) { throw new Error(`Roots not initialized properly. No current root. (E: 962acd3f60474a329bfbd7682c003916)`); }
998
+ if (roots.rel8ns.current.length === 0) { throw new Error(`Invalid Roots: empty current root rel8n. (E: fbdc13c157514efa86ade1bf9a38bbd6)`); }
999
+ if (roots.rel8ns.current.length > 1) { throw new Error(`Invalid Roots: multiple current roots selected. (E: 370fe6e3920a4a1299f879e6fcbbc448)`); }
1000
+
1001
+ const currentRootAddr = roots.rel8ns.current[0]!;
1002
+ const resCurrentRoot =
1003
+ await this.get({ addr: currentRootAddr, isMeta: true, space });
1004
+ if (resCurrentRoot.ibGibs?.length === 1) {
1005
+ return resCurrentRoot.ibGibs![0] as IbGib_V1<RootData>;
1006
+ } else {
1007
+ throw new Error(`could not get current root. addr: ${currentRootAddr} (E: 69de35c5f71e45a1a5d83228e136642b)`);
1008
+ }
1009
+ } catch (error) {
1010
+ console.error(`${lc} ${error.message}`);
1011
+ return undefined;
1012
+ }
1013
+ }
1014
+
1015
+ async setCurrentRoot({
1016
+ root,
1017
+ space,
1018
+ }: {
1019
+ root: IbGib_V1<RootData>,
1020
+ space?: IbGibSpaceAny,
1021
+ }): Promise<void> {
1022
+ const lc = `${this.lc}[${this.setCurrentRoot.name}]`;
1023
+ try {
1024
+ if (!root) { throw new Error(`root required.`); }
1025
+
1026
+ space = space ?? await this.getLocalUserSpace({});
1027
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: 222a3f5b39fc47ccb2a4aa3ddcd49969)`); }
1028
+
1029
+ return setCurrentRoot({
1030
+ root,
1031
+ space,
1032
+ zeroSpace: this.zeroSpace,
1033
+ fnBroadcast: (x) => this.fnBroadcast(x),
1034
+ fnUpdateBootstrap: (x) => this.fnUpdateBootstrap(x),
1035
+ });
1036
+
1037
+ // how to let others know roots has changed?
1038
+ } catch (error) {
1039
+ console.error(`${lc} ${error.message}`);
1040
+ throw error;
1041
+ }
1042
+ }
1043
+
1044
+ /**
1045
+ * Every tjp should be related to one of the roots in a space.
1046
+ *
1047
+ * You should NOT relate every ibgib frame of a given ibGib.
1048
+ */
1049
+ async rel8ToCurrentRoot({
1050
+ ibGib,
1051
+ linked,
1052
+ rel8nName,
1053
+ space,
1054
+ }: {
1055
+ ibGib: IbGib_V1,
1056
+ linked?: boolean,
1057
+ rel8nName?: string,
1058
+ space?: IbGibSpaceAny,
1059
+ }): Promise<void> {
1060
+ const lc = `${this.lc}[${this.rel8ToCurrentRoot.name}]`;
1061
+
1062
+ try {
1063
+ space = space ?? await this.getLocalUserSpace({});
1064
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: cbc4e519414a405eab7fe470eadbf8f0)`); }
1065
+
1066
+ return rel8ToCurrentRoot({
1067
+ ibGib,
1068
+ linked,
1069
+ rel8nName,
1070
+ space,
1071
+ zeroSpace: this.zeroSpace,
1072
+ fnBroadcast: (x) => this.fnBroadcast(x),
1073
+ fnUpdateBootstrap: (x) => this.fnUpdateBootstrap(x),
1074
+ });
1075
+ } catch (error) {
1076
+ console.error(`${lc} ${error.message}`);
1077
+ return;
1078
+ }
1079
+ }
1080
+
1081
+ /**
1082
+ * rel8s given ibgibs to special ibgib.
1083
+ * @see {@link rel8ToSpecialIbGib}
1084
+ * @returns new special ibgib addr
1085
+ */
1086
+ async rel8ToSpecialIbGib({
1087
+ type,
1088
+ rel8nName,
1089
+ ibGibsToRel8,
1090
+ ibGibsToUnRel8,
1091
+ linked,
1092
+ severPast,
1093
+ deletePreviousSpecialIbGib,
1094
+ space,
1095
+ }: {
1096
+ type: SpecialIbGibType,
1097
+ rel8nName: string,
1098
+ /**
1099
+ * multiple ibgibs to rel8
1100
+ */
1101
+ ibGibsToRel8?: IbGib_V1[],
1102
+ /**
1103
+ * multiple ibgibs to remove rel8n.
1104
+ */
1105
+ ibGibsToUnRel8?: IbGib_V1[],
1106
+ linked?: boolean,
1107
+ /**
1108
+ * Clears out the special.rel8ns.past array to an empty array.
1109
+ *
1110
+ * {@see deletePreviousSpecialIbGib} for driving use case.
1111
+ */
1112
+ severPast?: boolean,
1113
+ /**
1114
+ * Deletes the previous special ibGib.
1115
+ *
1116
+ * ## driving use case
1117
+ *
1118
+ * the latest ibGib is one that is completely ephemeral. It doesn't get attached
1119
+ * to the current root, and it only has the current instance. So we don't want to
1120
+ * keep around past incarnations.
1121
+ */
1122
+ deletePreviousSpecialIbGib?: boolean,
1123
+ space?: IbGibSpaceAny,
1124
+ }): Promise<IbGibAddr> {
1125
+ const lc = `${this.lc}[${this.rel8ToSpecialIbGib.name}](type:${type},rel8nName:${rel8nName})`;
1126
+ try {
1127
+ space = space ?? await this.getLocalUserSpace({});
1128
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: af863928bbc945549ffc4dcca830a70a)`); }
1129
+
1130
+ return rel8ToSpecialIbGib({
1131
+ type,
1132
+ rel8nName,
1133
+ ibGibsToRel8,
1134
+ ibGibsToUnRel8,
1135
+ linked,
1136
+ severPast,
1137
+ deletePreviousSpecialIbGib,
1138
+ space,
1139
+ zeroSpace: this.zeroSpace,
1140
+ fnUpdateBootstrap: (x) => this.fnUpdateBootstrap(x),
1141
+ fnBroadcast: (x) => this.fnBroadcast(x),
1142
+ });
1143
+
1144
+ } catch (error) {
1145
+ console.error(`${lc} ${error.message}`);
1146
+ throw error;
1147
+ }
1148
+ }
1149
+
1150
+ /**
1151
+ * Used for tracking tjpAddr -> latest ibGibAddr.
1152
+ *
1153
+ * Call this when you create a new ibGib.
1154
+ *
1155
+ * Need to put this in another service at some point, but crunch crunch
1156
+ * like pacman's lunch.
1157
+ */
1158
+ async registerNewIbGib({
1159
+ ibGib,
1160
+ space,
1161
+ }: {
1162
+ ibGib: IbGib_V1,
1163
+ space?: IbGibSpaceAny,
1164
+ }): Promise<void> {
1165
+ let lc = `${this.lc}[${this.registerNewIbGib.name}]`;
1166
+ try {
1167
+ const ibGibAddr: IbGibAddr = getIbGibAddr({ ibGib });
1168
+ lc = `${lc}[${ibGibAddr}]`;
1169
+
1170
+ space = space ?? await this.getLocalUserSpace({});
1171
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: d7e6d609a784449087cbaf1bbfb6b0c2)`); }
1172
+
1173
+ if (logalot) { console.log(`${lc} starting...`); }
1174
+
1175
+ return registerNewIbGib({
1176
+ ibGib,
1177
+ space,
1178
+ zeroSpace: this.zeroSpace,
1179
+ fnBroadcast: (x) => this.fnBroadcast(x),
1180
+ fnUpdateBootstrap: (x) => this.fnUpdateBootstrap(x),
1181
+ });
1182
+
1183
+ } catch (error) {
1184
+ console.error(`${lc} ${error.message}`);
1185
+ throw error;
1186
+ } finally {
1187
+ if (logalot) { console.log(`${lc} complete.`); }
1188
+ }
1189
+ }
1190
+
1191
+ /**
1192
+ * Will trigger a latest info event to be fired.
1193
+ * @param param0
1194
+ */
1195
+ async pingLatest_Local({
1196
+ ibGib,
1197
+ tjpIbGib,
1198
+ space,
1199
+ useCache,
1200
+ }: {
1201
+ ibGib: IbGib_V1<any>,
1202
+ tjpIbGib: IbGib_V1<any> | undefined,
1203
+ space?: IbGibSpaceAny,
1204
+ /**
1205
+ * If true, then will check the latest ibgib cache first. if found, will
1206
+ * just return that.
1207
+ */
1208
+ useCache: boolean,
1209
+ }): Promise<void> {
1210
+ let lc = `${this.lc}[${this.pingLatest_Local.name}]`;
1211
+ if (logalot) { console.log(`${lc} starting...`); }
1212
+ try {
1213
+ if (!ibGib) {
1214
+ if (logalot) { console.log(`${lc} ibGib falsy.`); }
1215
+ return; /* <<<< returns early */
1216
+ }
1217
+ if (isPrimitive({ ibGib })) {
1218
+ console.warn(`${lc} tried to ping latest for primitive. returning early... (W: 06c50cfe028cc04cca67e97a48e6fe22)`);
1219
+ return; /* <<<< returns early */
1220
+ }
1221
+
1222
+ if (ibGib.ib.startsWith(`witness space IonicSpace_V1`)) {
1223
+ // pinging a local user space, which for better or worse, does not
1224
+ // have a tjp (nor is it kept up to date via the latest index).
1225
+ // I can't remember, but I think there was a reason for the last bit,
1226
+ // but I probably should have given it a tjp. Anyway, the latest
1227
+ // "index" for local spaces is in the bootstrap. (Also the space is
1228
+ // not necessarily contained in the passed in `space` arg.)
1229
+ const { spaceId } = parseSpaceIb({ spaceIb: ibGib.ib });
1230
+ const latestSpace =
1231
+ await this.getLocalUserSpace({ localSpaceId: spaceId });
1232
+ // if (latestSpace.gib !== ibGib.gib) { // leavin this in for now. delete after awhile of normal operation. see https://github.com/wraiford/ibgib/commit/2247235a6f25945000fef419c08d5518ba2cfb48
1233
+ this._latestSubj.next({
1234
+ latestIbGib: latestSpace,
1235
+ latestAddr: getIbGibAddr({ ibGib: latestSpace }),
1236
+ tjpAddr: `${ibGib.ib}^gib`
1237
+ });
1238
+ // }
1239
+ return; /* <<<< returns early */
1240
+ }
1241
+
1242
+ space = space ?? await this.getLocalUserSpace({});
1243
+ if (!space) {
1244
+ console.warn(`${lc} space falsy and localUserSpace not initialized. (W: e6708e58618947a6b66f6a49406cbf35)`);
1245
+ return;
1246
+ }
1247
+
1248
+ let latestIbGib: IbGib_V1;
1249
+ let ibGibAddr = getIbGibAddr({ ibGib });
1250
+ let tjpAddr: IbGibAddr;
1251
+
1252
+ // if (!latestIbGib || !latestAddr) {
1253
+ // not found in cache or caller didn't allow using the cache
1254
+ let latestAddr = await this.getLatestAddr({ ibGib, tjp: tjpIbGib, space }) ?? ibGibAddr;
1255
+
1256
+ // get the tjp for the rel8nName mapping, and also for some checking logic
1257
+ if (!tjpIbGib) {
1258
+ tjpIbGib = await this.getTjpIbGib({ ibGib, space });
1259
+ if (!tjpIbGib) {
1260
+ console.warn(`${lc} tjp not found for ${ibGibAddr}? Should at least just be the ibGib's address itself. (W: 15d9c6daca7e442e968af36b4c18b8f7)`);
1261
+ tjpIbGib = ibGib;
1262
+ }
1263
+ }
1264
+ tjpAddr = getIbGibAddr({ ibGib: tjpIbGib });
1265
+
1266
+ if (latestAddr === ibGibAddr) {
1267
+ if (logalot) { console.log(`${lc} latestAddr === ibGibAddr (I: f38042421fcc3d148a26ed56651c2522)`); }
1268
+ latestIbGib = ibGib;
1269
+ } else {
1270
+ if (logalot) { console.log(`${lc} later version found. ibGibAddr: ${ibGibAddr}\nlatestAddr: ${latestAddr} (I: 53cabd1643df7d43635af642e4c90922)`); }
1271
+ let resLatestIbGib = await this.get({ addr: latestAddr, space });
1272
+ if (!resLatestIbGib.success || resLatestIbGib.ibGibs?.length !== 1) { throw new Error(`latest not found (E: bc54e433573a5a89c6436dc6a3b60922)`); }
1273
+ latestIbGib = resLatestIbGib.ibGibs![0];
1274
+
1275
+ // if (useCache) {
1276
+ // // we tried to use cache but it wasn't there, so put it for the next time.
1277
+ // if (logalot) { console.log(`${lc} putting in getLatest cache (I: 5f264edf03940a0f7e72252fd6fe0d22)`); }
1278
+ // console.log(`${lc} putting in getLatest cache (I: 5f264edf03940a0f7e72252fd6fe0d22)`);
1279
+ // setTimeout(async () => {
1280
+ // await this.latestCacheSvc.put({
1281
+ // addr: latestAddr,
1282
+ // ibGib: latestIbGib,
1283
+ // tjpAddr,
1284
+ // tjpIbGib: tjpIbGib,
1285
+ // });
1286
+ // }, Math.ceil(Math.random() * 10000));
1287
+ // }
1288
+ }
1289
+ // }
1290
+
1291
+ if (latestIbGib && latestAddr && tjpAddr) {
1292
+ this._latestSubj.next({
1293
+ latestIbGib,
1294
+ latestAddr,
1295
+ tjpAddr
1296
+ });
1297
+ } else {
1298
+ debugger;
1299
+ throw new Error(`(UNEXPECTED) latestIbGib, latestAddr and tjpAddr should all be truthy. (E: 20e71fa1a72c45deb1dc5083903a8622)`);
1300
+ }
1301
+ } catch (error) {
1302
+ console.error(`${lc} ${error.message}`);
1303
+ } finally {
1304
+ if (logalot) { console.log(`${lc} complete.`); }
1305
+ }
1306
+ }
1307
+
1308
+ // #region space facade (e.g. get, getLatest___, put, etc.)
1309
+
1310
+ /**
1311
+ * Convenience function for persisting a transform result, which has
1312
+ * a newIbGib and optionally intermediate ibGibs and/or dnas.
1313
+ *
1314
+ * it persists these ibgibs into the given space, else the current space.
1315
+ */
1316
+ async persistTransformResult({
1317
+ resTransform,
1318
+ isMeta,
1319
+ force,
1320
+ space,
1321
+ }: {
1322
+ resTransform: TransformResult<IbGib_V1>,
1323
+ isMeta?: boolean,
1324
+ force?: boolean,
1325
+ space?: IbGibSpaceAny,
1326
+ }): Promise<void> {
1327
+ const lc = `${this.lc}[${this.persistTransformResult.name}]`;
1328
+ try {
1329
+ space = space ?? await this.getLocalUserSpace({});
1330
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: f8b3d06006874b479240eb45f4015628)`); }
1331
+
1332
+ return persistTransformResult({
1333
+ resTransform,
1334
+ isMeta,
1335
+ force,
1336
+ space,
1337
+ });
1338
+ } catch (error) {
1339
+ console.error(`${lc} ${error.message}`);
1340
+ throw error;
1341
+ }
1342
+ }
1343
+
1344
+ /**
1345
+ * Wrapper for retrieving ibgib from a given space, else the current space.
1346
+ */
1347
+ async get({
1348
+ addr,
1349
+ addrs,
1350
+ isMeta,
1351
+ isDna,
1352
+ space,
1353
+ }: GetIbGibOpts): Promise<GetIbGibResult> {
1354
+ let lc = `${this.lc}[${this.get.name}]`;
1355
+ try {
1356
+ space = space ?? await this.getLocalUserSpace({});
1357
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: 86ccdcf3417a45b4a3a8c280fb9a6df7)`); }
1358
+
1359
+ return getFromSpace({ addr, addrs, isMeta, isDna, space });
1360
+ } catch (error) {
1361
+ console.error(`${lc} ${error.message}`);
1362
+ return Promise.resolve({ errorMsg: error.message });
1363
+ }
1364
+ }
1365
+
1366
+ /**
1367
+ * Wrapper for saving ibgib in a given space, else the current space.
1368
+ */
1369
+ async put({
1370
+ ibGib,
1371
+ ibGibs,
1372
+ isMeta,
1373
+ isDna,
1374
+ force,
1375
+ space,
1376
+ }: PutIbGibOpts): Promise<PutIbGibResult> {
1377
+ const lc = `${this.lc}[${this.put.name}]`;
1378
+ try {
1379
+ space = space ?? await this.getLocalUserSpace({});
1380
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: a00eebe0e3d348d09fda62a6be486b6c)`); }
1381
+
1382
+ return putInSpace({ ibGib, ibGibs, isMeta, isDna, force, space });
1383
+ } catch (error) {
1384
+ console.error(`${lc} ${error.message}`);
1385
+ return Promise.resolve({ errorMsg: error.message });
1386
+ }
1387
+ }
1388
+
1389
+ /**
1390
+ * Wrapper for removing ibgib from the a given space, else the current space.
1391
+ */
1392
+ async delete({
1393
+ addr,
1394
+ isMeta,
1395
+ isDna,
1396
+ space,
1397
+ }: DeleteIbGibOpts): Promise<DeleteIbGibResult> {
1398
+ const lc = `${this.lc}[${this.delete.name}]`;
1399
+ try {
1400
+ space = space ?? await this.getLocalUserSpace({});
1401
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: b4250b0045dc40629447f2cbd162faaa)`); }
1402
+
1403
+ return deleteFromSpace({ addr, isMeta, isDna, space });
1404
+ } catch (error) {
1405
+ console.error(`${lc} ${error.message}`);
1406
+ return Promise.resolve({ errorMsg: error.message });
1407
+ }
1408
+ }
1409
+
1410
+ /**
1411
+ * Wrapper for `getDependencyGraph` fn in `helper/space.ts`, but using
1412
+ * `this.localUserSpace` as default space.
1413
+ *
1414
+ * (refactoring!)
1415
+ *
1416
+ * ## note on space
1417
+ *
1418
+ * pass in `null` for space if you want to
1419
+ *
1420
+ * ## warning
1421
+ *
1422
+ * This does not (YET) have a flag that gets the latest ibgibs for the graph.
1423
+ * It only climbs the current graph, which may not cover all ibgibs when you
1424
+ * deal with ibGibs with tjps (timelines). We're going to eventually
1425
+ * combat this with auto-updating our rel8ns, but for now we're just going
1426
+ * to earmark this for the future.
1427
+ *
1428
+ * todo: auto-update or better
1429
+ *
1430
+ * @returns map of addr => ibGib
1431
+ */
1432
+ async getDependencyGraph(opts: GetDependencyGraphOptions): Promise<{ [addr: string]: IbGib_V1 }> {
1433
+ const lc = `${this.lc}[${this.getDependencyGraph.name}]`;
1434
+ try {
1435
+ opts.space = opts.space ?? await this.getLocalUserSpace({}) ?? null;
1436
+ if (!opts.space) { throw new Error(`(UNEXPECTED) space falsy and localUserSpace not initialized (?) (E: e2a35a23d12d48ebadcfd4f1a396d6c1)`); }
1437
+
1438
+ return getDependencyGraph(opts);
1439
+ } catch (error) {
1440
+ console.error(`${lc} ${error.message}`);
1441
+ throw error;
1442
+ }
1443
+ }
1444
+
1445
+ /**
1446
+ * Wrapper for getting the latest addr in the given space.
1447
+ *
1448
+ * ## warnings
1449
+ *
1450
+ * * This was written early and makes many assumptions.
1451
+ * * Meant to work with Ionic space atow.
1452
+ *
1453
+ * @returns latest addr in a given space (or localUserSpace)
1454
+ */
1455
+ async getLatestAddr({
1456
+ ibGib,
1457
+ addr,
1458
+ tjpAddr,
1459
+ tjp,
1460
+ space,
1461
+ }: {
1462
+ ibGib?: IbGib_V1<any>,
1463
+ addr?: IbGibAddr,
1464
+ tjpAddr?: IbGibAddr,
1465
+ tjp?: IbGib_V1<any>,
1466
+ space?: IbGibSpaceAny,
1467
+ }): Promise<IbGibAddr | undefined> {
1468
+ let lc = `${this.lc}[${this.getLatestAddr.name}]`;
1469
+ if (logalot) { console.log(`${lc} starting...`); }
1470
+ try {
1471
+ if (!ibGib && !tjp && !tjpAddr) {
1472
+ throw new Error(`ibGib && tjp && tjpAddr falsy (E: fe725654342c4d80a33219160b5d81d3)`);
1473
+ }
1474
+
1475
+ space = space ?? await this.getLocalUserSpace({});
1476
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: fd01bb85e91f4c54bfe8b35714d48a38)`); }
1477
+
1478
+ const resGetLatest = await getLatestAddrs({
1479
+ ibGibs: ibGib ? [ibGib] : undefined,
1480
+ addrs: addr ? [addr] : undefined,
1481
+ tjpAddrs: tjpAddr ? [tjpAddr] : undefined,
1482
+ tjps: tjp ? [tjp] : undefined,
1483
+ space,
1484
+ });
1485
+
1486
+ if (!resGetLatest) { throw new Error(`resGetLatest falsy (E: 3851bbe4427ae11771f222234e8c6622)`); }
1487
+ if (!resGetLatest.data) { throw new Error(`invalid resGetLatest: data falsy (E: 134e0f1f65edc69c6951c32e00a4bb22)`); }
1488
+ if (resGetLatest.data.success) {
1489
+ if (resGetLatest.data.addrs?.length === 1) {
1490
+ return resGetLatest.data.addrs[0];
1491
+ } else if (resGetLatest.data.addrsNotFound?.length === 1) {
1492
+ return undefined;
1493
+ } else if (resGetLatest.data.addrsErrored?.length === 1) {
1494
+ const emsg = resGetLatest.data.errors?.join('|') ?? "[unspecified error(s)] (E: 24f338036aa84ac99e3c39a660207222)";
1495
+ throw new Error(`resGetLatest had error(s): ${emsg}`);
1496
+ } else {
1497
+ throw new Error(`unknown error, invalid resGetLatest: ${pretty(resGetLatest)} (E: 6aa5aa225ebf49b588664370cb8feb22)`);
1498
+ }
1499
+ } else {
1500
+ const emsg = resGetLatest.data.errors?.join('|') ?? "[unspecified error(s)] (E: dcd6dcd6ec052fd112a4d48f1afa2922)";
1501
+ throw new Error(`resGetLatest had error(s): ${emsg}`);
1502
+ }
1503
+ } catch (error) {
1504
+ console.error(`${lc} ${error.message}`);
1505
+ throw error;
1506
+ } finally {
1507
+ if (logalot) { console.log(`${lc} complete.`); }
1508
+ }
1509
+ }
1510
+
1511
+ /**
1512
+ * Gets the tjpIbGib for the given `ibGib` in the given `space`.
1513
+ * atow, naive must be true.
1514
+ *
1515
+ *
1516
+ *
1517
+ * @returns tjpIbGib for the given `ibGib`
1518
+ */
1519
+ async getTjpIbGib({
1520
+ ibGib,
1521
+ naive = true,
1522
+ space,
1523
+ }: {
1524
+ ibGib: IbGib_V1<any>,
1525
+ naive?: boolean,
1526
+ space?: IbGibSpaceAny,
1527
+ }): Promise<IbGib_V1<any> | undefined> {
1528
+ const lc = `${this.lc}[${this.getTjpIbGib.name}]`;
1529
+
1530
+ try {
1531
+ space = space ?? await this.getLocalUserSpace({});
1532
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: 0ec806bd2c5641c4844a3698c922e1f6)`); }
1533
+ if (!space) {
1534
+ console.warn(`${lc} space falsy and localUserSpace not initialized.`);
1535
+ return ibGib;
1536
+ }
1537
+ return getTjpIbGib({ ibGib, naive, space });
1538
+ } catch (error) {
1539
+ console.error(`${lc} ${error.message}`);
1540
+ throw error;
1541
+ }
1542
+ }
1543
+
1544
+ /**
1545
+ * Gets one of the app's special ibGibs, e.g., TagsIbGib.
1546
+ *
1547
+ * When initializing tags, this will generate some boilerplate tags.
1548
+ * I'm going to be doing roots here also, and who knows what else, but each
1549
+ * one will have its own initialize specifics.
1550
+ *
1551
+ * @param initialize initialize (i.e. create) ONLY IF IbGib not found. Used for initializing app (first run).
1552
+ *
1553
+ * @see {@link createSpecial}
1554
+ * @see {@link createTags}
1555
+ */
1556
+ async getSpecialIbGib({
1557
+ type,
1558
+ initialize,
1559
+ space,
1560
+ lock,
1561
+ }: {
1562
+ type: SpecialIbGibType,
1563
+ initialize?: boolean,
1564
+ space?: IbGibSpaceAny,
1565
+ lock?: boolean,
1566
+ }): Promise<IbGib_V1 | null> {
1567
+ const lc = `${this.lc}[${this.getSpecialIbGib.name}]`;
1568
+ try {
1569
+ space = space ?? await this.getLocalUserSpace({ lock });
1570
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: e08e85d8422e479f9d101194fd26cbda)`); }
1571
+
1572
+ while (this.initializing) {
1573
+ if (logalot) { console.log(`${lc} hacky wait while initializing ibgibs service (I: 497d4becb94f4515a2ec389630420d6c)`); }
1574
+ await delay(100);
1575
+ }
1576
+ // debugger;
1577
+
1578
+ return getSpecialIbGib({
1579
+ type,
1580
+ initialize,
1581
+ space,
1582
+ zeroSpace: this.zeroSpace,
1583
+ fnUpdateBootstrap: (x) => this.fnUpdateBootstrap(x),
1584
+ fnBroadcast: (x) => this.fnBroadcast(x),
1585
+ fnGetInitializing: () => { return this._initializing; },
1586
+ fnSetInitializing: (value: boolean) => { this._initializing = value; }
1587
+ });
1588
+ } catch (error) {
1589
+ console.error(`${lc} ${error.message}`);
1590
+ return null;
1591
+ }
1592
+ }
1593
+
1594
+ async getSpecialRel8dIbGibs<TIbGib extends IbGib_V1 = IbGib_V1>({
1595
+ type,
1596
+ rel8nName,
1597
+ space,
1598
+ }: {
1599
+ type: SpecialIbGibType,
1600
+ rel8nName: string,
1601
+ space?: IbGibSpaceAny,
1602
+ }): Promise<TIbGib[]> {
1603
+ const lc = `${this.lc}[${this.getSpecialRel8dIbGibs.name}]`;
1604
+ try {
1605
+ space = space ?? await this.getLocalUserSpace({});
1606
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: 476c7d7c68f34197b740be5df09238a2)`); }
1607
+
1608
+ return getSpecialRel8dIbGibs({ type, rel8nName, space });
1609
+ } catch (error) {
1610
+ console.error(`${lc} ${error.message}`);
1611
+ throw error;
1612
+ }
1613
+ }
1614
+
1615
+ // #endregion space facade (e.g. get, getLatest___, put, etc.)
1616
+
1617
+ /**
1618
+ * Feels klugy.
1619
+ */
1620
+ async getPasswordForSecrets({
1621
+ secretIbGibs,
1622
+ fnPromptPassword,
1623
+ dontPrompt,
1624
+ checkCacheFirst,
1625
+ cacheAfter,
1626
+ }: {
1627
+ secretIbGibs: IbGib_V1<SecretData_V1>[],
1628
+ fnPromptPassword: (title: string, msg: string) => Promise<string | null>,
1629
+ dontPrompt?: boolean,
1630
+ checkCacheFirst?: boolean,
1631
+ cacheAfter?: boolean,
1632
+ }): Promise<string | null> {
1633
+ const lc = `${this.lc}[${this.getPasswordForSecrets.name}]`;
1634
+ /** Flag that we'll check in finally clause */
1635
+ let erroredDueToPromptTimeout = false;
1636
+ try {
1637
+ let tries = 0;
1638
+ while (this.isPrompting) {
1639
+ if (logalot) { console.log(`${lc} hacky wait while already prompting (I: 852007c8549d42c096defe0105b5e2e6)`); }
1640
+ await delay(100);
1641
+ tries++;
1642
+ if (tries > 1000) {
1643
+ erroredDueToPromptTimeout = true;
1644
+ throw new Error(`attempted to prompt for user password, but already prompting for quite awhile now. (E: 8bd4dc907422dd95862b2038ffe2b822)`);
1645
+ }
1646
+ }
1647
+
1648
+
1649
+ // used if we `checkCacheFirst` AND/OR if we `cacheAfter`
1650
+ const secretsCacheKey =
1651
+ secretIbGibs.map(ibGib => getIbGibAddr({ ibGib })).join('');
1652
+
1653
+ if (checkCacheFirst) {
1654
+ const cachedPassword =
1655
+ await this.getCachedSecretPassword({ cacheKey: secretsCacheKey });
1656
+
1657
+ if (cachedPassword) {
1658
+ // do NOT log the actual cachedPassword!!
1659
+ if (logalot) { console.log(`${lc} using cachedPassword.`); }
1660
+ return cachedPassword;
1661
+ }
1662
+ }
1663
+
1664
+ if (dontPrompt) {
1665
+ return null; /* <<<< returns early */
1666
+ }
1667
+
1668
+ // build prompt message
1669
+ this.isPrompting = true;
1670
+
1671
+ let secretInfos: SecretInfo_Password[] = [];
1672
+ for (let i = 0; i < secretIbGibs.length; i++) {
1673
+ const secretIbGib = secretIbGibs[i];
1674
+ if (!secretIbGib.data) { throw new Error(`invalid secretIbGib. data falsy.`); }
1675
+ if (secretIbGib.data!.type === 'password') {
1676
+ const secretInfo = secretIbGib.data as SecretInfo_Password;
1677
+ secretInfos.push(secretInfo);
1678
+ } else {
1679
+ throw new Error(`Only password secrets are implemented atm.`);
1680
+ }
1681
+ }
1682
+ const separator = '-------------';
1683
+ const secretInfosMsgBlock = secretInfos.map(secretInfo => {
1684
+ return `name: ${secretInfo.name}
1685
+ description: ${secretInfo.description}
1686
+ hint: ${secretInfo.hint}`;
1687
+ }).join('\n' + separator + '\n');
1688
+
1689
+ // prompt user
1690
+ const title = `Gimme a password.`;
1691
+ const msg =
1692
+ `Enter the password corresponding to the following secret(s):\n
1693
+ ${separator}\n\n
1694
+ ${secretInfosMsgBlock}
1695
+ `;
1696
+ let password = await fnPromptPassword(title, msg);
1697
+
1698
+ // cache if applicable
1699
+ if (password && cacheAfter) {
1700
+ await this.setCachedSecretPassword({
1701
+ cacheKey: secretsCacheKey,
1702
+ secretPassword: password,
1703
+ });
1704
+ }
1705
+
1706
+ // we're done
1707
+ return password;
1708
+ } catch (error) {
1709
+ console.error(`${lc} ${error.message}`);
1710
+ throw error;
1711
+ } finally {
1712
+ if (!erroredDueToPromptTimeout) {
1713
+ // if we errored due to prompt timeout, then some other call to this
1714
+ // function is prompting and we don't want to turn off their flag.
1715
+ this.isPrompting = false;
1716
+ }
1717
+ }
1718
+ }
1719
+
1720
+ async getCiphertextIbGib<TEncryptionIbGib extends IbGib_V1<EncryptionData_V1>, TMetadata = any>({
1721
+ plaintext,
1722
+ password,
1723
+ encryptionIbGib,
1724
+ confirm,
1725
+ persist,
1726
+ ibRoot,
1727
+ publicIbMetadata,
1728
+ publicMetadata,
1729
+ }: {
1730
+ /**
1731
+ * Um...data...to...erm...encrypt...(as a string)
1732
+ */
1733
+ plaintext: string,
1734
+ /**
1735
+ * Password to perform the encryption.
1736
+ */
1737
+ password: string,
1738
+ /**
1739
+ * Information about encryption, i.e. encryption settings.
1740
+ */
1741
+ encryptionIbGib: TEncryptionIbGib,
1742
+ /**
1743
+ * Decrypts and checks against original data
1744
+ */
1745
+ confirm?: boolean,
1746
+ /**
1747
+ * If true, will persist the ibgib
1748
+ */
1749
+ persist?: boolean,
1750
+ /**
1751
+ * If you provide this, the resulting ibgib will have the following format:
1752
+ * `${ibRoot} ${publicIbMetadata}`. Otherwise, this will default to:
1753
+ * `ciphertext ${publicIbMetadata}`, or just `ciphertext` if
1754
+ * `publicIbMetadata` is falsy.
1755
+ */
1756
+ ibRoot?: string,
1757
+ /**
1758
+ * If you want to include metadata in the ib itself of the
1759
+ * ciphertext ibgib. This will of course make this metadata
1760
+ * available without loading the full ibgib, but will increase
1761
+ * storage size because every address linking to the ibgib will
1762
+ * include this as well.
1763
+ */
1764
+ publicIbMetadata?: string,
1765
+ /**
1766
+ * If you want to include public, unencrypted metadata in the ibgib's
1767
+ * data body itself.
1768
+ */
1769
+ publicMetadata?: TMetadata,
1770
+ }): Promise<TransformResult<CiphertextIbGib_V1>> {
1771
+ const lc = `${this.lc}[${this.getCiphertextIbGib.name}]`;
1772
+ try {
1773
+ const encryptionInfo = encryptionIbGib.data;
1774
+ if (encryptionInfo?.method !== 'encrypt-gib (weak)') {
1775
+ throw new Error('only encrypt-gib is implemented.');
1776
+ }
1777
+ const info: EncryptionInfo_EncryptGib = encryptionInfo;
1778
+ const resEncrypt = await encrypt({
1779
+ dataToEncrypt: plaintext,
1780
+ secret: password,
1781
+ initialRecursions: info.initialRecursions,
1782
+ recursionsPerHash: info.recursionsPerHash,
1783
+ salt: info.salt,
1784
+ saltStrategy: info.saltStrategy,
1785
+ hashAlgorithm: info.hashAlgorithm,
1786
+ encryptedDataDelimiter: info.encryptedDataDelimiter,
1787
+ confirm,
1788
+ });
1789
+
1790
+ if (resEncrypt.warnings?.length ?? 0 > 0) { console.warn(`${lc} warnings: ${resEncrypt.warnings!.join('\n')}`); }
1791
+ if (resEncrypt.errors?.length ?? 0 > 0) { throw new Error(resEncrypt.errors!.join('\n')); }
1792
+ if (!resEncrypt.encryptedData) { throw new Error(`encryptedData is falsy`) }
1793
+
1794
+ const data: CiphertextData = { ciphertext: resEncrypt.encryptedData, };
1795
+ if (publicMetadata) { data.metadata = publicMetadata; }
1796
+
1797
+ const rel8ns: CiphertextRel8ns = {
1798
+ encryption: [getIbGibAddr({ ibGib: encryptionIbGib })],
1799
+ }
1800
+
1801
+ const resCiphertext = (
1802
+ await factory.firstGen({
1803
+ parentIbGib: factory.primitive({ ib: ibRoot || 'ciphertext' }),
1804
+ ib:
1805
+ publicIbMetadata ?
1806
+ `${ibRoot || 'ciphertext'} ${publicIbMetadata}` :
1807
+ `${ibRoot || 'ciphertext'}`,
1808
+ data,
1809
+ rel8ns,
1810
+ dna: false,
1811
+ tjp: { uuid: true, timestamp: true },
1812
+ nCounter: true,
1813
+ })
1814
+ ) as TransformResult<CiphertextIbGib_V1>;
1815
+
1816
+ if (!resCiphertext.newIbGib) { throw new Error('Error creating ciphertext ibgib.'); }
1817
+
1818
+ if (persist) {
1819
+ await this.persistTransformResult({ resTransform: resCiphertext });
1820
+ }
1821
+
1822
+ return resCiphertext;
1823
+ } catch (error) {
1824
+ console.error(`${lc} ${error.message}`);
1825
+ throw error;
1826
+ }
1827
+
1828
+ }
1829
+
1830
+ /**
1831
+ * Brings together a ciphertext and secretIbGibs to decrypt
1832
+ * the `ciphertextIbGib.data.ciphertext`
1833
+ * @returns plaintext string of `ciphertextIbGib.data.ciphertext`
1834
+ */
1835
+ async getPlaintextString({
1836
+ ciphertextIbGib,
1837
+ secretIbGibs,
1838
+ fnPromptPassword,
1839
+ dontPrompt,
1840
+ space,
1841
+ }: {
1842
+ ciphertextIbGib: CiphertextIbGib_V1,
1843
+ secretIbGibs: SecretIbGib_V1[],
1844
+ fnPromptPassword: (title: string, msg: string) => Promise<string | null>,
1845
+ dontPrompt?: boolean,
1846
+ space: IbGibSpaceAny,
1847
+ }): Promise<string> {
1848
+ const lc = `${this.lc}[${this.getPlaintextString.name}]`;
1849
+ try {
1850
+ // validate
1851
+ if ((secretIbGibs || []).length === 0) { throw new Error(`secretIbGibs required.`); }
1852
+ if (!ciphertextIbGib.data) { throw new Error(`ciphertextIbGib.data falsy (E: 598fc6473149e240a7d6916ecf642323)`); }
1853
+ if (!ciphertextIbGib.data.ciphertext) { throw new Error(`ciphertextIbGib.data.ciphertext falsy (E: 0e764a71ec214132a5a528f17f495a7c)`); }
1854
+ if (!ciphertextIbGib.rel8ns?.encryption) { throw new Error(`ciphertextIbGib.rel8ns.encryption falsy`) }
1855
+ if (ciphertextIbGib.rel8ns!.encryption!.length !== 1) { throw new Error(`ciphertextIbGib.rel8ns!.encryption!.length !== 1`); }
1856
+
1857
+ // get corresponding encryption ibgib for encryption settings
1858
+ const encryptionAddr = ciphertextIbGib.rel8ns!.encryption![0];
1859
+ const resEncryption = await this.get({ addr: encryptionAddr, space });
1860
+ if (!resEncryption.success) { throw new Error(`get encryption failed`); }
1861
+ if ((resEncryption.ibGibs || []).length !== 1) { throw new Error(`get encryption retrieved non-1 length (eesh)`); }
1862
+ const encryptionIbGib = resEncryption.ibGibs![0] as IbGib_V1<EncryptionData_V1>;
1863
+ if (!encryptionIbGib.data) { throw new Error('encryptionIbGib.data falsy'); }
1864
+
1865
+ // prompt user for the password
1866
+ const password = await this.getPasswordForSecrets({
1867
+ secretIbGibs,
1868
+ fnPromptPassword,
1869
+ dontPrompt,
1870
+ checkCacheFirst: true,
1871
+ cacheAfter: true,
1872
+ });
1873
+
1874
+ // we're about the decrypt, but maybe the data doesn't have everything.
1875
+ // So WARN for any defaults we're using.
1876
+ if (!encryptionIbGib.data.initialRecursions) { console.warn(`${lc} using default initialRecursions`); }
1877
+ if (!encryptionIbGib.data.recursionsPerHash) { console.warn(`${lc} using default recursionsPerHash`); }
1878
+ if (!encryptionIbGib.data.saltStrategy) { console.warn(`${lc} using default saltStrategy`); }
1879
+ if (!encryptionIbGib.data.hashAlgorithm) { console.warn(`${lc} using default hashAlgorithm`); }
1880
+
1881
+ // do actual decryption
1882
+ if (logalot) { console.log(`${lc} starting decrypt...`); }
1883
+ const timerName = 'sync_log decrypt';
1884
+ console.time(timerName);
1885
+ console.timeLog(timerName, 'decrypting...');
1886
+ const resDecrypt = await decrypt({
1887
+ encryptedData: ciphertextIbGib.data.ciphertext,
1888
+ secret: password ?? '',
1889
+ initialRecursions:
1890
+ encryptionIbGib.data.initialRecursions || DEFAULT_ENCRYPTION_INITIAL_RECURSIONS,
1891
+ recursionsPerHash:
1892
+ encryptionIbGib.data.recursionsPerHash || DEFAULT_ENCRYPTION_RECURSIONS_PER_HASH,
1893
+ salt: encryptionIbGib.data.salt,
1894
+ saltStrategy:
1895
+ encryptionIbGib.data.saltStrategy || DEFAULT_ENCRYPTION_SALT_STRATEGY,
1896
+ hashAlgorithm:
1897
+ encryptionIbGib.data.hashAlgorithm || DEFAULT_ENCRYPTION_HASH_ALGORITHM,
1898
+ encryptedDataDelimiter: encryptionIbGib.data.encryptedDataDelimiter,
1899
+ });
1900
+ console.timeLog(timerName, 'decrypting complete.');
1901
+ console.timeEnd(timerName);
1902
+ if (logalot) { console.log(`${lc} decrypt complete.`); }
1903
+ if (resDecrypt.errors?.length ?? 0 > 0) { throw new Error(resDecrypt.errors!.join('|')); }
1904
+
1905
+ if (!resDecrypt.decryptedData) { throw new Error(`(UNEXPECTED) resDecrypt has no errors but resDecrypt.decryptedData falsy? (E: 06e73992fb02e45e3c18db33f9a61b23)`); }
1906
+
1907
+ // we're done
1908
+ return resDecrypt.decryptedData;
1909
+ } catch (error) {
1910
+ console.error(`${lc} ${error.message}`);
1911
+ throw error;
1912
+ }
1913
+ }
1914
+
1915
+ async unwrapEncryptedSyncSpace({
1916
+ encryptedSpace,
1917
+ fnPromptPassword,
1918
+ dontPrompt,
1919
+ space,
1920
+ fnSpaceFactory,
1921
+ }: {
1922
+ encryptedSpace: IbGibSpaceAny,
1923
+ fnPromptPassword: (title: string, msg: string) => Promise<string | null>,
1924
+ dontPrompt?: boolean,
1925
+ space?: IbGibSpaceAny,
1926
+ fnSpaceFactory: (unencryptedSpaceData: any) => Promise<IbGibSpaceAny>,
1927
+ }): Promise<IbGibSpaceAny> {
1928
+ const lc = `${this.lc}[${this.unwrapEncryptedSyncSpace.name}]`;
1929
+ try {
1930
+ // validation
1931
+ if (!space) { throw new Error(`space required (E: d4d3eaa2d7b9143cf1173b8ae6344c23)`); }
1932
+ if (!encryptedSpace.rel8ns?.ciphertext) { throw new Error(`encryptedSpace is not a ciphertext`); }
1933
+ if (encryptedSpace.rel8ns!.ciphertext!.length !== 1) { throw new Error(`only 1 ciphertext rel8n allowed...`); }
1934
+
1935
+ // get ciphertext ibgib
1936
+ const ciphertextAddr = encryptedSpace.rel8ns!.ciphertext![0];
1937
+ const resCiphertext = await this.get({ addr: ciphertextAddr, space });
1938
+ if (!resCiphertext.success) { throw new Error(`get ciphertext failed`); }
1939
+ if ((resCiphertext.ibGibs || []).length !== 1) { throw new Error(`get ciphertext retrieved non-1 length (eesh)`); }
1940
+ const ciphertextIbGib = resCiphertext.ibGibs![0] as CiphertextIbGib_V1;
1941
+
1942
+ // get secrets associated with enciphered space
1943
+ if (!encryptedSpace.rel8ns?.secret) { throw new Error('!encryptionIbGib.rel8ns?.secret'); }
1944
+ const secretAddrs = encryptedSpace.rel8ns!.secret!;
1945
+ const localUserSpace = await this.getLocalUserSpace({});
1946
+ if (!localUserSpace) { throw new Error(`(UNEXPECTED) could not get localUserSpace? (E: 3c5687a15b3b58e3dcf1eca4f5fe5723)`); }
1947
+ const argGetSecrets = await localUserSpace.argy({
1948
+ argData: { ibGibAddrs: secretAddrs, cmd: 'get', }
1949
+ });
1950
+ const resSecrets = await localUserSpace.witness(argGetSecrets);
1951
+ if (!resSecrets.data?.success || (resSecrets.ibGibs || []).length === 0) {
1952
+ throw new Error(`couldn't get secret ibgibs`);
1953
+ }
1954
+ const secretIbGibs = resSecrets.ibGibs.concat() as IbGib_V1<SecretData_V1>[];
1955
+
1956
+ // get plaintext now that we have the ciphertext ibgib and secret ibgib(s)
1957
+ const plaintextString = await this.getPlaintextString({
1958
+ ciphertextIbGib: ciphertextIbGib,
1959
+ fnPromptPassword,
1960
+ dontPrompt,
1961
+ secretIbGibs,
1962
+ space,
1963
+ });
1964
+
1965
+ const syncSpaceData = JSON.parse(plaintextString);
1966
+ if (syncSpaceData.type !== 'sync') { throw new Error(`syncSpaceData.type !== 'sync'...this is the only one implemented right now`); }
1967
+ if (syncSpaceData.subtype !== 'aws-dynamodb') { throw new Error(`syncSpaceData.subtype !== 'aws-dynamodb'...only one right now dude`); }
1968
+
1969
+ // this is the original aws only implementation. I've just added (untested)
1970
+ // a quick factory function where the consumer would do the following code.
1971
+ const unwrappedSpace = await fnSpaceFactory(syncSpaceData);
1972
+ return unwrappedSpace;
1973
+ // so we have a syncspace data (only aws-dynamodb space right now).
1974
+ // load this data into a space class with behavior (not just the dto).
1975
+ // const awsSpace = new AWSDynamoSpace_V1(syncSpaceData, null);
1976
+ // awsSpace.gib = await getGib({ ibGib: awsSpace, hasTjp: false });
1977
+ // if (logalot) { console.log(`awsSpace.gib: ${awsSpace.gib}`); }
1978
+ // return awsSpace;
1979
+
1980
+ } catch (error) {
1981
+ console.error(`${lc} ${error.message}`);
1982
+ throw error;
1983
+ }
1984
+ }
1985
+
1986
+ /**
1987
+ * Caching user password secret in memory only.
1988
+ *
1989
+ * Just to prevent plaintext passwords from just sitting in memory,
1990
+ * this is a slight layer of indirection for caching
1991
+ *
1992
+ * @returns user password
1993
+ */
1994
+ protected async getCachedSecretPassword({
1995
+ cacheKey,
1996
+ }: {
1997
+ cacheKey: string,
1998
+ }): Promise<string | undefined> {
1999
+ const lc = `${this.lc}[${this.getCachedSecretPassword.name}]`;
2000
+ try {
2001
+ if (!cacheKey) { throw new Error(`secretAddr required`); }
2002
+ let entry = this.passwordCache[cacheKey];
2003
+ if (!entry) {
2004
+ if (logalot) { console.log(`${lc} secretAddr not cached: ${cacheKey}`); }
2005
+ return undefined;
2006
+ }
2007
+
2008
+ // settings must match, but I'm feeling lazy on DRYing
2009
+
2010
+ if (logalot) { console.log(`${lc} starting decrypt...`); }
2011
+ let resDecrypt = await decrypt({
2012
+ encryptedData: entry.encryptedPassword,
2013
+ secret: entry.tempMetaPassword,
2014
+ initialRecursions: 10000,
2015
+ recursionsPerHash: 5,
2016
+ salt: entry.salt,
2017
+ saltStrategy: 'appendPerHash',
2018
+ hashAlgorithm: 'SHA-512',
2019
+ });
2020
+ if (logalot) { console.log(`${lc} decrypt complete.`); }
2021
+
2022
+ if (!resDecrypt.decryptedData) { throw new Error(`resDecrypt.decryptedData falsy`); }
2023
+
2024
+ return resDecrypt.decryptedData;
2025
+ } catch (error) {
2026
+ console.error(`${lc} ${error.message}`);
2027
+ return undefined;
2028
+ }
2029
+ }
2030
+
2031
+ protected async setCachedSecretPassword({
2032
+ cacheKey,
2033
+ secretPassword,
2034
+ force,
2035
+ }: {
2036
+ cacheKey: string,
2037
+ secretPassword: string,
2038
+ force?: boolean,
2039
+ }): Promise<void> {
2040
+ const lc = `${this.lc}[${this.getCachedSecretPassword.name}]`;
2041
+ try {
2042
+ if (!cacheKey) { throw new Error(`secretAddr required`); }
2043
+
2044
+ if (this.passwordCache[cacheKey]) {
2045
+ if (force) {
2046
+ if (logalot) { console.log(`already cached, but force is true: ${cacheKey}`); }
2047
+ delete this.passwordCache[cacheKey];
2048
+ } else {
2049
+ if (logalot) { console.log(`already cached: ${cacheKey}`); }
2050
+ return undefined;
2051
+ }
2052
+ }
2053
+
2054
+ const tempMetaPassword = await getUUID(256);
2055
+ const salt = await getUUID();
2056
+ // settings must match, but I'm feeling lazy on DRYing
2057
+ let resEncrypt = await encrypt({
2058
+ dataToEncrypt: secretPassword,
2059
+ secret: tempMetaPassword,
2060
+ initialRecursions: 10000,
2061
+ recursionsPerHash: 5,
2062
+ salt: salt,
2063
+ saltStrategy: 'appendPerHash',
2064
+ hashAlgorithm: 'SHA-512',
2065
+ });
2066
+ if (!resEncrypt.encryptedData) { throw new Error(`resEncrypt.encryptedData falsy`); }
2067
+ const encryptedPassword = resEncrypt.encryptedData!;
2068
+
2069
+ let entry: TempCacheEntry =
2070
+ { tempMetaPassword, salt, encryptedPassword, };
2071
+
2072
+ this.passwordCache[cacheKey] = entry;
2073
+ if (logalot) { console.log(`${lc} entry added for ${cacheKey}.`); }
2074
+ } catch (error) {
2075
+ console.error(`${lc} ${error.message}`);
2076
+ return undefined;
2077
+ }
2078
+ }
2079
+
2080
+ /**
2081
+ * If we don't have outerspaces/cloud endpoints, we'll do that here.
2082
+ *
2083
+ * @returns true if creation was successfully created, else false.
2084
+ */
2085
+ protected async _createOuterspaceAndRequiredIbGibs({
2086
+ space,
2087
+ fnPromptSecret,
2088
+ fnPromptEncryption,
2089
+ fnPromptOuterSpace,
2090
+ }: {
2091
+ space: IbGibSpaceAny,
2092
+ fnPromptSecret: (space: IbGibSpaceAny) => Promise<SecretIbGib_V1 | undefined>,
2093
+ fnPromptEncryption: (space: IbGibSpaceAny) => Promise<EncryptionIbGib | undefined>,
2094
+ fnPromptOuterSpace: (space: IbGibSpaceAny) => Promise<IbGibSpaceAny | undefined>,
2095
+ }): Promise<boolean> {
2096
+ const lc = `${this.lc}[${this._createOuterspaceAndRequiredIbGibs.name}]`;
2097
+ try {
2098
+ const createdSecret = await this._createSecret({ space, fnPromptSecret });
2099
+ if (logalot) { console.log(`${lc} createdSecret: ${createdSecret} (I: 9fc011d8ecb1e10c86c86025be4d5c22)`); }
2100
+ if (!createdSecret) { return false; } // <<<< returns early
2101
+
2102
+ const createdEncryption = await this._createEncryption({ space, fnPromptEncryption });
2103
+ if (logalot) { console.log(`${lc} createdEncryption: ${createdEncryption} (I: 6796bbeb7338471e965cf1806d0dea9c)`); }
2104
+ if (!createdEncryption) { return false; } // <<<< returns early
2105
+
2106
+ const createdOuterspace = await this._createOuterspace({ space, fnPromptOuterSpace });
2107
+ if (logalot) { console.log(`${lc} createdOuterspace: ${createdOuterspace} (I: 6796bbeb7338471e965cf1806d0dea9c)`); }
2108
+ return createdOuterspace;
2109
+
2110
+ } catch (error) {
2111
+ console.error(`${lc} ${error.message}`);
2112
+ return false;
2113
+ }
2114
+ }
2115
+
2116
+ protected async _createSecret({
2117
+ space,
2118
+ fnPromptSecret,
2119
+ }: {
2120
+ space: IbGibSpaceAny,
2121
+ fnPromptSecret: (space: IbGibSpaceAny) => Promise<SecretIbGib_V1 | undefined>,
2122
+ }): Promise<boolean> {
2123
+ const lc = `${this.lc}[${this._createSecret.name}]`;
2124
+ try {
2125
+ if (logalot) { console.log(`${lc} starting...`); }
2126
+ const alert = this.getFnAlert!();
2127
+
2128
+ let secretIbGibs: IbGib_V1[] = await this.getSpecialRel8dIbGibs({
2129
+ type: "secrets",
2130
+ rel8nName: SECRET_REL8N_NAME,
2131
+ space,
2132
+ });
2133
+ if (secretIbGibs.length === 0) {
2134
+ await alert({
2135
+ title: 'first create some stuff...',
2136
+ msg: "First we'll need to do a couple things, like create a secret password, an encryption setting, and a cloud endpoint.",
2137
+ });
2138
+ }
2139
+ while (secretIbGibs.length === 0) {
2140
+ let secretIbGib = await fnPromptSecret(space);
2141
+ if (secretIbGib === undefined) {
2142
+ await alert({ title: 'cancelled', msg: 'Cancelled.' });
2143
+ return false;
2144
+ }
2145
+ await this.registerNewIbGib({ ibGib: secretIbGib, });
2146
+ await this.rel8ToSpecialIbGib({
2147
+ type: "secrets",
2148
+ rel8nName: SECRET_REL8N_NAME,
2149
+ ibGibsToRel8: [secretIbGib],
2150
+ space,
2151
+ });
2152
+ secretIbGibs = await this.getSpecialRel8dIbGibs({
2153
+ type: "secrets",
2154
+ rel8nName: SECRET_REL8N_NAME,
2155
+ space,
2156
+ });
2157
+ }
2158
+ return true;
2159
+ } catch (error) {
2160
+ console.error(`${lc} ${error.message}`);
2161
+ throw error;
2162
+ } finally {
2163
+ if (logalot) { console.log(`${lc} complete.`); }
2164
+ }
2165
+ }
2166
+
2167
+ protected async _createEncryption({
2168
+ space,
2169
+ fnPromptEncryption,
2170
+ }: {
2171
+ space: IbGibSpaceAny,
2172
+ fnPromptEncryption: (space: IbGibSpaceAny) => Promise<EncryptionIbGib | undefined>,
2173
+ }): Promise<boolean> {
2174
+ const lc = `${this.lc}[${this._createEncryption.name}]`;
2175
+ try {
2176
+ if (logalot) { console.log(`${lc} starting...`); }
2177
+ const alert = this.getFnAlert!();
2178
+
2179
+ let encryptionIbGibs: IbGib_V1[] = await this.getSpecialRel8dIbGibs({
2180
+ type: "encryptions",
2181
+ rel8nName: ENCRYPTION_REL8N_NAME,
2182
+ space,
2183
+ });
2184
+ if (encryptionIbGibs.length === 0) {
2185
+ await alert({
2186
+ title: 'next create an encryption...',
2187
+ msg: "Now we need to create an encryption setting. If you don't know what this is, just fill in the requirements and leave the others as defaults.",
2188
+ });
2189
+ }
2190
+ while (encryptionIbGibs.length === 0) {
2191
+ let encryptionIbGib = await fnPromptEncryption(space);
2192
+ if (encryptionIbGib === undefined) {
2193
+ await this.getFnAlert!()({ title: 'cancelled', msg: 'Cancelled.' });
2194
+ return false;
2195
+ }
2196
+ await this.registerNewIbGib({ ibGib: encryptionIbGib, space });
2197
+ await this.rel8ToSpecialIbGib({
2198
+ type: "encryptions",
2199
+ rel8nName: ENCRYPTION_REL8N_NAME,
2200
+ ibGibsToRel8: [encryptionIbGib],
2201
+ space,
2202
+ });
2203
+ encryptionIbGibs = await this.getSpecialRel8dIbGibs({
2204
+ type: "encryptions",
2205
+ rel8nName: ENCRYPTION_REL8N_NAME,
2206
+ space,
2207
+ });
2208
+ }
2209
+ return true;
2210
+ } catch (error) {
2211
+ console.error(`${lc} ${error.message}`);
2212
+ throw error;
2213
+ } finally {
2214
+ if (logalot) { console.log(`${lc} complete.`); }
2215
+ }
2216
+ }
2217
+
2218
+ protected async _createOuterspace({
2219
+ space,
2220
+ fnPromptOuterSpace,
2221
+ }: {
2222
+ space: IbGibSpaceAny,
2223
+ fnPromptOuterSpace: (space: IbGibSpaceAny) => Promise<IbGibSpaceAny | undefined>,
2224
+ }
2225
+ ): Promise<boolean> {
2226
+ const lc = `${this.lc}[${this._createOuterspace.name}]`;
2227
+ try {
2228
+ const alert = this.getFnAlert!();
2229
+ let outerspaceIbGibs: IbGib_V1[] = await this.getSpecialRel8dIbGibs({
2230
+ type: "outerspaces",
2231
+ rel8nName: SYNC_SPACE_REL8N_NAME,
2232
+ space,
2233
+ });
2234
+ if (outerspaceIbGibs.length === 0) {
2235
+ await alert({
2236
+ title: 'Now to outerspace...',
2237
+ msg: `Great! Now we can create an outerspace ibgib, which is like a connection from this local space to other spaces (like the cloud).`,
2238
+ });
2239
+ }
2240
+ while (outerspaceIbGibs.length === 0) {
2241
+ let outerspaceIbGib = await fnPromptOuterSpace(space);
2242
+ if (outerspaceIbGib === undefined) { break; }
2243
+ await this.registerNewIbGib({ ibGib: outerspaceIbGib, space });
2244
+ await this.rel8ToSpecialIbGib({
2245
+ type: "outerspaces",
2246
+ rel8nName: SYNC_SPACE_REL8N_NAME,
2247
+ ibGibsToRel8: [outerspaceIbGib],
2248
+ space,
2249
+ });
2250
+ await alert({
2251
+ title: 'Good job',
2252
+ msg: `Great! Now we can use this space to synchronize & import ibgibs.`,
2253
+ });
2254
+ outerspaceIbGibs = await this.getSpecialRel8dIbGibs({
2255
+ type: "outerspaces",
2256
+ rel8nName: SYNC_SPACE_REL8N_NAME,
2257
+ space,
2258
+ });
2259
+ }
2260
+ if (outerspaceIbGibs.length > 0) {
2261
+ return true;
2262
+ } else {
2263
+ await alert({ title: 'cancelled', msg: 'Cancelled.' });
2264
+ return false;
2265
+ }
2266
+ } catch (error) {
2267
+ console.log(`${lc} ${error.message}`);
2268
+ throw error;
2269
+ }
2270
+ }
2271
+
2272
+ async getAppSyncSpaces({
2273
+ unwrapEncrypted,
2274
+ createIfNone,
2275
+ dontPrompt,
2276
+ space,
2277
+ fnSpaceFactory,
2278
+ fnPromptSecret,
2279
+ fnPromptEncryption,
2280
+ fnPromptOuterSpace,
2281
+ }: {
2282
+ unwrapEncrypted: boolean,
2283
+ createIfNone: boolean,
2284
+ /**
2285
+ * If true, don't prompt the user if we don't have it already cached.
2286
+ *
2287
+ * We don't want the user to hit the page and then always have to type in
2288
+ * the password, just because my password code sucks atow.
2289
+ */
2290
+ dontPrompt?: boolean,
2291
+ space?: IbGibSpaceAny,
2292
+ fnSpaceFactory: (unencryptedSpaceData: any) => Promise<IbGibSpaceAny>,
2293
+ fnPromptSecret: (space: IbGibSpaceAny) => Promise<SecretIbGib_V1 | undefined>,
2294
+ fnPromptEncryption: (space: IbGibSpaceAny) => Promise<EncryptionIbGib | undefined>,
2295
+ fnPromptOuterSpace: (space: IbGibSpaceAny) => Promise<IbGibSpaceAny | undefined>,
2296
+ }): Promise<IbGibSpaceAny[]> {
2297
+ const lc = `${this.lc}[${this.getAppSyncSpaces.name}]`;
2298
+ try {
2299
+ // I don't want this pinging the user if we're on the welcome page.
2300
+ dontPrompt = dontPrompt ?? documentLocationIsAtWelcomePage();
2301
+
2302
+ space = space ?? await this.getLocalUserSpace({});
2303
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: bf09346708ba4d6e9a1389bd1b66d500)`); }
2304
+
2305
+ // get existing
2306
+ let appSyncSpaces: IbGibSpaceAny[] =
2307
+ await this.getSpecialRel8dIbGibs<IbGibSpaceAny>({
2308
+ type: "outerspaces",
2309
+ rel8nName: SYNC_SPACE_REL8N_NAME,
2310
+ space,
2311
+ });
2312
+
2313
+ // create if applicable
2314
+ if (appSyncSpaces.length === 0 && createIfNone) {
2315
+ const createdReqs = await this._createOuterspaceAndRequiredIbGibs({
2316
+ space,
2317
+ fnPromptSecret,
2318
+ fnPromptEncryption,
2319
+ fnPromptOuterSpace,
2320
+ });
2321
+ if (createdReqs) {
2322
+ appSyncSpaces = await this.getSpecialRel8dIbGibs<IbGibSpaceAny>({
2323
+ type: "outerspaces",
2324
+ rel8nName: SYNC_SPACE_REL8N_NAME,
2325
+ space,
2326
+ });
2327
+ }
2328
+ }
2329
+
2330
+ // unwrap if requested
2331
+ let resSpaces: IbGibSpaceAny[] = [];
2332
+ if (unwrapEncrypted) {
2333
+ for (let i = 0; i < appSyncSpaces.length; i++) {
2334
+ let syncSpace = appSyncSpaces[i];
2335
+
2336
+ if (syncSpace.rel8ns) {
2337
+ if (syncSpace.rel8ns.ciphertext) {
2338
+ syncSpace = await this.unwrapEncryptedSyncSpace({
2339
+ encryptedSpace: syncSpace,
2340
+ fnPromptPassword: this.getFnPromptPassword!(),
2341
+ dontPrompt,
2342
+ space,
2343
+ fnSpaceFactory,
2344
+ });
2345
+ }
2346
+
2347
+ resSpaces.push(syncSpace);
2348
+ } else {
2349
+ }
2350
+
2351
+ }
2352
+ } else {
2353
+ // still (probably) encrypted
2354
+ resSpaces = appSyncSpaces;
2355
+ }
2356
+
2357
+ return resSpaces;
2358
+ } catch (error) {
2359
+ console.error(`${lc} ${error.message}`);
2360
+ return [];
2361
+ }
2362
+ }
2363
+
2364
+ // async createRobbotIbGib({
2365
+ // robbotData,
2366
+ // space,
2367
+ // }: {
2368
+ // robbotData: RobbotData_V1,
2369
+ // space?: IbGibSpaceAny,
2370
+ // }): Promise<{ newRobbotIbGib: RobbotIbGib_V1, newRobbotsAddr: string }> {
2371
+ // const lc = `${this.lc}[${this.createRobbotIbGib.name}]`;
2372
+ // try {
2373
+ // space = space ?? await this.getLocalUserSpace({});
2374
+ // if (!space) { throw new Error(`space falsy and localUserSpace not initialized (E: 33ea7f4633484afa984225d037478ac4)`); }
2375
+
2376
+ // return createRobbotIbGib({
2377
+ // robbotData,
2378
+ // space,
2379
+ // zeroSpace: this.zeroSpace,
2380
+ // fnBroadcast: (x) => this.fnBroadcast(x),
2381
+ // fnUpdateBootstrap: (x) => this.fnUpdateBootstrap(x),
2382
+ // });
2383
+ // } catch (error) {
2384
+ // console.error(`${lc} ${error.message}`);
2385
+ // throw error;
2386
+ // }
2387
+ // }
2388
+
2389
+ async getAppRobbotIbGibs({
2390
+ createIfNone,
2391
+ space,
2392
+ }: {
2393
+ createIfNone: boolean,
2394
+ space?: IbGibSpaceAny,
2395
+ }): Promise<RobbotIbGib_V1[]> {
2396
+ const lc = `${this.lc}[${this.getAppRobbotIbGibs.name}]`;
2397
+ try {
2398
+ space = space ?? await this.getLocalUserSpace({});
2399
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: bf09346708ba4d6e9a1389bd1b66d500)`); }
2400
+
2401
+ // get existing. Note that these are not the robbot witnesses, but only
2402
+ // the robbot ibgib (dtos). They do not have a `witness` function on them
2403
+ // at this point.
2404
+ let appRobbots_MaybeOutOfDate: RobbotIbGib_V1[] =
2405
+ await this.getSpecialRel8dIbGibs<RobbotIbGib_V1>({
2406
+ type: "robbots",
2407
+ rel8nName: ROBBOT_REL8N_NAME,
2408
+ space,
2409
+ });
2410
+
2411
+ let appRobbots: RobbotIbGib_V1[] = [];
2412
+ for (let i = 0; i < appRobbots_MaybeOutOfDate.length; i++) {
2413
+ let robbotIbGib = appRobbots_MaybeOutOfDate[i];
2414
+ let robbotAddr = getIbGibAddr({ ibGib: robbotIbGib });
2415
+ const latestAddr = await this.getLatestAddr({ ibGib: robbotIbGib });
2416
+ if (latestAddr && latestAddr !== robbotAddr) {
2417
+ // robbot has a newer ibgib in its timeline
2418
+ let resGet = await this.get({ addr: latestAddr });
2419
+ if (!resGet || !resGet?.success || (resGet?.ibGibs ?? []).length === 0) {
2420
+ throw new Error(`could not get newer robbot ibgib (E: 15fa346c8ac17edb96e4b0870104c122)`);
2421
+ }
2422
+ robbotIbGib = resGet.ibGibs![0] as RobbotIbGib_V1;
2423
+ robbotAddr = getIbGibAddr({ ibGib: robbotIbGib });
2424
+ }
2425
+
2426
+ const errors = await validateCommonRobbotIbGib({ robbotIbGib });
2427
+ if ((errors ?? []).length === 0) {
2428
+ // only add if robbot doesn't have validation errors
2429
+ appRobbots.push(robbotIbGib);
2430
+ } else {
2431
+ console.error(`${lc} robbot ibGib (${robbotAddr}) has validation errors: ${errors}`)
2432
+ }
2433
+ }
2434
+
2435
+ // create if applicable
2436
+
2437
+ if (appRobbots.length === 0 && createIfNone) {
2438
+ console.error(`${lc} creating new robbot but should be not casting this on next line in src code. (E: 3252c5917e11421dbc1c26b280b8aeef)`);
2439
+ let robbot = await createNewRobbot({ ibgibs: this as any, space });
2440
+ if (robbot) {
2441
+ appRobbots = await this.getSpecialRel8dIbGibs<RobbotIbGib_V1>({
2442
+ type: "robbots",
2443
+ rel8nName: ROBBOT_REL8N_NAME,
2444
+ space,
2445
+ });
2446
+ }
2447
+ }
2448
+
2449
+ return appRobbots;
2450
+ } catch (error) {
2451
+ console.error(`${lc} ${error.message}`);
2452
+ return [];
2453
+ }
2454
+ }
2455
+
2456
+ async getAppAppIbGibs({
2457
+ createIfNone,
2458
+ space,
2459
+ }: {
2460
+ createIfNone: boolean,
2461
+ space?: IbGibSpaceAny,
2462
+ }): Promise<AppIbGib_V1[]> {
2463
+ const lc = `${this.lc}[${this.getAppAppIbGibs.name}]`;
2464
+ try {
2465
+ space = space ?? await this.getLocalUserSpace({});
2466
+ if (!space) { throw new Error(`space falsy and localUserSpace not initialized (?) (E: a5aec4d94f764c6a964179bbb743b577)`); }
2467
+
2468
+ // get existing. Note that these are not the app witnesses, but only
2469
+ // the app ibgib (dtos). They do not have a `witness` function on them
2470
+ // at this point.
2471
+ let appApps_MaybeOutOfDate: AppIbGib_V1[] =
2472
+ await this.getSpecialRel8dIbGibs<AppIbGib_V1>({
2473
+ type: "apps",
2474
+ rel8nName: APP_REL8N_NAME,
2475
+ space,
2476
+ });
2477
+
2478
+ let appApps: AppIbGib_V1[] = [];
2479
+ for (let i = 0; i < appApps_MaybeOutOfDate.length; i++) {
2480
+ const appIbGib = appApps_MaybeOutOfDate[i];
2481
+ const appAddr = getIbGibAddr({ ibGib: appIbGib });
2482
+ const latestAddr = await this.getLatestAddr({ ibGib: appIbGib });
2483
+ if (latestAddr && latestAddr !== appAddr) {
2484
+ // app has a newer ibgib in its timeline
2485
+ let resGet = await this.get({ addr: latestAddr });
2486
+ if (!resGet || !resGet?.success || (resGet?.ibGibs ?? []).length === 0) {
2487
+ throw new Error(`could not get newer app ibgib (E: de6a77634b1e4e16914ef110bb263d7c)`);
2488
+ }
2489
+ appApps.push(resGet.ibGibs![0] as AppIbGib_V1);
2490
+ } else {
2491
+ appApps.push(appIbGib);
2492
+ }
2493
+ }
2494
+
2495
+ // create if applicable
2496
+ if (appApps.length === 0 && createIfNone) {
2497
+ console.error(`${lc} creating new app but should be not casting this on next line in src code. (E: 3252c5917e11421dbc1c26b280b8aeef)`);
2498
+ let app = await createNewApp({ ibgibs: this as any, space });
2499
+ if (app) {
2500
+ appApps = await this.getSpecialRel8dIbGibs<AppIbGib_V1>({
2501
+ type: "apps",
2502
+ rel8nName: APP_REL8N_NAME,
2503
+ space,
2504
+ });
2505
+ }
2506
+ }
2507
+
2508
+ return appApps;
2509
+ } catch (error) {
2510
+ console.error(`${lc} ${error.message}`);
2511
+ return [];
2512
+ }
2513
+ }
2514
+
2515
+ // #region syncIbGibs related
2516
+
2517
+ protected subSagaSyncTimeLog: Subscription | undefined;
2518
+
2519
+
2520
+ async syncIbGibs({
2521
+ dependencyGraphIbGibs,
2522
+ // confirm,
2523
+ watch,
2524
+ fnPreSyncProgress,
2525
+ fnSpaceFactory,
2526
+ fnPromptSecret,
2527
+ fnPromptEncryption,
2528
+ fnPromptOuterSpace,
2529
+ }: {
2530
+ dependencyGraphIbGibs?: IbGib_V1[],
2531
+ // confirm?: boolean,
2532
+ /**
2533
+ * If true, will watch ibgibs in dependency graph that have timelines
2534
+ * (tjps).
2535
+ */
2536
+ watch?: boolean,
2537
+ /**
2538
+ * The meat of the sync process is tracked via a status within the sync
2539
+ * spaces themselves. I'm addign this fn to track the pre and post progress
2540
+ * stuff, since decrypting takes some time, and building the dependency
2541
+ * graphs takes time. Both of these happen before the actual syncing occurs
2542
+ * in the spaces.
2543
+ */
2544
+ fnPreSyncProgress?: (msg: string) => Promise<void>,
2545
+ fnSpaceFactory: (unencryptedSpaceData: any) => Promise<IbGibSpaceAny>,
2546
+ fnPromptSecret: (space: IbGibSpaceAny) => Promise<SecretIbGib_V1 | undefined>,
2547
+ fnPromptEncryption: (space: IbGibSpaceAny) => Promise<EncryptionIbGib | undefined>,
2548
+ fnPromptOuterSpace: (space: IbGibSpaceAny) => Promise<IbGibSpaceAny | undefined>,
2549
+ }): Promise<SyncSagaInfo[] | undefined> {
2550
+ const lc = `${this.lc}[${this.syncIbGibs.name}]`;
2551
+ // map of saga infos across all spaces
2552
+ // const sagaInfoMap: { [spaceGib: string]: SyncSagaInfo } = {};
2553
+ try {
2554
+ if (this.syncing) {
2555
+ console.warn(`already syncing. (E: dfa3ad58e97f4b18b4e4d7dc252208fb)`);
2556
+ return;
2557
+ }
2558
+ if (Object.values(this.sagaInfoMap).length > 0) { throw new Error(`this._syncing is false but sagaInfoMap not cleaned up(?). (E: bb69c808877c4931b5481585043c18e7)(UNEXPECTED)`); }
2559
+
2560
+ this._syncing = true;
2561
+
2562
+ // have to make sagaId and syncStatus$ early to enable timeLog calls
2563
+ const sagaId = (await getUUID()).slice(0, 24);
2564
+ const syncStatus$ = new ReplaySubject<SyncStatusIbGib>();
2565
+ const syncTimelogName = `sync_log ${sagaId}`;
2566
+ console.time(syncTimelogName);
2567
+ console.timeLog(syncTimelogName, 'start');
2568
+ this.subSagaSyncTimeLog = syncStatus$.subscribe({
2569
+ next: (status) => {
2570
+ if (status.data?.statusCode === StatusCode.completed) {
2571
+ console.timeLog(syncTimelogName, 'StatusCode.complete');
2572
+ }
2573
+ },
2574
+ error: (_: any) => {
2575
+ console.timeEnd(syncTimelogName)
2576
+ this.subSagaSyncTimeLog?.unsubscribe();
2577
+ },
2578
+ complete: () => {
2579
+ console.timeEnd(syncTimelogName)
2580
+ this.subSagaSyncTimeLog?.unsubscribe();
2581
+ }
2582
+ });
2583
+
2584
+ // #region validate
2585
+ if (logalot) { console.log(`${lc} starting...`); }
2586
+ if (!dependencyGraphIbGibs || dependencyGraphIbGibs.length === 0) { throw new Error(`ibGibs required. (E: 404c36475fb84fc285a23a67c0b8fcb2)`); }
2587
+ // #endregion
2588
+
2589
+ const localUserSpace = await this.getLocalUserSpace({});
2590
+ if (!localUserSpace?.data) { throw new Error(`localUserSpace?.data falsy (E: e7ff57e4d529cde903619078ee9b6e23)`); }
2591
+ if (!localUserSpace.gib) { throw new Error(`localUserSpace.gib falsy (E: 884d86d20e7a4468b3c8b2c3ab7dba7e)`); }
2592
+
2593
+ // #region get sync spaces and build participant infos
2594
+ if (logalot) { console.log(`${lc} get sync spaces (returns if none)`); }
2595
+ console.timeLog(syncTimelogName, 'getAppSyncSpaces starting (unwrapEncrypted is true) starting...');
2596
+ if (fnPreSyncProgress) { fnPreSyncProgress('getting app sync spaces... (I: aa2e8f32ab26457bad703218aa7fb47d)'); }
2597
+ const appSyncSpaces: IbGibSpaceAny[] = await this.getAppSyncSpaces({
2598
+ unwrapEncrypted: true,
2599
+ createIfNone: true,
2600
+ space: localUserSpace,
2601
+ fnSpaceFactory,
2602
+ fnPromptSecret,
2603
+ fnPromptEncryption,
2604
+ fnPromptOuterSpace,
2605
+ });
2606
+ if (fnPreSyncProgress) { fnPreSyncProgress('build complete. (I: 391a9aa7749d45b1aecf9a0010d9937f)'); }
2607
+ console.timeLog(syncTimelogName, 'getAppSyncSpaces starting (unwrapEncrypted is true) complete.');
2608
+ if (appSyncSpaces.length === 0) {
2609
+ const msg = `Can't sync without sync spaces...wrong password? Cancelling. Restart app to retry password (I know it sucks!...just me coding this thing right now)`;
2610
+ if (logalot) { console.log(`${lc} ${msg}`) };
2611
+ const fnAlert = this.getFnAlert!();
2612
+ await fnAlert({ title: "Cancelled", msg });
2613
+ this._syncing = false;
2614
+ console.timeLog(syncTimelogName, 'cancelled');
2615
+ console.timeEnd(syncTimelogName);
2616
+ return;
2617
+ }
2618
+ // const localUserSpace = await this.getLocalUserSpace({});
2619
+ const participants: ParticipantInfo[] = [
2620
+ // local user space is the src
2621
+ { id: localUserSpace.data.uuid, gib: localUserSpace.gib, s_d: 'src', },
2622
+
2623
+ // each sync space is a destination
2624
+ ...appSyncSpaces.map(s => {
2625
+ if (!s.data) { throw new Error(`space.data required. (E: 3c192771e84445a4b6476d5193b07e9d)`); }
2626
+ if (!s.data.uuid) { throw new Error(`space.data.uuid required. (E: d27e9998227840f99d45a3ed245f3196)`); }
2627
+ if (!s.gib) { throw new Error(`space.gib required. (E: db73aceb2f8445d8964ae49b59957072)`); }
2628
+ return { id: s.data.uuid, gib: s.gib, s_d: 'dest', } as ParticipantInfo;
2629
+ }),
2630
+ ];
2631
+ // #endregion
2632
+
2633
+ // if (fnPreSyncProgress) { fnPreSyncProgress('building dependency graph... (I: ae178a39c2594557b6d0489b02336ecd)'); }
2634
+ // get **ALL** ibgibs that we'll need to put/merge
2635
+ // const allIbGibsToSync: { [addr: string]: IbGib_V1 } = {};
2636
+ // dependencyGraphIbGibs.forEach(x => { allIbGibsToSync[getIbGibAddr({ ibGib: x })] = x });
2637
+
2638
+ // await this._getAllIbGibsToSyncFromGraph({ dependencyGraphIbGibs, space: localUserSpace });
2639
+
2640
+ // _NOW_ we can finally put/merge into sync spaces.
2641
+ // this returns to us the most recent versions which we can update
2642
+ // our local timelines if we so choose (which we will).
2643
+ // NOTE: we won't worry about what if different sync spaces have different
2644
+ // versions atm. We're just going to do this assuming sync spaces
2645
+ // are nice and coordinated (which they aren't).
2646
+
2647
+ if (logalot) { console.log(`${lc} syncing to spaces in parallel...`); }
2648
+ const multiSpaceOpId = await getUUID();
2649
+ const allSagaInfos: SyncSagaInfo[] = [];
2650
+ const startSyncPromises: Promise<void>[] = appSyncSpaces.map(async syncSpace => {
2651
+ // create the info that will track progress over entire sync saga
2652
+ const sagaInfo =
2653
+ await this._createNewSyncSagaInfo({
2654
+ multiSpaceOpId,
2655
+ allIbGibsToSync: dependencyGraphIbGibs,
2656
+ syncSpace,
2657
+ participants,
2658
+ sagaId,
2659
+ syncStatus$,
2660
+ });
2661
+ this.sagaInfoMap[sagaInfo.sagaId] = sagaInfo;
2662
+ allSagaInfos.push(sagaInfo);
2663
+ try {
2664
+ // _startSync creates a status observable that can keep us up to date
2665
+ // on the status updates throughout the sync saga. We can handle
2666
+ // updating our own local space based on those status updates.
2667
+ // await this._startSync({syncSagaInfo: sagaInfo, confirm});
2668
+ // await this._startSync({ syncSagaInfo: sagaInfo, watch, syncTimelogName });
2669
+
2670
+ // taking out watch for now
2671
+ await this._startSync({ syncSagaInfo: sagaInfo, watch: false, syncTimelogName });
2672
+ } catch (error) {
2673
+ // if this throws, then that is unexpected. The above result should
2674
+ // always be returned, and if it's errored then it should indicate as
2675
+ // such.
2676
+ console.error(`${lc} (UNEXPECTED) ${error.message}`);
2677
+ throw error;
2678
+ }
2679
+ });
2680
+
2681
+ // await just the initial starting of each space's sync operation. when
2682
+ // this promise is awaited, the sync operation is not done, only the
2683
+ // starting of all sync sagas across all spaces.
2684
+ console.timeLog(syncTimelogName, 'awaiting all startSyncPromises starting...');
2685
+ await Promise.all(startSyncPromises);
2686
+ console.timeLog(syncTimelogName, 'awaiting all startSyncPromises complete.');
2687
+
2688
+ // at this point, all spaces have prepared and are going. the sync saga
2689
+ // info attached to each arg/result ibgib has the observable syncStatus$
2690
+ // that will produce the status updates which can be interpreted &
2691
+ // responded to.
2692
+ await this._handleSagaUpdates();
2693
+ return allSagaInfos;
2694
+ } catch (error) {
2695
+ console.error(`${lc} ${error.message}`);
2696
+ this.finalizeAllSyncSagas_NoThrow({ error });
2697
+ throw error;
2698
+ } finally {
2699
+ if (logalot) { console.log(`${lc} complete.`); }
2700
+ }
2701
+ }
2702
+
2703
+ protected finalizeSyncSaga({
2704
+ sagaInfo,
2705
+ error,
2706
+ }: {
2707
+ sagaInfo: SyncSagaInfo,
2708
+ error?: any,
2709
+ }): void {
2710
+ const lc = `${this.lc}[${this.finalizeSyncSaga.name}]`;
2711
+ try {
2712
+ if (logalot) { console.log(`${lc} starting...`); }
2713
+ if (sagaInfo.complete) { return; }
2714
+ if (!sagaInfo.syncStatus$.closed) {
2715
+ if (error) {
2716
+ const emsg =
2717
+ typeof (error) === 'string' ? error : error.message ??
2718
+ `${lc} something went wrong (E: d7db873d9e8b4f14b5b490cadd9730f4)`;
2719
+ console.error(emsg);
2720
+ sagaInfo.syncStatus$.error(emsg);
2721
+ }
2722
+ sagaInfo.syncStatus$.complete();
2723
+ }
2724
+ // I think $.complete() closes subscriptions, but to be double sure...
2725
+ (sagaInfo.syncStatusSubscriptions ?? [])
2726
+ .filter(sub => sub && !sub.closed)
2727
+ .forEach(sub => { sub.unsubscribe(); });
2728
+
2729
+ if (logalot) { console.log(`${lc} setting sagaInfo.complete to true (I: 85c34469cdac404782c2024ad6b6fbd1)`); }
2730
+ sagaInfo.complete = true;
2731
+ this._updateSagaInfoMapAndIsSyncingFlag();
2732
+
2733
+ if (logalot) { console.log(`${lc} complete.`); }
2734
+ } catch (err) {
2735
+ console.error(`${lc} ${err.message}`);
2736
+ if (logalot) { console.log(`${lc} setting sagaInfo.complete to true (I: 23de6ef45eaf47a1918038dce3da7d78)`); }
2737
+ sagaInfo.complete = true;
2738
+ throw err;
2739
+ } finally {
2740
+ if (logalot) { console.log(`${lc} complete.`); }
2741
+ }
2742
+ }
2743
+
2744
+ protected finalizeAllSyncSagas_NoThrow({
2745
+ error,
2746
+ }: {
2747
+ error?: any,
2748
+ }): void {
2749
+ const lc = `${this.lc}[${this.finalizeAllSyncSagas_NoThrow.name}]`;
2750
+ try {
2751
+ const syncSagaInfos_NotComplete =
2752
+ Object.values(this.sagaInfoMap).filter(x => !x.complete);
2753
+ for (let i = 0; i < syncSagaInfos_NotComplete.length; i++) {
2754
+ const sagaInfo = syncSagaInfos_NotComplete[i];
2755
+ this.finalizeSyncSaga({ sagaInfo, error });
2756
+ }
2757
+ } catch (error) {
2758
+ console.error(`${lc}(UNEXPECTED) ${error.message}`);
2759
+ // caller expects does NOT rethrow!
2760
+ } finally {
2761
+ this._updateSagaInfoMapAndIsSyncingFlag();
2762
+ }
2763
+ }
2764
+
2765
+ protected _updateSagaInfoMapAndIsSyncingFlag(): void {
2766
+ const lc = `${this.lc}[${this._updateSagaInfoMapAndIsSyncingFlag.name}]`;
2767
+ try {
2768
+ if (logalot) { console.log(`${lc} starting...`); }
2769
+
2770
+ const sagaInfoKeys = Object.keys(this.sagaInfoMap || {});
2771
+
2772
+ if (sagaInfoKeys.length === 0) {
2773
+ this.sagaInfoMap = {};
2774
+ if (this._syncing) { this._syncing = false; }
2775
+ return; /* <<<< returns early */
2776
+ }
2777
+
2778
+ const sagaInfos = Object.values(this.sagaInfoMap);
2779
+ if (sagaInfos.every(info => info.complete)) {
2780
+ if (logalot) { console.log(`${lc} all sagaInfos are complete. finalizing sync. (I: f6d94deb5509e6b2e5371b9bfc007422)`); }
2781
+ this.sagaInfoMap = {};
2782
+ this._syncing = false;
2783
+ if (logalot) { console.log(`${lc} this._syncing is now false.`); }
2784
+ }
2785
+ } catch (error) {
2786
+ console.error(`${lc} ${error.message}`);
2787
+ throw error;
2788
+ } finally {
2789
+ if (logalot) { console.log(`${lc} complete.`); }
2790
+ }
2791
+ }
2792
+
2793
+ protected async _createNewSyncSagaInfo({
2794
+ multiSpaceOpId,
2795
+ allIbGibsToSync,
2796
+ syncSpace,
2797
+ participants,
2798
+ sagaId,
2799
+ syncStatus$,
2800
+ }: {
2801
+ multiSpaceOpId: string,
2802
+ allIbGibsToSync: IbGib_V1[],
2803
+ syncSpace: IbGibSpaceAny,
2804
+ participants: ParticipantInfo[],
2805
+ /**
2806
+ * have to make this early to enable the console.time/Log calls.
2807
+ */
2808
+ sagaId: string,
2809
+ /**
2810
+ * have to make this early to enable the console.time/Log calls.
2811
+ */
2812
+ syncStatus$: ReplaySubject<SyncStatusIbGib>,
2813
+ }): Promise<SyncSagaInfo> {
2814
+ const lc = `${this.lc}[${this._createNewSyncSagaInfo.name}]`;
2815
+ try {
2816
+ if (!multiSpaceOpId) { throw new Error(`multiSpaceOpId required. (E: a7e228dbd63948d784a67ddbb342e4f7)`); }
2817
+ if (!syncSpace.data) { throw new Error(`syncSpace.data required. (E: 1c44334cf4545de147ef2dc675406a23)`); }
2818
+
2819
+ // do the addrs outside of info initializer
2820
+ const syncAddrs_All = allIbGibsToSync.map(x => getIbGibAddr({ ibGib: x }));
2821
+ const syncAddrs_All_WithTjps = allIbGibsToSync
2822
+ .filter(x => hasTjp({ ibGib: x }))
2823
+ .map(x => getIbGibAddr({ ibGib: x }));
2824
+ const syncAddrs_All_AreTjps = allIbGibsToSync
2825
+ .filter(x => x.gib !== GIB && x.data?.isTjp === true)
2826
+ .map(x => getIbGibAddr({ ibGib: x }));
2827
+ const syncAddrs_All_WithoutTjps =
2828
+ syncAddrs_All.filter(addr => !syncAddrs_All_WithTjps.includes(addr));
2829
+
2830
+ // do the info initializer
2831
+ const syncSagaInfo: SyncSagaInfo = {
2832
+ multiSpaceOpId,
2833
+ outerSpace: syncSpace,
2834
+ // spaceGib: syncSpace.gib,
2835
+ spaceId: syncSpace.data.uuid,
2836
+ sagaId,
2837
+ participants,
2838
+ witnessFnArgsAndResults$: new ReplaySubject<SyncSpaceOptionsIbGib | SyncSpaceResultIbGib>(),
2839
+
2840
+ // syncStatus$: new ReplaySubject<SyncStatusIbGib>(),
2841
+ syncStatus$,
2842
+ syncStatusSubscriptions: [],
2843
+
2844
+ syncIbGibs_All: allIbGibsToSync,
2845
+ syncAddrs_All,
2846
+ syncAddrs_All_AreTjps,
2847
+ syncAddrs_All_WithTjps,
2848
+ syncAddrs_All_WithoutTjps,
2849
+ syncAddrs_Skipped: [],
2850
+ syncAddrs_ToDo: [],
2851
+ syncAddrs_InProgress: [],
2852
+ syncAddrs_Failed: [],
2853
+ };
2854
+
2855
+ const syncTimelogName = `sync_log ${syncSagaInfo.sagaId}`;
2856
+ // console.time(syncTimelogName);
2857
+ console.timeLog(syncTimelogName);
2858
+ // return it
2859
+ return syncSagaInfo;
2860
+ } catch (error) {
2861
+ console.error(`${lc} ${error.message}`);
2862
+ throw error;
2863
+ }
2864
+ }
2865
+
2866
+ /**
2867
+ * this {@link IbGibsService} acts as the local app space intermediary,
2868
+ * a broker between the local space and the sync space(s). So this
2869
+ * function's job is to coordinate sending the given ibgibs
2870
+ * to a single sync space and return the observable (subject)
2871
+ * corresponding to the multi-step sync process.
2872
+ *
2873
+ * The caller will be responsible for coordinating among sync
2874
+ * space results and handling updates to local space ibgib's,
2875
+ * such as rebasing/updating/etc.
2876
+ *
2877
+ * @returns
2878
+ */
2879
+ protected async _startSync({
2880
+ syncSagaInfo,
2881
+ watch,
2882
+ // confirm,
2883
+ syncTimelogName,
2884
+ }: {
2885
+ syncSagaInfo: SyncSagaInfo,
2886
+ watch?: boolean,
2887
+ /**
2888
+ * Will confirm via checking existence of ibgibs in sync space after
2889
+ * writing them.
2890
+ *
2891
+ * NOTE: IGNORED ATM
2892
+ */
2893
+ // confirm?: boolean,
2894
+ syncTimelogName: string,
2895
+ }): Promise<SyncSpaceResultIbGib> {
2896
+ const lc = `${this.lc}[${this._startSync.name}]`;
2897
+ try {
2898
+ const {
2899
+ multiSpaceOpId, participants, sagaId, outerSpace: syncSpace,
2900
+ syncIbGibs_All,
2901
+ syncAddrs_All, syncAddrs_All_WithTjps: syncAddrs_All_Tjps, syncAddrs_All_WithoutTjps: syncAddrs_All_NonTjps,
2902
+ } = syncSagaInfo;
2903
+
2904
+ // first we want to get the ball rolling
2905
+ // we will get back an ibGib that we can use to track the progress of the
2906
+ // entire operation wrt this space.
2907
+ console.timeLog(syncTimelogName, `getLocalUserSpace starting...`);
2908
+ const localUserSpace = await this.getLocalUserSpace({});
2909
+ if (!localUserSpace) { throw new Error(`localUserSpace required (E: e01fc13ce7c5a802b50aa0f9b2c13a23)`); }
2910
+ if (!localUserSpace.data) { throw new Error(`localUserSpace.data required (E: e01fc13ce7c5a802b50aa0f9b2c13a23)`); }
2911
+ console.timeLog(syncTimelogName, `getLocalUserSpace complete.`);
2912
+
2913
+ const argStartSync: SyncSpaceOptionsIbGib = await syncSpace.argy({
2914
+ argData: {
2915
+ // cmd: 'put', cmdModifiers: watch ? ['sync', 'watch'] : ['sync'],
2916
+ cmd: 'put', cmdModifiers: ['sync'],
2917
+ sagaId,
2918
+ participants,
2919
+ ibGibAddrs: syncAddrs_All,
2920
+ ibGibAddrs_All_Tjps: syncAddrs_All_Tjps,
2921
+ ibGibAddrs_All_NonTjps: syncAddrs_All_NonTjps,
2922
+ } as SyncSpaceOptionsData,
2923
+ ibGibs: syncIbGibs_All, // do we need to do this yet?
2924
+ ibMetadata: `sync src ${localUserSpace.data.name} srcId ${localUserSpace.data.uuid}`,
2925
+ });
2926
+ argStartSync.syncSagaInfo = syncSagaInfo;
2927
+
2928
+ // atow we only have one cycle. in the future, I think we will be having the possibility
2929
+ // of multiple cycles, which is why I have this structured as an observable
2930
+ // and not hard-coding a single arg/result in the saga info.
2931
+ syncSagaInfo.witnessFnArgsAndResults$.next(argStartSync);
2932
+ console.timeLog(syncTimelogName, `syncSpace witness starting...`);
2933
+ const resStartSync: SyncSpaceResultIbGib = await syncSpace.witness(argStartSync);
2934
+ console.timeLog(syncTimelogName, `syncSpace witness complete.`);
2935
+ if (!resStartSync.data?.statusTjpAddr) { throw new Error(`resStartSync.data.statusTjpAddr is falsy. sagaId: ${sagaId} (E: 727b5cc1a0254497bc6e06e9c6760564)`); }
2936
+ syncSagaInfo.witnessFnArgsAndResults$.next(resStartSync);
2937
+
2938
+ // in our return, we can check for updates since our last communication.
2939
+ // if (Object.keys(resStartSync.data.watchTjpUpdateMap ?? {}).length > 0) {
2940
+ // if (logalot) { console.log(`${lc} resStartSync.data.watchTjpUpdateMap: ${pretty(resStartSync.data.watchTjpUpdateMap)}`); }
2941
+ // console.timeLog(syncTimelogName, `handleWatchTjpUpdates starting...`);
2942
+ // await this.handleWatchTjpUpdates({
2943
+ // outerSpace: syncSpace,
2944
+ // updates: resStartSync.data.watchTjpUpdateMap,
2945
+ // localUserSpace,
2946
+ // });
2947
+ // console.timeLog(syncTimelogName, `handleWatchTjpUpdates complete.`);
2948
+ // }
2949
+
2950
+ // most of our handling will be in subscription to syncStatus$ updates.
2951
+ return resStartSync;
2952
+ } catch (error) {
2953
+ console.error(`${lc} ${error.message}`);
2954
+ throw error;
2955
+ }
2956
+ }
2957
+
2958
+ /**
2959
+ * Coming in to this function, we have results from one or more
2960
+ * sync spaces. Here are the combinations of expected outcomes:
2961
+ *
2962
+ * 1. All success, each returns the exact same new ib^gib address(es).
2963
+ * 2. All success, but different spaces return different ib^gib address(es).
2964
+ * 3. Some success, which returns exact same new ib^gib, some error.
2965
+ * 4. Some success, more than one ib^gib address(es), some error.
2966
+ * 5. All error.
2967
+ *
2968
+ * For the first naive implementation, we will always be optimistic,
2969
+ * assuming that errors will be resolved with enough attempts. This
2970
+ * is closely related to our initial optimistic strategy of
2971
+ * "access is authorization" + "unordered dna equivalence". These
2972
+ * strategies mean that we are simply, optimistically, and
2973
+ * naively applying incoming dna transforms to ibgib timelines.
2974
+ * So failure should only result from a failure at the communication
2975
+ * layer, assuming non-adversarial conditions.
2976
+ *
2977
+ * ## future
2978
+ *
2979
+ * The handling of this should absolutely be generalized to the
2980
+ * requirements for interspatial relationships. This is the language
2981
+ * that must be implemented to deal with this in a dynamic fashion.
2982
+ *
2983
+ * This does not equate to only "ad hoc" in the sense of untested/untried.
2984
+ * This means that we have an "on-chain", public linked requirement
2985
+ * for consensus as opposed to a hard-coded consensus as is defined
2986
+ * in this function.
2987
+ *
2988
+ * It is in this generalization that byzantine resolution can be
2989
+ * achieved through whichever mechanism is correct for the use case.
2990
+ */
2991
+ protected async _handleSagaUpdates(): Promise<void> {
2992
+ const lc = `${this.lc}[${this._handleSagaUpdates.name}]`;
2993
+ try {
2994
+ // at this point in execution, each space has returned the result ibgib
2995
+ // which has a syncStatus$ observable.
2996
+
2997
+ const infos = Object.values(this.sagaInfoMap);
2998
+ for (let i = 0; i < infos.length; i++) {
2999
+ const sagaInfo = infos[i];
3000
+
3001
+ let sub = sagaInfo.syncStatus$
3002
+ .pipe(
3003
+ concatMap(async (status: SyncStatusIbGib) => {
3004
+ if (logalot) { console.log(`${lc}(sagaId: ${sagaInfo.sagaId}) status received. ${status?.data?.statusCode ?? 'no status'}`) }
3005
+ await this._handleSyncStatusIbGib({ status, sagaInfo });
3006
+ return status;
3007
+ })
3008
+ ).subscribe({
3009
+ next: (status: SyncStatusIbGib) => {
3010
+ if (logalot) { console.log(`${lc}(sagaId: ${sagaInfo.sagaId}) subscribe next triggered. status: ${status?.data?.statusCode} (I: 41e1f61e5e1b422ead1d72a1c92c7d51)`); }
3011
+ },
3012
+ error: async (error: string) => {
3013
+ const emsg = `${lc}(sagaId: ${sagaInfo.sagaId}) syncStatus$.error: ${error}`;
3014
+ console.error(emsg);
3015
+ // await this.getFnAlert!()({title: 'couldnt this.syncIbGibs...', msg: emsg});
3016
+ this.finalizeSyncSaga({ sagaInfo, error: emsg });
3017
+ },
3018
+ complete: () => {
3019
+ if (logalot) { console.log(`${lc}(sagaId: ${sagaInfo.sagaId}) syncStatus$.complete.`); }
3020
+ }
3021
+ });
3022
+ // if (sagaInfo.syncStatusSubscriptions) { sagaInfo.syncStatusSubscriptions.push(sub); }
3023
+ if (!sagaInfo.syncStatusSubscriptions) { throw new Error(`sagaInfo.syncStatusSubscriptions array falsy? (E: f6d834beaa164c6ea1073d35b9fecd01)`) }
3024
+ sagaInfo.syncStatusSubscriptions.push(sub);
3025
+ }
3026
+
3027
+ } catch (error) {
3028
+ console.error(`${lc} ${error.message}`);
3029
+ throw error;
3030
+ }
3031
+ }
3032
+
3033
+ protected async _handleSyncStatusIbGib({
3034
+ status,
3035
+ sagaInfo,
3036
+ }: {
3037
+ status: SyncStatusIbGib,
3038
+ sagaInfo: SyncSagaInfo,
3039
+ }): Promise<void> {
3040
+ const lc = `${this.lc}[${this._handleSyncStatusIbGib.name}]`;
3041
+ try {
3042
+ if (logalot) { console.log(`${lc} starting...`); }
3043
+ // #region validate
3044
+ if (!status) { throw new Error(`falsy status. (E: 8da370a9f3df48a98cc08f1cccf5f2dc)`); }
3045
+ if (!status.data) { throw new Error(`falsy status.data. (E: 996d458c5cde4622ba1ed54c7e188815)`); }
3046
+ if (!status.data.statusCode) { throw new Error(`falsy status.data.statusCode (E: 7f2bec6b9dd0484eb7ef97966e6dd027)`); }
3047
+ // #endregion validate
3048
+
3049
+ let resStoreStatusLocally =
3050
+ await this.put({ ibGibs: status.statusIbGibGraph, isMeta: true });
3051
+ if (!resStoreStatusLocally.success) {
3052
+ // just log for now...the saving is supposed to the the log in the first place.
3053
+ console.error(`${lc}(UNEXPECTED) couldn't save status graph locally? sagaId: ${sagaInfo.sagaId} (E: b472101897824195b96b658c441dfb55)`);
3054
+ }
3055
+ const statusCode = status.data.statusCode;
3056
+ if (logalot) { console.log(`${lc} status update received. statusCode: ${statusCode}. sagaId: ${sagaInfo.sagaId}. spaceId: ${sagaInfo.spaceId}`); }
3057
+
3058
+ switch (statusCode) {
3059
+ case StatusCode.started:
3060
+ // nothing to do on start? hmm...
3061
+ break;
3062
+
3063
+ case StatusCode.inserted:
3064
+ // await this.handleSyncComplete_Inserted({sagaInfo, status});
3065
+ // nothing further to do? hmm...
3066
+ break;
3067
+
3068
+ case StatusCode.updated:
3069
+ // await this.handleSyncComplete_Updated({sagaInfo, status});
3070
+ // nothing further to do? hmm...
3071
+ break;
3072
+
3073
+ case StatusCode.merged_dna:
3074
+ await this.handleSyncStatus_Merged({ status });
3075
+ break;
3076
+
3077
+ case StatusCode.merged_state:
3078
+ await this.handleSyncStatus_Merged({ status });
3079
+ break;
3080
+
3081
+ case StatusCode.already_synced:
3082
+ // await this.handleSyncComplete_AlreadySynced({sagaInfo, status});
3083
+ // nothing further to do? hmm...
3084
+ break;
3085
+
3086
+ case StatusCode.completed:
3087
+ await this.handleSyncStatus_Complete({ sagaInfo });
3088
+ break;
3089
+
3090
+ case StatusCode.undefined:
3091
+ // atow undefined is used in primitive status parentage
3092
+ throw new Error(`statusCode is "undefined". Maybe published a primitive? sagaId: ${sagaInfo.sagaId} (E: c98376f35b194adf9bf12ff9259a2569)`);
3093
+
3094
+ default:
3095
+ // ?
3096
+ throw new Error(`(UNEXPECTED) unknown status.data.statusCode (${status.data.statusCode}). sagaId: ${sagaInfo.sagaId} (E: e4872abfc1ae4c27905793ca0f937a9b)`);
3097
+ }
3098
+ if (logalot) { console.log(`${lc} complete.`); }
3099
+ } catch (error) {
3100
+ const emsg = `${lc} ${error.message}`;
3101
+ console.error(emsg);
3102
+ sagaInfo.syncStatus$.error(emsg);
3103
+ }
3104
+ };
3105
+
3106
+ protected async handleSyncStatus_Merged({
3107
+ status,
3108
+ }: {
3109
+ status: SyncStatusIbGib,
3110
+ }): Promise<void> {
3111
+ const lc = `${this.lc}[${this.handleSyncStatus_Merged.name}]`;
3112
+ try {
3113
+ if (logalot) { console.log(`${lc} starting...`); }
3114
+ // #region validate
3115
+
3116
+ // not necessarily the case, if we only have changes on the store side, we apply no dna and create no side effects
3117
+ if ((status.createdIbGibs ?? []).length === 0 &&
3118
+ (status.storeOnlyIbGibs ?? []).length === 0
3119
+ ) { throw new Error('status.createdIbGibs and/or status.storeOnlyIbGibs required when merging. (E: d118bde47fb9434fa95d747f8e4f6b33)'); }
3120
+
3121
+ if (Object.keys(status.ibGibsMergeMap ?? {}).length === 0) { throw new Error('status.ibGibsMergeMap required when merging. (E: 0f06238e5535408f8980e0f9f82cf564)'); }
3122
+
3123
+ // #endregion validate
3124
+
3125
+ if (logalot) { console.log(`${lc} validated.`); }
3126
+
3127
+ // first, we will store the newly created ibgibs in the local space. Then
3128
+ // we want to rebase our local timeline to point to the new one. I believe
3129
+ // we can do this simply by registering the latest created ibgib which
3130
+ // will record it as the local latest ibgib in the tjp timeline. It may be
3131
+ // best to somehow tag the rebased ibgib, which would enable us to
3132
+ // optionally see this later without modifying the ibgib timeline by a
3133
+ // mut8 or rel8 function directly on the now-vestigial/abandoned timeline.
3134
+
3135
+ /**
3136
+ * groups the incoming `ibGibsToRegister` by tjp and registers each latest.
3137
+ *
3138
+ * @param ibGibsToRegister will call `registerNewIbGib` on the lastest in each timeline
3139
+ */
3140
+ const registerLatestInTimelines = async (ibGibsToRegister: IbGib_V1[]) => {
3141
+ const timelines =
3142
+ getTimelinesGroupedByTjp({ ibGibs: ibGibsToRegister });
3143
+ for (let i = 0; i < Object.keys(timelines).length; i++) {
3144
+ const tjpAddr = Object.keys(timelines)[i];
3145
+ const timeline = timelines[tjpAddr];
3146
+ const latestIbGibInTimeline = timeline[timeline.length - 1];
3147
+ // registerNewIbGib is idempotent if already registered as latest
3148
+ await this.registerNewIbGib({ ibGib: latestIbGibInTimeline });
3149
+ }
3150
+ };
3151
+
3152
+ // first, we will store the newly created ibgibs (if any) in the local space.
3153
+ // created ibgibs may not exist if only the sync space branch has changed.
3154
+ // meanwhile, we must collect all ibgib timelines (with tjps) to register
3155
+ if (status.createdIbGibs?.length ?? 0 > 0) {
3156
+ if (logalot) { console.log(`${lc} putting createdIbGibs (${status.createdIbGibs!.length}): ${status.createdIbGibs!.map(x => getIbGibAddr({ ibGib: x })).join('\n')}.`); }
3157
+ const resPutCreated = await this.put({ ibGibs: status.createdIbGibs });
3158
+ if (!resPutCreated.success) { throw new Error(`Couldn't save created ibGibs locally? (E: f8bc91259c5043d589cd2e7ad2220c1f)`); }
3159
+ if (status.storeOnlyIbGibs) {
3160
+ await registerLatestInTimelines(status.storeOnlyIbGibs)
3161
+ }
3162
+ } else {
3163
+ if (logalot) { console.log(`${lc} no createdIbGibs`); }
3164
+ }
3165
+
3166
+
3167
+ if (status.storeOnlyIbGibs?.length ?? 0 > 0) {
3168
+ if (logalot) { console.log(`${lc} putting storeOnlyIbGibs (${status.storeOnlyIbGibs!.length}): ${status.storeOnlyIbGibs!.map(x => getIbGibAddr({ ibGib: x })).join('\n')}.`); }
3169
+ console.warn(`${lc} putting storeOnlyIbGibs (${status.storeOnlyIbGibs!.length}): ${status.storeOnlyIbGibs!.map(x => getIbGibAddr({ ibGib: x })).join('\n')}.`);
3170
+ const resPutStoreOnly = await this.put({ ibGibs: status.storeOnlyIbGibs });
3171
+ if (!resPutStoreOnly.success) { throw new Error(`Couldn't save storeonly ibGibs locally? (E: c5ab044718ab42bba27f5852149b7ddc)`); }
3172
+ await registerLatestInTimelines(status.storeOnlyIbGibs!)
3173
+ } else {
3174
+ if (logalot) { console.log(`${lc} no storeOnlyIbGibs`); }
3175
+ }
3176
+
3177
+ // download any dependency ibgibs from the new latest ibgib that we don't have already.
3178
+
3179
+ // register the new latest ibgib.
3180
+ // merge map goes from old latest addr -> latest ibGib that was the result of the merge.
3181
+ let newLatestIbGibs = Object.values(status.ibGibsMergeMap ?? {});
3182
+ for (let i = 0; i < newLatestIbGibs.length; i++) {
3183
+ const latestIbGib = newLatestIbGibs[i];
3184
+ if (logalot) { console.log(`${lc} registering latestIbGib in localUserSpace: ${getIbGibAddr({ ibGib: latestIbGib })}`); }
3185
+ await this.registerNewIbGib({ ibGib: latestIbGib });
3186
+ }
3187
+
3188
+ if (logalot) { console.log(`${lc} complete.`); }
3189
+ } catch (error) {
3190
+ console.error(`${lc} ${error.message}`);
3191
+ throw error;
3192
+ }
3193
+ }
3194
+
3195
+ protected async handleSyncStatus_Complete({
3196
+ sagaInfo,
3197
+ }: {
3198
+ sagaInfo: SyncSagaInfo,
3199
+ }): Promise<void> {
3200
+ const lc = `${this.lc}[${this.handleSyncStatus_Complete.name}]`;
3201
+ try {
3202
+ // cleanup just this saga, which corresponds to a single sync space.
3203
+ this.finalizeSyncSaga({ sagaInfo });
3204
+
3205
+ // if this is the last saga across all spaces, clean up the rest.
3206
+ const allSagaInfos = Object.values(this.sagaInfoMap);
3207
+ if (allSagaInfos.every(x => x.complete)) {
3208
+ this.finalizeAllSyncSagas_NoThrow({});
3209
+ }
3210
+ } catch (error) {
3211
+ console.error(`${lc} ${error.message}`);
3212
+ this.finalizeAllSyncSagas_NoThrow({ error });
3213
+ }
3214
+ }
3215
+
3216
+ /**
3217
+ * Searches through timelines and gets every single one of them
3218
+ * in the local space. (ideally)
3219
+ *
3220
+ * ## notes
3221
+ *
3222
+ * So if you pass in an ibgib with a reference to old ibgibs that
3223
+ * have not been updated to the latest, this will get those latest.
3224
+ *
3225
+ * @returns all ibgibs related to given ibgibs
3226
+ */
3227
+ // protected async _getAllIbGibsToSyncFromGraph({
3228
+ // ibGibs,
3229
+ // dependencyGraphIbGibs,
3230
+ // space,
3231
+ // }: {
3232
+ // ibGibs: IbGib_V1[],
3233
+ // dependencyGraphIbGibs: IbGib_V1[],
3234
+ // space: IbGibSpaceAny,
3235
+ // }): Promise<IbGib_V1[]> {
3236
+ // const lc = `${this.lc}[${this._getAllIbGibsToSyncFromGraph.name}]`;
3237
+ // try {
3238
+ // if (dependencyGraphIbGibs?.length > 0) {
3239
+ // // already have the dependecy graph
3240
+ // }
3241
+
3242
+ // // need to get the ibgibs with tjps,
3243
+ // // then filter these to just the latest of given dependency graph ibgibs
3244
+ // // then get the latest for each of these in the local space.
3245
+ // let latestIbGibsWithTjps = await this._getLatestIbGibsWithTjps({ ibGibs: dependencyGraphIbGibs });
3246
+ // if (latestIbGibsWithTjps.length === 0) {
3247
+ // // we have no ibgib timelines, so the incoming dependency graph is complete.
3248
+ // return dependencyGraphIbGibs;
3249
+ // }
3250
+
3251
+ // // An issue arises that each time we get updates to tjp ibgibs, then there is the
3252
+ // // possibility of having additional tjps that weren't in the original graph.
3253
+ // // So we need to call multiple times until we get the same number in as out.
3254
+ // let latestIbGibsWithTjps_CHECK = await this._getLatestIbGibsWithTjps({ ibGibs: latestIbGibsWithTjps });
3255
+ // while (latestIbGibsWithTjps_CHECK.length > latestIbGibsWithTjps.length) {
3256
+ // console.warn(`${lc} another tjp found. calling getLatestIbGibsWithTjps again to check for more. (W: 9735c4194d1243269d4fe4a4ed93cf59)`);
3257
+ // latestIbGibsWithTjps = latestIbGibsWithTjps_CHECK;
3258
+ // latestIbGibsWithTjps_CHECK = await this._getLatestIbGibsWithTjps({ ibGibs: latestIbGibsWithTjps });
3259
+ // }
3260
+
3261
+ // // at this point, we have all of the latest tjp ibgibs, but not their
3262
+ // // dependency graphs. We need to get all dependencies from these and
3263
+ // // combine them with our given dependency graph.
3264
+
3265
+ // // we're going to translate the given dependencyGraphIbGibs that we already have to
3266
+ // // a map that `getDependencyGraph` understands so we don't waste time re-getting them.
3267
+ // let allIbGibsToMergeMap: { [addr: string]: IbGib_V1 } = {};
3268
+ // dependencyGraphIbGibs.forEach(x => { allIbGibsToMergeMap[getIbGibAddr(x)] = x; });
3269
+
3270
+ // // now we can combine both these latest ibGibs with the incoming ibgibs
3271
+ // for (let i = 0; i < latestIbGibsWithTjps.length; i++) {
3272
+ // const latestIbGibWithTjp = latestIbGibsWithTjps[i];
3273
+ // // anything that we have in the incoming dependency
3274
+ // // graph already has been fully traversed, so we put this in `gotten`
3275
+ // allIbGibsToMergeMap = await this.getDependencyGraph({
3276
+ // ibGib: latestIbGibWithTjp,
3277
+ // live: true,
3278
+ // gotten: allIbGibsToMergeMap,
3279
+ // maxRetries: DEFAULT_MAX_RETRIES_GET_DEPENDENCY_GRAPH_OUTERSPACE,
3280
+ // msBetweenRetries: DEFAULT_MS_BETWEEN_RETRIES_GET_DEPENDENCY_GRAPH_OUTERSPACE,
3281
+ // space,
3282
+ // });
3283
+ // }
3284
+
3285
+ // const allIbGibsToMerge = Object.values(allIbGibsToMergeMap);
3286
+
3287
+ // return allIbGibsToMerge;
3288
+ // } catch (error) {
3289
+ // console.error(`${lc} ${error.message}`);
3290
+ // throw error;
3291
+ // }
3292
+ // }
3293
+
3294
+ protected async _getLatestIbGibsWithTjps({
3295
+ ibGibs,
3296
+ warnIfMultipleLocalTimelines,
3297
+ }: {
3298
+ ibGibs: IbGib_V1[],
3299
+ /**
3300
+ * I've already written the code to check for this. But I think
3301
+ * this will be normal when put/merging.
3302
+ */
3303
+ warnIfMultipleLocalTimelines?: boolean,
3304
+ }): Promise<IbGib_V1[]> {
3305
+ const lc = `${this.lc}[${this._getLatestIbGibsWithTjps.name}]`;
3306
+ try {
3307
+ const result: IbGib_V1[] = [];
3308
+ const ibGibsWithTjp_Ungrouped = ibGibs.filter(x =>
3309
+ x.data?.isTjp || (x.rel8ns?.tjp?.length ?? 0) > 0 || x.gib?.includes(GIB_DELIMITER)
3310
+ );
3311
+ // group them by tjp
3312
+ const ibGibsWithTjp_GroupedByTjpGib =
3313
+ groupBy({ items: ibGibsWithTjp_Ungrouped, keyFn: x => getGibInfo({ gib: x.gib }).tjpGib ?? '' });
3314
+ const tjpGibs = Object.keys(ibGibsWithTjp_GroupedByTjpGib);
3315
+ for (let i = 0; i < tjpGibs.length; i++) {
3316
+ const group = ibGibsWithTjp_GroupedByTjpGib[tjpGibs[i]];
3317
+ if (warnIfMultipleLocalTimelines) {
3318
+ // quick check to warn if we have multiple n's for a tjpGib (multi-timeline)
3319
+ let nCounts: { [key: number]: number } = {};
3320
+ for (const ibGibFrame of group) {
3321
+ let n = ibGibFrame.data?.isTjp ? -1 : (ibGibFrame.data?.n ?? -2);
3322
+ if (n === -2) { throw new Error(`ibGibFrame.data.n is undefined. We're only working with those with n right now!`); }
3323
+ nCounts[n] = (nCounts[n] ?? 0) + 1;
3324
+ }
3325
+ if (Object.values(nCounts).some(count => count > 1)) {
3326
+ console.warn(`${lc} we have multiple local timelines.`)
3327
+ }
3328
+ }
3329
+
3330
+ // sort by n (ascending) and then grab the latest one
3331
+ const latestIbGibInGroup =
3332
+ group.sort((a, b) => (a.data?.n ?? -1) > (b.data?.n ?? -1) ? 1 : -1)[group.length - 1];
3333
+ result.push(latestIbGibInGroup);
3334
+ }
3335
+
3336
+ // we're done
3337
+ return result;
3338
+ } catch (error) {
3339
+ console.error(`${lc} ${error.message}`);
3340
+ throw error;
3341
+ }
3342
+ }
3343
+
3344
+ protected async handleWatchTjpUpdates({
3345
+ updates,
3346
+ outerSpace,
3347
+ localUserSpace,
3348
+ }: {
3349
+ updates: { [tjpAddr: string]: IbGibAddr; }
3350
+ outerSpace: IbGibSpaceAny,
3351
+ localUserSpace: IbGibSpaceAny,
3352
+ }): Promise<void> {
3353
+ const lc = `${this.lc}[${this.handleWatchTjpUpdates.name}]`;
3354
+ if (logalot) { console.log(`${lc} starting...`); }
3355
+ try {
3356
+ if (!outerSpace.data) { throw new Error(`outerSpace.data falsy (E: b5fde89e87bef9b8b6ce38e24be4a823)`); }
3357
+
3358
+ /**
3359
+ * compile list of addrs we have locally for all updates, so we don't try
3360
+ * to download them from outer space unnecessarily.
3361
+ */
3362
+ const latestAddrsLocallyWithUpdate: IbGibAddr[] = [];
3363
+ const tjpAddrs = Object.keys(updates);
3364
+ const latestAddrs_Store = Object.values(updates);
3365
+ for (let i = 0; i < tjpAddrs.length; i++) {
3366
+ const tjpAddr = tjpAddrs[i];
3367
+ if (logalot) { console.log(`${lc} tjpAddr: ${tjpAddr}`); }
3368
+ const latestAddrLocally = await this.getLatestAddr({ tjpAddr }) ?? tjpAddr;
3369
+ if (
3370
+ !latestAddrs_Store.includes(latestAddrLocally) &&
3371
+ !latestAddrsLocallyWithUpdate.includes(latestAddrLocally)
3372
+ ) {
3373
+ latestAddrsLocallyWithUpdate.push(latestAddrLocally);
3374
+ }
3375
+ }
3376
+ if (latestAddrsLocallyWithUpdate.length === 0) {
3377
+ if (logalot) { console.log(`${lc} latestAddrsLocallyWithUpdate.length === 0. We already had all of the updates locally perhaps. Returning early. (I: 844193c515084d0ebc348349f1ac41f4)`); }
3378
+ return; /* <<<< returns early */
3379
+ }
3380
+
3381
+ /**
3382
+ * LOCAL dependencies for latest LOCAL addrs for all tjpAddrs in updates.
3383
+ */
3384
+ const localDependencyGraphs = await this.getDependencyGraph({
3385
+ ibGibAddrs: latestAddrsLocallyWithUpdate,
3386
+ live: true,
3387
+ maxRetries: DEFAULT_MAX_RETRIES_GET_DEPENDENCY_GRAPH_OUTERSPACE,
3388
+ msBetweenRetries: DEFAULT_MS_BETWEEN_RETRIES_GET_DEPENDENCY_GRAPH_OUTERSPACE,
3389
+ space: localUserSpace,
3390
+ });
3391
+
3392
+ /** all addrs we already have locally */
3393
+ const addrsAlreadyStoredLocally = Object.keys(localDependencyGraphs);
3394
+
3395
+ // get dependency graph from outer space, skipping all addrs in local already
3396
+ const newerAddrsFromOuterSpace: IbGibAddr[] = Object.values(updates);
3397
+ const newerIbGibDependencyGraphFromOuterSpace = await getDependencyGraph({
3398
+ ibGibAddrs: newerAddrsFromOuterSpace,
3399
+ live: false,
3400
+ skipAddrs: addrsAlreadyStoredLocally,
3401
+ space: outerSpace,
3402
+ });
3403
+ const newerIbGibsFromOuterSpace: IbGib_V1[] =
3404
+ Object.values(newerIbGibDependencyGraphFromOuterSpace);
3405
+
3406
+ if (logalot) { console.log(`${lc} got ${newerIbGibsFromOuterSpace.length} ibGibs from outerspace`); }
3407
+
3408
+ // save locally
3409
+ if (logalot) { console.log(`${lc} saving new ibgibs from outerspace in local space...`); }
3410
+ if (newerIbGibsFromOuterSpace.length > 0) {
3411
+ await this.put({ ibGibs: newerIbGibsFromOuterSpace });
3412
+
3413
+ // register the newest tjp ibGibs locally
3414
+ if (logalot) { console.log(`${lc} registering "new" updated tjp ibgibs locally...`); }
3415
+ for (let i = 0; i < tjpAddrs.length; i++) {
3416
+ const tjpAddr = tjpAddrs[i];
3417
+ const updatedAddr = updates[tjpAddr];
3418
+ if (!addrsAlreadyStoredLocally.includes(updatedAddr)) {
3419
+ const updatedIbGib = newerIbGibDependencyGraphFromOuterSpace[updatedAddr];
3420
+ if (!updatedIbGib) {
3421
+ throw new Error(`did not get updatedIbGib (${updatedAddr}) from outerspace (${outerSpace.data.uuid}) (E: 818de70f5b444a3ba198ba6480a15b04)`);
3422
+ }
3423
+ await this.registerNewIbGib({ ibGib: updatedIbGib });
3424
+ }
3425
+ }
3426
+ }
3427
+
3428
+ } catch (error) {
3429
+ console.error(`${lc} ${error.message}`);
3430
+ // does not rethrow
3431
+ } finally {
3432
+ if (logalot) { console.log(`${lc} complete.`); }
3433
+ }
3434
+ }
3435
+
3436
+ // #endregion syncIbGibs related
3437
+
3438
+ // #region autosync
3439
+
3440
+ async enableAutosync({
3441
+ tjpIbGibs,
3442
+ }: {
3443
+ tjpIbGibs: IbGib_V1[],
3444
+ }): Promise<void> {
3445
+ const lc = `${this.lc}[${this.enableAutosync.name}]`;
3446
+ try {
3447
+ if (logalot) { console.log(`${lc} starting...`); }
3448
+ if ((tjpIbGibs ?? []).length === 0) { throw new Error(`tjps required. (E: 3e3bb6ef1a3d483795440e0efcef8e04)`); }
3449
+
3450
+ // validate tjps
3451
+ if (tjpIbGibs.some(tjp => !tjp.data?.isTjp)) {
3452
+ const wonkyTjpAddrs =
3453
+ tjpIbGibs
3454
+ .filter(tjp => !tjp.data?.isTjp)
3455
+ .map(x => getIbGibAddr({ ibGib: x }));
3456
+ console.warn(`${lc} unrelating tjp whose data.isTjp is false. tjpAddrs: ${wonkyTjpAddrs.join('|')} (W: 03b3d7a94e6d4ccaace3df4657a82322)`);
3457
+ }
3458
+
3459
+ // we'll use addrs to compare to cache and autosync special ibgib
3460
+ let tjpAddrs = tjpIbGibs.map(tjp => getIbGibAddr({ ibGib: tjp }));
3461
+
3462
+ // if we're already autosyncing all tjps, warn and return early check
3463
+ // locally since this is very fast/cheap
3464
+ let notAlreadySyncingTjpAddrs =
3465
+ tjpAddrs.filter(tjpAddr => !this._alwaysAutosyncTjpAddrsCache.has(tjpAddr));
3466
+ if (notAlreadySyncingTjpAddrs.length === 0) {
3467
+ console.warn(`${lc} all tjpAddrs already auto syncing. tjpAddrs: ${tjpAddrs.join('\n')} (W: 7fbe51c8187840efa1b259417053bd22)`);
3468
+ return; /* <<<< returns early */
3469
+ }
3470
+
3471
+ // ...and double check in autosyncs itself, more expensive though
3472
+ const autosyncsIbGib = await this.getSpecialIbGib({ type: "autosyncs" });
3473
+ if (!autosyncsIbGib?.rel8ns) { throw new Error(`autosyncsIbGib?.rel8ns falsy (E: 963313139e919c09aade8b8c412d7b23)`); }
3474
+ const alreadySyncing = autosyncsIbGib.rel8ns[AUTOSYNC_ALWAYS_REL8N_NAME] ?? [];
3475
+ notAlreadySyncingTjpAddrs = tjpAddrs.filter(tjpAddr => !alreadySyncing.includes(tjpAddr));
3476
+ if (notAlreadySyncingTjpAddrs.length === 0) {
3477
+ console.error(`${lc} (UNEXPECTED) all tjpAddrs already auto syncing per special ibgib. Proceeding without throwing here, but this means that the cache is out of sync with the special ibgib also. tjpAddrs: ${tjpAddrs.join('\n')} (E: 574e163118f043fa8c50cfd575e62122)`);
3478
+ return; /* <<<< returns early */
3479
+ }
3480
+
3481
+ // map back from tjp addrs to the tjp ibgibs
3482
+ const notAlreadySyncingTjps = notAlreadySyncingTjpAddrs.map(tjpAddr => {
3483
+ return tjpIbGibs.filter(tjp => getIbGibAddr({ ibGib: tjp }) === tjpAddr)[0];
3484
+ });
3485
+
3486
+ // execute rel8 transform and plumbing
3487
+ await this.rel8ToSpecialIbGib({
3488
+ type: 'autosyncs',
3489
+ ibGibsToRel8: notAlreadySyncingTjps,
3490
+ rel8nName: AUTOSYNC_ALWAYS_REL8N_NAME,
3491
+ });
3492
+
3493
+ // add to cache and we're done
3494
+ notAlreadySyncingTjpAddrs.forEach(tjpAddr => {
3495
+ this._alwaysAutosyncTjpAddrsCache.add(tjpAddr);
3496
+ });
3497
+ } catch (error) {
3498
+ console.error(`${lc} ${error.message}`);
3499
+ throw error;
3500
+ } finally {
3501
+ if (logalot) { console.log(`${lc} complete.`); }
3502
+ }
3503
+ }
3504
+
3505
+ async disableAutosync({
3506
+ tjpIbGibs,
3507
+ }: {
3508
+ tjpIbGibs: IbGib_V1[],
3509
+ }): Promise<void> {
3510
+ const lc = `${this.lc}[${this.disableAutosync.name}]`;
3511
+ try {
3512
+ if (logalot) { console.log(`${lc} starting...`); }
3513
+ if ((tjpIbGibs ?? []).length === 0) { throw new Error(`tjps required. (E: 7b7e34e20b5848b882e14ff8b6c53622)`); }
3514
+
3515
+ if (tjpIbGibs.some(tjp => !tjp.data?.isTjp)) {
3516
+ const wonkyTjpAddrs =
3517
+ tjpIbGibs
3518
+ .filter(tjp => !tjp.data?.isTjp)
3519
+ .map(x => getIbGibAddr({ ibGib: x }));
3520
+ console.warn(`${lc} unrelating tjp whose data.isTjp is false. tjpAddrs: ${wonkyTjpAddrs.join('|')} (W: 7babdf67dda54a2a9905c6c45ef36522)`);
3521
+ }
3522
+ let tjpAddrs = tjpIbGibs.map(tjp => getIbGibAddr({ ibGib: tjp }));
3523
+
3524
+ // if we're already autosyncing this tjp, warn and return early
3525
+ // check locally...
3526
+ // ...and double check in autosyncs itself.
3527
+ const autosyncsIbGib = await this.getSpecialIbGib({ type: "autosyncs" });
3528
+ if (!autosyncsIbGib?.rel8ns) { throw new Error(`(UNEXPECTED) invalid autosyncIbGibs. rel8ns falsy. (E: 5f6211c8a41896003db8bfc40230af22)`); }
3529
+ const alreadySyncing = autosyncsIbGib.rel8ns[AUTOSYNC_ALWAYS_REL8N_NAME] ?? [];
3530
+ const tjpAddrsToRemove: TjpIbGibAddr[] = [];
3531
+ tjpAddrs.forEach(tjpAddr => {
3532
+ if (alreadySyncing.includes(tjpAddr)) {
3533
+ if (logalot) { console.log(`${lc} disabling autosync for ${tjpAddr} (I: 63087804e6c436143971e5039b5e5e22)`); }
3534
+ tjpAddrsToRemove.push(tjpAddr);
3535
+ } else {
3536
+ if (logalot) { console.log(`${lc} already NOT auto syncing ${tjpAddr} (I: 88e00fea009964cd2bab4cc580aa2922)`); }
3537
+ }
3538
+ });
3539
+ if (tjpAddrsToRemove.length === 0) {
3540
+ console.warn(`${lc} tried to disable autosync for tjpAddrs but none were valid. returning early. (W: f9bdda90d906471aa56804d76b6e9522)`);
3541
+ return; /* <<<< returns early */
3542
+ }
3543
+
3544
+ const uniqueTjpAddrsToRemove = Array.from(new Set(tjpAddrsToRemove));
3545
+ const tjpsToRemove = uniqueTjpAddrsToRemove.map(tjpAddr => {
3546
+ return tjpIbGibs.filter(tjp => getIbGibAddr({ ibGib: tjp }) === tjpAddr)[0];
3547
+ });
3548
+
3549
+
3550
+ // execute rel8 transform and plumbing
3551
+ await this.rel8ToSpecialIbGib({
3552
+ type: 'autosyncs',
3553
+ ibGibsToUnRel8: tjpsToRemove,
3554
+ rel8nName: AUTOSYNC_ALWAYS_REL8N_NAME,
3555
+ });
3556
+
3557
+ // remove from cache and we're done
3558
+ uniqueTjpAddrsToRemove.forEach(tjpAddr => {
3559
+ this._alwaysAutosyncTjpAddrsCache.delete(tjpAddr);
3560
+ });
3561
+ } catch (error) {
3562
+ console.error(`${lc} ${error.message}`);
3563
+ throw error;
3564
+ } finally {
3565
+ if (logalot) { console.log(`${lc} complete.`); }
3566
+ }
3567
+ }
3568
+
3569
+ protected async loadAutoSyncs(): Promise<void> {
3570
+ const lc = `${this.lc}[${this.loadAutoSyncs.name}]`;
3571
+ try {
3572
+ if (logalot) { console.log(`${lc} starting...`); }
3573
+ const autosyncsIbGib = await this.getSpecialIbGib({ type: "autosyncs" });
3574
+ if (!autosyncsIbGib?.rel8ns) { throw new Error(`autosyncsIbGib?.rel8ns falsy (E: 3fdcdf40f33c41ed9b7a01a079a8fd71)`); }
3575
+ if (autosyncsIbGib.rel8ns) {
3576
+ this._alwaysAutosyncTjpAddrsCache =
3577
+ new Set(autosyncsIbGib.rel8ns[AUTOSYNC_ALWAYS_REL8N_NAME]);
3578
+ }
3579
+ } catch (error) {
3580
+ console.error(`${lc} ${error.message}`);
3581
+ throw error;
3582
+ } finally {
3583
+ if (logalot) { console.log(`${lc} complete.`); }
3584
+ }
3585
+ }
3586
+
3587
+ /**
3588
+ * checks to see if autosync is enabled for a given `tjp`.
3589
+ *
3590
+ * NOTE: this only checks cache atm.
3591
+ *
3592
+ * @returns true if autosync is enabled for the given tjp, else false
3593
+ */
3594
+ autosyncIsEnabled({
3595
+ tjp,
3596
+ }: {
3597
+ tjp: IbGib_V1,
3598
+ }): boolean {
3599
+ const lc = `${this.lc}[${this.autosyncIsEnabled.name}]`;
3600
+ try {
3601
+ if (logalot) { console.log(`${lc} starting...`); }
3602
+ return this._alwaysAutosyncTjpAddrsCache.has(getIbGibAddr({ ibGib: tjp }));
3603
+ } catch (error) {
3604
+ console.error(`${lc} ${error.message}`);
3605
+ throw error;
3606
+ } finally {
3607
+ if (logalot) { console.log(`${lc} complete.`); }
3608
+ }
3609
+ }
3610
+
3611
+ // #endregion autosync
3612
+
3613
+ // /**
3614
+ // * Prompts the user to select a new picture for the given `picIbGib`.
3615
+ // *
3616
+ // * If the user does select one, this creates the appropriate ibgibs (pic,
3617
+ // * binary, dependencies), saves them, registers the new pic (but not the bin)
3618
+ // * in the given `space` if specified, else in the current `localUserSpace`.
3619
+ // */
3620
+ // async updatePic({
3621
+ // picIbGib,
3622
+ // space,
3623
+ // }: {
3624
+ // /**
3625
+ // * picIbGib to update with a new image.
3626
+ // */
3627
+ // picIbGib: PicIbGib_V1,
3628
+ // /**
3629
+ // * space within which we are working, i.e., where the incoming `picIbGib` is
3630
+ // * and where we will save any new ibgibs.
3631
+ // */
3632
+ // space?: IbGibSpaceAny,
3633
+ // }): Promise<void> {
3634
+ // const lc = `${this.lc}[${this.updatePic.name}]`;
3635
+ // try {
3636
+ // if (logalot) { console.log(`${lc} starting...`); }
3637
+ // if (this.isPrompting) { throw new Error(`(UNEXPECTED) already prompting (E: 284a32506ede7aa51b17fe07b9619722)`); }
3638
+
3639
+ // space = space ?? await this.getLocalUserSpace({ lock: true });
3640
+ // if (!space) { throw new Error(`(UNEXPECTED) space falsy and localUserSpace not initialized (E: c1c0611e158b400daf07e9cb69dd8c8d)`); }
3641
+
3642
+ // let resUpdatePic = await this.fnPromptUpdatePic(space, picIbGib);
3643
+ // if (resUpdatePic) {
3644
+ // const [resCreatePic, resCreateBin] = resUpdatePic;
3645
+
3646
+ // // save the ibgibs
3647
+ // await this.persistTransformResult({ resTransform: resCreatePic, space });
3648
+ // await this.persistTransformResult({ resTransform: resCreateBin, space });
3649
+
3650
+ // // register the new pic ibgib (should be the only created ibgib with a timeline)
3651
+ // await this.registerNewIbGib({ ibGib: resCreatePic.newIbGib, space });
3652
+ // } else {
3653
+ // await this.getFnAlert!()({ title: 'k', msg: 'update pic cancelled.' });
3654
+ // }
3655
+ // } catch (error) {
3656
+ // console.error(`${lc} ${error.message}`);
3657
+ // throw error;
3658
+ // } finally {
3659
+ // if (logalot) { console.log(`${lc} complete.`); }
3660
+ // }
3661
+ // }
3662
+
3663
+ // async updateComment({
3664
+ // commentIbGib,
3665
+ // space,
3666
+ // }: {
3667
+ // /**
3668
+ // * commentIbGib to update with a new text
3669
+ // */
3670
+ // commentIbGib: CommentIbGib_V1,
3671
+ // /**
3672
+ // * space within which we are working, i.e., where the incoming `commentIbGib` is
3673
+ // * and where we will save any new ibgibs.
3674
+ // */
3675
+ // space?: IbGibSpaceAny,
3676
+ // }): Promise<void> {
3677
+ // const lc = `${this.lc}[${this.updateComment.name}]`;
3678
+ // try {
3679
+ // if (logalot) { console.log(`${lc} starting...`); }
3680
+ // if (this.isPrompting) { throw new Error(`(UNEXPECTED) already prompting (E: 551cc47d1e9e421097b5e154c813ac5f)`); }
3681
+
3682
+ // space = space ?? await this.getLocalUserSpace({ lock: true });
3683
+ // if (!space) { throw new Error(`(UNEXPECTED) space falsy and localUserSpace not initialized (E: 6e600ba21b864c2c90f0e05e715a739e)`); }
3684
+
3685
+ // let resUpdateComment = await this.fnPromptUpdateComment(space, commentIbGib);
3686
+ // if (resUpdateComment) {
3687
+ // // save the ibgibs
3688
+ // await this.persistTransformResult({ resTransform: resUpdateComment, space });
3689
+
3690
+ // // register the new comment ibgib (should be the only created ibgib with a timeline)
3691
+ // await this.registerNewIbGib({ ibGib: resUpdateComment.newIbGib, space });
3692
+ // } else {
3693
+ // await this.getFnAlert!()({ title: 'k', msg: 'update comment cancelled.' });
3694
+ // }
3695
+ // } catch (error) {
3696
+ // console.error(`${lc} ${error.message}`);
3697
+ // throw error;
3698
+ // } finally {
3699
+ // if (logalot) { console.log(`${lc} complete.`); }
3700
+ // }
3701
+ // }
3702
+ }