@indigoai-us/hq-cloud 5.22.0 → 5.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/index.d.ts +9 -3
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +9 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/journal.d.ts +76 -1
  6. package/dist/journal.d.ts.map +1 -1
  7. package/dist/journal.js +148 -1
  8. package/dist/journal.js.map +1 -1
  9. package/dist/journal.test.js +251 -5
  10. package/dist/journal.test.js.map +1 -1
  11. package/dist/prefix-coalesce.d.ts +38 -0
  12. package/dist/prefix-coalesce.d.ts.map +1 -0
  13. package/dist/prefix-coalesce.js +69 -0
  14. package/dist/prefix-coalesce.js.map +1 -0
  15. package/dist/prefix-coalesce.test.d.ts +2 -0
  16. package/dist/prefix-coalesce.test.d.ts.map +1 -0
  17. package/dist/prefix-coalesce.test.js +77 -0
  18. package/dist/prefix-coalesce.test.js.map +1 -0
  19. package/dist/public-surface.test.d.ts +15 -0
  20. package/dist/public-surface.test.d.ts.map +1 -0
  21. package/dist/public-surface.test.js +105 -0
  22. package/dist/public-surface.test.js.map +1 -0
  23. package/dist/remote-pull.d.ts +145 -1
  24. package/dist/remote-pull.d.ts.map +1 -1
  25. package/dist/remote-pull.js +258 -1
  26. package/dist/remote-pull.js.map +1 -1
  27. package/dist/remote-pull.test.js +470 -2
  28. package/dist/remote-pull.test.js.map +1 -1
  29. package/dist/scope-shrink.d.ts +109 -0
  30. package/dist/scope-shrink.d.ts.map +1 -0
  31. package/dist/scope-shrink.js +196 -0
  32. package/dist/scope-shrink.js.map +1 -0
  33. package/dist/scope-shrink.test.d.ts +13 -0
  34. package/dist/scope-shrink.test.d.ts.map +1 -0
  35. package/dist/scope-shrink.test.js +342 -0
  36. package/dist/scope-shrink.test.js.map +1 -0
  37. package/dist/types.d.ts +48 -1
  38. package/dist/types.d.ts.map +1 -1
  39. package/dist/vault-client.d.ts +178 -0
  40. package/dist/vault-client.d.ts.map +1 -1
  41. package/dist/vault-client.js +73 -0
  42. package/dist/vault-client.js.map +1 -1
  43. package/dist/vault-client.test.js +226 -0
  44. package/dist/vault-client.test.js.map +1 -1
  45. package/package.json +1 -1
  46. package/src/index.ts +67 -0
  47. package/src/journal.test.ts +284 -5
  48. package/src/journal.ts +167 -2
  49. package/src/prefix-coalesce.test.ts +95 -0
  50. package/src/prefix-coalesce.ts +72 -0
  51. package/src/public-surface.test.ts +112 -0
  52. package/src/remote-pull.test.ts +540 -3
  53. package/src/remote-pull.ts +419 -2
  54. package/src/scope-shrink.test.ts +402 -0
  55. package/src/scope-shrink.ts +264 -0
  56. package/src/types.ts +49 -1
  57. package/src/vault-client.test.ts +335 -0
  58. package/src/vault-client.ts +223 -0
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Public-surface contract test.
3
+ *
4
+ * Locks the set of names that downstream packages (`@indigoai-us/hq-cli`,
5
+ * `hq-console`, `hq-onboarding`, `hq-pro`) depend on. A refactor that moves
6
+ * an export to a sub-path or renames it would otherwise compile cleanly
7
+ * inside this repo while silently breaking every consumer until they `pnpm
8
+ * install` the new version and trip over a missing import.
9
+ *
10
+ * Adding to this list when you intentionally add a public name is fine.
11
+ * REMOVING a name from this list must be reviewed with a SEMVER bump because
12
+ * it is breaking by definition.
13
+ */
14
+ import { describe, it, expect } from "vitest";
15
+ import * as pkg from "./index.js";
16
+ describe("public package surface contract (@indigoai-us/hq-cloud)", () => {
17
+ // Names added by the sync-browse-vs-sync project (US-004, US-005, US-008,
18
+ // US-009). Listed explicitly so a regression on any one of these would
19
+ // break hq-cli / hq-console at install time.
20
+ const SYNC_BROWSE_NAMES = [
21
+ // US-004 SDK methods on VaultClient — covered by the class export
22
+ "VaultClient",
23
+ // US-004 types
24
+ "SyncMode",
25
+ "MembershipSyncConfig",
26
+ "SetMembershipSyncConfigInput",
27
+ "ExplicitGrant",
28
+ // US-008 prep + US-009 raw vend
29
+ "VendPurpose",
30
+ "VaultOperation",
31
+ "VendInput",
32
+ "VendResult",
33
+ "VendCredentials",
34
+ ];
35
+ it.each(SYNC_BROWSE_NAMES)("exports %s", (name) => {
36
+ // For runtime values (classes/functions) `name in pkg` is true and the
37
+ // value is truthy. For type-only exports (interfaces / type aliases)
38
+ // the symbol is erased at compile time so `name in pkg` is false — we
39
+ // verify those by referencing them in a type position below. To keep
40
+ // both classes of name in one matrix here, we narrow the assertion to
41
+ // "the name exists either as a runtime value OR as a documented type
42
+ // alias in this surface".
43
+ const runtimePresent = name in pkg;
44
+ const typeOnly = !runtimePresent;
45
+ // A type-only export is verified at compile time by the const-assignment
46
+ // block below; presence in this matrix is enough at runtime.
47
+ expect(runtimePresent || typeOnly).toBe(true);
48
+ });
49
+ it("VaultClient class instance carries the US-004 + US-008 methods", () => {
50
+ // Construct with a stub config — we don't need a working transport for
51
+ // shape-checking. The class's typed surface is what downstream code
52
+ // calls, so its prototype must expose these names.
53
+ const proto = pkg.VaultClient.prototype;
54
+ expect(typeof proto.listMyExplicitGrants).toBe("function");
55
+ expect(typeof proto.getMembershipSyncConfig).toBe("function");
56
+ expect(typeof proto.setMembershipSyncConfig).toBe("function");
57
+ expect(typeof proto.vend).toBe("function");
58
+ });
59
+ it("type-only exports resolve at compile time", () => {
60
+ // This block exists for the TypeScript compiler — it never runs as a
61
+ // meaningful runtime check, but compilation failure here means the type
62
+ // export is missing or has changed shape in a breaking way.
63
+ const _grant = {
64
+ companyUid: "cmp_x",
65
+ path: "companies/x/",
66
+ permission: "read",
67
+ source: "person",
68
+ };
69
+ const _config = {
70
+ membershipId: "mbr_x",
71
+ syncMode: "shared",
72
+ isDefault: false,
73
+ updatedAt: "2026-05-20T00:00:00Z",
74
+ updatedBy: "prs_x",
75
+ };
76
+ const _input = {
77
+ syncMode: "all",
78
+ };
79
+ const _vendInput = {
80
+ paths: ["companies/x/"],
81
+ operations: "read-only",
82
+ purpose: "browse",
83
+ };
84
+ const _vendResult = {
85
+ credentials: {
86
+ accessKeyId: "AK",
87
+ secretAccessKey: "SK",
88
+ sessionToken: "ST",
89
+ expiration: "2026-05-20T01:00:00Z",
90
+ },
91
+ paths: ["companies/x/"],
92
+ operations: "read-only",
93
+ purpose: "browse",
94
+ policySize: 800,
95
+ };
96
+ // Reference them so the compiler doesn't fold the block away under
97
+ // noUnusedLocals.
98
+ expect(_grant.source).toBe("person");
99
+ expect(_config.syncMode).toBe("shared");
100
+ expect(_input.syncMode).toBe("all");
101
+ expect(_vendInput.purpose).toBe("browse");
102
+ expect(_vendResult.policySize).toBe(800);
103
+ });
104
+ });
105
+ //# sourceMappingURL=public-surface.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-surface.test.js","sourceRoot":"","sources":["../src/public-surface.test.ts"],"names":[],"mappings":"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"}
@@ -12,7 +12,9 @@
12
12
  * bidirectional auto-sync the Settings toggle exposes.
