@ankhorage/infra 2.0.1 → 3.1.1

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +1 -1
  3. package/dist/project/index.d.ts +4 -0
  4. package/dist/project/index.d.ts.map +1 -0
  5. package/dist/project/index.js +4 -0
  6. package/dist/project/index.js.map +1 -0
  7. package/dist/project.d.ts.map +1 -1
  8. package/dist/project.js +5 -58
  9. package/dist/project.js.map +1 -1
  10. package/dist/readme-usage.js +13 -9
  11. package/dist/readme-usage.js.map +1 -1
  12. package/dist/runtime.d.ts +23 -1
  13. package/dist/runtime.d.ts.map +1 -1
  14. package/dist/runtime.js +82 -24
  15. package/dist/runtime.js.map +1 -1
  16. package/dist/testSupport.js +1 -1
  17. package/dist/testSupport.js.map +1 -1
  18. package/package.json +7 -2
  19. package/src/adapters/minikube/auth/supabase/index.test.ts +1 -1
  20. package/src/adapters/minikube/auth/supabase/oauthRuntime.test.ts +1 -1
  21. package/src/adapters/minikube/auth/supabase/profile.integration.test.ts +1 -1
  22. package/src/adapters/minikube/auth/supabase/providerAssignments.test.ts +1 -1
  23. package/src/adapters/minikube/base/appImageBuild.test.ts +1 -1
  24. package/src/adapters/minikube/base/index.test.ts +2 -2
  25. package/src/adapters/minikube/base/twoAppIsolation.integration.test.ts +2 -2
  26. package/src/adapters/minikube/secrets/index.test.ts +1 -1
  27. package/src/commands.test.ts +3 -3
  28. package/src/generatedApiDatabase.test.ts +1 -1
  29. package/src/index.test.ts +6 -6
  30. package/src/minikubeImageLifecycle.test.ts +1 -1
  31. package/src/package.test.ts +5 -0
  32. package/src/project/index.test.ts +70 -0
  33. package/src/project/index.ts +17 -0
  34. package/src/project.test.ts +2 -2
  35. package/src/project.ts +6 -81
  36. package/src/projectContractsParser.test.ts +49 -0
  37. package/src/projectInfrastructure.test.ts +5 -5
  38. package/src/readme-usage.ts +13 -9
  39. package/src/runtime.ts +131 -38
  40. package/src/testSupport.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @ankhorage/infra
2
2
 
3
+ ## 3.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - f38e50b: Delegate project `AppManifest` structural validation to the canonical parser published by `@ankhorage/contracts` and remove Infra's duplicate manifest guard implementation.
8
+
9
+ ## 3.1.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 2e90e4a: Expose the canonical project and lifecycle application-service boundary through `@ankhorage/infra/project`, including project reconciliation, lifecycle execution, and generated port-forward endpoint resolution for trusted consumers such as Studio.
14
+
15
+ ## 3.0.0
16
+
17
+ ### Major Changes
18
+
19
+ - e4549c9: Consume the canonical Contracts 6 module manifest boundary and require `infra.modules` instead of the removed legacy `infra.plugins` field.
20
+
3
21
  ## 2.0.1
4
22
 
5
23
  ### Patch Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # INFRA
5
5
 
6
- ![license: MIT](././paradox/badges/license.svg) ![npm: v1.1.0](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg)
6
+ ![license: MIT](././paradox/badges/license.svg) ![npm: v2.0.1](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg)
7
7
 
8
8
  Executable infra provider and standalone CLI for Ankhorage project workflows.
9
9
 
@@ -0,0 +1,4 @@
1
+ export { type ResolvedInfraProject, resolveInfraProject as resolveInfraWorkspaceProject, } from '../project.js';
2
+ export { type InfraSyncResult, resolveProjectInfrastructureTarget, syncProjectInfrastructure, } from '../projectInfrastructure.js';
3
+ export { type InfraLifecycleScript, type InfraPortForwardInfo, InfraScriptExecutionError, type InfraScriptOutput, resolveProjectInfrastructurePortForward, runProjectInfrastructureLifecycle, } from '../runtime.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/project/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,oBAAoB,EACzB,mBAAmB,IAAI,4BAA4B,GACpD,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,KAAK,eAAe,EACpB,kCAAkC,EAClC,yBAAyB,GAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,yBAAyB,EACzB,KAAK,iBAAiB,EACtB,uCAAuC,EACvC,iCAAiC,GAClC,MAAM,eAAe,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { resolveInfraProject as resolveInfraWorkspaceProject, } from '../project.js';
2
+ export { resolveProjectInfrastructureTarget, syncProjectInfrastructure, } from '../projectInfrastructure.js';
3
+ export { InfraScriptExecutionError, resolveProjectInfrastructurePortForward, runProjectInfrastructureLifecycle, } from '../runtime.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/project/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,mBAAmB,IAAI,4BAA4B,GACpD,MAAM,eAAe,CAAC;AACvB,OAAO,EAEL,kCAAkC,EAClC,yBAAyB,GAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAGL,yBAAyB,EAEzB,uCAAuC,EACvC,iCAAiC,GAClC,MAAM,eAAe,CAAC","sourcesContent":["export {\n type ResolvedInfraProject,\n resolveInfraProject as resolveInfraWorkspaceProject,\n} from '../project.js';\nexport {\n type InfraSyncResult,\n resolveProjectInfrastructureTarget,\n syncProjectInfrastructure,\n} from '../projectInfrastructure.js';\nexport {\n type InfraLifecycleScript,\n type InfraPortForwardInfo,\n InfraScriptExecutionError,\n type InfraScriptOutput,\n resolveProjectInfrastructurePortForward,\n runProjectInfrastructureLifecycle,\n} from '../runtime.js';\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,WAAW,EAAsB,MAAM,sBAAsB,CAAC;AAI5F,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE;IACjD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAqChC"}
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAiB,MAAM,sBAAsB,CAAC;AAIvE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE;IACjD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAiChC"}
package/dist/project.js CHANGED
@@ -1,4 +1,4 @@
1
- import { APP_CATEGORIES } from '@ankhorage/contracts';
1
+ import { isAppManifest } from '@ankhorage/contracts';
2
2
  import { promises as fs } from 'fs';
3
3
  import path from 'path';
