@cortexkit/antigravity-auth-core 3.0.5 → 3.0.7

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 (121) hide show
  1. package/dist/account-manager.d.ts +15 -23
  2. package/dist/account-manager.d.ts.map +1 -1
  3. package/dist/account-manager.js +18 -32
  4. package/dist/account-manager.js.map +1 -1
  5. package/dist/account-migration.d.ts +1 -1
  6. package/dist/account-migration.d.ts.map +1 -1
  7. package/dist/account-migration.js +228 -53
  8. package/dist/account-migration.js.map +1 -1
  9. package/dist/account-repository-types.d.ts +1 -1
  10. package/dist/account-selector.d.ts +13 -27
  11. package/dist/account-selector.d.ts.map +1 -1
  12. package/dist/account-selector.js +38 -145
  13. package/dist/account-selector.js.map +1 -1
  14. package/dist/account-types.d.ts +1 -3
  15. package/dist/account-types.d.ts.map +1 -1
  16. package/dist/agy-transport.d.ts.map +1 -1
  17. package/dist/agy-transport.js +2 -0
  18. package/dist/agy-transport.js.map +1 -1
  19. package/dist/antigravity/oauth.d.ts +5 -1
  20. package/dist/antigravity/oauth.d.ts.map +1 -1
  21. package/dist/antigravity/oauth.js +43 -9
  22. package/dist/antigravity/oauth.js.map +1 -1
  23. package/dist/antigravity-command-menu.d.ts +4 -8
  24. package/dist/antigravity-command-menu.d.ts.map +1 -1
  25. package/dist/antigravity-command-menu.js +14 -37
  26. package/dist/antigravity-command-menu.js.map +1 -1
  27. package/dist/common-auth-embedded/claustrum/consumer.d.ts +44 -4
  28. package/dist/common-auth-embedded/claustrum/consumer.js +134 -2
  29. package/dist/common-auth-embedded/claustrum/custody.d.ts +33 -1
  30. package/dist/common-auth-embedded/claustrum/custody.js +17 -0
  31. package/dist/common-auth-embedded/claustrum/enrollment.d.ts +6 -1
  32. package/dist/common-auth-embedded/claustrum/enrollment.js +6 -1
  33. package/dist/common-auth-embedded/claustrum/index.d.ts +3 -2
  34. package/dist/common-auth-embedded/claustrum/index.js +5 -3
  35. package/dist/common-auth-embedded/claustrum/insula.d.ts +69 -0
  36. package/dist/common-auth-embedded/claustrum/insula.js +152 -0
  37. package/dist/common-auth-embedded/claustrum/roster.d.ts +13 -0
  38. package/dist/common-auth-embedded/claustrum/roster.js +33 -3
  39. package/dist/common-auth-embedded/fs/refresh-file-lock.d.ts +8 -2
  40. package/dist/common-auth-embedded/fs/refresh-file-lock.js +26 -12
  41. package/dist/common-auth-embedded/pi-slot/lock.js +7 -3
  42. package/dist/common-auth-embedded/quota/codec.d.ts +2 -0
  43. package/dist/common-auth-embedded/quota/codec.js +2 -1
  44. package/dist/common-auth-embedded/quota/format.d.ts +40 -3
  45. package/dist/common-auth-embedded/quota/format.js +196 -43
  46. package/dist/common-auth-embedded/quota/index.d.ts +4 -4
  47. package/dist/common-auth-embedded/quota/index.js +2 -2
  48. package/dist/common-auth-embedded/quota/map.d.ts +11 -0
  49. package/dist/common-auth-embedded/quota/map.js +36 -1
  50. package/dist/common-auth-embedded/quota/merge.d.ts +3 -1
  51. package/dist/common-auth-embedded/quota/merge.js +21 -5
  52. package/dist/common-auth-embedded/quota/projection.d.ts +2 -1
  53. package/dist/common-auth-embedded/quota/projection.js +3 -1
  54. package/dist/common-auth-embedded/source-output.json +117 -101
  55. package/dist/common-auth-embedded/store/pool.d.ts +1 -1
  56. package/dist/common-auth-embedded/store/pool.js +0 -1
  57. package/dist/common-auth-embedded/store/refresh-lock.d.ts +0 -1
  58. package/dist/common-auth-embedded/store/refresh-lock.js +0 -1
  59. package/dist/common-auth-embedded/store/schema.d.ts +2 -0
  60. package/dist/common-auth-embedded/store/schema.js +3 -2
  61. package/dist/common-auth-runtime.d.ts +2 -1
  62. package/dist/common-auth-runtime.d.ts.map +1 -1
  63. package/dist/common-auth-runtime.js +2 -1
  64. package/dist/common-auth-runtime.js.map +1 -1
  65. package/dist/constants.d.ts +1 -26
  66. package/dist/constants.d.ts.map +1 -1
  67. package/dist/constants.js +1 -37
  68. package/dist/constants.js.map +1 -1
  69. package/dist/credential-redaction.d.ts +8 -4
  70. package/dist/credential-redaction.d.ts.map +1 -1
  71. package/dist/credential-redaction.js +55 -28
  72. package/dist/credential-redaction.js.map +1 -1
  73. package/dist/index.d.ts +5 -1
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +5 -1
  76. package/dist/index.js.map +1 -1
  77. package/dist/model-registry.d.ts +0 -1
  78. package/dist/model-registry.d.ts.map +1 -1
  79. package/dist/model-registry.js +3 -60
  80. package/dist/model-registry.js.map +1 -1
  81. package/dist/pi-paths.d.ts +14 -0
  82. package/dist/pi-paths.d.ts.map +1 -0
  83. package/dist/pi-paths.js +27 -0
  84. package/dist/pi-paths.js.map +1 -0
  85. package/dist/pre-response-deadline.d.ts +41 -0
  86. package/dist/pre-response-deadline.d.ts.map +1 -0
  87. package/dist/pre-response-deadline.js +150 -0
  88. package/dist/pre-response-deadline.js.map +1 -0
  89. package/dist/quota-manager.d.ts +7 -32
  90. package/dist/quota-manager.d.ts.map +1 -1
  91. package/dist/quota-manager.js +27 -76
  92. package/dist/quota-manager.js.map +1 -1
  93. package/dist/quota-types.d.ts +2 -11
  94. package/dist/quota-types.d.ts.map +1 -1
  95. package/dist/transform/index.d.ts +1 -1
  96. package/dist/transform/index.d.ts.map +1 -1
  97. package/dist/transform/index.js +1 -1
  98. package/dist/transform/index.js.map +1 -1
  99. package/dist/transform/model-resolver.d.ts +4 -28
  100. package/dist/transform/model-resolver.d.ts.map +1 -1
  101. package/dist/transform/model-resolver.js +33 -130
  102. package/dist/transform/model-resolver.js.map +1 -1
  103. package/dist/transform/types.d.ts +0 -5
  104. package/dist/transform/types.d.ts.map +1 -1
  105. package/dist/vault-account-source.d.ts +24 -3
  106. package/dist/vault-account-source.d.ts.map +1 -1
  107. package/dist/vault-account-source.js +39 -12
  108. package/dist/vault-account-source.js.map +1 -1
  109. package/dist/vault-folder-refusal.d.ts +32 -0
  110. package/dist/vault-folder-refusal.d.ts.map +1 -0
  111. package/dist/vault-folder-refusal.js +68 -0
  112. package/dist/vault-folder-refusal.js.map +1 -0
  113. package/dist/vault-grant-reach.d.ts +23 -0
  114. package/dist/vault-grant-reach.d.ts.map +1 -0
  115. package/dist/vault-grant-reach.js +15 -0
  116. package/dist/vault-grant-reach.js.map +1 -0
  117. package/dist/vault-provider-state.d.ts +1 -0
  118. package/dist/vault-provider-state.d.ts.map +1 -1
  119. package/dist/vault-provider-state.js +6 -0
  120. package/dist/vault-provider-state.js.map +1 -1
  121. package/package.json +3 -2
