@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.
- package/CHANGELOG.md +18 -0
- package/README.md +1 -1
- package/dist/project/index.d.ts +4 -0
- package/dist/project/index.d.ts.map +1 -0
- package/dist/project/index.js +4 -0
- package/dist/project/index.js.map +1 -0
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +5 -58
- package/dist/project.js.map +1 -1
- package/dist/readme-usage.js +13 -9
- package/dist/readme-usage.js.map +1 -1
- package/dist/runtime.d.ts +23 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +82 -24
- package/dist/runtime.js.map +1 -1
- package/dist/testSupport.js +1 -1
- package/dist/testSupport.js.map +1 -1
- package/package.json +7 -2
- package/src/adapters/minikube/auth/supabase/index.test.ts +1 -1
- package/src/adapters/minikube/auth/supabase/oauthRuntime.test.ts +1 -1
- package/src/adapters/minikube/auth/supabase/profile.integration.test.ts +1 -1
- package/src/adapters/minikube/auth/supabase/providerAssignments.test.ts +1 -1
- package/src/adapters/minikube/base/appImageBuild.test.ts +1 -1
- package/src/adapters/minikube/base/index.test.ts +2 -2
- package/src/adapters/minikube/base/twoAppIsolation.integration.test.ts +2 -2
- package/src/adapters/minikube/secrets/index.test.ts +1 -1
- package/src/commands.test.ts +3 -3
- package/src/generatedApiDatabase.test.ts +1 -1
- package/src/index.test.ts +6 -6
- package/src/minikubeImageLifecycle.test.ts +1 -1
- package/src/package.test.ts +5 -0
- package/src/project/index.test.ts +70 -0
- package/src/project/index.ts +17 -0
- package/src/project.test.ts +2 -2
- package/src/project.ts +6 -81
- package/src/projectContractsParser.test.ts +49 -0
- package/src/projectInfrastructure.test.ts +5 -5
- package/src/readme-usage.ts +13 -9
- package/src/runtime.ts +131 -38
- package/src/testSupport.ts +1 -1
|
@@ -101,7 +101,7 @@ describe('generateMinikubeBaseArtifacts app-owned cluster model', () => {
|
|
|
101
101
|
target: 'minikube',
|
|
102
102
|
monitoring: false,
|
|
103
103
|
},
|
|
104
|
-
|
|
104
|
+
modules: [],
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
appManifest: createAppManifest('frontend-only'),
|
|
@@ -508,7 +508,7 @@ function createSupabaseManifest(): InfraManifestInput {
|
|
|
508
508
|
provider: 'supabase',
|
|
509
509
|
buckets: ['assets'],
|
|
510
510
|
},
|
|
511
|
-
|
|
511
|
+
modules: [],
|
|
512
512
|
};
|
|
513
513
|
}
|
|
514
514
|
|
|
@@ -140,7 +140,7 @@ function generateAppOnlyInfrastructure(slug: string) {
|
|
|
140
140
|
target: 'minikube',
|
|
141
141
|
monitoring: false,
|
|
142
142
|
},
|
|
143
|
-
|
|
143
|
+
modules: [],
|
|
144
144
|
};
|
|
145
145
|
|
|
146
146
|
return generateInfrastructure(manifest, {
|
|
@@ -280,7 +280,7 @@ function generateSupabaseInfrastructure(slug: string) {
|
|
|
280
280
|
secretStore: {
|
|
281
281
|
provider: 'supabase-vault',
|
|
282
282
|
},
|
|
283
|
-
|
|
283
|
+
modules: [],
|
|
284
284
|
};
|
|
285
285
|
|
|
286
286
|
return generateInfrastructure(manifest, {
|
package/src/commands.test.ts
CHANGED
|
@@ -66,7 +66,7 @@ describe('infra command table', () => {
|
|
|
66
66
|
manifestPath: '/workspace/apps/shop/ankh.config.json',
|
|
67
67
|
projectId: 'shop',
|
|
68
68
|
projectPath: '/workspace/apps/shop',
|
|
69
|
-
manifest: createAppManifest('shop', {
|
|
69
|
+
manifest: createAppManifest('shop', { modules: [] }),
|
|
70
70
|
});
|
|
71
71
|
},
|
|
72
72
|
validateInfraSupport() {
|
|
@@ -104,7 +104,7 @@ describe('infra command table', () => {
|
|
|
104
104
|
manifestPath: '/workspace/apps/studio/ankh.config.json',
|
|
105
105
|
projectId: 'studio',
|
|
106
106
|
projectPath: '/workspace/apps/studio',
|
|
107
|
-
manifest: createAppManifest('studio', {
|
|
107
|
+
manifest: createAppManifest('studio', { modules: [] }),
|
|
108
108
|
});
|
|
109
109
|
},
|
|
110
110
|
syncProjectInfrastructure() {
|
|
@@ -146,7 +146,7 @@ describe('infra command table', () => {
|
|
|
146
146
|
projectPath: '/workspace/apps/shop',
|
|
147
147
|
manifest: createAppManifest('shop', {
|
|
148
148
|
deployment: { target: 'minikube', monitoring: false },
|
|
149
|
-
|
|
149
|
+
modules: [],
|
|
150
150
|
}),
|
|
151
151
|
});
|
|
152
152
|
},
|
|
@@ -327,7 +327,7 @@ describe('generated API database bridge', () => {
|
|
|
327
327
|
const infra: InfraManifestInput = {
|
|
328
328
|
deployment: { target: 'minikube', monitoring: false },
|
|
329
329
|
database: { provider: 'supabase', tier: 'dev' },
|
|
330
|
-
|
|
330
|
+
modules: [],
|
|
331
331
|
};
|
|
332
332
|
const appManifest = {
|
|
333
333
|
...createAppManifest('catalog', infra),
|
package/src/index.test.ts
CHANGED
|
@@ -42,7 +42,7 @@ describe('generateInfrastructure', () => {
|
|
|
42
42
|
domain: 'alpha.local',
|
|
43
43
|
cdn: false,
|
|
44
44
|
},
|
|
45
|
-
|
|
45
|
+
modules: [],
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
const result = generateInfrastructure(manifest, { appManifest: createAppManifest('alpha') });
|
|
@@ -125,7 +125,7 @@ describe('generateInfrastructure', () => {
|
|
|
125
125
|
provider: 'supabase',
|
|
126
126
|
buckets: ['assets', ' assets ', '', 'assets', 'uploads'],
|
|
127
127
|
},
|
|
128
|
-
|
|
128
|
+
modules: [],
|
|
129
129
|
};
|
|
130
130
|
|
|
131
131
|
const result = generateInfrastructure(manifest, {
|
|
@@ -163,7 +163,7 @@ describe('generateInfrastructure', () => {
|
|
|
163
163
|
provider: 'supabase',
|
|
164
164
|
tier: 'dev',
|
|
165
165
|
},
|
|
166
|
-
|
|
166
|
+
modules: [],
|
|
167
167
|
};
|
|
168
168
|
|
|
169
169
|
const appManifest: Pick<AppManifest, 'metadata' | 'navigator' | 'screens' | 'settings'> = {
|
|
@@ -237,7 +237,7 @@ describe('generateInfrastructure', () => {
|
|
|
237
237
|
|
|
238
238
|
test('throws for missing deployment', () => {
|
|
239
239
|
const manifest: InfraManifestInput = {
|
|
240
|
-
|
|
240
|
+
modules: [],
|
|
241
241
|
};
|
|
242
242
|
|
|
243
243
|
expect(() => generateInfrastructure(manifest)).toThrow('Deployment configuration is missing.');
|
|
@@ -249,7 +249,7 @@ describe('generateInfrastructure', () => {
|
|
|
249
249
|
target: 'kubernetes',
|
|
250
250
|
monitoring: false,
|
|
251
251
|
},
|
|
252
|
-
|
|
252
|
+
modules: [],
|
|
253
253
|
};
|
|
254
254
|
|
|
255
255
|
expect(() => generateInfrastructure(manifest)).toThrow(
|
|
@@ -267,7 +267,7 @@ describe('generateInfrastructure', () => {
|
|
|
267
267
|
provider: 'supabase',
|
|
268
268
|
tier: 'dev',
|
|
269
269
|
},
|
|
270
|
-
|
|
270
|
+
modules: [],
|
|
271
271
|
};
|
|
272
272
|
|
|
273
273
|
expect(() => generateInfrastructure(manifest, { namespaceHint: 'shop' })).toThrow(
|
package/src/package.test.ts
CHANGED
|
@@ -24,6 +24,11 @@ describe('package metadata', () => {
|
|
|
24
24
|
'ankhorage-infra': './dist/cli/bin.js',
|
|
25
25
|
});
|
|
26
26
|
expect(packageJson.ankh).toEqual(expectedAnkhMetadata);
|
|
27
|
+
expect(packageJson.exports['./project']).toEqual({
|
|
28
|
+
types: './dist/project/index.d.ts',
|
|
29
|
+
import: './dist/project/index.js',
|
|
30
|
+
default: './dist/project/index.js',
|
|
31
|
+
});
|
|
27
32
|
expect(JSON.parse(JSON.stringify(expectedAnkhMetadata))).toEqual(expectedAnkhMetadata);
|
|
28
33
|
|
|
29
34
|
const capabilityText = JSON.stringify(packageJson.ankh.capabilities);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from 'bun:test';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
resolveProjectInfrastructurePortForward,
|
|
8
|
+
runProjectInfrastructureLifecycle,
|
|
9
|
+
} from './index.js';
|
|
10
|
+
|
|
11
|
+
const temporaryPaths: string[] = [];
|
|
12
|
+
|
|
13
|
+
afterEach(async () => {
|
|
14
|
+
await Promise.all(
|
|
15
|
+
temporaryPaths.splice(0).map((entry) => fs.rm(entry, { force: true, recursive: true })),
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
async function createProjectFixture(): Promise<string> {
|
|
20
|
+
const projectPath = await fs.mkdtemp(path.join(os.tmpdir(), 'ankh-infra-project-'));
|
|
21
|
+
temporaryPaths.push(projectPath);
|
|
22
|
+
const infraRoot = path.join(projectPath, 'infra', 'minikube');
|
|
23
|
+
const scriptsRoot = path.join(infraRoot, 'scripts');
|
|
24
|
+
await fs.mkdir(scriptsRoot, { recursive: true });
|
|
25
|
+
await fs.writeFile(
|
|
26
|
+
path.join(scriptsRoot, 'status.sh'),
|
|
27
|
+
"#!/usr/bin/env bash\nprintf 'status-ok'\nprintf 'status-warning' >&2\n",
|
|
28
|
+
'utf8',
|
|
29
|
+
);
|
|
30
|
+
await fs.writeFile(
|
|
31
|
+
path.join(infraRoot, '.env.example'),
|
|
32
|
+
'APP_PORT_FORWARD_LOCAL_PORT=48123\n',
|
|
33
|
+
'utf8',
|
|
34
|
+
);
|
|
35
|
+
return projectPath;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe('@ankhorage/infra/project', () => {
|
|
39
|
+
test('executes generated lifecycle scripts without exposing script paths to consumers', async () => {
|
|
40
|
+
const projectPath = await createProjectFixture();
|
|
41
|
+
const stdout: string[] = [];
|
|
42
|
+
const stderr: string[] = [];
|
|
43
|
+
|
|
44
|
+
const result = await runProjectInfrastructureLifecycle({
|
|
45
|
+
projectId: 'example',
|
|
46
|
+
projectPath,
|
|
47
|
+
script: 'status',
|
|
48
|
+
target: 'minikube',
|
|
49
|
+
onStdout: (chunk) => stdout.push(chunk),
|
|
50
|
+
onStderr: (chunk) => stderr.push(chunk),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(result).toEqual({ stdout: 'status-ok', stderr: 'status-warning' });
|
|
54
|
+
expect(stdout.join('')).toBe('status-ok');
|
|
55
|
+
expect(stderr.join('')).toBe('status-warning');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('resolves the generated app port-forward endpoint through the owner API', async () => {
|
|
59
|
+
const projectPath = await createProjectFixture();
|
|
60
|
+
const result = await resolveProjectInfrastructurePortForward({
|
|
61
|
+
projectPath,
|
|
62
|
+
target: 'minikube',
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(result).toEqual({
|
|
66
|
+
localPort: 48123,
|
|
67
|
+
url: 'http://127.0.0.1:48123',
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export {
|
|
2
|
+
type ResolvedInfraProject,
|
|
3
|
+
resolveInfraProject as resolveInfraWorkspaceProject,
|
|
4
|
+
} from '../project.js';
|
|
5
|
+
export {
|
|
6
|
+
type InfraSyncResult,
|
|
7
|
+
resolveProjectInfrastructureTarget,
|
|
8
|
+
syncProjectInfrastructure,
|
|
9
|
+
} from '../projectInfrastructure.js';
|
|
10
|
+
export {
|
|
11
|
+
type InfraLifecycleScript,
|
|
12
|
+
type InfraPortForwardInfo,
|
|
13
|
+
InfraScriptExecutionError,
|
|
14
|
+
type InfraScriptOutput,
|
|
15
|
+
resolveProjectInfrastructurePortForward,
|
|
16
|
+
runProjectInfrastructureLifecycle,
|
|
17
|
+
} from '../runtime.js';
|
package/src/project.test.ts
CHANGED
|
@@ -58,7 +58,7 @@ describe('resolveInfraProject', () => {
|
|
|
58
58
|
|
|
59
59
|
expect(project.manifest.metadata.name).toBe('shop');
|
|
60
60
|
expect(project.manifest.metadata.slug).toBe('shop');
|
|
61
|
-
expect(project.manifest.infra.
|
|
61
|
+
expect(project.manifest.infra.modules).toEqual([]);
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
test('rejects malformed manifest JSON', async () => {
|
|
@@ -186,7 +186,7 @@ describe('resolveInfraProject', () => {
|
|
|
186
186
|
monitoring: false,
|
|
187
187
|
target: 'minikube',
|
|
188
188
|
},
|
|
189
|
-
|
|
189
|
+
modules: [],
|
|
190
190
|
});
|
|
191
191
|
const fixture = await createWorkspaceFixture({
|
|
192
192
|
manifest,
|
package/src/project.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type AppManifest, isAppManifest } from '@ankhorage/contracts';
|
|
2
2
|
import { promises as fs } from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
|
|
@@ -37,11 +37,7 @@ export async function resolveInfraProject(options: {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const manifestPath = path.join(projectPath, 'ankh.config.json');
|
|
40
|
-
const manifest = await readProjectManifest({
|
|
41
|
-
manifestPath,
|
|
42
|
-
projectId,
|
|
43
|
-
projectPath,
|
|
44
|
-
});
|
|
40
|
+
const manifest = await readProjectManifest({ manifestPath, projectId });
|
|
45
41
|
|
|
46
42
|
return {
|
|
47
43
|
appsRoot,
|
|
@@ -75,9 +71,7 @@ function createFallbackManifest(projectId: string): AppManifest {
|
|
|
75
71
|
},
|
|
76
72
|
themes: [],
|
|
77
73
|
activeThemeId: 'default',
|
|
78
|
-
infra: {
|
|
79
|
-
plugins: [],
|
|
80
|
-
},
|
|
74
|
+
infra: { modules: [] },
|
|
81
75
|
navigator: {
|
|
82
76
|
type: 'stack',
|
|
83
77
|
routes: [{ name: 'index', screenId: 'index' }],
|
|
@@ -95,15 +89,14 @@ function createFallbackManifest(projectId: string): AppManifest {
|
|
|
95
89
|
async function readProjectManifest(args: {
|
|
96
90
|
readonly manifestPath: string;
|
|
97
91
|
readonly projectId: string;
|
|
98
|
-
readonly projectPath: string;
|
|
99
92
|
}): Promise<AppManifest> {
|
|
100
93
|
if (!(await pathExists(args.manifestPath))) {
|
|
101
94
|
return createFallbackManifest(args.projectId);
|
|
102
95
|
}
|
|
103
96
|
|
|
104
97
|
const rawManifest = await fs.readFile(args.manifestPath, 'utf8');
|
|
105
|
-
|
|
106
98
|
let parsed: unknown;
|
|
99
|
+
|
|
107
100
|
try {
|
|
108
101
|
parsed = JSON.parse(rawManifest);
|
|
109
102
|
} catch (error) {
|
|
@@ -125,15 +118,10 @@ async function findWorkspaceRoot(startPath: string): Promise<string | null> {
|
|
|
125
118
|
|
|
126
119
|
for (;;) {
|
|
127
120
|
const appsRoot = path.join(currentPath, 'apps');
|
|
128
|
-
if (await isDirectory(appsRoot))
|
|
129
|
-
return currentPath;
|
|
130
|
-
}
|
|
121
|
+
if (await isDirectory(appsRoot)) return currentPath;
|
|
131
122
|
|
|
132
123
|
const parentPath = path.dirname(currentPath);
|
|
133
|
-
if (parentPath === currentPath)
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
|
|
124
|
+
if (parentPath === currentPath) return null;
|
|
137
125
|
currentPath = parentPath;
|
|
138
126
|
}
|
|
139
127
|
}
|
|
@@ -166,69 +154,6 @@ function validateExplicitProjectId(projectId: string): string {
|
|
|
166
154
|
return projectId;
|
|
167
155
|
}
|
|
168
156
|
|
|
169
|
-
function isAppManifest(value: unknown): value is AppManifest {
|
|
170
|
-
if (!isRecord(value)) {
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return (
|
|
175
|
-
isAppManifestMetadata(value.metadata) &&
|
|
176
|
-
Array.isArray(value.themes) &&
|
|
177
|
-
typeof value.activeThemeId === 'string' &&
|
|
178
|
-
isInfraManifestRecord(value.infra) &&
|
|
179
|
-
isNavigatorSpec(value.navigator) &&
|
|
180
|
-
isRecord(value.screens) &&
|
|
181
|
-
isSettingsRecord(value.settings)
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function isAppManifestMetadata(value: unknown): value is AppManifest['metadata'] {
|
|
186
|
-
return (
|
|
187
|
-
isRecord(value) &&
|
|
188
|
-
typeof value.name === 'string' &&
|
|
189
|
-
typeof value.slug === 'string' &&
|
|
190
|
-
typeof value.version === 'string' &&
|
|
191
|
-
APP_CATEGORIES.some((category) => category === value.category) &&
|
|
192
|
-
typeof value.themeId === 'string' &&
|
|
193
|
-
(value.created === undefined || typeof value.created === 'string') &&
|
|
194
|
-
(value.updated === undefined || typeof value.updated === 'string')
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function isInfraManifestRecord(value: unknown): value is AppManifest['infra'] {
|
|
199
|
-
return isRecord(value) && Array.isArray(value.plugins);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function isNavigatorSpec(value: unknown): value is NavigatorSpec {
|
|
203
|
-
return (
|
|
204
|
-
isRecord(value) &&
|
|
205
|
-
typeof value.type === 'string' &&
|
|
206
|
-
Array.isArray(value.routes) &&
|
|
207
|
-
(value.initialRouteName === undefined || typeof value.initialRouteName === 'string')
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function isSettingsRecord(value: unknown): value is AppManifest['settings'] {
|
|
212
|
-
return (
|
|
213
|
-
isRecord(value) &&
|
|
214
|
-
(value.apiBaseUrl === undefined || typeof value.apiBaseUrl === 'string') &&
|
|
215
|
-
isLocalizationRecord(value.localization)
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function isLocalizationRecord(value: unknown): value is AppManifest['settings']['localization'] {
|
|
220
|
-
return (
|
|
221
|
-
isRecord(value) &&
|
|
222
|
-
typeof value.defaultLocale === 'string' &&
|
|
223
|
-
Array.isArray(value.locales) &&
|
|
224
|
-
value.locales.every((locale) => typeof locale === 'string')
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
229
|
-
return typeof value === 'object' && value !== null;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
157
|
async function isDirectory(filePath: string): Promise<boolean> {
|
|
233
158
|
try {
|
|
234
159
|
const stat = await fs.stat(filePath);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { afterEach, expect, test } from 'bun:test';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
import { resolveInfraProject } from './project.js';
|
|
6
|
+
import { createAppManifest, createWorkspaceFixture } from './testSupport.js';
|
|
7
|
+
|
|
8
|
+
const tempRoots = new Set<string>();
|
|
9
|
+
|
|
10
|
+
afterEach(async () => {
|
|
11
|
+
await Promise.all(
|
|
12
|
+
[...tempRoots].map((rootPath) => fs.rm(rootPath, { force: true, recursive: true })),
|
|
13
|
+
);
|
|
14
|
+
tempRoots.clear();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('Infra rejects nested manifest shapes rejected by Contracts', async () => {
|
|
18
|
+
const manifest = createAppManifest('cards', { modules: [] });
|
|
19
|
+
const fixture = await createWorkspaceFixture({ manifest, projectId: 'cards' });
|
|
20
|
+
tempRoots.add(fixture.rootPath);
|
|
21
|
+
|
|
22
|
+
const malformedManifest = {
|
|
23
|
+
...manifest,
|
|
24
|
+
navigator: {
|
|
25
|
+
...manifest.navigator,
|
|
26
|
+
type: 'not-a-canonical-navigator',
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
await fs.writeFile(
|
|
30
|
+
path.join(fixture.projectPath, 'ankh.config.json'),
|
|
31
|
+
JSON.stringify(malformedManifest),
|
|
32
|
+
'utf8',
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const error = await captureError(
|
|
36
|
+
resolveInfraProject({ cwd: fixture.rootPath, projectId: fixture.projectId }),
|
|
37
|
+
);
|
|
38
|
+
expect(error.message).toContain('Project manifest has an invalid shape');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
async function captureError(promise: Promise<unknown>): Promise<Error> {
|
|
42
|
+
try {
|
|
43
|
+
await promise;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (error instanceof Error) return error;
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
throw new Error('Expected project resolution to reject.');
|
|
49
|
+
}
|
|
@@ -29,7 +29,7 @@ describe('project infrastructure sync', () => {
|
|
|
29
29
|
projectPath,
|
|
30
30
|
manifest: createAppManifest('shop', {
|
|
31
31
|
deployment: { target: 'minikube', monitoring: false },
|
|
32
|
-
|
|
32
|
+
modules: [],
|
|
33
33
|
}),
|
|
34
34
|
generateInfrastructureImpl() {
|
|
35
35
|
return {
|
|
@@ -77,7 +77,7 @@ describe('project infrastructure sync', () => {
|
|
|
77
77
|
|
|
78
78
|
const manifest = createAppManifest('shop', {
|
|
79
79
|
deployment: { target: 'minikube', monitoring: false },
|
|
80
|
-
|
|
80
|
+
modules: [],
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
await syncProjectInfrastructure({
|
|
@@ -141,7 +141,7 @@ describe('project infrastructure sync', () => {
|
|
|
141
141
|
const result = await syncProjectInfrastructure({
|
|
142
142
|
projectId: 'shop',
|
|
143
143
|
projectPath,
|
|
144
|
-
manifest: createAppManifest('shop', {
|
|
144
|
+
manifest: createAppManifest('shop', { modules: [] }),
|
|
145
145
|
});
|
|
146
146
|
|
|
147
147
|
expect(result.generated).toBe(0);
|
|
@@ -158,7 +158,7 @@ describe('project infrastructure sync', () => {
|
|
|
158
158
|
projectPath,
|
|
159
159
|
manifest: createAppManifest('studio', {
|
|
160
160
|
deployment: { target: 'minikube', monitoring: false },
|
|
161
|
-
|
|
161
|
+
modules: [],
|
|
162
162
|
}),
|
|
163
163
|
});
|
|
164
164
|
|
|
@@ -183,7 +183,7 @@ describe('project infrastructure sync', () => {
|
|
|
183
183
|
);
|
|
184
184
|
|
|
185
185
|
const target = await resolveProjectInfrastructureTarget({
|
|
186
|
-
manifest: createAppManifest('shop', {
|
|
186
|
+
manifest: createAppManifest('shop', { modules: [] }),
|
|
187
187
|
projectPath,
|
|
188
188
|
});
|
|
189
189
|
|
package/src/readme-usage.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { runCli } from './cli/bin.js';
|
|
2
2
|
|
|
3
3
|
/***
|
|
4
|
-
* Provider and
|
|
4
|
+
* Provider, CLI, and project API surface
|
|
5
5
|
*
|
|
6
|
-
* `@ankhorage/infra` owns
|
|
6
|
+
* `@ankhorage/infra` owns infrastructure generation, project reconciliation,
|
|
7
|
+
* and generated lifecycle execution.
|
|
7
8
|
*
|
|
8
|
-
* The same
|
|
9
|
+
* The same owner implementation backs CLI consumers and the public
|
|
10
|
+
* `@ankhorage/infra/project` application-service boundary. Trusted hosts such
|
|
11
|
+
* as Studio should call that subpath instead of reproducing ledger, generated
|
|
12
|
+
* file, lifecycle-script, or port-forward configuration semantics.
|
|
13
|
+
*
|
|
14
|
+
* CLI entrypoints:
|
|
9
15
|
*
|
|
10
16
|
* - `ankh infra ...`
|
|
11
17
|
* - `bunx @ankhorage/infra ...`
|
|
@@ -18,12 +24,10 @@ import { runCli } from './cli/bin.js';
|
|
|
18
24
|
* - `up`
|
|
19
25
|
* - `down`
|
|
20
26
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* - pass `[project]`, or
|
|
26
|
-
* - omit it when cwd is already inside `apps/<project>`
|
|
27
|
+
* Project resolution for the CLI is workspace-aware: pass `[project]`, or
|
|
28
|
+
* omit it when cwd is already inside `apps/<project>`. Programmatic project
|
|
29
|
+
* operations accept an explicit project path and do not require Studio or a
|
|
30
|
+
* particular workspace layout.
|
|
27
31
|
*
|
|
28
32
|
* @usage
|
|
29
33
|
*/
|