@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.cjs CHANGED
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __esm = (fn, res) => function __init() {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ };
6
9
  var __export = (target, all) => {
7
10
  for (var name in all)
8
11
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -17,27 +20,224 @@ var __copyProps = (to, from, except, desc) => {
17
20
  };
18
21
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
22
 
23
+ // src/registry.ts
24
+ var registry_exports = {};
25
+ __export(registry_exports, {
26
+ buildRegistryFromBundle: () => buildRegistryFromBundle,
27
+ collectAllDependencies: () => collectAllDependencies,
28
+ createRegistry: () => createRegistry,
29
+ detectCircularDependencies: () => detectCircularDependencies,
30
+ extractDependencies: () => extractDependencies,
31
+ extractDependenciesFromCode: () => extractDependenciesFromCode
32
+ });
33
+ function createRegistry() {
34
+ const components = /* @__PURE__ */ new Map();
35
+ return {
36
+ get(id, version) {
37
+ if (version) {
38
+ const key = `${id}@${version}`;
39
+ if (components.has(key)) {
40
+ return components.get(key);
41
+ }
42
+ }
43
+ for (const [key, component] of components) {
44
+ if (key.startsWith(`${id}@`)) {
45
+ return component;
46
+ }
47
+ }
48
+ return components.get(id);
49
+ },
50
+ has(id, version) {
51
+ if (version) {
52
+ return components.has(`${id}@${version}`);
53
+ }
54
+ for (const key of components.keys()) {
55
+ if (key.startsWith(`${id}@`) || key === id) {
56
+ return true;
57
+ }
58
+ }
59
+ return false;
60
+ },
61
+ set(id, version, component) {
62
+ components.set(`${id}@${version}`, component);
63
+ }
64
+ };
65
+ }
66
+ function buildRegistryFromBundle(data) {
67
+ const registry = createRegistry();
68
+ registry.set(data.id, data.version, {
69
+ layout: data.layout,
70
+ propsInterface: data.propsInterface
71
+ });
72
+ if (data.bundle) {
73
+ for (const [key, component] of Object.entries(data.bundle)) {
74
+ const [id, version] = key.split("@");
75
+ if (id && version) {
76
+ registry.set(id, version, component);
77
+ }
78
+ }
79
+ }
80
+ return registry;
81
+ }
82
+ function extractDependencies(elements) {
83
+ const dependencies = [];
84
+ for (const element of elements) {
85
+ const config = element.configuration;
86
+ if (!config) continue;
87
+ if (config.componentViewRef) {
88
+ dependencies.push({
89
+ id: config.componentViewRef,
90
+ version: config.componentViewVersion,
91
+ type: "viewRef",
92
+ elementId: element.i
93
+ });
94
+ }
95
+ if (config.blueprint) {
96
+ dependencies.push({
97
+ id: config.blueprint,
98
+ version: config.blueprintVersion,
99
+ type: "blueprint",
100
+ elementId: element.i
101
+ });
102
+ }
103
+ }
104
+ return dependencies;
105
+ }
106
+ function extractDependenciesFromCode(code) {
107
+ const dependencies = [];
108
+ const pattern = /renderDynamicList\s*\(\s*\{[^}]*blueprint\s*:\s*["']([^"']+)["']/g;
109
+ let match;
110
+ while ((match = pattern.exec(code)) !== null) {
111
+ dependencies.push({
112
+ id: match[1],
113
+ type: "blueprint"
114
+ });
115
+ }
116
+ return dependencies;
117
+ }
118
+ async function collectAllDependencies(rootId, rootVersion, fetchComponent2, maxDepth = 10) {
119
+ const manifest = {};
120
+ const visited = /* @__PURE__ */ new Set();
121
+ async function collect(id, version, via, depth) {
122
+ if (depth > maxDepth) {
123
+ console.warn(`Max dependency depth (${maxDepth}) reached for ${id}`);
124
+ return;
125
+ }
126
+ const key = `${id}@${version || "latest"}`;
127
+ if (visited.has(key)) {
128
+ return;
129
+ }
130
+ visited.add(key);
131
+ try {
132
+ const component = await fetchComponent2(id, version);
133
+ if (!component) {
134
+ console.warn(`Dependency not found: ${id}@${version || "latest"}`);
135
+ return;
136
+ }
137
+ manifest[id] = {
138
+ version: version || "latest",
139
+ resolved: component.version,
140
+ type: via ? "viewRef" : "viewRef",
141
+ // Will be set by caller
142
+ via
143
+ };
144
+ const nestedDeps = extractDependencies(component.layout);
145
+ for (const dep of nestedDeps) {
146
+ await collect(dep.id, dep.version, id, depth + 1);
147
+ }
148
+ } catch (error) {
149
+ console.error(`Failed to fetch dependency ${id}:`, error);
150
+ }
151
+ }
152
+ const rootComponent = await fetchComponent2(rootId, rootVersion);
153
+ if (rootComponent) {
154
+ const rootDeps = extractDependencies(rootComponent.layout);
155
+ for (const dep of rootDeps) {
156
+ manifest[dep.id] = {
157
+ version: dep.version || "latest",
158
+ resolved: "",
159
+ // Will be filled when fetched
160
+ type: dep.type
161
+ };
162
+ await collect(dep.id, dep.version, void 0, 1);
163
+ }
164
+ }
165
+ return manifest;
166
+ }
167
+ function detectCircularDependencies(manifest) {
168
+ const graph = /* @__PURE__ */ new Map();
169
+ for (const [id, entry] of Object.entries(manifest)) {
170
+ if (entry.via) {
171
+ const deps = graph.get(entry.via) || [];
172
+ deps.push(id);
173
+ graph.set(entry.via, deps);
174
+ }
175
+ }
176
+ const visited = /* @__PURE__ */ new Set();
177
+ const stack = /* @__PURE__ */ new Set();
178
+ const path = [];
179
+ function dfs(node) {
180
+ if (stack.has(node)) {
181
+ const cycleStart = path.indexOf(node);
182
+ return [...path.slice(cycleStart), node];
183
+ }
184
+ if (visited.has(node)) {
185
+ return null;
186
+ }
187
+ visited.add(node);
188
+ stack.add(node);
189
+ path.push(node);
190
+ const neighbors = graph.get(node) || [];
191
+ for (const neighbor of neighbors) {
192
+ const cycle = dfs(neighbor);
193
+ if (cycle) return cycle;
194
+ }
195
+ stack.delete(node);
196
+ path.pop();
197
+ return null;
198
+ }
199
+ for (const node of graph.keys()) {
200
+ const cycle = dfs(node);
201
+ if (cycle) return cycle;
202
+ }
203
+ return null;
204
+ }
205
+ var init_registry = __esm({
206
+ "src/registry.ts"() {
207
+ "use strict";
208
+ }
209
+ });
210
+
20
211
  // src/index.ts
21
212
  var index_exports = {};
22
213
  __export(index_exports, {
23
214
  DEFAULT_CACHE_CONFIG: () => DEFAULT_CACHE_CONFIG,
24
215
  DEFAULT_RETRY_CONFIG: () => DEFAULT_RETRY_CONFIG,
25
216
  applyStyles: () => applyStyles,
217
+ batchFetchComponents: () => batchFetchComponents,
26
218
  buildClassName: () => buildClassName,
27
219
  buildElementStyles: () => buildElementStyles,
220
+ buildRegistryFromBundle: () => buildRegistryFromBundle,
28
221
  bumpVersion: () => bumpVersion,
29
222
  camelToKebab: () => camelToKebab,
30
223
  clearAllCaches: () => clearAllCaches,
31
224
  clearLocalStorageCache: () => clearLocalStorageCache,
32
225
  clearMemoryCache: () => clearMemoryCache,
33
226
  clearStyles: () => clearStyles,
227
+ collectAllDependencies: () => collectAllDependencies,
34
228
  compareVersions: () => compareVersions,
229
+ createRegistry: () => createRegistry,
230
+ detectCircularDependencies: () => detectCircularDependencies,
35
231
  extractBindingKeys: () => extractBindingKeys,
232
+ extractDependencies: () => extractDependencies,
233
+ extractDependenciesFromCode: () => extractDependenciesFromCode,
36
234
  fetchComponent: () => fetchComponent,
235
+ fetchComponentWithDependencies: () => fetchComponentWithDependencies,
37
236
  formatStyleValue: () => formatStyleValue,
38
237
  formatVersion: () => formatVersion,
39
238
  generateTestCases: () => generateTestCases,
40
239
  getCacheKey: () => getCacheKey,
240
+ getDependencyTree: () => getDependencyTree,
41
241
  getFromCache: () => getFromCache,
42
242
  getMemoryCacheSize: () => getMemoryCacheSize,
43
243
  getRegistryUrl: () => getRegistryUrl,
@@ -49,6 +249,7 @@ __export(index_exports, {
49
249
  prefetchComponents: () => prefetchComponents,
50
250
  processStyles: () => processStyles,
51
251
  render: () => render,
252
+ renderDynamicList: () => renderDynamicList,
52
253
  resolveBindingPath: () => resolveBindingPath,
53
254
  resolveTemplate: () => resolveTemplate,
54
255
  resolveTemplateValue: () => resolveTemplateValue,
@@ -361,17 +562,39 @@ function applyStyles(element, styles) {
361
562
  }
362
563
  }
363
564
  }
565
+ var CANVAS_ONLY_STYLES = /* @__PURE__ */ new Set([
566
+ "transform",
567
+ "--translate-x",
568
+ "--translate-y",
569
+ "--width",
570
+ "--height",
571
+ "z-index",
572
+ "zIndex"
573
+ ]);
574
+ function filterCanvasStyles(style) {
575
+ const filtered = {};
576
+ for (const [key, value] of Object.entries(style)) {
577
+ if (CANVAS_ONLY_STYLES.has(key)) {
578
+ continue;
579
+ }
580
+ if (key === "transform" && typeof value === "string" && value.includes("translate(")) {
581
+ continue;
582
+ }
583
+ filtered[key] = value;
584
+ }
585
+ return filtered;
586
+ }
364
587
  function buildElementStyles(element, context) {
365
588
  const config = element.configuration || {};
366
589
  const combined = {};
367
590
  if (element.style) {
368
- Object.assign(combined, element.style);
591
+ Object.assign(combined, filterCanvasStyles(element.style));
369
592
  }
370
593
  if (config.style) {
371
- Object.assign(combined, config.style);
594
+ Object.assign(combined, filterCanvasStyles(config.style));
372
595
  }
373
596
  if (config.cssVariables) {
374
- Object.assign(combined, config.cssVariables);
597
+ Object.assign(combined, filterCanvasStyles(config.cssVariables));
375
598
  }
376
599
  return processStyles(combined, context);
377
600
  }
@@ -594,18 +817,84 @@ function createElement(element, context, eventHandlers) {
594
817
  attachEventHandlers(domElement, element.i, eventHandlers, elementState);
595
818
  return elementState;
596
819
  }
597
- function renderElement(element, tree, context, eventHandlers, elementStates) {
820
+ var globalRenderingStack = /* @__PURE__ */ new Set();
821
+ function renderComponentRef(element, container, context, state) {
822
+ const config = element.configuration;
823
+ if (!config?.componentViewRef) return void 0;
824
+ const refId = config.componentViewRef;
825
+ const refVersion = config.componentViewVersion;
826
+ if (globalRenderingStack.has(refId)) {
827
+ console.warn(`Circular dependency detected: ${refId} is already being rendered`);
828
+ const placeholder = document.createElement("div");
829
+ placeholder.setAttribute("data-servly-circular", refId);
830
+ placeholder.textContent = `[Circular: ${refId}]`;
831
+ container.appendChild(placeholder);
832
+ return void 0;
833
+ }
834
+ let component;
835
+ if (state.componentRegistry) {
836
+ component = state.componentRegistry.get(refId, refVersion);
837
+ }
838
+ if (!component) {
839
+ const placeholder = document.createElement("div");
840
+ placeholder.setAttribute("data-servly-loading", refId);
841
+ placeholder.className = "servly-loading";
842
+ container.appendChild(placeholder);
843
+ if (state.onDependencyNeeded) {
844
+ state.onDependencyNeeded(refId, refVersion).then((loaded) => {
845
+ if (loaded && state.componentRegistry) {
846
+ state.componentRegistry.set(refId, refVersion || "latest", loaded);
847
+ container.innerHTML = "";
848
+ renderComponentRef(element, container, context, state);
849
+ }
850
+ }).catch((err) => {
851
+ console.error(`Failed to load dependency ${refId}:`, err);
852
+ placeholder.textContent = `[Failed to load: ${refId}]`;
853
+ placeholder.className = "servly-error";
854
+ });
855
+ }
856
+ return void 0;
857
+ }
858
+ const refProps = config.componentViewProps ? resolveTemplatesDeep(config.componentViewProps, context) : {};
859
+ globalRenderingStack.add(refId);
860
+ const refContext = {
861
+ props: refProps,
862
+ state: context.state,
863
+ context: context.context
864
+ };
865
+ try {
866
+ const result = render({
867
+ container,
868
+ elements: component.layout,
869
+ context: refContext,
870
+ componentRegistry: state.componentRegistry,
871
+ onDependencyNeeded: state.onDependencyNeeded
872
+ });
873
+ return result;
874
+ } finally {
875
+ globalRenderingStack.delete(refId);
876
+ }
877
+ }
878
+ function renderElement(element, tree, context, eventHandlers, elementStates, state) {
598
879
  const elementState = createElement(element, context, eventHandlers);
599
880
  elementStates.set(element.i, elementState);
881
+ const config = element.configuration;
882
+ if (config?.componentViewRef) {
883
+ const nestedResult = renderComponentRef(element, elementState.domElement, context, state);
884
+ if (nestedResult) {
885
+ elementState.nestedRender = nestedResult;
886
+ }
887
+ return elementState.domElement;
888
+ }
600
889
  const children = tree.get(element.i) || [];
601
890
  for (const child of children) {
602
- const childElement = renderElement(child, tree, context, eventHandlers, elementStates);
891
+ const childElement = renderElement(child, tree, context, eventHandlers, elementStates, state);
603
892
  elementState.domElement.appendChild(childElement);
604
893
  }
605
894
  return elementState.domElement;
606
895
  }
607
896
  function render(options) {
608
- const { container, elements, context, eventHandlers } = options;
897
+ const { container, elements, context, eventHandlers, componentRegistry, onDependencyNeeded } = options;
609
898
  const tree = buildTree(elements);
610
899
  const rootElements = elements.filter((el) => !el.parent || el.parent === null);
611
900
  const state = {
@@ -614,7 +903,10 @@ function render(options) {
614
903
  context,
615
904
  eventHandlers,
616
905
  elementStates: /* @__PURE__ */ new Map(),
617
- rootElement: null
906
+ rootElement: null,
907
+ componentRegistry,
908
+ onDependencyNeeded,
909
+ renderingStack: /* @__PURE__ */ new Set()
618
910
  };
619
911
  container.innerHTML = "";
620
912
  if (rootElements.length === 0) {
@@ -630,14 +922,15 @@ function render(options) {
630
922
  tree,
631
923
  context,
632
924
  eventHandlers,
633
- state.elementStates
925
+ state.elementStates,
926
+ state
634
927
  );
635
928
  container.appendChild(state.rootElement);
636
929
  } else {
637
930
  const wrapper = document.createElement("div");
638
931
  wrapper.setAttribute("data-servly-wrapper", "true");
639
932
  for (const root of rootElements) {
640
- const rootElement = renderElement(root, tree, context, eventHandlers, state.elementStates);
933
+ const rootElement = renderElement(root, tree, context, eventHandlers, state.elementStates, state);
641
934
  wrapper.appendChild(rootElement);
642
935
  }
643
936
  state.rootElement = wrapper;
@@ -675,6 +968,9 @@ function update(state, newContext) {
675
968
  function destroy(state) {
676
969
  for (const elementState of state.elementStates.values()) {
677
970
  detachEventHandlers(elementState);
971
+ if (elementState.nestedRender) {
972
+ elementState.nestedRender.destroy();
973
+ }
678
974
  }
679
975
  state.elementStates.clear();
680
976
  if (state.rootElement && state.rootElement.parentNode) {
@@ -682,6 +978,66 @@ function destroy(state) {
682
978
  }
683
979
  state.rootElement = null;
684
980
  }
981
+ function renderDynamicList(options) {
982
+ const {
983
+ targetContainer,
984
+ blueprint,
985
+ blueprintVersion,
986
+ data,
987
+ renderType = "renderInto",
988
+ itemKey = "item",
989
+ indexKey = "index",
990
+ componentRegistry,
991
+ context = { props: {} }
992
+ } = options;
993
+ let container;
994
+ if (typeof targetContainer === "string") {
995
+ container = document.querySelector(targetContainer);
996
+ } else {
997
+ container = targetContainer;
998
+ }
999
+ if (!container) {
1000
+ console.error(`renderDynamicList: Container not found: ${targetContainer}`);
1001
+ return [];
1002
+ }
1003
+ const blueprintComponent = componentRegistry.get(blueprint, blueprintVersion);
1004
+ if (!blueprintComponent) {
1005
+ console.error(`renderDynamicList: Blueprint not found: ${blueprint}`);
1006
+ return [];
1007
+ }
1008
+ if (renderType === "renderInto") {
1009
+ container.innerHTML = "";
1010
+ }
1011
+ const results = [];
1012
+ const fragment = document.createDocumentFragment();
1013
+ data.forEach((item, index) => {
1014
+ const itemContainer = document.createElement("div");
1015
+ itemContainer.setAttribute("data-servly-list-item", String(index));
1016
+ const itemContext = {
1017
+ props: {
1018
+ ...context.props,
1019
+ [itemKey]: item,
1020
+ [indexKey]: index
1021
+ },
1022
+ state: context.state,
1023
+ context: context.context
1024
+ };
1025
+ const result = render({
1026
+ container: itemContainer,
1027
+ elements: blueprintComponent.layout,
1028
+ context: itemContext,
1029
+ componentRegistry
1030
+ });
1031
+ results.push(result);
1032
+ fragment.appendChild(itemContainer);
1033
+ });
1034
+ if (renderType === "prepend") {
1035
+ container.insertBefore(fragment, container.firstChild);
1036
+ } else {
1037
+ container.appendChild(fragment);
1038
+ }
1039
+ return results;
1040
+ }
685
1041
 
686
1042
  // src/cache.ts
687
1043
  var DEFAULT_CACHE_CONFIG = {
@@ -927,126 +1283,15 @@ function invalidateCache(id, version, config = DEFAULT_CACHE_CONFIG) {
927
1283
  }
928
1284
  }
929
1285
 
930
- // src/version.ts
931
- function parseVersion(version) {
932
- const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
933
- if (!match) return null;
934
- return {
935
- major: parseInt(match[1], 10),
936
- minor: parseInt(match[2], 10),
937
- patch: parseInt(match[3], 10)
938
- };
939
- }
940
- function compareVersions(a, b) {
941
- const parsedA = parseVersion(a);
942
- const parsedB = parseVersion(b);
943
- if (!parsedA || !parsedB) return 0;
944
- if (parsedA.major !== parsedB.major) {
945
- return parsedA.major > parsedB.major ? 1 : -1;
946
- }
947
- if (parsedA.minor !== parsedB.minor) {
948
- return parsedA.minor > parsedB.minor ? 1 : -1;
949
- }
950
- if (parsedA.patch !== parsedB.patch) {
951
- return parsedA.patch > parsedB.patch ? 1 : -1;
952
- }
953
- return 0;
954
- }
955
- function satisfiesVersion(version, specifier) {
956
- if (specifier === "latest" || specifier === "*") {
957
- return true;
958
- }
959
- const parsed = parseVersion(version);
960
- if (!parsed) return false;
961
- if (/^\d+\.\d+\.\d+$/.test(specifier)) {
962
- return version === specifier;
963
- }
964
- if (specifier.startsWith("^")) {
965
- const specParsed = parseVersion(specifier.slice(1));
966
- if (!specParsed) return false;
967
- if (parsed.major !== specParsed.major) return false;
968
- if (parsed.major === 0) {
969
- return parsed.minor === specParsed.minor && parsed.patch >= specParsed.patch;
970
- }
971
- return compareVersions(version, specifier.slice(1)) >= 0;
972
- }
973
- if (specifier.startsWith("~")) {
974
- const specParsed = parseVersion(specifier.slice(1));
975
- if (!specParsed) return false;
976
- return parsed.major === specParsed.major && parsed.minor === specParsed.minor && parsed.patch >= specParsed.patch;
977
- }
978
- if (specifier.startsWith(">=")) {
979
- return compareVersions(version, specifier.slice(2)) >= 0;
980
- }
981
- if (specifier.startsWith(">")) {
982
- return compareVersions(version, specifier.slice(1)) > 0;
983
- }
984
- if (specifier.startsWith("<=")) {
985
- return compareVersions(version, specifier.slice(2)) <= 0;
986
- }
987
- if (specifier.startsWith("<")) {
988
- return compareVersions(version, specifier.slice(1)) < 0;
989
- }
990
- return false;
991
- }
992
- function resolveVersion(versions, specifier = "latest") {
993
- if (versions.length === 0) {
994
- return null;
995
- }
996
- const sorted = [...versions].sort((a, b) => compareVersions(b, a));
997
- if (specifier === "latest" || specifier === "*") {
998
- return sorted[0];
999
- }
1000
- if (/^\d+\.\d+\.\d+$/.test(specifier)) {
1001
- return versions.includes(specifier) ? specifier : null;
1002
- }
1003
- for (const version of sorted) {
1004
- if (satisfiesVersion(version, specifier)) {
1005
- return version;
1006
- }
1007
- }
1008
- return null;
1009
- }
1010
- function bumpVersion(currentVersion, bumpType) {
1011
- const parsed = parseVersion(currentVersion);
1012
- if (!parsed) return "1.0.0";
1013
- switch (bumpType) {
1014
- case "major":
1015
- return `${parsed.major + 1}.0.0`;
1016
- case "minor":
1017
- return `${parsed.major}.${parsed.minor + 1}.0`;
1018
- case "patch":
1019
- return `${parsed.major}.${parsed.minor}.${parsed.patch + 1}`;
1020
- default:
1021
- return currentVersion;
1022
- }
1023
- }
1024
- function isValidSpecifier(specifier) {
1025
- if (specifier === "latest" || specifier === "*") {
1026
- return true;
1027
- }
1028
- if (/^\d+\.\d+\.\d+$/.test(specifier)) {
1029
- return true;
1030
- }
1031
- if (/^[\^~><]=?\d+\.\d+\.\d+$/.test(specifier)) {
1032
- return true;
1033
- }
1034
- return false;
1035
- }
1036
- function formatVersion(version) {
1037
- const parsed = parseVersion(version);
1038
- if (!parsed) return version;
1039
- return `v${parsed.major}.${parsed.minor}.${parsed.patch}`;
1040
- }
1041
-
1042
1286
  // src/fetcher.ts
1287
+ init_registry();
1043
1288
  var DEFAULT_RETRY_CONFIG = {
1044
1289
  maxRetries: 3,
1045
1290
  initialDelay: 1e3,
1046
1291
  maxDelay: 1e4,
1047
1292
  backoffMultiplier: 2
1048
1293
  };
1049
- var registryBaseUrl = "/api/components";
1294
+ var registryBaseUrl = "/api/views/registry";
1050
1295
  function setRegistryUrl(url) {
1051
1296
  registryBaseUrl = url;
1052
1297
  }
@@ -1080,19 +1325,15 @@ async function resolveVersionFromApi(id, specifier, apiKey) {
1080
1325
  }
1081
1326
  try {
1082
1327
  const response = await fetch(
1083
- `${baseUrl}/${id}/versions?specifier=${encodeURIComponent(specifier)}`,
1328
+ `${baseUrl}/${id}/resolve?specifier=${encodeURIComponent(specifier)}`,
1084
1329
  { headers }
1085
1330
  );
1086
1331
  if (!response.ok) {
1087
1332
  throw new Error(`Failed to resolve version: ${response.statusText}`);
1088
1333
  }
1089
1334
  const data = await response.json();
1090
- if (data.success && data.data?.resolvedVersion) {
1091
- return data.data.resolvedVersion;
1092
- }
1093
- if (data.data?.versions) {
1094
- const resolved = resolveVersion(data.data.versions, specifier);
1095
- if (resolved) return resolved;
1335
+ if (data.success && data.data?.resolved) {
1336
+ return data.data.resolved;
1096
1337
  }
1097
1338
  throw new Error(data.error || "Failed to resolve version");
1098
1339
  } catch (error) {
@@ -1100,7 +1341,7 @@ async function resolveVersionFromApi(id, specifier, apiKey) {
1100
1341
  return "latest";
1101
1342
  }
1102
1343
  }
1103
- async function fetchFromRegistry(id, version, apiKey) {
1344
+ async function fetchFromRegistry(id, version, apiKey, includeBundle) {
1104
1345
  const baseUrl = getRegistryUrl();
1105
1346
  const headers = {
1106
1347
  "Content-Type": "application/json"
@@ -1108,25 +1349,39 @@ async function fetchFromRegistry(id, version, apiKey) {
1108
1349
  if (apiKey) {
1109
1350
  headers["Authorization"] = `Bearer ${apiKey}`;
1110
1351
  }
1111
- const url = version && version !== "latest" ? `${baseUrl}/${id}/versions/${version}` : `${baseUrl}/${id}`;
1352
+ let url;
1353
+ if (version && version !== "latest" && /^\d+\.\d+\.\d+/.test(version)) {
1354
+ url = `${baseUrl}/${id}/versions/${version}`;
1355
+ } else if (version && version !== "latest") {
1356
+ url = `${baseUrl}/${id}?version=${encodeURIComponent(version)}`;
1357
+ } else {
1358
+ url = `${baseUrl}/${id}`;
1359
+ }
1360
+ if (includeBundle) {
1361
+ url += (url.includes("?") ? "&" : "?") + "bundle=true";
1362
+ }
1112
1363
  const response = await fetch(url, { headers });
1113
1364
  if (!response.ok) {
1114
1365
  if (response.status === 404) {
1115
- throw new Error(`Component not found: ${id}@${version}`);
1366
+ throw new Error(`View not found: ${id}@${version}`);
1116
1367
  }
1117
1368
  if (response.status === 401) {
1118
1369
  throw new Error("Unauthorized: Invalid or missing API key");
1119
1370
  }
1120
1371
  if (response.status === 403) {
1121
- throw new Error("Forbidden: Access denied to this component");
1372
+ throw new Error("Forbidden: Access denied to this view");
1122
1373
  }
1123
- throw new Error(`Failed to fetch component: ${response.statusText}`);
1374
+ throw new Error(`Failed to fetch view: ${response.statusText}`);
1124
1375
  }
1125
1376
  const data = await response.json();
1126
1377
  if (!data.success || !data.data) {
1127
- throw new Error(data.error || "Failed to fetch component data");
1378
+ throw new Error(data.error || "Failed to fetch view data");
1128
1379
  }
1129
- return data.data;
1380
+ const result = data.data;
1381
+ if (result.viewId && !result.id) {
1382
+ result.id = result.viewId;
1383
+ }
1384
+ return result;
1130
1385
  }
1131
1386
  async function fetchComponent(id, options = {}) {
1132
1387
  const {
@@ -1136,7 +1391,9 @@ async function fetchComponent(id, options = {}) {
1136
1391
  cacheConfig = DEFAULT_CACHE_CONFIG,
1137
1392
  retryConfig = {},
1138
1393
  forceRefresh = false,
1139
- signal
1394
+ signal,
1395
+ bundleStrategy = "eager",
1396
+ includeBundle = true
1140
1397
  } = options;
1141
1398
  const fullRetryConfig = {
1142
1399
  ...DEFAULT_RETRY_CONFIG,
@@ -1145,10 +1402,15 @@ async function fetchComponent(id, options = {}) {
1145
1402
  if (!forceRefresh) {
1146
1403
  const cached = getFromCache(id, version, cacheStrategy, cacheConfig);
1147
1404
  if (cached) {
1405
+ let registry;
1406
+ if (cached.bundle) {
1407
+ registry = buildRegistryFromBundle(cached);
1408
+ }
1148
1409
  return {
1149
1410
  data: cached,
1150
1411
  fromCache: true,
1151
- version: cached.version
1412
+ version: cached.version,
1413
+ registry
1152
1414
  };
1153
1415
  }
1154
1416
  }
@@ -1156,28 +1418,46 @@ async function fetchComponent(id, options = {}) {
1156
1418
  if (!forceRefresh && resolvedVersion !== version) {
1157
1419
  const cached = getFromCache(id, resolvedVersion, cacheStrategy, cacheConfig);
1158
1420
  if (cached) {
1421
+ let registry;
1422
+ if (cached.bundle) {
1423
+ registry = buildRegistryFromBundle(cached);
1424
+ }
1159
1425
  return {
1160
1426
  data: cached,
1161
1427
  fromCache: true,
1162
- version: resolvedVersion
1428
+ version: resolvedVersion,
1429
+ registry
1163
1430
  };
1164
1431
  }
1165
1432
  }
1166
1433
  let lastError = null;
1434
+ const shouldIncludeBundle = bundleStrategy !== "none" && includeBundle;
1167
1435
  for (let attempt = 0; attempt <= fullRetryConfig.maxRetries; attempt++) {
1168
1436
  if (signal?.aborted) {
1169
1437
  throw new Error("Fetch aborted");
1170
1438
  }
1171
1439
  try {
1172
- const data = await fetchFromRegistry(id, resolvedVersion, apiKey);
1440
+ const data = await fetchFromRegistry(id, resolvedVersion, apiKey, shouldIncludeBundle);
1173
1441
  setInCache(id, resolvedVersion, data, cacheStrategy, cacheConfig);
1174
1442
  if (version !== resolvedVersion) {
1175
1443
  setInCache(id, version, data, cacheStrategy, cacheConfig);
1176
1444
  }
1445
+ let registry;
1446
+ let pendingDependencies;
1447
+ if (data.bundle) {
1448
+ registry = buildRegistryFromBundle(data);
1449
+ } else if (data.dependencies && bundleStrategy === "lazy") {
1450
+ pendingDependencies = Object.entries(data.dependencies).map(([depId, entry]) => ({
1451
+ id: depId,
1452
+ version: entry.resolved || entry.version
1453
+ }));
1454
+ }
1177
1455
  return {
1178
1456
  data,
1179
1457
  fromCache: false,
1180
- version: resolvedVersion
1458
+ version: resolvedVersion,
1459
+ registry,
1460
+ pendingDependencies
1181
1461
  };
1182
1462
  } catch (error) {
1183
1463
  lastError = error instanceof Error ? error : new Error(String(error));
@@ -1192,10 +1472,60 @@ async function fetchComponent(id, options = {}) {
1192
1472
  }
1193
1473
  throw lastError || new Error("Failed to fetch component");
1194
1474
  }
1475
+ async function fetchComponentWithDependencies(id, options = {}) {
1476
+ const result = await fetchComponent(id, { ...options, includeBundle: true });
1477
+ if (result.pendingDependencies && result.pendingDependencies.length > 0) {
1478
+ const { createRegistry: createRegistry2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
1479
+ const registry = result.registry || createRegistry2();
1480
+ await Promise.all(
1481
+ result.pendingDependencies.map(async (dep) => {
1482
+ try {
1483
+ const depResult = await fetchComponent(dep.id, {
1484
+ ...options,
1485
+ version: dep.version,
1486
+ bundleStrategy: "none"
1487
+ // Don't recursively bundle
1488
+ });
1489
+ registry.set(dep.id, depResult.version, {
1490
+ layout: depResult.data.layout,
1491
+ propsInterface: depResult.data.propsInterface
1492
+ });
1493
+ } catch (err) {
1494
+ console.warn(`Failed to fetch dependency ${dep.id}:`, err);
1495
+ }
1496
+ })
1497
+ );
1498
+ result.registry = registry;
1499
+ result.pendingDependencies = void 0;
1500
+ }
1501
+ return result;
1502
+ }
1503
+ async function batchFetchComponents(components, options = {}) {
1504
+ const baseUrl = getRegistryUrl();
1505
+ const headers = {
1506
+ "Content-Type": "application/json"
1507
+ };
1508
+ if (options.apiKey) {
1509
+ headers["Authorization"] = `Bearer ${options.apiKey}`;
1510
+ }
1511
+ const response = await fetch(`${baseUrl}/batch`, {
1512
+ method: "POST",
1513
+ headers,
1514
+ body: JSON.stringify({ components })
1515
+ });
1516
+ if (!response.ok) {
1517
+ throw new Error(`Batch fetch failed: ${response.statusText}`);
1518
+ }
1519
+ const data = await response.json();
1520
+ if (!data.success || !data.data) {
1521
+ throw new Error(data.error || "Batch fetch failed");
1522
+ }
1523
+ return data.data;
1524
+ }
1195
1525
  async function prefetchComponents(ids, options = {}) {
1196
1526
  const promises = ids.map(
1197
1527
  ({ id, version }) => fetchComponent(id, { ...options, version }).catch((error) => {
1198
- console.warn(`Failed to prefetch component ${id}:`, error);
1528
+ console.warn(`Failed to prefetch view ${id}:`, error);
1199
1529
  })
1200
1530
  );
1201
1531
  await Promise.all(promises);
@@ -1204,19 +1534,164 @@ async function isComponentAvailable(id, version = "latest", options = {}) {
1204
1534
  const { cacheStrategy = "memory", cacheConfig = DEFAULT_CACHE_CONFIG } = options;
1205
1535
  const cached = getFromCache(id, version, cacheStrategy, cacheConfig);
1206
1536
  if (cached) {
1207
- return { available: true, cached: true };
1537
+ return { available: true, cached: true, version: cached.version };
1538
+ }
1539
+ const baseUrl = getRegistryUrl();
1540
+ const headers = {
1541
+ "Content-Type": "application/json"
1542
+ };
1543
+ if (options.apiKey) {
1544
+ headers["Authorization"] = `Bearer ${options.apiKey}`;
1208
1545
  }
1209
1546
  try {
1210
- await fetchComponent(id, {
1211
- ...options,
1212
- version,
1213
- retryConfig: { maxRetries: 0 }
1214
- });
1215
- return { available: true, cached: false };
1547
+ const url = version && version !== "latest" ? `${baseUrl}/${id}/available?version=${encodeURIComponent(version)}` : `${baseUrl}/${id}/available`;
1548
+ const response = await fetch(url, { headers });
1549
+ if (!response.ok) {
1550
+ return { available: false, cached: false };
1551
+ }
1552
+ const data = await response.json();
1553
+ if (data.success && data.data) {
1554
+ return data.data;
1555
+ }
1556
+ return { available: false, cached: false };
1216
1557
  } catch {
1217
1558
  return { available: false, cached: false };
1218
1559
  }
1219
1560
  }
1561
+ async function getDependencyTree(id, options = {}) {
1562
+ const baseUrl = getRegistryUrl();
1563
+ const headers = {
1564
+ "Content-Type": "application/json"
1565
+ };
1566
+ if (options.apiKey) {
1567
+ headers["Authorization"] = `Bearer ${options.apiKey}`;
1568
+ }
1569
+ const params = new URLSearchParams();
1570
+ if (options.version) params.set("version", options.version);
1571
+ if (options.depth) params.set("depth", String(options.depth));
1572
+ const url = `${baseUrl}/${id}/dependencies${params.toString() ? "?" + params.toString() : ""}`;
1573
+ const response = await fetch(url, { headers });
1574
+ if (!response.ok) {
1575
+ throw new Error(`Failed to get dependency tree: ${response.statusText}`);
1576
+ }
1577
+ const data = await response.json();
1578
+ if (!data.success || !data.data) {
1579
+ throw new Error(data.error || "Failed to get dependency tree");
1580
+ }
1581
+ return data.data;
1582
+ }
1583
+
1584
+ // src/version.ts
1585
+ function parseVersion(version) {
1586
+ const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
1587
+ if (!match) return null;
1588
+ return {
1589
+ major: parseInt(match[1], 10),
1590
+ minor: parseInt(match[2], 10),
1591
+ patch: parseInt(match[3], 10)
1592
+ };
1593
+ }
1594
+ function compareVersions(a, b) {
1595
+ const parsedA = parseVersion(a);
1596
+ const parsedB = parseVersion(b);
1597
+ if (!parsedA || !parsedB) return 0;
1598
+ if (parsedA.major !== parsedB.major) {
1599
+ return parsedA.major > parsedB.major ? 1 : -1;
1600
+ }
1601
+ if (parsedA.minor !== parsedB.minor) {
1602
+ return parsedA.minor > parsedB.minor ? 1 : -1;
1603
+ }
1604
+ if (parsedA.patch !== parsedB.patch) {
1605
+ return parsedA.patch > parsedB.patch ? 1 : -1;
1606
+ }
1607
+ return 0;
1608
+ }
1609
+ function satisfiesVersion(version, specifier) {
1610
+ if (specifier === "latest" || specifier === "*") {
1611
+ return true;
1612
+ }
1613
+ const parsed = parseVersion(version);
1614
+ if (!parsed) return false;
1615
+ if (/^\d+\.\d+\.\d+$/.test(specifier)) {
1616
+ return version === specifier;
1617
+ }
1618
+ if (specifier.startsWith("^")) {
1619
+ const specParsed = parseVersion(specifier.slice(1));
1620
+ if (!specParsed) return false;
1621
+ if (parsed.major !== specParsed.major) return false;
1622
+ if (parsed.major === 0) {
1623
+ return parsed.minor === specParsed.minor && parsed.patch >= specParsed.patch;
1624
+ }
1625
+ return compareVersions(version, specifier.slice(1)) >= 0;
1626
+ }
1627
+ if (specifier.startsWith("~")) {
1628
+ const specParsed = parseVersion(specifier.slice(1));
1629
+ if (!specParsed) return false;
1630
+ return parsed.major === specParsed.major && parsed.minor === specParsed.minor && parsed.patch >= specParsed.patch;
1631
+ }
1632
+ if (specifier.startsWith(">=")) {
1633
+ return compareVersions(version, specifier.slice(2)) >= 0;
1634
+ }
1635
+ if (specifier.startsWith(">")) {
1636
+ return compareVersions(version, specifier.slice(1)) > 0;
1637
+ }
1638
+ if (specifier.startsWith("<=")) {
1639
+ return compareVersions(version, specifier.slice(2)) <= 0;
1640
+ }
1641
+ if (specifier.startsWith("<")) {
1642
+ return compareVersions(version, specifier.slice(1)) < 0;
1643
+ }
1644
+ return false;
1645
+ }
1646
+ function resolveVersion(versions, specifier = "latest") {
1647
+ if (versions.length === 0) {
1648
+ return null;
1649
+ }
1650
+ const sorted = [...versions].sort((a, b) => compareVersions(b, a));
1651
+ if (specifier === "latest" || specifier === "*") {
1652
+ return sorted[0];
1653
+ }
1654
+ if (/^\d+\.\d+\.\d+$/.test(specifier)) {
1655
+ return versions.includes(specifier) ? specifier : null;
1656
+ }
1657
+ for (const version of sorted) {
1658
+ if (satisfiesVersion(version, specifier)) {
1659
+ return version;
1660
+ }
1661
+ }
1662
+ return null;
1663
+ }
1664
+ function bumpVersion(currentVersion, bumpType) {
1665
+ const parsed = parseVersion(currentVersion);
1666
+ if (!parsed) return "1.0.0";
1667
+ switch (bumpType) {
1668
+ case "major":
1669
+ return `${parsed.major + 1}.0.0`;
1670
+ case "minor":
1671
+ return `${parsed.major}.${parsed.minor + 1}.0`;
1672
+ case "patch":
1673
+ return `${parsed.major}.${parsed.minor}.${parsed.patch + 1}`;
1674
+ default:
1675
+ return currentVersion;
1676
+ }
1677
+ }
1678
+ function isValidSpecifier(specifier) {
1679
+ if (specifier === "latest" || specifier === "*") {
1680
+ return true;
1681
+ }
1682
+ if (/^\d+\.\d+\.\d+$/.test(specifier)) {
1683
+ return true;
1684
+ }
1685
+ if (/^[\^~><]=?\d+\.\d+\.\d+$/.test(specifier)) {
1686
+ return true;
1687
+ }
1688
+ return false;
1689
+ }
1690
+ function formatVersion(version) {
1691
+ const parsed = parseVersion(version);
1692
+ if (!parsed) return version;
1693
+ return `v${parsed.major}.${parsed.minor}.${parsed.patch}`;
1694
+ }
1220
1695
 
1221
1696
  // src/testRunner.ts
1222
1697
  function runTestCase(elements, testCase, container) {
@@ -1472,26 +1947,38 @@ function getSampleValue(def) {
1472
1947
  return def.defaultValue;
1473
1948
  }
1474
1949
  }
1950
+
1951
+ // src/index.ts
1952
+ init_registry();
1475
1953
  // Annotate the CommonJS export names for ESM import in node:
1476
1954
  0 && (module.exports = {
1477
1955
  DEFAULT_CACHE_CONFIG,
1478
1956
  DEFAULT_RETRY_CONFIG,
1479
1957
  applyStyles,
1958
+ batchFetchComponents,
1480
1959
  buildClassName,
1481
1960
  buildElementStyles,
1961
+ buildRegistryFromBundle,
1482
1962
  bumpVersion,
1483
1963
  camelToKebab,
1484
1964
  clearAllCaches,
1485
1965
  clearLocalStorageCache,
1486
1966
  clearMemoryCache,
1487
1967
  clearStyles,
1968
+ collectAllDependencies,
1488
1969
  compareVersions,
1970
+ createRegistry,
1971
+ detectCircularDependencies,
1489
1972
  extractBindingKeys,
1973
+ extractDependencies,
1974
+ extractDependenciesFromCode,
1490
1975
  fetchComponent,
1976
+ fetchComponentWithDependencies,
1491
1977
  formatStyleValue,
1492
1978
  formatVersion,
1493
1979
  generateTestCases,
1494
1980
  getCacheKey,
1981
+ getDependencyTree,
1495
1982
  getFromCache,
1496
1983
  getMemoryCacheSize,
1497
1984
  getRegistryUrl,
@@ -1503,6 +1990,7 @@ function getSampleValue(def) {
1503
1990
  prefetchComponents,
1504
1991
  processStyles,
1505
1992
  render,
1993
+ renderDynamicList,
1506
1994
  resolveBindingPath,
1507
1995
  resolveTemplate,
1508
1996
  resolveTemplateValue,