@kosdev-code/kos-ui-sdk 3.0.2 → 3.0.4

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
@@ -2127,6 +2127,22 @@ class PluginExtensionManager {
2127
2127
  return this.extensions;
2128
2128
  }
2129
2129
  }
2130
+ var KosContainerIndexEvents = /* @__PURE__ */ ((KosContainerIndexEvents2) => {
2131
+ KosContainerIndexEvents2["ADDED"] = "kos/model/container/index/added";
2132
+ KosContainerIndexEvents2["REMOVED"] = "kos/model/container/index/removed";
2133
+ KosContainerIndexEvents2["CHANGED"] = "kos/model/container/index/changed";
2134
+ KosContainerIndexEvents2["SEED"] = "kos/model/container/index/seed";
2135
+ KosContainerIndexEvents2["PURGE"] = "kos/model/container/index/purge";
2136
+ KosContainerIndexEvents2["REQUEST_SEED"] = "kos/model/container/index/request-seed";
2137
+ return KosContainerIndexEvents2;
2138
+ })(KosContainerIndexEvents || {});
2139
+ const INDEX_RECORD_SYMBOL = /* @__PURE__ */ Symbol.for(
2140
+ "@kosdev-code/kos-ui-sdk.indexRecord"
2141
+ );
2142
+ const INDEX_COLLECTION_KEY = "@kosdev-code/kos-ui-sdk.collection";
2143
+ function isIndexableItem(value) {
2144
+ return typeof value === "object" && value !== null && INDEX_RECORD_SYMBOL in value;
2145
+ }
2130
2146
  function arraysAreEqual(arr1, arr2) {
2131
2147
  if (arr1.length !== arr2.length) return false;
2132
2148
  return arr1.every((item1) => arr2.includes(item1));
@@ -2407,6 +2423,7 @@ class KosModelContainer {
2407
2423
  this._establishModelRelationships(model);
2408
2424
  this._enforceCapacityLimits();
2409
2425
  this._configureModelMonitoring(model);
2426
+ this._publishIndexAdded(model);
2410
2427
  if (!defer) {
2411
2428
  this.increment();
2412
2429
  }
@@ -2483,7 +2500,44 @@ class KosModelContainer {
2483
2500
  () => [disposer, ...computedDisposers].forEach((d) => d())
2484
2501
  );
2485
2502
  }
2503
+ /**
2504
+ * Publish KosContainerIndexEvents.ADDED for an indexable item.
2505
+ * No-op when the model carries no INDEX_RECORD_SYMBOL (not opted
2506
+ * into indexing) or when the record has no resolvable collection
2507
+ * (item has no own tag and no inheritable container context).
2508
+ *
2509
+ * @private
2510
+ */
2511
+ _publishIndexAdded(model) {
2512
+ if (!isIndexableItem(model)) return;
2513
+ const contribution = model[INDEX_RECORD_SYMBOL];
2514
+ if (!contribution.collection) return;
2515
+ const payload = {
2516
+ record: contribution
2517
+ };
2518
+ publish(KosContainerIndexEvents.ADDED, payload);
2519
+ }
2520
+ /**
2521
+ * Publish KosContainerIndexEvents.REMOVED for an indexable item.
2522
+ * Called with the model BEFORE it's deleted from the container so
2523
+ * the payload can carry modelType + collection.
2524
+ *
2525
+ * @private
2526
+ */
2527
+ _publishIndexRemoved(model) {
2528
+ if (!model || !isIndexableItem(model)) return;
2529
+ const contribution = model[INDEX_RECORD_SYMBOL];
2530
+ if (!contribution.collection) return;
2531
+ const payload = {
2532
+ id: contribution.id,
2533
+ modelType: contribution.modelType,
2534
+ collection: contribution.collection
2535
+ };
2536
+ publish(KosContainerIndexEvents.REMOVED, payload);
2537
+ }
2486
2538
  removeModel(id, defer) {
2539
+ const removedModel = this._data.get(id);
2540
+ this._publishIndexRemoved(removedModel);
2487
2541
  this._data.delete(id);
2488
2542
  const orderIndex = this._insertionOrder.indexOf(id);
2489
2543
  if (orderIndex !== -1) {
@@ -11543,6 +11597,135 @@ const KosContextManager = {
11543
11597
  };
11544
11598
  KosContextManager.createContext("root");
11545
11599
  const resolveKosContext = (model) => KosContextManager.getContext(model.id);
11600
+ function kosIndexAware(options) {
11601
+ return (target) => {
11602
+ const collection = options?.collection;
11603
+ const fields = options?.fields ?? [];
11604
+ installRecordAccessor(target, collection, fields);
11605
+ if (fields.length > 0) {
11606
+ installChangeEffect(target, fields);
11607
+ }
11608
+ return target;
11609
+ };
11610
+ }
11611
+ function installRecordAccessor(target, collection, fields) {
11612
+ Object.defineProperty(target.prototype, INDEX_RECORD_SYMBOL, {
11613
+ get() {
11614
+ const record = {
11615
+ id: this.id,
11616
+ modelType: this.modelTypeId
11617
+ };
11618
+ const resolved = typeof collection === "function" ? collection(this) : collection;
11619
+ const inherited = resolved === void 0 ? KosContextManager.getContext(this.id)?.get(
11620
+ INDEX_COLLECTION_KEY
11621
+ ) : void 0;
11622
+ const finalCollection = resolved ?? inherited;
11623
+ if (finalCollection !== void 0) {
11624
+ record.collection = finalCollection;
11625
+ }
11626
+ for (const field of fields) {
11627
+ record[field] = this[field];
11628
+ }
11629
+ return record;
11630
+ },
11631
+ enumerable: false,
11632
+ configurable: true
11633
+ });
11634
+ }
11635
+ function installChangeEffect(target, fields) {
11636
+ target.prototype[ModelEffects] = target.prototype[ModelEffects] || {};
11637
+ target.prototype[ModelEffects].handleIndexFieldChange = {
11638
+ dependencies: (model) => fields.map((field) => model[field]),
11639
+ value: function() {
11640
+ const record = this[INDEX_RECORD_SYMBOL];
11641
+ if (!record.collection) {
11642
+ return;
11643
+ }
11644
+ const payload = {
11645
+ record
11646
+ };
11647
+ publish(KosContainerIndexEvents.CHANGED, payload);
11648
+ }
11649
+ };
11650
+ }
11651
+ function kosContainerIndexAware(options) {
11652
+ return (target) => {
11653
+ const itemsProperty = options.itemsProperty;
11654
+ const declaredCollection = options.collection;
11655
+ installContextWriter(target, declaredCollection);
11656
+ installRequestSeedHandler(target, itemsProperty);
11657
+ installPurgeOnUnload(target, itemsProperty);
11658
+ return target;
11659
+ };
11660
+ }
11661
+ function installContextWriter(target, declaredCollection) {
11662
+ if (declaredCollection === void 0) return;
11663
+ const originalInit = target.prototype.init;
11664
+ target.prototype.init = async function(context) {
11665
+ context?.set(INDEX_COLLECTION_KEY, declaredCollection);
11666
+ if (originalInit) {
11667
+ await originalInit.call(this, context);
11668
+ }
11669
+ };
11670
+ }
11671
+ function installRequestSeedHandler(target, itemsProperty) {
11672
+ target.prototype[SubscriptionHandlers] = target.prototype[SubscriptionHandlers] || {};
11673
+ target.prototype[SubscriptionHandlers][KosContainerIndexEvents.REQUEST_SEED] = {
11674
+ websocket: false,
11675
+ skipParse: true,
11676
+ // The subscription manager calls transform(body) before
11677
+ // condition/handler. For an internal EventBus event with a
11678
+ // plain object payload we just want to pass it through
11679
+ // untouched.
11680
+ transform: (payload) => payload,
11681
+ condition: () => true,
11682
+ handler: function() {
11683
+ const items = this[itemsProperty] ?? [];
11684
+ const records = collectRecords(items);
11685
+ if (records.size === 0) return;
11686
+ const payload = {
11687
+ modelType: this.modelTypeId,
11688
+ items: Array.from(records.values())
11689
+ };
11690
+ publish(KosContainerIndexEvents.SEED, payload);
11691
+ }
11692
+ };
11693
+ }
11694
+ function installPurgeOnUnload(target, itemsProperty) {
11695
+ const originalUnload = target.prototype.unload;
11696
+ target.prototype.unload = function(context) {
11697
+ const items = this[itemsProperty] ?? [];
11698
+ const collections = /* @__PURE__ */ new Set();
11699
+ for (const item of items) {
11700
+ if (!isIndexableItem(item)) continue;
11701
+ const contribution = item[INDEX_RECORD_SYMBOL];
11702
+ if (contribution.collection) {
11703
+ collections.add(contribution.collection);
11704
+ }
11705
+ }
11706
+ for (const collection of collections) {
11707
+ const payload = {
11708
+ modelType: this.modelTypeId,
11709
+ collection
11710
+ };
11711
+ publish(KosContainerIndexEvents.PURGE, payload);
11712
+ }
11713
+ if (originalUnload) {
11714
+ return originalUnload.call(this, context);
11715
+ }
11716
+ return void 0;
11717
+ };
11718
+ }
11719
+ function collectRecords(items) {
11720
+ const result = /* @__PURE__ */ new Map();
11721
+ for (const item of items) {
11722
+ if (!isIndexableItem(item)) continue;
11723
+ const contribution = item[INDEX_RECORD_SYMBOL];
11724
+ if (!contribution.collection) continue;
11725
+ result.set(contribution.id, contribution);
11726
+ }
11727
+ return result;
11728
+ }
11546
11729
  const log$s = KosLog.createLogger({ name: "kos-data-container" });
11547
11730
  class KosDataContainer {
11548
11731
  _data;
@@ -26471,6 +26654,8 @@ export {
26471
26654
  HEADER_URL,
26472
26655
  HEADER_WORK_TRACKER,
26473
26656
  HttpRouteHandlers,
26657
+ INDEX_COLLECTION_KEY,
26658
+ INDEX_RECORD_SYMBOL,
26474
26659
  KOS_EXECUTION_CONTEXT,
26475
26660
  KOS_MODEL_ID,
26476
26661
  KOS_RECOVERING_SESSION_KEY,
@@ -26483,6 +26668,7 @@ export {
26483
26668
  Kos,
26484
26669
  Registration as KosConfigPropertySpec,
26485
26670
  KosContainerIndex,
26671
+ KosContainerIndexEvents,
26486
26672
  KosContextBean,
26487
26673
  KosContextManager,
26488
26674
  KosCore,
@@ -26737,6 +26923,7 @@ export {
26737
26923
  injectStateMachineSupport,
26738
26924
  isBoolean,
26739
26925
  isDisposable,
26926
+ isIndexableItem,
26740
26927
  isKosCompanionTypeFactory,
26741
26928
  isKosDataModel,
26742
26929
  isKosExecutionContext,
@@ -26759,12 +26946,14 @@ export {
26759
26946
  kosConfigBean,
26760
26947
  kosConfigProperty,
26761
26948
  kosContainerAware,
26949
+ kosContainerIndexAware,
26762
26950
  kosContext,
26763
26951
  kosDependency,
26764
26952
  kosEffect,
26765
26953
  kosFetch,
26766
26954
  kosFuture,
26767
26955
  kosFutureAware,
26956
+ kosIndexAware,
26768
26957
  kosLogger,
26769
26958
  kosLoggerAware,
26770
26959
  kosModel,