@open-mercato/core 0.4.6-develop-6d72ec5960 → 0.4.6-develop-cd1e2a9a0e

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 (226) hide show
  1. package/AGENTS.md +10 -0
  2. package/dist/generated/entities/integration_credentials/index.js +19 -0
  3. package/dist/generated/entities/integration_credentials/index.js.map +7 -0
  4. package/dist/generated/entities/integration_log/index.js +27 -0
  5. package/dist/generated/entities/integration_log/index.js.map +7 -0
  6. package/dist/generated/entities/integration_state/index.js +27 -0
  7. package/dist/generated/entities/integration_state/index.js.map +7 -0
  8. package/dist/generated/entities/sync_cursor/index.js +19 -0
  9. package/dist/generated/entities/sync_cursor/index.js.map +7 -0
  10. package/dist/generated/entities/sync_external_id_mapping/index.js +27 -0
  11. package/dist/generated/entities/sync_external_id_mapping/index.js.map +7 -0
  12. package/dist/generated/entities/sync_mapping/index.js +19 -0
  13. package/dist/generated/entities/sync_mapping/index.js.map +7 -0
  14. package/dist/generated/entities/sync_run/index.js +45 -0
  15. package/dist/generated/entities/sync_run/index.js.map +7 -0
  16. package/dist/generated/entities/sync_schedule/index.js +35 -0
  17. package/dist/generated/entities/sync_schedule/index.js.map +7 -0
  18. package/dist/generated/entities.ids.generated.js +14 -0
  19. package/dist/generated/entities.ids.generated.js.map +2 -2
  20. package/dist/generated/entity-fields-registry.js +16 -0
  21. package/dist/generated/entity-fields-registry.js.map +2 -2
  22. package/dist/modules/data_sync/acl.js +11 -0
  23. package/dist/modules/data_sync/acl.js.map +7 -0
  24. package/dist/modules/data_sync/api/mappings/[id]/route.js +137 -0
  25. package/dist/modules/data_sync/api/mappings/[id]/route.js.map +7 -0
  26. package/dist/modules/data_sync/api/mappings/route.js +132 -0
  27. package/dist/modules/data_sync/api/mappings/route.js.map +7 -0
  28. package/dist/modules/data_sync/api/run.js +87 -0
  29. package/dist/modules/data_sync/api/run.js.map +7 -0
  30. package/dist/modules/data_sync/api/runs/[id]/cancel.js +49 -0
  31. package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +7 -0
  32. package/dist/modules/data_sync/api/runs/[id]/retry.js +93 -0
  33. package/dist/modules/data_sync/api/runs/[id]/retry.js.map +7 -0
  34. package/dist/modules/data_sync/api/runs/[id]/route.js +69 -0
  35. package/dist/modules/data_sync/api/runs/[id]/route.js.map +7 -0
  36. package/dist/modules/data_sync/api/runs.js +66 -0
  37. package/dist/modules/data_sync/api/runs.js.map +7 -0
  38. package/dist/modules/data_sync/api/validate.js +66 -0
  39. package/dist/modules/data_sync/api/validate.js.map +7 -0
  40. package/dist/modules/data_sync/backend/data-sync/page.js +216 -0
  41. package/dist/modules/data_sync/backend/data-sync/page.js.map +7 -0
  42. package/dist/modules/data_sync/backend/data-sync/page.meta.js +25 -0
  43. package/dist/modules/data_sync/backend/data-sync/page.meta.js.map +7 -0
  44. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +178 -0
  45. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +7 -0
  46. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.meta.js +14 -0
  47. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.meta.js.map +7 -0
  48. package/dist/modules/data_sync/data/entities.js +228 -0
  49. package/dist/modules/data_sync/data/entities.js.map +7 -0
  50. package/dist/modules/data_sync/data/validators.js +32 -0
  51. package/dist/modules/data_sync/data/validators.js.map +7 -0
  52. package/dist/modules/data_sync/di.js +26 -0
  53. package/dist/modules/data_sync/di.js.map +7 -0
  54. package/dist/modules/data_sync/events.js +16 -0
  55. package/dist/modules/data_sync/events.js.map +7 -0
  56. package/dist/modules/data_sync/index.js +9 -0
  57. package/dist/modules/data_sync/index.js.map +7 -0
  58. package/dist/modules/data_sync/lib/adapter-registry.js +16 -0
  59. package/dist/modules/data_sync/lib/adapter-registry.js.map +7 -0
  60. package/dist/modules/data_sync/lib/adapter.js +1 -0
  61. package/dist/modules/data_sync/lib/adapter.js.map +7 -0
  62. package/dist/modules/data_sync/lib/id-mapping.js +79 -0
  63. package/dist/modules/data_sync/lib/id-mapping.js.map +7 -0
  64. package/dist/modules/data_sync/lib/queue.js +17 -0
  65. package/dist/modules/data_sync/lib/queue.js.map +7 -0
  66. package/dist/modules/data_sync/lib/sync-engine.js +309 -0
  67. package/dist/modules/data_sync/lib/sync-engine.js.map +7 -0
  68. package/dist/modules/data_sync/lib/sync-run-service.js +148 -0
  69. package/dist/modules/data_sync/lib/sync-run-service.js.map +7 -0
  70. package/dist/modules/data_sync/migrations/Migration20260304113737.js +17 -0
  71. package/dist/modules/data_sync/migrations/Migration20260304113737.js.map +7 -0
  72. package/dist/modules/data_sync/setup.js +13 -0
  73. package/dist/modules/data_sync/setup.js.map +7 -0
  74. package/dist/modules/data_sync/workers/sync-export.js +14 -0
  75. package/dist/modules/data_sync/workers/sync-export.js.map +7 -0
  76. package/dist/modules/data_sync/workers/sync-import.js +14 -0
  77. package/dist/modules/data_sync/workers/sync-import.js.map +7 -0
  78. package/dist/modules/data_sync/workers/sync-scheduled.js +63 -0
  79. package/dist/modules/data_sync/workers/sync-scheduled.js.map +7 -0
  80. package/dist/modules/entities/lib/encryptionDefaults.js +4 -0
  81. package/dist/modules/entities/lib/encryptionDefaults.js.map +2 -2
  82. package/dist/modules/integrations/acl.js +4 -1
  83. package/dist/modules/integrations/acl.js.map +2 -2
  84. package/dist/modules/integrations/api/[id]/credentials/route.js +127 -0
  85. package/dist/modules/integrations/api/[id]/credentials/route.js.map +7 -0
  86. package/dist/modules/integrations/api/[id]/health/route.js +46 -0
  87. package/dist/modules/integrations/api/[id]/health/route.js.map +7 -0
  88. package/dist/modules/integrations/api/[id]/route.js +65 -0
  89. package/dist/modules/integrations/api/[id]/route.js.map +7 -0
  90. package/dist/modules/integrations/api/[id]/state/route.js +109 -0
  91. package/dist/modules/integrations/api/[id]/state/route.js.map +7 -0
  92. package/dist/modules/integrations/api/[id]/version/route.js +117 -0
  93. package/dist/modules/integrations/api/[id]/version/route.js.map +7 -0
  94. package/dist/modules/integrations/api/guards.js +31 -0
  95. package/dist/modules/integrations/api/guards.js.map +7 -0
  96. package/dist/modules/integrations/api/logs/route.js +60 -0
  97. package/dist/modules/integrations/api/logs/route.js.map +7 -0
  98. package/dist/modules/integrations/api/openapi.js +25 -0
  99. package/dist/modules/integrations/api/openapi.js.map +7 -0
  100. package/dist/modules/integrations/api/route.js +68 -0
  101. package/dist/modules/integrations/api/route.js.map +7 -0
  102. package/dist/modules/integrations/backend/integrations/[id]/page.js +313 -0
  103. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +7 -0
  104. package/dist/modules/integrations/backend/integrations/[id]/page.meta.js +15 -0
  105. package/dist/modules/integrations/backend/integrations/[id]/page.meta.js.map +7 -0
  106. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +189 -0
  107. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +7 -0
  108. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.meta.js +15 -0
  109. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.meta.js.map +7 -0
  110. package/dist/modules/integrations/backend/integrations/page.js +212 -0
  111. package/dist/modules/integrations/backend/integrations/page.js.map +7 -0
  112. package/dist/modules/integrations/backend/integrations/page.meta.js +22 -0
  113. package/dist/modules/integrations/backend/integrations/page.meta.js.map +7 -0
  114. package/dist/modules/integrations/data/enrichers.js +27 -12
  115. package/dist/modules/integrations/data/enrichers.js.map +2 -2
  116. package/dist/modules/integrations/data/entities.js +136 -1
  117. package/dist/modules/integrations/data/entities.js.map +2 -2
  118. package/dist/modules/integrations/data/validators.js +36 -0
  119. package/dist/modules/integrations/data/validators.js.map +7 -0
  120. package/dist/modules/integrations/di.js +24 -0
  121. package/dist/modules/integrations/di.js.map +7 -0
  122. package/dist/modules/integrations/events.js +19 -0
  123. package/dist/modules/integrations/events.js.map +7 -0
  124. package/dist/modules/integrations/lib/credentials-service.js +159 -0
  125. package/dist/modules/integrations/lib/credentials-service.js.map +7 -0
  126. package/dist/modules/integrations/lib/health-service.js +37 -0
  127. package/dist/modules/integrations/lib/health-service.js.map +7 -0
  128. package/dist/modules/integrations/lib/log-service.js +66 -0
  129. package/dist/modules/integrations/lib/log-service.js.map +7 -0
  130. package/dist/modules/integrations/lib/registry-service.js +33 -0
  131. package/dist/modules/integrations/lib/registry-service.js.map +7 -0
  132. package/dist/modules/integrations/lib/state-service.js +55 -0
  133. package/dist/modules/integrations/lib/state-service.js.map +7 -0
  134. package/dist/modules/integrations/lib/types.js +1 -0
  135. package/dist/modules/integrations/lib/types.js.map +7 -0
  136. package/dist/modules/integrations/migrations/Migration20260304113737.js +19 -0
  137. package/dist/modules/integrations/migrations/Migration20260304113737.js.map +7 -0
  138. package/dist/modules/integrations/setup.js +2 -2
  139. package/dist/modules/integrations/setup.js.map +2 -2
  140. package/dist/modules/integrations/widgets/injection-table.js.map +1 -1
  141. package/dist/modules/integrations/workers/log-pruner.js +18 -0
  142. package/dist/modules/integrations/workers/log-pruner.js.map +7 -0
  143. package/generated/entities/integration_credentials/index.ts +8 -0
  144. package/generated/entities/integration_log/index.ts +12 -0
  145. package/generated/entities/integration_state/index.ts +12 -0
  146. package/generated/entities/sync_cursor/index.ts +8 -0
  147. package/generated/entities/sync_external_id_mapping/index.ts +12 -0
  148. package/generated/entities/sync_mapping/index.ts +8 -0
  149. package/generated/entities/sync_run/index.ts +21 -0
  150. package/generated/entities/sync_schedule/index.ts +16 -0
  151. package/generated/entities.ids.generated.ts +14 -0
  152. package/generated/entity-fields-registry.ts +16 -0
  153. package/package.json +2 -2
  154. package/src/modules/data_sync/AGENTS.md +157 -0
  155. package/src/modules/data_sync/acl.ts +7 -0
  156. package/src/modules/data_sync/api/mappings/[id]/route.ts +158 -0
  157. package/src/modules/data_sync/api/mappings/route.ts +144 -0
  158. package/src/modules/data_sync/api/run.ts +97 -0
  159. package/src/modules/data_sync/api/runs/[id]/cancel.ts +57 -0
  160. package/src/modules/data_sync/api/runs/[id]/retry.ts +108 -0
  161. package/src/modules/data_sync/api/runs/[id]/route.ts +81 -0
  162. package/src/modules/data_sync/api/runs.ts +69 -0
  163. package/src/modules/data_sync/api/validate.ts +73 -0
  164. package/src/modules/data_sync/backend/data-sync/page.meta.ts +21 -0
  165. package/src/modules/data_sync/backend/data-sync/page.tsx +244 -0
  166. package/src/modules/data_sync/backend/data-sync/runs/[id]/page.meta.ts +10 -0
  167. package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +278 -0
  168. package/src/modules/data_sync/data/entities.ts +180 -0
  169. package/src/modules/data_sync/data/validators.ts +35 -0
  170. package/src/modules/data_sync/di.ts +38 -0
  171. package/src/modules/data_sync/events.ts +12 -0
  172. package/src/modules/data_sync/i18n/de.json +48 -0
  173. package/src/modules/data_sync/i18n/en.json +48 -0
  174. package/src/modules/data_sync/i18n/es.json +48 -0
  175. package/src/modules/data_sync/i18n/pl.json +48 -0
  176. package/src/modules/data_sync/index.ts +5 -0
  177. package/src/modules/data_sync/lib/adapter-registry.ts +15 -0
  178. package/src/modules/data_sync/lib/adapter.ts +90 -0
  179. package/src/modules/data_sync/lib/id-mapping.ts +95 -0
  180. package/src/modules/data_sync/lib/queue.ts +19 -0
  181. package/src/modules/data_sync/lib/sync-engine.ts +375 -0
  182. package/src/modules/data_sync/lib/sync-run-service.ts +187 -0
  183. package/src/modules/data_sync/migrations/.snapshot-open-mercato.json +653 -0
  184. package/src/modules/data_sync/migrations/Migration20260304113737.ts +19 -0
  185. package/src/modules/data_sync/setup.ts +11 -0
  186. package/src/modules/data_sync/workers/sync-export.ts +27 -0
  187. package/src/modules/data_sync/workers/sync-import.ts +27 -0
  188. package/src/modules/data_sync/workers/sync-scheduled.ts +84 -0
  189. package/src/modules/entities/lib/encryptionDefaults.ts +4 -0
  190. package/src/modules/integrations/AGENTS.md +160 -0
  191. package/src/modules/integrations/acl.ts +3 -0
  192. package/src/modules/integrations/api/[id]/credentials/route.ts +142 -0
  193. package/src/modules/integrations/api/[id]/health/route.ts +53 -0
  194. package/src/modules/integrations/api/[id]/route.ts +76 -0
  195. package/src/modules/integrations/api/[id]/state/route.ts +121 -0
  196. package/src/modules/integrations/api/[id]/version/route.ts +132 -0
  197. package/src/modules/integrations/api/guards.ts +59 -0
  198. package/src/modules/integrations/api/logs/route.ts +63 -0
  199. package/src/modules/integrations/api/openapi.ts +22 -0
  200. package/src/modules/integrations/api/route.ts +73 -0
  201. package/src/modules/integrations/backend/integrations/[id]/page.meta.ts +11 -0
  202. package/src/modules/integrations/backend/integrations/[id]/page.tsx +424 -0
  203. package/src/modules/integrations/backend/integrations/bundle/[id]/page.meta.ts +11 -0
  204. package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +249 -0
  205. package/src/modules/integrations/backend/integrations/page.meta.ts +18 -0
  206. package/src/modules/integrations/backend/integrations/page.tsx +296 -0
  207. package/src/modules/integrations/data/enrichers.ts +35 -18
  208. package/src/modules/integrations/data/entities.ts +114 -5
  209. package/src/modules/integrations/data/validators.ts +41 -0
  210. package/src/modules/integrations/di.ts +31 -0
  211. package/src/modules/integrations/events.ts +17 -0
  212. package/src/modules/integrations/i18n/de.json +70 -0
  213. package/src/modules/integrations/i18n/en.json +70 -0
  214. package/src/modules/integrations/i18n/es.json +70 -0
  215. package/src/modules/integrations/i18n/pl.json +70 -0
  216. package/src/modules/integrations/lib/credentials-service.ts +204 -0
  217. package/src/modules/integrations/lib/health-service.ts +59 -0
  218. package/src/modules/integrations/lib/log-service.ts +84 -0
  219. package/src/modules/integrations/lib/registry-service.ts +42 -0
  220. package/src/modules/integrations/lib/state-service.ts +64 -0
  221. package/src/modules/integrations/lib/types.ts +4 -0
  222. package/src/modules/integrations/migrations/.snapshot-open-mercato.json +582 -0
  223. package/src/modules/integrations/migrations/Migration20260304113737.ts +21 -0
  224. package/src/modules/integrations/setup.ts +2 -2
  225. package/src/modules/integrations/widgets/injection-table.ts +1 -1
  226. package/src/modules/integrations/workers/log-pruner.ts +30 -0
