applesauce-core 5.1.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/casts/cast.d.ts +31 -0
- package/dist/casts/cast.js +67 -0
- package/dist/casts/index.d.ts +3 -0
- package/dist/casts/index.js +3 -0
- package/dist/casts/pubkey.d.ts +27 -0
- package/dist/casts/pubkey.js +49 -0
- package/dist/casts/user.d.ts +19 -0
- package/dist/casts/user.js +25 -0
- package/dist/factories/delete.d.ts +18 -0
- package/dist/factories/delete.js +23 -0
- package/dist/factories/event.d.ts +61 -0
- package/dist/factories/event.js +164 -0
- package/dist/factories/index.d.ts +5 -0
- package/dist/factories/index.js +5 -0
- package/dist/factories/mailboxes.d.ts +33 -0
- package/dist/factories/mailboxes.js +57 -0
- package/dist/factories/profile.d.ts +46 -0
- package/dist/factories/profile.js +80 -0
- package/dist/factories/types.d.ts +56 -0
- package/dist/helpers/event.d.ts +11 -2
- package/dist/helpers/event.js +2 -1
- package/dist/helpers/mailboxes.d.ts +5 -1
- package/dist/helpers/mailboxes.js +5 -0
- package/dist/helpers/pipeline.d.ts +14 -1
- package/dist/helpers/pipeline.js +17 -2
- package/dist/helpers/pointers.d.ts +25 -21
- package/dist/helpers/pointers.js +33 -18
- package/dist/helpers/profile.d.ts +2 -2
- package/dist/helpers/regexp.d.ts +2 -0
- package/dist/helpers/regexp.js +8 -2
- package/dist/helpers/relays.d.ts +3 -1
- package/dist/helpers/relays.js +8 -10
- package/dist/helpers/url.d.ts +1 -4
- package/dist/helpers/url.js +1 -4
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -1
- package/dist/observable/catch-error-inline.d.ts +3 -0
- package/dist/observable/catch-error-inline.js +5 -0
- package/dist/observable/chainable.d.ts +36 -0
- package/dist/observable/chainable.js +72 -0
- package/dist/observable/combine-latest-by-index.d.ts +10 -0
- package/dist/observable/combine-latest-by-index.js +121 -0
- package/dist/observable/combine-latest-by-key.d.ts +10 -0
- package/dist/observable/combine-latest-by-key.js +136 -0
- package/dist/observable/combine-latest-by-value.d.ts +10 -0
- package/dist/observable/combine-latest-by-value.js +117 -0
- package/dist/observable/combine-latest-by.d.ts +16 -0
- package/dist/observable/combine-latest-by.js +12 -0
- package/dist/observable/index.d.ts +10 -4
- package/dist/observable/index.js +10 -4
- package/dist/observable/timeout-with-ignore.d.ts +24 -0
- package/dist/observable/timeout-with-ignore.js +33 -0
- package/dist/operations/client.d.ts +1 -1
- package/dist/operations/client.js +2 -2
- package/dist/operations/content.d.ts +5 -2
- package/dist/operations/content.js +13 -9
- package/dist/operations/delete.d.ts +1 -1
- package/dist/operations/encrypted-content.d.ts +9 -3
- package/dist/operations/encrypted-content.js +9 -3
- package/dist/operations/event.d.ts +6 -4
- package/dist/operations/event.js +20 -12
- package/dist/operations/hidden-content.d.ts +8 -3
- package/dist/operations/hidden-content.js +11 -6
- package/dist/operations/mailboxes.d.ts +5 -1
- package/dist/operations/mailboxes.js +76 -0
- package/dist/operations/profile.d.ts +1 -1
- package/dist/operations/tag/common.d.ts +22 -7
- package/dist/operations/tag/common.js +33 -18
- package/dist/operations/tag/relay.d.ts +1 -1
- package/dist/operations/tags.d.ts +10 -4
- package/dist/operations/tags.js +19 -13
- package/package.json +20 -5
- package/dist/event-factory/event-factory.d.ts +0 -57
- package/dist/event-factory/event-factory.js +0 -94
- package/dist/event-factory/index.d.ts +0 -3
- package/dist/event-factory/index.js +0 -3
- package/dist/event-factory/methods.d.ts +0 -17
- package/dist/event-factory/methods.js +0 -44
- package/dist/event-factory/types.d.ts +0 -76
- /package/dist/{event-factory → factories}/types.js +0 -0
package/dist/helpers/url.d.ts
CHANGED
|
@@ -20,8 +20,5 @@ export declare function ensureProtocol(url: string, protocol?: string): string;
|
|
|
20
20
|
export declare function ensureWebSocketURL<T extends string | URL>(url: T): T;
|
|
21
21
|
/** Converts a domain or WS URL to a HTTP URL */
|
|
22
22
|
export declare function ensureHttpURL<T extends string | URL>(url: T): T;
|
|
23
|
-
/**
|
|
24
|
-
* Normalizes a string into a relay URL
|
|
25
|
-
* Does not remove the trailing slash
|
|
26
|
-
*/
|
|
23
|
+
/** Normalizes a string into a cleaner URL by dropping port if its not needed */
|
|
27
24
|
export declare function normalizeURL<T extends string | URL>(url: T): T;
|
package/dist/helpers/url.js
CHANGED
|
@@ -80,10 +80,7 @@ export function ensureHttpURL(url) {
|
|
|
80
80
|
// @ts-expect-error
|
|
81
81
|
return typeof url === "string" ? p.toString() : p;
|
|
82
82
|
}
|
|
83
|
-
/**
|
|
84
|
-
* Normalizes a string into a relay URL
|
|
85
|
-
* Does not remove the trailing slash
|
|
86
|
-
*/
|
|
83
|
+
/** Normalizes a string into a cleaner URL by dropping port if its not needed */
|
|
87
84
|
export function normalizeURL(url) {
|
|
88
85
|
let p = new URL(url);
|
|
89
86
|
// Remove any double slashes
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
export * from "./
|
|
1
|
+
export * from "./factories/index.js";
|
|
2
|
+
export * from "./casts/index.js";
|
|
2
3
|
export * from "./event-store/index.js";
|
|
3
4
|
export * from "./logger.js";
|
|
4
5
|
export * from "./observable/index.js";
|
|
5
6
|
export * as Helpers from "./helpers/index.js";
|
|
6
7
|
export * as Models from "./models/index.js";
|
|
7
8
|
export * as Operations from "./operations/index.js";
|
|
9
|
+
export * as Factories from "./factories/index.js";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
// Export from factories
|
|
2
|
+
export * from "./factories/index.js";
|
|
3
|
+
export * from "./casts/index.js";
|
|
2
4
|
export * from "./event-store/index.js";
|
|
3
5
|
export * from "./logger.js";
|
|
4
6
|
export * from "./observable/index.js";
|
|
5
7
|
export * as Helpers from "./helpers/index.js";
|
|
6
8
|
export * as Models from "./models/index.js";
|
|
7
9
|
export * as Operations from "./operations/index.js";
|
|
10
|
+
export * as Factories from "./factories/index.js";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Observable } from "rxjs";
|
|
2
|
+
/**
|
|
3
|
+
* Wraps an Observable in a Proxy that enables property chaining.
|
|
4
|
+
* When accessing a property ending with `$`, it uses switchMap to chain
|
|
5
|
+
* to that property's observable value.
|
|
6
|
+
* When accessing a non-observable property, it returns an Observable of that property's value.
|
|
7
|
+
*/
|
|
8
|
+
export declare function chainable<T>(observable: Observable<T>): ChainableObservable<T>;
|
|
9
|
+
/**
|
|
10
|
+
* Helper type to extract nullable parts (undefined/null) from a type
|
|
11
|
+
*/
|
|
12
|
+
type NullableParts<T> = Extract<T, undefined | null>;
|
|
13
|
+
/**
|
|
14
|
+
* Helper type to get the property type, preserving nullable parts from the parent type
|
|
15
|
+
*/
|
|
16
|
+
type PropChain<T, K extends keyof NonNullable<T>> = NonNullable<T>[K] | NullableParts<T>;
|
|
17
|
+
/**
|
|
18
|
+
* A chainable Observable type that allows property chaining.
|
|
19
|
+
* This type maps all properties to chainable observables:
|
|
20
|
+
* - Properties ending with $: extracts inner type from Observable<U> → ChainableObservable<U>
|
|
21
|
+
* - Other properties: uses property type directly → ChainableObservable<PropertyType>
|
|
22
|
+
*
|
|
23
|
+
* Note: TypeScript has limitations inferring through Proxy types. For better
|
|
24
|
+
* type inference, you may need to explicitly type the result:
|
|
25
|
+
*/
|
|
26
|
+
export type ChainableObservable<T> = Observable<T> & Omit<{
|
|
27
|
+
[K in keyof NonNullable<T> as K extends string ? K : never]: K extends `${infer _}$` ? NonNullable<T>[K] extends Observable<infer U> ? ChainableObservable<U | NullableParts<T>> : never : ChainableObservable<PropChain<T, K>>;
|
|
28
|
+
}, "$first" | "$last"> & {
|
|
29
|
+
/** Returns a promise that resolves with the first value or rejects with a timeout error */
|
|
30
|
+
$first(first?: number): Promise<NonNullable<T>>;
|
|
31
|
+
$first<V>(first?: number, fallback?: V): Promise<NonNullable<T> | V>;
|
|
32
|
+
/** Returns a promise that resolves with the last value or rejects with a timeout error */
|
|
33
|
+
$last(max?: number): Promise<NonNullable<T>>;
|
|
34
|
+
$last<V>(max?: number, fallback?: V): Promise<NonNullable<T> | V>;
|
|
35
|
+
};
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { filter, firstValueFrom, isObservable, lastValueFrom, map, of, switchMap, timeout } from "rxjs";
|
|
2
|
+
/**
|
|
3
|
+
* A symbol used to mark an Observable as chainable
|
|
4
|
+
*/
|
|
5
|
+
const CHAINABLE_CACHE_SYMBOL = Symbol.for("chainable-cache");
|
|
6
|
+
/**
|
|
7
|
+
* Wraps an Observable in a Proxy that enables property chaining.
|
|
8
|
+
* When accessing a property ending with `$`, it uses switchMap to chain
|
|
9
|
+
* to that property's observable value.
|
|
10
|
+
* When accessing a non-observable property, it returns an Observable of that property's value.
|
|
11
|
+
*/
|
|
12
|
+
export function chainable(observable) {
|
|
13
|
+
// Create a Proxy that intercepts property access
|
|
14
|
+
const proxy = new Proxy(observable, {
|
|
15
|
+
get(target$, prop) {
|
|
16
|
+
const cache = observable[CHAINABLE_CACHE_SYMBOL] ||
|
|
17
|
+
(observable[CHAINABLE_CACHE_SYMBOL] = {});
|
|
18
|
+
// Forward all Observable methods and properties
|
|
19
|
+
if (prop in target$ || typeof prop === "symbol") {
|
|
20
|
+
const value = target$[prop];
|
|
21
|
+
// If it's a function, bind it to the target
|
|
22
|
+
if (typeof value === "function")
|
|
23
|
+
return value.bind(target$);
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
if (typeof prop === "string") {
|
|
27
|
+
// Return cached observable if it exists
|
|
28
|
+
const cached = cache[prop];
|
|
29
|
+
if (cached)
|
|
30
|
+
return cached;
|
|
31
|
+
// Otherwise, create a new observable
|
|
32
|
+
let prop$;
|
|
33
|
+
// Extra observalbe helpers to make it easier to work with observables
|
|
34
|
+
if (prop === "$first") {
|
|
35
|
+
return (...args) => firstValueFrom(target$.pipe(filter((v) => v !== undefined && v !== null), args.length === 2
|
|
36
|
+
? timeout({ first: args[0], with: () => of(args[1]) })
|
|
37
|
+
: timeout({ first: args[0] ?? 10_000 })));
|
|
38
|
+
}
|
|
39
|
+
else if (prop === "$last") {
|
|
40
|
+
return (...args) => lastValueFrom(target$.pipe(filter((v) => v !== undefined && v !== null), args.length === 2
|
|
41
|
+
? timeout({ first: args[0], with: () => of(args[1]) })
|
|
42
|
+
: timeout({ first: args[0] ?? 10_000 })));
|
|
43
|
+
}
|
|
44
|
+
// Handle property access for properties ending with $
|
|
45
|
+
else if (prop.endsWith("$")) {
|
|
46
|
+
// Use switchMap to chain to the nested observable
|
|
47
|
+
prop$ = target$.pipe(switchMap((target) => {
|
|
48
|
+
const value = target === undefined || target === null ? target : target[prop];
|
|
49
|
+
// If value is an observable, return it
|
|
50
|
+
if (isObservable(value))
|
|
51
|
+
return value;
|
|
52
|
+
// Otherwise wrap it in an observable
|
|
53
|
+
else
|
|
54
|
+
return of(value);
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
// For non-$ properties, return an Observable of the property value
|
|
58
|
+
else {
|
|
59
|
+
prop$ = target$.pipe(
|
|
60
|
+
// Access the property on the value if target is not undefined or null
|
|
61
|
+
map((target) => (target === undefined || target === null ? target : target[prop])));
|
|
62
|
+
}
|
|
63
|
+
// Make the chained observable chainable too
|
|
64
|
+
const observable = chainable(prop$);
|
|
65
|
+
cache[prop] = observable;
|
|
66
|
+
return observable;
|
|
67
|
+
}
|
|
68
|
+
throw new Error(`Unable to access property "${prop}" on chainable observable`);
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
return proxy;
|
|
72
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Observable } from "rxjs";
|
|
2
|
+
/**
|
|
3
|
+
* Dynamic counterpart to `switchMap(() => combineLatest(...))` for arrays,
|
|
4
|
+
* with stable branches by index.
|
|
5
|
+
*
|
|
6
|
+
* Each array index gets its own long-lived branch (`of(value).pipe(...)`
|
|
7
|
+
* equivalent), and branches are only created/removed when indices are
|
|
8
|
+
* added/removed.
|
|
9
|
+
*/
|
|
10
|
+
export declare function combineLatestByIndex<T, R>(project: (source$: Observable<T>, index: number) => Observable<R>): (source$: Observable<readonly T[]>) => Observable<R[]>;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Observable, Subject } from "rxjs";
|
|
2
|
+
/** Sentinel to distinguish "no value yet" from a real value */
|
|
3
|
+
const NOT_YET = Symbol("NOT_YET");
|
|
4
|
+
/**
|
|
5
|
+
* Dynamic counterpart to `switchMap(() => combineLatest(...))` for arrays,
|
|
6
|
+
* with stable branches by index.
|
|
7
|
+
*
|
|
8
|
+
* Each array index gets its own long-lived branch (`of(value).pipe(...)`
|
|
9
|
+
* equivalent), and branches are only created/removed when indices are
|
|
10
|
+
* added/removed.
|
|
11
|
+
*/
|
|
12
|
+
export function combineLatestByIndex(project) {
|
|
13
|
+
return (source$) => new Observable((subscriber) => {
|
|
14
|
+
const slots = [];
|
|
15
|
+
let sourceCompleted = false;
|
|
16
|
+
let batchingSourceNext = false;
|
|
17
|
+
let emittedDuringBatch = false;
|
|
18
|
+
function checkComplete() {
|
|
19
|
+
if (!sourceCompleted)
|
|
20
|
+
return;
|
|
21
|
+
for (const slot of slots) {
|
|
22
|
+
if (!slot.completed)
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
subscriber.complete();
|
|
26
|
+
}
|
|
27
|
+
function tryEmit() {
|
|
28
|
+
for (const slot of slots) {
|
|
29
|
+
if (slot.value === NOT_YET)
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
subscriber.next(slots.map((slot) => slot.value));
|
|
33
|
+
}
|
|
34
|
+
function createSlot(index) {
|
|
35
|
+
const input = new Subject();
|
|
36
|
+
const slot = { input, value: NOT_YET, completed: false };
|
|
37
|
+
let projected$;
|
|
38
|
+
try {
|
|
39
|
+
projected$ = project(input, index);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
input.complete();
|
|
43
|
+
subscriber.error(err);
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
slot.sub = projected$.subscribe({
|
|
47
|
+
next(value) {
|
|
48
|
+
slot.value = value;
|
|
49
|
+
if (batchingSourceNext) {
|
|
50
|
+
emittedDuringBatch = true;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
tryEmit();
|
|
54
|
+
},
|
|
55
|
+
error(err) {
|
|
56
|
+
subscriber.error(err);
|
|
57
|
+
},
|
|
58
|
+
complete() {
|
|
59
|
+
slot.completed = true;
|
|
60
|
+
checkComplete();
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
return slot;
|
|
64
|
+
}
|
|
65
|
+
const sourceSub = source$.subscribe({
|
|
66
|
+
next(items) {
|
|
67
|
+
batchingSourceNext = true;
|
|
68
|
+
emittedDuringBatch = false;
|
|
69
|
+
while (slots.length > items.length) {
|
|
70
|
+
const slot = slots.pop();
|
|
71
|
+
if (!slot)
|
|
72
|
+
break;
|
|
73
|
+
slot.sub?.unsubscribe();
|
|
74
|
+
slot.input.complete();
|
|
75
|
+
}
|
|
76
|
+
while (slots.length < items.length) {
|
|
77
|
+
const slot = createSlot(slots.length);
|
|
78
|
+
if (!slot) {
|
|
79
|
+
batchingSourceNext = false;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
slots.push(slot);
|
|
83
|
+
}
|
|
84
|
+
for (let i = 0; i < items.length; i++) {
|
|
85
|
+
if (subscriber.closed) {
|
|
86
|
+
batchingSourceNext = false;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const slot = slots[i];
|
|
90
|
+
if (!slot) {
|
|
91
|
+
batchingSourceNext = false;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
slot.input.next(items[i]);
|
|
95
|
+
}
|
|
96
|
+
// Emit once for this source update if possible.
|
|
97
|
+
if (emittedDuringBatch || items.length === 0)
|
|
98
|
+
tryEmit();
|
|
99
|
+
batchingSourceNext = false;
|
|
100
|
+
},
|
|
101
|
+
error(err) {
|
|
102
|
+
subscriber.error(err);
|
|
103
|
+
},
|
|
104
|
+
complete() {
|
|
105
|
+
sourceCompleted = true;
|
|
106
|
+
for (const slot of slots) {
|
|
107
|
+
slot.input.complete();
|
|
108
|
+
}
|
|
109
|
+
checkComplete();
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
return () => {
|
|
113
|
+
sourceSub.unsubscribe();
|
|
114
|
+
for (const slot of slots) {
|
|
115
|
+
slot.sub?.unsubscribe();
|
|
116
|
+
slot.input.complete();
|
|
117
|
+
}
|
|
118
|
+
slots.length = 0;
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Observable, type OperatorFunction } from "rxjs";
|
|
2
|
+
/**
|
|
3
|
+
* Dynamic counterpart to `switchMap(() => combineLatest(...))` for records,
|
|
4
|
+
* with stable branches by key.
|
|
5
|
+
*
|
|
6
|
+
* Each record key gets its own long-lived branch. Branches are only
|
|
7
|
+
* created/removed when keys are added/removed; value updates are pushed through
|
|
8
|
+
* the existing key branch.
|
|
9
|
+
*/
|
|
10
|
+
export declare function combineLatestByKey<Input extends Record<string, unknown>, R>(project: (source$: Observable<Input[Extract<keyof Input, string>]>, key: Extract<keyof Input, string>) => Observable<R>): OperatorFunction<Input, Record<Extract<keyof Input, string>, R>>;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { Observable, Subject } from "rxjs";
|
|
2
|
+
/** Sentinel to distinguish "no value yet" from a real value */
|
|
3
|
+
const NOT_YET = Symbol("NOT_YET");
|
|
4
|
+
/**
|
|
5
|
+
* Dynamic counterpart to `switchMap(() => combineLatest(...))` for records,
|
|
6
|
+
* with stable branches by key.
|
|
7
|
+
*
|
|
8
|
+
* Each record key gets its own long-lived branch. Branches are only
|
|
9
|
+
* created/removed when keys are added/removed; value updates are pushed through
|
|
10
|
+
* the existing key branch.
|
|
11
|
+
*/
|
|
12
|
+
export function combineLatestByKey(project) {
|
|
13
|
+
return (source$) => new Observable((subscriber) => {
|
|
14
|
+
const slots = new Map();
|
|
15
|
+
let sourceCompleted = false;
|
|
16
|
+
let currentKeys = [];
|
|
17
|
+
let batchingSourceNext = false;
|
|
18
|
+
let emittedDuringBatch = false;
|
|
19
|
+
function checkComplete() {
|
|
20
|
+
if (!sourceCompleted)
|
|
21
|
+
return;
|
|
22
|
+
for (const slot of slots.values()) {
|
|
23
|
+
if (!slot.completed)
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
subscriber.complete();
|
|
27
|
+
}
|
|
28
|
+
function tryEmit() {
|
|
29
|
+
for (const key of currentKeys) {
|
|
30
|
+
const slot = slots.get(key);
|
|
31
|
+
if (!slot || slot.value === NOT_YET)
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const out = {};
|
|
35
|
+
for (const key of currentKeys) {
|
|
36
|
+
const slot = slots.get(key);
|
|
37
|
+
if (!slot)
|
|
38
|
+
return;
|
|
39
|
+
out[key] = slot.value;
|
|
40
|
+
}
|
|
41
|
+
subscriber.next(out);
|
|
42
|
+
if (batchingSourceNext)
|
|
43
|
+
emittedDuringBatch = true;
|
|
44
|
+
}
|
|
45
|
+
function createSlot(key) {
|
|
46
|
+
const input = new Subject();
|
|
47
|
+
const slot = { input, value: NOT_YET, completed: false };
|
|
48
|
+
let child$;
|
|
49
|
+
try {
|
|
50
|
+
child$ = project(input, key);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
input.complete();
|
|
54
|
+
subscriber.error(err);
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
slot.sub = child$.subscribe({
|
|
58
|
+
next(value) {
|
|
59
|
+
slot.value = value;
|
|
60
|
+
if (batchingSourceNext) {
|
|
61
|
+
emittedDuringBatch = true;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
tryEmit();
|
|
65
|
+
},
|
|
66
|
+
error(err) {
|
|
67
|
+
subscriber.error(err);
|
|
68
|
+
},
|
|
69
|
+
complete() {
|
|
70
|
+
slot.completed = true;
|
|
71
|
+
checkComplete();
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
return slot;
|
|
75
|
+
}
|
|
76
|
+
const sourceSub = source$.subscribe({
|
|
77
|
+
next(input) {
|
|
78
|
+
batchingSourceNext = true;
|
|
79
|
+
emittedDuringBatch = false;
|
|
80
|
+
const entries = Object.entries(input);
|
|
81
|
+
currentKeys = entries.map(([key]) => key);
|
|
82
|
+
const newKeysSet = new Set(currentKeys);
|
|
83
|
+
for (const [key, slot] of slots) {
|
|
84
|
+
if (!newKeysSet.has(key)) {
|
|
85
|
+
slot.sub?.unsubscribe();
|
|
86
|
+
slot.input.complete();
|
|
87
|
+
slots.delete(key);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
for (const key of currentKeys) {
|
|
91
|
+
if (!slots.has(key)) {
|
|
92
|
+
const slot = createSlot(key);
|
|
93
|
+
if (!slot) {
|
|
94
|
+
batchingSourceNext = false;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
slots.set(key, slot);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
for (const [key, value] of entries) {
|
|
101
|
+
if (subscriber.closed) {
|
|
102
|
+
batchingSourceNext = false;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const slot = slots.get(key);
|
|
106
|
+
if (!slot) {
|
|
107
|
+
batchingSourceNext = false;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
slot.input.next(value);
|
|
111
|
+
}
|
|
112
|
+
if (emittedDuringBatch || entries.length === 0)
|
|
113
|
+
tryEmit();
|
|
114
|
+
batchingSourceNext = false;
|
|
115
|
+
},
|
|
116
|
+
error(err) {
|
|
117
|
+
subscriber.error(err);
|
|
118
|
+
},
|
|
119
|
+
complete() {
|
|
120
|
+
sourceCompleted = true;
|
|
121
|
+
for (const slot of slots.values()) {
|
|
122
|
+
slot.input.complete();
|
|
123
|
+
}
|
|
124
|
+
checkComplete();
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
return () => {
|
|
128
|
+
sourceSub.unsubscribe();
|
|
129
|
+
for (const slot of slots.values()) {
|
|
130
|
+
slot.sub?.unsubscribe();
|
|
131
|
+
slot.input.complete();
|
|
132
|
+
}
|
|
133
|
+
slots.clear();
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Observable, type OperatorFunction } from "rxjs";
|
|
2
|
+
/**
|
|
3
|
+
* Dynamic counterpart to `switchMap((values) => combineLatest(values.map(...)))`
|
|
4
|
+
* for arrays, with stable branches by value.
|
|
5
|
+
*
|
|
6
|
+
* A single branch is created per unique array value (Map key semantics) and is
|
|
7
|
+
* only created/removed when values are added/removed across emissions. Duplicate
|
|
8
|
+
* values in the same array share the same branch.
|
|
9
|
+
*/
|
|
10
|
+
export declare function combineLatestByValue<T, R>(project: (value: T) => Observable<R>): OperatorFunction<readonly T[], Map<T, R>>;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Observable } from "rxjs";
|
|
2
|
+
/** Sentinel to distinguish "no value yet" from a real value */
|
|
3
|
+
const NOT_YET = Symbol("NOT_YET");
|
|
4
|
+
/**
|
|
5
|
+
* Dynamic counterpart to `switchMap((values) => combineLatest(values.map(...)))`
|
|
6
|
+
* for arrays, with stable branches by value.
|
|
7
|
+
*
|
|
8
|
+
* A single branch is created per unique array value (Map key semantics) and is
|
|
9
|
+
* only created/removed when values are added/removed across emissions. Duplicate
|
|
10
|
+
* values in the same array share the same branch.
|
|
11
|
+
*/
|
|
12
|
+
export function combineLatestByValue(project) {
|
|
13
|
+
return (source$) => new Observable((subscriber) => {
|
|
14
|
+
const slots = new Map();
|
|
15
|
+
let sourceCompleted = false;
|
|
16
|
+
let currentValues = [];
|
|
17
|
+
let batchingSourceNext = false;
|
|
18
|
+
function checkComplete() {
|
|
19
|
+
if (!sourceCompleted)
|
|
20
|
+
return;
|
|
21
|
+
for (const slot of slots.values()) {
|
|
22
|
+
if (!slot.completed)
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
subscriber.complete();
|
|
26
|
+
}
|
|
27
|
+
function tryEmit() {
|
|
28
|
+
for (const value of currentValues) {
|
|
29
|
+
const slot = slots.get(value);
|
|
30
|
+
if (!slot || slot.value === NOT_YET)
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const out = new Map();
|
|
34
|
+
for (const value of currentValues) {
|
|
35
|
+
const slot = slots.get(value);
|
|
36
|
+
if (!slot)
|
|
37
|
+
return;
|
|
38
|
+
out.set(value, slot.value);
|
|
39
|
+
}
|
|
40
|
+
subscriber.next(out);
|
|
41
|
+
}
|
|
42
|
+
function createSlot(value) {
|
|
43
|
+
const slot = { value: NOT_YET, completed: false };
|
|
44
|
+
let child$;
|
|
45
|
+
try {
|
|
46
|
+
child$ = project(value);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
subscriber.error(err);
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
slot.sub = child$.subscribe({
|
|
53
|
+
next(nextValue) {
|
|
54
|
+
slot.value = nextValue;
|
|
55
|
+
if (batchingSourceNext) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
tryEmit();
|
|
59
|
+
},
|
|
60
|
+
error(err) {
|
|
61
|
+
subscriber.error(err);
|
|
62
|
+
},
|
|
63
|
+
complete() {
|
|
64
|
+
slot.completed = true;
|
|
65
|
+
checkComplete();
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
return slot;
|
|
69
|
+
}
|
|
70
|
+
const sourceSub = source$.subscribe({
|
|
71
|
+
next(input) {
|
|
72
|
+
batchingSourceNext = true;
|
|
73
|
+
const uniqueValues = [];
|
|
74
|
+
const nextValueSet = new Set();
|
|
75
|
+
for (const value of input) {
|
|
76
|
+
if (nextValueSet.has(value))
|
|
77
|
+
continue;
|
|
78
|
+
nextValueSet.add(value);
|
|
79
|
+
uniqueValues.push(value);
|
|
80
|
+
}
|
|
81
|
+
currentValues = uniqueValues;
|
|
82
|
+
for (const [value, slot] of slots) {
|
|
83
|
+
if (!nextValueSet.has(value)) {
|
|
84
|
+
slot.sub?.unsubscribe();
|
|
85
|
+
slots.delete(value);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
for (const value of currentValues) {
|
|
89
|
+
if (!slots.has(value)) {
|
|
90
|
+
const slot = createSlot(value);
|
|
91
|
+
if (!slot) {
|
|
92
|
+
batchingSourceNext = false;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
slots.set(value, slot);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
tryEmit();
|
|
99
|
+
batchingSourceNext = false;
|
|
100
|
+
},
|
|
101
|
+
error(err) {
|
|
102
|
+
subscriber.error(err);
|
|
103
|
+
},
|
|
104
|
+
complete() {
|
|
105
|
+
sourceCompleted = true;
|
|
106
|
+
checkComplete();
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
return () => {
|
|
110
|
+
sourceSub.unsubscribe();
|
|
111
|
+
for (const slot of slots.values()) {
|
|
112
|
+
slot.sub?.unsubscribe();
|
|
113
|
+
}
|
|
114
|
+
slots.clear();
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type OperatorFunction } from "rxjs";
|
|
2
|
+
/** Operator object type: one branch operator per output key. */
|
|
3
|
+
type CombineLatestByObjectOperators<Input, Output extends Record<string, unknown>> = {
|
|
4
|
+
[K in keyof Output]: OperatorFunction<Input, Output[K]>;
|
|
5
|
+
};
|
|
6
|
+
/** Operator tuple type: one branch operator per output index. */
|
|
7
|
+
type CombineLatestByArrayOperators<Input, Output extends readonly unknown[]> = {
|
|
8
|
+
[K in keyof Output]: OperatorFunction<Input, Output[K]>;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Branches a source through multiple operators and combines branch outputs with
|
|
12
|
+
* `combineLatest` semantics.
|
|
13
|
+
*/
|
|
14
|
+
export declare function combineLatestBy<Input, Output extends Record<string, unknown>>(operators: CombineLatestByObjectOperators<Input, Output>): OperatorFunction<Input, Output>;
|
|
15
|
+
export declare function combineLatestBy<Input, Output extends readonly unknown[]>(operators: CombineLatestByArrayOperators<Input, Output>): OperatorFunction<Input, Output>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { combineLatest, connect } from "rxjs";
|
|
2
|
+
export function combineLatestBy(operators) {
|
|
3
|
+
return connect((shared$) => {
|
|
4
|
+
if (Array.isArray(operators)) {
|
|
5
|
+
const branches = operators.map((op) => shared$.pipe(op));
|
|
6
|
+
return combineLatest(branches);
|
|
7
|
+
}
|
|
8
|
+
const objectOperators = operators;
|
|
9
|
+
const branches = Object.fromEntries(Object.entries(objectOperators).map(([key, op]) => [key, shared$.pipe(op)]));
|
|
10
|
+
return combineLatest(branches);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
export { firstValueFrom, lastValueFrom,
|
|
2
|
-
export
|
|
1
|
+
export { BehaviorSubject, combineLatest, firstValueFrom, lastValueFrom, merge, Observable, ReplaySubject, Subject, } from "rxjs";
|
|
2
|
+
export * from "./catch-error-inline.js";
|
|
3
|
+
export * from "./chainable.js";
|
|
4
|
+
export * from "./combine-latest-by-index.js";
|
|
5
|
+
export * from "./combine-latest-by-key.js";
|
|
6
|
+
export * from "./combine-latest-by-value.js";
|
|
7
|
+
export * from "./combine-latest-by.js";
|
|
3
8
|
export * from "./defined.js";
|
|
4
9
|
export * from "./get-observable-value.js";
|
|
5
|
-
export * from "./map-events-to-timeline.js";
|
|
6
10
|
export * from "./map-events-to-store.js";
|
|
11
|
+
export * from "./map-events-to-timeline.js";
|
|
12
|
+
export * from "./relay-selection.js";
|
|
7
13
|
export * from "./simple-timeout.js";
|
|
14
|
+
export * from "./timeout-with-ignore.js";
|
|
8
15
|
export * from "./watch-event-updates.js";
|
|
9
16
|
export * from "./with-immediate-value.js";
|
|
10
|
-
export * from "./relay-selection.js";
|
package/dist/observable/index.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
// Re-export some useful rxjs functions
|
|
2
|
-
export { firstValueFrom, lastValueFrom,
|
|
3
|
-
export
|
|
2
|
+
export { BehaviorSubject, combineLatest, firstValueFrom, lastValueFrom, merge, Observable, ReplaySubject, Subject, } from "rxjs";
|
|
3
|
+
export * from "./catch-error-inline.js";
|
|
4
|
+
export * from "./chainable.js";
|
|
5
|
+
export * from "./combine-latest-by-index.js";
|
|
6
|
+
export * from "./combine-latest-by-key.js";
|
|
7
|
+
export * from "./combine-latest-by-value.js";
|
|
8
|
+
export * from "./combine-latest-by.js";
|
|
4
9
|
export * from "./defined.js";
|
|
5
10
|
export * from "./get-observable-value.js";
|
|
6
|
-
export * from "./map-events-to-timeline.js";
|
|
7
11
|
export * from "./map-events-to-store.js";
|
|
12
|
+
export * from "./map-events-to-timeline.js";
|
|
13
|
+
export * from "./relay-selection.js";
|
|
8
14
|
export * from "./simple-timeout.js";
|
|
15
|
+
export * from "./timeout-with-ignore.js";
|
|
9
16
|
export * from "./watch-event-updates.js";
|
|
10
17
|
export * from "./with-immediate-value.js";
|
|
11
|
-
export * from "./relay-selection.js";
|