elit 3.6.4 → 3.6.6

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 (52) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/README.md +14 -1
  4. package/dist/build.d.ts +4 -1
  5. package/dist/cli.cjs +746 -166
  6. package/dist/cli.mjs +746 -166
  7. package/dist/config.d.ts +8 -1
  8. package/dist/coverage.d.ts +4 -1
  9. package/dist/desktop-auto-render.cjs +5 -4
  10. package/dist/desktop-auto-render.d.ts +4 -1
  11. package/dist/desktop-auto-render.js +5 -4
  12. package/dist/desktop-auto-render.mjs +5 -4
  13. package/dist/dom.cjs +5 -4
  14. package/dist/dom.d.ts +2 -0
  15. package/dist/dom.js +5 -4
  16. package/dist/dom.mjs +5 -4
  17. package/dist/el.d.ts +2 -0
  18. package/dist/index.cjs +5 -4
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.js +5 -4
  21. package/dist/index.mjs +5 -4
  22. package/dist/native.cjs +5 -4
  23. package/dist/native.d.ts +2 -0
  24. package/dist/native.js +5 -4
  25. package/dist/native.mjs +5 -4
  26. package/dist/render-context.d.ts +4 -1
  27. package/dist/router.cjs +5 -4
  28. package/dist/router.d.ts +2 -0
  29. package/dist/router.js +5 -4
  30. package/dist/router.mjs +5 -4
  31. package/dist/{server-CcBFc2F5.d.ts → server-uMQvZAll.d.ts} +9 -0
  32. package/dist/server.cjs +146 -4
  33. package/dist/server.d.ts +4 -1
  34. package/dist/server.js +4494 -285
  35. package/dist/server.mjs +146 -4
  36. package/dist/smtp-server.cjs +115 -0
  37. package/dist/smtp-server.d.ts +41 -0
  38. package/dist/smtp-server.js +4186 -0
  39. package/dist/smtp-server.mjs +87 -0
  40. package/dist/state.cjs +5 -4
  41. package/dist/state.d.ts +2 -0
  42. package/dist/state.js +5 -4
  43. package/dist/state.mjs +5 -4
  44. package/dist/test-runtime.cjs +184 -141
  45. package/dist/test-runtime.js +193 -150
  46. package/dist/test-runtime.mjs +184 -141
  47. package/dist/test.cjs +143 -134
  48. package/dist/test.js +152 -143
  49. package/dist/test.mjs +143 -134
  50. package/dist/types.d.ts +34 -2
  51. package/dist/universal.d.ts +2 -0
  52. package/package.json +9 -1
package/dist/cli.mjs CHANGED
@@ -1997,8 +1997,8 @@ var require_chance = __commonJS({
1997
1997
  return i;
1998
1998
  });
1999
1999
  }