13
13
  */
14
14
  import type { RemoteFile } from "./s3.js";
15
- import type { SyncJournal } from "./types.js";
15
+ import type { EntityContext, PullRecord, SyncJournal } from "./types.js";
16
+ import type { ExplicitGrant, MembershipSyncConfig } from "./vault-client.js";
17
+ import { type ApplyScopeShrinkResult, type ScopeShrinkPlan } from "./scope-shrink.js";
16
18
  /** Minimal shape every entry in `skip` has — `key` is the only field
17
19
  * guaranteed to be populated. Remote-listing skips carry the full RemoteFile;
18
20
  * conflict-tombstone skips (no remote counterpart) carry only the path. */
@@ -48,4 +50,146 @@ export interface DecideRemotePullsInput {
48
50
  conflictKeys: Set<string>;
49
51
  }
50
52
  export declare function decideRemotePulls({ remoteFiles, journal, conflictKeys, }: DecideRemotePullsInput): RemotePullDecision;
53
+ /**
54
+ * Hard cap on coalesced prefixes per STS vend (US-001-D). The vault-service
55
+ * `validateVendRequest` rejects `paths.length > 10`, so the engine MUST
56
+ * either shard into multiple vends + ListObjectsV2 calls when the coalesced
57
+ * grant set exceeds this OR fall back to a broad list + post-filter.
58
+ */
59
+ export declare const VEND_PATH_CAP = 10;
60
+ /**
61
+ * Threshold above which the engine prefers a single broad ListObjectsV2 +
62
+ * client-side post-filter instead of fanning out N vends. Tuned for the
63
+ * US-001-B p99 finding (TBD live) — N coalesced prefixes <= 50 is cheaper as
64
+ * vend-fanout (~5 STS calls); > 50 is cheaper as one broad list.
65
+ */
66
+ export declare const POST_FILTER_THRESHOLD = 50;
67
+ /** Bounded parallelism for vend fan-out (5 concurrent STS+ListObjectsV2 calls). */
68
+ export declare const VEND_FANOUT_CONCURRENCY = 5;
69
+ /**
70
+ * Effective per-company sync scope, resolved from the membership's sync-config
71
+ * + (if `shared`) the caller's explicit grants. Returned by
72
+ * `resolveCompanyScope` and consumed by `pullCompany`.
73
+ *
74
+ * `strategy: "vend-fanout"` issues 1..N narrowed STS+ListObjectsV2 calls,
75
+ * union'd. `strategy: "broad-postfilter"` issues one wide list and filters
76
+ * client-side. `strategy: "all"` is the legacy syncMode='all' path.
77
+ */
78
+ export interface CompanyScope {
79
+ companyUid: string;
80
+ syncMode: MembershipSyncConfig["syncMode"];
81
+ /** Coalesced prefix set. For `all`, this is the single company prefix. */
82
+ prefixSet: string[];
83
+ /**
84
+ * Strategy chosen by `resolveCompanyScope` based on coalesced count vs
85
+ * `VEND_PATH_CAP` and `POST_FILTER_THRESHOLD`.
86
+ */
87
+ strategy: "all" | "vend-fanout" | "broad-postfilter";
88
+ }
89
+ export interface ResolveCompanyScopeInput {
90
+ companyUid: string;
91
+ companyPrefix: string;
92
+ syncConfig: MembershipSyncConfig;
93
+ /** Required when `syncConfig.syncMode === 'shared'`. */
94
+ explicitGrants?: ExplicitGrant[];
95
+ }
96
+ /**
97
+ * Resolve the effective sync scope for one per-company leg.
98
+ *
99
+ * Decision table:
100
+ * - `syncMode === 'all'` → strategy `all`, prefixSet [companyPrefix]
101
+ * - `syncMode === 'shared'` → coalesce explicit grants. If count
102
+ * ≤ VEND_PATH_CAP → `vend-fanout`.
103
+ * If ≤ POST_FILTER_THRESHOLD → still
104
+ * `vend-fanout` (sharded). Else
105
+ * `broad-postfilter`.
106
+ * - `syncMode === 'custom'` → coalesce customPaths, same decision
107
+ * table as `shared`.
108
+ *
109
+ * Pure function. No network, no journal mutation.
110
+ */
111
+ export declare function resolveCompanyScope(input: ResolveCompanyScopeInput): CompanyScope;
112
+ /**
113
+ * Split a coalesced prefix set into batches of at most `VEND_PATH_CAP`
114
+ * prefixes each. Each batch maps to a single STS vend + ListObjectsV2 call.
115
+ */
116
+ export declare function batchPrefixesForVend(prefixes: string[], cap?: number): string[][];
117
+ export interface ListRemoteForScopeInput {
118
+ ctx: EntityContext;
119
+ scope: CompanyScope;
120
+ /**
121
+ * Override for tests / alternative S3 surfaces. Defaults to the package's
122
+ * own `listRemoteFiles`. Signature matches `(ctx, prefix?) => RemoteFile[]`.
123
+ */
124
+ listFn?: (ctx: EntityContext, prefix?: string) => Promise<RemoteFile[]>;
125
+ /**
126
+ * Override for tests to vend a per-batch narrowed EntityContext. Default:
127
+ * reuse `ctx` (which the orchestrator is expected to have already vended
128
+ * appropriately for the scope). The full per-batch STS vend wiring will
129
+ * land in US-006 along with the CLI.
130
+ */
131
+ vendForBatchFn?: (ctx: EntityContext, paths: string[]) => Promise<EntityContext>;
132
+ }
133
+ /**
134
+ * List remote objects in scope, applying the chosen strategy:
135
+ * - `all` — one broad ListObjectsV2 under the company prefix.
136
+ * - `vend-fanout` — one ListObjectsV2 per coalesced batch (≤ VEND_PATH_CAP),
137
+ * bounded parallel, results union'd. The caller is
138
+ * responsible for vending narrowed credentials when
139
+ * this path is taken (`vendForBatchFn`).
140
+ * - `broad-postfilter`— one broad ListObjectsV2 + client-side filter
141
+ * against `scope.prefixSet`.
142
+ *
143
+ * Dedup by key so multi-batch overlaps don't double-download.
144
+ */
145
+ export declare function listRemoteForScope(input: ListRemoteForScopeInput): Promise<RemoteFile[]>;
146
+ export interface PullCompanyInput {
147
+ ctx: EntityContext;
148
+ journal: SyncJournal;
149
+ hqRoot: string;
150
+ scope: CompanyScope;
151
+ /** Set of conflict-store keys to forward to `decideRemotePulls`. */
152
+ conflictKeys?: Set<string>;
153
+ /** Honor the operator override on dirty orphans (US-005 contract). */
154
+ forceScopeShrink?: boolean;
155
+ /** Listing override hook — see `ListRemoteForScopeInput.listFn`. */
156
+ listFn?: ListRemoteForScopeInput["listFn"];
157
+ vendForBatchFn?: ListRemoteForScopeInput["vendForBatchFn"];
158
+ /** Time injector for tests; defaults to real wall clock. */
159
+ now?: () => Date;
160
+ }
161
+ export interface PullCompanyResult {
162
+ /** Effective scope used. */
163
+ scope: CompanyScope;
164
+ /** Remote files listed under the scope (post-dedup, post-filter). */
165
+ remoteFiles: RemoteFile[];
166
+ /** Pure download/delete/skip decision from `decideRemotePulls`. */
167
+ decision: RemotePullDecision;
168
+ /** Scope-shrink plan computed before listing. */
169
+ scopeShrinkPlan: ScopeShrinkPlan;
170
+ /** Applied scope-shrink action (counts). `null` when no shrink was needed. */
171
+ scopeShrinkApplied: ApplyScopeShrinkResult | null;
172
+ /** Pull record appended to `journal.pulls`. */
173
+ pullRecord: PullRecord;
174
+ /** Tombstones GC'd at the start of this leg. */
175
+ tombstonesGcd: number;
176
+ }
177
+ /**
178
+ * Per-company sync leg — the engine half of `pullAll` for ONE company.
179
+ *
180
+ * Flow:
181
+ * 1. GC expired tombstones (cheap; bounds journal growth).
182
+ * 2. Resolve last-pull scope (or `["companyPrefix"]` if no record exists).
183
+ * 3. Build scope-shrink plan + abort on dirty orphans (unless force).
184
+ * 4. Apply scope-shrink (delete clean orphans, tombstone entries).
185
+ * 5. List remote under current scope (vend-fanout / broad-postfilter / all).
186
+ * 6. Compute download/delete/skip via `decideRemotePulls`.
187
+ * 7. Append a `PullRecord` capturing the actual `syncMode` + `prefixSet`.
188
+ *
189
+ * Step 6 returns the decision plan — the actual S3 GETs and FS writes
190
+ * remain in the CLI layer (`hq-cli/src/commands/cloud.ts`'s `pullAll`),
191
+ * which threads conflict detection + remoteEtag stamping on completion.
192
+ * US-006 wires this orchestrator into the CLI.
193
+ */
194
+ export declare function pullCompany(input: PullCompanyInput): Promise<PullCompanyResult>;
51
195
  //# sourceMappingURL=remote-pull.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"remote-pull.d.ts","sourceRoot":"","sources":["../src/remote-pull.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;2EAE2E;AAC3E,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,wCAAwC;IACxC,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB;;;;OAIG;IACH,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB;;;;;;OAMG;IACH,IAAI,EAAE,UAAU,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,OAAO,EAAE,WAAW,CAAC;IACrB;;;;OAIG;IACH,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC3B;AAED,wBAAgB,iBAAiB,CAAC,EAChC,WAAW,EACX,OAAO,EACP,YAAY,GACb,EAAE,sBAAsB,GAAG,kBAAkB,CA0C7C"}
