@tailorkit/client-core 0.1.0-beta.23 → 0.1.0-beta.24
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/{chunk-RNTQ3LLF.js → chunk-BLDTSDUG.js} +195 -123
- package/dist/{chunk-K3QITWU4.js → chunk-CWDPTXSZ.js} +22 -113
- package/dist/chunk-FUBKKH3Q.js +96 -0
- package/dist/chunk-LNGQREZV.js +79 -0
- package/dist/chunk-N4X53WS2.js +669 -0
- package/dist/chunk-QWOVC765.js +222 -0
- package/dist/client/config.d.ts +66 -0
- package/dist/client/index.d.ts +7 -0
- package/dist/client/primitive-css.d.ts +10 -0
- package/dist/client/slot-types.d.ts +61 -0
- package/dist/client/value-memo.d.ts +2 -0
- package/dist/client/views.d.ts +8 -0
- package/dist/client.js +24 -6
- package/dist/index.js +48 -16
- package/dist/store/fetch/apps.d.ts +1 -0
- package/dist/store/fetch/cache.d.ts +3 -1
- package/dist/store/fetch/meta.d.ts +1 -0
- package/dist/store/fetch/preview-manager.d.ts +1 -0
- package/dist/store/fetch/slot.d.ts +1 -0
- package/dist/store/index.d.ts +2 -0
- package/dist/store/local/index.d.ts +7 -2
- package/dist/store/local/node-store.d.ts +2 -0
- package/dist/store/local/remote-view.d.ts +31 -0
- package/dist/store/local/slot-runtime.d.ts +36 -0
- package/dist/store/local/view-context-diagnostics.d.ts +10 -0
- package/dist/store/local/view-context-types.d.ts +21 -0
- package/dist/store/local/{view-registry.d.ts → view-context.d.ts} +5 -2
- package/dist/store/query.d.ts +39 -0
- package/dist/store/snapshot-store.d.ts +5 -0
- package/dist/store/store.d.ts +4 -1
- package/dist/store-fetch.js +5 -4
- package/dist/store-local.js +11 -3
- package/package.json +6 -5
- package/dist/chunk-DPR3U4U2.js +0 -57
- package/dist/chunk-ED3BWTXG.js +0 -128
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
// src/store/fetch/cache.ts
|
|
2
|
-
import {
|
|
2
|
+
import { computedAsync } from "@nanostores/async";
|
|
3
|
+
|
|
4
|
+
// src/store/snapshot-store.ts
|
|
5
|
+
import { atom, onStart, onStop, readonlyType } from "nanostores";
|
|
6
|
+
function createSnapshotStore(getSnapshot, subscribe) {
|
|
7
|
+
const state = atom(getSnapshot());
|
|
8
|
+
let stop;
|
|
9
|
+
const sync = () => state.set(getSnapshot());
|
|
10
|
+
state.get = () => {
|
|
11
|
+
sync();
|
|
12
|
+
return state.value;
|
|
13
|
+
};
|
|
14
|
+
onStart(state, () => {
|
|
15
|
+
stop = subscribe(sync);
|
|
16
|
+
sync();
|
|
17
|
+
});
|
|
18
|
+
onStop(state, () => {
|
|
19
|
+
stop?.();
|
|
20
|
+
stop = void 0;
|
|
21
|
+
});
|
|
22
|
+
return readonlyType(state);
|
|
23
|
+
}
|
|
24
|
+
function switchStore(selection) {
|
|
25
|
+
return createSnapshotStore(
|
|
26
|
+
() => selection.get().get(),
|
|
27
|
+
(listener) => {
|
|
28
|
+
let current = selection.get();
|
|
29
|
+
let stopCurrent = current.listen(listener);
|
|
30
|
+
const stopSelection = selection.listen(() => {
|
|
31
|
+
const next = selection.get();
|
|
32
|
+
if (next !== current) {
|
|
33
|
+
const previousStop = stopCurrent;
|
|
34
|
+
current = next;
|
|
35
|
+
stopCurrent = next.listen(listener);
|
|
36
|
+
previousStop();
|
|
37
|
+
}
|
|
38
|
+
listener();
|
|
39
|
+
});
|
|
40
|
+
return () => {
|
|
41
|
+
stopSelection();
|
|
42
|
+
stopCurrent();
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/store/fetch/cache.ts
|
|
49
|
+
import { atom as atom2 } from "nanostores";
|
|
3
50
|
var idle = () => ({
|
|
4
51
|
data: void 0,
|
|
5
52
|
error: null,
|
|
@@ -63,7 +110,7 @@ function createFetchCache(defaults = {}) {
|
|
|
63
110
|
let entry = entries.get(key);
|
|
64
111
|
if (!entry) {
|
|
65
112
|
entry = {
|
|
66
|
-
state:
|
|
113
|
+
state: atom2(idle()),
|
|
67
114
|
controller: null,
|
|
68
115
|
pending: null,
|
|
69
116
|
observers: 0,
|
|
@@ -123,38 +170,42 @@ function createFetchCache(defaults = {}) {
|
|
|
123
170
|
const controller = new AbortController();
|
|
124
171
|
entry.controller = controller;
|
|
125
172
|
const generation = ++entry.generation;
|
|
126
|
-
const run = async () => {
|
|
127
|
-
try {
|
|
128
|
-
const data = await fetcher(controller.signal);
|
|
129
|
-
if (entry.generation !== generation) return;
|
|
130
|
-
entry.invalidated = false;
|
|
131
|
-
entry.state.set({
|
|
132
|
-
data,
|
|
133
|
-
error: null,
|
|
134
|
-
status: "ready",
|
|
135
|
-
isFetching: false,
|
|
136
|
-
updatedAt: Date.now()
|
|
137
|
-
});
|
|
138
|
-
} catch (error) {
|
|
139
|
-
if (entry.generation !== generation) return;
|
|
140
|
-
entry.state.set({
|
|
141
|
-
...entry.state.get(),
|
|
142
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
143
|
-
status: "error",
|
|
144
|
-
isFetching: false
|
|
145
|
-
});
|
|
146
|
-
} finally {
|
|
147
|
-
if (entry.generation === generation) {
|
|
148
|
-
entry.controller = null;
|
|
149
|
-
entry.pending = null;
|
|
150
|
-
scheduleGc(key, entry);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
173
|
let start;
|
|
155
174
|
const pending = new Promise((resolve) => {
|
|
156
175
|
start = () => {
|
|
157
|
-
|
|
176
|
+
const request = computedAsync(atom2(controller.signal), (signal) => {
|
|
177
|
+
signal.throwIfAborted();
|
|
178
|
+
return fetcher(signal);
|
|
179
|
+
});
|
|
180
|
+
const stop = request.listen((result) => {
|
|
181
|
+
if (result.state === "loading") return;
|
|
182
|
+
stop();
|
|
183
|
+
if (entry.generation === generation) {
|
|
184
|
+
if (result.state === "ready") {
|
|
185
|
+
entry.invalidated = false;
|
|
186
|
+
entry.state.set({
|
|
187
|
+
data: result.value,
|
|
188
|
+
error: null,
|
|
189
|
+
status: "ready",
|
|
190
|
+
isFetching: false,
|
|
191
|
+
updatedAt: Date.now()
|
|
192
|
+
});
|
|
193
|
+
} else {
|
|
194
|
+
entry.state.set({
|
|
195
|
+
...entry.state.get(),
|
|
196
|
+
error: result.error instanceof Error ? result.error : new Error(String(result.error)),
|
|
197
|
+
status: "error",
|
|
198
|
+
isFetching: false
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
if (entry.generation === generation) {
|
|
202
|
+
entry.controller = null;
|
|
203
|
+
entry.pending = null;
|
|
204
|
+
scheduleGc(key, entry);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
resolve();
|
|
208
|
+
});
|
|
158
209
|
};
|
|
159
210
|
});
|
|
160
211
|
entry.pending = pending;
|
|
@@ -190,7 +241,7 @@ function createFetchCache(defaults = {}) {
|
|
|
190
241
|
return entry.observers ? store.fetch({ force: true }) : Promise.resolve();
|
|
191
242
|
}
|
|
192
243
|
};
|
|
193
|
-
return store;
|
|
244
|
+
return { ...store, state: createSnapshotStore(store.getSnapshot, store.subscribe) };
|
|
194
245
|
},
|
|
195
246
|
clear() {
|
|
196
247
|
for (const [key, entry] of entries) {
|
|
@@ -208,95 +259,6 @@ function toBaseUrl(value) {
|
|
|
208
259
|
return url;
|
|
209
260
|
}
|
|
210
261
|
|
|
211
|
-
// src/client/endpoints.ts
|
|
212
|
-
import { createClient, createSessionProvider, reference } from "@tailorkit/app/client";
|
|
213
|
-
import { previewMetadataSchema } from "@tailorkit/client-platform/preview";
|
|
214
|
-
var resolveInstances = reference(
|
|
215
|
-
"_tailorkit.instances.resolve",
|
|
216
|
-
"action"
|
|
217
|
-
);
|
|
218
|
-
function createEndpointClient(options) {
|
|
219
|
-
const baseUrl = toBaseUrl(options.baseUrl);
|
|
220
|
-
const request = (input, init) => (options.fetch ?? globalThis.fetch)(input, init);
|
|
221
|
-
const sessions = /* @__PURE__ */ new Map();
|
|
222
|
-
const createSessionEntry = (appId, key) => ({
|
|
223
|
-
key,
|
|
224
|
-
session: createSessionProvider({ baseUrl, appId, fetch: request }),
|
|
225
|
-
// Mounted consumers keep this wrapper, so resolve the current provider on every call.
|
|
226
|
-
provider: async (input) => {
|
|
227
|
-
let current = sessions.get(appId);
|
|
228
|
-
if (!current) {
|
|
229
|
-
current = createSessionEntry(appId);
|
|
230
|
-
sessions.set(appId, current);
|
|
231
|
-
}
|
|
232
|
-
const session = current.session;
|
|
233
|
-
const value = await session(input);
|
|
234
|
-
if (current.session !== session) {
|
|
235
|
-
throw new DOMException("The app session was invalidated", "AbortError");
|
|
236
|
-
}
|
|
237
|
-
return value;
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
const getSessionProvider = (app) => {
|
|
241
|
-
const key = JSON.stringify([app.currentDeployment?.id, app.preview?.sessionId]);
|
|
242
|
-
let entry = sessions.get(app.id);
|
|
243
|
-
if (entry && entry.key === void 0) entry.key = key;
|
|
244
|
-
if (!entry || entry.key !== key) {
|
|
245
|
-
if (entry) entry.session = void 0;
|
|
246
|
-
entry = createSessionEntry(app.id, key);
|
|
247
|
-
sessions.set(app.id, entry);
|
|
248
|
-
}
|
|
249
|
-
return entry.provider;
|
|
250
|
-
};
|
|
251
|
-
return {
|
|
252
|
-
baseUrl,
|
|
253
|
-
async apps(signal) {
|
|
254
|
-
const response = await request(new URL("apps", baseUrl), {
|
|
255
|
-
signal,
|
|
256
|
-
credentials: "same-origin"
|
|
257
|
-
});
|
|
258
|
-
if (!response.ok)
|
|
259
|
-
throw new Error(`Unable to fetch TailorKit apps from ${baseUrl.toString()}.`);
|
|
260
|
-
return await response.json();
|
|
261
|
-
},
|
|
262
|
-
async meta(signal) {
|
|
263
|
-
const response = await request(new URL("meta", baseUrl), {
|
|
264
|
-
signal,
|
|
265
|
-
credentials: "same-origin"
|
|
266
|
-
});
|
|
267
|
-
if (!response.ok)
|
|
268
|
-
throw new Error(`Unable to fetch TailorKit metadata from ${baseUrl.toString()}.`);
|
|
269
|
-
const value = await response.json();
|
|
270
|
-
return { ...value, assetsBaseUrl: value.assetsBaseUrl ?? null };
|
|
271
|
-
},
|
|
272
|
-
async slotInstances(app, input, signal) {
|
|
273
|
-
signal.throwIfAborted();
|
|
274
|
-
const backend = createClient({ getSession: getSessionProvider(app), fetch: request });
|
|
275
|
-
const abort = () => backend.close();
|
|
276
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
277
|
-
try {
|
|
278
|
-
return await backend.action(resolveInstances, input);
|
|
279
|
-
} finally {
|
|
280
|
-
signal.removeEventListener("abort", abort);
|
|
281
|
-
backend.close();
|
|
282
|
-
}
|
|
283
|
-
},
|
|
284
|
-
async previewMetadata(sessionId) {
|
|
285
|
-
const url = new URL("preview/metadata", baseUrl);
|
|
286
|
-
url.searchParams.set("sessionId", sessionId);
|
|
287
|
-
const response = await request(url, { credentials: "same-origin" });
|
|
288
|
-
if (response.status === 404) return null;
|
|
289
|
-
if (!response.ok) throw new Error("Unable to refresh TailorKit preview metadata.");
|
|
290
|
-
return previewMetadataSchema.parse(await response.json());
|
|
291
|
-
},
|
|
292
|
-
getSessionProvider,
|
|
293
|
-
clearSessions: () => {
|
|
294
|
-
for (const entry of sessions.values()) entry.session = void 0;
|
|
295
|
-
sessions.clear();
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
|
|
300
262
|
// src/client/slot-view.ts
|
|
301
263
|
import { composeViewContext, getViewHierarchy } from "@tailorkit/core/views";
|
|
302
264
|
function selectSlotView(views, slot, activePath, schema) {
|
|
@@ -318,11 +280,121 @@ function resolveSlotView(views, slot, activeView, schema) {
|
|
|
318
280
|
};
|
|
319
281
|
}
|
|
320
282
|
|
|
283
|
+
// src/store/fetch/slot.ts
|
|
284
|
+
function createSlotStore(client, options) {
|
|
285
|
+
const {
|
|
286
|
+
apps,
|
|
287
|
+
slot,
|
|
288
|
+
activeView,
|
|
289
|
+
staleTime,
|
|
290
|
+
gcTime,
|
|
291
|
+
appsStatus = "ready",
|
|
292
|
+
appsError = null
|
|
293
|
+
} = options;
|
|
294
|
+
const enabled = appsStatus === "ready";
|
|
295
|
+
const settings = {
|
|
296
|
+
...staleTime === void 0 ? {} : { staleTime },
|
|
297
|
+
...gcTime === void 0 ? {} : { gcTime }
|
|
298
|
+
};
|
|
299
|
+
const waiting = {
|
|
300
|
+
data: void 0,
|
|
301
|
+
error: appsStatus === "error" ? appsError : null,
|
|
302
|
+
status: appsStatus,
|
|
303
|
+
isFetching: false
|
|
304
|
+
};
|
|
305
|
+
const query = client.cache.getStore(
|
|
306
|
+
["tailorkit", client.baseUrl.toString(), "slot", apps, slot, activeView],
|
|
307
|
+
async (signal) => {
|
|
308
|
+
const candidates = apps.filter(
|
|
309
|
+
(app) => app.views?.some((view) => view.slot === slot && !view.disabled)
|
|
310
|
+
);
|
|
311
|
+
if (!candidates.length) return { status: "ready", data: [] };
|
|
312
|
+
const meta = client.meta();
|
|
313
|
+
await meta.fetch();
|
|
314
|
+
signal.throwIfAborted();
|
|
315
|
+
const snapshot2 = meta.getSnapshot();
|
|
316
|
+
if (snapshot2.error) throw snapshot2.error;
|
|
317
|
+
if (!snapshot2.data) throw new Error("TailorKit metadata is unavailable.");
|
|
318
|
+
const schema = snapshot2.data.schema;
|
|
319
|
+
const definition = schema.slots[slot];
|
|
320
|
+
if (!definition) return { status: "ready", data: [] };
|
|
321
|
+
if (definition.multiple !== true) {
|
|
322
|
+
return {
|
|
323
|
+
status: "ready",
|
|
324
|
+
data: candidates.filter(
|
|
325
|
+
(app) => app.views?.some(
|
|
326
|
+
(view) => view.slot === slot && !view.disabled && !view.instances && definition.views.includes(view.path)
|
|
327
|
+
)
|
|
328
|
+
).map((app) => ({ app }))
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
if (activeView === null) return { status: "idle" };
|
|
332
|
+
const matches = candidates.flatMap((app) => {
|
|
333
|
+
const resolved = resolveSlotView(app.views ?? [], slot, activeView, schema);
|
|
334
|
+
return resolved?.instances ? [{ app, resolved }] : [];
|
|
335
|
+
});
|
|
336
|
+
for (const { resolved } of matches) {
|
|
337
|
+
if (resolved.status === "error")
|
|
338
|
+
throw new Error(`Context for view "${resolved.view}" is unavailable.`);
|
|
339
|
+
}
|
|
340
|
+
if (matches.some(({ resolved }) => resolved.status !== "ready")) return { status: "loading" };
|
|
341
|
+
const controller = new AbortController();
|
|
342
|
+
const abort = () => controller.abort();
|
|
343
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
344
|
+
try {
|
|
345
|
+
const results = await Promise.all(
|
|
346
|
+
matches.map(async ({ app, resolved }) => {
|
|
347
|
+
if (resolved.status !== "ready") return [];
|
|
348
|
+
const data = await client.endpoints.slotInstances(
|
|
349
|
+
app,
|
|
350
|
+
{ slot, path: resolved.view, context: resolved.context },
|
|
351
|
+
controller.signal
|
|
352
|
+
);
|
|
353
|
+
return data.map((instance) => ({ ...instance, app }));
|
|
354
|
+
})
|
|
355
|
+
);
|
|
356
|
+
return { status: "ready", data: results.flat() };
|
|
357
|
+
} catch (error) {
|
|
358
|
+
controller.abort();
|
|
359
|
+
throw error;
|
|
360
|
+
} finally {
|
|
361
|
+
signal.removeEventListener("abort", abort);
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
{ ...client.cacheOptions?.slot, ...settings, abortOnUnsubscribe: true }
|
|
365
|
+
);
|
|
366
|
+
let last;
|
|
367
|
+
let snapshot;
|
|
368
|
+
const store = {
|
|
369
|
+
getSnapshot() {
|
|
370
|
+
if (!enabled) return waiting;
|
|
371
|
+
const current = query.getSnapshot();
|
|
372
|
+
if (current !== last) {
|
|
373
|
+
snapshot = {
|
|
374
|
+
data: current.data?.data,
|
|
375
|
+
error: current.error,
|
|
376
|
+
status: current.status === "ready" ? current.data.status : current.status,
|
|
377
|
+
isFetching: current.isFetching
|
|
378
|
+
};
|
|
379
|
+
last = current;
|
|
380
|
+
}
|
|
381
|
+
return snapshot;
|
|
382
|
+
},
|
|
383
|
+
subscribe: (listener) => enabled ? query.subscribe(listener) : () => {
|
|
384
|
+
},
|
|
385
|
+
fetch: (fetchOptions = {}) => enabled ? query.fetch(fetchOptions) : Promise.resolve(),
|
|
386
|
+
invalidate: () => enabled ? query.invalidate() : Promise.resolve()
|
|
387
|
+
};
|
|
388
|
+
return { ...store, state: createSnapshotStore(store.getSnapshot, store.subscribe) };
|
|
389
|
+
}
|
|
390
|
+
|
|
321
391
|
export {
|
|
392
|
+
createSnapshotStore,
|
|
393
|
+
switchStore,
|
|
322
394
|
serializeCacheKey,
|
|
323
395
|
createFetchCache,
|
|
324
396
|
toBaseUrl,
|
|
325
|
-
createEndpointClient,
|
|
326
397
|
selectSlotView,
|
|
327
|
-
resolveSlotView
|
|
398
|
+
resolveSlotView,
|
|
399
|
+
createSlotStore
|
|
328
400
|
};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
createEndpointClient
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
createEndpointClient
|
|
3
|
+
} from "./chunk-FUBKKH3Q.js";
|
|
4
|
+
import {
|
|
5
|
+
createSnapshotStore
|
|
6
|
+
} from "./chunk-BLDTSDUG.js";
|
|
5
7
|
|
|
6
8
|
// src/store/fetch/apps.ts
|
|
7
9
|
import { atom } from "nanostores";
|
|
@@ -77,7 +79,7 @@ function createAppsStore(client, initialApps) {
|
|
|
77
79
|
},
|
|
78
80
|
invalidate: () => supplied ? Promise.resolve() : query.invalidate()
|
|
79
81
|
};
|
|
80
|
-
return store;
|
|
82
|
+
return { ...store, state: createSnapshotStore(store.getSnapshot, store.subscribe) };
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
// src/store/fetch/meta.ts
|
|
@@ -85,7 +87,7 @@ function createMetadataStore(client) {
|
|
|
85
87
|
const query = client.meta();
|
|
86
88
|
let last;
|
|
87
89
|
let snapshot;
|
|
88
|
-
|
|
90
|
+
const store = {
|
|
89
91
|
getSnapshot() {
|
|
90
92
|
const current = query.getSnapshot();
|
|
91
93
|
if (current !== last) {
|
|
@@ -104,6 +106,7 @@ function createMetadataStore(client) {
|
|
|
104
106
|
fetch: query.fetch,
|
|
105
107
|
invalidate: query.invalidate
|
|
106
108
|
};
|
|
109
|
+
return { ...store, state: createSnapshotStore(store.getSnapshot, store.subscribe) };
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
// src/store/fetch/preview-manager.ts
|
|
@@ -275,7 +278,7 @@ function createPreviewManager(baseUrl, onEnded, onViews, refreshMetadata = creat
|
|
|
275
278
|
}
|
|
276
279
|
});
|
|
277
280
|
};
|
|
278
|
-
|
|
281
|
+
const manager = {
|
|
279
282
|
getSnapshot: (sessionId) => entries.get(sessionId)?.state.get() ?? empty,
|
|
280
283
|
updateApp: (app) => {
|
|
281
284
|
const sessionId = app.preview?.sessionId;
|
|
@@ -330,118 +333,24 @@ function createPreviewManager(baseUrl, onEnded, onViews, refreshMetadata = creat
|
|
|
330
333
|
entries.clear();
|
|
331
334
|
}
|
|
332
335
|
};
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// src/store/fetch/slot.ts
|
|
336
|
-
function createSlotStore(client, options) {
|
|
337
|
-
const {
|
|
338
|
-
apps,
|
|
339
|
-
slot,
|
|
340
|
-
activeView,
|
|
341
|
-
staleTime,
|
|
342
|
-
gcTime,
|
|
343
|
-
appsStatus = "ready",
|
|
344
|
-
appsError = null
|
|
345
|
-
} = options;
|
|
346
|
-
const enabled = appsStatus === "ready";
|
|
347
|
-
const settings = {
|
|
348
|
-
...staleTime === void 0 ? {} : { staleTime },
|
|
349
|
-
...gcTime === void 0 ? {} : { gcTime }
|
|
350
|
-
};
|
|
351
|
-
const waiting = {
|
|
352
|
-
data: void 0,
|
|
353
|
-
error: appsStatus === "error" ? appsError : null,
|
|
354
|
-
status: appsStatus,
|
|
355
|
-
isFetching: false
|
|
356
|
-
};
|
|
357
|
-
const query = client.cache.getStore(
|
|
358
|
-
["tailorkit", client.baseUrl.toString(), "slot", apps, slot, activeView],
|
|
359
|
-
async (signal) => {
|
|
360
|
-
const candidates = apps.filter(
|
|
361
|
-
(app) => app.views?.some((view) => view.slot === slot && !view.disabled)
|
|
362
|
-
);
|
|
363
|
-
if (!candidates.length) return { status: "ready", data: [] };
|
|
364
|
-
const meta = client.meta();
|
|
365
|
-
await meta.fetch();
|
|
366
|
-
signal.throwIfAborted();
|
|
367
|
-
const snapshot2 = meta.getSnapshot();
|
|
368
|
-
if (snapshot2.error) throw snapshot2.error;
|
|
369
|
-
if (!snapshot2.data) throw new Error("TailorKit metadata is unavailable.");
|
|
370
|
-
const schema = snapshot2.data.schema;
|
|
371
|
-
const definition = schema.slots[slot];
|
|
372
|
-
if (!definition) return { status: "ready", data: [] };
|
|
373
|
-
if (definition.multiple !== true) {
|
|
374
|
-
return {
|
|
375
|
-
status: "ready",
|
|
376
|
-
data: candidates.filter(
|
|
377
|
-
(app) => app.views?.some(
|
|
378
|
-
(view) => view.slot === slot && !view.disabled && !view.instances && definition.views.includes(view.path)
|
|
379
|
-
)
|
|
380
|
-
).map((app) => ({ app }))
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
if (activeView === null) return { status: "idle" };
|
|
384
|
-
const matches = candidates.flatMap((app) => {
|
|
385
|
-
const resolved = resolveSlotView(app.views ?? [], slot, activeView, schema);
|
|
386
|
-
return resolved?.instances ? [{ app, resolved }] : [];
|
|
387
|
-
});
|
|
388
|
-
for (const { resolved } of matches) {
|
|
389
|
-
if (resolved.status === "error")
|
|
390
|
-
throw new Error(`Context for view "${resolved.view}" is unavailable.`);
|
|
391
|
-
}
|
|
392
|
-
if (matches.some(({ resolved }) => resolved.status !== "ready")) return { status: "loading" };
|
|
393
|
-
const controller = new AbortController();
|
|
394
|
-
const abort = () => controller.abort();
|
|
395
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
396
|
-
try {
|
|
397
|
-
const results = await Promise.all(
|
|
398
|
-
matches.map(async ({ app, resolved }) => {
|
|
399
|
-
if (resolved.status !== "ready") return [];
|
|
400
|
-
const data = await client.endpoints.slotInstances(
|
|
401
|
-
app,
|
|
402
|
-
{ slot, path: resolved.view, context: resolved.context },
|
|
403
|
-
controller.signal
|
|
404
|
-
);
|
|
405
|
-
return data.map((instance) => ({ ...instance, app }));
|
|
406
|
-
})
|
|
407
|
-
);
|
|
408
|
-
return { status: "ready", data: results.flat() };
|
|
409
|
-
} catch (error) {
|
|
410
|
-
controller.abort();
|
|
411
|
-
throw error;
|
|
412
|
-
} finally {
|
|
413
|
-
signal.removeEventListener("abort", abort);
|
|
414
|
-
}
|
|
415
|
-
},
|
|
416
|
-
{ ...client.cacheOptions?.slot, ...settings, abortOnUnsubscribe: true }
|
|
417
|
-
);
|
|
418
|
-
let last;
|
|
419
|
-
let snapshot;
|
|
420
336
|
return {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
const
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
return snapshot;
|
|
434
|
-
},
|
|
435
|
-
subscribe: (listener) => enabled ? query.subscribe(listener) : () => {
|
|
436
|
-
},
|
|
437
|
-
fetch: (fetchOptions = {}) => enabled ? query.fetch(fetchOptions) : Promise.resolve(),
|
|
438
|
-
invalidate: () => enabled ? query.invalidate() : Promise.resolve()
|
|
337
|
+
...manager,
|
|
338
|
+
getStore(getApp) {
|
|
339
|
+
const sessionId = getApp().preview?.sessionId ?? "";
|
|
340
|
+
return createSnapshotStore(
|
|
341
|
+
() => manager.getSnapshot(sessionId),
|
|
342
|
+
(listener) => {
|
|
343
|
+
const app = getApp();
|
|
344
|
+
return app.preview?.sessionId === sessionId ? manager.subscribe(app, listener) : () => {
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
);
|
|
348
|
+
}
|
|
439
349
|
};
|
|
440
350
|
}
|
|
441
351
|
|
|
442
352
|
export {
|
|
443
353
|
createAppsStore,
|
|
444
354
|
createMetadataStore,
|
|
445
|
-
createPreviewManager
|
|
446
|
-
createSlotStore
|
|
355
|
+
createPreviewManager
|
|
447
356
|
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {
|
|
2
|
+
toBaseUrl
|
|
3
|
+
} from "./chunk-BLDTSDUG.js";
|
|
4
|
+
|
|
5
|
+
// src/client/endpoints.ts
|
|
6
|
+
import { createClient, createSessionProvider, reference } from "@tailorkit/app/client/connection";
|
|
7
|
+
import { previewMetadataSchema } from "@tailorkit/client-platform/preview";
|
|
8
|
+
var resolveInstances = reference(
|
|
9
|
+
"_tailorkit.instances.resolve",
|
|
10
|
+
"action"
|
|
11
|
+
);
|
|
12
|
+
function createEndpointClient(options) {
|
|
13
|
+
const baseUrl = toBaseUrl(options.baseUrl);
|
|
14
|
+
const request = (input, init) => (options.fetch ?? globalThis.fetch)(input, init);
|
|
15
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
16
|
+
const createSessionEntry = (appId, key) => ({
|
|
17
|
+
key,
|
|
18
|
+
session: createSessionProvider({ baseUrl, appId, fetch: request }),
|
|
19
|
+
// Mounted consumers keep this wrapper, so resolve the current provider on every call.
|
|
20
|
+
provider: async (input) => {
|
|
21
|
+
let current = sessions.get(appId);
|
|
22
|
+
if (!current) {
|
|
23
|
+
current = createSessionEntry(appId);
|
|
24
|
+
sessions.set(appId, current);
|
|
25
|
+
}
|
|
26
|
+
const session = current.session;
|
|
27
|
+
const value = await session(input);
|
|
28
|
+
if (current.session !== session) {
|
|
29
|
+
throw new DOMException("The app session was invalidated", "AbortError");
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const getSessionProvider = (app) => {
|
|
35
|
+
const key = JSON.stringify([app.currentDeployment?.id, app.preview?.sessionId]);
|
|
36
|
+
let entry = sessions.get(app.id);
|
|
37
|
+
if (entry && entry.key === void 0) entry.key = key;
|
|
38
|
+
if (!entry || entry.key !== key) {
|
|
39
|
+
if (entry) entry.session = void 0;
|
|
40
|
+
entry = createSessionEntry(app.id, key);
|
|
41
|
+
sessions.set(app.id, entry);
|
|
42
|
+
}
|
|
43
|
+
return entry.provider;
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
baseUrl,
|
|
47
|
+
async apps(signal) {
|
|
48
|
+
const response = await request(new URL("apps", baseUrl), {
|
|
49
|
+
signal,
|
|
50
|
+
credentials: "same-origin"
|
|
51
|
+
});
|
|
52
|
+
if (!response.ok)
|
|
53
|
+
throw new Error(`Unable to fetch TailorKit apps from ${baseUrl.toString()}.`);
|
|
54
|
+
return await response.json();
|
|
55
|
+
},
|
|
56
|
+
async meta(signal) {
|
|
57
|
+
const response = await request(new URL("meta", baseUrl), {
|
|
58
|
+
signal,
|
|
59
|
+
credentials: "same-origin"
|
|
60
|
+
});
|
|
61
|
+
if (!response.ok)
|
|
62
|
+
throw new Error(`Unable to fetch TailorKit metadata from ${baseUrl.toString()}.`);
|
|
63
|
+
const value = await response.json();
|
|
64
|
+
return { ...value, assetsBaseUrl: value.assetsBaseUrl ?? null };
|
|
65
|
+
},
|
|
66
|
+
async slotInstances(app, input, signal) {
|
|
67
|
+
signal.throwIfAborted();
|
|
68
|
+
const backend = createClient({ getSession: getSessionProvider(app), fetch: request });
|
|
69
|
+
const abort = () => backend.close();
|
|
70
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
71
|
+
try {
|
|
72
|
+
return await backend.action(resolveInstances, input);
|
|
73
|
+
} finally {
|
|
74
|
+
signal.removeEventListener("abort", abort);
|
|
75
|
+
backend.close();
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
async previewMetadata(sessionId) {
|
|
79
|
+
const url = new URL("preview/metadata", baseUrl);
|
|
80
|
+
url.searchParams.set("sessionId", sessionId);
|
|
81
|
+
const response = await request(url, { credentials: "same-origin" });
|
|
82
|
+
if (response.status === 404) return null;
|
|
83
|
+
if (!response.ok) throw new Error("Unable to refresh TailorKit preview metadata.");
|
|
84
|
+
return previewMetadataSchema.parse(await response.json());
|
|
85
|
+
},
|
|
86
|
+
getSessionProvider,
|
|
87
|
+
clearSessions: () => {
|
|
88
|
+
for (const entry of sessions.values()) entry.session = void 0;
|
|
89
|
+
sessions.clear();
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export {
|
|
95
|
+
createEndpointClient
|
|
96
|
+
};
|