@infracraft/pulumi 1.21.0 → 1.22.1

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.
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
3
  const require_git_guard = require('../git-guard.cjs');
4
- const require_sandbox = require('../sandbox-Bh7tQ1Jy.cjs');
4
+ const require_sandbox = require('../sandbox-BBnhmyM_.cjs');
5
5
  let _pulumi_pulumi = require("@pulumi/pulumi");
6
6
  _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
7
7
  let _pulumi_command = require("@pulumi/command");
@@ -38,7 +38,8 @@ function createDeployCommand(args, opts) {
38
38
  const cmd = new _pulumi_command.local.Command(args.name, {
39
39
  create,
40
40
  triggers: args.triggers,
41
- environment: args.environment
41
+ environment: args.environment,
42
+ stdin: args.stdin
42
43
  }, opts);
43
44
  return {
44
45
  command: cmd,
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.cjs","names":["isDeploySandbox","isGitGuard","SandboxMode","pulumi","buildSandboxScript","command"],"sources":["../../src/commands/deploy.ts"],"sourcesContent":["// src/commands/deploy.ts\nimport * as command from \"@pulumi/command\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { isGitGuard } from \"../git-guard\";\nimport { buildSandboxScript, isDeploySandbox, SandboxMode } from \"../sandbox\";\n\nexport interface CreateDeployCommandArgs {\n\t/** Resource name; the child command is `<name>` and the sandbox dir uses it. */\n\tname: string;\n\t/** Fully-formed platform deploy command (may be an Output, e.g. Railway). */\n\tcli: pulumi.Input<string>;\n\t/** Redeploy triggers (source/env hashes). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Upload-scoping excludes (applied only in stub mode). */\n\texcludePaths?: string[];\n\t/** Shell run in the working dir before `cli` (e.g. write railpack.json). */\n\tsetup?: string;\n\t/** Env passed to the command (secrets that survive preview; not the inlined ones). */\n\tenvironment?: Record<string, pulumi.Input<string>>;\n}\n\nexport interface CreateDeployCommandResult {\n\tcommand: command.local.Command;\n\t/** The deploy CLI's final stdout line (the production URL for Vercel/Fly). */\n\tdeploymentUrl: pulumi.Output<string>;\n}\n\n/** Reads a `dependsOn` opt into a flat array of resource instances. */\nexport function dependsOnList(\n\topts: pulumi.ComponentResourceOptions,\n): unknown[] {\n\tconst dep = opts.dependsOn;\n\n\tif (Array.isArray(dep)) {\n\t\treturn dep;\n\t}\n\n\treturn dep ? [dep] : [];\n}\n\n/**\n * Builds a sandboxed deploy command. Inspects `opts.dependsOn` by brand:\n * a DeploySandbox → isolate; a GitGuard → stub `.git`; GitGuard alone → throw.\n * The platform deploy resources call ONLY this; they never touch the sandbox.\n */\nexport function createDeployCommand(\n\targs: CreateDeployCommandArgs,\n\topts: pulumi.ComponentResourceOptions,\n): CreateDeployCommandResult {\n\tconst deps = dependsOnList(opts);\n\tconst sandbox = deps.some(isDeploySandbox);\n\tconst gitGuard = deps.some(isGitGuard);\n\n\tif (gitGuard && !sandbox) {\n\t\tthrow new Error(\n\t\t\t`[infracraft] ${args.name}: GitGuard has no effect without a DeploySandbox in dependsOn`,\n\t\t);\n\t}\n\n\tlet mode = SandboxMode.NONE;\n\n\tif (sandbox) {\n\t\tmode = gitGuard ? SandboxMode.STUB : SandboxMode.ORIGINAL;\n\t}\n\n\tconst env = pulumi.getStack();\n\n\tconst create = pulumi.output(args.cli).apply((cli) =>\n\t\tbuildSandboxScript({\n\t\t\tmode,\n\t\t\tappName: args.name,\n\t\t\tenv,\n\t\t\texcludePaths: args.excludePaths,\n\t\t\tsetup: args.setup,\n\t\t\tcli,\n\t\t}),\n\t);\n\n\tconst cmd = new command.local.Command(\n\t\targs.name,\n\t\t{ create, triggers: args.triggers, environment: args.environment },\n\t\topts,\n\t);\n\n\tconst deploymentUrl = cmd.stdout.apply(\n\t\t(out) => out.trim().split(\"\\n\").pop() ?? \"\",\n\t);\n\n\treturn { command: cmd, deploymentUrl };\n}\n"],"mappings":";;;;;;;;;;;AA6BA,SAAgB,cACf,MACY;CACZ,MAAM,MAAM,KAAK;CAEjB,IAAI,MAAM,QAAQ,GAAG,GACpB,OAAO;CAGR,OAAO,MAAM,CAAC,GAAG,IAAI,CAAC;AACvB;;;;;;AAOA,SAAgB,oBACf,MACA,MAC4B;CAC5B,MAAM,OAAO,cAAc,IAAI;CAC/B,MAAM,UAAU,KAAK,KAAKA,+BAAe;CACzC,MAAM,WAAW,KAAK,KAAKC,4BAAU;CAErC,IAAI,YAAY,CAAC,SAChB,MAAM,IAAI,MACT,gBAAgB,KAAK,KAAK,8DAC3B;CAGD,IAAI,OAAOC,4BAAY;CAEvB,IAAI,SACH,OAAO,WAAWA,4BAAY,OAAOA,4BAAY;CAGlD,MAAM,MAAMC,eAAO,SAAS;CAE5B,MAAM,SAASA,eAAO,OAAO,KAAK,GAAG,EAAE,OAAO,QAC7CC,mCAAmB;EAClB;EACA,SAAS,KAAK;EACd;EACA,cAAc,KAAK;EACnB,OAAO,KAAK;EACZ;CACD,CAAC,CACF;CAEA,MAAM,MAAM,IAAIC,gBAAQ,MAAM,QAC7B,KAAK,MACL;EAAE;EAAQ,UAAU,KAAK;EAAU,aAAa,KAAK;CAAY,GACjE,IACD;CAMA,OAAO;EAAE,SAAS;EAAK,eAJD,IAAI,OAAO,OAC/B,QAAQ,IAAI,KAAK,EAAE,MAAM,IAAI,EAAE,IAAI,KAAK,EAGP;CAAE;AACtC"}
1
+ {"version":3,"file":"deploy.cjs","names":["isDeploySandbox","isGitGuard","SandboxMode","pulumi","buildSandboxScript","command"],"sources":["../../src/commands/deploy.ts"],"sourcesContent":["// src/commands/deploy.ts\nimport * as command from \"@pulumi/command\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { isGitGuard } from \"../git-guard\";\nimport { buildSandboxScript, isDeploySandbox, SandboxMode } from \"../sandbox\";\n\nexport interface CreateDeployCommandArgs {\n\t/** Resource name; the child command is `<name>` and the sandbox dir uses it. */\n\tname: string;\n\t/** Fully-formed platform deploy command (may be an Output, e.g. Railway). */\n\tcli: pulumi.Input<string>;\n\t/** Redeploy triggers (source/env hashes). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Upload-scoping excludes (applied only in stub mode). */\n\texcludePaths?: string[];\n\t/** Shell run in the working dir before `cli` (e.g. write railpack.json). */\n\tsetup?: string;\n\t/** Env passed to the command (secrets that survive preview; not the inlined ones). */\n\tenvironment?: Record<string, pulumi.Input<string>>;\n\t/**\n\t * Piped to the command's standard input. The channel for secrets that are\n\t * unknown at preview (resource-output tokens): the `environment` map fails\n\t * preview on unknowns, and inlining into `cli` leaks the value in\n\t * pulumi-command's failure error, which Pulumi does not scrub.\n\t */\n\tstdin?: pulumi.Input<string>;\n}\n\nexport interface CreateDeployCommandResult {\n\tcommand: command.local.Command;\n\t/** The deploy CLI's final stdout line (the production URL for Vercel/Fly). */\n\tdeploymentUrl: pulumi.Output<string>;\n}\n\n/** Reads a `dependsOn` opt into a flat array of resource instances. */\nexport function dependsOnList(\n\topts: pulumi.ComponentResourceOptions,\n): unknown[] {\n\tconst dep = opts.dependsOn;\n\n\tif (Array.isArray(dep)) {\n\t\treturn dep;\n\t}\n\n\treturn dep ? [dep] : [];\n}\n\n/**\n * Builds a sandboxed deploy command. Inspects `opts.dependsOn` by brand:\n * a DeploySandbox → isolate; a GitGuard → stub `.git`; GitGuard alone → throw.\n * The platform deploy resources call ONLY this; they never touch the sandbox.\n */\nexport function createDeployCommand(\n\targs: CreateDeployCommandArgs,\n\topts: pulumi.ComponentResourceOptions,\n): CreateDeployCommandResult {\n\tconst deps = dependsOnList(opts);\n\tconst sandbox = deps.some(isDeploySandbox);\n\tconst gitGuard = deps.some(isGitGuard);\n\n\tif (gitGuard && !sandbox) {\n\t\tthrow new Error(\n\t\t\t`[infracraft] ${args.name}: GitGuard has no effect without a DeploySandbox in dependsOn`,\n\t\t);\n\t}\n\n\tlet mode = SandboxMode.NONE;\n\n\tif (sandbox) {\n\t\tmode = gitGuard ? SandboxMode.STUB : SandboxMode.ORIGINAL;\n\t}\n\n\tconst env = pulumi.getStack();\n\n\tconst create = pulumi.output(args.cli).apply((cli) =>\n\t\tbuildSandboxScript({\n\t\t\tmode,\n\t\t\tappName: args.name,\n\t\t\tenv,\n\t\t\texcludePaths: args.excludePaths,\n\t\t\tsetup: args.setup,\n\t\t\tcli,\n\t\t}),\n\t);\n\n\tconst cmd = new command.local.Command(\n\t\targs.name,\n\t\t{\n\t\t\tcreate,\n\t\t\ttriggers: args.triggers,\n\t\t\tenvironment: args.environment,\n\t\t\tstdin: args.stdin,\n\t\t},\n\t\topts,\n\t);\n\n\tconst deploymentUrl = cmd.stdout.apply(\n\t\t(out) => out.trim().split(\"\\n\").pop() ?? \"\",\n\t);\n\n\treturn { command: cmd, deploymentUrl };\n}\n"],"mappings":";;;;;;;;;;;AAoCA,SAAgB,cACf,MACY;CACZ,MAAM,MAAM,KAAK;CAEjB,IAAI,MAAM,QAAQ,GAAG,GACpB,OAAO;CAGR,OAAO,MAAM,CAAC,GAAG,IAAI,CAAC;AACvB;;;;;;AAOA,SAAgB,oBACf,MACA,MAC4B;CAC5B,MAAM,OAAO,cAAc,IAAI;CAC/B,MAAM,UAAU,KAAK,KAAKA,+BAAe;CACzC,MAAM,WAAW,KAAK,KAAKC,4BAAU;CAErC,IAAI,YAAY,CAAC,SAChB,MAAM,IAAI,MACT,gBAAgB,KAAK,KAAK,8DAC3B;CAGD,IAAI,OAAOC,4BAAY;CAEvB,IAAI,SACH,OAAO,WAAWA,4BAAY,OAAOA,4BAAY;CAGlD,MAAM,MAAMC,eAAO,SAAS;CAE5B,MAAM,SAASA,eAAO,OAAO,KAAK,GAAG,EAAE,OAAO,QAC7CC,mCAAmB;EAClB;EACA,SAAS,KAAK;EACd;EACA,cAAc,KAAK;EACnB,OAAO,KAAK;EACZ;CACD,CAAC,CACF;CAEA,MAAM,MAAM,IAAIC,gBAAQ,MAAM,QAC7B,KAAK,MACL;EACC;EACA,UAAU,KAAK;EACf,aAAa,KAAK;EAClB,OAAO,KAAK;CACb,GACA,IACD;CAMA,OAAO;EAAE,SAAS;EAAK,eAJD,IAAI,OAAO,OAC/B,QAAQ,IAAI,KAAK,EAAE,MAAM,IAAI,EAAE,IAAI,KAAK,EAGP;CAAE;AACtC"}
@@ -16,6 +16,13 @@ interface CreateDeployCommandArgs {
16
16
  setup?: string;
17
17
  /** Env passed to the command (secrets that survive preview; not the inlined ones). */
18
18
  environment?: Record<string, pulumi.Input<string>>;
19
+ /**
20
+ * Piped to the command's standard input. The channel for secrets that are
21
+ * unknown at preview (resource-output tokens): the `environment` map fails
22
+ * preview on unknowns, and inlining into `cli` leaks the value in
23
+ * pulumi-command's failure error, which Pulumi does not scrub.
24
+ */
25
+ stdin?: pulumi.Input<string>;
19
26
  }
20
27
  interface CreateDeployCommandResult {
21
28
  command: command.local.Command;
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.cts","names":[],"sources":["../../src/commands/deploy.ts"],"mappings":";;;;;UAOiB,uBAAA;;EAEhB,IAAA;EAFgB;EAIhB,GAAA,EAAK,MAAA,CAAO,KAAA;;EAEZ,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAAP;EAEvB,YAAA;EAI6B;EAF7B,KAAA;EAEoB;EAApB,WAAA,GAAc,MAAA,SAAe,MAAA,CAAO,KAAA;AAAA;AAAA,UAGpB,yBAAA;EAChB,OAAA,EAAS,OAAA,CAAQ,KAAA,CAAM,OAAA;EAZX;EAcZ,aAAA,EAAe,MAAA,CAAO,MAAM;AAAA;;iBAIb,aAAA,CACf,IAAA,EAAM,MAAA,CAAO,wBAAwB;;;;;;iBAgBtB,mBAAA,CACf,IAAA,EAAM,uBAAA,EACN,IAAA,EAAM,MAAA,CAAO,wBAAA,GACX,yBAAA"}
1
+ {"version":3,"file":"deploy.d.cts","names":[],"sources":["../../src/commands/deploy.ts"],"mappings":";;;;;UAOiB,uBAAA;;EAEhB,IAAA;EAFgB;EAIhB,GAAA,EAAK,MAAA,CAAO,KAAA;;EAEZ,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAAP;EAEvB,YAAA;EAI6B;EAF7B,KAAA;EASQ;EAPR,WAAA,GAAc,MAAA,SAAe,MAAA,CAAO,KAAA;EAOhB;;;;;;EAApB,KAAA,GAAQ,MAAA,CAAO,KAAA;AAAA;AAAA,UAGC,yBAAA;EAChB,OAAA,EAAS,OAAA,CAAQ,KAAA,CAAM,OAAA;EAfvB;EAiBA,aAAA,EAAe,MAAA,CAAO,MAAM;AAAA;;iBAIb,aAAA,CACf,IAAA,EAAM,MAAA,CAAO,wBAAwB;;;;;;iBAgBtB,mBAAA,CACf,IAAA,EAAM,uBAAA,EACN,IAAA,EAAM,MAAA,CAAO,wBAAA,GACX,yBAAA"}
@@ -16,6 +16,13 @@ interface CreateDeployCommandArgs {
16
16
  setup?: string;
17
17
  /** Env passed to the command (secrets that survive preview; not the inlined ones). */
18
18
  environment?: Record<string, pulumi.Input<string>>;
19
+ /**
20
+ * Piped to the command's standard input. The channel for secrets that are
21
+ * unknown at preview (resource-output tokens): the `environment` map fails
22
+ * preview on unknowns, and inlining into `cli` leaks the value in
23
+ * pulumi-command's failure error, which Pulumi does not scrub.
24
+ */
25
+ stdin?: pulumi.Input<string>;
19
26
  }
20
27
  interface CreateDeployCommandResult {
21
28
  command: command.local.Command;
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.mts","names":[],"sources":["../../src/commands/deploy.ts"],"mappings":";;;;;UAOiB,uBAAA;;EAEhB,IAAA;EAFgB;EAIhB,GAAA,EAAK,MAAA,CAAO,KAAA;;EAEZ,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAAP;EAEvB,YAAA;EAI6B;EAF7B,KAAA;EAEoB;EAApB,WAAA,GAAc,MAAA,SAAe,MAAA,CAAO,KAAA;AAAA;AAAA,UAGpB,yBAAA;EAChB,OAAA,EAAS,OAAA,CAAQ,KAAA,CAAM,OAAA;EAZX;EAcZ,aAAA,EAAe,MAAA,CAAO,MAAM;AAAA;;iBAIb,aAAA,CACf,IAAA,EAAM,MAAA,CAAO,wBAAwB;;;;;;iBAgBtB,mBAAA,CACf,IAAA,EAAM,uBAAA,EACN,IAAA,EAAM,MAAA,CAAO,wBAAA,GACX,yBAAA"}
1
+ {"version":3,"file":"deploy.d.mts","names":[],"sources":["../../src/commands/deploy.ts"],"mappings":";;;;;UAOiB,uBAAA;;EAEhB,IAAA;EAFgB;EAIhB,GAAA,EAAK,MAAA,CAAO,KAAA;;EAEZ,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAAP;EAEvB,YAAA;EAI6B;EAF7B,KAAA;EASQ;EAPR,WAAA,GAAc,MAAA,SAAe,MAAA,CAAO,KAAA;EAOhB;;;;;;EAApB,KAAA,GAAQ,MAAA,CAAO,KAAA;AAAA;AAAA,UAGC,yBAAA;EAChB,OAAA,EAAS,OAAA,CAAQ,KAAA,CAAM,OAAA;EAfvB;EAiBA,aAAA,EAAe,MAAA,CAAO,MAAM;AAAA;;iBAIb,aAAA,CACf,IAAA,EAAM,MAAA,CAAO,wBAAwB;;;;;;iBAgBtB,mBAAA,CACf,IAAA,EAAM,uBAAA,EACN,IAAA,EAAM,MAAA,CAAO,wBAAA,GACX,yBAAA"}
@@ -1,6 +1,6 @@
1
1
  import { t as __name } from "../chunk-OPjESj5l.mjs";
2
2
  import { isGitGuard } from "../git-guard.mjs";
3
- import { a as buildSandboxScript, n as isDeploySandbox, r as SandboxMode } from "../sandbox-8xQPhxw7.mjs";
3
+ import { a as buildSandboxScript, n as isDeploySandbox, r as SandboxMode } from "../sandbox-BQtqMu-5.mjs";
4
4
  import * as pulumi from "@pulumi/pulumi";
5
5
  import * as command from "@pulumi/command";
6
6
 
@@ -35,7 +35,8 @@ function createDeployCommand(args, opts) {
35
35
  const cmd = new command.local.Command(args.name, {
36
36
  create,
37
37
  triggers: args.triggers,
38
- environment: args.environment
38
+ environment: args.environment,
39
+ stdin: args.stdin
39
40
  }, opts);
40
41
  return {
41
42
  command: cmd,
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.mjs","names":[],"sources":["../../src/commands/deploy.ts"],"sourcesContent":["// src/commands/deploy.ts\nimport * as command from \"@pulumi/command\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { isGitGuard } from \"../git-guard\";\nimport { buildSandboxScript, isDeploySandbox, SandboxMode } from \"../sandbox\";\n\nexport interface CreateDeployCommandArgs {\n\t/** Resource name; the child command is `<name>` and the sandbox dir uses it. */\n\tname: string;\n\t/** Fully-formed platform deploy command (may be an Output, e.g. Railway). */\n\tcli: pulumi.Input<string>;\n\t/** Redeploy triggers (source/env hashes). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Upload-scoping excludes (applied only in stub mode). */\n\texcludePaths?: string[];\n\t/** Shell run in the working dir before `cli` (e.g. write railpack.json). */\n\tsetup?: string;\n\t/** Env passed to the command (secrets that survive preview; not the inlined ones). */\n\tenvironment?: Record<string, pulumi.Input<string>>;\n}\n\nexport interface CreateDeployCommandResult {\n\tcommand: command.local.Command;\n\t/** The deploy CLI's final stdout line (the production URL for Vercel/Fly). */\n\tdeploymentUrl: pulumi.Output<string>;\n}\n\n/** Reads a `dependsOn` opt into a flat array of resource instances. */\nexport function dependsOnList(\n\topts: pulumi.ComponentResourceOptions,\n): unknown[] {\n\tconst dep = opts.dependsOn;\n\n\tif (Array.isArray(dep)) {\n\t\treturn dep;\n\t}\n\n\treturn dep ? [dep] : [];\n}\n\n/**\n * Builds a sandboxed deploy command. Inspects `opts.dependsOn` by brand:\n * a DeploySandbox → isolate; a GitGuard → stub `.git`; GitGuard alone → throw.\n * The platform deploy resources call ONLY this; they never touch the sandbox.\n */\nexport function createDeployCommand(\n\targs: CreateDeployCommandArgs,\n\topts: pulumi.ComponentResourceOptions,\n): CreateDeployCommandResult {\n\tconst deps = dependsOnList(opts);\n\tconst sandbox = deps.some(isDeploySandbox);\n\tconst gitGuard = deps.some(isGitGuard);\n\n\tif (gitGuard && !sandbox) {\n\t\tthrow new Error(\n\t\t\t`[infracraft] ${args.name}: GitGuard has no effect without a DeploySandbox in dependsOn`,\n\t\t);\n\t}\n\n\tlet mode = SandboxMode.NONE;\n\n\tif (sandbox) {\n\t\tmode = gitGuard ? SandboxMode.STUB : SandboxMode.ORIGINAL;\n\t}\n\n\tconst env = pulumi.getStack();\n\n\tconst create = pulumi.output(args.cli).apply((cli) =>\n\t\tbuildSandboxScript({\n\t\t\tmode,\n\t\t\tappName: args.name,\n\t\t\tenv,\n\t\t\texcludePaths: args.excludePaths,\n\t\t\tsetup: args.setup,\n\t\t\tcli,\n\t\t}),\n\t);\n\n\tconst cmd = new command.local.Command(\n\t\targs.name,\n\t\t{ create, triggers: args.triggers, environment: args.environment },\n\t\topts,\n\t);\n\n\tconst deploymentUrl = cmd.stdout.apply(\n\t\t(out) => out.trim().split(\"\\n\").pop() ?? \"\",\n\t);\n\n\treturn { command: cmd, deploymentUrl };\n}\n"],"mappings":";;;;;;;;AA6BA,SAAgB,cACf,MACY;CACZ,MAAM,MAAM,KAAK;CAEjB,IAAI,MAAM,QAAQ,GAAG,GACpB,OAAO;CAGR,OAAO,MAAM,CAAC,GAAG,IAAI,CAAC;AACvB;;;;;;AAOA,SAAgB,oBACf,MACA,MAC4B;CAC5B,MAAM,OAAO,cAAc,IAAI;CAC/B,MAAM,UAAU,KAAK,KAAK,eAAe;CACzC,MAAM,WAAW,KAAK,KAAK,UAAU;CAErC,IAAI,YAAY,CAAC,SAChB,MAAM,IAAI,MACT,gBAAgB,KAAK,KAAK,8DAC3B;CAGD,IAAI,OAAO,YAAY;CAEvB,IAAI,SACH,OAAO,WAAW,YAAY,OAAO,YAAY;CAGlD,MAAM,MAAM,OAAO,SAAS;CAE5B,MAAM,SAAS,OAAO,OAAO,KAAK,GAAG,EAAE,OAAO,QAC7C,mBAAmB;EAClB;EACA,SAAS,KAAK;EACd;EACA,cAAc,KAAK;EACnB,OAAO,KAAK;EACZ;CACD,CAAC,CACF;CAEA,MAAM,MAAM,IAAI,QAAQ,MAAM,QAC7B,KAAK,MACL;EAAE;EAAQ,UAAU,KAAK;EAAU,aAAa,KAAK;CAAY,GACjE,IACD;CAMA,OAAO;EAAE,SAAS;EAAK,eAJD,IAAI,OAAO,OAC/B,QAAQ,IAAI,KAAK,EAAE,MAAM,IAAI,EAAE,IAAI,KAAK,EAGP;CAAE;AACtC"}
1
+ {"version":3,"file":"deploy.mjs","names":[],"sources":["../../src/commands/deploy.ts"],"sourcesContent":["// src/commands/deploy.ts\nimport * as command from \"@pulumi/command\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { isGitGuard } from \"../git-guard\";\nimport { buildSandboxScript, isDeploySandbox, SandboxMode } from \"../sandbox\";\n\nexport interface CreateDeployCommandArgs {\n\t/** Resource name; the child command is `<name>` and the sandbox dir uses it. */\n\tname: string;\n\t/** Fully-formed platform deploy command (may be an Output, e.g. Railway). */\n\tcli: pulumi.Input<string>;\n\t/** Redeploy triggers (source/env hashes). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Upload-scoping excludes (applied only in stub mode). */\n\texcludePaths?: string[];\n\t/** Shell run in the working dir before `cli` (e.g. write railpack.json). */\n\tsetup?: string;\n\t/** Env passed to the command (secrets that survive preview; not the inlined ones). */\n\tenvironment?: Record<string, pulumi.Input<string>>;\n\t/**\n\t * Piped to the command's standard input. The channel for secrets that are\n\t * unknown at preview (resource-output tokens): the `environment` map fails\n\t * preview on unknowns, and inlining into `cli` leaks the value in\n\t * pulumi-command's failure error, which Pulumi does not scrub.\n\t */\n\tstdin?: pulumi.Input<string>;\n}\n\nexport interface CreateDeployCommandResult {\n\tcommand: command.local.Command;\n\t/** The deploy CLI's final stdout line (the production URL for Vercel/Fly). */\n\tdeploymentUrl: pulumi.Output<string>;\n}\n\n/** Reads a `dependsOn` opt into a flat array of resource instances. */\nexport function dependsOnList(\n\topts: pulumi.ComponentResourceOptions,\n): unknown[] {\n\tconst dep = opts.dependsOn;\n\n\tif (Array.isArray(dep)) {\n\t\treturn dep;\n\t}\n\n\treturn dep ? [dep] : [];\n}\n\n/**\n * Builds a sandboxed deploy command. Inspects `opts.dependsOn` by brand:\n * a DeploySandbox → isolate; a GitGuard → stub `.git`; GitGuard alone → throw.\n * The platform deploy resources call ONLY this; they never touch the sandbox.\n */\nexport function createDeployCommand(\n\targs: CreateDeployCommandArgs,\n\topts: pulumi.ComponentResourceOptions,\n): CreateDeployCommandResult {\n\tconst deps = dependsOnList(opts);\n\tconst sandbox = deps.some(isDeploySandbox);\n\tconst gitGuard = deps.some(isGitGuard);\n\n\tif (gitGuard && !sandbox) {\n\t\tthrow new Error(\n\t\t\t`[infracraft] ${args.name}: GitGuard has no effect without a DeploySandbox in dependsOn`,\n\t\t);\n\t}\n\n\tlet mode = SandboxMode.NONE;\n\n\tif (sandbox) {\n\t\tmode = gitGuard ? SandboxMode.STUB : SandboxMode.ORIGINAL;\n\t}\n\n\tconst env = pulumi.getStack();\n\n\tconst create = pulumi.output(args.cli).apply((cli) =>\n\t\tbuildSandboxScript({\n\t\t\tmode,\n\t\t\tappName: args.name,\n\t\t\tenv,\n\t\t\texcludePaths: args.excludePaths,\n\t\t\tsetup: args.setup,\n\t\t\tcli,\n\t\t}),\n\t);\n\n\tconst cmd = new command.local.Command(\n\t\targs.name,\n\t\t{\n\t\t\tcreate,\n\t\t\ttriggers: args.triggers,\n\t\t\tenvironment: args.environment,\n\t\t\tstdin: args.stdin,\n\t\t},\n\t\topts,\n\t);\n\n\tconst deploymentUrl = cmd.stdout.apply(\n\t\t(out) => out.trim().split(\"\\n\").pop() ?? \"\",\n\t);\n\n\treturn { command: cmd, deploymentUrl };\n}\n"],"mappings":";;;;;;;;AAoCA,SAAgB,cACf,MACY;CACZ,MAAM,MAAM,KAAK;CAEjB,IAAI,MAAM,QAAQ,GAAG,GACpB,OAAO;CAGR,OAAO,MAAM,CAAC,GAAG,IAAI,CAAC;AACvB;;;;;;AAOA,SAAgB,oBACf,MACA,MAC4B;CAC5B,MAAM,OAAO,cAAc,IAAI;CAC/B,MAAM,UAAU,KAAK,KAAK,eAAe;CACzC,MAAM,WAAW,KAAK,KAAK,UAAU;CAErC,IAAI,YAAY,CAAC,SAChB,MAAM,IAAI,MACT,gBAAgB,KAAK,KAAK,8DAC3B;CAGD,IAAI,OAAO,YAAY;CAEvB,IAAI,SACH,OAAO,WAAW,YAAY,OAAO,YAAY;CAGlD,MAAM,MAAM,OAAO,SAAS;CAE5B,MAAM,SAAS,OAAO,OAAO,KAAK,GAAG,EAAE,OAAO,QAC7C,mBAAmB;EAClB;EACA,SAAS,KAAK;EACd;EACA,cAAc,KAAK;EACnB,OAAO,KAAK;EACZ;CACD,CAAC,CACF;CAEA,MAAM,MAAM,IAAI,QAAQ,MAAM,QAC7B,KAAK,MACL;EACC;EACA,UAAU,KAAK;EACf,aAAa,KAAK;EAClB,OAAO,KAAK;CACb,GACA,IACD;CAMA,OAAO;EAAE,SAAS;EAAK,eAJD,IAAI,OAAO,OAC/B,QAAQ,IAAI,KAAK,EAAE,MAAM,IAAI,EAAE,IAAI,KAAK,EAGP;CAAE;AACtC"}
@@ -27,14 +27,15 @@ var RailwayDeploy = class extends _pulumi_pulumi.ComponentResource {
27
27
  constructor(name, args, opts) {
28
28
  const { provider, project, environment, service, projectToken, ...pulumiOpts } = opts;
29
29
  super("infracraft:railway:Deploy", name, {}, pulumiOpts);
30
- const cli = _pulumi_pulumi.interpolate`IC_SINCE=$(node -e "process.stdout.write(String(Date.now()))"); IC_UP_OUT=$(RAILWAY_TOKEN=${projectToken} railway up --detach --json --project ${project.id} --service ${service.id} --environment ${environment.id} 2>&1); IC_UP_EXIT=$?; printf '%s\\n' "$IC_UP_OUT"; if [ -n "$INFRACRAFT_SKIP_DEPLOY_WAIT" ]; then exit "$IC_UP_EXIT"; fi; IC_UP_OUT="$IC_UP_OUT" IC_UP_EXIT=$IC_UP_EXIT IC_TOK=${projectToken} IC_PROJ=${project.id} IC_ENV=${environment.id} IC_SVC=${service.id} IC_SINCE=$IC_SINCE node "${MONITOR_BIN}"`;
30
+ const cli = _pulumi_pulumi.interpolate`IFS= read -r IC_TOK || true; IC_SINCE=$(node -e "process.stdout.write(String(Date.now()))"); IC_UP_OUT=$(RAILWAY_TOKEN="$IC_TOK" railway up --detach --json --project ${project.id} --service ${service.id} --environment ${environment.id} 2>&1); IC_UP_EXIT=$?; printf '%s\\n' "$IC_UP_OUT"; if [ -n "$INFRACRAFT_SKIP_DEPLOY_WAIT" ]; then exit "$IC_UP_EXIT"; fi; IC_UP_OUT="$IC_UP_OUT" IC_UP_EXIT=$IC_UP_EXIT IC_TOK="$IC_TOK" IC_PROJ=${project.id} IC_ENV=${environment.id} IC_SVC=${service.id} IC_SINCE=$IC_SINCE node "${MONITOR_BIN}"`;
31
31
  const setup = args.railpackConfig ? `printf '%s' '${JSON.stringify(args.railpackConfig).replace(/'/g, "'\\''")}' > railpack.json` : void 0;
32
32
  const { deploymentUrl } = require_commands_deploy.createDeployCommand({
33
33
  name,
34
34
  cli,
35
35
  triggers: args.triggers,
36
36
  excludePaths: args.excludePaths,
37
- setup
37
+ setup,
38
+ stdin: projectToken
38
39
  }, {
39
40
  parent: this,
40
41
  ...pulumiOpts
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.cjs","names":["pulumi","createDeployCommand"],"sources":["../../src/railway/deploy.ts"],"sourcesContent":["// src/railway/deploy.ts (replace entire file)\nimport { fileURLToPath } from \"node:url\";\n\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand } from \"../commands/deploy\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\nimport { RailwayBuilder, type RailwayService } from \"./service\";\n\nexport interface RailwayDeployConfig {\n\tbuilder?: RailwayBuilder;\n\tstartCommand?: string;\n\tpreDeployCommand?: string;\n}\n\nexport interface RailwayDeployArgs {\n\t/** Redeploy triggers (e.g. source hashes, env hashes). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Paths excluded from the upload when running with `DeploySandbox` + `GitGuard`. */\n\texcludePaths?: string[];\n\t/** Railpack configuration written to `railpack.json` before deploy. */\n\trailpackConfig?: Record<string, unknown>;\n}\n\ntype RailwayDeployOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\tprovider: RailwayProvider;\n\tproject: RailwayProject;\n\tenvironment: RailwayEnvironment;\n\tservice: RailwayService;\n\t/** Environment-scoped Railway deploy token (provision via RailwayProjectToken). */\n\tprojectToken: pulumi.Input<string>;\n};\n\n/**\n * Absolute path to the runnable deploy monitor, resolved next to this module in `dist`.\n * `railway up` only uploads + triggers; this bin makes the Railway GraphQL API — not the\n * CLI's exit code — the source of truth for pass/fail, and dumps build + deploy logs on\n * failure. Its logic lives in the unit-tested `deployment-monitor` module.\n */\nconst MONITOR_BIN = fileURLToPath(\n\tnew URL(\"./bin/monitor-deployment.mjs\", import.meta.url),\n);\n\n/**\n * Deploys a Railway service and waits for a terminal status. Isolation/git are the\n * seam's job (list a `DeploySandbox` and optionally a `GitGuard` in `dependsOn`).\n *\n * @example\n * ```typescript\n * new RailwayDeploy(\"mesh\", { triggers: [sourceHash], railpackConfig: { apt: [\"libatomic1\"] } },\n * { provider, project, environment, service, projectToken: token.token, dependsOn: [sandbox, gitGuard] });\n * ```\n */\nexport class RailwayDeploy extends pulumi.ComponentResource {\n\t/** The deploy CLI's final stdout line (Railway service URL when emitted). */\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayDeployArgs,\n\t\topts: RailwayDeployOptions,\n\t) {\n\t\tconst {\n\t\t\tprovider,\n\t\t\tproject,\n\t\t\tenvironment,\n\t\t\tservice,\n\t\t\tprojectToken,\n\t\t\t...pulumiOpts\n\t\t} = opts;\n\n\t\tsuper(\"infracraft:railway:Deploy\", name, {}, pulumiOpts);\n\n\t\t// `railway up --detach` uploads + triggers WITHOUT attaching to the build-log\n\t\t// stream — that long-lived stream is what intermittently times out and makes the\n\t\t// CLI exit non-zero even when the deploy actually succeeds. We capture its `--json`\n\t\t// output (for the exact deployment id) and exit code, re-emit it for visibility,\n\t\t// then hand off to the monitor bin which polls the Railway API to a terminal status.\n\t\t// The API — not the CLI exit code — decides pass/fail. Token is inlined (not via the\n\t\t// env map): an unknown secret in `environment` makes `pulumi preview` fail. IC_SINCE\n\t\t// is captured just before `railway up` as a createdAt fallback for id resolution.\n\t\tconst cli = pulumi.interpolate`IC_SINCE=$(node -e \"process.stdout.write(String(Date.now()))\"); IC_UP_OUT=$(RAILWAY_TOKEN=${projectToken} railway up --detach --json --project ${project.id} --service ${service.id} --environment ${environment.id} 2>&1); IC_UP_EXIT=$?; printf '%s\\\\n' \"$IC_UP_OUT\"; if [ -n \"$INFRACRAFT_SKIP_DEPLOY_WAIT\" ]; then exit \"$IC_UP_EXIT\"; fi; IC_UP_OUT=\"$IC_UP_OUT\" IC_UP_EXIT=$IC_UP_EXIT IC_TOK=${projectToken} IC_PROJ=${project.id} IC_ENV=${environment.id} IC_SVC=${service.id} IC_SINCE=$IC_SINCE node \"${MONITOR_BIN}\"`;\n\n\t\t// `printf '%s'` (not a bare format string) so railpack values containing %\n\t\t// are literal; the JSON is single-quote-escaped the POSIX way (' -> '\\'').\n\t\tconst setup = args.railpackConfig\n\t\t\t? `printf '%s' '${JSON.stringify(args.railpackConfig).replace(/'/g, \"'\\\\''\")}' > railpack.json`\n\t\t\t: undefined;\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli,\n\t\t\t\ttriggers: args.triggers,\n\t\t\t\texcludePaths: args.excludePaths,\n\t\t\t\tsetup,\n\t\t\t},\n\t\t\t{ parent: this, ...pulumiOpts },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;AA4CA,MAAM,0CACL,IAAI,IAAI,6EAA+C,CACxD;;;;;;;;;;;AAYA,IAAa,gBAAb,cAAmCA,eAAO,kBAAkB;CAI3D,YACC,MACA,MACA,MACC;EACD,MAAM,EACL,UACA,SACA,aACA,SACA,cACA,GAAG,eACA;EAEJ,MAAM,6BAA6B,MAAM,CAAC,GAAG,UAAU;EAUvD,MAAM,MAAM,eAAO,WAAW,6FAA6F,aAAa,wCAAwC,QAAQ,GAAG,aAAa,QAAQ,GAAG,iBAAiB,YAAY,GAAG,kLAAkL,aAAa,WAAW,QAAQ,GAAG,UAAU,YAAY,GAAG,UAAU,QAAQ,GAAG,4BAA4B,YAAY;EAI9hB,MAAM,QAAQ,KAAK,iBAChB,gBAAgB,KAAK,UAAU,KAAK,cAAc,EAAE,QAAQ,MAAM,OAAO,EAAE,qBAC3E;EAEH,MAAM,EAAE,kBAAkBC,4CACzB;GACC;GACA;GACA,UAAU,KAAK;GACf,cAAc,KAAK;GACnB;EACD,GACA;GAAE,QAAQ;GAAM,GAAG;EAAW,CAC/B;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
1
+ {"version":3,"file":"deploy.cjs","names":["pulumi","createDeployCommand"],"sources":["../../src/railway/deploy.ts"],"sourcesContent":["// src/railway/deploy.ts (replace entire file)\nimport { fileURLToPath } from \"node:url\";\n\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand } from \"../commands/deploy\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\nimport { RailwayBuilder, type RailwayService } from \"./service\";\n\nexport interface RailwayDeployConfig {\n\tbuilder?: RailwayBuilder;\n\tstartCommand?: string;\n\tpreDeployCommand?: string;\n}\n\nexport interface RailwayDeployArgs {\n\t/** Redeploy triggers (e.g. source hashes, env hashes). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Paths excluded from the upload when running with `DeploySandbox` + `GitGuard`. */\n\texcludePaths?: string[];\n\t/** Railpack configuration written to `railpack.json` before deploy. */\n\trailpackConfig?: Record<string, unknown>;\n}\n\ntype RailwayDeployOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\tprovider: RailwayProvider;\n\tproject: RailwayProject;\n\tenvironment: RailwayEnvironment;\n\tservice: RailwayService;\n\t/** Environment-scoped Railway deploy token (provision via RailwayProjectToken). */\n\tprojectToken: pulumi.Input<string>;\n};\n\n/**\n * Absolute path to the runnable deploy monitor, resolved next to this module in `dist`.\n * `railway up` only uploads + triggers; this bin makes the Railway GraphQL API — not the\n * CLI's exit code — the source of truth for pass/fail, and dumps build + deploy logs on\n * failure. Its logic lives in the unit-tested `deployment-monitor` module.\n */\nconst MONITOR_BIN = fileURLToPath(\n\tnew URL(\"./bin/monitor-deployment.mjs\", import.meta.url),\n);\n\n/**\n * Deploys a Railway service and waits for a terminal status. Isolation/git are the\n * seam's job (list a `DeploySandbox` and optionally a `GitGuard` in `dependsOn`).\n *\n * @example\n * ```typescript\n * new RailwayDeploy(\"mesh\", { triggers: [sourceHash], railpackConfig: { apt: [\"libatomic1\"] } },\n * { provider, project, environment, service, projectToken: token.token, dependsOn: [sandbox, gitGuard] });\n * ```\n */\nexport class RailwayDeploy extends pulumi.ComponentResource {\n\t/** The deploy CLI's final stdout line (Railway service URL when emitted). */\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayDeployArgs,\n\t\topts: RailwayDeployOptions,\n\t) {\n\t\tconst {\n\t\t\tprovider,\n\t\t\tproject,\n\t\t\tenvironment,\n\t\t\tservice,\n\t\t\tprojectToken,\n\t\t\t...pulumiOpts\n\t\t} = opts;\n\n\t\tsuper(\"infracraft:railway:Deploy\", name, {}, pulumiOpts);\n\n\t\t// `railway up --detach` uploads + triggers WITHOUT attaching to the build-log\n\t\t// stream — that long-lived stream is what intermittently times out and makes the\n\t\t// CLI exit non-zero even when the deploy actually succeeds. We capture its `--json`\n\t\t// output (for the exact deployment id) and exit code, re-emit it for visibility,\n\t\t// then hand off to the monitor bin which polls the Railway API to a terminal status.\n\t\t// The API — not the CLI exit code — decides pass/fail.\n\t\t//\n\t\t// The token travels via the command's STDIN, never in the script text: on failure\n\t\t// pulumi-command embeds the executed command verbatim in its error message, and\n\t\t// Pulumi does not scrub secrets from provider diagnostics — an inlined token prints\n\t\t// in plaintext exactly when a deploy fails. The `environment` map is no alternative:\n\t\t// an unknown secret there makes `pulumi preview` fail (the token is a resource\n\t\t// output, unknown on first preview). `|| true` keeps `set -e` alive when the stdin\n\t\t// payload has no trailing newline (read then exits 1 but still fills IC_TOK).\n\t\t// IC_SINCE is captured just before `railway up` as a createdAt fallback for id\n\t\t// resolution.\n\t\tconst cli = pulumi.interpolate`IFS= read -r IC_TOK || true; IC_SINCE=$(node -e \"process.stdout.write(String(Date.now()))\"); IC_UP_OUT=$(RAILWAY_TOKEN=\"$IC_TOK\" railway up --detach --json --project ${project.id} --service ${service.id} --environment ${environment.id} 2>&1); IC_UP_EXIT=$?; printf '%s\\\\n' \"$IC_UP_OUT\"; if [ -n \"$INFRACRAFT_SKIP_DEPLOY_WAIT\" ]; then exit \"$IC_UP_EXIT\"; fi; IC_UP_OUT=\"$IC_UP_OUT\" IC_UP_EXIT=$IC_UP_EXIT IC_TOK=\"$IC_TOK\" IC_PROJ=${project.id} IC_ENV=${environment.id} IC_SVC=${service.id} IC_SINCE=$IC_SINCE node \"${MONITOR_BIN}\"`;\n\n\t\t// `printf '%s'` (not a bare format string) so railpack values containing %\n\t\t// are literal; the JSON is single-quote-escaped the POSIX way (' -> '\\'').\n\t\tconst setup = args.railpackConfig\n\t\t\t? `printf '%s' '${JSON.stringify(args.railpackConfig).replace(/'/g, \"'\\\\''\")}' > railpack.json`\n\t\t\t: undefined;\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli,\n\t\t\t\ttriggers: args.triggers,\n\t\t\t\texcludePaths: args.excludePaths,\n\t\t\t\tsetup,\n\t\t\t\tstdin: projectToken,\n\t\t\t},\n\t\t\t{ parent: this, ...pulumiOpts },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;AA4CA,MAAM,0CACL,IAAI,IAAI,6EAA+C,CACxD;;;;;;;;;;;AAYA,IAAa,gBAAb,cAAmCA,eAAO,kBAAkB;CAI3D,YACC,MACA,MACA,MACC;EACD,MAAM,EACL,UACA,SACA,aACA,SACA,cACA,GAAG,eACA;EAEJ,MAAM,6BAA6B,MAAM,CAAC,GAAG,UAAU;EAkBvD,MAAM,MAAM,eAAO,WAAW,yKAAyK,QAAQ,GAAG,aAAa,QAAQ,GAAG,iBAAiB,YAAY,GAAG,oMAAoM,QAAQ,GAAG,UAAU,YAAY,GAAG,UAAU,QAAQ,GAAG,4BAA4B,YAAY;EAI/iB,MAAM,QAAQ,KAAK,iBAChB,gBAAgB,KAAK,UAAU,KAAK,cAAc,EAAE,QAAQ,MAAM,OAAO,EAAE,qBAC3E;EAEH,MAAM,EAAE,kBAAkBC,4CACzB;GACC;GACA;GACA,UAAU,KAAK;GACf,cAAc,KAAK;GACnB;GACA,OAAO;EACR,GACA;GAAE,QAAQ;GAAM,GAAG;EAAW,CAC/B;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
@@ -25,14 +25,15 @@ var RailwayDeploy = class extends pulumi.ComponentResource {
25
25
  constructor(name, args, opts) {
26
26
  const { provider, project, environment, service, projectToken, ...pulumiOpts } = opts;
27
27
  super("infracraft:railway:Deploy", name, {}, pulumiOpts);
28
- const cli = pulumi.interpolate`IC_SINCE=$(node -e "process.stdout.write(String(Date.now()))"); IC_UP_OUT=$(RAILWAY_TOKEN=${projectToken} railway up --detach --json --project ${project.id} --service ${service.id} --environment ${environment.id} 2>&1); IC_UP_EXIT=$?; printf '%s\\n' "$IC_UP_OUT"; if [ -n "$INFRACRAFT_SKIP_DEPLOY_WAIT" ]; then exit "$IC_UP_EXIT"; fi; IC_UP_OUT="$IC_UP_OUT" IC_UP_EXIT=$IC_UP_EXIT IC_TOK=${projectToken} IC_PROJ=${project.id} IC_ENV=${environment.id} IC_SVC=${service.id} IC_SINCE=$IC_SINCE node "${MONITOR_BIN}"`;
28
+ const cli = pulumi.interpolate`IFS= read -r IC_TOK || true; IC_SINCE=$(node -e "process.stdout.write(String(Date.now()))"); IC_UP_OUT=$(RAILWAY_TOKEN="$IC_TOK" railway up --detach --json --project ${project.id} --service ${service.id} --environment ${environment.id} 2>&1); IC_UP_EXIT=$?; printf '%s\\n' "$IC_UP_OUT"; if [ -n "$INFRACRAFT_SKIP_DEPLOY_WAIT" ]; then exit "$IC_UP_EXIT"; fi; IC_UP_OUT="$IC_UP_OUT" IC_UP_EXIT=$IC_UP_EXIT IC_TOK="$IC_TOK" IC_PROJ=${project.id} IC_ENV=${environment.id} IC_SVC=${service.id} IC_SINCE=$IC_SINCE node "${MONITOR_BIN}"`;
29
29
  const setup = args.railpackConfig ? `printf '%s' '${JSON.stringify(args.railpackConfig).replace(/'/g, "'\\''")}' > railpack.json` : void 0;
30
30
  const { deploymentUrl } = createDeployCommand({
31
31
  name,
32
32
  cli,
33
33
  triggers: args.triggers,
34
34
  excludePaths: args.excludePaths,
35
- setup
35
+ setup,
36
+ stdin: projectToken
36
37
  }, {
37
38
  parent: this,
38
39
  ...pulumiOpts
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.mjs","names":[],"sources":["../../src/railway/deploy.ts"],"sourcesContent":["// src/railway/deploy.ts (replace entire file)\nimport { fileURLToPath } from \"node:url\";\n\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand } from \"../commands/deploy\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\nimport { RailwayBuilder, type RailwayService } from \"./service\";\n\nexport interface RailwayDeployConfig {\n\tbuilder?: RailwayBuilder;\n\tstartCommand?: string;\n\tpreDeployCommand?: string;\n}\n\nexport interface RailwayDeployArgs {\n\t/** Redeploy triggers (e.g. source hashes, env hashes). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Paths excluded from the upload when running with `DeploySandbox` + `GitGuard`. */\n\texcludePaths?: string[];\n\t/** Railpack configuration written to `railpack.json` before deploy. */\n\trailpackConfig?: Record<string, unknown>;\n}\n\ntype RailwayDeployOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\tprovider: RailwayProvider;\n\tproject: RailwayProject;\n\tenvironment: RailwayEnvironment;\n\tservice: RailwayService;\n\t/** Environment-scoped Railway deploy token (provision via RailwayProjectToken). */\n\tprojectToken: pulumi.Input<string>;\n};\n\n/**\n * Absolute path to the runnable deploy monitor, resolved next to this module in `dist`.\n * `railway up` only uploads + triggers; this bin makes the Railway GraphQL API — not the\n * CLI's exit code — the source of truth for pass/fail, and dumps build + deploy logs on\n * failure. Its logic lives in the unit-tested `deployment-monitor` module.\n */\nconst MONITOR_BIN = fileURLToPath(\n\tnew URL(\"./bin/monitor-deployment.mjs\", import.meta.url),\n);\n\n/**\n * Deploys a Railway service and waits for a terminal status. Isolation/git are the\n * seam's job (list a `DeploySandbox` and optionally a `GitGuard` in `dependsOn`).\n *\n * @example\n * ```typescript\n * new RailwayDeploy(\"mesh\", { triggers: [sourceHash], railpackConfig: { apt: [\"libatomic1\"] } },\n * { provider, project, environment, service, projectToken: token.token, dependsOn: [sandbox, gitGuard] });\n * ```\n */\nexport class RailwayDeploy extends pulumi.ComponentResource {\n\t/** The deploy CLI's final stdout line (Railway service URL when emitted). */\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayDeployArgs,\n\t\topts: RailwayDeployOptions,\n\t) {\n\t\tconst {\n\t\t\tprovider,\n\t\t\tproject,\n\t\t\tenvironment,\n\t\t\tservice,\n\t\t\tprojectToken,\n\t\t\t...pulumiOpts\n\t\t} = opts;\n\n\t\tsuper(\"infracraft:railway:Deploy\", name, {}, pulumiOpts);\n\n\t\t// `railway up --detach` uploads + triggers WITHOUT attaching to the build-log\n\t\t// stream — that long-lived stream is what intermittently times out and makes the\n\t\t// CLI exit non-zero even when the deploy actually succeeds. We capture its `--json`\n\t\t// output (for the exact deployment id) and exit code, re-emit it for visibility,\n\t\t// then hand off to the monitor bin which polls the Railway API to a terminal status.\n\t\t// The API — not the CLI exit code — decides pass/fail. Token is inlined (not via the\n\t\t// env map): an unknown secret in `environment` makes `pulumi preview` fail. IC_SINCE\n\t\t// is captured just before `railway up` as a createdAt fallback for id resolution.\n\t\tconst cli = pulumi.interpolate`IC_SINCE=$(node -e \"process.stdout.write(String(Date.now()))\"); IC_UP_OUT=$(RAILWAY_TOKEN=${projectToken} railway up --detach --json --project ${project.id} --service ${service.id} --environment ${environment.id} 2>&1); IC_UP_EXIT=$?; printf '%s\\\\n' \"$IC_UP_OUT\"; if [ -n \"$INFRACRAFT_SKIP_DEPLOY_WAIT\" ]; then exit \"$IC_UP_EXIT\"; fi; IC_UP_OUT=\"$IC_UP_OUT\" IC_UP_EXIT=$IC_UP_EXIT IC_TOK=${projectToken} IC_PROJ=${project.id} IC_ENV=${environment.id} IC_SVC=${service.id} IC_SINCE=$IC_SINCE node \"${MONITOR_BIN}\"`;\n\n\t\t// `printf '%s'` (not a bare format string) so railpack values containing %\n\t\t// are literal; the JSON is single-quote-escaped the POSIX way (' -> '\\'').\n\t\tconst setup = args.railpackConfig\n\t\t\t? `printf '%s' '${JSON.stringify(args.railpackConfig).replace(/'/g, \"'\\\\''\")}' > railpack.json`\n\t\t\t: undefined;\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli,\n\t\t\t\ttriggers: args.triggers,\n\t\t\t\texcludePaths: args.excludePaths,\n\t\t\t\tsetup,\n\t\t\t},\n\t\t\t{ parent: this, ...pulumiOpts },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;AA4CA,MAAM,cAAc,cACnB,IAAI,IAAI,gCAAgC,OAAO,KAAK,GAAG,CACxD;;;;;;;;;;;AAYA,IAAa,gBAAb,cAAmC,OAAO,kBAAkB;CAI3D,YACC,MACA,MACA,MACC;EACD,MAAM,EACL,UACA,SACA,aACA,SACA,cACA,GAAG,eACA;EAEJ,MAAM,6BAA6B,MAAM,CAAC,GAAG,UAAU;EAUvD,MAAM,MAAM,OAAO,WAAW,6FAA6F,aAAa,wCAAwC,QAAQ,GAAG,aAAa,QAAQ,GAAG,iBAAiB,YAAY,GAAG,kLAAkL,aAAa,WAAW,QAAQ,GAAG,UAAU,YAAY,GAAG,UAAU,QAAQ,GAAG,4BAA4B,YAAY;EAI9hB,MAAM,QAAQ,KAAK,iBAChB,gBAAgB,KAAK,UAAU,KAAK,cAAc,EAAE,QAAQ,MAAM,OAAO,EAAE,qBAC3E;EAEH,MAAM,EAAE,kBAAkB,oBACzB;GACC;GACA;GACA,UAAU,KAAK;GACf,cAAc,KAAK;GACnB;EACD,GACA;GAAE,QAAQ;GAAM,GAAG;EAAW,CAC/B;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
1
+ {"version":3,"file":"deploy.mjs","names":[],"sources":["../../src/railway/deploy.ts"],"sourcesContent":["// src/railway/deploy.ts (replace entire file)\nimport { fileURLToPath } from \"node:url\";\n\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand } from \"../commands/deploy\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\nimport { RailwayBuilder, type RailwayService } from \"./service\";\n\nexport interface RailwayDeployConfig {\n\tbuilder?: RailwayBuilder;\n\tstartCommand?: string;\n\tpreDeployCommand?: string;\n}\n\nexport interface RailwayDeployArgs {\n\t/** Redeploy triggers (e.g. source hashes, env hashes). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Paths excluded from the upload when running with `DeploySandbox` + `GitGuard`. */\n\texcludePaths?: string[];\n\t/** Railpack configuration written to `railpack.json` before deploy. */\n\trailpackConfig?: Record<string, unknown>;\n}\n\ntype RailwayDeployOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\tprovider: RailwayProvider;\n\tproject: RailwayProject;\n\tenvironment: RailwayEnvironment;\n\tservice: RailwayService;\n\t/** Environment-scoped Railway deploy token (provision via RailwayProjectToken). */\n\tprojectToken: pulumi.Input<string>;\n};\n\n/**\n * Absolute path to the runnable deploy monitor, resolved next to this module in `dist`.\n * `railway up` only uploads + triggers; this bin makes the Railway GraphQL API — not the\n * CLI's exit code — the source of truth for pass/fail, and dumps build + deploy logs on\n * failure. Its logic lives in the unit-tested `deployment-monitor` module.\n */\nconst MONITOR_BIN = fileURLToPath(\n\tnew URL(\"./bin/monitor-deployment.mjs\", import.meta.url),\n);\n\n/**\n * Deploys a Railway service and waits for a terminal status. Isolation/git are the\n * seam's job (list a `DeploySandbox` and optionally a `GitGuard` in `dependsOn`).\n *\n * @example\n * ```typescript\n * new RailwayDeploy(\"mesh\", { triggers: [sourceHash], railpackConfig: { apt: [\"libatomic1\"] } },\n * { provider, project, environment, service, projectToken: token.token, dependsOn: [sandbox, gitGuard] });\n * ```\n */\nexport class RailwayDeploy extends pulumi.ComponentResource {\n\t/** The deploy CLI's final stdout line (Railway service URL when emitted). */\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayDeployArgs,\n\t\topts: RailwayDeployOptions,\n\t) {\n\t\tconst {\n\t\t\tprovider,\n\t\t\tproject,\n\t\t\tenvironment,\n\t\t\tservice,\n\t\t\tprojectToken,\n\t\t\t...pulumiOpts\n\t\t} = opts;\n\n\t\tsuper(\"infracraft:railway:Deploy\", name, {}, pulumiOpts);\n\n\t\t// `railway up --detach` uploads + triggers WITHOUT attaching to the build-log\n\t\t// stream — that long-lived stream is what intermittently times out and makes the\n\t\t// CLI exit non-zero even when the deploy actually succeeds. We capture its `--json`\n\t\t// output (for the exact deployment id) and exit code, re-emit it for visibility,\n\t\t// then hand off to the monitor bin which polls the Railway API to a terminal status.\n\t\t// The API — not the CLI exit code — decides pass/fail.\n\t\t//\n\t\t// The token travels via the command's STDIN, never in the script text: on failure\n\t\t// pulumi-command embeds the executed command verbatim in its error message, and\n\t\t// Pulumi does not scrub secrets from provider diagnostics — an inlined token prints\n\t\t// in plaintext exactly when a deploy fails. The `environment` map is no alternative:\n\t\t// an unknown secret there makes `pulumi preview` fail (the token is a resource\n\t\t// output, unknown on first preview). `|| true` keeps `set -e` alive when the stdin\n\t\t// payload has no trailing newline (read then exits 1 but still fills IC_TOK).\n\t\t// IC_SINCE is captured just before `railway up` as a createdAt fallback for id\n\t\t// resolution.\n\t\tconst cli = pulumi.interpolate`IFS= read -r IC_TOK || true; IC_SINCE=$(node -e \"process.stdout.write(String(Date.now()))\"); IC_UP_OUT=$(RAILWAY_TOKEN=\"$IC_TOK\" railway up --detach --json --project ${project.id} --service ${service.id} --environment ${environment.id} 2>&1); IC_UP_EXIT=$?; printf '%s\\\\n' \"$IC_UP_OUT\"; if [ -n \"$INFRACRAFT_SKIP_DEPLOY_WAIT\" ]; then exit \"$IC_UP_EXIT\"; fi; IC_UP_OUT=\"$IC_UP_OUT\" IC_UP_EXIT=$IC_UP_EXIT IC_TOK=\"$IC_TOK\" IC_PROJ=${project.id} IC_ENV=${environment.id} IC_SVC=${service.id} IC_SINCE=$IC_SINCE node \"${MONITOR_BIN}\"`;\n\n\t\t// `printf '%s'` (not a bare format string) so railpack values containing %\n\t\t// are literal; the JSON is single-quote-escaped the POSIX way (' -> '\\'').\n\t\tconst setup = args.railpackConfig\n\t\t\t? `printf '%s' '${JSON.stringify(args.railpackConfig).replace(/'/g, \"'\\\\''\")}' > railpack.json`\n\t\t\t: undefined;\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli,\n\t\t\t\ttriggers: args.triggers,\n\t\t\t\texcludePaths: args.excludePaths,\n\t\t\t\tsetup,\n\t\t\t\tstdin: projectToken,\n\t\t\t},\n\t\t\t{ parent: this, ...pulumiOpts },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;AA4CA,MAAM,cAAc,cACnB,IAAI,IAAI,gCAAgC,OAAO,KAAK,GAAG,CACxD;;;;;;;;;;;AAYA,IAAa,gBAAb,cAAmC,OAAO,kBAAkB;CAI3D,YACC,MACA,MACA,MACC;EACD,MAAM,EACL,UACA,SACA,aACA,SACA,cACA,GAAG,eACA;EAEJ,MAAM,6BAA6B,MAAM,CAAC,GAAG,UAAU;EAkBvD,MAAM,MAAM,OAAO,WAAW,yKAAyK,QAAQ,GAAG,aAAa,QAAQ,GAAG,iBAAiB,YAAY,GAAG,oMAAoM,QAAQ,GAAG,UAAU,YAAY,GAAG,UAAU,QAAQ,GAAG,4BAA4B,YAAY;EAI/iB,MAAM,QAAQ,KAAK,iBAChB,gBAAgB,KAAK,UAAU,KAAK,cAAc,EAAE,QAAQ,MAAM,OAAO,EAAE,qBAC3E;EAEH,MAAM,EAAE,kBAAkB,oBACzB;GACC;GACA;GACA,UAAU,KAAK;GACf,cAAc,KAAK;GACnB;GACA,OAAO;EACR,GACA;GAAE,QAAQ;GAAM,GAAG;EAAW,CAC/B;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
@@ -13,17 +13,18 @@ function escapeAwkRegex(path) {
13
13
  }
14
14
  /**
15
15
  * Builds the shell filter applied to the `git ls-files` list before
16
- * `rsync --files-from`. For `apps/<x>` it drops the app's files but keeps
17
- * `apps/<x>/package.json` (the monorepo workspace graph needs it during build);
18
- * any other entry drops the path and its subtree. Returns `cat` (passthrough)
16
+ * `rsync --files-from`. Every excluded entry drops the path and its subtree
17
+ * but keeps the entry's own `package.json`: an excluded directory may be a
18
+ * workspace member, and the package manager fails the whole install when the
19
+ * root manifest names a workspace whose manifest is missing from the copy
20
+ * (a kept manifest for a non-member is inert). Returns `cat` (passthrough)
19
21
  * when nothing is excluded. Uses awk so it is portable across macOS and Linux.
20
22
  */
21
23
  function buildSandboxFileFilter(excludePaths = []) {
22
24
  if (excludePaths.length === 0) return "cat";
23
25
  return `awk '${excludePaths.map((entry) => {
24
26
  const escaped = escapeAwkRegex(entry);
25
- if (entry.startsWith("apps/")) return `!(/^${escaped}\\// && !/^${escaped}\\/package\\.json$/)`;
26
- return `!/^${escaped}(\\/|$)/`;
27
+ return `!(/^${escaped}(\\/|$)/ && !/^${escaped}\\/package\\.json$/)`;
27
28
  }).join(" && ")}'`;
28
29
  }
29
30
  /** Root of the per-deploy sandbox tree. The DeploySandbox resource GCs this. */
@@ -156,4 +157,4 @@ Object.defineProperty(exports, 'isDeploySandbox', {
156
157
  return isDeploySandbox;
157
158
  }
158
159
  });
159
- //# sourceMappingURL=sandbox-Bh7tQ1Jy.cjs.map
160
+ //# sourceMappingURL=sandbox-BBnhmyM_.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-BBnhmyM_.cjs","names":["fs","path","pulumi"],"sources":["../../sandbox/dist/index.mjs","../src/sandbox.ts"],"sourcesContent":["import { t as __name } from \"./chunk-OPjESj5l.mjs\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n//#region src/index.ts\n/** Escapes a path literal for embedding inside an awk ERE. */\nfunction escapeAwkRegex(path) {\n\treturn path.replace(/[.[\\]{}()*+?^$|\\\\/]/g, \"\\\\$&\");\n}\n/**\n* Builds the shell filter applied to the `git ls-files` list before\n* `rsync --files-from`. Every excluded entry drops the path and its subtree\n* but keeps the entry's own `package.json`: an excluded directory may be a\n* workspace member, and the package manager fails the whole install when the\n* root manifest names a workspace whose manifest is missing from the copy\n* (a kept manifest for a non-member is inert). Returns `cat` (passthrough)\n* when nothing is excluded. Uses awk so it is portable across macOS and Linux.\n*/\nfunction buildSandboxFileFilter(excludePaths = []) {\n\tif (excludePaths.length === 0) return \"cat\";\n\treturn `awk '${excludePaths.map((entry) => {\n\t\tconst escaped = escapeAwkRegex(entry);\n\t\treturn `!(/^${escaped}(\\\\/|$)/ && !/^${escaped}\\\\/package\\\\.json$/)`;\n\t}).join(\" && \")}'`;\n}\n/** Root of the per-deploy sandbox tree. The DeploySandbox resource GCs this. */\nconst SANDBOX_ROOT = \"/tmp/infracraft\";\n/**\n* How a deploy's working copy is isolated. A closed set, so it is an enum rather\n* than a pair of booleans: the seam derives it from `dependsOn` presence\n* (DeploySandbox / GitGuard), which makes the impossible \"git-guard without a\n* sandbox\" state unrepresentable here.\n*/\nlet SandboxMode = /* @__PURE__ */ function(SandboxMode) {\n\t/** No isolation — run the CLI in the live working tree. */\n\tSandboxMode[\"NONE\"] = \"NONE\";\n\t/** Isolated copy carrying the repo's REAL `.git` (real commit/author exposed). */\n\tSandboxMode[\"ORIGINAL\"] = \"ORIGINAL\";\n\t/** Isolated copy with a metadata-free stub `.git` (no commit SHA / author). */\n\tSandboxMode[\"STUB\"] = \"STUB\";\n\treturn SandboxMode;\n}({});\n/**\n* Builds the shell for a deploy's `command.local.Command.create`. See\n* docs/superpowers/specs/2026-06-05-deploy-sandbox-design.md for the modes.\n*\n* Hardening: every script runs under POSIX `set -e` so any failed step aborts\n* instead of silently letting the platform CLI run against partial state.\n* `pipefail` is intentionally NOT used — the command runs under `/bin/sh`, which\n* may be `dash` (pipefail is a bash/ksh/zsh extension). To still catch a failing\n* `git ls-files` (a pipe would mask it, since an `rsync` of an empty list exits\n* 0), the copy is staged through intermediate list files rather than a pipe. The\n* `REPO` and `SANDBOX` command substitutions are explicitly guarded with clear\n* error messages.\n*/\nfunction buildSandboxScript(options) {\n\tconst { mode, appName, env, excludePaths, setup, cli } = options;\n\tconst head = `set -e; REPO=$(git rev-parse --show-toplevel) || { echo \"[infracraft] not inside a git repository\" >&2; exit 1; }`;\n\tconst runSetupAndCli = [setup, cli].filter(Boolean).join(\"; \");\n\tif (mode === \"NONE\") return `${head}; cd \"$REPO\"; ${runSetupAndCli}`;\n\tconst makeSandbox = [\n\t\t`PROJECT=$(basename \"$REPO\")`,\n\t\t`SANDBOX=$(mktemp -d \"${SANDBOX_ROOT}/$PROJECT-${env ? `${env}-${appName}` : appName}.XXXXXX\") || { echo \"[infracraft] mktemp failed\" >&2; exit 1; }`,\n\t\t`trap 'rm -rf \"$SANDBOX\"' EXIT`\n\t].join(\"; \");\n\tconst copy = [\n\t\t`git -C \"$REPO\" ls-files > \"$SANDBOX/.ic-ls\"`,\n\t\t`${mode === \"STUB\" ? buildSandboxFileFilter(excludePaths) : \"cat\"} \"$SANDBOX/.ic-ls\" > \"$SANDBOX/.ic-lsf\"`,\n\t\t`rsync -a --files-from=\"$SANDBOX/.ic-lsf\" \"$REPO\"/ \"$SANDBOX\"/`,\n\t\t`rm -f \"$SANDBOX/.ic-ls\" \"$SANDBOX/.ic-lsf\"`\n\t].join(\"; \");\n\tif (mode === \"STUB\") return `${head}; ${makeSandbox}; ${copy}; cd \"\\$SANDBOX\"; git init -q && git add -A; ${runSetupAndCli}`;\n\treturn `${head}; ${makeSandbox}; ${copy}; cp -c -R \"\\$REPO/.git\" \"\\$SANDBOX/.git\" 2>/dev/null || cp -R \"\\$REPO/.git\" \"\\$SANDBOX/.git\"; cd \"$SANDBOX\"; ${runSetupAndCli}`;\n}\n/** Sweep sandboxes orphaned by a hard-killed run (older than this). Kept well\n* above the worst-case deploy time (~30 min) but low enough that a SIGKILL\n* orphan does not linger for a day accumulating in /tmp. */\nconst STALE_SANDBOX_MS = 10800 * 1e3;\n/** mkdir the workspace root and GC stale sandboxes (best-effort). Sandboxes are\n* flat under the root (`/tmp/infracraft/<project>-<env>-<app>.XXXX`), so this\n* sweeps any entry older than the stale threshold. */\nfunction prepareSandboxWorkspace() {\n\tfs.mkdirSync(SANDBOX_ROOT, { recursive: true });\n\tlet entries = [];\n\ttry {\n\t\tentries = fs.readdirSync(SANDBOX_ROOT);\n\t} catch {\n\t\treturn;\n\t}\n\tconst now = Date.now();\n\tfor (const entry of entries) {\n\t\tconst full = path.join(SANDBOX_ROOT, entry);\n\t\ttry {\n\t\t\tif (now - fs.statSync(full).mtimeMs > STALE_SANDBOX_MS) fs.rmSync(full, {\n\t\t\t\trecursive: true,\n\t\t\t\tforce: true\n\t\t\t});\n\t\t} catch {}\n\t}\n}\n\n//#endregion\nexport { SandboxMode, buildSandboxFileFilter, buildSandboxScript, prepareSandboxWorkspace };\n//# sourceMappingURL=index.mjs.map","import { prepareSandboxWorkspace } from \"@infracraft/sandbox\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\nexport type { SandboxScriptOptions } from \"@infracraft/sandbox\";\nexport {\n\tbuildSandboxFileFilter,\n\tbuildSandboxScript,\n\tSandboxMode,\n} from \"@infracraft/sandbox\";\n\n/** Cross-bundle brand: `instanceof` is unreliable when the seam and the resource\n * come from different built entries, so the seam detects sandboxes by this. */\nconst DEPLOY_SANDBOX_BRAND = Symbol.for(\"@infracraft/pulumi/DeploySandbox\");\n\n/**\n * Isolation marker + workspace lifecycle. Listing it in a deploy's `dependsOn`\n * makes that deploy run in an isolated `/tmp/infracraft` copy. Carries no config;\n * the repo root is derived at runtime by the deploy command.\n */\nexport class DeploySandbox extends pulumi.ComponentResource {\n\tconstructor(name: string, opts?: pulumi.ComponentResourceOptions) {\n\t\tsuper(\"infracraft:sandbox:DeploySandbox\", name, {}, opts);\n\n\t\t(this as Record<symbol, unknown>)[DEPLOY_SANDBOX_BRAND] = true;\n\n\t\tif (!pulumi.runtime.isDryRun()) {\n\t\t\tprepareSandboxWorkspace();\n\t\t}\n\n\t\tthis.registerOutputs({});\n\t}\n}\n\n/** Bundle-safe check for a `DeploySandbox` in a `dependsOn` array. */\nexport function isDeploySandbox(value: unknown): value is DeploySandbox {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t(value as Record<symbol, unknown>)[DEPLOY_SANDBOX_BRAND] === true\n\t);\n}\n"],"mappings":";;;;;;;;;;AAMA,SAAS,eAAe,MAAM;CAC7B,OAAO,KAAK,QAAQ,wBAAwB,MAAM;AACnD;;;;;;;;;;AAUA,SAAS,uBAAuB,eAAe,CAAC,GAAG;CAClD,IAAI,aAAa,WAAW,GAAG,OAAO;CACtC,OAAO,QAAQ,aAAa,KAAK,UAAU;EAC1C,MAAM,UAAU,eAAe,KAAK;EACpC,OAAO,OAAO,QAAQ,iBAAiB,QAAQ;CAChD,CAAC,EAAE,KAAK,MAAM,EAAE;AACjB;;AAEA,MAAM,eAAe;;;;;;;AAOrB,IAAI,cAA8B,yBAAS,aAAa;;CAEvD,YAAY,UAAU;;CAEtB,YAAY,cAAc;;CAE1B,YAAY,UAAU;CACtB,OAAO;AACR,EAAE,CAAC,CAAC;;;;;;;;;;;;;;AAcJ,SAAS,mBAAmB,SAAS;CACpC,MAAM,EAAE,MAAM,SAAS,KAAK,cAAc,OAAO,QAAQ;CACzD,MAAM,OAAO;CACb,MAAM,iBAAiB,CAAC,OAAO,GAAG,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;CAC7D,IAAI,SAAS,QAAQ,OAAO,GAAG,KAAK,gBAAgB;CACpD,MAAM,cAAc;EACnB;EACA,wBAAwB,aAAa,YAAY,MAAM,GAAG,IAAI,GAAG,YAAY,QAAQ;EACrF;CACD,EAAE,KAAK,IAAI;CACX,MAAM,OAAO;EACZ;EACA,GAAG,SAAS,SAAS,uBAAuB,YAAY,IAAI,MAAM;EAClE;EACA;CACD,EAAE,KAAK,IAAI;CACX,IAAI,SAAS,QAAQ,OAAO,GAAG,KAAK,IAAI,YAAY,IAAI,KAAK,+CAA+C;CAC5G,OAAO,GAAG,KAAK,IAAI,YAAY,IAAI,KAAK,gHAAgH;AACzJ;;;;AAIA,MAAM,mBAAmB,QAAQ;;;;AAIjC,SAAS,0BAA0B;CAClC,QAAG,UAAU,cAAc,EAAE,WAAW,KAAK,CAAC;CAC9C,IAAI,UAAU,CAAC;CACf,IAAI;EACH,UAAUA,QAAG,YAAY,YAAY;CACtC,QAAQ;EACP;CACD;CACA,MAAM,MAAM,KAAK,IAAI;CACrB,KAAK,MAAM,SAAS,SAAS;EAC5B,MAAM,OAAOC,UAAK,KAAK,cAAc,KAAK;EAC1C,IAAI;GACH,IAAI,MAAMD,QAAG,SAAS,IAAI,EAAE,UAAU,kBAAkB,QAAG,OAAO,MAAM;IACvE,WAAW;IACX,OAAO;GACR,CAAC;EACF,QAAQ,CAAC;CACV;AACD;;;;;;ACvFA,MAAM,uBAAuB,OAAO,IAAI,kCAAkC;;;;;;AAO1E,IAAa,gBAAb,cAAmCE,eAAO,kBAAkB;CAC3D,YAAY,MAAc,MAAwC;EACjE,MAAM,oCAAoC,MAAM,CAAC,GAAG,IAAI;EAExD,AAAC,KAAiC,wBAAwB;EAE1D,IAAI,CAACA,eAAO,QAAQ,SAAS,GAC5B,wBAAwB;EAGzB,KAAK,gBAAgB,CAAC,CAAC;CACxB;AACD;;AAGA,SAAgB,gBAAgB,OAAwC;CACvE,OACC,OAAO,UAAU,YACjB,UAAU,QACT,MAAkC,0BAA0B;AAE/D"}
@@ -10,17 +10,18 @@ function escapeAwkRegex(path) {
10
10
  }
11
11
  /**
12
12
  * Builds the shell filter applied to the `git ls-files` list before
13
- * `rsync --files-from`. For `apps/<x>` it drops the app's files but keeps
14
- * `apps/<x>/package.json` (the monorepo workspace graph needs it during build);
15
- * any other entry drops the path and its subtree. Returns `cat` (passthrough)
13
+ * `rsync --files-from`. Every excluded entry drops the path and its subtree
14
+ * but keeps the entry's own `package.json`: an excluded directory may be a
15
+ * workspace member, and the package manager fails the whole install when the
16
+ * root manifest names a workspace whose manifest is missing from the copy
17
+ * (a kept manifest for a non-member is inert). Returns `cat` (passthrough)
16
18
  * when nothing is excluded. Uses awk so it is portable across macOS and Linux.
17
19
  */
18
20
  function buildSandboxFileFilter(excludePaths = []) {
19
21
  if (excludePaths.length === 0) return "cat";
20
22
  return `awk '${excludePaths.map((entry) => {
21
23
  const escaped = escapeAwkRegex(entry);
22
- if (entry.startsWith("apps/")) return `!(/^${escaped}\\// && !/^${escaped}\\/package\\.json$/)`;
23
- return `!/^${escaped}(\\/|$)/`;
24
+ return `!(/^${escaped}(\\/|$)/ && !/^${escaped}\\/package\\.json$/)`;
24
25
  }).join(" && ")}'`;
25
26
  }
26
27
  /** Root of the per-deploy sandbox tree. The DeploySandbox resource GCs this. */
@@ -124,4 +125,4 @@ function isDeploySandbox(value) {
124
125
 
125
126
  //#endregion
126
127
  export { buildSandboxScript as a, buildSandboxFileFilter as i, isDeploySandbox as n, SandboxMode as r, DeploySandbox as t };
127
- //# sourceMappingURL=sandbox-8xQPhxw7.mjs.map
128
+ //# sourceMappingURL=sandbox-BQtqMu-5.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-BQtqMu-5.mjs","names":[],"sources":["../../sandbox/dist/index.mjs","../src/sandbox.ts"],"sourcesContent":["import { t as __name } from \"./chunk-OPjESj5l.mjs\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n//#region src/index.ts\n/** Escapes a path literal for embedding inside an awk ERE. */\nfunction escapeAwkRegex(path) {\n\treturn path.replace(/[.[\\]{}()*+?^$|\\\\/]/g, \"\\\\$&\");\n}\n/**\n* Builds the shell filter applied to the `git ls-files` list before\n* `rsync --files-from`. Every excluded entry drops the path and its subtree\n* but keeps the entry's own `package.json`: an excluded directory may be a\n* workspace member, and the package manager fails the whole install when the\n* root manifest names a workspace whose manifest is missing from the copy\n* (a kept manifest for a non-member is inert). Returns `cat` (passthrough)\n* when nothing is excluded. Uses awk so it is portable across macOS and Linux.\n*/\nfunction buildSandboxFileFilter(excludePaths = []) {\n\tif (excludePaths.length === 0) return \"cat\";\n\treturn `awk '${excludePaths.map((entry) => {\n\t\tconst escaped = escapeAwkRegex(entry);\n\t\treturn `!(/^${escaped}(\\\\/|$)/ && !/^${escaped}\\\\/package\\\\.json$/)`;\n\t}).join(\" && \")}'`;\n}\n/** Root of the per-deploy sandbox tree. The DeploySandbox resource GCs this. */\nconst SANDBOX_ROOT = \"/tmp/infracraft\";\n/**\n* How a deploy's working copy is isolated. A closed set, so it is an enum rather\n* than a pair of booleans: the seam derives it from `dependsOn` presence\n* (DeploySandbox / GitGuard), which makes the impossible \"git-guard without a\n* sandbox\" state unrepresentable here.\n*/\nlet SandboxMode = /* @__PURE__ */ function(SandboxMode) {\n\t/** No isolation — run the CLI in the live working tree. */\n\tSandboxMode[\"NONE\"] = \"NONE\";\n\t/** Isolated copy carrying the repo's REAL `.git` (real commit/author exposed). */\n\tSandboxMode[\"ORIGINAL\"] = \"ORIGINAL\";\n\t/** Isolated copy with a metadata-free stub `.git` (no commit SHA / author). */\n\tSandboxMode[\"STUB\"] = \"STUB\";\n\treturn SandboxMode;\n}({});\n/**\n* Builds the shell for a deploy's `command.local.Command.create`. See\n* docs/superpowers/specs/2026-06-05-deploy-sandbox-design.md for the modes.\n*\n* Hardening: every script runs under POSIX `set -e` so any failed step aborts\n* instead of silently letting the platform CLI run against partial state.\n* `pipefail` is intentionally NOT used — the command runs under `/bin/sh`, which\n* may be `dash` (pipefail is a bash/ksh/zsh extension). To still catch a failing\n* `git ls-files` (a pipe would mask it, since an `rsync` of an empty list exits\n* 0), the copy is staged through intermediate list files rather than a pipe. The\n* `REPO` and `SANDBOX` command substitutions are explicitly guarded with clear\n* error messages.\n*/\nfunction buildSandboxScript(options) {\n\tconst { mode, appName, env, excludePaths, setup, cli } = options;\n\tconst head = `set -e; REPO=$(git rev-parse --show-toplevel) || { echo \"[infracraft] not inside a git repository\" >&2; exit 1; }`;\n\tconst runSetupAndCli = [setup, cli].filter(Boolean).join(\"; \");\n\tif (mode === \"NONE\") return `${head}; cd \"$REPO\"; ${runSetupAndCli}`;\n\tconst makeSandbox = [\n\t\t`PROJECT=$(basename \"$REPO\")`,\n\t\t`SANDBOX=$(mktemp -d \"${SANDBOX_ROOT}/$PROJECT-${env ? `${env}-${appName}` : appName}.XXXXXX\") || { echo \"[infracraft] mktemp failed\" >&2; exit 1; }`,\n\t\t`trap 'rm -rf \"$SANDBOX\"' EXIT`\n\t].join(\"; \");\n\tconst copy = [\n\t\t`git -C \"$REPO\" ls-files > \"$SANDBOX/.ic-ls\"`,\n\t\t`${mode === \"STUB\" ? buildSandboxFileFilter(excludePaths) : \"cat\"} \"$SANDBOX/.ic-ls\" > \"$SANDBOX/.ic-lsf\"`,\n\t\t`rsync -a --files-from=\"$SANDBOX/.ic-lsf\" \"$REPO\"/ \"$SANDBOX\"/`,\n\t\t`rm -f \"$SANDBOX/.ic-ls\" \"$SANDBOX/.ic-lsf\"`\n\t].join(\"; \");\n\tif (mode === \"STUB\") return `${head}; ${makeSandbox}; ${copy}; cd \"\\$SANDBOX\"; git init -q && git add -A; ${runSetupAndCli}`;\n\treturn `${head}; ${makeSandbox}; ${copy}; cp -c -R \"\\$REPO/.git\" \"\\$SANDBOX/.git\" 2>/dev/null || cp -R \"\\$REPO/.git\" \"\\$SANDBOX/.git\"; cd \"$SANDBOX\"; ${runSetupAndCli}`;\n}\n/** Sweep sandboxes orphaned by a hard-killed run (older than this). Kept well\n* above the worst-case deploy time (~30 min) but low enough that a SIGKILL\n* orphan does not linger for a day accumulating in /tmp. */\nconst STALE_SANDBOX_MS = 10800 * 1e3;\n/** mkdir the workspace root and GC stale sandboxes (best-effort). Sandboxes are\n* flat under the root (`/tmp/infracraft/<project>-<env>-<app>.XXXX`), so this\n* sweeps any entry older than the stale threshold. */\nfunction prepareSandboxWorkspace() {\n\tfs.mkdirSync(SANDBOX_ROOT, { recursive: true });\n\tlet entries = [];\n\ttry {\n\t\tentries = fs.readdirSync(SANDBOX_ROOT);\n\t} catch {\n\t\treturn;\n\t}\n\tconst now = Date.now();\n\tfor (const entry of entries) {\n\t\tconst full = path.join(SANDBOX_ROOT, entry);\n\t\ttry {\n\t\t\tif (now - fs.statSync(full).mtimeMs > STALE_SANDBOX_MS) fs.rmSync(full, {\n\t\t\t\trecursive: true,\n\t\t\t\tforce: true\n\t\t\t});\n\t\t} catch {}\n\t}\n}\n\n//#endregion\nexport { SandboxMode, buildSandboxFileFilter, buildSandboxScript, prepareSandboxWorkspace };\n//# sourceMappingURL=index.mjs.map","import { prepareSandboxWorkspace } from \"@infracraft/sandbox\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\nexport type { SandboxScriptOptions } from \"@infracraft/sandbox\";\nexport {\n\tbuildSandboxFileFilter,\n\tbuildSandboxScript,\n\tSandboxMode,\n} from \"@infracraft/sandbox\";\n\n/** Cross-bundle brand: `instanceof` is unreliable when the seam and the resource\n * come from different built entries, so the seam detects sandboxes by this. */\nconst DEPLOY_SANDBOX_BRAND = Symbol.for(\"@infracraft/pulumi/DeploySandbox\");\n\n/**\n * Isolation marker + workspace lifecycle. Listing it in a deploy's `dependsOn`\n * makes that deploy run in an isolated `/tmp/infracraft` copy. Carries no config;\n * the repo root is derived at runtime by the deploy command.\n */\nexport class DeploySandbox extends pulumi.ComponentResource {\n\tconstructor(name: string, opts?: pulumi.ComponentResourceOptions) {\n\t\tsuper(\"infracraft:sandbox:DeploySandbox\", name, {}, opts);\n\n\t\t(this as Record<symbol, unknown>)[DEPLOY_SANDBOX_BRAND] = true;\n\n\t\tif (!pulumi.runtime.isDryRun()) {\n\t\t\tprepareSandboxWorkspace();\n\t\t}\n\n\t\tthis.registerOutputs({});\n\t}\n}\n\n/** Bundle-safe check for a `DeploySandbox` in a `dependsOn` array. */\nexport function isDeploySandbox(value: unknown): value is DeploySandbox {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t(value as Record<symbol, unknown>)[DEPLOY_SANDBOX_BRAND] === true\n\t);\n}\n"],"mappings":";;;;;;;AAMA,SAAS,eAAe,MAAM;CAC7B,OAAO,KAAK,QAAQ,wBAAwB,MAAM;AACnD;;;;;;;;;;AAUA,SAAS,uBAAuB,eAAe,CAAC,GAAG;CAClD,IAAI,aAAa,WAAW,GAAG,OAAO;CACtC,OAAO,QAAQ,aAAa,KAAK,UAAU;EAC1C,MAAM,UAAU,eAAe,KAAK;EACpC,OAAO,OAAO,QAAQ,iBAAiB,QAAQ;CAChD,CAAC,EAAE,KAAK,MAAM,EAAE;AACjB;;AAEA,MAAM,eAAe;;;;;;;AAOrB,IAAI,cAA8B,yBAAS,aAAa;;CAEvD,YAAY,UAAU;;CAEtB,YAAY,cAAc;;CAE1B,YAAY,UAAU;CACtB,OAAO;AACR,EAAE,CAAC,CAAC;;;;;;;;;;;;;;AAcJ,SAAS,mBAAmB,SAAS;CACpC,MAAM,EAAE,MAAM,SAAS,KAAK,cAAc,OAAO,QAAQ;CACzD,MAAM,OAAO;CACb,MAAM,iBAAiB,CAAC,OAAO,GAAG,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;CAC7D,IAAI,SAAS,QAAQ,OAAO,GAAG,KAAK,gBAAgB;CACpD,MAAM,cAAc;EACnB;EACA,wBAAwB,aAAa,YAAY,MAAM,GAAG,IAAI,GAAG,YAAY,QAAQ;EACrF;CACD,EAAE,KAAK,IAAI;CACX,MAAM,OAAO;EACZ;EACA,GAAG,SAAS,SAAS,uBAAuB,YAAY,IAAI,MAAM;EAClE;EACA;CACD,EAAE,KAAK,IAAI;CACX,IAAI,SAAS,QAAQ,OAAO,GAAG,KAAK,IAAI,YAAY,IAAI,KAAK,+CAA+C;CAC5G,OAAO,GAAG,KAAK,IAAI,YAAY,IAAI,KAAK,gHAAgH;AACzJ;;;;AAIA,MAAM,mBAAmB,QAAQ;;;;AAIjC,SAAS,0BAA0B;CAClC,GAAG,UAAU,cAAc,EAAE,WAAW,KAAK,CAAC;CAC9C,IAAI,UAAU,CAAC;CACf,IAAI;EACH,UAAU,GAAG,YAAY,YAAY;CACtC,QAAQ;EACP;CACD;CACA,MAAM,MAAM,KAAK,IAAI;CACrB,KAAK,MAAM,SAAS,SAAS;EAC5B,MAAM,OAAO,KAAK,KAAK,cAAc,KAAK;EAC1C,IAAI;GACH,IAAI,MAAM,GAAG,SAAS,IAAI,EAAE,UAAU,kBAAkB,GAAG,OAAO,MAAM;IACvE,WAAW;IACX,OAAO;GACR,CAAC;EACF,QAAQ,CAAC;CACV;AACD;;;;;;ACvFA,MAAM,uBAAuB,OAAO,IAAI,kCAAkC;;;;;;AAO1E,IAAa,gBAAb,cAAmC,OAAO,kBAAkB;CAC3D,YAAY,MAAc,MAAwC;EACjE,MAAM,oCAAoC,MAAM,CAAC,GAAG,IAAI;EAExD,AAAC,KAAiC,wBAAwB;EAE1D,IAAI,CAAC,OAAO,QAAQ,SAAS,GAC5B,wBAAwB;EAGzB,KAAK,gBAAgB,CAAC,CAAC;CACxB;AACD;;AAGA,SAAgB,gBAAgB,OAAwC;CACvE,OACC,OAAO,UAAU,YACjB,UAAU,QACT,MAAkC,0BAA0B;AAE/D"}
package/dist/sandbox.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_sandbox = require('./sandbox-Bh7tQ1Jy.cjs');
2
+ const require_sandbox = require('./sandbox-BBnhmyM_.cjs');
3
3
 
4
4
  exports.DeploySandbox = require_sandbox.DeploySandbox;
5
5
  exports.SandboxMode = require_sandbox.SandboxMode;
@@ -5,9 +5,11 @@ import * as pulumi from "@pulumi/pulumi";
5
5
  //#region src/index.d.ts
6
6
  /**
7
7
  * Builds the shell filter applied to the `git ls-files` list before
8
- * `rsync --files-from`. For `apps/<x>` it drops the app's files but keeps
9
- * `apps/<x>/package.json` (the monorepo workspace graph needs it during build);
10
- * any other entry drops the path and its subtree. Returns `cat` (passthrough)
8
+ * `rsync --files-from`. Every excluded entry drops the path and its subtree
9
+ * but keeps the entry's own `package.json`: an excluded directory may be a
10
+ * workspace member, and the package manager fails the whole install when the
11
+ * root manifest names a workspace whose manifest is missing from the copy
12
+ * (a kept manifest for a non-member is inert). Returns `cat` (passthrough)
11
13
  * when nothing is excluded. Uses awk so it is portable across macOS and Linux.
12
14
  */
13
15
  declare function buildSandboxFileFilter(excludePaths?: string[]): string;
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox.d.cts","names":["t","__name","buildSandboxFileFilter","excludePaths","SandboxMode","NONE","ORIGINAL","STUB","SandboxScriptOptions","mode","appName","env","setup","cli","buildSandboxScript","options","prepareSandboxWorkspace"],"sources":["../../sandbox/dist/index.d.mts","../src/sandbox.ts"],"mappings":";;;;;;AAAmD;;;;AAUY;AAAA;iBAA9CE,sBAAAA,CAAuBC,YAAuB;;;;;;;aAOlDC,WAAAA;EAMP;EAJJC,IAAAA;;EAEAC,QAAAA;EAMAG;EAJAF,IAAAA;AAAAA;AAAAA,UAEQC,oBAAAA;EASRL;EAPAM,IAAAA,EAAML,WAAW;EAWjBS;EATAH,OAAAA;EASG;AAAA;EANHC,GAAAA;;EAEAR,YAAAA;EAmB+D;EAjB/DS,KAAAA;;EAEAC,GAAAA;AAAAA;;;;;;;;;;;;AClB+D;AAcjE;iBDmBiBC,kBAAAA,CAAmBC,OAA6B,EAApBP,oBAAoB;;;;;;;AArDd;;;;cCmBtC,aAAA,SAAsB,MAAA,CAAO,iBAAiB;cAC9C,IAAA,UAAc,IAAA,GAAO,MAAA,CAAO,wBAAA;AAAA;;iBAczB,eAAA,CAAgB,KAAA,YAAiB,KAAA,IAAS,aAAa"}
1
+ {"version":3,"file":"sandbox.d.cts","names":["t","__name","buildSandboxFileFilter","excludePaths","SandboxMode","NONE","ORIGINAL","STUB","SandboxScriptOptions","mode","appName","env","setup","cli","buildSandboxScript","options","prepareSandboxWorkspace"],"sources":["../../sandbox/dist/index.d.mts","../src/sandbox.ts"],"mappings":";;;;;;AAAmD;;;;AAYY;AAAA;;;iBAA9CE,sBAAAA,CAAuBC,YAAuB;;;;;AAazD;AAAA;aANOC,WAAAA;;EAEXC,IAAAA;EAQAI;EANAH,QAAAA;EAQAI;EANAH,IAAAA;AAAAA;AAAAA,UAEQC,oBAAAA;EAaRK;EAXAJ,IAAAA,EAAML,WAAW;EAWd;EATHM,OAAAA;EAwBiC;;EArBjCC,GAAAA;EAqB+D;EAnB/DR,YAAAA;;EAEAS,KAAAA;ECnBF;EDqBEC,GAAAA;AAAAA;;;;;;;;;;ACpB+D;AAcjE;;;iBDqBiBC,kBAAAA,CAAmBC,OAA6B,EAApBP,oBAAoB;;;;;;;AAvDd;;;;cCmBtC,aAAA,SAAsB,MAAA,CAAO,iBAAiB;cAC9C,IAAA,UAAc,IAAA,GAAO,MAAA,CAAO,wBAAA;AAAA;;iBAczB,eAAA,CAAgB,KAAA,YAAiB,KAAA,IAAS,aAAa"}
@@ -5,9 +5,11 @@ import * as pulumi from "@pulumi/pulumi";
5
5
  //#region src/index.d.ts
6
6
  /**
7
7
  * Builds the shell filter applied to the `git ls-files` list before
8
- * `rsync --files-from`. For `apps/<x>` it drops the app's files but keeps
9
- * `apps/<x>/package.json` (the monorepo workspace graph needs it during build);
10
- * any other entry drops the path and its subtree. Returns `cat` (passthrough)
8
+ * `rsync --files-from`. Every excluded entry drops the path and its subtree
9
+ * but keeps the entry's own `package.json`: an excluded directory may be a
10
+ * workspace member, and the package manager fails the whole install when the
11
+ * root manifest names a workspace whose manifest is missing from the copy
12
+ * (a kept manifest for a non-member is inert). Returns `cat` (passthrough)
11
13
  * when nothing is excluded. Uses awk so it is portable across macOS and Linux.
12
14
  */
13
15
  declare function buildSandboxFileFilter(excludePaths?: string[]): string;
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox.d.mts","names":["t","__name","buildSandboxFileFilter","excludePaths","SandboxMode","NONE","ORIGINAL","STUB","SandboxScriptOptions","mode","appName","env","setup","cli","buildSandboxScript","options","prepareSandboxWorkspace"],"sources":["../../sandbox/dist/index.d.mts","../src/sandbox.ts"],"mappings":";;;;;;AAAmD;;;;AAUY;AAAA;iBAA9CE,sBAAAA,CAAuBC,YAAuB;;;;;;;aAOlDC,WAAAA;EAMP;EAJJC,IAAAA;;EAEAC,QAAAA;EAMAG;EAJAF,IAAAA;AAAAA;AAAAA,UAEQC,oBAAAA;EASRL;EAPAM,IAAAA,EAAML,WAAW;EAWjBS;EATAH,OAAAA;EASG;AAAA;EANHC,GAAAA;;EAEAR,YAAAA;EAmB+D;EAjB/DS,KAAAA;;EAEAC,GAAAA;AAAAA;;;;;;;;;;;;AClB+D;AAcjE;iBDmBiBC,kBAAAA,CAAmBC,OAA6B,EAApBP,oBAAoB;;;;;;;AArDd;;;;cCmBtC,aAAA,SAAsB,MAAA,CAAO,iBAAiB;cAC9C,IAAA,UAAc,IAAA,GAAO,MAAA,CAAO,wBAAA;AAAA;;iBAczB,eAAA,CAAgB,KAAA,YAAiB,KAAA,IAAS,aAAa"}
1
+ {"version":3,"file":"sandbox.d.mts","names":["t","__name","buildSandboxFileFilter","excludePaths","SandboxMode","NONE","ORIGINAL","STUB","SandboxScriptOptions","mode","appName","env","setup","cli","buildSandboxScript","options","prepareSandboxWorkspace"],"sources":["../../sandbox/dist/index.d.mts","../src/sandbox.ts"],"mappings":";;;;;;AAAmD;;;;AAYY;AAAA;;;iBAA9CE,sBAAAA,CAAuBC,YAAuB;;;;;AAazD;AAAA;aANOC,WAAAA;;EAEXC,IAAAA;EAQAI;EANAH,QAAAA;EAQAI;EANAH,IAAAA;AAAAA;AAAAA,UAEQC,oBAAAA;EAaRK;EAXAJ,IAAAA,EAAML,WAAW;EAWd;EATHM,OAAAA;EAwBiC;;EArBjCC,GAAAA;EAqB+D;EAnB/DR,YAAAA;;EAEAS,KAAAA;ECnBF;EDqBEC,GAAAA;AAAAA;;;;;;;;;;ACpB+D;AAcjE;;;iBDqBiBC,kBAAAA,CAAmBC,OAA6B,EAApBP,oBAAoB;;;;;;;AAvDd;;;;cCmBtC,aAAA,SAAsB,MAAA,CAAO,iBAAiB;cAC9C,IAAA,UAAc,IAAA,GAAO,MAAA,CAAO,wBAAA;AAAA;;iBAczB,eAAA,CAAgB,KAAA,YAAiB,KAAA,IAAS,aAAa"}
package/dist/sandbox.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { a as buildSandboxScript, i as buildSandboxFileFilter, n as isDeploySandbox, r as SandboxMode, t as DeploySandbox } from "./sandbox-8xQPhxw7.mjs";
1
+ import { a as buildSandboxScript, i as buildSandboxFileFilter, n as isDeploySandbox, r as SandboxMode, t as DeploySandbox } from "./sandbox-BQtqMu-5.mjs";
2
2
 
3
3
  export { DeploySandbox, SandboxMode, buildSandboxFileFilter, buildSandboxScript, isDeploySandbox };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@infracraft/pulumi",
3
- "version": "1.21.0",
3
+ "version": "1.22.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Native Pulumi providers for Railway, Neon, Vercel, and Fly.io with adopt-or-create semantics and deploy orchestration. No Terraform bridge.",
@@ -1 +0,0 @@
1
- {"version":3,"file":"sandbox-8xQPhxw7.mjs","names":[],"sources":["../../sandbox/dist/index.mjs","../src/sandbox.ts"],"sourcesContent":["import { t as __name } from \"./chunk-OPjESj5l.mjs\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n//#region src/index.ts\n/** Escapes a path literal for embedding inside an awk ERE. */\nfunction escapeAwkRegex(path) {\n\treturn path.replace(/[.[\\]{}()*+?^$|\\\\/]/g, \"\\\\$&\");\n}\n/**\n* Builds the shell filter applied to the `git ls-files` list before\n* `rsync --files-from`. For `apps/<x>` it drops the app's files but keeps\n* `apps/<x>/package.json` (the monorepo workspace graph needs it during build);\n* any other entry drops the path and its subtree. Returns `cat` (passthrough)\n* when nothing is excluded. Uses awk so it is portable across macOS and Linux.\n*/\nfunction buildSandboxFileFilter(excludePaths = []) {\n\tif (excludePaths.length === 0) return \"cat\";\n\treturn `awk '${excludePaths.map((entry) => {\n\t\tconst escaped = escapeAwkRegex(entry);\n\t\tif (entry.startsWith(\"apps/\")) return `!(/^${escaped}\\\\// && !/^${escaped}\\\\/package\\\\.json$/)`;\n\t\treturn `!/^${escaped}(\\\\/|$)/`;\n\t}).join(\" && \")}'`;\n}\n/** Root of the per-deploy sandbox tree. The DeploySandbox resource GCs this. */\nconst SANDBOX_ROOT = \"/tmp/infracraft\";\n/**\n* How a deploy's working copy is isolated. A closed set, so it is an enum rather\n* than a pair of booleans: the seam derives it from `dependsOn` presence\n* (DeploySandbox / GitGuard), which makes the impossible \"git-guard without a\n* sandbox\" state unrepresentable here.\n*/\nlet SandboxMode = /* @__PURE__ */ function(SandboxMode) {\n\t/** No isolation — run the CLI in the live working tree. */\n\tSandboxMode[\"NONE\"] = \"NONE\";\n\t/** Isolated copy carrying the repo's REAL `.git` (real commit/author exposed). */\n\tSandboxMode[\"ORIGINAL\"] = \"ORIGINAL\";\n\t/** Isolated copy with a metadata-free stub `.git` (no commit SHA / author). */\n\tSandboxMode[\"STUB\"] = \"STUB\";\n\treturn SandboxMode;\n}({});\n/**\n* Builds the shell for a deploy's `command.local.Command.create`. See\n* docs/superpowers/specs/2026-06-05-deploy-sandbox-design.md for the modes.\n*\n* Hardening: every script runs under POSIX `set -e` so any failed step aborts\n* instead of silently letting the platform CLI run against partial state.\n* `pipefail` is intentionally NOT used — the command runs under `/bin/sh`, which\n* may be `dash` (pipefail is a bash/ksh/zsh extension). To still catch a failing\n* `git ls-files` (a pipe would mask it, since an `rsync` of an empty list exits\n* 0), the copy is staged through intermediate list files rather than a pipe. The\n* `REPO` and `SANDBOX` command substitutions are explicitly guarded with clear\n* error messages.\n*/\nfunction buildSandboxScript(options) {\n\tconst { mode, appName, env, excludePaths, setup, cli } = options;\n\tconst head = `set -e; REPO=$(git rev-parse --show-toplevel) || { echo \"[infracraft] not inside a git repository\" >&2; exit 1; }`;\n\tconst runSetupAndCli = [setup, cli].filter(Boolean).join(\"; \");\n\tif (mode === \"NONE\") return `${head}; cd \"$REPO\"; ${runSetupAndCli}`;\n\tconst makeSandbox = [\n\t\t`PROJECT=$(basename \"$REPO\")`,\n\t\t`SANDBOX=$(mktemp -d \"${SANDBOX_ROOT}/$PROJECT-${env ? `${env}-${appName}` : appName}.XXXXXX\") || { echo \"[infracraft] mktemp failed\" >&2; exit 1; }`,\n\t\t`trap 'rm -rf \"$SANDBOX\"' EXIT`\n\t].join(\"; \");\n\tconst copy = [\n\t\t`git -C \"$REPO\" ls-files > \"$SANDBOX/.ic-ls\"`,\n\t\t`${mode === \"STUB\" ? buildSandboxFileFilter(excludePaths) : \"cat\"} \"$SANDBOX/.ic-ls\" > \"$SANDBOX/.ic-lsf\"`,\n\t\t`rsync -a --files-from=\"$SANDBOX/.ic-lsf\" \"$REPO\"/ \"$SANDBOX\"/`,\n\t\t`rm -f \"$SANDBOX/.ic-ls\" \"$SANDBOX/.ic-lsf\"`\n\t].join(\"; \");\n\tif (mode === \"STUB\") return `${head}; ${makeSandbox}; ${copy}; cd \"\\$SANDBOX\"; git init -q && git add -A; ${runSetupAndCli}`;\n\treturn `${head}; ${makeSandbox}; ${copy}; cp -c -R \"\\$REPO/.git\" \"\\$SANDBOX/.git\" 2>/dev/null || cp -R \"\\$REPO/.git\" \"\\$SANDBOX/.git\"; cd \"$SANDBOX\"; ${runSetupAndCli}`;\n}\n/** Sweep sandboxes orphaned by a hard-killed run (older than this). Kept well\n* above the worst-case deploy time (~30 min) but low enough that a SIGKILL\n* orphan does not linger for a day accumulating in /tmp. */\nconst STALE_SANDBOX_MS = 10800 * 1e3;\n/** mkdir the workspace root and GC stale sandboxes (best-effort). Sandboxes are\n* flat under the root (`/tmp/infracraft/<project>-<env>-<app>.XXXX`), so this\n* sweeps any entry older than the stale threshold. */\nfunction prepareSandboxWorkspace() {\n\tfs.mkdirSync(SANDBOX_ROOT, { recursive: true });\n\tlet entries = [];\n\ttry {\n\t\tentries = fs.readdirSync(SANDBOX_ROOT);\n\t} catch {\n\t\treturn;\n\t}\n\tconst now = Date.now();\n\tfor (const entry of entries) {\n\t\tconst full = path.join(SANDBOX_ROOT, entry);\n\t\ttry {\n\t\t\tif (now - fs.statSync(full).mtimeMs > STALE_SANDBOX_MS) fs.rmSync(full, {\n\t\t\t\trecursive: true,\n\t\t\t\tforce: true\n\t\t\t});\n\t\t} catch {}\n\t}\n}\n\n//#endregion\nexport { SandboxMode, buildSandboxFileFilter, buildSandboxScript, prepareSandboxWorkspace };\n//# sourceMappingURL=index.mjs.map","import { prepareSandboxWorkspace } from \"@infracraft/sandbox\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\nexport type { SandboxScriptOptions } from \"@infracraft/sandbox\";\nexport {\n\tbuildSandboxFileFilter,\n\tbuildSandboxScript,\n\tSandboxMode,\n} from \"@infracraft/sandbox\";\n\n/** Cross-bundle brand: `instanceof` is unreliable when the seam and the resource\n * come from different built entries, so the seam detects sandboxes by this. */\nconst DEPLOY_SANDBOX_BRAND = Symbol.for(\"@infracraft/pulumi/DeploySandbox\");\n\n/**\n * Isolation marker + workspace lifecycle. Listing it in a deploy's `dependsOn`\n * makes that deploy run in an isolated `/tmp/infracraft` copy. Carries no config;\n * the repo root is derived at runtime by the deploy command.\n */\nexport class DeploySandbox extends pulumi.ComponentResource {\n\tconstructor(name: string, opts?: pulumi.ComponentResourceOptions) {\n\t\tsuper(\"infracraft:sandbox:DeploySandbox\", name, {}, opts);\n\n\t\t(this as Record<symbol, unknown>)[DEPLOY_SANDBOX_BRAND] = true;\n\n\t\tif (!pulumi.runtime.isDryRun()) {\n\t\t\tprepareSandboxWorkspace();\n\t\t}\n\n\t\tthis.registerOutputs({});\n\t}\n}\n\n/** Bundle-safe check for a `DeploySandbox` in a `dependsOn` array. */\nexport function isDeploySandbox(value: unknown): value is DeploySandbox {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t(value as Record<symbol, unknown>)[DEPLOY_SANDBOX_BRAND] === true\n\t);\n}\n"],"mappings":";;;;;;;AAMA,SAAS,eAAe,MAAM;CAC7B,OAAO,KAAK,QAAQ,wBAAwB,MAAM;AACnD;;;;;;;;AAQA,SAAS,uBAAuB,eAAe,CAAC,GAAG;CAClD,IAAI,aAAa,WAAW,GAAG,OAAO;CACtC,OAAO,QAAQ,aAAa,KAAK,UAAU;EAC1C,MAAM,UAAU,eAAe,KAAK;EACpC,IAAI,MAAM,WAAW,OAAO,GAAG,OAAO,OAAO,QAAQ,aAAa,QAAQ;EAC1E,OAAO,MAAM,QAAQ;CACtB,CAAC,EAAE,KAAK,MAAM,EAAE;AACjB;;AAEA,MAAM,eAAe;;;;;;;AAOrB,IAAI,cAA8B,yBAAS,aAAa;;CAEvD,YAAY,UAAU;;CAEtB,YAAY,cAAc;;CAE1B,YAAY,UAAU;CACtB,OAAO;AACR,EAAE,CAAC,CAAC;;;;;;;;;;;;;;AAcJ,SAAS,mBAAmB,SAAS;CACpC,MAAM,EAAE,MAAM,SAAS,KAAK,cAAc,OAAO,QAAQ;CACzD,MAAM,OAAO;CACb,MAAM,iBAAiB,CAAC,OAAO,GAAG,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;CAC7D,IAAI,SAAS,QAAQ,OAAO,GAAG,KAAK,gBAAgB;CACpD,MAAM,cAAc;EACnB;EACA,wBAAwB,aAAa,YAAY,MAAM,GAAG,IAAI,GAAG,YAAY,QAAQ;EACrF;CACD,EAAE,KAAK,IAAI;CACX,MAAM,OAAO;EACZ;EACA,GAAG,SAAS,SAAS,uBAAuB,YAAY,IAAI,MAAM;EAClE;EACA;CACD,EAAE,KAAK,IAAI;CACX,IAAI,SAAS,QAAQ,OAAO,GAAG,KAAK,IAAI,YAAY,IAAI,KAAK,+CAA+C;CAC5G,OAAO,GAAG,KAAK,IAAI,YAAY,IAAI,KAAK,gHAAgH;AACzJ;;;;AAIA,MAAM,mBAAmB,QAAQ;;;;AAIjC,SAAS,0BAA0B;CAClC,GAAG,UAAU,cAAc,EAAE,WAAW,KAAK,CAAC;CAC9C,IAAI,UAAU,CAAC;CACf,IAAI;EACH,UAAU,GAAG,YAAY,YAAY;CACtC,QAAQ;EACP;CACD;CACA,MAAM,MAAM,KAAK,IAAI;CACrB,KAAK,MAAM,SAAS,SAAS;EAC5B,MAAM,OAAO,KAAK,KAAK,cAAc,KAAK;EAC1C,IAAI;GACH,IAAI,MAAM,GAAG,SAAS,IAAI,EAAE,UAAU,kBAAkB,GAAG,OAAO,MAAM;IACvE,WAAW;IACX,OAAO;GACR,CAAC;EACF,QAAQ,CAAC;CACV;AACD;;;;;;ACtFA,MAAM,uBAAuB,OAAO,IAAI,kCAAkC;;;;;;AAO1E,IAAa,gBAAb,cAAmC,OAAO,kBAAkB;CAC3D,YAAY,MAAc,MAAwC;EACjE,MAAM,oCAAoC,MAAM,CAAC,GAAG,IAAI;EAExD,AAAC,KAAiC,wBAAwB;EAE1D,IAAI,CAAC,OAAO,QAAQ,SAAS,GAC5B,wBAAwB;EAGzB,KAAK,gBAAgB,CAAC,CAAC;CACxB;AACD;;AAGA,SAAgB,gBAAgB,OAAwC;CACvE,OACC,OAAO,UAAU,YACjB,UAAU,QACT,MAAkC,0BAA0B;AAE/D"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"sandbox-Bh7tQ1Jy.cjs","names":["fs","path","pulumi"],"sources":["../../sandbox/dist/index.mjs","../src/sandbox.ts"],"sourcesContent":["import { t as __name } from \"./chunk-OPjESj5l.mjs\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n//#region src/index.ts\n/** Escapes a path literal for embedding inside an awk ERE. */\nfunction escapeAwkRegex(path) {\n\treturn path.replace(/[.[\\]{}()*+?^$|\\\\/]/g, \"\\\\$&\");\n}\n/**\n* Builds the shell filter applied to the `git ls-files` list before\n* `rsync --files-from`. For `apps/<x>` it drops the app's files but keeps\n* `apps/<x>/package.json` (the monorepo workspace graph needs it during build);\n* any other entry drops the path and its subtree. Returns `cat` (passthrough)\n* when nothing is excluded. Uses awk so it is portable across macOS and Linux.\n*/\nfunction buildSandboxFileFilter(excludePaths = []) {\n\tif (excludePaths.length === 0) return \"cat\";\n\treturn `awk '${excludePaths.map((entry) => {\n\t\tconst escaped = escapeAwkRegex(entry);\n\t\tif (entry.startsWith(\"apps/\")) return `!(/^${escaped}\\\\// && !/^${escaped}\\\\/package\\\\.json$/)`;\n\t\treturn `!/^${escaped}(\\\\/|$)/`;\n\t}).join(\" && \")}'`;\n}\n/** Root of the per-deploy sandbox tree. The DeploySandbox resource GCs this. */\nconst SANDBOX_ROOT = \"/tmp/infracraft\";\n/**\n* How a deploy's working copy is isolated. A closed set, so it is an enum rather\n* than a pair of booleans: the seam derives it from `dependsOn` presence\n* (DeploySandbox / GitGuard), which makes the impossible \"git-guard without a\n* sandbox\" state unrepresentable here.\n*/\nlet SandboxMode = /* @__PURE__ */ function(SandboxMode) {\n\t/** No isolation — run the CLI in the live working tree. */\n\tSandboxMode[\"NONE\"] = \"NONE\";\n\t/** Isolated copy carrying the repo's REAL `.git` (real commit/author exposed). */\n\tSandboxMode[\"ORIGINAL\"] = \"ORIGINAL\";\n\t/** Isolated copy with a metadata-free stub `.git` (no commit SHA / author). */\n\tSandboxMode[\"STUB\"] = \"STUB\";\n\treturn SandboxMode;\n}({});\n/**\n* Builds the shell for a deploy's `command.local.Command.create`. See\n* docs/superpowers/specs/2026-06-05-deploy-sandbox-design.md for the modes.\n*\n* Hardening: every script runs under POSIX `set -e` so any failed step aborts\n* instead of silently letting the platform CLI run against partial state.\n* `pipefail` is intentionally NOT used — the command runs under `/bin/sh`, which\n* may be `dash` (pipefail is a bash/ksh/zsh extension). To still catch a failing\n* `git ls-files` (a pipe would mask it, since an `rsync` of an empty list exits\n* 0), the copy is staged through intermediate list files rather than a pipe. The\n* `REPO` and `SANDBOX` command substitutions are explicitly guarded with clear\n* error messages.\n*/\nfunction buildSandboxScript(options) {\n\tconst { mode, appName, env, excludePaths, setup, cli } = options;\n\tconst head = `set -e; REPO=$(git rev-parse --show-toplevel) || { echo \"[infracraft] not inside a git repository\" >&2; exit 1; }`;\n\tconst runSetupAndCli = [setup, cli].filter(Boolean).join(\"; \");\n\tif (mode === \"NONE\") return `${head}; cd \"$REPO\"; ${runSetupAndCli}`;\n\tconst makeSandbox = [\n\t\t`PROJECT=$(basename \"$REPO\")`,\n\t\t`SANDBOX=$(mktemp -d \"${SANDBOX_ROOT}/$PROJECT-${env ? `${env}-${appName}` : appName}.XXXXXX\") || { echo \"[infracraft] mktemp failed\" >&2; exit 1; }`,\n\t\t`trap 'rm -rf \"$SANDBOX\"' EXIT`\n\t].join(\"; \");\n\tconst copy = [\n\t\t`git -C \"$REPO\" ls-files > \"$SANDBOX/.ic-ls\"`,\n\t\t`${mode === \"STUB\" ? buildSandboxFileFilter(excludePaths) : \"cat\"} \"$SANDBOX/.ic-ls\" > \"$SANDBOX/.ic-lsf\"`,\n\t\t`rsync -a --files-from=\"$SANDBOX/.ic-lsf\" \"$REPO\"/ \"$SANDBOX\"/`,\n\t\t`rm -f \"$SANDBOX/.ic-ls\" \"$SANDBOX/.ic-lsf\"`\n\t].join(\"; \");\n\tif (mode === \"STUB\") return `${head}; ${makeSandbox}; ${copy}; cd \"\\$SANDBOX\"; git init -q && git add -A; ${runSetupAndCli}`;\n\treturn `${head}; ${makeSandbox}; ${copy}; cp -c -R \"\\$REPO/.git\" \"\\$SANDBOX/.git\" 2>/dev/null || cp -R \"\\$REPO/.git\" \"\\$SANDBOX/.git\"; cd \"$SANDBOX\"; ${runSetupAndCli}`;\n}\n/** Sweep sandboxes orphaned by a hard-killed run (older than this). Kept well\n* above the worst-case deploy time (~30 min) but low enough that a SIGKILL\n* orphan does not linger for a day accumulating in /tmp. */\nconst STALE_SANDBOX_MS = 10800 * 1e3;\n/** mkdir the workspace root and GC stale sandboxes (best-effort). Sandboxes are\n* flat under the root (`/tmp/infracraft/<project>-<env>-<app>.XXXX`), so this\n* sweeps any entry older than the stale threshold. */\nfunction prepareSandboxWorkspace() {\n\tfs.mkdirSync(SANDBOX_ROOT, { recursive: true });\n\tlet entries = [];\n\ttry {\n\t\tentries = fs.readdirSync(SANDBOX_ROOT);\n\t} catch {\n\t\treturn;\n\t}\n\tconst now = Date.now();\n\tfor (const entry of entries) {\n\t\tconst full = path.join(SANDBOX_ROOT, entry);\n\t\ttry {\n\t\t\tif (now - fs.statSync(full).mtimeMs > STALE_SANDBOX_MS) fs.rmSync(full, {\n\t\t\t\trecursive: true,\n\t\t\t\tforce: true\n\t\t\t});\n\t\t} catch {}\n\t}\n}\n\n//#endregion\nexport { SandboxMode, buildSandboxFileFilter, buildSandboxScript, prepareSandboxWorkspace };\n//# sourceMappingURL=index.mjs.map","import { prepareSandboxWorkspace } from \"@infracraft/sandbox\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\nexport type { SandboxScriptOptions } from \"@infracraft/sandbox\";\nexport {\n\tbuildSandboxFileFilter,\n\tbuildSandboxScript,\n\tSandboxMode,\n} from \"@infracraft/sandbox\";\n\n/** Cross-bundle brand: `instanceof` is unreliable when the seam and the resource\n * come from different built entries, so the seam detects sandboxes by this. */\nconst DEPLOY_SANDBOX_BRAND = Symbol.for(\"@infracraft/pulumi/DeploySandbox\");\n\n/**\n * Isolation marker + workspace lifecycle. Listing it in a deploy's `dependsOn`\n * makes that deploy run in an isolated `/tmp/infracraft` copy. Carries no config;\n * the repo root is derived at runtime by the deploy command.\n */\nexport class DeploySandbox extends pulumi.ComponentResource {\n\tconstructor(name: string, opts?: pulumi.ComponentResourceOptions) {\n\t\tsuper(\"infracraft:sandbox:DeploySandbox\", name, {}, opts);\n\n\t\t(this as Record<symbol, unknown>)[DEPLOY_SANDBOX_BRAND] = true;\n\n\t\tif (!pulumi.runtime.isDryRun()) {\n\t\t\tprepareSandboxWorkspace();\n\t\t}\n\n\t\tthis.registerOutputs({});\n\t}\n}\n\n/** Bundle-safe check for a `DeploySandbox` in a `dependsOn` array. */\nexport function isDeploySandbox(value: unknown): value is DeploySandbox {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t(value as Record<symbol, unknown>)[DEPLOY_SANDBOX_BRAND] === true\n\t);\n}\n"],"mappings":";;;;;;;;;;AAMA,SAAS,eAAe,MAAM;CAC7B,OAAO,KAAK,QAAQ,wBAAwB,MAAM;AACnD;;;;;;;;AAQA,SAAS,uBAAuB,eAAe,CAAC,GAAG;CAClD,IAAI,aAAa,WAAW,GAAG,OAAO;CACtC,OAAO,QAAQ,aAAa,KAAK,UAAU;EAC1C,MAAM,UAAU,eAAe,KAAK;EACpC,IAAI,MAAM,WAAW,OAAO,GAAG,OAAO,OAAO,QAAQ,aAAa,QAAQ;EAC1E,OAAO,MAAM,QAAQ;CACtB,CAAC,EAAE,KAAK,MAAM,EAAE;AACjB;;AAEA,MAAM,eAAe;;;;;;;AAOrB,IAAI,cAA8B,yBAAS,aAAa;;CAEvD,YAAY,UAAU;;CAEtB,YAAY,cAAc;;CAE1B,YAAY,UAAU;CACtB,OAAO;AACR,EAAE,CAAC,CAAC;;;;;;;;;;;;;;AAcJ,SAAS,mBAAmB,SAAS;CACpC,MAAM,EAAE,MAAM,SAAS,KAAK,cAAc,OAAO,QAAQ;CACzD,MAAM,OAAO;CACb,MAAM,iBAAiB,CAAC,OAAO,GAAG,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;CAC7D,IAAI,SAAS,QAAQ,OAAO,GAAG,KAAK,gBAAgB;CACpD,MAAM,cAAc;EACnB;EACA,wBAAwB,aAAa,YAAY,MAAM,GAAG,IAAI,GAAG,YAAY,QAAQ;EACrF;CACD,EAAE,KAAK,IAAI;CACX,MAAM,OAAO;EACZ;EACA,GAAG,SAAS,SAAS,uBAAuB,YAAY,IAAI,MAAM;EAClE;EACA;CACD,EAAE,KAAK,IAAI;CACX,IAAI,SAAS,QAAQ,OAAO,GAAG,KAAK,IAAI,YAAY,IAAI,KAAK,+CAA+C;CAC5G,OAAO,GAAG,KAAK,IAAI,YAAY,IAAI,KAAK,gHAAgH;AACzJ;;;;AAIA,MAAM,mBAAmB,QAAQ;;;;AAIjC,SAAS,0BAA0B;CAClC,QAAG,UAAU,cAAc,EAAE,WAAW,KAAK,CAAC;CAC9C,IAAI,UAAU,CAAC;CACf,IAAI;EACH,UAAUA,QAAG,YAAY,YAAY;CACtC,QAAQ;EACP;CACD;CACA,MAAM,MAAM,KAAK,IAAI;CACrB,KAAK,MAAM,SAAS,SAAS;EAC5B,MAAM,OAAOC,UAAK,KAAK,cAAc,KAAK;EAC1C,IAAI;GACH,IAAI,MAAMD,QAAG,SAAS,IAAI,EAAE,UAAU,kBAAkB,QAAG,OAAO,MAAM;IACvE,WAAW;IACX,OAAO;GACR,CAAC;EACF,QAAQ,CAAC;CACV;AACD;;;;;;ACtFA,MAAM,uBAAuB,OAAO,IAAI,kCAAkC;;;;;;AAO1E,IAAa,gBAAb,cAAmCE,eAAO,kBAAkB;CAC3D,YAAY,MAAc,MAAwC;EACjE,MAAM,oCAAoC,MAAM,CAAC,GAAG,IAAI;EAExD,AAAC,KAAiC,wBAAwB;EAE1D,IAAI,CAACA,eAAO,QAAQ,SAAS,GAC5B,wBAAwB;EAGzB,KAAK,gBAAgB,CAAC,CAAC;CACxB;AACD;;AAGA,SAAgB,gBAAgB,OAAwC;CACvE,OACC,OAAO,UAAU,YACjB,UAAU,QACT,MAAkC,0BAA0B;AAE/D"}