@postxl/generators 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend-authentication/generators/authentication-service.generator.js +1 -1
- package/dist/backend-core/backend.generator.js +0 -4
- package/dist/backend-core/backend.generator.js.map +1 -1
- package/dist/backend-core/modules/backend-module-xlport.generator.js +1 -1
- package/dist/backend-excel-io/excel-io.generator.d.ts +19 -0
- package/dist/backend-excel-io/excel-io.generator.js +106 -0
- package/dist/backend-excel-io/excel-io.generator.js.map +1 -0
- package/dist/backend-excel-io/generators/excel-io-service.generator.d.ts +9 -0
- package/dist/backend-excel-io/generators/excel-io-service.generator.js +680 -0
- package/dist/backend-excel-io/generators/excel-io-service.generator.js.map +1 -0
- package/dist/backend-excel-io/index.d.ts +2 -0
- package/dist/backend-excel-io/index.js +22 -0
- package/dist/backend-excel-io/index.js.map +1 -0
- package/dist/backend-excel-io/template/README.md +24 -0
- package/dist/backend-excel-io/template/excel-io.controller.ts +195 -0
- package/dist/backend-excel-io/template/excel-io.module.ts +17 -0
- package/dist/backend-import/generators/detect-delta/detect-delta-functions.generator.js +148 -13
- package/dist/backend-import/generators/detect-delta/detect-delta-functions.generator.js.map +1 -1
- package/dist/backend-import/generators/filter-error-rows.generator.d.ts +2 -0
- package/dist/backend-import/generators/filter-error-rows.generator.js +28 -0
- package/dist/backend-import/generators/filter-error-rows.generator.js.map +1 -0
- package/dist/backend-import/generators/import-service.generator.js +126 -2
- package/dist/backend-import/generators/import-service.generator.js.map +1 -1
- package/dist/backend-import/import.generator.js +2 -0
- package/dist/backend-import/import.generator.js.map +1 -1
- package/dist/backend-repositories/generators/model-repository.generator.js +17 -2
- package/dist/backend-repositories/generators/model-repository.generator.js.map +1 -1
- package/dist/backend-router-trpc/generators/app-routes.generator.js +5 -0
- package/dist/backend-router-trpc/generators/app-routes.generator.js.map +1 -1
- package/dist/backend-router-trpc/generators/excel-io-route.generator.d.ts +4 -0
- package/dist/backend-router-trpc/generators/excel-io-route.generator.js +35 -0
- package/dist/backend-router-trpc/generators/excel-io-route.generator.js.map +1 -0
- package/dist/backend-router-trpc/generators/trpc-plugin.generator.js +6 -0
- package/dist/backend-router-trpc/generators/trpc-plugin.generator.js.map +1 -1
- package/dist/backend-router-trpc/generators/trpc-router-module.generator.js +8 -1
- package/dist/backend-router-trpc/generators/trpc-router-module.generator.js.map +1 -1
- package/dist/backend-router-trpc/generators/trpc-shared.generator.js +9 -0
- package/dist/backend-router-trpc/generators/trpc-shared.generator.js.map +1 -1
- package/dist/backend-router-trpc/router-trpc.generator.d.ts +2 -1
- package/dist/backend-router-trpc/router-trpc.generator.js +4 -0
- package/dist/backend-router-trpc/router-trpc.generator.js.map +1 -1
- package/dist/base/base.generator.js +0 -4
- package/dist/base/base.generator.js.map +1 -1
- package/dist/decoders/datamodel-decoder.generator.js +91 -1
- package/dist/decoders/datamodel-decoder.generator.js.map +1 -1
- package/dist/decoders/decoders.generator.d.ts +9 -0
- package/dist/decoders/decoders.generator.js +15 -0
- package/dist/decoders/decoders.generator.js.map +1 -1
- package/dist/devops/devops.generator.d.ts +5 -1
- package/dist/devops/devops.generator.js +5 -4
- package/dist/devops/devops.generator.js.map +1 -1
- package/dist/devops/generators/docker-compose-yml.generator.d.ts +1 -1
- package/dist/devops/generators/docker-compose-yml.generator.js +16 -1
- package/dist/devops/generators/docker-compose-yml.generator.js.map +1 -1
- package/dist/e2e/template/e2e/specs/example.spec.ts-snapshots/Navigate-to-homepage-and-take-snapshot-1-chromium-linux.png +0 -0
- package/dist/frontend-actions/generators/filter-utils.generator.js +1 -1
- package/dist/frontend-admin/admin.generator.d.ts +2 -0
- package/dist/frontend-admin/admin.generator.js +28 -0
- package/dist/frontend-admin/admin.generator.js.map +1 -1
- package/dist/frontend-admin/generators/admin-global-actions.generator.js +78 -0
- package/dist/frontend-admin/generators/admin-global-actions.generator.js.map +1 -1
- package/dist/frontend-admin/generators/admin-overview-page.generator.js +21 -1
- package/dist/frontend-admin/generators/admin-overview-page.generator.js.map +1 -1
- package/dist/frontend-admin/generators/admin-sidebar.generator.js +20 -0
- package/dist/frontend-admin/generators/admin-sidebar.generator.js.map +1 -1
- package/dist/frontend-admin/generators/excel-io-page.generator.d.ts +4 -0
- package/dist/frontend-admin/generators/excel-io-page.generator.js +258 -0
- package/dist/frontend-admin/generators/excel-io-page.generator.js.map +1 -0
- package/dist/frontend-admin/generators/import-review-page-result-stage.generator.d.ts +1 -0
- package/dist/frontend-admin/generators/import-review-page-result-stage.generator.js +104 -0
- package/dist/frontend-admin/generators/import-review-page-result-stage.generator.js.map +1 -0
- package/dist/frontend-admin/generators/import-review-page-review-stage.generator.d.ts +1 -0
- package/dist/frontend-admin/generators/import-review-page-review-stage.generator.js +1031 -0
- package/dist/frontend-admin/generators/import-review-page-review-stage.generator.js.map +1 -0
- package/dist/frontend-admin/generators/import-review-page-upload-stage.generator.d.ts +1 -0
- package/dist/frontend-admin/generators/import-review-page-upload-stage.generator.js +77 -0
- package/dist/frontend-admin/generators/import-review-page-upload-stage.generator.js.map +1 -0
- package/dist/frontend-admin/generators/import-review-page.generator.d.ts +7 -0
- package/dist/frontend-admin/generators/import-review-page.generator.js +180 -0
- package/dist/frontend-admin/generators/import-review-page.generator.js.map +1 -0
- package/dist/frontend-admin/generators/model-admin-page.generator.js +28 -51
- package/dist/frontend-admin/generators/model-admin-page.generator.js.map +1 -1
- package/dist/frontend-admin/utils.js +25 -33
- package/dist/frontend-admin/utils.js.map +1 -1
- package/dist/frontend-core/frontend.generator.js +1 -2
- package/dist/frontend-core/frontend.generator.js.map +1 -1
- package/dist/frontend-core/template/src/components/admin/excel-io-actions.tsx +64 -0
- package/dist/frontend-core/template/src/components/admin/table-view-panel.tsx +41 -3
- package/dist/frontend-core/template/src/hooks/use-excel-io.ts +137 -0
- package/dist/frontend-core/template/src/hooks/use-import-review.ts +143 -0
- package/dist/frontend-core/template/src/lib/excel-download.ts +28 -0
- package/dist/frontend-core/types/hook.d.ts +1 -1
- package/dist/frontend-tables/generators/model-table.generator.js +21 -13
- package/dist/frontend-tables/generators/model-table.generator.js.map +1 -1
- package/dist/generators.js +2 -0
- package/dist/generators.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/seed-data/seed-data.generator.d.ts +3 -0
- package/dist/seed-data/seed-data.generator.js +45 -1
- package/dist/seed-data/seed-data.generator.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateExcelIoService = generateExcelIoService;
|
|
4
|
+
function generateExcelIoService(context) {
|
|
5
|
+
const models = [...context.models.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
6
|
+
const keyFieldMapEntries = models
|
|
7
|
+
.flatMap((model) => [
|
|
8
|
+
` ${model._conjugated.camelCase}: '${model.keyField.name}',`,
|
|
9
|
+
` ${model._conjugated.camelCasePlural}: '${model.keyField.name}',`,
|
|
10
|
+
])
|
|
11
|
+
.join('\n');
|
|
12
|
+
const modelNames = models.map((model) => `'${model._conjugated.camelCase}'`).join(',\n ');
|
|
13
|
+
const allModelsDataModel = models
|
|
14
|
+
.map((model) => ` ${model._conjugated.camelCasePlural}: await this.viewService.${model.view.service.variableName}.getList(user),`)
|
|
15
|
+
.join('\n');
|
|
16
|
+
const modelCases = models
|
|
17
|
+
.map((model) => `
|
|
18
|
+
case '${model._conjugated.camelCase}': {
|
|
19
|
+
const filters = ${model.types.filter.decoder.name}.parse(rawFilters ?? {})
|
|
20
|
+
const sort = ${model.types.sort.decoder.name}.optional().parse(rawSort)
|
|
21
|
+
const items = await this.viewService.${model.view.service.variableName}.getFiltered({ filters, sort, user })
|
|
22
|
+
return this.renderExcel({
|
|
23
|
+
dataModel: { ${model._conjugated.camelCasePlural}: items },
|
|
24
|
+
filename: \`${'${timeStamp()}'} ${model._conjugated.kebabCasePlural}.xlsx\`,
|
|
25
|
+
templateIds: ['${model._conjugated.camelCase}.xlsx', ...ALL_MODELS_TEMPLATE_IDS],
|
|
26
|
+
})
|
|
27
|
+
}`)
|
|
28
|
+
.join('');
|
|
29
|
+
const filterSortImports = models
|
|
30
|
+
.map((model) => ` ${model.types.filter.decoder.name},\n ${model.types.sort.decoder.name},`)
|
|
31
|
+
.join('\n');
|
|
32
|
+
return /*ts*/ `
|
|
33
|
+
import { DispatcherService } from '@actions/dispatcher.service'
|
|
34
|
+
import { flattenExcelDataModel, type DataModel, type RecordParseError } from '@decoders/data-model.decoder'
|
|
35
|
+
import type { Action_Import, ImportExecutionSummary } from '@import/import.service'
|
|
36
|
+
import { ImportService } from '@import/import.service'
|
|
37
|
+
import type { Delta } from '@import/detect-delta/models.detect-delta'
|
|
38
|
+
import { extractErrors } from '@import/detect-delta/models.detect-delta'
|
|
39
|
+
import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common'
|
|
40
|
+
import type { User } from '@types'
|
|
41
|
+
import {
|
|
42
|
+
${filterSortImports}
|
|
43
|
+
} from '@types'
|
|
44
|
+
import { ViewService } from '@view/view.service'
|
|
45
|
+
import { XlPortService } from '@xlport/xlport.service'
|
|
46
|
+
|
|
47
|
+
import { randomUUID } from 'node:crypto'
|
|
48
|
+
import { Readable } from 'node:stream'
|
|
49
|
+
import z from 'zod'
|
|
50
|
+
|
|
51
|
+
import { ExhaustiveSwitchCheck, timeStamp } from '@postxl/utils'
|
|
52
|
+
|
|
53
|
+
const modelNameDecoder = z.enum([
|
|
54
|
+
${modelNames}
|
|
55
|
+
])
|
|
56
|
+
|
|
57
|
+
export type ExportModelName = z.infer<typeof modelNameDecoder>
|
|
58
|
+
const ALL_MODELS_TEMPLATE_IDS = ['data-model.xlsx'] as const
|
|
59
|
+
|
|
60
|
+
const PREVIEW_TTL_MS = 30 * 60 * 1000 // 30 minutes
|
|
61
|
+
const PREVIEW_MAX_ENTRIES = 50
|
|
62
|
+
|
|
63
|
+
const KEY_FIELD_MAP: Record<string, string> = {
|
|
64
|
+
${keyFieldMapEntries}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
type StoredPreview = {
|
|
68
|
+
delta: Delta
|
|
69
|
+
parseErrors: RecordParseError[]
|
|
70
|
+
includeDetailedUnchangedRecords: boolean
|
|
71
|
+
filename: string
|
|
72
|
+
createdAt: number
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type ImportPreviewModelSummary = {
|
|
76
|
+
create: number
|
|
77
|
+
update: number
|
|
78
|
+
delete: number
|
|
79
|
+
unchanged: number
|
|
80
|
+
errors: number
|
|
81
|
+
items: ImportPreviewItem[]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type ImportPreviewItem = {
|
|
85
|
+
type: 'create' | 'update' | 'delete' | 'unchanged' | 'errors'
|
|
86
|
+
deltaIndex?: number
|
|
87
|
+
id: string
|
|
88
|
+
keyFieldValue: string
|
|
89
|
+
input: Record<string, unknown>
|
|
90
|
+
delta?: Record<string, { old: unknown; new: unknown }>
|
|
91
|
+
errors?: unknown[]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type ImportPreviewResponse = {
|
|
95
|
+
previewSessionId: string
|
|
96
|
+
filename: string
|
|
97
|
+
summary: {
|
|
98
|
+
create: number
|
|
99
|
+
update: number
|
|
100
|
+
delete: number
|
|
101
|
+
unchanged: number
|
|
102
|
+
errors: number
|
|
103
|
+
}
|
|
104
|
+
models: Record<string, ImportPreviewModelSummary>
|
|
105
|
+
processingErrors?: string[]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export type ImportPreviewSessionResponse = {
|
|
109
|
+
previewSessionId: string
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export type ImportPreviewOptions = {
|
|
113
|
+
includeDetailedUnchangedRecords?: boolean
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const zImportPreviewSessionRequestDecoder = z.object({
|
|
117
|
+
previewSessionId: z.string().min(1, 'Preview session id is required'),
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
export type ImportPreviewSessionRequest = z.infer<typeof zImportPreviewSessionRequestDecoder>
|
|
121
|
+
|
|
122
|
+
export const zImportExecuteRequestDecoder = z.object({
|
|
123
|
+
previewSessionId: z.string().min(1, 'Preview session id is required'),
|
|
124
|
+
ignoreErrors: z.boolean().default(false),
|
|
125
|
+
confirmDeletes: z.boolean().default(false),
|
|
126
|
+
selectedDeltaIndicesByModel: z.record(z.string(), z.array(z.number().int().min(0))).default({}),
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
export type ImportExecuteRequest = z.infer<typeof zImportExecuteRequestDecoder>
|
|
130
|
+
|
|
131
|
+
@Injectable()
|
|
132
|
+
export class ExcelIoService {
|
|
133
|
+
private readonly logger = new Logger(ExcelIoService.name)
|
|
134
|
+
private readonly previewStore = new Map<string, StoredPreview>()
|
|
135
|
+
|
|
136
|
+
constructor(
|
|
137
|
+
private readonly viewService: ViewService,
|
|
138
|
+
private readonly xlPortService: XlPortService,
|
|
139
|
+
@Inject(forwardRef(() => DispatcherService)) private readonly dispatcherService: DispatcherService,
|
|
140
|
+
@Inject(forwardRef(() => ImportService)) private readonly importService: ImportService,
|
|
141
|
+
) {}
|
|
142
|
+
|
|
143
|
+
public parseModelName(model: string): ExportModelName {
|
|
144
|
+
return modelNameDecoder.parse(model)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public async exportAllModelsToExcel(user: User) {
|
|
148
|
+
this.logger.log('Exporting all models to Excel')
|
|
149
|
+
|
|
150
|
+
const dataModel: DataModel = {
|
|
151
|
+
${allModelsDataModel}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return this.renderExcel({ dataModel, filename: \`${'${timeStamp()}'} all-models.xlsx\` })
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public async exportModelToExcel({
|
|
158
|
+
model,
|
|
159
|
+
rawFilters,
|
|
160
|
+
rawSort,
|
|
161
|
+
user,
|
|
162
|
+
}: {
|
|
163
|
+
model: ExportModelName
|
|
164
|
+
rawFilters?: unknown
|
|
165
|
+
rawSort?: unknown
|
|
166
|
+
user: User
|
|
167
|
+
}) {
|
|
168
|
+
this.logger.log(\`Exporting model ${'${model}'} to Excel\`)
|
|
169
|
+
|
|
170
|
+
switch (model) {${modelCases}
|
|
171
|
+
default:
|
|
172
|
+
throw new ExhaustiveSwitchCheck(model)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
public async importExcel({ data, filename, user }: { data: Buffer; filename: string; user: User }) {
|
|
177
|
+
this.logger.log('Importing uploaded Excel file through import action')
|
|
178
|
+
|
|
179
|
+
const action: Action_Import = {
|
|
180
|
+
scope: 'import',
|
|
181
|
+
type: 'excel',
|
|
182
|
+
payload: {
|
|
183
|
+
filename,
|
|
184
|
+
data,
|
|
185
|
+
},
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return this.dispatcherService.dispatch({ action, user })
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
public async previewImportExcel({
|
|
192
|
+
data,
|
|
193
|
+
filename,
|
|
194
|
+
includeDetailedUnchangedRecords = false,
|
|
195
|
+
}: {
|
|
196
|
+
data: Buffer
|
|
197
|
+
filename: string
|
|
198
|
+
includeDetailedUnchangedRecords?: boolean
|
|
199
|
+
}): Promise<ImportPreviewSessionResponse> {
|
|
200
|
+
this.logger.log('Previewing Excel import (read-only)')
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
const { delta, parseErrors } = await this.importService.previewExcelImport(data)
|
|
204
|
+
const previewSessionId = randomUUID()
|
|
205
|
+
|
|
206
|
+
this.cleanupExpiredPreviews()
|
|
207
|
+
this.previewStore.set(previewSessionId, {
|
|
208
|
+
delta,
|
|
209
|
+
parseErrors,
|
|
210
|
+
includeDetailedUnchangedRecords,
|
|
211
|
+
filename,
|
|
212
|
+
createdAt: Date.now(),
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
return { previewSessionId }
|
|
216
|
+
} catch (error) {
|
|
217
|
+
this.logger.error('Error during preview delta detection or schema parsing', error)
|
|
218
|
+
throw new Error(error instanceof Error ? error.message : String(error))
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
public getImportPreviewBySessionId({ previewSessionId }: ImportPreviewSessionRequest): ImportPreviewResponse {
|
|
223
|
+
this.logger.log(\`Retrieving import preview session: \${previewSessionId}\`)
|
|
224
|
+
const stored = this.getStoredPreview(previewSessionId)
|
|
225
|
+
return buildPreviewResponse(
|
|
226
|
+
previewSessionId,
|
|
227
|
+
stored.filename,
|
|
228
|
+
stored.delta,
|
|
229
|
+
stored.parseErrors,
|
|
230
|
+
stored.includeDetailedUnchangedRecords,
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
public async executeImportFromPreview({
|
|
235
|
+
previewSessionId,
|
|
236
|
+
ignoreErrors,
|
|
237
|
+
confirmDeletes,
|
|
238
|
+
selectedDeltaIndicesByModel,
|
|
239
|
+
user,
|
|
240
|
+
}: ImportExecuteRequest & { user: User }): Promise<ImportExecutionSummary> {
|
|
241
|
+
this.logger.log(\`Executing import from preview session: \${previewSessionId}\`)
|
|
242
|
+
const stored = this.getStoredPreview(previewSessionId)
|
|
243
|
+
const selectedDelta = filterDeltaBySelectedIndices(stored.delta, selectedDeltaIndicesByModel)
|
|
244
|
+
|
|
245
|
+
if (!deltaHasExecutableChanges(selectedDelta)) {
|
|
246
|
+
throw new Error('No executable rows selected. Select at least one create, update, or delete row.')
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const hasDeletes = deltaHasDeletes(selectedDelta)
|
|
250
|
+
if (hasDeletes && !confirmDeletes) {
|
|
251
|
+
throw new Error('Import contains delete operations. You must confirm deletes before executing.')
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const hasErrors = Object.keys(extractErrors(selectedDelta)).length > 0
|
|
255
|
+
if (hasErrors && !ignoreErrors) {
|
|
256
|
+
throw new Error('Import contains errors. Enable "Ignore errors" to skip errored rows, or fix the data.')
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const action: Action_Import = {
|
|
260
|
+
scope: 'import',
|
|
261
|
+
type: 'reviewedExcel',
|
|
262
|
+
payload: {
|
|
263
|
+
filename: stored.filename,
|
|
264
|
+
delta: selectedDelta,
|
|
265
|
+
ignoreErrors,
|
|
266
|
+
},
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const result = await this.dispatcherService.dispatch({ action, user })
|
|
270
|
+
|
|
271
|
+
this.previewStore.delete(previewSessionId)
|
|
272
|
+
|
|
273
|
+
return result as ImportExecutionSummary
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private getStoredPreview(previewSessionId: string): StoredPreview {
|
|
277
|
+
this.cleanupExpiredPreviews()
|
|
278
|
+
|
|
279
|
+
const stored = this.previewStore.get(previewSessionId)
|
|
280
|
+
if (!stored) {
|
|
281
|
+
throw new Error('Invalid or expired preview session id')
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (Date.now() - stored.createdAt > PREVIEW_TTL_MS) {
|
|
285
|
+
this.previewStore.delete(previewSessionId)
|
|
286
|
+
throw new Error('Preview session has expired. Please upload and preview again.')
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return stored
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
private cleanupExpiredPreviews() {
|
|
293
|
+
const now = Date.now()
|
|
294
|
+
for (const [key, value] of this.previewStore.entries()) {
|
|
295
|
+
if (now - value.createdAt > PREVIEW_TTL_MS) {
|
|
296
|
+
this.previewStore.delete(key)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (this.previewStore.size >= PREVIEW_MAX_ENTRIES) {
|
|
301
|
+
const oldest = [...this.previewStore.entries()].sort((a, b) => a[1].createdAt - b[1].createdAt)
|
|
302
|
+
const toRemove = oldest.slice(0, this.previewStore.size - PREVIEW_MAX_ENTRIES + 1)
|
|
303
|
+
for (const [key] of toRemove) {
|
|
304
|
+
this.previewStore.delete(key)
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private async renderExcel({
|
|
310
|
+
dataModel,
|
|
311
|
+
filename,
|
|
312
|
+
templateIds = ALL_MODELS_TEMPLATE_IDS,
|
|
313
|
+
}: {
|
|
314
|
+
dataModel: DataModel
|
|
315
|
+
filename: string
|
|
316
|
+
templateIds?: readonly string[]
|
|
317
|
+
}) {
|
|
318
|
+
const data = flattenExcelDataModel(dataModel)
|
|
319
|
+
const file = await this.exportWithTemplateIds(data, templateIds)
|
|
320
|
+
|
|
321
|
+
return { filename, file }
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private async exportWithTemplateIds(
|
|
325
|
+
data: ReturnType<typeof flattenExcelDataModel>,
|
|
326
|
+
templateIds: readonly string[],
|
|
327
|
+
): Promise<Buffer> {
|
|
328
|
+
const errors: string[] = []
|
|
329
|
+
|
|
330
|
+
for (const templateId of templateIds) {
|
|
331
|
+
try {
|
|
332
|
+
const raw = await this.xlPortService.exportToFile({ templateId, data })
|
|
333
|
+
const buffer = await toBuffer(raw)
|
|
334
|
+
const validated = parseXlPortExportBuffer(buffer)
|
|
335
|
+
this.logger.log(\`Excel export succeeded via templateId(\${templateId})\`)
|
|
336
|
+
return validated
|
|
337
|
+
} catch (error) {
|
|
338
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
339
|
+
errors.push(\`templateId(\${templateId}): \${message}\`)
|
|
340
|
+
this.logger.warn(\`Excel export attempt failed (templateId=\${templateId}): \${message}\`)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
throw new Error(
|
|
345
|
+
\`Unable to export Excel via xlport templateId lookup. Tried \${templateIds.join(', ')}. Details: \${errors.join(' | ')}\`,
|
|
346
|
+
)
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async function toBuffer(value: unknown): Promise<Buffer> {
|
|
351
|
+
if (Buffer.isBuffer(value)) {
|
|
352
|
+
return value
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (value instanceof Uint8Array) {
|
|
356
|
+
return Buffer.from(value)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (typeof value === 'string') {
|
|
360
|
+
return Buffer.from(value)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (value instanceof Readable) {
|
|
364
|
+
const asyncValue = asAsyncIterable(value)
|
|
365
|
+
const chunks: Buffer[] = []
|
|
366
|
+
for await (const chunk of asyncValue) {
|
|
367
|
+
if (Buffer.isBuffer(chunk)) {
|
|
368
|
+
chunks.push(chunk)
|
|
369
|
+
} else if (chunk instanceof Uint8Array) {
|
|
370
|
+
chunks.push(Buffer.from(chunk))
|
|
371
|
+
} else {
|
|
372
|
+
chunks.push(Buffer.from(String(chunk)))
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const buffer = Buffer.concat(chunks)
|
|
377
|
+
if (buffer.length === 0) {
|
|
378
|
+
throw new Error('xlport returned an empty Excel stream')
|
|
379
|
+
}
|
|
380
|
+
return buffer
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
throw new Error('Unexpected xlport export response type')
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function asAsyncIterable(value: unknown): AsyncIterable<unknown> {
|
|
387
|
+
if (!value || typeof value !== 'object') {
|
|
388
|
+
throw new TypeError('xlport stream value is not iterable')
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const asyncIterator = (value as { [Symbol.asyncIterator]?: unknown })[Symbol.asyncIterator]
|
|
392
|
+
if (typeof asyncIterator !== 'function') {
|
|
393
|
+
throw new TypeError('xlport stream value is not async iterable')
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return value as AsyncIterable<unknown>
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
type PreviewTotals = {
|
|
400
|
+
create: number
|
|
401
|
+
update: number
|
|
402
|
+
delete: number
|
|
403
|
+
unchanged: number
|
|
404
|
+
errors: number
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function createPreviewTotals(): PreviewTotals {
|
|
408
|
+
return { create: 0, update: 0, delete: 0, unchanged: 0, errors: 0 }
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function createModelSummary(): ImportPreviewModelSummary {
|
|
412
|
+
return {
|
|
413
|
+
create: 0,
|
|
414
|
+
update: 0,
|
|
415
|
+
delete: 0,
|
|
416
|
+
unchanged: 0,
|
|
417
|
+
errors: 0,
|
|
418
|
+
items: [],
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function ensureModelSummary(
|
|
423
|
+
models: Record<string, ImportPreviewModelSummary>,
|
|
424
|
+
modelName: string,
|
|
425
|
+
): ImportPreviewModelSummary {
|
|
426
|
+
const existing = models[modelName]
|
|
427
|
+
if (existing) {
|
|
428
|
+
return existing
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const created = createModelSummary()
|
|
432
|
+
models[modelName] = created
|
|
433
|
+
return created
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function toPreviewText(value: unknown): string {
|
|
437
|
+
if (value === null || value === undefined) {
|
|
438
|
+
return ''
|
|
439
|
+
}
|
|
440
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
441
|
+
return String(value) // NOSONAR
|
|
442
|
+
}
|
|
443
|
+
if (value instanceof Date) {
|
|
444
|
+
return value.toISOString()
|
|
445
|
+
}
|
|
446
|
+
if (typeof value === 'object') {
|
|
447
|
+
try {
|
|
448
|
+
return JSON.stringify(value)
|
|
449
|
+
} catch {
|
|
450
|
+
return '[object]'
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return String(value) // NOSONAR
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function getKeyFieldValue(modelName: string, inputRecord: Record<string, unknown>): string {
|
|
457
|
+
const keyFieldName = KEY_FIELD_MAP[modelName]
|
|
458
|
+
if (!keyFieldName) {
|
|
459
|
+
return ''
|
|
460
|
+
}
|
|
461
|
+
return toPreviewText(inputRecord[keyFieldName] ?? inputRecord[excelNameFor(keyFieldName)])
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function incrementPreviewTotals(
|
|
465
|
+
modelSummary: ImportPreviewModelSummary,
|
|
466
|
+
totals: PreviewTotals,
|
|
467
|
+
type: ImportPreviewItem['type'],
|
|
468
|
+
): void {
|
|
469
|
+
switch (type) {
|
|
470
|
+
case 'create':
|
|
471
|
+
modelSummary.create++
|
|
472
|
+
totals.create++
|
|
473
|
+
return
|
|
474
|
+
case 'update':
|
|
475
|
+
modelSummary.update++
|
|
476
|
+
totals.update++
|
|
477
|
+
return
|
|
478
|
+
case 'delete':
|
|
479
|
+
modelSummary.delete++
|
|
480
|
+
totals.delete++
|
|
481
|
+
return
|
|
482
|
+
case 'unchanged':
|
|
483
|
+
modelSummary.unchanged++
|
|
484
|
+
totals.unchanged++
|
|
485
|
+
return
|
|
486
|
+
case 'errors':
|
|
487
|
+
modelSummary.errors++
|
|
488
|
+
totals.errors++
|
|
489
|
+
return
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
type DeltaPreviewItemLike = {
|
|
494
|
+
type: ImportPreviewItem['type']
|
|
495
|
+
input: unknown
|
|
496
|
+
delta?: unknown
|
|
497
|
+
errors?: unknown[]
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function appendDeltaPreviewItem(
|
|
501
|
+
modelName: string,
|
|
502
|
+
item: DeltaPreviewItemLike,
|
|
503
|
+
deltaIndex: number,
|
|
504
|
+
includeDetailedUnchangedRecords: boolean,
|
|
505
|
+
modelSummary: ImportPreviewModelSummary,
|
|
506
|
+
totals: PreviewTotals,
|
|
507
|
+
): void {
|
|
508
|
+
incrementPreviewTotals(modelSummary, totals, item.type)
|
|
509
|
+
|
|
510
|
+
if (item.type === 'unchanged' && !includeDetailedUnchangedRecords) {
|
|
511
|
+
return
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const inputRecord = item.input as Record<string, unknown>
|
|
515
|
+
const previewItem: ImportPreviewItem = {
|
|
516
|
+
type: item.type,
|
|
517
|
+
deltaIndex,
|
|
518
|
+
id: toPreviewText(inputRecord.id),
|
|
519
|
+
keyFieldValue: getKeyFieldValue(modelName, inputRecord),
|
|
520
|
+
input: inputRecord,
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (item.type === 'update') {
|
|
524
|
+
previewItem.delta = item.delta as Record<string, { old: unknown; new: unknown }>
|
|
525
|
+
}
|
|
526
|
+
if (item.type === 'errors' && 'errors' in item) {
|
|
527
|
+
previewItem.errors = item.errors as unknown[]
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
modelSummary.items.push(previewItem)
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function appendParseErrorItem(
|
|
534
|
+
parseError: RecordParseError,
|
|
535
|
+
models: Record<string, ImportPreviewModelSummary>,
|
|
536
|
+
totals: PreviewTotals,
|
|
537
|
+
): void {
|
|
538
|
+
const modelSummary = ensureModelSummary(models, parseError.model)
|
|
539
|
+
incrementPreviewTotals(modelSummary, totals, 'errors')
|
|
540
|
+
modelSummary.items.push({
|
|
541
|
+
type: 'errors',
|
|
542
|
+
id: '',
|
|
543
|
+
keyFieldValue: getKeyFieldValue(parseError.model, parseError.raw),
|
|
544
|
+
input: parseError.raw,
|
|
545
|
+
errors: parseError.issues.map((issue) => ({ error: 'parse-error', message: issue })),
|
|
546
|
+
})
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function buildPreviewResponse(
|
|
550
|
+
previewSessionId: string,
|
|
551
|
+
filename: string,
|
|
552
|
+
delta: Delta,
|
|
553
|
+
parseErrors: RecordParseError[] = [],
|
|
554
|
+
includeDetailedUnchangedRecords = false,
|
|
555
|
+
): ImportPreviewResponse {
|
|
556
|
+
const models: Record<string, ImportPreviewModelSummary> = {}
|
|
557
|
+
const totals = createPreviewTotals()
|
|
558
|
+
|
|
559
|
+
for (const [modelName, items] of Object.entries(delta)) {
|
|
560
|
+
if (!items?.length) {
|
|
561
|
+
continue
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const modelSummary = ensureModelSummary(models, modelName)
|
|
565
|
+
for (const [deltaIndex, item] of items.entries()) {
|
|
566
|
+
appendDeltaPreviewItem(
|
|
567
|
+
modelName,
|
|
568
|
+
item as DeltaPreviewItemLike,
|
|
569
|
+
deltaIndex,
|
|
570
|
+
includeDetailedUnchangedRecords,
|
|
571
|
+
modelSummary,
|
|
572
|
+
totals,
|
|
573
|
+
)
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Inject per-record parse errors as 'errors' type items in the corresponding model
|
|
578
|
+
for (const parseError of parseErrors) {
|
|
579
|
+
appendParseErrorItem(parseError, models, totals)
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
return {
|
|
583
|
+
previewSessionId,
|
|
584
|
+
filename,
|
|
585
|
+
summary: {
|
|
586
|
+
create: totals.create,
|
|
587
|
+
update: totals.update,
|
|
588
|
+
delete: totals.delete,
|
|
589
|
+
unchanged: totals.unchanged,
|
|
590
|
+
errors: totals.errors,
|
|
591
|
+
},
|
|
592
|
+
models,
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Parse error raw data uses Excel column names (PascalCase/space-separated),
|
|
598
|
+
* but KEY_FIELD_MAP uses camelCase field names. This helper converts a camelCase
|
|
599
|
+
* field name to a likely Excel column name for lookup in raw data.
|
|
600
|
+
*/
|
|
601
|
+
function excelNameFor(fieldName: string): string {
|
|
602
|
+
// Convert camelCase to Title Case with spaces (e.g., 'blogTitle' -> 'Blog Title', 'slug' -> 'Slug')
|
|
603
|
+
return fieldName
|
|
604
|
+
.replaceAll(/[A-Z]/g, ' $&')
|
|
605
|
+
.replace(/^./, s => s.toUpperCase())
|
|
606
|
+
.trim()
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function deltaHasDeletes(delta: Delta): boolean {
|
|
610
|
+
for (const items of Object.values(delta)) {
|
|
611
|
+
if (items?.some((item: { type: string }) => item.type === 'delete')) {
|
|
612
|
+
return true
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
return false
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function deltaHasExecutableChanges(delta: Delta): boolean {
|
|
619
|
+
for (const items of Object.values(delta)) {
|
|
620
|
+
if (!items?.length) {
|
|
621
|
+
continue
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
for (const item of items) {
|
|
625
|
+
if (item.type === 'create' || item.type === 'update' || item.type === 'delete') {
|
|
626
|
+
return true
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return false
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function filterDeltaBySelectedIndices(delta: Delta, selectedDeltaIndicesByModel: Record<string, number[]>): Delta {
|
|
634
|
+
const hasSelectionMap = Object.keys(selectedDeltaIndicesByModel).length > 0
|
|
635
|
+
if (!hasSelectionMap) {
|
|
636
|
+
return delta
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const filteredEntries = Object.entries(delta).map(([modelName, items]) => {
|
|
640
|
+
if (!items?.length) {
|
|
641
|
+
return [modelName, items]
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const selectedIndices = selectedDeltaIndicesByModel[modelName] ?? []
|
|
645
|
+
const selectedSet = new Set(selectedIndices)
|
|
646
|
+
const filteredItems = items.filter((item, index) => {
|
|
647
|
+
if (item.type !== 'create' && item.type !== 'update' && item.type !== 'delete') {
|
|
648
|
+
return false
|
|
649
|
+
}
|
|
650
|
+
return selectedSet.has(index)
|
|
651
|
+
})
|
|
652
|
+
return [modelName, filteredItems]
|
|
653
|
+
})
|
|
654
|
+
|
|
655
|
+
return Object.fromEntries(filteredEntries) as Delta
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function parseXlPortExportBuffer(buffer: Buffer): Buffer {
|
|
659
|
+
if (buffer.length >= 2 && buffer[0] === 0x50 && buffer[1] === 0x4b) {
|
|
660
|
+
return buffer
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const text = buffer.toString('utf8').trim()
|
|
664
|
+
if (text.startsWith('{')) {
|
|
665
|
+
try {
|
|
666
|
+
const parsed = JSON.parse(text) as { message?: string; status?: string }
|
|
667
|
+
throw new Error(\`xlport returned JSON error (\${parsed.status ?? 'unknown'}): \${parsed.message ?? text}\`)
|
|
668
|
+
} catch (error) {
|
|
669
|
+
if (error instanceof SyntaxError) {
|
|
670
|
+
throw new Error(\`xlport returned non-xlsx payload: \${text.slice(0, 300)}\`)
|
|
671
|
+
}
|
|
672
|
+
throw error
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
throw new Error(\`xlport returned non-xlsx payload (\${buffer.length} bytes)\`)
|
|
677
|
+
}
|
|
678
|
+
`;
|
|
679
|
+
}
|
|
680
|
+
//# sourceMappingURL=excel-io-service.generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"excel-io-service.generator.js","sourceRoot":"","sources":["../../../src/backend-excel-io/generators/excel-io-service.generator.ts"],"names":[],"mappings":";;AAUA,wDA8qBC;AA9qBD,SAAgB,sBAAsB,CAAC,OAA4B;IACjE,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;IAExF,MAAM,kBAAkB,GAAG,MAAM;SAC9B,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;QAClB,KAAK,KAAK,CAAC,WAAW,CAAC,SAAS,MAAM,KAAK,CAAC,QAAQ,CAAC,IAAI,IAAI;QAC7D,KAAK,KAAK,CAAC,WAAW,CAAC,eAAe,MAAM,KAAK,CAAC,QAAQ,CAAC,IAAI,IAAI;KACpE,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,WAAW,CAAC,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAE1F,MAAM,kBAAkB,GAAG,MAAM;SAC9B,GAAG,CACF,CAAC,KAAK,EAAE,EAAE,CACR,SAAS,KAAK,CAAC,WAAW,CAAC,eAAe,4BAA4B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,iBAAiB,CACzH;SACA,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,UAAU,GAAG,MAAM;SACtB,GAAG,CACF,CAAC,KAAK,EAAE,EAAE,CAAC;cACH,KAAK,CAAC,WAAW,CAAC,SAAS;0BACf,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI;uBAClC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI;+CACL,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY;;yBAErD,KAAK,CAAC,WAAW,CAAC,eAAe;wBAClC,gBAAgB,IAAI,KAAK,CAAC,WAAW,CAAC,eAAe;2BAClD,KAAK,CAAC,WAAW,CAAC,SAAS;;QAE9C,CACH;SACA,IAAI,CAAC,EAAE,CAAC,CAAA;IAEX,MAAM,iBAAiB,GAAG,MAAM;SAC7B,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,QAAQ,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC;SAC5F,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,OAAO,MAAM,CAAC;;;;;;;;;;EAUd,iBAAiB;;;;;;;;;;;;IAYf,UAAU;;;;;;;;;;EAUZ,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuFlB,kBAAkB;;;uDAGmC,gBAAgB;;;;;;;;;;;;;;wCAc/B,UAAU;;sBAE5B,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4f/B,CAAA;AACD,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.backendExcelIoGeneratorId = exports.backendExcelIoGenerator = void 0;
|
|
18
|
+
__exportStar(require("./excel-io.generator"), exports);
|
|
19
|
+
var excel_io_generator_1 = require("./excel-io.generator");
|
|
20
|
+
Object.defineProperty(exports, "backendExcelIoGenerator", { enumerable: true, get: function () { return excel_io_generator_1.generator; } });
|
|
21
|
+
Object.defineProperty(exports, "backendExcelIoGeneratorId", { enumerable: true, get: function () { return excel_io_generator_1.generatorId; } });
|
|
22
|
+
//# sourceMappingURL=index.js.map
|