@ceupay/template-cli 0.1.0-alpha.2 → 0.1.0-alpha.3

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/main.cjs CHANGED
@@ -49,7 +49,7 @@ var require_liquid_node = __commonJS({
49
49
  "use strict";
50
50
  Object.defineProperty(exports2, "__esModule", { value: true });
51
51
  var stream = require("stream");
52
- var path3 = require("path");
52
+ var path4 = require("path");
53
53
  var fs$1 = require("fs");
54
54
  var crypto = require("crypto");
55
55
  var Token = class {
@@ -1511,9 +1511,9 @@ var require_liquid_node = __commonJS({
1511
1511
  return fs$1.readFileSync(filepath, "utf8");
1512
1512
  }
1513
1513
  function resolve(root, file, ext) {
1514
- if (!path3.extname(file))
1514
+ if (!path4.extname(file))
1515
1515
  file += ext;
1516
- return path3.resolve(root, file);
1516
+ return path4.resolve(root, file);
1517
1517
  }
1518
1518
  function fallback(file) {
1519
1519
  try {
@@ -1522,14 +1522,14 @@ var require_liquid_node = __commonJS({
1522
1522
  }
1523
1523
  }
1524
1524
  function dirname(filepath) {
1525
- return path3.dirname(filepath);
1525
+ return path4.dirname(filepath);
1526
1526
  }
1527
1527
  var realpathAsync = promisify(fs$1.realpath);
1528
1528
  async function contains(root, file) {
1529
1529
  try {
1530
1530
  const realRoot = await realpathAsync(root);
1531
1531
  const realFile = await realpathAsync(file);
1532
- const prefix = realRoot.endsWith(path3.sep) ? realRoot : realRoot + path3.sep;
1532
+ const prefix = realRoot.endsWith(path4.sep) ? realRoot : realRoot + path4.sep;
1533
1533
  return realFile.startsWith(prefix);
1534
1534
  } catch {
1535
1535
  return false;
@@ -1539,13 +1539,13 @@ var require_liquid_node = __commonJS({
1539
1539
  try {
1540
1540
  const realRoot = fs$1.realpathSync(root);
1541
1541
  const realFile = fs$1.realpathSync(file);
1542
- const prefix = realRoot.endsWith(path3.sep) ? realRoot : realRoot + path3.sep;
1542
+ const prefix = realRoot.endsWith(path4.sep) ? realRoot : realRoot + path4.sep;
1543
1543
  return realFile.startsWith(prefix);
1544
1544
  } catch {
1545
1545
  return false;
1546
1546
  }
1547
1547
  }
1548
- var fs3 = /* @__PURE__ */ Object.freeze({
1548
+ var fs4 = /* @__PURE__ */ Object.freeze({
1549
1549
  __proto__: null,
1550
1550
  exists,
1551
1551
  readFile,
@@ -1556,7 +1556,7 @@ var require_liquid_node = __commonJS({
1556
1556
  dirname,
1557
1557
  contains,
1558
1558
  containsSync,
1559
- sep: path3.sep
1559
+ sep: path4.sep
1560
1560
  });
1561
1561
  function chargeJsonReplacerValue(memoryLimit, val) {
1562
1562
  if (typeof val === "string") {
@@ -1632,13 +1632,13 @@ var require_liquid_node = __commonJS({
1632
1632
  """: '"',
1633
1633
  "'": "'"
1634
1634
  };
1635
- function escape3(str3) {
1635
+ function escape4(str3) {
1636
1636
  str3 = stringify(str3);
1637
1637
  this.context.memoryLimit.use(str3.length);
1638
1638
  return str3.replace(/&|<|>|"|'/g, (m) => escapeMap[m]);
1639
1639
  }
1640
1640
  function xml_escape(str3) {
1641
- return escape3.call(this, str3);
1641
+ return escape4.call(this, str3);
1642
1642
  }
1643
1643
  function unescape2(str3) {
1644
1644
  str3 = stringify(str3);
@@ -1646,7 +1646,7 @@ var require_liquid_node = __commonJS({
1646
1646
  return str3.replace(/&(amp|lt|gt|#34|#39);/g, (m) => unescapeMap[m]);
1647
1647
  }
1648
1648
  function escape_once(str3) {
1649
- return escape3.call(this, unescape2.call(this, str3));
1649
+ return escape4.call(this, unescape2.call(this, str3));
1650
1650
  }
1651
1651
  function newline_to_br(v) {
1652
1652
  const str3 = stringify(v);
@@ -1681,7 +1681,7 @@ var require_liquid_node = __commonJS({
1681
1681
  }
1682
1682
  var htmlFilters = /* @__PURE__ */ Object.freeze({
1683
1683
  __proto__: null,
1684
- escape: escape3,
1684
+ escape: escape4,
1685
1685
  xml_escape,
1686
1686
  escape_once,
1687
1687
  newline_to_br,
@@ -1738,7 +1738,7 @@ var require_liquid_node = __commonJS({
1738
1738
  keyValueSeparator: ":",
1739
1739
  cache: void 0,
1740
1740
  extname: "",
1741
- fs: fs3,
1741
+ fs: fs4,
1742
1742
  dynamicPartials: true,
1743
1743
  jsTruthy: false,
1744
1744
  dateFormat: "%A, %B %-e, %Y at %-l:%M %P %z",
@@ -1803,7 +1803,7 @@ var require_liquid_node = __commonJS({
1803
1803
  }
1804
1804
  function getOutputEscapeFunction(nameOrFunction) {
1805
1805
  if (nameOrFunction === "escape")
1806
- return escape3;
1806
+ return escape4;
1807
1807
  if (nameOrFunction === "json")
1808
1808
  return misc.json;
1809
1809
  assert(isFunction(nameOrFunction), "`outputEscape` need to be of type string or function");
@@ -2100,10 +2100,10 @@ var require_liquid_node = __commonJS({
2100
2100
  readHashes(jekyllStyle) {
2101
2101
  const hashes = [];
2102
2102
  while (true) {
2103
- const hash = this.readHash(jekyllStyle);
2104
- if (!hash)
2103
+ const hash2 = this.readHash(jekyllStyle);
2104
+ if (!hash2)
2105
2105
  return hashes;
2106
- hashes.push(hash);
2106
+ hashes.push(hash2);
2107
2107
  }
2108
2108
  }
2109
2109
  readHash(jekyllStyle) {
@@ -2360,16 +2360,16 @@ var require_liquid_node = __commonJS({
2360
2360
  constructor(input, jekyllStyle) {
2361
2361
  this.hash = {};
2362
2362
  const tokenizer = input instanceof Tokenizer2 ? input : new Tokenizer2(input, {});
2363
- for (const hash of tokenizer.readHashes(jekyllStyle)) {
2364
- this.hash[hash.name.content] = hash.value;
2363
+ for (const hash2 of tokenizer.readHashes(jekyllStyle)) {
2364
+ this.hash[hash2.name.content] = hash2.value;
2365
2365
  }
2366
2366
  }
2367
2367
  *render(ctx) {
2368
- const hash = {};
2368
+ const hash2 = {};
2369
2369
  for (const key of Object.keys(this.hash)) {
2370
- hash[key] = this.hash[key] === void 0 ? true : yield evalToken(this.hash[key], ctx);
2370
+ hash2[key] = this.hash[key] === void 0 ? true : yield evalToken(this.hash[key], ctx);
2371
2371
  }
2372
- return hash;
2372
+ return hash2;
2373
2373
  }
2374
2374
  };
2375
2375
  function createTagClass(options) {
@@ -2381,8 +2381,8 @@ var require_liquid_node = __commonJS({
2381
2381
  }
2382
2382
  }
2383
2383
  *render(ctx, emitter) {
2384
- const hash = yield new Hash(this.token.args, ctx.opts.keyValueSeparator).render(ctx);
2385
- return yield options.render.call(this, ctx, emitter, hash);
2384
+ const hash2 = yield new Hash(this.token.args, ctx.opts.keyValueSeparator).render(ctx);
2385
+ return yield options.render.call(this, ctx, emitter, hash2);
2386
2386
  }
2387
2387
  };
2388
2388
  }
@@ -2757,9 +2757,9 @@ var require_liquid_node = __commonJS({
2757
2757
  } else {
2758
2758
  this.shouldLoadRelative = (_referencedFile) => false;
2759
2759
  }
2760
- const fs4 = options.fs;
2761
- this.contains = toLiquidAsync(fs4.contains?.bind(fs4) || (async () => true), fs4.containsSync?.bind(fs4) || (() => true));
2762
- this.exists = toLiquidAsync(fs4.exists?.bind(fs4) || (async () => false), fs4.existsSync?.bind(fs4));
2760
+ const fs5 = options.fs;
2761
+ this.contains = toLiquidAsync(fs5.contains?.bind(fs5) || (async () => true), fs5.containsSync?.bind(fs5) || (() => true));
2762
+ this.exists = toLiquidAsync(fs5.exists?.bind(fs5) || (async () => false), fs5.existsSync?.bind(fs5));
2763
2763
  }
2764
2764
  *lookup(file, type, sync, currentFile) {
2765
2765
  const dirs = this.options[type];
@@ -2779,25 +2779,25 @@ var require_liquid_node = __commonJS({
2779
2779
  throw this.lookupError(file, dirs);
2780
2780
  }
2781
2781
  *candidates(file, dirs, currentFile) {
2782
- const { fs: fs4, extname } = this.options;
2782
+ const { fs: fs5, extname } = this.options;
2783
2783
  if (this.shouldLoadRelative(file) && currentFile) {
2784
- const referenced = fs4.resolve(this.dirname(currentFile), file, extname);
2784
+ const referenced = fs5.resolve(this.dirname(currentFile), file, extname);
2785
2785
  yield referenced;
2786
2786
  }
2787
2787
  for (const dir of dirs) {
2788
- const referenced = fs4.resolve(dir, file, extname);
2788
+ const referenced = fs5.resolve(dir, file, extname);
2789
2789
  yield referenced;
2790
2790
  }
2791
- if (fs4.fallback !== void 0) {
2792
- const filepath = fs4.fallback(file);
2791
+ if (fs5.fallback !== void 0) {
2792
+ const filepath = fs5.fallback(file);
2793
2793
  if (filepath !== void 0)
2794
2794
  yield filepath;
2795
2795
  }
2796
2796
  }
2797
- dirname(path4) {
2798
- const fs4 = this.options.fs;
2799
- assert(fs4.dirname, "`fs.dirname` is required for relative reference");
2800
- return fs4.dirname(path4);
2797
+ dirname(path5) {
2798
+ const fs5 = this.options.fs;
2799
+ assert(fs5.dirname, "`fs.dirname` is required for relative reference");
2800
+ return fs5.dirname(path5);
2801
2801
  }
2802
2802
  lookupError(file, roots) {
2803
2803
  const err = new Error("ENOENT");
@@ -3866,17 +3866,17 @@ var require_liquid_node = __commonJS({
3866
3866
  }
3867
3867
  const continueKey = "continue-" + this.variable + "-" + this.collection.getText();
3868
3868
  ctx.push(createScope({ continue: ctx.getRegister(continueKey, {}) }));
3869
- const hash = yield this.hash.render(ctx);
3869
+ const hash2 = yield this.hash.render(ctx);
3870
3870
  ctx.pop();
3871
- const modifiers = this.liquid.options.orderedFilterParameters ? Object.keys(hash).filter((x) => MODIFIERS.includes(x)) : MODIFIERS.filter((x) => hash[x] !== void 0);
3871
+ const modifiers = this.liquid.options.orderedFilterParameters ? Object.keys(hash2).filter((x) => MODIFIERS.includes(x)) : MODIFIERS.filter((x) => hash2[x] !== void 0);
3872
3872
  collection = modifiers.reduce((collection2, modifier) => {
3873
3873
  if (modifier === "offset")
3874
- return offset(collection2, hash["offset"]);
3874
+ return offset(collection2, hash2["offset"]);
3875
3875
  if (modifier === "limit")
3876
- return limit(collection2, hash["limit"]);
3876
+ return limit(collection2, hash2["limit"]);
3877
3877
  return reversed(collection2);
3878
3878
  }, collection);
3879
- ctx.setRegister(continueKey, (hash["offset"] || 0) + collection.length);
3879
+ ctx.setRegister(continueKey, (hash2["offset"] || 0) + collection.length);
3880
3880
  const scope = createScope({ forloop: new ForloopDrop(collection.length, this.collection.getText(), this.variable) });
3881
3881
  ctx.push(scope);
3882
3882
  for (const item of collection) {
@@ -4076,12 +4076,12 @@ var require_liquid_node = __commonJS({
4076
4076
  this.hash = new Hash(tokenizer, liquid.options.keyValueSeparator);
4077
4077
  }
4078
4078
  *render(ctx, emitter) {
4079
- const { liquid, hash } = this;
4079
+ const { liquid, hash: hash2 } = this;
4080
4080
  const filepath = yield renderFilePath(this.file, ctx, liquid);
4081
4081
  assert(filepath, () => `illegal file path "${filepath}"`);
4082
4082
  const childCtx = ctx.spawn();
4083
4083
  const scope = childCtx.bottom();
4084
- __assign(scope, yield hash.render(ctx));
4084
+ __assign(scope, yield hash2.render(ctx));
4085
4085
  if (this.with) {
4086
4086
  const { value, alias } = this.with;
4087
4087
  scope[alias || filepath] = yield evalToken(value, ctx);
@@ -4196,14 +4196,14 @@ var require_liquid_node = __commonJS({
4196
4196
  this.hash = new Hash(tokenizer, liquid.options.jekyllInclude || liquid.options.keyValueSeparator);
4197
4197
  }
4198
4198
  *render(ctx, emitter) {
4199
- const { liquid, hash, withVar } = this;
4199
+ const { liquid, hash: hash2, withVar } = this;
4200
4200
  const { renderer } = liquid;
4201
4201
  const filepath = yield renderFilePath(this.file, ctx, liquid);
4202
4202
  assert(filepath, () => `illegal file path "${filepath}"`);
4203
4203
  const saved = ctx.saveRegister("blocks", "blockMode");
4204
4204
  ctx.setRegister("blocks", {});
4205
4205
  ctx.setRegister("blockMode", BlockMode.OUTPUT);
4206
- const scope = createScope(yield hash.render(ctx));
4206
+ const scope = createScope(yield hash2.render(ctx));
4207
4207
  if (withVar)
4208
4208
  scope[filepath] = yield evalToken(withVar, ctx);
4209
4209
  const templates = yield liquid._parsePartialFile(filepath, ctx.sync, this.currentFile);
@@ -5038,7 +5038,7 @@ var require_tokenize = __commonJS({
5038
5038
  module2.exports = function tokenizer(input, options = {}) {
5039
5039
  let css = input.css.valueOf();
5040
5040
  let ignore = options.ignoreErrors;
5041
- let code, content, escape3, next, quote;
5041
+ let code, content, escape4, next, quote;
5042
5042
  let currentToken, escaped, escapePos, n, prev;
5043
5043
  let length = css.length;
5044
5044
  let pos = 0;
@@ -5163,13 +5163,13 @@ var require_tokenize = __commonJS({
5163
5163
  }
5164
5164
  case BACKSLASH: {
5165
5165
  next = pos;
5166
- escape3 = true;
5166
+ escape4 = true;
5167
5167
  while (css.charCodeAt(next + 1) === BACKSLASH) {
5168
5168
  next += 1;
5169
- escape3 = !escape3;
5169
+ escape4 = !escape4;
5170
5170
  }
5171
5171
  code = css.charCodeAt(next + 1);
5172
- if (escape3 && code !== SLASH && code !== SPACE && code !== NEWLINE && code !== TAB && code !== CR && code !== FEED) {
5172
+ if (escape4 && code !== SLASH && code !== SPACE && code !== NEWLINE && code !== TAB && code !== CR && code !== FEED) {
5173
5173
  next += 1;
5174
5174
  if (RE_HEX_ESCAPE.test(css.charAt(next))) {
5175
5175
  while (RE_HEX_ESCAPE.test(css.charAt(next + 1))) {
@@ -6956,27 +6956,27 @@ var require_util = __commonJS({
6956
6956
  };
6957
6957
  }
6958
6958
  var normalize = lruMemoize(function normalize2(aPath) {
6959
- var path3 = aPath;
6959
+ var path4 = aPath;
6960
6960
  var url = urlParse(aPath);
6961
6961
  if (url) {
6962
6962
  if (!url.path) {
6963
6963
  return aPath;
6964
6964
  }
6965
- path3 = url.path;
6965
+ path4 = url.path;
6966
6966
  }
6967
- var isAbsolute = exports2.isAbsolute(path3);
6967
+ var isAbsolute = exports2.isAbsolute(path4);
6968
6968
  var parts = [];
6969
6969
  var start = 0;
6970
6970
  var i = 0;
6971
6971
  while (true) {
6972
6972
  start = i;
6973
- i = path3.indexOf("/", start);
6973
+ i = path4.indexOf("/", start);
6974
6974
  if (i === -1) {
6975
- parts.push(path3.slice(start));
6975
+ parts.push(path4.slice(start));
6976
6976
  break;
6977
6977
  } else {
6978
- parts.push(path3.slice(start, i));
6979
- while (i < path3.length && path3[i] === "/") {
6978
+ parts.push(path4.slice(start, i));
6979
+ while (i < path4.length && path4[i] === "/") {
6980
6980
  i++;
6981
6981
  }
6982
6982
  }
@@ -6997,15 +6997,15 @@ var require_util = __commonJS({
6997
6997
  }
6998
6998
  }
6999
6999
  }
7000
- path3 = parts.join("/");
7001
- if (path3 === "") {
7002
- path3 = isAbsolute ? "/" : ".";
7000
+ path4 = parts.join("/");
7001
+ if (path4 === "") {
7002
+ path4 = isAbsolute ? "/" : ".";
7003
7003
  }
7004
7004
  if (url) {
7005
- url.path = path3;
7005
+ url.path = path4;
7006
7006
  return urlGenerate(url);
7007
7007
  }
7008
- return path3;
7008
+ return path4;
7009
7009
  });
7010
7010
  exports2.normalize = normalize;
7011
7011
  function join(aRoot, aPath) {
@@ -8730,11 +8730,11 @@ var require_previous_map = __commonJS({
8730
8730
  var { existsSync, readFileSync, realpathSync } = require("fs");
8731
8731
  var { dirname, isAbsolute, join, relative, sep } = require("path");
8732
8732
  var { SourceMapConsumer, SourceMapGenerator } = require_source_map();
8733
- function realPath(path3) {
8733
+ function realPath(path4) {
8734
8734
  try {
8735
- return realpathSync(path3);
8735
+ return realpathSync(path4);
8736
8736
  } catch {
8737
- return path3;
8737
+ return path4;
8738
8738
  }
8739
8739
  }
8740
8740
  function fromBase64(str3) {
@@ -8797,19 +8797,19 @@ var require_previous_map = __commonJS({
8797
8797
  this.annotation = this.getAnnotationURL(css.substring(start, end));
8798
8798
  }
8799
8799
  }
8800
- loadFile(path3, cssFile, trusted) {
8800
+ loadFile(path4, cssFile, trusted) {
8801
8801
  if (!trusted && !this.unsafeMap) {
8802
- if (!/\.map$/i.test(path3)) return void 0;
8802
+ if (!/\.map$/i.test(path4)) return void 0;
8803
8803
  if (!cssFile) return void 0;
8804
- let rel = relative(realPath(dirname(cssFile)), realPath(path3));
8804
+ let rel = relative(realPath(dirname(cssFile)), realPath(path4));
8805
8805
  if (rel === ".." || rel.startsWith(".." + sep) || isAbsolute(rel)) {
8806
8806
  return void 0;
8807
8807
  }
8808
8808
  }
8809
- this.root = dirname(path3);
8810
- if (existsSync(path3)) {
8811
- this.mapFile = path3;
8812
- return readFileSync(path3, "utf-8").toString().trim();
8809
+ this.root = dirname(path4);
8810
+ if (existsSync(path4)) {
8811
+ this.mapFile = path4;
8812
+ return readFileSync(path4, "utf-8").toString().trim();
8813
8813
  }
8814
8814
  }
8815
8815
  loadMap(file, prev) {
@@ -9184,7 +9184,7 @@ var require_list = __commonJS({
9184
9184
  let func = 0;
9185
9185
  let inQuote = false;
9186
9186
  let prevQuote = "";
9187
- let escape3 = false;
9187
+ let escape4 = false;
9188
9188
  let inComment = false;
9189
9189
  for (let i = 0; i < string.length; i++) {
9190
9190
  let letter = string[i];
@@ -9197,10 +9197,10 @@ var require_list = __commonJS({
9197
9197
  }
9198
9198
  continue;
9199
9199
  }
9200
- if (escape3) {
9201
- escape3 = false;
9200
+ if (escape4) {
9201
+ escape4 = false;
9202
9202
  } else if (letter === "\\") {
9203
- escape3 = true;
9203
+ escape4 = true;
9204
9204
  } else if (inQuote) {
9205
9205
  if (letter === prevQuote) {
9206
9206
  inQuote = false;
@@ -9605,9 +9605,9 @@ var require_map_generator = __commonJS({
9605
9605
  if (typeof this.mapOpts.annotation === "string") {
9606
9606
  from = dirname(resolve(from, this.mapOpts.annotation));
9607
9607
  }
9608
- let path3 = relative(from, file);
9609
- this.memoizedPaths.set(file, path3);
9610
- return path3;
9608
+ let path4 = relative(from, file);
9609
+ this.memoizedPaths.set(file, path4);
9610
+ return path4;
9611
9611
  }
9612
9612
  previous() {
9613
9613
  if (!this.previousMaps) {
@@ -9662,12 +9662,12 @@ var require_map_generator = __commonJS({
9662
9662
  return window.btoa(unescape(encodeURIComponent(str3)));
9663
9663
  }
9664
9664
  }
9665
- toFileUrl(path3) {
9666
- let cached = this.memoizedFileURLs.get(path3);
9665
+ toFileUrl(path4) {
9666
+ let cached = this.memoizedFileURLs.get(path4);
9667
9667
  if (cached) return cached;
9668
9668
  if (pathToFileURL) {
9669
- let fileURL = pathToFileURL(path3).toString();
9670
- this.memoizedFileURLs.set(path3, fileURL);
9669
+ let fileURL = pathToFileURL(path4).toString();
9670
+ this.memoizedFileURLs.set(path4, fileURL);
9671
9671
  return fileURL;
9672
9672
  } else {
9673
9673
  throw new Error(
@@ -9675,14 +9675,14 @@ var require_map_generator = __commonJS({
9675
9675
  );
9676
9676
  }
9677
9677
  }
9678
- toUrl(path3) {
9679
- let cached = this.memoizedURLs.get(path3);
9678
+ toUrl(path4) {
9679
+ let cached = this.memoizedURLs.get(path4);
9680
9680
  if (cached) return cached;
9681
9681
  if (sep === "\\") {
9682
- path3 = path3.replace(/\\/g, "/");
9682
+ path4 = path4.replace(/\\/g, "/");
9683
9683
  }
9684
- let url = encodeURI(path3).replace(/[#?]/g, encodeURIComponent);
9685
- this.memoizedURLs.set(path3, url);
9684
+ let url = encodeURI(path4).replace(/[#?]/g, encodeURIComponent);
9685
+ this.memoizedURLs.set(path4, url);
9686
9686
  return url;
9687
9687
  }
9688
9688
  };
@@ -16829,13 +16829,13 @@ function normalizeFiles(raw) {
16829
16829
  const files = {};
16830
16830
  if (!raw || typeof raw !== "object") return files;
16831
16831
  let total = 0;
16832
- for (const [path3, content] of Object.entries(raw)) {
16832
+ for (const [path4, content] of Object.entries(raw)) {
16833
16833
  if (Object.keys(files).length >= TEMPLATE_LIMITS.maxFiles) break;
16834
- if (!FILE_PATH.test(path3) || typeof content !== "string") continue;
16834
+ if (!FILE_PATH.test(path4) || typeof content !== "string") continue;
16835
16835
  if (Buffer.byteLength(content, "utf8") > TEMPLATE_LIMITS.maxFileBytes) continue;
16836
16836
  total += Buffer.byteLength(content, "utf8");
16837
16837
  if (total > TEMPLATE_LIMITS.maxTotalBytes) break;
16838
- files[path3] = content;
16838
+ files[path4] = content;
16839
16839
  }
16840
16840
  return files;
16841
16841
  }
@@ -16880,8 +16880,8 @@ var TEMPLATE_LIMITS2 = {
16880
16880
  maxValuesDepth: 5
16881
16881
  };
16882
16882
  var FILE_PATH2 = /^(pages|layouts|components|styles|scripts)\/[a-z0-9][a-z0-9-_]{0,31}(\/[a-z0-9][a-z0-9-_]{0,31})?\.(liquid|css|js)$/;
16883
- function isValidTemplatePath(path3) {
16884
- return FILE_PATH2.test(path3);
16883
+ function isValidTemplatePath(path4) {
16884
+ return FILE_PATH2.test(path4);
16885
16885
  }
16886
16886
  var FIELD_KEY2 = /^[a-z][a-z0-9_]{0,39}$/;
16887
16887
  function byteLength(value) {
@@ -16941,13 +16941,13 @@ function normalizeFiles2(raw) {
16941
16941
  const files = {};
16942
16942
  if (!raw || typeof raw !== "object") return files;
16943
16943
  let total = 0;
16944
- for (const [path3, content] of Object.entries(raw)) {
16944
+ for (const [path4, content] of Object.entries(raw)) {
16945
16945
  if (Object.keys(files).length >= TEMPLATE_LIMITS2.maxFiles) break;
16946
- if (!FILE_PATH2.test(path3) || typeof content !== "string") continue;
16946
+ if (!FILE_PATH2.test(path4) || typeof content !== "string") continue;
16947
16947
  if (byteLength(content) > TEMPLATE_LIMITS2.maxFileBytes) continue;
16948
16948
  total += byteLength(content);
16949
16949
  if (total > TEMPLATE_LIMITS2.maxTotalBytes) break;
16950
- files[path3] = content;
16950
+ files[path4] = content;
16951
16951
  }
16952
16952
  return files;
16953
16953
  }
@@ -21120,7 +21120,7 @@ function createEngine(files) {
21120
21120
  return engine;
21121
21121
  }
21122
21122
  function collectTemplateCss(template) {
21123
- const css = Object.keys(template.files).filter((path3) => path3.startsWith("styles/") && path3.endsWith(".css")).sort().map((path3) => template.files[path3]).join("\n");
21123
+ const css = Object.keys(template.files).filter((path4) => path4.startsWith("styles/") && path4.endsWith(".css")).sort().map((path4) => template.files[path4]).join("\n");
21124
21124
  return sanitizeTemplateCss(css);
21125
21125
  }
21126
21126
  async function renderTemplatePage(template, page, context) {
@@ -21155,6 +21155,260 @@ function isTemplateBodyPage(value) {
21155
21155
  return typeof value === "string" && TEMPLATE_BODY_PAGES.some((page) => page === value);
21156
21156
  }
21157
21157
 
21158
+ // ../../site/src/lib/template/language-reference.ts
21159
+ var TEMPLATE_TAGS = [
21160
+ {
21161
+ name: "assign",
21162
+ description: "Atribui um valor a uma vari\xE1vel local.",
21163
+ example: '{% assign title = "Loja" %}{{ title }}'
21164
+ },
21165
+ {
21166
+ name: "capture",
21167
+ description: "Captura um bloco em texto.",
21168
+ example: "{% capture title %}Loja {{ year }}{% endcapture %}{{ title }}"
21169
+ },
21170
+ {
21171
+ name: "if",
21172
+ description: "Condi\xE7\xE3o com elsif e else; encerra com endif.",
21173
+ example: "{% if products.size > 0 %}Cat\xE1logo{% elsif store.name %}Loja{% else %}Vazio{% endif %}"
21174
+ },
21175
+ {
21176
+ name: "unless",
21177
+ description: "Condi\xE7\xE3o invertida, com else opcional.",
21178
+ example: "{% unless products.size == 0 %}Produtos{% else %}Vazio{% endunless %}"
21179
+ },
21180
+ {
21181
+ name: "case",
21182
+ description: "Seleciona um bloco when ou else.",
21183
+ example: '{% case page.kind %}{% when "index" %}In\xEDcio{% else %}Detalhe{% endcase %}'
21184
+ },
21185
+ {
21186
+ name: "for",
21187
+ description: "Itera listas ou intervalos. Suporta limit, offset, reversed, else e forloop.",
21188
+ example: "{% for n in (1..3) limit: 2 %}{{ forloop.index }}:{{ n }}{% else %}Vazio{% endfor %}"
21189
+ },
21190
+ { name: "break", description: "Encerra o la\xE7o atual.", example: "{% for n in (1..3) %}{{ n }}{% break %}{% endfor %}" },
21191
+ {
21192
+ name: "continue",
21193
+ description: "Pula para a pr\xF3xima itera\xE7\xE3o.",
21194
+ example: "{% for n in (1..3) %}{% if n == 2 %}{% continue %}{% endif %}{{ n }}{% endfor %}"
21195
+ },
21196
+ {
21197
+ name: "cycle",
21198
+ description: "Alterna valores durante o la\xE7o; aceita grupo nomeado.",
21199
+ example: '{% for n in (1..3) %}{% cycle "par", "\xEDmpar" %}{% endfor %}'
21200
+ },
21201
+ {
21202
+ name: "increment",
21203
+ description: "Contador independente, come\xE7ando em zero.",
21204
+ example: "{% increment counter %}{% increment counter %}"
21205
+ },
21206
+ {
21207
+ name: "decrement",
21208
+ description: "Contador independente, come\xE7ando em menos um.",
21209
+ example: "{% decrement counter %}{% decrement counter %}"
21210
+ },
21211
+ { name: "echo", description: "Exibe express\xE3o, com escape da sa\xEDda.", example: "{% echo store.name | upcase %}" },
21212
+ {
21213
+ name: "liquid",
21214
+ description: "Agrupa comandos, um por linha, sem delimitadores internos.",
21215
+ example: '{% liquid\nassign title = "Loja"\necho title\n%}'
21216
+ },
21217
+ {
21218
+ name: "comment",
21219
+ description: "Ignora o conte\xFAdo at\xE9 endcomment.",
21220
+ example: "{% comment %}Observa\xE7\xE3o interna{% endcomment %}Loja"
21221
+ },
21222
+ { name: "#", description: "Coment\xE1rio em linha.", example: "{% # observa\xE7\xE3o %}Loja" },
21223
+ {
21224
+ name: "raw",
21225
+ description: "Exibe texto Liquid sem interpretar vari\xE1veis.",
21226
+ example: "{% raw %}{{ exemplo }}{% endraw %}"
21227
+ },
21228
+ {
21229
+ name: "render",
21230
+ description: "Renderiza arquivo do projeto em escopo isolado; aceita argumentos, with e for.",
21231
+ example: '{% render "components/reference", title: "Loja" %}'
21232
+ },
21233
+ {
21234
+ name: "include",
21235
+ description: "Inclui arquivo do projeto no escopo atual; prefira render para isolamento.",
21236
+ example: '{% include "components/reference" %}'
21237
+ },
21238
+ {
21239
+ name: "layout",
21240
+ description: "Aplica arquivo de layout com blocos nomeados.",
21241
+ example: '{% layout "layouts/reference" %}{% block content %}Loja{% endblock %}'
21242
+ },
21243
+ {
21244
+ name: "block",
21245
+ description: "Declara bloco substitu\xEDvel em um layout; block.super acessa o conte\xFAdo herdado.",
21246
+ example: "{% block content %}Loja{% endblock %}"
21247
+ },
21248
+ {
21249
+ name: "tablerow",
21250
+ description: "Produz linhas/c\xE9lulas HTML para uma lista; cols, limit, offset e tablerowloop.",
21251
+ example: "<table>{% tablerow n in (1..3) cols: 2 %}{{ n }}{% endtablerow %}</table>"
21252
+ }
21253
+ ];
21254
+ var FILTER_ROWS = [
21255
+ ["escape", "Escapa HTML.", '"<b>" | escape'],
21256
+ ["xml_escape", "Escapa entidades XML.", '"<b>" | xml_escape'],
21257
+ ["escape_once", "Escapa sem duplicar entidades existentes.", '"&amp;<" | escape_once'],
21258
+ ["newline_to_br", "Converte quebras de linha em br.", '"texto" | newline_to_br'],
21259
+ ["strip_html", "Remove marca\xE7\xE3o HTML.", '"<b>Loja</b>" | strip_html'],
21260
+ ["abs", "Valor absoluto.", "-3 | abs"],
21261
+ ["at_least", "Aplica valor m\xEDnimo.", "2 | at_least: 3"],
21262
+ ["at_most", "Aplica valor m\xE1ximo.", "5 | at_most: 3"],
21263
+ ["ceil", "Arredonda para cima.", "2.2 | ceil"],
21264
+ ["floor", "Arredonda para baixo.", "2.8 | floor"],
21265
+ ["divided_by", "Divide pelo argumento.", "8 | divided_by: 2"],
21266
+ ["minus", "Subtrai argumento.", "8 | minus: 2"],
21267
+ ["plus", "Soma argumento.", "8 | plus: 2"],
21268
+ ["modulo", "Resto da divis\xE3o.", "8 | modulo: 3"],
21269
+ ["times", "Multiplica argumento.", "8 | times: 2"],
21270
+ ["round", "Arredonda; argumento define casas decimais.", "2.567 | round: 2"],
21271
+ ["url_decode", "Decodifica texto de URL.", '"a%20b" | url_decode'],
21272
+ ["url_encode", "Codifica texto de URL.", '"a b" | url_encode'],
21273
+ ["cgi_escape", "Codifica texto para query CGI.", '"a b" | cgi_escape'],
21274
+ ["uri_escape", "Escapa caracteres n\xE3o seguros de URI.", '"a b" | uri_escape'],
21275
+ [
21276
+ "slugify",
21277
+ "Vers\xE3o CeuPay: remove acentos, converte para min\xFAsculas e separa palavras com h\xEDfen.",
21278
+ '"C\xE9u Azul" | slugify'
21279
+ ],
21280
+ ["join", "Une lista com separador.", '"a,b" | split: "," | join: " / "'],
21281
+ ["first", "Primeiro elemento.", '"a,b" | split: "," | first'],
21282
+ ["last", "\xDAltimo elemento.", '"a,b" | split: "," | last'],
21283
+ ["reverse", "Inverte lista.", '"a,b" | split: "," | reverse | join: ","'],
21284
+ ["sort", "Ordena lista; argumento opcional seleciona propriedade.", '"b,a" | split: "," | sort | join: ","'],
21285
+ ["sort_natural", "Ordena ignorando caixa.", '"b,A" | split: "," | sort_natural | join: ","'],
21286
+ ["size", "Tamanho de texto/lista.", '"Loja" | size'],
21287
+ ["map", "Extrai propriedade de cada item.", 'products | map: "name" | join: ","'],
21288
+ ["sum", "Soma valores ou propriedade num\xE9rica.", 'products | sum: "price_cents"'],
21289
+ ["compact", "Remove valores nulos; aceita propriedade.", 'products | map: "name" | compact | size'],
21290
+ ["concat", "Combina duas listas.", "products | concat: products | size"],
21291
+ ["push", "Acrescenta um item ao fim de uma c\xF3pia.", '"a,b" | split: "," | push: "c" | join: ","'],
21292
+ ["unshift", "Acrescenta um item ao in\xEDcio de uma c\xF3pia.", '"a,b" | split: "," | unshift: "c" | join: ","'],
21293
+ ["pop", "Remove o \xFAltimo item de uma c\xF3pia.", '"a,b" | split: "," | pop | join: ","'],
21294
+ ["shift", "Remove o primeiro item de uma c\xF3pia.", '"a,b" | split: "," | shift | join: ","'],
21295
+ ["slice", "Seleciona trecho por \xEDndice e tamanho.", '"Loja" | slice: 0, 2'],
21296
+ ["where", "Seleciona itens por propriedade e valor.", 'products | where: "in_stock", true | size'],
21297
+ ["reject", "Exclui itens por propriedade e valor.", 'products | reject: "in_stock", false | size'],
21298
+ ["where_exp", "Seleciona por express\xE3o Liquid.", 'products | where_exp: "p", "p.price_cents > 0" | size'],
21299
+ ["reject_exp", "Exclui por express\xE3o Liquid.", 'products | reject_exp: "p", "p.price_cents == 0" | size'],
21300
+ ["group_by", "Agrupa por propriedade; itens exp\xF5em name/items.", 'products | group_by: "category" | size'],
21301
+ ["group_by_exp", "Agrupa pelo resultado de express\xE3o.", 'products | group_by_exp: "p", "p.category" | size'],
21302
+ ["has", "Verifica se existe item com propriedade e valor.", 'products | has: "in_stock", true'],
21303
+ ["has_exp", "Verifica exist\xEAncia por express\xE3o.", 'products | has_exp: "p", "p.price_cents > 0"'],
21304
+ ["find_index", "\xCDndice do primeiro item com propriedade e valor.", 'products | find_index: "in_stock", true'],
21305
+ ["find_index_exp", "\xCDndice do primeiro item que atende express\xE3o.", 'products | find_index_exp: "p", "p.price_cents > 0"'],
21306
+ ["find", "Primeiro item com propriedade e valor.", 'products | find: "in_stock", true | json'],
21307
+ ["find_exp", "Primeiro item que atende express\xE3o.", 'products | find_exp: "p", "p.price_cents > 0" | json'],
21308
+ ["uniq", "Remove duplicatas; aceita propriedade.", '"a,a,b" | split: "," | uniq | join: ","'],
21309
+ ["sample", "Amostra aleat\xF3ria; n\xE3o use em decis\xF5es de pre\xE7o ou entrega.", '"a,b" | split: "," | sample'],
21310
+ ["date", "Formata data usando marcadores strftime.", '"2026-10-29" | date: "%d/%m/%Y"'],
21311
+ ["date_to_xmlschema", "Formata data ISO/XML.", '"2026-10-29" | date_to_xmlschema'],
21312
+ ["date_to_rfc822", "Formata data RFC822.", '"2026-10-29" | date_to_rfc822'],
21313
+ ["date_to_string", "Data curta leg\xEDvel.", '"2026-10-29" | date_to_string'],
21314
+ ["date_to_long_string", "Data longa leg\xEDvel.", '"2026-10-29" | date_to_long_string'],
21315
+ ["append", "Acrescenta texto ao fim.", '"Loja" | append: " CeuPay"'],
21316
+ ["prepend", "Acrescenta texto ao in\xEDcio.", '"Loja" | prepend: "Minha "'],
21317
+ ["lstrip", "Remove espa\xE7o \xE0 esquerda.", '" Loja " | lstrip'],
21318
+ ["rstrip", "Remove espa\xE7o \xE0 direita.", '" Loja " | rstrip'],
21319
+ ["strip", "Remove espa\xE7o nas extremidades.", '" Loja " | strip'],
21320
+ ["downcase", "Converte para min\xFAsculas.", '"LOJA" | downcase'],
21321
+ ["upcase", "Converte para mai\xFAsculas.", '"loja" | upcase'],
21322
+ ["remove", "Remove todas as ocorr\xEAncias.", '"abcabc" | remove: "a"'],
21323
+ ["remove_first", "Remove a primeira ocorr\xEAncia.", '"abcabc" | remove_first: "a"'],
21324
+ ["remove_last", "Remove a \xFAltima ocorr\xEAncia.", '"abcabc" | remove_last: "a"'],
21325
+ ["split", "Divide texto em lista.", '"a,b" | split: "," | size'],
21326
+ ["strip_newlines", "Remove quebras de linha.", '"Loja" | strip_newlines'],
21327
+ ["squish", "Compacta espa\xE7os e remove extremidades.", '" a b " | squish'],
21328
+ ["capitalize", "Inicial mai\xFAscula e restante min\xFAsculo.", '"loja" | capitalize'],
21329
+ ["replace", "Substitui todas as ocorr\xEAncias.", '"abcabc" | replace: "a", "x"'],
21330
+ ["replace_first", "Substitui a primeira ocorr\xEAncia.", '"abcabc" | replace_first: "a", "x"'],
21331
+ ["replace_last", "Substitui a \xFAltima ocorr\xEAncia.", '"abcabc" | replace_last: "a", "x"'],
21332
+ ["truncate", "Limita caracteres; aceita sufixo.", '"Loja CeuPay" | truncate: 7, "\u2026"'],
21333
+ ["truncatewords", "Limita palavras; aceita sufixo.", '"Minha Loja CeuPay" | truncatewords: 2'],
21334
+ ["normalize_whitespace", "Normaliza espa\xE7os.", '"a b" | normalize_whitespace'],
21335
+ ["number_of_words", "Conta palavras.", '"Minha loja" | number_of_words'],
21336
+ ["array_to_sentence_string", "Formata lista como frase.", '"a,b,c" | split: "," | array_to_sentence_string'],
21337
+ ["base64_encode", "Codifica texto em Base64; n\xE3o protege segredos.", '"Loja" | base64_encode'],
21338
+ ["base64_decode", "Decodifica Base64.", '"TG9qYQ==" | base64_decode'],
21339
+ ["sha256", "Hash SHA-256 de texto p\xFAblico.", '"Loja" | sha256'],
21340
+ [
21341
+ "hmac_sha256",
21342
+ "HMAC de texto com chave expl\xEDcita fict\xEDcia; nunca forne\xE7a credenciais ao contexto.",
21343
+ '"Loja" | hmac_sha256: "exemplo-publico"'
21344
+ ],
21345
+ ["default", "Fallback para null, false ou vazio; allow_false preserva false.", '"" | default: "Loja"'],
21346
+ ["raw", "Pula escape da sa\xEDda Liquid; a sanitiza\xE7\xE3o HTML final continua obrigat\xF3ria.", '"<b>Loja</b>" | raw'],
21347
+ ["jsonify", "Serializa para JSON.", "products | jsonify"],
21348
+ ["json", "Serializa para JSON, equivalente a jsonify.", "products | json"],
21349
+ ["inspect", "Representa\xE7\xE3o JSON para depura\xE7\xE3o de dados p\xFAblicos.", "products | inspect"],
21350
+ ["to_integer", "Converte para inteiro.", '"12" | to_integer']
21351
+ ];
21352
+ var TEMPLATE_FILTERS = FILTER_ROWS.map(([name, description, expression]) => ({
21353
+ name,
21354
+ description,
21355
+ example: `{{ ${expression} }}`
21356
+ }));
21357
+
21358
+ // src/reference.ts
21359
+ var agentInstructions = `# CeuPay store template
21360
+
21361
+ Read CEUPAY-REFERENCE.md before editing. This project belongs to one authorized store.
21362
+ Edit ceupay-template.json and pages/, layouts/, components/, styles/, scripts/ only.
21363
+ Use standard Liquid {{ output }} and {% tag %}. Never invent globals or a load tag.
21364
+ Use npx @ceupay/template-cli@alpha validate . and serve . for interactive fictitious-data preview.
21365
+ No credentials belong in this project, browser code, commits, prompts or generated documentation.
21366
+ Do not read environment secrets. The merchant pastes the short-lived session into the CLI terminal.
21367
+ The --watch --yes mode publishes valid saved changes to the live store: tell the merchant before saving edits.
21368
+ Do not start remote publication, replay, restoration or synchronization without the merchant's authorization.
21369
+ Preserve .ceupay-remote.json, locks and .ceupay-operations/; never edit or delete operation receipts to bypass conflicts.
21370
+ On uncertainty stop; recover the SAME operation with replay and pull a fresh folder before continuing.
21371
+ Run no seller JavaScript in Node. Seller scripts execute only in the opaque browser preview iframe.
21372
+ Checkout, identity, stock and payment authority remain on the platform.
21373
+ Full references: https://froghub.com.br/docs/templates-referencia
21374
+ `;
21375
+ var templateReference = `# CeuPay template reference
21376
+
21377
+ Files: ceupay-template.json (format ceupay-template-project, formatVersion 1, template metadata without files).
21378
+ Metadata: name, baseId (string|null), version, schema.groups, values.
21379
+ Code directories: pages, layouts, components, styles, scripts. Maximum 40 files, 100000 bytes/file, 400000 bytes total.
21380
+ One optional subdirectory; segments are lowercase letters/numbers/hyphen/underscore, at most 32 characters.
21381
+ Required page: pages/index.liquid. Optional pages: catalog, product, post. Header/footer: components/header.liquid and footer.liquid.
21382
+ HTML is sanitized; CSS is scoped; scripts/*.js are classic scripts, isolated with sandbox=allow-scripts, never allow-same-origin.
21383
+ window.ceupay.navigate(href), addToCart(productId, quantity) are validated bridges; local preview SIMULATES these actions.
21384
+
21385
+ Globals: store{name,slug,tagline,description,logo_url,banner_url,server_ip,initial,social{discord,youtube,instagram,tiktok,wiki},texts,show_reviews,show_news,show_online_players}; settings; products[]; categories[{name,slug,anchor,count,products}]; reviews[{rating,comment,author}]; news[{id,title,summary,published_at,url}]; players{online,reporting_servers,observed_at,valid_until}; connections[{label,platform,host,port,address,key,button_label}]; stats{products_count,categories_count,reviews_count,reviews_average,cheapest_price}; urls{home,products,catalog?,cart,checkout,account}; year; page.kind.
21386
+ Product: id,name,slug,url,description,image_url,images,type,category,price(formatted),price_cents(number),in_stock,available_count(null=unlimited/unknown),stock_mode?,has_variants?,variants?,benefits[].
21387
+ Product page: product. Post page: post{id,title,summary,body,published_at,url}. Catalog: catalog{search,page,total_pages,total,previous_url,next_url}; products is filtered/paginated. Other detail globals are null.
21388
+ Schema: groups[{title,fields[]}]; field{key,label,type,description,default,options:[{value,label}],fields:[],max:null|number}.
21389
+ Types: text,textarea,url,number,color,toggle,select,image,richtext,array,packages. packages resolves selected public products. array uses nested fields.
21390
+ Keys: lowercase initial letter, lowercase letters/numbers/underscore, max40 chars, unique. Max30 groups/40 fields per group/50 select options/100 array or package selections. Values max120000 bytes/depth5.
21391
+ No private orders, payment data, credentials or arbitrary entity loading. Missing values are empty; unknown filters are no-ops, not extra features. Empty arrays/strings are truthy in Liquid; test size or blank. Only false and nil are falsy.
21392
+ Operators: == != > < >= <= contains and or. Use assign instead of unsupported parentheses in conditions. Whitespace trim: {{- value -}} and {%- tag -%}.
21393
+ Limits: render CPU2500ms, memory64000000, parse600000; in-memory project partials only. Prefer explicit names with render.
21394
+
21395
+ ## Tags
21396
+ ${TEMPLATE_TAGS.map((row) => `### ${row.name}
21397
+ ${row.description}
21398
+
21399
+ \`\`\`liquid
21400
+ ${row.example}
21401
+ \`\`\``).join("\n\n")}
21402
+
21403
+ ## Filters
21404
+ ${TEMPLATE_FILTERS.map((row) => `### ${row.name}
21405
+ ${row.description}
21406
+
21407
+ \`\`\`liquid
21408
+ ${row.example}
21409
+ \`\`\``).join("\n\n")}
21410
+ `;
21411
+
21158
21412
  // src/project.ts
21159
21413
  var ProjectError = class extends Error {
21160
21414
  };
@@ -21288,6 +21542,14 @@ async function createProject(directory, template) {
21288
21542
  await import_promises.default.mkdir(import_node_path.default.dirname(target2), { recursive: true, mode: 448 });
21289
21543
  await import_promises.default.writeFile(target2, contents, { flag: "wx", mode: 384 });
21290
21544
  }
21545
+ for (const [file, contents] of Object.entries({
21546
+ "AGENTS.md": agentInstructions,
21547
+ "CLAUDE.md": agentInstructions,
21548
+ "CEUPAY-REFERENCE.md": templateReference,
21549
+ ".gitignore": ".ceupay-remote*\n.ceupay-operations/\n.env*\n"
21550
+ })) {
21551
+ await import_promises.default.writeFile(import_node_path.default.join(root, file), contents, { flag: "wx", mode: 384 });
21552
+ }
21291
21553
  }
21292
21554
  async function initialize(baseId, directory) {
21293
21555
  const template = cloneBaseTemplate(baseId);
@@ -21924,9 +22186,16 @@ async function boundedFetch(request) {
21924
22186
  function connection(options) {
21925
22187
  if (process.env.NODE_TLS_REJECT_UNAUTHORIZED === "0") return fail2("A verifica\xE7\xE3o TLS precisa estar ativa.");
21926
22188
  const apiOrigin = origin(options["api-origin"] ?? process.env.CEUPAY_API_ORIGIN ?? "https://froghub.com.br");
21927
- const key = process.env.CEUPAY_API_KEY;
22189
+ const key = process.env.CEUPAY_EDITOR_TOKEN ?? process.env.CEUPAY_API_KEY;
21928
22190
  try {
21929
- return { client: createCeuPayClient({ apiOrigin, apiKey: key ?? "", fetch: boundedFetch }), apiOrigin };
22191
+ return {
22192
+ client: createCeuPayClient({
22193
+ apiOrigin,
22194
+ apiKey: key ?? "",
22195
+ fetch: boundedFetch
22196
+ }),
22197
+ apiOrigin
22198
+ };
21930
22199
  } catch {
21931
22200
  return fail2("Configure CEUPAY_API_KEY no ambiente; n\xE3o use a chave como argumento.");
21932
22201
  }
@@ -21980,11 +22249,14 @@ async function current(client, storeId) {
21980
22249
  async function historical(client, storeId, selected) {
21981
22250
  const value = snapshot(
21982
22251
  await network(
21983
- async () => result(await client.GET("/v1/templates/history/{revision}", { params: { path: { revision: selected } } }))
22252
+ async () => result(
22253
+ await client.GET("/v1/templates/history/{revision}", {
22254
+ params: { path: { revision: selected } }
22255
+ })
22256
+ )
21984
22257
  )
21985
22258
  );
21986
- if (value.storeId !== storeId || value.revision !== selected)
21987
- return fail2("Vers\xE3o hist\xF3rica de outra loja ou revis\xE3o.");
22259
+ if (value.storeId !== storeId || value.revision !== selected) return fail2("Vers\xE3o hist\xF3rica de outra loja ou revis\xE3o.");
21988
22260
  return value;
21989
22261
  }
21990
22262
  function bind(apiOrigin, head) {
@@ -22018,21 +22290,11 @@ function checkOrigin(actual, configured) {
22018
22290
  function target(options, saved) {
22019
22291
  const selected = options.store ?? saved?.storeId;
22020
22292
  if (!uuid(selected)) return fail2("Informe --store com o UUID da loja para um projeto ainda sem v\xEDnculo.");
22021
- if (saved && saved.storeId.toLowerCase() !== selected.toLowerCase())
22022
- return fail2("O projeto est\xE1 vinculado a outra loja.");
22293
+ if (saved && saved.storeId.toLowerCase() !== selected.toLowerCase()) return fail2("O projeto est\xE1 vinculado a outra loja.");
22023
22294
  return selected.toLowerCase();
22024
22295
  }
22025
22296
  function operation(raw) {
22026
- if (!record2(raw) || !exact(raw, [
22027
- "format",
22028
- "formatVersion",
22029
- "apiOrigin",
22030
- "storeId",
22031
- "kind",
22032
- "templateHash",
22033
- "sourceChecksum",
22034
- "request"
22035
- ]) || raw.format !== "ceupay-template-operation" || raw.formatVersion !== 1 || !uuid(raw.storeId) || origin(raw.apiOrigin) !== raw.apiOrigin || !record2(raw.request))
22297
+ if (!record2(raw) || !exact(raw, ["format", "formatVersion", "apiOrigin", "storeId", "kind", "templateHash", "sourceChecksum", "request"]) || raw.format !== "ceupay-template-operation" || raw.formatVersion !== 1 || !uuid(raw.storeId) || origin(raw.apiOrigin) !== raw.apiOrigin || !record2(raw.request))
22036
22298
  return fail2("Arquivo da opera\xE7\xE3o inv\xE1lido.");
22037
22299
  const request = raw.request, kind = raw.kind;
22038
22300
  if (kind !== "PUBLISH" && kind !== "RESTORE" || !exact(request, [
@@ -22049,8 +22311,7 @@ function operation(raw) {
22049
22311
  checkedTemplate(request.template);
22050
22312
  } else if (!revision(request.sourceRevision) || !checksum(raw.sourceChecksum) || raw.templateHash !== null)
22051
22313
  return fail2("Restaura\xE7\xE3o inv\xE1lida.");
22052
- if (Buffer.byteLength(JSON.stringify(request), "utf8") > 600 * 1024)
22053
- return fail2("O corpo da opera\xE7\xE3o excede 600 KiB.");
22314
+ if (Buffer.byteLength(JSON.stringify(request), "utf8") > 600 * 1024) return fail2("O corpo da opera\xE7\xE3o excede 600 KiB.");
22054
22315
  return raw;
22055
22316
  }
22056
22317
  async function saveOperation(file, value) {
@@ -22069,12 +22330,15 @@ async function apply(client, value) {
22069
22330
  await current(client, value.storeId);
22070
22331
  if (value.kind === "RESTORE") {
22071
22332
  const source = await historical(client, value.storeId, value.request.sourceRevision);
22072
- if (source.checksum !== value.sourceChecksum)
22073
- return fail2("O snapshot hist\xF3rico mudou; revis\xE3o do operador necess\xE1ria.");
22333
+ if (source.checksum !== value.sourceChecksum) return fail2("O snapshot hist\xF3rico mudou; revis\xE3o do operador necess\xE1ria.");
22074
22334
  }
22075
22335
  const raw = await network(
22076
22336
  async () => result(
22077
- value.kind === "PUBLISH" ? await client.POST("/v1/templates/publications", { body: value.request }) : await client.POST("/v1/templates/restorations", { body: value.request })
22337
+ value.kind === "PUBLISH" ? await client.POST("/v1/templates/publications", {
22338
+ body: value.request
22339
+ }) : await client.POST("/v1/templates/restorations", {
22340
+ body: value.request
22341
+ })
22078
22342
  ),
22079
22343
  true
22080
22344
  );
@@ -22140,6 +22404,7 @@ async function publishProject(directory, value, client, apiOrigin) {
22140
22404
  console.error(
22141
22405
  "Opera\xE7\xE3o confirmada; v\xEDnculo local n\xE3o atualizado. Baixe o tema atual em outra pasta e revise suas altera\xE7\xF5es."
22142
22406
  );
22407
+ return updated;
22143
22408
  } finally {
22144
22409
  await handle.close().catch(() => void 0);
22145
22410
  try {
@@ -22187,20 +22452,35 @@ async function runRemote(command, args) {
22187
22452
  if (limit < 1 || limit > 50) return fail2("Quantidade deve ser de 1 a 50.");
22188
22453
  const beforeRevision = options.before === void 0 ? void 0 : integer(options.before, "Revis\xE3o");
22189
22454
  const page = await network(
22190
- async () => result(await client.GET("/v1/templates/history", { params: { query: { limit, beforeRevision } } }))
22455
+ async () => result(
22456
+ await client.GET("/v1/templates/history", {
22457
+ params: { query: { limit, beforeRevision } }
22458
+ })
22459
+ )
22191
22460
  );
22192
22461
  if (!record2(page) || !Array.isArray(page.data) || page.data.length > limit || page.nextRevision !== null && !revision(page.nextRevision))
22193
22462
  return fail2("Hist\xF3rico inv\xE1lido.");
22194
22463
  const rows = page.data.map((value2) => {
22195
22464
  if (!record2(value2) || !revision(value2.revision) || !checksum(value2.checksum) || value2.origin !== "BASELINE" && value2.origin !== "WRITE" || typeof value2.createdAt !== "string" || !Number.isFinite(Date.parse(value2.createdAt)))
22196
22465
  return fail2("Metadados do hist\xF3rico inv\xE1lidos.");
22197
- return { revision: value2.revision, checksum: value2.checksum, origin: value2.origin, createdAt: value2.createdAt };
22466
+ return {
22467
+ revision: value2.revision,
22468
+ checksum: value2.checksum,
22469
+ origin: value2.origin,
22470
+ createdAt: value2.createdAt
22471
+ };
22198
22472
  });
22199
22473
  if (rows.some(
22200
22474
  (value2, index) => beforeRevision !== void 0 && value2.revision >= beforeRevision || index > 0 && rows[index - 1].revision <= value2.revision
22201
22475
  ) || page.nextRevision !== null && page.nextRevision !== rows.at(-1)?.revision)
22202
22476
  return fail2("Pagina\xE7\xE3o do hist\xF3rico inconsistente.");
22203
- console.log(json({ storeId: head.storeId, data: rows, nextRevision: page.nextRevision }).trim());
22477
+ console.log(
22478
+ json({
22479
+ storeId: head.storeId,
22480
+ data: rows,
22481
+ nextRevision: page.nextRevision
22482
+ }).trim()
22483
+ );
22204
22484
  return;
22205
22485
  }
22206
22486
  if (command === "prepare-publish") {
@@ -22242,16 +22522,279 @@ async function runRemote(command, args) {
22242
22522
  checkOrigin(value.apiOrigin, apiOrigin);
22243
22523
  if (command === "publish") {
22244
22524
  if (value.kind !== "PUBLISH") return fail2("Use restore para uma opera\xE7\xE3o de restaura\xE7\xE3o.");
22245
- await publishProject(first, value, client, apiOrigin);
22246
- return;
22525
+ return publishProject(first, value, client, apiOrigin);
22247
22526
  }
22248
22527
  if (command === "restore" && value.kind !== "RESTORE") return fail2("Use publish ou replay para uma publica\xE7\xE3o.");
22249
22528
  console.log(json(receipt(await apply(client, value))).trim());
22250
22529
  }
22530
+ async function remoteMatchesProject(directory) {
22531
+ const root = await privateRoot(directory), saved = await binding(root);
22532
+ if (!saved) return fail2("O projeto precisa ser baixado com pull antes da sincroniza\xE7\xE3o.");
22533
+ const { client, apiOrigin } = connection({});
22534
+ checkOrigin(saved.apiOrigin, apiOrigin);
22535
+ const head = await current(client, saved.storeId);
22536
+ if (saved.revision !== head.revision || saved.checksum !== head.checksum)
22537
+ return fail2("O tema mudou na loja. Baixe uma pasta nova e revise as altera\xE7\xF5es.");
22538
+ if (!head.theme?.template) return false;
22539
+ return digest({ ...await loadProject(root), version: 1 }) === digest({ ...checkedTemplate(head.theme.template), version: 1 });
22540
+ }
22541
+
22542
+ // src/local.ts
22543
+ var import_promises3 = __toESM(require("node:fs/promises"), 1);
22544
+ var import_node_path3 = __toESM(require("node:path"), 1);
22545
+ var import_node_http = __toESM(require("node:http"), 1);
22546
+ var import_node_crypto2 = require("node:crypto");
22547
+ var import_promises4 = require("node:timers/promises");
22548
+
22549
+ // ../../site/src/lib/template/sandbox-doc.ts
22550
+ function collectTemplateScripts(template) {
22551
+ return Object.keys(template.files).filter((path4) => path4.startsWith("scripts/") && path4.endsWith(".js")).sort().map((path4) => `// ${path4}
22552
+ ${template.files[path4]}`).join("\n;\n");
22553
+ }
22554
+ var BRIDGE = `
22555
+ (function () {
22556
+ var send = function (message) { parent.postMessage(Object.assign({ source: 'ceupay-template' }, message), '*'); };
22557
+ window.ceupay = {
22558
+ addToCart: function (productId, quantity) { send({ type: 'addToCart', productId: String(productId), quantity: Number(quantity) || 1 }); },
22559
+ navigate: function (href) { send({ type: 'navigate', href: String(href) }); }
22560
+ };
22561
+ document.addEventListener('click', function (event) {
22562
+ var link = event.target && event.target.closest ? event.target.closest('a[href]') : null;
22563
+ if (!link) return;
22564
+ var href = link.getAttribute('href') || '';
22565
+ if (href.charAt(0) === '#') return;
22566
+ event.preventDefault();
22567
+ send({ type: 'navigate', href: href, blank: link.getAttribute('target') === '_blank' });
22568
+ });
22569
+ var report = function () { send({ type: 'resize', height: Math.ceil(document.documentElement.scrollHeight) }); };
22570
+ new ResizeObserver(report).observe(document.documentElement);
22571
+ window.addEventListener('load', report);
22572
+ })();
22573
+ `;
22574
+ function escapeScript(code) {
22575
+ return code.replaceAll("</script", "<\\/script").replaceAll("<!--", "<\\!--");
22576
+ }
22577
+ function escapeAttribute2(value) {
22578
+ return value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;");
22579
+ }
22580
+ function buildSandboxDocument({
22581
+ template,
22582
+ html,
22583
+ wrapperStyle,
22584
+ nonce,
22585
+ page = "pages/index"
22586
+ }) {
22587
+ const css = collectTemplateCss(template);
22588
+ const scripts = escapeScript(collectTemplateScripts(template));
22589
+ const safeNonce = escapeAttribute2(nonce);
22590
+ return `<!doctype html><html lang="pt-BR"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><base target="_blank"><style>html,body{margin:0;overflow:hidden;background:transparent;color:#fff;font-family:system-ui,sans-serif}</style><style>${css}</style></head><body><div data-store-template data-template-page="${escapeAttribute2(page)}" style="${escapeAttribute2(wrapperStyle)}">${html}</div><script nonce="${safeNonce}">${BRIDGE}</script><script nonce="${safeNonce}">try{${scripts}
22591
+ }catch(e){console.error(e)}</script></body></html>`;
22592
+ }
22593
+
22594
+ // src/local.ts
22595
+ var hash = (value) => (0, import_node_crypto2.createHash)("sha256").update(JSON.stringify(value)).digest("hex");
22596
+ var escape3 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll('"', "&quot;");
22597
+ async function sessionSecret() {
22598
+ if (process.env.CEUPAY_EDITOR_TOKEN) return process.env.CEUPAY_EDITOR_TOKEN;
22599
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
22600
+ throw new ProjectError("Configure CEUPAY_EDITOR_TOKEN ou use um terminal interativo.");
22601
+ process.stdout.write("Cole a sess\xE3o criada no painel (entrada oculta): ");
22602
+ return new Promise((resolve, reject2) => {
22603
+ let value = "";
22604
+ const previous = process.stdin.isRaw;
22605
+ process.stdin.setRawMode(true);
22606
+ process.stdin.resume();
22607
+ process.stdin.setEncoding("utf8");
22608
+ const finish = () => {
22609
+ process.stdin.off("data", receive);
22610
+ process.stdin.setRawMode(previous);
22611
+ process.stdin.pause();
22612
+ process.stdout.write("\n");
22613
+ };
22614
+ const receive = (chunk) => {
22615
+ for (const character of chunk) {
22616
+ if (character === "") {
22617
+ finish();
22618
+ reject2(new ProjectError("Edi\xE7\xE3o cancelada."));
22619
+ return;
22620
+ }
22621
+ if (character === "\r" || character === "\n") {
22622
+ finish();
22623
+ if (!/^cc_prod_[a-f0-9]{64}$/.test(value)) reject2(new ProjectError("Sess\xE3o inv\xE1lida."));
22624
+ else resolve(value);
22625
+ return;
22626
+ }
22627
+ if (character === "\x7F" || character === "\b") value = value.slice(0, -1);
22628
+ else if (value.length < 80 && /[a-z0-9_]/i.test(character)) value += character;
22629
+ }
22630
+ };
22631
+ process.stdin.on("data", receive);
22632
+ });
22633
+ }
22634
+ async function previewDocument(directory, page = "index") {
22635
+ if (!["index", "catalog", "product", "post"].includes(page)) throw new ProjectError("P\xE1gina inv\xE1lida.");
22636
+ const template = await loadProject(directory);
22637
+ const demo = findBaseDemo(template.baseId ?? "fogueira") ?? findBaseDemo("fogueira");
22638
+ if (!demo) throw new ProjectError("Cat\xE1logo de exemplo indispon\xEDvel.");
22639
+ const selected = `pages/${page}`;
22640
+ if (!template.files[selected + ".liquid"]) throw new ProjectError("P\xE1gina ausente no projeto.");
22641
+ const context = buildDemoContext(template, demo.demo, "/", selected);
22642
+ const html = [];
22643
+ for (const file of ["components/header", selected, "components/footer"]) {
22644
+ if (!template.files[file + ".liquid"]) continue;
22645
+ const result2 = await renderTemplatePage(template, file, context);
22646
+ if (!result2.ok) throw new ProjectError("Falha no Liquid da p\xE1gina.");
22647
+ html.push(result2.data.html);
22648
+ }
22649
+ const nonce = (0, import_node_crypto2.randomBytes)(24).toString("base64");
22650
+ const wrapperStyle = Object.entries(templateCssVars(template)).map(([key, value]) => `${key}:${value}`).join(";");
22651
+ const source = buildSandboxDocument({
22652
+ template,
22653
+ html: html.join(""),
22654
+ wrapperStyle,
22655
+ nonce,
22656
+ page: selected
22657
+ });
22658
+ const policy = `default-src 'none'; script-src 'nonce-${nonce}'; style-src 'unsafe-inline'; img-src https: data:; frame-src 'self'; connect-src 'self'; base-uri 'none'; form-action 'none'`;
22659
+ return `<!doctype html><html lang="pt-BR"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta http-equiv="Content-Security-Policy" content="${escape3(policy)}"><title>CeuPay \u2014 pr\xE9via local</title></head><body>
22660
+ <nav aria-label="P\xE1ginas da pr\xE9via"><a href="/?page=index">In\xEDcio</a> \xB7 <a href="/?page=catalog">Cat\xE1logo</a> \xB7 <a href="/?page=product">Produto</a> \xB7 <a href="/?page=post">Post</a></nav>
22661
+ <p id="notice" role="status">Cat\xE1logo fict\xEDcio. Compras e navega\xE7\xE3o do template s\xE3o simuladas. Arquivos locais recarregam automaticamente.</p>
22662
+ <iframe title="Template da loja" sandbox="allow-scripts" referrerpolicy="no-referrer" style="width:100%;min-height:80vh;border:0" srcdoc="${escape3(source)}"></iframe>
22663
+ <script nonce="${nonce}">const frame=document.querySelector('iframe');window.addEventListener('message',e=>{if(e.source!==frame.contentWindow||e.origin!=='null'||!e.data||e.data.source!=='ceupay-template')return;if(e.data.type==='resize'&&Number.isFinite(e.data.height)){frame.style.height=Math.max(300,Math.min(30000,e.data.height))+'px';}else if(e.data.type==='navigate'||e.data.type==='addToCart'){document.getElementById('notice').textContent='Simula\xE7\xE3o: '+e.data.type;}});let version=${JSON.stringify(hash(template))};setInterval(async()=>{try{const next=await(await fetch('/version')).text();if(version!==undefined&&version!==next)location.reload();version=next;}catch{}},1000);</script></body></html>`;
22664
+ }
22665
+ async function serveProject(directory) {
22666
+ await privateRoot(directory);
22667
+ const server = import_node_http.default.createServer((request, response) => {
22668
+ void (async () => {
22669
+ const expected = `127.0.0.1:${server.address().port}`;
22670
+ if (request.method !== "GET" || request.headers.host !== expected || request.headers.origin && request.headers.origin !== `http://${expected}` || request.headers["sec-fetch-site"] && !["same-origin", "none"].includes(String(request.headers["sec-fetch-site"]))) {
22671
+ response.writeHead(403);
22672
+ response.end();
22673
+ return;
22674
+ }
22675
+ const url2 = new URL(request.url ?? "/", `http://${expected}`);
22676
+ response.setHeader("Cache-Control", "no-store");
22677
+ response.setHeader("X-Content-Type-Options", "nosniff");
22678
+ response.setHeader("Referrer-Policy", "no-referrer");
22679
+ response.setHeader("Content-Security-Policy", "frame-ancestors 'none'");
22680
+ if (url2.pathname === "/version") {
22681
+ const project = await loadProject(directory);
22682
+ response.setHeader("Content-Type", "text/plain");
22683
+ response.end(hash(project));
22684
+ } else if (url2.pathname === "/") {
22685
+ const document = await previewDocument(directory, url2.searchParams.get("page") ?? "index");
22686
+ response.setHeader("Content-Type", "text/html; charset=utf-8");
22687
+ response.end(document);
22688
+ } else {
22689
+ response.writeHead(404);
22690
+ response.end();
22691
+ }
22692
+ })().catch(() => {
22693
+ if (!response.headersSent)
22694
+ response.writeHead(422, {
22695
+ "Content-Type": "text/plain; charset=utf-8"
22696
+ });
22697
+ response.end("Template inv\xE1lido. Corrija os arquivos e recarregue.");
22698
+ });
22699
+ });
22700
+ await new Promise((resolve, reject2) => {
22701
+ server.once("error", reject2);
22702
+ server.listen(0, "127.0.0.1", resolve);
22703
+ });
22704
+ const url = `http://127.0.0.1:${server.address().port}`;
22705
+ return {
22706
+ url,
22707
+ close: () => new Promise((resolve, reject2) => server.close((error) => error ? reject2(error) : resolve()))
22708
+ };
22709
+ }
22710
+ async function watchProject(directory, signal) {
22711
+ const root = await privateRoot(directory), operations = import_node_path3.default.join(root, ".ceupay-operations");
22712
+ await import_promises3.default.mkdir(operations, { mode: 448 }).catch((error) => {
22713
+ if (error.code !== "EEXIST") throw error;
22714
+ });
22715
+ await privateRoot(operations);
22716
+ for (const file of await import_promises3.default.readdir(operations)) {
22717
+ if (file.endsWith(".json")) {
22718
+ try {
22719
+ await import_promises3.default.lstat(import_node_path3.default.join(operations, file + ".confirmed"));
22720
+ } catch {
22721
+ throw new ProjectError(
22722
+ "H\xE1 opera\xE7\xE3o pendente. Preserve os arquivos, recupere com replay e baixe uma pasta nova antes de sincronizar."
22723
+ );
22724
+ }
22725
+ }
22726
+ }
22727
+ let observed = hash(await loadProject(root));
22728
+ let published = await remoteMatchesProject(root) ? observed : "";
22729
+ while (!signal.aborted) {
22730
+ await (0, import_promises4.setTimeout)(750, void 0, { signal }).catch(() => void 0);
22731
+ if (signal.aborted) break;
22732
+ let next;
22733
+ try {
22734
+ next = hash(await loadProject(root));
22735
+ } catch {
22736
+ continue;
22737
+ }
22738
+ if (next !== observed) {
22739
+ observed = next;
22740
+ continue;
22741
+ }
22742
+ if (next === published) continue;
22743
+ const file = import_node_path3.default.join(operations, (0, import_node_crypto2.randomUUID)() + ".json");
22744
+ await runRemote("prepare-publish", [root, file]);
22745
+ const updated = await runRemote("publish", [root, file, "--yes"]);
22746
+ if (updated !== true) throw new ProjectError("V\xEDnculo local n\xE3o avan\xE7ou. Preserve a opera\xE7\xE3o e baixe uma pasta nova.");
22747
+ await writeNew(file + ".confirmed", "confirmed\n");
22748
+ published = next;
22749
+ }
22750
+ }
22751
+ async function runLocal(command, args) {
22752
+ const [directory, ...flags] = args;
22753
+ if (!directory || flags.some((flag) => !["--watch", "--yes"].includes(flag)) || new Set(flags).size !== flags.length || flags.includes("--watch") !== flags.includes("--yes") || command === "serve" && flags.length)
22754
+ throw new ProjectError("Use start <pasta> [--watch --yes] ou serve <pasta>.");
22755
+ if (command === "start") {
22756
+ process.env.CEUPAY_EDITOR_TOKEN = await sessionSecret();
22757
+ try {
22758
+ await import_promises3.default.lstat(directory);
22759
+ } catch (error) {
22760
+ if (error.code !== "ENOENT") throw error;
22761
+ try {
22762
+ await runRemote("pull", [directory]);
22763
+ } catch (error2) {
22764
+ if (!(error2 instanceof ProjectError) || !error2.message.includes("n\xE3o tem template customizado")) throw error2;
22765
+ await runRemote("pull", [directory, "--base", "horizonte"]);
22766
+ }
22767
+ }
22768
+ }
22769
+ await loadProject(directory);
22770
+ const preview2 = await serveProject(directory);
22771
+ console.log(`Pr\xE9via interativa: ${preview2.url}
22772
+ Abra a pasta no Claude/Codex. Ctrl+C encerra.`);
22773
+ const controller = new AbortController();
22774
+ const stop = () => controller.abort();
22775
+ process.once("SIGINT", stop);
22776
+ process.once("SIGTERM", stop);
22777
+ try {
22778
+ if (flags.includes("--watch")) await watchProject(directory, controller.signal);
22779
+ else
22780
+ await new Promise(
22781
+ (resolve) => controller.signal.addEventListener("abort", () => resolve(), {
22782
+ once: true
22783
+ })
22784
+ );
22785
+ } finally {
22786
+ process.off("SIGINT", stop);
22787
+ process.off("SIGTERM", stop);
22788
+ await preview2.close();
22789
+ delete process.env.CEUPAY_EDITOR_TOKEN;
22790
+ }
22791
+ }
22251
22792
 
22252
22793
  // src/main.ts
22253
22794
  var HELP = `CeuPay templates \u2014 desenvolvimento local e piloto remoto
22254
22795
  bases
22796
+ start <pasta> [--watch --yes]
22797
+ serve <pasta>
22255
22798
  init <modelo> <nova-pasta>
22256
22799
  import <exportacao.json> <nova-pasta>
22257
22800
  validate <pasta>
@@ -22273,6 +22816,10 @@ async function main(args) {
22273
22816
  const [major, minor] = process.versions.node.split(".").map(Number);
22274
22817
  if (major < 22 || major === 22 && minor < 12) throw new ProjectError("Use Node 22.12 ou superior.");
22275
22818
  const [command, first, second] = args;
22819
+ if (command === "start" || command === "serve") {
22820
+ await runLocal(command, args.slice(1));
22821
+ return;
22822
+ }
22276
22823
  if (!args.length || args.length === 1 && ["help", "--help"].includes(command)) {
22277
22824
  console.log(HELP);
22278
22825
  return;
@@ -22291,7 +22838,8 @@ async function main(args) {
22291
22838
  return;
22292
22839
  }
22293
22840
  if (command === "preview") {
22294
- if (args.length !== 3 && !(args.length === 5 && args[3] === "--page")) throw new ProjectError("Argumentos inv\xE1lidos. Consulte help.");
22841
+ if (args.length !== 3 && !(args.length === 5 && args[3] === "--page"))
22842
+ throw new ProjectError("Argumentos inv\xE1lidos. Consulte help.");
22295
22843
  await preview(first, second, args.length === 5 ? `pages/${args[4]}` : "pages/index");
22296
22844
  console.log("Pr\xE9via local criada.");
22297
22845
  return;