@endora-commerce/mod-pim-connector 0.100.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.
Files changed (29) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +49 -0
  3. package/dist/backend/entities/pim-connector-activation-lock.entity.d.ts +15 -0
  4. package/dist/backend/entities/pim-connector-activation-lock.entity.d.ts.map +1 -0
  5. package/dist/backend/entities/pim-connector-activation-lock.entity.js +46 -0
  6. package/dist/backend/entities/pim-connector-activation-lock.entity.js.map +1 -0
  7. package/dist/backend/index.d.ts +5 -0
  8. package/dist/backend/index.d.ts.map +1 -0
  9. package/dist/backend/index.js +106 -0
  10. package/dist/backend/index.js.map +1 -0
  11. package/dist/backend/services/pim-connector-registry.service.d.ts +48 -0
  12. package/dist/backend/services/pim-connector-registry.service.d.ts.map +1 -0
  13. package/dist/backend/services/pim-connector-registry.service.js +83 -0
  14. package/dist/backend/services/pim-connector-registry.service.js.map +1 -0
  15. package/dist/manifest.d.ts +167 -0
  16. package/dist/manifest.d.ts.map +1 -0
  17. package/dist/manifest.js +45 -0
  18. package/dist/manifest.js.map +1 -0
  19. package/dist/migrations/20260826T153500_pim_connector_init.d.ts +11 -0
  20. package/dist/migrations/20260826T153500_pim_connector_init.d.ts.map +1 -0
  21. package/dist/migrations/20260826T153500_pim_connector_init.js +29 -0
  22. package/dist/migrations/20260826T153500_pim_connector_init.js.map +1 -0
  23. package/dist/migrations/index.d.ts +4 -0
  24. package/dist/migrations/index.d.ts.map +1 -0
  25. package/dist/migrations/index.js +4 -0
  26. package/dist/migrations/index.js.map +1 -0
  27. package/i18n/en.json +5 -0
  28. package/i18n/pl.json +5 -0
  29. package/package.json +65 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Endora sp. z o.o. and the Endora Commerce contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,49 @@