@@ -0,0 +1,19 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260304113737 extends Migration {
3
+ async up() {
4
+ this.addSql(`create table "integration_credentials" ("id" uuid not null default gen_random_uuid(), "integration_id" text not null, "credentials" jsonb not null, "organization_id" uuid not null, "tenant_id" uuid not null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, constraint "integration_credentials_pkey" primary key ("id"));`);
5
+ this.addSql(`create index "integration_credentials_integration_id_organizatio_291ea_index" on "integration_credentials" ("integration_id", "organization_id", "tenant_id");`);
6
+ this.addSql(`create table "integration_logs" ("id" uuid not null default gen_random_uuid(), "integration_id" text not null, "run_id" uuid null, "scope_entity_type" text null, "scope_entity_id" uuid null, "level" text not null, "message" text not null, "code" text null, "payload" jsonb null, "organization_id" uuid not null, "tenant_id" uuid not null, "created_at" timestamptz not null, constraint "integration_logs_pkey" primary key ("id"));`);
7
+ this.addSql(`create index "integration_logs_level_organization_id_tenant_id_c_107e7_index" on "integration_logs" ("level", "organization_id", "tenant_id", "created_at");`);
8
+ this.addSql(`create index "integration_logs_integration_id_organization_id_te_38189_index" on "integration_logs" ("integration_id", "organization_id", "tenant_id", "created_at");`);
9
+ this.addSql(`create table "integration_states" ("id" uuid not null default gen_random_uuid(), "integration_id" text not null, "is_enabled" boolean not null default true, "api_version" text null, "reauth_required" boolean not null default false, "last_health_status" text null, "last_health_checked_at" timestamptz null, "organization_id" uuid not null, "tenant_id" uuid not null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, constraint "integration_states_pkey" primary key ("id"));`);
10
+ this.addSql(`create index "integration_states_integration_id_organization_id__32acc_index" on "integration_states" ("integration_id", "organization_id", "tenant_id");`);
11
+ this.addSql(`create table "sync_external_id_mappings" ("id" uuid not null default gen_random_uuid(), "integration_id" text not null, "internal_entity_type" text not null, "internal_entity_id" uuid not null, "external_id" text not null, "sync_status" text not null default 'not_synced', "last_synced_at" timestamptz null, "organization_id" uuid not null, "tenant_id" uuid not null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, constraint "sync_external_id_mappings_pkey" primary key ("id"));`);
12
+ this.addSql(`create index "sync_external_id_mappings_integration_id_external__c088c_index" on "sync_external_id_mappings" ("integration_id", "external_id", "organization_id");`);
13
+ this.addSql(`create index "sync_external_id_mappings_internal_entity_type_int_f9194_index" on "sync_external_id_mappings" ("internal_entity_type", "internal_entity_id", "organization_id");`);
14
+ }
15
+ }
16
+ export {
17
+ Migration20260304113737
18
+ };
19
+ //# sourceMappingURL=Migration20260304113737.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/integrations/migrations/Migration20260304113737.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260304113737 extends Migration {\n\n override async up(): Promise<void> {\n this.addSql(`create table \"integration_credentials\" (\"id\" uuid not null default gen_random_uuid(), \"integration_id\" text not null, \"credentials\" jsonb not null, \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"created_at\" timestamptz not null, \"updated_at\" timestamptz not null, \"deleted_at\" timestamptz null, constraint \"integration_credentials_pkey\" primary key (\"id\"));`);\n this.addSql(`create index \"integration_credentials_integration_id_organizatio_291ea_index\" on \"integration_credentials\" (\"integration_id\", \"organization_id\", \"tenant_id\");`);\n\n this.addSql(`create table \"integration_logs\" (\"id\" uuid not null default gen_random_uuid(), \"integration_id\" text not null, \"run_id\" uuid null, \"scope_entity_type\" text null, \"scope_entity_id\" uuid null, \"level\" text not null, \"message\" text not null, \"code\" text null, \"payload\" jsonb null, \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"created_at\" timestamptz not null, constraint \"integration_logs_pkey\" primary key (\"id\"));`);\n this.addSql(`create index \"integration_logs_level_organization_id_tenant_id_c_107e7_index\" on \"integration_logs\" (\"level\", \"organization_id\", \"tenant_id\", \"created_at\");`);\n this.addSql(`create index \"integration_logs_integration_id_organization_id_te_38189_index\" on \"integration_logs\" (\"integration_id\", \"organization_id\", \"tenant_id\", \"created_at\");`);\n\n this.addSql(`create table \"integration_states\" (\"id\" uuid not null default gen_random_uuid(), \"integration_id\" text not null, \"is_enabled\" boolean not null default true, \"api_version\" text null, \"reauth_required\" boolean not null default false, \"last_health_status\" text null, \"last_health_checked_at\" timestamptz null, \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"created_at\" timestamptz not null, \"updated_at\" timestamptz not null, \"deleted_at\" timestamptz null, constraint \"integration_states_pkey\" primary key (\"id\"));`);\n this.addSql(`create index \"integration_states_integration_id_organization_id__32acc_index\" on \"integration_states\" (\"integration_id\", \"organization_id\", \"tenant_id\");`);\n\n this.addSql(`create table \"sync_external_id_mappings\" (\"id\" uuid not null default gen_random_uuid(), \"integration_id\" text not null, \"internal_entity_type\" text not null, \"internal_entity_id\" uuid not null, \"external_id\" text not null, \"sync_status\" text not null default 'not_synced', \"last_synced_at\" timestamptz null, \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"created_at\" timestamptz not null, \"updated_at\" timestamptz not null, \"deleted_at\" timestamptz null, constraint \"sync_external_id_mappings_pkey\" primary key (\"id\"));`);\n this.addSql(`create index \"sync_external_id_mappings_integration_id_external__c088c_index\" on \"sync_external_id_mappings\" (\"integration_id\", \"external_id\", \"organization_id\");`);\n this.addSql(`create index \"sync_external_id_mappings_internal_entity_type_int_f9194_index\" on \"sync_external_id_mappings\" (\"internal_entity_type\", \"internal_entity_id\", \"organization_id\");`);\n }\n\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,gCAAgC,UAAU;AAAA,EAErD,MAAe,KAAoB;AACjC,SAAK,OAAO,qXAAqX;AACjY,SAAK,OAAO,gKAAgK;AAE5K,SAAK,OAAO,+aAA+a;AAC3b,SAAK,OAAO,8JAA8J;AAC1K,SAAK,OAAO,uKAAuK;AAEnL,SAAK,OAAO,+gBAA+gB;AAC3hB,SAAK,OAAO,2JAA2J;AAEvK,SAAK,OAAO,uhBAAuhB;AACniB,SAAK,OAAO,oKAAoK;AAChL,SAAK,OAAO,iLAAiL;AAAA,EAC/L;AAEF;",
6
+ "names": []
7
+ }
@@ -1,7 +1,7 @@
1
1
  const setup = {
2
2
  defaultRoleFeatures: {
3
- superadmin: ["integrations.view", "integrations.manage"],
4
- admin: ["integrations.view", "integrations.manage"],
3
+ superadmin: ["integrations.view", "integrations.manage", "integrations.credentials.manage"],
4
+ admin: ["integrations.view", "integrations.manage", "integrations.credentials.manage"],
5
5
  employee: ["integrations.view"]
6
6
  }
7
7
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/integrations/setup.ts"],
4
- "sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n superadmin: ['integrations.view', 'integrations.manage'],\n admin: ['integrations.view', 'integrations.manage'],\n employee: ['integrations.view'],\n },\n}\n\nexport default setup\n"],
5
- "mappings": "AAEO,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,YAAY,CAAC,qBAAqB,qBAAqB;AAAA,IACvD,OAAO,CAAC,qBAAqB,qBAAqB;AAAA,IAClD,UAAU,CAAC,mBAAmB;AAAA,EAChC;AACF;AAEA,IAAO,gBAAQ;",
4
+ "sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n superadmin: ['integrations.view', 'integrations.manage', 'integrations.credentials.manage'],\n admin: ['integrations.view', 'integrations.manage', 'integrations.credentials.manage'],\n employee: ['integrations.view'],\n },\n}\n\nexport default setup\n"],
5
+ "mappings": "AAEO,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,YAAY,CAAC,qBAAqB,uBAAuB,iCAAiC;AAAA,IAC1F,OAAO,CAAC,qBAAqB,uBAAuB,iCAAiC;AAAA,IACrF,UAAU,CAAC,mBAAmB;AAAA,EAChC;AACF;AAEA,IAAO,gBAAQ;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/integrations/widgets/injection-table.ts"],
4
- "sourcesContent": ["import type { ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'\n\n/**\n * Integrations module injection table.\n *\n * The external-ids widget auto-appears on entity detail pages via wildcard spot matching.\n * Status badge widgets are registered by individual integration modules (e.g. sync_medusa).\n */\nexport const injectionTable: ModuleInjectionTable = {\n // External IDs widget appears in detail page sidebars for all entities\n 'detail:*:sidebar': {\n widgetId: 'integrations.injection.external-ids',\n priority: -10,\n },\n}\n\nexport default injectionTable\n"],
4
+ "sourcesContent": ["import type { ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'\n\n/**\n * Integrations module injection table.\n *\n * The external-ids widget auto-appears on entity detail pages via wildcard spot matching.\n * Status badge widgets are registered by individual integration provider modules.\n */\nexport const injectionTable: ModuleInjectionTable = {\n // External IDs widget appears in detail page sidebars for all entities\n 'detail:*:sidebar': {\n widgetId: 'integrations.injection.external-ids',\n priority: -10,\n },\n}\n\nexport default injectionTable\n"],
5
5
  "mappings": "AAQO,MAAM,iBAAuC;AAAA;AAAA,EAElD,oBAAoB;AAAA,IAClB,UAAU;AAAA,IACV,UAAU;AAAA,EACZ;AACF;AAEA,IAAO,0BAAQ;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,18 @@