4
4
  export async function resolveInfraProject(options) {
@@ -18,11 +18,7 @@ export async function resolveInfraProject(options) {
18
18
  throw new Error(`Project '${projectId}' not found.`);
19
19
  }
20
20
  const manifestPath = path.join(projectPath, 'ankh.config.json');
21
- const manifest = await readProjectManifest({
22
- manifestPath,
23
- projectId,
24
- projectPath,
25
- });
21
+ const manifest = await readProjectManifest({ manifestPath, projectId });
26
22
  return {
27
23
  appsRoot,
28
24
  manifest,
@@ -51,9 +47,7 @@ function createFallbackManifest(projectId) {
51
47
  },
52
48
  themes: [],
53
49
  activeThemeId: 'default',
54
- infra: {
55
- plugins: [],
56
- },
50
+ infra: { modules: [] },
57
51
  navigator: {
58
52
  type: 'stack',
59
53
  routes: [{ name: 'index', screenId: 'index' }],
@@ -88,13 +82,11 @@ async function findWorkspaceRoot(startPath) {
88
82
  let currentPath = path.resolve(startPath);
89
83
  for (;;) {
90
84
  const appsRoot = path.join(currentPath, 'apps');
91
- if (await isDirectory(appsRoot)) {
85
+ if (await isDirectory(appsRoot))
92
86
  return currentPath;
93
- }
94
87
  const parentPath = path.dirname(currentPath);
95
- if (parentPath === currentPath) {
88
+ if (parentPath === currentPath)
96
89
  return null;
97
- }
98
90
  currentPath = parentPath;
99
91
  }
100
92
  }
@@ -119,51 +111,6 @@ function validateExplicitProjectId(projectId) {
119
111
  }
120
112
  return projectId;
121
113
  }
122
- function isAppManifest(value) {
123
- if (!isRecord(value)) {
124
- return false;
125
- }
126
- return (isAppManifestMetadata(value.metadata) &&
127
- Array.isArray(value.themes) &&
128
- typeof value.activeThemeId === 'string' &&
129
- isInfraManifestRecord(value.infra) &&
130
- isNavigatorSpec(value.navigator) &&
131
- isRecord(value.screens) &&
132
- isSettingsRecord(value.settings));
133
- }
134
- function isAppManifestMetadata(value) {
135
- return (isRecord(value) &&
136
- typeof value.name === 'string' &&
137
- typeof value.slug === 'string' &&
138
- typeof value.version === 'string' &&
139
- APP_CATEGORIES.some((category) => category === value.category) &&
140
- typeof value.themeId === 'string' &&
141
- (value.created === undefined || typeof value.created === 'string') &&
142
- (value.updated === undefined || typeof value.updated === 'string'));
143
- }
144
- function isInfraManifestRecord(value) {
145
- return isRecord(value) && Array.isArray(value.plugins);
146
- }
147
- function isNavigatorSpec(value) {
148
- return (isRecord(value) &&
149
- typeof value.type === 'string' &&
150
- Array.isArray(value.routes) &&
151
- (value.initialRouteName === undefined || typeof value.initialRouteName === 'string'));
152
- }
153
- function isSettingsRecord(value) {
154
- return (isRecord(value) &&
155
- (value.apiBaseUrl === undefined || typeof value.apiBaseUrl === 'string') &&
156
- isLocalizationRecord(value.localization));
157
- }
158
- function isLocalizationRecord(value) {
159
- return (isRecord(value) &&
160
- typeof value.defaultLocale === 'string' &&
161
- Array.isArray(value.locales) &&
162
- value.locales.every((locale) => typeof locale === 'string'));
163
- }
164
- function isRecord(value) {
165
- return typeof value === 'object' && value !== null;
166
- }
167
114
  async function isDirectory(filePath) {
168
115
  try {
169
116
  const stat = await fs.stat(filePath);
@@ -1 +1 @@
1
- {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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","sourcesContent":["import { APP_CATEGORIES, type AppManifest, type NavigatorSpec } from '@ankhorage/contracts';\nimport { promises as fs } from 'fs';\nimport path from 'path';\n\nexport interface ResolvedInfraProject {\n readonly appsRoot: string;\n readonly manifest: AppManifest;\n readonly manifestPath: string;\n readonly projectId: string;\n readonly projectPath: string;\n readonly workspaceRoot: string;\n}\n\nexport async function resolveInfraProject(options: {\n readonly cwd: string;\n readonly projectId?: string;\n}): Promise<ResolvedInfraProject> {\n const workspaceRoot = await findWorkspaceRoot(options.cwd);\n if (workspaceRoot === null) {\n throw new Error(`Could not find an Ankh workspace root from cwd: ${options.cwd}`);\n }\n\n const appsRoot = path.join(workspaceRoot, 'apps');\n const projectId =\n options.projectId === undefined\n ? inferProjectId(options.cwd, appsRoot)\n : validateExplicitProjectId(options.projectId);\n if (projectId === null) {\n throw new Error(\n 'Could not infer a project from cwd. Pass a project id or run the command inside apps/<project>.',\n );\n }\n\n const projectPath = resolveProjectPath(appsRoot, projectId);\n if (!(await pathExists(projectPath))) {\n throw new Error(`Project '${projectId}' not found.`);\n }\n\n const manifestPath = path.join(projectPath, 'ankh.config.json');\n const manifest = await readProjectManifest({\n manifestPath,\n projectId,\n projectPath,\n });\n\n return {\n appsRoot,\n manifest,\n manifestPath,\n projectId,\n projectPath,\n workspaceRoot,\n };\n}\n\nfunction resolveProjectPath(appsRoot: string, projectId: string): string {\n const projectPath = path.resolve(appsRoot, projectId);\n const appsRootPath = `${path.resolve(appsRoot)}${path.sep}`;\n\n if (!projectPath.startsWith(appsRootPath)) {\n throw new Error('Security check failed while resolving the project path.');\n }\n\n return projectPath;\n}\n\nfunction createFallbackManifest(projectId: string): AppManifest {\n return {\n metadata: {\n name: projectId,\n slug: projectId,\n version: '0.0.0',\n category: 'developer_tools',\n themeId: 'default',\n },\n themes: [],\n activeThemeId: 'default',\n infra: {\n plugins: [],\n },\n navigator: {\n type: 'stack',\n routes: [{ name: 'index', screenId: 'index' }],\n },\n screens: {},\n settings: {\n localization: {\n defaultLocale: 'en',\n locales: ['en'],\n },\n },\n };\n}\n\nasync function readProjectManifest(args: {\n readonly manifestPath: string;\n readonly projectId: string;\n readonly projectPath: string;\n}): Promise<AppManifest> {\n if (!(await pathExists(args.manifestPath))) {\n return createFallbackManifest(args.projectId);\n }\n\n const rawManifest = await fs.readFile(args.manifestPath, 'utf8');\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawManifest);\n } catch (error) {\n throw new Error(\n `Project manifest is not valid JSON: ${args.manifestPath}: ${getErrorMessage(error)}`,\n { cause: error },\n );\n }\n\n if (!isAppManifest(parsed)) {\n throw new Error(`Project manifest has an invalid shape: ${args.manifestPath}`);\n }\n\n return parsed;\n}\n\nasync function findWorkspaceRoot(startPath: string): Promise<string | null> {\n let currentPath = path.resolve(startPath);\n\n for (;;) {\n const appsRoot = path.join(currentPath, 'apps');\n if (await isDirectory(appsRoot)) {\n return currentPath;\n }\n\n const parentPath = path.dirname(currentPath);\n if (parentPath === currentPath) {\n return null;\n }\n\n currentPath = parentPath;\n }\n}\n\nfunction inferProjectId(cwd: string, appsRoot: string): string | null {\n const relativePath = path.relative(path.resolve(appsRoot), path.resolve(cwd));\n if (relativePath === '' || relativePath.startsWith('..') || path.isAbsolute(relativePath)) {\n return null;\n }\n\n const [projectId] = relativePath.split(path.sep);\n return projectId === undefined || projectId.length === 0 ? null : path.basename(projectId);\n}\n\nfunction validateExplicitProjectId(projectId: string): string {\n if (projectId.trim().length === 0) {\n throw new Error('Invalid project id: expected a single safe project segment.');\n }\n\n if (\n projectId === '.' ||\n projectId === '..' ||\n path.isAbsolute(projectId) ||\n projectId.includes('/') ||\n projectId.includes('\\\\')\n ) {\n throw new Error(`Invalid project id: ${projectId}. Expected a single safe project segment.`);\n }\n\n return projectId;\n}\n\nfunction isAppManifest(value: unknown): value is AppManifest {\n if (!isRecord(value)) {\n return false;\n }\n\n return (\n isAppManifestMetadata(value.metadata) &&\n Array.isArray(value.themes) &&\n typeof value.activeThemeId === 'string' &&\n isInfraManifestRecord(value.infra) &&\n isNavigatorSpec(value.navigator) &&\n isRecord(value.screens) &&\n isSettingsRecord(value.settings)\n );\n}\n\nfunction isAppManifestMetadata(value: unknown): value is AppManifest['metadata'] {\n return (\n isRecord(value) &&\n typeof value.name === 'string' &&\n typeof value.slug === 'string' &&\n typeof value.version === 'string' &&\n APP_CATEGORIES.some((category) => category === value.category) &&\n typeof value.themeId === 'string' &&\n (value.created === undefined || typeof value.created === 'string') &&\n (value.updated === undefined || typeof value.updated === 'string')\n );\n}\n\nfunction isInfraManifestRecord(value: unknown): value is AppManifest['infra'] {\n return isRecord(value) && Array.isArray(value.plugins);\n}\n\nfunction isNavigatorSpec(value: unknown): value is NavigatorSpec {\n return (\n isRecord(value) &&\n typeof value.type === 'string' &&\n Array.isArray(value.routes) &&\n (value.initialRouteName === undefined || typeof value.initialRouteName === 'string')\n );\n}\n\nfunction isSettingsRecord(value: unknown): value is AppManifest['settings'] {\n return (\n isRecord(value) &&\n (value.apiBaseUrl === undefined || typeof value.apiBaseUrl === 'string') &&\n isLocalizationRecord(value.localization)\n );\n}\n\nfunction isLocalizationRecord(value: unknown): value is AppManifest['settings']['localization'] {\n return (\n isRecord(value) &&\n typeof value.defaultLocale === 'string' &&\n Array.isArray(value.locales) &&\n value.locales.every((locale) => typeof locale === 'string')\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nasync function isDirectory(filePath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(filePath);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"]}
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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","sourcesContent":["import { type AppManifest, isAppManifest } from '@ankhorage/contracts';\nimport { promises as fs } from 'fs';\nimport path from 'path';\n\nexport interface ResolvedInfraProject {\n readonly appsRoot: string;\n readonly manifest: AppManifest;\n readonly manifestPath: string;\n readonly projectId: string;\n readonly projectPath: string;\n readonly workspaceRoot: string;\n}\n\nexport async function resolveInfraProject(options: {\n readonly cwd: string;\n readonly projectId?: string;\n}): Promise<ResolvedInfraProject> {\n const workspaceRoot = await findWorkspaceRoot(options.cwd);\n if (workspaceRoot === null) {\n throw new Error(`Could not find an Ankh workspace root from cwd: ${options.cwd}`);\n }\n\n const appsRoot = path.join(workspaceRoot, 'apps');\n const projectId =\n options.projectId === undefined\n ? inferProjectId(options.cwd, appsRoot)\n : validateExplicitProjectId(options.projectId);\n if (projectId === null) {\n throw new Error(\n 'Could not infer a project from cwd. Pass a project id or run the command inside apps/<project>.',\n );\n }\n\n const projectPath = resolveProjectPath(appsRoot, projectId);\n if (!(await pathExists(projectPath))) {\n throw new Error(`Project '${projectId}' not found.`);\n }\n\n const manifestPath = path.join(projectPath, 'ankh.config.json');\n const manifest = await readProjectManifest({ manifestPath, projectId });\n\n return {\n appsRoot,\n manifest,\n manifestPath,\n projectId,\n projectPath,\n workspaceRoot,\n };\n}\n\nfunction resolveProjectPath(appsRoot: string, projectId: string): string {\n const projectPath = path.resolve(appsRoot, projectId);\n const appsRootPath = `${path.resolve(appsRoot)}${path.sep}`;\n\n if (!projectPath.startsWith(appsRootPath)) {\n throw new Error('Security check failed while resolving the project path.');\n }\n\n return projectPath;\n}\n\nfunction createFallbackManifest(projectId: string): AppManifest {\n return {\n metadata: {\n name: projectId,\n slug: projectId,\n version: '0.0.0',\n category: 'developer_tools',\n themeId: 'default',\n },\n themes: [],\n activeThemeId: 'default',\n infra: { modules: [] },\n navigator: {\n type: 'stack',\n routes: [{ name: 'index', screenId: 'index' }],\n },\n screens: {},\n settings: {\n localization: {\n defaultLocale: 'en',\n locales: ['en'],\n },\n },\n };\n}\n\nasync function readProjectManifest(args: {\n readonly manifestPath: string;\n readonly projectId: string;\n}): Promise<AppManifest> {\n if (!(await pathExists(args.manifestPath))) {\n return createFallbackManifest(args.projectId);\n }\n\n const rawManifest = await fs.readFile(args.manifestPath, 'utf8');\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(rawManifest);\n } catch (error) {\n throw new Error(\n `Project manifest is not valid JSON: ${args.manifestPath}: ${getErrorMessage(error)}`,\n { cause: error },\n );\n }\n\n if (!isAppManifest(parsed)) {\n throw new Error(`Project manifest has an invalid shape: ${args.manifestPath}`);\n }\n\n return parsed;\n}\n\nasync function findWorkspaceRoot(startPath: string): Promise<string | null> {\n let currentPath = path.resolve(startPath);\n\n for (;;) {\n const appsRoot = path.join(currentPath, 'apps');\n if (await isDirectory(appsRoot)) return currentPath;\n\n const parentPath = path.dirname(currentPath);\n if (parentPath === currentPath) return null;\n currentPath = parentPath;\n }\n}\n\nfunction inferProjectId(cwd: string, appsRoot: string): string | null {\n const relativePath = path.relative(path.resolve(appsRoot), path.resolve(cwd));\n if (relativePath === '' || relativePath.startsWith('..') || path.isAbsolute(relativePath)) {\n return null;\n }\n\n const [projectId] = relativePath.split(path.sep);\n return projectId === undefined || projectId.length === 0 ? null : path.basename(projectId);\n}\n\nfunction validateExplicitProjectId(projectId: string): string {\n if (projectId.trim().length === 0) {\n throw new Error('Invalid project id: expected a single safe project segment.');\n }\n\n if (\n projectId === '.' ||\n projectId === '..' ||\n path.isAbsolute(projectId) ||\n projectId.includes('/') ||\n projectId.includes('\\\\')\n ) {\n throw new Error(`Invalid project id: ${projectId}. Expected a single safe project segment.`);\n }\n\n return projectId;\n}\n\nasync function isDirectory(filePath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(filePath);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"]}
@@ -1,10 +1,16 @@
1
1
  import { runCli } from './cli/bin.js';
2
2
  /***
3
- * Provider and CLI surface
3
+ * Provider, CLI, and project API surface
4
4
  *
5
- * `@ankhorage/infra` owns infra command behavior.
5
+ * `@ankhorage/infra` owns infrastructure generation, project reconciliation,
6
+ * and generated lifecycle execution.
6
7
  *
7
- * The same shared command implementation backs both:
8
+ * The same owner implementation backs CLI consumers and the public
9
+ * `@ankhorage/infra/project` application-service boundary. Trusted hosts such
10
+ * as Studio should call that subpath instead of reproducing ledger, generated
11
+ * file, lifecycle-script, or port-forward configuration semantics.
12
+ *
13
+ * CLI entrypoints:
8
14
  *
9
15
  * - `ankh infra ...`
10
16
  * - `bunx @ankhorage/infra ...`
@@ -17,12 +23,10 @@ import { runCli } from './cli/bin.js';
17
23
  * - `up`
18
24
  * - `down`
19
25
  *
20
- * `status` runs the generated live runtime status script for a project.
21
- *
22
- * Project resolution is project-aware:
23
- *
24
- * - pass `[project]`, or
25
- * - omit it when cwd is already inside `apps/<project>`
26
+ * Project resolution for the CLI is workspace-aware: pass `[project]`, or
27
+ * omit it when cwd is already inside `apps/<project>`. Programmatic project
28
+ * operations accept an explicit project path and do not require Studio or a
29
+ * particular workspace layout.
26
30
  *
27
31
  * @usage
28
32
  */
@@ -1 +1 @@
1
- {"version":3,"file":"readme-usage.js","sourceRoot":"","sources":["../src/readme-usage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC","sourcesContent":["import { runCli } from './cli/bin.js';\n\n/***\n * Provider and CLI surface\n *\n * `@ankhorage/infra` owns infra command behavior.\n *\n * The same shared command implementation backs both:\n *\n * - `ankh infra ...`\n * - `bunx @ankhorage/infra ...`\n *\n * Current command surface:\n *\n * - `validate`\n * - `generate`\n * - `status`\n * - `up`\n * - `down`\n *\n * `status` runs the generated live runtime status script for a project.\n *\n * Project resolution is project-aware:\n *\n * - pass `[project]`, or\n * - omit it when cwd is already inside `apps/<project>`\n *\n * @usage\n */\nawait runCli(['--help']);\n"]}
1
+ {"version":3,"file":"readme-usage.js","sourceRoot":"","sources":["../src/readme-usage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC","sourcesContent":["import { runCli } from './cli/bin.js';\n\n/***\n * Provider, CLI, and project API surface\n *\n * `@ankhorage/infra` owns infrastructure generation, project reconciliation,\n * and generated lifecycle execution.\n *\n * The same owner implementation backs CLI consumers and the public\n * `@ankhorage/infra/project` application-service boundary. Trusted hosts such\n * as Studio should call that subpath instead of reproducing ledger, generated\n * file, lifecycle-script, or port-forward configuration semantics.\n *\n * CLI entrypoints:\n *\n * - `ankh infra ...`\n * - `bunx @ankhorage/infra ...`\n *\n * Current command surface:\n *\n * - `validate`\n * - `generate`\n * - `status`\n * - `up`\n * - `down`\n *\n * Project resolution for the CLI is workspace-aware: pass `[project]`, or\n * omit it when cwd is already inside `apps/<project>`. Programmatic project\n * operations accept an explicit project path and do not require Studio or a\n * particular workspace layout.\n *\n * @usage\n */\nawait runCli(['--help']);\n"]}
package/dist/runtime.d.ts CHANGED
@@ -1,5 +1,13 @@
1
1
  import type { InfraCommandContext } from './commandContext.js';
2
- export type InfraLifecycleScript = 'down' | 'status' | 'up';
2
+ export type InfraLifecycleScript = 'destroy' | 'down' | 'port-forward' | 'reset' | 'status' | 'up';
3
+ export interface InfraScriptOutput {
4
+ readonly stderr: string;
5
+ readonly stdout: string;
6
+ }
7
+ export interface InfraPortForwardInfo {
8
+ readonly localPort: number;
9
+ readonly url: string;
10
+ }
3
11
  export declare class InfraScriptExecutionError extends Error {
4
12
  readonly exitCode: number | null;
5
13
  readonly stderr: string;
@@ -11,6 +19,20 @@ export declare class InfraScriptExecutionError extends Error {
11
19
  readonly stdout: string;
12
20
  });
13
21
  }
22
+ export declare function runProjectInfrastructureLifecycle(args: {
23
+ readonly args?: readonly string[];
24
+ readonly env?: Readonly<Record<string, string | undefined>>;
25
+ readonly onStderr?: (chunk: string) => void;
26
+ readonly onStdout?: (chunk: string) => void;
27
+ readonly projectId: string;
28
+ readonly projectPath: string;
29
+ readonly script: InfraLifecycleScript;
30
+ readonly target: string;
31
+ }): Promise<InfraScriptOutput>;
32
+ export declare function resolveProjectInfrastructurePortForward(args: {
33
+ readonly projectPath: string;
34
+ readonly target: string;
35
+ }): Promise<InfraPortForwardInfo>;
14
36
  export declare function runProjectInfraScript(args: {
15
37
  readonly context: InfraCommandContext;
16
38
  readonly projectId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;AAE5D,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,IAAI,EAAE;QAChB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB;CAOF;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC,IAAI,CAAC,CAYhB;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GAAG,MAAM,CAET"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,MAAM,GAAG,cAAc,GAAG,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC;AAEnG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,IAAI,EAAE;QAChB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB;CAOF;AAED,wBAAsB,iCAAiC,CAAC,IAAI,EAAE;IAC5D,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC5D,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAe7B;AAED,wBAAsB,uCAAuC,CAAC,IAAI,EAAE;IAClE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAkBhC;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhB;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GAAG,MAAM,CAET"}
package/dist/runtime.js CHANGED
@@ -13,16 +13,45 @@ export class InfraScriptExecutionError extends Error {
13
13
  this.stdout = args.stdout;
14
14
  }
15
15
  }
16
- export async function runProjectInfraScript(args) {
16
+ export async function runProjectInfrastructureLifecycle(args) {
17
17
  const scriptPath = resolveProjectInfraScriptPath(args);
18
18
  if (!(await pathExists(scriptPath))) {
19
19
  throw new Error(`Infra script not found: ${scriptPath}. Run infra generate for project '${args.projectId}' first.`);
20
20
  }
21
- await runShellScript({
22
- context: args.context,
21
+ return runShellScript({
22
+ args: args.args ?? [],
23
+ env: args.env ?? process.env,
24
+ onStderr: args.onStderr,
25
+ onStdout: args.onStdout,
23
26
  scriptPath,
24
27
  });
25
28
  }
29
+ export async function resolveProjectInfrastructurePortForward(args) {
30
+ const infraRoot = path.join(args.projectPath, 'infra', args.target);
31
+ const envPath = path.join(infraRoot, '.env');
32
+ const fallbackEnvPath = path.join(infraRoot, '.env.example');
33
+ const sourcePath = (await pathExists(envPath)) ? envPath : fallbackEnvPath;
34
+ const env = await readSimpleEnvMap(sourcePath);
35
+ const localPort = parsePositivePort(env.get('APP_PORT_FORWARD_LOCAL_PORT'));
36
+ if (localPort === null) {
37
+ throw new Error(`Generated Infra did not provide a valid APP_PORT_FORWARD_LOCAL_PORT in ${sourcePath}. Regenerate infrastructure first.`);
38
+ }
39
+ return {
40
+ localPort,
41
+ url: `http://127.0.0.1:${localPort}`,
42
+ };
43
+ }
44
+ export async function runProjectInfraScript(args) {
45
+ await runProjectInfrastructureLifecycle({
46
+ env: args.context.env,
47
+ onStderr: (chunk) => args.context.writeStderr(chunk),
48
+ onStdout: (chunk) => args.context.writeStdout(chunk),
49
+ projectId: args.projectId,
50
+ projectPath: args.projectPath,
51
+ script: args.script,
52
+ target: args.target,
53
+ });
54
+ }
26
55
  export function resolveProjectInfraScriptPath(args) {
27
56
  return path.join(args.projectPath, getInfraScriptsDirectory(args.target), `${args.script}.sh`);
28
57
  }
@@ -35,46 +64,75 @@ function getInfraScriptsDirectory(target) {
35
64
  }
36
65
  }
37
66
  async function runShellScript(args) {
38
- await new Promise((resolve, reject) => {
39
- const child = spawn('bash', [args.scriptPath], {
67
+ return new Promise((resolve, reject) => {
68
+ const child = spawn('bash', [args.scriptPath, ...args.args], {
40
69
  cwd: path.dirname(args.scriptPath),
41
- env: { ...args.context.env },
70
+ env: { ...args.env },
42
71
  stdio: ['ignore', 'pipe', 'pipe'],
43
72
  });
44
73
  let stdout = '';
45
74
  let stderr = '';
46
75
  child.stdout.setEncoding('utf8');
76
+ child.stderr.setEncoding('utf8');
47
77
  child.stdout.on('data', (chunk) => {
48
78
  stdout += chunk;
49
- args.context.writeStdout(chunk);
79
+ args.onStdout?.(chunk);
50
80
  });
51
- child.stderr.setEncoding('utf8');
52
81
  child.stderr.on('data', (chunk) => {
53
82
  stderr += chunk;
54
- args.context.writeStderr(chunk);
55
- });
56
- child.once('error', (error) => {
57
- reject(new InfraScriptExecutionError({
58
- exitCode: null,
59
- message: `Failed to start infra script '${args.scriptPath}': ${error.message}`,
60
- stderr,
61
- stdout,
62
- }));
83
+ args.onStderr?.(chunk);
63
84
  });
85
+ child.once('error', (error) => reject(createStartError(args.scriptPath, error, stdout, stderr)));
64
86
  child.once('close', (exitCode) => {
65
87
  if (exitCode === 0) {
66
- resolve();
88
+ resolve({ stderr, stdout });
67
89
  return;
68
90
  }
69
- reject(new InfraScriptExecutionError({
70
- exitCode,
71
- message: `Infra script '${args.scriptPath}' exited with code ${exitCode ?? 'unknown'}.`,
72
- stderr,
73
- stdout,
74
- }));
91
+ reject(createExitError(args.scriptPath, exitCode, stdout, stderr));
75
92
  });
76
93
  });
77
94
  }
95
+ function createStartError(scriptPath, error, stdout, stderr) {
96
+ return new InfraScriptExecutionError({
97
+ exitCode: null,
98
+ message: `Failed to start infra script '${scriptPath}': ${error.message}`,
99
+ stderr,
100
+ stdout,
101
+ });
102
+ }
103
+ function createExitError(scriptPath, exitCode, stdout, stderr) {
104
+ return new InfraScriptExecutionError({
105
+ exitCode,
106
+ message: `Infra script '${scriptPath}' exited with code ${exitCode ?? 'unknown'}.`,
107
+ stderr,
108
+ stdout,
109
+ });
110
+ }
111
+ async function readSimpleEnvMap(filePath) {
112
+ const out = new Map();
113
+ const content = await fs.readFile(filePath, 'utf8');
114
+ for (const line of content.split(/\r?\n/u)) {
115
+ const trimmed = line.trim();
116
+ if (!trimmed || trimmed.startsWith('#'))
117
+ continue;
118
+ const separator = trimmed.indexOf('=');
119
+ if (separator <= 0)
120
+ continue;
121
+ const key = trimmed.slice(0, separator).trim();
122
+ const value = trimmed
123
+ .slice(separator + 1)
124
+ .trim()
125
+ .replace(/^['"]|['"]$/g, '');
126
+ out.set(key, value);
127
+ }
128
+ return out;
129
+ }
130
+ function parsePositivePort(value) {
131
+ if (value === undefined)
132
+ return null;
133
+ const parsed = Number.parseInt(value.trim(), 10);
134
+ return Number.isInteger(parsed) && parsed >= 1 && parsed <= 65_535 ? parsed : null;
135
+ }
78
136
  async function pathExists(filePath) {
79
137
  try {
80
138
  await fs.access(filePath);
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from 'child_process';\nimport { promises as fs } from 'fs';\nimport path from 'path';\n\nimport type { InfraCommandContext } from './commandContext.js';\n\nexport type InfraLifecycleScript = 'down' | 'status' | 'up';\n\nexport class InfraScriptExecutionError extends Error {\n readonly exitCode: number | null;\n readonly stderr: string;\n readonly stdout: string;\n\n constructor(args: {\n readonly exitCode: number | null;\n readonly message: string;\n readonly stderr: string;\n readonly stdout: string;\n }) {\n super(args.message);\n this.name = 'InfraScriptExecutionError';\n this.exitCode = args.exitCode;\n this.stderr = args.stderr;\n this.stdout = args.stdout;\n }\n}\n\nexport async function runProjectInfraScript(args: {\n readonly context: InfraCommandContext;\n readonly projectId: string;\n readonly projectPath: string;\n readonly script: InfraLifecycleScript;\n readonly target: string;\n}): Promise<void> {\n const scriptPath = resolveProjectInfraScriptPath(args);\n if (!(await pathExists(scriptPath))) {\n throw new Error(\n `Infra script not found: ${scriptPath}. Run infra generate for project '${args.projectId}' first.`,\n );\n }\n\n await runShellScript({\n context: args.context,\n scriptPath,\n });\n}\n\nexport function resolveProjectInfraScriptPath(args: {\n readonly projectPath: string;\n readonly script: InfraLifecycleScript;\n readonly target: string;\n}): string {\n return path.join(args.projectPath, getInfraScriptsDirectory(args.target), `${args.script}.sh`);\n}\n\nfunction getInfraScriptsDirectory(target: string): string {\n switch (target) {\n case 'minikube':\n return 'infra/minikube/scripts';\n default:\n throw new Error(`Unsupported deployment target for infra scripts: ${target}`);\n }\n}\n\nasync function runShellScript(args: {\n readonly context: InfraCommandContext;\n readonly scriptPath: string;\n}): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const child = spawn('bash', [args.scriptPath], {\n cwd: path.dirname(args.scriptPath),\n env: { ...args.context.env },\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n let stdout = '';\n let stderr = '';\n\n child.stdout.setEncoding('utf8');\n child.stdout.on('data', (chunk: string) => {\n stdout += chunk;\n args.context.writeStdout(chunk);\n });\n\n child.stderr.setEncoding('utf8');\n child.stderr.on('data', (chunk: string) => {\n stderr += chunk;\n args.context.writeStderr(chunk);\n });\n\n child.once('error', (error) => {\n reject(\n new InfraScriptExecutionError({\n exitCode: null,\n message: `Failed to start infra script '${args.scriptPath}': ${error.message}`,\n stderr,\n stdout,\n }),\n );\n });\n\n child.once('close', (exitCode) => {\n if (exitCode === 0) {\n resolve();\n return;\n }\n\n reject(\n new InfraScriptExecutionError({\n exitCode,\n message: `Infra script '${args.scriptPath}' exited with code ${exitCode ?? 'unknown'}.`,\n stderr,\n stdout,\n }),\n );\n });\n });\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n"]}
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from 'child_process';\nimport { promises as fs } from 'fs';\nimport path from 'path';\n\nimport type { InfraCommandContext } from './commandContext.js';\n\nexport type InfraLifecycleScript = 'destroy' | 'down' | 'port-forward' | 'reset' | 'status' | 'up';\n\nexport interface InfraScriptOutput {\n readonly stderr: string;\n readonly stdout: string;\n}\n\nexport interface InfraPortForwardInfo {\n readonly localPort: number;\n readonly url: string;\n}\n\nexport class InfraScriptExecutionError extends Error {\n readonly exitCode: number | null;\n readonly stderr: string;\n readonly stdout: string;\n\n constructor(args: {\n readonly exitCode: number | null;\n readonly message: string;\n readonly stderr: string;\n readonly stdout: string;\n }) {\n super(args.message);\n this.name = 'InfraScriptExecutionError';\n this.exitCode = args.exitCode;\n this.stderr = args.stderr;\n this.stdout = args.stdout;\n }\n}\n\nexport async function runProjectInfrastructureLifecycle(args: {\n readonly args?: readonly string[];\n readonly env?: Readonly<Record<string, string | undefined>>;\n readonly onStderr?: (chunk: string) => void;\n readonly onStdout?: (chunk: string) => void;\n readonly projectId: string;\n readonly projectPath: string;\n readonly script: InfraLifecycleScript;\n readonly target: string;\n}): Promise<InfraScriptOutput> {\n const scriptPath = resolveProjectInfraScriptPath(args);\n if (!(await pathExists(scriptPath))) {\n throw new Error(\n `Infra script not found: ${scriptPath}. Run infra generate for project '${args.projectId}' first.`,\n );\n }\n\n return runShellScript({\n args: args.args ?? [],\n env: args.env ?? process.env,\n onStderr: args.onStderr,\n onStdout: args.onStdout,\n scriptPath,\n });\n}\n\nexport async function resolveProjectInfrastructurePortForward(args: {\n readonly projectPath: string;\n readonly target: string;\n}): Promise<InfraPortForwardInfo> {\n const infraRoot = path.join(args.projectPath, 'infra', args.target);\n const envPath = path.join(infraRoot, '.env');\n const fallbackEnvPath = path.join(infraRoot, '.env.example');\n const sourcePath = (await pathExists(envPath)) ? envPath : fallbackEnvPath;\n const env = await readSimpleEnvMap(sourcePath);\n const localPort = parsePositivePort(env.get('APP_PORT_FORWARD_LOCAL_PORT'));\n\n if (localPort === null) {\n throw new Error(\n `Generated Infra did not provide a valid APP_PORT_FORWARD_LOCAL_PORT in ${sourcePath}. Regenerate infrastructure first.`,\n );\n }\n\n return {\n localPort,\n url: `http://127.0.0.1:${localPort}`,\n };\n}\n\nexport async function runProjectInfraScript(args: {\n readonly context: InfraCommandContext;\n readonly projectId: string;\n readonly projectPath: string;\n readonly script: InfraLifecycleScript;\n readonly target: string;\n}): Promise<void> {\n await runProjectInfrastructureLifecycle({\n env: args.context.env,\n onStderr: (chunk) => args.context.writeStderr(chunk),\n onStdout: (chunk) => args.context.writeStdout(chunk),\n projectId: args.projectId,\n projectPath: args.projectPath,\n script: args.script,\n target: args.target,\n });\n}\n\nexport function resolveProjectInfraScriptPath(args: {\n readonly projectPath: string;\n readonly script: InfraLifecycleScript;\n readonly target: string;\n}): string {\n return path.join(args.projectPath, getInfraScriptsDirectory(args.target), `${args.script}.sh`);\n}\n\nfunction getInfraScriptsDirectory(target: string): string {\n switch (target) {\n case 'minikube':\n return 'infra/minikube/scripts';\n default:\n throw new Error(`Unsupported deployment target for infra scripts: ${target}`);\n }\n}\n\nasync function runShellScript(args: {\n readonly args: readonly string[];\n readonly env: Readonly<Record<string, string | undefined>>;\n readonly onStderr?: (chunk: string) => void;\n readonly onStdout?: (chunk: string) => void;\n readonly scriptPath: string;\n}): Promise<InfraScriptOutput> {\n return new Promise<InfraScriptOutput>((resolve, reject) => {\n const child = spawn('bash', [args.scriptPath, ...args.args], {\n cwd: path.dirname(args.scriptPath),\n env: { ...args.env },\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n let stdout = '';\n let stderr = '';\n child.stdout.setEncoding('utf8');\n child.stderr.setEncoding('utf8');\n child.stdout.on('data', (chunk: string) => {\n stdout += chunk;\n args.onStdout?.(chunk);\n });\n child.stderr.on('data', (chunk: string) => {\n stderr += chunk;\n args.onStderr?.(chunk);\n });\n child.once('error', (error) =>\n reject(createStartError(args.scriptPath, error, stdout, stderr)),\n );\n child.once('close', (exitCode) => {\n if (exitCode === 0) {\n resolve({ stderr, stdout });\n return;\n }\n reject(createExitError(args.scriptPath, exitCode, stdout, stderr));\n });\n });\n}\n\nfunction createStartError(\n scriptPath: string,\n error: Error,\n stdout: string,\n stderr: string,\n): InfraScriptExecutionError {\n return new InfraScriptExecutionError({\n exitCode: null,\n message: `Failed to start infra script '${scriptPath}': ${error.message}`,\n stderr,\n stdout,\n });\n}\n\nfunction createExitError(\n scriptPath: string,\n exitCode: number | null,\n stdout: string,\n stderr: string,\n): InfraScriptExecutionError {\n return new InfraScriptExecutionError({\n exitCode,\n message: `Infra script '${scriptPath}' exited with code ${exitCode ?? 'unknown'}.`,\n stderr,\n stdout,\n });\n}\n\nasync function readSimpleEnvMap(filePath: string): Promise<Map<string, string>> {\n const out = new Map<string, string>();\n const content = await fs.readFile(filePath, 'utf8');\n for (const line of content.split(/\\r?\\n/u)) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const separator = trimmed.indexOf('=');\n if (separator <= 0) continue;\n const key = trimmed.slice(0, separator).trim();\n const value = trimmed\n .slice(separator + 1)\n .trim()\n .replace(/^['\"]|['\"]$/g, '');\n out.set(key, value);\n }\n return out;\n}\n\nfunction parsePositivePort(value: string | undefined): number | null {\n if (value === undefined) return null;\n const parsed = Number.parseInt(value.trim(), 10);\n return Number.isInteger(parsed) && parsed >= 1 && parsed <= 65_535 ? parsed : null;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n"]}
@@ -40,7 +40,7 @@ export async function createWorkspaceFixture(args = {}) {
40
40
  rootPath,
41
41
  };
42
42
  }
43
- export function createAppManifest(projectId, infra = { plugins: [] }) {
43
+ export function createAppManifest(projectId, infra = { modules: [] }) {
44
44
  return {
45
45
  metadata: {
46
46
  name: projectId,
@@ -1 +1 @@
1
- {"version":3,"file":"testSupport.js","sourceRoot":"","sources":["../src/testSupport.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AppManifest } from '@ankhorage/contracts';\nimport { promises as fs } from 'fs';\nimport os from 'os';\nimport path from 'path';\n\nimport type { InfraCommandContext } from './commandContext.js';\n\nexport interface CapturedCommandContext {\n readonly context: InfraCommandContext;\n readonly stderr: { value: string };\n readonly stdout: { value: string };\n}\n\nexport function createCapturedCommandContext(\n cwd: string,\n version = '0.2.1',\n): CapturedCommandContext {\n const stdout = { value: '' };\n const stderr = { value: '' };\n\n return {\n stdout,\n stderr,\n context: {\n cwd,\n env: process.env,\n version,\n writeStdout(text: string) {\n stdout.value += text;\n },\n writeStderr(text: string) {\n stderr.value += text;\n },\n },\n };\n}\n\nexport async function createWorkspaceFixture(\n args: {\n readonly manifest?: AppManifest;\n readonly manifestFile?: 'invalid-json' | 'missing';\n readonly projectId?: string;\n } = {},\n): Promise<{\n readonly appsRoot: string;\n readonly projectId: string;\n readonly projectPath: string;\n readonly rootPath: string;\n}> {\n const rootPath = await fs.mkdtemp(path.join(os.tmpdir(), 'ankhorage-infra-'));\n const appsRoot = path.join(rootPath, 'apps');\n const projectId = args.projectId ?? 'shop';\n const projectPath = path.join(appsRoot, projectId);\n\n await fs.mkdir(projectPath, { recursive: true });\n await fs.writeFile(\n path.join(projectPath, 'package.json'),\n JSON.stringify({ name: projectId, version: '1.0.0' }, null, 2),\n 'utf8',\n );\n\n if (args.manifestFile === 'invalid-json') {\n await fs.writeFile(path.join(projectPath, 'ankh.config.json'), '{invalid', 'utf8');\n } else if (args.manifestFile !== 'missing') {\n await fs.writeFile(\n path.join(projectPath, 'ankh.config.json'),\n `${JSON.stringify(args.manifest ?? createAppManifest(projectId), null, 2)}\\n`,\n 'utf8',\n );\n }\n\n return {\n appsRoot,\n projectId,\n projectPath,\n rootPath,\n };\n}\n\nexport function createAppManifest(\n projectId: string,\n infra: AppManifest['infra'] = { plugins: [] },\n): AppManifest {\n return {\n metadata: {\n name: projectId,\n slug: projectId,\n version: '1.0.0',\n category: 'developer_tools',\n themeId: 'default',\n },\n themes: [],\n activeThemeId: 'default',\n infra,\n navigator: {\n type: 'stack',\n routes: [{ name: 'index', screenId: 'index' }],\n },\n screens: {},\n settings: {\n localization: {\n defaultLocale: 'en',\n locales: ['en'],\n },\n },\n };\n}\n"]}
1
+ {"version":3,"file":"testSupport.js","sourceRoot":"","sources":["../src/testSupport.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AppManifest } from '@ankhorage/contracts';\nimport { promises as fs } from 'fs';\nimport os from 'os';\nimport path from 'path';\n\nimport type { InfraCommandContext } from './commandContext.js';\n\nexport interface CapturedCommandContext {\n readonly context: InfraCommandContext;\n readonly stderr: { value: string };\n readonly stdout: { value: string };\n}\n\nexport function createCapturedCommandContext(\n cwd: string,\n version = '0.2.1',\n): CapturedCommandContext {\n const stdout = { value: '' };\n const stderr = { value: '' };\n\n return {\n stdout,\n stderr,\n context: {\n cwd,\n env: process.env,\n version,\n writeStdout(text: string) {\n stdout.value += text;\n },\n writeStderr(text: string) {\n stderr.value += text;\n },\n },\n };\n}\n\nexport async function createWorkspaceFixture(\n args: {\n readonly manifest?: AppManifest;\n readonly manifestFile?: 'invalid-json' | 'missing';\n readonly projectId?: string;\n } = {},\n): Promise<{\n readonly appsRoot: string;\n readonly projectId: string;\n readonly projectPath: string;\n readonly rootPath: string;\n}> {\n const rootPath = await fs.mkdtemp(path.join(os.tmpdir(), 'ankhorage-infra-'));\n const appsRoot = path.join(rootPath, 'apps');\n const projectId = args.projectId ?? 'shop';\n const projectPath = path.join(appsRoot, projectId);\n\n await fs.mkdir(projectPath, { recursive: true });\n await fs.writeFile(\n path.join(projectPath, 'package.json'),\n JSON.stringify({ name: projectId, version: '1.0.0' }, null, 2),\n 'utf8',\n );\n\n if (args.manifestFile === 'invalid-json') {\n await fs.writeFile(path.join(projectPath, 'ankh.config.json'), '{invalid', 'utf8');\n } else if (args.manifestFile !== 'missing') {\n await fs.writeFile(\n path.join(projectPath, 'ankh.config.json'),\n `${JSON.stringify(args.manifest ?? createAppManifest(projectId), null, 2)}\\n`,\n 'utf8',\n );\n }\n\n return {\n appsRoot,\n projectId,\n projectPath,\n rootPath,\n };\n}\n\nexport function createAppManifest(\n projectId: string,\n infra: AppManifest['infra'] = { modules: [] },\n): AppManifest {\n return {\n metadata: {\n name: projectId,\n slug: projectId,\n version: '1.0.0',\n category: 'developer_tools',\n themeId: 'default',\n },\n themes: [],\n activeThemeId: 'default',\n infra,\n navigator: {\n type: 'stack',\n routes: [{ name: 'index', screenId: 'index' }],\n },\n screens: {},\n settings: {\n localization: {\n defaultLocale: 'en',\n locales: ['en'],\n },\n },\n };\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/infra",
3
3
  "type": "module",
4
- "version": "2.0.1",
4
+ "version": "3.1.1",
5
5
  "description": "Project-aware infrastructure generation library and executable Ankh provider for Ankhorage.",
6
6
  "homepage": "https://github.com/ankhorage/infra#readme",
7
7
  "bugs": {
@@ -35,7 +35,7 @@
35
35
  "openapi"
36
36
  ],
37
37
  "dependencies": {
38
- "@ankhorage/contracts": "^4.0.2",
38
+ "@ankhorage/contracts": "^7.2.0",
39
39
  "@ankhorage/supabase-vault": "^0.2.4"
40
40
  },
41
41
  "files": [
@@ -62,6 +62,11 @@
62
62
  "import": "./dist/metadata/index.js",
63
63
  "default": "./dist/metadata/index.js"
64
64
  },
65
+ "./project": {
66
+ "types": "./dist/project/index.d.ts",
67
+ "import": "./dist/project/index.js",
68
+ "default": "./dist/project/index.js"
69
+ },
65
70
  "./package.json": "./package.json"
66
71
  },
67
72
  "bin": {
@@ -222,6 +222,6 @@ function createManifest(auth: Pick<NonNullable<InfraManifest['auth']>, 'profile'
222
222
  provider: 'supabase',
223
223
  tier: 'dev',
224
224
  },
225
- plugins: [],
225
+ modules: [],
226
226
  };
227
227
  }
@@ -137,7 +137,7 @@ function createOAuthManifest(): InfraManifest {
137
137
  secretStore: {
138
138
  provider: 'supabase-vault',
139
139
  },
140
- plugins: [],
140
+ modules: [],
141
141
  };
142
142
  }
143
143
 
@@ -1023,6 +1023,6 @@ function createManifest(args: {
1023
1023
  networking: {
1024
1024
  cdn: false,
1025
1025
  },
1026
- plugins: [],
1026
+ modules: [],
1027
1027
  };
1028
1028
  }
@@ -21,7 +21,7 @@ test('keeps multiple provider redirect assignments as separate shell arguments',
21
21
  },
22
22
  database: { provider: 'supabase', tier: 'dev' },
23
23
  secretStore: { provider: 'supabase-vault' },
24
- plugins: [],
24
+ modules: [],
25
25
  };
26
26
  const result = generateInfrastructure(manifest, {
27
27
  appManifest: createAppManifest('multi-oauth'),
@@ -33,7 +33,7 @@ test('re-exports rotated public environment values before building the app image
33
33
  {
34
34
  deployment: { target: 'minikube', monitoring: false },
35
35
  auth: { scope: 'global', provider: 'supabase' },
36
- plugins: [],
36
+ modules: [],
37
37
  },
38
38
  { appManifest: createAppManifest(APP_SLUG) },
39
39
  );