@objectstack/core 3.0.8 → 3.0.9
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 +10 -10
- package/CHANGELOG.md +7 -0
- package/dist/index.cjs +396 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +231 -1
- package/dist/index.d.ts +231 -1
- package/dist/index.js +394 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +4 -0
- package/src/namespace-resolver.test.ts +130 -0
- package/src/namespace-resolver.ts +188 -0
- package/src/package-manager.test.ts +225 -0
- package/src/package-manager.ts +428 -0
package/dist/index.js
CHANGED
|
@@ -4438,15 +4438,409 @@ var DependencyResolver = class {
|
|
|
4438
4438
|
}
|
|
4439
4439
|
}
|
|
4440
4440
|
};
|
|
4441
|
+
|
|
4442
|
+
// src/namespace-resolver.ts
|
|
4443
|
+
var NamespaceResolver = class {
|
|
4444
|
+
constructor(logger) {
|
|
4445
|
+
this.registry = /* @__PURE__ */ new Map();
|
|
4446
|
+
this.logger = logger.child({ component: "NamespaceResolver" });
|
|
4447
|
+
}
|
|
4448
|
+
/**
|
|
4449
|
+
* Register namespaces owned by a package.
|
|
4450
|
+
*/
|
|
4451
|
+
register(packageId, namespaces) {
|
|
4452
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4453
|
+
for (const ns of namespaces) {
|
|
4454
|
+
if (this.registry.has(ns)) {
|
|
4455
|
+
const existing = this.registry.get(ns);
|
|
4456
|
+
if (existing.packageId !== packageId) {
|
|
4457
|
+
this.logger.warn("Overwriting namespace entry", { namespace: ns, existing: existing.packageId, incoming: packageId });
|
|
4458
|
+
}
|
|
4459
|
+
}
|
|
4460
|
+
this.registry.set(ns, { namespace: ns, packageId, registeredAt: now });
|
|
4461
|
+
this.logger.debug("Namespace registered", { namespace: ns, packageId });
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
4464
|
+
/**
|
|
4465
|
+
* Unregister all namespaces belonging to a package.
|
|
4466
|
+
*/
|
|
4467
|
+
unregister(packageId) {
|
|
4468
|
+
const removed = [];
|
|
4469
|
+
for (const [ns, entry] of this.registry) {
|
|
4470
|
+
if (entry.packageId === packageId) {
|
|
4471
|
+
this.registry.delete(ns);
|
|
4472
|
+
removed.push(ns);
|
|
4473
|
+
}
|
|
4474
|
+
}
|
|
4475
|
+
this.logger.debug("Namespaces unregistered", { packageId, count: removed.length });
|
|
4476
|
+
return removed;
|
|
4477
|
+
}
|
|
4478
|
+
/**
|
|
4479
|
+
* Check whether a set of namespaces is available for a given package.
|
|
4480
|
+
*/
|
|
4481
|
+
checkAvailability(packageId, namespaces) {
|
|
4482
|
+
const conflicts = [];
|
|
4483
|
+
const suggestions = {};
|
|
4484
|
+
for (const ns of namespaces) {
|
|
4485
|
+
const existing = this.registry.get(ns);
|
|
4486
|
+
if (existing && existing.packageId !== packageId) {
|
|
4487
|
+
const suggestion = this.suggestAlternative(ns, packageId);
|
|
4488
|
+
conflicts.push({
|
|
4489
|
+
namespace: ns,
|
|
4490
|
+
existingPackageId: existing.packageId,
|
|
4491
|
+
incomingPackageId: packageId,
|
|
4492
|
+
suggestion
|
|
4493
|
+
});
|
|
4494
|
+
suggestions[ns] = suggestion;
|
|
4495
|
+
}
|
|
4496
|
+
}
|
|
4497
|
+
return {
|
|
4498
|
+
available: conflicts.length === 0,
|
|
4499
|
+
conflicts,
|
|
4500
|
+
suggestions
|
|
4501
|
+
};
|
|
4502
|
+
}
|
|
4503
|
+
/**
|
|
4504
|
+
* Extract namespace strings from a package's metadata definition.
|
|
4505
|
+
*/
|
|
4506
|
+
extractNamespaces(config) {
|
|
4507
|
+
const namespaces = [];
|
|
4508
|
+
const categories = [
|
|
4509
|
+
"objects",
|
|
4510
|
+
"views",
|
|
4511
|
+
"pages",
|
|
4512
|
+
"flows",
|
|
4513
|
+
"workflows",
|
|
4514
|
+
"apps",
|
|
4515
|
+
"dashboards",
|
|
4516
|
+
"reports",
|
|
4517
|
+
"actions",
|
|
4518
|
+
"agents"
|
|
4519
|
+
];
|
|
4520
|
+
for (const category of categories) {
|
|
4521
|
+
const items = config[category];
|
|
4522
|
+
if (Array.isArray(items)) {
|
|
4523
|
+
for (const item of items) {
|
|
4524
|
+
const name = item?.name;
|
|
4525
|
+
if (typeof name === "string") {
|
|
4526
|
+
namespaces.push(`${category}.${name}`);
|
|
4527
|
+
}
|
|
4528
|
+
}
|
|
4529
|
+
} else if (items && typeof items === "object") {
|
|
4530
|
+
for (const key of Object.keys(items)) {
|
|
4531
|
+
namespaces.push(`${category}.${key}`);
|
|
4532
|
+
}
|
|
4533
|
+
}
|
|
4534
|
+
}
|
|
4535
|
+
return namespaces;
|
|
4536
|
+
}
|
|
4537
|
+
/**
|
|
4538
|
+
* Get all registered entries.
|
|
4539
|
+
*/
|
|
4540
|
+
getRegistry() {
|
|
4541
|
+
return this.registry;
|
|
4542
|
+
}
|
|
4543
|
+
/**
|
|
4544
|
+
* Get all namespaces belonging to a specific package.
|
|
4545
|
+
*/
|
|
4546
|
+
getPackageNamespaces(packageId) {
|
|
4547
|
+
const namespaces = [];
|
|
4548
|
+
for (const [ns, entry] of this.registry) {
|
|
4549
|
+
if (entry.packageId === packageId) {
|
|
4550
|
+
namespaces.push(ns);
|
|
4551
|
+
}
|
|
4552
|
+
}
|
|
4553
|
+
return namespaces;
|
|
4554
|
+
}
|
|
4555
|
+
/**
|
|
4556
|
+
* Generate a prefixed alternative namespace to avoid conflicts.
|
|
4557
|
+
*/
|
|
4558
|
+
suggestAlternative(ns, packageId) {
|
|
4559
|
+
const shortName = packageId.replace(/^@[^/]+\//, "").replace(/^plugin-/, "").replace(/-/g, "_");
|
|
4560
|
+
const parts = ns.split(".");
|
|
4561
|
+
if (parts.length >= 2) {
|
|
4562
|
+
return `${parts[0]}.${shortName}_${parts.slice(1).join(".")}`;
|
|
4563
|
+
}
|
|
4564
|
+
return `${shortName}_${ns}`;
|
|
4565
|
+
}
|
|
4566
|
+
};
|
|
4567
|
+
|
|
4568
|
+
// src/package-manager.ts
|
|
4569
|
+
var PackageManager = class {
|
|
4570
|
+
constructor(logger, options = {}) {
|
|
4571
|
+
this.packages = /* @__PURE__ */ new Map();
|
|
4572
|
+
this.snapshots = /* @__PURE__ */ new Map();
|
|
4573
|
+
this.logger = logger.child({ component: "PackageManager" });
|
|
4574
|
+
this.dependencyResolver = new DependencyResolver(logger);
|
|
4575
|
+
this.namespaceResolver = new NamespaceResolver(logger);
|
|
4576
|
+
this.platformVersion = options.platformVersion || "3.0.0";
|
|
4577
|
+
}
|
|
4578
|
+
/**
|
|
4579
|
+
* Install a package with full dependency resolution and namespace checking.
|
|
4580
|
+
*/
|
|
4581
|
+
async install(packageId, version, manifest) {
|
|
4582
|
+
this.logger.info("Installing package", { packageId, version });
|
|
4583
|
+
if (this.packages.has(packageId)) {
|
|
4584
|
+
const existing = this.packages.get(packageId);
|
|
4585
|
+
if (existing.status === "installed") {
|
|
4586
|
+
return {
|
|
4587
|
+
success: false,
|
|
4588
|
+
packageId,
|
|
4589
|
+
version,
|
|
4590
|
+
installedDependencies: [],
|
|
4591
|
+
namespaceConflicts: [],
|
|
4592
|
+
errorMessage: `Package ${packageId}@${existing.version} is already installed. Use upgrade instead.`
|
|
4593
|
+
};
|
|
4594
|
+
}
|
|
4595
|
+
}
|
|
4596
|
+
const engine = manifest.engine?.objectstack;
|
|
4597
|
+
if (engine) {
|
|
4598
|
+
const platformSemver = SemanticVersionManager.parse(this.platformVersion);
|
|
4599
|
+
if (!SemanticVersionManager.satisfies(platformSemver, engine)) {
|
|
4600
|
+
return {
|
|
4601
|
+
success: false,
|
|
4602
|
+
packageId,
|
|
4603
|
+
version,
|
|
4604
|
+
installedDependencies: [],
|
|
4605
|
+
namespaceConflicts: [],
|
|
4606
|
+
errorMessage: `Package requires platform ${engine}, but current platform is v${this.platformVersion}`
|
|
4607
|
+
};
|
|
4608
|
+
}
|
|
4609
|
+
}
|
|
4610
|
+
const namespaces = this.namespaceResolver.extractNamespaces(manifest);
|
|
4611
|
+
const nsCheck = this.namespaceResolver.checkAvailability(packageId, namespaces);
|
|
4612
|
+
if (!nsCheck.available) {
|
|
4613
|
+
return {
|
|
4614
|
+
success: false,
|
|
4615
|
+
packageId,
|
|
4616
|
+
version,
|
|
4617
|
+
installedDependencies: [],
|
|
4618
|
+
namespaceConflicts: nsCheck.conflicts.map((c) => ({
|
|
4619
|
+
namespace: c.namespace,
|
|
4620
|
+
existingPackageId: c.existingPackageId
|
|
4621
|
+
})),
|
|
4622
|
+
errorMessage: `Namespace conflicts detected: ${nsCheck.conflicts.map((c) => c.namespace).join(", ")}`
|
|
4623
|
+
};
|
|
4624
|
+
}
|
|
4625
|
+
const deps = manifest.dependencies;
|
|
4626
|
+
const depNames = deps ? Object.keys(deps) : [];
|
|
4627
|
+
const missingDeps = depNames.filter((d) => !this.packages.has(d));
|
|
4628
|
+
if (missingDeps.length > 0) {
|
|
4629
|
+
return {
|
|
4630
|
+
success: false,
|
|
4631
|
+
packageId,
|
|
4632
|
+
version,
|
|
4633
|
+
installedDependencies: [],
|
|
4634
|
+
namespaceConflicts: [],
|
|
4635
|
+
errorMessage: `Missing dependencies: ${missingDeps.join(", ")}`
|
|
4636
|
+
};
|
|
4637
|
+
}
|
|
4638
|
+
this.packages.set(packageId, {
|
|
4639
|
+
packageId,
|
|
4640
|
+
version,
|
|
4641
|
+
manifest,
|
|
4642
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4643
|
+
status: "installed",
|
|
4644
|
+
namespaces,
|
|
4645
|
+
dependencies: depNames
|
|
4646
|
+
});
|
|
4647
|
+
this.namespaceResolver.register(packageId, namespaces);
|
|
4648
|
+
this.logger.info("Package installed", { packageId, version, namespaces: namespaces.length });
|
|
4649
|
+
return {
|
|
4650
|
+
success: true,
|
|
4651
|
+
packageId,
|
|
4652
|
+
version,
|
|
4653
|
+
installedDependencies: depNames,
|
|
4654
|
+
namespaceConflicts: []
|
|
4655
|
+
};
|
|
4656
|
+
}
|
|
4657
|
+
/**
|
|
4658
|
+
* Uninstall a package, checking for dependents first.
|
|
4659
|
+
*/
|
|
4660
|
+
async uninstall(packageId) {
|
|
4661
|
+
const pkg = this.packages.get(packageId);
|
|
4662
|
+
if (!pkg) {
|
|
4663
|
+
return { success: false, errorMessage: `Package ${packageId} is not installed` };
|
|
4664
|
+
}
|
|
4665
|
+
const dependents = [];
|
|
4666
|
+
for (const [id, record] of this.packages) {
|
|
4667
|
+
if (id !== packageId && record.dependencies.includes(packageId)) {
|
|
4668
|
+
dependents.push(id);
|
|
4669
|
+
}
|
|
4670
|
+
}
|
|
4671
|
+
if (dependents.length > 0) {
|
|
4672
|
+
return {
|
|
4673
|
+
success: false,
|
|
4674
|
+
errorMessage: `Cannot uninstall ${packageId}: depended upon by ${dependents.join(", ")}`
|
|
4675
|
+
};
|
|
4676
|
+
}
|
|
4677
|
+
this.namespaceResolver.unregister(packageId);
|
|
4678
|
+
this.packages.delete(packageId);
|
|
4679
|
+
this.snapshots.delete(packageId);
|
|
4680
|
+
this.logger.info("Package uninstalled", { packageId });
|
|
4681
|
+
return { success: true };
|
|
4682
|
+
}
|
|
4683
|
+
/**
|
|
4684
|
+
* Upgrade a package: snapshot → update → register.
|
|
4685
|
+
*/
|
|
4686
|
+
async upgrade(packageId, newVersion, newManifest) {
|
|
4687
|
+
const existing = this.packages.get(packageId);
|
|
4688
|
+
if (!existing) {
|
|
4689
|
+
return {
|
|
4690
|
+
success: false,
|
|
4691
|
+
packageId,
|
|
4692
|
+
fromVersion: "",
|
|
4693
|
+
toVersion: newVersion,
|
|
4694
|
+
snapshot: { packageId, previousVersion: "", previousManifest: {}, previousNamespaces: [], installedAt: "", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
4695
|
+
errorMessage: `Package ${packageId} is not installed`
|
|
4696
|
+
};
|
|
4697
|
+
}
|
|
4698
|
+
const snapshot = {
|
|
4699
|
+
packageId,
|
|
4700
|
+
previousVersion: existing.version,
|
|
4701
|
+
previousManifest: existing.manifest,
|
|
4702
|
+
previousNamespaces: [...existing.namespaces],
|
|
4703
|
+
installedAt: existing.installedAt,
|
|
4704
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4705
|
+
};
|
|
4706
|
+
this.snapshots.set(packageId, snapshot);
|
|
4707
|
+
const engine = newManifest.engine?.objectstack;
|
|
4708
|
+
if (engine) {
|
|
4709
|
+
const platformSemver = SemanticVersionManager.parse(this.platformVersion);
|
|
4710
|
+
if (!SemanticVersionManager.satisfies(platformSemver, engine)) {
|
|
4711
|
+
return {
|
|
4712
|
+
success: false,
|
|
4713
|
+
packageId,
|
|
4714
|
+
fromVersion: existing.version,
|
|
4715
|
+
toVersion: newVersion,
|
|
4716
|
+
snapshot,
|
|
4717
|
+
errorMessage: `New version requires platform ${engine}, current is v${this.platformVersion}`
|
|
4718
|
+
};
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
const newNamespaces = this.namespaceResolver.extractNamespaces(newManifest);
|
|
4722
|
+
this.namespaceResolver.unregister(packageId);
|
|
4723
|
+
const nsCheck = this.namespaceResolver.checkAvailability(packageId, newNamespaces);
|
|
4724
|
+
if (!nsCheck.available) {
|
|
4725
|
+
this.namespaceResolver.register(packageId, existing.namespaces);
|
|
4726
|
+
return {
|
|
4727
|
+
success: false,
|
|
4728
|
+
packageId,
|
|
4729
|
+
fromVersion: existing.version,
|
|
4730
|
+
toVersion: newVersion,
|
|
4731
|
+
snapshot,
|
|
4732
|
+
errorMessage: `Namespace conflicts in new version: ${nsCheck.conflicts.map((c) => c.namespace).join(", ")}`
|
|
4733
|
+
};
|
|
4734
|
+
}
|
|
4735
|
+
this.namespaceResolver.register(packageId, newNamespaces);
|
|
4736
|
+
const deps = newManifest.dependencies;
|
|
4737
|
+
this.packages.set(packageId, {
|
|
4738
|
+
packageId,
|
|
4739
|
+
version: newVersion,
|
|
4740
|
+
manifest: newManifest,
|
|
4741
|
+
installedAt: existing.installedAt,
|
|
4742
|
+
status: "installed",
|
|
4743
|
+
namespaces: newNamespaces,
|
|
4744
|
+
dependencies: deps ? Object.keys(deps) : []
|
|
4745
|
+
});
|
|
4746
|
+
this.logger.info("Package upgraded", { packageId, from: existing.version, to: newVersion });
|
|
4747
|
+
return {
|
|
4748
|
+
success: true,
|
|
4749
|
+
packageId,
|
|
4750
|
+
fromVersion: existing.version,
|
|
4751
|
+
toVersion: newVersion,
|
|
4752
|
+
snapshot
|
|
4753
|
+
};
|
|
4754
|
+
}
|
|
4755
|
+
/**
|
|
4756
|
+
* Rollback a package to its pre-upgrade snapshot.
|
|
4757
|
+
*/
|
|
4758
|
+
async rollback(packageId) {
|
|
4759
|
+
const snapshot = this.snapshots.get(packageId);
|
|
4760
|
+
if (!snapshot) {
|
|
4761
|
+
return {
|
|
4762
|
+
success: false,
|
|
4763
|
+
packageId,
|
|
4764
|
+
restoredVersion: "",
|
|
4765
|
+
errorMessage: `No upgrade snapshot found for ${packageId}`
|
|
4766
|
+
};
|
|
4767
|
+
}
|
|
4768
|
+
this.namespaceResolver.unregister(packageId);
|
|
4769
|
+
this.namespaceResolver.register(packageId, snapshot.previousNamespaces);
|
|
4770
|
+
const deps = snapshot.previousManifest.dependencies;
|
|
4771
|
+
this.packages.set(packageId, {
|
|
4772
|
+
packageId,
|
|
4773
|
+
version: snapshot.previousVersion,
|
|
4774
|
+
manifest: snapshot.previousManifest,
|
|
4775
|
+
installedAt: snapshot.installedAt,
|
|
4776
|
+
status: "installed",
|
|
4777
|
+
namespaces: snapshot.previousNamespaces,
|
|
4778
|
+
dependencies: deps ? Object.keys(deps) : []
|
|
4779
|
+
});
|
|
4780
|
+
this.snapshots.delete(packageId);
|
|
4781
|
+
this.logger.info("Package rolled back", { packageId, to: snapshot.previousVersion });
|
|
4782
|
+
return {
|
|
4783
|
+
success: true,
|
|
4784
|
+
packageId,
|
|
4785
|
+
restoredVersion: snapshot.previousVersion
|
|
4786
|
+
};
|
|
4787
|
+
}
|
|
4788
|
+
/**
|
|
4789
|
+
* Get an installed package record.
|
|
4790
|
+
*/
|
|
4791
|
+
getPackage(packageId) {
|
|
4792
|
+
return this.packages.get(packageId);
|
|
4793
|
+
}
|
|
4794
|
+
/**
|
|
4795
|
+
* List all installed packages.
|
|
4796
|
+
*/
|
|
4797
|
+
listPackages() {
|
|
4798
|
+
return Array.from(this.packages.values());
|
|
4799
|
+
}
|
|
4800
|
+
/**
|
|
4801
|
+
* Resolve dependencies for a set of packages.
|
|
4802
|
+
*/
|
|
4803
|
+
resolveDependencies(packages) {
|
|
4804
|
+
return this.dependencyResolver.resolve(packages);
|
|
4805
|
+
}
|
|
4806
|
+
/**
|
|
4807
|
+
* Check namespace availability for a package's metadata.
|
|
4808
|
+
*/
|
|
4809
|
+
checkNamespaces(packageId, config) {
|
|
4810
|
+
const namespaces = this.namespaceResolver.extractNamespaces(config);
|
|
4811
|
+
const result = this.namespaceResolver.checkAvailability(packageId, namespaces);
|
|
4812
|
+
return {
|
|
4813
|
+
available: result.available,
|
|
4814
|
+
conflicts: result.conflicts.map((c) => ({
|
|
4815
|
+
namespace: c.namespace,
|
|
4816
|
+
existingPackageId: c.existingPackageId
|
|
4817
|
+
}))
|
|
4818
|
+
};
|
|
4819
|
+
}
|
|
4820
|
+
/**
|
|
4821
|
+
* Get the namespace resolver instance.
|
|
4822
|
+
*/
|
|
4823
|
+
getNamespaceResolver() {
|
|
4824
|
+
return this.namespaceResolver;
|
|
4825
|
+
}
|
|
4826
|
+
/**
|
|
4827
|
+
* Get a snapshot for a given package (if available).
|
|
4828
|
+
*/
|
|
4829
|
+
getSnapshot(packageId) {
|
|
4830
|
+
return this.snapshots.get(packageId);
|
|
4831
|
+
}
|
|
4832
|
+
};
|
|
4441
4833
|
export {
|
|
4442
4834
|
ApiRegistry,
|
|
4443
4835
|
CORE_FALLBACK_FACTORIES,
|
|
4444
4836
|
DependencyResolver,
|
|
4445
4837
|
HotReloadManager,
|
|
4446
4838
|
LiteKernel,
|
|
4839
|
+
NamespaceResolver,
|
|
4447
4840
|
ObjectKernel,
|
|
4448
4841
|
ObjectKernelBase,
|
|
4449
4842
|
ObjectLogger,
|
|
4843
|
+
PackageManager,
|
|
4450
4844
|
PluginConfigValidator,
|
|
4451
4845
|
PluginHealthMonitor,
|
|
4452
4846
|
PluginLoader,
|