@wrongstack/governance 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -511,6 +511,7 @@ import * as path3 from "node:path";
511
511
  import * as fs from "node:fs/promises";
512
512
  import * as net from "node:net";
513
513
  import * as path2 from "node:path";
514
+ import { atomicWrite, restrictFilePermissions } from "@wrongstack/persistence";
514
515
 
515
516
  // src/ipc-endpoint.ts
516
517
  import { createHash as createHash3 } from "node:crypto";
@@ -601,18 +602,30 @@ async function ensurePrivateDirectory(projectRoot) {
601
602
  if (process.platform !== "win32") await fs.chmod(directory, 448);
602
603
  return directory;
603
604
  }
605
+ var TRANSIENT_READ_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY"]);
606
+ var READ_RETRY_DELAYS_MS = [15, 40, 100, 250];
604
607
  async function readBounded(pathname) {
605
- try {
606
- const value = await fs.readFile(pathname);
607
- if (value.byteLength > GOVERNANCE_DAEMON_METADATA_MAX_BYTES) {
608
- throw new Error(
609
- `Governance daemon metadata exceeds ${GOVERNANCE_DAEMON_METADATA_MAX_BYTES} bytes.`
610
- );
608
+ let attempt = 0;
609
+ for (; ; ) {
610
+ try {
611
+ const value = await fs.readFile(pathname);
612
+ if (value.byteLength > GOVERNANCE_DAEMON_METADATA_MAX_BYTES) {
613
+ throw new Error(
614
+ `Governance daemon metadata exceeds ${GOVERNANCE_DAEMON_METADATA_MAX_BYTES} bytes.`
615
+ );
616
+ }
617
+ return value.toString("utf8");
618
+ } catch (error) {
619
+ const code = errorCode(error);
620
+ if (code === "ENOENT") return null;
621
+ const delayMs = READ_RETRY_DELAYS_MS[attempt];
622
+ if (process.platform === "win32" && code !== void 0 && TRANSIENT_READ_CODES.has(code) && delayMs !== void 0) {
623
+ await delay(delayMs);
624
+ attempt++;
625
+ continue;
626
+ }
627
+ throw error;
611
628
  }
612
- return value.toString("utf8");
613
- } catch (error) {
614
- if (errorCode(error) === "ENOENT") return null;
615
- throw error;
616
629
  }
617
630
  }
618
631
  function parseMetadata(raw, projectRoot) {
@@ -747,19 +760,9 @@ async function writeGovernanceDaemonAttachmentBroker(projectRoot, broker) {
747
760
  const expected = validated.broker;
748
761
  await ensurePrivateDirectory(projectRoot);
749
762
  const pathname = governanceDaemonAttachmentBrokerPath(projectRoot);
750
- const temporary = `${pathname}.${process.pid}.${expected.instanceId}.tmp`;
751
- await fs.writeFile(temporary, `${JSON.stringify(expected, null, 2)}
752
- `, {
753
- encoding: "utf8",
754
- mode: 384
755
- });
756
- if (process.platform !== "win32") await fs.chmod(temporary, 384);
757
- try {
758
- await fs.rename(temporary, pathname);
759
- } catch (error) {
760
- await fs.rm(temporary, { force: true }).catch(() => void 0);
761
- throw error;
762
- }
763
+ await atomicWrite(pathname, `${JSON.stringify(expected, null, 2)}
764
+ `, { mode: 384 });
765
+ await restrictFilePermissions(pathname).catch(() => void 0);
763
766
  }
764
767
  async function readGovernanceDaemonAttachmentBroker(projectRoot) {
765
768
  const raw = await readBounded(governanceDaemonAttachmentBrokerPath(projectRoot));
@@ -799,19 +802,9 @@ async function writeGovernanceDaemonMetadata(projectRoot, metadata) {
799
802
  const expected = createGovernanceDaemonMetadata(metadata);
800
803
  await ensurePrivateDirectory(projectRoot);
801
804
  const pathname = governanceDaemonMetadataPath(projectRoot);
802
- const temporary = `${pathname}.${process.pid}.${expected.instanceId}.tmp`;
803
- await fs.writeFile(temporary, `${JSON.stringify(expected, null, 2)}
804
- `, {
805
- encoding: "utf8",
806
- mode: 384
807
- });
808
- try {
809
- await fs.rename(temporary, pathname);
810
- } catch {
811
- await fs.rm(pathname, { force: true });
812
- await fs.rename(temporary, pathname);
813
- }
814
- if (process.platform !== "win32") await fs.chmod(pathname, 384);
805
+ await atomicWrite(pathname, `${JSON.stringify(expected, null, 2)}
806
+ `, { mode: 384 });
807
+ await restrictFilePermissions(pathname).catch(() => void 0);
815
808
  }
816
809
  async function readGovernanceDaemonMetadata(projectRoot) {
817
810
  const raw = await readBounded(governanceDaemonMetadataPath(projectRoot));
@@ -8,6 +8,7 @@ import { fileURLToPath } from "node:url";
8
8
  import * as fs from "node:fs/promises";
9
9
  import * as net from "node:net";
10
10
  import * as path2 from "node:path";
11
+ import { atomicWrite, restrictFilePermissions } from "@wrongstack/persistence";
11
12
 
12
13
  // src/ipc-endpoint.ts
13
14
  import { createHash } from "node:crypto";
@@ -121,18 +122,30 @@ async function ensurePrivateDirectory(projectRoot) {
121
122
  if (process.platform !== "win32") await fs.chmod(directory, 448);
122
123
  return directory;
123
124
  }
125
+ var TRANSIENT_READ_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY"]);
126
+ var READ_RETRY_DELAYS_MS = [15, 40, 100, 250];
124
127
  async function readBounded(pathname) {
125
- try {
126
- const value = await fs.readFile(pathname);
127
- if (value.byteLength > GOVERNANCE_DAEMON_METADATA_MAX_BYTES) {
128
- throw new Error(
129
- `Governance daemon metadata exceeds ${GOVERNANCE_DAEMON_METADATA_MAX_BYTES} bytes.`
130
- );
128
+ let attempt = 0;
129
+ for (; ; ) {
130
+ try {
131
+ const value = await fs.readFile(pathname);
132
+ if (value.byteLength > GOVERNANCE_DAEMON_METADATA_MAX_BYTES) {
133
+ throw new Error(
134
+ `Governance daemon metadata exceeds ${GOVERNANCE_DAEMON_METADATA_MAX_BYTES} bytes.`
135
+ );
136
+ }
137
+ return value.toString("utf8");
138
+ } catch (error) {
139
+ const code = errorCode(error);
140
+ if (code === "ENOENT") return null;
141
+ const delayMs = READ_RETRY_DELAYS_MS[attempt];
142
+ if (process.platform === "win32" && code !== void 0 && TRANSIENT_READ_CODES.has(code) && delayMs !== void 0) {
143
+ await delay(delayMs);
144
+ attempt++;
145
+ continue;
146
+ }
147
+ throw error;
131
148
  }
132
- return value.toString("utf8");
133
- } catch (error) {
134
- if (errorCode(error) === "ENOENT") return null;
135
- throw error;
136
149
  }
137
150
  }
138
151
  function parseMetadata(raw, projectRoot) {
@@ -267,19 +280,9 @@ async function writeGovernanceDaemonAttachmentBroker(projectRoot, broker) {
267
280
  const expected = validated.broker;
268
281
  await ensurePrivateDirectory(projectRoot);
269
282
  const pathname = governanceDaemonAttachmentBrokerPath(projectRoot);
270
- const temporary = `${pathname}.${process.pid}.${expected.instanceId}.tmp`;
271
- await fs.writeFile(temporary, `${JSON.stringify(expected, null, 2)}
272
- `, {
273
- encoding: "utf8",
274
- mode: 384
275
- });
276
- if (process.platform !== "win32") await fs.chmod(temporary, 384);
277
- try {
278
- await fs.rename(temporary, pathname);
279
- } catch (error) {
280
- await fs.rm(temporary, { force: true }).catch(() => void 0);
281
- throw error;
282
- }
283
+ await atomicWrite(pathname, `${JSON.stringify(expected, null, 2)}
284
+ `, { mode: 384 });
285
+ await restrictFilePermissions(pathname).catch(() => void 0);
283
286
  }
284
287
  async function removeOwnedGovernanceDaemonAttachmentBroker(projectRoot, owner) {
285
288
  const pathname = governanceDaemonAttachmentBrokerPath(projectRoot);
@@ -315,19 +318,9 @@ async function writeGovernanceDaemonMetadata(projectRoot, metadata) {
315
318
  const expected = createGovernanceDaemonMetadata(metadata);
316
319
  await ensurePrivateDirectory(projectRoot);
317
320
  const pathname = governanceDaemonMetadataPath(projectRoot);
318
- const temporary = `${pathname}.${process.pid}.${expected.instanceId}.tmp`;
319
- await fs.writeFile(temporary, `${JSON.stringify(expected, null, 2)}
320
- `, {
321
- encoding: "utf8",
322
- mode: 384
323
- });
324
- try {
325
- await fs.rename(temporary, pathname);
326
- } catch {
327
- await fs.rm(pathname, { force: true });
328
- await fs.rename(temporary, pathname);
329
- }
330
- if (process.platform !== "win32") await fs.chmod(pathname, 384);
321
+ await atomicWrite(pathname, `${JSON.stringify(expected, null, 2)}
322
+ `, { mode: 384 });
323
+ await restrictFilePermissions(pathname).catch(() => void 0);
331
324
  }
332
325
  async function readGovernanceDaemonMetadata(projectRoot) {
333
326
  const raw = await readBounded(governanceDaemonMetadataPath(projectRoot));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/governance",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "license": "MIT",
5
5
  "description": "Deterministic workflow contracts and transition policy for WrongStack orchestration.",
6
6
  "repository": {
@@ -27,7 +27,7 @@
27
27
  "README.md"
28
28
  ],
29
29
  "dependencies": {
30
- "@wrongstack/persistence": "1.0.2"
30
+ "@wrongstack/persistence": "1.0.4"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^26.2.0",