@porulle/plugin-channel-connector 0.52.0 → 0.53.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/index.d.ts CHANGED
@@ -5,8 +5,6 @@ export { ChannelConnectorService, HERO_IMAGE_BYTE_CAP, selectImportImages, CATAL
5
5
  export { isValidCatalogMappingFieldPath, matchFieldPath, mergeCatalogFieldMapping, normalizeCatalogFieldMapping, compareCatalogFieldMappingSpecificity, providerCatalogFieldMappingDefaults, selectCatalogFieldMapping, validateCatalogMappingRow, } from "./catalog-field-mapping.js";
6
6
  export type { CatalogDeferredMedia, CatalogMediaFailure, CatalogMediaFailureReason, CatalogPageConvergence, ImportImageSelection, } from "./service.js";
7
7
  export type { CatalogFieldMapping, CatalogFieldMappingInput, CatalogFieldMappingRow, CatalogFieldTarget, } from "./catalog-field-mapping.js";
8
- /** ~320 Neon HTTP subrequests per product against a 10,000 per-invocation cap → hard ceiling near 31; 20 leaves margin for heavier products. */
9
- export declare const CHANNEL_IMPORT_MAX_ITEMS_PER_INVOCATION = 20;
10
8
  /**
11
9
  * How many bounded batches one sweep may walk before it refuses rather than loops.
12
10
  *
package/dist/index.js CHANGED
@@ -10,8 +10,6 @@ import { oauthStateEventId, signState, verifyState } from "./oauth-state.js";
10
10
  export { mockChannelConnector } from "./mock-connector.js";
11
11
  export { ChannelConnectorService, HERO_IMAGE_BYTE_CAP, selectImportImages, CATALOG_OUTBOUND_SUPPRESSION_WINDOW_MS, CATALOG_PUSH_BATCH_SIZES, CATALOG_PUSH_MAX_ATTEMPTS, canCatalogPushTransition, canExportTransition, catalogPushConcurrencyKey, catalogPushRetryDelayMs, CHANNEL_INVENTORY_MAX_ITEMS_PER_INVOCATION, isCatalogPushBreakerOpen, } from "./service.js";
12
12
  export { isValidCatalogMappingFieldPath, matchFieldPath, mergeCatalogFieldMapping, normalizeCatalogFieldMapping, compareCatalogFieldMappingSpecificity, providerCatalogFieldMappingDefaults, selectCatalogFieldMapping, validateCatalogMappingRow, } from "./catalog-field-mapping.js";
13
- /** ~320 Neon HTTP subrequests per product against a 10,000 per-invocation cap → hard ceiling near 31; 20 leaves margin for heavier products. */
14
- export const CHANNEL_IMPORT_MAX_ITEMS_PER_INVOCATION = 20;
15
13
  /**
16
14
  * How many bounded batches one sweep may walk before it refuses rather than loops.
17
15
  *
@@ -169,67 +167,6 @@ export function channelConnectorPlugin(options = {}) {
169
167
  return { output: { enqueued: stores.length } };
170
168
  },
171
169
  },
172
- {
173
- slug: "channel/import-catalog",
174
- concurrency: { key: (input) => String(input.storeId), supersedes: true },
175
- durableSteps: true,
176
- handler: async ({ input, ctx }) => {
177
- const service = new ChannelConnectorService(ctx.db, ctx.services, options);
178
- const orgId = String(input.orgId);
179
- const storeId = String(input.storeId);
180
- const result = await walkBatches(ctx, "import-catalog", storeId, async () => {
181
- const page = await service.importCatalog(orgId, storeId, createSystemActor(orgId), {
182
- maxItems: CHANNEL_IMPORT_MAX_ITEMS_PER_INVOCATION,
183
- });
184
- if (!page.ok)
185
- throw new Error(page.error);
186
- return {
187
- exhausted: page.value.exhausted,
188
- counted: page.value.imported,
189
- cursor: page.value.cursor ?? null,
190
- ...(page.value.warnings ? { warnings: page.value.warnings } : {}),
191
- // UNCONDITIONAL, unlike `warnings` and `failures` beside it. The step's return value is
192
- // the only per-batch seam a host application has — `walkBatches` keeps just `last` — so
193
- // this is where a page gets its identity. Omitting it when empty would make "this batch
194
- // committed nothing" and "this plugin build does not report entities" the same
195
- // `undefined` at the seam, and a caller that collapses those enqueues nothing and
196
- // reports success. The service's own return declares it unconditional for the same
197
- // reason; dropping it here would have undone that one line later.
198
- entityIds: page.value.entityIds,
199
- ...(page.value.failures ? { failures: page.value.failures } : {}),
200
- };
201
- });
202
- const jobs = ctx.services.jobs;
203
- // The catalog is always exhausted by the time the walk above returns, so this hand-off is
204
- // unconditional. It is the ONE enqueue this task is allowed: it starts a DIFFERENT task
205
- // once, spending a single level of the request chain's 32, rather than one per page the
206
- // way the continuation it replaced did.
207
- //
208
- // A finished catalog is not a usable one. `importCatalog` writes entities, variants and
209
- // prices and never touches `inventory_levels`, so a store whose sweep ends here has a
210
- // catalog in which every variant rolls up as out of stock — which is what a consumer
211
- // projection publishes and what a shopper is shown.
212
- //
213
- // Inventory used to arrive from `reconcile`, reachable only through the
214
- // `channel/reconcile-sweep` cron. A deployment that removes its crons therefore loses a
215
- // data-plane write silently, with every suite still green. Levelling inventory here keeps
216
- // it inside the one operator action — "import this store" — instead of behind a second
217
- // one somebody has to remember.
218
- await jobs.enqueue("channel/sync-inventory", { orgId, storeId }, {
219
- organizationId: orgId,
220
- concurrencyKey: storeId,
221
- });
222
- return {
223
- output: {
224
- imported: result.counted,
225
- cursor: result.last.cursor ?? null,
226
- exhausted: true,
227
- batches: result.batches,
228
- ...(result.warnings.length > 0 ? { warnings: result.warnings } : {}),
229
- },
230
- };
231
- },
232
- },
233
170
  {
234
171
  slug: "channel/backfill-catalog",
235
172
  concurrency: { key: (input) => String(input.storeId) },
package/dist/service.d.ts CHANGED
@@ -392,7 +392,6 @@ export declare class ChannelConnectorService {
392
392
  storeDomain: string;
393
393
  webhookSecret?: string;
394
394
  }): Promise<PluginResult<PublicConnectedStore>>;
395
- private get optionsJobs();
396
395
  disconnectStore(orgId: string, id: string): Promise<PluginResult<PublicConnectedStore>>;
397
396
  disconnectStoreSystem(orgId: string, id: string, redactDomain?: boolean): Promise<PluginResult<PublicConnectedStore>>;
398
397
  getStore(orgId: string, id: string): Promise<PluginResult<PublicConnectedStore>>;
package/dist/service.js CHANGED
@@ -2126,19 +2126,10 @@ export class ChannelConnectorService {
2126
2126
  return PluginErr(registration.error.message, "CONNECTOR_REGISTRATION_FAILED");
2127
2127
  }
2128
2128
  }
2129
- const jobs = this.optionsJobs;
2130
- if (jobs) {
2131
- await jobs.enqueue("channel/import-catalog", { orgId, storeId: rows[0].id }, {
2132
- organizationId: orgId,
2133
- concurrencyKey: rows[0].id,
2134
- supersedes: true,
2135
- });
2136
- }
2129
+ // Connecting starts no import. The host's operator route starts one (and levels inventory after
2130
+ // it); connect used to enqueue a second, sequential walk that ran beside the host's own.
2137
2131
  return Ok(redactStore(store));
2138
2132
  }
2139
- get optionsJobs() {
2140
- return this.jobs;
2141
- }
2142
2133
  async disconnectStore(orgId, id) {
2143
2134
  return this.disconnectStoreSystem(orgId, id);
2144
2135
  }