@absolutejs/deploy 0.16.1 → 0.18.0

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.
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/releaseArtifact.ts"],
4
+ "sourcesContent": [
5
+ "import { mkdir, mkdtemp, rm, stat } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\n\nconst DEFAULT_MAX_BYTES = 2_147_483_648;\nconst ERROR_DETAIL_LIMIT = 500;\nconst SAFE_RELEASE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;\n\nexport type ReleaseArtifactMetadata = {\n bytes: number;\n releaseId: string;\n sha256: string;\n};\n\nexport type CreatedReleaseArtifact = {\n dispose: () => Promise<void>;\n file: Bun.BunFile;\n metadata: ReleaseArtifactMetadata;\n path: string;\n};\n\nexport class ReleaseArtifactError extends Error {}\n\nconst run = async (command: string[]) => {\n const process = Bun.spawn(command, { stderr: \"pipe\", stdout: \"pipe\" });\n const [exitCode, stderr, stdout] = await Promise.all([\n process.exited,\n new Response(process.stderr).text(),\n new Response(process.stdout).text(),\n ]);\n if (exitCode !== 0)\n throw new ReleaseArtifactError(\n `${command[0]} failed (${exitCode}): ${stderr.slice(0, ERROR_DETAIL_LIMIT)}`,\n );\n\n return stdout;\n};\n\nconst assertReleaseId = (value: string) => {\n if (!SAFE_RELEASE_ID.test(value))\n throw new ReleaseArtifactError(\"Release id is invalid\");\n\n return value;\n};\n\nconst assertExclude = (value: string) => {\n if (\n value.length === 0 ||\n value.startsWith(\"-\") ||\n value.startsWith(\"/\") ||\n value.split(\"/\").some((part) => part === \"..\") ||\n /[\\0\\r\\n]/.test(value)\n )\n throw new ReleaseArtifactError(`Invalid release exclusion: ${value}`);\n\n return value.replace(/^\\.\\//, \"\");\n};\n\nconst sha256File = async (file: Blob) => {\n const hasher = new Bun.CryptoHasher(\"sha256\");\n for await (const chunk of file.stream()) hasher.update(chunk);\n\n return hasher.digest(\"hex\");\n};\n\nexport const createReleaseArtifact = async (options: {\n exclude?: string[];\n releaseId?: string;\n sourceRoot: string;\n temporaryRoot?: string;\n}): Promise<CreatedReleaseArtifact> => {\n const releaseId = assertReleaseId(options.releaseId ?? crypto.randomUUID());\n const source = path.resolve(options.sourceRoot);\n const sourceStats = await stat(source).catch(() => null);\n if (!sourceStats?.isDirectory())\n throw new ReleaseArtifactError(\"Release source root is not a directory\");\n if (!(await Bun.file(path.join(source, \"package.json\")).exists()))\n throw new ReleaseArtifactError(\"Release source has no package.json\");\n const temporary = await mkdtemp(\n path.join(options.temporaryRoot ?? tmpdir(), \"absolutejs-release-\"),\n );\n const archivePath = path.join(temporary, `${releaseId}.tgz`);\n try {\n await run([\n \"tar\",\n \"-czf\",\n archivePath,\n ...(options.exclude ?? []).map(\n (value) => `--exclude=./${assertExclude(value)}`,\n ),\n \"-C\",\n source,\n \".\",\n ]);\n const file = Bun.file(archivePath);\n\n return {\n dispose: () => rm(temporary, { force: true, recursive: true }),\n file,\n metadata: {\n bytes: file.size,\n releaseId,\n sha256: await sha256File(file),\n },\n path: archivePath,\n };\n } catch (error) {\n await rm(temporary, { force: true, recursive: true });\n throw error;\n }\n};\n\nexport const receiveReleaseArtifact = async (options: {\n destination: string;\n expectedBytes: number;\n expectedSha256: string;\n maxBytes?: number;\n stream: ReadableStream<Uint8Array>;\n}) => {\n const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n if (\n !Number.isSafeInteger(options.expectedBytes) ||\n options.expectedBytes < 1 ||\n options.expectedBytes > maxBytes ||\n !/^[a-f0-9]{64}$/.test(options.expectedSha256)\n )\n throw new ReleaseArtifactError(\"Release artifact metadata is invalid\");\n await mkdir(path.dirname(options.destination), { recursive: true });\n const writer = Bun.file(options.destination).writer();\n const hasher = new Bun.CryptoHasher(\"sha256\");\n let bytes = 0;\n try {\n for await (const chunk of options.stream) {\n bytes += chunk.byteLength;\n if (bytes > options.expectedBytes || bytes > maxBytes)\n throw new ReleaseArtifactError(\n \"Release artifact exceeds its declared size\",\n );\n hasher.update(chunk);\n writer.write(chunk);\n }\n } catch (error) {\n await rm(options.destination, { force: true });\n throw error;\n } finally {\n await writer.end();\n }\n if (\n bytes !== options.expectedBytes ||\n hasher.digest(\"hex\") !== options.expectedSha256\n ) {\n await rm(options.destination, { force: true });\n throw new ReleaseArtifactError(\n \"Release artifact integrity verification failed\",\n );\n }\n\n return { bytes, sha256: options.expectedSha256 };\n};\n\nexport const extractReleaseArtifact = async (options: {\n archivePath: string;\n destination: string;\n}) => {\n const [names, verbose] = await Promise.all([\n run([\"tar\", \"-tzf\", options.archivePath]),\n run([\"tar\", \"-tvzf\", options.archivePath]),\n ]);\n const unsafePath = names\n .split(\"\\n\")\n .filter(Boolean)\n .some(\n (entry) =>\n entry.startsWith(\"/\") || entry.split(\"/\").some((part) => part === \"..\"),\n );\n const unsafeType = verbose\n .split(\"\\n\")\n .filter(Boolean)\n .some((entry) => entry[0] !== \"-\" && entry[0] !== \"d\");\n if (unsafePath || unsafeType)\n throw new ReleaseArtifactError(\"Release artifact contains an unsafe entry\");\n await rm(options.destination, { force: true, recursive: true });\n await mkdir(options.destination, { recursive: true });\n await run([\n \"tar\",\n \"-xzf\",\n options.archivePath,\n \"--no-same-owner\",\n \"--no-same-permissions\",\n \"-C\",\n options.destination,\n ]);\n if (\n !(await Bun.file(path.join(options.destination, \"package.json\")).exists())\n )\n throw new ReleaseArtifactError(\"Release artifact has no package.json\");\n\n return { extracted: true } as const;\n};\n"
6
+ ],
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AAEA,IAAM,oBAAoB;AAC1B,IAAM,qBAAqB;AAC3B,IAAM,kBAAkB;AAAA;AAejB,MAAM,6BAA6B,MAAM;AAAC;AAEjD,IAAM,MAAM,OAAO,YAAsB;AAAA,EACvC,MAAM,UAAU,IAAI,MAAM,SAAS,EAAE,QAAQ,QAAQ,QAAQ,OAAO,CAAC;AAAA,EACrE,OAAO,UAAU,QAAQ,UAAU,MAAM,QAAQ,IAAI;AAAA,IACnD,QAAQ;AAAA,IACR,IAAI,SAAS,QAAQ,MAAM,EAAE,KAAK;AAAA,IAClC,IAAI,SAAS,QAAQ,MAAM,EAAE,KAAK;AAAA,EACpC,CAAC;AAAA,EACD,IAAI,aAAa;AAAA,IACf,MAAM,IAAI,qBACR,GAAG,QAAQ,cAAc,cAAc,OAAO,MAAM,GAAG,kBAAkB,GAC3E;AAAA,EAEF,OAAO;AAAA;AAGT,IAAM,kBAAkB,CAAC,UAAkB;AAAA,EACzC,IAAI,CAAC,gBAAgB,KAAK,KAAK;AAAA,IAC7B,MAAM,IAAI,qBAAqB,uBAAuB;AAAA,EAExD,OAAO;AAAA;AAGT,IAAM,gBAAgB,CAAC,UAAkB;AAAA,EACvC,IACE,MAAM,WAAW,KACjB,MAAM,WAAW,GAAG,KACpB,MAAM,WAAW,GAAG,KACpB,MAAM,MAAM,GAAG,EAAE,KAAK,CAAC,SAAS,SAAS,IAAI,KAC7C,WAAW,KAAK,KAAK;AAAA,IAErB,MAAM,IAAI,qBAAqB,8BAA8B,OAAO;AAAA,EAEtE,OAAO,MAAM,QAAQ,SAAS,EAAE;AAAA;AAGlC,IAAM,aAAa,OAAO,SAAe;AAAA,EACvC,MAAM,SAAS,IAAI,IAAI,aAAa,QAAQ;AAAA,EAC5C,iBAAiB,SAAS,KAAK,OAAO;AAAA,IAAG,OAAO,OAAO,KAAK;AAAA,EAE5D,OAAO,OAAO,OAAO,KAAK;AAAA;AAGrB,IAAM,wBAAwB,OAAO,YAKL;AAAA,EACrC,MAAM,YAAY,gBAAgB,QAAQ,aAAa,OAAO,WAAW,CAAC;AAAA,EAC1E,MAAM,SAAS,KAAK,QAAQ,QAAQ,UAAU;AAAA,EAC9C,MAAM,cAAc,MAAM,KAAK,MAAM,EAAE,MAAM,MAAM,IAAI;AAAA,EACvD,IAAI,CAAC,aAAa,YAAY;AAAA,IAC5B,MAAM,IAAI,qBAAqB,wCAAwC;AAAA,EACzE,IAAI,CAAE,MAAM,IAAI,KAAK,KAAK,KAAK,QAAQ,cAAc,CAAC,EAAE,OAAO;AAAA,IAC7D,MAAM,IAAI,qBAAqB,oCAAoC;AAAA,EACrE,MAAM,YAAY,MAAM,QACtB,KAAK,KAAK,QAAQ,iBAAiB,OAAO,GAAG,qBAAqB,CACpE;AAAA,EACA,MAAM,cAAc,KAAK,KAAK,WAAW,GAAG,eAAe;AAAA,EAC3D,IAAI;AAAA,IACF,MAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,IAAI,QAAQ,WAAW,CAAC,GAAG,IACzB,CAAC,UAAU,eAAe,cAAc,KAAK,GAC/C;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,IACD,MAAM,OAAO,IAAI,KAAK,WAAW;AAAA,IAEjC,OAAO;AAAA,MACL,SAAS,MAAM,GAAG,WAAW,EAAE,OAAO,MAAM,WAAW,KAAK,CAAC;AAAA,MAC7D;AAAA,MACA,UAAU;AAAA,QACR,OAAO,KAAK;AAAA,QACZ;AAAA,QACA,QAAQ,MAAM,WAAW,IAAI;AAAA,MAC/B;AAAA,MACA,MAAM;AAAA,IACR;AAAA,IACA,OAAO,OAAO;AAAA,IACd,MAAM,GAAG,WAAW,EAAE,OAAO,MAAM,WAAW,KAAK,CAAC;AAAA,IACpD,MAAM;AAAA;AAAA;AAIH,IAAM,yBAAyB,OAAO,YAMvC;AAAA,EACJ,MAAM,WAAW,QAAQ,YAAY;AAAA,EACrC,IACE,CAAC,OAAO,cAAc,QAAQ,aAAa,KAC3C,QAAQ,gBAAgB,KACxB,QAAQ,gBAAgB,YACxB,CAAC,iBAAiB,KAAK,QAAQ,cAAc;AAAA,IAE7C,MAAM,IAAI,qBAAqB,sCAAsC;AAAA,EACvE,MAAM,MAAM,KAAK,QAAQ,QAAQ,WAAW,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,EAClE,MAAM,SAAS,IAAI,KAAK,QAAQ,WAAW,EAAE,OAAO;AAAA,EACpD,MAAM,SAAS,IAAI,IAAI,aAAa,QAAQ;AAAA,EAC5C,IAAI,QAAQ;AAAA,EACZ,IAAI;AAAA,IACF,iBAAiB,SAAS,QAAQ,QAAQ;AAAA,MACxC,SAAS,MAAM;AAAA,MACf,IAAI,QAAQ,QAAQ,iBAAiB,QAAQ;AAAA,QAC3C,MAAM,IAAI,qBACR,4CACF;AAAA,MACF,OAAO,OAAO,KAAK;AAAA,MACnB,OAAO,MAAM,KAAK;AAAA,IACpB;AAAA,IACA,OAAO,OAAO;AAAA,IACd,MAAM,GAAG,QAAQ,aAAa,EAAE,OAAO,KAAK,CAAC;AAAA,IAC7C,MAAM;AAAA,YACN;AAAA,IACA,MAAM,OAAO,IAAI;AAAA;AAAA,EAEnB,IACE,UAAU,QAAQ,iBAClB,OAAO,OAAO,KAAK,MAAM,QAAQ,gBACjC;AAAA,IACA,MAAM,GAAG,QAAQ,aAAa,EAAE,OAAO,KAAK,CAAC;AAAA,IAC7C,MAAM,IAAI,qBACR,gDACF;AAAA,EACF;AAAA,EAEA,OAAO,EAAE,OAAO,QAAQ,QAAQ,eAAe;AAAA;AAG1C,IAAM,yBAAyB,OAAO,YAGvC;AAAA,EACJ,OAAO,OAAO,WAAW,MAAM,QAAQ,IAAI;AAAA,IACzC,IAAI,CAAC,OAAO,QAAQ,QAAQ,WAAW,CAAC;AAAA,IACxC,IAAI,CAAC,OAAO,SAAS,QAAQ,WAAW,CAAC;AAAA,EAC3C,CAAC;AAAA,EACD,MAAM,aAAa,MAChB,MAAM;AAAA,CAAI,EACV,OAAO,OAAO,EACd,KACC,CAAC,UACC,MAAM,WAAW,GAAG,KAAK,MAAM,MAAM,GAAG,EAAE,KAAK,CAAC,SAAS,SAAS,IAAI,CAC1E;AAAA,EACF,MAAM,aAAa,QAChB,MAAM;AAAA,CAAI,EACV,OAAO,OAAO,EACd,KAAK,CAAC,UAAU,MAAM,OAAO,OAAO,MAAM,OAAO,GAAG;AAAA,EACvD,IAAI,cAAc;AAAA,IAChB,MAAM,IAAI,qBAAqB,2CAA2C;AAAA,EAC5E,MAAM,GAAG,QAAQ,aAAa,EAAE,OAAO,MAAM,WAAW,KAAK,CAAC;AAAA,EAC9D,MAAM,MAAM,QAAQ,aAAa,EAAE,WAAW,KAAK,CAAC;AAAA,EACpD,MAAM,IAAI;AAAA,IACR;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,IACE,CAAE,MAAM,IAAI,KAAK,KAAK,KAAK,QAAQ,aAAa,cAAc,CAAC,EAAE,OAAO;AAAA,IAExE,MAAM,IAAI,qBAAqB,sCAAsC;AAAA,EAEvE,OAAO,EAAE,WAAW,KAAK;AAAA;",
8
+ "debugId": "2D5742B97EBCA7BB64756E2164756E21",
9
+ "names": []
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/deploy",
3
- "version": "0.16.1",
3
+ "version": "0.18.0",
4
4
  "description": "Generic Bun-project deploy pipeline. A Target (localTarget / sshTarget) is anywhere you can exec + upload — DigitalOcean droplets, Linode, Hetzner, Vultr, your own boxes. Bundled pipeline: prepare → upload → install → build → link → restart → verify. Atomic symlink swap, release history, prune, hooks. SSH shells out to system ssh/rsync — zero ssh2 deps.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,7 +30,7 @@
