@servlyadmin/runtime-core 0.1.0 → 0.1.2

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.js CHANGED
@@ -1,3 +1,12 @@
1
+ import {
2
+ buildRegistryFromBundle,
3
+ collectAllDependencies,
4
+ createRegistry,
5
+ detectCircularDependencies,
6
+ extractDependencies,
7
+ extractDependenciesFromCode
8
+ } from "./chunk-CIUQK4GA.js";
9
+
1
10
  // src/bindings.ts
2
11
  var BINDING_SOURCES = [
3
12
  "props",
@@ -294,17 +303,39 @@ function applyStyles(element, styles) {
294
303
  }
295
304
  }
296
305
  }
306
+ var CANVAS_ONLY_STYLES = /* @__PURE__ */ new Set([
307
+ "transform",
308
+ "--translate-x",
309
+ "--translate-y",
310
+ "--width",
311
+ "--height",
312
+ "z-index",
313
+ "zIndex"
314
+ ]);
315
+ function filterCanvasStyles(style) {
316
+ const filtered = {};
317
+ for (const [key, value] of Object.entries(style)) {
318
+ if (CANVAS_ONLY_STYLES.has(key)) {
319
+ continue;
320
+ }
321
+ if (key === "transform" && typeof value === "string" && value.includes("translate(")) {
322
+ continue;
323
+ }
324
+ filtered[key] = value;
325
+ }
326
+ return filtered;
327
+ }
297
328
  function buildElementStyles(element, context) {
298
329
  const config = element.configuration || {};
299
330
  const combined = {};
300
331
  if (element.style) {
301
- Object.assign(combined, element.style);
332
+ Object.assign(combined, filterCanvasStyles(element.style));
302
333
  }
303
334
  if (config.style) {
304
- Object.assign(combined, config.style);
335
+ Object.assign(combined, filterCanvasStyles(config.style));
305
336
  }
306
337
  if (config.cssVariables) {
307
- Object.assign(combined, config.cssVariables);
338
+ Object.assign(combined, filterCanvasStyles(config.cssVariables));
308
339
  }
309
340
  return processStyles(combined, context);
310
341
  }
@@ -527,18 +558,84 @@ function createElement(element, context, eventHandlers) {
527
558
  attachEventHandlers(domElement, element.i, eventHandlers, elementState);
528
559
  return elementState;
529
560
  }
