@intent-framework/core 0.1.0-alpha.7 → 0.1.0-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ export type { AskNode, AnyAskNode, AskKind, AskBuilder } from "./ask.js";
7
7
  export type { ActNode, ActCondition, ActStatus, FeedbackConfig, ActBuilder, NavigationService, ActionExecutionContext, DefaultScreenServices } from "./act.js";
8
8
  export type { FlowNode, FlowStep, FlowBuilder } from "./flow.js";
9
9
  export type { SurfaceNode, SurfaceBuilder } from "./surface.js";
10
- export type { ResourceNode, ResourceConfig, ResourceLoadContext, ResourceStatus, AnyResourceNode } from "./resource.js";
10
+ export type { ResourceNode, ResourceConfig, ResourceCacheOptions, ResourceLoadContext, ResourceStatus, AnyResourceNode } from "./resource.js";
11
11
  export { ResourceRef, createResourceNode } from "./resource.js";
12
12
  export { createScreenRuntime } from "./runtime.js";
13
13
  export type { ScreenRuntime } from "./runtime.js";
package/dist/index.js CHANGED
@@ -37,7 +37,7 @@ function signal(initial) {
37
37
 
38
38
  //#endregion
39
39
  //#region src/resource.ts
40
- function createResourceNode(id, name, loader, autoLoad = true) {
40
+ function createResourceNode(id, name, loader, autoLoad = true, cache) {
41
41
  const statusSignal = signal(0);
42
42
  const staleSignal = signal(0);
43
43
  let currentStatus = "idle";
@@ -45,8 +45,11 @@ function createResourceNode(id, name, loader, autoLoad = true) {
45
45
  let currentError = void 0;
46
46
  let currentStale = false;
47
47
  let lastContext = void 0;
48
+ let _staleTimer = null;
49
+ let _inFlightPromise = null;
48
50
  const notify = () => statusSignal.set(statusSignal.get() + 1);
49
51
  const staleNotify = () => staleSignal.set(staleSignal.get() + 1);
52
+ const shouldDeduplicate = cache ? cache.deduplicate !== false : false;
50
53
  let _ready;
51
54
  let _pending;
52
55
  let _failed;
@@ -67,7 +70,23 @@ function createResourceNode(id, name, loader, autoLoad = true) {
67
70
  if (!_stale) _stale = createCondition(() => currentStale, (notify$1) => staleSignal.subscribe(() => notify$1()));
68
71
  return _stale;
69
72
  }
70
- async function executeLoad(context) {
73
+ function _clearStaleTimer() {
74
+ if (_staleTimer != null) {
75
+ clearTimeout(_staleTimer);
76
+ _staleTimer = null;
77
+ }
78
+ }
79
+ function _startStaleTimer() {
80
+ _clearStaleTimer();
81
+ if (cache?.staleTime != null && isFinite(cache.staleTime)) _staleTimer = setTimeout(() => {
82
+ if (!currentStale) {
83
+ currentStale = true;
84
+ staleNotify();
85
+ }
86
+ }, cache.staleTime);
87
+ }
88
+ function executeLoad(context) {
89
+ if (shouldDeduplicate && _inFlightPromise) return _inFlightPromise;
71
90
  currentStale = false;
72
91
  staleNotify();
73
92
  currentStatus = "pending";
@@ -76,20 +95,27 @@ function createResourceNode(id, name, loader, autoLoad = true) {
76
95
  notify();
77
96
  if (context !== void 0) lastContext = context;
78
97
  const loadContext = context ?? lastContext ?? {};
79
- try {
80
- const result = await Promise.resolve(loader(loadContext));
81
- currentValue = result;
82
- currentStatus = "ready";
83
- currentStale = false;
84
- notify();
85
- staleNotify();
86
- } catch (e) {
87
- currentError = e;
88
- currentStatus = "failed";
89
- currentStale = false;
90
- notify();
91
- staleNotify();
92
- }
98
+ const promise = (async () => {
99
+ try {
100
+ const result = await Promise.resolve(loader(loadContext));
101
+ currentValue = result;
102
+ currentStatus = "ready";
103
+ currentStale = false;
104
+ notify();
105
+ staleNotify();
106
+ _startStaleTimer();
107
+ } catch (e) {
108
+ currentError = e;
109
+ currentStatus = "failed";
110
+ currentStale = false;
111
+ notify();
112
+ staleNotify();
113
+ } finally {
114
+ _inFlightPromise = null;
115
+ }
116
+ })();
117
+ _inFlightPromise = promise;
118
+ return promise;
93
119
  }
94
120
  function invalidate() {
95
121
  if (!currentStale) {
@@ -97,6 +123,10 @@ function createResourceNode(id, name, loader, autoLoad = true) {
97
123
  staleNotify();
98
124
  }
99
125
  }
126
+ function dispose() {
127
+ _clearStaleTimer();
128
+ _inFlightPromise = null;
129
+ }
100
130
  const node = {
101
131
  id,
102
132
  name,
@@ -127,7 +157,8 @@ function createResourceNode(id, name, loader, autoLoad = true) {
127
157
  invalidate,
128
158
  subscribe(fn) {
129
159
  return statusSignal.subscribe(fn);
130
- }
160
+ },
161
+ dispose
131
162
  };
132
163
  return node;
133
164
  }
@@ -683,6 +714,7 @@ function screen(name, fn) {
683
714
  name: n,
684
715
  autoLoad: config.autoLoad ?? true,
685
716
  loader: config.load,
717
+ cache: config.cache,
686
718
  ref
687
719
  });
688
720
  return ref;
@@ -907,7 +939,7 @@ var ScreenRuntime = class {
907
939
  this._started = true;
908
940
  const nodeMap = /* @__PURE__ */ new Map();
909
941
  for (const config of this._screen.resourceConfigs) {
910
- const node = createResourceNode(config.id, config.name, config.loader, false);
942
+ const node = createResourceNode(config.id, config.name, config.loader, false, config.cache);
911
943
  this._resourceNodes.push(node);
912
944
  nodeMap.set(config.id, node);
913
945
  }
@@ -929,6 +961,7 @@ var ScreenRuntime = class {
929
961
  this._disposed = true;
930
962
  for (const unsub of this._unsubscribers) unsub();
931
963
  this._unsubscribers = [];
964
+ for (const node of this._resourceNodes) node.dispose();
932
965
  for (const config of this._screen.resourceConfigs) if (config.ref && this._resourceNodeMap) {
933
966
  const node = this._resourceNodeMap.get(config.id);
934
967
  if (node) config.ref._disconnect(node);
@@ -18,17 +18,23 @@ export type ResourceNode<TValue, TServices extends object = DefaultScreenService
18
18
  reload: (context?: ResourceLoadContext<TServices>) => Promise<void>;
19
19
  invalidate: () => void;
20
20
  subscribe: (fn: () => void) => () => void;
21
+ dispose: () => void;
21
22
  };
22
23
  export type AnyResourceNode = ResourceNode<unknown, any>;
24
+ export type ResourceCacheOptions = {
25
+ staleTime?: number;
26
+ deduplicate?: boolean;
27
+ };
23
28
  export type ResourceConfig<TValue = unknown, TServices extends object = DefaultScreenServices> = {
24
29
  id: string;
25
30
  name: string;
26
31
  autoLoad: boolean;
27
32
  loader: ResourceLoader<TValue, TServices>;
33
+ cache?: ResourceCacheOptions;
28
34
  ref?: ResourceRef<TValue, TServices>;
29
35
  };
30
36
  export declare function createResourceConfig<TValue, TServices extends object = DefaultScreenServices>(id: string, name: string, loader: ResourceLoader<TValue, TServices>, autoLoad?: boolean): ResourceConfig<TValue, TServices>;
31
- export declare function createResourceNode<TValue, TServices extends object = DefaultScreenServices>(id: string, name: string, loader: ResourceLoader<TValue, TServices>, autoLoad?: boolean): ResourceNode<TValue, TServices>;
37
+ export declare function createResourceNode<TValue, TServices extends object = DefaultScreenServices>(id: string, name: string, loader: ResourceLoader<TValue, TServices>, autoLoad?: boolean, cache?: ResourceCacheOptions): ResourceNode<TValue, TServices>;
32
38
  export declare class ResourceRef<TValue, TServices extends object = DefaultScreenServices> {
33
39
  readonly id: string;
34
40
  readonly name: string;
package/dist/screen.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { AnyAskNode } from "./ask.js";
2
2
  import type { ActNode, DefaultScreenServices } from "./act.js";
3
3
  import type { FlowNode } from "./flow.js";
4
4
  import type { SurfaceNode } from "./surface.js";
5
- import type { ResourceConfig, ResourceLoadContext } from "./resource.js";
5
+ import type { ResourceCacheOptions, ResourceConfig, ResourceLoadContext } from "./resource.js";
6
6
  import { ResourceRef } from "./resource.js";
7
7
  import { type TextState, type BooleanState, type ChoiceState } from "./state.js";
8
8
  import { AskBuilder } from "./ask.js";
@@ -31,6 +31,7 @@ export type ScreenBuilder<TServices extends object = DefaultScreenServices> = {
31
31
  resource: <T>(name: string, config: {
32
32
  load: (() => Promise<T>) | ((context: ResourceLoadContext<TServices>) => Promise<T>);
33
33
  autoLoad?: boolean;
34
+ cache?: ResourceCacheOptions;
34
35
  }) => ResourceRef<T, TServices>;
35
36
  };
36
37
  export type ScreenDefinition<TServices extends object = DefaultScreenServices> = {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.1.0-alpha.7",
6
+ "version": "0.1.0-alpha.8",
7
7
  "description": "Platformless semantic graph and runtime for Intent applications",
8
8
  "license": "MIT",
9
9
  "repository": {