@objectstack/core 4.0.3 → 4.0.5
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/README.md +95 -10
- package/dist/index.cjs +169 -507
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -223
- package/dist/index.d.ts +24 -223
- package/dist/index.js +175 -505
- package/dist/index.js.map +1 -1
- package/dist/logger.cjs +177 -0
- package/dist/logger.cjs.map +1 -0
- package/dist/logger.d.cts +26 -0
- package/dist/logger.d.ts +26 -0
- package/dist/logger.js +158 -0
- package/dist/logger.js.map +1 -0
- package/package.json +36 -15
- package/.turbo/turbo-build.log +0 -22
- package/ADVANCED_FEATURES.md +0 -380
- package/API_REGISTRY.md +0 -392
- package/CHANGELOG.md +0 -465
- package/PHASE2_IMPLEMENTATION.md +0 -388
- package/REFACTORING_SUMMARY.md +0 -40
- package/examples/api-registry-example.ts +0 -559
- package/examples/kernel-features-example.ts +0 -311
- package/examples/phase2-integration.ts +0 -357
- package/src/api-registry-plugin.test.ts +0 -393
- package/src/api-registry-plugin.ts +0 -89
- package/src/api-registry.test.ts +0 -1089
- package/src/api-registry.ts +0 -739
- package/src/contracts/data-engine.ts +0 -57
- package/src/contracts/http-server.ts +0 -151
- package/src/contracts/logger.ts +0 -72
- package/src/dependency-resolver.test.ts +0 -287
- package/src/dependency-resolver.ts +0 -390
- package/src/fallbacks/fallbacks.test.ts +0 -281
- package/src/fallbacks/index.ts +0 -26
- package/src/fallbacks/memory-cache.ts +0 -34
- package/src/fallbacks/memory-i18n.ts +0 -112
- package/src/fallbacks/memory-job.ts +0 -23
- package/src/fallbacks/memory-metadata.ts +0 -50
- package/src/fallbacks/memory-queue.ts +0 -28
- package/src/health-monitor.test.ts +0 -81
- package/src/health-monitor.ts +0 -318
- package/src/hot-reload.ts +0 -382
- package/src/index.ts +0 -50
- package/src/kernel-base.ts +0 -273
- package/src/kernel.test.ts +0 -624
- package/src/kernel.ts +0 -631
- package/src/lite-kernel.test.ts +0 -248
- package/src/lite-kernel.ts +0 -137
- package/src/logger.test.ts +0 -116
- package/src/logger.ts +0 -355
- package/src/namespace-resolver.test.ts +0 -130
- package/src/namespace-resolver.ts +0 -188
- package/src/package-manager.test.ts +0 -225
- package/src/package-manager.ts +0 -428
- package/src/plugin-loader.test.ts +0 -421
- package/src/plugin-loader.ts +0 -484
- package/src/qa/adapter.ts +0 -16
- package/src/qa/http-adapter.ts +0 -116
- package/src/qa/index.ts +0 -5
- package/src/qa/runner.ts +0 -189
- package/src/security/index.ts +0 -50
- package/src/security/permission-manager.test.ts +0 -256
- package/src/security/permission-manager.ts +0 -338
- package/src/security/plugin-config-validator.test.ts +0 -276
- package/src/security/plugin-config-validator.ts +0 -193
- package/src/security/plugin-permission-enforcer.test.ts +0 -251
- package/src/security/plugin-permission-enforcer.ts +0 -436
- package/src/security/plugin-signature-verifier.ts +0 -403
- package/src/security/sandbox-runtime.ts +0 -462
- package/src/security/security-scanner.ts +0 -367
- package/src/types.ts +0 -120
- package/src/utils/env.test.ts +0 -62
- package/src/utils/env.ts +0 -53
- package/tsconfig.json +0 -10
- package/vitest.config.ts +0 -10
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { PackageManager } from './package-manager.js';
|
|
3
|
-
import { createLogger } from './logger.js';
|
|
4
|
-
|
|
5
|
-
describe('PackageManager', () => {
|
|
6
|
-
let manager: PackageManager;
|
|
7
|
-
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
const logger = createLogger({ level: 'silent' });
|
|
10
|
-
manager = new PackageManager(logger, { platformVersion: '3.0.0' });
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
describe('install', () => {
|
|
14
|
-
it('should install a package successfully', async () => {
|
|
15
|
-
const result = await manager.install('pkg-a', '1.0.0', {
|
|
16
|
-
objects: { task: { label: 'Task' } },
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
expect(result.success).toBe(true);
|
|
20
|
-
expect(result.packageId).toBe('pkg-a');
|
|
21
|
-
expect(result.version).toBe('1.0.0');
|
|
22
|
-
expect(manager.getPackage('pkg-a')).toBeDefined();
|
|
23
|
-
expect(manager.getPackage('pkg-a')?.status).toBe('installed');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should reject already installed package', async () => {
|
|
27
|
-
await manager.install('pkg-a', '1.0.0', {});
|
|
28
|
-
const result = await manager.install('pkg-a', '1.0.0', {});
|
|
29
|
-
|
|
30
|
-
expect(result.success).toBe(false);
|
|
31
|
-
expect(result.errorMessage).toContain('already installed');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should detect namespace conflicts', async () => {
|
|
35
|
-
await manager.install('pkg-a', '1.0.0', {
|
|
36
|
-
objects: { task: {} },
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const result = await manager.install('pkg-b', '1.0.0', {
|
|
40
|
-
objects: { task: {} },
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
expect(result.success).toBe(false);
|
|
44
|
-
expect(result.namespaceConflicts).toHaveLength(1);
|
|
45
|
-
expect(result.namespaceConflicts[0].namespace).toBe('objects.task');
|
|
46
|
-
expect(result.namespaceConflicts[0].existingPackageId).toBe('pkg-a');
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should reject incompatible platform version', async () => {
|
|
50
|
-
const result = await manager.install('pkg-a', '1.0.0', {
|
|
51
|
-
engine: { objectstack: '>=4.0.0' },
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
expect(result.success).toBe(false);
|
|
55
|
-
expect(result.errorMessage).toContain('platform');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('should reject missing dependencies', async () => {
|
|
59
|
-
const result = await manager.install('pkg-a', '1.0.0', {
|
|
60
|
-
dependencies: { 'pkg-b': '^1.0.0' },
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
expect(result.success).toBe(false);
|
|
64
|
-
expect(result.errorMessage).toContain('Missing dependencies');
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('should succeed when dependencies are installed', async () => {
|
|
68
|
-
await manager.install('pkg-b', '1.0.0', {});
|
|
69
|
-
const result = await manager.install('pkg-a', '1.0.0', {
|
|
70
|
-
dependencies: { 'pkg-b': '^1.0.0' },
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
expect(result.success).toBe(true);
|
|
74
|
-
expect(result.installedDependencies).toContain('pkg-b');
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe('uninstall', () => {
|
|
79
|
-
it('should uninstall a package', async () => {
|
|
80
|
-
await manager.install('pkg-a', '1.0.0', {});
|
|
81
|
-
const result = await manager.uninstall('pkg-a');
|
|
82
|
-
|
|
83
|
-
expect(result.success).toBe(true);
|
|
84
|
-
expect(manager.getPackage('pkg-a')).toBeUndefined();
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('should prevent uninstalling if dependents exist', async () => {
|
|
88
|
-
await manager.install('pkg-a', '1.0.0', {});
|
|
89
|
-
await manager.install('pkg-b', '1.0.0', {
|
|
90
|
-
dependencies: { 'pkg-a': '^1.0.0' },
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const result = await manager.uninstall('pkg-a');
|
|
94
|
-
expect(result.success).toBe(false);
|
|
95
|
-
expect(result.errorMessage).toContain('depended upon');
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('should reject uninstalling unknown package', async () => {
|
|
99
|
-
const result = await manager.uninstall('unknown');
|
|
100
|
-
expect(result.success).toBe(false);
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
describe('upgrade', () => {
|
|
105
|
-
it('should upgrade a package and create snapshot', async () => {
|
|
106
|
-
await manager.install('pkg-a', '1.0.0', {
|
|
107
|
-
objects: { task: {} },
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const result = await manager.upgrade('pkg-a', '2.0.0', {
|
|
111
|
-
objects: { task: {}, project: {} },
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
expect(result.success).toBe(true);
|
|
115
|
-
expect(result.fromVersion).toBe('1.0.0');
|
|
116
|
-
expect(result.toVersion).toBe('2.0.0');
|
|
117
|
-
expect(result.snapshot.previousVersion).toBe('1.0.0');
|
|
118
|
-
expect(manager.getPackage('pkg-a')?.version).toBe('2.0.0');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('should reject upgrade for uninstalled package', async () => {
|
|
122
|
-
const result = await manager.upgrade('unknown', '2.0.0', {});
|
|
123
|
-
expect(result.success).toBe(false);
|
|
124
|
-
expect(result.errorMessage).toContain('not installed');
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should detect namespace conflicts during upgrade', async () => {
|
|
128
|
-
await manager.install('pkg-a', '1.0.0', { objects: { task: {} } });
|
|
129
|
-
await manager.install('pkg-b', '1.0.0', { objects: { project: {} } });
|
|
130
|
-
|
|
131
|
-
// pkg-a upgrade tries to add objects.project which is owned by pkg-b
|
|
132
|
-
const result = await manager.upgrade('pkg-a', '2.0.0', {
|
|
133
|
-
objects: { task: {}, project: {} },
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
expect(result.success).toBe(false);
|
|
137
|
-
expect(result.errorMessage).toContain('Namespace conflicts');
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('should reject platform-incompatible upgrade', async () => {
|
|
141
|
-
await manager.install('pkg-a', '1.0.0', {});
|
|
142
|
-
|
|
143
|
-
const result = await manager.upgrade('pkg-a', '2.0.0', {
|
|
144
|
-
engine: { objectstack: '>=5.0.0' },
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
expect(result.success).toBe(false);
|
|
148
|
-
expect(result.errorMessage).toContain('platform');
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
describe('rollback', () => {
|
|
153
|
-
it('should rollback to pre-upgrade state', async () => {
|
|
154
|
-
await manager.install('pkg-a', '1.0.0', {
|
|
155
|
-
objects: { task: {} },
|
|
156
|
-
});
|
|
157
|
-
await manager.upgrade('pkg-a', '2.0.0', {
|
|
158
|
-
objects: { task: {}, project: {} },
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
const result = await manager.rollback('pkg-a');
|
|
162
|
-
expect(result.success).toBe(true);
|
|
163
|
-
expect(result.restoredVersion).toBe('1.0.0');
|
|
164
|
-
expect(manager.getPackage('pkg-a')?.version).toBe('1.0.0');
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('should reject rollback without snapshot', async () => {
|
|
168
|
-
await manager.install('pkg-a', '1.0.0', {});
|
|
169
|
-
const result = await manager.rollback('pkg-a');
|
|
170
|
-
|
|
171
|
-
expect(result.success).toBe(false);
|
|
172
|
-
expect(result.errorMessage).toContain('No upgrade snapshot');
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
describe('listPackages', () => {
|
|
177
|
-
it('should list all installed packages', async () => {
|
|
178
|
-
await manager.install('pkg-a', '1.0.0', {});
|
|
179
|
-
await manager.install('pkg-b', '2.0.0', {});
|
|
180
|
-
|
|
181
|
-
const list = manager.listPackages();
|
|
182
|
-
expect(list).toHaveLength(2);
|
|
183
|
-
expect(list.map(p => p.packageId)).toContain('pkg-a');
|
|
184
|
-
expect(list.map(p => p.packageId)).toContain('pkg-b');
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
describe('checkNamespaces', () => {
|
|
189
|
-
it('should check namespace availability', async () => {
|
|
190
|
-
await manager.install('pkg-a', '1.0.0', {
|
|
191
|
-
objects: { task: {} },
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
const result = manager.checkNamespaces('pkg-b', {
|
|
195
|
-
objects: { task: {} },
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
expect(result.available).toBe(false);
|
|
199
|
-
expect(result.conflicts).toHaveLength(1);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it('should report available when no conflicts', () => {
|
|
203
|
-
const result = manager.checkNamespaces('pkg-a', {
|
|
204
|
-
objects: { task: {} },
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
expect(result.available).toBe(true);
|
|
208
|
-
expect(result.conflicts).toHaveLength(0);
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
describe('resolveDependencies', () => {
|
|
213
|
-
it('should resolve dependencies in topological order', () => {
|
|
214
|
-
const packages = new Map([
|
|
215
|
-
['a', { dependencies: [] as string[] }],
|
|
216
|
-
['b', { dependencies: ['a'] }],
|
|
217
|
-
['c', { dependencies: ['a', 'b'] }],
|
|
218
|
-
]);
|
|
219
|
-
|
|
220
|
-
const order = manager.resolveDependencies(packages);
|
|
221
|
-
expect(order.indexOf('a')).toBeLessThan(order.indexOf('b'));
|
|
222
|
-
expect(order.indexOf('b')).toBeLessThan(order.indexOf('c'));
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
});
|
package/src/package-manager.ts
DELETED
|
@@ -1,428 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import type { ObjectLogger } from './logger.js';
|
|
4
|
-
import { DependencyResolver, SemanticVersionManager } from './dependency-resolver.js';
|
|
5
|
-
import { NamespaceResolver } from './namespace-resolver.js';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Installed package record in the runtime registry.
|
|
9
|
-
*/
|
|
10
|
-
export interface InstalledPackageRecord {
|
|
11
|
-
/** Package identifier */
|
|
12
|
-
packageId: string;
|
|
13
|
-
/** Package version */
|
|
14
|
-
version: string;
|
|
15
|
-
/** Package manifest */
|
|
16
|
-
manifest: Record<string, unknown>;
|
|
17
|
-
/** Installation timestamp */
|
|
18
|
-
installedAt: string;
|
|
19
|
-
/** Current status */
|
|
20
|
-
status: 'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error';
|
|
21
|
-
/** Namespaces registered by this package */
|
|
22
|
-
namespaces: string[];
|
|
23
|
-
/** Dependencies of this package */
|
|
24
|
-
dependencies: string[];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Snapshot of a package's state before upgrade (for rollback).
|
|
29
|
-
*/
|
|
30
|
-
export interface PackageSnapshot {
|
|
31
|
-
/** Package identifier */
|
|
32
|
-
packageId: string;
|
|
33
|
-
/** Version before upgrade */
|
|
34
|
-
previousVersion: string;
|
|
35
|
-
/** Full manifest before upgrade */
|
|
36
|
-
previousManifest: Record<string, unknown>;
|
|
37
|
-
/** Namespaces before upgrade */
|
|
38
|
-
previousNamespaces: string[];
|
|
39
|
-
/** Original installation timestamp */
|
|
40
|
-
installedAt: string;
|
|
41
|
-
/** Snapshot timestamp */
|
|
42
|
-
createdAt: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Result of a package installation attempt.
|
|
47
|
-
*/
|
|
48
|
-
export interface InstallResult {
|
|
49
|
-
success: boolean;
|
|
50
|
-
packageId: string;
|
|
51
|
-
version: string;
|
|
52
|
-
installedDependencies: string[];
|
|
53
|
-
namespaceConflicts: Array<{ namespace: string; existingPackageId: string }>;
|
|
54
|
-
errorMessage?: string;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Result of an upgrade attempt.
|
|
59
|
-
*/
|
|
60
|
-
export interface UpgradeResult {
|
|
61
|
-
success: boolean;
|
|
62
|
-
packageId: string;
|
|
63
|
-
fromVersion: string;
|
|
64
|
-
toVersion: string;
|
|
65
|
-
snapshot: PackageSnapshot;
|
|
66
|
-
errorMessage?: string;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Result of a rollback attempt.
|
|
71
|
-
*/
|
|
72
|
-
export interface RollbackResult {
|
|
73
|
-
success: boolean;
|
|
74
|
-
packageId: string;
|
|
75
|
-
restoredVersion: string;
|
|
76
|
-
errorMessage?: string;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Package Manager
|
|
81
|
-
*
|
|
82
|
-
* Runtime implementation for the full package lifecycle:
|
|
83
|
-
* install → upgrade → rollback → uninstall.
|
|
84
|
-
*
|
|
85
|
-
* Consumes the protocol schemas defined in @objectstack/spec:
|
|
86
|
-
* - DependencyResolutionResultSchema
|
|
87
|
-
* - NamespaceConflictErrorSchema
|
|
88
|
-
* - UpgradePlanSchema / UpgradeSnapshotSchema
|
|
89
|
-
* - PackageArtifactSchema
|
|
90
|
-
*
|
|
91
|
-
* Coordinates with:
|
|
92
|
-
* - DependencyResolver for topological ordering and conflict detection
|
|
93
|
-
* - NamespaceResolver for metadata collision prevention
|
|
94
|
-
*/
|
|
95
|
-
export class PackageManager {
|
|
96
|
-
private logger: ObjectLogger;
|
|
97
|
-
private packages: Map<string, InstalledPackageRecord> = new Map();
|
|
98
|
-
private snapshots: Map<string, PackageSnapshot> = new Map();
|
|
99
|
-
private dependencyResolver: DependencyResolver;
|
|
100
|
-
private namespaceResolver: NamespaceResolver;
|
|
101
|
-
private platformVersion: string;
|
|
102
|
-
|
|
103
|
-
constructor(
|
|
104
|
-
logger: ObjectLogger,
|
|
105
|
-
options: { platformVersion?: string } = {},
|
|
106
|
-
) {
|
|
107
|
-
this.logger = logger.child({ component: 'PackageManager' });
|
|
108
|
-
this.dependencyResolver = new DependencyResolver(logger);
|
|
109
|
-
this.namespaceResolver = new NamespaceResolver(logger);
|
|
110
|
-
this.platformVersion = options.platformVersion || '3.0.0';
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Install a package with full dependency resolution and namespace checking.
|
|
115
|
-
*/
|
|
116
|
-
async install(
|
|
117
|
-
packageId: string,
|
|
118
|
-
version: string,
|
|
119
|
-
manifest: Record<string, unknown>,
|
|
120
|
-
): Promise<InstallResult> {
|
|
121
|
-
this.logger.info('Installing package', { packageId, version });
|
|
122
|
-
|
|
123
|
-
// 1. Check if already installed
|
|
124
|
-
if (this.packages.has(packageId)) {
|
|
125
|
-
const existing = this.packages.get(packageId)!;
|
|
126
|
-
if (existing.status === 'installed') {
|
|
127
|
-
return {
|
|
128
|
-
success: false,
|
|
129
|
-
packageId,
|
|
130
|
-
version,
|
|
131
|
-
installedDependencies: [],
|
|
132
|
-
namespaceConflicts: [],
|
|
133
|
-
errorMessage: `Package ${packageId}@${existing.version} is already installed. Use upgrade instead.`,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// 2. Check platform compatibility
|
|
139
|
-
const engine = (manifest as any).engine?.objectstack as string | undefined;
|
|
140
|
-
if (engine) {
|
|
141
|
-
const platformSemver = SemanticVersionManager.parse(this.platformVersion);
|
|
142
|
-
if (!SemanticVersionManager.satisfies(platformSemver, engine)) {
|
|
143
|
-
return {
|
|
144
|
-
success: false,
|
|
145
|
-
packageId,
|
|
146
|
-
version,
|
|
147
|
-
installedDependencies: [],
|
|
148
|
-
namespaceConflicts: [],
|
|
149
|
-
errorMessage: `Package requires platform ${engine}, but current platform is v${this.platformVersion}`,
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// 3. Check namespace conflicts
|
|
155
|
-
const namespaces = this.namespaceResolver.extractNamespaces(manifest);
|
|
156
|
-
const nsCheck = this.namespaceResolver.checkAvailability(packageId, namespaces);
|
|
157
|
-
if (!nsCheck.available) {
|
|
158
|
-
return {
|
|
159
|
-
success: false,
|
|
160
|
-
packageId,
|
|
161
|
-
version,
|
|
162
|
-
installedDependencies: [],
|
|
163
|
-
namespaceConflicts: nsCheck.conflicts.map(c => ({
|
|
164
|
-
namespace: c.namespace,
|
|
165
|
-
existingPackageId: c.existingPackageId,
|
|
166
|
-
})),
|
|
167
|
-
errorMessage: `Namespace conflicts detected: ${nsCheck.conflicts.map(c => c.namespace).join(', ')}`,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// 4. Resolve dependencies
|
|
172
|
-
const deps = (manifest as any).dependencies as Record<string, string> | undefined;
|
|
173
|
-
const depNames = deps ? Object.keys(deps) : [];
|
|
174
|
-
const missingDeps = depNames.filter(d => !this.packages.has(d));
|
|
175
|
-
if (missingDeps.length > 0) {
|
|
176
|
-
return {
|
|
177
|
-
success: false,
|
|
178
|
-
packageId,
|
|
179
|
-
version,
|
|
180
|
-
installedDependencies: [],
|
|
181
|
-
namespaceConflicts: [],
|
|
182
|
-
errorMessage: `Missing dependencies: ${missingDeps.join(', ')}`,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// 5. Register package
|
|
187
|
-
this.packages.set(packageId, {
|
|
188
|
-
packageId,
|
|
189
|
-
version,
|
|
190
|
-
manifest,
|
|
191
|
-
installedAt: new Date().toISOString(),
|
|
192
|
-
status: 'installed',
|
|
193
|
-
namespaces,
|
|
194
|
-
dependencies: depNames,
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
// 6. Register namespaces
|
|
198
|
-
this.namespaceResolver.register(packageId, namespaces);
|
|
199
|
-
|
|
200
|
-
this.logger.info('Package installed', { packageId, version, namespaces: namespaces.length });
|
|
201
|
-
|
|
202
|
-
return {
|
|
203
|
-
success: true,
|
|
204
|
-
packageId,
|
|
205
|
-
version,
|
|
206
|
-
installedDependencies: depNames,
|
|
207
|
-
namespaceConflicts: [],
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Uninstall a package, checking for dependents first.
|
|
213
|
-
*/
|
|
214
|
-
async uninstall(packageId: string): Promise<{ success: boolean; errorMessage?: string }> {
|
|
215
|
-
const pkg = this.packages.get(packageId);
|
|
216
|
-
if (!pkg) {
|
|
217
|
-
return { success: false, errorMessage: `Package ${packageId} is not installed` };
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Check if other packages depend on this one
|
|
221
|
-
const dependents: string[] = [];
|
|
222
|
-
for (const [id, record] of this.packages) {
|
|
223
|
-
if (id !== packageId && record.dependencies.includes(packageId)) {
|
|
224
|
-
dependents.push(id);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (dependents.length > 0) {
|
|
229
|
-
return {
|
|
230
|
-
success: false,
|
|
231
|
-
errorMessage: `Cannot uninstall ${packageId}: depended upon by ${dependents.join(', ')}`,
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Remove namespaces and package
|
|
236
|
-
this.namespaceResolver.unregister(packageId);
|
|
237
|
-
this.packages.delete(packageId);
|
|
238
|
-
this.snapshots.delete(packageId);
|
|
239
|
-
|
|
240
|
-
this.logger.info('Package uninstalled', { packageId });
|
|
241
|
-
return { success: true };
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Upgrade a package: snapshot → update → register.
|
|
246
|
-
*/
|
|
247
|
-
async upgrade(
|
|
248
|
-
packageId: string,
|
|
249
|
-
newVersion: string,
|
|
250
|
-
newManifest: Record<string, unknown>,
|
|
251
|
-
): Promise<UpgradeResult> {
|
|
252
|
-
const existing = this.packages.get(packageId);
|
|
253
|
-
if (!existing) {
|
|
254
|
-
return {
|
|
255
|
-
success: false,
|
|
256
|
-
packageId,
|
|
257
|
-
fromVersion: '',
|
|
258
|
-
toVersion: newVersion,
|
|
259
|
-
snapshot: { packageId, previousVersion: '', previousManifest: {}, previousNamespaces: [], installedAt: '', createdAt: new Date().toISOString() },
|
|
260
|
-
errorMessage: `Package ${packageId} is not installed`,
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// 1. Create snapshot for rollback
|
|
265
|
-
const snapshot: PackageSnapshot = {
|
|
266
|
-
packageId,
|
|
267
|
-
previousVersion: existing.version,
|
|
268
|
-
previousManifest: existing.manifest,
|
|
269
|
-
previousNamespaces: [...existing.namespaces],
|
|
270
|
-
installedAt: existing.installedAt,
|
|
271
|
-
createdAt: new Date().toISOString(),
|
|
272
|
-
};
|
|
273
|
-
this.snapshots.set(packageId, snapshot);
|
|
274
|
-
|
|
275
|
-
// 2. Check platform compatibility
|
|
276
|
-
const engine = (newManifest as any).engine?.objectstack as string | undefined;
|
|
277
|
-
if (engine) {
|
|
278
|
-
const platformSemver = SemanticVersionManager.parse(this.platformVersion);
|
|
279
|
-
if (!SemanticVersionManager.satisfies(platformSemver, engine)) {
|
|
280
|
-
return {
|
|
281
|
-
success: false,
|
|
282
|
-
packageId,
|
|
283
|
-
fromVersion: existing.version,
|
|
284
|
-
toVersion: newVersion,
|
|
285
|
-
snapshot,
|
|
286
|
-
errorMessage: `New version requires platform ${engine}, current is v${this.platformVersion}`,
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// 3. Check namespace changes
|
|
292
|
-
const newNamespaces = this.namespaceResolver.extractNamespaces(newManifest);
|
|
293
|
-
// Temporarily remove old namespaces to check new ones
|
|
294
|
-
this.namespaceResolver.unregister(packageId);
|
|
295
|
-
const nsCheck = this.namespaceResolver.checkAvailability(packageId, newNamespaces);
|
|
296
|
-
if (!nsCheck.available) {
|
|
297
|
-
// Restore old namespaces on failure
|
|
298
|
-
this.namespaceResolver.register(packageId, existing.namespaces);
|
|
299
|
-
return {
|
|
300
|
-
success: false,
|
|
301
|
-
packageId,
|
|
302
|
-
fromVersion: existing.version,
|
|
303
|
-
toVersion: newVersion,
|
|
304
|
-
snapshot,
|
|
305
|
-
errorMessage: `Namespace conflicts in new version: ${nsCheck.conflicts.map(c => c.namespace).join(', ')}`,
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// 4. Register new namespaces and update record
|
|
310
|
-
this.namespaceResolver.register(packageId, newNamespaces);
|
|
311
|
-
const deps = (newManifest as any).dependencies as Record<string, string> | undefined;
|
|
312
|
-
|
|
313
|
-
this.packages.set(packageId, {
|
|
314
|
-
packageId,
|
|
315
|
-
version: newVersion,
|
|
316
|
-
manifest: newManifest,
|
|
317
|
-
installedAt: existing.installedAt,
|
|
318
|
-
status: 'installed',
|
|
319
|
-
namespaces: newNamespaces,
|
|
320
|
-
dependencies: deps ? Object.keys(deps) : [],
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
this.logger.info('Package upgraded', { packageId, from: existing.version, to: newVersion });
|
|
324
|
-
|
|
325
|
-
return {
|
|
326
|
-
success: true,
|
|
327
|
-
packageId,
|
|
328
|
-
fromVersion: existing.version,
|
|
329
|
-
toVersion: newVersion,
|
|
330
|
-
snapshot,
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Rollback a package to its pre-upgrade snapshot.
|
|
336
|
-
*/
|
|
337
|
-
async rollback(packageId: string): Promise<RollbackResult> {
|
|
338
|
-
const snapshot = this.snapshots.get(packageId);
|
|
339
|
-
if (!snapshot) {
|
|
340
|
-
return {
|
|
341
|
-
success: false,
|
|
342
|
-
packageId,
|
|
343
|
-
restoredVersion: '',
|
|
344
|
-
errorMessage: `No upgrade snapshot found for ${packageId}`,
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// Restore previous state
|
|
349
|
-
this.namespaceResolver.unregister(packageId);
|
|
350
|
-
this.namespaceResolver.register(packageId, snapshot.previousNamespaces);
|
|
351
|
-
|
|
352
|
-
const deps = (snapshot.previousManifest as any).dependencies as Record<string, string> | undefined;
|
|
353
|
-
this.packages.set(packageId, {
|
|
354
|
-
packageId,
|
|
355
|
-
version: snapshot.previousVersion,
|
|
356
|
-
manifest: snapshot.previousManifest,
|
|
357
|
-
installedAt: snapshot.installedAt,
|
|
358
|
-
status: 'installed',
|
|
359
|
-
namespaces: snapshot.previousNamespaces,
|
|
360
|
-
dependencies: deps ? Object.keys(deps) : [],
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
this.snapshots.delete(packageId);
|
|
364
|
-
|
|
365
|
-
this.logger.info('Package rolled back', { packageId, to: snapshot.previousVersion });
|
|
366
|
-
|
|
367
|
-
return {
|
|
368
|
-
success: true,
|
|
369
|
-
packageId,
|
|
370
|
-
restoredVersion: snapshot.previousVersion,
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Get an installed package record.
|
|
376
|
-
*/
|
|
377
|
-
getPackage(packageId: string): InstalledPackageRecord | undefined {
|
|
378
|
-
return this.packages.get(packageId);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* List all installed packages.
|
|
383
|
-
*/
|
|
384
|
-
listPackages(): InstalledPackageRecord[] {
|
|
385
|
-
return Array.from(this.packages.values());
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Resolve dependencies for a set of packages.
|
|
390
|
-
*/
|
|
391
|
-
resolveDependencies(
|
|
392
|
-
packages: Map<string, { version?: string; dependencies?: string[] }>,
|
|
393
|
-
): string[] {
|
|
394
|
-
return this.dependencyResolver.resolve(packages);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Check namespace availability for a package's metadata.
|
|
399
|
-
*/
|
|
400
|
-
checkNamespaces(packageId: string, config: Record<string, unknown>): {
|
|
401
|
-
available: boolean;
|
|
402
|
-
conflicts: Array<{ namespace: string; existingPackageId: string }>;
|
|
403
|
-
} {
|
|
404
|
-
const namespaces = this.namespaceResolver.extractNamespaces(config);
|
|
405
|
-
const result = this.namespaceResolver.checkAvailability(packageId, namespaces);
|
|
406
|
-
return {
|
|
407
|
-
available: result.available,
|
|
408
|
-
conflicts: result.conflicts.map(c => ({
|
|
409
|
-
namespace: c.namespace,
|
|
410
|
-
existingPackageId: c.existingPackageId,
|
|
411
|
-
})),
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Get the namespace resolver instance.
|
|
417
|
-
*/
|
|
418
|
-
getNamespaceResolver(): NamespaceResolver {
|
|
419
|
-
return this.namespaceResolver;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* Get a snapshot for a given package (if available).
|
|
424
|
-
*/
|
|
425
|
-
getSnapshot(packageId: string): PackageSnapshot | undefined {
|
|
426
|
-
return this.snapshots.get(packageId);
|
|
427
|
-
}
|
|
428
|
-
}
|