530
- function renderElement(element, tree, context, eventHandlers, elementStates) {
561
+ var globalRenderingStack = /* @__PURE__ */ new Set();
562
+ function renderComponentRef(element, container, context, state) {
563
+ const config = element.configuration;
564
+ if (!config?.componentViewRef) return void 0;
565
+ const refId = config.componentViewRef;
566
+ const refVersion = config.componentViewVersion;
567
+ if (globalRenderingStack.has(refId)) {
568
+ console.warn(`Circular dependency detected: ${refId} is already being rendered`);
569
+ const placeholder = document.createElement("div");
570
+ placeholder.setAttribute("data-servly-circular", refId);
571
+ placeholder.textContent = `[Circular: ${refId}]`;
572
+ container.appendChild(placeholder);
573
+ return void 0;
574
+ }
575
+ let component;
576
+ if (state.componentRegistry) {
577
+ component = state.componentRegistry.get(refId, refVersion);
578
+ }
579
+ if (!component) {
580
+ const placeholder = document.createElement("div");
581
+ placeholder.setAttribute("data-servly-loading", refId);
582
+ placeholder.className = "servly-loading";
583
+ container.appendChild(placeholder);
584
+ if (state.onDependencyNeeded) {
585
+ state.onDependencyNeeded(refId, refVersion).then((loaded) => {
586
+ if (loaded && state.componentRegistry) {
587
+ state.componentRegistry.set(refId, refVersion || "latest", loaded);
588
+ container.innerHTML = "";
589
+ renderComponentRef(element, container, context, state);
590
+ }
591
+ }).catch((err) => {
592
+ console.error(`Failed to load dependency ${refId}:`, err);
593
+ placeholder.textContent = `[Failed to load: ${refId}]`;
594
+ placeholder.className = "servly-error";
595
+ });
596
+ }
597
+ return void 0;
598
+ }
599
+ const refProps = config.componentViewProps ? resolveTemplatesDeep(config.componentViewProps, context) : {};
600
+ globalRenderingStack.add(refId);
601
+ const refContext = {
602
+ props: refProps,
603
+ state: context.state,
604
+ context: context.context
605
+ };
606
+ try {
607
+ const result = render({
608
+ container,
609
+ elements: component.layout,
610
+ context: refContext,
611
+ componentRegistry: state.componentRegistry,
612
+ onDependencyNeeded: state.onDependencyNeeded
613
+ });
614
+ return result;
615
+ } finally {
616
+ globalRenderingStack.delete(refId);
617
+ }
618
+ }
619
+ function renderElement(element, tree, context, eventHandlers, elementStates, state) {
531
620
  const elementState = createElement(element, context, eventHandlers);
532
621
  elementStates.set(element.i, elementState);
622
+ const config = element.configuration;
623
+ if (config?.componentViewRef) {
624
+ const nestedResult = renderComponentRef(element, elementState.domElement, context, state);
625
+ if (nestedResult) {
626
+ elementState.nestedRender = nestedResult;
627
+ }
628
+ return elementState.domElement;
629
+ }
533
630
  const children = tree.get(element.i) || [];
534
631
  for (const child of children) {
535
- const childElement = renderElement(child, tree, context, eventHandlers, elementStates);
632
+ const childElement = renderElement(child, tree, context, eventHandlers, elementStates, state);
536
633
  elementState.domElement.appendChild(childElement);
537
634
  }
538
635
  return elementState.domElement;
539
636
  }
540
637
  function render(options) {
541
- const { container, elements, context, eventHandlers } = options;
638
+ const { container, elements, context, eventHandlers, componentRegistry, onDependencyNeeded } = options;
542
639
  const tree = buildTree(elements);
543
640
  const rootElements = elements.filter((el) => !el.parent || el.parent === null);
544
641
  const state = {
@@ -547,7 +644,10 @@ function render(options) {
547
644
  context,
548
645
  eventHandlers,
549
646
  elementStates: /* @__PURE__ */ new Map(),
550
- rootElement: null
647
+ rootElement: null,
648
+ componentRegistry,
649
+ onDependencyNeeded,
650
+ renderingStack: /* @__PURE__ */ new Set()
551
651
  };
552
652
  container.innerHTML = "";
553
653
  if (rootElements.length === 0) {
@@ -563,14 +663,15 @@ function render(options) {
563
663
  tree,
564
664
  context,
565
665
  eventHandlers,
566
- state.elementStates
666
+ state.elementStates,
667
+ state
567
668
  );
568
669
  container.appendChild(state.rootElement);
569
670
  } else {
570
671
  const wrapper = document.createElement("div");
571
672
  wrapper.setAttribute("data-servly-wrapper", "true");
572
673
  for (const root of rootElements) {
573
- const rootElement = renderElement(root, tree, context, eventHandlers, state.elementStates);
674
+ const rootElement = renderElement(root, tree, context, eventHandlers, state.elementStates, state);
574
675
  wrapper.appendChild(rootElement);
575
676
  }
576
677
  state.rootElement = wrapper;
@@ -608,6 +709,9 @@ function update(state, newContext) {
608
709
  function destroy(state) {
609
710
  for (const elementState of state.elementStates.values()) {
610
711
  detachEventHandlers(elementState);
712
+ if (elementState.nestedRender) {
713
+ elementState.nestedRender.destroy();
714
+ }
611
715
  }
612
716
  state.elementStates.clear();
613
717
  if (state.rootElement && state.rootElement.parentNode) {
@@ -615,6 +719,66 @@ function destroy(state) {
615
719
  }
616
720
  state.rootElement = null;
617
721
  }
722
+ function renderDynamicList(options) {
723
+ const {
724
+ targetContainer,
725
+ blueprint,
726
+ blueprintVersion,
727
+ data,
728
+ renderType = "renderInto",
729
+ itemKey = "item",
730
+ indexKey = "index",
731
+ componentRegistry,
732
+ context = { props: {} }
733
+ } = options;
734
+ let container;
735
+ if (typeof targetContainer === "string") {
736
+ container = document.querySelector(targetContainer);
737
+ } else {
738
+ container = targetContainer;
739
+ }
740
+ if (!container) {
741
+ console.error(`renderDynamicList: Container not found: ${targetContainer}`);
742
+ return [];
743
+ }
744
+ const blueprintComponent = componentRegistry.get(blueprint, blueprintVersion);
745
+ if (!blueprintComponent) {
746
+ console.error(`renderDynamicList: Blueprint not found: ${blueprint}`);
747
+ return [];
748
+ }
749
+ if (renderType === "renderInto") {
750
+ container.innerHTML = "";
751
+ }
752
+ const results = [];
753
+ const fragment = document.createDocumentFragment();
754
+ data.forEach((item, index) => {
755
+ const itemContainer = document.createElement("div");
756
+ itemContainer.setAttribute("data-servly-list-item", String(index));
757
+ const itemContext = {
758
+ props: {
759
+ ...context.props,
760
+ [itemKey]: item,
761
+ [indexKey]: index
762
+ },
763
+ state: context.state,
764
+ context: context.context
765
+ };
766
+ const result = render({
767
+ container: itemContainer,
768
+ elements: blueprintComponent.layout,
769
+ context: itemContext,
770
+ componentRegistry
771
+ });
772
+ results.push(result);
773
+ fragment.appendChild(itemContainer);
774
+ });
775
+ if (renderType === "prepend") {
776
+ container.insertBefore(fragment, container.firstChild);
777
+ } else {
778
+ container.appendChild(fragment);
779
+ }
780
+ return results;
781
+ }
618
782
 
619
783
  // src/cache.ts
620
784
  var DEFAULT_CACHE_CONFIG = {
@@ -860,118 +1024,6 @@ function invalidateCache(id, version, config = DEFAULT_CACHE_CONFIG) {
860
1024
  }
861
1025
  }
862
1026
 
863
- // src/version.ts
864
- function parseVersion(version) {
865
- const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
866
- if (!match) return null;
867
- return {
868
- major: parseInt(match[1], 10),
869
- minor: parseInt(match[2], 10),
870
- patch: parseInt(match[3], 10)
871
- };
872
- }
873
- function compareVersions(a, b) {
874
- const parsedA = parseVersion(a);
875
- const parsedB = parseVersion(b);
876
- if (!parsedA || !parsedB) return 0;
877
- if (parsedA.major !== parsedB.major) {
878
- return parsedA.major > parsedB.major ? 1 : -1;
879
- }
880
- if (parsedA.minor !== parsedB.minor) {
881
- return parsedA.minor > parsedB.minor ? 1 : -1;
882
- }
883
- if (parsedA.patch !== parsedB.patch) {
884
- return parsedA.patch > parsedB.patch ? 1 : -1;
885
- }
886
- return 0;
887
- }
888
- function satisfiesVersion(version, specifier) {
889
- if (specifier === "latest" || specifier === "*") {
890
- return true;
891
- }
892
- const parsed = parseVersion(version);
893
- if (!parsed) return false;
894
- if (/^\d+\.\d+\.\d+$/.test(specifier)) {
895
- return version === specifier;
896
- }
897
- if (specifier.startsWith("^")) {
898
- const specParsed = parseVersion(specifier.slice(1));
899
- if (!specParsed) return false;
900
- if (parsed.major !== specParsed.major) return false;
901
- if (parsed.major === 0) {
902
- return parsed.minor === specParsed.minor && parsed.patch >= specParsed.patch;
903
- }
904
- return compareVersions(version, specifier.slice(1)) >= 0;
905
- }
906
- if (specifier.startsWith("~")) {
907
- const specParsed = parseVersion(specifier.slice(1));
908
- if (!specParsed) return false;
909
- return parsed.major === specParsed.major && parsed.minor === specParsed.minor && parsed.patch >= specParsed.patch;
910
- }
911
- if (specifier.startsWith(">=")) {
912
- return compareVersions(version, specifier.slice(2)) >= 0;
913
- }
914
- if (specifier.startsWith(">")) {
915
- return compareVersions(version, specifier.slice(1)) > 0;
916
- }
917
- if (specifier.startsWith("<=")) {
918
- return compareVersions(version, specifier.slice(2)) <= 0;
919
- }
920
- if (specifier.startsWith("<")) {
921
- return compareVersions(version, specifier.slice(1)) < 0;
922
- }
923
- return false;
924
- }
925
- function resolveVersion(versions, specifier = "latest") {
926
- if (versions.length === 0) {
927
- return null;
928
- }
929
- const sorted = [...versions].sort((a, b) => compareVersions(b, a));
930
- if (specifier === "latest" || specifier === "*") {
931
- return sorted[0];
932
- }
933
- if (/^\d+\.\d+\.\d+$/.test(specifier)) {
934
- return versions.includes(specifier) ? specifier : null;
935
- }
936
- for (const version of sorted) {
937
- if (satisfiesVersion(version, specifier)) {
938
- return version;
939
- }
940
- }
941
- return null;
942
- }
943
- function bumpVersion(currentVersion, bumpType) {
944
- const parsed = parseVersion(currentVersion);
945
- if (!parsed) return "1.0.0";
946
- switch (bumpType) {
947
- case "major":
948
- return `${parsed.major + 1}.0.0`;
949
- case "minor":
950
- return `${parsed.major}.${parsed.minor + 1}.0`;
951
- case "patch":
952
- return `${parsed.major}.${parsed.minor}.${parsed.patch + 1}`;
953
- default:
954
- return currentVersion;
955
- }
956
- }
957
- function isValidSpecifier(specifier) {
958
- if (specifier === "latest" || specifier === "*") {
959
- return true;
960
- }
961
- if (/^\d+\.\d+\.\d+$/.test(specifier)) {
962
- return true;
963
- }
964
- if (/^[\^~><]=?\d+\.\d+\.\d+$/.test(specifier)) {
965
- return true;
966
- }
967
- return false;
968
- }
969
- function formatVersion(version) {
970
- const parsed = parseVersion(version);
971
- if (!parsed) return version;
972
- return `v${parsed.major}.${parsed.minor}.${parsed.patch}`;
973
- }
974
-
975
1027
  // src/fetcher.ts
976
1028
  var DEFAULT_RETRY_CONFIG = {
977
1029
  maxRetries: 3,
@@ -979,7 +1031,7 @@ var DEFAULT_RETRY_CONFIG = {
979
1031
  maxDelay: 1e4,
980
1032
  backoffMultiplier: 2
981
1033
  };
982
- var registryBaseUrl = "/api/components";
1034
+ var registryBaseUrl = "/api/views/registry";
983
1035
  function setRegistryUrl(url) {
984
1036
  registryBaseUrl = url;
985
1037
  }
@@ -1013,19 +1065,15 @@ async function resolveVersionFromApi(id, specifier, apiKey) {
1013
1065
  }
1014
1066
  try {
1015
1067
  const response = await fetch(
1016
- `${baseUrl}/${id}/versions?specifier=${encodeURIComponent(specifier)}`,
1068
+ `${baseUrl}/${id}/resolve?specifier=${encodeURIComponent(specifier)}`,
1017
1069
  { headers }
1018
1070
  );
1019
1071
  if (!response.ok) {
1020
1072
  throw new Error(`Failed to resolve version: ${response.statusText}`);
1021
1073
  }
1022
1074
  const data = await response.json();
1023
- if (data.success && data.data?.resolvedVersion) {
1024
- return data.data.resolvedVersion;
1025
- }
1026
- if (data.data?.versions) {
1027
- const resolved = resolveVersion(data.data.versions, specifier);
1028
- if (resolved) return resolved;
1075
+ if (data.success && data.data?.resolved) {
1076
+ return data.data.resolved;
1029
1077
  }
1030
1078
  throw new Error(data.error || "Failed to resolve version");
1031
1079
  } catch (error) {
@@ -1033,7 +1081,7 @@ async function resolveVersionFromApi(id, specifier, apiKey) {
1033
1081
  return "latest";
1034
1082
  }
1035
1083
  }
1036
- async function fetchFromRegistry(id, version, apiKey) {
1084
+ async function fetchFromRegistry(id, version, apiKey, includeBundle) {
1037
1085
  const baseUrl = getRegistryUrl();
1038
1086
  const headers = {
1039
1087
  "Content-Type": "application/json"
@@ -1041,25 +1089,39 @@ async function fetchFromRegistry(id, version, apiKey) {
1041
1089
  if (apiKey) {
1042
1090
  headers["Authorization"] = `Bearer ${apiKey}`;
1043
1091
  }
1044
- const url = version && version !== "latest" ? `${baseUrl}/${id}/versions/${version}` : `${baseUrl}/${id}`;
1092
+ let url;
1093
+ if (version && version !== "latest" && /^\d+\.\d+\.\d+/.test(version)) {
1094
+ url = `${baseUrl}/${id}/versions/${version}`;
1095
+ } else if (version && version !== "latest") {
1096
+ url = `${baseUrl}/${id}?version=${encodeURIComponent(version)}`;
1097
+ } else {
1098
+ url = `${baseUrl}/${id}`;
1099
+ }
1100
+ if (includeBundle) {
1101
+ url += (url.includes("?") ? "&" : "?") + "bundle=true";
1102
+ }
1045
1103
  const response = await fetch(url, { headers });
1046
1104
  if (!response.ok) {
1047
1105
  if (response.status === 404) {
1048
- throw new Error(`Component not found: ${id}@${version}`);
1106
+ throw new Error(`View not found: ${id}@${version}`);
1049
1107
  }
1050
1108
  if (response.status === 401) {
1051
1109
  throw new Error("Unauthorized: Invalid or missing API key");
1052
1110
  }
1053
1111
  if (response.status === 403) {
1054
- throw new Error("Forbidden: Access denied to this component");
1112
+ throw new Error("Forbidden: Access denied to this view");
1055
1113
  }
1056
- throw new Error(`Failed to fetch component: ${response.statusText}`);
1114
+ throw new Error(`Failed to fetch view: ${response.statusText}`);
1057
1115
  }
1058
1116
  const data = await response.json();
1059
1117
  if (!data.success || !data.data) {
1060
- throw new Error(data.error || "Failed to fetch component data");
1118
+ throw new Error(data.error || "Failed to fetch view data");
1061
1119
  }
1062
- return data.data;
1120
+ const result = data.data;
1121
+ if (result.viewId && !result.id) {
1122
+ result.id = result.viewId;
1123
+ }
1124
+ return result;
1063
1125
  }
1064
1126
  async function fetchComponent(id, options = {}) {
1065
1127
  const {
@@ -1069,7 +1131,9 @@ async function fetchComponent(id, options = {}) {
1069
1131
  cacheConfig = DEFAULT_CACHE_CONFIG,
1070
1132
  retryConfig = {},
1071
1133
  forceRefresh = false,
1072
- signal
1134
+ signal,
1135
+ bundleStrategy = "eager",
1136
+ includeBundle = true
1073
1137
  } = options;
1074
1138
  const fullRetryConfig = {
1075
1139
  ...DEFAULT_RETRY_CONFIG,
@@ -1078,10 +1142,15 @@ async function fetchComponent(id, options = {}) {
1078
1142
  if (!forceRefresh) {
1079
1143
  const cached = getFromCache(id, version, cacheStrategy, cacheConfig);
1080
1144
  if (cached) {
1145
+ let registry;
1146
+ if (cached.bundle) {
1147
+ registry = buildRegistryFromBundle(cached);
1148
+ }
1081
1149
  return {
1082
1150
  data: cached,
1083
1151
  fromCache: true,
1084
- version: cached.version
1152
+ version: cached.version,
1153
+ registry
1085
1154
  };
1086
1155
  }
1087
1156
  }
@@ -1089,28 +1158,46 @@ async function fetchComponent(id, options = {}) {
1089
1158
  if (!forceRefresh && resolvedVersion !== version) {
1090
1159
  const cached = getFromCache(id, resolvedVersion, cacheStrategy, cacheConfig);
1091
1160
  if (cached) {
1161
+ let registry;
1162
+ if (cached.bundle) {
1163
+ registry = buildRegistryFromBundle(cached);
1164
+ }
1092
1165
  return {
1093
1166
  data: cached,
1094
1167
  fromCache: true,
1095
- version: resolvedVersion
1168
+ version: resolvedVersion,
1169
+ registry
1096
1170
  };
1097
1171
  }
1098
1172
  }
1099
1173
  let lastError = null;
1174
+ const shouldIncludeBundle = bundleStrategy !== "none" && includeBundle;
1100
1175
  for (let attempt = 0; attempt <= fullRetryConfig.maxRetries; attempt++) {
1101
1176
  if (signal?.aborted) {
1102
1177
  throw new Error("Fetch aborted");
1103
1178
  }
1104
1179
  try {
1105
- const data = await fetchFromRegistry(id, resolvedVersion, apiKey);
1180
+ const data = await fetchFromRegistry(id, resolvedVersion, apiKey, shouldIncludeBundle);
1106
1181
  setInCache(id, resolvedVersion, data, cacheStrategy, cacheConfig);
1107
1182
  if (version !== resolvedVersion) {
1108
1183
  setInCache(id, version, data, cacheStrategy, cacheConfig);
1109
1184
  }
1185
+ let registry;
1186
+ let pendingDependencies;
1187
+ if (data.bundle) {
1188
+ registry = buildRegistryFromBundle(data);
1189
+ } else if (data.dependencies && bundleStrategy === "lazy") {
1190
+ pendingDependencies = Object.entries(data.dependencies).map(([depId, entry]) => ({
1191
+ id: depId,
1192
+ version: entry.resolved || entry.version
1193
+ }));
1194
+ }
1110
1195
  return {
1111
1196
  data,
1112
1197
  fromCache: false,
1113
- version: resolvedVersion
1198
+ version: resolvedVersion,
1199
+ registry,
1200
+ pendingDependencies
1114
1201
  };
1115
1202
  } catch (error) {
1116
1203
  lastError = error instanceof Error ? error : new Error(String(error));
@@ -1125,10 +1212,60 @@ async function fetchComponent(id, options = {}) {
1125
1212
  }
1126
1213
  throw lastError || new Error("Failed to fetch component");
1127
1214
  }
1215
+ async function fetchComponentWithDependencies(id, options = {}) {
1216
+ const result = await fetchComponent(id, { ...options, includeBundle: true });
1217
+ if (result.pendingDependencies && result.pendingDependencies.length > 0) {
1218
+ const { createRegistry: createRegistry2 } = await import("./registry-GCCVK65D.js");
1219
+ const registry = result.registry || createRegistry2();
1220
+ await Promise.all(
1221
+ result.pendingDependencies.map(async (dep) => {
1222
+ try {
1223
+ const depResult = await fetchComponent(dep.id, {
1224
+ ...options,
1225
+ version: dep.version,
1226
+ bundleStrategy: "none"
1227
+ // Don't recursively bundle
1228
+ });
1229
+ registry.set(dep.id, depResult.version, {
1230
+ layout: depResult.data.layout,
1231
+ propsInterface: depResult.data.propsInterface
1232
+ });
1233
+ } catch (err) {
1234
+ console.warn(`Failed to fetch dependency ${dep.id}:`, err);
1235
+ }
1236
+ })
1237
+ );
1238
+ result.registry = registry;
1239
+ result.pendingDependencies = void 0;
1240
+ }
1241
+ return result;
1242
+ }
1243
+ async function batchFetchComponents(components, options = {}) {
1244
+ const baseUrl = getRegistryUrl();
1245
+ const headers = {
1246
+ "Content-Type": "application/json"
1247
+ };
1248
+ if (options.apiKey) {
1249
+ headers["Authorization"] = `Bearer ${options.apiKey}`;
1250
+ }
1251
+ const response = await fetch(`${baseUrl}/batch`, {
1252
+ method: "POST",
1253
+ headers,
1254
+ body: JSON.stringify({ components })
1255
+ });
1256
+ if (!response.ok) {
1257
+ throw new Error(`Batch fetch failed: ${response.statusText}`);
1258
+ }
1259
+ const data = await response.json();
1260
+ if (!data.success || !data.data) {
1261
+ throw new Error(data.error || "Batch fetch failed");
1262
+ }
1263
+ return data.data;
1264
+ }
1128
1265
  async function prefetchComponents(ids, options = {}) {
1129
1266
  const promises = ids.map(
1130
1267
  ({ id, version }) => fetchComponent(id, { ...options, version }).catch((error) => {
1131
- console.warn(`Failed to prefetch component ${id}:`, error);
1268
+ console.warn(`Failed to prefetch view ${id}:`, error);
1132
1269
  })
1133
1270
  );
1134
1271
  await Promise.all(promises);
@@ -1137,19 +1274,164 @@ async function isComponentAvailable(id, version = "latest", options = {}) {
1137
1274
  const { cacheStrategy = "memory", cacheConfig = DEFAULT_CACHE_CONFIG } = options;
1138
1275
  const cached = getFromCache(id, version, cacheStrategy, cacheConfig);
1139
1276
  if (cached) {
1140
- return { available: true, cached: true };
1277
+ return { available: true, cached: true, version: cached.version };
1278
+ }
1279
+ const baseUrl = getRegistryUrl();
1280
+ const headers = {
1281
+ "Content-Type": "application/json"
1282
+ };
1283
+ if (options.apiKey) {
1284
+ headers["Authorization"] = `Bearer ${options.apiKey}`;
1141
1285
  }
1142
1286
  try {
1143
- await fetchComponent(id, {
1144
- ...options,
1145
- version,
1146
- retryConfig: { maxRetries: 0 }
1147
- });
1148
- return { available: true, cached: false };
1287
+ const url = version && version !== "latest" ? `${baseUrl}/${id}/available?version=${encodeURIComponent(version)}` : `${baseUrl}/${id}/available`;
1288
+ const response = await fetch(url, { headers });
1289
+ if (!response.ok) {
1290
+ return { available: false, cached: false };
1291
+ }
1292
+ const data = await response.json();
1293
+ if (data.success && data.data) {
1294
+ return data.data;
1295
+ }
1296
+ return { available: false, cached: false };
1149
1297
  } catch {
1150
1298
  return { available: false, cached: false };
1151
1299
  }
1152
1300
  }
1301
+ async function getDependencyTree(id, options = {}) {
1302
+ const baseUrl = getRegistryUrl();
1303
+ const headers = {
1304
+ "Content-Type": "application/json"
1305
+ };
1306
+ if (options.apiKey) {
1307
+ headers["Authorization"] = `Bearer ${options.apiKey}`;
1308
+ }
1309
+ const params = new URLSearchParams();
1310
+ if (options.version) params.set("version", options.version);
1311
+ if (options.depth) params.set("depth", String(options.depth));
1312
+ const url = `${baseUrl}/${id}/dependencies${params.toString() ? "?" + params.toString() : ""}`;
1313
+ const response = await fetch(url, { headers });
1314
+ if (!response.ok) {
1315
+ throw new Error(`Failed to get dependency tree: ${response.statusText}`);
1316
+ }
1317
+ const data = await response.json();
1318
+ if (!data.success || !data.data) {
1319
+ throw new Error(data.error || "Failed to get dependency tree");
1320
+ }
1321
+ return data.data;
1322
+ }
1323
+
1324
+ // src/version.ts
1325
+ function parseVersion(version) {
1326
+ const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
1327
+ if (!match) return null;
1328
+ return {
1329
+ major: parseInt(match[1], 10),
1330
+ minor: parseInt(match[2], 10),
1331
+ patch: parseInt(match[3], 10)
1332
+ };
1333
+ }
1334
+ function compareVersions(a, b) {
1335
+ const parsedA = parseVersion(a);
1336
+ const parsedB = parseVersion(b);
1337
+ if (!parsedA || !parsedB) return 0;
1338
+ if (parsedA.major !== parsedB.major) {
1339
+ return parsedA.major > parsedB.major ? 1 : -1;
1340
+ }
1341
+ if (parsedA.minor !== parsedB.minor) {
1342
+ return parsedA.minor > parsedB.minor ? 1 : -1;
1343
+ }
1344
+ if (parsedA.patch !== parsedB.patch) {
1345
+ return parsedA.patch > parsedB.patch ? 1 : -1;
1346
+ }
1347
+ return 0;
1348
+ }
1349
+ function satisfiesVersion(version, specifier) {
1350
+ if (specifier === "latest" || specifier === "*") {
1351
+ return true;
1352
+ }
1353
+ const parsed = parseVersion(version);
1354
+ if (!parsed) return false;
1355
+ if (/^\d+\.\d+\.\d+$/.test(specifier)) {
1356
+ return version === specifier;
1357
+ }
1358
+ if (specifier.startsWith("^")) {
1359
+ const specParsed = parseVersion(specifier.slice(1));
1360
+ if (!specParsed) return false;
1361
+ if (parsed.major !== specParsed.major) return false;
1362
+ if (parsed.major === 0) {
1363
+ return parsed.minor === specParsed.minor && parsed.patch >= specParsed.patch;
1364
+ }
1365
+ return compareVersions(version, specifier.slice(1)) >= 0;
1366
+ }
1367
+ if (specifier.startsWith("~")) {
1368
+ const specParsed = parseVersion(specifier.slice(1));
1369
+ if (!specParsed) return false;
1370
+ return parsed.major === specParsed.major && parsed.minor === specParsed.minor && parsed.patch >= specParsed.patch;
1371
+ }
1372
+ if (specifier.startsWith(">=")) {
1373
+ return compareVersions(version, specifier.slice(2)) >= 0;
1374
+ }
1375
+ if (specifier.startsWith(">")) {
1376
+ return compareVersions(version, specifier.slice(1)) > 0;
1377
+ }
1378
+ if (specifier.startsWith("<=")) {
1379
+ return compareVersions(version, specifier.slice(2)) <= 0;
1380
+ }
1381
+ if (specifier.startsWith("<")) {
1382
+ return compareVersions(version, specifier.slice(1)) < 0;
1383
+ }
1384
+ return false;
1385
+ }
1386
+ function resolveVersion(versions, specifier = "latest") {
1387
+ if (versions.length === 0) {
1388
+ return null;
1389
+ }
1390
+ const sorted = [...versions].sort((a, b) => compareVersions(b, a));
1391
+ if (specifier === "latest" || specifier === "*") {
1392
+ return sorted[0];
1393
+ }
1394
+ if (/^\d+\.\d+\.\d+$/.test(specifier)) {
1395
+ return versions.includes(specifier) ? specifier : null;
1396
+ }
1397
+ for (const version of sorted) {
1398
+ if (satisfiesVersion(version, specifier)) {
1399
+ return version;
1400
+ }
1401
+ }
1402
+ return null;
1403
+ }
1404
+ function bumpVersion(currentVersion, bumpType) {
1405
+ const parsed = parseVersion(currentVersion);
1406
+ if (!parsed) return "1.0.0";
1407
+ switch (bumpType) {
1408
+ case "major":
1409
+ return `${parsed.major + 1}.0.0`;
1410
+ case "minor":
1411
+ return `${parsed.major}.${parsed.minor + 1}.0`;
1412
+ case "patch":
1413
+ return `${parsed.major}.${parsed.minor}.${parsed.patch + 1}`;
1414
+ default:
1415
+ return currentVersion;
1416
+ }
1417
+ }
1418
+ function isValidSpecifier(specifier) {
1419
+ if (specifier === "latest" || specifier === "*") {
1420
+ return true;
1421
+ }
1422
+ if (/^\d+\.\d+\.\d+$/.test(specifier)) {
1423
+ return true;
1424
+ }
1425
+ if (/^[\^~><]=?\d+\.\d+\.\d+$/.test(specifier)) {
1426
+ return true;
1427
+ }
1428
+ return false;
1429
+ }
1430
+ function formatVersion(version) {
1431
+ const parsed = parseVersion(version);
1432
+ if (!parsed) return version;
1433
+ return `v${parsed.major}.${parsed.minor}.${parsed.patch}`;
1434
+ }
1153
1435
 
1154
1436
  // src/testRunner.ts
1155
1437
  function runTestCase(elements, testCase, container) {
@@ -1409,21 +1691,30 @@ export {
1409
1691
  DEFAULT_CACHE_CONFIG,
1410
1692
  DEFAULT_RETRY_CONFIG,
1411
1693
  applyStyles,
1694
+ batchFetchComponents,
1412
1695
  buildClassName,
1413
1696
  buildElementStyles,
1697
+ buildRegistryFromBundle,
1414
1698
  bumpVersion,
1415
1699
  camelToKebab,
1416
1700
  clearAllCaches,
1417
1701
  clearLocalStorageCache,
1418
1702
  clearMemoryCache,
1419
1703
  clearStyles,
1704
+ collectAllDependencies,
1420
1705
  compareVersions,
1706
+ createRegistry,
1707
+ detectCircularDependencies,
1421
1708
  extractBindingKeys,
1709
+ extractDependencies,
1710
+ extractDependenciesFromCode,
1422
1711
  fetchComponent,
1712
+ fetchComponentWithDependencies,
1423
1713
  formatStyleValue,
1424
1714
  formatVersion,
1425
1715
  generateTestCases,
1426
1716
  getCacheKey,
1717
+ getDependencyTree,
1427
1718
  getFromCache,
1428
1719
  getMemoryCacheSize,
1429
1720
  getRegistryUrl,
@@ -1435,6 +1726,7 @@ export {
1435
1726
  prefetchComponents,
1436
1727
  processStyles,
1437
1728
  render,
1729
+ renderDynamicList,
1438
1730
  resolveBindingPath,
1439
1731
  resolveTemplate,
1440
1732
  resolveTemplateValue,