@atcute/cache 0.1.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/LICENSE +14 -0
- package/README.md +105 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/predicates.d.ts +14 -0
- package/dist/predicates.d.ts.map +1 -0
- package/dist/predicates.js +25 -0
- package/dist/predicates.js.map +1 -0
- package/dist/store.d.ts +94 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +291 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +31 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -0
- package/dist/walker.d.ts +34 -0
- package/dist/walker.d.ts.map +1 -0
- package/dist/walker.js +179 -0
- package/dist/walker.js.map +1 -0
- package/lib/index.ts +3 -0
- package/lib/predicates.ts +39 -0
- package/lib/store.ts +364 -0
- package/lib/types.ts +55 -0
- package/lib/walker.ts +259 -0
- package/package.json +32 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
BSD Zero Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mary
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
9
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
10
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
11
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
12
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
13
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
14
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# @atcute/cache
|
|
2
|
+
|
|
3
|
+
> [!WARNING]
|
|
4
|
+
> experimental package - API may change
|
|
5
|
+
|
|
6
|
+
normalized cache store for AT Protocol.
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm install @atcute/cache
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
stores entities by their unique keys and automatically deduplicates nested references. when an
|
|
13
|
+
entity appears in multiple API responses (e.g., a profile in a post author and in followers), they
|
|
14
|
+
share the same object reference in memory.
|
|
15
|
+
|
|
16
|
+
## usage
|
|
17
|
+
|
|
18
|
+
### setting up the cache
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { NormalizedCache } from '@atcute/cache';
|
|
22
|
+
import { AppBskyActorDefs, AppBskyFeedDefs } from '@atcute/bluesky';
|
|
23
|
+
|
|
24
|
+
const cache = new NormalizedCache();
|
|
25
|
+
|
|
26
|
+
// register entity types with key extractors
|
|
27
|
+
cache.define({
|
|
28
|
+
schema: AppBskyFeedDefs.postViewSchema,
|
|
29
|
+
key: (post) => post.uri,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
cache.define({
|
|
33
|
+
schema: AppBskyActorDefs.profileViewBasicSchema,
|
|
34
|
+
key: (profile) => profile.did,
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### normalizing API responses
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { AppBskyFeedGetTimeline } from '@atcute/bluesky';
|
|
42
|
+
|
|
43
|
+
const response = await rpc.get('app.bsky.feed.getTimeline', { params: {} });
|
|
44
|
+
|
|
45
|
+
// walks the response, extracts entities, and stores them
|
|
46
|
+
const timeline = cache.normalize(AppBskyFeedGetTimeline.mainSchema.output.schema, response.data);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### reading from cache
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
const post = cache.get(AppBskyFeedDefs.postViewSchema, 'at://did:plc:.../app.bsky.feed.post/...');
|
|
53
|
+
const profile = cache.get(AppBskyActorDefs.profileViewBasicSchema, 'did:plc:...');
|
|
54
|
+
|
|
55
|
+
// check if entity exists
|
|
56
|
+
if (cache.has(AppBskyFeedDefs.postViewSchema, postUri)) {
|
|
57
|
+
// ...
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// get all cached entities of a type
|
|
61
|
+
const allPosts = cache.getAll(AppBskyFeedDefs.postViewSchema);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### optimistic updates
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
// update a post's like count immediately, before the API responds
|
|
68
|
+
cache.update(AppBskyFeedDefs.postViewSchema, postUri, (post) => ({
|
|
69
|
+
...post,
|
|
70
|
+
viewer: { ...post.viewer, like: tempLikeUri },
|
|
71
|
+
likeCount: (post.likeCount ?? 0) + 1,
|
|
72
|
+
}));
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### subscribing to changes
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
// subscribe to a specific entity
|
|
79
|
+
const unsubscribe = cache.subscribe(AppBskyFeedDefs.postViewSchema, postUri, (post) => {
|
|
80
|
+
console.log('post changed:', post);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// subscribe to all entities of a type
|
|
84
|
+
const unsubscribeType = cache.subscribeType(AppBskyActorDefs.profileViewBasicSchema, (key, profile) => {
|
|
85
|
+
console.log(`profile ${key} changed:`, profile);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// clean up when done
|
|
89
|
+
unsubscribe();
|
|
90
|
+
unsubscribeType();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### custom merge logic
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
cache.define({
|
|
97
|
+
schema: AppBskyActorDefs.profileViewBasicSchema,
|
|
98
|
+
key: (profile) => profile.did,
|
|
99
|
+
// custom merge: prefer existing avatar if new one is missing
|
|
100
|
+
merge: (existing, incoming) => ({
|
|
101
|
+
...incoming,
|
|
102
|
+
avatar: incoming.avatar ?? existing.avatar,
|
|
103
|
+
}),
|
|
104
|
+
});
|
|
105
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,YAAY,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACzD,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ArraySchema, BaseSchema, LiteralSchema, NullableSchema, ObjectSchema, OptionalSchema, VariantSchema } from '@atcute/lexicons/validations';
|
|
2
|
+
/** check if schema is an object schema */
|
|
3
|
+
export declare const isObjectSchema: (schema: BaseSchema<unknown, unknown>) => schema is ObjectSchema<import("@atcute/lexicons/validations").LooseObjectShape>;
|
|
4
|
+
/** check if schema is an array schema */
|
|
5
|
+
export declare const isArraySchema: (schema: BaseSchema<unknown, unknown>) => schema is ArraySchema<BaseSchema<unknown, unknown>>;
|
|
6
|
+
/** check if schema is a variant schema */
|
|
7
|
+
export declare const isVariantSchema: (schema: BaseSchema<unknown, unknown>) => schema is VariantSchema<readonly ObjectSchema<any>[], boolean>;
|
|
8
|
+
/** check if schema is an optional schema */
|
|
9
|
+
export declare const isOptionalSchema: (schema: BaseSchema<unknown, unknown>) => schema is OptionalSchema<BaseSchema<unknown, unknown>, unknown>;
|
|
10
|
+
/** check if schema is a nullable schema */
|
|
11
|
+
export declare const isNullableSchema: (schema: BaseSchema<unknown, unknown>) => schema is NullableSchema<BaseSchema<unknown, unknown>>;
|
|
12
|
+
/** check if schema is a literal schema */
|
|
13
|
+
export declare const isLiteralSchema: (schema: BaseSchema<unknown, unknown>) => schema is LiteralSchema<string | number | boolean>;
|
|
14
|
+
//# sourceMappingURL=predicates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"predicates.d.ts","sourceRoot":"","sources":["../lib/predicates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,WAAW,EACX,UAAU,EACV,aAAa,EACb,cAAc,EACd,YAAY,EACZ,cAAc,EACd,aAAa,EACb,MAAM,8BAA8B,CAAC;AAEtC,0CAA0C;AAC1C,eAAO,MAAM,cAAc,2HAE1B,CAAC;AAEF,yCAAyC;AACzC,eAAO,MAAM,aAAa,+FAEzB,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,eAAe,0GAE3B,CAAC;AAEF,4CAA4C;AAC5C,eAAO,MAAM,gBAAgB,2GAE5B,CAAC;AAEF,2CAA2C;AAC3C,eAAO,MAAM,gBAAgB,kGAE5B,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,eAAe,8FAE3B,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** check if schema is an object schema */
|
|
2
|
+
export const isObjectSchema = (schema) => {
|
|
3
|
+
return schema.type === 'object';
|
|
4
|
+
};
|
|
5
|
+
/** check if schema is an array schema */
|
|
6
|
+
export const isArraySchema = (schema) => {
|
|
7
|
+
return schema.type === 'array';
|
|
8
|
+
};
|
|
9
|
+
/** check if schema is a variant schema */
|
|
10
|
+
export const isVariantSchema = (schema) => {
|
|
11
|
+
return schema.type === 'variant';
|
|
12
|
+
};
|
|
13
|
+
/** check if schema is an optional schema */
|
|
14
|
+
export const isOptionalSchema = (schema) => {
|
|
15
|
+
return schema.type === 'optional';
|
|
16
|
+
};
|
|
17
|
+
/** check if schema is a nullable schema */
|
|
18
|
+
export const isNullableSchema = (schema) => {
|
|
19
|
+
return schema.type === 'nullable';
|
|
20
|
+
};
|
|
21
|
+
/** check if schema is a literal schema */
|
|
22
|
+
export const isLiteralSchema = (schema) => {
|
|
23
|
+
return schema.type === 'literal';
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=predicates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"predicates.js","sourceRoot":"","sources":["../lib/predicates.ts"],"names":[],"mappings":"AAUA,0CAA0C;AAC1C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAkB,EAA0B,EAAE,CAAC;IAC7E,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC;AAAA,CAChC,CAAC;AAEF,yCAAyC;AACzC,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAkB,EAAyB,EAAE,CAAC;IAC3E,OAAO,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC;AAAA,CAC/B,CAAC;AAEF,0CAA0C;AAC1C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAkB,EAA2B,EAAE,CAAC;IAC/E,OAAO,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC;AAAA,CACjC,CAAC;AAEF,4CAA4C;AAC5C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAkB,EAA4B,EAAE,CAAC;IACjF,OAAO,MAAM,CAAC,IAAI,KAAK,UAAU,CAAC;AAAA,CAClC,CAAC;AAEF,2CAA2C;AAC3C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAkB,EAA4B,EAAE,CAAC;IACjF,OAAO,MAAM,CAAC,IAAI,KAAK,UAAU,CAAC;AAAA,CAClC,CAAC;AAEF,0CAA0C;AAC1C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAkB,EAA2B,EAAE,CAAC;IAC/E,OAAO,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC;AAAA,CACjC,CAAC"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { BaseSchema, InferOutput, ObjectSchema } from '@atcute/lexicons/validations';
|
|
2
|
+
import type { EntityDefinition, EntitySubscriber, TypeSubscriber } from './types.js';
|
|
3
|
+
export interface NormalizedCacheOptions {
|
|
4
|
+
wrapEntity?: (entity: unknown) => unknown;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* normalized cache store for AT Protocol responses
|
|
8
|
+
*/
|
|
9
|
+
export declare class NormalizedCache {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(options?: NormalizedCacheOptions);
|
|
12
|
+
/**
|
|
13
|
+
* register an entity type for normalization
|
|
14
|
+
* @param definition entity definition with schema, key extractor, and optional merge function
|
|
15
|
+
*/
|
|
16
|
+
define<T extends ObjectSchema>(definition: EntityDefinition<T>): void;
|
|
17
|
+
/**
|
|
18
|
+
* walk response using schema, normalize and cache entities
|
|
19
|
+
* @param schema the response schema
|
|
20
|
+
* @param data the response data
|
|
21
|
+
* @returns response with cached entity refs swapped in
|
|
22
|
+
*/
|
|
23
|
+
normalize<T extends BaseSchema>(schema: T, data: InferOutput<T>): InferOutput<T>;
|
|
24
|
+
/**
|
|
25
|
+
* create a reusable normalizer function for a schema
|
|
26
|
+
* @param schema the response schema
|
|
27
|
+
* @returns function that normalizes data according to schema
|
|
28
|
+
*/
|
|
29
|
+
normalizer<T extends BaseSchema>(schema: T): (data: InferOutput<T>) => InferOutput<T>;
|
|
30
|
+
/**
|
|
31
|
+
* get entity from cache by schema and key
|
|
32
|
+
* @param schema the entity schema
|
|
33
|
+
* @param key the entity key
|
|
34
|
+
* @returns the cached entity or undefined if not found/collected
|
|
35
|
+
*/
|
|
36
|
+
get<T extends ObjectSchema>(schema: T, key: string): InferOutput<T> | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* check if entity exists in cache
|
|
39
|
+
* @param schema the entity schema
|
|
40
|
+
* @param key the entity key
|
|
41
|
+
*/
|
|
42
|
+
has(schema: ObjectSchema, key: string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* get all cached entities of a type
|
|
45
|
+
* @param schema the entity schema
|
|
46
|
+
* @returns map of key to entity (only includes live refs)
|
|
47
|
+
*/
|
|
48
|
+
getAll<T extends ObjectSchema>(schema: T): Map<string, InferOutput<T>>;
|
|
49
|
+
/**
|
|
50
|
+
* set entity directly in cache
|
|
51
|
+
* @param schema the entity schema
|
|
52
|
+
* @param key the entity key
|
|
53
|
+
* @param entity the entity to cache
|
|
54
|
+
*/
|
|
55
|
+
set<T extends ObjectSchema>(schema: T, key: string, entity: InferOutput<T>): void;
|
|
56
|
+
/**
|
|
57
|
+
* update entity with updater function
|
|
58
|
+
* @param schema the entity schema
|
|
59
|
+
* @param key the entity key
|
|
60
|
+
* @param updater function that returns updated entity
|
|
61
|
+
* @returns true if entity was found and updated
|
|
62
|
+
*/
|
|
63
|
+
update<T extends ObjectSchema>(schema: T, key: string, updater: (entity: InferOutput<T>) => InferOutput<T>): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* delete entity from cache
|
|
66
|
+
* @param schema the entity schema
|
|
67
|
+
* @param key the entity key
|
|
68
|
+
* @returns true if entity was found and deleted
|
|
69
|
+
*/
|
|
70
|
+
delete(schema: ObjectSchema, key: string): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* delete all entities of a type
|
|
73
|
+
* @param schema the entity schema
|
|
74
|
+
*/
|
|
75
|
+
deleteType(schema: ObjectSchema): void;
|
|
76
|
+
/** clear entire cache */
|
|
77
|
+
clear(): void;
|
|
78
|
+
/**
|
|
79
|
+
* subscribe to changes for a specific entity
|
|
80
|
+
* @param schema the entity schema
|
|
81
|
+
* @param key the entity key
|
|
82
|
+
* @param callback called when entity changes
|
|
83
|
+
* @returns unsubscribe function
|
|
84
|
+
*/
|
|
85
|
+
subscribe<T extends ObjectSchema>(schema: T, key: string, callback: EntitySubscriber<InferOutput<T>>): () => void;
|
|
86
|
+
/**
|
|
87
|
+
* subscribe to all changes for an entity type
|
|
88
|
+
* @param schema the entity schema
|
|
89
|
+
* @param callback called when any entity of this type changes
|
|
90
|
+
* @returns unsubscribe function
|
|
91
|
+
*/
|
|
92
|
+
subscribeType<T extends ObjectSchema>(schema: T, callback: TypeSubscriber<InferOutput<T>>): () => void;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../lib/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAE1F,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAgB,cAAc,EAAE,MAAM,YAAY,CAAC;AAanG,MAAM,WAAW,sBAAsB;IACtC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;CAC1C;AAED;;GAEG;AACH,qBAAa,eAAe;;IAsB3B,YAAY,OAAO,CAAC,EAAE,sBAAsB,EAE3C;IAyDD;;;OAGG;IACH,MAAM,CAAC,CAAC,SAAS,YAAY,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAAI,CAqBpE;IAED;;;;;OAKG;IACH,SAAS,CAAC,CAAC,SAAS,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAE/E;IAED;;;;OAIG;IACH,UAAU,CAAC,CAAC,SAAS,UAAU,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,CAEpF;IAED;;;;;OAKG;IACH,GAAG,CAAC,CAAC,SAAS,YAAY,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAQ9E;IAED;;;;OAIG;IACH,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ9C;IAED;;;;OAIG;IACH,MAAM,CAAC,CAAC,SAAS,YAAY,EAAE,MAAM,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAgBrE;IAED;;;;;OAKG;IACH,GAAG,CAAC,CAAC,SAAS,YAAY,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAmBhF;IAED;;;;;;OAMG;IACH,MAAM,CAAC,CAAC,SAAS,YAAY,EAC5B,MAAM,EAAE,CAAC,EACT,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,GACjD,OAAO,CAiBT;IAED;;;;;OAKG;IACH,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAcjD;IAED;;;OAGG;IACH,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAYrC;IAED,yBAAyB;IACzB,KAAK,IAAI,IAAI,CASZ;IAED;;;;;;OAMG;IACH,SAAS,CAAC,CAAC,SAAS,YAAY,EAC/B,MAAM,EAAE,CAAC,EACT,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GACxC,MAAM,IAAI,CAoBZ;IAED;;;;;OAKG;IACH,aAAa,CAAC,CAAC,SAAS,YAAY,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAWrG;CACD"}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { getTypeIdFromSchema } from './types.js';
|
|
2
|
+
import { WalkerCache } from './walker.js';
|
|
3
|
+
/**
|
|
4
|
+
* normalized cache store for AT Protocol responses
|
|
5
|
+
*/
|
|
6
|
+
export class NormalizedCache {
|
|
7
|
+
#schemaToTypeId = new Map();
|
|
8
|
+
#stores = new Map();
|
|
9
|
+
#wrapEntity;
|
|
10
|
+
#walkerCache = new WalkerCache({
|
|
11
|
+
isEntityType: (typeId) => this.#stores.has(typeId),
|
|
12
|
+
upsertEntity: (typeId, incoming) => this.#upsertEntity(typeId, incoming),
|
|
13
|
+
});
|
|
14
|
+
#registry = new FinalizationRegistry((held) => {
|
|
15
|
+
const store = this.#stores.get(held.typeId);
|
|
16
|
+
if (store) {
|
|
17
|
+
const ref = store.entities.get(held.key);
|
|
18
|
+
// only delete if the ref is actually dead (not replaced with a new one)
|
|
19
|
+
if (ref !== undefined && ref.deref() === undefined) {
|
|
20
|
+
store.entities.delete(held.key);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
constructor(options) {
|
|
25
|
+
this.#wrapEntity = options?.wrapEntity;
|
|
26
|
+
}
|
|
27
|
+
#getTypeId(schema) {
|
|
28
|
+
let typeId = this.#schemaToTypeId.get(schema);
|
|
29
|
+
if (typeId === undefined) {
|
|
30
|
+
typeId = getTypeIdFromSchema(schema);
|
|
31
|
+
if (typeId !== undefined) {
|
|
32
|
+
this.#schemaToTypeId.set(schema, typeId);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return typeId;
|
|
36
|
+
}
|
|
37
|
+
#getStore(schema) {
|
|
38
|
+
const typeId = this.#getTypeId(schema);
|
|
39
|
+
return typeId ? this.#stores.get(typeId) : undefined;
|
|
40
|
+
}
|
|
41
|
+
#notifySubscribers(store, key, entity) {
|
|
42
|
+
// notify entity-specific subscribers
|
|
43
|
+
const entitySubs = store.subscribers.get(key);
|
|
44
|
+
if (entitySubs) {
|
|
45
|
+
for (const cb of entitySubs) {
|
|
46
|
+
cb(entity);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// notify type subscribers
|
|
50
|
+
for (const cb of store.typeSubscribers) {
|
|
51
|
+
cb(key, entity);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
#upsertEntity(typeId, incoming) {
|
|
55
|
+
const store = this.#stores.get(typeId);
|
|
56
|
+
const key = store.definition.key(incoming);
|
|
57
|
+
const existingRef = store.entities.get(key);
|
|
58
|
+
const existing = existingRef?.deref();
|
|
59
|
+
if (existing !== undefined) {
|
|
60
|
+
// merge incoming into existing
|
|
61
|
+
const merge = store.definition.merge;
|
|
62
|
+
const merged = merge ? merge(existing, incoming) : incoming;
|
|
63
|
+
Object.assign(existing, merged);
|
|
64
|
+
this.#notifySubscribers(store, key, existing);
|
|
65
|
+
return existing;
|
|
66
|
+
}
|
|
67
|
+
// new entity - wrap and store it
|
|
68
|
+
const entity = this.#wrapEntity ? this.#wrapEntity(incoming) : incoming;
|
|
69
|
+
store.entities.set(key, new WeakRef(entity));
|
|
70
|
+
this.#registry.register(entity, { typeId, key });
|
|
71
|
+
this.#notifySubscribers(store, key, entity);
|
|
72
|
+
return entity;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* register an entity type for normalization
|
|
76
|
+
* @param definition entity definition with schema, key extractor, and optional merge function
|
|
77
|
+
*/
|
|
78
|
+
define(definition) {
|
|
79
|
+
const typeId = getTypeIdFromSchema(definition.schema);
|
|
80
|
+
if (typeId === undefined) {
|
|
81
|
+
throw new Error('schema must have a $type literal field');
|
|
82
|
+
}
|
|
83
|
+
if (this.#stores.has(typeId)) {
|
|
84
|
+
throw new Error(`entity type "${typeId}" is already defined`);
|
|
85
|
+
}
|
|
86
|
+
this.#stores.set(typeId, {
|
|
87
|
+
definition: definition,
|
|
88
|
+
entities: new Map(),
|
|
89
|
+
subscribers: new Map(),
|
|
90
|
+
typeSubscribers: new Set(),
|
|
91
|
+
});
|
|
92
|
+
this.#schemaToTypeId.set(definition.schema, typeId);
|
|
93
|
+
// invalidate cached walkers since entity types changed
|
|
94
|
+
this.#walkerCache.invalidate();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* walk response using schema, normalize and cache entities
|
|
98
|
+
* @param schema the response schema
|
|
99
|
+
* @param data the response data
|
|
100
|
+
* @returns response with cached entity refs swapped in
|
|
101
|
+
*/
|
|
102
|
+
normalize(schema, data) {
|
|
103
|
+
return this.#walkerCache.getWalker(schema)(data);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* create a reusable normalizer function for a schema
|
|
107
|
+
* @param schema the response schema
|
|
108
|
+
* @returns function that normalizes data according to schema
|
|
109
|
+
*/
|
|
110
|
+
normalizer(schema) {
|
|
111
|
+
return (data) => this.normalize(schema, data);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* get entity from cache by schema and key
|
|
115
|
+
* @param schema the entity schema
|
|
116
|
+
* @param key the entity key
|
|
117
|
+
* @returns the cached entity or undefined if not found/collected
|
|
118
|
+
*/
|
|
119
|
+
get(schema, key) {
|
|
120
|
+
const store = this.#getStore(schema);
|
|
121
|
+
if (!store) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
const ref = store.entities.get(key);
|
|
125
|
+
return ref?.deref();
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* check if entity exists in cache
|
|
129
|
+
* @param schema the entity schema
|
|
130
|
+
* @param key the entity key
|
|
131
|
+
*/
|
|
132
|
+
has(schema, key) {
|
|
133
|
+
const store = this.#getStore(schema);
|
|
134
|
+
if (!store) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
const ref = store.entities.get(key);
|
|
138
|
+
return ref?.deref() !== undefined;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* get all cached entities of a type
|
|
142
|
+
* @param schema the entity schema
|
|
143
|
+
* @returns map of key to entity (only includes live refs)
|
|
144
|
+
*/
|
|
145
|
+
getAll(schema) {
|
|
146
|
+
const store = this.#getStore(schema);
|
|
147
|
+
const result = new Map();
|
|
148
|
+
if (!store) {
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
for (const [key, ref] of store.entities) {
|
|
152
|
+
const entity = ref.deref();
|
|
153
|
+
if (entity !== undefined) {
|
|
154
|
+
result.set(key, entity);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* set entity directly in cache
|
|
161
|
+
* @param schema the entity schema
|
|
162
|
+
* @param key the entity key
|
|
163
|
+
* @param entity the entity to cache
|
|
164
|
+
*/
|
|
165
|
+
set(schema, key, entity) {
|
|
166
|
+
const typeId = this.#getTypeId(schema);
|
|
167
|
+
if (typeId === undefined || !this.#stores.has(typeId)) {
|
|
168
|
+
throw new Error('schema is not registered');
|
|
169
|
+
}
|
|
170
|
+
const store = this.#stores.get(typeId);
|
|
171
|
+
const existingRef = store.entities.get(key);
|
|
172
|
+
const existing = existingRef?.deref();
|
|
173
|
+
if (existing !== undefined) {
|
|
174
|
+
Object.assign(existing, entity);
|
|
175
|
+
this.#notifySubscribers(store, key, existing);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
const wrapped = this.#wrapEntity ? this.#wrapEntity(entity) : entity;
|
|
179
|
+
store.entities.set(key, new WeakRef(wrapped));
|
|
180
|
+
this.#registry.register(wrapped, { typeId, key });
|
|
181
|
+
this.#notifySubscribers(store, key, wrapped);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* update entity with updater function
|
|
186
|
+
* @param schema the entity schema
|
|
187
|
+
* @param key the entity key
|
|
188
|
+
* @param updater function that returns updated entity
|
|
189
|
+
* @returns true if entity was found and updated
|
|
190
|
+
*/
|
|
191
|
+
update(schema, key, updater) {
|
|
192
|
+
const store = this.#getStore(schema);
|
|
193
|
+
if (!store) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
const ref = store.entities.get(key);
|
|
197
|
+
const existing = ref?.deref();
|
|
198
|
+
if (existing === undefined) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
const updated = updater(existing);
|
|
202
|
+
Object.assign(existing, updated);
|
|
203
|
+
this.#notifySubscribers(store, key, existing);
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* delete entity from cache
|
|
208
|
+
* @param schema the entity schema
|
|
209
|
+
* @param key the entity key
|
|
210
|
+
* @returns true if entity was found and deleted
|
|
211
|
+
*/
|
|
212
|
+
delete(schema, key) {
|
|
213
|
+
const store = this.#getStore(schema);
|
|
214
|
+
if (!store) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
const existed = store.entities.has(key);
|
|
218
|
+
store.entities.delete(key);
|
|
219
|
+
if (existed) {
|
|
220
|
+
this.#notifySubscribers(store, key, undefined);
|
|
221
|
+
}
|
|
222
|
+
return existed;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* delete all entities of a type
|
|
226
|
+
* @param schema the entity schema
|
|
227
|
+
*/
|
|
228
|
+
deleteType(schema) {
|
|
229
|
+
const store = this.#getStore(schema);
|
|
230
|
+
if (!store) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const keys = [...store.entities.keys()];
|
|
234
|
+
store.entities.clear();
|
|
235
|
+
for (const key of keys) {
|
|
236
|
+
this.#notifySubscribers(store, key, undefined);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/** clear entire cache */
|
|
240
|
+
clear() {
|
|
241
|
+
for (const [_typeId, store] of this.#stores) {
|
|
242
|
+
const keys = [...store.entities.keys()];
|
|
243
|
+
store.entities.clear();
|
|
244
|
+
for (const key of keys) {
|
|
245
|
+
this.#notifySubscribers(store, key, undefined);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* subscribe to changes for a specific entity
|
|
251
|
+
* @param schema the entity schema
|
|
252
|
+
* @param key the entity key
|
|
253
|
+
* @param callback called when entity changes
|
|
254
|
+
* @returns unsubscribe function
|
|
255
|
+
*/
|
|
256
|
+
subscribe(schema, key, callback) {
|
|
257
|
+
const store = this.#getStore(schema);
|
|
258
|
+
if (!store) {
|
|
259
|
+
throw new Error('schema is not registered');
|
|
260
|
+
}
|
|
261
|
+
let subs = store.subscribers.get(key);
|
|
262
|
+
if (!subs) {
|
|
263
|
+
subs = new Set();
|
|
264
|
+
store.subscribers.set(key, subs);
|
|
265
|
+
}
|
|
266
|
+
subs.add(callback);
|
|
267
|
+
return () => {
|
|
268
|
+
subs.delete(callback);
|
|
269
|
+
if (subs.size === 0) {
|
|
270
|
+
store.subscribers.delete(key);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* subscribe to all changes for an entity type
|
|
276
|
+
* @param schema the entity schema
|
|
277
|
+
* @param callback called when any entity of this type changes
|
|
278
|
+
* @returns unsubscribe function
|
|
279
|
+
*/
|
|
280
|
+
subscribeType(schema, callback) {
|
|
281
|
+
const store = this.#getStore(schema);
|
|
282
|
+
if (!store) {
|
|
283
|
+
throw new Error('schema is not registered');
|
|
284
|
+
}
|
|
285
|
+
store.typeSubscribers.add(callback);
|
|
286
|
+
return () => {
|
|
287
|
+
store.typeSubscribers.delete(callback);
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../lib/store.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { InferOutput, ObjectSchema } from '@atcute/lexicons/validations';
|
|
2
|
+
/** entity type identifier, extracted from schema's $type literal */
|
|
3
|
+
export type EntityTypeId = string;
|
|
4
|
+
/**
|
|
5
|
+
* definition for an entity type that can be normalized
|
|
6
|
+
* @template T the object schema type
|
|
7
|
+
*/
|
|
8
|
+
export interface EntityDefinition<T extends ObjectSchema = ObjectSchema> {
|
|
9
|
+
/** the schema for this entity type */
|
|
10
|
+
schema: T;
|
|
11
|
+
/** extract cache key from entity instance */
|
|
12
|
+
key: (entity: InferOutput<T>) => string;
|
|
13
|
+
/**
|
|
14
|
+
* merge strategy when entity already exists in cache
|
|
15
|
+
* @param existing the currently cached entity
|
|
16
|
+
* @param incoming the new entity data
|
|
17
|
+
* @returns partial entity with fields to update
|
|
18
|
+
*/
|
|
19
|
+
merge?: (existing: InferOutput<T>, incoming: InferOutput<T>) => Partial<InferOutput<T>>;
|
|
20
|
+
}
|
|
21
|
+
/** subscriber callback type */
|
|
22
|
+
export type EntitySubscriber<T> = (entity: T | undefined) => void;
|
|
23
|
+
/** type-level subscriber callback */
|
|
24
|
+
export type TypeSubscriber<T> = (key: string, entity: T | undefined) => void;
|
|
25
|
+
/**
|
|
26
|
+
* extract the $type literal value from an object schema
|
|
27
|
+
* @param schema object schema with $type field
|
|
28
|
+
* @returns the $type string value or undefined
|
|
29
|
+
*/
|
|
30
|
+
export declare const getTypeIdFromSchema: (schema: ObjectSchema<import("@atcute/lexicons/validations").LooseObjectShape>) => string | undefined;
|
|
31
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../lib/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,WAAW,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAI1F,oEAAoE;AACpE,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AAElC;;;GAGG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY;IACtE,sCAAsC;IACtC,MAAM,EAAE,CAAC,CAAC;IACV,6CAA6C;IAC7C,GAAG,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC;IACxC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;CACxF;AAED,+BAA+B;AAC/B,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,SAAS,KAAK,IAAI,CAAC;AAElE,qCAAqC;AACrC,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,SAAS,KAAK,IAAI,CAAC;AAE7E;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,uGAkB/B,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { isLiteralSchema, isOptionalSchema } from './predicates.js';
|
|
2
|
+
/**
|
|
3
|
+
* extract the $type literal value from an object schema
|
|
4
|
+
* @param schema object schema with $type field
|
|
5
|
+
* @returns the $type string value or undefined
|
|
6
|
+
*/
|
|
7
|
+
export const getTypeIdFromSchema = (schema) => {
|
|
8
|
+
const shape = schema.shape;
|
|
9
|
+
let typeField = shape.$type;
|
|
10
|
+
if (typeField === undefined) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
// unwrap optional
|
|
14
|
+
if (isOptionalSchema(typeField)) {
|
|
15
|
+
typeField = typeField.wrapped;
|
|
16
|
+
}
|
|
17
|
+
if (isLiteralSchema(typeField) && typeof typeField.expected === 'string') {
|
|
18
|
+
return typeField.expected;
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../lib/types.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AA6BpE;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAoB,EAA4B,EAAE,CAAC;IACtF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAC3B,IAAI,SAAS,GAA2B,KAAK,CAAC,KAAK,CAAC;IAEpD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,kBAAkB;IAClB,IAAI,gBAAgB,CAAC,SAAS,CAAC,EAAE,CAAC;QACjC,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC;IAC/B,CAAC;IAED,IAAI,eAAe,CAAC,SAAS,CAAC,IAAI,OAAO,SAAS,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1E,OAAO,SAAS,CAAC,QAAQ,CAAC;IAC3B,CAAC;IAED,OAAO,SAAS,CAAC;AAAA,CACjB,CAAC"}
|