@php-wasm/node 3.1.21 → 3.1.25

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/index.js CHANGED
@@ -391,7 +391,7 @@ async function withNetworking(phpModuleArgs = {}) {
391
391
  // packages/php-wasm/node/src/lib/load-runtime.ts
392
392
  import {
393
393
  loadPHPRuntime,
394
- FSHelpers as FSHelpers5,
394
+ FSHelpers,
395
395
  FileLockManagerComposite,
396
396
  createLegacyPhpIniPreRunStep,
397
397
  isLegacyPHPVersion,
@@ -466,8 +466,8 @@ function bindUserSpace({ fileLockManager }, {
466
466
  [F_WRLCK]: "exclusive",
467
467
  [F_UNLCK]: "unlocked"
468
468
  },
469
- is_path_to_shared_fs(path2) {
470
- const { node } = FS.lookupPath(path2, { noent_okay: true });
469
+ is_path_to_shared_fs(path3) {
470
+ const { node } = FS.lookupPath(path3, { noent_okay: true });
471
471
  if (!node) {
472
472
  return false;
473
473
  }
@@ -1098,7 +1098,7 @@ function bindUserSpace({ fileLockManager }, {
1098
1098
  }
1099
1099
 
1100
1100
  // packages/php-wasm/node/src/lib/load-runtime.ts
1101
- import fs5 from "fs";
1101
+ import fs2 from "fs";
1102
1102
 
1103
1103
  // packages/php-wasm/node/src/lib/file-lock-manager-for-posix.ts
1104
1104
  import { MAX_ADDRESSABLE_FILE_OFFSET } from "@php-wasm/universal";
@@ -1116,7 +1116,7 @@ var FileLockManagerForPosix = class {
1116
1116
  this.wholeFileLockMap = /* @__PURE__ */ new Map();
1117
1117
  this.rangeLockedFds = /* @__PURE__ */ new Map();
1118
1118
  }
1119
- lockWholeFile(path2, op) {
1119
+ lockWholeFile(path3, op) {
1120
1120
  const opType = op.type === "unlock" ? "un" : op.waitForLock ? op.type === "exclusive" ? "ex" : "sh" : op.type === "exclusive" ? "exnb" : "shnb";
1121
1121
  try {
1122
1122
  flockSync(op.fd, opType);
@@ -1128,7 +1128,7 @@ var FileLockManagerForPosix = class {
1128
1128
  }
1129
1129
  this.wholeFileLockMap.get(op.pid).set(op.fd, {
1130
1130
  ...op,
1131
- path: path2
1131
+ path: path3
1132
1132
  });
1133
1133
  }
1134
1134
  return true;
@@ -1139,7 +1139,7 @@ var FileLockManagerForPosix = class {
1139
1139
  return false;
1140
1140
  }
1141
1141
  }
1142
- lockFileByteRange(path2, op, waitForLock) {
1142
+ lockFileByteRange(path3, op, waitForLock) {
1143
1143
  if (op.start === op.end) {
1144
1144
  op = {
1145
1145
  ...op,
@@ -1160,10 +1160,10 @@ var FileLockManagerForPosix = class {
1160
1160
  this.rangeLockedFds.set(op.pid, /* @__PURE__ */ new Map());
1161
1161
  }
1162
1162
  const pidMap = this.rangeLockedFds.get(op.pid);
1163
- if (!pidMap.has(path2)) {
1164
- pidMap.set(path2, /* @__PURE__ */ new Set());
1163
+ if (!pidMap.has(path3)) {
1164
+ pidMap.set(path3, /* @__PURE__ */ new Set());
1165
1165
  }
1166
- pidMap.get(path2).add(op.fd);
1166
+ pidMap.get(path3).add(op.fd);
1167
1167
  return true;
1168
1168
  } catch (e) {
1169
1169
  if (!isLockDenialError(e)) {
@@ -1172,13 +1172,13 @@ var FileLockManagerForPosix = class {
1172
1172
  return false;
1173
1173
  }
1174
1174
  }
1175
- findFirstConflictingByteRangeLock(path2, op) {
1175
+ findFirstConflictingByteRangeLock(path3, op) {
1176
1176
  if (op.type === "unlocked") {
1177
1177
  return void 0;
1178
1178
  }
1179
- const obtainedLock = this.lockFileByteRange(path2, op, false);
1179
+ const obtainedLock = this.lockFileByteRange(path3, op, false);
1180
1180
  if (obtainedLock) {
1181
- this.lockFileByteRange(path2, { ...op, type: "unlocked" }, true);
1181
+ this.lockFileByteRange(path3, { ...op, type: "unlocked" }, true);
1182
1182
  return void 0;
1183
1183
  }
1184
1184
  return {
@@ -1206,11 +1206,11 @@ var FileLockManagerForPosix = class {
1206
1206
  }
1207
1207
  this.wholeFileLockMap.delete(targetPid);
1208
1208
  }
1209
- for (const [path2, fdSet] of this.rangeLockedFds.get(targetPid) ?? []) {
1209
+ for (const [path3, fdSet] of this.rangeLockedFds.get(targetPid) ?? []) {
1210
1210
  for (const fd of fdSet) {
1211
1211
  try {
1212
1212
  this.lockFileByteRange(
1213
- path2,
1213
+ path3,
1214
1214
  {
1215
1215
  pid: targetPid,
1216
1216
  fd,
@@ -1222,7 +1222,7 @@ var FileLockManagerForPosix = class {
1222
1222
  );
1223
1223
  } catch (e) {
1224
1224
  logger2.error(
1225
- `releaseLocksForProcess: failed to unlock byte range for pid=${targetPid} fd=${fd} path=${path2}`,
1225
+ `releaseLocksForProcess: failed to unlock byte range for pid=${targetPid} fd=${fd} path=${path3}`,
1226
1226
  e
1227
1227
  );
1228
1228
  }
@@ -1286,7 +1286,7 @@ var FileLockManagerForWindows = class {
1286
1286
  this.wholeFileLockMap = /* @__PURE__ */ new Map();
1287
1287
  this.rangeLockedFds = /* @__PURE__ */ new Map();
1288
1288
  }
1289
- lockWholeFile(path2, op) {
1289
+ lockWholeFile(path3, op) {
1290
1290
  const start = 0n;
1291
1291
  const end = 2n ** 64n - 1n;
1292
1292
  if (op.type === "unlock") {
@@ -1298,7 +1298,7 @@ var FileLockManagerForWindows = class {
1298
1298
  }
1299
1299
  } else {
1300
1300
  logger3.warn(
1301
- `lockWholeFile: unlock failed for pid=${op.pid} fd=${op.fd} path=${path2}`
1301
+ `lockWholeFile: unlock failed for pid=${op.pid} fd=${op.fd} path=${path3}`
1302
1302
  );
1303
1303
  }
1304
1304
  return success2;
@@ -1366,22 +1366,22 @@ var FileLockManagerForWindows = class {
1366
1366
  }
1367
1367
  this.wholeFileLockMap.get(op.pid).set(op.fd, {
1368
1368
  ...op,
1369
- path: path2
1369
+ path: path3
1370
1370
  });
1371
1371
  }
1372
1372
  return success;
1373
1373
  }
1374
- lockFileByteRange(path2, op, waitForLock) {
1374
+ lockFileByteRange(path3, op, waitForLock) {
1375
1375
  if (op.start === op.end) {
1376
1376
  op = {
1377
1377
  ...op,
1378
1378
  end: MAX_ADDRESSABLE_FILE_OFFSET2
1379
1379
  };
1380
1380
  }
1381
- if (!this.rangeLockedFds.has(path2)) {
1382
- this.rangeLockedFds.set(path2, new FileLockIntervalTree());
1381
+ if (!this.rangeLockedFds.has(path3)) {
1382
+ this.rangeLockedFds.set(path3, new FileLockIntervalTree());
1383
1383
  }
1384
- const lockedRangeTree = this.rangeLockedFds.get(path2);
1384
+ const lockedRangeTree = this.rangeLockedFds.get(path3);
1385
1385
  const overlappingLocks = lockedRangeTree.findOverlapping(op);
1386
1386
  let preexistingLock;
1387
1387
  if (overlappingLocks.length === 1 && overlappingLocks[0].pid === op.pid && // NOTE: FD shouldn't matter for fcntl() F_SETLK because it is a process-level lock,
@@ -1466,12 +1466,12 @@ var FileLockManagerForWindows = class {
1466
1466
  return true;
1467
1467
  }
1468
1468
  }
1469
- findFirstConflictingByteRangeLock(path2, op) {
1469
+ findFirstConflictingByteRangeLock(path3, op) {
1470
1470
  if (op.type === "unlocked") {
1471
1471
  return void 0;
1472
1472
  }
1473
- const obtainedLock = !!this.lockFileByteRange(path2, op, false);
1474
- this.lockFileByteRange(path2, { ...op, type: "unlocked" }, false);
1473
+ const obtainedLock = !!this.lockFileByteRange(path3, op, false);
1474
+ this.lockFileByteRange(path3, { ...op, type: "unlocked" }, false);
1475
1475
  if (obtainedLock) {
1476
1476
  return void 0;
1477
1477
  }
@@ -1500,18 +1500,18 @@ var FileLockManagerForWindows = class {
1500
1500
  }
1501
1501
  this.wholeFileLockMap.delete(targetPid);
1502
1502
  }
1503
- for (const [path2, lockedRangeTree] of this.rangeLockedFds.entries()) {
1503
+ for (const [path3, lockedRangeTree] of this.rangeLockedFds.entries()) {
1504
1504
  const rangesLockedByTargetPid = lockedRangeTree.findLocksForProcess(targetPid);
1505
1505
  for (const op of rangesLockedByTargetPid) {
1506
1506
  try {
1507
1507
  this.lockFileByteRange(
1508
- path2,
1508
+ path3,
1509
1509
  { ...op, type: "unlocked" },
1510
1510
  false
1511
1511
  );
1512
1512
  } catch (e) {
1513
1513
  logger3.error(
1514
- `releaseLocksForProcess: failed to unlock byte range for pid=${targetPid} fd=${op.fd} path=${path2}`,
1514
+ `releaseLocksForProcess: failed to unlock byte range for pid=${targetPid} fd=${op.fd} path=${path3}`,
1515
1515
  e
1516
1516
  );
1517
1517
  }
@@ -1547,204 +1547,63 @@ var FileLockManagerForWindows = class {
1547
1547
  }
1548
1548
  };
1549
1549
 
1550
- // packages/php-wasm/node/src/lib/extensions/xdebug/with-xdebug.ts
1550
+ // packages/php-wasm/node/src/lib/extensions/load-extensions.ts
1551
1551
  import { DEFAULT_IDE_KEY } from "@php-wasm/cli-util";
1552
1552
  import {
1553
- FSHelpers,
1554
- LatestSupportedPHPVersion as LatestSupportedPHPVersion3,
1555
- SupportedPHPVersions,
1556
- SupportedPHPVersionsList
1553
+ withResolvedPHPExtensions,
1554
+ resolvePHPExtension,
1555
+ SupportedPHPVersions
1557
1556
  } from "@php-wasm/universal";
1558
1557
  import fs from "fs";
1558
+ import path2 from "path";
1559
1559
 
1560
- // packages/php-wasm/node/src/lib/extensions/xdebug/get-xdebug-extension-module.ts
1560
+ // packages/php-wasm/node/src/lib/extensions/intl/get-intl-extension-module.ts
1561
1561
  import { LatestSupportedPHPVersion as LatestSupportedPHPVersion2 } from "@php-wasm/universal";
1562
- async function getXdebugExtensionModule(version = LatestSupportedPHPVersion2) {
1562
+ async function getIntlExtensionModule(version = LatestSupportedPHPVersion2) {
1563
1563
  switch (version) {
1564
1564
  case "8.5":
1565
- return (await import("@php-wasm/node-8-5")).getXdebugExtensionPath();
1565
+ return (await import("@php-wasm/node-8-5")).getIntlExtensionPath();
1566
1566
  case "8.4":
1567
- return (await import("@php-wasm/node-8-4")).getXdebugExtensionPath();
1567
+ return (await import("@php-wasm/node-8-4")).getIntlExtensionPath();
1568
1568
  case "8.3":
1569
- return (await import("@php-wasm/node-8-3")).getXdebugExtensionPath();
1569
+ return (await import("@php-wasm/node-8-3")).getIntlExtensionPath();
1570
1570
  case "8.2":
1571
- return (await import("@php-wasm/node-8-2")).getXdebugExtensionPath();
1571
+ return (await import("@php-wasm/node-8-2")).getIntlExtensionPath();
1572
1572
  case "8.1":
1573
- return (await import("@php-wasm/node-8-1")).getXdebugExtensionPath();
1573
+ return (await import("@php-wasm/node-8-1")).getIntlExtensionPath();
1574
1574
  case "8.0":
1575
- return (await import("@php-wasm/node-8-0")).getXdebugExtensionPath();
1575
+ return (await import("@php-wasm/node-8-0")).getIntlExtensionPath();
1576
1576
  case "7.4":
1577
- return (await import("@php-wasm/node-7-4")).getXdebugExtensionPath();
1577
+ return (await import("@php-wasm/node-7-4")).getIntlExtensionPath();
1578
1578
  }
1579
1579
  throw new Error(`Unsupported PHP version ${version}`);
1580
1580
  }
1581
1581
 
1582
- // packages/php-wasm/node/src/lib/extensions/xdebug/with-xdebug.ts
1583
- async function withXdebug(version = LatestSupportedPHPVersion3, options, xdebugOptions) {
1584
- const fileName = "xdebug.so";
1585
- const filePath = await getXdebugExtensionModule(version);
1586
- const extension = fs.readFileSync(filePath);
1587
- return {
1588
- ...options,
1589
- ENV: {
1590
- ...options.ENV,
1591
- PHP_INI_SCAN_DIR: "/internal/shared/extensions"
1592
- },
1593
- onRuntimeInitialized: (phpRuntime) => {
1594
- if (options.onRuntimeInitialized) {
1595
- options.onRuntimeInitialized(phpRuntime);
1596
- }
1597
- if (!FSHelpers.fileExists(
1598
- phpRuntime.FS,
1599
- "/internal/shared/extensions"
1600
- )) {
1601
- phpRuntime.FS.mkdirTree("/internal/shared/extensions");
1602
- }
1603
- if (!FSHelpers.fileExists(
1604
- phpRuntime.FS,
1605
- `/internal/shared/extensions/${fileName}`
1606
- )) {
1607
- phpRuntime.FS.writeFile(
1608
- `/internal/shared/extensions/${fileName}`,
1609
- new Uint8Array(extension)
1610
- );
1611
- }
1612
- if (!FSHelpers.fileExists(
1613
- phpRuntime.FS,
1614
- "/internal/shared/extensions/xdebug.ini"
1615
- )) {
1616
- const ideKey = xdebugOptions.ideKey || DEFAULT_IDE_KEY;
1617
- phpRuntime.FS.writeFile(
1618
- "/internal/shared/extensions/xdebug.ini",
1619
- [
1620
- "zend_extension=/internal/shared/extensions/xdebug.so",
1621
- "xdebug.mode=debug,develop",
1622
- "xdebug.start_with_request=yes",
1623
- `xdebug.idekey="${ideKey}"`,
1624
- // Path mapping is only available starting
1625
- // from Xdebug 3.5, which is used by PHP 8.5+
1626
- // Previous versions will ignore this entry.
1627
- "xdebug.path_mapping=yes"
1628
- ].join("\n")
1629
- );
1630
- }
1631
- const isPHP85orHigher = SupportedPHPVersionsList.indexOf(version) <= SupportedPHPVersions.indexOf("8.5");
1632
- if (isPHP85orHigher) {
1633
- const { pathMappings, pathSkippings } = xdebugOptions;
1634
- if (!pathMappings && !pathSkippings)
1635
- return;
1636
- phpRuntime.FS.mkdir("/.xdebug");
1637
- if (pathMappings) {
1638
- phpRuntime.FS.writeFile(
1639
- "/.xdebug/path.map",
1640
- pathMappings.map((map) => `${map.vfsPath} = ${map.hostPath}`).join("\n")
1641
- );
1642
- }
1643
- if (pathSkippings) {
1644
- phpRuntime.FS.writeFile(
1645
- "/.xdebug/skip.map",
1646
- pathSkippings.map((path2) => `${path2} = SKIP`).join("\n")
1647
- );
1648
- }
1649
- }
1650
- }
1651
- };
1652
- }
1653
-
1654
- // packages/php-wasm/node/src/lib/extensions/intl/with-intl.ts
1655
- import { LatestSupportedPHPVersion as LatestSupportedPHPVersion5, FSHelpers as FSHelpers2 } from "@php-wasm/universal";
1656
- import fs2 from "fs";
1657
- import path from "path";
1658
-
1659
- // packages/php-wasm/node/src/lib/extensions/intl/get-intl-extension-module.ts
1660
- import { LatestSupportedPHPVersion as LatestSupportedPHPVersion4 } from "@php-wasm/universal";
1661
- async function getIntlExtensionModule(version = LatestSupportedPHPVersion4) {
1582
+ // packages/php-wasm/node/src/lib/extensions/memcached/get-memcached-extension-module.ts
1583
+ import { LatestSupportedPHPVersion as LatestSupportedPHPVersion3 } from "@php-wasm/universal";
1584
+ async function getMemcachedExtensionModule(version = LatestSupportedPHPVersion3) {
1662
1585
  switch (version) {
1663
1586
  case "8.5":
1664
- return (await import("@php-wasm/node-8-5")).getIntlExtensionPath();
1587
+ return (await import("@php-wasm/node-8-5")).getMemcachedExtensionPath();
1665
1588
  case "8.4":
1666
- return (await import("@php-wasm/node-8-4")).getIntlExtensionPath();
1589
+ return (await import("@php-wasm/node-8-4")).getMemcachedExtensionPath();
1667
1590
  case "8.3":
1668
- return (await import("@php-wasm/node-8-3")).getIntlExtensionPath();
1591
+ return (await import("@php-wasm/node-8-3")).getMemcachedExtensionPath();
1669
1592
  case "8.2":
1670
- return (await import("@php-wasm/node-8-2")).getIntlExtensionPath();
1593
+ return (await import("@php-wasm/node-8-2")).getMemcachedExtensionPath();
1671
1594
  case "8.1":
1672
- return (await import("@php-wasm/node-8-1")).getIntlExtensionPath();
1595
+ return (await import("@php-wasm/node-8-1")).getMemcachedExtensionPath();
1673
1596
  case "8.0":
1674
- return (await import("@php-wasm/node-8-0")).getIntlExtensionPath();
1597
+ return (await import("@php-wasm/node-8-0")).getMemcachedExtensionPath();
1675
1598
  case "7.4":
1676
- return (await import("@php-wasm/node-7-4")).getIntlExtensionPath();
1599
+ return (await import("@php-wasm/node-7-4")).getMemcachedExtensionPath();
1677
1600
  }
1678
1601
  throw new Error(`Unsupported PHP version ${version}`);
1679
1602
  }
1680
1603
 
1681
- // packages/php-wasm/node/src/lib/extensions/intl/with-intl.ts
1682
- async function withIntl(version = LatestSupportedPHPVersion5, options) {
1683
- const extensionName = "intl.so";
1684
- const extensionPath = await getIntlExtensionModule(version);
1685
- const extension = fs2.readFileSync(extensionPath);
1686
- const dataName = "icu.dat";
1687
- const moduleDir = typeof __dirname !== "undefined" ? __dirname : import.meta.dirname;
1688
- const dataPath = path.join(moduleDir, "shared", dataName);
1689
- const ICUData = fs2.readFileSync(dataPath);
1690
- return {
1691
- ...options,
1692
- ENV: {
1693
- ...options.ENV,
1694
- PHP_INI_SCAN_DIR: "/internal/shared/extensions",
1695
- ICU_DATA: "/internal/shared"
1696
- },
1697
- onRuntimeInitialized: (phpRuntime) => {
1698
- if (options.onRuntimeInitialized) {
1699
- options.onRuntimeInitialized(phpRuntime);
1700
- }
1701
- if (!FSHelpers2.fileExists(
1702
- phpRuntime.FS,
1703
- "/internal/shared/extensions"
1704
- )) {
1705
- phpRuntime.FS.mkdirTree("/internal/shared/extensions");
1706
- }
1707
- if (!FSHelpers2.fileExists(
1708
- phpRuntime.FS,
1709
- `/internal/shared/extensions/${extensionName}`
1710
- )) {
1711
- phpRuntime.FS.writeFile(
1712
- `/internal/shared/extensions/${extensionName}`,
1713
- new Uint8Array(extension)
1714
- );
1715
- }
1716
- if (!FSHelpers2.fileExists(
1717
- phpRuntime.FS,
1718
- "/internal/shared/extensions/intl.ini"
1719
- )) {
1720
- phpRuntime.FS.writeFile(
1721
- "/internal/shared/extensions/intl.ini",
1722
- [
1723
- `extension=/internal/shared/extensions/${extensionName}`
1724
- ].join("\n")
1725
- );
1726
- }
1727
- if (!FSHelpers2.fileExists(
1728
- phpRuntime.FS,
1729
- `${phpRuntime.ENV.ICU_DATA}/${dataName}`
1730
- )) {
1731
- phpRuntime.FS.mkdirTree(phpRuntime.ENV.ICU_DATA);
1732
- phpRuntime.FS.writeFile(
1733
- `${phpRuntime.ENV.ICU_DATA}/icudt74l.dat`,
1734
- new Uint8Array(ICUData)
1735
- );
1736
- }
1737
- }
1738
- };
1739
- }
1740
-
1741
- // packages/php-wasm/node/src/lib/extensions/redis/with-redis.ts
1742
- import { LatestSupportedPHPVersion as LatestSupportedPHPVersion7, FSHelpers as FSHelpers3 } from "@php-wasm/universal";
1743
- import fs3 from "fs";
1744
-
1745
1604
  // packages/php-wasm/node/src/lib/extensions/redis/get-redis-extension-module.ts
1746
- import { LatestSupportedPHPVersion as LatestSupportedPHPVersion6 } from "@php-wasm/universal";
1747
- async function getRedisExtensionModule(version = LatestSupportedPHPVersion6) {
1605
+ import { LatestSupportedPHPVersion as LatestSupportedPHPVersion4 } from "@php-wasm/universal";
1606
+ async function getRedisExtensionModule(version = LatestSupportedPHPVersion4) {
1748
1607
  switch (version) {
1749
1608
  case "8.5":
1750
1609
  return (await import("@php-wasm/node-8-5")).getRedisExtensionPath();
@@ -1764,125 +1623,230 @@ async function getRedisExtensionModule(version = LatestSupportedPHPVersion6) {
1764
1623
  throw new Error(`Unsupported PHP version ${version}`);
1765
1624
  }
1766
1625
 
1767
- // packages/php-wasm/node/src/lib/extensions/redis/with-redis.ts
1768
- async function withRedis(version = LatestSupportedPHPVersion7, options) {
1769
- const extensionName = "redis.so";
1770
- const extensionPath = await getRedisExtensionModule(version);
1771
- const extension = fs3.readFileSync(extensionPath);
1772
- return {
1773
- ...options,
1774
- ENV: {
1775
- ...options.ENV,
1776
- PHP_INI_SCAN_DIR: "/internal/shared/extensions"
1777
- },
1778
- onRuntimeInitialized: (phpRuntime) => {
1779
- if (options.onRuntimeInitialized) {
1780
- options.onRuntimeInitialized(phpRuntime);
1781
- }
1782
- if (!FSHelpers3.fileExists(
1783
- phpRuntime.FS,
1784
- "/internal/shared/extensions"
1785
- )) {
1786
- phpRuntime.FS.mkdirTree("/internal/shared/extensions");
1787
- }
1788
- if (!FSHelpers3.fileExists(
1789
- phpRuntime.FS,
1790
- `/internal/shared/extensions/${extensionName}`
1791
- )) {
1792
- phpRuntime.FS.writeFile(
1793
- `/internal/shared/extensions/${extensionName}`,
1794
- new Uint8Array(extension)
1795
- );
1796
- }
1797
- if (!FSHelpers3.fileExists(
1798
- phpRuntime.FS,
1799
- "/internal/shared/extensions/redis.ini"
1800
- )) {
1801
- phpRuntime.FS.writeFile(
1802
- "/internal/shared/extensions/redis.ini",
1803
- [
1804
- `extension=/internal/shared/extensions/${extensionName}`
1805
- ].join("\n")
1806
- );
1807
- }
1808
- }
1809
- };
1810
- }
1811
-
1812
- // packages/php-wasm/node/src/lib/extensions/memcached/with-memcached.ts
1813
- import { LatestSupportedPHPVersion as LatestSupportedPHPVersion9, FSHelpers as FSHelpers4 } from "@php-wasm/universal";
1814
- import fs4 from "fs";
1815
-
1816
- // packages/php-wasm/node/src/lib/extensions/memcached/get-memcached-extension-module.ts
1817
- import { LatestSupportedPHPVersion as LatestSupportedPHPVersion8 } from "@php-wasm/universal";
1818
- async function getMemcachedExtensionModule(version = LatestSupportedPHPVersion8) {
1626
+ // packages/php-wasm/node/src/lib/extensions/xdebug/get-xdebug-extension-module.ts
1627
+ import { LatestSupportedPHPVersion as LatestSupportedPHPVersion5 } from "@php-wasm/universal";
1628
+ async function getXdebugExtensionModule(version = LatestSupportedPHPVersion5) {
1819
1629
  switch (version) {
1820
1630
  case "8.5":
1821
- return (await import("@php-wasm/node-8-5")).getMemcachedExtensionPath();
1631
+ return (await import("@php-wasm/node-8-5")).getXdebugExtensionPath();
1822
1632
  case "8.4":
1823
- return (await import("@php-wasm/node-8-4")).getMemcachedExtensionPath();
1633
+ return (await import("@php-wasm/node-8-4")).getXdebugExtensionPath();
1824
1634
  case "8.3":
1825
- return (await import("@php-wasm/node-8-3")).getMemcachedExtensionPath();
1635
+ return (await import("@php-wasm/node-8-3")).getXdebugExtensionPath();
1826
1636
  case "8.2":
1827
- return (await import("@php-wasm/node-8-2")).getMemcachedExtensionPath();
1637
+ return (await import("@php-wasm/node-8-2")).getXdebugExtensionPath();
1828
1638
  case "8.1":
1829
- return (await import("@php-wasm/node-8-1")).getMemcachedExtensionPath();
1639
+ return (await import("@php-wasm/node-8-1")).getXdebugExtensionPath();
1830
1640
  case "8.0":
1831
- return (await import("@php-wasm/node-8-0")).getMemcachedExtensionPath();
1641
+ return (await import("@php-wasm/node-8-0")).getXdebugExtensionPath();
1832
1642
  case "7.4":
1833
- return (await import("@php-wasm/node-7-4")).getMemcachedExtensionPath();
1643
+ return (await import("@php-wasm/node-7-4")).getXdebugExtensionPath();
1834
1644
  }
1835
1645
  throw new Error(`Unsupported PHP version ${version}`);
1836
1646
  }
1837
1647
 
1838
- // packages/php-wasm/node/src/lib/extensions/memcached/with-memcached.ts
1839
- async function withMemcached(version = LatestSupportedPHPVersion9, options) {
1840
- const extensionName = "memcached.so";
1841
- const extensionPath = await getMemcachedExtensionModule(version);
1842
- const extension = fs4.readFileSync(extensionPath);
1648
+ // packages/php-wasm/node/src/lib/extensions/node-extension-resources.ts
1649
+ import { readFile } from "fs/promises";
1650
+ import path from "path";
1651
+ import { fileURLToPath, pathToFileURL } from "url";
1652
+ function normalizeNodeExtensionSource(source) {
1653
+ if (source.format === "url") {
1654
+ return {
1655
+ ...source,
1656
+ url: toNodeResourceUrl(source.url)
1657
+ };
1658
+ }
1659
+ if (source.format !== "manifest") {
1660
+ return source;
1661
+ }
1662
+ if ("manifest" in source) {
1663
+ return source.baseUrl ? {
1664
+ ...source,
1665
+ baseUrl: toNodeResourceUrl(source.baseUrl)
1666
+ } : source;
1667
+ }
1843
1668
  return {
1844
- ...options,
1845
- ENV: {
1846
- ...options.ENV,
1847
- PHP_INI_SCAN_DIR: "/internal/shared/extensions"
1848
- },
1849
- onRuntimeInitialized: (phpRuntime) => {
1850
- if (options.onRuntimeInitialized) {
1851
- options.onRuntimeInitialized(phpRuntime);
1852
- }
1853
- if (!FSHelpers4.fileExists(
1854
- phpRuntime.FS,
1855
- "/internal/shared/extensions"
1856
- )) {
1857
- phpRuntime.FS.mkdirTree("/internal/shared/extensions");
1858
- }
1859
- if (!FSHelpers4.fileExists(
1860
- phpRuntime.FS,
1861
- `/internal/shared/extensions/${extensionName}`
1862
- )) {
1863
- phpRuntime.FS.writeFile(
1864
- `/internal/shared/extensions/${extensionName}`,
1865
- new Uint8Array(extension)
1866
- );
1867
- }
1868
- if (!FSHelpers4.fileExists(
1869
- phpRuntime.FS,
1870
- "/internal/shared/extensions/memcached.ini"
1871
- )) {
1872
- phpRuntime.FS.writeFile(
1873
- "/internal/shared/extensions/memcached.ini",
1874
- [
1875
- `extension=/internal/shared/extensions/${extensionName}`
1876
- ].join("\n")
1877
- );
1878
- }
1669
+ ...source,
1670
+ manifestUrl: toNodeResourceUrl(source.manifestUrl)
1671
+ };
1672
+ }
1673
+ async function fetchNodeExtensionResource(input) {
1674
+ const url = input instanceof Request ? new URL(input.url) : input instanceof URL ? input : toNodeResourceUrl(String(input));
1675
+ if (url.protocol === "file:") {
1676
+ try {
1677
+ return new Response(await readFile(fileURLToPath(url)));
1678
+ } catch (error) {
1679
+ return new Response(String(error), {
1680
+ status: 404,
1681
+ statusText: "Not Found"
1682
+ });
1683
+ }
1684
+ }
1685
+ return fetch(input);
1686
+ }
1687
+ function toNodeResourceUrl(urlOrPath) {
1688
+ if (urlOrPath instanceof URL) {
1689
+ return urlOrPath;
1690
+ }
1691
+ try {
1692
+ const url = new URL(urlOrPath);
1693
+ if (url.protocol === "http:" || url.protocol === "https:" || url.protocol === "file:") {
1694
+ return url;
1879
1695
  }
1696
+ } catch {
1697
+ }
1698
+ return pathToFileURL(path.resolve(urlOrPath));
1699
+ }
1700
+
1701
+ // packages/php-wasm/node/src/lib/extensions/load-extensions.ts
1702
+ async function withPHPExtensions(version, asyncMode, options, extensions = []) {
1703
+ if (!extensions.length) {
1704
+ return options;
1705
+ }
1706
+ const resolvedExtensions = await Promise.all(
1707
+ extensions.map(
1708
+ (extension) => resolveRuntimePHPExtension(version, asyncMode, extension)
1709
+ )
1710
+ );
1711
+ return withResolvedPHPExtensions(options, resolvedExtensions);
1712
+ }
1713
+ async function resolveRuntimePHPExtension(version, asyncMode, extension) {
1714
+ if (typeof extension === "object" && "source" in extension) {
1715
+ if (asyncMode === "asyncify") {
1716
+ throw new Error(
1717
+ "External PHP extensions require JSPI. Asyncify is only supported for PHP.wasm bundled extensions."
1718
+ );
1719
+ }
1720
+ return await resolvePHPExtension({
1721
+ ...extension,
1722
+ source: normalizeNodeExtensionSource(extension.source),
1723
+ phpVersion: version,
1724
+ fetch: extension.fetch ?? fetchNodeExtensionResource
1725
+ });
1726
+ }
1727
+ const builtIn = typeof extension === "string" ? { name: extension } : extension;
1728
+ switch (builtIn.name) {
1729
+ case "intl": {
1730
+ const extensionPath = await getIntlExtensionModule(version);
1731
+ const soBytes = new Uint8Array(fs.readFileSync(extensionPath));
1732
+ const dataName = "icu.dat";
1733
+ const moduleDir = typeof __dirname !== "undefined" ? __dirname : import.meta.dirname;
1734
+ const ICUData = fs.readFileSync(
1735
+ resolveIntlDataPath(moduleDir, dataName)
1736
+ );
1737
+ return await resolvePHPExtension({
1738
+ source: {
1739
+ format: "so",
1740
+ name: "intl",
1741
+ bytes: soBytes
1742
+ },
1743
+ phpVersion: version,
1744
+ env: {
1745
+ ICU_DATA: "/internal/shared"
1746
+ },
1747
+ extraFiles: {
1748
+ targetPath: "/internal/shared",
1749
+ files: {
1750
+ // The Intl extension looks for the hard-coded ICU data name.
1751
+ "icudt74l.dat": new Uint8Array(ICUData)
1752
+ }
1753
+ }
1754
+ });
1755
+ }
1756
+ case "redis": {
1757
+ const extensionPath = await getRedisExtensionModule(version);
1758
+ return await resolvePHPExtension({
1759
+ source: {
1760
+ format: "so",
1761
+ name: "redis",
1762
+ bytes: new Uint8Array(fs.readFileSync(extensionPath))
1763
+ },
1764
+ phpVersion: version
1765
+ });
1766
+ }
1767
+ case "memcached": {
1768
+ const extensionPath = await getMemcachedExtensionModule(version);
1769
+ return await resolvePHPExtension({
1770
+ source: {
1771
+ format: "so",
1772
+ name: "memcached",
1773
+ bytes: new Uint8Array(fs.readFileSync(extensionPath))
1774
+ },
1775
+ phpVersion: version
1776
+ });
1777
+ }
1778
+ case "xdebug": {
1779
+ const xdebugOptions = builtIn.options ?? {};
1780
+ const filePath = await getXdebugExtensionModule(version);
1781
+ const ideKey = xdebugOptions.ideKey || DEFAULT_IDE_KEY;
1782
+ return await resolvePHPExtension({
1783
+ source: {
1784
+ format: "so",
1785
+ name: "xdebug",
1786
+ bytes: new Uint8Array(fs.readFileSync(filePath))
1787
+ },
1788
+ phpVersion: version,
1789
+ loadWithIniDirective: "zend_extension",
1790
+ iniEntries: {
1791
+ "xdebug.mode": "debug,develop",
1792
+ "xdebug.start_with_request": "yes",
1793
+ "xdebug.idekey": `"${ideKey}"`,
1794
+ // Path mapping is only available starting from Xdebug 3.5,
1795
+ // which is used by PHP 8.5+. Previous versions ignore it.
1796
+ "xdebug.path_mapping": "yes"
1797
+ },
1798
+ extraFiles: resolveXdebugExtraFiles(version, xdebugOptions)
1799
+ });
1800
+ }
1801
+ default:
1802
+ throw new Error(
1803
+ `Unknown bundled PHP extension: ${String(builtIn.name)}.`
1804
+ );
1805
+ }
1806
+ }
1807
+ function resolveIntlDataPath(moduleDir, dataName) {
1808
+ const candidatePaths = [
1809
+ // Built package layout: dist/packages/php-wasm/node/shared/icu.dat.
1810
+ path2.join(moduleDir, "shared", dataName),
1811
+ // Source/test layout: src/lib/extensions/intl/shared/icu.dat.
1812
+ path2.join(moduleDir, "intl", "shared", dataName)
1813
+ ];
1814
+ const dataPath = candidatePaths.find(
1815
+ (candidate) => fs.existsSync(candidate)
1816
+ );
1817
+ if (!dataPath) {
1818
+ throw new Error(
1819
+ `Could not find ${dataName}. Checked: ${candidatePaths.join(", ")}`
1820
+ );
1821
+ }
1822
+ return dataPath;
1823
+ }
1824
+ function resolveXdebugExtraFiles(version, xdebugOptions) {
1825
+ const isPHP85orHigher = SupportedPHPVersions.indexOf(version) <= SupportedPHPVersions.indexOf("8.5");
1826
+ if (!isPHP85orHigher) {
1827
+ return void 0;
1828
+ }
1829
+ const { pathMappings, pathSkippings } = xdebugOptions;
1830
+ if (!pathMappings && !pathSkippings) {
1831
+ return void 0;
1832
+ }
1833
+ const files = {};
1834
+ if (pathMappings) {
1835
+ files["path.map"] = pathMappings.map((map) => `${map.vfsPath} = ${map.hostPath}`).join("\n");
1836
+ }
1837
+ if (pathSkippings) {
1838
+ files["skip.map"] = pathSkippings.map((path3) => `${path3} = SKIP`).join("\n");
1839
+ }
1840
+ return {
1841
+ targetPath: "/.xdebug",
1842
+ files
1880
1843
  };
1881
1844
  }
1882
1845
 
1883
1846
  // packages/php-wasm/node/src/lib/load-runtime.ts
1884
1847
  import { dirname, joinPaths, toPosixPath } from "@php-wasm/util";
1885
1848
  import { platform } from "os";
1849
+ import { jspi } from "wasm-feature-detect";
1886
1850
  var dangerousDefaultProcessIdAllocator = process.env.VITEST ? new ProcessIdAllocator() : void 0;
1887
1851
  async function loadNodeRuntime(phpVersion, options = {}) {
1888
1852
  const processId = options.emscriptenOptions?.processId ?? // !! Only assign a default process ID during test.
@@ -1890,6 +1854,22 @@ async function loadNodeRuntime(phpVersion, options = {}) {
1890
1854
  // could break file locking and lead to database corruption.
1891
1855
  (process.env.VITEST ? dangerousDefaultProcessIdAllocator.claim() : void 0);
1892
1856
  const isLegacy = isLegacyPHPVersion(phpVersion);
1857
+ const phpWasmAsyncMode = await jspi() ? "jspi" : "asyncify";
1858
+ const requestedExtensions = [...options.extensions ?? []];
1859
+ if (options.withIntl && !hasBuiltInExtension(requestedExtensions, "intl")) {
1860
+ requestedExtensions.push("intl");
1861
+ }
1862
+ if (options.withRedis && !hasBuiltInExtension(requestedExtensions, "redis")) {
1863
+ requestedExtensions.push("redis");
1864
+ }
1865
+ if (options.withMemcached && !hasBuiltInExtension(requestedExtensions, "memcached")) {
1866
+ requestedExtensions.push("memcached");
1867
+ }
1868
+ if (options.withXdebug && !hasBuiltInExtension(requestedExtensions, "xdebug")) {
1869
+ requestedExtensions.push(
1870
+ typeof options.withXdebug === "object" ? { name: "xdebug", options: options.withXdebug } : "xdebug"
1871
+ );
1872
+ }
1893
1873
  let emscriptenOptions = {
1894
1874
  /**
1895
1875
  * Emscripten default behavior is to kill the process when
@@ -1908,6 +1888,7 @@ async function loadNodeRuntime(phpVersion, options = {}) {
1908
1888
  return bindUserSpace({ fileLockManager }, userSpaceContext);
1909
1889
  },
1910
1890
  ...options.emscriptenOptions || {},
1891
+ phpWasmAsyncMode,
1911
1892
  processId,
1912
1893
  // For legacy PHP: pre-create php.ini via a preRun step. See
1913
1894
  // createLegacyPhpIniPreRunStep for why this must run before
@@ -1923,7 +1904,7 @@ async function loadNodeRuntime(phpVersion, options = {}) {
1923
1904
  if (options?.followSymlinks === true) {
1924
1905
  phpRuntime.FS.filesystems.NODEFS.node_ops.readlink = (node) => {
1925
1906
  const absoluteSourcePath = phpRuntime.FS.filesystems.NODEFS.tryFSOperation(
1926
- () => fs5.realpathSync(
1907
+ () => fs2.realpathSync(
1927
1908
  phpRuntime.FS.filesystems.NODEFS.realPath(node)
1928
1909
  )
1929
1910
  );
@@ -1932,9 +1913,9 @@ async function loadNodeRuntime(phpVersion, options = {}) {
1932
1913
  `/internal/symlinks`,
1933
1914
  normalizedPath
1934
1915
  );
1935
- if (fs5.existsSync(absoluteSourcePath)) {
1936
- const sourceStat = fs5.statSync(absoluteSourcePath);
1937
- if (!FSHelpers5.fileExists(
1916
+ if (fs2.existsSync(absoluteSourcePath)) {
1917
+ const sourceStat = fs2.statSync(absoluteSourcePath);
1918
+ if (!FSHelpers.fileExists(
1938
1919
  phpRuntime.FS,
1939
1920
  symlinkMountPath
1940
1921
  )) {
@@ -1973,68 +1954,58 @@ async function loadNodeRuntime(phpVersion, options = {}) {
1973
1954
  phpRuntime.FS.root.mount.opts.root = ".";
1974
1955
  }
1975
1956
  };
1976
- if (isLegacy && (options?.withXdebug || options?.withIntl || options?.withRedis || options?.withMemcached)) {
1957
+ if (isLegacy && requestedExtensions.length) {
1977
1958
  throw new Error(
1978
1959
  `Extensions (xdebug, intl, redis, memcached) are not available for legacy PHP ${phpVersion}.`
1979
1960
  );
1980
1961
  }
1981
1962
  if (!isLegacy) {
1982
1963
  const modernVersion = phpVersion;
1983
- if (options?.withXdebug) {
1984
- emscriptenOptions = await withXdebug(
1985
- modernVersion,
1986
- emscriptenOptions,
1987
- typeof options.withXdebug === "object" ? options.withXdebug : {}
1988
- );
1989
- }
1990
- if (options?.withIntl === true) {
1991
- emscriptenOptions = await withIntl(
1992
- modernVersion,
1993
- emscriptenOptions
1994
- );
1995
- }
1996
- if (options?.withRedis === true) {
1997
- emscriptenOptions = await withRedis(
1998
- modernVersion,
1999
- emscriptenOptions
2000
- );
2001
- }
2002
- if (options?.withMemcached === true) {
2003
- emscriptenOptions = await withMemcached(
2004
- modernVersion,
2005
- emscriptenOptions
2006
- );
2007
- }
1964
+ emscriptenOptions = await withPHPExtensions(
1965
+ modernVersion,
1966
+ phpWasmAsyncMode,
1967
+ emscriptenOptions,
1968
+ requestedExtensions
1969
+ );
2008
1970
  }
2009
1971
  emscriptenOptions = await withNetworking(emscriptenOptions);
2010
1972
  const phpLoaderModule = await getPHPLoaderModule(phpVersion);
2011
1973
  const runtimeId = await loadPHPRuntime(phpLoaderModule, emscriptenOptions);
2012
1974
  return runtimeId;
2013
1975
  }
1976
+ function hasBuiltInExtension(extensions, name) {
1977
+ return extensions.some((extension) => {
1978
+ if (typeof extension === "string") {
1979
+ return extension === name;
1980
+ }
1981
+ return !("source" in extension) && extension.name === name;
1982
+ });
1983
+ }
2014
1984
 
2015
1985
  // packages/php-wasm/node/src/lib/use-host-filesystem.ts
2016
- import { lstatSync as lstatSync2, readdirSync } from "node:fs";
1986
+ import { lstatSync, readdirSync } from "node:fs";
2017
1987
 
2018
1988
  // packages/php-wasm/node/src/lib/node-fs-mount.ts
2019
1989
  import {
2020
- FSHelpers as FSHelpers6
1990
+ FSHelpers as FSHelpers2
2021
1991
  } from "@php-wasm/universal";
2022
1992
  import { isParentOf } from "@php-wasm/util";
2023
- import { lstatSync } from "fs";
1993
+ import { realpathSync, statSync } from "fs";
2024
1994
  import { dirname as dirname2 } from "path";
2025
1995
  function createNodeFsMountHandler(localPath) {
2026
1996
  return function(php, FS, vfsMountPoint) {
2027
1997
  let removeVfsNode = false;
2028
- if (!FSHelpers6.fileExists(FS, vfsMountPoint)) {
2029
- const lstat = lstatSync(localPath);
2030
- if (lstat.isFile() || lstat.isSymbolicLink()) {
1998
+ const mountRoot = realpathSync(localPath);
1999
+ if (!FSHelpers2.fileExists(FS, vfsMountPoint)) {
2000
+ const stat = statSync(mountRoot);
2001
+ if (stat.isFile()) {
2031
2002
  FS.mkdirTree(dirname2(vfsMountPoint));
2032
2003
  FS.writeFile(vfsMountPoint, "");
2033
- } else if (lstat.isDirectory()) {
2004
+ } else if (stat.isDirectory()) {
2034
2005
  FS.mkdirTree(vfsMountPoint);
2035
2006
  } else {
2036
2007
  throw new Error(
2037
- "Unsupported file type. PHP-wasm supports only symlinks that link to files, directories, or symlinks."
2008
+ "Unsupported file type. PHP-wasm supports mounting only files and directories, including symlinks that resolve to files or directories."
2038
2009
  );
2039
2010
  }
2040
2011
  removeVfsNode = true;
@@ -2051,7 +2022,7 @@ function createNodeFsMountHandler(localPath) {
2051
2022
  }
2052
2023
  throw e;
2053
2024
  }
2054
- FS.mount(FS.filesystems["NODEFS"], { root: localPath }, vfsMountPoint);
2025
+ FS.mount(FS.filesystems["NODEFS"], { root: mountRoot }, vfsMountPoint);
2055
2026
  return () => {
2056
2027
  FS.unmount(vfsMountPoint);
2057
2028
  if (removeVfsNode) {
@@ -2087,20 +2058,20 @@ function useHostFilesystem(php) {
2087
2058
  }
2088
2059
  php.chdir(process.cwd());
2089
2060
  }
2090
- function statPathFollowSymlinks(path2) {
2091
- let stat = lstatSync2(path2);
2061
+ function statPathFollowSymlinks(path3) {
2062
+ let stat = lstatSync(path3);
2092
2063
  if (stat.isSymbolicLink()) {
2093
- const fs6 = __require("fs");
2094
- let target = path2;
2064
+ const fs3 = __require("fs");
2065
+ let target = path3;
2095
2066
  const seen = /* @__PURE__ */ new Set();
2096
2067
  while (true) {
2097
2068
  if (seen.has(target)) {
2098
- throw new Error(`Symlink loop detected: ${path2}`);
2069
+ throw new Error(`Symlink loop detected: ${path3}`);
2099
2070
  }
2100
2071
  seen.add(target);
2101
- const linkStat = lstatSync2(target);
2072
+ const linkStat = lstatSync(target);
2102
2073
  if (linkStat.isSymbolicLink()) {
2103
- target = fs6.realpathSync(target);
2074
+ target = fs3.realpathSync(target);
2104
2075
  continue;
2105
2076
  }
2106
2077
  stat = linkStat;
@@ -2117,6 +2088,5 @@ export {
2117
2088
  getPHPLoaderModule,
2118
2089
  loadNodeRuntime,
2119
2090
  useHostFilesystem,
2120
- withNetworking,
2121
- withXdebug
2091
+ withNetworking
2122
2092
  };