@kosdev-code/kos-ui-sdk 3.0.17 → 3.0.18

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/index.js CHANGED
@@ -766,6 +766,17 @@ const defaultConfig = {
766
766
  };
767
767
  const config = globalThis.getKosConfig?.() || JSON.stringify(defaultConfig);
768
768
  const configObj = JSON.parse(config);
769
+ const urlProfiles = () => {
770
+ if (typeof window === "undefined") {
771
+ return [];
772
+ }
773
+ const value = getQueryParams()?.["kosProfiles"];
774
+ return value ? value.split(",").map((profile) => profile.trim()).filter(Boolean) : [];
775
+ };
776
+ configObj.profiles = [
777
+ ...configObj.profiles ?? [],
778
+ ...urlProfiles().filter((profile) => !configObj.profiles?.includes(profile))
779
+ ];
769
780
  globalThis.kosConfig = configObj;
770
781
  const KosGlobalConfig = configObj;
771
782
  const resolveKosProfiles = () => KosGlobalConfig.profiles || [];
@@ -4190,6 +4201,33 @@ class KosDependencyManager {
4190
4201
  uses.filter((id) => id !== dependencyId)
4191
4202
  );
4192
4203
  }
4204
+ /**
4205
+ * Drops every edge into and out of a model. Called when the model is
4206
+ * destroyed, so its dependencies are not left pinned by a model that is
4207
+ * gone and can never release them.
4208
+ */
4209
+ removeAll(modelId) {
4210
+ for (const dependencyId of this._usesCache.get(modelId) ?? []) {
4211
+ const usedBy = this._usedByCache.get(dependencyId);
4212
+ if (usedBy) {
4213
+ this._usedByCache.set(
4214
+ dependencyId,
4215
+ usedBy.filter((id) => id !== modelId)
4216
+ );
4217
+ }
4218
+ }
4219
+ this._usesCache.delete(modelId);
4220
+ for (const dependentId of this._usedByCache.get(modelId) ?? []) {
4221
+ const uses = this._usesCache.get(dependentId);
4222
+ if (uses) {
4223
+ this._usesCache.set(
4224
+ dependentId,
4225
+ uses.filter((id) => id !== modelId)
4226
+ );
4227
+ }
4228
+ }
4229
+ this._usedByCache.delete(modelId);
4230
+ }
4193
4231
  canDestroy(modelId) {
4194
4232
  const usedBy = this._usedByCache.get(modelId);
4195
4233
  if (usedBy?.length) {
@@ -4267,6 +4305,19 @@ class KosModelCache {
4267
4305
  return this._preloaded;
4268
4306
  }
4269
4307
  }
4308
+ const cache$1 = /* @__PURE__ */ new Map();
4309
+ const modelHookCache = {
4310
+ has: (key) => cache$1.has(key),
4311
+ get: (key) => cache$1.get(key),
4312
+ set: (key, entry) => cache$1.set(key, entry),
4313
+ delete: (key) => cache$1.delete(key),
4314
+ clear: () => cache$1.clear()
4315
+ };
4316
+ const clearModelHookCacheEntry = (modelId) => {
4317
+ if (modelId) {
4318
+ cache$1.delete(modelId);
4319
+ }
4320
+ };
4270
4321
  class KosModelError extends Error {
4271
4322
  context;
4272
4323
  originalCause;
@@ -8335,6 +8386,7 @@ class KosMockRecorder {
8335
8386
  pending = /* @__PURE__ */ new Map();
8336
8387
  exchanges = [];
8337
8388
  pushes = [];
8389
+ sends = [];
8338
8390
  get isRecording() {
8339
8391
  return this.recording;
8340
8392
  }
@@ -8346,6 +8398,7 @@ class KosMockRecorder {
8346
8398
  this.pending.clear();
8347
8399
  this.exchanges = [];
8348
8400
  this.pushes = [];
8401
+ this.sends = [];
8349
8402
  KosLog.info(
8350
8403
  `KosMock recorder started${options.name ? ` (${options.name})` : ""}`
8351
8404
  );
@@ -8354,7 +8407,7 @@ class KosMockRecorder {
8354
8407
  const fixture = this.toFixture();
8355
8408
  this.recording = false;
8356
8409
  KosLog.info(
8357
- `KosMock recorder stopped: ${fixture.exchanges.length} exchanges, ${fixture.pushes.length} pushes`
8410
+ `KosMock recorder stopped: ${fixture.exchanges.length} exchanges, ${fixture.pushes.length} pushes, ${fixture.sends?.length ?? 0} sends`
8358
8411
  );
8359
8412
  return fixture;
8360
8413
  }
@@ -8364,7 +8417,8 @@ class KosMockRecorder {
8364
8417
  name: this.name,
8365
8418
  capturedAt: this.capturedAt,
8366
8419
  exchanges: [...this.exchanges],
8367
- pushes: [...this.pushes]
8420
+ pushes: [...this.pushes],
8421
+ sends: [...this.sends]
8368
8422
  };
8369
8423
  }
8370
8424
  download(filename) {
@@ -8393,6 +8447,7 @@ class KosMockRecorder {
8393
8447
  }
8394
8448
  const frame = decodeRequestFrame(data);
8395
8449
  if (!frame) {
8450
+ this.noteSend(data);
8396
8451
  return;
8397
8452
  }
8398
8453
  this.pending.set(frame.requestId, {
@@ -8400,7 +8455,31 @@ class KosMockRecorder {
8400
8455
  method: frame.method,
8401
8456
  url: frame.url,
8402
8457
  tracker: frame.tracker,
8403
- requestBody: frame.rawBody
8458
+ requestBody: frame.rawBody,
8459
+ requestHeaders: frame.headers
8460
+ });
8461
+ }
8462
+ /**
8463
+ * Everything else a window sends: alias registration, broker subscribes,
8464
+ * client-sent futures. What a window asked for is not recoverable from the
8465
+ * responses it got back, so it is captured rather than discarded.
8466
+ */
8467
+ noteSend(data) {
8468
+ if (typeof data !== "string") {
8469
+ return;
8470
+ }
8471
+ let headers;
8472
+ let body;
8473
+ try {
8474
+ ({ headers, body } = processKosMessage(data));
8475
+ } catch {
8476
+ return;
8477
+ }
8478
+ this.sends.push({
8479
+ tMs: this.elapsed(),
8480
+ type: headers["type"],
8481
+ headers,
8482
+ ...body ? { body } : {}
8404
8483
  });
8405
8484
  }
8406
8485
  /** Interceptor tap: every inbound frame (real, mocked, injected). Internal. */
@@ -8426,6 +8505,7 @@ class KosMockRecorder {
8426
8505
  ...request2,
8427
8506
  status: parseInt(headers["status"] ?? "200", 10) || 200,
8428
8507
  responseBody: body ?? "",
8508
+ responseHeaders: headers,
8429
8509
  latencyMs: Math.max(0, this.elapsed() - request2.tMs),
8430
8510
  ...headers[KOS_MOCKED_HEADER] === "true" ? { mocked: true } : {}
8431
8511
  });
@@ -8437,7 +8517,8 @@ class KosMockRecorder {
8437
8517
  tMs: this.elapsed(),
8438
8518
  kind: "topic",
8439
8519
  topic,
8440
- body: body ?? ""
8520
+ body: body ?? "",
8521
+ headers
8441
8522
  });
8442
8523
  return;
8443
8524
  }
@@ -8451,7 +8532,8 @@ class KosMockRecorder {
8451
8532
  tMs: this.elapsed(),
8452
8533
  kind: "future",
8453
8534
  tracker,
8454
- body: body ?? ""
8535
+ body: body ?? "",
8536
+ headers
8455
8537
  });
