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

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, ResourceCacheOptions, ResourceLoadContext, ResourceStatus, AnyResourceNode } from "./resource.js";
10
+ export type { ResourceNode, ResourceConfig, ResourceCacheOptions, ResourceLoadContext, ResourceStatus, ResourceKey, 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
@@ -40,13 +40,26 @@ function signal(initial) {
40
40
  function createResourceNode(id, name, loader, autoLoad = true, cache) {
41
41
  const statusSignal = signal(0);
42
42
  const staleSignal = signal(0);
43
+ const hasKey = typeof cache?.key === "function";
44
+ const DEFAULT_KEY = "";
45
+ function createEntry() {
46
+ return {
47
+ value: void 0,
48
+ status: "idle",
49
+ error: void 0,
50
+ stale: false,
51
+ staleTimer: null,
52
+ inFlightPromise: null
53
+ };
54
+ }
55
+ const entries = /* @__PURE__ */ new Map();
56
+ let _activeKey = DEFAULT_KEY;
57
+ if (!hasKey) entries.set(DEFAULT_KEY, createEntry());
43
58
  let currentStatus = "idle";
44
59
  let currentValue = void 0;
45
60
  let currentError = void 0;
46
61
  let currentStale = false;
47
62
  let lastContext = void 0;
48
- let _staleTimer = null;
49
- let _inFlightPromise = null;
50
63
  const notify = () => statusSignal.set(statusSignal.get() + 1);
51
64
  const staleNotify = () => staleSignal.set(staleSignal.get() + 1);
52
65
  const shouldDeduplicate = cache ? cache.deduplicate !== false : false;
@@ -54,6 +67,69 @@ function createResourceNode(id, name, loader, autoLoad = true, cache) {
54
67
  let _pending;
55
68
  let _failed;
56
69
  let _stale;
70
+ function getActiveEntry() {
71
+ let entry = entries.get(_activeKey);
72
+ if (!entry) {
73
+ entry = createEntry();
74
+ entries.set(_activeKey, entry);
75
+ }
76
+ return entry;
77
+ }
78
+ function syncFromEntry(entry) {
79
+ currentStatus = entry.status;
80
+ currentValue = entry.value;
81
+ currentError = entry.error;
82
+ if (currentStale !== entry.stale) {
83
+ currentStale = entry.stale;
84
+ staleNotify();
85
+ }
86
+ notify();
87
+ }
88
+ function syncFromActiveEntry() {
89
+ syncFromEntry(getActiveEntry());
90
+ }
91
+ function encodeResourceKey(key) {
92
+ if (Array.isArray(key)) return ["array", key.map(encodeResourceKey)];
93
+ if (key === null) return ["null"];
94
+ if (key === void 0) return ["undefined"];
95
+ if (typeof key === "string") return ["string", key];
96
+ if (typeof key === "boolean") return ["boolean", key];
97
+ if (typeof key === "number") {
98
+ if (Number.isNaN(key)) return ["number", "NaN"];
99
+ if (key === Infinity) return ["number", "Infinity"];
100
+ if (key === -Infinity) return ["number", "-Infinity"];
101
+ if (Object.is(key, -0)) return ["number", "-0"];
102
+ return ["number", key];
103
+ }
104
+ const exhaustive = key;
105
+ return exhaustive;
106
+ }
107
+ function normalizeKey(key) {
108
+ return JSON.stringify(encodeResourceKey(key));
109
+ }
110
+ function resolveKey(ctx) {
111
+ if (!hasKey) return DEFAULT_KEY;
112
+ const context = ctx ?? lastContext ?? {};
113
+ return normalizeKey(cache.key(context));
114
+ }
115
+ function _clearEntryStaleTimer(entry) {
116
+ if (entry.staleTimer != null) {
117
+ clearTimeout(entry.staleTimer);
118
+ entry.staleTimer = null;
119
+ }
120
+ }
121
+ function _startEntryStaleTimer(entry) {
122
+ _clearEntryStaleTimer(entry);
123
+ if (cache?.staleTime != null && isFinite(cache.staleTime)) entry.staleTimer = setTimeout(() => {
124
+ if (!entry.stale) {
125
+ entry.stale = true;
126
+ if (entry === getActiveEntry()) {
127
+ currentStale = true;
128
+ staleNotify();
129
+ }
130
+ }
131
+ }, cache.staleTime);
132
+ }
57
133
  function getReady() {
58
134
  if (!_ready) _ready = createCondition(() => currentStatus === "ready", (notify$1) => statusSignal.subscribe(() => notify$1()));
59
135
  return _ready;
@@ -70,62 +146,59 @@ function createResourceNode(id, name, loader, autoLoad = true, cache) {
70
146
  if (!_stale) _stale = createCondition(() => currentStale, (notify$1) => staleSignal.subscribe(() => notify$1()));
71
147
  return _stale;
72
148
  }
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
149
  function executeLoad(context) {
89
- if (shouldDeduplicate && _inFlightPromise) return _inFlightPromise;
90
- currentStale = false;
91
- staleNotify();
92
- currentStatus = "pending";
93
- currentValue = void 0;
94
- currentError = void 0;
95
- notify();
150
+ const key = resolveKey(context);
151
+ _activeKey = key;
152
+ let entry = entries.get(key);
153
+ if (!entry) {
154
+ entry = createEntry();
155
+ entries.set(key, entry);
156
+ }
157
+ if (shouldDeduplicate && entry.inFlightPromise) return entry.inFlightPromise;
96
158
  if (context !== void 0) lastContext = context;
97
159
  const loadContext = context ?? lastContext ?? {};
160
+ entry.stale = false;
161
+ entry.status = "pending";
162
+ entry.value = void 0;
163
+ entry.error = void 0;
164
+ syncFromActiveEntry();
98
165
  const promise = (async () => {
99
166
  try {
100
167
  const result = await Promise.resolve(loader(loadContext));
101
- currentValue = result;
102
- currentStatus = "ready";
103
- currentStale = false;
104
- notify();
168
+ entry.value = result;
169
+ entry.status = "ready";
170
+ entry.stale = false;
171
+ if (entry === getActiveEntry()) syncFromEntry(entry);
105
172
  staleNotify();
106
- _startStaleTimer();
173
+ _startEntryStaleTimer(entry);
107
174
  } catch (e) {
108
- currentError = e;
109
- currentStatus = "failed";
110
- currentStale = false;
111
- notify();
175
+ entry.error = e;
176
+ entry.status = "failed";
177
+ entry.stale = false;
178
+ if (entry === getActiveEntry()) syncFromEntry(entry);
112
179
  staleNotify();
113
180
  } finally {
114
- _inFlightPromise = null;
181
+ entry.inFlightPromise = null;
115
182
  }
116
183
  })();
117
- _inFlightPromise = promise;
184
+ entry.inFlightPromise = promise;
118
185
  return promise;
119
186
  }
120
187
  function invalidate() {
121
- if (!currentStale) {
122
- currentStale = true;
123
- staleNotify();
188
+ const entry = getActiveEntry();
189
+ if (!entry.stale) {
190
+ entry.stale = true;
191
+ if (entry === getActiveEntry()) {
192
+ currentStale = true;
193
+ staleNotify();
194
+ }
124
195
  }
125
196
  }
126
197
  function dispose() {
127
- _clearStaleTimer();
128
- _inFlightPromise = null;
198
+ for (const entry of entries.values()) {
199
+ _clearEntryStaleTimer(entry);
200
+ entry.inFlightPromise = null;
201
+ }
129
202
  }
130
203
  const node = {
131
204
  id,
@@ -1,6 +1,7 @@
1
1
  import { type Condition } from "./signal.js";
2
2
  import type { ActionExecutionContext, DefaultScreenServices } from "./act.js";
3
3
  export type ResourceStatus = "idle" | "pending" | "ready" | "failed";
4
+ export type ResourceKey = string | number | boolean | null | undefined | ResourceKey[];
4
5
  export type ResourceLoadContext<TServices extends object = DefaultScreenServices> = ActionExecutionContext<TServices>;
5
6
  type ResourceLoader<TValue, TServices extends object> = (() => TValue | Promise<TValue>) | ((context: ResourceLoadContext<TServices>) => TValue | Promise<TValue>);
6
7
  export type ResourceNode<TValue, TServices extends object = DefaultScreenServices> = {
@@ -21,7 +22,8 @@ export type ResourceNode<TValue, TServices extends object = DefaultScreenService
21
22
  dispose: () => void;
22
23
  };
23
24
  export type AnyResourceNode = ResourceNode<unknown, any>;
24
- export type ResourceCacheOptions = {
25
+ export type ResourceCacheOptions<TServices extends object = DefaultScreenServices> = {
26
+ key?: (context: ResourceLoadContext<TServices>) => ResourceKey;
25
27
  staleTime?: number;
26
28
  deduplicate?: boolean;
27
29
  };
@@ -30,11 +32,11 @@ export type ResourceConfig<TValue = unknown, TServices extends object = DefaultS
30
32
  name: string;
31
33
  autoLoad: boolean;
32
34
  loader: ResourceLoader<TValue, TServices>;
33
- cache?: ResourceCacheOptions;
35
+ cache?: ResourceCacheOptions<TServices>;
34
36
  ref?: ResourceRef<TValue, TServices>;
35
37
  };
36
38
  export declare function createResourceConfig<TValue, TServices extends object = DefaultScreenServices>(id: string, name: string, loader: ResourceLoader<TValue, TServices>, autoLoad?: boolean): ResourceConfig<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>;
39
+ export declare function createResourceNode<TValue, TServices extends object = DefaultScreenServices>(id: string, name: string, loader: ResourceLoader<TValue, TServices>, autoLoad?: boolean, cache?: ResourceCacheOptions<TServices>): ResourceNode<TValue, TServices>;
38
40
  export declare class ResourceRef<TValue, TServices extends object = DefaultScreenServices> {
39
41
  readonly id: string;
40
42
  readonly name: string;
package/dist/screen.d.ts CHANGED
@@ -31,7 +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
+ cache?: ResourceCacheOptions<TServices>;
35
35
  }) => ResourceRef<T, TServices>;
36
36
  };
37
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.8",
6
+ "version": "0.1.0-alpha.9",
7
7
  "description": "Platformless semantic graph and runtime for Intent applications",
8
8
  "license": "MIT",
9
9
  "repository": {