fetchium 0.2.2 → 0.2.3

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cjs/development/{QueryClient-BZpqASy3.js → QueryClient-Ce5Mnumb.js} +2 -2
  3. package/dist/{esm/production/QueryClient-CS4iUKWj.js.map → cjs/development/QueryClient-Ce5Mnumb.js.map} +1 -1
  4. package/dist/cjs/development/index.js +1 -1
  5. package/dist/cjs/{production/mutation-CfFdNkYV.js → development/mutation-GI_gTQEB.js} +2 -2
  6. package/dist/cjs/{production/mutation-CfFdNkYV.js.map → development/mutation-GI_gTQEB.js.map} +1 -1
  7. package/dist/cjs/development/react/index.js +1 -1
  8. package/dist/cjs/development/rest/index.js +1 -1
  9. package/dist/cjs/development/topic/index.js +1 -1
  10. package/dist/cjs/development/topic/index.js.map +1 -1
  11. package/dist/cjs/production/{QueryClient-BmoHLlvu.js → QueryClient-BXGk-5PR.js} +2 -2
  12. package/dist/cjs/production/{QueryClient-BmoHLlvu.js.map → QueryClient-BXGk-5PR.js.map} +1 -1
  13. package/dist/cjs/production/index.js +1 -1
  14. package/dist/cjs/{development/mutation-COeBCn7p.js → production/mutation-Bleah98u.js} +2 -2
  15. package/dist/cjs/{development/mutation-COeBCn7p.js.map → production/mutation-Bleah98u.js.map} +1 -1
  16. package/dist/cjs/production/react/index.js +1 -1
  17. package/dist/cjs/production/rest/index.js +1 -1
  18. package/dist/cjs/production/topic/index.js +1 -1
  19. package/dist/cjs/production/topic/index.js.map +1 -1
  20. package/dist/esm/QueryClient.d.ts +15 -1
  21. package/dist/esm/QueryClient.d.ts.map +1 -1
  22. package/dist/esm/development/{QueryClient-4co72n4i.js → QueryClient-CmMSNSpt.js} +36 -12
  23. package/dist/esm/development/{QueryClient-4co72n4i.js.map → QueryClient-CmMSNSpt.js.map} +1 -1
  24. package/dist/esm/development/index.js +2 -2
  25. package/dist/esm/development/{mutation-BMAWDUP4.js → mutation-BAM3eYqd.js} +2 -2
  26. package/dist/esm/development/{mutation-BMAWDUP4.js.map → mutation-BAM3eYqd.js.map} +1 -1
  27. package/dist/esm/development/react/index.js +1 -1
  28. package/dist/esm/development/rest/index.js +2 -2
  29. package/dist/esm/development/topic/index.js +19 -18
  30. package/dist/esm/development/topic/index.js.map +1 -1
  31. package/dist/esm/production/{QueryClient-CS4iUKWj.js → QueryClient-3aWu_mJE.js} +31 -13
  32. package/dist/{cjs/development/QueryClient-BZpqASy3.js.map → esm/production/QueryClient-3aWu_mJE.js.map} +1 -1
  33. package/dist/esm/production/index.js +2 -2
  34. package/dist/esm/production/{mutation-B1EiA34B.js → mutation-YpiJLNWU.js} +2 -2
  35. package/dist/esm/production/{mutation-B1EiA34B.js.map → mutation-YpiJLNWU.js.map} +1 -1
  36. package/dist/esm/production/react/index.js +1 -1
  37. package/dist/esm/production/rest/index.js +2 -2
  38. package/dist/esm/production/topic/index.js +19 -18
  39. package/dist/esm/production/topic/index.js.map +1 -1
  40. package/dist/esm/topic/TopicQuery.d.ts +1 -1
  41. package/dist/esm/topic/TopicQuery.d.ts.map +1 -1
  42. package/package.json +1 -1
  43. package/plugin/docs/core/streaming.md +7 -9
