@open-mercato/cli 0.10.1-develop.7990.1.82d0a183c5 → 0.10.1-develop.8004.1.a8a79ee43f
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/.turbo/turbo-build.log +1 -1
- package/build.mjs +1 -0
- package/dist/agentic/guides/module-facts.json +162 -162
- package/dist/agentic/guides/module-facts.v2.json +162 -162
- package/dist/agentic/guides/module-tool.md +21 -0
- package/dist/agentic/guides/modules/agent_orchestrator/index.md +1 -1
- package/dist/agentic/guides/modules/ai_assistant/index.md +1 -1
- package/dist/agentic/guides/modules/api_docs/index.md +1 -1
- package/dist/agentic/guides/modules/api_keys/index.md +1 -1
- package/dist/agentic/guides/modules/attachments/index.md +1 -1
- package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
- package/dist/agentic/guides/modules/auth/index.md +1 -1
- package/dist/agentic/guides/modules/availability/index.md +1 -1
- package/dist/agentic/guides/modules/business_rules/index.md +1 -1
- package/dist/agentic/guides/modules/carrier_inpost/index.md +1 -1
- package/dist/agentic/guides/modules/cart/index.md +1 -1
- package/dist/agentic/guides/modules/catalog/index.md +1 -1
- package/dist/agentic/guides/modules/channel_apns/index.md +1 -1
- package/dist/agentic/guides/modules/channel_discord/index.md +1 -1
- package/dist/agentic/guides/modules/channel_expo/index.md +1 -1
- package/dist/agentic/guides/modules/channel_fcm/index.md +1 -1
- package/dist/agentic/guides/modules/channel_gmail/index.md +1 -1
- package/dist/agentic/guides/modules/channel_imap/index.md +1 -1
- package/dist/agentic/guides/modules/channel_resend/index.md +1 -1
- package/dist/agentic/guides/modules/channel_ses/index.md +1 -1
- package/dist/agentic/guides/modules/channel_smtp/index.md +1 -1
- package/dist/agentic/guides/modules/checkout/index.md +1 -1
- package/dist/agentic/guides/modules/communication_channels/index.md +1 -1
- package/dist/agentic/guides/modules/configs/index.md +1 -1
- package/dist/agentic/guides/modules/content/index.md +1 -1
- package/dist/agentic/guides/modules/currencies/index.md +1 -1
- package/dist/agentic/guides/modules/customer_accounts/index.md +1 -1
- package/dist/agentic/guides/modules/customer_groups/index.md +1 -1
- package/dist/agentic/guides/modules/customers/index.md +1 -1
- package/dist/agentic/guides/modules/dashboards/index.md +1 -1
- package/dist/agentic/guides/modules/data_sync/index.md +1 -1
- package/dist/agentic/guides/modules/design_system/index.md +1 -1
- package/dist/agentic/guides/modules/devices/index.md +1 -1
- package/dist/agentic/guides/modules/dictionaries/index.md +1 -1
- package/dist/agentic/guides/modules/directory/index.md +1 -1
- package/dist/agentic/guides/modules/documents/index.md +1 -1
- package/dist/agentic/guides/modules/ecommerce/index.md +1 -1
- package/dist/agentic/guides/modules/entities/index.md +1 -1
- package/dist/agentic/guides/modules/eudr/index.md +1 -1
- package/dist/agentic/guides/modules/events/index.md +1 -1
- package/dist/agentic/guides/modules/feature_toggles/index.md +1 -1
- package/dist/agentic/guides/modules/forms/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_autopay/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_tpay/index.md +1 -1
- package/dist/agentic/guides/modules/generators/index.md +1 -1
- package/dist/agentic/guides/modules/inbox_ops/index.md +1 -1
- package/dist/agentic/guides/modules/integrations/index.md +1 -1
- package/dist/agentic/guides/modules/messages/index.md +1 -1
- package/dist/agentic/guides/modules/notifications/index.md +1 -1
- package/dist/agentic/guides/modules/onboarding/index.md +1 -1
- package/dist/agentic/guides/modules/payment_gateways/index.md +1 -1
- package/dist/agentic/guides/modules/perspectives/index.md +1 -1
- package/dist/agentic/guides/modules/phone_calls/index.md +1 -1
- package/dist/agentic/guides/modules/planner/index.md +1 -1
- package/dist/agentic/guides/modules/portal/index.md +1 -1
- package/dist/agentic/guides/modules/progress/index.md +1 -1
- package/dist/agentic/guides/modules/promotions/index.md +1 -1
- package/dist/agentic/guides/modules/push_notifications/index.md +1 -1
- package/dist/agentic/guides/modules/query_index/index.md +1 -1
- package/dist/agentic/guides/modules/record_locks/index.md +1 -1
- package/dist/agentic/guides/modules/resources/index.md +1 -1
- package/dist/agentic/guides/modules/sales/index.md +1 -1
- package/dist/agentic/guides/modules/scheduler/index.md +1 -1
- package/dist/agentic/guides/modules/search/index.md +1 -1
- package/dist/agentic/guides/modules/security/index.md +1 -1
- package/dist/agentic/guides/modules/seeds/index.md +1 -1
- package/dist/agentic/guides/modules/shipping_carriers/index.md +1 -1
- package/dist/agentic/guides/modules/sso/index.md +1 -1
- package/dist/agentic/guides/modules/staff/index.md +1 -1
- package/dist/agentic/guides/modules/storage_s3/index.md +1 -1
- package/dist/agentic/guides/modules/sync_akeneo/index.md +1 -1
- package/dist/agentic/guides/modules/sync_excel/index.md +1 -1
- package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
- package/dist/agentic/guides/modules/telemetry/index.md +1 -1
- package/dist/agentic/guides/modules/tillio/index.md +1 -1
- package/dist/agentic/guides/modules/translations/index.md +1 -1
- package/dist/agentic/guides/modules/warranty_claims/index.md +1 -1
- package/dist/agentic/guides/modules/webhooks/index.md +1 -1
- package/dist/agentic/guides/modules/wms/index.md +1 -1
- package/dist/agentic/guides/modules/workflows/index.md +1 -1
- package/dist/agentic/guides/reference-module-facts.json +1 -1
- package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +4 -0
- package/dist/agentic/guides/upstream/manifest.json +2 -2
- package/dist/agentic/shared/ai/harness/cases.json +23 -9
- package/dist/agentic/shared/ai/harness/source-link-inventory.json +90 -4
- package/dist/i18n/de.json +4 -0
- package/dist/i18n/en.json +4 -0
- package/dist/i18n/es.json +4 -0
- package/dist/i18n/ko.json +4 -0
- package/dist/i18n/pl.json +4 -0
- package/dist/lib/__fixtures__/module-search/built-in.json +235 -0
- package/dist/lib/__fixtures__/module-search/catalog-parity.json +4076 -0
- package/dist/lib/__fixtures__/module-search/modules-index.json +1980 -0
- package/dist/lib/__fixtures__/official-module-package/package.json +33 -0
- package/dist/lib/__integration__/TC-INT-007.spec.js +207 -19
- package/dist/lib/__integration__/TC-INT-007.spec.js.map +2 -2
- package/dist/lib/__integration__/TC-INT-009.spec.js +267 -0
- package/dist/lib/__integration__/TC-INT-009.spec.js.map +7 -0
- package/dist/lib/eject.js +25 -9
- package/dist/lib/eject.js.map +2 -2
- package/dist/lib/generators/entity-ids.js +2 -0
- package/dist/lib/generators/entity-ids.js.map +2 -2
- package/dist/lib/generators/module-di.js +2 -0
- package/dist/lib/generators/module-di.js.map +2 -2
- package/dist/lib/generators/module-entities.js +2 -0
- package/dist/lib/generators/module-entities.js.map +2 -2
- package/dist/lib/generators/module-package-sources.js +6 -4
- package/dist/lib/generators/module-package-sources.js.map +2 -2
- package/dist/lib/generators/module-registry.js +32 -86
- package/dist/lib/generators/module-registry.js.map +2 -2
- package/dist/lib/generators/openapi.js +2 -0
- package/dist/lib/generators/openapi.js.map +2 -2
- package/dist/lib/generators/source-exports.js +73 -0
- package/dist/lib/generators/source-exports.js.map +7 -0
- package/dist/lib/init-secrets.js +33 -1
- package/dist/lib/init-secrets.js.map +2 -2
- package/dist/lib/init-summary.js +44 -0
- package/dist/lib/init-summary.js.map +7 -0
- package/dist/lib/module-github.js +98 -0
- package/dist/lib/module-github.js.map +7 -0
- package/dist/lib/module-identity.js +131 -0
- package/dist/lib/module-identity.js.map +7 -0
- package/dist/lib/module-inspection.js +124 -0
- package/dist/lib/module-inspection.js.map +7 -0
- package/dist/lib/module-install.js +150 -21
- package/dist/lib/module-install.js.map +3 -3
- package/dist/lib/module-package.js +205 -15
- package/dist/lib/module-package.js.map +2 -2
- package/dist/lib/modules-config.js +69 -30
- package/dist/lib/modules-config.js.map +2 -2
- package/dist/lib/resolver.js +33 -34
- package/dist/lib/resolver.js.map +2 -2
- package/dist/mercato.js +17 -37
- package/dist/mercato.js.map +2 -2
- package/package.json +7 -7
- package/src/__tests__/mercato.test.ts +71 -4
- package/src/i18n/de.json +4 -0
- package/src/i18n/en.json +4 -0
- package/src/i18n/es.json +4 -0
- package/src/i18n/ko.json +4 -0
- package/src/i18n/pl.json +4 -0
- package/src/lib/__fixtures__/official-module-package/dist/modules/test_package/widgets/injection/test/widget.js +1 -0
- package/src/lib/__integration__/TC-INT-007.spec.ts +219 -19
- package/src/lib/__integration__/TC-INT-009.spec.ts +279 -0
- package/src/lib/__tests__/eject.test.ts +98 -4
- package/src/lib/__tests__/init-secrets.test.ts +85 -1
- package/src/lib/__tests__/init-summary.test.ts +50 -0
- package/src/lib/__tests__/module-github.test.ts +108 -0
- package/src/lib/__tests__/module-identity.test.ts +144 -0
- package/src/lib/__tests__/module-inspection.test.ts +195 -0
- package/src/lib/__tests__/module-install.test.ts +399 -10
- package/src/lib/__tests__/module-package.test.ts +154 -0
- package/src/lib/__tests__/modules-config.test.ts +55 -0
- package/src/lib/__tests__/resolver.packages.test.ts +29 -0
- package/src/lib/eject.ts +28 -10
- package/src/lib/generators/__tests__/module-id-collisions.test.ts +146 -0
- package/src/lib/generators/__tests__/module-package-sources.test.ts +19 -2
- package/src/lib/generators/__tests__/module-requirements.test.ts +56 -0
- package/src/lib/generators/entity-ids.ts +2 -0
- package/src/lib/generators/module-di.ts +2 -0
- package/src/lib/generators/module-entities.ts +2 -0
- package/src/lib/generators/module-package-sources.ts +7 -5
- package/src/lib/generators/module-registry.ts +33 -115
- package/src/lib/generators/openapi.ts +3 -0
- package/src/lib/generators/source-exports.ts +93 -0
- package/src/lib/init-secrets.ts +37 -0
- package/src/lib/init-summary.ts +54 -0
- package/src/lib/module-github.ts +109 -0
- package/src/lib/module-identity.ts +145 -0
- package/src/lib/module-inspection.ts +133 -0
- package/src/lib/module-install.ts +170 -22
- package/src/lib/module-package.ts +243 -16
- package/src/lib/modules-config.ts +80 -32
- package/src/lib/resolver.ts +35 -39
- package/src/mercato.ts +18 -44
- package/src/types/cross-spawn.d.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/cli",
|
|
3
|
-
"version": "0.10.1-develop.
|
|
3
|
+
"version": "0.10.1-develop.8004.1.a8a79ee43f",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -60,10 +60,10 @@
|
|
|
60
60
|
"@mikro-orm/decorators": "^7.1.14",
|
|
61
61
|
"@mikro-orm/migrations": "^7.1.14",
|
|
62
62
|
"@mikro-orm/postgresql": "^7.1.14",
|
|
63
|
-
"@open-mercato/events": "0.10.1-develop.
|
|
64
|
-
"@open-mercato/queue": "0.10.1-develop.
|
|
65
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
66
|
-
"@open-mercato/telemetry": "0.10.1-develop.
|
|
63
|
+
"@open-mercato/events": "0.10.1-develop.8004.1.a8a79ee43f",
|
|
64
|
+
"@open-mercato/queue": "0.10.1-develop.8004.1.a8a79ee43f",
|
|
65
|
+
"@open-mercato/shared": "0.10.1-develop.8004.1.a8a79ee43f",
|
|
66
|
+
"@open-mercato/telemetry": "0.10.1-develop.8004.1.a8a79ee43f",
|
|
67
67
|
"cross-spawn": "^7.0.6",
|
|
68
68
|
"minisearch": "7.2.0",
|
|
69
69
|
"pg": "8.23.0",
|
|
@@ -75,10 +75,10 @@
|
|
|
75
75
|
"typescript-js": "npm:typescript@6.0.3"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
78
|
+
"@open-mercato/shared": "0.10.1-develop.8004.1.a8a79ee43f"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
81
|
+
"@open-mercato/shared": "0.10.1-develop.8004.1.a8a79ee43f",
|
|
82
82
|
"@types/jest": "^30.0.0",
|
|
83
83
|
"jest": "^30.4.2",
|
|
84
84
|
"ts-jest": "^29.4.12"
|
|
@@ -1112,6 +1112,47 @@ describe('init command failure output', () => {
|
|
|
1112
1112
|
)
|
|
1113
1113
|
}
|
|
1114
1114
|
|
|
1115
|
+
it.each(['--reinstall', '--no-demo-users'])('rejects unsafe production init before side effects with %s', async (flag) => {
|
|
1116
|
+
const previousEnv = { ...process.env }
|
|
1117
|
+
jest.spyOn(console, 'error').mockImplementation()
|
|
1118
|
+
jest.spyOn(console, 'log').mockImplementation()
|
|
1119
|
+
const authSetup = jest.fn().mockResolvedValue(undefined)
|
|
1120
|
+
mockInitDependencies({ authSetup })
|
|
1121
|
+
process.env.NODE_ENV = 'production'
|
|
1122
|
+
process.env.OM_INIT_SUPERADMIN_PASSWORD = 'password'
|
|
1123
|
+
try {
|
|
1124
|
+
const mercato = await import('../mercato')
|
|
1125
|
+
expect(await mercato.run(['node', 'mercato', 'init', flag, '--skip-password-policy'])).toBe(1)
|
|
1126
|
+
const { Client } = await import('pg')
|
|
1127
|
+
const { generateEntityIds } = await import('../lib/generators')
|
|
1128
|
+
const { dbMigrate } = await import('../lib/db')
|
|
1129
|
+
expect(Client).not.toHaveBeenCalled()
|
|
1130
|
+
expect(generateEntityIds).not.toHaveBeenCalled()
|
|
1131
|
+
expect(dbMigrate).not.toHaveBeenCalled()
|
|
1132
|
+
expect(authSetup).not.toHaveBeenCalled()
|
|
1133
|
+
} finally {
|
|
1134
|
+
process.env = previousEnv
|
|
1135
|
+
}
|
|
1136
|
+
})
|
|
1137
|
+
|
|
1138
|
+
it('passes valid production credentials to auth setup without bypassing policy', async () => {
|
|
1139
|
+
const previousEnv = { ...process.env }
|
|
1140
|
+
jest.spyOn(console, 'error').mockImplementation()
|
|
1141
|
+
jest.spyOn(console, 'log').mockImplementation()
|
|
1142
|
+
const authSetup = jest.fn().mockResolvedValue(undefined)
|
|
1143
|
+
mockInitDependencies({ authSetup })
|
|
1144
|
+
process.env.NODE_ENV = 'production'
|
|
1145
|
+
process.env.OM_INIT_SUPERADMIN_PASSWORD = 'Unique-Owner-937!'
|
|
1146
|
+
try {
|
|
1147
|
+
const mercato = await import('../mercato')
|
|
1148
|
+
expect(await mercato.run(['node', 'mercato', 'init', '--no-demo-users', '--skip-password-policy'])).toBe(0)
|
|
1149
|
+
expect(authSetup).toHaveBeenCalledWith(expect.arrayContaining(['--password', 'Unique-Owner-937!']))
|
|
1150
|
+
expect(authSetup.mock.calls[0][0]).not.toContain('--skip-password-policy')
|
|
1151
|
+
} finally {
|
|
1152
|
+
process.env = previousEnv
|
|
1153
|
+
}
|
|
1154
|
+
})
|
|
1155
|
+
|
|
1115
1156
|
it('passes --include-demo-users to auth setup by default', async () => {
|
|
1116
1157
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation()
|
|
1117
1158
|
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation()
|
|
@@ -1125,8 +1166,8 @@ describe('init command failure output', () => {
|
|
|
1125
1166
|
expect(authSetup).toHaveBeenCalledWith(expect.arrayContaining(['--include-demo-users']))
|
|
1126
1167
|
expect(consoleLogSpy).toHaveBeenCalledWith('👤 Demo accounts (admin@/employee@): enabled')
|
|
1127
1168
|
const loggedLines = consoleLogSpy.mock.calls.map((args) => args.join(' '))
|
|
1128
|
-
expect(loggedLines.some((line) => line.includes('🧰 Admin
|
|
1129
|
-
expect(loggedLines.some((line) => line.includes('👷 Employee
|
|
1169
|
+
expect(loggedLines.some((line) => line.includes('🧰 Admin'))).toBe(true)
|
|
1170
|
+
expect(loggedLines.some((line) => line.includes('👷 Employee'))).toBe(true)
|
|
1130
1171
|
|
|
1131
1172
|
consoleErrorSpy.mockRestore()
|
|
1132
1173
|
consoleLogSpy.mockRestore()
|
|
@@ -1148,8 +1189,8 @@ describe('init command failure output', () => {
|
|
|
1148
1189
|
'👤 Demo accounts (admin@/employee@): skipped (--no-demo-users)',
|
|
1149
1190
|
)
|
|
1150
1191
|
const loggedLines = consoleLogSpy.mock.calls.map((args) => args.join(' '))
|
|
1151
|
-
expect(loggedLines.some((line) => line.includes('🧰 Admin
|
|
1152
|
-
expect(loggedLines.some((line) => line.includes('👷 Employee
|
|
1192
|
+
expect(loggedLines.some((line) => line.includes('🧰 Admin'))).toBe(false)
|
|
1193
|
+
expect(loggedLines.some((line) => line.includes('👷 Employee'))).toBe(false)
|
|
1153
1194
|
|
|
1154
1195
|
consoleErrorSpy.mockRestore()
|
|
1155
1196
|
consoleLogSpy.mockRestore()
|
|
@@ -1336,6 +1377,22 @@ describe('init on an already initialized database', () => {
|
|
|
1336
1377
|
consoleLogSpy.mockRestore()
|
|
1337
1378
|
})
|
|
1338
1379
|
|
|
1380
|
+
it('skips an initialized production database without bootstrap credentials', async () => {
|
|
1381
|
+
const previousEnv = { ...process.env }
|
|
1382
|
+
jest.spyOn(console, 'error').mockImplementation()
|
|
1383
|
+
jest.spyOn(console, 'log').mockImplementation()
|
|
1384
|
+
const { generateEntityIds } = mockSeededDatabase('3')
|
|
1385
|
+
process.env.NODE_ENV = 'production'
|
|
1386
|
+
delete process.env.OM_INIT_SUPERADMIN_PASSWORD
|
|
1387
|
+
try {
|
|
1388
|
+
const mercato = await import('../mercato')
|
|
1389
|
+
expect(await mercato.run(['node', 'mercato', 'init', '--skip-if-initialized'])).toBe(0)
|
|
1390
|
+
expect(generateEntityIds).not.toHaveBeenCalled()
|
|
1391
|
+
} finally {
|
|
1392
|
+
process.env = previousEnv
|
|
1393
|
+
}
|
|
1394
|
+
})
|
|
1395
|
+
|
|
1339
1396
|
it('keeps the data, names the target database and exits 0 with --skip-if-initialized', async () => {
|
|
1340
1397
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation()
|
|
1341
1398
|
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation()
|
|
@@ -1735,6 +1792,11 @@ describe('server dev managed process exits', () => {
|
|
|
1735
1792
|
expect(exitCode).toBe(0)
|
|
1736
1793
|
expect(consoleErrorSpy).not.toHaveBeenCalled()
|
|
1737
1794
|
expect(consoleLogSpy).toHaveBeenCalledWith('[server] Skipping scheduler auto-start — module not enabled')
|
|
1795
|
+
const { spawn } = await import('child_process')
|
|
1796
|
+
const nextSpawn = (spawn as jest.Mock).mock.calls.find((call) =>
|
|
1797
|
+
pathIncludes(call[1]?.[0] ?? '', 'next/dist/bin/next') && call[1]?.includes('dev'),
|
|
1798
|
+
)
|
|
1799
|
+
expect(nextSpawn?.[2]?.env.NODE_ENV).toBe('development')
|
|
1738
1800
|
|
|
1739
1801
|
consoleErrorSpy.mockRestore()
|
|
1740
1802
|
consoleLogSpy.mockRestore()
|
|
@@ -2477,6 +2539,11 @@ describe('server start managed process exits', () => {
|
|
|
2477
2539
|
expect(exitCode).toBe(0)
|
|
2478
2540
|
expect(consoleErrorSpy).not.toHaveBeenCalled()
|
|
2479
2541
|
expect(consoleLogSpy).toHaveBeenCalledWith('[server] Skipping scheduler auto-start — module not enabled')
|
|
2542
|
+
const { spawn } = await import('child_process')
|
|
2543
|
+
const nextSpawn = (spawn as jest.Mock).mock.calls.find((call) =>
|
|
2544
|
+
pathIncludes(call[1]?.[0] ?? '', 'next/dist/bin/next') && call[1]?.includes('start'),
|
|
2545
|
+
)
|
|
2546
|
+
expect(nextSpawn?.[2]?.env.NODE_ENV).toBe('production')
|
|
2480
2547
|
|
|
2481
2548
|
consoleErrorSpy.mockRestore()
|
|
2482
2549
|
consoleLogSpy.mockRestore()
|
package/src/i18n/de.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli.init.errors.superadminPasswordPolicy": "Das Superadmin-Passwort für die Produktion erfüllt die konfigurierte Passwortrichtlinie nicht. Flags zum Umgehen der Passwortrichtlinie gelten nur außerhalb der Produktion.",
|
|
3
|
+
"cli.init.errors.superadminPasswordRequired": "Die Produktionsinitialisierung erfordert ein eindeutiges Superadmin-Passwort über OM_INIT_SUPERADMIN_PASSWORD oder --password; integrierte Platzhalter sind nicht zulässig."
|
|
4
|
+
}
|
package/src/i18n/en.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli.init.errors.superadminPasswordPolicy": "Production superadmin password does not meet the configured password policy. Password-policy bypass flags apply only outside production.",
|
|
3
|
+
"cli.init.errors.superadminPasswordRequired": "Production initialization requires a unique superadmin password via OM_INIT_SUPERADMIN_PASSWORD or --password; built-in placeholders are not allowed."
|
|
4
|
+
}
|
package/src/i18n/es.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli.init.errors.superadminPasswordPolicy": "La contraseña de superadministrador en producción no cumple la política de contraseñas configurada. Las opciones para omitir la política solo se aplican fuera de producción.",
|
|
3
|
+
"cli.init.errors.superadminPasswordRequired": "La inicialización en producción requiere una contraseña única de superadministrador mediante OM_INIT_SUPERADMIN_PASSWORD o --password; no se permiten los valores predeterminados de ejemplo."
|
|
4
|
+
}
|
package/src/i18n/ko.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli.init.errors.superadminPasswordPolicy": "프로덕션 슈퍼관리자 비밀번호가 설정된 비밀번호 정책을 충족하지 않습니다. 비밀번호 정책 우회 플래그는 프로덕션 외 환경에서만 적용됩니다.",
|
|
3
|
+
"cli.init.errors.superadminPasswordRequired": "프로덕션 초기화에는 OM_INIT_SUPERADMIN_PASSWORD 또는 --password로 지정한 고유한 슈퍼관리자 비밀번호가 필요합니다. 기본 예시 비밀번호는 사용할 수 없습니다."
|
|
4
|
+
}
|
package/src/i18n/pl.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli.init.errors.superadminPasswordPolicy": "Hasło superadministratora w środowisku produkcyjnym nie spełnia skonfigurowanej polityki haseł. Flagi pomijające politykę haseł działają tylko poza środowiskiem produkcyjnym.",
|
|
3
|
+
"cli.init.errors.superadminPasswordRequired": "Inicjalizacja produkcyjna wymaga unikalnego hasła superadministratora podanego przez OM_INIT_SUPERADMIN_PASSWORD lub --password; wbudowane hasła przykładowe są niedozwolone."
|
|
4
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function widget() {}
|
|
@@ -22,21 +22,33 @@ function yarnBinary(): string {
|
|
|
22
22
|
return process.platform === 'win32' ? 'yarn.cmd' : 'yarn'
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function runCommand(command: string, args: string[], cwd: string): string {
|
|
25
|
+
function runCommand(command: string, args: string[], cwd: string, environment: NodeJS.ProcessEnv = {}): string {
|
|
26
26
|
const yarnCacheFolder = path.join(cwd, '.yarn', 'cache')
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
27
|
+
try {
|
|
28
|
+
return execFileSync(command, args, {
|
|
29
|
+
cwd,
|
|
30
|
+
encoding: 'utf8',
|
|
31
|
+
env: {
|
|
32
|
+
...process.env,
|
|
33
|
+
FORCE_COLOR: '0',
|
|
34
|
+
NODE_NO_WARNINGS: '1',
|
|
35
|
+
YARN_CACHE_FOLDER: yarnCacheFolder,
|
|
36
|
+
YARN_ENABLE_GLOBAL_CACHE: '0',
|
|
37
|
+
YARN_ENABLE_IMMUTABLE_INSTALLS: '0',
|
|
38
|
+
YARN_NODE_LINKER: 'node-modules',
|
|
39
|
+
...environment,
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (!error || typeof error !== 'object') throw error
|
|
44
|
+
const errorRecord = error as Record<string, unknown>
|
|
45
|
+
const outputs = ['stdout', 'stderr'].flatMap((streamName) => {
|
|
46
|
+
const output = errorRecord[streamName]
|
|
47
|
+
return typeof output === 'string' || Buffer.isBuffer(output) ? [output.toString().trim()] : []
|
|
48
|
+
}).filter(Boolean)
|
|
49
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
50
|
+
throw new Error(outputs.length > 0 ? `${message}\n${outputs.join('\n')}` : message)
|
|
51
|
+
}
|
|
40
52
|
}
|
|
41
53
|
|
|
42
54
|
function runMercato(args: string[], cwd: string): string {
|
|
@@ -77,6 +89,7 @@ const commentedModulesSource = [
|
|
|
77
89
|
].join('\n')
|
|
78
90
|
|
|
79
91
|
function createMonorepoFixture(rootDir: string): string {
|
|
92
|
+
writeFile(path.join(rootDir, 'yarn.lock'), '')
|
|
80
93
|
writeFile(
|
|
81
94
|
path.join(rootDir, 'package.json'),
|
|
82
95
|
JSON.stringify(
|
|
@@ -113,6 +126,7 @@ function createMonorepoFixture(rootDir: string): string {
|
|
|
113
126
|
}
|
|
114
127
|
|
|
115
128
|
function createStandaloneFixture(rootDir: string): string {
|
|
129
|
+
writeFile(path.join(rootDir, 'yarn.lock'), '')
|
|
116
130
|
writeFile(
|
|
117
131
|
path.join(rootDir, '.yarnrc.yml'),
|
|
118
132
|
['nodeLinker: node-modules', 'enableGlobalCache: false', 'cacheFolder: ./.yarn/cache', ''].join('\n'),
|
|
@@ -196,14 +210,196 @@ test.describe('TC-INT-007: CLI official module install and eject flows', () => {
|
|
|
196
210
|
const modulesSource = readFile(path.join(appDir, 'src', 'modules.ts'))
|
|
197
211
|
const cssSource = readFile(path.join(appDir, '.mercato', 'generated', 'module-package-sources.css'))
|
|
198
212
|
|
|
199
|
-
expect(modulesSource).
|
|
213
|
+
expect(modulesSource).toMatch(/id: 'test_package', from: '@app'/)
|
|
214
|
+
expect(modulesSource).not.toContain('locator:')
|
|
215
|
+
expect(modulesSource).toContain("packageName: '@open-mercato/test-package'")
|
|
200
216
|
expect(fs.existsSync(path.join(appDir, 'src', 'modules', 'test_package', 'index.ts'))).toBe(true)
|
|
217
|
+
expect(readFile(path.join(appDir, 'src', 'modules', 'test_package', 'index.ts'))).toBe(readFile(path.join(fixtureModulePackage, 'src', 'modules', 'test_package', 'index.ts')))
|
|
201
218
|
expect(cssSource).toBe('')
|
|
202
219
|
} finally {
|
|
203
220
|
fs.rmSync(appDir, { recursive: true, force: true })
|
|
204
221
|
}
|
|
205
222
|
})
|
|
206
223
|
|
|
224
|
+
test('module add reports dependencies that must be enabled before registration', () => {
|
|
225
|
+
const appDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mercato-cli-standalone-dependencies-'))
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
createStandaloneFixture(appDir)
|
|
229
|
+
const packageDir = path.join(appDir, 'fixture-module-package')
|
|
230
|
+
fs.cpSync(fixtureModulePackage, packageDir, { recursive: true })
|
|
231
|
+
writeFile(
|
|
232
|
+
path.join(packageDir, 'src', 'modules', 'test_package', 'index.ts'),
|
|
233
|
+
"export const metadata = { id: 'test_package', requires: ['customers'], ejectable: true }\n",
|
|
234
|
+
)
|
|
235
|
+
runCommand(yarnBinary(), ['install'], appDir)
|
|
236
|
+
|
|
237
|
+
expect(() => runMercato(
|
|
238
|
+
['module', 'add', '@open-mercato/test-package@file:./fixture-module-package'],
|
|
239
|
+
appDir,
|
|
240
|
+
)).toThrow(/requires module\(s\) that are not enabled: customers/)
|
|
241
|
+
|
|
242
|
+
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).toBe('export const enabledModules = []\n')
|
|
243
|
+
} finally {
|
|
244
|
+
fs.rmSync(appDir, { recursive: true, force: true })
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test('module add resolves canonical requirements from retained ejected origins', () => {
|
|
249
|
+
const appDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mercato-cli-canonical-requires-'))
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
createStandaloneFixture(appDir)
|
|
253
|
+
const packageDir = path.join(appDir, 'fixture-module-package')
|
|
254
|
+
fs.cpSync(fixtureModulePackage, packageDir, { recursive: true })
|
|
255
|
+
const requirements = [
|
|
256
|
+
'@pkarw/marketing_consents@preview',
|
|
257
|
+
'npm:@pkarw/marketing_consents@1.0.0',
|
|
258
|
+
'github:pkarw/marketing-consents',
|
|
259
|
+
'npm:consent-helper@next',
|
|
260
|
+
'github:pkarw/consent-tools#main',
|
|
261
|
+
'pkarw/consent-tools',
|
|
262
|
+
'https://github.com/pkarw/consent-tools.git#release',
|
|
263
|
+
'ssh://git@github.com/pkarw/consent-tools.git#main',
|
|
264
|
+
'git@github.com:pkarw/consent-tools.git',
|
|
265
|
+
'github:pkarw/installed-tools',
|
|
266
|
+
'npm:@open-mercato/core@preview',
|
|
267
|
+
]
|
|
268
|
+
writeFile(
|
|
269
|
+
path.join(packageDir, 'src', 'modules', 'test_package', 'index.ts'),
|
|
270
|
+
`export const metadata = { id: 'test_package', requires: ${JSON.stringify(requirements)}, ejectable: true }\n`,
|
|
271
|
+
)
|
|
272
|
+
const entries = [
|
|
273
|
+
{ id: 'marketing_consents', from: '@app', locator: 'github:pkarw/marketing-consents', packageName: '@pkarw/marketing_consents' },
|
|
274
|
+
{ id: 'npm_consents', from: '@app', locator: 'npm:@pkarw/marketing_consents', packageName: '@pkarw/marketing_consents' },
|
|
275
|
+
{ id: 'npm_core', from: '@app', locator: 'npm:@open-mercato/core', packageName: '@open-mercato/core' },
|
|
276
|
+
{ id: 'consent_helper', from: '@app', locator: 'npm:consent-helper' },
|
|
277
|
+
{ id: 'consent_tools', from: '@app', locator: 'github:pkarw/consent-tools' },
|
|
278
|
+
]
|
|
279
|
+
for (const entry of entries) {
|
|
280
|
+
writeFile(
|
|
281
|
+
path.join(appDir, 'src', 'modules', entry.id, 'index.ts'),
|
|
282
|
+
`export const metadata = { id: '${entry.id}' }\n`,
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
const corePackageDir = path.join(appDir, 'vendor', 'core')
|
|
286
|
+
writeFile(
|
|
287
|
+
path.join(corePackageDir, 'package.json'),
|
|
288
|
+
JSON.stringify({ name: '@open-mercato/core', version: '0.4.7', type: 'module', repository: { type: 'git', url: 'https://github.com/pkarw/installed-tools.git' } }),
|
|
289
|
+
)
|
|
290
|
+
writeFile(
|
|
291
|
+
path.join(corePackageDir, 'src', 'modules', 'repository_tools', 'index.ts'),
|
|
292
|
+
"export const metadata = { id: 'repository_tools' }\n",
|
|
293
|
+
)
|
|
294
|
+
writeFile(
|
|
295
|
+
path.join(corePackageDir, 'dist', 'modules', 'repository_tools', 'index.js'),
|
|
296
|
+
"export const metadata = { id: 'repository_tools' }\n",
|
|
297
|
+
)
|
|
298
|
+
const enabledEntries = [...entries, { id: 'repository_tools', from: '@open-mercato/core', locator: 'github:pkarw/installed-tools' }]
|
|
299
|
+
writeFile(path.join(appDir, 'src', 'modules.ts'), `export const enabledModules = ${JSON.stringify(enabledEntries)}\n`)
|
|
300
|
+
runCommand(yarnBinary(), ['install'], appDir)
|
|
301
|
+
|
|
302
|
+
runMercato(['module', 'add', 'npm:@open-mercato/test-package@file:./fixture-module-package'], appDir)
|
|
303
|
+
|
|
304
|
+
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).toContain("id: 'test_package'")
|
|
305
|
+
} finally {
|
|
306
|
+
fs.rmSync(appDir, { recursive: true, force: true })
|
|
307
|
+
}
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
test('same-name modules from different origins do not satisfy canonical requirements', () => {
|
|
311
|
+
const appDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mercato-cli-wrong-origin-'))
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
createStandaloneFixture(appDir)
|
|
315
|
+
const packageDir = path.join(appDir, 'fixture-module-package')
|
|
316
|
+
fs.cpSync(fixtureModulePackage, packageDir, { recursive: true })
|
|
317
|
+
writeFile(
|
|
318
|
+
path.join(packageDir, 'src', 'modules', 'test_package', 'index.ts'),
|
|
319
|
+
"export const metadata = { id: 'test_package', requires: ['@pkarw/marketing_consents', 'github:pkarw/consent-tools'], ejectable: true }\n",
|
|
320
|
+
)
|
|
321
|
+
const modulesSource = `export const enabledModules = [
|
|
322
|
+
{ id: 'marketing_consents', from: '@app', locator: 'npm:@open-mercato/marketing_consents' },
|
|
323
|
+
{ id: 'consent_tools', from: '@app', locator: 'github:another-owner/consent-tools' },
|
|
324
|
+
]\n`
|
|
325
|
+
writeFile(path.join(appDir, 'src', 'modules.ts'), modulesSource)
|
|
326
|
+
runCommand(yarnBinary(), ['install'], appDir)
|
|
327
|
+
|
|
328
|
+
expect(() => runMercato(
|
|
329
|
+
['module', 'add', '@open-mercato/test-package@file:./fixture-module-package', '--eject'],
|
|
330
|
+
appDir,
|
|
331
|
+
)).toThrow(/@pkarw\/marketing_consents[\s\S]*github:pkarw\/consent-tools[\s\S]*npx create-mercato-module install/)
|
|
332
|
+
|
|
333
|
+
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).toBe(modulesSource)
|
|
334
|
+
expect(fs.existsSync(path.join(appDir, 'src', 'modules', 'test_package'))).toBe(false)
|
|
335
|
+
} finally {
|
|
336
|
+
fs.rmSync(appDir, { recursive: true, force: true })
|
|
337
|
+
}
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
test('module add rejects a same-ID source collision before ejecting', () => {
|
|
341
|
+
const appDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mercato-cli-module-collision-'))
|
|
342
|
+
|
|
343
|
+
try {
|
|
344
|
+
createStandaloneFixture(appDir)
|
|
345
|
+
const modulesSource = "export const enabledModules = [{ id: 'test_package', from: '@app', locator: 'github:another-owner/test-package' }]\n"
|
|
346
|
+
const localSource = "export const metadata = { id: 'test_package', locator: 'github:another-owner/test-package' }\n"
|
|
347
|
+
writeFile(path.join(appDir, 'src', 'modules.ts'), modulesSource)
|
|
348
|
+
writeFile(path.join(appDir, 'src', 'modules', 'test_package', 'index.ts'), localSource)
|
|
349
|
+
runCommand(yarnBinary(), ['install'], appDir)
|
|
350
|
+
|
|
351
|
+
expect(() => runMercato(
|
|
352
|
+
['module', 'add', `@open-mercato/test-package@file:${fixtureModulePackage}`, '--eject'],
|
|
353
|
+
appDir,
|
|
354
|
+
)).toThrow(/(?=[\s\S]*test_package)(?=[\s\S]*(?:collision|already|conflict))(?=[\s\S]*another-owner)(?=[\s\S]*@open-mercato\/test-package)/i)
|
|
355
|
+
|
|
356
|
+
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).toBe(modulesSource)
|
|
357
|
+
expect(readFile(path.join(appDir, 'src', 'modules', 'test_package', 'index.ts'))).toBe(localSource)
|
|
358
|
+
} finally {
|
|
359
|
+
fs.rmSync(appDir, { recursive: true, force: true })
|
|
360
|
+
}
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
test('GitHub locators detect a runtime ID collision before invoking Yarn', () => {
|
|
364
|
+
const appDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mercato-cli-github-collision-'))
|
|
365
|
+
try {
|
|
366
|
+
createStandaloneFixture(appDir)
|
|
367
|
+
const modulesSource = "export const enabledModules = [{ id: 'test_package', from: '@app', locator: 'github:other/test-package' }]\n"
|
|
368
|
+
writeFile(path.join(appDir, 'src', 'modules.ts'), modulesSource)
|
|
369
|
+
const originalManifest = readFile(path.join(appDir, 'package.json'))
|
|
370
|
+
const executableDir = path.join(appDir, 'bin')
|
|
371
|
+
const packageManifest = Buffer.from(JSON.stringify({ name: '@pkarw/test-package' })).toString('base64')
|
|
372
|
+
const ghPath = path.join(executableDir, 'gh')
|
|
373
|
+
writeFile(ghPath, `#!/usr/bin/env node
|
|
374
|
+
const endpoint = process.argv[3]
|
|
375
|
+
const sha = 'a'.repeat(40)
|
|
376
|
+
const value = endpoint === 'user' ? { login: 'pkarw' }
|
|
377
|
+
: endpoint.includes('/commits/') ? { sha }
|
|
378
|
+
: endpoint.includes('/contents/') ? { encoding: 'base64', content: ${JSON.stringify(packageManifest)} }
|
|
379
|
+
: endpoint.includes('/git/trees/') ? { tree: [{ path: 'src/modules/test_package/index.ts' }] }
|
|
380
|
+
: { default_branch: 'main' }
|
|
381
|
+
process.stdout.write(JSON.stringify(value))
|
|
382
|
+
`)
|
|
383
|
+
const yarnPath = path.join(executableDir, 'yarn')
|
|
384
|
+
const marker = path.join(appDir, 'yarn-invoked')
|
|
385
|
+
writeFile(yarnPath, "#!/usr/bin/env node\nrequire('node:fs').writeFileSync(process.env.COLLISION_YARN_MARKER, 'invoked')\nprocess.exit(1)\n")
|
|
386
|
+
fs.chmodSync(ghPath, 0o755)
|
|
387
|
+
fs.chmodSync(yarnPath, 0o755)
|
|
388
|
+
for (const locator of ['github:pkarw/test-package', 'https://github.com/pkarw/test-package.git', 'git@github.com:pkarw/test-package.git', 'github:test-package']) {
|
|
389
|
+
expect(() => runCommand(process.execPath, [cliBin, 'module', 'add', locator, '--allow-third-party'], appDir, {
|
|
390
|
+
PATH: executableDir + path.delimiter + process.env.PATH,
|
|
391
|
+
COLLISION_YARN_MARKER: marker,
|
|
392
|
+
})).toThrow(/Module name collision[\s\S]*test_package[\s\S]*github:other\/test-package[\s\S]*github:pkarw\/test-package/)
|
|
393
|
+
}
|
|
394
|
+
expect(fs.existsSync(marker)).toBe(false)
|
|
395
|
+
expect(readFile(path.join(appDir, 'package.json'))).toBe(originalManifest)
|
|
396
|
+
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).toBe(modulesSource)
|
|
397
|
+
expect(fs.existsSync(path.join(appDir, 'src', 'modules', 'test_package'))).toBe(false)
|
|
398
|
+
} finally {
|
|
399
|
+
fs.rmSync(appDir, { recursive: true, force: true })
|
|
400
|
+
}
|
|
401
|
+
})
|
|
402
|
+
|
|
207
403
|
test('module enable supports package-backed and ejected flows plus both eject entrypoints', () => {
|
|
208
404
|
const appDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mercato-cli-standalone-eject-'))
|
|
209
405
|
|
|
@@ -218,25 +414,29 @@ test.describe('TC-INT-007: CLI official module install and eject flows', () => {
|
|
|
218
414
|
|
|
219
415
|
runMercato(['module', 'enable', '@open-mercato/test-package'], appDir)
|
|
220
416
|
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).toContain(
|
|
221
|
-
"
|
|
417
|
+
"id: 'test_package', from: '@open-mercato/test-package'",
|
|
222
418
|
)
|
|
223
419
|
|
|
224
420
|
writeFile(path.join(appDir, 'src', 'modules.ts'), 'export const enabledModules = []\n')
|
|
225
421
|
fs.rmSync(path.join(appDir, 'src', 'modules', 'test_package'), { recursive: true, force: true })
|
|
226
422
|
runMercato(['module', 'enable', '@open-mercato/test-package', '--eject'], appDir)
|
|
227
|
-
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).toContain("
|
|
423
|
+
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).toContain("id: 'test_package', from: '@app'")
|
|
228
424
|
expect(fs.existsSync(path.join(appDir, 'src', 'modules', 'test_package', 'index.ts'))).toBe(true)
|
|
229
425
|
expect(readFile(path.join(appDir, '.mercato', 'generated', 'module-package-sources.css'))).toBe('')
|
|
230
426
|
|
|
231
427
|
writeFile(path.join(appDir, 'src', 'modules.ts'), "export const enabledModules = [{ id: 'test_package', from: '@open-mercato/test-package' }]\n")
|
|
232
428
|
fs.rmSync(path.join(appDir, 'src', 'modules', 'test_package'), { recursive: true, force: true })
|
|
233
429
|
runMercato(['module', 'eject', 'test_package'], appDir)
|
|
234
|
-
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).toContain("
|
|
430
|
+
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).toContain("packageName: '@open-mercato/test-package'")
|
|
431
|
+
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).not.toContain("locator: 'npm:@open-mercato/test-package'")
|
|
432
|
+
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).toContain("id: 'test_package', from: '@app'")
|
|
235
433
|
|
|
236
434
|
writeFile(path.join(appDir, 'src', 'modules.ts'), "export const enabledModules = [{ id: 'test_package', from: '@open-mercato/test-package' }]\n")
|
|
237
435
|
fs.rmSync(path.join(appDir, 'src', 'modules', 'test_package'), { recursive: true, force: true })
|
|
238
436
|
runMercato(['eject', 'test_package'], appDir)
|
|
239
|
-
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).toContain("
|
|
437
|
+
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).toContain("packageName: '@open-mercato/test-package'")
|
|
438
|
+
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).not.toContain("locator: 'npm:@open-mercato/test-package'")
|
|
439
|
+
expect(readFile(path.join(appDir, 'src', 'modules.ts'))).toContain("id: 'test_package', from: '@app'")
|
|
240
440
|
} finally {
|
|
241
441
|
fs.rmSync(appDir, { recursive: true, force: true })
|
|
242
442
|
}
|