8456
8538
  }
8457
8539
  }
@@ -9272,32 +9354,53 @@ const resolveSingleton = () => {
9272
9354
  };
9273
9355
  const kosMockRegistry = resolveSingleton();
9274
9356
  const KosMock = kosMockRegistry;
9275
- if (typeof window !== "undefined" && window.location?.search) {
9276
- const params2 = getQueryParams();
9277
- if (params2["kosMock"] === "standalone") {
9357
+ const PROFILE_MOCK = "studio.mock";
9358
+ const PROFILE_MOCK_STANDALONE = "studio.mock.standalone";
9359
+ const PROFILE_MOCK_RECORD = "studio.mock.record";
9360
+ const PROFILE_MOCK_FIXTURE_SERVER = "studio.mock.fixtureServer";
9361
+ const PROFILE_MOCK_FIXTURE = "studio.mock.fixture";
9362
+ const PROFILE_MOCK_SETUP = "studio.mock.setup";
9363
+ const profileValue = (name) => {
9364
+ const match = resolveKosProfiles().find(
9365
+ (profile) => profile === name || profile.startsWith(`${name}=`)
9366
+ );
9367
+ return match === void 0 ? void 0 : match.slice(name.length + 1);
9368
+ };
9369
+ if (typeof window !== "undefined") {
9370
+ const params2 = window.location?.search ? getQueryParams() : {};
9371
+ const setting = (param, profile) => params2[param] !== void 0 ? params2[param] : profileValue(profile);
9372
+ const mock = params2["kosMock"] !== void 0 ? params2["kosMock"] : hasKosProfile(PROFILE_MOCK_STANDALONE) ? "standalone" : hasKosProfile(PROFILE_MOCK) ? "on" : void 0;
9373
+ const record = setting("kosRecord", PROFILE_MOCK_RECORD);
9374
+ const fixtureServer = setting(
9375
+ "kosFixtureServer",
9376
+ PROFILE_MOCK_FIXTURE_SERVER
9377
+ );
9378
+ const fixture = setting("kosFixture", PROFILE_MOCK_FIXTURE);
9379
+ const setups = setting("kosSetup", PROFILE_MOCK_SETUP);
9380
+ if (mock === "standalone") {
9278
9381
  KosMock.configure({ standalone: true });
9279
- } else if (params2["kosMock"] === "on") {
9382
+ } else if (mock === "on") {
9280
9383
  KosMock.enable();
9281
9384
  }
9282
- if (params2["kosRecord"] !== void 0) {
9283
- KosMock.recorder.start({ name: params2["kosRecord"] || void 0 });
9385
+ if (record !== void 0) {
9386
+ KosMock.recorder.start({ name: record || void 0 });
9284
9387
  }
9285
- if (params2["kosFixtureServer"] !== void 0) {
9286
- KosMock.fixtures.useServer(params2["kosFixtureServer"] || true);
9388
+ if (fixtureServer !== void 0) {
9389
+ KosMock.fixtures.useServer(fixtureServer || true);
9287
9390
  }
9288
9391
  const bootLoads = [];
9289
- if (params2["kosFixture"]) {
9392
+ if (fixture) {
9290
9393
  bootLoads.push(
9291
- KosMock.fixtures.play(params2["kosFixture"]).then(() => void 0).catch(
9394
+ KosMock.fixtures.play(fixture).then(() => void 0).catch(
9292
9395
  (error) => KosLog.error(
9293
- `KosMock: boot fixture "${params2["kosFixture"]}" failed to load`,
9396
+ `KosMock: boot fixture "${fixture}" failed to load`,
9294
9397
  error?.message
9295
9398
  )
9296
9399
  )
9297
9400
  );
9298
9401
  }
9299
- if (params2["kosSetup"]) {
9300
- for (const name of params2["kosSetup"].split(",").filter(Boolean)) {
9402
+ if (setups) {
9403
+ for (const name of setups.split(",").filter(Boolean)) {
9301
9404
  bootLoads.push(
9302
9405
  KosMock.setups.load(name).then(() => void 0).catch(
9303
9406
  (error) => KosLog.error(
@@ -11721,6 +11824,8 @@ class KosModelManager {
11721
11824
  if (model?.modelId && this.dependencies.canDestroy(model.modelId)) {
11722
11825
  await model.unload?.();
11723
11826
  this.removeModel(model);
11827
+ this.dependencies.removeAll(model.modelId);
11828
+ clearModelHookCacheEntry(model.modelId);
11724
11829
  }
11725
11830
  }
11726
11831
  /**
@@ -26800,33 +26905,32 @@ const waitForRetry = (depth = 0) => new Promise((resolve) => {
26800
26905
  resolve(true);
26801
26906
  }, 2 ** depth * 10);
26802
26907
  });
26803
- const cache$1 = /* @__PURE__ */ new Map();
26804
- function fetchData$1(key, fetcher) {
26805
- if (cache$1.has(key)) {
26806
- const entry = cache$1.get(key);
26908
+ function readModelHookEntry(key, fetcher) {
26909
+ const entry = modelHookCache.get(key);
26910
+ if (entry) {
26807
26911
  if (entry.status === "finished") {
26808
26912
  const kosModel2 = KosCore.getInstance().modelManager.getModelById(key);
26809
26913
  return { kosModel: kosModel2, model: kosModel2?.modelData };
26810
- } else {
26811
- throw entry.promise;
26812
26914
  }
26813
- } else {
26814
- const promise = fetcher().then(
26815
- () => {
26816
- cache$1.set(key, { status: "finished", key });
26817
- },
26818
- (error) => {
26819
- cache$1.set(key, { status: "error", error });
26820
- throw error;
26821
- }
26822
- );
26823
- const entry = { status: "pending", promise };
26824
- cache$1.set(key, entry);
26825
- throw promise;
26915
+ if (entry.status === "error") {
26916
+ throw entry.error;
26917
+ }
26918
+ throw entry.promise;
26826
26919
  }
26920
+ const promise = fetcher().then(
26921
+ () => {
26922
+ modelHookCache.set(key, { status: "finished", key });
26923
+ },
26924
+ (error) => {
26925
+ modelHookCache.set(key, { status: "error", error });
26926
+ throw error;
26927
+ }
26928
+ );
26929
+ modelHookCache.set(key, { status: "pending", promise });
26930
+ throw promise;
26827
26931
  }
26828
26932
  function useSuspenseData$1(key, fetcher) {
26829
- const data = fetchData$1(key, fetcher);
26933
+ const data = readModelHookEntry(key, fetcher);
26830
26934
  return data;
26831
26935
  }
26832
26936
  async function fetchModel$1(kosCore, modelOptions) {
@@ -26914,7 +27018,7 @@ const useKosModel = (modelOptions) => {
26914
27018
  if (destroyOnUnmount) {
26915
27019
  const modelId2 = model.id;
26916
27020
  destroyKosModel(model).then(() => {
26917
- cache$1.delete(modelId2);
27021
+ clearModelHookCacheEntry(modelId2);
26918
27022
  disposer?.();
26919
27023
  });
26920
27024
  }
@@ -26923,7 +27027,7 @@ const useKosModel = (modelOptions) => {
26923
27027
  } else {
26924
27028
  if (destroyOnUnmount && model) {
26925
27029
  destroyKosModel(model).then(() => {
26926
- cache$1.delete(modelId);
27030
+ clearModelHookCacheEntry(modelId);
26927
27031
  disposer?.();
26928
27032
  });
26929
27033
  }
@@ -29196,6 +29300,7 @@ export {
29196
29300
  checkAppsStarted,
29197
29301
  checkWildcardPattern,
29198
29302
  clearAllServiceResponses,
29303
+ clearModelHookCacheEntry,
29199
29304
  clearPath,
29200
29305
  clearServiceResponse,
29201
29306
  convert,
@@ -29354,6 +29459,7 @@ export {
29354
29459
  mapUpdateDtoToConfigBeanModel,
29355
29460
  modelEventTopicFactory,
29356
29461
  modelFactory,
29462
+ modelHookCache,
29357
29463
  modelTypeEventTopicFactory,
29358
29464
  modifyConfigBean,
29359
29465
  modifyFuture,
@@ -29365,6 +29471,7 @@ export {
29365
29471
  processId,
29366
29472
  processMiddleware,
29367
29473
  put,
29474
+ readModelHookEntry,
29368
29475
  registerCompanionModel,
29369
29476
  registerCoreModels,
29370
29477
  registerExtensionPoint,