1
+ const metadata = {
2
+ queue: "integration-log-pruner",
3
+ id: "integrations:log-pruner",
4
+ concurrency: 1
5
+ };
6
+ async function handle(job, ctx) {
7
+ const logService = ctx.resolve("integrationLogService");
8
+ const pruned = await logService.pruneOlderThan(job.payload.retentionDays, job.payload.scope);
9
+ if (pruned > 0) {
10
+ const logger = logService.scoped("integrations", job.payload.scope);
11
+ await logger.info(`Pruned ${pruned} log entries older than ${job.payload.retentionDays} days`);
12
+ }
13
+ }
14
+ export {
15
+ handle as default,
16
+ metadata
17
+ };
18
+ //# sourceMappingURL=log-pruner.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/integrations/workers/log-pruner.ts"],
4
+ "sourcesContent": ["import type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'\nimport type { IntegrationLogService } from '../lib/log-service'\n\ntype LogPrunerPayload = {\n retentionDays: number\n scope: {\n organizationId: string\n tenantId: string\n }\n}\n\nexport const metadata: WorkerMeta = {\n queue: 'integration-log-pruner',\n id: 'integrations:log-pruner',\n concurrency: 1,\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport default async function handle(job: QueuedJob<LogPrunerPayload>, ctx: HandlerContext): Promise<void> {\n const logService = ctx.resolve<IntegrationLogService>('integrationLogService')\n const pruned = await logService.pruneOlderThan(job.payload.retentionDays, job.payload.scope)\n\n if (pruned > 0) {\n const logger = logService.scoped('integrations', job.payload.scope)\n await logger.info(`Pruned ${pruned} log entries older than ${job.payload.retentionDays} days`)\n }\n}\n"],
5
+ "mappings": "AAWO,MAAM,WAAuB;AAAA,EAClC,OAAO;AAAA,EACP,IAAI;AAAA,EACJ,aAAa;AACf;AAMA,eAAO,OAA8B,KAAkC,KAAoC;AACzG,QAAM,aAAa,IAAI,QAA+B,uBAAuB;AAC7E,QAAM,SAAS,MAAM,WAAW,eAAe,IAAI,QAAQ,eAAe,IAAI,QAAQ,KAAK;AAE3F,MAAI,SAAS,GAAG;AACd,UAAM,SAAS,WAAW,OAAO,gBAAgB,IAAI,QAAQ,KAAK;AAClE,UAAM,OAAO,KAAK,UAAU,MAAM,2BAA2B,IAAI,QAAQ,aAAa,OAAO;AAAA,EAC/F;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,8 @@
1
+ export const id = 'id'
2
+ export const integration_id = 'integration_id'
3
+ export const credentials = 'credentials'
4
+ export const organization_id = 'organization_id'
5
+ export const tenant_id = 'tenant_id'
6
+ export const created_at = 'created_at'
7
+ export const updated_at = 'updated_at'
8
+ export const deleted_at = 'deleted_at'
@@ -0,0 +1,12 @@
1
+ export const id = 'id'
2
+ export const integration_id = 'integration_id'
3
+ export const run_id = 'run_id'
4
+ export const scope_entity_type = 'scope_entity_type'
5
+ export const scope_entity_id = 'scope_entity_id'
6
+ export const level = 'level'
7
+ export const message = 'message'
8
+ export const code = 'code'
9
+ export const payload = 'payload'
10
+ export const organization_id = 'organization_id'
11
+ export const tenant_id = 'tenant_id'
12
+ export const created_at = 'created_at'
@@ -0,0 +1,12 @@
1
+ export const id = 'id'
2
+ export const integration_id = 'integration_id'
3
+ export const is_enabled = 'is_enabled'
4
+ export const api_version = 'api_version'
5
+ export const reauth_required = 'reauth_required'
6
+ export const last_health_status = 'last_health_status'
7
+ export const last_health_checked_at = 'last_health_checked_at'
8
+ export const organization_id = 'organization_id'
9
+ export const tenant_id = 'tenant_id'
10
+ export const created_at = 'created_at'
11
+ export const updated_at = 'updated_at'
12
+ export const deleted_at = 'deleted_at'
@@ -0,0 +1,8 @@
1
+ export const id = 'id'
2
+ export const integration_id = 'integration_id'
3
+ export const entity_type = 'entity_type'
4
+ export const direction = 'direction'
5
+ export const cursor = 'cursor'
6
+ export const organization_id = 'organization_id'
7
+ export const tenant_id = 'tenant_id'
8
+ export const updated_at = 'updated_at'
@@ -0,0 +1,12 @@
1
+ export const id = 'id'
2
+ export const integration_id = 'integration_id'
3
+ export const internal_entity_type = 'internal_entity_type'
4
+ export const internal_entity_id = 'internal_entity_id'
5
+ export const external_id = 'external_id'
6
+ export const sync_status = 'sync_status'
7
+ export const last_synced_at = 'last_synced_at'
8
+ export const organization_id = 'organization_id'
9
+ export const tenant_id = 'tenant_id'
10
+ export const created_at = 'created_at'
11
+ export const updated_at = 'updated_at'
12
+ export const deleted_at = 'deleted_at'
@@ -0,0 +1,8 @@
1
+ export const id = 'id'
2
+ export const integration_id = 'integration_id'
3
+ export const entity_type = 'entity_type'
4
+ export const mapping = 'mapping'
5
+ export const organization_id = 'organization_id'
6
+ export const tenant_id = 'tenant_id'
7
+ export const created_at = 'created_at'
8
+ export const updated_at = 'updated_at'
@@ -0,0 +1,21 @@
1
+ export const id = 'id'
2
+ export const integration_id = 'integration_id'
3
+ export const entity_type = 'entity_type'
4
+ export const direction = 'direction'
5
+ export const status = 'status'
6
+ export const cursor = 'cursor'
7
+ export const initial_cursor = 'initial_cursor'
8
+ export const created_count = 'created_count'
9
+ export const updated_count = 'updated_count'
10
+ export const skipped_count = 'skipped_count'
11
+ export const failed_count = 'failed_count'
12
+ export const batches_completed = 'batches_completed'
13
+ export const last_error = 'last_error'
14
+ export const progress_job_id = 'progress_job_id'
15
+ export const job_id = 'job_id'
16
+ export const triggered_by = 'triggered_by'
17
+ export const organization_id = 'organization_id'
18
+ export const tenant_id = 'tenant_id'
19
+ export const created_at = 'created_at'
20
+ export const updated_at = 'updated_at'
21
+ export const deleted_at = 'deleted_at'
@@ -0,0 +1,16 @@
1
+ export const id = 'id'
2
+ export const integration_id = 'integration_id'
3
+ export const entity_type = 'entity_type'
4
+ export const direction = 'direction'
5
+ export const schedule_type = 'schedule_type'
6
+ export const schedule_value = 'schedule_value'
7
+ export const timezone = 'timezone'
8
+ export const full_sync = 'full_sync'
9
+ export const is_enabled = 'is_enabled'
10
+ export const scheduled_job_id = 'scheduled_job_id'
11
+ export const last_run_at = 'last_run_at'
12
+ export const organization_id = 'organization_id'
13
+ export const tenant_id = 'tenant_id'
14
+ export const created_at = 'created_at'
15
+ export const updated_at = 'updated_at'
16
+ export const deleted_at = 'deleted_at'
@@ -24,6 +24,8 @@ export const M = {
24
24
  "staff": "staff",
25
25
  "notifications": "notifications",
26
26
  "progress": "progress",
27
+ "integrations": "integrations",
28
+ "data_sync": "data_sync",
27
29
  "messages": "messages",
28
30
  "translations": "translations",
29
31
  "inbox_ops": "inbox_ops"
@@ -198,6 +200,18 @@ export const E = {
198
200
  "progress": {
199
201
  "progress_job": "progress:progress_job"
200
202
  },
203
+ "integrations": {
204
+ "sync_external_id_mapping": "integrations:sync_external_id_mapping",
205
+ "integration_credentials": "integrations:integration_credentials",
206
+ "integration_state": "integrations:integration_state",
207
+ "integration_log": "integrations:integration_log"
208
+ },
209
+ "data_sync": {
210
+ "sync_run": "data_sync:sync_run",
211
+ "sync_cursor": "data_sync:sync_cursor",
212
+ "sync_mapping": "data_sync:sync_mapping",
213
+ "sync_schedule": "data_sync:sync_schedule"
214
+ },
201
215
  "messages": {
202
216
  "message": "messages:message",
203
217
  "message_recipient": "messages:message_recipient",
@@ -61,6 +61,9 @@ import * as inbox_proposal_action from './entities/inbox_proposal_action/index'
61
61
  import * as inbox_settings from './entities/inbox_settings/index'
62
62
  import * as indexer_error_log from './entities/indexer_error_log/index'
63
63
  import * as indexer_status_log from './entities/indexer_status_log/index'
64
+ import * as integration_credentials from './entities/integration_credentials/index'
65
+ import * as integration_log from './entities/integration_log/index'
66
+ import * as integration_state from './entities/integration_state/index'
64
67
  import * as message from './entities/message/index'
65
68
  import * as message_access_token from './entities/message_access_token/index'
66
69
  import * as message_confirmation from './entities/message_confirmation/index'
@@ -123,6 +126,11 @@ import * as staff_team_member_comment from './entities/staff_team_member_comment
123
126
  import * as staff_team_member_job_history from './entities/staff_team_member_job_history/index'
124
127
  import * as staff_team_role from './entities/staff_team_role/index'
125
128
  import * as step_instance from './entities/step_instance/index'
129
+ import * as sync_cursor from './entities/sync_cursor/index'
130
+ import * as sync_external_id_mapping from './entities/sync_external_id_mapping/index'
131
+ import * as sync_mapping from './entities/sync_mapping/index'
132
+ import * as sync_run from './entities/sync_run/index'
133
+ import * as sync_schedule from './entities/sync_schedule/index'
126
134
  import * as tenant from './entities/tenant/index'
127
135
  import * as upgrade_action_run from './entities/upgrade_action_run/index'
128
136
  import * as user from './entities/user/index'
@@ -197,6 +205,9 @@ export const entityFieldsRegistry: Record<string, Record<string, string>> = {
197
205
  inbox_settings,
198
206
  indexer_error_log,
199
207
  indexer_status_log,
208
+ integration_credentials,
209
+ integration_log,
210
+ integration_state,
200
211
  message,
201
212
  message_access_token,
202
213
  message_confirmation,
@@ -259,6 +270,11 @@ export const entityFieldsRegistry: Record<string, Record<string, string>> = {
259
270
  staff_team_member_job_history,
260
271
  staff_team_role,
261
272
  step_instance,
273
+ sync_cursor,
274
+ sync_external_id_mapping,
275
+ sync_mapping,
276
+ sync_run,
277
+ sync_schedule,
262
278
  tenant,
263
279
  upgrade_action_run,
264
280
  user,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.4.6-develop-6d72ec5960",
3
+ "version": "0.4.6-develop-cd1e2a9a0e",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -207,7 +207,7 @@
207
207
  }
208
208
  },
209
209
  "dependencies": {
210
- "@open-mercato/shared": "0.4.6-develop-6d72ec5960",
210
+ "@open-mercato/shared": "0.4.6-develop-cd1e2a9a0e",
211
211
  "@types/html-to-text": "^9.0.4",
212
212
  "@types/semver": "^7.5.8",
213
213
  "@xyflow/react": "^12.6.0",
@@ -0,0 +1,157 @@
1
+ # Data Sync Module — Agent Guide
2
+
3
+ The `data_sync` module provides a streaming data synchronization hub for import/export operations with external systems. It uses an adapter pattern where provider modules register `DataSyncAdapter` implementations.
4
+
5
+ **Spec**: `.ai/specs/SPEC-045b-data-sync-hub.md`
6
+
7
+ ---
8
+
9
+ ## Module Structure
10
+
11
+ ```
12
+ packages/core/src/modules/data_sync/
13
+ ├── index.ts # Module metadata
14
+ ├── di.ts # DI registrations
15
+ ├── acl.ts # Features: view, run, configure
16
+ ├── setup.ts # Default role features
17
+ ├── events.ts # 4 run lifecycle events
18
+ ├── data/
19
+ │ ├── entities.ts # SyncRun, SyncCursor, SyncMapping, SyncSchedule
20
+ │ └── validators.ts # Zod schemas
21
+ ├── lib/
22
+ │ ├── adapter.ts # DataSyncAdapter interface + batch types
23
+ │ ├── adapter-registry.ts # Register/get adapters by providerKey
24
+ │ ├── id-mapping.ts # External ID ↔ local ID lookup and storage
25
+ │ ├── queue.ts # Queue helper for enqueuing sync jobs
26
+ │ ├── sync-engine.ts # Orchestrates streaming import/export with progress
27
+ │ └── sync-run-service.ts # CRUD for SyncRun + cursor management
28
+ ├── api/
29
+ │ ├── run.ts # POST /api/data_sync/run — start a sync
30
+ │ ├── runs.ts # GET /api/data_sync/runs — list runs
31
+ │ ├── validate.ts # POST /api/data_sync/validate — validate connection
32
+ │ ├── runs/[id]/
33
+ │ │ ├── route.ts # GET — run detail
34
+ │ │ ├── cancel.ts # POST — cancel running sync
35
+ │ │ └── retry.ts # POST — retry failed sync
36
+ │ └── mappings/
37
+ │ ├── route.ts # GET/POST — list/create field mappings
38
+ │ └── [id]/route.ts # GET/PUT/DELETE — manage individual mapping
39
+ ├── workers/
40
+ │ ├── sync-import.ts # Queue handler for import jobs (concurrency: 5)
41
+ │ ├── sync-export.ts # Queue handler for export jobs (concurrency: 5)
42
+ │ └── sync-scheduled.ts # Handles scheduler dispatch → creates run + enqueues
43
+ ├── backend/
44
+ │ └── data-sync/
45
+ │ ├── page.tsx # Sync runs dashboard (DataTable)
46
+ │ ├── page.meta.ts
47
+ │ └── runs/[id]/
48
+ │ ├── page.tsx # Run detail (progress bar, counters, logs)
49
+ │ └── page.meta.ts
50
+ └── i18n/
51
+ ├── en.json
52
+ └── pl.json
53
+ ```
54
+
55
+ ## Key Services (DI)
56
+
57
+ | Service Name | Purpose |
58
+ |---|---|
59
+ | `dataSyncRunService` | CRUD for SyncRun, cursor management, overlap detection |
60
+ | `dataSyncEngine` | Orchestrates streaming import/export with batch processing, progress, error logging |
61
+ | `externalIdMappingService` | Maps local entity IDs ↔ external system IDs |
62
+
63
+ ## Adapter Contract
64
+
65
+ Provider modules implement `DataSyncAdapter`:
66
+
67
+ ```typescript
68
+ interface DataSyncAdapter {
69
+ providerKey: string
70
+ direction: 'import' | 'export' | 'bidirectional'
71
+ supportedEntities: string[]
72
+ streamImport(entityType: string, cursor: string | null, config: SyncConfig): AsyncIterable<ImportBatch>
73
+ streamExport?(entityType: string, cursor: string | null, config: SyncConfig): AsyncIterable<ExportBatch>
74
+ getInitialCursor?(entityType: string): Promise<string | null>
75
+ getMapping?(entityType: string): Promise<FieldMapping[]>
76
+ validateConnection?(credentials: Record<string, unknown>): Promise<{ valid: boolean; message?: string }>
77
+ }
78
+ ```
79
+
80
+ Register adapters in your provider module's `di.ts`:
81
+ ```typescript
82
+ registerDataSyncAdapter(myAdapter)
83
+ ```
84
+
85
+ ## Run Lifecycle
86
+
87
+ `pending` → `running` → `completed` | `failed` | `cancelled`
88
+
89
+ - **Cursor persistence**: After each batch, cursor is saved to `SyncCursor`
90
+ - **Resume**: Retry reads the last successful cursor, resumes from there
91
+ - **Progress**: Linked to `ProgressJob` via `progressJobId` for `ProgressTopBar` display
92
+ - **Cancellation**: Via `progressService.isCancellationRequested()`
93
+
94
+ ## Queue Names
95
+
96
+ | Queue | Worker | Concurrency |
97
+ |---|---|---|
98
+ | `data-sync-import` | `sync-import.ts` | 5 |
99
+ | `data-sync-export` | `sync-export.ts` | 5 |
100
+ | `data-sync-scheduled` | `sync-scheduled.ts` | 3 |
101
+
102
+ ## Events
103
+
104
+ | Event ID | Emitted When |
105
+ |---|---|
106
+ | `data_sync.run.started` | Sync run begins processing |
107
+ | `data_sync.run.completed` | Sync run finishes successfully |
108
+ | `data_sync.run.failed` | Sync run fails |
109
+ | `data_sync.run.cancelled` | Sync run is cancelled |
110
+
111
+ ## ACL Features
112
+
113
+ - `data_sync.view` — view sync runs and progress
114
+ - `data_sync.run` — trigger, cancel, retry syncs
115
+ - `data_sync.configure` — manage field mappings and schedules
116
+
117
+ ## UMES Extensibility
118
+
119
+ Data sync providers can leverage the **Unified Module Extension System (UMES)** to extend platform UI and behavior.
120
+
121
+ ### Available Extension Points for Sync Providers
122
+
123
+ | Extension Mechanism | Use Case | Files |
124
+ |---|---|---|
125
+ | **Widget Injection** | Inject sync status badges, mapping previews, or progress indicators into entity pages | `widgets/injection/`, `widgets/injection-table.ts` |
126
+ | **Event Subscribers** | React to sync lifecycle events (`data_sync.run.completed`, etc.) for side-effects | `subscribers/*.ts` |
127
+ | **Entity Extensions** | Link sync metadata to core entities | `data/extensions.ts` |
128
+ | **Response Enrichers** | Attach sync status or external ID data to other modules' API responses | `data/enrichers.ts` |
129
+ | **Notifications** | Emit in-app notifications on sync completion/failure | `notifications.ts`, `subscribers/` |
130
+ | **DOM Event Bridge** | Push real-time sync progress to browser (SSE) | Set `clientBroadcast: true` in event definitions |
131
+ | **Menu Injection** | Add sidebar items for provider-specific sync dashboards | via `useInjectedMenuItems` |
132
+
133
+ ### Progress Delivery Contract
134
+
135
+ - `ProgressTopBar` polls `/api/progress/active` every 5s (`useProgressPoll`).
136
+ - Create `ProgressJob` in `run`/`retry` endpoints; start/update/complete/fail in `sync-engine`.
137
+ - Include `progressJob` details in run detail response.
138
+ - SSE DOM bridge forwards only events with `clientBroadcast: true`.
139
+ - `data_sync.run.*` events are not yet marked `clientBroadcast: true` — polling is the active mechanism.
140
+ - Do not claim SSE-driven progress for sync runs until event definitions and client wiring are explicitly enabled.
141
+
142
+ ### Integration Test Expectations
143
+
144
+ - Module-local integration tests go under `__integration__/`
145
+ - Use helpers from `@open-mercato/core/modules/core/__integration__/helpers/*`
146
+ - Tests must create prerequisites via API and clean up in `finally`
147
+ - Avoid hard dependency on late-phase modules; keep tests scoped to implemented contracts
148
+
149
+ ## MUST Rules
150
+
151
+ - **Always scope by organizationId + tenantId** — every entity query
152
+ - **Never import from provider adapter modules** — data_sync is generic
153
+ - **Use the queue system** — never run syncs inline in API handlers
154
+ - **Persist cursor after each batch** — enables resume on failure
155
+ - **Log item-level errors** — don't stop the sync for individual item failures
156
+ - **Check for overlap** before starting a new run (same integration + entityType + direction)
157
+ - **API routes must export `openApi`** for documentation generation
@@ -0,0 +1,7 @@
1
+ export const features = [
2
+ { id: 'data_sync.view', title: 'View sync runs', module: 'data_sync' },
3
+ { id: 'data_sync.run', title: 'Run sync operations', module: 'data_sync' },
4
+ { id: 'data_sync.configure', title: 'Configure sync mappings and schedules', module: 'data_sync' },
5
+ ]
6
+
7
+ export default features
@@ -0,0 +1,158 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { z } from 'zod'
3
+ import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
4
+ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
5
+ import type { EntityManager } from '@mikro-orm/postgresql'
6
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
7
+ import { SyncMapping } from '../../../data/entities'
8
+
9
+ const idParamsSchema = z.object({ id: z.string().uuid() })
10
+
11
+ const updateMappingSchema = z.object({
12
+ mapping: z.record(z.string(), z.unknown()),
13
+ })
14
+
15
+ export const metadata = {
16
+ GET: { requireAuth: true, requireFeatures: ['data_sync.view'] },
17
+ PUT: { requireAuth: true, requireFeatures: ['data_sync.configure'] },
18
+ DELETE: { requireAuth: true, requireFeatures: ['data_sync.configure'] },
19
+ }
20
+
21
+ export const openApi = {
22
+ tags: ['DataSync'],
23
+ summary: 'Get, update, or delete a field mapping',
24
+ }
25
+
26
+ async function resolveParams(ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {
27
+ const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')
28
+ ? await (ctx.params as Promise<{ id?: string }>)
29
+ : (ctx.params as { id?: string } | undefined)
30
+ return idParamsSchema.safeParse(rawParams)
31
+ }
32
+
33
+ export async function GET(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {
34
+ const auth = await getAuthFromRequest(req)
35
+ if (!auth?.tenantId || !auth.orgId) {
36
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
37
+ }
38
+
39
+ const parsedParams = await resolveParams(ctx)
40
+ if (!parsedParams.success) {
41
+ return NextResponse.json({ error: 'Invalid mapping id' }, { status: 400 })
42
+ }
43
+
44
+ const container = await createRequestContainer()
45
+ const em = container.resolve('em') as EntityManager
46
+ const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }
47
+
48
+ const mapping = await findOneWithDecryption(
49
+ em,
50
+ SyncMapping,
51
+ {
52
+ id: parsedParams.data.id,
53
+ organizationId: auth.orgId as string,
54
+ tenantId: auth.tenantId,
55
+ },
56
+ undefined,
57
+ scope,
58
+ )
59
+
60
+ if (!mapping) {
61
+ return NextResponse.json({ error: 'Mapping not found' }, { status: 404 })
62
+ }
63
+
64
+ return NextResponse.json({
65
+ id: mapping.id,
66
+ integrationId: mapping.integrationId,
67
+ entityType: mapping.entityType,
68
+ mapping: mapping.mapping,
69
+ createdAt: mapping.createdAt.toISOString(),
70
+ updatedAt: mapping.updatedAt.toISOString(),
71
+ })
72
+ }
73
+
74
+ export async function PUT(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {
75
+ const auth = await getAuthFromRequest(req)
76
+ if (!auth?.tenantId || !auth.orgId) {
77
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
78
+ }
79
+
80
+ const parsedParams = await resolveParams(ctx)
81
+ if (!parsedParams.success) {
82
+ return NextResponse.json({ error: 'Invalid mapping id' }, { status: 400 })
83
+ }
84
+
85
+ const payload = await req.json().catch(() => null)
86
+ const parsedBody = updateMappingSchema.safeParse(payload)
87
+ if (!parsedBody.success) {
88
+ return NextResponse.json({ error: 'Invalid payload', details: parsedBody.error.flatten() }, { status: 422 })
89
+ }
90
+
91
+ const container = await createRequestContainer()
92
+ const em = container.resolve('em') as EntityManager
93
+ const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }
94
+
95
+ const mapping = await findOneWithDecryption(
96
+ em,
97
+ SyncMapping,
98
+ {
99
+ id: parsedParams.data.id,
100
+ organizationId: auth.orgId as string,
101
+ tenantId: auth.tenantId,
102
+ },
103
+ undefined,
104
+ scope,
105
+ )
106
+
107
+ if (!mapping) {
108
+ return NextResponse.json({ error: 'Mapping not found' }, { status: 404 })
109
+ }
110
+
111
+ mapping.mapping = parsedBody.data.mapping
112
+ await em.flush()
113
+
114
+ return NextResponse.json({
115
+ id: mapping.id,
116
+ integrationId: mapping.integrationId,
117
+ entityType: mapping.entityType,
118
+ mapping: mapping.mapping,
119
+ updatedAt: mapping.updatedAt.toISOString(),
120
+ })
121
+ }
122
+
123
+ export async function DELETE(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {
124
+ const auth = await getAuthFromRequest(req)
125
+ if (!auth?.tenantId || !auth.orgId) {
126
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
127
+ }
128
+
129
+ const parsedParams = await resolveParams(ctx)
130
+ if (!parsedParams.success) {
131
+ return NextResponse.json({ error: 'Invalid mapping id' }, { status: 400 })
132
+ }
133
+
134
+ const container = await createRequestContainer()
135
+ const em = container.resolve('em') as EntityManager
136
+ const scope = { organizationId: auth.orgId as string, tenantId: auth.tenantId }
137
+
138
+ const mapping = await findOneWithDecryption(
139
+ em,
140
+ SyncMapping,
141
+ {
142
+ id: parsedParams.data.id,
143
+ organizationId: auth.orgId as string,
144
+ tenantId: auth.tenantId,
145
+ },
146
+ undefined,
147
+ scope,
148
+ )
149
+
150
+ if (!mapping) {
151
+ return NextResponse.json({ error: 'Mapping not found' }, { status: 404 })
152
+ }
153
+
154
+ em.remove(mapping)
155
+ await em.flush()
156
+
157
+ return NextResponse.json({ deleted: true })
158
+ }