@@ -833,7 +833,7 @@ async function syncDirectory(path, boundary) {
833
833
  }
834
834
  await boundary?.('after-directory-sync');
835
835
  }
836
- async function exclusiveBytes(path, bytes, expectedHash, boundary) {
836
+ async function exclusiveBytes(path, bytes, expectedHash, boundary, pointerStage = false) {
837
837
  let handle;
838
838
  try {
839
839
  handle = await open(path, 'wx', 0o600);
@@ -856,7 +856,10 @@ async function exclusiveBytes(path, bytes, expectedHash, boundary) {
856
856
  const reopened = await secureRead(path);
857
857
  if (digest(reopened) !== expectedHash || !reopened.equals(bytes))
858
858
  throw new AccountMigrationError('exclusive backup or recovery copy differs');
859
- await syncFile(path);
859
+ // A new pointer stage was synced through its write handle. Reused stages and
860
+ // all backup, bootstrap, retired and export copies retain the reopened barrier.
861
+ if (handle === undefined || !pointerStage)
862
+ await syncFile(path);
860
863
  await syncDirectory(dirname(path), boundary);
861
864
  }
862
865
  async function readJournal(paths) {
@@ -1663,7 +1666,7 @@ export function createAccountMigrationFactory(modules) {
1663
1666
  throw new AccountMigrationError('source changed before pointer publication');
1664
1667
  if (!journal)
1665
1668
  throw new AccountMigrationError('capture journal missing');
1666
- await publishGenerationPointer(modules, physical, journal, parentLease, prior, options.onBoundary);
1669
+ await publishGenerationPointer(modules, physical, journal, parentLease, lease, prior, options.onBoundary);
1667
1670
  });
1668
1671
  await options.onBoundary?.('after-capture');
1669
1672
  }
@@ -2029,7 +2032,7 @@ async function expectedFreshArtifacts(paths, journal) {
2029
2032
  !isDeepStrictEqual(decodeAccountStoreGeneration(config[ACCOUNT_STORE_GENERATION_SETTINGS_KEY]), generationJson(journal)))
2030
2033
  throw new AccountMigrationError('fresh generation setting differs');
2031
2034
  }
2032
- /** Explicit fresh initialization publishes an owned pointer/journal before first login; pointer absence alone never authorizes credential service. */
2035
+ /** Publishes a complete empty generation, or validates and resumes an unfinished generation already named by the pointer. */
2033
2036
  export async function initializeFreshAccountStore(modules, options) {
2034
2037
  resolveAccountStorePaths(options.legacyPath);
2035
2038
  try {
@@ -2047,49 +2050,9 @@ export async function initializeFreshAccountStore(modules, options) {
2047
2050
  timeoutMs: 2_000,
2048
2051
  renew: true,
2049
2052
  }, async (lease) => {
2050
- let journal = await readJournal(physical);
2051
- if (pointer === undefined) {
2052
- await options.onBoundary?.('before-capture');
2053
- await assertSourceAbsent(physical);
2054
- await modules.fs.withLock(physical.legacyPath, {
2055
- name: 'accounts',
2056
- ttlMs: 10_000,
2057
- timeoutMs: 2_000,
2058
- renew: true,
2059
- }, async (accountsLease) => {
2060
- await assertSourceAbsent(physical);
2061
- await accountsLease.assertOwned();
2062
- await lease.assertOwned();
2063
- if (journal !== undefined ||
2064
- (await exists(physical.configPath)) ||
2065
- (await exists(physical.statePath)))
2066
- throw new AccountMigrationError('fresh stage has unrelated artifacts');
2067
- journal = {
2068
- schemaVersion: 1,
2069
- id: pathGenerationId(physical),
2070
- legacyPath: physical.legacyPath,
2071
- storeDir: physical.storeDir,
2072
- sourceKind: 'absent',
2073
- status: 'pending',
2074
- operation: 'migrate',
2075
- phase: 'capture',
2076
- manifest: {
2077
- sourceVersion: null,
2078
- normalizationClock: options.now(),
2079
- accounts: [],
2080
- routing: { schemaVersion: 1 },
2081
- effectiveActiveIndex: 0,
2082
- },
2083
- mapping: [],
2084
- completedRows: [],
2085
- };
2086
- await saveJournal(modules, physical, lease, journal, true, options.onBoundary);
2087
- await options.onBoundary?.('pointer:before-publication');
2088
- await assertSourceAbsent(physical);
2089
- await publishGenerationPointer(modules, physical, journal, parentLease, undefined, options.onBoundary);
2090
- await options.onBoundary?.('pointer:after-publication');
2091
- });
2092
- }
2053
+ const journal = await readJournal(physical);
2054
+ if (pointer === undefined)
2055
+ return initializePrivateEmptyGeneration(modules, physical, parentLease, lease, options);
2093
2056
  if (journal?.sourceKind !== 'absent' ||
2094
2057
  journal.operation !== 'migrate' ||
2095
2058
  journal.status === 'inactive')
@@ -2168,6 +2131,205 @@ export async function initializeFreshAccountStore(modules, options) {
2168
2131
  throw error;
2169
2132
  }
2170
2133
  }
2134
+ // These files are not reachable through a pointer yet. Their final barriers are
2135
+ // grouped immediately before publication, rather than at each construction step.
2136
+ async function exclusiveUnpublishedBytes(path, bytes) {
2137
+ const handle = await open(path, constants.O_WRONLY |
2138
+ constants.O_CREAT |
2139
+ constants.O_EXCL |
2140
+ constants.O_NOFOLLOW, 0o600);
2141
+ try {
2142
+ await handle.writeFile(bytes);
2143
+ }
2144
+ finally {
2145
+ await handle.close();
2146
+ }
2147
+ if (!(await secureRead(path)).equals(bytes))
2148
+ throw new AccountMigrationError('private stage readback differs');
2149
+ }
2150
+ async function initializePrivateEmptyGeneration(modules, paths, parentLease, managementLease, options) {
2151
+ return modules.fs.withLock(paths.legacyPath, { name: 'accounts', ttlMs: 10_000, timeoutMs: 2_000, renew: true }, async (accountsLease) => {
2152
+ const directory = await lstat(paths.storeDir);
2153
+ const assertOwned = async () => {
2154
+ await parentLease.assertOwned();
2155
+ await managementLease.assertOwned();
2156
+ await accountsLease.assertOwned();
2157
+ const current = await lstat(paths.storeDir);
2158
+ if (!current.isDirectory() ||
2159
+ current.isSymbolicLink() ||
2160
+ current.dev !== directory.dev ||
2161
+ current.ino !== directory.ino ||
2162
+ !sameFilesystemPath(await realpath(paths.storeDir), paths.storeDir))
2163
+ throw new AccountMigrationError('private generation directory changed');
2164
+ privateMode(current.mode, 0o700);
2165
+ };
2166
+ const boundary = options.onBoundary;
2167
+ await assertOwned();
2168
+ await assertSourceAbsent(paths);
2169
+ if (await readPointer(paths.legacyPath))
2170
+ throw new AccountMigrationError('private initialization found a competing pointer');
2171
+ if ((await readJournal(paths)) ||
2172
+ (await exists(paths.configPath)) ||
2173
+ (await exists(paths.statePath)))
2174
+ throw new AccountMigrationError('fresh stage has unrelated artifacts');
2175
+ const journal = {
2176
+ schemaVersion: 1,
2177
+ id: pathGenerationId(paths),
2178
+ legacyPath: paths.legacyPath,
2179
+ storeDir: paths.storeDir,
2180
+ sourceKind: 'absent',
2181
+ status: 'pending',
2182
+ operation: 'migrate',
2183
+ phase: 'build',
2184
+ manifest: {
2185
+ sourceVersion: null,
2186
+ normalizationClock: options.now(),
2187
+ accounts: [],
2188
+ routing: { schemaVersion: 1 },
2189
+ effectiveActiveIndex: 0,
2190
+ },
2191
+ mapping: [],
2192
+ completedRows: [],
2193
+ };
2194
+ await assertOwned();
2195
+ await exclusiveUnpublishedBytes(paths.configPath, Buffer.from('{"version":1,"accounts":[]}\n'));
2196
+ await boundary?.('empty-stage:after-seed');
2197
+ const store = openMigrationStore(modules, paths, options.now, async (step, info) => {
2198
+ await assertOwned();
2199
+ if (!step.includes('config') || (await exists(paths.statePath)))
2200
+ throw new AccountMigrationError('private empty stage attempted a credential write');
2201
+ await secureRead(paths.configPath);
2202
+ await boundary?.(`public:${info.operation}:${step}`);
2203
+ await assertOwned();
2204
+ });
2205
+ await assertOwned();
2206
+ await store.initialize();
2207
+ await boundary?.('empty-stage:after-initialize');
2208
+ await assertOwned();
2209
+ await store.updateSettings((settings) => {
2210
+ settings[ROUTING_SETTINGS_KEY] = encodeRoutingSettings(journal.manifest.routing);
2211
+ settings[ACCOUNT_STORE_GENERATION_SETTINGS_KEY] =
2212
+ generationJson(journal);
2213
+ settings[MANAGEMENT_SETTINGS_KEY] = {
2214
+ id: journal.id,
2215
+ kind: 'migration',
2216
+ targets: [],
2217
+ progress: { step: 'build', completedTargets: [] },
2218
+ };
2219
+ return settings;
2220
+ });
2221
+ await boundary?.('empty-stage:after-build-settings');
2222
+ await expectedFreshArtifacts(paths, journal);
2223
+ await assertStateOwnership(paths, journal, true);
2224
+ await verifyImported(store, journal);
2225
+ // Preserve the historical verification input: the real, reopened build
2226
+ // config includes management, unlike the final management-free config.
2227
+ const snapshot = await secureRead(paths.configPath);
2228
+ await verifyImported(store, journal);
2229
+ journal.verification = {
2230
+ manifestSha256: digest(canonical(manifestJson(journal.manifest))),
2231
+ configSha256: digest(snapshot),
2232
+ statePresent: false,
2233
+ durability: accountMigrationDurability(),
2234
+ };
2235
+ await assertOwned();
2236
+ await assertSourceAbsent(paths);
2237
+ journal.sourceAbsenceVerified = true;
2238
+ await management(store, journal, true);
2239
+ await boundary?.('empty-stage:after-clear-settings');
2240
+ await expectedFreshArtifacts(paths, journal);
2241
+ await currentSuccessor(store, paths, journal);
2242
+ if ((await settingsOf(store))[MANAGEMENT_SETTINGS_KEY] !== undefined)
2243
+ throw new AccountMigrationError('private stage still has management work');
2244
+ journal.phase = 'activate';
2245
+ journal.status = 'active';
2246
+ decodeAccountMigrationJournal(journalJson(journal));
2247
+ await assertOwned();
2248
+ await exclusiveUnpublishedBytes(paths.migrationPath, Buffer.from(`${JSON.stringify(journalJson(journal), null, 2)}\n`));
2249
+ await boundary?.('empty-stage:after-journal');
2250
+ await syncFile(paths.configPath);
2251
+ await boundary?.('empty-stage:after-config-sync');
2252
+ await syncFile(paths.migrationPath);
2253
+ await boundary?.('empty-stage:after-journal-sync');
2254
+ for (const [name, path] of [
2255
+ ['backups', paths.backupsDir],
2256
+ ['retired', paths.retiredDir],
2257
+ ['transfers', paths.transfersDir],
2258
+ ]) {
2259
+ await syncDirectory(path);
2260
+ await boundary?.(`empty-stage:after-${name}-sync`);
2261
+ }
2262
+ await syncDirectory(paths.storeDir);
2263
+ await boundary?.('empty-stage:after-generation-sync');
2264
+ await boundary?.('pointer:before-publication');
2265
+ const reopened = await readJournal(paths);
2266
+ if (!isDeepStrictEqual(reopened, journal))
2267
+ throw new AccountMigrationError('publication requires its complete synced canonical journal');
2268
+ const pointer = {
2269
+ schemaVersion: 1,
2270
+ id: journal.id,
2271
+ directoryBasename: basename(paths.storeDir),
2272
+ };
2273
+ const path = accountStorePointerPath(paths.legacyPath);
2274
+ const stage = `${path}.${journal.id}.tmp`;
2275
+ const bytes = Buffer.from(`${JSON.stringify(pointer, null, 2)}\n`);
2276
+ await assertOwned();
2277
+ let stageHandle;
2278
+ try {
2279
+ stageHandle = await open(stage, 'wx', 0o600);
2280
+ }
2281
+ catch (error) {
2282
+ if (errno(error) !== 'EEXIST')
2283
+ throw error;
2284
+ }
2285
+ if (stageHandle) {
2286
+ try {
2287
+ await stageHandle.writeFile(bytes);
2288
+ await stageHandle.sync();
2289
+ }
2290
+ finally {
2291
+ await stageHandle.close();
2292
+ }
2293
+ }
2294
+ if (!(await secureRead(stage)).equals(bytes))
2295
+ throw new AccountMigrationError('exclusive pointer stage differs');
2296
+ if (!stageHandle)
2297
+ await syncFile(stage);
2298
+ await boundary?.('empty-stage:after-pointer-stage');
2299
+ await syncDirectory(dirname(path));
2300
+ await boundary?.('empty-stage:after-parent-prelink-sync');
2301
+ await boundary?.('pointer:before-link');
2302
+ await assertOwned();
2303
+ await assertSourceAbsent(paths);
2304
+ if (await exists(path))
2305
+ throw new AccountMigrationError('foreign or competing pointer publication refuses overwrite');
2306
+ if (await exists(resolveAccountStorePaths(paths.legacyPath).storeDir))
2307
+ throw new AccountMigrationError('foreign logical store directory appeared before publication');
2308
+ try {
2309
+ await link(stage, path);
2310
+ }
2311
+ catch (error) {
2312
+ if (errno(error) === 'EEXIST')
2313
+ throw new AccountMigrationError('foreign or competing pointer publication refuses overwrite');
2314
+ throw new AccountMigrationError('exclusive pointer hard-link publication failed');
2315
+ }
2316
+ await boundary?.('pointer:after-link-before-parent-sync');
2317
+ await syncDirectory(dirname(path));
2318
+ if (!isDeepStrictEqual(await readPointer(paths.legacyPath), pointer) ||
2319
+ !isDeepStrictEqual(await readJournal(paths), journal))
2320
+ throw new AccountMigrationError('pointer publication readback differs');
2321
+ await expectedFreshArtifacts(paths, journal);
2322
+ await currentSuccessor(store, paths, journal);
2323
+ const admission = await readAccountStoreAdmission(paths.legacyPath, modules, options.now, journal.id);
2324
+ if (admission.status !== 'active' ||
2325
+ !isDeepStrictEqual(admission.receipt, receipt(journal)))
2326
+ throw new AccountMigrationError('published empty generation is not admitted');
2327
+ await boundary?.('pointer:after-sync-before-stage-cleanup');
2328
+ await cleanupPointerStage(paths.legacyPath, pointer, parentLease);
2329
+ await assertOwned();
2330
+ return { status: 'completed', receipt: receipt(journal) };
2331
+ });
2332
+ }
2171
2333
  async function allocateGeneration(legacyPath) {
2172
2334
  if (await exists(resolveAccountStorePaths(legacyPath).storeDir))
2173
2335
  throw new AccountMigrationError('foreign logical directory refuses generation allocation');
@@ -2176,15 +2338,20 @@ async function allocateGeneration(legacyPath) {
2176
2338
  await prepareDirectories(paths);
2177
2339
  return paths;
2178
2340
  }
2179
- async function publishGenerationPointer(modules, paths, journal, parentLease, prior, boundary) {
2341
+ async function publishGenerationPointer(modules, paths, journal, parentLease, managementLease, prior, boundary, journalSavedBy) {
2180
2342
  await parentLease.assertOwned();
2343
+ await managementLease.assertOwned();
2181
2344
  const currentJournal = await readJournal(paths);
2182
2345
  if (!currentJournal ||
2183
2346
  currentJournal.id !== journal.id ||
2184
2347
  !isDeepStrictEqual(journalJson(currentJournal), journalJson(journal)))
2185
2348
  throw new AccountMigrationError('publication requires its complete synced canonical journal');
2186
- await syncFile(paths.migrationPath);
2187
- await syncDirectory(paths.storeDir);
2349
+ // Callers without a still-held save-owner opt-in retain both barriers rather
2350
+ // than infer that the canonical journal was persisted by an earlier operation.
2351
+ if (journalSavedBy !== managementLease) {
2352
+ await syncFile(paths.migrationPath);
2353
+ await syncDirectory(paths.storeDir);
2354
+ }
2188
2355
  const pointer = {
2189
2356
  schemaVersion: 1,
2190
2357
  id: journal.id,
@@ -2194,8 +2361,9 @@ async function publishGenerationPointer(modules, paths, journal, parentLease, pr
2194
2361
  if (prior === undefined) {
2195
2362
  const stage = `${path}.${journal.id}.tmp`;
2196
2363
  const bytes = Buffer.from(`${JSON.stringify(pointer, null, 2)}\n`);
2197
- await exclusiveBytes(stage, bytes, digest(bytes));
2364
+ await exclusiveBytes(stage, bytes, digest(bytes), undefined, true);
2198
2365
  await parentLease.assertOwned();
2366
+ await managementLease.assertOwned();
2199
2367
  await boundary?.('pointer:before-link');
2200
2368
  if (await exists(resolveAccountStorePaths(paths.legacyPath).storeDir))
2201
2369
  throw new AccountMigrationError('foreign logical store directory appeared before publication');
@@ -2229,6 +2397,7 @@ async function publishGenerationPointer(modules, paths, journal, parentLease, pr
2229
2397
  stageName: () => stageName,
2230
2398
  beforeRename: async () => {
2231
2399
  await parentLease.assertOwned();
2400
+ await managementLease.assertOwned();
2232
2401
  if (!isDeepStrictEqual(await readPointer(paths.legacyPath), prior) ||
2233
2402
  (await readJournal(oldPaths))?.status !== 'inactive')
2234
2403
  throw new AccountMigrationError('inactive pointer changed before transition');
@@ -2239,6 +2408,7 @@ async function publishGenerationPointer(modules, paths, journal, parentLease, pr
2239
2408
  await syncDirectory(dirname(path));
2240
2409
  }
2241
2410
  await parentLease.assertOwned();
2411
+ await managementLease.assertOwned();
2242
2412
  }
2243
2413
  function withParentTopology(modules, legacyPath, body) {
2244
2414
  return modules.fs.withLock(legacyPath, {
@@ -2268,9 +2438,14 @@ async function finishPointerPublication(legacyPath, pointer, lease) {
2268
2438
  const path = accountStorePointerPath(legacyPath);
2269
2439
  await syncFile(path);
2270
2440
  await syncDirectory(dirname(path));
2441
+ if (await cleanupPointerStage(legacyPath, pointer, lease))
2442
+ await syncDirectory(dirname(path));
2443
+ }
2444
+ async function cleanupPointerStage(legacyPath, pointer, lease) {
2445
+ const path = accountStorePointerPath(legacyPath);
2271
2446
  const stage = `${path}.${pointer.id}.tmp`;
2272
2447
  if (!(await exists(stage)))
2273
- return;
2448
+ return false;
2274
2449
  const bytes = await secureRead(stage);
2275
2450
  if (!isDeepStrictEqual(decodeAccountStorePointer(jsonBytes(bytes), legacyPath), pointer))
2276
2451
  throw new AccountMigrationError('pointer stage differs from publication authority');
@@ -2279,6 +2454,6 @@ async function finishPointerPublication(legacyPath, pointer, lease) {
2279
2454
  throw new AccountMigrationError('pointer stage is not the published hard link');
2280
2455
  await lease.assertOwned();
2281
2456
  await unlink(stage);
2282
- await syncDirectory(dirname(path));
2457
+ return true;
2283
2458
  }
2284
2459
  //# sourceMappingURL=account-migration.js.map