30
30
  "release"
31
31
  ],
32
32
  "scripts": {
33
- "build": "rm -rf dist && bun build src/index.ts src/infrastructure.ts src/digitalocean.ts src/digitaloceanInfrastructure.ts src/gcp.ts src/hetzner.ts src/hetznerInfrastructure.ts src/linode.ts src/linodeInfrastructure.ts src/vultr.ts src/vultrInfrastructure.ts src/dns.ts src/cloudflare.ts src/digitaloceanDns.ts src/hetznerDns.ts src/route53.ts src/tls.ts src/env.ts src/preview.ts --outdir dist --root src --sourcemap --target=bun && tsc --project tsconfig.build.json",
33
+ "build": "rm -rf dist && bun build src/index.ts src/releaseArtifact.ts src/infrastructure.ts src/edgeIngress.ts src/digitalocean.ts src/digitaloceanInfrastructure.ts src/digitaloceanIngress.ts src/gcp.ts src/gcpIngress.ts src/hetzner.ts src/hetznerInfrastructure.ts src/linode.ts src/linodeInfrastructure.ts src/vultr.ts src/vultrInfrastructure.ts src/dns.ts src/cloudflare.ts src/digitaloceanDns.ts src/hetznerDns.ts src/route53.ts src/tls.ts src/env.ts src/preview.ts --outdir dist --root src --sourcemap --target=bun && tsc --project tsconfig.build.json",
34
34
  "test": "bun test tests/",
35
35
  "typecheck": "tsc --noEmit",
36
36
  "format": "prettier --write \"./**/*.{ts,json,md}\"",
@@ -69,11 +69,31 @@
69
69
  "import": "./dist/infrastructure.js",
70
70
  "default": "./dist/infrastructure.js"
71
71
  },
