tina4-nodejs 3.13.136 → 3.13.138

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.
Files changed (243) hide show
  1. package/CLAUDE.md +33 -3
  2. package/COMMERCIAL-LICENSE.md +8 -0
  3. package/LICENSE +373 -0
  4. package/NOTICE +11 -0
  5. package/README.md +24 -2
  6. package/package.json +44 -6
  7. package/packages/cli/dist/bin.js +19440 -18017
  8. package/packages/cli/src/bin.ts +8 -0
  9. package/packages/cli/src/commands/build.ts +8 -0
  10. package/packages/cli/src/commands/generate.ts +8 -0
  11. package/packages/cli/src/commands/init.ts +8 -0
  12. package/packages/cli/src/commands/lint.ts +8 -0
  13. package/packages/cli/src/commands/migrate.ts +8 -0
  14. package/packages/cli/src/commands/migrateCreate.ts +8 -0
  15. package/packages/cli/src/commands/migrateRollback.ts +8 -0
  16. package/packages/cli/src/commands/migrateStatus.ts +8 -0
  17. package/packages/cli/src/commands/queue.ts +8 -0
  18. package/packages/cli/src/commands/routes.ts +8 -0
  19. package/packages/cli/src/commands/seed.ts +8 -0
  20. package/packages/cli/src/commands/serve.ts +11 -0
  21. package/packages/cli/src/commands/test.ts +8 -0
  22. package/packages/core/dist/index.js +19432 -18012
  23. package/packages/core/gallery/auth/src/routes/api/gallery/auth/login/post.ts +8 -0
  24. package/packages/core/gallery/auth/src/routes/api/gallery/auth/verify/get.ts +8 -0
  25. package/packages/core/gallery/auth/src/routes/gallery/auth/get.ts +8 -0
  26. package/packages/core/gallery/database/src/routes/api/gallery/db/notes/get.ts +8 -0
  27. package/packages/core/gallery/database/src/routes/api/gallery/db/notes/post.ts +8 -0
  28. package/packages/core/gallery/database/src/routes/api/gallery/db/tables/get.ts +8 -0
  29. package/packages/core/gallery/error-overlay/src/routes/api/gallery/crash/get.ts +8 -0
  30. package/packages/core/gallery/orm/src/routes/api/gallery/products/get.ts +8 -0
  31. package/packages/core/gallery/orm/src/routes/api/gallery/products/post.ts +8 -0
  32. package/packages/core/gallery/queue/src/lib/queueDb.ts +8 -0
  33. package/packages/core/gallery/queue/src/routes/api/gallery/queue/consume/post.ts +8 -0
  34. package/packages/core/gallery/queue/src/routes/api/gallery/queue/fail/post.ts +8 -0
  35. package/packages/core/gallery/queue/src/routes/api/gallery/queue/produce/post.ts +8 -0
  36. package/packages/core/gallery/queue/src/routes/api/gallery/queue/retry/post.ts +8 -0
  37. package/packages/core/gallery/queue/src/routes/api/gallery/queue/status/get.ts +8 -0
  38. package/packages/core/gallery/queue/src/routes/gallery/queue/get.ts +8 -0
  39. package/packages/core/gallery/rest-api/src/routes/api/gallery/hello/get.ts +8 -0
  40. package/packages/core/gallery/rest-api/src/routes/api/gallery/hello/post.ts +8 -0
  41. package/packages/core/gallery/templates/src/routes/gallery/page/get.ts +8 -0
  42. package/packages/core/gallery/templates/src/templates/gallery_page.twig +8 -0
  43. package/packages/core/public/THIRD-PARTY-NOTICES.md +55 -0
  44. package/packages/core/public/bundled-component-provenance.json +48 -0
  45. package/packages/core/public/js/tina4js.min.js +13 -3
  46. package/packages/core/public/licenses/swagger-ui-3.10.0-original-notice.txt +11 -0
  47. package/packages/core/public/licenses/swagger-ui-5.17.14-NOTICE.txt +2 -0
  48. package/packages/core/public/licenses/swagger-ui-Apache-2.0.txt +202 -0
  49. package/packages/core/public/licenses/tina4-css-MIT.txt +21 -0
  50. package/packages/core/public/licenses/tina4-dev-admin-components.json +240 -0
  51. package/packages/core/public/licenses/tina4-dev-admin-third-party.txt +919 -0
  52. package/packages/core/public/licenses/tina4-js-MIT.txt +20 -0
  53. package/packages/core/src/__feedback/widget.js +8 -0
  54. package/packages/core/src/_missing.ts +8 -0
  55. package/packages/core/src/ai.ts +8 -0
  56. package/packages/core/src/aiClient.ts +22 -7
  57. package/packages/core/src/api.ts +15 -3
  58. package/packages/core/src/auth.ts +21 -8
  59. package/packages/core/src/authGate.ts +8 -0
  60. package/packages/core/src/background.ts +8 -0
  61. package/packages/core/src/cache.ts +43 -4
  62. package/packages/core/src/constants.ts +8 -0
  63. package/packages/core/src/container.ts +8 -0
  64. package/packages/core/src/context/chunker.ts +8 -0
  65. package/packages/core/src/context/index.ts +8 -0
  66. package/packages/core/src/devAdmin.ts +185 -59
  67. package/packages/core/src/devMailbox.ts +8 -0
  68. package/packages/core/src/dispatchPipeline.ts +8 -0
  69. package/packages/core/src/docs.ts +8 -0
  70. package/packages/core/src/docsAutoDiscovery.ts +8 -0
  71. package/packages/core/src/docsParser.ts +8 -0
  72. package/packages/core/src/docsScanner.ts +44 -0
  73. package/packages/core/src/docsSignatures.ts +8 -0
  74. package/packages/core/src/dotenv.ts +8 -0
  75. package/packages/core/src/env.ts +8 -0
  76. package/packages/core/src/errorOverlay.ts +8 -0
  77. package/packages/core/src/events.ts +8 -0
  78. package/packages/core/src/fakeData.ts +8 -0
  79. package/packages/core/src/feedback.ts +8 -0
  80. package/packages/core/src/graphql.ts +16 -5
  81. package/packages/core/src/health.ts +8 -0
  82. package/packages/core/src/htmlElement.ts +8 -0
  83. package/packages/core/src/i18n.ts +8 -0
  84. package/packages/core/src/index.ts +8 -0
  85. package/packages/core/src/job.ts +8 -0
  86. package/packages/core/src/logger.ts +8 -0
  87. package/packages/core/src/mcp.test.ts +8 -0
  88. package/packages/core/src/mcp.ts +8 -0
  89. package/packages/core/src/messenger.ts +143 -86
  90. package/packages/core/src/metrics.ts +13 -0
  91. package/packages/core/src/middleware.ts +1178 -1096
  92. package/packages/core/src/mqtt.ts +8 -0
  93. package/packages/core/src/mqttMessage.ts +8 -0
  94. package/packages/core/src/optionalPackage.ts +48 -0
  95. package/packages/core/src/plan.ts +8 -0
  96. package/packages/core/src/portTakeover.ts +8 -0
  97. package/packages/core/src/projectIndex.ts +8 -0
  98. package/packages/core/src/projectIndexExtractors.ts +8 -0
  99. package/packages/core/src/projectIndexStorage.ts +8 -0
  100. package/packages/core/src/push.ts +8 -0
  101. package/packages/core/src/queue.ts +8 -0
  102. package/packages/core/src/queueBackends/kafkaBackend.ts +8 -0
  103. package/packages/core/src/queueBackends/liteBackend.ts +8 -0
  104. package/packages/core/src/queueBackends/mongoBackend.ts +21 -8
  105. package/packages/core/src/queueBackends/rabbitmqBackend.ts +8 -0
  106. package/packages/core/src/rateLimiter.ts +8 -0
  107. package/packages/core/src/request.ts +59 -10
  108. package/packages/core/src/response.ts +79 -5
  109. package/packages/core/src/routeDiscovery.ts +8 -0
  110. package/packages/core/src/router.ts +8 -0
  111. package/packages/core/src/server.ts +73 -24
  112. package/packages/core/src/service.ts +8 -0
  113. package/packages/core/src/session.ts +8 -0
  114. package/packages/core/src/sessionHandlers/childError.ts +8 -0
  115. package/packages/core/src/sessionHandlers/databaseHandler.ts +8 -0
  116. package/packages/core/src/sessionHandlers/memcachedHandler.ts +8 -0
  117. package/packages/core/src/sessionHandlers/mongoClient.ts +8 -0
  118. package/packages/core/src/sessionHandlers/mongoHandler.ts +8 -0
  119. package/packages/core/src/sessionHandlers/respClient.ts +8 -0
  120. package/packages/core/src/sessionHandlers/sqlClient.ts +8 -0
  121. package/packages/core/src/sessionHandlers/syncBridge.ts +8 -0
  122. package/packages/core/src/sessionHandlers/syncSocket.ts +8 -0
  123. package/packages/core/src/sessionHandlers/valkeyHandler.ts +8 -0
  124. package/packages/core/src/sso.ts +8 -0
  125. package/packages/core/src/static.ts +8 -0
  126. package/packages/core/src/test.ts +8 -0
  127. package/packages/core/src/testClient.ts +8 -0
  128. package/packages/core/src/testing.ts +8 -0
  129. package/packages/core/src/transport.ts +369 -0
  130. package/packages/core/src/trustedProxy.ts +8 -0
  131. package/packages/core/src/types.ts +13 -0
  132. package/packages/core/src/validator.ts +8 -0
  133. package/packages/core/src/version.ts +8 -0
  134. package/packages/core/src/websocket.ts +8 -0
  135. package/packages/core/src/websocketBackplane.ts +49 -32
  136. package/packages/core/src/websocketConnection.ts +8 -0
  137. package/packages/core/src/wsdl.ts +197 -115
  138. package/packages/core/templates/errors/302.twig +8 -0
  139. package/packages/core/templates/errors/401.twig +8 -0
  140. package/packages/core/templates/errors/403.twig +8 -0
  141. package/packages/core/templates/errors/404.twig +8 -0
  142. package/packages/core/templates/errors/500.twig +8 -0
  143. package/packages/core/templates/errors/502.twig +8 -0
  144. package/packages/core/templates/errors/503.twig +8 -0
  145. package/packages/core/templates/errors/base.twig +8 -0
  146. package/packages/frond/dist/index.js +151 -17
  147. package/packages/frond/src/engine.ts +177 -17
  148. package/packages/frond/src/index.ts +8 -0
  149. package/packages/orm/dist/index.js +8135 -6713
  150. package/packages/orm/src/adapters/firebird.ts +50 -3
  151. package/packages/orm/src/adapters/mongodb.ts +8 -0
  152. package/packages/orm/src/adapters/mssql.ts +29 -7
  153. package/packages/orm/src/adapters/mysql.ts +17 -1
  154. package/packages/orm/src/adapters/odbc.ts +11 -0
  155. package/packages/orm/src/adapters/pg-types.d.ts +8 -0
  156. package/packages/orm/src/adapters/postgres.ts +34 -8
  157. package/packages/orm/src/adapters/sqlDialect.ts +57 -0
  158. package/packages/orm/src/adapters/sqlite.ts +26 -0
  159. package/packages/orm/src/autoCrud.ts +78 -36
  160. package/packages/orm/src/baseModel.ts +61 -29
  161. package/packages/orm/src/cachedDatabase.ts +67 -1
  162. package/packages/orm/src/connectTimeout.ts +8 -0
  163. package/packages/orm/src/database.ts +649 -401
  164. package/packages/orm/src/databaseResult.ts +8 -0
  165. package/packages/orm/src/databaseUrl.ts +8 -0
  166. package/packages/orm/src/docstore.ts +23 -1
  167. package/packages/orm/src/fakeData.ts +8 -0
  168. package/packages/orm/src/graph/adapters/arango.ts +8 -0
  169. package/packages/orm/src/graph/adapters/bolt.ts +8 -0
  170. package/packages/orm/src/graph/adapters/ultipa.ts +8 -0
  171. package/packages/orm/src/graph/connectTimeout.ts +8 -0
  172. package/packages/orm/src/graph/errors.ts +8 -0
  173. package/packages/orm/src/graph/graphAdapter.ts +8 -0
  174. package/packages/orm/src/graph/graphDatabase.ts +8 -0
  175. package/packages/orm/src/graph/graphUrl.ts +10 -1
  176. package/packages/orm/src/graph/shapes.ts +8 -0
  177. package/packages/orm/src/index.ts +147 -139
  178. package/packages/orm/src/migration.ts +21 -0
  179. package/packages/orm/src/model.ts +8 -0
  180. package/packages/orm/src/modelCollection.ts +8 -0
  181. package/packages/orm/src/point.ts +8 -0
  182. package/packages/orm/src/query.ts +149 -33
  183. package/packages/orm/src/queryBuilder.ts +8 -0
  184. package/packages/orm/src/realtime/index.ts +8 -0
  185. package/packages/orm/src/realtime/models/attachment.ts +8 -0
  186. package/packages/orm/src/realtime/models/channel.ts +8 -0
  187. package/packages/orm/src/realtime/models/channelMember.ts +8 -0
  188. package/packages/orm/src/realtime/models/message.ts +8 -0
  189. package/packages/orm/src/realtime/models/workspace.ts +8 -0
  190. package/packages/orm/src/realtime/realtime.ts +8 -0
  191. package/packages/orm/src/realtime/storage.ts +43 -12
  192. package/packages/orm/src/seeder.ts +8 -0
  193. package/packages/orm/src/seederTable.ts +8 -0
  194. package/packages/orm/src/seederTypes.ts +8 -0
  195. package/packages/orm/src/sqlTranslator.ts +185 -8
  196. package/packages/orm/src/types.ts +15 -0
  197. package/packages/orm/src/validation.ts +8 -0
  198. package/packages/swagger/src/generator.ts +8 -0
  199. package/packages/swagger/src/index.ts +8 -0
  200. package/packages/swagger/src/ui.ts +8 -0
  201. package/types/core/src/aiClient.d.ts +2 -2
  202. package/types/core/src/background.d.ts +0 -16
  203. package/types/core/src/cache.d.ts +6 -0
  204. package/types/core/src/devAdmin.d.ts +18 -12
  205. package/types/core/src/docs.d.ts +0 -17
  206. package/types/core/src/errorOverlay.d.ts +0 -24
  207. package/types/core/src/events.d.ts +0 -12
  208. package/types/core/src/feedback.d.ts +0 -23
  209. package/types/core/src/graphql.d.ts +0 -21
  210. package/types/core/src/htmlElement.d.ts +0 -15
  211. package/types/core/src/messenger.d.ts +12 -1
  212. package/types/core/src/metrics.d.ts +2 -0
  213. package/types/core/src/middleware.d.ts +45 -1
  214. package/types/core/src/mqtt.d.ts +0 -33
  215. package/types/core/src/optionalPackage.d.ts +25 -0
  216. package/types/core/src/plan.d.ts +0 -10
  217. package/types/core/src/projectIndex.d.ts +0 -10
  218. package/types/core/src/queueBackends/mongoBackend.d.ts +2 -0
  219. package/types/core/src/request.d.ts +12 -0
  220. package/types/core/src/response.d.ts +13 -0
  221. package/types/core/src/server.d.ts +19 -0
  222. package/types/core/src/sessionHandlers/childError.d.ts +0 -15
  223. package/types/core/src/transport.d.ts +28 -0
  224. package/types/core/src/trustedProxy.d.ts +0 -16
  225. package/types/core/src/types.d.ts +5 -0
  226. package/types/core/src/websocketBackplane.d.ts +1 -0
  227. package/types/core/src/wsdl.d.ts +8 -22
  228. package/types/frond/src/engine.d.ts +7 -1
  229. package/types/orm/src/adapters/firebird.d.ts +12 -0
  230. package/types/orm/src/adapters/mssql.d.ts +11 -0
  231. package/types/orm/src/adapters/mysql.d.ts +2 -0
  232. package/types/orm/src/adapters/postgres.d.ts +11 -0
  233. package/types/orm/src/adapters/sqlDialect.d.ts +25 -0
  234. package/types/orm/src/adapters/sqlite.d.ts +7 -0
  235. package/types/orm/src/baseModel.d.ts +5 -0
  236. package/types/orm/src/cachedDatabase.d.ts +27 -0
  237. package/types/orm/src/database.d.ts +46 -7
  238. package/types/orm/src/graph/graphUrl.d.ts +0 -8
  239. package/types/orm/src/index.d.ts +1 -1
  240. package/types/orm/src/query.d.ts +48 -2
  241. package/types/orm/src/realtime/storage.d.ts +2 -1
  242. package/types/orm/src/sqlTranslator.d.ts +70 -18
  243. package/types/orm/src/types.d.ts +6 -0
