@open-mercato/core 0.6.2-develop.3330.1.8ad2b3b84c → 0.6.2-develop.3366.1.4692954e44

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 (102) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/sync_excel_upload/index.js +37 -0
  3. package/dist/generated/entities/sync_excel_upload/index.js.map +7 -0
  4. package/dist/generated/entities.ids.generated.js +4 -0
  5. package/dist/generated/entities.ids.generated.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +19 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/modules/customers/search.js +3 -2
  9. package/dist/modules/customers/search.js.map +2 -2
  10. package/dist/modules/data_sync/api/runs/[id]/cancel.js +16 -0
  11. package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
  12. package/dist/modules/data_sync/di.js +2 -1
  13. package/dist/modules/data_sync/di.js.map +2 -2
  14. package/dist/modules/data_sync/lib/sync-engine.js +178 -54
  15. package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
  16. package/dist/modules/data_sync/lib/sync-run-service.js +23 -0
  17. package/dist/modules/data_sync/lib/sync-run-service.js.map +3 -3
  18. package/dist/modules/integrations/backend/integrations/[id]/page.js +241 -55
  19. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +3 -3
  20. package/dist/modules/sync_excel/acl.js +10 -0
  21. package/dist/modules/sync_excel/acl.js.map +7 -0
  22. package/dist/modules/sync_excel/api/import/route.js +164 -0
  23. package/dist/modules/sync_excel/api/import/route.js.map +7 -0
  24. package/dist/modules/sync_excel/api/preview/route.js +84 -0
  25. package/dist/modules/sync_excel/api/preview/route.js.map +7 -0
  26. package/dist/modules/sync_excel/api/upload/route.js +131 -0
  27. package/dist/modules/sync_excel/api/upload/route.js.map +7 -0
  28. package/dist/modules/sync_excel/data/entities.js +79 -0
  29. package/dist/modules/sync_excel/data/entities.js.map +7 -0
  30. package/dist/modules/sync_excel/data/validators.js +58 -0
  31. package/dist/modules/sync_excel/data/validators.js.map +7 -0
  32. package/dist/modules/sync_excel/di.js +11 -0
  33. package/dist/modules/sync_excel/di.js.map +7 -0
  34. package/dist/modules/sync_excel/index.js +11 -0
  35. package/dist/modules/sync_excel/index.js.map +7 -0
  36. package/dist/modules/sync_excel/integration.js +36 -0
  37. package/dist/modules/sync_excel/integration.js.map +7 -0
  38. package/dist/modules/sync_excel/lib/adapters/customers.js +852 -0
  39. package/dist/modules/sync_excel/lib/adapters/customers.js.map +7 -0
  40. package/dist/modules/sync_excel/lib/column-detector.js +77 -0
  41. package/dist/modules/sync_excel/lib/column-detector.js.map +7 -0
  42. package/dist/modules/sync_excel/lib/mapping.js +16 -0
  43. package/dist/modules/sync_excel/lib/mapping.js.map +7 -0
  44. package/dist/modules/sync_excel/lib/parser.js +125 -0
  45. package/dist/modules/sync_excel/lib/parser.js.map +7 -0
  46. package/dist/modules/sync_excel/lib/scope.js +44 -0
  47. package/dist/modules/sync_excel/lib/scope.js.map +7 -0
  48. package/dist/modules/sync_excel/lib/upload-storage.js +63 -0
  49. package/dist/modules/sync_excel/lib/upload-storage.js.map +7 -0
  50. package/dist/modules/sync_excel/migrations/Migration20260329181120_sync_excel.js +11 -0
  51. package/dist/modules/sync_excel/migrations/Migration20260329181120_sync_excel.js.map +7 -0
  52. package/dist/modules/sync_excel/setup.js +19 -0
  53. package/dist/modules/sync_excel/setup.js.map +7 -0
  54. package/dist/modules/sync_excel/widgets/injection/upload-config/target-options.js +293 -0
  55. package/dist/modules/sync_excel/widgets/injection/upload-config/target-options.js.map +7 -0
  56. package/dist/modules/sync_excel/widgets/injection/upload-config/widget.client.js +934 -0
  57. package/dist/modules/sync_excel/widgets/injection/upload-config/widget.client.js.map +7 -0
  58. package/dist/modules/sync_excel/widgets/injection/upload-config/widget.js +15 -0
  59. package/dist/modules/sync_excel/widgets/injection/upload-config/widget.js.map +7 -0
  60. package/dist/modules/sync_excel/widgets/injection-table.js +17 -0
  61. package/dist/modules/sync_excel/widgets/injection-table.js.map +7 -0
  62. package/generated/entities/sync_excel_upload/index.ts +17 -0
  63. package/generated/entities.ids.generated.ts +4 -0
  64. package/generated/entity-fields-registry.ts +19 -0
  65. package/package.json +7 -7
  66. package/src/modules/customers/search.ts +3 -2
  67. package/src/modules/data_sync/api/runs/[id]/cancel.ts +18 -0
  68. package/src/modules/data_sync/di.ts +4 -1
  69. package/src/modules/data_sync/lib/adapter.ts +15 -0
  70. package/src/modules/data_sync/lib/sync-engine.ts +205 -52
  71. package/src/modules/data_sync/lib/sync-run-service.ts +24 -0
  72. package/src/modules/integrations/backend/integrations/[id]/page.tsx +220 -15
  73. package/src/modules/integrations/i18n/de.json +9 -0
  74. package/src/modules/integrations/i18n/en.json +9 -0
  75. package/src/modules/integrations/i18n/es.json +9 -0
  76. package/src/modules/integrations/i18n/pl.json +9 -0
  77. package/src/modules/sync_excel/acl.ts +6 -0
  78. package/src/modules/sync_excel/api/import/route.ts +182 -0
  79. package/src/modules/sync_excel/api/preview/route.ts +89 -0
  80. package/src/modules/sync_excel/api/upload/route.ts +144 -0
  81. package/src/modules/sync_excel/data/entities.ts +61 -0
  82. package/src/modules/sync_excel/data/validators.ts +58 -0
  83. package/src/modules/sync_excel/di.ts +9 -0
  84. package/src/modules/sync_excel/i18n/de.json +107 -0
  85. package/src/modules/sync_excel/i18n/en.json +107 -0
  86. package/src/modules/sync_excel/i18n/es.json +107 -0
  87. package/src/modules/sync_excel/i18n/pl.json +107 -0
  88. package/src/modules/sync_excel/index.ts +7 -0
  89. package/src/modules/sync_excel/integration.ts +31 -0
  90. package/src/modules/sync_excel/lib/adapters/customers.ts +1097 -0
  91. package/src/modules/sync_excel/lib/column-detector.ts +91 -0
  92. package/src/modules/sync_excel/lib/mapping.ts +19 -0
  93. package/src/modules/sync_excel/lib/parser.ts +165 -0
  94. package/src/modules/sync_excel/lib/scope.ts +63 -0
  95. package/src/modules/sync_excel/lib/upload-storage.ts +79 -0
  96. package/src/modules/sync_excel/migrations/.snapshot-open-mercato.json +199 -0
  97. package/src/modules/sync_excel/migrations/Migration20260329181120_sync_excel.ts +10 -0
  98. package/src/modules/sync_excel/setup.ts +20 -0
  99. package/src/modules/sync_excel/widgets/injection/upload-config/target-options.ts +351 -0
  100. package/src/modules/sync_excel/widgets/injection/upload-config/widget.client.tsx +1224 -0
  101. package/src/modules/sync_excel/widgets/injection/upload-config/widget.ts +30 -0
  102. package/src/modules/sync_excel/widgets/injection-table.ts +15 -0
