@porulle/core 0.50.3 → 0.52.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.
- package/dist/modules/catalog/repository/index.d.ts +6 -0
- package/dist/modules/catalog/repository/index.d.ts.map +1 -1
- package/dist/modules/catalog/repository/index.js +25 -11
- package/dist/modules/catalog/schema.d.ts +17 -0
- package/dist/modules/catalog/schema.d.ts.map +1 -1
- package/dist/modules/catalog/schema.js +4 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/modules/catalog/repository/index.ts +29 -11
- package/src/modules/catalog/schema.ts +4 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@porulle/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
"eslint": "^9.39.1",
|
|
63
63
|
"typescript": "5.9.2",
|
|
64
64
|
"vitest": "^3.2.4",
|
|
65
|
-
"@porulle/
|
|
66
|
-
"@porulle/
|
|
65
|
+
"@porulle/typescript-config": "0.1.0",
|
|
66
|
+
"@porulle/eslint-config": "0.1.0"
|
|
67
67
|
},
|
|
68
68
|
"publishConfig": {
|
|
69
69
|
"access": "public"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { eq, and, asc, count, desc, gt, inArray, isNull, lt, max, ne, or, type SQL } from "drizzle-orm";
|
|
1
|
+
import { eq, and, asc, count, desc, gt, inArray, isNull, lt, max, ne, or, sql, type SQL } from "drizzle-orm";
|
|
2
2
|
import type { TxContext } from "../../../kernel/database/tx-context.js";
|
|
3
3
|
import { CommerceNotFoundError } from "../../../kernel/errors.js";
|
|
4
4
|
import type {
|
|
@@ -1290,12 +1290,29 @@ export class CatalogRepository {
|
|
|
1290
1290
|
const db = this.getDb(ctx);
|
|
1291
1291
|
const rows = await db
|
|
1292
1292
|
.update(variants)
|
|
1293
|
-
.set(data)
|
|
1293
|
+
.set({ ...data, updatedAt: sql`now()` })
|
|
1294
1294
|
.where(eq(variants.id, id))
|
|
1295
1295
|
.returning();
|
|
1296
1296
|
return rows[0];
|
|
1297
1297
|
}
|
|
1298
1298
|
|
|
1299
|
+
/**
|
|
1300
|
+
* Run a write to variants' option values and bump those variants' `updated_at` in ONE
|
|
1301
|
+
* transaction (a savepoint when the caller already holds one). A bump that could commit without
|
|
1302
|
+
* its write, or a write without its bump, is exactly the version drift the column exists to end.
|
|
1303
|
+
*/
|
|
1304
|
+
private async touchingVariants(
|
|
1305
|
+
variantIds: readonly string[],
|
|
1306
|
+
ctx: TxContext | undefined,
|
|
1307
|
+
write: (db: DbOrTx) => Promise<void>,
|
|
1308
|
+
): Promise<void> {
|
|
1309
|
+
if (variantIds.length === 0) return;
|
|
1310
|
+
await this.getDb(ctx).transaction(async (tx) => {
|
|
1311
|
+
await write(tx);
|
|
1312
|
+
await tx.update(variants).set({ updatedAt: sql`now()` }).where(inArray(variants.id, [...new Set(variantIds)]));
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1299
1316
|
async deleteVariantsByEntityId(
|
|
1300
1317
|
entityId: string,
|
|
1301
1318
|
ctx?: TxContext,
|
|
@@ -1324,32 +1341,33 @@ export class CatalogRepository {
|
|
|
1324
1341
|
ctx?: TxContext,
|
|
1325
1342
|
): Promise<void> {
|
|
1326
1343
|
if (data.length === 0) return;
|
|
1327
|
-
|
|
1328
|
-
|
|
1344
|
+
await this.touchingVariants(data.map((row) => row.variantId), ctx, async (db) => {
|
|
1345
|
+
await db.insert(variantOptionValues).values(data).onConflictDoNothing();
|
|
1346
|
+
});
|
|
1329
1347
|
}
|
|
1330
1348
|
|
|
1331
1349
|
async deleteVariantOptionValuesByVariantId(
|
|
1332
1350
|
variantId: string,
|
|
1333
1351
|
ctx?: TxContext,
|
|
1334
1352
|
): Promise<void> {
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1353
|
+
await this.touchingVariants([variantId], ctx, async (db) => {
|
|
1354
|
+
await db
|
|
1355
|
+
.delete(variantOptionValues)
|
|
1356
|
+
.where(eq(variantOptionValues.variantId, variantId));
|
|
1357
|
+
});
|
|
1339
1358
|
}
|
|
1340
1359
|
|
|
1341
1360
|
async deleteVariantOptionValuesByEntityId(
|
|
1342
1361
|
entityId: string,
|
|
1343
1362
|
ctx?: TxContext,
|
|
1344
1363
|
): Promise<void> {
|
|
1345
|
-
const db = this.getDb(ctx);
|
|
1346
1364
|
// Get all variant IDs for this entity first
|
|
1347
1365
|
const entityVariants = await this.findVariantsByEntityId(entityId, ctx);
|
|
1348
1366
|
const variantIds = entityVariants.map((v) => v.id);
|
|
1349
|
-
|
|
1367
|
+
await this.touchingVariants(variantIds, ctx, async (db) => {
|
|
1350
1368
|
await db
|
|
1351
1369
|
.delete(variantOptionValues)
|
|
1352
1370
|
.where(inArray(variantOptionValues.variantId, variantIds));
|
|
1353
|
-
}
|
|
1371
|
+
});
|
|
1354
1372
|
}
|
|
1355
1373
|
}
|
|
@@ -337,6 +337,10 @@ export const variants = pgTable(
|
|
|
337
337
|
.default("active"),
|
|
338
338
|
sortOrder: integer("sort_order").notNull().default(0),
|
|
339
339
|
metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
|
|
340
|
+
// Bumped on EVERY write to the variant or its option values, so a variant-only change is
|
|
341
|
+
// visible to anything that versions a product from its timestamps (the entity row is not
|
|
342
|
+
// touched by a variant write).
|
|
343
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
|
|
340
344
|
},
|
|
341
345
|
(table) => ({
|
|
342
346
|
barcodeIdx: index("idx_variants_barcode").on(table.barcode),
|