@indigoai-us/hq-cloud 6.18.16 → 6.18.18
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/bin/sync-runner-company.d.ts.map +1 -1
- package/dist/bin/sync-runner-company.js +26 -1
- package/dist/bin/sync-runner-company.js.map +1 -1
- package/dist/bin/sync-runner-company.test.js +184 -3
- package/dist/bin/sync-runner-company.test.js.map +1 -1
- package/dist/bin/sync-runner.d.ts +13 -0
- package/dist/bin/sync-runner.d.ts.map +1 -1
- package/dist/bin/sync-runner.js.map +1 -1
- package/dist/bin/sync-runner.test.js +57 -2
- package/dist/bin/sync-runner.test.js.map +1 -1
- package/dist/cli/rescue-core.d.ts.map +1 -1
- package/dist/cli/rescue-core.js +21 -6
- package/dist/cli/rescue-core.js.map +1 -1
- package/dist/cli/rescue-rsync-status.test.js +23 -0
- package/dist/cli/rescue-rsync-status.test.js.map +1 -1
- package/dist/cli/share-plan-limit-presign.test.d.ts +2 -0
- package/dist/cli/share-plan-limit-presign.test.d.ts.map +1 -0
- package/dist/cli/share-plan-limit-presign.test.js +271 -0
- package/dist/cli/share-plan-limit-presign.test.js.map +1 -0
- package/dist/cli/share.d.ts +22 -0
- package/dist/cli/share.d.ts.map +1 -1
- package/dist/cli/share.js +151 -25
- package/dist/cli/share.js.map +1 -1
- package/dist/cli/share.test.js +262 -18
- package/dist/cli/share.test.js.map +1 -1
- package/dist/cli/sync.d.ts +63 -1
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +42 -1
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +44 -0
- package/dist/cli/sync.test.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/object-io.d.ts +40 -0
- package/dist/object-io.d.ts.map +1 -1
- package/dist/object-io.js +122 -5
- package/dist/object-io.js.map +1 -1
- package/dist/object-io.test.js +144 -1
- package/dist/object-io.test.js.map +1 -1
- package/dist/plan-limit-pause.d.ts +61 -0
- package/dist/plan-limit-pause.d.ts.map +1 -0
- package/dist/plan-limit-pause.js +252 -0
- package/dist/plan-limit-pause.js.map +1 -0
- package/dist/plan-limit-pause.test.d.ts +2 -0
- package/dist/plan-limit-pause.test.d.ts.map +1 -0
- package/dist/plan-limit-pause.test.js +150 -0
- package/dist/plan-limit-pause.test.js.map +1 -0
- package/dist/plan-limit.d.ts +87 -0
- package/dist/plan-limit.d.ts.map +1 -0
- package/dist/plan-limit.fixtures.d.ts +34 -0
- package/dist/plan-limit.fixtures.d.ts.map +1 -0
- package/dist/plan-limit.fixtures.js +96 -0
- package/dist/plan-limit.fixtures.js.map +1 -0
- package/dist/plan-limit.js +157 -0
- package/dist/plan-limit.js.map +1 -0
- package/dist/plan-limit.test.d.ts +2 -0
- package/dist/plan-limit.test.d.ts.map +1 -0
- package/dist/plan-limit.test.js +132 -0
- package/dist/plan-limit.test.js.map +1 -0
- package/dist/sync/index.d.ts +1 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/mutation-client.d.ts +14 -2
- package/dist/sync/mutation-client.d.ts.map +1 -1
- package/dist/sync/mutation-client.js +34 -2
- package/dist/sync/mutation-client.js.map +1 -1
- package/dist/sync/mutation-client.test.js +49 -0
- package/dist/sync/mutation-client.test.js.map +1 -1
- package/dist/sync-progress.d.ts.map +1 -1
- package/dist/sync-progress.js +29 -0
- package/dist/sync-progress.js.map +1 -1
- package/dist/sync-progress.test.js +86 -0
- package/dist/sync-progress.test.js.map +1 -1
- package/dist/vault-client.d.ts +49 -6
- package/dist/vault-client.d.ts.map +1 -1
- package/dist/vault-client.js +115 -43
- package/dist/vault-client.js.map +1 -1
- package/dist/vault-client.test.js +86 -10
- package/dist/vault-client.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-vault record of uploads the plan limit refused, so the sync runner does
|
|
3
|
+
* not presign the same refused files on every pass.
|
|
4
|
+
*
|
|
5
|
+
* File: `{stateDir}/plan-limit-pause.{scope}.json`, where `scope` is the
|
|
6
|
+
* vault's entity uid (`cmp_…` for a company, the person uid for the personal
|
|
7
|
+
* vault). Each paused path records when it was refused. A path is skipped
|
|
8
|
+
* until {@link PLAN_LIMIT_PAUSE_RETRY_MS} has passed or the plan state
|
|
9
|
+
* changes. The plan state has changed when:
|
|
10
|
+
*
|
|
11
|
+
* - the runner's once-per-pass admission check says a company vault accepts
|
|
12
|
+
* uploads again (see `share()`),
|
|
13
|
+
* - any upload to the same vault succeeds (the server accepted a write, so
|
|
14
|
+
* the files stop is no longer refusing; every pause for that vault is
|
|
15
|
+
* dropped), or
|
|
16
|
+
* - a client that changed the plan calls {@link clearPlanLimitPauses}.
|
|
17
|
+
*
|
|
18
|
+
* A pause whose stamp lies in the future (the clock moved backwards, or the
|
|
19
|
+
* record came from a machine with a skewed clock) has no effect and is pruned,
|
|
20
|
+
* so a pause can never last longer than {@link PLAN_LIMIT_PAUSE_RETRY_MS}.
|
|
21
|
+
*
|
|
22
|
+
* The record is advisory. A missing, unreadable or corrupt file means nothing
|
|
23
|
+
* is paused; the worst case is one extra presign per file, never a skipped
|
|
24
|
+
* upload that the server would have accepted for more than an hour.
|
|
25
|
+
*/
|
|
26
|
+
import * as fs from "fs";
|
|
27
|
+
import * as path from "path";
|
|
28
|
+
import { getStateDir } from "./journal.js";
|
|
29
|
+
import { parsePlanLimitRefusal } from "./plan-limit.js";
|
|
30
|
+
/** A refused file is retried after this long even if nothing else changed. */
|
|
31
|
+
export const PLAN_LIMIT_PAUSE_RETRY_MS = 60 * 60 * 1000;
|
|
32
|
+
const FILE_PREFIX = "plan-limit-pause.";
|
|
33
|
+
const FILE_SUFFIX = ".json";
|
|
34
|
+
const SCHEMA_VERSION = 1;
|
|
35
|
+
let clock = () => Date.now();
|
|
36
|
+
/** Test-only clock override; `null` restores `Date.now`. */
|
|
37
|
+
export function setPlanLimitPauseClockForTesting(now) {
|
|
38
|
+
clock = now ?? (() => Date.now());
|
|
39
|
+
}
|
|
40
|
+
/** Current time as the pause store sees it. */
|
|
41
|
+
export function planLimitPauseNow() {
|
|
42
|
+
return clock();
|
|
43
|
+
}
|
|
44
|
+
function sanitizeScope(scope) {
|
|
45
|
+
const cleaned = scope.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
46
|
+
if (!cleaned || /^[_-]+$/.test(cleaned)) {
|
|
47
|
+
throw new Error(`plan-limit pause: scope "${scope}" sanitizes to an empty identifier`);
|
|
48
|
+
}
|
|
49
|
+
return cleaned;
|
|
50
|
+
}
|
|
51
|
+
/** Where the pause record for `scope` lives. */
|
|
52
|
+
export function planLimitPausePath(scope) {
|
|
53
|
+
return path.join(getStateDir(), `${FILE_PREFIX}${sanitizeScope(scope)}${FILE_SUFFIX}`);
|
|
54
|
+
}
|
|
55
|
+
function isRecord(value) {
|
|
56
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
57
|
+
}
|
|
58
|
+
function readPauseFile(file, scope) {
|
|
59
|
+
const empty = { version: SCHEMA_VERSION, scope, paths: {} };
|
|
60
|
+
let raw;
|
|
61
|
+
try {
|
|
62
|
+
raw = fs.readFileSync(file, "utf8");
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return empty;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const parsed = JSON.parse(raw);
|
|
69
|
+
if (!isRecord(parsed) ||
|
|
70
|
+
parsed.version !== SCHEMA_VERSION ||
|
|
71
|
+
!isRecord(parsed.paths)) {
|
|
72
|
+
return empty;
|
|
73
|
+
}
|
|
74
|
+
// Pauses are only honoured with the refusal that caused them: it is what
|
|
75
|
+
// the runner reports while it skips the files. A record without a valid
|
|
76
|
+
// refusal (a plan-limit marker and an https upgrade link) is ignored.
|
|
77
|
+
const refusal = parsePlanLimitRefusal(parsed.refusal);
|
|
78
|
+
if (!refusal)
|
|
79
|
+
return empty;
|
|
80
|
+
const paths = {};
|
|
81
|
+
for (const [key, entry] of Object.entries(parsed.paths)) {
|
|
82
|
+
if (isRecord(entry) &&
|
|
83
|
+
typeof entry.pausedAt === "string" &&
|
|
84
|
+
!Number.isNaN(Date.parse(entry.pausedAt))) {
|
|
85
|
+
paths[key] = { pausedAt: entry.pausedAt };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { version: SCHEMA_VERSION, scope, refusal, paths };
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return empty;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** End of `entry`'s pause window if it is in effect at `now`. */
|
|
95
|
+
function activeUntil(entry, now) {
|
|
96
|
+
const pausedAt = Date.parse(entry.pausedAt);
|
|
97
|
+
if (Number.isNaN(pausedAt) || pausedAt > now)
|
|
98
|
+
return undefined;
|
|
99
|
+
const until = pausedAt + PLAN_LIMIT_PAUSE_RETRY_MS;
|
|
100
|
+
if (until - now > PLAN_LIMIT_PAUSE_RETRY_MS || now >= until)
|
|
101
|
+
return undefined;
|
|
102
|
+
return until;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The pause record for one vault. Load once per push pass with
|
|
106
|
+
* {@link PlanLimitPauseStore.open}, consult {@link isPaused} while admitting
|
|
107
|
+
* uploads, then apply the pass's outcome and {@link save}.
|
|
108
|
+
*/
|
|
109
|
+
export class PlanLimitPauseStore {
|
|
110
|
+
/** Null when the scope cannot name a file; the store then never persists. */
|
|
111
|
+
file;
|
|
112
|
+
data;
|
|
113
|
+
dirty = false;
|
|
114
|
+
constructor(file, data) {
|
|
115
|
+
this.file = file;
|
|
116
|
+
this.data = data;
|
|
117
|
+
}
|
|
118
|
+
/** Read the record for `scope`; never throws. */
|
|
119
|
+
static open(scope) {
|
|
120
|
+
let file;
|
|
121
|
+
try {
|
|
122
|
+
file = planLimitPausePath(scope);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
file = null;
|
|
126
|
+
}
|
|
127
|
+
const data = file
|
|
128
|
+
? readPauseFile(file, scope)
|
|
129
|
+
: { version: SCHEMA_VERSION, scope, paths: {} };
|
|
130
|
+
return new PlanLimitPauseStore(file, data);
|
|
131
|
+
}
|
|
132
|
+
/** Last refusal recorded for this vault, if any path is still paused. */
|
|
133
|
+
get refusal() {
|
|
134
|
+
return this.data.refusal;
|
|
135
|
+
}
|
|
136
|
+
/** Number of recorded paths (including ones whose pause has lapsed). */
|
|
137
|
+
get size() {
|
|
138
|
+
return Object.keys(this.data.paths).length;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Epoch ms after which `relativePath` is retried, or undefined when it has
|
|
142
|
+
* no pause in effect at `now`. A pause is in effect only while
|
|
143
|
+
* `pausedAt <= now < pausedAt + PLAN_LIMIT_PAUSE_RETRY_MS`: a future stamp,
|
|
144
|
+
* or an end further than one retry window ahead, is a clock change and is
|
|
145
|
+
* ignored.
|
|
146
|
+
*/
|
|
147
|
+
pausedUntil(relativePath, now = planLimitPauseNow()) {
|
|
148
|
+
const entry = this.data.paths[relativePath];
|
|
149
|
+
if (!entry)
|
|
150
|
+
return undefined;
|
|
151
|
+
return activeUntil(entry, now);
|
|
152
|
+
}
|
|
153
|
+
/** True while `relativePath` is inside its pause window at `now`. */
|
|
154
|
+
isPaused(relativePath, now = planLimitPauseNow()) {
|
|
155
|
+
return this.pausedUntil(relativePath, now) !== undefined;
|
|
156
|
+
}
|
|
157
|
+
/** True when any recorded path is paused at `now`. */
|
|
158
|
+
hasActivePauses(now = planLimitPauseNow()) {
|
|
159
|
+
return Object.values(this.data.paths).some((entry) => activeUntil(entry, now) !== undefined);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Replace the stored refusal details (for example with a fresh 402 from an
|
|
163
|
+
* admission check) without restarting any pause window.
|
|
164
|
+
*/
|
|
165
|
+
refreshRefusal(refusal) {
|
|
166
|
+
if (this.size === 0)
|
|
167
|
+
return;
|
|
168
|
+
this.data.refusal = refusal;
|
|
169
|
+
this.dirty = true;
|
|
170
|
+
}
|
|
171
|
+
/** Record `paths` as refused at `now`, restarting their pause window. */
|
|
172
|
+
pause(paths, refusal, now) {
|
|
173
|
+
if (paths.length === 0)
|
|
174
|
+
return;
|
|
175
|
+
const pausedAt = new Date(now).toISOString();
|
|
176
|
+
for (const p of paths)
|
|
177
|
+
this.data.paths[p] = { pausedAt };
|
|
178
|
+
this.data.refusal = refusal;
|
|
179
|
+
this.dirty = true;
|
|
180
|
+
}
|
|
181
|
+
/** Drop every pause: the plan state changed. */
|
|
182
|
+
clear() {
|
|
183
|
+
if (this.size === 0 && this.data.refusal === undefined)
|
|
184
|
+
return;
|
|
185
|
+
this.data = { version: SCHEMA_VERSION, scope: this.data.scope, paths: {} };
|
|
186
|
+
this.dirty = true;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Persist the record. Lapsed entries are pruned first, so files that were
|
|
190
|
+
* deleted or uploaded elsewhere do not accumulate. An empty record removes
|
|
191
|
+
* the file. Write failures are swallowed: the record is advisory.
|
|
192
|
+
*/
|
|
193
|
+
save(now = planLimitPauseNow()) {
|
|
194
|
+
for (const [key, entry] of Object.entries(this.data.paths)) {
|
|
195
|
+
// Lapsed, future-stamped and over-long entries all go.
|
|
196
|
+
if (activeUntil(entry, now) === undefined) {
|
|
197
|
+
delete this.data.paths[key];
|
|
198
|
+
this.dirty = true;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (!this.dirty || this.file === null)
|
|
202
|
+
return;
|
|
203
|
+
this.dirty = false;
|
|
204
|
+
try {
|
|
205
|
+
if (this.size === 0) {
|
|
206
|
+
fs.rmSync(this.file, { force: true });
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
fs.mkdirSync(path.dirname(this.file), { recursive: true });
|
|
210
|
+
const tmp = `${this.file}.${process.pid}.tmp`;
|
|
211
|
+
fs.writeFileSync(tmp, `${JSON.stringify(this.data)}\n`, { mode: 0o600 });
|
|
212
|
+
fs.renameSync(tmp, this.file);
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Advisory state: a failed write means the next pass presigns again.
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Forget recorded plan-limit refusals so the next sync pass retries paused
|
|
221
|
+
* files at once. Call after a plan change (for example an upgrade). With no
|
|
222
|
+
* `scope`, clears every vault's record.
|
|
223
|
+
*/
|
|
224
|
+
export function clearPlanLimitPauses(scope) {
|
|
225
|
+
if (scope !== undefined) {
|
|
226
|
+
try {
|
|
227
|
+
fs.rmSync(planLimitPausePath(scope), { force: true });
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
// Advisory state.
|
|
231
|
+
}
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
let names;
|
|
235
|
+
try {
|
|
236
|
+
names = fs.readdirSync(getStateDir());
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
for (const name of names) {
|
|
242
|
+
if (!name.startsWith(FILE_PREFIX) || !name.endsWith(FILE_SUFFIX))
|
|
243
|
+
continue;
|
|
244
|
+
try {
|
|
245
|
+
fs.rmSync(path.join(getStateDir(), name), { force: true });
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
// Advisory state.
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=plan-limit-pause.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-limit-pause.js","sourceRoot":"","sources":["../src/plan-limit-pause.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-limit-pause.test.d.ts","sourceRoot":"","sources":["../src/plan-limit-pause.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as os from "os";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { PLAN_LIMIT_PAUSE_RETRY_MS, PlanLimitPauseStore, clearPlanLimitPauses, planLimitPausePath, } from "./plan-limit-pause.js";
|
|
6
|
+
import { parsePlanLimitRefusal } from "./plan-limit.js";
|
|
7
|
+
import { canonicalPlanLimitBody } from "./plan-limit.fixtures.js";
|
|
8
|
+
const refusal = parsePlanLimitRefusal(canonicalPlanLimitBody());
|
|
9
|
+
const T0 = Date.parse("2026-09-28T12:00:00.000Z");
|
|
10
|
+
describe("PlanLimitPauseStore", () => {
|
|
11
|
+
let stateDir;
|
|
12
|
+
let previousStateDir;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
previousStateDir = process.env.HQ_STATE_DIR;
|
|
15
|
+
stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "hq-plan-limit-pause-"));
|
|
16
|
+
process.env.HQ_STATE_DIR = stateDir;
|
|
17
|
+
});
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
if (previousStateDir === undefined)
|
|
20
|
+
delete process.env.HQ_STATE_DIR;
|
|
21
|
+
else
|
|
22
|
+
process.env.HQ_STATE_DIR = previousStateDir;
|
|
23
|
+
fs.rmSync(stateDir, { recursive: true, force: true });
|
|
24
|
+
});
|
|
25
|
+
it("starts empty when no record exists", () => {
|
|
26
|
+
const store = PlanLimitPauseStore.open("cmp_acme");
|
|
27
|
+
expect(store.size).toBe(0);
|
|
28
|
+
expect(store.isPaused("a.md", T0)).toBe(false);
|
|
29
|
+
expect(store.refusal).toBeUndefined();
|
|
30
|
+
});
|
|
31
|
+
it("pauses refused paths for an hour and persists them per vault", () => {
|
|
32
|
+
const store = PlanLimitPauseStore.open("cmp_acme");
|
|
33
|
+
store.pause(["a.md", "b.md"], refusal, T0);
|
|
34
|
+
store.save(T0);
|
|
35
|
+
const file = planLimitPausePath("cmp_acme");
|
|
36
|
+
expect(file).toBe(path.join(stateDir, "plan-limit-pause.cmp_acme.json"));
|
|
37
|
+
expect(fs.existsSync(file)).toBe(true);
|
|
38
|
+
const reopened = PlanLimitPauseStore.open("cmp_acme");
|
|
39
|
+
expect(reopened.isPaused("a.md", T0 + PLAN_LIMIT_PAUSE_RETRY_MS - 1)).toBe(true);
|
|
40
|
+
expect(reopened.isPaused("a.md", T0 + PLAN_LIMIT_PAUSE_RETRY_MS)).toBe(false);
|
|
41
|
+
expect(reopened.isPaused("c.md", T0)).toBe(false);
|
|
42
|
+
expect(reopened.pausedUntil("b.md", T0)).toBe(T0 + PLAN_LIMIT_PAUSE_RETRY_MS);
|
|
43
|
+
expect(reopened.refusal?.upgradeUrl).toBe(refusal.upgradeUrl);
|
|
44
|
+
// Another vault is untouched.
|
|
45
|
+
expect(PlanLimitPauseStore.open("cmp_other").isPaused("a.md", T0)).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
it("clear drops every pause and removes the file", () => {
|
|
48
|
+
const store = PlanLimitPauseStore.open("cmp_acme");
|
|
49
|
+
store.pause(["a.md"], refusal, T0);
|
|
50
|
+
store.save(T0);
|
|
51
|
+
const again = PlanLimitPauseStore.open("cmp_acme");
|
|
52
|
+
again.clear();
|
|
53
|
+
again.save(T0);
|
|
54
|
+
expect(fs.existsSync(planLimitPausePath("cmp_acme"))).toBe(false);
|
|
55
|
+
expect(PlanLimitPauseStore.open("cmp_acme").isPaused("a.md", T0)).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
it("prunes lapsed entries on save", () => {
|
|
58
|
+
const store = PlanLimitPauseStore.open("cmp_acme");
|
|
59
|
+
store.pause(["old.md"], refusal, T0);
|
|
60
|
+
store.pause(["new.md"], refusal, T0 + PLAN_LIMIT_PAUSE_RETRY_MS);
|
|
61
|
+
store.save(T0 + PLAN_LIMIT_PAUSE_RETRY_MS);
|
|
62
|
+
const reopened = PlanLimitPauseStore.open("cmp_acme");
|
|
63
|
+
expect(reopened.size).toBe(1);
|
|
64
|
+
expect(reopened.isPaused("new.md", T0 + PLAN_LIMIT_PAUSE_RETRY_MS)).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
it("ignores a pause stamped in the future (clock moved backwards) and prunes it", () => {
|
|
67
|
+
fs.writeFileSync(planLimitPausePath("cmp_acme"), JSON.stringify({
|
|
68
|
+
version: 1,
|
|
69
|
+
scope: "cmp_acme",
|
|
70
|
+
refusal,
|
|
71
|
+
paths: {
|
|
72
|
+
"future.md": { pausedAt: new Date(T0 + 5 * 60 * 1000).toISOString() },
|
|
73
|
+
"current.md": { pausedAt: new Date(T0 - 5 * 60 * 1000).toISOString() },
|
|
74
|
+
},
|
|
75
|
+
}));
|
|
76
|
+
const store = PlanLimitPauseStore.open("cmp_acme");
|
|
77
|
+
expect(store.isPaused("future.md", T0)).toBe(false);
|
|
78
|
+
expect(store.pausedUntil("future.md", T0)).toBeUndefined();
|
|
79
|
+
expect(store.isPaused("current.md", T0)).toBe(true);
|
|
80
|
+
store.save(T0);
|
|
81
|
+
expect(Object.keys(JSON.parse(fs.readFileSync(planLimitPausePath("cmp_acme"), "utf8")).paths)).toEqual([
|
|
82
|
+
"current.md",
|
|
83
|
+
]);
|
|
84
|
+
});
|
|
85
|
+
it("ignores a pause whose end is further than one retry window ahead", () => {
|
|
86
|
+
const store = PlanLimitPauseStore.open("cmp_acme");
|
|
87
|
+
// Refused at T0, then the clock is set back two hours: the pause would
|
|
88
|
+
// otherwise last three hours.
|
|
89
|
+
store.pause(["a.md"], refusal, T0);
|
|
90
|
+
const earlier = T0 - 2 * PLAN_LIMIT_PAUSE_RETRY_MS;
|
|
91
|
+
expect(store.pausedUntil("a.md", earlier)).toBeUndefined();
|
|
92
|
+
expect(store.isPaused("a.md", earlier)).toBe(false);
|
|
93
|
+
expect(store.hasActivePauses(earlier)).toBe(false);
|
|
94
|
+
expect(store.hasActivePauses(T0)).toBe(true);
|
|
95
|
+
// At the moment of the refusal the window is exactly one retry long.
|
|
96
|
+
expect(store.pausedUntil("a.md", T0)).toBe(T0 + PLAN_LIMIT_PAUSE_RETRY_MS);
|
|
97
|
+
});
|
|
98
|
+
it("ignores a record whose stored refusal is not a valid plan-limit refusal", () => {
|
|
99
|
+
for (const stored of [
|
|
100
|
+
undefined,
|
|
101
|
+
{ upgradeUrl: "https://billing.example.test/x" }, // no marker
|
|
102
|
+
{ ...refusal, upgradeUrl: "http://billing.example.test/x" }, // not https
|
|
103
|
+
]) {
|
|
104
|
+
fs.writeFileSync(planLimitPausePath("cmp_acme"), JSON.stringify({
|
|
105
|
+
version: 1,
|
|
106
|
+
scope: "cmp_acme",
|
|
107
|
+
...(stored ? { refusal: stored } : {}),
|
|
108
|
+
paths: { "a.md": { pausedAt: new Date(T0).toISOString() } },
|
|
109
|
+
}));
|
|
110
|
+
const store = PlanLimitPauseStore.open("cmp_acme");
|
|
111
|
+
expect(store.isPaused("a.md", T0)).toBe(false);
|
|
112
|
+
expect(store.refusal).toBeUndefined();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
it("refreshRefusal replaces the stored details without restarting the window", () => {
|
|
116
|
+
const store = PlanLimitPauseStore.open("cmp_acme");
|
|
117
|
+
store.pause(["a.md"], refusal, T0);
|
|
118
|
+
store.refreshRefusal({ ...refusal, message: "Fresh sentence." });
|
|
119
|
+
store.save(T0 + 1000);
|
|
120
|
+
const reopened = PlanLimitPauseStore.open("cmp_acme");
|
|
121
|
+
expect(reopened.refusal?.message).toBe("Fresh sentence.");
|
|
122
|
+
expect(reopened.pausedUntil("a.md", T0 + 1000)).toBe(T0 + PLAN_LIMIT_PAUSE_RETRY_MS);
|
|
123
|
+
});
|
|
124
|
+
it("treats a corrupt or foreign-version record as empty", () => {
|
|
125
|
+
fs.writeFileSync(planLimitPausePath("cmp_acme"), "{not json");
|
|
126
|
+
expect(PlanLimitPauseStore.open("cmp_acme").size).toBe(0);
|
|
127
|
+
fs.writeFileSync(planLimitPausePath("cmp_acme"), JSON.stringify({ version: 99, paths: { "a.md": { pausedAt: new Date(T0).toISOString() } } }));
|
|
128
|
+
expect(PlanLimitPauseStore.open("cmp_acme").isPaused("a.md", T0)).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
it("never throws for an unusable scope and never writes for it", () => {
|
|
131
|
+
const store = PlanLimitPauseStore.open("");
|
|
132
|
+
store.pause(["a.md"], refusal, T0);
|
|
133
|
+
expect(() => store.save(T0)).not.toThrow();
|
|
134
|
+
expect(fs.readdirSync(stateDir)).toEqual([]);
|
|
135
|
+
});
|
|
136
|
+
it("clearPlanLimitPauses clears one vault or all of them", () => {
|
|
137
|
+
for (const scope of ["cmp_a", "cmp_b", "prs_me"]) {
|
|
138
|
+
const store = PlanLimitPauseStore.open(scope);
|
|
139
|
+
store.pause(["a.md"], refusal, T0);
|
|
140
|
+
store.save(T0);
|
|
141
|
+
}
|
|
142
|
+
fs.writeFileSync(path.join(stateDir, "sync-journal.acme.json"), "{}");
|
|
143
|
+
clearPlanLimitPauses("cmp_a");
|
|
144
|
+
expect(fs.existsSync(planLimitPausePath("cmp_a"))).toBe(false);
|
|
145
|
+
expect(fs.existsSync(planLimitPausePath("cmp_b"))).toBe(true);
|
|
146
|
+
clearPlanLimitPauses();
|
|
147
|
+
expect(fs.readdirSync(stateDir)).toEqual(["sync-journal.acme.json"]);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
//# sourceMappingURL=plan-limit-pause.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-limit-pause.test.js","sourceRoot":"","sources":["../src/plan-limit-pause.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One parser for every shape the vault service uses to refuse a write because
|
|
3
|
+
* the company (or personal vault) is over its plan limits.
|
|
4
|
+
*
|
|
5
|
+
* Shapes accepted:
|
|
6
|
+
*
|
|
7
|
+
* 1. The canonical hard-stop body, sent with HTTP 402:
|
|
8
|
+
* `{ error: "plan_limit_reached", code: "PLAN_LIMIT_EXCEEDED", status: 402,
|
|
9
|
+
* blocked, resources: [{ resource, used, limit }], message, fixOptions,
|
|
10
|
+
* freeMonth?, upgradeUrl }`
|
|
11
|
+
* 2. The legacy membership 402:
|
|
12
|
+
* `{ code: "PLAN_LIMIT_EXCEEDED", resource, used, limit, requiredPlan, upgradeUrl }`
|
|
13
|
+
* 3. A per-item presign row: `{ key, op, error, code: "PLAN_LIMIT_REACHED" |
|
|
14
|
+
* "PLAN_LIMIT_EXCEEDED", upgradeUrl }`. Here `error` is either the machine
|
|
15
|
+
* value `plan_limit_reached` or a human sentence.
|
|
16
|
+
* 4. The sync mutation-prepare skip, sent with HTTP 200:
|
|
17
|
+
* `{ skipped: true, code, message, error, blocked, resources, upgradeUrl }`
|
|
18
|
+
*
|
|
19
|
+
* A body is a plan-limit refusal only when it carries a plan-limit marker
|
|
20
|
+
* (`error: "plan_limit_reached"`, or `code` of `PLAN_LIMIT_EXCEEDED` /
|
|
21
|
+
* `PLAN_LIMIT_REACHED`) and an `https:` `upgradeUrl`. The HTTP status alone
|
|
22
|
+
* never makes a body a plan-limit refusal: other 402s (billing, payment
|
|
23
|
+
* method) keep their own handling.
|
|
24
|
+
*
|
|
25
|
+
* This module has no imports so the vault client, the presign transport and
|
|
26
|
+
* the mutation client can all share it without a cycle.
|
|
27
|
+
*/
|
|
28
|
+
/** Machine `error` value of the canonical hard-stop body. */
|
|
29
|
+
export declare const PLAN_LIMIT_REACHED_ERROR = "plan_limit_reached";
|
|
30
|
+
/** Machine `code` values that mark a plan-limit refusal (current and legacy). */
|
|
31
|
+
export declare const PLAN_LIMIT_CODES: ReadonlySet<string>;
|
|
32
|
+
/** One over-limit resource from the refusal body. */
|
|
33
|
+
export interface PlanLimitResource {
|
|
34
|
+
resource: string;
|
|
35
|
+
used: number;
|
|
36
|
+
limit: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The parts of a plan-limit refusal a client can show: the server's sentence,
|
|
40
|
+
* the upgrade link, and which resources are over.
|
|
41
|
+
*/
|
|
42
|
+
export interface PlanLimitRefusal {
|
|
43
|
+
/** Server-selected billing destination. Always present. */
|
|
44
|
+
upgradeUrl: string;
|
|
45
|
+
/** Customer-facing sentence from the server, when it sent one. */
|
|
46
|
+
message?: string;
|
|
47
|
+
/** `PLAN_LIMIT_EXCEEDED` or `PLAN_LIMIT_REACHED` when the body carried one. */
|
|
48
|
+
code?: string;
|
|
49
|
+
/** `plan_limit_reached` when the body carried the machine error value. */
|
|
50
|
+
error?: string;
|
|
51
|
+
/** The refused action, e.g. `files.create`. */
|
|
52
|
+
blocked?: string;
|
|
53
|
+
/** Over-limit resources with their usage and limit. */
|
|
54
|
+
resources?: PlanLimitResource[];
|
|
55
|
+
/** Trim-back target per resource. */
|
|
56
|
+
fixOptions?: Record<string, number>;
|
|
57
|
+
/** Free-month offer details, passed through as the server sent them. */
|
|
58
|
+
freeMonth?: Record<string, unknown>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* True for an absolute `https:` URL. Upgrade links are opened in a browser
|
|
62
|
+
* or printed for the user to click, so any other scheme is rejected.
|
|
63
|
+
*/
|
|
64
|
+
export declare function isHttpsUpgradeUrl(value: unknown): value is string;
|
|
65
|
+
/** True when `error` or `code` names a plan-limit refusal. */
|
|
66
|
+
export declare function hasPlanLimitMarker(fields: {
|
|
67
|
+
error?: unknown;
|
|
68
|
+
code?: unknown;
|
|
69
|
+
}): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Parse a response body (already JSON-decoded), a per-item presign row, or a
|
|
72
|
+
* stored refusal into a plan-limit refusal. Returns null unless the body has a
|
|
73
|
+
* plan-limit marker and an `https:` upgrade link.
|
|
74
|
+
*/
|
|
75
|
+
export declare function parsePlanLimitRefusal(body: unknown): PlanLimitRefusal | null;
|
|
76
|
+
/** Parse a raw response text; non-JSON text is never a plan-limit refusal. */
|
|
77
|
+
export declare function parsePlanLimitRefusalText(text: string): PlanLimitRefusal | null;
|
|
78
|
+
/**
|
|
79
|
+
* One console line for a `plan-limit` sync event: how many files were
|
|
80
|
+
* refused, the server's sentence, and the upgrade link.
|
|
81
|
+
*/
|
|
82
|
+
export declare function formatPlanLimitNotice(event: {
|
|
83
|
+
upgradeUrl: string;
|
|
84
|
+
count?: number;
|
|
85
|
+
message?: string;
|
|
86
|
+
}): string;
|
|
87
|
+
//# sourceMappingURL=plan-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-limit.d.ts","sourceRoot":"","sources":["../src/plan-limit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,6DAA6D;AAC7D,eAAO,MAAM,wBAAwB,uBAAuB,CAAC;AAE7D,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAG/C,CAAC;AAEH,qDAAqD;AACrD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,SAAS,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAChC,qCAAqC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAcD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAOjE;AAED,8DAA8D;AAC9D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IACzC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAKV;AAmCD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAyB5E;AAED,8EAA8E;AAC9E,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAM/E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,MAAM,CAOT"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan-limit refusal bodies as the vault service sends them, for tests.
|
|
3
|
+
*
|
|
4
|
+
* `canonicalPlanLimitBody` mirrors the hard-stop 402 body the server builds
|
|
5
|
+
* for a Starter company over storage (`error`, `status`, `blocked`,
|
|
6
|
+
* `resources`, `message`, `fixOptions`, `freeMonth`, `upgradeUrl`) plus the
|
|
7
|
+
* `code: "PLAN_LIMIT_EXCEEDED"` older clients key on. The other bodies are
|
|
8
|
+
* the legacy and per-item shapes clients must keep accepting.
|
|
9
|
+
*/
|
|
10
|
+
export declare const PLAN_LIMIT_UPGRADE_URL = "https://billing.example.test/companies/acme/billing";
|
|
11
|
+
/** The canonical HTTP 402 hard-stop body for a company over storage. */
|
|
12
|
+
export declare function canonicalPlanLimitBody(): Record<string, unknown>;
|
|
13
|
+
/** The legacy membership 402 (seat limit). */
|
|
14
|
+
export declare function legacyMembershipPlanLimitBody(): Record<string, unknown>;
|
|
15
|
+
/** A per-item personal-scope presign refusal row. */
|
|
16
|
+
export declare function personalPresignRefusalRow(key: string): Record<string, unknown>;
|
|
17
|
+
/**
|
|
18
|
+
* A per-item company presign refusal row, exactly as the server builds it
|
|
19
|
+
* (`companyPresignRefusalItemError` in the presign handler): the refusal
|
|
20
|
+
* sentence in `error`, the legacy `PLAN_LIMIT_EXCEEDED` code and the upgrade
|
|
21
|
+
* link. The server sends it only for the puts of a MIXED batch; a put-only
|
|
22
|
+
* batch gets the whole-batch 402 body instead.
|
|
23
|
+
*/
|
|
24
|
+
export declare function companyPresignRefusalRow(key: string): Record<string, unknown>;
|
|
25
|
+
/** The admission check's answer when the company is under its limits. */
|
|
26
|
+
export declare function admissionAllowedBody(): Record<string, unknown>;
|
|
27
|
+
/**
|
|
28
|
+
* Today's production answer to an admission body on a presign route that
|
|
29
|
+
* predates the admission check: the body names no `company`, so it is a 400.
|
|
30
|
+
*/
|
|
31
|
+
export declare function admissionUnsupportedBody(): Record<string, unknown>;
|
|
32
|
+
/** Mutation-prepare's HTTP 200 skip body. */
|
|
33
|
+
export declare function mutationPrepareSkippedBody(): Record<string, unknown>;
|
|
34
|
+
//# sourceMappingURL=plan-limit.fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-limit.fixtures.d.ts","sourceRoot":"","sources":["../src/plan-limit.fixtures.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,sBAAsB,wDACoB,CAAC;AAMxD,wEAAwE;AACxE,wBAAgB,sBAAsB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkBhE;AAED,8CAA8C;AAC9C,wBAAgB,6BAA6B,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASvE;AAED,qDAAqD;AACrD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQ9E;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAS7E;AAED,yEAAyE;AACzE,wBAAgB,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE9D;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAElE;AAED,6CAA6C;AAC7C,wBAAgB,0BAA0B,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWpE"}
|