@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/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,