2000
- function testRange(test2, errorMessage) {
2001
- if (test2) {
2000
+ function testRange(test, errorMessage) {
2001
+ if (test) {
2002
2002
  throw new RangeError(errorMessage);
2003
2003
  }
2004
2004
  }
@@ -54905,12 +54905,126 @@ ${k.ValidationErrorsFormatter.format(i2)}`);
54905
54905
  // src/test-runtime.ts
54906
54906
  import { transformSync } from "esbuild";
54907
54907
  import { SourceMapConsumer } from "source-map";
54908
- function escapeRegex(str) {
54908
+ function escapeRegex2(str) {
54909
54909
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
54910
54910
  }
54911
+ function resolveTestLoader(filePath) {
54912
+ return /\.(?:ts|tsx|mts|cts)$/i.test(filePath) ? "ts" : "js";
54913
+ }
54914
+ function createTestTransformOptions(filePath, format2, sourcemap) {
54915
+ return {
54916
+ loader: resolveTestLoader(filePath),
54917
+ format: format2,
54918
+ sourcemap,
54919
+ sourcefile: filePath,
54920
+ target: "es2020",
54921
+ tsconfigRaw: {
54922
+ compilerOptions: {
54923
+ jsx: "react",
54924
+ jsxFactory: "h",
54925
+ jsxFragmentFactory: "Fragment"
54926
+ }
54927
+ }
54928
+ };
54929
+ }
54930
+ function resolveExistingTestModulePath(basePath) {
54931
+ const nodePath = __require("path");
54932
+ if (existsSync(basePath) && statSync(basePath).isFile()) {
54933
+ return basePath;
54934
+ }
54935
+ for (const extension of TEST_MODULE_EXTENSIONS) {
54936
+ const candidatePath = `${basePath}${extension}`;
54937
+ if (existsSync(candidatePath) && statSync(candidatePath).isFile()) {
54938
+ return candidatePath;
54939
+ }
54940
+ }
54941
+ if (existsSync(basePath) && statSync(basePath).isDirectory()) {
54942
+ const packageJsonPath = nodePath.join(basePath, "package.json");
54943
+ if (existsSync(packageJsonPath) && statSync(packageJsonPath).isFile()) {
54944
+ try {
54945
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
54946
+ for (const candidateEntry of [packageJson.main, packageJson.module]) {
54947
+ if (typeof candidateEntry !== "string" || candidateEntry.trim().length === 0) {
54948
+ continue;
54949
+ }
54950
+ try {
54951
+ return resolveExistingTestModulePath(nodePath.resolve(basePath, candidateEntry));
54952
+ } catch {
54953
+ continue;
54954
+ }
54955
+ }
54956
+ } catch {
54957
+ }
54958
+ }
54959
+ for (const extension of TEST_MODULE_EXTENSIONS) {
54960
+ const candidatePath = nodePath.join(basePath, `index${extension}`);
54961
+ if (existsSync(candidatePath) && statSync(candidatePath).isFile()) {
54962
+ return candidatePath;
54963
+ }
54964
+ }
54965
+ }
54966
+ return basePath;
54967
+ }
54968
+ function resolveTestModulePath(fromFilePath, specifier) {
54969
+ if (!specifier.startsWith(".") && !specifier.startsWith("/")) {
54970
+ return specifier;
54971
+ }
54972
+ const nodePath = __require("path");
54973
+ const basePath = specifier.startsWith(".") ? nodePath.resolve(dirname(fromFilePath), specifier) : specifier;
54974
+ return resolveExistingTestModulePath(basePath);
54975
+ }
54976
+ function shouldTranspileTestModule(filePath) {
54977
+ return /\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs)$/i.test(filePath);
54978
+ }
54979
+ function createTestModuleRequire(fromFilePath, moduleCache) {
54980
+ return (specifier) => {
54981
+ if (specifier.startsWith("elit/") || specifier === "elit") {
54982
+ return __require(specifier);
54983
+ }
54984
+ const resolvedPath = resolveTestModulePath(fromFilePath, specifier);
54985
+ if (resolvedPath === specifier) {
54986
+ return __require(specifier);
54987
+ }
54988
+ if (!existsSync(resolvedPath) || !statSync(resolvedPath).isFile()) {
54989
+ return __require(resolvedPath);
54990
+ }
54991
+ if (!shouldTranspileTestModule(resolvedPath)) {
54992
+ return __require(resolvedPath);
54993
+ }
54994
+ return loadTranspiledTestModule(resolvedPath, moduleCache);
54995
+ };
54996
+ }
54997
+ function loadTranspiledTestModule(modulePath, moduleCache) {
54998
+ const cached = moduleCache.get(modulePath);
54999
+ if (cached) {
55000
+ return cached.exports;
55001
+ }
55002
+ const source = readFileSync(modulePath, "utf-8");
55003
+ let transpiled;
55004
+ try {
55005
+ transpiled = transformSync(source, createTestTransformOptions(modulePath, "cjs", false));
55006
+ } catch (error) {
55007
+ throw new Error(`Failed to transpile test dependency ${modulePath}: ${error instanceof Error ? error.message : String(error)}`);
55008
+ }
55009
+ const moduleRecord = { exports: {} };
55010
+ const moduleObj = { exports: moduleRecord.exports };
55011
+ moduleCache.set(modulePath, moduleRecord);
55012
+ try {
55013
+ const fn = new Function("module", "exports", "require", "__filename", "__dirname", transpiled.code);
55014
+ const requireFn = createTestModuleRequire(modulePath, moduleCache);
55015
+ fn(moduleObj, moduleObj.exports, requireFn, modulePath, dirname(modulePath));
55016
+ } catch (error) {
55017
+ throw new Error(`Failed to execute test dependency ${modulePath}: ${error instanceof Error ? error.message : String(error)}`);
55018
+ }
55019
+ moduleRecord.exports = moduleObj.exports;
55020
+ if (!modulePath.includes(".test.") && !modulePath.includes(".spec.")) {
55021
+ coveredFiles.add(modulePath);
55022
+ }
55023
+ return moduleRecord.exports;
55024
+ }
54911
55025
  function createTestFunction(defaultTimeout = 5e3) {
54912
55026
  const testFn = function(name, fn, timeout) {
54913
- const test2 = {
55027
+ const test = {
54914
55028
  name,
54915
55029
  fn,
54916
55030
  skip: currentSuite.skip,
@@ -54919,10 +55033,10 @@ function createTestFunction(defaultTimeout = 5e3) {
54919
55033
  timeout: timeout ?? defaultTimeout,
54920
55034
  suite: currentSuite
54921
55035
  };
54922
- currentSuite.tests.push(test2);
55036
+ currentSuite.tests.push(test);
54923
55037
  };
54924
55038
  testFn.skip = (name, fn, timeout) => {
54925
- const test2 = {
55039
+ const test = {
54926
55040
  name,
54927
55041
  fn,
54928
55042
  skip: true,
@@ -54931,11 +55045,11 @@ function createTestFunction(defaultTimeout = 5e3) {
54931
55045
  timeout: timeout ?? defaultTimeout,
54932
55046
  suite: currentSuite
54933
55047
  };
54934
- currentSuite.tests.push(test2);
55048
+ currentSuite.tests.push(test);
54935
55049
  };
54936
55050
  testFn.only = (name, fn, timeout) => {
54937
55051
  hasOnly = true;
54938
- const test2 = {
55052
+ const test = {
54939
55053
  name,
54940
55054
  fn,
54941
55055
  skip: false,
@@ -54944,10 +55058,10 @@ function createTestFunction(defaultTimeout = 5e3) {
54944
55058
  timeout: timeout ?? defaultTimeout,
54945
55059
  suite: currentSuite
54946
55060
  };
54947
- currentSuite.tests.push(test2);
55061
+ currentSuite.tests.push(test);
54948
55062
  };
54949
55063
  testFn.todo = (name, fn, timeout) => {
54950
- const test2 = {
55064
+ const test = {
54951
55065
  name,
54952
55066
  fn,
54953
55067
  skip: false,
@@ -54956,7 +55070,7 @@ function createTestFunction(defaultTimeout = 5e3) {
54956
55070
  timeout: timeout ?? defaultTimeout,
54957
55071
  suite: currentSuite
54958
55072
  };
54959
- currentSuite.tests.push(test2);
55073
+ currentSuite.tests.push(test);
54960
55074
  };
54961
55075
  return testFn;
54962
55076
  }
@@ -55066,29 +55180,7 @@ async function runTests(options) {
55066
55180
  try {
55067
55181
  const source = await readFile(file, "utf-8");
55068
55182
  const testFileDir = dirname(file);
55069
- const importRegex = /import\s+{\s*([^}]+)\s*}\s+from\s+['"]([^'"]+)['"]/g;
55070
- const imports = {};
55071
- let importIndex = 0;
55072
- let codeWithoutImports = source.replace(importRegex, (_, named, path) => {
55073
- const varName = `__import_${importIndex++}`;
55074
- const trimmedNamed = named.trim();
55075
- imports[varName] = { path, named: trimmedNamed };
55076
- return `// ${trimmedNamed} import injected later
55077
- `;
55078
- });
55079
- const result2 = transformSync(codeWithoutImports, {
55080
- loader: file.endsWith(".ts") || file.endsWith(".tsx") ? "ts" : "js",
55081
- format: "iife",
55082
- sourcemap: "inline",
55083
- target: "es2020",
55084
- tsconfigRaw: {
55085
- compilerOptions: {
55086
- jsx: "react",
55087
- jsxFactory: "h",
55088
- jsxFragmentFactory: "Fragment"
55089
- }
55090
- }
55091
- });
55183
+ const result2 = transformSync(source, createTestTransformOptions(file, "cjs", "inline"));
55092
55184
  let code = result2.code;
55093
55185
  const sourceMapMatch = code.match(/\/\/# sourceMappingURL=data:application\/json;base64,(.+)/);
55094
55186
  if (sourceMapMatch) {
@@ -55099,99 +55191,15 @@ async function runTests(options) {
55099
55191
  } else {
55100
55192
  currentSourceMapConsumer = void 0;
55101
55193
  }
55102
- const importedValues = {};
55103
- const importParamNames = [];
55104
- const importAssignments = [];
55105
- if (Object.keys(imports).length > 0) {
55106
- for (const [, { path, named }] of Object.entries(imports)) {
55107
- let resolvedPath = path;
55108
- if (path.startsWith(".")) {
55109
- const nodePath = __require("path");
55110
- resolvedPath = nodePath.resolve(testFileDir, path);
55111
- }
55112
- if (!resolvedPath.endsWith(".ts") && !resolvedPath.endsWith(".js") && !resolvedPath.endsWith(".mjs") && !resolvedPath.endsWith(".cjs")) {
55113
- resolvedPath += ".ts";
55114
- }
55115
- if (resolvedPath.endsWith(".ts")) {
55116
- try {
55117
- const importSource = await readFile(resolvedPath, "utf-8");
55118
- const transpiled = transformSync(importSource, {
55119
- loader: "ts",
55120
- format: "cjs",
55121
- target: "es2020",
55122
- tsconfigRaw: {
55123
- compilerOptions: {
55124
- jsx: "react",
55125
- jsxFactory: "h",
55126
- jsxFragmentFactory: "Fragment"
55127
- }
55128
- }
55129
- });
55130
- const moduleExports = {};
55131
- const moduleObj = { exports: moduleExports };
55132
- const fn2 = new Function("module", "exports", "require", "__filename", "__dirname", transpiled.code);
55133
- const requireFn = (id) => {
55134
- if (id.startsWith("elit/") || id === "elit") {
55135
- return __require(id);
55136
- }
55137
- if (id.startsWith(".")) {
55138
- const nodePath = __require("path");
55139
- const absPath = nodePath.resolve(dirname(resolvedPath), id);
55140
- return __require(absPath);
55141
- }
55142
- return __require(id);
55143
- };
55144
- fn2(moduleObj, moduleExports, requireFn, resolvedPath, dirname(resolvedPath));
55145
- if (!resolvedPath.includes(".test.") && !resolvedPath.includes(".spec.")) {
55146
- coveredFiles.add(resolvedPath);
55147
- }
55148
- let exportedValue = moduleObj.exports[named];
55149
- if (exportedValue === void 0 && moduleObj.exports.default) {
55150
- exportedValue = moduleObj.exports.default[named];
55151
- }
55152
- if (exportedValue === void 0 && typeof moduleObj.exports === "object") {
55153
- exportedValue = moduleObj.exports[named];
55154
- }
55155
- const paramKey = `__import_${Math.random().toString(36).substring(2, 11)}`;
55156
- importedValues[paramKey] = exportedValue;
55157
- importParamNames.push(paramKey);
55158
- importAssignments.push(`const ${named} = ${paramKey};`);
55159
- } catch (err) {
55160
- const paramKey = `__import_${Math.random().toString(36).substring(2, 11)}`;
55161
- importedValues[paramKey] = null;
55162
- importParamNames.push(paramKey);
55163
- importAssignments.push(`const ${named} = ${paramKey}; /* Error importing ${resolvedPath}: ${err} */`);
55164
- }
55165
- } else {
55166
- const requiredModule = __require(resolvedPath);
55167
- const exportedValue = requiredModule[named];
55168
- const paramKey = `__import_${Math.random().toString(36).substring(2, 11)}`;
55169
- importedValues[paramKey] = exportedValue;
55170
- importParamNames.push(paramKey);
55171
- importAssignments.push(`const ${named} = ${paramKey};`);
55172
- }
55173
- }
55174
- }
55175
- let preamble = "";
55176
- if (Object.keys(imports).length > 0) {
55177
- const iifeStartMatch = code.match(/^(\s*(?:var\s+\w+\s*=\s*)?\(\(\)\s*=>\s*\{\n)/);
55178
- if (iifeStartMatch) {
55179
- const iifePrefix = iifeStartMatch[1];
55180
- const assignments = `${importAssignments.join("\n")}
55181
- `;
55182
- preamble = iifePrefix;
55183
- code = iifePrefix + assignments + code.slice(iifeStartMatch[1].length);
55184
- } else {
55185
- preamble = importAssignments.join("\n") + "\n";
55186
- code = preamble + code;
55187
- }
55188
- }
55189
- wrapperLineOffset = preamble.split("\n").length;
55194
+ wrapperLineOffset = 0;
55190
55195
  setupGlobals();
55191
- const allParams = ["describe", "it", "test", "expect", "beforeAll", "afterAll", "beforeEach", "afterEach", "vi", "require", "module", "__filename", "__dirname", ...importParamNames];
55192
- const allArgs = [describe, it, test, expect, beforeAll, afterAll, beforeEach, afterEach, vi, __require, module, file, testFileDir, ...importParamNames.map((p) => importedValues[p])];
55193
- const fn = new Function(...allParams, code);
55194
- await fn(...allArgs);
55196
+ const moduleCache = /* @__PURE__ */ new Map();
55197
+ const moduleRecord = { exports: {} };
55198
+ const moduleObj = { exports: moduleRecord.exports };
55199
+ moduleCache.set(file, moduleRecord);
55200
+ const fn = new Function("module", "exports", "require", "__filename", "__dirname", code);
55201
+ const requireFn = createTestModuleRequire(file, moduleCache);
55202
+ await fn(moduleObj, moduleObj.exports, requireFn, file, testFileDir);
55195
55203
  await executeSuite(currentSuite, timeout, bail);
55196
55204
  if (currentSourceMapConsumer) {
55197
55205
  currentSourceMapConsumer.destroy();
@@ -55226,13 +55234,13 @@ async function runTests(options) {
55226
55234
  async function executeSuite(suite, timeout, bail, parentMatched = false) {
55227
55235
  let directMatch = false;
55228
55236
  if (describePattern) {
55229
- const escapedPattern = escapeRegex(describePattern);
55237
+ const escapedPattern = escapeRegex2(describePattern);
55230
55238
  const regex = new RegExp(escapedPattern, "i");
55231
55239
  directMatch = regex.test(suite.name);
55232
55240
  }
55233
55241
  function suiteOrDescendantMatches(s) {
55234
55242
  if (!describePattern) return true;
55235
- const escapedPattern = escapeRegex(describePattern);
55243
+ const escapedPattern = escapeRegex2(describePattern);
55236
55244
  const regex = new RegExp(escapedPattern, "i");
55237
55245
  if (regex.test(s.name)) return true;
55238
55246
  for (const child of s.suites) {
@@ -55256,22 +55264,22 @@ async function executeSuite(suite, timeout, bail, parentMatched = false) {
55256
55264
  for (const hook of beforeAllHooks) {
55257
55265
  await hook();
55258
55266
  }
55259
- for (const test2 of suite.tests) {
55260
- if (hasOnly && !test2.only && !suite.only) {
55267
+ for (const test of suite.tests) {
55268
+ if (hasOnly && !test.only && !suite.only) {
55261
55269
  continue;
55262
55270
  }
55263
55271
  let testMatches = true;
55264
55272
  if (testPattern) {
55265
- const escapedPattern = escapeRegex(testPattern);
55273
+ const escapedPattern = escapeRegex2(testPattern);
55266
55274
  const regex = new RegExp(escapedPattern, "i");
55267
- testMatches = regex.test(test2.name);
55275
+ testMatches = regex.test(test.name);
55268
55276
  }
55269
55277
  if (!testMatches) {
55270
55278
  continue;
55271
55279
  }
55272
- if (test2.skip || suite.skip) {
55280
+ if (test.skip || suite.skip) {
55273
55281
  testResults.push({
55274
- name: test2.name,
55282
+ name: test.name,
55275
55283
  status: "skip",
55276
55284
  duration: 0,
55277
55285
  suite: suite.name,
@@ -55279,9 +55287,9 @@ async function executeSuite(suite, timeout, bail, parentMatched = false) {
55279
55287
  });
55280
55288
  continue;
55281
55289
  }
55282
- if (test2.todo) {
55290
+ if (test.todo) {
55283
55291
  testResults.push({
55284
- name: test2.name,
55292
+ name: test.name,
55285
55293
  status: "todo",
55286
55294
  duration: 0,
55287
55295
  suite: suite.name,
@@ -55295,13 +55303,13 @@ async function executeSuite(suite, timeout, bail, parentMatched = false) {
55295
55303
  const startTime = Date.now();
55296
55304
  try {
55297
55305
  await Promise.race([
55298
- test2.fn(),
55306
+ test.fn(),
55299
55307
  new Promise(
55300
- (_, reject) => setTimeout(() => reject(new Error(`Test timed out after ${test2.timeout}ms`)), test2.timeout)
55308
+ (_, reject) => setTimeout(() => reject(new Error(`Test timed out after ${test.timeout}ms`)), test.timeout)
55301
55309
  )
55302
55310
  ]);
55303
55311
  testResults.push({
55304
- name: test2.name,
55312
+ name: test.name,
55305
55313
  status: "pass",
55306
55314
  duration: Date.now() - startTime,
55307
55315
  suite: suite.name,
@@ -55315,7 +55323,7 @@ async function executeSuite(suite, timeout, bail, parentMatched = false) {
55315
55323
  codeSnippet = error.codeSnippet;
55316
55324
  }
55317
55325
  testResults.push({
55318
- name: test2.name,
55326
+ name: test.name,
55319
55327
  status: "fail",
55320
55328
  duration: Date.now() - startTime,
55321
55329
  error,
@@ -55364,7 +55372,7 @@ function getCoveredFiles() {
55364
55372
  function resetCoveredFiles() {
55365
55373
  coveredFiles.clear();
55366
55374
  }
55367
- var AssertionError, currentSuite, testResults, hasOnly, coveredFiles, describePattern, testPattern, currentTestFile, currentSourceMapConsumer, wrapperLineOffset, Expect, vi, beforeAllHooks, afterAllHooks, beforeEachHooks, afterEachHooks, beforeAll, afterAll, beforeEach, afterEach, globals;
55375
+ var AssertionError, currentSuite, testResults, hasOnly, coveredFiles, describePattern, testPattern, currentTestFile, currentSourceMapConsumer, wrapperLineOffset, TEST_MODULE_EXTENSIONS, Expect, vi, beforeAllHooks, afterAllHooks, beforeEachHooks, afterEachHooks, beforeAll, afterAll, beforeEach, afterEach, globals;
55368
55376
  var init_test_runtime = __esm({
55369
55377
  "src/test-runtime.ts"() {
55370
55378
  "use strict";
@@ -55395,6 +55403,7 @@ var init_test_runtime = __esm({
55395
55403
  currentTestFile = void 0;
55396
55404
  currentSourceMapConsumer = void 0;
55397
55405
  wrapperLineOffset = 0;
55406
+ TEST_MODULE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".json"];
55398
55407
  Expect = class _Expect {
55399
55408
  constructor(actual, isNot = false, isAsync = false) {
55400
55409
  this.actual = actual;
@@ -60460,6 +60469,71 @@ function lookup(path) {
60460
60469
  // src/server.ts
60461
60470
  init_runtime();
60462
60471
 
60472
+ // src/smtp-server.ts
60473
+ import { SMTPServer } from "smtp-server";
60474
+ var DEFAULT_SMTP_PORT = 2525;
60475
+ var DEFAULT_SMTP_HOST = "127.0.0.1";
60476
+ function resolveSmtpServerConfig(config = {}) {
60477
+ const { port = DEFAULT_SMTP_PORT, host = DEFAULT_SMTP_HOST, label, ...serverOptions } = config;
60478
+ return {
60479
+ ...serverOptions,
60480
+ port,
60481
+ host,
60482
+ label
60483
+ };
60484
+ }
60485
+ function normalizeSmtpServerConfigs(input) {
60486
+ const configs = Array.isArray(input) ? input : input ? [input] : [];
60487
+ return configs.map((config) => resolveSmtpServerConfig(config));
60488
+ }
60489
+ function closeSmtpServer(server) {
60490
+ return new Promise((resolve9, reject) => {
60491
+ let settled = false;
60492
+ const finish = (error) => {
60493
+ if (settled) {
60494
+ return;
60495
+ }
60496
+ settled = true;
60497
+ if (error) {
60498
+ reject(error);
60499
+ return;
60500
+ }
60501
+ resolve9();
60502
+ };
60503
+ const handleCloseError = (error) => {
60504
+ const errorCode = error.code;
60505
+ if (errorCode === "ERR_SERVER_NOT_RUNNING") {
60506
+ finish();
60507
+ return;
60508
+ }
60509
+ finish(error);
60510
+ };
60511
+ try {
60512
+ server.close(() => finish());
60513
+ } catch (error) {
60514
+ handleCloseError(error);
60515
+ }
60516
+ });
60517
+ }
60518
+ function createSmtpServer(config = {}) {
60519
+ const resolvedConfig = resolveSmtpServerConfig(config);
60520
+ const { port, host, label: _label, ...serverOptions } = resolvedConfig;
60521
+ const server = new SMTPServer(serverOptions);
60522
+ return {
60523
+ server,
60524
+ config: resolvedConfig,
60525
+ listen(callback) {
60526
+ return callback ? server.listen(port, host, callback) : server.listen(port, host);
60527
+ },
60528
+ address() {
60529
+ return server.server.address();
60530
+ },
60531
+ close() {
60532
+ return closeSmtpServer(server);
60533
+ }
60534
+ };
60535
+ }
60536
+
60463
60537
  // src/render-context.ts
60464
60538
  var RUNTIME_TARGET_KEY = "__ELIT_RUNTIME_TARGET__";
60465
60539
  var CAPTURED_RENDER_KEY = "__ELIT_CAPTURED_RENDER__";
@@ -60913,6 +60987,7 @@ var DomNode = class {
60913
60987
  html += `</${tagName}>${newLine}`;
60914
60988
  return html;
60915
60989
  }
60990
+ const isRawText = tagName === "script" || tagName === "style";
60916
60991
  if (children && children.length > 0) {
60917
60992
  const resolvedChildren = children.map((c) => {
60918
60993
  const resolved = this.resolveStateValue(c);
@@ -60928,11 +61003,11 @@ var DomNode = class {
60928
61003
  if (Array.isArray(child)) {
60929
61004
  for (const c of child) {
60930
61005
  if (!shouldSkipChild(c)) {
60931
- html += this.renderToString(c, { pretty, indent: indent5 + 1 });
61006
+ html += isRawText && typeof c === "string" ? c : this.renderToString(c, { pretty, indent: indent5 + 1 });
60932
61007
  }
60933
61008
  }
60934
61009
  } else {
60935
- html += this.renderToString(child, { pretty, indent: indent5 + 1 });
61010
+ html += isRawText && typeof child === "string" ? child : this.renderToString(child, { pretty, indent: indent5 + 1 });
60936
61011
  }
60937
61012
  }
60938
61013
  html += indentStr;
@@ -60942,11 +61017,11 @@ var DomNode = class {
60942
61017
  if (Array.isArray(child)) {
60943
61018
  for (const c of child) {
60944
61019
  if (!shouldSkipChild(c)) {
60945
- html += this.renderToString(c, { pretty: false, indent: 0 });
61020
+ html += isRawText && typeof c === "string" ? c : this.renderToString(c, { pretty: false, indent: 0 });
60946
61021
  }
60947
61022
  }
60948
61023
  } else {
60949
- html += this.renderToString(child, { pretty: false, indent: 0 });
61024
+ html += isRawText && typeof child === "string" ? child : this.renderToString(child, { pretty: false, indent: 0 });
60950
61025
  }
60951
61026
  }
60952
61027
  }
@@ -61874,6 +61949,56 @@ var defaultOptions = {
61874
61949
  worker: [],
61875
61950
  mode: "dev"
61876
61951
  };
61952
+ function createSmtpBindingKey(config) {
61953
+ return `${config.host}:${config.port}`;
61954
+ }
61955
+ function createSmtpServerLabel(config) {
61956
+ return config.label || createSmtpBindingKey(config);
61957
+ }
61958
+ function formatSmtpServerAddress(address, fallback) {
61959
+ if (typeof address === "string") {
61960
+ return address;
61961
+ }
61962
+ if (address) {
61963
+ return `${address.address}:${address.port}`;
61964
+ }
61965
+ return createSmtpBindingKey(fallback);
61966
+ }
61967
+ function collectSmtpServerConfigs(config, usesClientArray) {
61968
+ const modeLabel = config.mode || "dev";
61969
+ const smtpConfigs = normalizeSmtpServerConfigs(config.smtp).map((smtpConfig, index) => ({
61970
+ ...smtpConfig,
61971
+ label: smtpConfig.label || `${modeLabel}.smtp[${index}]`
61972
+ }));
61973
+ if (!usesClientArray || !config.clients) {
61974
+ return smtpConfigs;
61975
+ }
61976
+ for (let clientIndex = 0; clientIndex < config.clients.length; clientIndex += 1) {
61977
+ const client = config.clients[clientIndex];
61978
+ const clientDescriptor = client.basePath || client.root;
61979
+ const clientPrefix = clientDescriptor ? `${modeLabel}.clients[${clientIndex}] (${clientDescriptor})` : `${modeLabel}.clients[${clientIndex}]`;
61980
+ smtpConfigs.push(...normalizeSmtpServerConfigs(client.smtp).map((smtpConfig, smtpIndex) => ({
61981
+ ...smtpConfig,
61982
+ label: smtpConfig.label || `${clientPrefix}.smtp[${smtpIndex}]`
61983
+ })));
61984
+ }
61985
+ return smtpConfigs;
61986
+ }
61987
+ function assertUniqueSmtpServerBindings(configs) {
61988
+ const seenBindings = /* @__PURE__ */ new Map();
61989
+ for (const smtpConfig of configs) {
61990
+ if (smtpConfig.port === 0) {
61991
+ continue;
61992
+ }
61993
+ const bindingKey = createSmtpBindingKey(smtpConfig);
61994
+ const currentLabel = createSmtpServerLabel(smtpConfig);
61995
+ const previousLabel = seenBindings.get(bindingKey);
61996
+ if (previousLabel) {
61997
+ throw new Error(`Duplicate SMTP server binding "${bindingKey}" configured for ${previousLabel} and ${currentLabel}`);
61998
+ }
61999
+ seenBindings.set(bindingKey, currentLabel);
62000
+ }
62001
+ }
61877
62002
  function shouldUseClientFallbackRoot(primaryRoot, fallbackRoot, indexPath) {
61878
62003
  if (!fallbackRoot) {
61879
62004
  return false;
@@ -62008,6 +62133,7 @@ function createDevServer(options) {
62008
62133
  const globalWebSocketEndpoints = usesClientArray ? normalizeWebSocketEndpoints(config.ws) : [];
62009
62134
  const normalizedWebSocketEndpoints = [...normalizedClients.flatMap((client) => client.ws), ...globalWebSocketEndpoints];
62010
62135
  const seenWebSocketPaths = /* @__PURE__ */ new Set();
62136
+ const smtpServerConfigs = collectSmtpServerConfigs(config, usesClientArray);
62011
62137
  for (const endpoint of normalizedWebSocketEndpoints) {
62012
62138
  if (endpoint.path === ELIT_INTERNAL_WS_PATH) {
62013
62139
  throw new Error(`WebSocket path "${ELIT_INTERNAL_WS_PATH}" is reserved for Elit internals`);
@@ -62017,6 +62143,21 @@ function createDevServer(options) {
62017
62143
  }
62018
62144
  seenWebSocketPaths.add(endpoint.path);
62019
62145
  }
62146
+ assertUniqueSmtpServerBindings(smtpServerConfigs);
62147
+ const smtpServers = smtpServerConfigs.map((smtpConfig) => {
62148
+ const smtpServer = createSmtpServer(smtpConfig);
62149
+ const smtpLabel = createSmtpServerLabel(smtpServer.config);
62150
+ smtpServer.server.on("error", (error) => {
62151
+ console.error(`[SMTP] ${smtpLabel} error:`, error);
62152
+ });
62153
+ if (config.logging) {
62154
+ smtpServer.server.server.once("listening", () => {
62155
+ console.log(`[SMTP] ${smtpLabel} listening on ${formatSmtpServerAddress(smtpServer.address(), smtpServer.config)}`);
62156
+ });
62157
+ }
62158
+ smtpServer.listen();
62159
+ return smtpServer;
62160
+ });
62020
62161
  const globalProxyHandler = config.proxy ? createProxyHandler(config.proxy) : null;
62021
62162
  const server = createServer(async (req, res) => {
62022
62163
  const originalUrl = req.url || "/";
@@ -62557,6 +62698,15 @@ ${elitImportMap}`;
62557
62698
  if (config.logging) console.log("\n[Server] Shutting down...");
62558
62699
  transformCache.clear();
62559
62700
  if (watcher) await watcher.close();
62701
+ if (smtpServers.length > 0) {
62702
+ await Promise.all(smtpServers.map(async (smtpServer) => {
62703
+ try {
62704
+ await smtpServer.close();
62705
+ } catch (error) {
62706
+ console.error(`[SMTP] ${createSmtpServerLabel(smtpServer.config)} close error:`, error);
62707
+ }
62708
+ }));
62709
+ }
62560
62710
  if (webSocketServers.length > 0) {
62561
62711
  webSocketServers.forEach((wsServer) => wsServer.close());
62562
62712
  wsClients.clear();
@@ -62573,6 +62723,7 @@ ${elitImportMap}`;
62573
62723
  return {
62574
62724
  server,
62575
62725
  wss,
62726
+ smtpServers,
62576
62727
  url: primaryUrl,
62577
62728
  state: stateManager,
62578
62729
  close
@@ -74452,7 +74603,7 @@ var WAPK_AUTH_TAG_LENGTH = 16;
74452
74603
  var WAPK_SCRYPT_OPTIONS = { N: 16384, r: 8, p: 1 };
74453
74604
  var DEFAULT_GOOGLE_DRIVE_TOKEN_ENV = "GOOGLE_DRIVE_ACCESS_TOKEN";
74454
74605
  var DEFAULT_WAPK_ONLINE_URL_ENV = "ELIT_WAPK_ONLINE_URL";
74455
- var DEFAULT_WAPK_ONLINE_URLS = ["https://wapk.d-osc.com/"];
74606
+ var DEFAULT_WAPK_ONLINE_URLS = ["http://wapk.d-osc.com/"];
74456
74607
  var WAPK_ONLINE_CREATE_PATH = "/api/shared-session/create";
74457
74608
  var WAPK_ONLINE_READ_PATH = "/api/shared-session/read";
74458
74609
  var WAPK_ONLINE_CLOSE_PATH = "/api/shared-session/close";
@@ -74490,6 +74641,113 @@ function normalizeNonEmptyString(value) {
74490
74641
  const normalized = value.trim();
74491
74642
  return normalized.length > 0 ? normalized : void 0;
74492
74643
  }
74644
+ function normalizeGeneratedIdentifier(value) {
74645
+ const normalized = value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
74646
+ return normalized.length > 0 ? normalized : void 0;
74647
+ }
74648
+ function joinGeneratedIdentifier(...segments) {
74649
+ const normalizedSegments = segments.map((segment) => segment ? normalizeGeneratedIdentifier(segment) : void 0).filter((segment) => Boolean(segment));
74650
+ return normalizedSegments.length > 0 ? normalizedSegments.join(".") : void 0;
74651
+ }
74652
+ function parseScopedPackageName(value) {
74653
+ const normalizedValue = normalizeNonEmptyString(value);
74654
+ if (!normalizedValue) {
74655
+ return {};
74656
+ }
74657
+ if (!normalizedValue.startsWith("@")) {
74658
+ return {
74659
+ packageName: normalizedValue
74660
+ };
74661
+ }
74662
+ const scopeSeparatorIndex = normalizedValue.indexOf("/");
74663
+ if (scopeSeparatorIndex === -1) {
74664
+ return {
74665
+ packageName: normalizedValue
74666
+ };
74667
+ }
74668
+ return {
74669
+ scope: normalizedValue.slice(1, scopeSeparatorIndex),
74670
+ packageName: normalizedValue.slice(scopeSeparatorIndex + 1)
74671
+ };
74672
+ }
74673
+ function readPackageAuthorMetadata(value) {
74674
+ if (typeof value === "string") {
74675
+ const normalizedValue = value.trim();
74676
+ if (!normalizedValue) {
74677
+ return {};
74678
+ }
74679
+ const email = normalizedValue.match(/<([^>]+)>/)?.[1];
74680
+ const url = normalizedValue.match(/\(([^)]+)\)/)?.[1];
74681
+ const name = normalizedValue.replace(/<[^>]+>/g, " ").replace(/\([^)]+\)/g, " ").replace(/\s+/g, " ").trim();
74682
+ return {
74683
+ name: normalizeNonEmptyString(name),
74684
+ email: normalizeNonEmptyString(email),
74685
+ url: normalizeNonEmptyString(url)
74686
+ };
74687
+ }
74688
+ if (!isRecord(value)) {
74689
+ return {};
74690
+ }
74691
+ return {
74692
+ name: normalizeNonEmptyString(value.name),
74693
+ email: normalizeNonEmptyString(value.email),
74694
+ url: normalizeNonEmptyString(value.url)
74695
+ };
74696
+ }
74697
+ function extractPublisherIdFromRepository(value) {
74698
+ const repositoryUrl = typeof value === "string" ? value : isRecord(value) ? normalizeNonEmptyString(value.url) : void 0;
74699
+ const normalizedRepositoryUrl = normalizeNonEmptyString(repositoryUrl);
74700
+ if (!normalizedRepositoryUrl) {
74701
+ return void 0;
74702
+ }
74703
+ const shorthandMatch = normalizedRepositoryUrl.match(/^(?:github|gitlab|bitbucket):([^/]+)\/.+$/i);
74704
+ if (shorthandMatch?.[1]) {
74705
+ return normalizeGeneratedIdentifier(shorthandMatch[1]);
74706
+ }
74707
+ const sshMatch = normalizedRepositoryUrl.match(/^[^@]+@[^:]+:([^/]+)\/.+$/i);
74708
+ if (sshMatch?.[1]) {
74709
+ return normalizeGeneratedIdentifier(sshMatch[1]);
74710
+ }
74711
+ try {
74712
+ const parsed = new URL(normalizedRepositoryUrl.replace(/^git\+/, ""));
74713
+ const firstPathSegment = parsed.pathname.replace(/\.git$/i, "").split("/").filter(Boolean)[0];
74714
+ return normalizeGeneratedIdentifier(firstPathSegment ?? parsed.hostname.replace(/^www\./i, ""));
74715
+ } catch {
74716
+ return void 0;
74717
+ }
74718
+ }
74719
+ function extractPublisherIdFromUrl(value) {
74720
+ const normalizedValue = normalizeNonEmptyString(value);
74721
+ if (!normalizedValue) {
74722
+ return void 0;
74723
+ }
74724
+ try {
74725
+ const parsed = new URL(normalizedValue);
74726
+ return normalizeGeneratedIdentifier(parsed.hostname.replace(/^www\./i, ""));
74727
+ } catch {
74728
+ return void 0;
74729
+ }
74730
+ }
74731
+ function extractPublisherIdFromEmail(value) {
74732
+ const normalizedValue = normalizeNonEmptyString(value);
74733
+ if (!normalizedValue) {
74734
+ return void 0;
74735
+ }
74736
+ const domain = normalizedValue.split("@")[1];
74737
+ return domain ? normalizeGeneratedIdentifier(domain.replace(/^www\./i, "")) : void 0;
74738
+ }
74739
+ function resolveAutoGeneratedWapkAppId(packageName, fallbackName) {
74740
+ const scopedPackage = parseScopedPackageName(packageName);
74741
+ return joinGeneratedIdentifier(scopedPackage.scope, scopedPackage.packageName ?? fallbackName);
74742
+ }
74743
+ function resolveAutoGeneratedWapkPublisherId(packageJson, fallbackName) {
74744
+ const scopedPackage = parseScopedPackageName(typeof packageJson?.name === "string" ? packageJson.name : void 0);
74745
+ if (scopedPackage.scope) {
74746
+ return normalizeGeneratedIdentifier(scopedPackage.scope);
74747
+ }
74748
+ const author = readPackageAuthorMetadata(packageJson?.author);
74749
+ return extractPublisherIdFromRepository(packageJson?.repository) ?? extractPublisherIdFromUrl(packageJson?.homepage) ?? extractPublisherIdFromUrl(author.url) ?? extractPublisherIdFromEmail(author.email) ?? normalizeGeneratedIdentifier(author.name ?? fallbackName);
74750
+ }
74493
74751
  function normalizeStringMap(value) {
74494
74752
  if (!isRecord(value)) {
74495
74753
  return void 0;
@@ -74529,6 +74787,8 @@ function normalizeWapkConfig(value) {
74529
74787
  runtime: normalizeRuntime(value.runtime ?? value.engine),
74530
74788
  entry: typeof value.entry === "string" ? value.entry : void 0,
74531
74789
  scripts: normalizeStringMap(value.scripts ?? value.script),
74790
+ appId: normalizeNonEmptyString(value.appId),
74791
+ publisherId: normalizeNonEmptyString(value.publisherId),
74532
74792
  port: normalizePort(value.port),
74533
74793
  env: normalizeStringMap(value.env),
74534
74794
  desktop: normalizeDesktopConfig(value.desktop),
@@ -74818,6 +75078,7 @@ async function readWapkProjectConfig(directory) {
74818
75078
  const elitConfig = await loadConfig(directory);
74819
75079
  const elitWapkConfig = normalizeWapkConfig(elitConfig?.wapk);
74820
75080
  const packageJson = readJsonFile(packageJsonPath);
75081
+ const packageJsonWapk = isRecord(packageJson?.wapk) ? packageJson.wapk : void 0;
74821
75082
  const packageScripts = normalizeStringMap(packageJson?.scripts) ?? {};
74822
75083
  const selectedScripts = elitWapkConfig.scripts ?? packageScripts;
74823
75084
  const inferred = inferRuntimeAndEntryFromScript(selectedScripts.start ?? packageScripts.start);
@@ -74841,12 +75102,16 @@ async function readWapkProjectConfig(directory) {
74841
75102
  if (!existsSync2(entryPath) || !statSync2(entryPath).isFile()) {
74842
75103
  throw new Error(`WAPK entry not found: ${entryPath}`);
74843
75104
  }
75105
+ const appId = elitWapkConfig.appId ?? normalizeNonEmptyString(packageJson?.appId) ?? normalizeNonEmptyString(packageJsonWapk?.appId) ?? resolveAutoGeneratedWapkAppId(typeof packageJson?.name === "string" ? packageJson.name : void 0, name);
75106
+ const publisherId = elitWapkConfig.publisherId ?? normalizeNonEmptyString(packageJson?.publisherId) ?? normalizeNonEmptyString(packageJsonWapk?.publisherId) ?? resolveAutoGeneratedWapkPublisherId(packageJson, name);
74844
75107
  return {
74845
75108
  name,
74846
75109
  version,
74847
75110
  runtime: runtime2,
74848
75111
  entry,
74849
75112
  scripts: selectedScripts,
75113
+ appId,
75114
+ publisherId,
74850
75115
  port: elitWapkConfig.port,
74851
75116
  env: elitWapkConfig.env,
74852
75117
  desktop: elitWapkConfig.desktop,
@@ -74856,11 +75121,14 @@ async function readWapkProjectConfig(directory) {
74856
75121
  function readIgnorePatterns(directory) {
74857
75122
  return readLineIgnorePatterns(join2(directory, ".wapkignore"));
74858
75123
  }
75124
+ function parsePatternLines(content) {
75125
+ return content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && (!line.startsWith("#") || line.startsWith("\\#")));
75126
+ }
74859
75127
  function readLineIgnorePatterns(filePath) {
74860
75128
  if (!existsSync2(filePath)) {
74861
75129
  return [];
74862
75130
  }
74863
- return readFileSync2(filePath, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
75131
+ return parsePatternLines(readFileSync2(filePath, "utf8"));
74864
75132
  }
74865
75133
  function normalizePackageEntry(value) {
74866
75134
  const normalized = normalizeNonEmptyString(value)?.replace(/^[.][\\/]/, "").split("\\").join("/");
@@ -75041,23 +75309,188 @@ function resolveLinkedDependencyArchivePrefix(archivePrefix, dependencyName) {
75041
75309
  }
75042
75310
  return `${normalizedPrefix.slice(0, markerIndex + marker.length)}${dependencyName}`;
75043
75311
  }
75044
- function shouldIgnore(relativePath2, ignorePatterns) {
75045
- const pathParts = relativePath2.split("/");
75312
+ function escapeRegex(text) {
75313
+ return text.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
75314
+ }
75315
+ function globPatternToRegex(pattern, options = {}) {
75316
+ let regex = "";
75317
+ for (let index = 0; index < pattern.length; index++) {
75318
+ const char = pattern[index];
75319
+ if (char === "*") {
75320
+ const nextChar = pattern[index + 1];
75321
+ const nextNextChar = pattern[index + 2];
75322
+ if (nextChar === "*") {
75323
+ if (nextNextChar === "/") {
75324
+ regex += "(?:.*?/)?";
75325
+ index += 2;
75326
+ continue;
75327
+ }
75328
+ regex += ".*";
75329
+ index += 1;
75330
+ continue;
75331
+ }
75332
+ regex += "[^/]*";
75333
+ continue;
75334
+ }
75335
+ if (char === "?") {
75336
+ regex += "[^/]";
75337
+ continue;
75338
+ }
75339
+ regex += escapeRegex(char);
75340
+ }
75341
+ const suffix = options.directoryOnly ? "(?:$|/.*)" : "$";
75342
+ const prefix = options.matchSegmentsOnly ? "^(?:.*?/)?" : "^";
75343
+ return new RegExp(`${prefix}${regex}${suffix}`);
75344
+ }
75345
+ function normalizeIgnorePattern(pattern) {
75346
+ let normalizedPattern = pattern.trim();
75347
+ if (!normalizedPattern) {
75348
+ return void 0;
75349
+ }
75350
+ let negate = false;
75351
+ if (normalizedPattern.startsWith("\\!") || normalizedPattern.startsWith("\\#")) {
75352
+ normalizedPattern = normalizedPattern.slice(1);
75353
+ } else if (normalizedPattern.startsWith("!")) {
75354
+ negate = true;
75355
+ normalizedPattern = normalizedPattern.slice(1).trim();
75356
+ }
75357
+ if (!normalizedPattern) {
75358
+ return void 0;
75359
+ }
75360
+ const directoryOnly = normalizedPattern.endsWith("/");
75361
+ if (directoryOnly) {
75362
+ normalizedPattern = normalizedPattern.replace(/\/+$/, "");
75363
+ }
75364
+ normalizedPattern = normalizedPattern.replace(/^\.\//, "").replace(/^\//, "").replace(/\\/g, "/");
75365
+ if (!normalizedPattern) {
75366
+ return void 0;
75367
+ }
75368
+ return {
75369
+ directoryOnly,
75370
+ matchSegmentsOnly: !normalizedPattern.includes("/"),
75371
+ negate,
75372
+ pattern: normalizedPattern
75373
+ };
75374
+ }
75375
+ function matchesIgnorePattern(relativePath2, pattern, isDirectory) {
75376
+ const normalizedRule = normalizeIgnorePattern(pattern);
75377
+ if (!normalizedRule) {
75378
+ return false;
75379
+ }
75380
+ if (normalizedRule.directoryOnly && !isDirectory) {
75381
+ return false;
75382
+ }
75383
+ const hasGlob = /[*?]/.test(normalizedRule.pattern);
75384
+ if (!hasGlob) {
75385
+ if (normalizedRule.matchSegmentsOnly) {
75386
+ return relativePath2 === normalizedRule.pattern || relativePath2.split("/").includes(normalizedRule.pattern);
75387
+ }
75388
+ return relativePath2 === normalizedRule.pattern;
75389
+ }
75390
+ if (normalizedRule.matchSegmentsOnly) {
75391
+ const segmentRegex = globPatternToRegex(normalizedRule.pattern);
75392
+ return relativePath2.split("/").some((segment) => segmentRegex.test(segment));
75393
+ }
75394
+ return globPatternToRegex(normalizedRule.pattern, { directoryOnly: normalizedRule.directoryOnly }).test(relativePath2);
75395
+ }
75396
+ function shouldIgnore(relativePath2, ignorePatterns, isDirectory) {
75397
+ let ignored = false;
75046
75398
  for (const pattern of ignorePatterns) {
75047
- if (relativePath2 === pattern || pathParts.includes(pattern)) {
75048
- return true;
75399
+ const normalizedRule = normalizeIgnorePattern(pattern);
75400
+ if (!normalizedRule) {
75401
+ continue;
75049
75402
  }
75050
- if (pattern.endsWith("*")) {
75051
- const prefix = pattern.slice(0, -1);
75052
- if (relativePath2.startsWith(prefix) || pathParts.some((part) => part.startsWith(prefix))) {
75053
- return true;
75054
- }
75403
+ if (!matchesIgnorePattern(relativePath2, pattern, isDirectory)) {
75404
+ continue;
75055
75405
  }
75056
- if (pattern.startsWith("*.") && relativePath2.endsWith(pattern.slice(1))) {
75057
- return true;
75406
+ ignored = !normalizedRule.negate;
75407
+ }
75408
+ return ignored;
75409
+ }
75410
+ function matchesPatchPattern(relativePath2, pattern) {
75411
+ const normalizedRule = normalizeIgnorePattern(pattern);
75412
+ if (!normalizedRule) {
75413
+ return false;
75414
+ }
75415
+ const normalizedPath = relativePath2.replace(/\\/g, "/");
75416
+ const subtreeSelector = normalizedRule.pattern.endsWith("/*") && !normalizedRule.pattern.slice(0, -2).includes("*") && !normalizedRule.pattern.slice(0, -2).includes("?");
75417
+ if (subtreeSelector) {
75418
+ const directoryPath = normalizedRule.pattern.slice(0, -2);
75419
+ return directoryPath.length > 0 && normalizedPath.startsWith(`${directoryPath}/`);
75420
+ }
75421
+ const hasGlob = /[*?]/.test(normalizedRule.pattern);
75422
+ if (!hasGlob) {
75423
+ if (normalizedRule.directoryOnly) {
75424
+ return normalizedPath === normalizedRule.pattern || normalizedPath.startsWith(`${normalizedRule.pattern}/`);
75425
+ }
75426
+ return normalizedPath === normalizedRule.pattern;
75427
+ }
75428
+ return globPatternToRegex(normalizedRule.pattern, {
75429
+ directoryOnly: normalizedRule.directoryOnly,
75430
+ matchSegmentsOnly: false
75431
+ }).test(normalizedPath);
75432
+ }
75433
+ function shouldPatchArchivePath(relativePath2, patchPatterns) {
75434
+ let selected = false;
75435
+ for (const pattern of patchPatterns) {
75436
+ const normalizedRule = normalizeIgnorePattern(pattern);
75437
+ if (!normalizedRule) {
75438
+ continue;
75439
+ }
75440
+ if (!matchesPatchPattern(relativePath2, pattern)) {
75441
+ continue;
75058
75442
  }
75443
+ selected = !normalizedRule.negate;
75444
+ }
75445
+ return selected;
75446
+ }
75447
+ function resolvePatchManifestPatterns(files) {
75448
+ const patchManifest = files.find((file) => file.path === ".wapkpatch");
75449
+ if (!patchManifest) {
75450
+ throw new Error("Patch archive must include a .wapkpatch manifest file.");
75451
+ }
75452
+ const patterns = parsePatternLines(patchManifest.content.toString("utf8"));
75453
+ if (patterns.length === 0) {
75454
+ throw new Error("Patch archive .wapkpatch must define at least one patch rule.");
75455
+ }
75456
+ return patterns;
75457
+ }
75458
+ function applyPatchEntriesToFiles(targetFiles, patchFiles) {
75459
+ const fileMap = new Map(targetFiles.map((file) => [file.path, file]));
75460
+ const fileOrder = targetFiles.map((file) => file.path);
75461
+ const addedPaths = [];
75462
+ const updatedPaths = [];
75463
+ const unchangedPaths = [];
75464
+ for (const patchFile of [...patchFiles].sort((left, right) => left.path.localeCompare(right.path))) {
75465
+ const existing = fileMap.get(patchFile.path);
75466
+ const nextEntry = {
75467
+ path: patchFile.path,
75468
+ content: Buffer.from(patchFile.content),
75469
+ mode: patchFile.mode
75470
+ };
75471
+ if (!existing) {
75472
+ addedPaths.push(patchFile.path);
75473
+ fileOrder.push(patchFile.path);
75474
+ } else if (existing.mode === patchFile.mode && existing.content.equals(patchFile.content)) {
75475
+ unchangedPaths.push(patchFile.path);
75476
+ } else {
75477
+ updatedPaths.push(patchFile.path);
75478
+ }
75479
+ fileMap.set(patchFile.path, nextEntry);
75059
75480
  }
75060
- return false;
75481
+ return {
75482
+ files: fileOrder.map((filePath) => {
75483
+ const file = fileMap.get(filePath);
75484
+ if (!file) {
75485
+ throw new Error(`Internal WAPK patch error: missing file entry for ${filePath}`);
75486
+ }
75487
+ return file;
75488
+ }),
75489
+ patchedPaths: [...updatedPaths, ...addedPaths],
75490
+ addedPaths,
75491
+ updatedPaths,
75492
+ unchangedPaths
75493
+ };
75061
75494
  }
75062
75495
  function collectFiles(directory, baseDirectory, ignorePatterns) {
75063
75496
  const files = [];
@@ -75065,7 +75498,7 @@ function collectFiles(directory, baseDirectory, ignorePatterns) {
75065
75498
  for (const entry of entries) {
75066
75499
  const fullPath = join2(directory, entry.name);
75067
75500
  const relativePath2 = relative2(baseDirectory, fullPath).split("\\").join("/");
75068
- if (shouldIgnore(relativePath2, ignorePatterns)) {
75501
+ if (shouldIgnore(relativePath2, ignorePatterns, entry.isDirectory())) {
75069
75502
  continue;
75070
75503
  }
75071
75504
  if (entry.isSymbolicLink()) {
@@ -75214,6 +75647,8 @@ function decodeWapkPayload(buffer) {
75214
75647
  runtime: normalizeRuntime(rawHeader.runtime ?? rawHeader.engine) ?? "node",
75215
75648
  entry: typeof rawHeader.entry === "string" ? rawHeader.entry : "index.js",
75216
75649
  scripts: normalizeStringMap(rawHeader.scripts) ?? {},
75650
+ appId: normalizeNonEmptyString(rawHeader.appId),
75651
+ publisherId: normalizeNonEmptyString(rawHeader.publisherId),
75217
75652
  port: normalizePort(rawHeader.port),
75218
75653
  env: normalizeStringMap(rawHeader.env),
75219
75654
  desktop: normalizeDesktopConfig(rawHeader.desktop),
@@ -75711,11 +76146,14 @@ function sanitizeOnlineArchiveFileName(label, fallback) {
75711
76146
  const fileName = sanitized.length > 0 ? sanitized : "app.wapk";
75712
76147
  return fileName.toLowerCase().endsWith(".wapk") ? fileName : `${fileName}.wapk`;
75713
76148
  }
76149
+ var WAPK_ONLINE_JOIN_SOURCE_QUERY_PARAM = "launchSource";
76150
+ var WAPK_ONLINE_JOIN_SOURCE_QUERY_VALUE = "elit-wapk-online";
75714
76151
  function buildOnlineJoinUrl(baseUrl, joinKey) {
75715
76152
  const joinUrl = new URL(baseUrl.toString());
75716
76153
  joinUrl.search = "";
75717
76154
  joinUrl.hash = "";
75718
76155
  joinUrl.searchParams.set("join", joinKey);
76156
+ joinUrl.searchParams.set(WAPK_ONLINE_JOIN_SOURCE_QUERY_PARAM, WAPK_ONLINE_JOIN_SOURCE_QUERY_VALUE);
75719
76157
  return joinUrl.toString();
75720
76158
  }
75721
76159
  async function probeOnlineLauncherUrl(url) {
@@ -75896,11 +76334,16 @@ async function closeWapkOnlineSharedSession(launcherUrl, session) {
75896
76334
  function isPmWapkOnlineShutdownEnabled() {
75897
76335
  return process.env[WAPK_ONLINE_PM_SHUTDOWN_ENV] === "1" && Boolean(process.stdin) && !process.stdin.isTTY;
75898
76336
  }
75899
- async function waitForWapkOnlineSessionShutdown(launcherUrl, session, archiveHandle, lock) {
76337
+ function getWapkOnlineProcessDetails() {
76338
+ return `pid ${process.pid}, ppid ${process.ppid}`;
76339
+ }
76340
+ async function waitForWapkOnlineSessionShutdown(launcherUrl, session, archiveHandle, lock, options = {}) {
75900
76341
  let snapshotRevision = 0;
75901
76342
  let snapshotSyncPending = false;
75902
76343
  let snapshotSyncPromise = Promise.resolve();
75903
76344
  let lastSnapshotSyncError = null;
76345
+ const allowSigtermClose = options.allowSigtermClose === true;
76346
+ const processDetails = getWapkOnlineProcessDetails();
75904
76347
  const syncGuestSnapshotUpdates = () => {
75905
76348
  if (snapshotSyncPending) {
75906
76349
  return snapshotSyncPromise;
@@ -75934,6 +76377,7 @@ async function waitForWapkOnlineSessionShutdown(launcherUrl, session, archiveHan
75934
76377
  void syncGuestSnapshotUpdates();
75935
76378
  }, WAPK_ONLINE_KEEPALIVE_INTERVAL_MS);
75936
76379
  const pmManaged = isPmWapkOnlineShutdownEnabled();
76380
+ let ignoredSigTermLogged = false;
75937
76381
  let stdinBuffer = "";
75938
76382
  const cleanup = () => {
75939
76383
  clearInterval(keepAlive);
@@ -75952,7 +76396,17 @@ async function waitForWapkOnlineSessionShutdown(launcherUrl, session, archiveHan
75952
76396
  finish({ kind: "signal", signal: "SIGINT" });
75953
76397
  };
75954
76398
  const onSigTerm = () => {
75955
- finish({ kind: "signal", signal: "SIGTERM" });
76399
+ if (allowSigtermClose) {
76400
+ finish({ kind: "signal", signal: "SIGTERM" });
76401
+ return;
76402
+ }
76403
+ if (ignoredSigTermLogged) {
76404
+ return;
76405
+ }
76406
+ ignoredSigTermLogged = true;
76407
+ console.warn(
76408
+ pmManaged ? `[wapk] Ignoring SIGTERM while shared session ${session.joinKey} is active (${processDetails}). Use elit pm stop, restart, or delete to close the session.` : `[wapk] Ignoring SIGTERM while shared session ${session.joinKey} is active (${processDetails}). Press Ctrl+C to stop sharing, or pass --allow-sigterm-close to close on SIGTERM.`
76409
+ );
75956
76410
  };
75957
76411
  const onStdinData = (chunk) => {
75958
76412
  stdinBuffer += typeof chunk === "string" ? chunk : chunk.toString("utf8");
@@ -75977,9 +76431,12 @@ async function waitForWapkOnlineSessionShutdown(launcherUrl, session, archiveHan
75977
76431
  if (shutdownTrigger.kind === "pm") {
75978
76432
  console.log(`
75979
76433
  [wapk] PM requested shutdown for shared session ${session.joinKey}...`);
76434
+ } else if (shutdownTrigger.signal === "SIGTERM") {
76435
+ console.log(`
76436
+ [wapk] Received SIGTERM for shared session ${session.joinKey} (${processDetails}); closing because --allow-sigterm-close is enabled...`);
75980
76437
  } else {
75981
76438
  console.log(`
75982
- [wapk] Closing shared session ${session.joinKey}...`);
76439
+ [wapk] Received ${shutdownTrigger.signal}; closing shared session ${session.joinKey}...`);
75983
76440
  }
75984
76441
  try {
75985
76442
  await closeWapkOnlineSharedSession(launcherUrl, session);
@@ -76019,7 +76476,9 @@ async function runWapkOnline(archiveSpecifier, options) {
76019
76476
  process.exitCode = await waitForWapkOnlineSessionShutdown(launcherUrl, {
76020
76477
  joinKey: response.joinKey,
76021
76478
  adminToken: response.adminToken
76022
- }, archiveHandle, onlineArchiveLock);
76479
+ }, archiveHandle, onlineArchiveLock, {
76480
+ allowSigtermClose: options.allowSigtermClose
76481
+ });
76023
76482
  }
