Skip to content

Commit 308f826

Browse files
fix(metadata-protocol): a failed seed in a package publish counts the rows earlier seeds wrote (#27802)
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F <!-- claude-projects-attribution: {"github_login":"os-sales"} --> _Requested by **Sales** · [project thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuPMZ8BHWtPf6nBTp51qSM64)_ Fixes #27393 Clause-②: no Metadata-protocol: none Skills-docs: none — nothing an author writes changes; only the values of an existing receipt become truthful ## 维护者速读 - 改了什么:包发布(publish-drafts)里某个 seed 失败时,`seedApplied` 回执的 `inserted`/`updated` 现在等于失败前实际写入的行数,`error` 点名是哪个对象的 seed 让加载停下;回执形状不变。 - 为什么改:以前失败分支硬编码 `inserted: 0, updated: 0`,但前面的 seed 已经写进表里,回执说"什么都没写"是错的数据。 - 风险与回滚:只改失败分支的取值与文案;发布哪些 seed、失败后后续 seed 不再应用、不回滚,这三点都不变。回滚即 revert 本 PR。 - 先看:`packages/metadata-protocol/src/protocol-publish-batch.ts`(catch 分支)、`packages/metadata-protocol/src/seed-loader.ts`(`load()` 的 catch 与 `notePartialWrites`)、`packages/objectql/src/publish-package-drafts-seed-receipt-counts.test.ts`。 - 两分钟验证:`pnpm --filter @objectstack/metadata-protocol build && pnpm --filter @objectstack/objectql exec vitest run src/publish-package-drafts-seed-receipt-counts.test.ts`,预期 5 passed。 - 需要你做的:无,席位落地。 ## What changes Semantics chosen and stated: **truthful aggregate counts**, inside the existing `seedApplied` shape (`success/inserted/updated/error/errors`). When a seed in the batch fails: - `inserted` / `updated` are the rows the loader actually wrote before it stopped — the finished datasets plus any rows the failing dataset itself wrote before it threw; - `success` stays `false`; - `error` names the seed that stopped the load by the object its dataset populates: `seed apply stopped at the seed for object 'OBJECT': ...`, followed by the same sentence as before (a declared refusal quoted, anything else withheld as `seed apply failed`). Not changed: which seeds are published, that the loader stops at the failing dataset (later seeds are not applied), and that nothing is rolled back. All-or-none and per-seed counts were not chosen: the first is a larger behaviour change, the second needs a spec edit. Mechanism: - `seed-loader.ts`: `load()` now catches a throw and records on the thrown error (a module-private `WeakMap`, read through the new `seedLoadPartialWrites(err)`) what the load had written: the sum over finished dataset results plus the in-flight dataset's counters, and that dataset's object. The error itself is rethrown unchanged, so its declared status and message reach the caller as before. `loadDataset` keeps its insert/update counters in one object the load can read. `seedLoadPartialWrites` is not re-exported from the package index: the published surface does not move. - `protocol-publish-batch.ts`: the `applySeedBodies` catch reads that record instead of hard-coding zeros. No record means the throw came before the loader wrote anything (the request parse, or the loader import), so 0 is the truthful count there. ## Tests - `packages/objectql/src/publish-package-drafts-seed-receipt-counts.test.ts` (new) — the real `publishPackageDrafts` door, real ObjectQL engine, real `SeedLoaderService`, in-memory driver (objectql carries no sqlite driver; harness copied from `publish-package-drafts-response-conformance.test.ts`). Every count is checked against `engine.find` on the seeded object: 1. seed 1 writes 2 rows, seed 2 names a missing object → `success: false`, `inserted: 2`, `error` names `seedrepro_nope`; outcome `published`, `publishedCount: 2` (unchanged). 2. seed 1 UPDATES 2 existing rows, seed 2 fails → `updated: 2`, `inserted: 0`. 3. control: every seed succeeds → `{ success: true, inserted: 3, updated: 0 }`, as before. 4. the first seed fails, a good one follows → engine holds 0 rows, receipt 0/0. 5. the only seed fails → 0/0. - `packages/metadata-protocol/src/seed-loader-seed-files.test.ts` (3 cases added, reusing that file's pinned engine double) — a throw INSIDE a dataset after one of its rows landed (the file service failing while the row's seed file settles): `{ inserted: 3, updated: 0, failedObject: 'pw_doc' }` against the store; a throw before any dataset: `{ inserted: 0, updated: 0 }`; a foreign error carries no reading. Red first: on the unfixed tree, cases 1 and 2 failed with `expected +0 to be 2`; 3 passed. ## Acceptance notes - The runtime route `POST /packages/:id/publish-drafts` keeps a fallback seed apply (`packages/runtime/src/domains/packages.ts`, `applyPublishedSeeds`, with its own `../seed-loader.js` copy) for protocols that do not self-apply seeds. Its catch reports `seedApplied` without `inserted`/`updated` at all. It runs only when the protocol returned no `seedApplied`; `metadata-protocol`'s `publishPackageDrafts` always returns one when the batch has a seed, so this door is not reached by the stock protocol. Not touched here (outside this card's file surface). - The failing seed is named by its object, not its metadata name: the seed bodies handed to `applySeedBodies` carry no name, and threading the names through would edit `protocol.ts` beyond this card's surface. ## Verification <details><summary>Ablation — hard-coded zeros restored (metadata-protocol dist rebuilt, door test)</summary> ``` ablation-replace: ok mutation landed: anchor 1 -> 0, blob 838c1b9 -> abbf2653dc95 ✓ dist/: marker present in 2 built files -- the ablation is live in the artifact the suite consumes. × seed 1 writes 2 rows, seed 2 names a missing object: inserted 2, success false, error names seed 2 × seed 1 UPDATES 2 existing rows, seed 2 fails: updated 2, inserted 0 AssertionError: expected +0 to be 2 // Object.is equality Tests 2 failed | 3 passed (5) ablation-replace: ok restored: blob == HEAD (838c1b9) and `git diff HEAD` is empty -- restore leg: rebuilt; ablation-dist-preflight --absent: marker absent from all 24 built files; tree clean against HEAD Tests 5 passed (5) ``` </details> <details><summary>Ablation — in-flight dataset count dropped (seed-loader, src-resolved)</summary> ``` ablation-replace: ok mutation landed: anchor 1 -> 0, blob 6557a0c -> a234cd9c4dfb × counts the finished datasets AND the rows the throwing dataset wrote, naming its object - "inserted": 3, + "inserted": 2, Tests 1 failed | 2 passed (3) ablation-replace: ok restored: blob == HEAD (6557a0c) and `git diff HEAD` is empty ``` (taken before the cases moved into `seed-loader-seed-files.test.ts`; same assertions) </details> <details><summary>Package tests, typecheck, lint, gates (head 1c64cb6)</summary> - `@objectstack/metadata-protocol` full suite (after the origin/main merge, at 86b1fe7): Test Files 281 passed | 4 skipped; Tests 21805 passed | 26 skipped. At 1c64cb6: `seed-loader-seed-files.test.ts` 22 passed; objectql `publish-package-drafts-seed-receipt-counts` + `protocol-publish-package-drafts` + `publish-package-drafts-response-conformance` 38 passed. - `typecheck`: metadata-protocol exit 0, objectql exit 0. - eslint `--no-inline-config` on `packages/metadata-protocol` and `packages/objectql`: exit 0. - `dispatch-gates --commands`: 59 derived families, all run; `--ran` reconciliation: 58 run, 1 NOT MEASURED — `check:dual-build-cjs-loads` exit 3, PREREQUISITE NOT MET (it needs every package's dist/; left to CI). </details> --- _Generated by [Claude Code](https://claude.ai/code/session_01L6bhgUCizoYWQVJWjhdR6F)_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01L6bhgUCizoYWQVJWjhdR6F --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent b83baed commit 308f826

5 files changed

Lines changed: 441 additions & 17 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@objectstack/metadata-protocol': patch
3+
---
4+
5+
fix(metadata-protocol): when a seed in a package publish fails, the `seedApplied` receipt counts the rows the earlier seeds wrote and names the seed that stopped the load
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
`POST /api/v1/packages/:id/publish-drafts` applies every published seed body in one load. The load writes seed by seed and stops at the first one that throws (for example, a seed naming an object that does not exist); the rows the seeds ahead of it wrote stay written. The receipt used to answer `inserted: 0, updated: 0` all the same, so an author or Studio reading it was told nothing had been written while the table held those rows.
11+
12+
Now the receipt keeps its shape and its counters describe what was written:
13+
14+
- `inserted` / `updated` are the rows the load wrote before it stopped, including any rows the failing seed itself wrote before it threw.
15+
- `success` is `false`, as before.
16+
- `error` names the seed that stopped the load by the object its dataset populates: `seed apply stopped at the seed for object 'OBJECT': ...`, followed by the same sentence as before (a refusal the error declared is quoted, anything else is withheld as `seed apply failed`).
17+
18+
Unchanged: which seeds are published, that the seeds after the failing one are not applied, and that nothing is rolled back. A throw before the loader writes anything (a malformed seed body, for example) still answers `inserted: 0, updated: 0` and the same `error` as before.

‎packages/metadata-protocol/src/protocol-publish-batch.ts‎

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
* `ObjectStackProtocolImplementation` method of the same name as it stood,
1212
* re-indented one level and otherwise unedited: the same reads, in the same
1313
* order, answering with the same receipts and the same degradations.
14+
* Edited since the move: `applySeedBodies`' failure receipt counts the rows the
15+
* loader wrote before it threw and names the seed that stopped it.
1416
*
1517
* ## How the moved bodies reach the class
1618
*
@@ -46,6 +48,7 @@ import {
4648
seedRequestValidationError,
4749
} from './protocol.js';
4850
import { CLOSURE_CONTEXT_KEY_BY_TYPE, type RuntimePendingDeclarations } from './runtime-authoring-gate.js';
51+
import type { SeedLoadPartialWrites } from './seed-loader.js';
4952

5053
/**
5154
* The `ObjectStackProtocolImplementation` members the functions below read —
@@ -72,14 +75,18 @@ export async function applySeedBodies(
7275
success: boolean; inserted: number; updated: number; error?: string; errors?: unknown[];
7376
issues?: Array<{ path: string; message: string; code?: string | undefined }>;
7477
}> {
78+
// The loader's reader of what a THROWN load had written. Unset until the
79+
// loader module is imported, which no write precedes.
80+
let partialWritesOf: ((e: unknown) => SeedLoadPartialWrites | undefined) | undefined;
7581
try {
7682
const seeds = bodies.filter(
7783
(b: any) => b && typeof b.object === 'string' && Array.isArray(b.records),
7884
);
7985
if (seeds.length === 0) {
8086
return { success: false, inserted: 0, updated: 0, error: 'seed apply: no readable seed bodies' };
8187
}
82-
const { SeedLoaderService } = await import('./seed-loader.js');
88+
const { SeedLoaderService, seedLoadPartialWrites } = await import('./seed-loader.js');
89+
partialWritesOf = seedLoadPartialWrites;
8390
const { SeedLoaderRequestSchema } = await import('@objectstack/spec/data');
8491
// The loader only needs `getObject` from IMetadataService (dependency
8592
// graph + field introspection); satisfy it from the protocol's own
@@ -134,9 +141,21 @@ export async function applySeedBodies(
134141
// which key. That declaration is the whole reason this site could
135142
// be converted rather than escalated.
136143
console.warn(`[protocol.applySeedBodies] seed apply failed: ${e?.message ?? e}`);
144+
// The counters describe what was WRITTEN. The loader writes dataset
145+
// by dataset and stops at the one that throws; the rows of the
146+
// datasets ahead of it stay written (nothing rolls them back), so the
147+
// receipt carries the loader's own count of them and names the seed
148+
// that stopped it. No reading means the throw came before the loader
149+
// wrote anything — the request parse, or the loader's import.
150+
const written = partialWritesOf?.(e);
151+
const failure = clientFacingFailureText(e, 'seed apply failed');
137152
return {
138-
success: false, inserted: 0, updated: 0,
139-
error: clientFacingFailureText(e, 'seed apply failed'),
153+
success: false,
154+
inserted: written?.inserted ?? 0,
155+
updated: written?.updated ?? 0,
156+
error: written?.failedObject
157+
? `seed apply stopped at the seed for object '${written.failedObject}': ${failure}`
158+
: failure,
140159
// [#10524] A DECLARED refusal's structured findings ride the
141160
// receipt beside the headline `error` — the message is a
142161
// one-sentence headline now, so this is where the per-key

‎packages/metadata-protocol/src/seed-loader-seed-files.test.ts‎

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { createHash } from 'node:crypto';
44
import { describe, it, expect, vi } from 'vitest';
5-
import { SeedLoaderService } from './seed-loader.js';
5+
import { SeedLoaderService, seedLoadPartialWrites } from './seed-loader.js';
66
import type {
77
FieldFileDescription,
88
FieldFileSlot,
@@ -518,3 +518,62 @@ describe('a seed of many files yields to waiting requests (#26670)', () => {
518518
expect(Math.max(...sizes)).toBeLessThanOrEqual(20);
519519
});
520520
});
521+
522+
/**
523+
* `seedLoadPartialWrites` — what a load that THREW had already written.
524+
*
525+
* A load writes dataset by dataset and rolls nothing back when a later one
526+
* throws, so the publish batch's `seedApplied` receipt reads the rows that
527+
* landed off the error: the finished datasets' and those of the dataset that
528+
* threw part-way. The publish-door pins live in `@objectstack/objectql`'s
529+
* `publish-package-drafts-seed-receipt-counts.test.ts`; these pin what that
530+
* door cannot stage — a throw INSIDE a dataset after one of its rows was
531+
* written (the file service failing while that row's seed file is settled) —
532+
* and a throw before any dataset ran. Counts are checked against the store.
533+
*/
534+
describe('seedLoadPartialWrites: the rows a thrown load had written', () => {
535+
/** `pw_tag` loads first (`pw_doc` looks it up); neither looks itself up. */
536+
function partialMetadata(fail?: Error): IMetadataService {
537+
const objects: Record<string, any> = {
538+
pw_tag: { name: 'pw_tag', fields: { name: { type: 'text' } } },
539+
pw_doc: { name: 'pw_doc', fields: { code: { type: 'text' }, file: { type: 'file' }, tag: { type: 'lookup', reference: 'pw_tag' } } },
540+
};
541+
return {
542+
getObject: vi.fn(async (name: string) => { if (fail) throw fail; return objects[name]; }),
543+
} as unknown as IMetadataService;
544+
}
545+
const partialSeeds = () => [
546+
dataset([{ code: 'D1', tag: 'T1', file: seedFile('fixtures/v1.pdf') }], undefined, { object: 'pw_doc' }),
547+
{ object: 'pw_tag', mode: 'upsert', env: ['prod', 'dev', 'test'], records: [{ name: 'T1' }, { name: 'T2' }] },
548+
] as any;
549+
550+
it('counts the finished datasets AND the rows the throwing dataset wrote, naming its object', async () => {
551+
const { engine, store } = createEngine();
552+
const { service } = createFieldFiles();
553+
service.describeFiles = vi.fn(async () => { throw new Error('the storage service is down'); });
554+
const seedLoader = new SeedLoaderService(engine, partialMetadata(), createLogger(), { getFieldFiles: () => service });
555+
556+
const err = await seedLoader.load({ seeds: partialSeeds(), config: CONFIG }).then(() => undefined, (e: unknown) => e);
557+
558+
expect((err as Error).message).toBe('the storage service is down');
559+
expect((store.pw_tag ?? []).map((r) => r.name).sort()).toEqual(['T1', 'T2']);
560+
expect((store.pw_doc ?? []).map((r) => r.code)).toEqual(['D1']);
561+
expect(seedLoadPartialWrites(err)).toEqual({ inserted: 3, updated: 0, failedObject: 'pw_doc' });
562+
});
563+
564+
it('a load that throws before any dataset wrote: zero, and no object named', async () => {
565+
const { engine, store } = createEngine();
566+
const seedLoader = new SeedLoaderService(engine, partialMetadata(new Error('metadata store unavailable')), createLogger());
567+
568+
const err = await seedLoader.load({ seeds: partialSeeds(), config: CONFIG }).then(() => undefined, (e: unknown) => e);
569+
570+
expect((err as Error).message).toBe('metadata store unavailable');
571+
expect(store).toEqual({});
572+
expect(seedLoadPartialWrites(err)).toEqual({ inserted: 0, updated: 0 });
573+
});
574+
575+
it('an error that did not come out of a load carries no reading', () => {
576+
expect(seedLoadPartialWrites(new Error('elsewhere'))).toBeUndefined();
577+
expect(seedLoadPartialWrites('a string')).toBeUndefined();
578+
});
579+
});

‎packages/metadata-protocol/src/seed-loader.ts‎

Lines changed: 80 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,35 @@ interface ReplayComparands {
558558
readonly masked: ReadonlySet<string>;
559559
}
560560

561+
/**
562+
* What a {@link SeedLoaderService.load} that THREW had already written.
563+
*
564+
* A load writes dataset by dataset and nothing undoes a dataset's rows when a
565+
* later one throws, so a caller that reports the failure must still be able to
566+
* say what landed: a receipt answering `inserted: 0` while the table holds the
567+
* earlier datasets' rows is a receipt that lies. Read with
568+
* {@link seedLoadPartialWrites} on the error the load threw.
569+
*/
570+
export interface SeedLoadPartialWrites {
571+
/** Rows inserted before the load threw — finished datasets plus the one that threw. */
572+
readonly inserted: number;
573+
/** Rows updated before the load threw, counted the same way. */
574+
readonly updated: number;
575+
/** The object of the dataset that was loading when the load threw; absent when it threw outside any dataset. */
576+
readonly failedObject?: string;
577+
}
578+
579+
/** The writes a thrown load had made, keyed by the error it threw. */
580+
const partialWritesByError = new WeakMap<object, SeedLoadPartialWrites>();
581+
582+
/**
583+
* What the {@link SeedLoaderService.load} that threw `err` had written, or
584+
* `undefined` when `err` did not come out of a load.
585+
*/
586+
export function seedLoadPartialWrites(err: unknown): SeedLoadPartialWrites | undefined {
587+
return err !== null && typeof err === 'object' ? partialWritesByError.get(err) : undefined;
588+
}
589+
561590
/** What a {@link SeedLoaderService} is built with beyond its engine, metadata and logger. */
562591
export interface SeedLoaderServiceOptions {
563592
/**
@@ -703,6 +732,17 @@ export class SeedLoaderService implements ISeedLoaderService {
703732
*/
704733
private uncomparedCredentials = new Map<string, { fields: Set<string>; rows: number }>();
705734

735+
/**
736+
* The CURRENT {@link load}'s writes so far, for {@link SeedLoadPartialWrites}:
737+
* the finished datasets' results, and the counters of the dataset loading
738+
* now. Reset per `load`; an instance field for {@link summariesStale}'s
739+
* reason (datasets load strictly sequentially).
740+
*/
741+
private loadProgress?: {
742+
finished: readonly SeedLoadResultParsed[];
743+
loading?: { object: string; written: { inserted: number; updated: number } };
744+
};
745+
706746
/**
707747
* [#25779] The `field-files` service (`FIELD_FILE_SERVICE`) a seed file is
708748
* stored through, read per dataset. Absent — a loader built without one, or
@@ -744,10 +784,34 @@ export class SeedLoaderService implements ISeedLoaderService {
744784
* it.
745785
*/
746786
async load(request: SeedLoaderRequestParsed): Promise<SeedLoaderResultParsed> {
747-
return runWithAdvisoryAggregation(
748-
() => this.loadDatasets(request),
749-
(groups) => this.reportAdvisorySummary(groups),
750-
);
787+
this.loadProgress = undefined;
788+
try {
789+
return await runWithAdvisoryAggregation(
790+
() => this.loadDatasets(request),
791+
(groups) => this.reportAdvisorySummary(groups),
792+
);
793+
} catch (err) {
794+
throw this.notePartialWrites(err);
795+
}
796+
}
797+
798+
/**
799+
* Record on `err` what this load wrote before it threw (see
800+
* {@link SeedLoadPartialWrites}) and hand back the error to rethrow — the
801+
* same error, so its declared status and message reach the caller as they
802+
* did; a thrown non-object is wrapped, since only an object can carry it.
803+
*/
804+
private notePartialWrites(err: unknown): unknown {
805+
const finished = this.loadProgress?.finished ?? [];
806+
const loading = this.loadProgress?.loading;
807+
const partial: SeedLoadPartialWrites = {
808+
inserted: finished.reduce((sum, r) => sum + r.inserted, 0) + (loading?.written.inserted ?? 0),
809+
updated: finished.reduce((sum, r) => sum + r.updated, 0) + (loading?.written.updated ?? 0),
810+
...(loading ? { failedObject: loading.object } : {}),
811+
};
812+
const carrier = err !== null && typeof err === 'object' ? err : new Error(String(err));
813+
partialWritesByError.set(carrier, partial);
814+
return carrier;
751815
}
752816

753817
/**
@@ -852,6 +916,7 @@ export class SeedLoaderService implements ISeedLoaderService {
852916
this.warnOnUnresolvedLocaleScope(config, request.seeds);
853917
const allErrors: ReferenceResolutionError[] = [];
854918
const allResults: SeedLoadResultParsed[] = [];
919+
this.loadProgress = { finished: allResults };
855920
// Per-load counter — a service instance can be reused across loads.
856921
this.summariesStale = 0;
857922
// [#9071] Same per-load lifetime, same reason: a publish between two loads
@@ -1127,8 +1192,9 @@ export class SeedLoaderService implements ISeedLoaderService {
11271192
// `matchesExistingRow` in the record loop below.
11281193
const createOnlyColumns = createOnlySeedColumns(dataset, externalId);
11291194

1130-
let inserted = 0;
1131-
let updated = 0;
1195+
// Read by `notePartialWrites` should this dataset throw part-way.
1196+
const written = { inserted: 0, updated: 0 };
1197+
if (this.loadProgress) this.loadProgress.loading = { object: objectName, written };
11321198
let skipped = 0;
11331199
let errored = 0;
11341200
let referencesResolved = 0;
@@ -1342,7 +1408,7 @@ export class SeedLoaderService implements ISeedLoaderService {
13421408
for (const res of writeResults) {
13431409
const { recordIndex, externalIdValue, record } = batch[res.index];
13441410
if (res.ok) {
1345-
inserted++;
1411+
written.inserted++;
13461412
const internalId = this.extractId(res.record);
13471413
if (internalId) noteLanded(recordIndex, internalId); // [commit 9a884c6e4]
13481414
if (externalIdValue && internalId) {
@@ -2046,8 +2112,8 @@ export class SeedLoaderService implements ISeedLoaderService {
20462112
objectName, record, mode, externalId, existingRecords, comparands
20472113
);
20482114

2049-
if (result.action === 'inserted') inserted++;
2050-
else if (result.action === 'updated') updated++;
2115+
if (result.action === 'inserted') written.inserted++;
2116+
else if (result.action === 'updated') written.updated++;
20512117
else if (result.action === 'skipped') {
20522118
skipped++;
20532119
if (result.uncompared) this.noteUncomparedCredentials(objectName, result.uncompared); // [#24624]
@@ -2110,7 +2176,7 @@ export class SeedLoaderService implements ISeedLoaderService {
21102176
}
21112177
try {
21122178
await this.writeRecoveringSummary(objectName, () => withTransientRetry(() => this.engine.update(objectName, { ...record, id: decision.id }, opts)));
2113-
updated++;
2179+
written.updated++;
21142180
await settleSeedFiles(i, decision.id); // [#25779]
21152181
} catch (err: any) {
21162182
errored++;
@@ -2142,7 +2208,7 @@ export class SeedLoaderService implements ISeedLoaderService {
21422208
if (externalIdValue) {
21432209
insertedRecords.get(objectName)!.set(externalIdValue, `dry-run-id-${i}`);
21442210
}
2145-
inserted++; // Count as "would be inserted"
2211+
written.inserted++; // Count as "would be inserted"
21462212
}
21472213
}
21482214

@@ -2176,11 +2242,12 @@ export class SeedLoaderService implements ISeedLoaderService {
21762242
.map(([, id]) => id),
21772243
)];
21782244

2245+
if (this.loadProgress) this.loadProgress.loading = undefined;
21792246
return {
21802247
object: objectName,
21812248
mode,
2182-
inserted,
2183-
updated,
2249+
inserted: written.inserted,
2250+
updated: written.updated,
21842251
skipped,
21852252
errored,
21862253
total: dataset.records.length,

0 commit comments

Comments
 (0)