72
+ "./edge-ingress": {
73
+ "types": "./dist/edgeIngress.d.ts",
74
+ "import": "./dist/edgeIngress.js",
75
+ "default": "./dist/edgeIngress.js"
76
+ },
77
+ "./release-artifact": {
78
+ "types": "./dist/releaseArtifact.d.ts",
79
+ "import": "./dist/releaseArtifact.js",
80
+ "default": "./dist/releaseArtifact.js"
81
+ },
82
+ "./digitalocean-ingress": {
83
+ "types": "./dist/digitaloceanIngress.d.ts",
84
+ "import": "./dist/digitaloceanIngress.js",
85
+ "default": "./dist/digitaloceanIngress.js"
86
+ },
72
87
  "./gcp": {
73
88
  "types": "./dist/gcp.d.ts",
74
89
  "import": "./dist/gcp.js",
75
90
  "default": "./dist/gcp.js"
76
91
  },
92
+ "./gcp-ingress": {
93
+ "types": "./dist/gcpIngress.d.ts",
94
+ "import": "./dist/gcpIngress.js",
95
+ "default": "./dist/gcpIngress.js"
96
+ },
77
97
  "./hetzner": {
78
98
  "types": "./dist/hetzner.d.ts",
79
99
  "import": "./dist/hetzner.js",