@open-mercato/cli 0.6.7-develop.6566.1.15385fa179 → 0.6.7-develop.6568.1.9f57ffc393
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/AGENTS.md +5 -3
- package/dist/agentic/guides/module-facts.json +53 -53
- package/dist/agentic/guides/modules/ai_assistant.md +1 -1
- package/dist/agentic/guides/modules/api_docs.md +1 -1
- package/dist/agentic/guides/modules/api_keys.md +1 -1
- package/dist/agentic/guides/modules/attachments.md +1 -1
- package/dist/agentic/guides/modules/audit_logs.md +1 -1
- package/dist/agentic/guides/modules/auth.md +1 -1
- package/dist/agentic/guides/modules/business_rules.md +1 -1
- package/dist/agentic/guides/modules/catalog.md +1 -1
- package/dist/agentic/guides/modules/channel_gmail.md +1 -1
- package/dist/agentic/guides/modules/channel_imap.md +1 -1
- package/dist/agentic/guides/modules/checkout.md +1 -1
- package/dist/agentic/guides/modules/communication_channels.md +1 -1
- package/dist/agentic/guides/modules/configs.md +1 -1
- package/dist/agentic/guides/modules/content.md +1 -1
- package/dist/agentic/guides/modules/currencies.md +1 -1
- package/dist/agentic/guides/modules/customer_accounts.md +1 -1
- package/dist/agentic/guides/modules/customers.md +1 -1
- package/dist/agentic/guides/modules/dashboards.md +1 -1
- package/dist/agentic/guides/modules/data_sync.md +1 -1
- package/dist/agentic/guides/modules/dictionaries.md +1 -1
- package/dist/agentic/guides/modules/directory.md +1 -1
- package/dist/agentic/guides/modules/entities.md +1 -1
- package/dist/agentic/guides/modules/events.md +1 -1
- package/dist/agentic/guides/modules/feature_toggles.md +1 -1
- package/dist/agentic/guides/modules/gateway_stripe.md +1 -1
- package/dist/agentic/guides/modules/generators.md +1 -1
- package/dist/agentic/guides/modules/inbox_ops.md +1 -1
- package/dist/agentic/guides/modules/integrations.md +1 -1
- package/dist/agentic/guides/modules/messages.md +1 -1
- package/dist/agentic/guides/modules/notifications.md +1 -1
- package/dist/agentic/guides/modules/onboarding.md +1 -1
- package/dist/agentic/guides/modules/payment_gateways.md +1 -1
- package/dist/agentic/guides/modules/perspectives.md +1 -1
- package/dist/agentic/guides/modules/planner.md +1 -1
- package/dist/agentic/guides/modules/portal.md +1 -1
- package/dist/agentic/guides/modules/progress.md +1 -1
- package/dist/agentic/guides/modules/query_index.md +1 -1
- package/dist/agentic/guides/modules/record_locks.md +1 -1
- package/dist/agentic/guides/modules/resources.md +1 -1
- package/dist/agentic/guides/modules/sales.md +1 -1
- package/dist/agentic/guides/modules/scheduler.md +1 -1
- package/dist/agentic/guides/modules/search.md +1 -1
- package/dist/agentic/guides/modules/security.md +1 -1
- package/dist/agentic/guides/modules/shipping_carriers.md +1 -1
- package/dist/agentic/guides/modules/sso.md +1 -1
- package/dist/agentic/guides/modules/staff.md +1 -1
- package/dist/agentic/guides/modules/storage_s3.md +1 -1
- package/dist/agentic/guides/modules/sync_akeneo.md +1 -1
- package/dist/agentic/guides/modules/sync_excel.md +1 -1
- package/dist/agentic/guides/modules/system_status_overlays.md +1 -1
- package/dist/agentic/guides/modules/translations.md +1 -1
- package/dist/agentic/guides/modules/webhooks.md +1 -1
- package/dist/agentic/guides/modules/workflows.md +1 -1
- package/dist/lib/post-generate-invalidation.js +49 -0
- package/dist/lib/post-generate-invalidation.js.map +7 -0
- package/dist/mercato.js +20 -20
- package/dist/mercato.js.map +2 -2
- package/package.json +5 -5
- package/src/__tests__/mercato.test.ts +63 -21
- package/src/lib/__tests__/post-generate-invalidation.test.ts +81 -0
- package/src/lib/post-generate-invalidation.ts +62 -0
- package/src/mercato.ts +20 -20
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/cli",
|
|
3
|
-
"version": "0.6.7-develop.
|
|
3
|
+
"version": "0.6.7-develop.6568.1.9f57ffc393",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
"@mikro-orm/decorators": "^7.1.5",
|
|
61
61
|
"@mikro-orm/migrations": "^7.1.5",
|
|
62
62
|
"@mikro-orm/postgresql": "^7.1.5",
|
|
63
|
-
"@open-mercato/queue": "0.6.7-develop.
|
|
64
|
-
"@open-mercato/shared": "0.6.7-develop.
|
|
63
|
+
"@open-mercato/queue": "0.6.7-develop.6568.1.9f57ffc393",
|
|
64
|
+
"@open-mercato/shared": "0.6.7-develop.6568.1.9f57ffc393",
|
|
65
65
|
"cross-spawn": "^7.0.6",
|
|
66
66
|
"pg": "8.22.0",
|
|
67
67
|
"semver": "^7.8.5",
|
|
@@ -72,10 +72,10 @@
|
|
|
72
72
|
"typescript-js": "npm:typescript@6.0.3"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
|
-
"@open-mercato/shared": "0.6.7-develop.
|
|
75
|
+
"@open-mercato/shared": "0.6.7-develop.6568.1.9f57ffc393"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
|
-
"@open-mercato/shared": "0.6.7-develop.
|
|
78
|
+
"@open-mercato/shared": "0.6.7-develop.6568.1.9f57ffc393",
|
|
79
79
|
"@types/jest": "^30.0.0",
|
|
80
80
|
"jest": "^30.4.2",
|
|
81
81
|
"ts-jest": "^29.4.11"
|
|
@@ -531,7 +531,7 @@ describe('init command failure output', () => {
|
|
|
531
531
|
})
|
|
532
532
|
})
|
|
533
533
|
|
|
534
|
-
describe('generate post-step structural
|
|
534
|
+
describe('generate post-step structural invalidation', () => {
|
|
535
535
|
beforeEach(() => {
|
|
536
536
|
jest.restoreAllMocks()
|
|
537
537
|
jest.resetModules()
|
|
@@ -540,11 +540,12 @@ describe('generate post-step structural cache purge', () => {
|
|
|
540
540
|
afterEach(() => {
|
|
541
541
|
jest.dontMock('../lib/generators')
|
|
542
542
|
jest.dontMock('../lib/resolver')
|
|
543
|
+
jest.dontMock('../lib/post-generate-invalidation')
|
|
543
544
|
jest.dontMock('@open-mercato/shared/lib/bootstrap/dynamicLoader')
|
|
544
545
|
jest.resetModules()
|
|
545
546
|
})
|
|
546
547
|
|
|
547
|
-
it('
|
|
548
|
+
it('uses the lightweight invalidation helper after successful generation', async () => {
|
|
548
549
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation()
|
|
549
550
|
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation()
|
|
550
551
|
const generateEntityIds = jest.fn().mockResolvedValue({
|
|
@@ -559,7 +560,12 @@ describe('generate post-step structural cache purge', () => {
|
|
|
559
560
|
const generateModuleDi = jest.fn().mockResolvedValue(undefined)
|
|
560
561
|
const generateModulePackageSources = jest.fn().mockResolvedValue(undefined)
|
|
561
562
|
const generateOpenApi = jest.fn().mockResolvedValue(undefined)
|
|
562
|
-
const
|
|
563
|
+
const invalidate = jest.fn().mockResolvedValue({
|
|
564
|
+
cacheEntriesDeleted: 2,
|
|
565
|
+
generatedFilesTouched: ['/tmp/test-app/.mercato/generated/modules.generated.ts'],
|
|
566
|
+
cacheError: null,
|
|
567
|
+
generatedFilesError: null,
|
|
568
|
+
})
|
|
563
569
|
|
|
564
570
|
jest.doMock('../lib/generators', () => ({
|
|
565
571
|
generateEntityIds,
|
|
@@ -574,25 +580,22 @@ describe('generate post-step structural cache purge', () => {
|
|
|
574
580
|
jest.doMock('../lib/resolver', () => ({
|
|
575
581
|
createResolver: () => ({
|
|
576
582
|
getAppDir: () => '/tmp/test-app',
|
|
583
|
+
loadEnabledModules: () => [{ id: 'configs', from: '@open-mercato/core' }],
|
|
577
584
|
}),
|
|
578
585
|
}))
|
|
579
|
-
jest.doMock('
|
|
580
|
-
|
|
581
|
-
modules: [
|
|
582
|
-
{
|
|
583
|
-
id: 'configs',
|
|
584
|
-
cli: [{ command: 'cache', run: cacheRun }],
|
|
585
|
-
},
|
|
586
|
-
],
|
|
587
|
-
}),
|
|
586
|
+
jest.doMock('../lib/post-generate-invalidation', () => ({
|
|
587
|
+
runPostGenerateStructuralInvalidation: invalidate,
|
|
588
588
|
}))
|
|
589
|
+
jest.doMock('@open-mercato/shared/lib/bootstrap/dynamicLoader', () => {
|
|
590
|
+
throw new Error('post-generation invalidation must not bootstrap generated modules')
|
|
591
|
+
})
|
|
589
592
|
|
|
590
593
|
const mercato = await import('../mercato')
|
|
591
594
|
const exitCode = await mercato.run(['node', 'mercato', 'generate'])
|
|
592
595
|
|
|
593
596
|
expect(exitCode).toBe(0)
|
|
594
597
|
expect(generateEntityIds).toHaveBeenCalled()
|
|
595
|
-
expect(
|
|
598
|
+
expect(invalidate).toHaveBeenCalledWith('/tmp/test-app')
|
|
596
599
|
|
|
597
600
|
consoleErrorSpy.mockRestore()
|
|
598
601
|
consoleLogSpy.mockRestore()
|
|
@@ -616,32 +619,33 @@ describe('generate post-step structural cache purge', () => {
|
|
|
616
619
|
generateModulePackageSources: jest.fn().mockResolvedValue(unchangedResult),
|
|
617
620
|
generateOpenApi: jest.fn().mockResolvedValue(unchangedResult),
|
|
618
621
|
}
|
|
619
|
-
const
|
|
622
|
+
const invalidate = jest.fn()
|
|
620
623
|
|
|
621
624
|
jest.doMock('../lib/generators', () => generators)
|
|
622
625
|
jest.doMock('../lib/resolver', () => ({
|
|
623
626
|
createResolver: () => ({
|
|
624
627
|
getAppDir: () => '/tmp/test-app',
|
|
628
|
+
loadEnabledModules: () => [{ id: 'configs', from: '@open-mercato/core' }],
|
|
625
629
|
}),
|
|
626
630
|
}))
|
|
627
|
-
jest.doMock('
|
|
628
|
-
|
|
631
|
+
jest.doMock('../lib/post-generate-invalidation', () => ({
|
|
632
|
+
runPostGenerateStructuralInvalidation: invalidate,
|
|
629
633
|
}))
|
|
630
634
|
|
|
631
635
|
const mercato = await import('../mercato')
|
|
632
636
|
const exitCode = await mercato.run(['node', 'mercato', 'generate'])
|
|
633
637
|
|
|
634
638
|
expect(exitCode).toBe(0)
|
|
635
|
-
expect(
|
|
639
|
+
expect(invalidate).not.toHaveBeenCalled()
|
|
636
640
|
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
637
|
-
'[generate] Generated outputs unchanged; skipping structural
|
|
641
|
+
'[generate] Generated outputs unchanged; skipping structural invalidation.',
|
|
638
642
|
)
|
|
639
643
|
|
|
640
644
|
consoleErrorSpy.mockRestore()
|
|
641
645
|
consoleLogSpy.mockRestore()
|
|
642
646
|
})
|
|
643
647
|
|
|
644
|
-
it('keeps generation successful when
|
|
648
|
+
it('keeps generation successful when lightweight invalidation fails', async () => {
|
|
645
649
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation()
|
|
646
650
|
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation()
|
|
647
651
|
const generateEntityIds = jest.fn().mockResolvedValue({
|
|
@@ -656,6 +660,7 @@ describe('generate post-step structural cache purge', () => {
|
|
|
656
660
|
const generateModuleDi = jest.fn().mockResolvedValue(undefined)
|
|
657
661
|
const generateModulePackageSources = jest.fn().mockResolvedValue(undefined)
|
|
658
662
|
const generateOpenApi = jest.fn().mockResolvedValue(undefined)
|
|
663
|
+
const invalidate = jest.fn().mockRejectedValue(new Error('cache maintenance unavailable'))
|
|
659
664
|
|
|
660
665
|
jest.doMock('../lib/generators', () => ({
|
|
661
666
|
generateEntityIds,
|
|
@@ -670,10 +675,11 @@ describe('generate post-step structural cache purge', () => {
|
|
|
670
675
|
jest.doMock('../lib/resolver', () => ({
|
|
671
676
|
createResolver: () => ({
|
|
672
677
|
getAppDir: () => '/tmp/test-app',
|
|
678
|
+
loadEnabledModules: () => [{ id: 'configs', from: '@open-mercato/core' }],
|
|
673
679
|
}),
|
|
674
680
|
}))
|
|
675
|
-
jest.doMock('
|
|
676
|
-
|
|
681
|
+
jest.doMock('../lib/post-generate-invalidation', () => ({
|
|
682
|
+
runPostGenerateStructuralInvalidation: invalidate,
|
|
677
683
|
}))
|
|
678
684
|
|
|
679
685
|
const mercato = await import('../mercato')
|
|
@@ -681,6 +687,42 @@ describe('generate post-step structural cache purge', () => {
|
|
|
681
687
|
|
|
682
688
|
expect(exitCode).toBe(0)
|
|
683
689
|
expect(generateEntityIds).toHaveBeenCalled()
|
|
690
|
+
expect(invalidate).toHaveBeenCalledWith('/tmp/test-app')
|
|
691
|
+
|
|
692
|
+
consoleErrorSpy.mockRestore()
|
|
693
|
+
consoleLogSpy.mockRestore()
|
|
694
|
+
})
|
|
695
|
+
|
|
696
|
+
it('skips cache infrastructure entirely when configs is not enabled', async () => {
|
|
697
|
+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation()
|
|
698
|
+
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation()
|
|
699
|
+
const generate = jest.fn().mockResolvedValue(undefined)
|
|
700
|
+
|
|
701
|
+
jest.doMock('../lib/generators', () => ({
|
|
702
|
+
generateEntityIds: generate,
|
|
703
|
+
generateModuleRegistry: generate,
|
|
704
|
+
generateModuleRegistryApp: generate,
|
|
705
|
+
generateModuleRegistryCli: generate,
|
|
706
|
+
generateModuleEntities: generate,
|
|
707
|
+
generateModuleDi: generate,
|
|
708
|
+
generateModulePackageSources: generate,
|
|
709
|
+
generateOpenApi: generate,
|
|
710
|
+
}))
|
|
711
|
+
jest.doMock('../lib/resolver', () => ({
|
|
712
|
+
createResolver: () => ({
|
|
713
|
+
getAppDir: () => '/tmp/test-app',
|
|
714
|
+
loadEnabledModules: () => [{ id: 'auth', from: '@open-mercato/core' }],
|
|
715
|
+
}),
|
|
716
|
+
}))
|
|
717
|
+
jest.doMock('../lib/post-generate-invalidation', () => {
|
|
718
|
+
throw new Error('cache infrastructure must stay unloaded without configs')
|
|
719
|
+
})
|
|
720
|
+
|
|
721
|
+
const mercato = await import('../mercato')
|
|
722
|
+
const exitCode = await mercato.run(['node', 'mercato', 'generate'])
|
|
723
|
+
|
|
724
|
+
expect(exitCode).toBe(0)
|
|
725
|
+
expect(generate).toHaveBeenCalled()
|
|
684
726
|
|
|
685
727
|
consoleErrorSpy.mockRestore()
|
|
686
728
|
consoleLogSpy.mockRestore()
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { purgeConfiguredCachePatternsAcrossTenantScopes } from '@open-mercato/shared/lib/cache/maintenance'
|
|
5
|
+
import { runPostGenerateStructuralInvalidation } from '../post-generate-invalidation'
|
|
6
|
+
|
|
7
|
+
jest.mock('@open-mercato/shared/lib/cache/maintenance', () => ({
|
|
8
|
+
purgeConfiguredCachePatternsAcrossTenantScopes: jest.fn(),
|
|
9
|
+
}))
|
|
10
|
+
|
|
11
|
+
const purgeCache = jest.mocked(purgeConfiguredCachePatternsAcrossTenantScopes)
|
|
12
|
+
|
|
13
|
+
describe('runPostGenerateStructuralInvalidation', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
purgeCache.mockReset().mockResolvedValue(3)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('purges structural cache patterns and refreshes only generated artifacts', async () => {
|
|
19
|
+
const appDir = fs.mkdtempSync(path.join(os.tmpdir(), 'post-generate-invalidation-'))
|
|
20
|
+
try {
|
|
21
|
+
const generatedDir = path.join(appDir, '.mercato', 'generated')
|
|
22
|
+
fs.mkdirSync(generatedDir, { recursive: true })
|
|
23
|
+
const generatedTs = path.join(generatedDir, 'modules.app.generated.ts')
|
|
24
|
+
const generatedChecksum = path.join(generatedDir, 'modules.app.generated.checksum')
|
|
25
|
+
const unrelated = path.join(generatedDir, 'README.md')
|
|
26
|
+
fs.writeFileSync(generatedTs, 'export const modules = []\n')
|
|
27
|
+
fs.writeFileSync(generatedChecksum, 'abc\n')
|
|
28
|
+
fs.writeFileSync(unrelated, 'keep\n')
|
|
29
|
+
|
|
30
|
+
const result = await runPostGenerateStructuralInvalidation(appDir)
|
|
31
|
+
|
|
32
|
+
expect(purgeCache).toHaveBeenCalledWith([
|
|
33
|
+
'nav:*',
|
|
34
|
+
'crud|*|*|*/admin/nav|*',
|
|
35
|
+
'crud|*|*|*/portal/nav|*',
|
|
36
|
+
])
|
|
37
|
+
expect(result).toEqual({
|
|
38
|
+
cacheEntriesDeleted: 3,
|
|
39
|
+
generatedFilesTouched: [generatedChecksum, generatedTs],
|
|
40
|
+
cacheError: null,
|
|
41
|
+
generatedFilesError: null,
|
|
42
|
+
})
|
|
43
|
+
expect(fs.readFileSync(generatedTs, 'utf8')).toBe('export const modules = []\n')
|
|
44
|
+
expect(fs.readFileSync(unrelated, 'utf8')).toBe('keep\n')
|
|
45
|
+
} finally {
|
|
46
|
+
fs.rmSync(appDir, { recursive: true, force: true })
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('still refreshes generated artifacts when cache maintenance fails', async () => {
|
|
51
|
+
const appDir = fs.mkdtempSync(path.join(os.tmpdir(), 'post-generate-invalidation-failure-'))
|
|
52
|
+
const error = new Error('cache unavailable')
|
|
53
|
+
try {
|
|
54
|
+
const generatedDir = path.join(appDir, '.mercato', 'generated')
|
|
55
|
+
fs.mkdirSync(generatedDir, { recursive: true })
|
|
56
|
+
const generatedTs = path.join(generatedDir, 'modules.generated.ts')
|
|
57
|
+
fs.writeFileSync(generatedTs, 'export {}\n')
|
|
58
|
+
purgeCache.mockRejectedValueOnce(error)
|
|
59
|
+
|
|
60
|
+
const result = await runPostGenerateStructuralInvalidation(appDir)
|
|
61
|
+
|
|
62
|
+
expect(result.cacheEntriesDeleted).toBe(0)
|
|
63
|
+
expect(result.cacheError).toBe(error)
|
|
64
|
+
expect(result.generatedFilesTouched).toEqual([generatedTs])
|
|
65
|
+
expect(result.generatedFilesError).toBeNull()
|
|
66
|
+
} finally {
|
|
67
|
+
fs.rmSync(appDir, { recursive: true, force: true })
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('treats a missing generated directory as a successful no-op', async () => {
|
|
72
|
+
const appDir = fs.mkdtempSync(path.join(os.tmpdir(), 'post-generate-invalidation-empty-'))
|
|
73
|
+
try {
|
|
74
|
+
const result = await runPostGenerateStructuralInvalidation(appDir)
|
|
75
|
+
expect(result.generatedFilesTouched).toEqual([])
|
|
76
|
+
expect(result.generatedFilesError).toBeNull()
|
|
77
|
+
} finally {
|
|
78
|
+
fs.rmSync(appDir, { recursive: true, force: true })
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
})
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { purgeConfiguredCachePatternsAcrossTenantScopes } from '@open-mercato/shared/lib/cache/maintenance'
|
|
4
|
+
|
|
5
|
+
const STRUCTURAL_CACHE_PATTERNS = [
|
|
6
|
+
'nav:*',
|
|
7
|
+
'crud|*|*|*/admin/nav|*',
|
|
8
|
+
'crud|*|*|*/portal/nav|*',
|
|
9
|
+
]
|
|
10
|
+
const TOUCHABLE_GENERATED_FILE_PATTERN = /\.generated(?:\.[a-z0-9]+)?(?:\.ts|\.checksum)$/i
|
|
11
|
+
|
|
12
|
+
export type PostGenerateStructuralInvalidationResult = {
|
|
13
|
+
cacheEntriesDeleted: number
|
|
14
|
+
generatedFilesTouched: string[]
|
|
15
|
+
cacheError: unknown | null
|
|
16
|
+
generatedFilesError: unknown | null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function touchGeneratedFiles(appDir: string): string[] {
|
|
20
|
+
const generatedDir = path.join(appDir, '.mercato', 'generated')
|
|
21
|
+
if (!fs.existsSync(generatedDir) || !fs.statSync(generatedDir).isDirectory()) return []
|
|
22
|
+
|
|
23
|
+
const touched: string[] = []
|
|
24
|
+
const entries = fs.readdirSync(generatedDir, { withFileTypes: true })
|
|
25
|
+
.sort((left, right) => left.name.localeCompare(right.name))
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
if (!entry.isFile() || !TOUCHABLE_GENERATED_FILE_PATTERN.test(entry.name)) continue
|
|
28
|
+
const filePath = path.join(generatedDir, entry.name)
|
|
29
|
+
const contents = fs.readFileSync(filePath)
|
|
30
|
+
fs.writeFileSync(filePath, contents)
|
|
31
|
+
touched.push(filePath)
|
|
32
|
+
}
|
|
33
|
+
return touched
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function runPostGenerateStructuralInvalidation(
|
|
37
|
+
appDir: string,
|
|
38
|
+
): Promise<PostGenerateStructuralInvalidationResult> {
|
|
39
|
+
let cacheEntriesDeleted = 0
|
|
40
|
+
let generatedFilesTouched: string[] = []
|
|
41
|
+
let cacheError: unknown | null = null
|
|
42
|
+
let generatedFilesError: unknown | null = null
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
generatedFilesTouched = touchGeneratedFiles(appDir)
|
|
46
|
+
} catch (error) {
|
|
47
|
+
generatedFilesError = error
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
cacheEntriesDeleted = await purgeConfiguredCachePatternsAcrossTenantScopes(STRUCTURAL_CACHE_PATTERNS)
|
|
52
|
+
} catch (error) {
|
|
53
|
+
cacheError = error
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
cacheEntriesDeleted,
|
|
58
|
+
generatedFilesTouched,
|
|
59
|
+
cacheError,
|
|
60
|
+
generatedFilesError,
|
|
61
|
+
}
|
|
62
|
+
}
|
package/src/mercato.ts
CHANGED
|
@@ -706,40 +706,40 @@ async function runModuleCommand(
|
|
|
706
706
|
return true
|
|
707
707
|
}
|
|
708
708
|
|
|
709
|
-
async function
|
|
709
|
+
async function runPostGenerateStructuralInvalidation(quiet: boolean): Promise<void> {
|
|
710
710
|
try {
|
|
711
|
-
const
|
|
712
|
-
import('@open-mercato/shared/lib/bootstrap/dynamicLoader'),
|
|
713
|
-
import('./lib/resolver'),
|
|
714
|
-
])
|
|
711
|
+
const { createResolver } = await import('./lib/resolver')
|
|
715
712
|
const resolver = createResolver()
|
|
716
713
|
const appDir = resolver.getAppDir()
|
|
717
|
-
const
|
|
718
|
-
registerCliModules(data.modules)
|
|
719
|
-
const configsModule = data.modules.find((mod) => mod.id === 'configs')
|
|
720
|
-
const hasCacheCommand = configsModule?.cli?.some((command) => command.command === 'cache') ?? false
|
|
714
|
+
const hasConfigsModule = resolver.loadEnabledModules().some((module) => module.id === 'configs')
|
|
721
715
|
|
|
722
|
-
if (!
|
|
716
|
+
if (!hasConfigsModule) {
|
|
723
717
|
if (!quiet) {
|
|
724
|
-
console.log('[generate] Skipping structural
|
|
718
|
+
console.log('[generate] Skipping structural invalidation: the "configs" module is not enabled in this app.')
|
|
725
719
|
}
|
|
726
720
|
return
|
|
727
721
|
}
|
|
728
722
|
|
|
723
|
+
const { runPostGenerateStructuralInvalidation: invalidate } = await import('./lib/post-generate-invalidation')
|
|
729
724
|
if (!quiet) {
|
|
730
|
-
console.log('[generate]
|
|
725
|
+
console.log('[generate] Invalidating generated structure...')
|
|
731
726
|
}
|
|
732
|
-
|
|
733
|
-
optional: true,
|
|
734
|
-
silentOptional: quiet,
|
|
735
|
-
})
|
|
727
|
+
const result = await invalidate(appDir)
|
|
736
728
|
if (!quiet) {
|
|
737
|
-
|
|
729
|
+
if (result.cacheError) {
|
|
730
|
+
console.log(`[generate] Structural cache purge skipped: ${formatCliFailureMessage('configs', 'cache', result.cacheError)}`)
|
|
731
|
+
}
|
|
732
|
+
if (result.generatedFilesError) {
|
|
733
|
+
console.log(`[generate] Generated artifact refresh skipped: ${formatCliFailureMessage('generate', 'refresh', result.generatedFilesError)}`)
|
|
734
|
+
}
|
|
735
|
+
console.log(
|
|
736
|
+
`[generate] Structural invalidation completed (${result.cacheEntriesDeleted} cache entr${result.cacheEntriesDeleted === 1 ? 'y' : 'ies'}, ${result.generatedFilesTouched.length} generated artifact${result.generatedFilesTouched.length === 1 ? '' : 's'} refreshed).`,
|
|
737
|
+
)
|
|
738
738
|
}
|
|
739
739
|
} catch (error) {
|
|
740
740
|
if (!quiet) {
|
|
741
741
|
const message = formatCliFailureMessage('configs', 'cache', error)
|
|
742
|
-
console.log(`[generate] Skipping structural
|
|
742
|
+
console.log(`[generate] Skipping structural invalidation: ${message}`)
|
|
743
743
|
}
|
|
744
744
|
}
|
|
745
745
|
}
|
|
@@ -780,11 +780,11 @@ async function runGeneratorSuiteWithStructuralInvalidation(quiet: boolean): Prom
|
|
|
780
780
|
const generatedFilesChanged = await runGeneratorSuite(quiet)
|
|
781
781
|
if (!generatedFilesChanged) {
|
|
782
782
|
if (!quiet) {
|
|
783
|
-
console.log('[generate] Generated outputs unchanged; skipping structural
|
|
783
|
+
console.log('[generate] Generated outputs unchanged; skipping structural invalidation.')
|
|
784
784
|
}
|
|
785
785
|
return
|
|
786
786
|
}
|
|
787
|
-
await
|
|
787
|
+
await runPostGenerateStructuralInvalidation(quiet)
|
|
788
788
|
}
|
|
789
789
|
|
|
790
790
|
/**
|