holosphere 1.3.0-alpha3 → 1.3.0-alpha5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/content.js +194 -37
- package/federation.js +167 -29
- package/global.js +5 -2
- package/hologram.js +21 -4
- package/holosphere-bundle.esm.js +571 -283
- package/holosphere-bundle.js +571 -283
- package/holosphere-bundle.min.js +8 -8
- package/holosphere.d.ts +67 -7
- package/holosphere.js +49 -4
- package/package.json +1 -1
- package/utils.js +29 -7
package/holosphere.d.ts
CHANGED
|
@@ -20,6 +20,17 @@ interface PutOptions {
|
|
|
20
20
|
interface GetOptions {
|
|
21
21
|
resolveHolograms?: boolean;
|
|
22
22
|
validationOptions?: object;
|
|
23
|
+
/** Per-`.once()` deadline in ms; cold paths resolve `null` after this. Default 8000. Pass `0` to disable. */
|
|
24
|
+
timeout?: number;
|
|
25
|
+
/** Return `_deleted: true` soft-tombstoned records instead of treating them as not-found. Default false. */
|
|
26
|
+
includeDeleted?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface GetAllOptions {
|
|
30
|
+
/** Per-`.once()` deadline in ms; cold paths resolve `[]` after this. Default 8000. Pass `0` to disable. */
|
|
31
|
+
timeout?: number;
|
|
32
|
+
/** Include `_deleted: true` soft-tombstoned records in the response. Default false. */
|
|
33
|
+
includeDeleted?: boolean;
|
|
23
34
|
}
|
|
24
35
|
|
|
25
36
|
interface ResolveHologramOptions {
|
|
@@ -42,22 +53,69 @@ interface Hologram {
|
|
|
42
53
|
*
|
|
43
54
|
* On success: `isHologram === true` and source* fields point at the origin.
|
|
44
55
|
* On failure: `isHologram === false` and `error` describes why resolution failed.
|
|
56
|
+
*
|
|
57
|
+
* **Exported.** Domain types in consumers should declare
|
|
58
|
+
* `_hologram?: ResolvedHologramMeta` instead of inlining the shape.
|
|
45
59
|
*/
|
|
46
|
-
interface ResolvedHologramMeta {
|
|
60
|
+
export interface ResolvedHologramMeta {
|
|
47
61
|
isHologram: boolean;
|
|
48
62
|
soul: string;
|
|
49
63
|
sourceHolon?: string | null;
|
|
50
64
|
sourceLens?: string | null;
|
|
51
65
|
sourceKey?: string | null;
|
|
66
|
+
/** Display name of the source holon — stamped by `resolveHologram` when known. */
|
|
67
|
+
sourceHolonName?: string;
|
|
52
68
|
resolvedAt: number;
|
|
53
69
|
error?: string;
|
|
54
70
|
}
|
|
55
71
|
|
|
56
|
-
interface ResolvedHologramData {
|
|
72
|
+
export interface ResolvedHologramData {
|
|
57
73
|
_hologram: ResolvedHologramMeta;
|
|
58
74
|
[key: string]: any;
|
|
59
75
|
}
|
|
60
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Federation provenance envelope stamped on records that arrived via a
|
|
79
|
+
* federated partner. Set by `getFederated` and by `propagate` when writing
|
|
80
|
+
* to outbound partners.
|
|
81
|
+
*
|
|
82
|
+
* **Exported.** Domain types should declare `_federation?: FederationMeta`
|
|
83
|
+
* instead of inlining the shape.
|
|
84
|
+
*/
|
|
85
|
+
export interface FederationMeta {
|
|
86
|
+
/** The holon the record was propagated FROM. */
|
|
87
|
+
origin: string;
|
|
88
|
+
/** The lens it was published under at the origin. */
|
|
89
|
+
sourceLens: string;
|
|
90
|
+
/** Origin holon's display name (best-effort; absent if the source has no name). */
|
|
91
|
+
originName?: string;
|
|
92
|
+
/** Source-side id of the record, useful when local-side keys differ. */
|
|
93
|
+
originalId?: string;
|
|
94
|
+
/** Wall-clock ms when the propagation was emitted. */
|
|
95
|
+
propagatedAt?: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Soft-tombstone marker recognised by `get`/`getAll`. A record with
|
|
100
|
+
* `_deleted: true` is treated as not-found in the default response and
|
|
101
|
+
* surfaced only when `{ includeDeleted: true }` is passed.
|
|
102
|
+
*
|
|
103
|
+
* **Exported.** Domain types that want to allow tombstoned records on the
|
|
104
|
+
* wire should declare `_deleted?: boolean`.
|
|
105
|
+
*/
|
|
106
|
+
export type DeletedMarker = boolean;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Convenience mixin: the three envelope fields the library stamps onto
|
|
110
|
+
* records. Domain types can extend or intersect with this to avoid
|
|
111
|
+
* redeclaring the shapes.
|
|
112
|
+
*/
|
|
113
|
+
export interface HolosphereEnvelope {
|
|
114
|
+
_hologram?: ResolvedHologramMeta;
|
|
115
|
+
_federation?: FederationMeta;
|
|
116
|
+
_deleted?: DeletedMarker;
|
|
117
|
+
}
|
|
118
|
+
|
|
61
119
|
/**
|
|
62
120
|
* Per-partner directional lens config. Directions are from the holding
|
|
63
121
|
* space's perspective: `inbound` lenses are received from the partner,
|
|
@@ -168,7 +226,7 @@ declare class HoloSphere {
|
|
|
168
226
|
put(holon: string, lens: string, data: object, password?: string | null, options?: PutOptions): Promise<PutResult>;
|
|
169
227
|
get(holon: string, lens: string): Promise<any | null>;
|
|
170
228
|
get(holon: string, lens: string, key: string, password?: string | null, options?: GetOptions): Promise<any | null>;
|
|
171
|
-
getAll(holon: string, lens: string, password?: string | null): Promise<Array<any>>;
|
|
229
|
+
getAll(holon: string, lens: string, password?: string | null, options?: GetAllOptions): Promise<Array<any>>;
|
|
172
230
|
parse(rawData: any): Promise<object | null>;
|
|
173
231
|
delete(holon: string, lens: string, key: string, password?: string | null): Promise<boolean>;
|
|
174
232
|
deleteAll(holon: string, lens: string, password?: string | null): Promise<boolean>;
|
|
@@ -187,8 +245,8 @@ declare class HoloSphere {
|
|
|
187
245
|
getAllGlobal(tableName: string, password?: string | null): Promise<Array<any>>;
|
|
188
246
|
deleteGlobal(tableName: string, key: string, password?: string | null): Promise<boolean>;
|
|
189
247
|
deleteAllGlobal(tableName: string, password?: string | null): Promise<boolean>;
|
|
190
|
-
subscribeGlobal(lens: string, key: string | null, callback: (data: any, key?: string) => void, options?: { realtimeOnly?: boolean }):
|
|
191
|
-
subscribeGlobal(lens: string, callback: (data: any, key?: string) => void):
|
|
248
|
+
subscribeGlobal(lens: string, key: string | null, callback: (data: any, key?: string) => void, options?: { realtimeOnly?: boolean }): { unsubscribe: () => void };
|
|
249
|
+
subscribeGlobal(lens: string, callback: (data: any, key?: string) => void): { unsubscribe: () => void };
|
|
192
250
|
|
|
193
251
|
// Hologram
|
|
194
252
|
createHologram(holon: string, lens: string, data: { id: string, [key: string]: any }): Hologram;
|
|
@@ -210,8 +268,10 @@ declare class HoloSphere {
|
|
|
210
268
|
getScalespace(lat: number, lng: number): string[];
|
|
211
269
|
getHolonScalespace(holon: string): string[];
|
|
212
270
|
|
|
213
|
-
// Subscription
|
|
214
|
-
|
|
271
|
+
// Subscription. Returns synchronously — `await` on the return value
|
|
272
|
+
// still works (await on a non-Promise resolves to the value), so both
|
|
273
|
+
// call styles produce the same `{ unsubscribe }` shape.
|
|
274
|
+
subscribe(holon: string, lens: string, callback: (data: any, key?: string) => void): { unsubscribe: () => void };
|
|
215
275
|
|
|
216
276
|
// Federation - v1 style
|
|
217
277
|
federate(holonId1: string, holonId2: string, password1?: string | null, password2?: string | null, bidirectional?: boolean, lensConfig?: { inbound?: string[], outbound?: string[] }): Promise<boolean>;
|
package/holosphere.js
CHANGED
|
@@ -125,6 +125,10 @@ class HoloSphere {
|
|
|
125
125
|
// Initialize schema cache
|
|
126
126
|
this.schemaCache = new Map();
|
|
127
127
|
|
|
128
|
+
// Holon-name cache so resolveHologram + getFederated don't refetch
|
|
129
|
+
// `settings/<holon>` for every hologram from the same source.
|
|
130
|
+
this._holonNameCache = new Map();
|
|
131
|
+
|
|
128
132
|
// Initialize allowed authors set (for canWrite)
|
|
129
133
|
this._allowedAuthors = new Set();
|
|
130
134
|
}
|
|
@@ -192,8 +196,8 @@ class HoloSphere {
|
|
|
192
196
|
return ContentOps.get(this, holon, lens, key, password, options);
|
|
193
197
|
}
|
|
194
198
|
|
|
195
|
-
async getAll(holon, lens, password = null) {
|
|
196
|
-
return ContentOps.getAll(this, holon, lens, password);
|
|
199
|
+
async getAll(holon, lens, password = null, options = {}) {
|
|
200
|
+
return ContentOps.getAll(this, holon, lens, password, options);
|
|
197
201
|
}
|
|
198
202
|
|
|
199
203
|
async parse(rawData) {
|
|
@@ -262,8 +266,10 @@ class HoloSphere {
|
|
|
262
266
|
/**
|
|
263
267
|
* Subscribe to real-time changes in a global table.
|
|
264
268
|
* v2-compatible: subscribeGlobal(lens, key, callback, options)
|
|
269
|
+
*
|
|
270
|
+
* Returns synchronously — see {@link subscribe}.
|
|
265
271
|
*/
|
|
266
|
-
|
|
272
|
+
subscribeGlobal(lens, keyOrCallback, callbackOrOptions, options = {}) {
|
|
267
273
|
let key, callback;
|
|
268
274
|
if (typeof keyOrCallback === 'function') {
|
|
269
275
|
callback = keyOrCallback;
|
|
@@ -336,7 +342,14 @@ class HoloSphere {
|
|
|
336
342
|
return Utils.getHolonScalespace(holon);
|
|
337
343
|
}
|
|
338
344
|
|
|
339
|
-
|
|
345
|
+
/**
|
|
346
|
+
* Subscribe to real-time changes for a holon/lens.
|
|
347
|
+
*
|
|
348
|
+
* Synchronous return: `{ unsubscribe: () => void }`. Callers do not
|
|
349
|
+
* need to `await` — both `const s = holosphere.subscribe(...)` and
|
|
350
|
+
* `const s = await holosphere.subscribe(...)` yield the same shape.
|
|
351
|
+
*/
|
|
352
|
+
subscribe(holon, lens, callback) {
|
|
340
353
|
return Utils.subscribe(this, holon, lens, callback);
|
|
341
354
|
}
|
|
342
355
|
|
|
@@ -344,6 +357,38 @@ class HoloSphere {
|
|
|
344
357
|
return Utils.notifySubscribers(this, data);
|
|
345
358
|
}
|
|
346
359
|
|
|
360
|
+
/**
|
|
361
|
+
* Resolve a holon's display name from its `settings/<holon>` record.
|
|
362
|
+
* Cached per-instance so repeated lookups (e.g. one per hologram in a
|
|
363
|
+
* federated batch) only fetch once. Returns `null` when no name is set.
|
|
364
|
+
*
|
|
365
|
+
* Used internally by `resolveHologram` to stamp
|
|
366
|
+
* `_hologram.sourceHolonName` so every consumer of a resolved hologram
|
|
367
|
+
* already has the display name without a second round-trip.
|
|
368
|
+
*/
|
|
369
|
+
async getHolonName(holonId) {
|
|
370
|
+
if (!holonId) return null;
|
|
371
|
+
const key = String(holonId);
|
|
372
|
+
if (this._holonNameCache.has(key)) return this._holonNameCache.get(key);
|
|
373
|
+
try {
|
|
374
|
+
const settings = await this.get(key, 'settings', key);
|
|
375
|
+
let name = null;
|
|
376
|
+
if (settings) {
|
|
377
|
+
if (Array.isArray(settings)) {
|
|
378
|
+
const found = settings.find(s => s && typeof s.name === 'string' && s.name.trim() !== '');
|
|
379
|
+
name = found ? found.name : null;
|
|
380
|
+
} else if (typeof settings.name === 'string' && settings.name.trim() !== '') {
|
|
381
|
+
name = settings.name;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
this._holonNameCache.set(key, name);
|
|
385
|
+
return name;
|
|
386
|
+
} catch {
|
|
387
|
+
this._holonNameCache.set(key, null);
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
347
392
|
generateId() {
|
|
348
393
|
return Utils.generateId();
|
|
349
394
|
}
|
package/package.json
CHANGED
package/utils.js
CHANGED
|
@@ -44,13 +44,21 @@ export function getHolonScalespace(holon) { // Doesn't need holoInstance
|
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* Subscribes to changes in a specific holon and lens.
|
|
47
|
+
*
|
|
48
|
+
* Returns **synchronously** — the call has no internal awaits, so callers
|
|
49
|
+
* never need to `await` the result. (`await` on the return value still
|
|
50
|
+
* works as before; it just resolves through the value unchanged.) This
|
|
51
|
+
* means `const sub = holosphere.subscribe(...); sub.unsubscribe();` is the
|
|
52
|
+
* canonical pattern and there is no `Promise<{ unsubscribe }>` shape to
|
|
53
|
+
* disambiguate against the resolved object.
|
|
54
|
+
*
|
|
47
55
|
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
48
56
|
* @param {string} holon - The holon identifier.
|
|
49
57
|
* @param {string} lens - The lens to subscribe to.
|
|
50
58
|
* @param {function} callback - The callback to execute on changes.
|
|
51
|
-
* @returns {
|
|
59
|
+
* @returns {{ unsubscribe: () => void }} - Subscription with unsubscribe method.
|
|
52
60
|
*/
|
|
53
|
-
export
|
|
61
|
+
export function subscribe(holoInstance, holon, lens, callback) {
|
|
54
62
|
if (!holon || !lens) {
|
|
55
63
|
throw new Error('subscribe: Missing holon or lens parameters:', holon, lens);
|
|
56
64
|
}
|
|
@@ -82,6 +90,17 @@ export async function subscribe(holoInstance, holon, lens, callback) {
|
|
|
82
90
|
}
|
|
83
91
|
}
|
|
84
92
|
|
|
93
|
+
// Subscribers expect `object | null`. `parse()` can return
|
|
94
|
+
// a string/number/boolean when a legacy or corrupted leaf
|
|
95
|
+
// happened to be a JSON-encoded primitive (e.g.
|
|
96
|
+
// `'"hello"'` parses to `'hello'`). Drop those so every
|
|
97
|
+
// consumer can stop guarding with
|
|
98
|
+
// `typeof x === 'string' ? JSON.parse(x) : x`.
|
|
99
|
+
if (parsed !== null && (typeof parsed !== 'object' || Array.isArray(parsed))) {
|
|
100
|
+
console.warn(`[holosphere.subscribe] dropping non-object payload at ${holon}/${lens}/${key}:`, typeof parsed);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
85
104
|
// Check again if subscription ID still exists before calling callback
|
|
86
105
|
if (holoInstance.subscriptions[subscriptionId]) {
|
|
87
106
|
callback(parsed, key);
|
|
@@ -102,9 +121,14 @@ export async function subscribe(holoInstance, holon, lens, callback) {
|
|
|
102
121
|
gunListener: gunListener // Store the listener too (optional, maybe needed for close?)
|
|
103
122
|
};
|
|
104
123
|
|
|
105
|
-
// Return an object with unsubscribe method
|
|
124
|
+
// Return an object with unsubscribe method.
|
|
125
|
+
// `unsubscribe` is sync — nothing it does (`mapChain.off()`,
|
|
126
|
+
// `delete subscriptions[id]`) blocks. Keeping it sync means the
|
|
127
|
+
// returned shape is `{ unsubscribe: () => void }` rather than
|
|
128
|
+
// `() => Promise<void>`, matching what callers expect when they
|
|
129
|
+
// store it in a `() => void` cleanup slot.
|
|
106
130
|
return {
|
|
107
|
-
unsubscribe:
|
|
131
|
+
unsubscribe: () => {
|
|
108
132
|
const sub = holoInstance.subscriptions[subscriptionId];
|
|
109
133
|
if (!sub) {
|
|
110
134
|
return;
|
|
@@ -114,9 +138,7 @@ export async function subscribe(holoInstance, holon, lens, callback) {
|
|
|
114
138
|
// Turn off the Gun subscription using the stored mapChain reference
|
|
115
139
|
if (sub.mapChain) { // Check if mapChain exists
|
|
116
140
|
sub.mapChain.off(); // Call off() on the chain where .on() was attached
|
|
117
|
-
|
|
118
|
-
// await new Promise(res => setTimeout(res, 50));
|
|
119
|
-
} // We might not need to call off() on gunListener explicitly
|
|
141
|
+
}
|
|
120
142
|
|
|
121
143
|
// Remove from subscriptions object AFTER turning off listener
|
|
122
144
|
delete holoInstance.subscriptions[subscriptionId];
|