@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 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
+ ```
@@ -0,0 +1,4 @@
1
+ export { NormalizedCache } from './store.js';
2
+ export type { NormalizedCacheOptions } from './store.js';
3
+ export type { EntityDefinition, EntitySubscriber, EntityTypeId, TypeSubscriber } from './types.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -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,2 @@
1
+ export { NormalizedCache } from './store.js';
2
+ //# sourceMappingURL=index.js.map
@@ -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"}
@@ -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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"}
@@ -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"}