@ankhorage/infra 0.2.1 → 0.3.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 +12 -0
- package/README.md +43 -2
- package/dist/cli/index.d.ts +10 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +27 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +39 -0
- package/dist/cli.js.map +1 -0
- package/dist/commandContext.d.ts +12 -0
- package/dist/commandContext.d.ts.map +1 -0
- package/dist/commandContext.js +15 -0
- package/dist/commandContext.js.map +1 -0
- package/dist/commands.d.ts +95 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +256 -0
- package/dist/commands.js.map +1 -0
- package/dist/infraValidation.d.ts +3 -0
- package/dist/infraValidation.d.ts.map +1 -0
- package/dist/infraValidation.js +58 -0
- package/dist/infraValidation.js.map +1 -0
- package/dist/metadata/index.d.ts +2 -0
- package/dist/metadata/index.d.ts.map +1 -0
- package/dist/metadata/index.js +2 -0
- package/dist/metadata/index.js.map +1 -0
- package/dist/packageMetadata.d.ts +10 -0
- package/dist/packageMetadata.d.ts.map +1 -0
- package/dist/packageMetadata.js +17 -0
- package/dist/packageMetadata.js.map +1 -0
- package/dist/project.d.ts +14 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +202 -0
- package/dist/project.js.map +1 -0
- package/dist/projectInfrastructure.d.ts +22 -0
- package/dist/projectInfrastructure.d.ts.map +1 -0
- package/dist/projectInfrastructure.js +120 -0
- package/dist/projectInfrastructure.js.map +1 -0
- package/dist/readme-usage.d.ts +2 -0
- package/dist/readme-usage.d.ts.map +1 -0
- package/dist/readme-usage.js +30 -0
- package/dist/readme-usage.js.map +1 -0
- package/dist/runtime.d.ts +26 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +87 -0
- package/dist/runtime.js.map +1 -0
- package/dist/testSupport.d.ts +24 -0
- package/dist/testSupport.d.ts.map +1 -0
- package/dist/testSupport.js +73 -0
- package/dist/testSupport.js.map +1 -0
- package/package.json +23 -3
- package/src/cli/index.test.ts +32 -0
- package/src/cli/index.ts +53 -0
- package/src/cli.test.ts +137 -0
- package/src/cli.ts +67 -0
- package/src/commandContext.ts +27 -0
- package/src/commands.test.ts +219 -0
- package/src/commands.ts +404 -0
- package/src/infraValidation.ts +90 -0
- package/src/metadata/index.ts +7 -0
- package/src/package.test.ts +27 -0
- package/src/packageMetadata.ts +24 -0
- package/src/project.test.ts +218 -0
- package/src/project.ts +270 -0
- package/src/projectInfrastructure.test.ts +207 -0
- package/src/projectInfrastructure.ts +177 -0
- package/src/readme-usage.ts +30 -0
- package/src/runtime.test.ts +124 -0
- package/src/runtime.ts +127 -0
- package/src/testSupport.ts +112 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { AUTH_PROVIDERS, AUTH_SCOPES, AUTHZ_ENGINES, DATABASE_PROVIDERS, DEPLOYMENT_TARGETS, } from '@ankhorage/contracts';
|
|
2
|
+
export function validateInfraSupport(manifest) {
|
|
3
|
+
const warnings = [];
|
|
4
|
+
const deploymentTarget = manifest.deployment?.target;
|
|
5
|
+
if (deploymentTarget !== undefined && !isSupported(deploymentTarget, DEPLOYMENT_TARGETS)) {
|
|
6
|
+
warnings.push(warningForUnsupportedValue({
|
|
7
|
+
path: 'infra.deployment.target',
|
|
8
|
+
value: deploymentTarget,
|
|
9
|
+
supported: DEPLOYMENT_TARGETS,
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
12
|
+
const databaseProvider = manifest.database?.provider;
|
|
13
|
+
if (databaseProvider !== undefined && !isSupported(databaseProvider, DATABASE_PROVIDERS)) {
|
|
14
|
+
warnings.push(warningForUnsupportedValue({
|
|
15
|
+
path: 'infra.database.provider',
|
|
16
|
+
value: databaseProvider,
|
|
17
|
+
supported: DATABASE_PROVIDERS,
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
const authProvider = manifest.auth?.provider;
|
|
21
|
+
if (authProvider !== undefined && !isSupported(authProvider, AUTH_PROVIDERS)) {
|
|
22
|
+
warnings.push(warningForUnsupportedValue({
|
|
23
|
+
path: 'infra.auth.provider',
|
|
24
|
+
value: authProvider,
|
|
25
|
+
supported: AUTH_PROVIDERS,
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
const authScope = manifest.auth?.scope;
|
|
29
|
+
if (authScope !== undefined && !isSupported(authScope, AUTH_SCOPES)) {
|
|
30
|
+
warnings.push(warningForUnsupportedValue({
|
|
31
|
+
path: 'infra.auth.scope',
|
|
32
|
+
value: authScope,
|
|
33
|
+
supported: AUTH_SCOPES,
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
const authzEngine = manifest.auth?.authorization.engine;
|
|
37
|
+
if (authzEngine !== undefined && !isSupported(authzEngine, AUTHZ_ENGINES)) {
|
|
38
|
+
warnings.push(warningForUnsupportedValue({
|
|
39
|
+
path: 'infra.auth.authorization.engine',
|
|
40
|
+
value: authzEngine,
|
|
41
|
+
supported: AUTHZ_ENGINES,
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
return warnings;
|
|
45
|
+
}
|
|
46
|
+
function warningForUnsupportedValue(args) {
|
|
47
|
+
return `${args.path} "${args.value}" is not currently supported by implemented generators. Supported values: ${formatSupportedValues(args.supported)}.`;
|
|
48
|
+
}
|
|
49
|
+
function formatSupportedValues(values) {
|
|
50
|
+
if (values.length === 0) {
|
|
51
|
+
return '(none)';
|
|
52
|
+
}
|
|
53
|
+
return values.join(', ');
|
|
54
|
+
}
|
|
55
|
+
function isSupported(value, supported) {
|
|
56
|
+
return supported.includes(value);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=infraValidation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"infraValidation.js","sourceRoot":"","sources":["../src/infraValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAI9B,MAAM,UAAU,oBAAoB,CAAC,QAA4B;IAC/D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,MAAM,gBAAgB,GAAG,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IACrD,IAAI,gBAAgB,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,EAAE,CAAC;QACzF,QAAQ,CAAC,IAAI,CACX,0BAA0B,CAAC;YACzB,IAAI,EAAE,yBAAyB;YAC/B,KAAK,EAAE,gBAAgB;YACvB,SAAS,EAAE,kBAAkB;SAC9B,CAAC,CACH,CAAC;IACJ,CAAC;IAED,MAAM,gBAAgB,GAAG,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IACrD,IAAI,gBAAgB,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,EAAE,CAAC;QACzF,QAAQ,CAAC,IAAI,CACX,0BAA0B,CAAC;YACzB,IAAI,EAAE,yBAAyB;YAC/B,KAAK,EAAE,gBAAgB;YACvB,SAAS,EAAE,kBAAkB;SAC9B,CAAC,CACH,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAC7C,IAAI,YAAY,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE,cAAc,CAAC,EAAE,CAAC;QAC7E,QAAQ,CAAC,IAAI,CACX,0BAA0B,CAAC;YACzB,IAAI,EAAE,qBAAqB;YAC3B,KAAK,EAAE,YAAY;YACnB,SAAS,EAAE,cAAc;SAC1B,CAAC,CACH,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACvC,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,CAAC;QACpE,QAAQ,CAAC,IAAI,CACX,0BAA0B,CAAC;YACzB,IAAI,EAAE,kBAAkB;YACxB,KAAK,EAAE,SAAS;YAChB,SAAS,EAAE,WAAW;SACvB,CAAC,CACH,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC;IACxD,IAAI,WAAW,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,aAAa,CAAC,EAAE,CAAC;QAC1E,QAAQ,CAAC,IAAI,CACX,0BAA0B,CAAC;YACzB,IAAI,EAAE,iCAAiC;YACvC,KAAK,EAAE,WAAW;YAClB,SAAS,EAAE,aAAa;SACzB,CAAC,CACH,CAAC;IACJ,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,0BAA0B,CAAC,IAInC;IACC,OAAO,GAAG,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,6EAA6E,qBAAqB,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;AAC1J,CAAC;AAED,SAAS,qBAAqB,CAAC,MAAyB;IACtD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,WAAW,CAAC,KAAa,EAAE,SAA4B;IAC9D,OAAO,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACnC,CAAC","sourcesContent":["import {\n AUTH_PROVIDERS,\n AUTH_SCOPES,\n AUTHZ_ENGINES,\n DATABASE_PROVIDERS,\n DEPLOYMENT_TARGETS,\n} from '@ankhorage/contracts';\n\nimport type { InfraManifestInput } from './types.js';\n\nexport function validateInfraSupport(manifest: InfraManifestInput): readonly string[] {\n const warnings: string[] = [];\n\n const deploymentTarget = manifest.deployment?.target;\n if (deploymentTarget !== undefined && !isSupported(deploymentTarget, DEPLOYMENT_TARGETS)) {\n warnings.push(\n warningForUnsupportedValue({\n path: 'infra.deployment.target',\n value: deploymentTarget,\n supported: DEPLOYMENT_TARGETS,\n }),\n );\n }\n\n const databaseProvider = manifest.database?.provider;\n if (databaseProvider !== undefined && !isSupported(databaseProvider, DATABASE_PROVIDERS)) {\n warnings.push(\n warningForUnsupportedValue({\n path: 'infra.database.provider',\n value: databaseProvider,\n supported: DATABASE_PROVIDERS,\n }),\n );\n }\n\n const authProvider = manifest.auth?.provider;\n if (authProvider !== undefined && !isSupported(authProvider, AUTH_PROVIDERS)) {\n warnings.push(\n warningForUnsupportedValue({\n path: 'infra.auth.provider',\n value: authProvider,\n supported: AUTH_PROVIDERS,\n }),\n );\n }\n\n const authScope = manifest.auth?.scope;\n if (authScope !== undefined && !isSupported(authScope, AUTH_SCOPES)) {\n warnings.push(\n warningForUnsupportedValue({\n path: 'infra.auth.scope',\n value: authScope,\n supported: AUTH_SCOPES,\n }),\n );\n }\n\n const authzEngine = manifest.auth?.authorization.engine;\n if (authzEngine !== undefined && !isSupported(authzEngine, AUTHZ_ENGINES)) {\n warnings.push(\n warningForUnsupportedValue({\n path: 'infra.auth.authorization.engine',\n value: authzEngine,\n supported: AUTHZ_ENGINES,\n }),\n );\n }\n\n return warnings;\n}\n\nfunction warningForUnsupportedValue(args: {\n readonly path: string;\n readonly value: string;\n readonly supported: readonly string[];\n}): string {\n return `${args.path} \"${args.value}\" is not currently supported by implemented generators. Supported values: ${formatSupportedValues(args.supported)}.`;\n}\n\nfunction formatSupportedValues(values: readonly string[]): string {\n if (values.length === 0) {\n return '(none)';\n }\n\n return values.join(', ');\n}\n\nfunction isSupported(value: string, supported: readonly string[]): boolean {\n return supported.includes(value);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/metadata/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/metadata/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC","sourcesContent":["export {\n INFRA_CAPABILITIES,\n INFRA_COMMAND_CATEGORY,\n INFRA_PACKAGE_METADATA,\n INFRA_PACKAGE_NAME,\n INFRA_PACKAGE_VERSION,\n} from '../packageMetadata.js';\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const INFRA_PACKAGE_NAME: string;
|
|
2
|
+
export declare const INFRA_PACKAGE_VERSION: string;
|
|
3
|
+
export declare const INFRA_COMMAND_CATEGORY: "infra";
|
|
4
|
+
export declare const INFRA_CAPABILITIES: readonly ["infra.validate", "infra.generate", "infra.status", "infra.up", "infra.down"];
|
|
5
|
+
export declare const INFRA_PACKAGE_METADATA: {
|
|
6
|
+
readonly category: "infra";
|
|
7
|
+
readonly provider: "./dist/ankh.provider.js";
|
|
8
|
+
readonly capabilities: readonly ["infra.validate", "infra.generate", "infra.status", "infra.up", "infra.down"];
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=packageMetadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packageMetadata.d.ts","sourceRoot":"","sources":["../src/packageMetadata.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,kBAAkB,QAAmB,CAAC;AACnD,eAAO,MAAM,qBAAqB,QAAsB,CAAC;AACzD,eAAO,MAAM,sBAAsB,SAAiD,CAAC;AACrF,eAAO,MAAM,kBAAkB,yFAMiB,CAAC;AAEjD,eAAO,MAAM,sBAAsB;;;;CAIK,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import packageJson from '../package.json';
|
|
2
|
+
export const INFRA_PACKAGE_NAME = packageJson.name;
|
|
3
|
+
export const INFRA_PACKAGE_VERSION = packageJson.version;
|
|
4
|
+
export const INFRA_COMMAND_CATEGORY = 'infra';
|
|
5
|
+
export const INFRA_CAPABILITIES = [
|
|
6
|
+
'infra.validate',
|
|
7
|
+
'infra.generate',
|
|
8
|
+
'infra.status',
|
|
9
|
+
'infra.up',
|
|
10
|
+
'infra.down',
|
|
11
|
+
];
|
|
12
|
+
export const INFRA_PACKAGE_METADATA = {
|
|
13
|
+
category: INFRA_COMMAND_CATEGORY,
|
|
14
|
+
provider: './dist/ankh.provider.js',
|
|
15
|
+
capabilities: INFRA_CAPABILITIES,
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=packageMetadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packageMetadata.js","sourceRoot":"","sources":["../src/packageMetadata.ts"],"names":[],"mappings":"AAMA,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAE1C,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC,IAAI,CAAC;AACnD,MAAM,CAAC,MAAM,qBAAqB,GAAG,WAAW,CAAC,OAAO,CAAC;AACzD,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAA8C,CAAC;AACrF,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,gBAAgB;IAChB,gBAAgB;IAChB,cAAc;IACd,UAAU;IACV,YAAY;CACkC,CAAC;AAEjD,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,QAAQ,EAAE,sBAAsB;IAChC,QAAQ,EAAE,yBAAyB;IACnC,YAAY,EAAE,kBAAkB;CACM,CAAC","sourcesContent":["import type {\n AnkhCapabilityId,\n AnkhCommandCategory,\n AnkhPackageMetadata,\n} from '@ankhorage/contracts/cli';\n\nimport packageJson from '../package.json';\n\nexport const INFRA_PACKAGE_NAME = packageJson.name;\nexport const INFRA_PACKAGE_VERSION = packageJson.version;\nexport const INFRA_COMMAND_CATEGORY = 'infra' as const satisfies AnkhCommandCategory;\nexport const INFRA_CAPABILITIES = [\n 'infra.validate',\n 'infra.generate',\n 'infra.status',\n 'infra.up',\n 'infra.down',\n] as const satisfies readonly AnkhCapabilityId[];\n\nexport const INFRA_PACKAGE_METADATA = {\n category: INFRA_COMMAND_CATEGORY,\n provider: './dist/ankh.provider.js',\n capabilities: INFRA_CAPABILITIES,\n} as const satisfies AnkhPackageMetadata;\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AppManifest } from '@ankhorage/contracts';
|
|
2
|
+
export interface ResolvedInfraProject {
|
|
3
|
+
readonly appsRoot: string;
|
|
4
|
+
readonly manifest: AppManifest;
|
|
5
|
+
readonly manifestPath: string;
|
|
6
|
+
readonly projectId: string;
|
|
7
|
+
readonly projectPath: string;
|
|
8
|
+
readonly workspaceRoot: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function resolveInfraProject(options: {
|
|
11
|
+
readonly cwd: string;
|
|
12
|
+
readonly projectId?: string;
|
|
13
|
+
}): Promise<ResolvedInfraProject>;
|
|
14
|
+
//# sourceMappingURL=project.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAiC,MAAM,sBAAsB,CAAC;AAIvF,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"}
|
package/dist/project.js
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
export async function resolveInfraProject(options) {
|
|
4
|
+
const workspaceRoot = await findWorkspaceRoot(options.cwd);
|
|
5
|
+
if (workspaceRoot === null) {
|
|
6
|
+
throw new Error(`Could not find an Ankh workspace root from cwd: ${options.cwd}`);
|
|
7
|
+
}
|
|
8
|
+
const appsRoot = path.join(workspaceRoot, 'apps');
|
|
9
|
+
const projectId = options.projectId === undefined
|
|
10
|
+
? inferProjectId(options.cwd, appsRoot)
|
|
11
|
+
: validateExplicitProjectId(options.projectId);
|
|
12
|
+
if (projectId === null) {
|
|
13
|
+
throw new Error('Could not infer a project from cwd. Pass a project id or run the command inside apps/<project>.');
|
|
14
|
+
}
|
|
15
|
+
const projectPath = resolveProjectPath(appsRoot, projectId);
|
|
16
|
+
if (!(await pathExists(projectPath))) {
|
|
17
|
+
throw new Error(`Project '${projectId}' not found.`);
|
|
18
|
+
}
|
|
19
|
+
const manifestPath = path.join(projectPath, 'ankh.config.json');
|
|
20
|
+
const manifest = await readProjectManifest({
|
|
21
|
+
manifestPath,
|
|
22
|
+
projectId,
|
|
23
|
+
projectPath,
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
appsRoot,
|
|
27
|
+
manifest,
|
|
28
|
+
manifestPath,
|
|
29
|
+
projectId,
|
|
30
|
+
projectPath,
|
|
31
|
+
workspaceRoot,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function resolveProjectPath(appsRoot, projectId) {
|
|
35
|
+
const projectPath = path.resolve(appsRoot, projectId);
|
|
36
|
+
const appsRootPath = `${path.resolve(appsRoot)}${path.sep}`;
|
|
37
|
+
if (!projectPath.startsWith(appsRootPath)) {
|
|
38
|
+
throw new Error('Security check failed while resolving the project path.');
|
|
39
|
+
}
|
|
40
|
+
return projectPath;
|
|
41
|
+
}
|
|
42
|
+
function createFallbackManifest(projectId) {
|
|
43
|
+
return {
|
|
44
|
+
metadata: {
|
|
45
|
+
name: projectId,
|
|
46
|
+
slug: projectId,
|
|
47
|
+
version: '0.0.0',
|
|
48
|
+
themeId: 'default',
|
|
49
|
+
},
|
|
50
|
+
themes: [],
|
|
51
|
+
activeThemeId: 'default',
|
|
52
|
+
infra: {
|
|
53
|
+
plugins: [],
|
|
54
|
+
},
|
|
55
|
+
navigator: {
|
|
56
|
+
type: 'stack',
|
|
57
|
+
routes: [{ name: 'index', screenId: 'index' }],
|
|
58
|
+
},
|
|
59
|
+
screens: {},
|
|
60
|
+
settings: {
|
|
61
|
+
localization: {
|
|
62
|
+
defaultLocale: 'en',
|
|
63
|
+
locales: ['en'],
|
|
64
|
+
},
|
|
65
|
+
authFlow: {
|
|
66
|
+
signInRoute: '/sign-in',
|
|
67
|
+
signOutRoute: '/sign-out',
|
|
68
|
+
postSignInRoute: '/',
|
|
69
|
+
unauthorizedRoute: '/sign-in',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async function readProjectManifest(args) {
|
|
75
|
+
if (!(await pathExists(args.manifestPath))) {
|
|
76
|
+
return createFallbackManifest(args.projectId);
|
|
77
|
+
}
|
|
78
|
+
const rawManifest = await fs.readFile(args.manifestPath, 'utf8');
|
|
79
|
+
let parsed;
|
|
80
|
+
try {
|
|
81
|
+
parsed = JSON.parse(rawManifest);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
throw new Error(`Project manifest is not valid JSON: ${args.manifestPath}: ${getErrorMessage(error)}`, { cause: error });
|
|
85
|
+
}
|
|
86
|
+
if (!isAppManifest(parsed)) {
|
|
87
|
+
throw new Error(`Project manifest has an invalid shape: ${args.manifestPath}`);
|
|
88
|
+
}
|
|
89
|
+
return parsed;
|
|
90
|
+
}
|
|
91
|
+
async function findWorkspaceRoot(startPath) {
|
|
92
|
+
let currentPath = path.resolve(startPath);
|
|
93
|
+
for (;;) {
|
|
94
|
+
const appsRoot = path.join(currentPath, 'apps');
|
|
95
|
+
if (await isDirectory(appsRoot)) {
|
|
96
|
+
return currentPath;
|
|
97
|
+
}
|
|
98
|
+
const parentPath = path.dirname(currentPath);
|
|
99
|
+
if (parentPath === currentPath) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
currentPath = parentPath;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function inferProjectId(cwd, appsRoot) {
|
|
106
|
+
const relativePath = path.relative(path.resolve(appsRoot), path.resolve(cwd));
|
|
107
|
+
if (relativePath === '' || relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const [projectId] = relativePath.split(path.sep);
|
|
111
|
+
return projectId === undefined || projectId.length === 0 ? null : path.basename(projectId);
|
|
112
|
+
}
|
|
113
|
+
function validateExplicitProjectId(projectId) {
|
|
114
|
+
if (projectId.trim().length === 0) {
|
|
115
|
+
throw new Error('Invalid project id: expected a single safe project segment.');
|
|
116
|
+
}
|
|
117
|
+
if (projectId === '.' ||
|
|
118
|
+
projectId === '..' ||
|
|
119
|
+
path.isAbsolute(projectId) ||
|
|
120
|
+
projectId.includes('/') ||
|
|
121
|
+
projectId.includes('\\')) {
|
|
122
|
+
throw new Error(`Invalid project id: ${projectId}. Expected a single safe project segment.`);
|
|
123
|
+
}
|
|
124
|
+
return projectId;
|
|
125
|
+
}
|
|
126
|
+
function isAppManifest(value) {
|
|
127
|
+
if (!isRecord(value)) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
return (isAppManifestMetadata(value.metadata) &&
|
|
131
|
+
Array.isArray(value.themes) &&
|
|
132
|
+
typeof value.activeThemeId === 'string' &&
|
|
133
|
+
isInfraManifestRecord(value.infra) &&
|
|
134
|
+
isNavigatorSpec(value.navigator) &&
|
|
135
|
+
isRecord(value.screens) &&
|
|
136
|
+
isSettingsRecord(value.settings));
|
|
137
|
+
}
|
|
138
|
+
function isAppManifestMetadata(value) {
|
|
139
|
+
return (isRecord(value) &&
|
|
140
|
+
typeof value.name === 'string' &&
|
|
141
|
+
typeof value.slug === 'string' &&
|
|
142
|
+
typeof value.version === 'string' &&
|
|
143
|
+
typeof value.themeId === 'string' &&
|
|
144
|
+
(value.created === undefined || typeof value.created === 'string') &&
|
|
145
|
+
(value.updated === undefined || typeof value.updated === 'string'));
|
|
146
|
+
}
|
|
147
|
+
function isInfraManifestRecord(value) {
|
|
148
|
+
return isRecord(value) && Array.isArray(value.plugins);
|
|
149
|
+
}
|
|
150
|
+
function isNavigatorSpec(value) {
|
|
151
|
+
return (isRecord(value) &&
|
|
152
|
+
typeof value.type === 'string' &&
|
|
153
|
+
Array.isArray(value.routes) &&
|
|
154
|
+
(value.initialRouteName === undefined || typeof value.initialRouteName === 'string'));
|
|
155
|
+
}
|
|
156
|
+
function isSettingsRecord(value) {
|
|
157
|
+
return (isRecord(value) &&
|
|
158
|
+
(value.apiBaseUrl === undefined || typeof value.apiBaseUrl === 'string') &&
|
|
159
|
+
isLocalizationRecord(value.localization) &&
|
|
160
|
+
isAuthFlowConfig(value.authFlow));
|
|
161
|
+
}
|
|
162
|
+
function isLocalizationRecord(value) {
|
|
163
|
+
return (isRecord(value) &&
|
|
164
|
+
typeof value.defaultLocale === 'string' &&
|
|
165
|
+
Array.isArray(value.locales) &&
|
|
166
|
+
value.locales.every((locale) => typeof locale === 'string'));
|
|
167
|
+
}
|
|
168
|
+
function isAuthFlowConfig(value) {
|
|
169
|
+
return (isRecord(value) &&
|
|
170
|
+
typeof value.signInRoute === 'string' &&
|
|
171
|
+
typeof value.postSignInRoute === 'string' &&
|
|
172
|
+
(value.signUpRoute === undefined || typeof value.signUpRoute === 'string') &&
|
|
173
|
+
(value.signOutRoute === undefined || typeof value.signOutRoute === 'string') &&
|
|
174
|
+
(value.forgotPasswordRoute === undefined || typeof value.forgotPasswordRoute === 'string') &&
|
|
175
|
+
(value.otpRoute === undefined || typeof value.otpRoute === 'string') &&
|
|
176
|
+
(value.unauthorizedRoute === undefined || typeof value.unauthorizedRoute === 'string'));
|
|
177
|
+
}
|
|
178
|
+
function isRecord(value) {
|
|
179
|
+
return typeof value === 'object' && value !== null;
|
|
180
|
+
}
|
|
181
|
+
async function isDirectory(filePath) {
|
|
182
|
+
try {
|
|
183
|
+
const stat = await fs.stat(filePath);
|
|
184
|
+
return stat.isDirectory();
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async function pathExists(filePath) {
|
|
191
|
+
try {
|
|
192
|
+
await fs.access(filePath);
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function getErrorMessage(error) {
|
|
200
|
+
return error instanceof Error ? error.message : String(error);
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AppManifest, AuthFlowConfig, 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 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 authFlow: {\n signInRoute: '/sign-in',\n signOutRoute: '/sign-out',\n postSignInRoute: '/',\n unauthorizedRoute: '/sign-in',\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 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 isAuthFlowConfig(value.authFlow)\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 isAuthFlowConfig(value: unknown): value is AuthFlowConfig {\n return (\n isRecord(value) &&\n typeof value.signInRoute === 'string' &&\n typeof value.postSignInRoute === 'string' &&\n (value.signUpRoute === undefined || typeof value.signUpRoute === 'string') &&\n (value.signOutRoute === undefined || typeof value.signOutRoute === 'string') &&\n (value.forgotPasswordRoute === undefined || typeof value.forgotPasswordRoute === 'string') &&\n (value.otpRoute === undefined || typeof value.otpRoute === 'string') &&\n (value.unauthorizedRoute === undefined || typeof value.unauthorizedRoute === '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"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AppManifest } from '@ankhorage/contracts';
|
|
2
|
+
import { generateInfrastructure } from './index.js';
|
|
3
|
+
export interface InfraSyncResult {
|
|
4
|
+
readonly generated: number;
|
|
5
|
+
readonly removed: number;
|
|
6
|
+
readonly warnings: readonly string[];
|
|
7
|
+
readonly skipped?: {
|
|
8
|
+
readonly reason: string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export declare function syncProjectInfrastructure(args: {
|
|
12
|
+
readonly generateInfrastructureImpl?: typeof generateInfrastructure;
|
|
13
|
+
readonly manifest: AppManifest;
|
|
14
|
+
readonly projectId: string;
|
|
15
|
+
readonly projectPath: string;
|
|
16
|
+
}): Promise<InfraSyncResult>;
|
|
17
|
+
export declare function resolveProjectInfrastructureTarget(args: {
|
|
18
|
+
readonly manifest: AppManifest;
|
|
19
|
+
readonly projectPath: string;
|
|
20
|
+
}): Promise<string | null>;
|
|
21
|
+
export declare function resolveProjectFile(projectPath: string, filePath: string): string;
|
|
22
|
+
//# sourceMappingURL=projectInfrastructure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projectInfrastructure.d.ts","sourceRoot":"","sources":["../src/projectInfrastructure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAIxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAcpD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE;QACjB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAED,wBAAsB,yBAAyB,CAAC,IAAI,EAAE;IACpD,QAAQ,CAAC,0BAA0B,CAAC,EAAE,OAAO,sBAAsB,CAAC;IACpE,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B,GAAG,OAAO,CAAC,eAAe,CAAC,CAiE3B;AAED,wBAAsB,kCAAkC,CAAC,IAAI,EAAE;IAC7D,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQzB;AAED,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUhF"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { generateInfrastructure } from './index.js';
|
|
4
|
+
import { validateInfraSupport } from './infraValidation.js';
|
|
5
|
+
const INFRA_LEDGER_RELATIVE_PATH = '.ankh/infra-ledger.json';
|
|
6
|
+
const STUDIO_PROJECT_ID = 'studio';
|
|
7
|
+
export async function syncProjectInfrastructure(args) {
|
|
8
|
+
const supportWarnings = validateInfraSupport(args.manifest.infra);
|
|
9
|
+
if (args.projectId === STUDIO_PROJECT_ID) {
|
|
10
|
+
return {
|
|
11
|
+
generated: 0,
|
|
12
|
+
removed: 0,
|
|
13
|
+
warnings: supportWarnings,
|
|
14
|
+
skipped: {
|
|
15
|
+
reason: 'apps/studio is the dashboard and is not a generated app target.',
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const previousLedger = await readInfraLedger(args.projectPath);
|
|
20
|
+
const previousFiles = new Set(previousLedger?.files ?? []);
|
|
21
|
+
if (args.manifest.infra.deployment === undefined) {
|
|
22
|
+
const removed = await removeFiles(args.projectPath, previousFiles);
|
|
23
|
+
await removeInfraLedger(args.projectPath);
|
|
24
|
+
return {
|
|
25
|
+
generated: 0,
|
|
26
|
+
removed,
|
|
27
|
+
warnings: supportWarnings,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const generated = (args.generateInfrastructureImpl ?? generateInfrastructure)(args.manifest.infra, {
|
|
31
|
+
namespaceHint: args.projectId,
|
|
32
|
+
appManifest: args.manifest,
|
|
33
|
+
});
|
|
34
|
+
const combinedWarnings = uniqueStrings([...supportWarnings, ...generated.warnings]);
|
|
35
|
+
const nextFiles = new Set();
|
|
36
|
+
for (const file of generated.files) {
|
|
37
|
+
const outputPath = resolveProjectFile(args.projectPath, file.path);
|
|
38
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
39
|
+
await fs.writeFile(outputPath, file.content, 'utf8');
|
|
40
|
+
if (file.executable === true) {
|
|
41
|
+
await fs.chmod(outputPath, 0o755);
|
|
42
|
+
}
|
|
43
|
+
nextFiles.add(file.path);
|
|
44
|
+
}
|
|
45
|
+
const staleFiles = new Set([...previousFiles].filter((filePath) => !nextFiles.has(filePath)));
|
|
46
|
+
const removed = await removeFiles(args.projectPath, staleFiles);
|
|
47
|
+
await writeInfraLedger(args.projectPath, {
|
|
48
|
+
schemaVersion: 1,
|
|
49
|
+
generatedAt: new Date().toISOString(),
|
|
50
|
+
target: generated.meta.target,
|
|
51
|
+
files: [...nextFiles].sort(),
|
|
52
|
+
warnings: combinedWarnings,
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
generated: generated.files.length,
|
|
56
|
+
removed,
|
|
57
|
+
warnings: combinedWarnings,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export async function resolveProjectInfrastructureTarget(args) {
|
|
61
|
+
const manifestTarget = args.manifest.infra.deployment?.target;
|
|
62
|
+
if (manifestTarget !== undefined) {
|
|
63
|
+
return manifestTarget;
|
|
64
|
+
}
|
|
65
|
+
const ledger = await readInfraLedger(args.projectPath);
|
|
66
|
+
return ledger?.target ?? null;
|
|
67
|
+
}
|
|
68
|
+
export function resolveProjectFile(projectPath, filePath) {
|
|
69
|
+
const rootPath = path.resolve(projectPath);
|
|
70
|
+
const resolvedPath = path.resolve(rootPath, filePath);
|
|
71
|
+
const relativePath = path.relative(rootPath, resolvedPath);
|
|
72
|
+
if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
|
|
73
|
+
throw new Error(`Invalid infra output path outside project root: ${filePath}`);
|
|
74
|
+
}
|
|
75
|
+
return resolvedPath;
|
|
76
|
+
}
|
|
77
|
+
async function readInfraLedger(projectPath) {
|
|
78
|
+
const ledgerPath = path.join(projectPath, INFRA_LEDGER_RELATIVE_PATH);
|
|
79
|
+
try {
|
|
80
|
+
const rawLedger = await fs.readFile(ledgerPath, 'utf8');
|
|
81
|
+
const parsedLedger = JSON.parse(rawLedger);
|
|
82
|
+
return isInfraLedger(parsedLedger) ? parsedLedger : null;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function writeInfraLedger(projectPath, ledger) {
|
|
89
|
+
const ledgerPath = path.join(projectPath, INFRA_LEDGER_RELATIVE_PATH);
|
|
90
|
+
await fs.mkdir(path.dirname(ledgerPath), { recursive: true });
|
|
91
|
+
await fs.writeFile(ledgerPath, `${JSON.stringify(ledger, null, 2)}\n`, 'utf8');
|
|
92
|
+
}
|
|
93
|
+
async function removeInfraLedger(projectPath) {
|
|
94
|
+
await fs.rm(path.join(projectPath, INFRA_LEDGER_RELATIVE_PATH), { force: true });
|
|
95
|
+
}
|
|
96
|
+
async function removeFiles(projectPath, files) {
|
|
97
|
+
let removedFiles = 0;
|
|
98
|
+
for (const filePath of files) {
|
|
99
|
+
await fs.rm(resolveProjectFile(projectPath, filePath), { force: true });
|
|
100
|
+
removedFiles += 1;
|
|
101
|
+
}
|
|
102
|
+
return removedFiles;
|
|
103
|
+
}
|
|
104
|
+
function isInfraLedger(value) {
|
|
105
|
+
return (isRecord(value) &&
|
|
106
|
+
value.schemaVersion === 1 &&
|
|
107
|
+
typeof value.generatedAt === 'string' &&
|
|
108
|
+
typeof value.target === 'string' &&
|
|
109
|
+
Array.isArray(value.files) &&
|
|
110
|
+
value.files.every((filePath) => typeof filePath === 'string') &&
|
|
111
|
+
Array.isArray(value.warnings) &&
|
|
112
|
+
value.warnings.every((warning) => typeof warning === 'string'));
|
|
113
|
+
}
|
|
114
|
+
function isRecord(value) {
|
|
115
|
+
return typeof value === 'object' && value !== null;
|
|
116
|
+
}
|
|
117
|
+
function uniqueStrings(values) {
|
|
118
|
+
return [...new Set(values)];
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=projectInfrastructure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projectInfrastructure.js","sourceRoot":"","sources":["../src/projectInfrastructure.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AppManifest } from '@ankhorage/contracts';\nimport { promises as fs } from 'fs';\nimport path from 'path';\n\nimport { generateInfrastructure } from './index.js';\nimport { validateInfraSupport } from './infraValidation.js';\n\nconst INFRA_LEDGER_RELATIVE_PATH = '.ankh/infra-ledger.json';\nconst STUDIO_PROJECT_ID = 'studio';\n\ninterface InfraLedger {\n readonly schemaVersion: 1;\n readonly generatedAt: string;\n readonly target: string;\n readonly files: readonly string[];\n readonly warnings: readonly string[];\n}\n\nexport interface InfraSyncResult {\n readonly generated: number;\n readonly removed: number;\n readonly warnings: readonly string[];\n readonly skipped?: {\n readonly reason: string;\n };\n}\n\nexport async function syncProjectInfrastructure(args: {\n readonly generateInfrastructureImpl?: typeof generateInfrastructure;\n readonly manifest: AppManifest;\n readonly projectId: string;\n readonly projectPath: string;\n}): Promise<InfraSyncResult> {\n const supportWarnings = validateInfraSupport(args.manifest.infra);\n\n if (args.projectId === STUDIO_PROJECT_ID) {\n return {\n generated: 0,\n removed: 0,\n warnings: supportWarnings,\n skipped: {\n reason: 'apps/studio is the dashboard and is not a generated app target.',\n },\n };\n }\n\n const previousLedger = await readInfraLedger(args.projectPath);\n const previousFiles = new Set(previousLedger?.files ?? []);\n\n if (args.manifest.infra.deployment === undefined) {\n const removed = await removeFiles(args.projectPath, previousFiles);\n await removeInfraLedger(args.projectPath);\n return {\n generated: 0,\n removed,\n warnings: supportWarnings,\n };\n }\n\n const generated = (args.generateInfrastructureImpl ?? generateInfrastructure)(\n args.manifest.infra,\n {\n namespaceHint: args.projectId,\n appManifest: args.manifest,\n },\n );\n const combinedWarnings = uniqueStrings([...supportWarnings, ...generated.warnings]);\n const nextFiles = new Set<string>();\n\n for (const file of generated.files) {\n const outputPath = resolveProjectFile(args.projectPath, file.path);\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.writeFile(outputPath, file.content, 'utf8');\n\n if (file.executable === true) {\n await fs.chmod(outputPath, 0o755);\n }\n\n nextFiles.add(file.path);\n }\n\n const staleFiles = new Set([...previousFiles].filter((filePath) => !nextFiles.has(filePath)));\n const removed = await removeFiles(args.projectPath, staleFiles);\n\n await writeInfraLedger(args.projectPath, {\n schemaVersion: 1,\n generatedAt: new Date().toISOString(),\n target: generated.meta.target,\n files: [...nextFiles].sort(),\n warnings: combinedWarnings,\n });\n\n return {\n generated: generated.files.length,\n removed,\n warnings: combinedWarnings,\n };\n}\n\nexport async function resolveProjectInfrastructureTarget(args: {\n readonly manifest: AppManifest;\n readonly projectPath: string;\n}): Promise<string | null> {\n const manifestTarget = args.manifest.infra.deployment?.target;\n if (manifestTarget !== undefined) {\n return manifestTarget;\n }\n\n const ledger = await readInfraLedger(args.projectPath);\n return ledger?.target ?? null;\n}\n\nexport function resolveProjectFile(projectPath: string, filePath: string): string {\n const rootPath = path.resolve(projectPath);\n const resolvedPath = path.resolve(rootPath, filePath);\n const relativePath = path.relative(rootPath, resolvedPath);\n\n if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {\n throw new Error(`Invalid infra output path outside project root: ${filePath}`);\n }\n\n return resolvedPath;\n}\n\nasync function readInfraLedger(projectPath: string): Promise<InfraLedger | null> {\n const ledgerPath = path.join(projectPath, INFRA_LEDGER_RELATIVE_PATH);\n\n try {\n const rawLedger = await fs.readFile(ledgerPath, 'utf8');\n const parsedLedger: unknown = JSON.parse(rawLedger);\n return isInfraLedger(parsedLedger) ? parsedLedger : null;\n } catch {\n return null;\n }\n}\n\nasync function writeInfraLedger(projectPath: string, ledger: InfraLedger): Promise<void> {\n const ledgerPath = path.join(projectPath, INFRA_LEDGER_RELATIVE_PATH);\n await fs.mkdir(path.dirname(ledgerPath), { recursive: true });\n await fs.writeFile(ledgerPath, `${JSON.stringify(ledger, null, 2)}\\n`, 'utf8');\n}\n\nasync function removeInfraLedger(projectPath: string): Promise<void> {\n await fs.rm(path.join(projectPath, INFRA_LEDGER_RELATIVE_PATH), { force: true });\n}\n\nasync function removeFiles(projectPath: string, files: ReadonlySet<string>): Promise<number> {\n let removedFiles = 0;\n\n for (const filePath of files) {\n await fs.rm(resolveProjectFile(projectPath, filePath), { force: true });\n removedFiles += 1;\n }\n\n return removedFiles;\n}\n\nfunction isInfraLedger(value: unknown): value is InfraLedger {\n return (\n isRecord(value) &&\n value.schemaVersion === 1 &&\n typeof value.generatedAt === 'string' &&\n typeof value.target === 'string' &&\n Array.isArray(value.files) &&\n value.files.every((filePath) => typeof filePath === 'string') &&\n Array.isArray(value.warnings) &&\n value.warnings.every((warning) => typeof warning === 'string')\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction uniqueStrings(values: readonly string[]): readonly string[] {\n return [...new Set(values)];\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readme-usage.d.ts","sourceRoot":"","sources":["../src/readme-usage.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { runCli } from './cli.js';
|
|
2
|
+
/***
|
|
3
|
+
* Provider and CLI surface
|
|
4
|
+
*
|
|
5
|
+
* `@ankhorage/infra` owns infra command behavior.
|
|
6
|
+
*
|
|
7
|
+
* The same shared command implementation backs both:
|
|
8
|
+
*
|
|
9
|
+
* - `ankh infra ...`
|
|
10
|
+
* - `bunx @ankhorage/infra ...`
|
|
11
|
+
*
|
|
12
|
+
* Current command surface:
|
|
13
|
+
*
|
|
14
|
+
* - `validate`
|
|
15
|
+
* - `generate`
|
|
16
|
+
* - `status`
|
|
17
|
+
* - `up`
|
|
18
|
+
* - `down`
|
|
19
|
+
*
|
|
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
|
+
*
|
|
27
|
+
* @usage
|
|
28
|
+
*/
|
|
29
|
+
await runCli(['--help']);
|
|
30
|
+
//# sourceMappingURL=readme-usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readme-usage.js","sourceRoot":"","sources":["../src/readme-usage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC","sourcesContent":["import { runCli } from './cli.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"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { InfraCommandContext } from './commandContext.js';
|
|
2
|
+
export type InfraLifecycleScript = 'down' | 'status' | 'up';
|
|
3
|
+
export declare class InfraScriptExecutionError extends Error {
|
|
4
|
+
readonly exitCode: number | null;
|
|
5
|
+
readonly stderr: string;
|
|
6
|
+
readonly stdout: string;
|
|
7
|
+
constructor(args: {
|
|
8
|
+
readonly exitCode: number | null;
|
|
9
|
+
readonly message: string;
|
|
10
|
+
readonly stderr: string;
|
|
11
|
+
readonly stdout: string;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export declare function runProjectInfraScript(args: {
|
|
15
|
+
readonly context: InfraCommandContext;
|
|
16
|
+
readonly projectId: string;
|
|
17
|
+
readonly projectPath: string;
|
|
18
|
+
readonly script: InfraLifecycleScript;
|
|
19
|
+
readonly target: string;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
export declare function resolveProjectInfraScriptPath(args: {
|
|
22
|
+
readonly projectPath: string;
|
|
23
|
+
readonly script: InfraLifecycleScript;
|
|
24
|
+
readonly target: string;
|
|
25
|
+
}): string;
|
|
26
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +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"}
|