@bakers-build/addon-ports-frp 0.5.1-next.855 → 0.5.1-next.856

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.
@@ -58,7 +58,11 @@ async function readRoutesFile(path, deps) {
58
58
  if (proxies.length === 0 && Object.keys(parsed).length === 1) {
59
59
  return { kind: 'absent' };
60
60
  }
61
- return { kind: 'ok', proxies: proxies };
61
+ return {
62
+ kind: 'ok',
63
+ proxies: proxies,
64
+ onlyProxies: Object.keys(parsed).length === 1,
65
+ };
62
66
  }
63
67
  async function writeRoutesFile(path, dir, content, deps) {
64
68
  await deps.mkdir(dir);
@@ -66,13 +70,16 @@ async function writeRoutesFile(path, dir, content, deps) {
66
70
  await deps.writeFile(tmp, `${JSON.stringify(content, null, 2)}\n`, 0o600);
67
71
  await deps.rename(tmp, path);
68
72
  }
69
- function lastAcknowledgedGeneration(records) {
73
+ /** Every generation a record carries — a stale record must not hide the
74
+ * newest one, so the file is ours if it matches any of them. */
75
+ function recordedGenerations(records) {
76
+ const generations = new Set();
70
77
  for (const r of records) {
71
78
  const g = r.data?.generation;
72
79
  if (typeof g === 'string')
73
- return g;
80
+ generations.add(g);
74
81
  }
75
- return undefined;
82
+ return generations;
76
83
  }
77
84
  export async function reconcileFrp(records, cfg, deps, context) {
78
85
  let next = records;
@@ -124,7 +131,10 @@ export async function reconcileFrp(records, cfg, deps, context) {
124
131
  return { records: next, ok };
125
132
  }
126
133
  const desiredGeneration = computeGeneration(cfg.identity, proxies);
127
- const lastGeneration = lastAcknowledgedGeneration(records);
134
+ const knownGenerations = recordedGenerations(records);
135
+ // Intent is persisted before reconcile, so a write whose generation was
136
+ // never recorded (lost write) still names only proxies of known records.
137
+ const knownNames = new Set(records.map((r) => `${r.ticket}-${r.key}`));
128
138
  const dir = cfg.routesPath.slice(0, cfg.routesPath.lastIndexOf('/'));
129
139
  const existing = await readRoutesFile(cfg.routesPath, deps);
130
140
  // The file carries no identity/generation of its own (C3) — its content
@@ -140,7 +150,9 @@ export async function reconcileFrp(records, cfg, deps, context) {
140
150
  const isForeign = existing.kind === 'malformed' ||
141
151
  (existing.kind === 'ok' &&
142
152
  existingGeneration !== desiredGeneration &&
143
- existingGeneration !== lastGeneration);
153
+ !knownGenerations.has(existingGeneration) &&
154
+ !(existing.onlyProxies &&
155
+ existing.proxies.every((p) => knownNames.has(p.name))));
144
156
  if (isForeign) {
145
157
  // Foreign file (or one this client didn't write, or doesn't recognise
146
158
  // from the last acknowledged/current generation) — refuse to clobber it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bakers-build/addon-ports-frp",
3
- "version": "0.5.1-next.855",
3
+ "version": "0.5.1-next.856",
4
4
  "description": "bakers.build ports transport: a self-hosted frp (frpc) client over an operator-owned config, reconciled via its local admin API. Implements @bakers-build/addon-ports's PortsTransport contract.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -19,11 +19,11 @@
19
19
  "directory": "bakers-cli/addons/ports-frp"
20
20
  },
21
21
  "peerDependencies": {
22
- "@bakers-build/addon-ports": "^0.5.1-next.855",
23
- "@bakers-build/core": "^0.5.1-next.855"
22
+ "@bakers-build/addon-ports": "^0.5.1-next.856",
23
+ "@bakers-build/core": "^0.5.1-next.856"
24
24
  },
25
25
  "bakersBuild": {
26
- "commit": "bea27960d8d0cfb827f72aae305ed5820ecd28f3",
26
+ "commit": "756501eea816e36604ee443b4b3dc8a9e6c89692",
27
27
  "channel": "next"
28
28
  }
29
29
  }