@@ -6,6 +6,7 @@ var SafeString = class {
6
6
  constructor(value) {
7
7
  this.value = value;
8
8
  }
9
+ value;
9
10
  toString() {
10
11
  return this.value;
11
12
  }
@@ -365,6 +366,9 @@ function resolveMethodPart(value, part, context) {
365
366
  if (!methodMatch || typeof value !== "object" || value === null) return { matched: false, value };
366
367
  const methodName = methodMatch[1];
367
368
  const rawArgs = methodMatch[2] || "";
369
+ if (unsafeKey(methodName) || !Object.prototype.hasOwnProperty.call(value, methodName)) {
370
+ return { matched: false, value };
371
+ }
368
372
  const fn = value[methodName];
369
373
  if (typeof fn !== "function") return { matched: false, value };
370
374
  const args = rawArgs.trim() ? splitArgs(rawArgs).map((a) => evalExpr(a.trim(), context)) : [];
@@ -384,7 +388,7 @@ function resolvePathKey(part, isBracket, context) {
384
388
  function resolvePathPart(value, key) {
385
389
  if (typeof value !== "object" || value === null) return { found: false, value: null };
386
390
  if (Array.isArray(value) && typeof key === "number") return { found: true, value: value[key] };
387
- if (!(key in value)) return { found: false, value: null };
391
+ if (unsafeKey(key) || !Object.prototype.hasOwnProperty.call(value, key)) return { found: false, value: null };
388
392
  const member = value[key];
389
393
  return { found: true, value: typeof member === "function" ? member.call(value) : member };
390
394
  }
@@ -504,7 +508,7 @@ function splitOutsideQuotes(expr, sep2) {
504
508
  parts.push(expr.slice(currentStart));
505
509
  return parts;
506
510
  }
507
- var EXPR_NOT_MATCHED = Symbol("frond-expression-not-matched");
511
+ var EXPR_NOT_MATCHED = /* @__PURE__ */ Symbol("frond-expression-not-matched");
508
512
  function parenthesizedInner(expr) {
509
513
  if (expr.length < 2 || expr[0] !== "(" || !expr.endsWith(")")) return null;
510
514
  let depth = 0;
@@ -634,7 +638,7 @@ function evalDottedFunction(name, rawArgs, context) {
634
638
  const lastDot = name.lastIndexOf(".");
635
639
  const owner = resolveVar(name.slice(0, lastDot), context);
636
640
  const member = name.slice(lastDot + 1);
637
- if (!owner || typeof owner !== "object" || !(member in owner)) {
641
+ if (!owner || typeof owner !== "object" || unsafeKey(member) || !Object.prototype.hasOwnProperty.call(owner, member)) {
638
642
  return EXPR_NOT_MATCHED;
639
643
  }
640
644
  const method = owner[member];
@@ -960,6 +964,7 @@ var VarRef = class {
960
964
  constructor(name) {
961
965
  this.name = name;
962
966
  }
967
+ name;
963
968
  };
964
969
  function coerceArg(t) {
965
970
  if (/^-?\d+$/.test(t)) return parseInt(t, 10);
@@ -1063,6 +1068,113 @@ function splitArgs(raw) {
1063
1068
  function htmlEscape(str) {
1064
1069
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
1065
1070
  }
1071
+ var JS_SAFE_RE = /[A-Za-z0-9,._]/;
1072
+ function jsEscape(value) {
1073
+ let out = "";
1074
+ for (const ch of String(value)) {
1075
+ if (JS_SAFE_RE.test(ch) && ch.length === 1) {
1076
+ out += ch;
1077
+ continue;
1078
+ }
1079
+ let code = ch.codePointAt(0);
1080
+ if (code < 128) {
1081
+ out += "\\x" + code.toString(16).toUpperCase().padStart(2, "0");
1082
+ } else if (code <= 65535) {
1083
+ out += "\\u" + code.toString(16).toUpperCase().padStart(4, "0");
1084
+ } else {
1085
+ code -= 65536;
1086
+ out += "\\u" + (55296 + (code >> 10)).toString(16).toUpperCase().padStart(4, "0");
1087
+ out += "\\u" + (56320 + (code & 1023)).toString(16).toUpperCase().padStart(4, "0");
1088
+ }
1089
+ }
1090
+ return out;
1091
+ }
1092
+ function cssEscape(value) {
1093
+ let out = "";
1094
+ for (const ch of String(value)) {
1095
+ out += /[A-Za-z0-9]/.test(ch) ? ch : "\\" + ch.codePointAt(0).toString(16).toUpperCase().padStart(6, "0") + " ";
1096
+ }
1097
+ return out;
1098
+ }
1099
+ function htmlAttrEscape(value) {
1100
+ let out = "";
1101
+ for (const ch of String(value)) {
1102
+ out += /[A-Za-z0-9,.\-_]/.test(ch) ? ch : "&#x" + ch.codePointAt(0).toString(16).toUpperCase().padStart(2, "0") + ";";
1103
+ }
1104
+ return out;
1105
+ }
1106
+ function urlEscape(value) {
1107
+ return encodeURIComponent(String(value)).replace(
1108
+ /[!'()*]/g,
1109
+ (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase()
1110
+ );
1111
+ }
1112
+ function escapeStrategy(value, strategy = "html") {
1113
+ const s = String(strategy).trim().replace(/^['"]|['"]$/g, "");
1114
+ switch (s) {
1115
+ case "":
1116
+ case "html":
1117
+ return new SafeString(htmlEscape(String(value)));
1118
+ case "js":
1119
+ return new SafeString(jsEscape(value));
1120
+ case "url":
1121
+ return new SafeString(urlEscape(value));
1122
+ case "css":
1123
+ return new SafeString(cssEscape(value));
1124
+ case "html_attr":
1125
+ case "attr":
1126
+ return new SafeString(htmlAttrEscape(value));
1127
+ default:
1128
+ throw new Error(`Unknown escape strategy: ${strategy}`);
1129
+ }
1130
+ }
1131
+ var MEDIA_TYPE_RE = /^[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*\/[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*$/;
1132
+ function sanitiseMediaType(type) {
1133
+ const t = String(type).trim();
1134
+ return MEDIA_TYPE_RE.test(t) ? t : "application/octet-stream";
1135
+ }
1136
+ var SANDBOX_EXPR_KEYWORDS = /* @__PURE__ */ new Set([
1137
+ "true",
1138
+ "false",
1139
+ "none",
1140
+ "null",
1141
+ "nil",
1142
+ "and",
1143
+ "or",
1144
+ "not",
1145
+ "in",
1146
+ "is",
1147
+ "loop",
1148
+ "if",
1149
+ "else",
1150
+ "empty",
1151
+ "starts",
1152
+ "ends",
1153
+ "with",
1154
+ "matches"
1155
+ ]);
1156
+ var SANDBOX_IDENT_RE = /(?<![.\w'"])([A-Za-z_][A-Za-z0-9_]*)/g;
1157
+ var SANDBOX_STRING_RE = /'[^']*'|"[^"]*"/g;
1158
+ var UNSAFE_KEYS = /* @__PURE__ */ new Set([
1159
+ "constructor",
1160
+ "__proto__",
1161
+ "prototype",
1162
+ "__defineGetter__",
1163
+ "__defineSetter__",
1164
+ "__lookupGetter__",
1165
+ "__lookupSetter__",
1166
+ "__defineProperty__",
1167
+ "valueOf"
1168
+ ]);
1169
+ function unsafeKey(key) {
1170
+ return UNSAFE_KEYS.has(String(key));
1171
+ }
1172
+ function isEscapableStructured(value) {
1173
+ return value !== null && typeof value === "object" && !(value instanceof SafeString);
1174
+ }
1175
+ function toOutputString(value) {
1176
+ return String(value);
1177
+ }
1066
1178
  function dateFilter(value, fmt) {
1067
1179
  let dt;
1068
1180
  if (value instanceof Date) {
@@ -1183,8 +1295,8 @@ var BUILTIN_FILTERS = {
1183
1295
  default: (v, fallback) => v !== null && v !== void 0 && v !== "" ? v : fallback !== void 0 ? fallback : "",
1184
1296
  raw: (v) => v,
1185
1297
  safe: (v) => v,
1186
- escape: (v) => htmlEscape(String(v)),
1187
- e: (v) => htmlEscape(String(v)),
1298
+ escape: (v, ...a) => escapeStrategy(v, a[0] ?? "html"),
1299
+ e: (v, ...a) => escapeStrategy(v, a[0] ?? "html"),
1188
1300
  striptags: (v) => String(v).replace(STRIP_TAGS_RE, ""),
1189
1301
  nl2br: (v) => new SafeString(htmlEscape(String(v)).replace(/\n/g, "<br />\n")),
1190
1302
  abs: (v) => typeof v === "number" ? Math.abs(v) : v,
@@ -1273,9 +1385,9 @@ var BUILTIN_FILTERS = {
1273
1385
  base64decode: (v) => Buffer.from(String(v), "base64").toString("utf-8"),
1274
1386
  data_uri: (v) => {
1275
1387
  if (v && typeof v === "object" && "content" in v) {
1276
- const ct = v.type ?? "application/octet-stream";
1388
+ const ct = sanitiseMediaType(String(v.type ?? "application/octet-stream"));
1277
1389
  const raw = Buffer.isBuffer(v.content) ? v.content : Buffer.from(String(v.content));
1278
- return `data:${ct};base64,${raw.toString("base64")}`;
1390
+ return new SafeString(`data:${ct};base64,${raw.toString("base64")}`);
1279
1391
  }
1280
1392
  return String(v);
1281
1393
  },
@@ -1295,7 +1407,7 @@ var BUILTIN_FILTERS = {
1295
1407
  // broke byte-parity for the one filter whose whole job is a wire format.
1296
1408
  tojson: (v) => jsonSafe(v),
1297
1409
  to_json: (v) => jsonSafe(v),
1298
- js_escape: (v) => new SafeString(String(v).replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t"))
1410
+ js_escape: (v) => new SafeString(jsEscape(v))
1299
1411
  };
1300
1412
  function _b64url(data) {
1301
1413
  return data.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
@@ -2002,6 +2114,7 @@ var Frond = class _Frond {
2002
2114
  return this.evalVarInner(expr, context);
2003
2115
  }
2004
2116
  evalVarRaw(expr, context) {
2117
+ if (this._sandbox && !this.sandboxExprOk(expr)) return null;
2005
2118
  const [varName, filters] = parseFilterChain(expr);
2006
2119
  let value = evalExpr(varName, context);
2007
2120
  for (const [fname, rawArgs] of filters) {
@@ -2044,16 +2157,33 @@ var Frond = class _Frond {
2044
2157
  const fn = this.filters[fname];
2045
2158
  return fn ? fn(value, ...args) : value;
2046
2159
  }
2047
- variablePermitted(varName) {
2048
- if (!this._sandbox || this._allowedVars === null) return true;
2049
- const rootVar = varName.split(".")[0].split("[")[0].trim();
2050
- return !rootVar || rootVar === "loop" || this._allowedVars.has(rootVar);
2160
+ /**
2161
+ * Sandbox gate for a WHOLE expression (F6/ADR-0077): refuse a dunder walk or
2162
+ * a root variable outside the allow-list. Applied to output, if conditions,
2163
+ * set right-hand sides and for iterables so none can smuggle a blocked
2164
+ * variable past the per-output gate.
2165
+ */
2166
+ sandboxExprOk(expr) {
2167
+ if (!this._sandbox) return true;
2168
+ if (expr.includes("__")) return false;
2169
+ if (this._allowedVars === null) return true;
2170
+ const stripped = expr.replace(SANDBOX_STRING_RE, "");
2171
+ let m;
2172
+ SANDBOX_IDENT_RE.lastIndex = 0;
2173
+ while ((m = SANDBOX_IDENT_RE.exec(stripped)) !== null) {
2174
+ const ident = m[1];
2175
+ if (SANDBOX_EXPR_KEYWORDS.has(ident.toLowerCase())) continue;
2176
+ if (ident in this.filters) continue;
2177
+ if (!this._allowedVars.has(ident)) return false;
2178
+ }
2179
+ return true;
2051
2180
  }
2052
2181
  resolveConcatenation(expr, context) {
2053
2182
  if (findOutsideQuotes(expr, "~") < 0) return { handled: false, value: void 0 };
2054
2183
  let value = evalExpr(expr, context);
2055
2184
  if (value instanceof SafeString) return { handled: true, value: value.value };
2056
2185
  if (this._autoEscape && typeof value === "string") value = htmlEscape(value);
2186
+ else if (this._autoEscape && isEscapableStructured(value)) value = htmlEscape(toOutputString(value));
2057
2187
  return { handled: true, value };
2058
2188
  }
2059
2189
  applyRenderedFilters(value, filters, context) {
@@ -2064,7 +2194,6 @@ var Frond = class _Frond {
2064
2194
  if (this.filterPermitted(fname)) safe = true;
2065
2195
  continue;
2066
2196
  }
2067
- if ((fname === "escape" || fname === "e") && this.filterPermitted(fname)) safe = true;
2068
2197
  if (!this.filterPermitted(fname)) continue;
2069
2198
  value = this.applyRenderedFilter(value, fname, args);
2070
2199
  }
@@ -2072,7 +2201,7 @@ var Frond = class _Frond {
2072
2201
  }
2073
2202
  evalVarInner(expr, context) {
2074
2203
  const [varName, filters] = parseFilterChain(expr);
2075
- if (!this.variablePermitted(varName)) return "";
2204
+ if (this._sandbox && !this.sandboxExprOk(expr)) return "";
2076
2205
  const concatenated = this.resolveConcatenation(expr, context);
2077
2206
  if (concatenated.handled) return concatenated.value;
2078
2207
  const applied = this.applyRenderedFilters(evalExpr(varName, context), filters, context);
@@ -2080,8 +2209,12 @@ var Frond = class _Frond {
2080
2209
  if (value instanceof SafeString) {
2081
2210
  return value.value;
2082
2211
  }
2083
- if (!applied.safe && this._autoEscape && typeof value === "string") {
2084
- value = htmlEscape(value);
2212
+ if (!applied.safe && this._autoEscape) {
2213
+ if (typeof value === "string") {
2214
+ value = htmlEscape(value);
2215
+ } else if (isEscapableStructured(value)) {
2216
+ value = new SafeString(htmlEscape(toOutputString(value)));
2217
+ }
2085
2218
  }
2086
2219
  return value;
2087
2220
  }
@@ -2188,7 +2321,8 @@ var Frond = class _Frond {
2188
2321
  const var1 = forMatch[1];
2189
2322
  const var2 = forMatch[2] || null;
2190
2323
  const { bodyTokens, elseTokens, next: i } = this.collectForTokens(tokens, start);
2191
- const iterable = evalExpr(forMatch[3].trim(), context);
2324
+ const iterableExpr = forMatch[3].trim();
2325
+ const iterable = this._sandbox && !this.sandboxExprOk(iterableExpr) ? null : evalExpr(iterableExpr, context);
2192
2326
  const { items, isDict } = this.forItems(iterable);
2193
2327
  if (items.length === 0) return [elseTokens.length ? this.renderTokens([...elseTokens], context) : "", i];
2194
2328
  const output = [];
@@ -1,3 +1,11 @@
1
+ /*
2
+ Copyright (c) 2026 Code Infinity
3
+ SPDX-License-Identifier: MPL-2.0
4
+ This Source Code Form is subject to the terms of the Mozilla Public
5
+ License, v. 2.0. If a copy of the MPL was not distributed with this
6
+ file, You can obtain one at https://mozilla.org/MPL/2.0/.
7
+ */
8
+
1
9
  /**
2
10
  * Tina4 Frond Engine — Lexer, parser, and runtime.
3
11
  * Zero-dependency Twig-like template engine.
@@ -570,6 +578,11 @@ function resolveMethodPart(value: unknown, part: string, context: Record<string,
570
578
  if (!methodMatch || typeof value !== "object" || value === null) return { matched: false, value };
571
579
  const methodName = methodMatch[1];
572
580
  const rawArgs = methodMatch[2] || "";
581
+ // F6/ADR-0077: never traverse a prototype/constructor/dunder key -- that is
582
+ // the Function-constructor and prototype-pollution escape.
583
+ if (unsafeKey(methodName) || !Object.prototype.hasOwnProperty.call(value, methodName)) {
584
+ return { matched: false, value };
585
+ }
573
586
  const fn = (value as Record<string, unknown>)[methodName];
574
587
  if (typeof fn !== "function") return { matched: false, value };
575
588
  const args = rawArgs.trim() ? splitArgs(rawArgs).map(a => evalExpr(a.trim(), context)) : [];
@@ -592,7 +605,9 @@ function resolvePathKey(part: string, isBracket: boolean, context: Record<string
592
605
  function resolvePathPart(value: unknown, key: string | number): { found: boolean; value: unknown } {
593
606
  if (typeof value !== "object" || value === null) return { found: false, value: null };
594
607
  if (Array.isArray(value) && typeof key === "number") return { found: true, value: value[key] };
595
- if (!(key in (value as Record<string, unknown>))) return { found: false, value: null };
608
+ // F6/ADR-0077: only own, non-dunder properties are reachable, so an
609
+ // inherited prototype method (constructor, __proto__, ...) cannot be called.
610
+ if (unsafeKey(key) || !Object.prototype.hasOwnProperty.call(value, key)) return { found: false, value: null };
596
611
  const member = (value as Record<string, unknown>)[key as string];
597
612
  return { found: true, value: typeof member === "function" ? member.call(value) : member };
598
613
  }
@@ -889,7 +904,10 @@ function evalDottedFunction(name: string, rawArgs: string, context: Record<strin
889
904
  const lastDot = name.lastIndexOf(".");
890
905
  const owner = resolveVar(name.slice(0, lastDot), context);
891
906
  const member = name.slice(lastDot + 1);
892
- if (!owner || typeof owner !== "object" || !(member in (owner as Record<string, unknown>))) {
907
+ // F6/ADR-0077: never call through a prototype/constructor/dunder key or an
908
+ // inherited method -- that is the Function-constructor and prototype escape.
909
+ if (!owner || typeof owner !== "object" || unsafeKey(member) ||
910
+ !Object.prototype.hasOwnProperty.call(owner, member)) {
893
911
  return EXPR_NOT_MATCHED;
894
912
  }
895
913
  const method = (owner as Record<string, unknown>)[member];
@@ -1361,6 +1379,115 @@ function htmlEscape(str: string): string {
1361
1379
  .replace(/'/g, "&#x27;");
1362
1380
  }
1363
1381
 
1382
+ // -- Escape strategies (ADR-0077) -------------------------------------------
1383
+ // Shared by js_escape and e(strategy); byte-identical to the Python master.
1384
+ // Twig-compatible. Iterates code points so astral characters emit a surrogate
1385
+ // pair for the js strategy.
1386
+ const JS_SAFE_RE = /[A-Za-z0-9,._]/;
1387
+ function jsEscape(value: unknown): string {
1388
+ let out = "";
1389
+ for (const ch of String(value)) {
1390
+ if (JS_SAFE_RE.test(ch) && ch.length === 1) {
1391
+ out += ch;
1392
+ continue;
1393
+ }
1394
+ let code = ch.codePointAt(0)!;
1395
+ if (code < 0x80) {
1396
+ out += "\\x" + code.toString(16).toUpperCase().padStart(2, "0");
1397
+ } else if (code <= 0xffff) {
1398
+ out += "\\u" + code.toString(16).toUpperCase().padStart(4, "0");
1399
+ } else {
1400
+ code -= 0x10000;
1401
+ out += "\\u" + (0xd800 + (code >> 10)).toString(16).toUpperCase().padStart(4, "0");
1402
+ out += "\\u" + (0xdc00 + (code & 0x3ff)).toString(16).toUpperCase().padStart(4, "0");
1403
+ }
1404
+ }
1405
+ return out;
1406
+ }
1407
+
1408
+ function cssEscape(value: unknown): string {
1409
+ let out = "";
1410
+ for (const ch of String(value)) {
1411
+ out += /[A-Za-z0-9]/.test(ch)
1412
+ ? ch
1413
+ : "\\" + ch.codePointAt(0)!.toString(16).toUpperCase().padStart(6, "0") + " ";
1414
+ }
1415
+ return out;
1416
+ }
1417
+
1418
+ function htmlAttrEscape(value: unknown): string {
1419
+ let out = "";
1420
+ for (const ch of String(value)) {
1421
+ out += /[A-Za-z0-9,.\-_]/.test(ch)
1422
+ ? ch
1423
+ : "&#x" + ch.codePointAt(0)!.toString(16).toUpperCase().padStart(2, "0") + ";";
1424
+ }
1425
+ return out;
1426
+ }
1427
+
1428
+ function urlEscape(value: unknown): string {
1429
+ return encodeURIComponent(String(value)).replace(
1430
+ /[!'()*]/g,
1431
+ (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase(),
1432
+ );
1433
+ }
1434
+
1435
+ // Dispatch e(strategy)/escape(strategy). Unknown strategy throws so a typo can
1436
+ // never silently pass the value through unescaped.
1437
+ function escapeStrategy(value: unknown, strategy: string = "html"): SafeString {
1438
+ const s = String(strategy).trim().replace(/^['"]|['"]$/g, "");
1439
+ switch (s) {
1440
+ case "":
1441
+ case "html":
1442
+ return new SafeString(htmlEscape(String(value)));
1443
+ case "js":
1444
+ return new SafeString(jsEscape(value));
1445
+ case "url":
1446
+ return new SafeString(urlEscape(value));
1447
+ case "css":
1448
+ return new SafeString(cssEscape(value));
1449
+ case "html_attr":
1450
+ case "attr":
1451
+ return new SafeString(htmlAttrEscape(value));
1452
+ default:
1453
+ throw new Error(`Unknown escape strategy: ${strategy}`);
1454
+ }
1455
+ }
1456
+
1457
+ // Confine a client-supplied media type to type/subtype (F3/ADR-0077).
1458
+ const MEDIA_TYPE_RE = /^[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*\/[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*$/;
1459
+ function sanitiseMediaType(type: string): string {
1460
+ const t = String(type).trim();
1461
+ return MEDIA_TYPE_RE.test(t) ? t : "application/octet-stream";
1462
+ }
1463
+
1464
+ // Property keys that must never be traversed/invoked through {{ obj.x }} —
1465
+ // prototype pollution and the Function constructor RCE (F6/ADR-0077).
1466
+ const SANDBOX_EXPR_KEYWORDS = new Set([
1467
+ "true","false","none","null","nil","and","or","not","in","is","loop",
1468
+ "if","else","empty","starts","ends","with","matches",
1469
+ ]);
1470
+ const SANDBOX_IDENT_RE = /(?<![.\w'"])([A-Za-z_][A-Za-z0-9_]*)/g;
1471
+ const SANDBOX_STRING_RE = /'[^']*'|"[^"]*"/g;
1472
+ const UNSAFE_KEYS = new Set([
1473
+ "constructor", "__proto__", "prototype", "__defineGetter__",
1474
+ "__defineSetter__", "__lookupGetter__", "__lookupSetter__",
1475
+ "__defineProperty__", "valueOf",
1476
+ ]);
1477
+ function unsafeKey(key: string | number): boolean {
1478
+ return UNSAFE_KEYS.has(String(key));
1479
+ }
1480
+
1481
+ // A value whose rendered string form must be auto-escaped: an array/object
1482
+ // (not a SafeString). Strings, numbers, booleans and null are handled by the
1483
+ // caller's own branches. (F4/ADR-0077)
1484
+ function isEscapableStructured(value: unknown): boolean {
1485
+ return value !== null && typeof value === "object" && !(value instanceof SafeString);
1486
+ }
1487
+ function toOutputString(value: unknown): string {
1488
+ return String(value);
1489
+ }
1490
+
1364
1491
  function dateFilter(value: unknown, fmt: string): string {
1365
1492
  let dt: Date;
1366
1493
  if (value instanceof Date) {
@@ -1514,8 +1641,8 @@ const BUILTIN_FILTERS: Record<string, FilterFn> = {
1514
1641
  default: (v, fallback) => (v !== null && v !== undefined && v !== "") ? v : (fallback !== undefined ? fallback : ""),
1515
1642
  raw: (v) => v,
1516
1643
  safe: (v) => v,
1517
- escape: (v) => htmlEscape(String(v)),
1518
- e: (v) => htmlEscape(String(v)),
1644
+ escape: (v, ...a) => escapeStrategy(v, (a[0] as string) ?? "html"),
1645
+ e: (v, ...a) => escapeStrategy(v, (a[0] as string) ?? "html"),
1519
1646
  striptags: (v) => String(v).replace(STRIP_TAGS_RE, ""),
1520
1647
  nl2br: (v) => new SafeString(htmlEscape(String(v)).replace(/\n/g, "<br />\n")),
1521
1648
  abs: (v) => typeof v === "number" ? Math.abs(v) : v,
@@ -1604,9 +1731,9 @@ const BUILTIN_FILTERS: Record<string, FilterFn> = {
1604
1731
  base64decode: (v) => Buffer.from(String(v), "base64").toString("utf-8"),
1605
1732
  data_uri: (v) => {
1606
1733
  if (v && typeof v === "object" && "content" in v) {
1607
- const ct = (v as any).type ?? "application/octet-stream";
1734
+ const ct = sanitiseMediaType(String((v as any).type ?? "application/octet-stream"));
1608
1735
  const raw = Buffer.isBuffer((v as any).content) ? (v as any).content : Buffer.from(String((v as any).content));
1609
- return `data:${ct};base64,${raw.toString("base64")}`;
1736
+ return new SafeString(`data:${ct};base64,${raw.toString("base64")}`);
1610
1737
  }
1611
1738
  return String(v);
1612
1739
  },
@@ -1626,7 +1753,7 @@ const BUILTIN_FILTERS: Record<string, FilterFn> = {
1626
1753
  // broke byte-parity for the one filter whose whole job is a wire format.
1627
1754
  tojson: (v) => jsonSafe(v),
1628
1755
  to_json: (v) => jsonSafe(v),
1629
- js_escape: (v) => new SafeString(String(v).replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")),
1756
+ js_escape: (v) => new SafeString(jsEscape(v)),
1630
1757
  };
1631
1758
 
1632
1759
  // ── Form Token ────────────────────────────────────────────────
@@ -2526,6 +2653,9 @@ export class Frond {
2526
2653
  }
2527
2654
 
2528
2655
  private evalVarRaw(expr: string, context: Record<string, unknown>): unknown {
2656
+ // F6/ADR-0077: gate the whole condition/RHS so a comparison or iterable
2657
+ // cannot leak a blocked variable.
2658
+ if (this._sandbox && !this.sandboxExprOk(expr)) return null;
2529
2659
  const [varName, filters] = parseFilterChain(expr);
2530
2660
  let value = evalExpr(varName, context);
2531
2661
  for (const [fname, rawArgs] of filters) {
@@ -2576,10 +2706,26 @@ export class Frond {
2576
2706
  return fn ? fn(value, ...args) : value;
2577
2707
  }
2578
2708
 
2579
- private variablePermitted(varName: string): boolean {
2580
- if (!this._sandbox || this._allowedVars === null) return true;
2581
- const rootVar = varName.split(".")[0].split("[")[0].trim();
2582
- return !rootVar || rootVar === "loop" || this._allowedVars.has(rootVar);
2709
+ /**
2710
+ * Sandbox gate for a WHOLE expression (F6/ADR-0077): refuse a dunder walk or
2711
+ * a root variable outside the allow-list. Applied to output, if conditions,
2712
+ * set right-hand sides and for iterables so none can smuggle a blocked
2713
+ * variable past the per-output gate.
2714
+ */
2715
+ private sandboxExprOk(expr: string): boolean {
2716
+ if (!this._sandbox) return true;
2717
+ if (expr.includes("__")) return false;
2718
+ if (this._allowedVars === null) return true;
2719
+ const stripped = expr.replace(SANDBOX_STRING_RE, "");
2720
+ let m: RegExpExecArray | null;
2721
+ SANDBOX_IDENT_RE.lastIndex = 0;
2722
+ while ((m = SANDBOX_IDENT_RE.exec(stripped)) !== null) {
2723
+ const ident = m[1];
2724
+ if (SANDBOX_EXPR_KEYWORDS.has(ident.toLowerCase())) continue;
2725
+ if (ident in this.filters) continue;
2726
+ if (!this._allowedVars.has(ident)) return false;
2727
+ }
2728
+ return true;
2583
2729
  }
2584
2730
 
2585
2731
  private resolveConcatenation(
@@ -2590,6 +2736,7 @@ export class Frond {
2590
2736
  let value = evalExpr(expr, context);
2591
2737
  if (value instanceof SafeString) return { handled: true, value: value.value };
2592
2738
  if (this._autoEscape && typeof value === "string") value = htmlEscape(value);
2739
+ else if (this._autoEscape && isEscapableStructured(value)) value = htmlEscape(toOutputString(value));
2593
2740
  return { handled: true, value };
2594
2741
  }
2595
2742
 
@@ -2605,7 +2752,10 @@ export class Frond {
2605
2752
  if (this.filterPermitted(fname)) safe = true;
2606
2753
  continue;
2607
2754
  }
2608
- if ((fname === "escape" || fname === "e") && this.filterPermitted(fname)) safe = true;
2755
+ // F2/ADR-0077: do NOT keep a persistent "safe" flag for escape/e. The
2756
+ // filter returns a SafeString; if a later filter (replace/format/join)
2757
+ // transforms it, the result is a plain string and is re-escaped at
2758
+ // output. Keeping a safe flag here let post-escape filters emit raw.
2609
2759
  if (!this.filterPermitted(fname)) continue;
2610
2760
  value = this.applyRenderedFilter(value, fname, args);
2611
2761
  }
@@ -2614,7 +2764,7 @@ export class Frond {
2614
2764
 
2615
2765
  private evalVarInner(expr: string, context: Record<string, unknown>): unknown {
2616
2766
  const [varName, filters] = parseFilterChain(expr);
2617
- if (!this.variablePermitted(varName)) return "";
2767
+ if (this._sandbox && !this.sandboxExprOk(expr)) return "";
2618
2768
  const concatenated = this.resolveConcatenation(expr, context);
2619
2769
  if (concatenated.handled) return concatenated.value;
2620
2770
  const applied = this.applyRenderedFilters(evalExpr(varName, context), filters, context);
@@ -2625,9 +2775,16 @@ export class Frond {
2625
2775
  return value.value;
2626
2776
  }
2627
2777
 
2628
- // Auto-escape HTML unless marked safe or auto-escape is disabled
2629
- if (!applied.safe && this._autoEscape && typeof value === "string") {
2630
- value = htmlEscape(value);
2778
+ // Auto-escape HTML unless marked safe or auto-escape is disabled. A plain
2779
+ // string is escaped directly; an array/object is escaped on its rendered
2780
+ // string form (F4/ADR-0077) -- before this, only string values were
2781
+ // escaped, so {{ items }} holding markup emitted it raw.
2782
+ if (!applied.safe && this._autoEscape) {
2783
+ if (typeof value === "string") {
2784
+ value = htmlEscape(value);
2785
+ } else if (isEscapableStructured(value)) {
2786
+ value = new SafeString(htmlEscape(toOutputString(value)));
2787
+ }
2631
2788
  }
2632
2789
 
2633
2790
  return value;
@@ -2756,7 +2913,10 @@ export class Frond {
2756
2913
  const { bodyTokens, elseTokens, next: i } = this.collectForTokens(tokens, start);
2757
2914
 
2758
2915
  // Evaluate iterable
2759
- const iterable = evalExpr(forMatch[3].trim(), context);
2916
+ const iterableExpr = forMatch[3].trim();
2917
+ const iterable = (this._sandbox && !this.sandboxExprOk(iterableExpr))
2918
+ ? null
2919
+ : evalExpr(iterableExpr, context);
2760
2920
  const { items, isDict } = this.forItems(iterable);
2761
2921
  if (items.length === 0) return [elseTokens.length ? this.renderTokens([...elseTokens], context) : "", i];
2762
2922
 
@@ -1,2 +1,10 @@
1
+ /*
2
+ Copyright (c) 2026 Code Infinity
3
+ SPDX-License-Identifier: MPL-2.0
4
+ This Source Code Form is subject to the terms of the Mozilla Public
5
+ License, v. 2.0. If a copy of the MPL was not distributed with this
6
+ file, You can obtain one at https://mozilla.org/MPL/2.0/.
7
+ */
8
+
1
9
  export { Frond } from "./engine.js";
2
10
  export type { FilterFn, TestFn, LiveProvider, LiveRequest, LiveResponse, LiveBroadcaster } from "./engine.js";