@porulle/plugin-channel-connector 0.52.0 → 0.53.1

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/plugin-channel-connector",
3
- "version": "0.52.0",
3
+ "version": "0.53.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -19,15 +19,15 @@
19
19
  "dependencies": {
20
20
  "@hono/zod-openapi": "^1.2.2",
21
21
  "hono": "^4.12.5",
22
- "@porulle/core": "0.52.0"
22
+ "@porulle/core": "0.53.1"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^24.5.2",
26
26
  "eslint": "^9.39.1",
27
27
  "typescript": "5.9.2",
28
28
  "vitest": "^3.2.4",
29
- "@porulle/eslint-config": "0.1.0",
30
- "@porulle/typescript-config": "0.1.0"
29
+ "@porulle/typescript-config": "0.1.0",
30
+ "@porulle/eslint-config": "0.1.0"
31
31
  },
32
32
  "publishConfig": {
33
33
  "access": "public"
package/src/index.ts CHANGED
@@ -92,8 +92,6 @@ export type {
92
92
  CatalogFieldMappingRow,
93
93
  CatalogFieldTarget,
94
94
  } from "./catalog-field-mapping.js";
95
- /** ~320 Neon HTTP subrequests per product against a 10,000 per-invocation cap → hard ceiling near 31; 20 leaves margin for heavier products. */
96
- export const CHANNEL_IMPORT_MAX_ITEMS_PER_INVOCATION = 20;
97
95
 
98
96
  /**
99
97
  * How many bounded batches one sweep may walk before it refuses rather than loops.
@@ -326,71 +324,6 @@ export function channelConnectorPlugin(options: ChannelConnectorPluginOptions =
326
324
  return { output: { enqueued: stores.length } };
327
325
  },
328
326
  },
329
- {
330
- slug: "channel/import-catalog",
331
- concurrency: { key: (input: Record<string, unknown>) => String(input.storeId), supersedes: true },
332
- durableSteps: true,
333
- handler: async ({ input, ctx }: { input: Record<string, unknown>; ctx: import("@porulle/core").TaskContext }) => {
334
- const service = new ChannelConnectorService(ctx.db, ctx.services, options);
335
- const orgId = String(input.orgId);
336
- const storeId = String(input.storeId);
337
- const result = await walkBatches(
338
- ctx,
339
- "import-catalog",
340
- storeId,
341
- async () => {
342
- const page = await service.importCatalog(orgId, storeId, createSystemActor(orgId), {
343
- maxItems: CHANNEL_IMPORT_MAX_ITEMS_PER_INVOCATION,
344
- });
345
- if (!page.ok) throw new Error(page.error);
346
- return {
347
- exhausted: page.value.exhausted,
348
- counted: page.value.imported,
349
- cursor: page.value.cursor ?? null,
350
- ...(page.value.warnings ? { warnings: page.value.warnings } : {}),
351
- // UNCONDITIONAL, unlike `warnings` and `failures` beside it. The step's return value is
352
- // the only per-batch seam a host application has — `walkBatches` keeps just `last` — so
353
- // this is where a page gets its identity. Omitting it when empty would make "this batch
354
- // committed nothing" and "this plugin build does not report entities" the same
355
- // `undefined` at the seam, and a caller that collapses those enqueues nothing and
356
- // reports success. The service's own return declares it unconditional for the same
357
- // reason; dropping it here would have undone that one line later.
358
- entityIds: page.value.entityIds,
359
- ...(page.value.failures ? { failures: page.value.failures } : {}),
360
- };
361
- },
362
- );
363
- const jobs = ctx.services.jobs as JobsAdapter;
364
- // The catalog is always exhausted by the time the walk above returns, so this hand-off is
365
- // unconditional. It is the ONE enqueue this task is allowed: it starts a DIFFERENT task
366
- // once, spending a single level of the request chain's 32, rather than one per page the
367
- // way the continuation it replaced did.
368
- //
369
- // A finished catalog is not a usable one. `importCatalog` writes entities, variants and
370
- // prices and never touches `inventory_levels`, so a store whose sweep ends here has a
371
- // catalog in which every variant rolls up as out of stock — which is what a consumer
372
- // projection publishes and what a shopper is shown.
373
- //
374
- // Inventory used to arrive from `reconcile`, reachable only through the
375
- // `channel/reconcile-sweep` cron. A deployment that removes its crons therefore loses a
376
- // data-plane write silently, with every suite still green. Levelling inventory here keeps
377
- // it inside the one operator action — "import this store" — instead of behind a second
378
- // one somebody has to remember.
379
- await jobs.enqueue("channel/sync-inventory", { orgId, storeId }, {
380
- organizationId: orgId,
381
- concurrencyKey: storeId,
382
- });
383
- return {
384
- output: {
385
- imported: result.counted,
386
- cursor: result.last.cursor ?? null,
387
- exhausted: true,
388
- batches: result.batches,
389
- ...(result.warnings.length > 0 ? { warnings: result.warnings } : {}),
390
- },
391
- };
392
- },
393
- },
394
327
  {
395
328
  slug: "channel/backfill-catalog",
396
329
  concurrency: { key: (input: Record<string, unknown>) => String(input.storeId) },
package/src/service.ts CHANGED
@@ -2963,21 +2963,11 @@ export class ChannelConnectorService {
2963
2963
  return PluginErr(registration.error.message, "CONNECTOR_REGISTRATION_FAILED");
2964
2964
  }
2965
2965
  }
2966
- const jobs = this.optionsJobs;
2967
- if (jobs) {
2968
- await jobs.enqueue("channel/import-catalog", { orgId, storeId: (rows[0] as ConnectedStore).id }, {
2969
- organizationId: orgId,
2970
- concurrencyKey: (rows[0] as ConnectedStore).id,
2971
- supersedes: true,
2972
- });
2973
- }
2966
+ // Connecting starts no import. The host's operator route starts one (and levels inventory after
2967
+ // it); connect used to enqueue a second, sequential walk that ran beside the host's own.
2974
2968
  return Ok(redactStore(store));
2975
2969
  }
2976
2970
 
2977
- private get optionsJobs(): JobsAdapter | undefined {
2978
- return this.jobs;
2979
- }
2980
-
2981
2971
  async disconnectStore(orgId: string, id: string): Promise<PluginResult<PublicConnectedStore>> {
2982
2972
  return this.disconnectStoreSystem(orgId, id);
2983
2973
  }