@akanjs/devkit 3.0.0-beta.6 → 3.0.0-beta.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akanjs/devkit",
3
- "version": "3.0.0-beta.6",
3
+ "version": "3.0.0-beta.8",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -44,7 +44,7 @@
44
44
  "@langchain/openai": "^1.4.6",
45
45
  "@tailwindcss/node": "^4.3.0",
46
46
  "@trapezedev/project": "^7.1.4",
47
- "akanjs": "3.0.0-beta.6",
47
+ "akanjs": "3.0.0-beta.8",
48
48
  "chalk": "^5.6.2",
49
49
  "commander": "^14.0.3",
50
50
  "dayjs": "^1.11.20",
package/subspace.test.ts CHANGED
@@ -424,6 +424,27 @@ describe("SubspaceConfig", () => {
424
424
  ).toThrow(/duplicate subspace/);
425
425
  });
426
426
 
427
+ test("refuses two subspaces on one cloud workspace, and takes a declaration with none", () => {
428
+ expect(
429
+ () =>
430
+ new SubspaceConfig({
431
+ subspaces: [
432
+ { name: "a", repo: "a.git", apps: ["one"], workspaceId: "project" },
433
+ { name: "b", repo: "b.git", apps: ["two"], workspaceId: "project" },
434
+ ],
435
+ }),
436
+ ).toThrow(/same workspaceId/);
437
+ expect(
438
+ () =>
439
+ new SubspaceConfig({
440
+ subspaces: [
441
+ { name: "a", repo: "a.git", apps: ["one"] },
442
+ { name: "b", repo: "b.git", apps: ["two"], workspaceId: "project" },
443
+ ],
444
+ }),
445
+ ).not.toThrow();
446
+ });
447
+
427
448
  test("defaults the pushable branches and rejects anything else", () => {
428
449
  const config = new SubspaceConfig({ subspaces: [{ name: "a", repo: "a.git", apps: ["one"] }] });
429
450
  expect(config.pushableBranches).toEqual(SubspaceConfig.defaultPushableBranches);
package/subspace.ts CHANGED
@@ -55,6 +55,17 @@ export interface SubspacePushResult {
55
55
  prunedDependencies?: string[];
56
56
  }
57
57
 
58
+ export interface SubspaceEnvUploadResult {
59
+ name: string;
60
+ /** The cloud workspace whose env archive was replaced: the subspace's own, never this workspace's. */
61
+ workspaceId: string;
62
+ host: string;
63
+ outcome: "uploaded" | "cancelled";
64
+ apps: string[];
65
+ libs: string[];
66
+ files: string[];
67
+ }
68
+
58
69
  export interface SubspacePullResult {
59
70
  name: string;
60
71
  applied: string[];
@@ -100,6 +111,9 @@ export class Subspace {
100
111
  get apps() {
101
112
  return this.#declaration.apps;
102
113
  }
114
+ get workspaceId() {
115
+ return this.#declaration.workspaceId;
116
+ }
103
117
  get #remote() {
104
118
  return `subspace-${this.#declaration.name}`;
105
119
  }
@@ -700,6 +714,25 @@ export function formatSubspacePullResult(result: SubspacePullResult) {
700
714
  return sections.join("\n");
701
715
  }
702
716
 
717
+ export function formatSubspaceEnvUpload(result: SubspaceEnvUploadResult) {
718
+ const sections = [
719
+ `Akan Subspace Env Upload — ${result.name}`,
720
+ `cloud workspace: ${result.workspaceId} · host: ${result.host}`,
721
+ "",
722
+ ...(result.outcome === "cancelled"
723
+ ? [" cancelled — the subspace's env archive is untouched."]
724
+ : [
725
+ `Uploaded (${result.files.length}), replacing that workspace's whole archive:`,
726
+ "",
727
+ ...result.files.map((file) => ` ${file}`),
728
+ "",
729
+ ` apps: ${result.apps.join(", ")}`,
730
+ ` libs: ${result.libs.join(", ") || "(none)"}`,
731
+ ]),
732
+ ];
733
+ return sections.join("\n");
734
+ }
735
+
703
736
  export function formatSubspaceDiff(result: SubspaceDiffResult) {
704
737
  const total = result.app.files.length + result.libs.files.length;
705
738
  const sections = [
package/subspaceConfig.ts CHANGED
@@ -37,6 +37,7 @@ export class SubspaceConfig {
37
37
  #assertValid() {
38
38
  const seenNames = new Set<string>();
39
39
  const appOwners = new Map<string, string>();
40
+ const idOwners = new Map<string, string>();
40
41
  for (const subspace of this.subspaces) {
41
42
  if (!subspace.name || !subspace.repo)
42
43
  throw new Error(`${SubspaceConfig.fileName}: every subspace needs a name and a repo`);
@@ -55,6 +56,15 @@ export class SubspaceConfig {
55
56
  );
56
57
  appOwners.set(app, subspace.name);
57
58
  }
59
+ if (!subspace.workspaceId) continue;
60
+ const idOwner = idOwners.get(subspace.workspaceId);
61
+ //* An env upload replaces the cloud workspace's archive whole, so two subspaces sharing one would
62
+ //* each overwrite the other's values.
63
+ if (idOwner)
64
+ throw new Error(
65
+ `${SubspaceConfig.fileName}: subspaces "${idOwner}" and "${subspace.name}" declare the same workspaceId`,
66
+ );
67
+ idOwners.set(subspace.workspaceId, subspace.name);
58
68
  }
59
69
  }
60
70