76024
76483
  async function writeWapkArchiveFromMemory(archiveHandle, header, files, lock) {
76025
76484
  const updatedHeader = {
@@ -76276,6 +76735,8 @@ async function packWapkDirectory(directory, options = {}) {
76276
76735
  runtime: config.runtime,
76277
76736
  entry: config.entry,
76278
76737
  scripts: config.scripts,
76738
+ appId: config.appId,
76739
+ publisherId: config.publisherId,
76279
76740
  port: config.port,
76280
76741
  env: config.env,
76281
76742
  desktop: config.desktop,
@@ -76307,6 +76768,56 @@ function extractWapkArchive(wapkPath, outputDir = ".", options = {}) {
76307
76768
  console.log(`Extracted ${archive.files.length} files to: ${extractDirectory}`);
76308
76769
  return extractDirectory;
76309
76770
  }
76771
+ async function patchWapkArchive(wapkPath, options) {
76772
+ const targetHandle = resolveArchiveHandle(wapkPath);
76773
+ const targetSnapshot = await targetHandle.readSnapshot();
76774
+ const targetEnvelope = parseWapkEnvelope(targetSnapshot.buffer);
76775
+ const targetArchive = decodeWapk(targetSnapshot.buffer, options);
76776
+ const targetLock = targetEnvelope.version === WAPK_LOCKED_VERSION ? resolveArchiveCredentials(options) : void 0;
76777
+ const patchArchive = readWapkArchive(options.from, {
76778
+ password: options.fromPassword ?? options.password
76779
+ });
76780
+ const patchPatterns = resolvePatchManifestPatterns(patchArchive.files);
76781
+ const selectedPatchFiles = patchArchive.files.filter((file) => file.path !== ".wapkpatch").filter((file) => shouldPatchArchivePath(file.path, patchPatterns));
76782
+ const patchResult = applyPatchEntriesToFiles(targetArchive.files, selectedPatchFiles);
76783
+ console.log(`[wapk] Target: ${targetSnapshot.label ?? targetHandle.label}`);
76784
+ console.log(`[wapk] Patch: ${options.from}`);
76785
+ console.log(`[wapk] Rules: ${patchPatterns.length}`);
76786
+ if (selectedPatchFiles.length === 0) {
76787
+ console.log("[wapk] No files matched .wapkpatch. Archive was not modified.");
76788
+ return {
76789
+ archiveLabel: targetSnapshot.label ?? targetHandle.label,
76790
+ patchedPaths: [],
76791
+ addedPaths: [],
76792
+ updatedPaths: [],
76793
+ unchangedPaths: []
76794
+ };
76795
+ }
76796
+ if (patchResult.patchedPaths.length === 0) {
76797
+ console.log("[wapk] Matching patch files were already up to date. Archive was not modified.");
76798
+ return {
76799
+ archiveLabel: targetSnapshot.label ?? targetHandle.label,
76800
+ patchedPaths: [],
76801
+ addedPaths: [],
76802
+ updatedPaths: [],
76803
+ unchangedPaths: patchResult.unchangedPaths
76804
+ };
76805
+ }
76806
+ const writeResult = await writeWapkArchiveFromMemory(
76807
+ targetHandle,
76808
+ targetArchive.header,
76809
+ patchResult.files,
76810
+ targetLock
76811
+ );
76812
+ console.log(`[wapk] Applied ${patchResult.patchedPaths.length} patch file${patchResult.patchedPaths.length === 1 ? "" : "s"}.`);
76813
+ return {
76814
+ archiveLabel: writeResult.label,
76815
+ patchedPaths: patchResult.patchedPaths,
76816
+ addedPaths: patchResult.addedPaths,
76817
+ updatedPaths: patchResult.updatedPaths,
76818
+ unchangedPaths: patchResult.unchangedPaths
76819
+ };
76820
+ }
76310
76821
  async function prepareWapkApp(wapkPath, options = {}) {
76311
76822
  const archiveHandle = resolveArchiveHandle(wapkPath, options.googleDrive);
76312
76823
  const archivePath = archiveHandle.identifier;
@@ -76415,6 +76926,8 @@ function inspectWapkArchive(wapkPath, options = {}) {
76415
76926
  console.log(`App: ${decoded.header.version}`);
76416
76927
  console.log(`Runtime: ${decoded.header.runtime}`);
76417
76928
  console.log(`Entry: ${decoded.header.entry}`);
76929
+ console.log(`App ID: ${decoded.header.appId ?? "n/a"}`);
76930
+ console.log(`Publisher:${decoded.header.publisherId ? ` ${decoded.header.publisherId}` : " n/a"}`);
76418
76931
  console.log(`Port: ${decoded.header.port ?? "default"}`);
76419
76932
  console.log(`Created: ${decoded.header.createdAt}`);
76420
76933
  if (decoded.header.env && Object.keys(decoded.header.env).length > 0) {
@@ -76446,6 +76959,8 @@ function printWapkHelp() {
76446
76959
  " elit wapk gdrive://<fileId> --online",
76447
76960
  " elit wapk pack [directory]",
76448
76961
  " elit wapk pack [directory] --password secret-123",
76962
+ " elit wapk patch <file.wapk> --from <patch.wapk>",
76963
+ " elit wapk patch <file.wapk> --use <patch.wapk>",
76449
76964
  " elit wapk inspect <file.wapk>",
76450
76965
  " elit wapk extract <file.wapk>",
76451
76966
  "",
@@ -76457,24 +76972,32 @@ function printWapkHelp() {
76457
76972
  " --archive-watch Pull external archive changes back into the temp workdir",
76458
76973
  " --no-archive-watch Disable external archive read sync",
76459
76974
  " --online Create an Elit Run share session, stay alive, and close on Ctrl+C",
76975
+ " --allow-sigterm-close Allow SIGTERM to close an online shared session",
76460
76976
  " --online-url <url> Elit Run URL (default: auto-detect localhost:4177 or localhost:4179)",
76461
76977
  " --google-drive-file-id <id> Run a remote .wapk directly from Google Drive",
76462
76978
  " --google-drive-token-env <name> Env var containing the Google Drive OAuth token",
76463
76979
  " --google-drive-access-token <value> OAuth token for Google Drive API calls",
76464
76980
  " --google-drive-shared-drive Include supportsAllDrives=true for shared drives",
76981
+ " --from <file.wapk> Patch source archive for elit wapk patch",
76982
+ " --use <file.wapk> Alias for --from",
76983
+ " --from-password <value> Password for unlocking the patch archive",
76465
76984
  " --include-deps Legacy compatibility flag; node_modules are packed by default",
76466
76985
  " --password <value> Password for locking or unlocking the archive",
76467
76986
  " -h, --help Show this help",
76468
76987
  "",
76469
76988
  "Notes:",
76470
76989
  " - Pack reads wapk from elit.config.* and falls back to package.json.",
76471
- " - Pack includes node_modules by default; use .wapkignore if you need to exclude them.",
76990
+ " - If appId or publisherId is not configured, pack auto-generates stable defaults from package metadata.",
76991
+ " - Pack includes node_modules by default; use .wapkignore if you need to exclude them, and !pattern to re-include later matches.",
76992
+ " - Patch reads .wapkpatch from the patch archive and applies only matching archive-relative paths.",
76993
+ " - Patch keeps the target archive metadata and lock mode; use --from-password when the patch archive uses a different password.",
76472
76994
  " - Run never installs dependencies automatically; archives must include the runtime dependencies they need.",
76473
76995
  " - Run mode can read config.wapk.run for default file/runtime/live-sync options.",
76474
76996
  " - Browser-style archives with scripts.start or wapk.script.start run that start script automatically.",
76475
76997
  " - Run mode keeps files in RAM and syncs changes both to and from the archive source.",
76476
76998
  " - Google Drive mode talks to the Drive API directly; no local archive file is required.",
76477
76999
  " - Online mode creates a shared session on Elit Run directly, keeps the CLI alive, and closes it on Ctrl+C.",
77000
+ " - Online mode ignores SIGTERM by default; pass --allow-sigterm-close if an external supervisor should close the shared session with SIGTERM.",
76478
77001
  " - Locked archives in online mode must provide --password so the CLI can build the shared snapshot.",
76479
77002
  " - Locked archives require the same password for run/extract/inspect.",
76480
77003
  " - Archives stay unlocked by default unless a password is provided.",
@@ -76524,6 +77047,7 @@ function parseRunArgs(args) {
76524
77047
  let archiveSyncInterval;
76525
77048
  let online;
76526
77049
  let onlineUrl;
77050
+ let allowSigtermClose;
76527
77051
  let password;
76528
77052
  for (let index = 0; index < args.length; index++) {
76529
77053
  const arg = args[index];
@@ -76575,6 +77099,10 @@ function parseRunArgs(args) {
76575
77099
  onlineUrl = readRequiredOptionValue(args, ++index, "--online-url");
76576
77100
  break;
76577
77101
  }
77102
+ case "--allow-sigterm-close": {
77103
+ allowSigtermClose = true;
77104
+ break;
77105
+ }
76578
77106
  case "--google-drive-file-id": {
76579
77107
  googleDrive = {
76580
77108
  ...googleDrive,
@@ -76617,7 +77145,7 @@ function parseRunArgs(args) {
76617
77145
  break;
76618
77146
  }
76619
77147
  }
76620
- return { file, googleDrive, runtime: runtime2, syncInterval, useWatcher, watchArchive, archiveSyncInterval, online, onlineUrl, password };
77148
+ return { file, googleDrive, runtime: runtime2, syncInterval, useWatcher, watchArchive, archiveSyncInterval, online, onlineUrl, allowSigtermClose, password };
76621
77149
  }
76622
77150
  function parsePackArgs(args) {
76623
77151
  let directory = ".";
@@ -76643,6 +77171,46 @@ function parsePackArgs(args) {
76643
77171
  }
76644
77172
  return { directory, includeDeps, password };
76645
77173
  }
77174
+ function parsePatchArgs(args) {
77175
+ let file;
77176
+ let from;
77177
+ let password;
77178
+ let fromPassword;
77179
+ for (let index = 0; index < args.length; index++) {
77180
+ const arg = args[index];
77181
+ switch (arg) {
77182
+ case "--from":
77183
+ case "--use": {
77184
+ if (from) {
77185
+ throw new Error("WAPK patch accepts exactly one patch archive via --from or --use.");
77186
+ }
77187
+ from = readRequiredOptionValue(args, ++index, arg);
77188
+ break;
77189
+ }
77190
+ case "--password": {
77191
+ password = readRequiredOptionValue(args, ++index, "--password");
77192
+ break;
77193
+ }
77194
+ case "--from-password": {
77195
+ fromPassword = readRequiredOptionValue(args, ++index, "--from-password");
77196
+ break;
77197
+ }
77198
+ default:
77199
+ if (arg.startsWith("-")) {
77200
+ throw new Error(`Unknown WAPK option: ${arg}`);
77201
+ }
77202
+ if (file) {
77203
+ throw new Error("Usage: elit wapk patch <file.wapk> --from <patch.wapk>");
77204
+ }
77205
+ file = arg;
77206
+ break;
77207
+ }
77208
+ }
77209
+ if (!file || !from) {
77210
+ throw new Error("Usage: elit wapk patch <file.wapk> --from <patch.wapk>");
77211
+ }
77212
+ return { file, from, password, fromPassword };
77213
+ }
76646
77214
  async function readConfiguredWapkRunDefaults(cwd) {
76647
77215
  const config = await loadConfig(cwd);
76648
77216
  const runConfig = normalizeWapkRunConfig(config?.wapk?.run);
@@ -76682,6 +77250,7 @@ function resolveConfiguredWapkRunOptions(options, defaults) {
76682
77250
  archiveSyncInterval: options.archiveSyncInterval ?? defaults?.archiveSyncInterval,
76683
77251
  online: options.online ?? defaults?.online ?? Boolean(onlineUrl),
76684
77252
  onlineUrl,
77253
+ allowSigtermClose: options.allowSigtermClose === true,
76685
77254
  password: options.password ?? defaults?.password
76686
77255
  };
76687
77256
  }
@@ -76714,6 +77283,15 @@ async function runWapkCommand(args, cwd = process.cwd()) {
76714
77283
  });
76715
77284
  return;
76716
77285
  }
77286
+ if (args[0] === "patch") {
77287
+ const options = parsePatchArgs(args.slice(1));
77288
+ await patchWapkArchive(options.file, {
77289
+ from: options.from,
77290
+ password: options.password,
77291
+ fromPassword: options.fromPassword
77292
+ });
77293
+ return;
77294
+ }
76717
77295
  if (args[0] === "inspect") {
76718
77296
  const options = parseArchiveAccessArgs(args.slice(1), "Usage: elit wapk inspect <file.wapk>");
76719
77297
  inspectWapkArchive(options.file, options);
@@ -76742,6 +77320,7 @@ async function runWapkCommand(args, cwd = process.cwd()) {
76742
77320
  await runWapkOnline(archiveSpecifier, {
76743
77321
  googleDrive: runOptions.googleDrive,
76744
77322
  onlineUrl: runOptions.onlineUrl,
77323
+ allowSigtermClose: runOptions.allowSigtermClose,
76745
77324
  password: runOptions.password
76746
77325
  });
76747
77326
  return;
@@ -83074,6 +83653,7 @@ WAPK Options:
83074
83653
  elit wapk run [file.wapk] Run a packaged app or the configured default archive
83075
83654
  elit wapk run --google-drive-file-id <id> Run a packaged app directly from Google Drive
83076
83655
  elit wapk pack [directory] Pack a directory into a .wapk archive
83656
+ elit wapk patch <file.wapk> --from <patch.wapk> Apply a manifest-driven patch archive
83077
83657
  elit wapk inspect <file.wapk> Inspect a .wapk archive
83078
83658
  elit wapk extract <file.wapk> Extract a .wapk archive
83079
83659
  elit wapk --runtime node|bun|deno [file] Override the packaged runtime