@@ -0,0 +1,164 @@
1
+ import { NextResponse } from "next/server";
2
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
3
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
+ import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
5
+ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
6
+ import { z } from "zod";
7
+ import { startDataSyncRun } from "@open-mercato/core/modules/data_sync/lib/start-run";
8
+ import { SyncMapping } from "@open-mercato/core/modules/data_sync/data/entities";
9
+ import { SyncExcelUpload } from "../../data/entities.js";
10
+ import { Attachment } from "../../../attachments/data/entities.js";
11
+ import { createCursor } from "../../lib/adapters/customers.js";
12
+ import {
13
+ syncExcelImportRequestSchema,
14
+ syncExcelImportResponseSchema
15
+ } from "../../data/validators.js";
16
+ import { resolveSyncExcelConcreteScope } from "../../lib/scope.js";
17
+ const metadata = {
18
+ POST: { requireAuth: true, requireFeatures: ["sync_excel.run"] }
19
+ };
20
+ const errorSchema = z.object({
21
+ error: z.string()
22
+ });
23
+ const openApi = {
24
+ tags: ["SyncExcel"],
25
+ summary: "Start a CSV import run for a stored sync_excel upload",
26
+ methods: {
27
+ POST: {
28
+ summary: "Start CSV import",
29
+ responses: [
30
+ { status: 201, description: "Import run started", schema: syncExcelImportResponseSchema },
31
+ { status: 401, description: "Unauthorized", schema: errorSchema },
32
+ { status: 404, description: "Upload not found", schema: errorSchema },
33
+ { status: 409, description: "Import overlap detected", schema: errorSchema },
34
+ { status: 422, description: "Invalid import payload", schema: errorSchema }
35
+ ]
36
+ }
37
+ }
38
+ };
39
+ async function POST(request) {
40
+ const auth = await getAuthFromRequest(request);
41
+ if (!auth?.tenantId) {
42
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
43
+ }
44
+ const payload = await readJsonSafe(request);
45
+ const parsedPayload = syncExcelImportRequestSchema.safeParse(payload);
46
+ if (!parsedPayload.success) {
47
+ return NextResponse.json({ error: "Invalid import payload." }, { status: 422 });
48
+ }
49
+ const container = await createRequestContainer();
50
+ const em = container.resolve("em");
51
+ const syncRunService = container.resolve("dataSyncRunService");
52
+ const progressService = container.resolve("progressService");
53
+ const credentialsService = container.resolve("integrationCredentialsService");
54
+ const integrationStateService = container.resolve("integrationStateService");
55
+ const scopeResult = await resolveSyncExcelConcreteScope({ auth, container, request });
56
+ if (!scopeResult.ok) {
57
+ return NextResponse.json({ error: scopeResult.error }, { status: scopeResult.status });
58
+ }
59
+ const { scope } = scopeResult;
60
+ const upload = await findOneWithDecryption(
61
+ em,
62
+ SyncExcelUpload,
63
+ {
64
+ id: parsedPayload.data.uploadId,
65
+ organizationId: scope.organizationId,
66
+ tenantId: scope.tenantId
67
+ },
68
+ void 0,
69
+ scope
70
+ );
71
+ if (!upload) {
72
+ return NextResponse.json({ error: "Upload preview not found." }, { status: 404 });
73
+ }
74
+ const attachment = await findOneWithDecryption(
75
+ em,
76
+ Attachment,
77
+ {
78
+ id: upload.attachmentId,
79
+ organizationId: scope.organizationId,
80
+ tenantId: scope.tenantId
81
+ },
82
+ void 0,
83
+ scope
84
+ );
85
+ if (!attachment) {
86
+ return NextResponse.json({ error: "Upload attachment not found." }, { status: 404 });
87
+ }
88
+ if (upload.entityType !== parsedPayload.data.entityType) {
89
+ return NextResponse.json({ error: "Upload entity type does not match requested import target." }, { status: 422 });
90
+ }
91
+ if (parsedPayload.data.mapping.entityType !== parsedPayload.data.entityType) {
92
+ return NextResponse.json({ error: "Mapping entity type does not match requested import target." }, { status: 422 });
93
+ }
94
+ const overlap = await syncRunService.findRunningOverlap("sync_excel", parsedPayload.data.entityType, "import", scope);
95
+ if (overlap) {
96
+ return NextResponse.json({ error: "A sync_excel import is already in progress for this entity type." }, { status: 409 });
97
+ }
98
+ const existingMapping = await findOneWithDecryption(
99
+ em,
100
+ SyncMapping,
101
+ {
102
+ integrationId: "sync_excel",
103
+ entityType: parsedPayload.data.entityType,
104
+ organizationId: scope.organizationId,
105
+ tenantId: scope.tenantId
106
+ },
107
+ void 0,
108
+ scope
109
+ );
110
+ if (existingMapping) {
111
+ existingMapping.mapping = parsedPayload.data.mapping;
112
+ } else {
113
+ em.persist(em.create(SyncMapping, {
114
+ integrationId: "sync_excel",
115
+ entityType: parsedPayload.data.entityType,
116
+ mapping: parsedPayload.data.mapping,
117
+ organizationId: scope.organizationId,
118
+ tenantId: scope.tenantId
119
+ }));
120
+ }
121
+ await credentialsService.save("sync_excel", {}, scope);
122
+ await integrationStateService.upsert("sync_excel", { isEnabled: true }, scope);
123
+ await em.flush();
124
+ const { run, progressJob } = await startDataSyncRun({
125
+ syncRunService,
126
+ progressService,
127
+ scope: {
128
+ ...scope,
129
+ userId: auth.sub
130
+ },
131
+ input: {
132
+ integrationId: "sync_excel",
133
+ entityType: parsedPayload.data.entityType,
134
+ direction: "import",
135
+ cursor: createCursor(upload.id, 0),
136
+ triggeredBy: auth.sub,
137
+ batchSize: parsedPayload.data.batchSize ?? 100,
138
+ progressJob: {
139
+ jobType: "sync_excel:import",
140
+ name: `CSV import \u2014 ${parsedPayload.data.entityType}`,
141
+ description: upload.filename,
142
+ meta: {
143
+ integrationId: "sync_excel",
144
+ uploadId: upload.id,
145
+ hiddenFromTopBar: false
146
+ }
147
+ }
148
+ }
149
+ });
150
+ upload.syncRunId = run.id;
151
+ upload.status = "importing";
152
+ await em.flush();
153
+ return NextResponse.json(syncExcelImportResponseSchema.parse({
154
+ runId: run.id,
155
+ progressJobId: progressJob?.id ?? null,
156
+ status: run.status
157
+ }), { status: 201 });
158
+ }
159
+ export {
160
+ POST,
161
+ metadata,
162
+ openApi
163
+ };
164
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/sync_excel/api/import/route.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { z } from 'zod'\nimport type { ProgressService } from '@open-mercato/core/modules/progress/lib/progressService'\nimport type { SyncRunService } from '@open-mercato/core/modules/data_sync/lib/sync-run-service'\nimport { startDataSyncRun } from '@open-mercato/core/modules/data_sync/lib/start-run'\nimport { SyncMapping } from '@open-mercato/core/modules/data_sync/data/entities'\nimport type { CredentialsService } from '@open-mercato/core/modules/integrations/lib/credentials-service'\nimport type { IntegrationStateService } from '@open-mercato/core/modules/integrations/lib/state-service'\nimport { SyncExcelUpload } from '../../data/entities'\nimport { Attachment } from '../../../attachments/data/entities'\nimport { createCursor } from '../../lib/adapters/customers'\nimport {\n syncExcelImportRequestSchema,\n syncExcelImportResponseSchema,\n} from '../../data/validators'\nimport { resolveSyncExcelConcreteScope } from '../../lib/scope'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['sync_excel.run'] },\n}\n\nconst errorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi = {\n tags: ['SyncExcel'],\n summary: 'Start a CSV import run for a stored sync_excel upload',\n methods: {\n POST: {\n summary: 'Start CSV import',\n responses: [\n { status: 201, description: 'Import run started', schema: syncExcelImportResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 404, description: 'Upload not found', schema: errorSchema },\n { status: 409, description: 'Import overlap detected', schema: errorSchema },\n { status: 422, description: 'Invalid import payload', schema: errorSchema },\n ],\n },\n },\n}\n\nexport async function POST(request: Request) {\n const auth = await getAuthFromRequest(request)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const payload = await readJsonSafe(request)\n const parsedPayload = syncExcelImportRequestSchema.safeParse(payload)\n if (!parsedPayload.success) {\n return NextResponse.json({ error: 'Invalid import payload.' }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const credentialsService = container.resolve('integrationCredentialsService') as CredentialsService\n const integrationStateService = container.resolve('integrationStateService') as IntegrationStateService\n const scopeResult = await resolveSyncExcelConcreteScope({ auth, container, request })\n if (!scopeResult.ok) {\n return NextResponse.json({ error: scopeResult.error }, { status: scopeResult.status })\n }\n const { scope } = scopeResult\n\n const upload = await findOneWithDecryption(\n em,\n SyncExcelUpload,\n {\n id: parsedPayload.data.uploadId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (!upload) {\n return NextResponse.json({ error: 'Upload preview not found.' }, { status: 404 })\n }\n\n const attachment = await findOneWithDecryption(\n em,\n Attachment,\n {\n id: upload.attachmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (!attachment) {\n return NextResponse.json({ error: 'Upload attachment not found.' }, { status: 404 })\n }\n\n if (upload.entityType !== parsedPayload.data.entityType) {\n return NextResponse.json({ error: 'Upload entity type does not match requested import target.' }, { status: 422 })\n }\n\n if (parsedPayload.data.mapping.entityType !== parsedPayload.data.entityType) {\n return NextResponse.json({ error: 'Mapping entity type does not match requested import target.' }, { status: 422 })\n }\n\n const overlap = await syncRunService.findRunningOverlap('sync_excel', parsedPayload.data.entityType, 'import', scope)\n if (overlap) {\n return NextResponse.json({ error: 'A sync_excel import is already in progress for this entity type.' }, { status: 409 })\n }\n\n const existingMapping = await findOneWithDecryption(\n em,\n SyncMapping,\n {\n integrationId: 'sync_excel',\n entityType: parsedPayload.data.entityType,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (existingMapping) {\n existingMapping.mapping = parsedPayload.data.mapping\n } else {\n em.persist(em.create(SyncMapping, {\n integrationId: 'sync_excel',\n entityType: parsedPayload.data.entityType,\n mapping: parsedPayload.data.mapping,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n }))\n }\n\n await credentialsService.save('sync_excel', {}, scope)\n await integrationStateService.upsert('sync_excel', { isEnabled: true }, scope)\n await em.flush()\n\n const { run, progressJob } = await startDataSyncRun({\n syncRunService,\n progressService,\n scope: {\n ...scope,\n userId: auth.sub,\n },\n input: {\n integrationId: 'sync_excel',\n entityType: parsedPayload.data.entityType,\n direction: 'import',\n cursor: createCursor(upload.id, 0),\n triggeredBy: auth.sub,\n batchSize: parsedPayload.data.batchSize ?? 100,\n progressJob: {\n jobType: 'sync_excel:import',\n name: `CSV import \u2014 ${parsedPayload.data.entityType}`,\n description: upload.filename,\n meta: {\n integrationId: 'sync_excel',\n uploadId: upload.id,\n hiddenFromTopBar: false,\n },\n },\n },\n })\n\n upload.syncRunId = run.id\n upload.status = 'importing'\n await em.flush()\n\n return NextResponse.json(syncExcelImportResponseSchema.parse({\n runId: run.id,\n progressJobId: progressJob?.id ?? null,\n status: run.status,\n }), { status: 201 })\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,84 @@
1
+ import { NextResponse } from "next/server";
2
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
3
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
+ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
5
+ import { z } from "zod";
6
+ import { syncExcelPreviewQuerySchema, syncExcelUploadResponseSchema } from "../../data/validators.js";
7
+ import { SyncExcelUpload } from "../../data/entities.js";
8
+ import { buildSuggestedMapping } from "../../lib/mapping.js";
9
+ import { resolveSyncExcelConcreteScope } from "../../lib/scope.js";
10
+ const metadata = {
11
+ GET: { requireAuth: true, requireFeatures: ["sync_excel.view"] }
12
+ };
13
+ const errorSchema = z.object({
14
+ error: z.string()
15
+ });
16
+ const openApi = {
17
+ tags: ["SyncExcel"],
18
+ summary: "Fetch stored preview for a sync_excel upload",
19
+ methods: {
20
+ GET: {
21
+ summary: "Fetch upload preview",
22
+ responses: [
23
+ { status: 200, description: "Stored upload preview", schema: syncExcelUploadResponseSchema },
24
+ { status: 401, description: "Unauthorized", schema: errorSchema },
25
+ { status: 422, description: "Invalid query", schema: errorSchema },
26
+ { status: 404, description: "Upload preview not found", schema: errorSchema }
27
+ ]
28
+ }
29
+ }
30
+ };
31
+ async function GET(request) {
32
+ const auth = await getAuthFromRequest(request);
33
+ if (!auth?.tenantId) {
34
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
35
+ }
36
+ const url = new URL(request.url);
37
+ const parsedQuery = syncExcelPreviewQuerySchema.safeParse({
38
+ uploadId: url.searchParams.get("uploadId"),
39
+ entityType: url.searchParams.get("entityType") ?? void 0
40
+ });
41
+ if (!parsedQuery.success) {
42
+ return NextResponse.json({ error: "Invalid query" }, { status: 422 });
43
+ }
44
+ const container = await createRequestContainer();
45
+ const scopeResult = await resolveSyncExcelConcreteScope({ auth, container, request });
46
+ if (!scopeResult.ok) {
47
+ return NextResponse.json({ error: scopeResult.error }, { status: scopeResult.status });
48
+ }
49
+ const { scope } = scopeResult;
50
+ const em = container.resolve("em");
51
+ const upload = await findOneWithDecryption(
52
+ em,
53
+ SyncExcelUpload,
54
+ {
55
+ id: parsedQuery.data.uploadId,
56
+ organizationId: scope.organizationId,
57
+ tenantId: scope.tenantId
58
+ },
59
+ void 0,
60
+ scope
61
+ );
62
+ if (!upload) {
63
+ return NextResponse.json({ error: "Upload preview not found." }, { status: 404 });
64
+ }
65
+ const entityType = parsedQuery.data.entityType ?? upload.entityType;
66
+ const suggestedMapping = buildSuggestedMapping(entityType, upload.headers);
67
+ return NextResponse.json(syncExcelUploadResponseSchema.parse({
68
+ uploadId: upload.id,
69
+ filename: upload.filename,
70
+ mimeType: upload.mimeType,
71
+ fileSize: upload.fileSize,
72
+ entityType,
73
+ headers: upload.headers,
74
+ sampleRows: upload.sampleRows,
75
+ totalRows: upload.totalRows,
76
+ suggestedMapping
77
+ }));
78
+ }
79
+ export {
80
+ GET,
81
+ metadata,
82
+ openApi
83
+ };
84
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/sync_excel/api/preview/route.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { z } from 'zod'\nimport { syncExcelPreviewQuerySchema, syncExcelUploadResponseSchema } from '../../data/validators'\nimport { SyncExcelUpload } from '../../data/entities'\nimport { buildSuggestedMapping } from '../../lib/mapping'\nimport { resolveSyncExcelConcreteScope } from '../../lib/scope'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['sync_excel.view'] },\n}\n\nconst errorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi = {\n tags: ['SyncExcel'],\n summary: 'Fetch stored preview for a sync_excel upload',\n methods: {\n GET: {\n summary: 'Fetch upload preview',\n responses: [\n { status: 200, description: 'Stored upload preview', schema: syncExcelUploadResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 422, description: 'Invalid query', schema: errorSchema },\n { status: 404, description: 'Upload preview not found', schema: errorSchema },\n ],\n },\n },\n}\n\nexport async function GET(request: Request) {\n const auth = await getAuthFromRequest(request)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(request.url)\n const parsedQuery = syncExcelPreviewQuerySchema.safeParse({\n uploadId: url.searchParams.get('uploadId'),\n entityType: url.searchParams.get('entityType') ?? undefined,\n })\n\n if (!parsedQuery.success) {\n return NextResponse.json({ error: 'Invalid query' }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const scopeResult = await resolveSyncExcelConcreteScope({ auth, container, request })\n if (!scopeResult.ok) {\n return NextResponse.json({ error: scopeResult.error }, { status: scopeResult.status })\n }\n const { scope } = scopeResult\n const em = container.resolve('em') as EntityManager\n const upload = await findOneWithDecryption(\n em,\n SyncExcelUpload,\n {\n id: parsedQuery.data.uploadId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (!upload) {\n return NextResponse.json({ error: 'Upload preview not found.' }, { status: 404 })\n }\n\n const entityType = parsedQuery.data.entityType ?? upload.entityType\n const suggestedMapping = buildSuggestedMapping(entityType, upload.headers)\n\n return NextResponse.json(syncExcelUploadResponseSchema.parse({\n uploadId: upload.id,\n filename: upload.filename,\n mimeType: upload.mimeType,\n fileSize: upload.fileSize,\n entityType,\n headers: upload.headers,\n sampleRows: upload.sampleRows,\n totalRows: upload.totalRows,\n suggestedMapping,\n }))\n}\n"],
5
+ "mappings": "AACA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,6BAA6B;AACtC,SAAS,SAAS;AAClB,SAAS,6BAA6B,qCAAqC;AAC3E,SAAS,uBAAuB;AAChC,SAAS,6BAA6B;AACtC,SAAS,qCAAqC;AAEvC,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,iBAAiB,EAAE;AACjE;AAEA,MAAM,cAAc,EAAE,OAAO;AAAA,EAC3B,OAAO,EAAE,OAAO;AAClB,CAAC;AAEM,MAAM,UAAU;AAAA,EACrB,MAAM,CAAC,WAAW;AAAA,EAClB,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,MACH,SAAS;AAAA,MACT,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,yBAAyB,QAAQ,8BAA8B;AAAA,QAC3F,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,YAAY;AAAA,QAChE,EAAE,QAAQ,KAAK,aAAa,iBAAiB,QAAQ,YAAY;AAAA,QACjE,EAAE,QAAQ,KAAK,aAAa,4BAA4B,QAAQ,YAAY;AAAA,MAC9E;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAsB,IAAI,SAAkB;AAC1C,QAAM,OAAO,MAAM,mBAAmB,OAAO;AAC7C,MAAI,CAAC,MAAM,UAAU;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,QAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,QAAM,cAAc,4BAA4B,UAAU;AAAA,IACxD,UAAU,IAAI,aAAa,IAAI,UAAU;AAAA,IACzC,YAAY,IAAI,aAAa,IAAI,YAAY,KAAK;AAAA,EACpD,CAAC;AAED,MAAI,CAAC,YAAY,SAAS;AACxB,WAAO,aAAa,KAAK,EAAE,OAAO,gBAAgB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACtE;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,cAAc,MAAM,8BAA8B,EAAE,MAAM,WAAW,QAAQ,CAAC;AACpF,MAAI,CAAC,YAAY,IAAI;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,YAAY,MAAM,GAAG,EAAE,QAAQ,YAAY,OAAO,CAAC;AAAA,EACvF;AACA,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,KAAK,UAAU,QAAQ,IAAI;AACjC,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,MACE,IAAI,YAAY,KAAK;AAAA,MACrB,gBAAgB,MAAM;AAAA,MACtB,UAAU,MAAM;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,MAAI,CAAC,QAAQ;AACX,WAAO,aAAa,KAAK,EAAE,OAAO,4BAA4B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClF;AAEA,QAAM,aAAa,YAAY,KAAK,cAAc,OAAO;AACzD,QAAM,mBAAmB,sBAAsB,YAAY,OAAO,OAAO;AAEzE,SAAO,aAAa,KAAK,8BAA8B,MAAM;AAAA,IAC3D,UAAU,OAAO;AAAA,IACjB,UAAU,OAAO;AAAA,IACjB,UAAU,OAAO;AAAA,IACjB,UAAU,OAAO;AAAA,IACjB;AAAA,IACA,SAAS,OAAO;AAAA,IAChB,YAAY,OAAO;AAAA,IACnB,WAAW,OAAO;AAAA,IAClB;AAAA,EACF,CAAC,CAAC;AACJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,131 @@
1
+ import { randomUUID } from "crypto";
2
+ import { NextResponse } from "next/server";
3
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
+ import { z } from "zod";
6
+ import { syncExcelUploadResponseSchema, syncExcelEntityTypeSchema } from "../../data/validators.js";
7
+ import { SyncExcelUpload } from "../../data/entities.js";
8
+ import { buildSuggestedMapping } from "../../lib/mapping.js";
9
+ import { parseCsvPreview } from "../../lib/parser.js";
10
+ import { createSyncExcelUploadAttachment } from "../../lib/upload-storage.js";
11
+ import { resolveSyncExcelConcreteScope } from "../../lib/scope.js";
12
+ import { isMultipartRequestWithinUploadLimit, resolveDefaultAttachmentMaxUploadBytes } from "../../../attachments/lib/upload-limits.js";
13
+ const metadata = {
14
+ POST: { requireAuth: true, requireFeatures: ["sync_excel.run"] }
15
+ };
16
+ const multipartSchema = z.object({
17
+ entityType: syncExcelEntityTypeSchema.default("customers.person")
18
+ });
19
+ const errorSchema = z.object({
20
+ error: z.string()
21
+ });
22
+ const openApi = {
23
+ tags: ["SyncExcel"],
24
+ summary: "Upload CSV file for sync_excel preview",
25
+ methods: {
26
+ POST: {
27
+ summary: "Upload CSV file",
28
+ requestBody: {
29
+ contentType: "multipart/form-data"
30
+ },
31
+ responses: [
32
+ { status: 200, description: "CSV uploaded and preview parsed", schema: syncExcelUploadResponseSchema }
33
+ ],
34
+ errors: [
35
+ { status: 400, description: "Invalid multipart payload", schema: errorSchema },
36
+ { status: 401, description: "Unauthorized", schema: errorSchema },
37
+ { status: 413, description: "CSV upload exceeds the maximum upload size", schema: errorSchema },
38
+ { status: 422, description: "Unsupported entity type or file type", schema: errorSchema }
39
+ ]
40
+ }
41
+ }
42
+ };
43
+ async function POST(request) {
44
+ const auth = await getAuthFromRequest(request);
45
+ if (!auth?.tenantId) {
46
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
47
+ }
48
+ const container = await createRequestContainer();
49
+ const scopeResult = await resolveSyncExcelConcreteScope({ auth, container, request });
50
+ if (!scopeResult.ok) {
51
+ return NextResponse.json({ error: scopeResult.error }, { status: scopeResult.status });
52
+ }
53
+ const { scope } = scopeResult;
54
+ const contentType = request.headers.get("content-type") || "";
55
+ if (!contentType.toLowerCase().includes("multipart/form-data")) {
56
+ return NextResponse.json({ error: "Expected multipart/form-data" }, { status: 400 });
57
+ }
58
+ if (!isMultipartRequestWithinUploadLimit(request.headers.get("content-length"))) {
59
+ return NextResponse.json({ error: "CSV upload exceeds the maximum upload size." }, { status: 413 });
60
+ }
61
+ const formData = await request.formData();
62
+ const parsedPayload = multipartSchema.safeParse({
63
+ entityType: typeof formData.get("entityType") === "string" ? formData.get("entityType") : void 0
64
+ });
65
+ if (!parsedPayload.success) {
66
+ return NextResponse.json({ error: "Invalid upload payload." }, { status: 422 });
67
+ }
68
+ const file = formData.get("file");
69
+ if (!(file instanceof File)) {
70
+ return NextResponse.json({ error: "Select a CSV file to upload." }, { status: 400 });
71
+ }
72
+ const supportedMimeTypes = /* @__PURE__ */ new Set(["text/csv", "application/vnd.ms-excel", "application/csv"]);
73
+ const isCsvByMime = supportedMimeTypes.has(file.type);
74
+ const isCsvByName = file.name.toLowerCase().endsWith(".csv");
75
+ if (!isCsvByMime && !isCsvByName) {
76
+ return NextResponse.json({ error: "Only CSV uploads are supported in this foundation slice." }, { status: 422 });
77
+ }
78
+ const maxUploadBytes = resolveDefaultAttachmentMaxUploadBytes();
79
+ if (file.size > maxUploadBytes) {
80
+ return NextResponse.json({ error: "CSV upload exceeds the maximum upload size." }, { status: 413 });
81
+ }
82
+ const fileBuffer = Buffer.from(await file.arrayBuffer());
83
+ const preview = parseCsvPreview(fileBuffer, { maxRows: 5 });
84
+ const suggestedMapping = buildSuggestedMapping(parsedPayload.data.entityType, preview.headers);
85
+ const em = container.resolve("em");
86
+ const uploadId = randomUUID();
87
+ const attachment = await createSyncExcelUploadAttachment({
88
+ em,
89
+ uploadId,
90
+ organizationId: scope.organizationId,
91
+ tenantId: scope.tenantId,
92
+ fileName: file.name,
93
+ mimeType: file.type || "text/csv",
94
+ buffer: fileBuffer
95
+ });
96
+ const upload = em.create(SyncExcelUpload, {
97
+ id: uploadId,
98
+ attachmentId: attachment.id,
99
+ filename: file.name,
100
+ mimeType: file.type || "text/csv",
101
+ fileSize: fileBuffer.length,
102
+ entityType: parsedPayload.data.entityType,
103
+ delimiter: preview.delimiter,
104
+ encoding: preview.encoding,
105
+ headers: preview.headers,
106
+ sampleRows: preview.sampleRows,
107
+ totalRows: preview.totalRows,
108
+ status: "uploaded",
109
+ organizationId: scope.organizationId,
110
+ tenantId: scope.tenantId
111
+ });
112
+ em.persist(upload);
113
+ await em.flush();
114
+ return NextResponse.json(syncExcelUploadResponseSchema.parse({
115
+ uploadId: upload.id,
116
+ filename: upload.filename,
117
+ mimeType: upload.mimeType,
118
+ fileSize: upload.fileSize,
119
+ entityType: upload.entityType,
120
+ headers: upload.headers,
121
+ sampleRows: upload.sampleRows,
122
+ totalRows: upload.totalRows,
123
+ suggestedMapping
124
+ }));
125
+ }
126
+ export {
127
+ POST,
128
+ metadata,
129
+ openApi
130
+ };
131
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/sync_excel/api/upload/route.ts"],
4
+ "sourcesContent": ["import { randomUUID } from 'crypto'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { z } from 'zod'\nimport { syncExcelUploadResponseSchema, syncExcelEntityTypeSchema } from '../../data/validators'\nimport { SyncExcelUpload } from '../../data/entities'\nimport { buildSuggestedMapping } from '../../lib/mapping'\nimport { parseCsvPreview } from '../../lib/parser'\nimport { createSyncExcelUploadAttachment } from '../../lib/upload-storage'\nimport { resolveSyncExcelConcreteScope } from '../../lib/scope'\nimport { isMultipartRequestWithinUploadLimit, resolveDefaultAttachmentMaxUploadBytes } from '../../../attachments/lib/upload-limits'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['sync_excel.run'] },\n}\n\nconst multipartSchema = z.object({\n entityType: syncExcelEntityTypeSchema.default('customers.person'),\n})\n\nconst errorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi = {\n tags: ['SyncExcel'],\n summary: 'Upload CSV file for sync_excel preview',\n methods: {\n POST: {\n summary: 'Upload CSV file',\n requestBody: {\n contentType: 'multipart/form-data',\n },\n responses: [\n { status: 200, description: 'CSV uploaded and preview parsed', schema: syncExcelUploadResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid multipart payload', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 413, description: 'CSV upload exceeds the maximum upload size', schema: errorSchema },\n { status: 422, description: 'Unsupported entity type or file type', schema: errorSchema },\n ],\n },\n },\n}\n\nexport async function POST(request: Request) {\n const auth = await getAuthFromRequest(request)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const scopeResult = await resolveSyncExcelConcreteScope({ auth, container, request })\n if (!scopeResult.ok) {\n return NextResponse.json({ error: scopeResult.error }, { status: scopeResult.status })\n }\n const { scope } = scopeResult\n\n const contentType = request.headers.get('content-type') || ''\n if (!contentType.toLowerCase().includes('multipart/form-data')) {\n return NextResponse.json({ error: 'Expected multipart/form-data' }, { status: 400 })\n }\n if (!isMultipartRequestWithinUploadLimit(request.headers.get('content-length'))) {\n return NextResponse.json({ error: 'CSV upload exceeds the maximum upload size.' }, { status: 413 })\n }\n\n const formData = await request.formData()\n const parsedPayload = multipartSchema.safeParse({\n entityType: typeof formData.get('entityType') === 'string' ? formData.get('entityType') : undefined,\n })\n\n if (!parsedPayload.success) {\n return NextResponse.json({ error: 'Invalid upload payload.' }, { status: 422 })\n }\n\n const file = formData.get('file')\n if (!(file instanceof File)) {\n return NextResponse.json({ error: 'Select a CSV file to upload.' }, { status: 400 })\n }\n\n const supportedMimeTypes = new Set(['text/csv', 'application/vnd.ms-excel', 'application/csv'])\n const isCsvByMime = supportedMimeTypes.has(file.type)\n const isCsvByName = file.name.toLowerCase().endsWith('.csv')\n\n if (!isCsvByMime && !isCsvByName) {\n return NextResponse.json({ error: 'Only CSV uploads are supported in this foundation slice.' }, { status: 422 })\n }\n\n const maxUploadBytes = resolveDefaultAttachmentMaxUploadBytes()\n if (file.size > maxUploadBytes) {\n return NextResponse.json({ error: 'CSV upload exceeds the maximum upload size.' }, { status: 413 })\n }\n\n const fileBuffer = Buffer.from(await file.arrayBuffer())\n const preview = parseCsvPreview(fileBuffer, { maxRows: 5 })\n const suggestedMapping = buildSuggestedMapping(parsedPayload.data.entityType, preview.headers)\n\n const em = container.resolve('em') as EntityManager\n const uploadId = randomUUID()\n const attachment = await createSyncExcelUploadAttachment({\n em,\n uploadId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n fileName: file.name,\n mimeType: file.type || 'text/csv',\n buffer: fileBuffer,\n })\n\n const upload = em.create(SyncExcelUpload, {\n id: uploadId,\n attachmentId: attachment.id,\n filename: file.name,\n mimeType: file.type || 'text/csv',\n fileSize: fileBuffer.length,\n entityType: parsedPayload.data.entityType,\n delimiter: preview.delimiter,\n encoding: preview.encoding,\n headers: preview.headers,\n sampleRows: preview.sampleRows,\n totalRows: preview.totalRows,\n status: 'uploaded',\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n\n em.persist(upload)\n await em.flush()\n\n return NextResponse.json(syncExcelUploadResponseSchema.parse({\n uploadId: upload.id,\n filename: upload.filename,\n mimeType: upload.mimeType,\n fileSize: upload.fileSize,\n entityType: upload.entityType,\n headers: upload.headers,\n sampleRows: upload.sampleRows,\n totalRows: upload.totalRows,\n suggestedMapping,\n }))\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,79 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result) __defProp(target, key, result);
9
+ return result;
10
+ };
11
+ import { OptionalProps } from "@mikro-orm/core";
12
+ import { Entity, Index, PrimaryKey, Property } from "@mikro-orm/decorators/legacy";
13
+ OptionalProps;
14
+ let SyncExcelUpload = class {
15
+ constructor() {
16
+ this.status = "uploaded";
17
+ this.createdAt = /* @__PURE__ */ new Date();
18
+ this.updatedAt = /* @__PURE__ */ new Date();
19
+ }
20
+ };
21
+ __decorateClass([
22
+ PrimaryKey({ type: "uuid", defaultRaw: "gen_random_uuid()" })
23
+ ], SyncExcelUpload.prototype, "id", 2);
24
+ __decorateClass([
25
+ Property({ name: "attachment_id", type: "uuid" })
26
+ ], SyncExcelUpload.prototype, "attachmentId", 2);
27
+ __decorateClass([
28
+ Property({ name: "filename", type: "text" })
29
+ ], SyncExcelUpload.prototype, "filename", 2);
30
+ __decorateClass([
31
+ Property({ name: "mime_type", type: "text" })
32
+ ], SyncExcelUpload.prototype, "mimeType", 2);
33
+ __decorateClass([
34
+ Property({ name: "file_size", type: "int" })
35
+ ], SyncExcelUpload.prototype, "fileSize", 2);
36
+ __decorateClass([
37
+ Property({ name: "entity_type", type: "text" })
38
+ ], SyncExcelUpload.prototype, "entityType", 2);
39
+ __decorateClass([
40
+ Property({ name: "delimiter", type: "text", nullable: true })
41
+ ], SyncExcelUpload.prototype, "delimiter", 2);
42
+ __decorateClass([
43
+ Property({ name: "encoding", type: "text", nullable: true })
44
+ ], SyncExcelUpload.prototype, "encoding", 2);
45
+ __decorateClass([
46
+ Property({ name: "headers", type: "json" })
47
+ ], SyncExcelUpload.prototype, "headers", 2);
48
+ __decorateClass([
49
+ Property({ name: "sample_rows", type: "json" })
50
+ ], SyncExcelUpload.prototype, "sampleRows", 2);
51
+ __decorateClass([
52
+ Property({ name: "total_rows", type: "int" })
53
+ ], SyncExcelUpload.prototype, "totalRows", 2);
54
+ __decorateClass([
55
+ Property({ name: "status", type: "text", default: "uploaded" })
56
+ ], SyncExcelUpload.prototype, "status", 2);
57
+ __decorateClass([
58
+ Property({ name: "sync_run_id", type: "uuid", nullable: true })
59
+ ], SyncExcelUpload.prototype, "syncRunId", 2);
60
+ __decorateClass([
61
+ Property({ name: "organization_id", type: "uuid" })
62
+ ], SyncExcelUpload.prototype, "organizationId", 2);
63
+ __decorateClass([
64
+ Property({ name: "tenant_id", type: "uuid" })
65
+ ], SyncExcelUpload.prototype, "tenantId", 2);
66
+ __decorateClass([
67
+ Property({ name: "created_at", type: Date, onCreate: () => /* @__PURE__ */ new Date() })
68
+ ], SyncExcelUpload.prototype, "createdAt", 2);
69
+ __decorateClass([
70
+ Property({ name: "updated_at", type: Date, onUpdate: () => /* @__PURE__ */ new Date() })
71
+ ], SyncExcelUpload.prototype, "updatedAt", 2);
72
+ SyncExcelUpload = __decorateClass([
73
+ Entity({ tableName: "sync_excel_uploads" }),
74
+ Index({ properties: ["organizationId", "tenantId", "status"] })
75
+ ], SyncExcelUpload);
76
+ export {
77
+ SyncExcelUpload
78
+ };
79
+ //# sourceMappingURL=entities.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/sync_excel/data/entities.ts"],
4
+ "sourcesContent": ["import { OptionalProps } from '@mikro-orm/core'\nimport { Entity, Index, PrimaryKey, Property } from '@mikro-orm/decorators/legacy'\n\nexport type SyncExcelUploadStatus = 'uploaded' | 'previewed' | 'importing' | 'completed' | 'failed'\n\n@Entity({ tableName: 'sync_excel_uploads' })\n@Index({ properties: ['organizationId', 'tenantId', 'status'] })\nexport class SyncExcelUpload {\n [OptionalProps]?: 'delimiter' | 'encoding' | 'status' | 'syncRunId' | 'createdAt' | 'updatedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'attachment_id', type: 'uuid' })\n attachmentId!: string\n\n @Property({ name: 'filename', type: 'text' })\n filename!: string\n\n @Property({ name: 'mime_type', type: 'text' })\n mimeType!: string\n\n @Property({ name: 'file_size', type: 'int' })\n fileSize!: number\n\n @Property({ name: 'entity_type', type: 'text' })\n entityType!: string\n\n @Property({ name: 'delimiter', type: 'text', nullable: true })\n delimiter?: string | null\n\n @Property({ name: 'encoding', type: 'text', nullable: true })\n encoding?: string | null\n\n @Property({ name: 'headers', type: 'json' })\n headers!: string[]\n\n @Property({ name: 'sample_rows', type: 'json' })\n sampleRows!: Array<Record<string, string | null>>\n\n @Property({ name: 'total_rows', type: 'int' })\n totalRows!: number\n\n @Property({ name: 'status', type: 'text', default: 'uploaded' })\n status: SyncExcelUploadStatus = 'uploaded'\n\n @Property({ name: 'sync_run_id', type: 'uuid', nullable: true })\n syncRunId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n}\n"],
5
+ "mappings": ";;;;;;;;;;AAAA,SAAS,qBAAqB;AAC9B,SAAS,QAAQ,OAAO,YAAY,gBAAgB;AAOjD;AADI,IAAM,kBAAN,MAAsB;AAAA,EAAtB;AAqCL,kBAAgC;AAYhC,qBAAkB,oBAAI,KAAK;AAG3B,qBAAkB,oBAAI,KAAK;AAAA;AAC7B;AAjDE;AAAA,EADC,WAAW,EAAE,MAAM,QAAQ,YAAY,oBAAoB,CAAC;AAAA,GAHlD,gBAIX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,iBAAiB,MAAM,OAAO,CAAC;AAAA,GANtC,gBAOX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,YAAY,MAAM,OAAO,CAAC;AAAA,GATjC,gBAUX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,aAAa,MAAM,OAAO,CAAC;AAAA,GAZlC,gBAaX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,aAAa,MAAM,MAAM,CAAC;AAAA,GAfjC,gBAgBX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,eAAe,MAAM,OAAO,CAAC;AAAA,GAlBpC,gBAmBX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,aAAa,MAAM,QAAQ,UAAU,KAAK,CAAC;AAAA,GArBlD,gBAsBX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,YAAY,MAAM,QAAQ,UAAU,KAAK,CAAC;AAAA,GAxBjD,gBAyBX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,WAAW,MAAM,OAAO,CAAC;AAAA,GA3BhC,gBA4BX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,eAAe,MAAM,OAAO,CAAC;AAAA,GA9BpC,gBA+BX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,cAAc,MAAM,MAAM,CAAC;AAAA,GAjClC,gBAkCX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,UAAU,MAAM,QAAQ,SAAS,WAAW,CAAC;AAAA,GApCpD,gBAqCX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,eAAe,MAAM,QAAQ,UAAU,KAAK,CAAC;AAAA,GAvCpD,gBAwCX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,mBAAmB,MAAM,OAAO,CAAC;AAAA,GA1CxC,gBA2CX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,aAAa,MAAM,OAAO,CAAC;AAAA,GA7ClC,gBA8CX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,cAAc,MAAM,MAAM,UAAU,MAAM,oBAAI,KAAK,EAAE,CAAC;AAAA,GAhD7D,gBAiDX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,cAAc,MAAM,MAAM,UAAU,MAAM,oBAAI,KAAK,EAAE,CAAC;AAAA,GAnD7D,gBAoDX;AApDW,kBAAN;AAAA,EAFN,OAAO,EAAE,WAAW,qBAAqB,CAAC;AAAA,EAC1C,MAAM,EAAE,YAAY,CAAC,kBAAkB,YAAY,QAAQ,EAAE,CAAC;AAAA,GAClD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,58 @@
1
+ import { z } from "zod";
2
+ const syncExcelEntityTypes = ["customers.person"];
3
+ const syncExcelEntityTypeSchema = z.enum(syncExcelEntityTypes);
4
+ const syncExcelPreviewRowSchema = z.record(z.string(), z.string().nullable());
5
+ const syncExcelSuggestedFieldSchema = z.object({
6
+ externalField: z.string().min(1),
7
+ localField: z.string().min(1),
8
+ transform: z.string().optional(),
9
+ required: z.boolean().optional(),
10
+ defaultValue: z.unknown().optional(),
11
+ mappingKind: z.enum(["core", "relation", "external_id", "custom_field", "metadata", "ignore"]).optional(),
12
+ dedupeRole: z.enum(["primary", "secondary"]).optional()
13
+ });
14
+ const syncExcelSuggestedMappingSchema = z.object({
15
+ entityType: syncExcelEntityTypeSchema,
16
+ matchStrategy: z.enum(["externalId", "email", "custom"]),
17
+ matchField: z.string().optional(),
18
+ fields: z.array(syncExcelSuggestedFieldSchema),
19
+ unmappedColumns: z.array(z.string())
20
+ });
21
+ const syncExcelUploadResponseSchema = z.object({
22
+ uploadId: z.string().uuid(),
23
+ filename: z.string().min(1),
24
+ mimeType: z.string().min(1),
25
+ fileSize: z.number().int().nonnegative(),
26
+ entityType: syncExcelEntityTypeSchema,
27
+ headers: z.array(z.string()),
28
+ sampleRows: z.array(syncExcelPreviewRowSchema),
29
+ totalRows: z.number().int().nonnegative(),
30
+ suggestedMapping: syncExcelSuggestedMappingSchema
31
+ });
32
+ const syncExcelPreviewQuerySchema = z.object({
33
+ uploadId: z.string().uuid(),
34
+ entityType: syncExcelEntityTypeSchema.optional()
35
+ });
36
+ const syncExcelImportRequestSchema = z.object({
37
+ uploadId: z.string().uuid(),
38
+ entityType: syncExcelEntityTypeSchema,
39
+ mapping: syncExcelSuggestedMappingSchema,
40
+ batchSize: z.number().int().min(1).max(1e3).default(100).optional()
41
+ });
42
+ const syncExcelImportResponseSchema = z.object({
43
+ runId: z.string().uuid(),
44
+ progressJobId: z.string().uuid().nullable(),
45
+ status: z.enum(["pending", "running", "completed", "failed", "cancelled"])
46
+ });
47
+ export {
48
+ syncExcelEntityTypeSchema,
49
+ syncExcelEntityTypes,
50
+ syncExcelImportRequestSchema,
51
+ syncExcelImportResponseSchema,
52
+ syncExcelPreviewQuerySchema,
53
+ syncExcelPreviewRowSchema,
54
+ syncExcelSuggestedFieldSchema,
55
+ syncExcelSuggestedMappingSchema,
56
+ syncExcelUploadResponseSchema
57
+ };
58
+ //# sourceMappingURL=validators.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/sync_excel/data/validators.ts"],
4
+ "sourcesContent": ["import { z } from 'zod'\n\nexport const syncExcelEntityTypes = ['customers.person'] as const\n\nexport const syncExcelEntityTypeSchema = z.enum(syncExcelEntityTypes)\n\nexport const syncExcelPreviewRowSchema = z.record(z.string(), z.string().nullable())\n\nexport const syncExcelSuggestedFieldSchema = z.object({\n externalField: z.string().min(1),\n localField: z.string().min(1),\n transform: z.string().optional(),\n required: z.boolean().optional(),\n defaultValue: z.unknown().optional(),\n mappingKind: z.enum(['core', 'relation', 'external_id', 'custom_field', 'metadata', 'ignore']).optional(),\n dedupeRole: z.enum(['primary', 'secondary']).optional(),\n})\n\nexport const syncExcelSuggestedMappingSchema = z.object({\n entityType: syncExcelEntityTypeSchema,\n matchStrategy: z.enum(['externalId', 'email', 'custom']),\n matchField: z.string().optional(),\n fields: z.array(syncExcelSuggestedFieldSchema),\n unmappedColumns: z.array(z.string()),\n})\n\nexport const syncExcelUploadResponseSchema = z.object({\n uploadId: z.string().uuid(),\n filename: z.string().min(1),\n mimeType: z.string().min(1),\n fileSize: z.number().int().nonnegative(),\n entityType: syncExcelEntityTypeSchema,\n headers: z.array(z.string()),\n sampleRows: z.array(syncExcelPreviewRowSchema),\n totalRows: z.number().int().nonnegative(),\n suggestedMapping: syncExcelSuggestedMappingSchema,\n})\n\nexport const syncExcelPreviewQuerySchema = z.object({\n uploadId: z.string().uuid(),\n entityType: syncExcelEntityTypeSchema.optional(),\n})\n\nexport const syncExcelImportRequestSchema = z.object({\n uploadId: z.string().uuid(),\n entityType: syncExcelEntityTypeSchema,\n mapping: syncExcelSuggestedMappingSchema,\n batchSize: z.number().int().min(1).max(1000).default(100).optional(),\n})\n\nexport const syncExcelImportResponseSchema = z.object({\n runId: z.string().uuid(),\n progressJobId: z.string().uuid().nullable(),\n status: z.enum(['pending', 'running', 'completed', 'failed', 'cancelled']),\n})\n\nexport type SyncExcelPreviewQuery = z.infer<typeof syncExcelPreviewQuerySchema>\nexport type SyncExcelImportRequest = z.infer<typeof syncExcelImportRequestSchema>\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAEX,MAAM,uBAAuB,CAAC,kBAAkB;AAEhD,MAAM,4BAA4B,EAAE,KAAK,oBAAoB;AAE7D,MAAM,4BAA4B,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,EAAE,SAAS,CAAC;AAE5E,MAAM,gCAAgC,EAAE,OAAO;AAAA,EACpD,eAAe,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/B,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC5B,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,UAAU,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC/B,cAAc,EAAE,QAAQ,EAAE,SAAS;AAAA,EACnC,aAAa,EAAE,KAAK,CAAC,QAAQ,YAAY,eAAe,gBAAgB,YAAY,QAAQ,CAAC,EAAE,SAAS;AAAA,EACxG,YAAY,EAAE,KAAK,CAAC,WAAW,WAAW,CAAC,EAAE,SAAS;AACxD,CAAC;AAEM,MAAM,kCAAkC,EAAE,OAAO;AAAA,EACtD,YAAY;AAAA,EACZ,eAAe,EAAE,KAAK,CAAC,cAAc,SAAS,QAAQ,CAAC;AAAA,EACvD,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,EAChC,QAAQ,EAAE,MAAM,6BAA6B;AAAA,EAC7C,iBAAiB,EAAE,MAAM,EAAE,OAAO,CAAC;AACrC,CAAC;AAEM,MAAM,gCAAgC,EAAE,OAAO;AAAA,EACpD,UAAU,EAAE,OAAO,EAAE,KAAK;AAAA,EAC1B,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC1B,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC1B,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EACvC,YAAY;AAAA,EACZ,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EAC3B,YAAY,EAAE,MAAM,yBAAyB;AAAA,EAC7C,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EACxC,kBAAkB;AACpB,CAAC;AAEM,MAAM,8BAA8B,EAAE,OAAO;AAAA,EAClD,UAAU,EAAE,OAAO,EAAE,KAAK;AAAA,EAC1B,YAAY,0BAA0B,SAAS;AACjD,CAAC;AAEM,MAAM,+BAA+B,EAAE,OAAO;AAAA,EACnD,UAAU,EAAE,OAAO,EAAE,KAAK;AAAA,EAC1B,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI,EAAE,QAAQ,GAAG,EAAE,SAAS;AACrE,CAAC;AAEM,MAAM,gCAAgC,EAAE,OAAO;AAAA,EACpD,OAAO,EAAE,OAAO,EAAE,KAAK;AAAA,EACvB,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC1C,QAAQ,EAAE,KAAK,CAAC,WAAW,WAAW,aAAa,UAAU,WAAW,CAAC;AAC3E,CAAC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,11 @@
1
+ import { registerDataSyncAdapter } from "../data_sync/lib/adapter-registry.js";
2
+ import { syncExcelCustomersAdapter } from "./lib/adapters/customers.js";
3
+ function register(_container) {
4
+ registerDataSyncAdapter(syncExcelCustomersAdapter);
5
+ }
6
+ var di_default = register;
7
+ export {
8
+ di_default as default,
9
+ register
10
+ };
11
+ //# sourceMappingURL=di.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/sync_excel/di.ts"],
4
+ "sourcesContent": ["import type { AppContainer } from '@open-mercato/shared/lib/di/container'\nimport { registerDataSyncAdapter } from '../data_sync/lib/adapter-registry'\nimport { syncExcelCustomersAdapter } from './lib/adapters/customers'\n\nexport function register(_container: AppContainer) {\n registerDataSyncAdapter(syncExcelCustomersAdapter)\n}\n\nexport default register\n"],
5
+ "mappings": "AACA,SAAS,+BAA+B;AACxC,SAAS,iCAAiC;AAEnC,SAAS,SAAS,YAA0B;AACjD,0BAAwB,yBAAyB;AACnD;AAEA,IAAO,aAAQ;",
6
+ "names": []
7
+ }