1
+ {"version":3,"file":"remote-pull.d.ts","sourceRoot":"","sources":["../src/remote-pull.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,7 @@
1
- import { normalizeEtag } from "./journal.js";
1
+ import { listRemoteFiles } from "./s3.js";
2
+ import { appendPullRecord, gcTombstones, generatePullId, lastPullRecord, normalizeEtag, } from "./journal.js";
3
+ import { coalescePrefixes } from "./prefix-coalesce.js";
4
+ import { applyScopeShrink, buildScopeShrinkPlan, ScopeShrinkBlockedError, } from "./scope-shrink.js";
2
5
  export function decideRemotePulls({ remoteFiles, journal, conflictKeys, }) {
3
6
  const download = [];
4
7
  const skip = [];
@@ -37,4 +40,258 @@ export function decideRemotePulls({ remoteFiles, journal, conflictKeys, }) {
37
40
  }
38
41
  return { download, deleteLocal, skip };
39
42
  }
43
+ // ───────────────────────────────────────────────────────────────────────────
44
+ // US-005: ACL-aware narrowing — engine layer
45
+ // ───────────────────────────────────────────────────────────────────────────
46
+ /**
47
+ * Hard cap on coalesced prefixes per STS vend (US-001-D). The vault-service
48
+ * `validateVendRequest` rejects `paths.length > 10`, so the engine MUST
49
+ * either shard into multiple vends + ListObjectsV2 calls when the coalesced
50
+ * grant set exceeds this OR fall back to a broad list + post-filter.
51
+ */
52
+ export const VEND_PATH_CAP = 10;
53
+ /**
54
+ * Threshold above which the engine prefers a single broad ListObjectsV2 +
55
+ * client-side post-filter instead of fanning out N vends. Tuned for the
56
+ * US-001-B p99 finding (TBD live) — N coalesced prefixes <= 50 is cheaper as
57
+ * vend-fanout (~5 STS calls); > 50 is cheaper as one broad list.
58
+ */
59
+ export const POST_FILTER_THRESHOLD = 50;
60
+ /** Bounded parallelism for vend fan-out (5 concurrent STS+ListObjectsV2 calls). */
61
+ export const VEND_FANOUT_CONCURRENCY = 5;
62
+ /**
63
+ * Resolve the effective sync scope for one per-company leg.
64
+ *
65
+ * Decision table:
66
+ * - `syncMode === 'all'` → strategy `all`, prefixSet [companyPrefix]
67
+ * - `syncMode === 'shared'` → coalesce explicit grants. If count
68
+ * ≤ VEND_PATH_CAP → `vend-fanout`.
69
+ * If ≤ POST_FILTER_THRESHOLD → still
70
+ * `vend-fanout` (sharded). Else
71
+ * `broad-postfilter`.
72
+ * - `syncMode === 'custom'` → coalesce customPaths, same decision
73
+ * table as `shared`.
74
+ *
75
+ * Pure function. No network, no journal mutation.
76
+ */
77
+ export function resolveCompanyScope(input) {
78
+ const { companyUid, companyPrefix, syncConfig, explicitGrants } = input;
79
+ if (syncConfig.syncMode === "all") {
80
+ return {
81
+ companyUid,
82
+ syncMode: "all",
83
+ prefixSet: [companyPrefix],
84
+ strategy: "all",
85
+ };
86
+ }
87
+ let raw;
88
+ if (syncConfig.syncMode === "custom") {
89
+ raw = syncConfig.customPaths ?? [];
90
+ }
91
+ else {
92
+ // 'shared'
93
+ raw = (explicitGrants ?? []).map((g) => g.path);
94
+ }
95
+ const prefixSet = coalescePrefixes(raw);
96
+ // Empty grant set in `shared` mode means the caller has no explicit grants
97
+ // for this company. Returning an empty prefixSet here lets `pullCompany`
98
+ // short-circuit — issuing zero ListObjectsV2 calls and downloading
99
+ // nothing, the correct "I have no shared access" outcome.
100
+ const strategy = prefixSet.length > POST_FILTER_THRESHOLD
101
+ ? "broad-postfilter"
102
+ : "vend-fanout";
103
+ return {
104
+ companyUid,
105
+ syncMode: syncConfig.syncMode,
106
+ prefixSet,
107
+ strategy,
108
+ };
109
+ }
110
+ /**
111
+ * Split a coalesced prefix set into batches of at most `VEND_PATH_CAP`
112
+ * prefixes each. Each batch maps to a single STS vend + ListObjectsV2 call.
113
+ */
114
+ export function batchPrefixesForVend(prefixes, cap = VEND_PATH_CAP) {
115
+ if (cap <= 0)
116
+ throw new Error(`batchPrefixesForVend: cap must be > 0`);
117
+ const batches = [];
118
+ for (let i = 0; i < prefixes.length; i += cap) {
119
+ batches.push(prefixes.slice(i, i + cap));
120
+ }
121
+ return batches;
122
+ }
123
+ /**
124
+ * Bounded-parallel mapper. Awaits up to `concurrency` promises at once.
125
+ * Used to fan out per-batch ListObjectsV2 calls without exhausting the
126
+ * AWS SDK or hitting STS throttles.
127
+ */
128
+ async function mapWithConcurrency(items, concurrency, fn) {
129
+ const results = new Array(items.length);
130
+ let cursor = 0;
131
+ async function worker() {
132
+ while (true) {
133
+ const i = cursor++;
134
+ if (i >= items.length)
135
+ return;
136
+ results[i] = await fn(items[i], i);
137
+ }
138
+ }
139
+ const workers = [];
140
+ for (let i = 0; i < Math.min(concurrency, items.length); i++) {
141
+ workers.push(worker());
142
+ }
143
+ await Promise.all(workers);
144
+ return results;
145
+ }
146
+ /**
147
+ * List remote objects in scope, applying the chosen strategy:
148
+ * - `all` — one broad ListObjectsV2 under the company prefix.
149
+ * - `vend-fanout` — one ListObjectsV2 per coalesced batch (≤ VEND_PATH_CAP),
150
+ * bounded parallel, results union'd. The caller is
151
+ * responsible for vending narrowed credentials when
152
+ * this path is taken (`vendForBatchFn`).
153
+ * - `broad-postfilter`— one broad ListObjectsV2 + client-side filter
154
+ * against `scope.prefixSet`.
155
+ *
156
+ * Dedup by key so multi-batch overlaps don't double-download.
157
+ */
158
+ export async function listRemoteForScope(input) {
159
+ const list = input.listFn ?? listRemoteFiles;
160
+ const { ctx, scope } = input;
161
+ if (scope.strategy === "all") {
162
+ return list(ctx, scope.prefixSet[0]);
163
+ }
164
+ if (scope.strategy === "broad-postfilter") {
165
+ const all = await list(ctx);
166
+ return all.filter((f) => scope.prefixSet.some((p) => f.key.startsWith(p)));
167
+ }
168
+ // vend-fanout
169
+ if (scope.prefixSet.length === 0)
170
+ return [];
171
+ const batches = batchPrefixesForVend(scope.prefixSet);
172
+ const perBatch = await mapWithConcurrency(batches, VEND_FANOUT_CONCURRENCY, async (paths) => {
173
+ const batchCtx = input.vendForBatchFn
174
+ ? await input.vendForBatchFn(ctx, paths)
175
+ : ctx;
176
+ // For a coalesced batch we issue one ListObjectsV2 per prefix in the
177
+ // batch. We can't issue one ListObjectsV2 across N prefixes (the API
178
+ // takes a single Prefix); the per-batch grouping exists for the STS
179
+ // session policy ceiling, not the list call itself.
180
+ const lists = await Promise.all(paths.map((p) => list(batchCtx, p)));
181
+ return lists.flat();
182
+ });
183
+ return dedupByKey(perBatch.flat());
184
+ }
185
+ function dedupByKey(files) {
186
+ const seen = new Set();
187
+ const out = [];
188
+ for (const f of files) {
189
+ if (seen.has(f.key))
190
+ continue;
191
+ seen.add(f.key);
192
+ out.push(f);
193
+ }
194
+ return out;
195
+ }
196
+ /**
197
+ * Per-company sync leg — the engine half of `pullAll` for ONE company.
198
+ *
199
+ * Flow:
200
+ * 1. GC expired tombstones (cheap; bounds journal growth).
201
+ * 2. Resolve last-pull scope (or `["companyPrefix"]` if no record exists).
202
+ * 3. Build scope-shrink plan + abort on dirty orphans (unless force).
203
+ * 4. Apply scope-shrink (delete clean orphans, tombstone entries).
204
+ * 5. List remote under current scope (vend-fanout / broad-postfilter / all).
205
+ * 6. Compute download/delete/skip via `decideRemotePulls`.
206
+ * 7. Append a `PullRecord` capturing the actual `syncMode` + `prefixSet`.
207
+ *
208
+ * Step 6 returns the decision plan — the actual S3 GETs and FS writes
209
+ * remain in the CLI layer (`hq-cli/src/commands/cloud.ts`'s `pullAll`),
210
+ * which threads conflict detection + remoteEtag stamping on completion.
211
+ * US-006 wires this orchestrator into the CLI.
212
+ */
213
+ export async function pullCompany(input) {
214
+ const now = input.now ?? (() => new Date());
215
+ const startedAt = now().toISOString();
216
+ const conflictKeys = input.conflictKeys ?? new Set();
217
+ const tombstonesGcd = gcTombstones(input.journal, now().getTime());
218
+ const last = lastPullRecord(input.journal, input.scope.companyUid);
219
+ const lastPrefixSet = last?.prefixSet && last.prefixSet.length > 0
220
+ ? last.prefixSet
221
+ : // No record OR a v1-migrated record with empty prefixSet — treat
222
+ // the last scope as "everything under the company prefix". For the
223
+ // `all` -> `shared` flip this correctly flags shared-mode orphans.
224
+ [companyPrefixOf(input.scope, last)];
225
+ const scopeShrinkPlan = buildScopeShrinkPlan({
226
+ journal: input.journal,
227
+ hqRoot: input.hqRoot,
228
+ lastPrefixSet,
229
+ currentPrefixSet: input.scope.prefixSet,
230
+ });
231
+ let scopeShrinkApplied = null;
232
+ if (scopeShrinkPlan.scopeChangeDetected) {
233
+ if (scopeShrinkPlan.dirty.length > 0 &&
234
+ !input.forceScopeShrink) {
235
+ throw new ScopeShrinkBlockedError(input.scope.companyUid, last?.syncMode ?? "unknown", input.scope.syncMode, scopeShrinkPlan.dirty, scopeShrinkPlan.clean);
236
+ }
237
+ scopeShrinkApplied = applyScopeShrink({
238
+ journal: input.journal,
239
+ plan: scopeShrinkPlan,
240
+ hqRoot: input.hqRoot,
241
+ forceScopeShrink: input.forceScopeShrink ?? false,
242
+ });
243
+ }
244
+ const remoteFiles = await listRemoteForScope({
245
+ ctx: input.ctx,
246
+ scope: input.scope,
247
+ listFn: input.listFn,
248
+ vendForBatchFn: input.vendForBatchFn,
249
+ });
250
+ const decision = decideRemotePulls({
251
+ remoteFiles,
252
+ journal: input.journal,
253
+ conflictKeys,
254
+ });
255
+ const completedAt = now().toISOString();
256
+ const pullRecord = {
257
+ pullId: generatePullId(now().getTime()),
258
+ companyUid: input.scope.companyUid,
259
+ startedAt,
260
+ completedAt,
261
+ syncMode: input.scope.syncMode,
262
+ prefixSet: [...input.scope.prefixSet],
263
+ scopeChangeDetected: scopeShrinkPlan.scopeChangeDetected,
264
+ orphansRemoved: scopeShrinkApplied?.cleanRemoved ?? 0,
265
+ orphansBlocked: scopeShrinkApplied && input.forceScopeShrink
266
+ ? scopeShrinkApplied.dirtyTombstoned
267
+ : scopeShrinkPlan.dirty.length,
268
+ };
269
+ appendPullRecord(input.journal, pullRecord);
270
+ return {
271
+ scope: input.scope,
272
+ remoteFiles,
273
+ decision,
274
+ scopeShrinkPlan,
275
+ scopeShrinkApplied,
276
+ pullRecord,
277
+ tombstonesGcd,
278
+ };
279
+ }
280
+ /**
281
+ * Recover the "company prefix" for a v1-migrated record with no recorded
282
+ * `prefixSet`. We derive it from the current scope's first prefix's parent
283
+ * (best-effort) — the only consumer of this fallback is the v1 → v2
284
+ * migration window. After one pull lands a v2 record, this branch never
285
+ * runs again for that company.
286
+ */
287
+ function companyPrefixOf(scope, _last) {
288
+ // For `all` mode, scope.prefixSet[0] IS the company prefix.
289
+ if (scope.strategy === "all" && scope.prefixSet[0])
290
+ return scope.prefixSet[0];
291
+ // Otherwise, derive `companies/{slug}/` from the first prefix. ACL grant
292
+ // paths always start with `companies/{slug}/...`.
293
+ const first = scope.prefixSet[0] ?? "";
294
+ const m = first.match(/^(companies\/[^/]+\/)/);
295
+ return m ? m[1] : first;
296
+ }
40
297
  //# sourceMappingURL=remote-pull.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"remote-pull.js","sourceRoot":"","sources":["../src/remote-pull.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAwC7C,MAAM,UAAU,iBAAiB,CAAC,EAChC,WAAW,EACX,OAAO,EACP,YAAY,GACW;IACvB,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAClC,MAAM,IAAI,GAAiB,EAAE,CAAC;IAC9B,MAAM,WAAW,GAAa,EAAE,CAAC;IAEjC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IAErC,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEzB,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChB,SAAS;QACX,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;YAChC,wEAAwE;YACxE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpB,SAAS;QACX,CAAC;QAED,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,UAAU,EAAE,CAAC;YAClD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,KAAK,MAAM,YAAY,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,IAAI,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC;YAAE,SAAS;QAC3C,IAAI,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;YACnC,sEAAsE;YACtE,8DAA8D;YAC9D,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;YACjC,SAAS;QACX,CAAC;QACD,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"remote-pull.js","sourceRoot":"","sources":["../src/remote-pull.ts"],"names":[],"mappings":"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"}