dolphin-client 1.0.9 → 1.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
@@ -41,7 +41,7 @@ var APIHandler = class {
41
41
  target.patch = (pathOrBody, bodyOrOptions, options) => typeof pathOrBody === "string" ? this.request("PATCH", pathOrBody, bodyOrOptions, options) : this.request("PATCH", joined, pathOrBody, bodyOrOptions);
42
42
  target.del = (pathOrOptions, options) => typeof pathOrOptions === "string" ? this.request("DELETE", pathOrOptions, null, options) : this.request("DELETE", joined, null, pathOrOptions);
43
43
  target.request = (method, subPath, body, options) => {
44
- const finalPath = subPath ? `${joined}/${subPath.startsWith("/") ? subPath.slice(1) : subPath}` : joined;
44
+ const finalPath = subPath ? joined ? `${joined}/${subPath.startsWith("/") ? subPath.slice(1) : subPath}` : subPath : joined;
45
45
  return this.request(method, finalPath, body, options);
46
46
  };
47
47
  target.requestDirect = (method, path, body, options) => {
@@ -225,8 +225,9 @@ var APIHandler = class {
225
225
  const _isRetry = options._isRetry === true;
226
226
  let finalMethod = method.toUpperCase();
227
227
  let finalBody = body;
228
+ const hasBody = !["GET", "HEAD"].includes(finalMethod);
228
229
  const headers = {
229
- "Content-Type": "application/json",
230
+ ...hasBody ? { "Content-Type": "application/json" } : {},
230
231
  ...options.headers || {}
231
232
  };
232
233
  if (["PUT", "PATCH", "DELETE"].includes(finalMethod)) {
@@ -996,6 +997,82 @@ function attachDOMBinding(clientProto) {
996
997
  function escapeRegExp(str) {
997
998
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
998
999
  }
1000
+ function evaluateExpression(expr, ctx) {
1001
+ if (!ctx || typeof ctx !== "object") return void 0;
1002
+ try {
1003
+ const safeCtx = new Proxy(ctx, {
1004
+ has(target, prop) {
1005
+ return true;
1006
+ },
1007
+ get(target, prop) {
1008
+ if (typeof prop === "string") {
1009
+ if (prop in target) return target[prop];
1010
+ if (typeof globalThis !== "undefined" && prop in globalThis) return globalThis[prop];
1011
+ if (typeof window !== "undefined" && prop in window) return window[prop];
1012
+ }
1013
+ return void 0;
1014
+ }
1015
+ });
1016
+ const fn = new Function("ctx", `with(ctx) { return (${expr}); }`);
1017
+ return fn(safeCtx);
1018
+ } catch {
1019
+ return ctx[expr];
1020
+ }
1021
+ }
1022
+ function splitByUnquotedChar(str, char) {
1023
+ const parts = [];
1024
+ let current = "";
1025
+ let inSingleQuote = false;
1026
+ let inDoubleQuote = false;
1027
+ let inBacktick = false;
1028
+ let depth = 0;
1029
+ for (let i = 0; i < str.length; i++) {
1030
+ const c = str[i];
1031
+ if (c === "'" && !inDoubleQuote && !inBacktick) {
1032
+ inSingleQuote = !inSingleQuote;
1033
+ } else if (c === '"' && !inSingleQuote && !inBacktick) {
1034
+ inDoubleQuote = !inDoubleQuote;
1035
+ } else if (c === "`" && !inSingleQuote && !inDoubleQuote) {
1036
+ inBacktick = !inBacktick;
1037
+ } else if (c === "(" || c === "[" || c === "{") {
1038
+ if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth++;
1039
+ } else if (c === ")" || c === "]" || c === "}") {
1040
+ if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth--;
1041
+ }
1042
+ if (c === char && !inSingleQuote && !inDoubleQuote && !inBacktick && depth === 0) {
1043
+ parts.push(current);
1044
+ current = "";
1045
+ } else {
1046
+ current += c;
1047
+ }
1048
+ }
1049
+ parts.push(current);
1050
+ return parts;
1051
+ }
1052
+ function splitFirstUnquotedColon(str) {
1053
+ let inSingleQuote = false;
1054
+ let inDoubleQuote = false;
1055
+ let inBacktick = false;
1056
+ let depth = 0;
1057
+ for (let i = 0; i < str.length; i++) {
1058
+ const c = str[i];
1059
+ if (c === "'" && !inDoubleQuote && !inBacktick) {
1060
+ inSingleQuote = !inSingleQuote;
1061
+ } else if (c === '"' && !inSingleQuote && !inBacktick) {
1062
+ inDoubleQuote = !inDoubleQuote;
1063
+ } else if (c === "`" && !inSingleQuote && !inDoubleQuote) {
1064
+ inBacktick = !inBacktick;
1065
+ } else if (c === "(" || c === "[" || c === "{") {
1066
+ if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth++;
1067
+ } else if (c === ")" || c === "]" || c === "}") {
1068
+ if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth--;
1069
+ }
1070
+ if (c === ":" && !inSingleQuote && !inDoubleQuote && !inBacktick && depth === 0) {
1071
+ return [str.slice(0, i), str.slice(i + 1)];
1072
+ }
1073
+ }
1074
+ return null;
1075
+ }
999
1076
  function resolveTemplate(el) {
1000
1077
  const template = el.getAttribute("data-rt-template");
1001
1078
  if (!template) return null;
@@ -1012,9 +1089,26 @@ function attachDOMBinding(clientProto) {
1012
1089
  if (!templateStr.includes("{#if") && !templateStr.includes("{#each")) {
1013
1090
  let result = templateStr;
1014
1091
  for (let key in context) {
1015
- const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1016
- result = result.replace(new RegExp(`\\{\\{${escapedKey}\\}\\}`, "g"), context[key] !== void 0 && context[key] !== null ? context[key] : "");
1017
- }
1092
+ const escapedKey = key.replace(/[.*+?^$${}()|[\]\\]/g, "\\$&");
1093
+ result = result.replace(new RegExp("\\{\\{" + escapedKey + "\\}}", "g"), context[key] !== void 0 && context[key] !== null ? context[key] : "");
1094
+ }
1095
+ result = result.replace(/\{\{([\s\S]*?)\}\}/g, (match, expr) => {
1096
+ const trimmed = expr.trim();
1097
+ if (!trimmed) return "";
1098
+ if (/^[a-zA-Z_$][a-zA-Z0-9_$]*(?:\??\.[a-zA-Z_$][a-zA-Z0-9_$]*)+$/.test(trimmed)) {
1099
+ const parts = trimmed.split(/\??\./);
1100
+ let val = context;
1101
+ for (const part of parts) {
1102
+ if (val === void 0 || val === null) {
1103
+ val = void 0;
1104
+ break;
1105
+ }
1106
+ val = val[part];
1107
+ }
1108
+ return val !== void 0 && val !== null ? val : "";
1109
+ }
1110
+ return match;
1111
+ });
1018
1112
  return result;
1019
1113
  }
1020
1114
  try {
@@ -1134,7 +1228,9 @@ function attachDOMBinding(clientProto) {
1134
1228
  if (typeof document === "undefined") return html;
1135
1229
  try {
1136
1230
  const parser = new DOMParser();
1137
- const doc = parser.parseFromString(html, "text/html");
1231
+ const hasBodyOrHtml = /<\s*(?:body|html)\b/i.test(html);
1232
+ const parseString = hasBodyOrHtml ? html : `<body>${html}</body>`;
1233
+ const doc = parser.parseFromString(parseString, "text/html");
1138
1234
  const body = doc.body;
1139
1235
  const sanitizeNode = (el) => {
1140
1236
  const tag = el.tagName.toLowerCase();
@@ -1248,7 +1344,7 @@ function attachDOMBinding(clientProto) {
1248
1344
  });
1249
1345
  }
1250
1346
  }
1251
- clientProto.setStoreState = function(storeName, key, val) {
1347
+ clientProto.setStoreState = function(storeName, key, val, originEl) {
1252
1348
  this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
1253
1349
  if (!this.uiStores.has(storeName)) {
1254
1350
  this.uiStores.set(storeName, {});
@@ -1261,6 +1357,7 @@ function attachDOMBinding(clientProto) {
1261
1357
  if (typeof document !== "undefined") {
1262
1358
  const readElements = document.querySelectorAll(`[data-store-read="${storeName}.${key}"]`);
1263
1359
  readElements.forEach((el) => {
1360
+ if (el === originEl) return;
1264
1361
  if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") {
1265
1362
  if (el.type === "checkbox") {
1266
1363
  el.checked = !!val;
@@ -1336,20 +1433,49 @@ function attachDOMBinding(clientProto) {
1336
1433
  if (key) return ctx[key];
1337
1434
  return ctx;
1338
1435
  }
1339
- current = current.parentElement;
1436
+ current = current.parentElement || current.parentNode;
1340
1437
  }
1341
1438
  return null;
1342
1439
  };
1343
1440
  clientProto._executeStoreAction = function(expression, element) {
1344
1441
  this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
1442
+ const parentCtx = element && typeof this.getClosestContext === "function" ? this.getClosestContext(element) : null;
1345
1443
  const context = new Proxy({}, {
1346
1444
  has: (target, prop) => {
1347
1445
  return true;
1348
1446
  },
1349
1447
  get: (target, prop) => {
1350
1448
  if (typeof prop === "string") {
1449
+ if (prop === "log") {
1450
+ return (arg) => {
1451
+ if (arg === void 0) {
1452
+ const allStores = {};
1453
+ this.uiStores.forEach((val, key) => {
1454
+ allStores[key] = { ...val };
1455
+ });
1456
+ console.log(`%c\u{1F4CA} [Dolphin All UI Stores]:`, "color: #06b6d4; font-weight: bold;", allStores);
1457
+ } else if (arg && typeof arg === "object" && arg.__isStoreProxy__) {
1458
+ const storeName = arg.__storeName__;
1459
+ const store = this.uiStores.get(storeName);
1460
+ console.log(`%c\u{1F4CA} [Dolphin Store: ${storeName}]:`, "color: #06b6d4; font-weight: bold;", store ? { ...store } : {});
1461
+ } else {
1462
+ console.log(`%c\u{1F4CA} [Dolphin Log]:`, "color: #06b6d4; font-weight: bold;", arg);
1463
+ }
1464
+ };
1465
+ }
1466
+ if (parentCtx && parentCtx[prop] !== void 0) {
1467
+ return parentCtx[prop];
1468
+ }
1469
+ if (typeof globalThis !== "undefined" && prop in globalThis) {
1470
+ return globalThis[prop];
1471
+ }
1472
+ if (typeof window !== "undefined" && prop in window) {
1473
+ return window[prop];
1474
+ }
1351
1475
  return new Proxy({}, {
1352
1476
  get: (subTarget, subProp) => {
1477
+ if (subProp === "__storeName__") return prop;
1478
+ if (subProp === "__isStoreProxy__") return true;
1353
1479
  if (typeof subProp === "string") {
1354
1480
  return this.getStoreState(prop, subProp);
1355
1481
  }
@@ -1391,7 +1517,7 @@ function attachDOMBinding(clientProto) {
1391
1517
  const storeName = parts[0];
1392
1518
  const key = parts[1];
1393
1519
  const val = e.target.type === "checkbox" ? e.target.checked : e.target.value;
1394
- this.setStoreState(storeName, key, val);
1520
+ this.setStoreState(storeName, key, val, e.target);
1395
1521
  }
1396
1522
  }
1397
1523
  const rules = e.target.getAttribute("data-rt-validate");
@@ -1751,24 +1877,33 @@ function attachDOMBinding(clientProto) {
1751
1877
  const processNode = (node) => {
1752
1878
  if (node.hasAttribute("data-rt-text")) {
1753
1879
  const key = node.getAttribute("data-rt-text");
1754
- if (key && processedPayload[key] !== void 0 && processedPayload[key] !== null) node.textContent = processedPayload[key];
1880
+ if (key) {
1881
+ const val = evaluateExpression(key, processedPayload);
1882
+ if (val !== void 0 && val !== null) node.textContent = val;
1883
+ }
1755
1884
  }
1756
1885
  if (node.hasAttribute("data-rt-html")) {
1757
1886
  const key = node.getAttribute("data-rt-html");
1758
- if (key && processedPayload[key] !== void 0 && processedPayload[key] !== null) {
1759
- node.innerHTML = sanitizeHTML(processedPayload[key]);
1887
+ if (key) {
1888
+ const val = evaluateExpression(key, processedPayload);
1889
+ if (val !== void 0 && val !== null) {
1890
+ node.innerHTML = sanitizeHTML(val);
1891
+ }
1760
1892
  }
1761
1893
  }
1762
1894
  if (node.hasAttribute("data-rt-attr")) {
1763
1895
  const attrStr = node.getAttribute("data-rt-attr");
1764
1896
  if (attrStr) {
1765
- attrStr.split(",").forEach((b) => {
1766
- const parts = b.split(":");
1767
- if (parts.length === 2) {
1768
- const attrName = parts[0].trim();
1769
- const key = parts[1].trim();
1770
- if (attrName && key && processedPayload[key] !== void 0 && processedPayload[key] !== null) {
1771
- node.setAttribute(attrName, processedPayload[key]);
1897
+ splitByUnquotedChar(attrStr, ",").forEach((b) => {
1898
+ const pair = splitFirstUnquotedColon(b);
1899
+ if (pair) {
1900
+ const attrName = pair[0].trim();
1901
+ const key = pair[1].trim();
1902
+ if (attrName && key) {
1903
+ const val = evaluateExpression(key, processedPayload);
1904
+ if (val !== void 0 && val !== null) {
1905
+ node.setAttribute(attrName, val);
1906
+ }
1772
1907
  }
1773
1908
  }
1774
1909
  });
@@ -1777,13 +1912,13 @@ function attachDOMBinding(clientProto) {
1777
1912
  if (node.hasAttribute("data-rt-class")) {
1778
1913
  const classStr = node.getAttribute("data-rt-class");
1779
1914
  if (classStr) {
1780
- classStr.split(",").forEach((b) => {
1781
- const parts = b.split(":");
1782
- if (parts.length === 2) {
1783
- const className = parts[0].trim();
1784
- const key = parts[1].trim();
1915
+ splitByUnquotedChar(classStr, ",").forEach((b) => {
1916
+ const pair = splitFirstUnquotedColon(b);
1917
+ if (pair) {
1918
+ const className = pair[0].trim();
1919
+ const key = pair[1].trim();
1785
1920
  const classNames = className.split(/\s+/).filter(Boolean);
1786
- if (processedPayload[key]) {
1921
+ if (evaluateExpression(key, processedPayload)) {
1787
1922
  classNames.forEach((c) => node.classList.add(c));
1788
1923
  } else {
1789
1924
  classNames.forEach((c) => node.classList.remove(c));
@@ -1795,7 +1930,7 @@ function attachDOMBinding(clientProto) {
1795
1930
  if (node.hasAttribute("data-rt-if")) {
1796
1931
  const key = node.getAttribute("data-rt-if");
1797
1932
  if (key) {
1798
- if (processedPayload[key]) {
1933
+ if (evaluateExpression(key, processedPayload)) {
1799
1934
  node.style.display = "";
1800
1935
  } else {
1801
1936
  node.style.display = "none";
@@ -1805,7 +1940,7 @@ function attachDOMBinding(clientProto) {
1805
1940
  if (node.hasAttribute("data-rt-hide")) {
1806
1941
  const key = node.getAttribute("data-rt-hide");
1807
1942
  if (key) {
1808
- if (processedPayload[key]) {
1943
+ if (evaluateExpression(key, processedPayload)) {
1809
1944
  node.style.display = "none";
1810
1945
  } else {
1811
1946
  node.style.display = "";
@@ -1855,21 +1990,35 @@ function attachDOMBinding(clientProto) {
1855
1990
  return;
1856
1991
  }
1857
1992
  resolvingSet.add(src);
1858
- let promise = componentPromiseCache.get(src);
1993
+ const hashIndex = src.indexOf("#");
1994
+ const url = hashIndex !== -1 ? src.substring(0, hashIndex) : src;
1995
+ const selector = hashIndex !== -1 ? src.substring(hashIndex) : null;
1996
+ let promise = componentPromiseCache.get(url);
1859
1997
  if (!promise) {
1860
- promise = fetch(src).then((res) => {
1998
+ promise = fetch(url).then((res) => {
1861
1999
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
1862
2000
  return res.text();
1863
2001
  });
1864
- promise.catch(() => componentPromiseCache.delete(src));
1865
- componentPromiseCache.set(src, promise);
2002
+ promise.catch(() => componentPromiseCache.delete(url));
2003
+ componentPromiseCache.set(url, promise);
1866
2004
  }
1867
2005
  let content = "";
1868
2006
  try {
1869
2007
  content = await promise;
2008
+ if (selector && typeof DOMParser !== "undefined") {
2009
+ const parser = new DOMParser();
2010
+ const doc = parser.parseFromString(content, "text/html");
2011
+ const targetEl = doc.querySelector(selector);
2012
+ if (targetEl) {
2013
+ content = targetEl.outerHTML;
2014
+ } else {
2015
+ console.warn(`[Dolphin Component Warning]: Selector "${selector}" not found in imported file "${url}".`);
2016
+ content = `<span style="color:orange;font-weight:bold;">Selector ${selector} not found in ${url}</span>`;
2017
+ }
2018
+ }
1870
2019
  } catch (err) {
1871
- console.error(`[Dolphin Component Error]: Failed to fetch component "${src}":`, err);
1872
- content = `<span style="color:red;font-weight:bold;">Failed to import ${src}</span>`;
2020
+ console.error(`[Dolphin Component Error]: Failed to fetch component "${url}":`, err);
2021
+ content = `<span style="color:red;font-weight:bold;">Failed to import ${url}</span>`;
1873
2022
  }
1874
2023
  el.innerHTML = sanitizeHTML(content);
1875
2024
  el.removeAttribute("data-import");