fetchium 0.4.1 → 0.4.2
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/CHANGELOG.md +6 -0
- package/dist/cjs/development/topic/index.js +1 -1
- package/dist/cjs/development/topic/index.js.map +1 -1
- package/dist/cjs/production/topic/index.js +1 -1
- package/dist/cjs/production/topic/index.js.map +1 -1
- package/dist/esm/development/topic/index.js +26 -21
- package/dist/esm/development/topic/index.js.map +1 -1
- package/dist/esm/production/topic/index.js +26 -21
- package/dist/esm/production/topic/index.js.map +1 -1
- package/dist/esm/topic/TopicQuery.d.ts +2 -1
- package/dist/esm/topic/TopicQuery.d.ts.map +1 -1
- package/dist/esm/topic/TopicQueryAdapter.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# fetchium
|
|
2
2
|
|
|
3
|
+
## 0.4.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 117531d: Add `getTopic()` to `TopicQuery` for parity with `getPath()` on `RESTQuery`. The static `topic` template only supports fixed-shape topics, so consumers with variable-segment-count or conditionally-shaped topics had to hand-author one query class per shape. Subclasses can now override `getTopic()` to compute the topic dynamically at execution time over the resolved params (e.g. `'layout:' + this.params.segments.map(encodeURIComponent).join(':')`); when defined it takes precedence over the `topic` field. The `topic` field is now optional, matching `path?` on `RESTQuery`. Existing queries that define `topic` as a static template continue to work unchanged.
|
|
8
|
+
|
|
3
9
|
## 0.4.1
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("../QueryClient-D_e76rYS.js"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("../QueryClient-D_e76rYS.js"),f=require("../QueryAdapter-DUo338ga.js");class n extends f.QueryAdapter{_topics=new Map;fulfillTopic(e,i){const t=this._topics.get(e);if(t===void 0){this._topics.set(e,{status:"fulfilled",data:i});return}t.status==="pending"&&(t.status="fulfilled",t.data=i,t.resolve(i))}rejectTopic(e,i){const t=this._topics.get(e);if(t===void 0){this._topics.set(e,{status:"rejected",error:i});return}t.status==="pending"&&(t.status="rejected",t.error=i,t.reject(i))}clearTopic(e){this._topics.delete(e)}clearAll(){this._topics.clear()}async send(e,i){const t=e;t._topicAdapter=this;const r=t.getTopic?t.getTopic():t.topic;if(r===void 0)throw new Error("TopicQuery requires a topic. Define `topic` as a field or override `getTopic()`.");const s=this._topics.get(r);if(s)switch(s.status){case"fulfilled":return s.data;case"rejected":throw s.error;case"pending":return s.promise}let c,o;const p=new Promise((a,l)=>{c=a,o=l});return this._topics.set(r,{status:"pending",promise:p,resolve:c,reject:o}),this.subscribe(r),p}sendMutationEvent(e){this.queryClient.applyMutationEvent(e)}}class g extends d.Query{static adapter=n;topic;getIdentityKey(){return`topic:${this.topic??""}`}getConfig(){return{staleTime:0,subscribe:()=>()=>{const e=this._topicAdapter,i=this.getTopic?this.getTopic():this.topic;i!==void 0&&e?.unsubscribe(i)}}}}exports.TopicQuery=g;exports.TopicQueryAdapter=n;
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/topic/TopicQueryAdapter.ts","../../../../src/topic/TopicQuery.ts"],"sourcesContent":["import { QueryAdapter } from '../QueryAdapter.js';\nimport type { Query } from '../query.js';\nimport type { MutationEvent } from '../types.js';\n\n// ================================\n// TopicQueryAdapter — abstract adapter for topic-based subscriptions\n// ================================\n\ninterface TopicCtx extends Query {\n topic: string;\n _topicAdapter?: TopicQueryAdapter;\n}\n\ninterface TopicState {\n status: 'pending' | 'fulfilled' | 'rejected';\n promise?: Promise<unknown>;\n resolve?: (data: unknown) => void;\n reject?: (error: unknown) => void;\n data?: unknown;\n error?: unknown;\n}\n\nexport abstract class TopicQueryAdapter extends QueryAdapter {\n private _topics = new Map<string, TopicState>();\n\n /**\n * Called when a query activates for a given topic.\n * Implementations should start delivering data for this topic,\n * calling `fulfillTopic()` when initial data is available and\n * `sendMutationEvent()` for ongoing updates.\n */\n abstract subscribe(topic: string): void;\n\n /**\n * Called when the query deactivates. Implementations should\n * tear down any resources for this topic.\n */\n abstract unsubscribe(topic: string): void;\n\n /**\n * Resolve the pending promise for a topic with initial data.\n * Can be called before `send()` — the data will be picked up\n * when the query activates.\n */\n protected fulfillTopic(topic: string, data: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'fulfilled', data });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'fulfilled';\n state.data = data;\n state.resolve!(data);\n }\n }\n\n /**\n * Reject the pending promise for a topic.\n * Can be called before `send()` — the error will be propagated\n * when the query activates.\n */\n protected rejectTopic(topic: string, error: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'rejected', error });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'rejected';\n state.error = error;\n state.reject!(error);\n }\n }\n\n /**\n * Clears internal state for a topic. Called automatically by\n * `unsubscribe` — subclasses generally don't need to call this.\n */\n protected clearTopic(topic: string): void {\n this._topics.delete(topic);\n }\n\n protected clearAll(): void {\n this._topics.clear();\n }\n\n override async send(ctx: Query, _signal: AbortSignal): Promise<unknown> {\n const topicCtx = ctx as TopicCtx;\n topicCtx._topicAdapter = this;\n const topic = topicCtx.topic;\n\n const existing = this._topics.get(topic);\n\n if (existing) {\n switch (existing.status) {\n case 'fulfilled':\n return existing.data;\n case 'rejected':\n throw existing.error;\n case 'pending':\n return existing.promise;\n }\n }\n\n // No state yet — create a deferred and subscribe\n let resolve!: (data: unknown) => void;\n let reject!: (error: unknown) => void;\n const promise = new Promise<unknown>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n this._topics.set(topic, { status: 'pending', promise, resolve, reject });\n this.subscribe(topic);\n\n return promise;\n }\n\n /**\n * Convenience wrapper — pushes a mutation event through the QueryClient\n * so that entities and live collections are updated reactively.\n */\n protected sendMutationEvent(event: MutationEvent): void {\n this.queryClient!.applyMutationEvent(event);\n }\n}\n","import { Query } from '../query.js';\nimport { TopicQueryAdapter } from './TopicQueryAdapter.js';\nimport type { QueryAdapterClass } from '../QueryAdapter.js';\nimport type { QueryConfigOptions } from '../query-types.js';\n\n// ================================\n// TopicQuery — declarative topic-based query definition\n// ================================\n\nexport abstract class TopicQuery extends Query {\n // Explicit type lets subclasses override with adapters that take constructor args.\n static override adapter: QueryAdapterClass<TopicQueryAdapter> = TopicQueryAdapter;\n\n
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/topic/TopicQueryAdapter.ts","../../../../src/topic/TopicQuery.ts"],"sourcesContent":["import { QueryAdapter } from '../QueryAdapter.js';\nimport type { Query } from '../query.js';\nimport type { MutationEvent } from '../types.js';\n\n// ================================\n// TopicQueryAdapter — abstract adapter for topic-based subscriptions\n// ================================\n\ninterface TopicCtx extends Query {\n topic?: string;\n getTopic?(): string;\n _topicAdapter?: TopicQueryAdapter;\n}\n\ninterface TopicState {\n status: 'pending' | 'fulfilled' | 'rejected';\n promise?: Promise<unknown>;\n resolve?: (data: unknown) => void;\n reject?: (error: unknown) => void;\n data?: unknown;\n error?: unknown;\n}\n\nexport abstract class TopicQueryAdapter extends QueryAdapter {\n private _topics = new Map<string, TopicState>();\n\n /**\n * Called when a query activates for a given topic.\n * Implementations should start delivering data for this topic,\n * calling `fulfillTopic()` when initial data is available and\n * `sendMutationEvent()` for ongoing updates.\n */\n abstract subscribe(topic: string): void;\n\n /**\n * Called when the query deactivates. Implementations should\n * tear down any resources for this topic.\n */\n abstract unsubscribe(topic: string): void;\n\n /**\n * Resolve the pending promise for a topic with initial data.\n * Can be called before `send()` — the data will be picked up\n * when the query activates.\n */\n protected fulfillTopic(topic: string, data: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'fulfilled', data });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'fulfilled';\n state.data = data;\n state.resolve!(data);\n }\n }\n\n /**\n * Reject the pending promise for a topic.\n * Can be called before `send()` — the error will be propagated\n * when the query activates.\n */\n protected rejectTopic(topic: string, error: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'rejected', error });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'rejected';\n state.error = error;\n state.reject!(error);\n }\n }\n\n /**\n * Clears internal state for a topic. Called automatically by\n * `unsubscribe` — subclasses generally don't need to call this.\n */\n protected clearTopic(topic: string): void {\n this._topics.delete(topic);\n }\n\n protected clearAll(): void {\n this._topics.clear();\n }\n\n override async send(ctx: Query, _signal: AbortSignal): Promise<unknown> {\n const topicCtx = ctx as TopicCtx;\n topicCtx._topicAdapter = this;\n const topic = topicCtx.getTopic ? topicCtx.getTopic() : topicCtx.topic;\n\n if (topic === undefined) {\n throw new Error('TopicQuery requires a topic. Define `topic` as a field or override `getTopic()`.');\n }\n\n const existing = this._topics.get(topic);\n\n if (existing) {\n switch (existing.status) {\n case 'fulfilled':\n return existing.data;\n case 'rejected':\n throw existing.error;\n case 'pending':\n return existing.promise;\n }\n }\n\n // No state yet — create a deferred and subscribe\n let resolve!: (data: unknown) => void;\n let reject!: (error: unknown) => void;\n const promise = new Promise<unknown>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n this._topics.set(topic, { status: 'pending', promise, resolve, reject });\n this.subscribe(topic);\n\n return promise;\n }\n\n /**\n * Convenience wrapper — pushes a mutation event through the QueryClient\n * so that entities and live collections are updated reactively.\n */\n protected sendMutationEvent(event: MutationEvent): void {\n this.queryClient!.applyMutationEvent(event);\n }\n}\n","import { Query } from '../query.js';\nimport { TopicQueryAdapter } from './TopicQueryAdapter.js';\nimport type { QueryAdapterClass } from '../QueryAdapter.js';\nimport type { QueryConfigOptions } from '../query-types.js';\n\n// ================================\n// TopicQuery — declarative topic-based query definition\n// ================================\n\nexport abstract class TopicQuery extends Query {\n // Explicit type lets subclasses override with adapters that take constructor args.\n static override adapter: QueryAdapterClass<TopicQueryAdapter> = TopicQueryAdapter;\n\n topic?: string;\n\n // User-overridable getter — the adapter reads this from the execution context.\n getTopic?(): string;\n\n getIdentityKey(): string {\n return `topic:${this.topic ?? ''}`;\n }\n\n getConfig(): QueryConfigOptions {\n return {\n staleTime: 0,\n subscribe: () => {\n return () => {\n const adapter = (this as Record<string, any>)._topicAdapter as TopicQueryAdapter | undefined;\n const topic = this.getTopic ? this.getTopic() : this.topic;\n if (topic !== undefined) {\n adapter?.unsubscribe(topic);\n }\n };\n },\n };\n }\n}\n"],"names":["TopicQueryAdapter","QueryAdapter","topic","data","state","error","ctx","_signal","topicCtx","existing","resolve","reject","promise","res","rej","event","TopicQuery","Query","adapter"],"mappings":"uKAuBO,MAAeA,UAA0BC,EAAAA,YAAa,CACnD,YAAc,IAqBZ,aAAaC,EAAeC,EAAqB,CACzD,MAAMC,EAAQ,KAAK,QAAQ,IAAIF,CAAK,EAEpC,GAAIE,IAAU,OAAW,CACvB,KAAK,QAAQ,IAAIF,EAAO,CAAE,OAAQ,YAAa,KAAAC,EAAM,EACrD,MACF,CAEIC,EAAM,SAAW,YACnBA,EAAM,OAAS,YACfA,EAAM,KAAOD,EACbC,EAAM,QAASD,CAAI,EAEvB,CAOU,YAAYD,EAAeG,EAAsB,CACzD,MAAMD,EAAQ,KAAK,QAAQ,IAAIF,CAAK,EAEpC,GAAIE,IAAU,OAAW,CACvB,KAAK,QAAQ,IAAIF,EAAO,CAAE,OAAQ,WAAY,MAAAG,EAAO,EACrD,MACF,CAEID,EAAM,SAAW,YACnBA,EAAM,OAAS,WACfA,EAAM,MAAQC,EACdD,EAAM,OAAQC,CAAK,EAEvB,CAMU,WAAWH,EAAqB,CACxC,KAAK,QAAQ,OAAOA,CAAK,CAC3B,CAEU,UAAiB,CACzB,KAAK,QAAQ,MAAA,CACf,CAEA,MAAe,KAAKI,EAAYC,EAAwC,CACtE,MAAMC,EAAWF,EACjBE,EAAS,cAAgB,KACzB,MAAMN,EAAQM,EAAS,SAAWA,EAAS,SAAA,EAAaA,EAAS,MAEjE,GAAIN,IAAU,OACZ,MAAM,IAAI,MAAM,kFAAkF,EAGpG,MAAMO,EAAW,KAAK,QAAQ,IAAIP,CAAK,EAEvC,GAAIO,EACF,OAAQA,EAAS,OAAA,CACf,IAAK,YACH,OAAOA,EAAS,KAClB,IAAK,WACH,MAAMA,EAAS,MACjB,IAAK,UACH,OAAOA,EAAS,OAAA,CAKtB,IAAIC,EACAC,EACJ,MAAMC,EAAU,IAAI,QAAiB,CAACC,EAAKC,IAAQ,CACjDJ,EAAUG,EACVF,EAASG,CACX,CAAC,EAED,YAAK,QAAQ,IAAIZ,EAAO,CAAE,OAAQ,UAAW,QAAAU,EAAS,QAAAF,EAAS,OAAAC,EAAQ,EACvE,KAAK,UAAUT,CAAK,EAEbU,CACT,CAMU,kBAAkBG,EAA4B,CACtD,KAAK,YAAa,mBAAmBA,CAAK,CAC5C,CACF,CC9HO,MAAeC,UAAmBC,EAAAA,KAAM,CAE7C,OAAgB,QAAgDjB,EAEhE,MAKA,gBAAyB,CACvB,MAAO,SAAS,KAAK,OAAS,EAAE,EAClC,CAEA,WAAgC,CAC9B,MAAO,CACL,UAAW,EACX,UAAW,IACF,IAAM,CACX,MAAMkB,EAAW,KAA6B,cACxChB,EAAQ,KAAK,SAAW,KAAK,SAAA,EAAa,KAAK,MACjDA,IAAU,QACZgB,GAAS,YAAYhB,CAAK,CAE9B,CACF,CAEJ,CACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("../QueryClient-BPdrfgtE.js"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("../QueryClient-BPdrfgtE.js"),f=require("../QueryAdapter-DUo338ga.js");class n extends f.QueryAdapter{_topics=new Map;fulfillTopic(e,i){const t=this._topics.get(e);if(t===void 0){this._topics.set(e,{status:"fulfilled",data:i});return}t.status==="pending"&&(t.status="fulfilled",t.data=i,t.resolve(i))}rejectTopic(e,i){const t=this._topics.get(e);if(t===void 0){this._topics.set(e,{status:"rejected",error:i});return}t.status==="pending"&&(t.status="rejected",t.error=i,t.reject(i))}clearTopic(e){this._topics.delete(e)}clearAll(){this._topics.clear()}async send(e,i){const t=e;t._topicAdapter=this;const r=t.getTopic?t.getTopic():t.topic;if(r===void 0)throw new Error("TopicQuery requires a topic. Define `topic` as a field or override `getTopic()`.");const s=this._topics.get(r);if(s)switch(s.status){case"fulfilled":return s.data;case"rejected":throw s.error;case"pending":return s.promise}let c,o;const p=new Promise((a,l)=>{c=a,o=l});return this._topics.set(r,{status:"pending",promise:p,resolve:c,reject:o}),this.subscribe(r),p}sendMutationEvent(e){this.queryClient.applyMutationEvent(e)}}class g extends d.Query{static adapter=n;topic;getIdentityKey(){return`topic:${this.topic??""}`}getConfig(){return{staleTime:0,subscribe:()=>()=>{const e=this._topicAdapter,i=this.getTopic?this.getTopic():this.topic;i!==void 0&&e?.unsubscribe(i)}}}}exports.TopicQuery=g;exports.TopicQueryAdapter=n;
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/topic/TopicQueryAdapter.ts","../../../../src/topic/TopicQuery.ts"],"sourcesContent":["import { QueryAdapter } from '../QueryAdapter.js';\nimport type { Query } from '../query.js';\nimport type { MutationEvent } from '../types.js';\n\n// ================================\n// TopicQueryAdapter — abstract adapter for topic-based subscriptions\n// ================================\n\ninterface TopicCtx extends Query {\n topic: string;\n _topicAdapter?: TopicQueryAdapter;\n}\n\ninterface TopicState {\n status: 'pending' | 'fulfilled' | 'rejected';\n promise?: Promise<unknown>;\n resolve?: (data: unknown) => void;\n reject?: (error: unknown) => void;\n data?: unknown;\n error?: unknown;\n}\n\nexport abstract class TopicQueryAdapter extends QueryAdapter {\n private _topics = new Map<string, TopicState>();\n\n /**\n * Called when a query activates for a given topic.\n * Implementations should start delivering data for this topic,\n * calling `fulfillTopic()` when initial data is available and\n * `sendMutationEvent()` for ongoing updates.\n */\n abstract subscribe(topic: string): void;\n\n /**\n * Called when the query deactivates. Implementations should\n * tear down any resources for this topic.\n */\n abstract unsubscribe(topic: string): void;\n\n /**\n * Resolve the pending promise for a topic with initial data.\n * Can be called before `send()` — the data will be picked up\n * when the query activates.\n */\n protected fulfillTopic(topic: string, data: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'fulfilled', data });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'fulfilled';\n state.data = data;\n state.resolve!(data);\n }\n }\n\n /**\n * Reject the pending promise for a topic.\n * Can be called before `send()` — the error will be propagated\n * when the query activates.\n */\n protected rejectTopic(topic: string, error: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'rejected', error });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'rejected';\n state.error = error;\n state.reject!(error);\n }\n }\n\n /**\n * Clears internal state for a topic. Called automatically by\n * `unsubscribe` — subclasses generally don't need to call this.\n */\n protected clearTopic(topic: string): void {\n this._topics.delete(topic);\n }\n\n protected clearAll(): void {\n this._topics.clear();\n }\n\n override async send(ctx: Query, _signal: AbortSignal): Promise<unknown> {\n const topicCtx = ctx as TopicCtx;\n topicCtx._topicAdapter = this;\n const topic = topicCtx.topic;\n\n const existing = this._topics.get(topic);\n\n if (existing) {\n switch (existing.status) {\n case 'fulfilled':\n return existing.data;\n case 'rejected':\n throw existing.error;\n case 'pending':\n return existing.promise;\n }\n }\n\n // No state yet — create a deferred and subscribe\n let resolve!: (data: unknown) => void;\n let reject!: (error: unknown) => void;\n const promise = new Promise<unknown>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n this._topics.set(topic, { status: 'pending', promise, resolve, reject });\n this.subscribe(topic);\n\n return promise;\n }\n\n /**\n * Convenience wrapper — pushes a mutation event through the QueryClient\n * so that entities and live collections are updated reactively.\n */\n protected sendMutationEvent(event: MutationEvent): void {\n this.queryClient!.applyMutationEvent(event);\n }\n}\n","import { Query } from '../query.js';\nimport { TopicQueryAdapter } from './TopicQueryAdapter.js';\nimport type { QueryAdapterClass } from '../QueryAdapter.js';\nimport type { QueryConfigOptions } from '../query-types.js';\n\n// ================================\n// TopicQuery — declarative topic-based query definition\n// ================================\n\nexport abstract class TopicQuery extends Query {\n // Explicit type lets subclasses override with adapters that take constructor args.\n static override adapter: QueryAdapterClass<TopicQueryAdapter> = TopicQueryAdapter;\n\n
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/topic/TopicQueryAdapter.ts","../../../../src/topic/TopicQuery.ts"],"sourcesContent":["import { QueryAdapter } from '../QueryAdapter.js';\nimport type { Query } from '../query.js';\nimport type { MutationEvent } from '../types.js';\n\n// ================================\n// TopicQueryAdapter — abstract adapter for topic-based subscriptions\n// ================================\n\ninterface TopicCtx extends Query {\n topic?: string;\n getTopic?(): string;\n _topicAdapter?: TopicQueryAdapter;\n}\n\ninterface TopicState {\n status: 'pending' | 'fulfilled' | 'rejected';\n promise?: Promise<unknown>;\n resolve?: (data: unknown) => void;\n reject?: (error: unknown) => void;\n data?: unknown;\n error?: unknown;\n}\n\nexport abstract class TopicQueryAdapter extends QueryAdapter {\n private _topics = new Map<string, TopicState>();\n\n /**\n * Called when a query activates for a given topic.\n * Implementations should start delivering data for this topic,\n * calling `fulfillTopic()` when initial data is available and\n * `sendMutationEvent()` for ongoing updates.\n */\n abstract subscribe(topic: string): void;\n\n /**\n * Called when the query deactivates. Implementations should\n * tear down any resources for this topic.\n */\n abstract unsubscribe(topic: string): void;\n\n /**\n * Resolve the pending promise for a topic with initial data.\n * Can be called before `send()` — the data will be picked up\n * when the query activates.\n */\n protected fulfillTopic(topic: string, data: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'fulfilled', data });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'fulfilled';\n state.data = data;\n state.resolve!(data);\n }\n }\n\n /**\n * Reject the pending promise for a topic.\n * Can be called before `send()` — the error will be propagated\n * when the query activates.\n */\n protected rejectTopic(topic: string, error: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'rejected', error });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'rejected';\n state.error = error;\n state.reject!(error);\n }\n }\n\n /**\n * Clears internal state for a topic. Called automatically by\n * `unsubscribe` — subclasses generally don't need to call this.\n */\n protected clearTopic(topic: string): void {\n this._topics.delete(topic);\n }\n\n protected clearAll(): void {\n this._topics.clear();\n }\n\n override async send(ctx: Query, _signal: AbortSignal): Promise<unknown> {\n const topicCtx = ctx as TopicCtx;\n topicCtx._topicAdapter = this;\n const topic = topicCtx.getTopic ? topicCtx.getTopic() : topicCtx.topic;\n\n if (topic === undefined) {\n throw new Error('TopicQuery requires a topic. Define `topic` as a field or override `getTopic()`.');\n }\n\n const existing = this._topics.get(topic);\n\n if (existing) {\n switch (existing.status) {\n case 'fulfilled':\n return existing.data;\n case 'rejected':\n throw existing.error;\n case 'pending':\n return existing.promise;\n }\n }\n\n // No state yet — create a deferred and subscribe\n let resolve!: (data: unknown) => void;\n let reject!: (error: unknown) => void;\n const promise = new Promise<unknown>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n this._topics.set(topic, { status: 'pending', promise, resolve, reject });\n this.subscribe(topic);\n\n return promise;\n }\n\n /**\n * Convenience wrapper — pushes a mutation event through the QueryClient\n * so that entities and live collections are updated reactively.\n */\n protected sendMutationEvent(event: MutationEvent): void {\n this.queryClient!.applyMutationEvent(event);\n }\n}\n","import { Query } from '../query.js';\nimport { TopicQueryAdapter } from './TopicQueryAdapter.js';\nimport type { QueryAdapterClass } from '../QueryAdapter.js';\nimport type { QueryConfigOptions } from '../query-types.js';\n\n// ================================\n// TopicQuery — declarative topic-based query definition\n// ================================\n\nexport abstract class TopicQuery extends Query {\n // Explicit type lets subclasses override with adapters that take constructor args.\n static override adapter: QueryAdapterClass<TopicQueryAdapter> = TopicQueryAdapter;\n\n topic?: string;\n\n // User-overridable getter — the adapter reads this from the execution context.\n getTopic?(): string;\n\n getIdentityKey(): string {\n return `topic:${this.topic ?? ''}`;\n }\n\n getConfig(): QueryConfigOptions {\n return {\n staleTime: 0,\n subscribe: () => {\n return () => {\n const adapter = (this as Record<string, any>)._topicAdapter as TopicQueryAdapter | undefined;\n const topic = this.getTopic ? this.getTopic() : this.topic;\n if (topic !== undefined) {\n adapter?.unsubscribe(topic);\n }\n };\n },\n };\n }\n}\n"],"names":["TopicQueryAdapter","QueryAdapter","topic","data","state","error","ctx","_signal","topicCtx","existing","resolve","reject","promise","res","rej","event","TopicQuery","Query","adapter"],"mappings":"uKAuBO,MAAeA,UAA0BC,EAAAA,YAAa,CACnD,YAAc,IAqBZ,aAAaC,EAAeC,EAAqB,CACzD,MAAMC,EAAQ,KAAK,QAAQ,IAAIF,CAAK,EAEpC,GAAIE,IAAU,OAAW,CACvB,KAAK,QAAQ,IAAIF,EAAO,CAAE,OAAQ,YAAa,KAAAC,EAAM,EACrD,MACF,CAEIC,EAAM,SAAW,YACnBA,EAAM,OAAS,YACfA,EAAM,KAAOD,EACbC,EAAM,QAASD,CAAI,EAEvB,CAOU,YAAYD,EAAeG,EAAsB,CACzD,MAAMD,EAAQ,KAAK,QAAQ,IAAIF,CAAK,EAEpC,GAAIE,IAAU,OAAW,CACvB,KAAK,QAAQ,IAAIF,EAAO,CAAE,OAAQ,WAAY,MAAAG,EAAO,EACrD,MACF,CAEID,EAAM,SAAW,YACnBA,EAAM,OAAS,WACfA,EAAM,MAAQC,EACdD,EAAM,OAAQC,CAAK,EAEvB,CAMU,WAAWH,EAAqB,CACxC,KAAK,QAAQ,OAAOA,CAAK,CAC3B,CAEU,UAAiB,CACzB,KAAK,QAAQ,MAAA,CACf,CAEA,MAAe,KAAKI,EAAYC,EAAwC,CACtE,MAAMC,EAAWF,EACjBE,EAAS,cAAgB,KACzB,MAAMN,EAAQM,EAAS,SAAWA,EAAS,SAAA,EAAaA,EAAS,MAEjE,GAAIN,IAAU,OACZ,MAAM,IAAI,MAAM,kFAAkF,EAGpG,MAAMO,EAAW,KAAK,QAAQ,IAAIP,CAAK,EAEvC,GAAIO,EACF,OAAQA,EAAS,OAAA,CACf,IAAK,YACH,OAAOA,EAAS,KAClB,IAAK,WACH,MAAMA,EAAS,MACjB,IAAK,UACH,OAAOA,EAAS,OAAA,CAKtB,IAAIC,EACAC,EACJ,MAAMC,EAAU,IAAI,QAAiB,CAACC,EAAKC,IAAQ,CACjDJ,EAAUG,EACVF,EAASG,CACX,CAAC,EAED,YAAK,QAAQ,IAAIZ,EAAO,CAAE,OAAQ,UAAW,QAAAU,EAAS,QAAAF,EAAS,OAAAC,EAAQ,EACvE,KAAK,UAAUT,CAAK,EAEbU,CACT,CAMU,kBAAkBG,EAA4B,CACtD,KAAK,YAAa,mBAAmBA,CAAK,CAC5C,CACF,CC9HO,MAAeC,UAAmBC,EAAAA,KAAM,CAE7C,OAAgB,QAAgDjB,EAEhE,MAKA,gBAAyB,CACvB,MAAO,SAAS,KAAK,OAAS,EAAE,EAClC,CAEA,WAAgC,CAC9B,MAAO,CACL,UAAW,EACX,UAAW,IACF,IAAM,CACX,MAAMkB,EAAW,KAA6B,cACxChB,EAAQ,KAAK,SAAW,KAAK,SAAA,EAAa,KAAK,MACjDA,IAAU,QACZgB,GAAS,YAAYhB,CAAK,CAE9B,CACF,CAEJ,CACF"}
|
|
@@ -7,26 +7,26 @@ class f extends d {
|
|
|
7
7
|
* Can be called before `send()` — the data will be picked up
|
|
8
8
|
* when the query activates.
|
|
9
9
|
*/
|
|
10
|
-
fulfillTopic(e,
|
|
10
|
+
fulfillTopic(e, i) {
|
|
11
11
|
const t = this._topics.get(e);
|
|
12
12
|
if (t === void 0) {
|
|
13
|
-
this._topics.set(e, { status: "fulfilled", data:
|
|
13
|
+
this._topics.set(e, { status: "fulfilled", data: i });
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
|
-
t.status === "pending" && (t.status = "fulfilled", t.data =
|
|
16
|
+
t.status === "pending" && (t.status = "fulfilled", t.data = i, t.resolve(i));
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* Reject the pending promise for a topic.
|
|
20
20
|
* Can be called before `send()` — the error will be propagated
|
|
21
21
|
* when the query activates.
|
|
22
22
|
*/
|
|
23
|
-
rejectTopic(e,
|
|
23
|
+
rejectTopic(e, i) {
|
|
24
24
|
const t = this._topics.get(e);
|
|
25
25
|
if (t === void 0) {
|
|
26
|
-
this._topics.set(e, { status: "rejected", error:
|
|
26
|
+
this._topics.set(e, { status: "rejected", error: i });
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
-
t.status === "pending" && (t.status = "rejected", t.error =
|
|
29
|
+
t.status === "pending" && (t.status = "rejected", t.error = i, t.reject(i));
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
32
|
* Clears internal state for a topic. Called automatically by
|
|
@@ -38,24 +38,27 @@ class f extends d {
|
|
|
38
38
|
clearAll() {
|
|
39
39
|
this._topics.clear();
|
|
40
40
|
}
|
|
41
|
-
async send(e,
|
|
41
|
+
async send(e, i) {
|
|
42
42
|
const t = e;
|
|
43
43
|
t._topicAdapter = this;
|
|
44
|
-
const
|
|
45
|
-
if (
|
|
46
|
-
|
|
44
|
+
const o = t.getTopic ? t.getTopic() : t.topic;
|
|
45
|
+
if (o === void 0)
|
|
46
|
+
throw new Error("TopicQuery requires a topic. Define `topic` as a field or override `getTopic()`.");
|
|
47
|
+
const s = this._topics.get(o);
|
|
48
|
+
if (s)
|
|
49
|
+
switch (s.status) {
|
|
47
50
|
case "fulfilled":
|
|
48
|
-
return
|
|
51
|
+
return s.data;
|
|
49
52
|
case "rejected":
|
|
50
|
-
throw
|
|
53
|
+
throw s.error;
|
|
51
54
|
case "pending":
|
|
52
|
-
return
|
|
55
|
+
return s.promise;
|
|
53
56
|
}
|
|
54
|
-
let
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
+
let r, c;
|
|
58
|
+
const p = new Promise((a, u) => {
|
|
59
|
+
r = a, c = u;
|
|
57
60
|
});
|
|
58
|
-
return this._topics.set(
|
|
61
|
+
return this._topics.set(o, { status: "pending", promise: p, resolve: r, reject: c }), this.subscribe(o), p;
|
|
59
62
|
}
|
|
60
63
|
/**
|
|
61
64
|
* Convenience wrapper — pushes a mutation event through the QueryClient
|
|
@@ -65,23 +68,25 @@ class f extends d {
|
|
|
65
68
|
this.queryClient.applyMutationEvent(e);
|
|
66
69
|
}
|
|
67
70
|
}
|
|
68
|
-
class
|
|
71
|
+
class T extends l {
|
|
69
72
|
// Explicit type lets subclasses override with adapters that take constructor args.
|
|
70
73
|
static adapter = f;
|
|
74
|
+
topic;
|
|
71
75
|
getIdentityKey() {
|
|
72
|
-
return `topic:${this.topic}`;
|
|
76
|
+
return `topic:${this.topic ?? ""}`;
|
|
73
77
|
}
|
|
74
78
|
getConfig() {
|
|
75
79
|
return {
|
|
76
80
|
staleTime: 0,
|
|
77
81
|
subscribe: () => () => {
|
|
78
|
-
this._topicAdapter
|
|
82
|
+
const e = this._topicAdapter, i = this.getTopic ? this.getTopic() : this.topic;
|
|
83
|
+
i !== void 0 && e?.unsubscribe(i);
|
|
79
84
|
}
|
|
80
85
|
};
|
|
81
86
|
}
|
|
82
87
|
}
|
|
83
88
|
export {
|
|
84
|
-
|
|
89
|
+
T as TopicQuery,
|
|
85
90
|
f as TopicQueryAdapter
|
|
86
91
|
};
|
|
87
92
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/topic/TopicQueryAdapter.ts","../../../../src/topic/TopicQuery.ts"],"sourcesContent":["import { QueryAdapter } from '../QueryAdapter.js';\nimport type { Query } from '../query.js';\nimport type { MutationEvent } from '../types.js';\n\n// ================================\n// TopicQueryAdapter — abstract adapter for topic-based subscriptions\n// ================================\n\ninterface TopicCtx extends Query {\n topic: string;\n _topicAdapter?: TopicQueryAdapter;\n}\n\ninterface TopicState {\n status: 'pending' | 'fulfilled' | 'rejected';\n promise?: Promise<unknown>;\n resolve?: (data: unknown) => void;\n reject?: (error: unknown) => void;\n data?: unknown;\n error?: unknown;\n}\n\nexport abstract class TopicQueryAdapter extends QueryAdapter {\n private _topics = new Map<string, TopicState>();\n\n /**\n * Called when a query activates for a given topic.\n * Implementations should start delivering data for this topic,\n * calling `fulfillTopic()` when initial data is available and\n * `sendMutationEvent()` for ongoing updates.\n */\n abstract subscribe(topic: string): void;\n\n /**\n * Called when the query deactivates. Implementations should\n * tear down any resources for this topic.\n */\n abstract unsubscribe(topic: string): void;\n\n /**\n * Resolve the pending promise for a topic with initial data.\n * Can be called before `send()` — the data will be picked up\n * when the query activates.\n */\n protected fulfillTopic(topic: string, data: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'fulfilled', data });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'fulfilled';\n state.data = data;\n state.resolve!(data);\n }\n }\n\n /**\n * Reject the pending promise for a topic.\n * Can be called before `send()` — the error will be propagated\n * when the query activates.\n */\n protected rejectTopic(topic: string, error: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'rejected', error });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'rejected';\n state.error = error;\n state.reject!(error);\n }\n }\n\n /**\n * Clears internal state for a topic. Called automatically by\n * `unsubscribe` — subclasses generally don't need to call this.\n */\n protected clearTopic(topic: string): void {\n this._topics.delete(topic);\n }\n\n protected clearAll(): void {\n this._topics.clear();\n }\n\n override async send(ctx: Query, _signal: AbortSignal): Promise<unknown> {\n const topicCtx = ctx as TopicCtx;\n topicCtx._topicAdapter = this;\n const topic = topicCtx.topic;\n\n const existing = this._topics.get(topic);\n\n if (existing) {\n switch (existing.status) {\n case 'fulfilled':\n return existing.data;\n case 'rejected':\n throw existing.error;\n case 'pending':\n return existing.promise;\n }\n }\n\n // No state yet — create a deferred and subscribe\n let resolve!: (data: unknown) => void;\n let reject!: (error: unknown) => void;\n const promise = new Promise<unknown>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n this._topics.set(topic, { status: 'pending', promise, resolve, reject });\n this.subscribe(topic);\n\n return promise;\n }\n\n /**\n * Convenience wrapper — pushes a mutation event through the QueryClient\n * so that entities and live collections are updated reactively.\n */\n protected sendMutationEvent(event: MutationEvent): void {\n this.queryClient!.applyMutationEvent(event);\n }\n}\n","import { Query } from '../query.js';\nimport { TopicQueryAdapter } from './TopicQueryAdapter.js';\nimport type { QueryAdapterClass } from '../QueryAdapter.js';\nimport type { QueryConfigOptions } from '../query-types.js';\n\n// ================================\n// TopicQuery — declarative topic-based query definition\n// ================================\n\nexport abstract class TopicQuery extends Query {\n // Explicit type lets subclasses override with adapters that take constructor args.\n static override adapter: QueryAdapterClass<TopicQueryAdapter> = TopicQueryAdapter;\n\n
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/topic/TopicQueryAdapter.ts","../../../../src/topic/TopicQuery.ts"],"sourcesContent":["import { QueryAdapter } from '../QueryAdapter.js';\nimport type { Query } from '../query.js';\nimport type { MutationEvent } from '../types.js';\n\n// ================================\n// TopicQueryAdapter — abstract adapter for topic-based subscriptions\n// ================================\n\ninterface TopicCtx extends Query {\n topic?: string;\n getTopic?(): string;\n _topicAdapter?: TopicQueryAdapter;\n}\n\ninterface TopicState {\n status: 'pending' | 'fulfilled' | 'rejected';\n promise?: Promise<unknown>;\n resolve?: (data: unknown) => void;\n reject?: (error: unknown) => void;\n data?: unknown;\n error?: unknown;\n}\n\nexport abstract class TopicQueryAdapter extends QueryAdapter {\n private _topics = new Map<string, TopicState>();\n\n /**\n * Called when a query activates for a given topic.\n * Implementations should start delivering data for this topic,\n * calling `fulfillTopic()` when initial data is available and\n * `sendMutationEvent()` for ongoing updates.\n */\n abstract subscribe(topic: string): void;\n\n /**\n * Called when the query deactivates. Implementations should\n * tear down any resources for this topic.\n */\n abstract unsubscribe(topic: string): void;\n\n /**\n * Resolve the pending promise for a topic with initial data.\n * Can be called before `send()` — the data will be picked up\n * when the query activates.\n */\n protected fulfillTopic(topic: string, data: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'fulfilled', data });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'fulfilled';\n state.data = data;\n state.resolve!(data);\n }\n }\n\n /**\n * Reject the pending promise for a topic.\n * Can be called before `send()` — the error will be propagated\n * when the query activates.\n */\n protected rejectTopic(topic: string, error: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'rejected', error });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'rejected';\n state.error = error;\n state.reject!(error);\n }\n }\n\n /**\n * Clears internal state for a topic. Called automatically by\n * `unsubscribe` — subclasses generally don't need to call this.\n */\n protected clearTopic(topic: string): void {\n this._topics.delete(topic);\n }\n\n protected clearAll(): void {\n this._topics.clear();\n }\n\n override async send(ctx: Query, _signal: AbortSignal): Promise<unknown> {\n const topicCtx = ctx as TopicCtx;\n topicCtx._topicAdapter = this;\n const topic = topicCtx.getTopic ? topicCtx.getTopic() : topicCtx.topic;\n\n if (topic === undefined) {\n throw new Error('TopicQuery requires a topic. Define `topic` as a field or override `getTopic()`.');\n }\n\n const existing = this._topics.get(topic);\n\n if (existing) {\n switch (existing.status) {\n case 'fulfilled':\n return existing.data;\n case 'rejected':\n throw existing.error;\n case 'pending':\n return existing.promise;\n }\n }\n\n // No state yet — create a deferred and subscribe\n let resolve!: (data: unknown) => void;\n let reject!: (error: unknown) => void;\n const promise = new Promise<unknown>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n this._topics.set(topic, { status: 'pending', promise, resolve, reject });\n this.subscribe(topic);\n\n return promise;\n }\n\n /**\n * Convenience wrapper — pushes a mutation event through the QueryClient\n * so that entities and live collections are updated reactively.\n */\n protected sendMutationEvent(event: MutationEvent): void {\n this.queryClient!.applyMutationEvent(event);\n }\n}\n","import { Query } from '../query.js';\nimport { TopicQueryAdapter } from './TopicQueryAdapter.js';\nimport type { QueryAdapterClass } from '../QueryAdapter.js';\nimport type { QueryConfigOptions } from '../query-types.js';\n\n// ================================\n// TopicQuery — declarative topic-based query definition\n// ================================\n\nexport abstract class TopicQuery extends Query {\n // Explicit type lets subclasses override with adapters that take constructor args.\n static override adapter: QueryAdapterClass<TopicQueryAdapter> = TopicQueryAdapter;\n\n topic?: string;\n\n // User-overridable getter — the adapter reads this from the execution context.\n getTopic?(): string;\n\n getIdentityKey(): string {\n return `topic:${this.topic ?? ''}`;\n }\n\n getConfig(): QueryConfigOptions {\n return {\n staleTime: 0,\n subscribe: () => {\n return () => {\n const adapter = (this as Record<string, any>)._topicAdapter as TopicQueryAdapter | undefined;\n const topic = this.getTopic ? this.getTopic() : this.topic;\n if (topic !== undefined) {\n adapter?.unsubscribe(topic);\n }\n };\n },\n };\n }\n}\n"],"names":["TopicQueryAdapter","QueryAdapter","topic","data","state","error","ctx","_signal","topicCtx","existing","resolve","reject","promise","res","rej","event","TopicQuery","Query","adapter"],"mappings":";;AAuBO,MAAeA,UAA0BC,EAAa;AAAA,EACnD,8BAAc,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBZ,aAAaC,GAAeC,GAAqB;AACzD,UAAMC,IAAQ,KAAK,QAAQ,IAAIF,CAAK;AAEpC,QAAIE,MAAU,QAAW;AACvB,WAAK,QAAQ,IAAIF,GAAO,EAAE,QAAQ,aAAa,MAAAC,GAAM;AACrD;AAAA,IACF;AAEA,IAAIC,EAAM,WAAW,cACnBA,EAAM,SAAS,aACfA,EAAM,OAAOD,GACbC,EAAM,QAASD,CAAI;AAAA,EAEvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOU,YAAYD,GAAeG,GAAsB;AACzD,UAAMD,IAAQ,KAAK,QAAQ,IAAIF,CAAK;AAEpC,QAAIE,MAAU,QAAW;AACvB,WAAK,QAAQ,IAAIF,GAAO,EAAE,QAAQ,YAAY,OAAAG,GAAO;AACrD;AAAA,IACF;AAEA,IAAID,EAAM,WAAW,cACnBA,EAAM,SAAS,YACfA,EAAM,QAAQC,GACdD,EAAM,OAAQC,CAAK;AAAA,EAEvB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMU,WAAWH,GAAqB;AACxC,SAAK,QAAQ,OAAOA,CAAK;AAAA,EAC3B;AAAA,EAEU,WAAiB;AACzB,SAAK,QAAQ,MAAA;AAAA,EACf;AAAA,EAEA,MAAe,KAAKI,GAAYC,GAAwC;AACtE,UAAMC,IAAWF;AACjB,IAAAE,EAAS,gBAAgB;AACzB,UAAMN,IAAQM,EAAS,WAAWA,EAAS,SAAA,IAAaA,EAAS;AAEjE,QAAIN,MAAU;AACZ,YAAM,IAAI,MAAM,kFAAkF;AAGpG,UAAMO,IAAW,KAAK,QAAQ,IAAIP,CAAK;AAEvC,QAAIO;AACF,cAAQA,EAAS,QAAA;AAAA,QACf,KAAK;AACH,iBAAOA,EAAS;AAAA,QAClB,KAAK;AACH,gBAAMA,EAAS;AAAA,QACjB,KAAK;AACH,iBAAOA,EAAS;AAAA,MAAA;AAKtB,QAAIC,GACAC;AACJ,UAAMC,IAAU,IAAI,QAAiB,CAACC,GAAKC,MAAQ;AACjD,MAAAJ,IAAUG,GACVF,IAASG;AAAA,IACX,CAAC;AAED,gBAAK,QAAQ,IAAIZ,GAAO,EAAE,QAAQ,WAAW,SAAAU,GAAS,SAAAF,GAAS,QAAAC,GAAQ,GACvE,KAAK,UAAUT,CAAK,GAEbU;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMU,kBAAkBG,GAA4B;AACtD,SAAK,YAAa,mBAAmBA,CAAK;AAAA,EAC5C;AACF;AC9HO,MAAeC,UAAmBC,EAAM;AAAA;AAAA,EAE7C,OAAgB,UAAgDjB;AAAA,EAEhE;AAAA,EAKA,iBAAyB;AACvB,WAAO,SAAS,KAAK,SAAS,EAAE;AAAA,EAClC;AAAA,EAEA,YAAgC;AAC9B,WAAO;AAAA,MACL,WAAW;AAAA,MACX,WAAW,MACF,MAAM;AACX,cAAMkB,IAAW,KAA6B,eACxChB,IAAQ,KAAK,WAAW,KAAK,SAAA,IAAa,KAAK;AACrD,QAAIA,MAAU,UACZgB,GAAS,YAAYhB,CAAK;AAAA,MAE9B;AAAA,IACF;AAAA,EAEJ;AACF;"}
|
|
@@ -7,26 +7,26 @@ class f extends d {
|
|
|
7
7
|
* Can be called before `send()` — the data will be picked up
|
|
8
8
|
* when the query activates.
|
|
9
9
|
*/
|
|
10
|
-
fulfillTopic(e,
|
|
10
|
+
fulfillTopic(e, i) {
|
|
11
11
|
const t = this._topics.get(e);
|
|
12
12
|
if (t === void 0) {
|
|
13
|
-
this._topics.set(e, { status: "fulfilled", data:
|
|
13
|
+
this._topics.set(e, { status: "fulfilled", data: i });
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
|
-
t.status === "pending" && (t.status = "fulfilled", t.data =
|
|
16
|
+
t.status === "pending" && (t.status = "fulfilled", t.data = i, t.resolve(i));
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* Reject the pending promise for a topic.
|
|
20
20
|
* Can be called before `send()` — the error will be propagated
|
|
21
21
|
* when the query activates.
|
|
22
22
|
*/
|
|
23
|
-
rejectTopic(e,
|
|
23
|
+
rejectTopic(e, i) {
|
|
24
24
|
const t = this._topics.get(e);
|
|
25
25
|
if (t === void 0) {
|
|
26
|
-
this._topics.set(e, { status: "rejected", error:
|
|
26
|
+
this._topics.set(e, { status: "rejected", error: i });
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
-
t.status === "pending" && (t.status = "rejected", t.error =
|
|
29
|
+
t.status === "pending" && (t.status = "rejected", t.error = i, t.reject(i));
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
32
|
* Clears internal state for a topic. Called automatically by
|
|
@@ -38,24 +38,27 @@ class f extends d {
|
|
|
38
38
|
clearAll() {
|
|
39
39
|
this._topics.clear();
|
|
40
40
|
}
|
|
41
|
-
async send(e,
|
|
41
|
+
async send(e, i) {
|
|
42
42
|
const t = e;
|
|
43
43
|
t._topicAdapter = this;
|
|
44
|
-
const
|
|
45
|
-
if (
|
|
46
|
-
|
|
44
|
+
const o = t.getTopic ? t.getTopic() : t.topic;
|
|
45
|
+
if (o === void 0)
|
|
46
|
+
throw new Error("TopicQuery requires a topic. Define `topic` as a field or override `getTopic()`.");
|
|
47
|
+
const s = this._topics.get(o);
|
|
48
|
+
if (s)
|
|
49
|
+
switch (s.status) {
|
|
47
50
|
case "fulfilled":
|
|
48
|
-
return
|
|
51
|
+
return s.data;
|
|
49
52
|
case "rejected":
|
|
50
|
-
throw
|
|
53
|
+
throw s.error;
|
|
51
54
|
case "pending":
|
|
52
|
-
return
|
|
55
|
+
return s.promise;
|
|
53
56
|
}
|
|
54
|
-
let
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
+
let r, c;
|
|
58
|
+
const p = new Promise((a, u) => {
|
|
59
|
+
r = a, c = u;
|
|
57
60
|
});
|
|
58
|
-
return this._topics.set(
|
|
61
|
+
return this._topics.set(o, { status: "pending", promise: p, resolve: r, reject: c }), this.subscribe(o), p;
|
|
59
62
|
}
|
|
60
63
|
/**
|
|
61
64
|
* Convenience wrapper — pushes a mutation event through the QueryClient
|
|
@@ -65,23 +68,25 @@ class f extends d {
|
|
|
65
68
|
this.queryClient.applyMutationEvent(e);
|
|
66
69
|
}
|
|
67
70
|
}
|
|
68
|
-
class
|
|
71
|
+
class T extends l {
|
|
69
72
|
// Explicit type lets subclasses override with adapters that take constructor args.
|
|
70
73
|
static adapter = f;
|
|
74
|
+
topic;
|
|
71
75
|
getIdentityKey() {
|
|
72
|
-
return `topic:${this.topic}`;
|
|
76
|
+
return `topic:${this.topic ?? ""}`;
|
|
73
77
|
}
|
|
74
78
|
getConfig() {
|
|
75
79
|
return {
|
|
76
80
|
staleTime: 0,
|
|
77
81
|
subscribe: () => () => {
|
|
78
|
-
this._topicAdapter
|
|
82
|
+
const e = this._topicAdapter, i = this.getTopic ? this.getTopic() : this.topic;
|
|
83
|
+
i !== void 0 && e?.unsubscribe(i);
|
|
79
84
|
}
|
|
80
85
|
};
|
|
81
86
|
}
|
|
82
87
|
}
|
|
83
88
|
export {
|
|
84
|
-
|
|
89
|
+
T as TopicQuery,
|
|
85
90
|
f as TopicQueryAdapter
|
|
86
91
|
};
|
|
87
92
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/topic/TopicQueryAdapter.ts","../../../../src/topic/TopicQuery.ts"],"sourcesContent":["import { QueryAdapter } from '../QueryAdapter.js';\nimport type { Query } from '../query.js';\nimport type { MutationEvent } from '../types.js';\n\n// ================================\n// TopicQueryAdapter — abstract adapter for topic-based subscriptions\n// ================================\n\ninterface TopicCtx extends Query {\n topic: string;\n _topicAdapter?: TopicQueryAdapter;\n}\n\ninterface TopicState {\n status: 'pending' | 'fulfilled' | 'rejected';\n promise?: Promise<unknown>;\n resolve?: (data: unknown) => void;\n reject?: (error: unknown) => void;\n data?: unknown;\n error?: unknown;\n}\n\nexport abstract class TopicQueryAdapter extends QueryAdapter {\n private _topics = new Map<string, TopicState>();\n\n /**\n * Called when a query activates for a given topic.\n * Implementations should start delivering data for this topic,\n * calling `fulfillTopic()` when initial data is available and\n * `sendMutationEvent()` for ongoing updates.\n */\n abstract subscribe(topic: string): void;\n\n /**\n * Called when the query deactivates. Implementations should\n * tear down any resources for this topic.\n */\n abstract unsubscribe(topic: string): void;\n\n /**\n * Resolve the pending promise for a topic with initial data.\n * Can be called before `send()` — the data will be picked up\n * when the query activates.\n */\n protected fulfillTopic(topic: string, data: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'fulfilled', data });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'fulfilled';\n state.data = data;\n state.resolve!(data);\n }\n }\n\n /**\n * Reject the pending promise for a topic.\n * Can be called before `send()` — the error will be propagated\n * when the query activates.\n */\n protected rejectTopic(topic: string, error: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'rejected', error });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'rejected';\n state.error = error;\n state.reject!(error);\n }\n }\n\n /**\n * Clears internal state for a topic. Called automatically by\n * `unsubscribe` — subclasses generally don't need to call this.\n */\n protected clearTopic(topic: string): void {\n this._topics.delete(topic);\n }\n\n protected clearAll(): void {\n this._topics.clear();\n }\n\n override async send(ctx: Query, _signal: AbortSignal): Promise<unknown> {\n const topicCtx = ctx as TopicCtx;\n topicCtx._topicAdapter = this;\n const topic = topicCtx.topic;\n\n const existing = this._topics.get(topic);\n\n if (existing) {\n switch (existing.status) {\n case 'fulfilled':\n return existing.data;\n case 'rejected':\n throw existing.error;\n case 'pending':\n return existing.promise;\n }\n }\n\n // No state yet — create a deferred and subscribe\n let resolve!: (data: unknown) => void;\n let reject!: (error: unknown) => void;\n const promise = new Promise<unknown>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n this._topics.set(topic, { status: 'pending', promise, resolve, reject });\n this.subscribe(topic);\n\n return promise;\n }\n\n /**\n * Convenience wrapper — pushes a mutation event through the QueryClient\n * so that entities and live collections are updated reactively.\n */\n protected sendMutationEvent(event: MutationEvent): void {\n this.queryClient!.applyMutationEvent(event);\n }\n}\n","import { Query } from '../query.js';\nimport { TopicQueryAdapter } from './TopicQueryAdapter.js';\nimport type { QueryAdapterClass } from '../QueryAdapter.js';\nimport type { QueryConfigOptions } from '../query-types.js';\n\n// ================================\n// TopicQuery — declarative topic-based query definition\n// ================================\n\nexport abstract class TopicQuery extends Query {\n // Explicit type lets subclasses override with adapters that take constructor args.\n static override adapter: QueryAdapterClass<TopicQueryAdapter> = TopicQueryAdapter;\n\n
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/topic/TopicQueryAdapter.ts","../../../../src/topic/TopicQuery.ts"],"sourcesContent":["import { QueryAdapter } from '../QueryAdapter.js';\nimport type { Query } from '../query.js';\nimport type { MutationEvent } from '../types.js';\n\n// ================================\n// TopicQueryAdapter — abstract adapter for topic-based subscriptions\n// ================================\n\ninterface TopicCtx extends Query {\n topic?: string;\n getTopic?(): string;\n _topicAdapter?: TopicQueryAdapter;\n}\n\ninterface TopicState {\n status: 'pending' | 'fulfilled' | 'rejected';\n promise?: Promise<unknown>;\n resolve?: (data: unknown) => void;\n reject?: (error: unknown) => void;\n data?: unknown;\n error?: unknown;\n}\n\nexport abstract class TopicQueryAdapter extends QueryAdapter {\n private _topics = new Map<string, TopicState>();\n\n /**\n * Called when a query activates for a given topic.\n * Implementations should start delivering data for this topic,\n * calling `fulfillTopic()` when initial data is available and\n * `sendMutationEvent()` for ongoing updates.\n */\n abstract subscribe(topic: string): void;\n\n /**\n * Called when the query deactivates. Implementations should\n * tear down any resources for this topic.\n */\n abstract unsubscribe(topic: string): void;\n\n /**\n * Resolve the pending promise for a topic with initial data.\n * Can be called before `send()` — the data will be picked up\n * when the query activates.\n */\n protected fulfillTopic(topic: string, data: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'fulfilled', data });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'fulfilled';\n state.data = data;\n state.resolve!(data);\n }\n }\n\n /**\n * Reject the pending promise for a topic.\n * Can be called before `send()` — the error will be propagated\n * when the query activates.\n */\n protected rejectTopic(topic: string, error: unknown): void {\n const state = this._topics.get(topic);\n\n if (state === undefined) {\n this._topics.set(topic, { status: 'rejected', error });\n return;\n }\n\n if (state.status === 'pending') {\n state.status = 'rejected';\n state.error = error;\n state.reject!(error);\n }\n }\n\n /**\n * Clears internal state for a topic. Called automatically by\n * `unsubscribe` — subclasses generally don't need to call this.\n */\n protected clearTopic(topic: string): void {\n this._topics.delete(topic);\n }\n\n protected clearAll(): void {\n this._topics.clear();\n }\n\n override async send(ctx: Query, _signal: AbortSignal): Promise<unknown> {\n const topicCtx = ctx as TopicCtx;\n topicCtx._topicAdapter = this;\n const topic = topicCtx.getTopic ? topicCtx.getTopic() : topicCtx.topic;\n\n if (topic === undefined) {\n throw new Error('TopicQuery requires a topic. Define `topic` as a field or override `getTopic()`.');\n }\n\n const existing = this._topics.get(topic);\n\n if (existing) {\n switch (existing.status) {\n case 'fulfilled':\n return existing.data;\n case 'rejected':\n throw existing.error;\n case 'pending':\n return existing.promise;\n }\n }\n\n // No state yet — create a deferred and subscribe\n let resolve!: (data: unknown) => void;\n let reject!: (error: unknown) => void;\n const promise = new Promise<unknown>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n this._topics.set(topic, { status: 'pending', promise, resolve, reject });\n this.subscribe(topic);\n\n return promise;\n }\n\n /**\n * Convenience wrapper — pushes a mutation event through the QueryClient\n * so that entities and live collections are updated reactively.\n */\n protected sendMutationEvent(event: MutationEvent): void {\n this.queryClient!.applyMutationEvent(event);\n }\n}\n","import { Query } from '../query.js';\nimport { TopicQueryAdapter } from './TopicQueryAdapter.js';\nimport type { QueryAdapterClass } from '../QueryAdapter.js';\nimport type { QueryConfigOptions } from '../query-types.js';\n\n// ================================\n// TopicQuery — declarative topic-based query definition\n// ================================\n\nexport abstract class TopicQuery extends Query {\n // Explicit type lets subclasses override with adapters that take constructor args.\n static override adapter: QueryAdapterClass<TopicQueryAdapter> = TopicQueryAdapter;\n\n topic?: string;\n\n // User-overridable getter — the adapter reads this from the execution context.\n getTopic?(): string;\n\n getIdentityKey(): string {\n return `topic:${this.topic ?? ''}`;\n }\n\n getConfig(): QueryConfigOptions {\n return {\n staleTime: 0,\n subscribe: () => {\n return () => {\n const adapter = (this as Record<string, any>)._topicAdapter as TopicQueryAdapter | undefined;\n const topic = this.getTopic ? this.getTopic() : this.topic;\n if (topic !== undefined) {\n adapter?.unsubscribe(topic);\n }\n };\n },\n };\n }\n}\n"],"names":["TopicQueryAdapter","QueryAdapter","topic","data","state","error","ctx","_signal","topicCtx","existing","resolve","reject","promise","res","rej","event","TopicQuery","Query","adapter"],"mappings":";;AAuBO,MAAeA,UAA0BC,EAAa;AAAA,EACnD,8BAAc,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBZ,aAAaC,GAAeC,GAAqB;AACzD,UAAMC,IAAQ,KAAK,QAAQ,IAAIF,CAAK;AAEpC,QAAIE,MAAU,QAAW;AACvB,WAAK,QAAQ,IAAIF,GAAO,EAAE,QAAQ,aAAa,MAAAC,GAAM;AACrD;AAAA,IACF;AAEA,IAAIC,EAAM,WAAW,cACnBA,EAAM,SAAS,aACfA,EAAM,OAAOD,GACbC,EAAM,QAASD,CAAI;AAAA,EAEvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOU,YAAYD,GAAeG,GAAsB;AACzD,UAAMD,IAAQ,KAAK,QAAQ,IAAIF,CAAK;AAEpC,QAAIE,MAAU,QAAW;AACvB,WAAK,QAAQ,IAAIF,GAAO,EAAE,QAAQ,YAAY,OAAAG,GAAO;AACrD;AAAA,IACF;AAEA,IAAID,EAAM,WAAW,cACnBA,EAAM,SAAS,YACfA,EAAM,QAAQC,GACdD,EAAM,OAAQC,CAAK;AAAA,EAEvB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMU,WAAWH,GAAqB;AACxC,SAAK,QAAQ,OAAOA,CAAK;AAAA,EAC3B;AAAA,EAEU,WAAiB;AACzB,SAAK,QAAQ,MAAA;AAAA,EACf;AAAA,EAEA,MAAe,KAAKI,GAAYC,GAAwC;AACtE,UAAMC,IAAWF;AACjB,IAAAE,EAAS,gBAAgB;AACzB,UAAMN,IAAQM,EAAS,WAAWA,EAAS,SAAA,IAAaA,EAAS;AAEjE,QAAIN,MAAU;AACZ,YAAM,IAAI,MAAM,kFAAkF;AAGpG,UAAMO,IAAW,KAAK,QAAQ,IAAIP,CAAK;AAEvC,QAAIO;AACF,cAAQA,EAAS,QAAA;AAAA,QACf,KAAK;AACH,iBAAOA,EAAS;AAAA,QAClB,KAAK;AACH,gBAAMA,EAAS;AAAA,QACjB,KAAK;AACH,iBAAOA,EAAS;AAAA,MAAA;AAKtB,QAAIC,GACAC;AACJ,UAAMC,IAAU,IAAI,QAAiB,CAACC,GAAKC,MAAQ;AACjD,MAAAJ,IAAUG,GACVF,IAASG;AAAA,IACX,CAAC;AAED,gBAAK,QAAQ,IAAIZ,GAAO,EAAE,QAAQ,WAAW,SAAAU,GAAS,SAAAF,GAAS,QAAAC,GAAQ,GACvE,KAAK,UAAUT,CAAK,GAEbU;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMU,kBAAkBG,GAA4B;AACtD,SAAK,YAAa,mBAAmBA,CAAK;AAAA,EAC5C;AACF;AC9HO,MAAeC,UAAmBC,EAAM;AAAA;AAAA,EAE7C,OAAgB,UAAgDjB;AAAA,EAEhE;AAAA,EAKA,iBAAyB;AACvB,WAAO,SAAS,KAAK,SAAS,EAAE;AAAA,EAClC;AAAA,EAEA,YAAgC;AAC9B,WAAO;AAAA,MACL,WAAW;AAAA,MACX,WAAW,MACF,MAAM;AACX,cAAMkB,IAAW,KAA6B,eACxChB,IAAQ,KAAK,WAAW,KAAK,SAAA,IAAa,KAAK;AACrD,QAAIA,MAAU,UACZgB,GAAS,YAAYhB,CAAK;AAAA,MAE9B;AAAA,IACF;AAAA,EAEJ;AACF;"}
|
|
@@ -4,7 +4,8 @@ import type { QueryAdapterClass } from '../QueryAdapter.js';
|
|
|
4
4
|
import type { QueryConfigOptions } from '../query-types.js';
|
|
5
5
|
export declare abstract class TopicQuery extends Query {
|
|
6
6
|
static adapter: QueryAdapterClass<TopicQueryAdapter>;
|
|
7
|
-
|
|
7
|
+
topic?: string;
|
|
8
|
+
getTopic?(): string;
|
|
8
9
|
getIdentityKey(): string;
|
|
9
10
|
getConfig(): QueryConfigOptions;
|
|
10
11
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TopicQuery.d.ts","sourceRoot":"","sources":["../../../src/topic/TopicQuery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAM5D,8BAAsB,UAAW,SAAQ,KAAK;IAE5C,OAAgB,OAAO,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAqB;IAElF,
|
|
1
|
+
{"version":3,"file":"TopicQuery.d.ts","sourceRoot":"","sources":["../../../src/topic/TopicQuery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAM5D,8BAAsB,UAAW,SAAQ,KAAK;IAE5C,OAAgB,OAAO,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAqB;IAElF,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,QAAQ,CAAC,IAAI,MAAM;IAEnB,cAAc,IAAI,MAAM;IAIxB,SAAS,IAAI,kBAAkB;CAchC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TopicQueryAdapter.d.ts","sourceRoot":"","sources":["../../../src/topic/TopicQueryAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"TopicQueryAdapter.d.ts","sourceRoot":"","sources":["../../../src/topic/TopicQueryAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAqBjD,8BAAsB,iBAAkB,SAAQ,YAAY;IAC1D,OAAO,CAAC,OAAO,CAAiC;IAEhD;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAEvC;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAEzC;;;;OAIG;IACH,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI;IAe1D;;;;OAIG;IACH,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAe1D;;;OAGG;IACH,SAAS,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIzC,SAAS,CAAC,QAAQ,IAAI,IAAI;IAIX,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAoCvE;;;OAGG;IACH,SAAS,CAAC,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;CAGxD"}
|