@@ -1,6 +1,6 @@
1
- import { A as r, E as s, G as t, L as o, a as n, M as i, N as M, b as g, c as u, d as y, e as f, Q as C, f as N, g as Q, h as d, R as k, i as l, j as p, k as m, q as E, r as R, t as w } from "./QueryClient-CS4iUKWj.js";
1
+ import { A as r, E as s, G as t, L as o, a as n, M as i, N as M, b as g, c as u, d as y, e as f, Q as C, f as N, g as Q, h as d, R as k, i as l, j as p, k as m, q as E, r as R, t as w } from "./QueryClient-3aWu_mJE.js";
2
2
  import { Q as F } from "./QueryAdapter-Bu5UJjE4.js";
3
- import { M as A, g as K, m as Y } from "./mutation-B1EiA34B.js";
3
+ import { M as A, g as K, m as Y } from "./mutation-YpiJLNWU.js";
4
4
  export {
5
5
  r as ARRAY_KEY,
6
6
  s as Entity,
@@ -1,5 +1,5 @@
1
1
  import { getContext as h } from "signalium";
2
- import { l as y, h as D, m as w, V as u, t as d } from "./QueryClient-CS4iUKWj.js";
2
+ import { l as y, h as D, m as w, V as u, t as d } from "./QueryClient-3aWu_mJE.js";
3
3
  class x {
4
4
  static adapter;
5
5
  params;
@@ -55,4 +55,4 @@ export {
55
55
  S as g,
56
56
  C as m
57
57
  };
58
- //# sourceMappingURL=mutation-B1EiA34B.js.map
58
+ //# sourceMappingURL=mutation-YpiJLNWU.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mutation-B1EiA34B.js","sources":["../../../src/mutation.ts"],"sourcesContent":["import { getContext, ReactiveTask } from 'signalium';\nimport { ExtractType, InternalTypeDef, MutationEffects, TypeDef, RetryConfig, TypeDefShape } from './types.js';\nimport { QueryClientContext, type QueryContext } from './QueryClient.js';\nimport { ValidatorDef, t } from './typeDefs.js';\nimport { createDefinitionProxy, extractDefinition, type CapturedDefinition } from './fieldRef.js';\nimport type { QueryAdapter, QueryAdapterClass } from './QueryAdapter.js';\n\n// ================================\n// Mutation Definition Types\n// ================================\n\nexport interface MutationConfigOptions {\n retry?: RetryConfig | number | false;\n}\n\nexport interface MutationDefinition<Request, Response> {\n id: string;\n requestShape: InternalTypeDef;\n responseShape: InternalTypeDef | undefined;\n captured: CapturedDefinition<Mutation>;\n optimisticUpdates: boolean;\n config?: MutationConfigOptions;\n effects?: MutationEffects;\n hasGetEffects: boolean;\n adapterClass: QueryAdapterClass;\n}\n\n// ================================\n// Mutation base class\n// ================================\n\nexport abstract class Mutation {\n static adapter?: QueryAdapterClass;\n\n readonly params?: TypeDefShape;\n readonly result?: TypeDefShape;\n readonly optimisticUpdates?: boolean;\n readonly config?: MutationConfigOptions;\n readonly effects?: Readonly<MutationEffects>;\n\n declare context: QueryContext;\n\n abstract getIdentityKey(): unknown;\n\n getEffects?(): MutationEffects;\n\n constructor() {\n return createDefinitionProxy(this);\n }\n}\n\n// ================================\n// Mutation definition cache and lookup\n// ================================\n\nconst mutationDefCache = new WeakMap<new () => Mutation, () => MutationDefinition<any, any>>();\n\nexport const mutationKeyForClass = (cls: new () => Mutation): string => {\n const getMutationDef = mutationDefCache.get(cls);\n\n if (getMutationDef === undefined) {\n throw new Error('Mutation definition not found');\n }\n\n return getMutationDef().id;\n};\n\n// ================================\n// Internal: build mutation definition from class\n// ================================\n\nfunction buildMutationDefinition(MutationClass: new () => Mutation): () => MutationDefinition<any, any> {\n let cached = mutationDefCache.get(MutationClass);\n\n if (cached !== undefined) {\n return cached;\n }\n\n let mutationDefinition: MutationDefinition<any, any> | undefined;\n\n const getter = (): MutationDefinition<any, any> => {\n if (mutationDefinition !== undefined) {\n return mutationDefinition;\n }\n\n const instance = new MutationClass();\n const captured = extractDefinition(instance);\n const { fields } = captured;\n\n const id = `mutation:${String(captured.methods.getIdentityKey.call(fields))}`;\n\n const requestDef = fields.params ?? {};\n const requestShape = (requestDef instanceof ValidatorDef\n ? requestDef\n : t.object(requestDef)) as unknown as InternalTypeDef;\n const responseDef = fields.result;\n const responseShape =\n responseDef !== undefined\n ? ((responseDef instanceof ValidatorDef ? responseDef : t.object(responseDef)) as unknown as InternalTypeDef)\n : undefined;\n\n const adapterClass = (MutationClass as typeof Mutation).adapter;\n if (!adapterClass) {\n throw new Error(\n `Mutation class \"${MutationClass.name}\" must define a static \\`adapter\\` property. ` +\n `Extend RESTMutation (from fetchium/rest) or set \\`static adapter = MyAdapter\\` on your mutation class.`,\n );\n }\n\n mutationDefinition = {\n id,\n requestShape,\n responseShape,\n captured,\n optimisticUpdates: fields.optimisticUpdates ?? false,\n config: fields.config,\n effects: fields.effects,\n hasGetEffects: typeof captured.methods.getEffects === 'function',\n adapterClass,\n };\n\n return mutationDefinition;\n };\n\n mutationDefCache.set(MutationClass, getter);\n return getter;\n}\n\n// ================================\n// Public API\n// ================================\n\nexport function getMutation<T extends Mutation>(\n MutationClass: new () => T,\n): ReactiveTask<Readonly<ExtractType<T['result']>>, [ExtractType<T['params']>]> {\n const getMutationDef = buildMutationDefinition(MutationClass);\n\n const queryClient = getContext(QueryClientContext);\n\n if (queryClient === undefined) {\n throw new Error('QueryClient not found');\n }\n\n return queryClient.getMutation<any, any>(getMutationDef());\n}\n"],"names":["Mutation","createDefinitionProxy","mutationDefCache","mutationKeyForClass","cls","getMutationDef","buildMutationDefinition","MutationClass","cached","mutationDefinition","getter","instance","captured","extractDefinition","fields","id","requestDef","requestShape","ValidatorDef","t","responseDef","responseShape","adapterClass","getMutation","queryClient","getContext","QueryClientContext"],"mappings":";;AA+BO,MAAeA,EAAS;AAAA,EAC7B,OAAO;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAQT,cAAc;AACZ,WAAOC,EAAsB,IAAI;AAAA,EACnC;AACF;AAMA,MAAMC,wBAAuB,QAAA,GAEhBC,IAAsB,CAACC,MAAoC;AACtE,QAAMC,IAAiBH,EAAiB,IAAIE,CAAG;AAE/C,MAAIC,MAAmB;AACrB,UAAM,IAAI,MAAM,+BAA+B;AAGjD,SAAOA,IAAiB;AAC1B;AAMA,SAASC,EAAwBC,GAAuE;AACtG,MAAIC,IAASN,EAAiB,IAAIK,CAAa;AAE/C,MAAIC,MAAW;AACb,WAAOA;AAGT,MAAIC;AAEJ,QAAMC,IAAS,MAAoC;AACjD,QAAID,MAAuB;AACzB,aAAOA;AAGT,UAAME,IAAW,IAAIJ,EAAA,GACfK,IAAWC,EAAkBF,CAAQ,GACrC,EAAE,QAAAG,MAAWF,GAEbG,IAAK,YAAY,OAAOH,EAAS,QAAQ,eAAe,KAAKE,CAAM,CAAC,CAAC,IAErEE,IAAaF,EAAO,UAAU,CAAA,GAC9BG,IAAgBD,aAAsBE,IACxCF,IACAG,EAAE,OAAOH,CAAU,GACjBI,IAAcN,EAAO,QACrBO,IACJD,MAAgB,SACVA,aAAuBF,IAAeE,IAAcD,EAAE,OAAOC,CAAW,IAC1E,QAEAE,IAAgBf,EAAkC;AACxD,QAAI,CAACe;AACH,YAAM,IAAI;AAAA,QACR,mBAAmBf,EAAc,IAAI;AAAA,MAAA;AAKzC,WAAAE,IAAqB;AAAA,MACnB,IAAAM;AAAA,MACA,cAAAE;AAAA,MACA,eAAAI;AAAA,MACA,UAAAT;AAAA,MACA,mBAAmBE,EAAO,qBAAqB;AAAA,MAC/C,QAAQA,EAAO;AAAA,MACf,SAASA,EAAO;AAAA,MAChB,eAAe,OAAOF,EAAS,QAAQ,cAAe;AAAA,MACtD,cAAAU;AAAA,IAAA,GAGKb;AAAA,EACT;AAEA,SAAAP,EAAiB,IAAIK,GAAeG,CAAM,GACnCA;AACT;AAMO,SAASa,EACdhB,GAC8E;AAC9E,QAAMF,IAAiBC,EAAwBC,CAAa,GAEtDiB,IAAcC,EAAWC,CAAkB;AAEjD,MAAIF,MAAgB;AAClB,UAAM,IAAI,MAAM,uBAAuB;AAGzC,SAAOA,EAAY,YAAsBnB,GAAgB;AAC3D;"}
1
+ {"version":3,"file":"mutation-YpiJLNWU.js","sources":["../../../src/mutation.ts"],"sourcesContent":["import { getContext, ReactiveTask } from 'signalium';\nimport { ExtractType, InternalTypeDef, MutationEffects, TypeDef, RetryConfig, TypeDefShape } from './types.js';\nimport { QueryClientContext, type QueryContext } from './QueryClient.js';\nimport { ValidatorDef, t } from './typeDefs.js';\nimport { createDefinitionProxy, extractDefinition, type CapturedDefinition } from './fieldRef.js';\nimport type { QueryAdapter, QueryAdapterClass } from './QueryAdapter.js';\n\n// ================================\n// Mutation Definition Types\n// ================================\n\nexport interface MutationConfigOptions {\n retry?: RetryConfig | number | false;\n}\n\nexport interface MutationDefinition<Request, Response> {\n id: string;\n requestShape: InternalTypeDef;\n responseShape: InternalTypeDef | undefined;\n captured: CapturedDefinition<Mutation>;\n optimisticUpdates: boolean;\n config?: MutationConfigOptions;\n effects?: MutationEffects;\n hasGetEffects: boolean;\n adapterClass: QueryAdapterClass;\n}\n\n// ================================\n// Mutation base class\n// ================================\n\nexport abstract class Mutation {\n static adapter?: QueryAdapterClass;\n\n readonly params?: TypeDefShape;\n readonly result?: TypeDefShape;\n readonly optimisticUpdates?: boolean;\n readonly config?: MutationConfigOptions;\n readonly effects?: Readonly<MutationEffects>;\n\n declare context: QueryContext;\n\n abstract getIdentityKey(): unknown;\n\n getEffects?(): MutationEffects;\n\n constructor() {\n return createDefinitionProxy(this);\n }\n}\n\n// ================================\n// Mutation definition cache and lookup\n// ================================\n\nconst mutationDefCache = new WeakMap<new () => Mutation, () => MutationDefinition<any, any>>();\n\nexport const mutationKeyForClass = (cls: new () => Mutation): string => {\n const getMutationDef = mutationDefCache.get(cls);\n\n if (getMutationDef === undefined) {\n throw new Error('Mutation definition not found');\n }\n\n return getMutationDef().id;\n};\n\n// ================================\n// Internal: build mutation definition from class\n// ================================\n\nfunction buildMutationDefinition(MutationClass: new () => Mutation): () => MutationDefinition<any, any> {\n let cached = mutationDefCache.get(MutationClass);\n\n if (cached !== undefined) {\n return cached;\n }\n\n let mutationDefinition: MutationDefinition<any, any> | undefined;\n\n const getter = (): MutationDefinition<any, any> => {\n if (mutationDefinition !== undefined) {\n return mutationDefinition;\n }\n\n const instance = new MutationClass();\n const captured = extractDefinition(instance);\n const { fields } = captured;\n\n const id = `mutation:${String(captured.methods.getIdentityKey.call(fields))}`;\n\n const requestDef = fields.params ?? {};\n const requestShape = (requestDef instanceof ValidatorDef\n ? requestDef\n : t.object(requestDef)) as unknown as InternalTypeDef;\n const responseDef = fields.result;\n const responseShape =\n responseDef !== undefined\n ? ((responseDef instanceof ValidatorDef ? responseDef : t.object(responseDef)) as unknown as InternalTypeDef)\n : undefined;\n\n const adapterClass = (MutationClass as typeof Mutation).adapter;\n if (!adapterClass) {\n throw new Error(\n `Mutation class \"${MutationClass.name}\" must define a static \\`adapter\\` property. ` +\n `Extend RESTMutation (from fetchium/rest) or set \\`static adapter = MyAdapter\\` on your mutation class.`,\n );\n }\n\n mutationDefinition = {\n id,\n requestShape,\n responseShape,\n captured,\n optimisticUpdates: fields.optimisticUpdates ?? false,\n config: fields.config,\n effects: fields.effects,\n hasGetEffects: typeof captured.methods.getEffects === 'function',\n adapterClass,\n };\n\n return mutationDefinition;\n };\n\n mutationDefCache.set(MutationClass, getter);\n return getter;\n}\n\n// ================================\n// Public API\n// ================================\n\nexport function getMutation<T extends Mutation>(\n MutationClass: new () => T,\n): ReactiveTask<Readonly<ExtractType<T['result']>>, [ExtractType<T['params']>]> {\n const getMutationDef = buildMutationDefinition(MutationClass);\n\n const queryClient = getContext(QueryClientContext);\n\n if (queryClient === undefined) {\n throw new Error('QueryClient not found');\n }\n\n return queryClient.getMutation<any, any>(getMutationDef());\n}\n"],"names":["Mutation","createDefinitionProxy","mutationDefCache","mutationKeyForClass","cls","getMutationDef","buildMutationDefinition","MutationClass","cached","mutationDefinition","getter","instance","captured","extractDefinition","fields","id","requestDef","requestShape","ValidatorDef","t","responseDef","responseShape","adapterClass","getMutation","queryClient","getContext","QueryClientContext"],"mappings":";;AA+BO,MAAeA,EAAS;AAAA,EAC7B,OAAO;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAQT,cAAc;AACZ,WAAOC,EAAsB,IAAI;AAAA,EACnC;AACF;AAMA,MAAMC,wBAAuB,QAAA,GAEhBC,IAAsB,CAACC,MAAoC;AACtE,QAAMC,IAAiBH,EAAiB,IAAIE,CAAG;AAE/C,MAAIC,MAAmB;AACrB,UAAM,IAAI,MAAM,+BAA+B;AAGjD,SAAOA,IAAiB;AAC1B;AAMA,SAASC,EAAwBC,GAAuE;AACtG,MAAIC,IAASN,EAAiB,IAAIK,CAAa;AAE/C,MAAIC,MAAW;AACb,WAAOA;AAGT,MAAIC;AAEJ,QAAMC,IAAS,MAAoC;AACjD,QAAID,MAAuB;AACzB,aAAOA;AAGT,UAAME,IAAW,IAAIJ,EAAA,GACfK,IAAWC,EAAkBF,CAAQ,GACrC,EAAE,QAAAG,MAAWF,GAEbG,IAAK,YAAY,OAAOH,EAAS,QAAQ,eAAe,KAAKE,CAAM,CAAC,CAAC,IAErEE,IAAaF,EAAO,UAAU,CAAA,GAC9BG,IAAgBD,aAAsBE,IACxCF,IACAG,EAAE,OAAOH,CAAU,GACjBI,IAAcN,EAAO,QACrBO,IACJD,MAAgB,SACVA,aAAuBF,IAAeE,IAAcD,EAAE,OAAOC,CAAW,IAC1E,QAEAE,IAAgBf,EAAkC;AACxD,QAAI,CAACe;AACH,YAAM,IAAI;AAAA,QACR,mBAAmBf,EAAc,IAAI;AAAA,MAAA;AAKzC,WAAAE,IAAqB;AAAA,MACnB,IAAAM;AAAA,MACA,cAAAE;AAAA,MACA,eAAAI;AAAA,MACA,UAAAT;AAAA,MACA,mBAAmBE,EAAO,qBAAqB;AAAA,MAC/C,QAAQA,EAAO;AAAA,MACf,SAASA,EAAO;AAAA,MAChB,eAAe,OAAOF,EAAS,QAAQ,cAAe;AAAA,MACtD,cAAAU;AAAA,IAAA,GAGKb;AAAA,EACT;AAEA,SAAAP,EAAiB,IAAIK,GAAeG,CAAM,GACnCA;AACT;AAMO,SAASa,EACdhB,GAC8E;AAC9E,QAAMF,IAAiBC,EAAwBC,CAAa,GAEtDiB,IAAcC,EAAWC,CAAkB;AAEjD,MAAIF,MAAgB;AAClB,UAAM,IAAI,MAAM,uBAAuB;AAGzC,SAAOA,EAAY,YAAsBnB,GAAgB;AAC3D;"}
@@ -1,6 +1,6 @@
1
1
  import { useReactive as i } from "signalium/react";
2
2
  import { reactive as f } from "signalium";
3
- import { k as u } from "../QueryClient-CS4iUKWj.js";
3
+ import { k as u } from "../QueryClient-3aWu_mJE.js";
4
4
  function n(e) {
5
5
  if (Array.isArray(e))
6
6
  return e.map(n);
@@ -1,6 +1,6 @@
1
- import { n as v, o as U, f as O } from "../QueryClient-CS4iUKWj.js";
1
+ import { n as v, o as U, f as O } from "../QueryClient-3aWu_mJE.js";
2
2
  import { Q as T } from "../QueryAdapter-Bu5UJjE4.js";
3
- import { M as w } from "../mutation-B1EiA34B.js";
3
+ import { M as w } from "../mutation-YpiJLNWU.js";
4
4
  class P extends T {
5
5
  _fetch;
6
6
  _baseUrl;
@@ -1,20 +1,6 @@
1
- import { f as l } from "../QueryClient-CS4iUKWj.js";
1
+ import { f as l } from "../QueryClient-3aWu_mJE.js";
2
2
  import { Q as d } from "../QueryAdapter-Bu5UJjE4.js";
3
- class g extends l {
4
- static adapter;
5
- getIdentityKey() {
6
- return `topic:${this.topic}`;
7
- }
8
- getConfig() {
9
- return {
10
- staleTime: 0,
11
- subscribe: () => () => {
12
- this._topicAdapter?.unsubscribe(this.topic);
13
- }
14
- };
15
- }
16
- }
17
- class _ extends d {
3
+ class f extends d {
18
4
  _topics = /* @__PURE__ */ new Map();
19
5
  /**
20
6
  * Resolve the pending promise for a topic with initial data.
@@ -79,8 +65,23 @@ class _ extends d {
79
65
  this.queryClient.applyMutationEvent(e);
80
66
  }
81
67
  }
68
+ class _ extends l {
69
+ // Explicit type lets subclasses override with adapters that take constructor args.
70
+ static adapter = f;
71
+ getIdentityKey() {
72
+ return `topic:${this.topic}`;
73
+ }
74
+ getConfig() {
75
+ return {
76
+ staleTime: 0,
77
+ subscribe: () => () => {
78
+ this._topicAdapter?.unsubscribe(this.topic);
79
+ }
80
+ };
81
+ }
82
+ }
82
83
  export {
83
- g as TopicQuery,
84
- _ as TopicQueryAdapter
84
+ _ as TopicQuery,
85
+ f as TopicQueryAdapter
85
86
  };
86
87
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/topic/TopicQuery.ts","../../../../src/topic/TopicQueryAdapter.ts"],"sourcesContent":["import { Query } from '../query.js';\nimport type { 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 static override adapter: QueryAdapterClass<TopicQueryAdapter>;\n\n abstract topic: 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 adapter?.unsubscribe(this.topic);\n };\n },\n };\n }\n}\n","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"],"names":["TopicQuery","Query","TopicQueryAdapter","QueryAdapter","topic","data","state","error","ctx","_signal","topicCtx","existing","resolve","reject","promise","res","rej","event"],"mappings":";;AASO,MAAeA,UAAmBC,EAAM;AAAA,EAC7C,OAAgB;AAAA,EAIhB,iBAAyB;AACvB,WAAO,SAAS,KAAK,KAAK;AAAA,EAC5B;AAAA,EAEA,YAAgC;AAC9B,WAAO;AAAA,MACL,WAAW;AAAA,MACX,WAAW,MACF,MAAM;AAEX,QADiB,KAA6B,eACrC,YAAY,KAAK,KAAK;AAAA,MACjC;AAAA,IACF;AAAA,EAEJ;AACF;ACPO,MAAeC,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,OAEjBC,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;"}
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 abstract topic: 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 adapter?.unsubscribe(this.topic);\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"],"mappings":";;AAsBO,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,OAEjBC,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;ACzHO,MAAeC,UAAmBC,EAAM;AAAA;AAAA,EAE7C,OAAgB,UAAgDjB;AAAA,EAIhE,iBAAyB;AACvB,WAAO,SAAS,KAAK,KAAK;AAAA,EAC5B;AAAA,EAEA,YAAgC;AAC9B,WAAO;AAAA,MACL,WAAW;AAAA,MACX,WAAW,MACF,MAAM;AAEX,QADiB,KAA6B,eACrC,YAAY,KAAK,KAAK;AAAA,MACjC;AAAA,IACF;AAAA,EAEJ;AACF;"}
@@ -1,5 +1,5 @@
1
1
  import { Query } from '../query.js';
2
- import type { TopicQueryAdapter } from './TopicQueryAdapter.js';
2
+ import { TopicQueryAdapter } from './TopicQueryAdapter.js';
3
3
  import type { QueryAdapterClass } from '../QueryAdapter.js';
4
4
  import type { QueryConfigOptions } from '../query-types.js';
5
5
  export declare abstract class TopicQuery extends Query {
@@ -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,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAM5D,8BAAsB,UAAW,SAAQ,KAAK;IAC5C,OAAgB,OAAO,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAE9D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,cAAc,IAAI,MAAM;IAIxB,SAAS,IAAI,kBAAkB;CAWhC"}
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,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,cAAc,IAAI,MAAM;IAIxB,SAAS,IAAI,kBAAkB;CAWhC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fetchium",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -139,7 +139,7 @@ A topic query extends `TopicQuery` and provides a `topic` field and a `result` s
139
139
  import { t } from 'fetchium';
140
140
  import { TopicQuery } from 'fetchium/topic';
141
141
 
142
- class GetPrices extends MyTopicQuery {
142
+ class GetPrices extends TopicQuery {
143
143
  topic = 'prices:live';
144
144
 
145
145
  result = {
@@ -151,7 +151,7 @@ class GetPrices extends MyTopicQuery {
151
151
  Topics can be parameterized using `this.params`, just like paths in `RESTQuery`:
152
152
 
153
153
  ```tsx
154
- class GetBalances extends MyTopicQuery {
154
+ class GetBalances extends TopicQuery {
155
155
  params = { walletId: t.string };
156
156
 
157
157
  topic = `balances:${this.params.walletId}`;
@@ -231,13 +231,11 @@ const queryClient = new QueryClient({
231
231
  });
232
232
  ```
233
233
 
234
- Then make your topic query classes reference the adapter:
234
+ Topic query classes that extend `TopicQuery` directly resolve to the registered `MyStreamAdapter` automatically. Internally, `TopicQuery` declares `static adapter = TopicQueryAdapter` (the abstract base), and `QueryClient` looks up registered adapters by `instanceof` match, so any subclass of `TopicQueryAdapter` you register fulfills the lookup.
235
235
 
236
- ```tsx
237
- abstract class MyTopicQuery extends TopicQuery {
238
- static override adapter = MyStreamAdapter;
239
- }
240
- ```
236
+ {% callout title="One streaming adapter per QueryClient" %}
237
+ Register at most one `TopicQueryAdapter` subclass on a given `QueryClient`. If your app needs multiple streaming protocols, create a separate `QueryClient` for each. Dev builds throw if more than one registered adapter satisfies the same lookup.
238
+ {% /callout %}
241
239
 
242
240
  ### Pre-fulfillment
243
241
 
@@ -515,7 +513,7 @@ In practice, most applications combine multiple real-time strategies:
515
513
 
516
514
  ```tsx
517
515
  // Topic-based streaming for live market data
518
- class GetPrices extends MyTopicQuery {
516
+ class GetPrices extends TopicQuery {
519
517
  topic = 'prices:live';
520
518
  result = { prices: t.liveArray(Price) };
521
519
  }