1
+ <!-- Generated by `pnpm --filter backend run manifests:generate`. Delete this line to take the file over; a README without it is never regenerated. -->
2
+
3
+ # @endora-commerce/mod-pim-connector
4
+
5
+ Shared operator behaviours for PIM connectors: mutual exclusion, run/issue vocabulary and field-protection path grammar.
6
+
7
+ ## What this is
8
+
9
+ An **Endora Commerce module package**. Its module id is `pim_connector` — the identity of record in the platform's registries, in its migrations and in its setting, permission and translation codes.
10
+
11
+ A module is not imported by application code. The platform discovers the extension packages an instance has installed, reads the manifest on the root subpath and composes the module from it, so installing this package and starting the instance is the whole integration.
12
+
13
+ ## Entry points
14
+
15
+ | Import | Contents |
16
+ | --- | --- |
17
+ | `@endora-commerce/mod-pim-connector` | the module manifest — its id, version, dependencies, settings and activation |
18
+ | `@endora-commerce/mod-pim-connector/backend` | the composition root the platform calls, with the entities, services, routes and workers it registers |
19
+ | `@endora-commerce/mod-pim-connector/migrations` | the module’s own schema migrations, in the order the platform runs them |
20
+
21
+ ## Depends on
22
+
23
+ Everything below is a **peer** dependency, so the application resolves exactly one copy of each — two copies of a host or of React are a runtime failure rather than a type error. An entry marked *optional* is needed only by the layers that use it.
24
+
25
+ **Endora packages**
26
+
27
+ - `@endora-commerce/contracts`
28
+ - `@endora-commerce/platform`
29
+
30
+ **Third-party**
31
+
32
+ - `@mikro-orm/core` ^6
33
+ - `@mikro-orm/migrations` ^6
34
+ - `@mikro-orm/postgresql` ^6
35
+
36
+ ## What the tarball carries
37
+
38
+ - `dist/` — the compiled JavaScript and its type declarations
39
+ - `i18n/` — the translation bundles this package ships (`en`, `pl`), loaded by the platform
40
+
41
+ ## Install
42
+
43
+ ```bash
44
+ pnpm add @endora-commerce/mod-pim-connector
45
+ ```
46
+
47
+ ## Licence
48
+
49
+ MIT — the text is in `LICENSE`, beside this file.
@@ -0,0 +1,15 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ /** Fixed primary key — one diagnostic row per deployment (feature 089, data-model.md). */
3
+ export declare const PIM_CONNECTOR_ACTIVATION_LOCK_SINGLETON_ID = "00000000-0000-4000-8000-000000000001";
4
+ /**
5
+ * Mirrors the last successful PIM connector activation write for audit/diagnostics.
6
+ * Authoritative mutual-exclusion checks read sibling activation Settings.
7
+ */
8
+ export declare class PimConnectorActivationLock {
9
+ [OptionalProps]?: 'updatedAt';
10
+ id: string;
11
+ activeModuleId?: string | null;
12
+ updatedAt: Date;
13
+ updatedByAdminId?: string | null;
14
+ }
15
+ //# sourceMappingURL=pim-connector-activation-lock.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pim-connector-activation-lock.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/pim-connector-activation-lock.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,aAAa,EAAwB,MAAM,iBAAiB,CAAC;AAG9E,0FAA0F;AAC1F,eAAO,MAAM,0CAA0C,yCAAyC,CAAC;AAEjG;;;GAGG;AACH,qBAEa,0BAA0B;IACrC,CAAC,aAAa,CAAC,CAAC,EAAE,WAAW,CAAC;IAG9B,EAAE,EAAE,MAAM,CAA8C;IAGxD,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAG/B,SAAS,EAAE,IAAI,CAAc;IAG7B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC"}
@@ -0,0 +1,46 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Entity, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core';
11
+ import { GlobalEntity } from '@endora-commerce/platform/tenancy';
12
+ /** Fixed primary key — one diagnostic row per deployment (feature 089, data-model.md). */
13
+ export const PIM_CONNECTOR_ACTIVATION_LOCK_SINGLETON_ID = '00000000-0000-4000-8000-000000000001';
14
+ /**
15
+ * Mirrors the last successful PIM connector activation write for audit/diagnostics.
16
+ * Authoritative mutual-exclusion checks read sibling activation Settings.
17
+ */
18
+ let PimConnectorActivationLock = class PimConnectorActivationLock {
19
+ [OptionalProps];
20
+ id = PIM_CONNECTOR_ACTIVATION_LOCK_SINGLETON_ID;
21
+ activeModuleId;
22
+ updatedAt = new Date();
23
+ updatedByAdminId;
24
+ };
25
+ __decorate([
26
+ PrimaryKey({ type: 'uuid' }),
27
+ __metadata("design:type", String)
28
+ ], PimConnectorActivationLock.prototype, "id", void 0);
29
+ __decorate([
30
+ Property({ type: 'varchar', length: 64, fieldName: 'active_module_id', nullable: true }),
31
+ __metadata("design:type", Object)
32
+ ], PimConnectorActivationLock.prototype, "activeModuleId", void 0);
33
+ __decorate([
34
+ Property({ type: 'timestamptz', fieldName: 'updated_at' }),
35
+ __metadata("design:type", Date)
36
+ ], PimConnectorActivationLock.prototype, "updatedAt", void 0);
37
+ __decorate([
38
+ Property({ type: 'uuid', fieldName: 'updated_by_admin_id', nullable: true }),
39
+ __metadata("design:type", Object)
40
+ ], PimConnectorActivationLock.prototype, "updatedByAdminId", void 0);
41
+ PimConnectorActivationLock = __decorate([
42
+ GlobalEntity(),
43
+ Entity({ tableName: 'pim_connector_activation_lock' })
44
+ ], PimConnectorActivationLock);
45
+ export { PimConnectorActivationLock };
46
+ //# sourceMappingURL=pim-connector-activation-lock.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pim-connector-activation-lock.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/pim-connector-activation-lock.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAEjE,0FAA0F;AAC1F,MAAM,CAAC,MAAM,0CAA0C,GAAG,sCAAsC,CAAC;AAEjG;;;GAGG;AAGI,IAAM,0BAA0B,GAAhC,MAAM,0BAA0B;IACrC,CAAC,aAAa,CAAC,CAAe;IAG9B,EAAE,GAAW,0CAA0C,CAAC;IAGxD,cAAc,CAAiB;IAG/B,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;IAG7B,gBAAgB,CAAiB;CAClC,CAAA;AAVC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;sDAC2B;AAGxD;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,kBAAkB,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;kEAC1D;AAG/B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;8BAChD,IAAI;6DAAc;AAG7B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,qBAAqB,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;oEAC5C;AAbtB,0BAA0B;IAFtC,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,+BAA+B,EAAE,CAAC;GAC1C,0BAA0B,CActC"}
@@ -0,0 +1,5 @@
1
+ import type { ModuleContext } from '@endora-commerce/platform/kernel';
2
+ import { PimConnectorActivationLock } from './entities/pim-connector-activation-lock.entity.js';
3
+ export declare function registerModule(ctx: ModuleContext): void;
4
+ export declare const entities: (typeof PimConnectorActivationLock)[];
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAGtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,oDAAoD,CAAC;AAYhG,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA6FvD;AAgBD,eAAO,MAAM,QAAQ,uCAA+B,CAAC"}
@@ -0,0 +1,106 @@
1
+ import { effectiveState, lazyPort } from '@endora-commerce/platform/kernel';
2
+ import { CAPABILITY_KEYS } from '@endora-commerce/contracts';
3
+ import { PimConnectorActivationLock } from './entities/pim-connector-activation-lock.entity.js';
4
+ import { PimConnectorRegistryService } from './services/pim-connector-registry.service.js';
5
+ export function registerModule(ctx) {
6
+ ctx.di.providePort('pimConnectorRegistryPort', ctx
7
+ .asFunction(({ emFactory }) => new PimConnectorRegistryService(emFactory))
8
+ .singleton());
9
+ /**
10
+ * **The** exclusivity seam — one interceptor, registered by the capability's
11
+ * owner over the derived family (feature 132,
12
+ * `specs/132-connector-family-discovery/contracts/capability-exclusivity.md`
13
+ * R1.1–R1.2).
14
+ *
15
+ * It used to be registered by **one member**, with that member's own id
16
+ * hard-coded in the handler, so every new connector had to remember to write one
17
+ * — and two of the four shipped ones did not. Registered here over
18
+ * `declaredMembersOfCapability`, a connector that declares membership is
19
+ * covered **by existing**, and the failure mode stops being a silent gap.
20
+ *
21
+ * Three properties of the placement are load-bearing:
22
+ *
23
+ * - **This module is always present.** It declares
24
+ * `activation.nonDeactivatable`, so the seam cannot be switched off by the
25
+ * operator whose activation attempt it is judging.
26
+ * - **Interceptor gating uses the platform axis** (`registryCache.isEnabled`),
27
+ * so the handler still runs while the *member* is operator-deactivated —
28
+ * which is exactly when an activation attempt arrives.
29
+ * - **It refuses before the audited Command runs** (`pre`), so a refused
30
+ * activation writes nothing: Principle XIII keeps the write audited and this
31
+ * keeps the refusal out of the write's way.
32
+ *
33
+ * The membership test reads **declared** members, not effective ones: the
34
+ * question is "is the module being activated one of mine", and a module about
35
+ * to be switched on is by definition not effectively present. Which members
36
+ * *hold* the claim is the registry's question, and it resolves effective
37
+ * presence (R2.2).
38
+ */
39
+ ctx.interceptors([
40
+ {
41
+ id: 'refuse-when-another-family-member-is-active',
42
+ target: 'POST /api/v1/admin/modules/:id/activation',
43
+ phase: 'pre',
44
+ handler: async (interceptorCtx) => {
45
+ const { params, body } = interceptorCtx;
46
+ const moduleId = params !== null && typeof params === 'object' && 'id' in params
47
+ ? String(params.id)
48
+ : '';
49
+ const active = body !== null &&
50
+ typeof body === 'object' &&
51
+ 'active' in body &&
52
+ body.active === true;
53
+ // Deactivation is never refused by this seam (R2.4).
54
+ if (moduleId === '' || !active)
55
+ return;
56
+ if (!isFamilyMember(moduleId))
57
+ return;
58
+ await lazyPort(ctx, 'pimConnectorRegistryPort').assertCanActivate(moduleId);
59
+ },
60
+ },
61
+ ]);
62
+ /**
63
+ * Feature 089 / T079 — mirror a member's successful activation flip onto the
64
+ * diagnostic lock row. Owned here (always present) so the handler still runs
65
+ * when the event fires before local presence has refreshed after activate, and
66
+ * after deactivate when the member itself is already absent.
67
+ *
68
+ * Feature 132 — the test compared `event.moduleId` against one connector's id
69
+ * written out here, so the lock was accurate for one connector out of four and
70
+ * this module named a member it does not own. It is a membership test over the
71
+ * derived family now, and **declared**
72
+ * membership is the right side of it for the reason above: on a deactivation the
73
+ * member is already absent, so an effective-presence test would drop exactly the
74
+ * event that has to clear the lock.
75
+ */
76
+ ctx.subscribe('module.activation.changed', async (payload) => {
77
+ const event = payload;
78
+ const moduleId = event.moduleId;
79
+ if (moduleId === undefined || typeof event.active !== 'boolean')
80
+ return;
81
+ if (!isFamilyMember(moduleId))
82
+ return;
83
+ const registry = lazyPort(ctx, 'pimConnectorRegistryPort');
84
+ if (event.active) {
85
+ await registry.recordActive(moduleId, null);
86
+ }
87
+ else {
88
+ await registry.clearActive(moduleId);
89
+ }
90
+ });
91
+ }
92
+ /**
93
+ * Whether `moduleId` declared membership of the capability this module owns.
94
+ *
95
+ * The family is read from the platform on every call rather than captured at
96
+ * registration: `registryCache` is re-installed by the `b2b:module:state-changed`
97
+ * refresh, and a list closed over at composition time would answer for the
98
+ * deployment as it was when this module registered.
99
+ */
100
+ function isFamilyMember(moduleId) {
101
+ return effectiveState
102
+ .declaredMembersOfCapability(CAPABILITY_KEYS.PIM_CONNECTOR)
103
+ .includes(moduleId);
104
+ }
105
+ export const entities = [PimConnectorActivationLock];
106
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAiC,MAAM,4BAA4B,CAAC;AAC5F,OAAO,EAAE,0BAA0B,EAAE,MAAM,oDAAoD,CAAC;AAChG,OAAO,EAAE,2BAA2B,EAAE,MAAM,8CAA8C,CAAC;AAW3F,MAAM,UAAU,cAAc,CAAC,GAAkB;IAC/C,GAAG,CAAC,EAAE,CAAC,WAAW,CAChB,0BAA0B,EAC1B,GAAG;SACA,UAAU,CAAC,CAAC,EAAE,SAAS,EAAsB,EAAE,EAAE,CAAC,IAAI,2BAA2B,CAAC,SAAS,CAAC,CAAC;SAC7F,SAAS,EAAE,CACf,CAAC;IAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,GAAG,CAAC,YAAY,CAAC;QACf;YACE,EAAE,EAAE,6CAA6C;YACjD,MAAM,EAAE,2CAA2C;YACnD,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,KAAK,EAAE,cAGf,EAAiB,EAAE;gBAClB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,cAAc,CAAC;gBACxC,MAAM,QAAQ,GACZ,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,IAAI,IAAI,MAAM;oBAC7D,CAAC,CAAC,MAAM,CAAE,MAA0B,CAAC,EAAE,CAAC;oBACxC,CAAC,CAAC,EAAE,CAAC;gBACT,MAAM,MAAM,GACV,IAAI,KAAK,IAAI;oBACb,OAAO,IAAI,KAAK,QAAQ;oBACxB,QAAQ,IAAI,IAAI;oBACf,IAA4B,CAAC,MAAM,KAAK,IAAI,CAAC;gBAChD,qDAAqD;gBACrD,IAAI,QAAQ,KAAK,EAAE,IAAI,CAAC,MAAM;oBAAE,OAAO;gBACvC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC;oBAAE,OAAO;gBACtC,MAAM,QAAQ,CAA2B,GAAG,EAAE,0BAA0B,CAAC,CAAC,iBAAiB,CACzF,QAAQ,CACT,CAAC;YACJ,CAAC;SACF;KACF,CAAC,CAAC;IAEH;;;;;;;;;;;;;OAaG;IACH,GAAG,CAAC,SAAS,CAAC,2BAA2B,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC3D,MAAM,KAAK,GAAG,OAAyC,CAAC;QACxD,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAChC,IAAI,QAAQ,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO;QACxE,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC;YAAE,OAAO;QACtC,MAAM,QAAQ,GAAG,QAAQ,CAA2B,GAAG,EAAE,0BAA0B,CAAC,CAAC;QACrF,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,QAAQ,CAAC,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACN,MAAM,QAAQ,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QACvC,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,cAAc,CAAC,QAAgB;IACtC,OAAO,cAAc;SAClB,2BAA2B,CAAC,eAAe,CAAC,aAAa,CAAC;SAC1D,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,0BAA0B,CAAC,CAAC"}
@@ -0,0 +1,48 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import { type PimConnectorRegistryPort } from '@endora-commerce/contracts';
3
+ /**
4
+ * How the registry asks **who is in the family and present** (feature 132,
5
+ * `capability-exclusivity.md` R2.1–R2.2).
6
+ *
7
+ * Two things moved here and both were defects.
8
+ *
9
+ * The member list was `PIM_CONNECTOR_MODULES`, a hand-written array in
10
+ * `@endora-commerce/contracts` that named two of the four shipped connectors — so
11
+ * exclusivity covered 2 of the 12 ordered pairs, a connector outside this
12
+ * repository could not join at all, and an overlay module had to edit a core file
13
+ * to. It is now the members' own manifest declarations, derived on every
14
+ * composition, so a connector that declares membership is covered **by existing**.
15
+ *
16
+ * The axis was `presence(id)?.operatorActivated`, which answers `true` for a
17
+ * module whose *platform* availability is off — a connector the deployment never
18
+ * installed holding a claim that refuses another. It is now the conjunction
19
+ * `effectiveState.membersOfCapability` computes: a module that is off behaves as
20
+ * if never installed (Principle XVII, spec FR-012).
21
+ *
22
+ * The default reads the platform's effective state directly, so it stays in
23
+ * lockstep with `/platform/modules` flips — those refresh presence immediately
24
+ * and do not go through the settings cache the activation Command bypasses.
25
+ */
26
+ export interface PimConnectorFamilyReader {
27
+ /** The family members whose **effective** presence is true, in manifest order. */
28
+ activeMembers(): readonly string[];
29
+ }
30
+ export declare class PimConnectorRegistryService implements PimConnectorRegistryPort {
31
+ private readonly emFactory;
32
+ private readonly family;
33
+ constructor(emFactory: () => EntityManager, family?: PimConnectorFamilyReader);
34
+ assertCanActivate(moduleId: string): Promise<void>;
35
+ recordActive(moduleId: string, adminId: string | null): Promise<void>;
36
+ clearActive(moduleId: string): Promise<void>;
37
+ getActiveModuleId(): Promise<string | null>;
38
+ /**
39
+ * The member holding the claim, or `null`.
40
+ *
41
+ * `exceptModuleId` is excluded even though an effectively-present module is by
42
+ * definition not the one being switched on: the activation route is idempotent,
43
+ * so `POST {active:true}` against the module that already holds the claim must
44
+ * not be refused by the claim it holds.
45
+ */
46
+ private findActiveSibling;
47
+ }
48
+ //# sourceMappingURL=pim-connector-registry.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pim-connector-registry.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/pim-connector-registry.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAGL,KAAK,wBAAwB,EAC9B,MAAM,4BAA4B,CAAC;AAQpC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,WAAW,wBAAwB;IACvC,kFAAkF;IAClF,aAAa,IAAI,SAAS,MAAM,EAAE,CAAC;CACpC;AAQD,qBAAa,2BAA4B,YAAW,wBAAwB;IAExE,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,SAAS,EAAE,MAAM,aAAa,EAC9B,MAAM,GAAE,wBAA8C;IAGnE,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYlD,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBrE,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAe5C,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQjD;;;;;;;OAOG;IACH,OAAO,CAAC,iBAAiB;CAO1B"}
@@ -0,0 +1,83 @@
1
+ import { CAPABILITY_KEYS, ERROR_CODES, } from '@endora-commerce/contracts';
2
+ import { effectiveState } from '@endora-commerce/platform/kernel';
3
+ import { HttpError } from '@endora-commerce/platform/http';
4
+ import { PIM_CONNECTOR_ACTIVATION_LOCK_SINGLETON_ID, PimConnectorActivationLock, } from '../entities/pim-connector-activation-lock.entity.js';
5
+ const defaultFamilyReader = {
6
+ activeMembers() {
7
+ return effectiveState.membersOfCapability(CAPABILITY_KEYS.PIM_CONNECTOR);
8
+ },
9
+ };
10
+ export class PimConnectorRegistryService {
11
+ emFactory;
12
+ family;
13
+ constructor(emFactory, family = defaultFamilyReader) {
14
+ this.emFactory = emFactory;
15
+ this.family = family;
16
+ }
17
+ async assertCanActivate(moduleId) {
18
+ const activeSibling = this.findActiveSibling(moduleId);
19
+ if (activeSibling !== null) {
20
+ throw new HttpError(409, ERROR_CODES.PIM_CONNECTOR_ALREADY_ACTIVE, `Another PIM connector is already active: ${activeSibling}`, { activeModuleId: activeSibling });
21
+ }
22
+ }
23
+ async recordActive(moduleId, adminId) {
24
+ const em = this.emFactory();
25
+ let row = await em.findOne(PimConnectorActivationLock, {
26
+ id: PIM_CONNECTOR_ACTIVATION_LOCK_SINGLETON_ID,
27
+ });
28
+ if (row === null) {
29
+ // command-coverage-ignore: mutual-exclusion bookkeeping. Mirrors the operator's
30
+ // PIM activation choice in the lock row; the activation write itself is audited.
31
+ row = em.create(PimConnectorActivationLock, {
32
+ id: PIM_CONNECTOR_ACTIVATION_LOCK_SINGLETON_ID,
33
+ activeModuleId: moduleId,
34
+ updatedByAdminId: adminId,
35
+ updatedAt: new Date(),
36
+ });
37
+ }
38
+ else {
39
+ row.activeModuleId = moduleId;
40
+ row.updatedByAdminId = adminId;
41
+ row.updatedAt = new Date();
42
+ }
43
+ await em.flush();
44
+ }
45
+ async clearActive(moduleId) {
46
+ const em = this.emFactory();
47
+ const row = await em.findOne(PimConnectorActivationLock, {
48
+ id: PIM_CONNECTOR_ACTIVATION_LOCK_SINGLETON_ID,
49
+ });
50
+ if (row === null || row.activeModuleId !== moduleId) {
51
+ return;
52
+ }
53
+ // command-coverage-ignore: mutual-exclusion bookkeeping. Clears the lock when the
54
+ // named connector deactivates; the activation write itself is audited.
55
+ row.activeModuleId = null;
56
+ row.updatedAt = new Date();
57
+ await em.flush();
58
+ }
59
+ async getActiveModuleId() {
60
+ const em = this.emFactory();
61
+ const row = await em.findOne(PimConnectorActivationLock, {
62
+ id: PIM_CONNECTOR_ACTIVATION_LOCK_SINGLETON_ID,
63
+ });
64
+ return row?.activeModuleId ?? null;
65
+ }
66
+ /**
67
+ * The member holding the claim, or `null`.
68
+ *
69
+ * `exceptModuleId` is excluded even though an effectively-present module is by
70
+ * definition not the one being switched on: the activation route is idempotent,
71
+ * so `POST {active:true}` against the module that already holds the claim must
72
+ * not be refused by the claim it holds.
73
+ */
74
+ findActiveSibling(exceptModuleId) {
75
+ for (const moduleId of this.family.activeMembers()) {
76
+ if (moduleId === exceptModuleId)
77
+ continue;
78
+ return moduleId;
79
+ }
80
+ return null;
81
+ }
82
+ }
83
+ //# sourceMappingURL=pim-connector-registry.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pim-connector-registry.service.js","sourceRoot":"","sources":["../../../src/backend/services/pim-connector-registry.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Shared PIM connector infrastructure — feature 089.
3
+ *
4
+ * Owns mutual-exclusion registry and field-path grammar shared by UnoPim now
5
+ * and future PIM connectors. No import pipeline.
6
+ */
7
+ export declare const manifest: {
8
+ id: string;
9
+ name: string;
10
+ version: string;
11
+ dependencies: string[];
12
+ description?: string | undefined;
13
+ acknowledgedDependencies?: {
14
+ moduleId: string;
15
+ port: string;
16
+ reason: string;
17
+ }[] | undefined;
18
+ nonBindingDependencies?: {
19
+ moduleId: string;
20
+ name: string;
21
+ kind: "contributes-to" | "degrades-without" | "refuses-without";
22
+ reason: string;
23
+ whenAbsent?: string | undefined;
24
+ }[] | undefined;
25
+ activation?: {
26
+ settingCode: string;
27
+ default: boolean;
28
+ } | {
29
+ nonDeactivatable: true;
30
+ reason: string;
31
+ } | undefined;
32
+ settings?: {
33
+ moduleCode: string;
34
+ groups: {
35
+ code: string;
36
+ name: string;
37
+ salesChannelCodes?: string[] | undefined;
38
+ isSystemProtected?: boolean | undefined;
39
+ }[];
40
+ settings: {
41
+ code: string;
42
+ name: string;
43
+ valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
44
+ defaultValue: unknown;
45
+ description?: string | undefined;
46
+ groupCode?: string | undefined;
47
+ previousDefaultValues?: unknown[] | undefined;
48
+ salesChannelCodes?: string[] | undefined;
49
+ enumOptions?: string[] | undefined;
50
+ configurationType?: string | undefined;
51
+ hidden?: boolean | undefined;
52
+ }[];
53
+ } | undefined;
54
+ i18n?: {
55
+ bundlesDir: string;
56
+ } | undefined;
57
+ docs?: false | {
58
+ dir: string;
59
+ } | undefined;
60
+ demo?: false | {
61
+ summary: string;
62
+ seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
63
+ reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
64
+ after?: readonly string[] | undefined;
65
+ package?: string | undefined;
66
+ } | undefined;
67
+ actions?: {
68
+ id: string;
69
+ labelKey: string;
70
+ icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
71
+ targetRoute: string;
72
+ keywords: string[];
73
+ weight: number;
74
+ descriptionKey?: string | undefined;
75
+ requiredPermission?: string | undefined;
76
+ }[] | undefined;
77
+ permissions?: {
78
+ code: string;
79
+ label: string;
80
+ module?: string | undefined;
81
+ description?: string | undefined;
82
+ requires?: string[] | undefined;
83
+ }[] | undefined;
84
+ transactionalEmails?: {
85
+ code: string;
86
+ name: string;
87
+ variables: {
88
+ key: string;
89
+ label: string;
90
+ sampleValue?: string | undefined;
91
+ description?: string | undefined;
92
+ }[];
93
+ description?: string | undefined;
94
+ group?: string | undefined;
95
+ }[] | undefined;
96
+ capabilities?: string[] | undefined;
97
+ exclusiveCapabilities?: {
98
+ key: string;
99
+ errorCode: string;
100
+ }[] | undefined;
101
+ errorCodes?: {
102
+ code: string;
103
+ tokens?: string[] | undefined;
104
+ }[] | undefined;
105
+ blocks?: {
106
+ name: string;
107
+ labelKey: string;
108
+ category: string;
109
+ contexts: ("invoice" | "email" | "cms" | "newsletter")[];
110
+ fields: Record<string, {
111
+ type: "number" | "object" | "array" | "text" | "textarea" | "select" | "radio" | "external" | "uuid" | "richtext";
112
+ label?: string | undefined;
113
+ required?: boolean | undefined;
114
+ options?: {
115
+ label: string;
116
+ value: string | number;
117
+ }[] | undefined;
118
+ refKind?: string | undefined;
119
+ }>;
120
+ descriptionKey?: string | undefined;
121
+ defaultProps?: Record<string, unknown> | undefined;
122
+ responsiveFields?: string[] | undefined;
123
+ previewIcon?: string | undefined;
124
+ weight?: number | undefined;
125
+ }[] | undefined;
126
+ blockCategories?: {
127
+ key: string;
128
+ titleKey: string;
129
+ contexts: ("invoice" | "email" | "cms" | "newsletter")[];
130
+ weight?: number | undefined;
131
+ visible?: boolean | undefined;
132
+ }[] | undefined;
133
+ env?: {
134
+ name: string;
135
+ describes: {
136
+ en: string;
137
+ pl: string;
138
+ };
139
+ requirement: {
140
+ kind: "required";
141
+ } | {
142
+ kind: "requiredWhen";
143
+ input: string;
144
+ equals: string;
145
+ } | {
146
+ kind: "optional";
147
+ without: {
148
+ en: string;
149
+ pl: string;
150
+ };
151
+ };
152
+ secret: boolean;
153
+ generable: boolean;
154
+ owner: {
155
+ kind: "platform";
156
+ } | {
157
+ kind: "application";
158
+ application: "admin" | "backend" | "storefront";
159
+ } | {
160
+ kind: "module";
161
+ moduleId: string;
162
+ };
163
+ consumers: ("admin" | "backend" | "storefront")[];
164
+ addressOf: "admin" | "backend" | "storefront" | null;
165
+ }[] | undefined;
166
+ };
167
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsCnB,CAAC"}
@@ -0,0 +1,45 @@
1
+ import { CAPABILITY_KEYS, defineModuleManifest } from '@endora-commerce/contracts';
2
+ /**
3
+ * Shared PIM connector infrastructure — feature 089.
4
+ *
5
+ * Owns mutual-exclusion registry and field-path grammar shared by UnoPim now
6
+ * and future PIM connectors. No import pipeline.
7
+ */
8
+ export const manifest = defineModuleManifest({
9
+ id: 'pim_connector',
10
+ name: 'PIM connector shared layer',
11
+ description: 'Shared operator behaviours for PIM connectors: mutual exclusion, run/issue vocabulary and field-protection path grammar.',
12
+ version: '1.0.0',
13
+ dependencies: ['settings', 'admin_users'],
14
+ activation: {
15
+ nonDeactivatable: true,
16
+ reason: 'The shared PIM connector registry must stay available so operator activation of a PIM connector can be refused when another is already active.',
17
+ },
18
+ i18n: { bundlesDir: 'i18n' },
19
+ /**
20
+ * Feature 132 — this module **owns** the PIM connector capability and declares
21
+ * it mutually exclusive, minting the code an operator meets
22
+ * (`contracts/module-capabilities.md` R3.1).
23
+ *
24
+ * The owner declares exclusivity and a member never can, which is what keeps
25
+ * three things from coming loose. The refusal code stays on the semantic owner,
26
+ * so a member raises `PIM_CONNECTOR_ALREADY_ACTIVE` and must **not** declare it
27
+ * (D-95.2, and one member's manifest carries the paragraph explaining that).
28
+ * A connector cannot make the family exclusive by accident and cannot un-make
29
+ * it. And a deployment that installs a connector without this shared layer has
30
+ * a family that is simply not exclusive there (R3.5) — the honest answer, since
31
+ * without this module there is no lock row and nothing to enforce with.
32
+ *
33
+ * This module is **not** a member of the key it owns: an owner that were also a
34
+ * member would exclude itself from its own family, and `defineModuleManifest`
35
+ * refuses the pair (R3.3 / R4.4).
36
+ */
37
+ exclusiveCapabilities: [
38
+ {
39
+ key: CAPABILITY_KEYS.PIM_CONNECTOR,
40
+ errorCode: 'PIM_CONNECTOR_ALREADY_ACTIVE',
41
+ },
42
+ ],
43
+ errorCodes: [{ code: 'PIM_CONNECTOR_ALREADY_ACTIVE' }],
44
+ });
45
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAEnF;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAoB,CAAC;IAC3C,EAAE,EAAE,eAAe;IACnB,IAAI,EAAE,4BAA4B;IAClC,WAAW,EACT,0HAA0H;IAC5H,OAAO,EAAE,OAAO;IAChB,YAAY,EAAE,CAAC,UAAU,EAAE,aAAa,CAAC;IACzC,UAAU,EAAE;QACV,gBAAgB,EAAE,IAAI;QACtB,MAAM,EACJ,gJAAgJ;KACnJ;IACD,IAAI,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE;IAC5B;;;;;;;;;;;;;;;;;OAiBG;IACH,qBAAqB,EAAE;QACrB;YACE,GAAG,EAAE,eAAe,CAAC,aAAa;YAClC,SAAS,EAAE,8BAA8B;SAC1C;KACF;IACD,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,8BAA8B,EAAE,CAAC;CACvD,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * Feature 089 — `pim_connector` shared layer (init).
4
+ *
5
+ * Creates `pim_connector_activation_lock` per data-model.md.
6
+ */
7
+ export declare class Migration20260826T153500PimConnectorInit extends Migration {
8
+ up(): Promise<void>;
9
+ down(): Promise<void>;
10
+ }
11
+ //# sourceMappingURL=20260826T153500_pim_connector_init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260826T153500_pim_connector_init.d.ts","sourceRoot":"","sources":["../../src/migrations/20260826T153500_pim_connector_init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;GAIG;AACH,qBAAa,wCAAyC,SAAQ,SAAS;IACtD,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAkBnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAGrC"}
@@ -0,0 +1,29 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * Feature 089 — `pim_connector` shared layer (init).
4
+ *
5
+ * Creates `pim_connector_activation_lock` per data-model.md.
6
+ */
7
+ export class Migration20260826T153500PimConnectorInit extends Migration {
8
+ async up() {
9
+ this.addSql(`
10
+ create table "pim_connector_activation_lock" (
11
+ "id" uuid not null,
12
+ "active_module_id" varchar(64) null,
13
+ "updated_at" timestamptz not null,
14
+ "updated_by_admin_id" uuid null,
15
+ constraint "pim_connector_activation_lock_pkey" primary key ("id")
16
+ );
17
+ `);
18
+ this.addSql(`
19
+ alter table "pim_connector_activation_lock"
20
+ add constraint "pim_connector_activation_lock_updated_by_admin_id_foreign"
21
+ foreign key ("updated_by_admin_id") references "admin_users" ("id")
22
+ on update cascade on delete set null;
23
+ `);
24
+ }
25
+ async down() {
26
+ this.addSql(`drop table if exists "pim_connector_activation_lock" cascade;`);
27
+ }
28
+ }
29
+ //# sourceMappingURL=20260826T153500_pim_connector_init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260826T153500_pim_connector_init.js","sourceRoot":"","sources":["../../src/migrations/20260826T153500_pim_connector_init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;GAIG;AACH,MAAM,OAAO,wCAAyC,SAAQ,SAAS;IAC5D,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CAAC;;;;;;;;KAQX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC;;;;;KAKX,CAAC,CAAC;IACL,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,+DAA+D,CAAC,CAAC;IAC/E,CAAC;CACF"}
@@ -0,0 +1,4 @@
1
+ import { Migration20260826T153500PimConnectorInit } from './20260826T153500_pim_connector_init.js';
2
+ export declare const migrations: (typeof Migration20260826T153500PimConnectorInit)[];
3
+ export { Migration20260826T153500PimConnectorInit };
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wCAAwC,EAAE,MAAM,yCAAyC,CAAC;AAEnG,eAAO,MAAM,UAAU,qDAA6C,CAAC;AAErE,OAAO,EAAE,wCAAwC,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Migration20260826T153500PimConnectorInit } from './20260826T153500_pim_connector_init.js';
2
+ export const migrations = [Migration20260826T153500PimConnectorInit];
3
+ export { Migration20260826T153500PimConnectorInit };
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wCAAwC,EAAE,MAAM,yCAAyC,CAAC;AAEnG,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,wCAAwC,CAAC,CAAC;AAErE,OAAO,EAAE,wCAAwC,EAAE,CAAC"}
package/i18n/en.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "registry.alreadyActive": "Another PIM connector is already active: {{moduleId}}",
3
+ "registry.alreadyActive.detail": "Deactivate the other connector before activating this one.",
4
+ "errors.PIM_CONNECTOR_ALREADY_ACTIVE": "Another PIM connector is already active ({activeModuleId}). Switch it off first."
5
+ }
package/i18n/pl.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "registry.alreadyActive": "Inny konektor PIM jest już aktywny: {{moduleId}}",
3
+ "registry.alreadyActive.detail": "Dezaktywuj drugi konektor przed włączeniem tego.",
4
+ "errors.PIM_CONNECTOR_ALREADY_ACTIVE": "Inny connector PIM jest już aktywny ({activeModuleId}). Najpierw go wyłącz."
5
+ }
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@endora-commerce/mod-pim-connector",
3
+ "version": "0.100.0",
4
+ "type": "module",
5
+ "sideEffects": false,
6
+ "description": "Shared operator behaviours for PIM connectors: mutual exclusion, run/issue vocabulary and field-protection path grammar.",
7
+ "license": "MIT",
8
+ "endora": {
9
+ "type": "module",
10
+ "id": "pim_connector"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/endora-commerce/endora-commerce.git",
15
+ "directory": "packages/modules/pim_connector"
16
+ },
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/manifest.d.ts",
23
+ "default": "./dist/manifest.js"
24
+ },
25
+ "./backend": {
26
+ "types": "./dist/backend/index.d.ts",
27
+ "default": "./dist/backend/index.js"
28
+ },
29
+ "./migrations": {
30
+ "types": "./dist/migrations/index.d.ts",
31
+ "default": "./dist/migrations/index.js"
32
+ },
33
+ "./package.json": "./package.json"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "i18n"
38
+ ],
39
+ "engines": {
40
+ "node": ">=22.18.0"
41
+ },
42
+ "peerDependencies": {
43
+ "@mikro-orm/core": "^6",
44
+ "@mikro-orm/migrations": "^6",
45
+ "@mikro-orm/postgresql": "^6",
46
+ "@endora-commerce/contracts": "0.100.0",
47
+ "@endora-commerce/platform": "0.100.0"
48
+ },
49
+ "devDependencies": {
50
+ "@mikro-orm/core": "^6.6.13",
51
+ "@mikro-orm/migrations": "^6.6.13",
52
+ "@mikro-orm/postgresql": "^6.6.13",
53
+ "@types/node": "^22.9.0",
54
+ "typescript": "^5.9.3",
55
+ "vitest": "^4.1.11",
56
+ "@endora-commerce/contracts": "0.100.0",
57
+ "@endora-commerce/platform": "0.100.0"
58
+ },
59
+ "scripts": {
60
+ "build": "tsc -p tsconfig.build.json",
61
+ "typecheck": "tsc -p tsconfig.json",
62
+ "lint": "eslint src",
63
+ "test": "vitest run"
64
+ }
65
+ }