@porulle/core 0.50.1 → 0.50.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@porulle/core",
3
- "version": "0.50.1",
3
+ "version": "0.50.3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -139,6 +139,9 @@ export interface ImportProductsReport extends Record<string, unknown> {
139
139
 
140
140
  type Writer = Pick<PluginDb, "insert" | "select">;
141
141
 
142
+ /** The drizzle handle a transaction context carries (typed `unknown` on the context). */
143
+ const writerOf = (txCtx: TxContext): PluginDb => txCtx.tx as PluginDb;
144
+
142
145
  type Failure = { code: ImportRowFailureCode; error: string };
143
146
 
144
147
  function failed(ref: string, code: ImportRowFailureCode, error: string): ImportProductRowResult {
@@ -473,9 +476,18 @@ export class CatalogImportService {
473
476
  if (errorPolicy === "reject-everything" && memoryFailures.size > 0) return Ok(rejectAll());
474
477
 
475
478
  try {
479
+ const candidates = page.map((item, index) => ({ item, index })).filter(({ index }) => !memoryFailures.has(index));
480
+ // The shared vocabulary is resolved in its OWN short transaction, never in the page's, and
481
+ // never in a caller's (no ctx is passed on purpose). Inside the page transaction a newly
482
+ // inserted tag's unique key stayed uncommitted for the whole page, so every other page naming
483
+ // it — another store's, or this store's next one — waited on that index until this page
484
+ // committed: 36.6 s behind one 89 s page on the sim, 2026-09-24. The cost is that a page
485
+ // rejected afterwards can leave new vocabulary behind; it is organization-wide and reusable.
486
+ const taxonomy = await this.withTransaction(actor, undefined, (txCtx) =>
487
+ resolveTaxonomy(writerOf(txCtx), orgId, candidates.map(({ item }) => item)));
488
+
476
489
  const report = await this.withTransaction(actor, ctx, async (txCtx): Promise<ImportProductsReport> => {
477
- const tx = txCtx.tx as PluginDb;
478
- const candidates = page.map((item, index) => ({ item, index })).filter(({ index }) => !memoryFailures.has(index));
490
+ const tx = writerOf(txCtx);
479
491
 
480
492
  // Page-level reads: the slugs already taken, then the shared vocabulary.
481
493
  const slugs = candidates.map(({ item }) => item.slug);
@@ -495,7 +507,6 @@ export class CatalogImportService {
495
507
  if (errorPolicy === "reject-everything" && writable.length < candidates.length) {
496
508
  throw new RejectEverything();
497
509
  }
498
- const taxonomy = await resolveTaxonomy(tx, orgId, writable.map(({ item }) => item));
499
510
 
500
511
  const written: Array<{ index: number; write: ItemWrite }> = [];
501
512
  for (const { item, index } of writable) {