@hstm-labs/forge-deliverer 0.1.11
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/README.md +44 -0
- package/dist/archiver.d.ts +23 -0
- package/dist/archiver.d.ts.map +1 -0
- package/dist/archiver.js +43 -0
- package/dist/archiver.js.map +1 -0
- package/dist/artifact-merger.d.ts +27 -0
- package/dist/artifact-merger.d.ts.map +1 -0
- package/dist/artifact-merger.js +64 -0
- package/dist/artifact-merger.js.map +1 -0
- package/dist/deliver-stage.d.ts +44 -0
- package/dist/deliver-stage.d.ts.map +1 -0
- package/dist/deliver-stage.js +191 -0
- package/dist/deliver-stage.js.map +1 -0
- package/dist/delivery-writer.d.ts +21 -0
- package/dist/delivery-writer.d.ts.map +1 -0
- package/dist/delivery-writer.js +58 -0
- package/dist/delivery-writer.js.map +1 -0
- package/dist/formatters.d.ts +22 -0
- package/dist/formatters.d.ts.map +1 -0
- package/dist/formatters.js +101 -0
- package/dist/formatters.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/self-containment.d.ts +22 -0
- package/dist/self-containment.d.ts.map +1 -0
- package/dist/self-containment.js +134 -0
- package/dist/self-containment.js.map +1 -0
- package/dist/types.d.ts +71 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +22 -0
package/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# @hstm-labs/forge-deliverer
|
|
2
|
+
|
|
3
|
+
Output packaging and delivery for Forge — merges artifacts from all generation stages, validates self-containment, creates compressed archives, and writes deliverables to configurable targets.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @hstm-labs/forge-deliverer
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Public API
|
|
12
|
+
|
|
13
|
+
### Types
|
|
14
|
+
|
|
15
|
+
- `DeliveryResult` — complete delivery output with manifest and archive path
|
|
16
|
+
- `DeliveryManifest`, `DeliveryStageEntry` — manifest tracking all delivered files
|
|
17
|
+
- `SelfContainmentResult`, `SelfContainmentCheck` — self-containment validation
|
|
18
|
+
- `DeliveryOptions` — delivery configuration
|
|
19
|
+
|
|
20
|
+
### Classes
|
|
21
|
+
|
|
22
|
+
- `DeliverStage` — pipeline stage implementing `PipelineStage` interface
|
|
23
|
+
|
|
24
|
+
### Functions
|
|
25
|
+
|
|
26
|
+
- `mergeArtifacts(stageOutputs)` — merge artifacts from all pipeline stages
|
|
27
|
+
- `validateSelfContainment(outputDir)` — verify no dangling imports/references
|
|
28
|
+
- `createArchive(outputDir, archivePath)` — create .tgz archive
|
|
29
|
+
- `deliverToTarget(archive, target)` — write to configured delivery target
|
|
30
|
+
- `formatDeliveryResult()`, `deliveryResultToJson()` — output formatters
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { DeliverStage } from '@hstm-labs/forge-deliverer';
|
|
36
|
+
|
|
37
|
+
const stage = new DeliverStage();
|
|
38
|
+
const result = await stage.execute(input);
|
|
39
|
+
console.log(`Delivered to: ${result.data.archivePath}`);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
|
|
44
|
+
[MIT](../../LICENSE)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Archive creation for the Forge delivery stage.
|
|
3
|
+
*
|
|
4
|
+
* Creates a tgz or zip archive from the staging directory,
|
|
5
|
+
* excluding `.git`, `node_modules`, and `.DS_Store`.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Create an archive from the staging directory.
|
|
9
|
+
*
|
|
10
|
+
* Uses the system `tar` command for tgz archives. Falls back
|
|
11
|
+
* gracefully with a warning if archive creation fails.
|
|
12
|
+
*
|
|
13
|
+
* @param stagingDir - Source directory to archive
|
|
14
|
+
* @param outputPath - Full path for the output archive file
|
|
15
|
+
* @param format - Archive format: 'tgz' or 'zip'
|
|
16
|
+
* @returns Archive path and size in bytes
|
|
17
|
+
* @throws {ForgeError} FORGE-DLVR-001 if archive creation fails
|
|
18
|
+
*/
|
|
19
|
+
export declare function createArchive(stagingDir: string, outputPath: string, format: 'tgz' | 'zip'): Promise<{
|
|
20
|
+
archivePath: string;
|
|
21
|
+
sizeBytes: number;
|
|
22
|
+
}>;
|
|
23
|
+
//# sourceMappingURL=archiver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archiver.d.ts","sourceRoot":"","sources":["../src/archiver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,KAAK,GAAG,KAAK,GACpB,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CA0BrD"}
|
package/dist/archiver.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Archive creation for the Forge delivery stage.
|
|
3
|
+
*
|
|
4
|
+
* Creates a tgz or zip archive from the staging directory,
|
|
5
|
+
* excluding `.git`, `node_modules`, and `.DS_Store`.
|
|
6
|
+
*/
|
|
7
|
+
import { execSync } from 'node:child_process';
|
|
8
|
+
import { statSync } from 'node:fs';
|
|
9
|
+
import { ForgeError, ErrorCodes } from '@hstm-labs/forge-common';
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Public API
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
/**
|
|
14
|
+
* Create an archive from the staging directory.
|
|
15
|
+
*
|
|
16
|
+
* Uses the system `tar` command for tgz archives. Falls back
|
|
17
|
+
* gracefully with a warning if archive creation fails.
|
|
18
|
+
*
|
|
19
|
+
* @param stagingDir - Source directory to archive
|
|
20
|
+
* @param outputPath - Full path for the output archive file
|
|
21
|
+
* @param format - Archive format: 'tgz' or 'zip'
|
|
22
|
+
* @returns Archive path and size in bytes
|
|
23
|
+
* @throws {ForgeError} FORGE-DLVR-001 if archive creation fails
|
|
24
|
+
*/
|
|
25
|
+
export async function createArchive(stagingDir, outputPath, format) {
|
|
26
|
+
try {
|
|
27
|
+
if (format === 'tgz') {
|
|
28
|
+
execSync(`tar -czf "${outputPath}" --exclude='.git' --exclude='node_modules' --exclude='.DS_Store' -C "${stagingDir}" .`, { stdio: 'pipe' });
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
execSync(`zip -r "${outputPath}" . -x '.git/*' -x 'node_modules/*' -x '.DS_Store'`, { cwd: stagingDir, stdio: 'pipe' });
|
|
32
|
+
}
|
|
33
|
+
const stat = statSync(outputPath);
|
|
34
|
+
return { archivePath: outputPath, sizeBytes: stat.size };
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const cause = error instanceof Error ? error : undefined;
|
|
38
|
+
throw new ForgeError(ErrorCodes.DLVR.PACKAGING_FAILURE, `Failed to create ${format} archive at '${outputPath}'. ` +
|
|
39
|
+
'Ensure tar/zip is available on your system. ' +
|
|
40
|
+
`Error: ${cause?.message ?? 'Unknown error'}`, cause !== undefined ? { cause } : undefined);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=archiver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archiver.js","sourceRoot":"","sources":["../src/archiver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAEjE,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,UAAkB,EAClB,UAAkB,EAClB,MAAqB;IAErB,IAAI,CAAC;QACH,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACrB,QAAQ,CACN,aAAa,UAAU,yEAAyE,UAAU,KAAK,EAC/G,EAAE,KAAK,EAAE,MAAM,EAAE,CAClB,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,QAAQ,CACN,WAAW,UAAU,oDAAoD,EACzE,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,CACnC,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;QAClC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IAC3D,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;QACzD,MAAM,IAAI,UAAU,CAClB,UAAU,CAAC,IAAI,CAAC,iBAAiB,EACjC,oBAAoB,MAAM,gBAAgB,UAAU,KAAK;YACvD,8CAA8C;YAC9C,UAAU,KAAK,EAAE,OAAO,IAAI,eAAe,EAAE,EAC/C,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAC5C,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Artifact merger for the Forge delivery stage.
|
|
3
|
+
*
|
|
4
|
+
* Merges generated artifacts from all pipeline stages into a cohesive
|
|
5
|
+
* project structure in a target directory (staging or project root).
|
|
6
|
+
* For layouts like scalable-monorepo, service grouping (e.g. services/identity/...)
|
|
7
|
+
* is achieved by generator-emitted artifact paths under the stage's target base.
|
|
8
|
+
*/
|
|
9
|
+
import type { StageName, Workspace, RunStore } from '@hstm-labs/forge-common';
|
|
10
|
+
import type { DeliveryStageEntry } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Merge artifacts from all pipeline stages into a target directory.
|
|
13
|
+
*
|
|
14
|
+
* Reads artifact files from their on-disk locations and copies them
|
|
15
|
+
* into a project-like directory structure using the layout's stage mapping.
|
|
16
|
+
*
|
|
17
|
+
* @param workspace - Resolved workspace paths
|
|
18
|
+
* @param runId - Run ID to merge artifacts for
|
|
19
|
+
* @param store - RunStore for artifact metadata
|
|
20
|
+
* @param stageMapping - Maps stage name to target directory (from project layout)
|
|
21
|
+
* @param targetDir - Directory to merge into (e.g. staging dir or project root)
|
|
22
|
+
* @returns Stage entries for the manifest
|
|
23
|
+
*/
|
|
24
|
+
export declare function mergeArtifacts(workspace: Workspace, runId: string, store: RunStore, stageMapping: Partial<Record<StageName, string>>, targetDir: string): {
|
|
25
|
+
stageEntries: DeliveryStageEntry[];
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=artifact-merger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-merger.d.ts","sourceRoot":"","sources":["../src/artifact-merger.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAC9E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAMrD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,QAAQ,EACf,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,EAChD,SAAS,EAAE,MAAM,GAChB;IAAE,YAAY,EAAE,kBAAkB,EAAE,CAAA;CAAE,CAsDxC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Artifact merger for the Forge delivery stage.
|
|
3
|
+
*
|
|
4
|
+
* Merges generated artifacts from all pipeline stages into a cohesive
|
|
5
|
+
* project structure in a target directory (staging or project root).
|
|
6
|
+
* For layouts like scalable-monorepo, service grouping (e.g. services/identity/...)
|
|
7
|
+
* is achieved by generator-emitted artifact paths under the stage's target base.
|
|
8
|
+
*/
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { mkdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Public API
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
/**
|
|
15
|
+
* Merge artifacts from all pipeline stages into a target directory.
|
|
16
|
+
*
|
|
17
|
+
* Reads artifact files from their on-disk locations and copies them
|
|
18
|
+
* into a project-like directory structure using the layout's stage mapping.
|
|
19
|
+
*
|
|
20
|
+
* @param workspace - Resolved workspace paths
|
|
21
|
+
* @param runId - Run ID to merge artifacts for
|
|
22
|
+
* @param store - RunStore for artifact metadata
|
|
23
|
+
* @param stageMapping - Maps stage name to target directory (from project layout)
|
|
24
|
+
* @param targetDir - Directory to merge into (e.g. staging dir or project root)
|
|
25
|
+
* @returns Stage entries for the manifest
|
|
26
|
+
*/
|
|
27
|
+
export function mergeArtifacts(workspace, runId, store, stageMapping, targetDir) {
|
|
28
|
+
mkdirSync(targetDir, { recursive: true });
|
|
29
|
+
const stageEntries = [];
|
|
30
|
+
const stages = store.getStages(runId);
|
|
31
|
+
for (const stage of stages) {
|
|
32
|
+
if (stage.status !== 'COMPLETED')
|
|
33
|
+
continue;
|
|
34
|
+
const artifacts = store.getArtifacts(runId, stage.stageName);
|
|
35
|
+
if (artifacts.length === 0)
|
|
36
|
+
continue;
|
|
37
|
+
const stageTargetDir = stageMapping[stage.stageName];
|
|
38
|
+
if (stageTargetDir === undefined)
|
|
39
|
+
continue;
|
|
40
|
+
const files = [];
|
|
41
|
+
for (const artifact of artifacts) {
|
|
42
|
+
const sourcePath = join(workspace.forgeDir, 'runs', runId, 'stages', artifact.stageName, 'artifacts', artifact.filePath);
|
|
43
|
+
// Determine target file path within target directory
|
|
44
|
+
const relativeTarget = stageTargetDir === '.'
|
|
45
|
+
? artifact.filePath
|
|
46
|
+
: join(stageTargetDir, artifact.filePath);
|
|
47
|
+
const destPath = join(targetDir, relativeTarget);
|
|
48
|
+
const destDir = join(destPath, '..');
|
|
49
|
+
mkdirSync(destDir, { recursive: true });
|
|
50
|
+
if (existsSync(sourcePath)) {
|
|
51
|
+
const content = readFileSync(sourcePath);
|
|
52
|
+
writeFileSync(destPath, content);
|
|
53
|
+
}
|
|
54
|
+
files.push(relativeTarget);
|
|
55
|
+
}
|
|
56
|
+
stageEntries.push({
|
|
57
|
+
stageName: stage.stageName,
|
|
58
|
+
artifactCount: files.length,
|
|
59
|
+
files,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return { stageEntries };
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=artifact-merger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-merger.js","sourceRoot":"","sources":["../src/artifact-merger.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAI7E,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,cAAc,CAC5B,SAAoB,EACpB,KAAa,EACb,KAAe,EACf,YAAgD,EAChD,SAAiB;IAEjB,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,MAAM,YAAY,GAAyB,EAAE,CAAC;IAC9C,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAEtC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW;YAAE,SAAS;QAE3C,MAAM,SAAS,GAAG,KAAK,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QAC7D,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAErC,MAAM,cAAc,GAAG,YAAY,CAAC,KAAK,CAAC,SAAsB,CAAC,CAAC;QAClE,IAAI,cAAc,KAAK,SAAS;YAAE,SAAS;QAE3C,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,UAAU,GAAG,IAAI,CACrB,SAAS,CAAC,QAAQ,EAClB,MAAM,EACN,KAAK,EACL,QAAQ,EACR,QAAQ,CAAC,SAAS,EAClB,WAAW,EACX,QAAQ,CAAC,QAAQ,CAClB,CAAC;YAEF,qDAAqD;YACrD,MAAM,cAAc,GAClB,cAAc,KAAK,GAAG;gBACpB,CAAC,CAAC,QAAQ,CAAC,QAAQ;gBACnB,CAAC,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAE9C,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;YACjD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACrC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAExC,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC3B,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;gBACzC,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACnC,CAAC;YAED,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC7B,CAAC;QAED,YAAY,CAAC,IAAI,CAAC;YAChB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,aAAa,EAAE,KAAK,CAAC,MAAM;YAC3B,KAAK;SACN,CAAC,CAAC;IACL,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeliverStage — final pipeline stage that packages and delivers
|
|
3
|
+
* all generated artifacts.
|
|
4
|
+
*
|
|
5
|
+
* Merges artifacts from all completed stages into a cohesive project
|
|
6
|
+
* structure, validates self-containment, creates an archive, and
|
|
7
|
+
* delivers to a target directory.
|
|
8
|
+
*/
|
|
9
|
+
import type { StageName } from '@hstm-labs/forge-common';
|
|
10
|
+
import type { PipelineStage, PipelineStageInput, PipelineStageOutput, PipelineContext } from '@hstm-labs/forge-core';
|
|
11
|
+
/**
|
|
12
|
+
* Pipeline stage that packages and delivers all generated artifacts.
|
|
13
|
+
*
|
|
14
|
+
* This is the final stage in the Forge pipeline. It:
|
|
15
|
+
* 1. Merges artifacts from all completed stages into a project structure
|
|
16
|
+
* 2. Generates minimal package.json and README.md if missing
|
|
17
|
+
* 3. Validates self-containment
|
|
18
|
+
* 4. Delivers to a target directory
|
|
19
|
+
* 5. Creates an archive (tgz)
|
|
20
|
+
* 6. Writes a delivery manifest
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const stage = new DeliverStage();
|
|
25
|
+
* const output = await stage.execute(input, context);
|
|
26
|
+
* const result = output.data?.delivery as DeliveryResult;
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare class DeliverStage implements PipelineStage {
|
|
30
|
+
readonly name: StageName;
|
|
31
|
+
readonly dependsOn: StageName[];
|
|
32
|
+
readonly requiresLLM = false;
|
|
33
|
+
/**
|
|
34
|
+
* Execute the delivery stage.
|
|
35
|
+
*
|
|
36
|
+
* @param _input - Pipeline stage input (not used — reads from disk)
|
|
37
|
+
* @param context - Pipeline context with config, workspace, and store
|
|
38
|
+
* @returns Stage output with delivery manifest and DeliveryResult in data
|
|
39
|
+
* @throws {ForgeError} FORGE-PIPE-001 if store is missing
|
|
40
|
+
* @throws {ForgeError} FORGE-DLVR-001 if archive creation fails
|
|
41
|
+
*/
|
|
42
|
+
execute(_input: PipelineStageInput, context: PipelineContext): Promise<PipelineStageOutput>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=deliver-stage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deliver-stage.d.ts","sourceRoot":"","sources":["../src/deliver-stage.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAOzD,OAAO,KAAK,EACV,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EAChB,MAAM,uBAAuB,CAAC;AAmG/B;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,YAAa,YAAW,aAAa;IAChD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAa;IACrC,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,CAAc;IAC7C,QAAQ,CAAC,WAAW,SAAS;IAE7B;;;;;;;;OAQG;IACG,OAAO,CACX,MAAM,EAAE,kBAAkB,EAC1B,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,mBAAmB,CAAC;CA+GhC"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeliverStage — final pipeline stage that packages and delivers
|
|
3
|
+
* all generated artifacts.
|
|
4
|
+
*
|
|
5
|
+
* Merges artifacts from all completed stages into a cohesive project
|
|
6
|
+
* structure, validates self-containment, creates an archive, and
|
|
7
|
+
* delivers to a target directory.
|
|
8
|
+
*/
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { existsSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { ForgeError, ErrorCodes, hashContent, getLayout, } from '@hstm-labs/forge-common';
|
|
12
|
+
import { mergeArtifacts } from './artifact-merger.js';
|
|
13
|
+
import { validateSelfContainment } from './self-containment.js';
|
|
14
|
+
import { createArchive } from './archiver.js';
|
|
15
|
+
import { deliverToTarget } from './delivery-writer.js';
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Constants
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
/** Forge version placeholder — replaced by semantic-release in CI. */
|
|
20
|
+
const FORGE_VERSION = '0.0.0';
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Helpers
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
/**
|
|
25
|
+
* Generate a minimal package.json for the delivered project.
|
|
26
|
+
*
|
|
27
|
+
* @param stagingDir - Staging directory to write to
|
|
28
|
+
*/
|
|
29
|
+
function generateMinimalPackageJson(stagingDir) {
|
|
30
|
+
const pkg = {
|
|
31
|
+
name: 'forge-generated-project',
|
|
32
|
+
version: '1.0.0',
|
|
33
|
+
private: true,
|
|
34
|
+
scripts: {
|
|
35
|
+
build: 'echo "Add your build command here"',
|
|
36
|
+
start: 'echo "Add your start command here"',
|
|
37
|
+
test: 'echo "Add your test command here"',
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
writeFileSync(join(stagingDir, 'package.json'), JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Generate a minimal README.md for the delivered project.
|
|
44
|
+
*
|
|
45
|
+
* @param stagingDir - Staging directory to write to
|
|
46
|
+
* @param runId - Run ID for reference
|
|
47
|
+
*/
|
|
48
|
+
function generateMinimalReadme(stagingDir, runId) {
|
|
49
|
+
const content = [
|
|
50
|
+
'# Generated Project',
|
|
51
|
+
'',
|
|
52
|
+
`This project was generated by [Forge](https://github.com/hstm-labs/forge) (run: \`${runId}\`).`,
|
|
53
|
+
'',
|
|
54
|
+
'## Setup',
|
|
55
|
+
'',
|
|
56
|
+
'1. Install dependencies:',
|
|
57
|
+
' ```bash',
|
|
58
|
+
' npm install',
|
|
59
|
+
' ```',
|
|
60
|
+
'',
|
|
61
|
+
'2. Build the project:',
|
|
62
|
+
' ```bash',
|
|
63
|
+
' npm run build',
|
|
64
|
+
' ```',
|
|
65
|
+
'',
|
|
66
|
+
'3. Start the application:',
|
|
67
|
+
' ```bash',
|
|
68
|
+
' npm start',
|
|
69
|
+
' ```',
|
|
70
|
+
'',
|
|
71
|
+
'## Project Structure',
|
|
72
|
+
'',
|
|
73
|
+
'- `src/api/` — API routes and handlers',
|
|
74
|
+
'- `src/ui/` — UI pages and components',
|
|
75
|
+
'- `src/security/` — Security middleware and configuration',
|
|
76
|
+
'- `data/` — Seed data and migrations',
|
|
77
|
+
'- `tests/` — Generated test suites',
|
|
78
|
+
'- `docs/` — Architecture and compliance reports',
|
|
79
|
+
'',
|
|
80
|
+
'## Documentation',
|
|
81
|
+
'',
|
|
82
|
+
'See the `docs/` directory for architecture decisions and compliance reports.',
|
|
83
|
+
'',
|
|
84
|
+
].join('\n');
|
|
85
|
+
writeFileSync(join(stagingDir, 'README.md'), content, 'utf-8');
|
|
86
|
+
}
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Stage
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
/**
|
|
91
|
+
* Pipeline stage that packages and delivers all generated artifacts.
|
|
92
|
+
*
|
|
93
|
+
* This is the final stage in the Forge pipeline. It:
|
|
94
|
+
* 1. Merges artifacts from all completed stages into a project structure
|
|
95
|
+
* 2. Generates minimal package.json and README.md if missing
|
|
96
|
+
* 3. Validates self-containment
|
|
97
|
+
* 4. Delivers to a target directory
|
|
98
|
+
* 5. Creates an archive (tgz)
|
|
99
|
+
* 6. Writes a delivery manifest
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```ts
|
|
103
|
+
* const stage = new DeliverStage();
|
|
104
|
+
* const output = await stage.execute(input, context);
|
|
105
|
+
* const result = output.data?.delivery as DeliveryResult;
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export class DeliverStage {
|
|
109
|
+
name = 'deliver';
|
|
110
|
+
dependsOn = ['verify'];
|
|
111
|
+
requiresLLM = false;
|
|
112
|
+
/**
|
|
113
|
+
* Execute the delivery stage.
|
|
114
|
+
*
|
|
115
|
+
* @param _input - Pipeline stage input (not used — reads from disk)
|
|
116
|
+
* @param context - Pipeline context with config, workspace, and store
|
|
117
|
+
* @returns Stage output with delivery manifest and DeliveryResult in data
|
|
118
|
+
* @throws {ForgeError} FORGE-PIPE-001 if store is missing
|
|
119
|
+
* @throws {ForgeError} FORGE-DLVR-001 if archive creation fails
|
|
120
|
+
*/
|
|
121
|
+
async execute(_input, context) {
|
|
122
|
+
// 1. Validate store is available
|
|
123
|
+
if (context.store === undefined) {
|
|
124
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Deliver stage requires a RunStore, but none was provided.');
|
|
125
|
+
}
|
|
126
|
+
// 2. Resolve layout and merge artifacts into staging directory
|
|
127
|
+
const layoutName = context.config.layout ?? 'standard';
|
|
128
|
+
const layout = getLayout(layoutName);
|
|
129
|
+
const stagingDir = join(context.workspace.forgeDir, 'runs', context.runId, 'staging');
|
|
130
|
+
const { stageEntries } = mergeArtifacts(context.workspace, context.runId, context.store, layout.stageMapping, stagingDir);
|
|
131
|
+
// 3. Generate minimal package.json and README.md if not present
|
|
132
|
+
if (!existsSync(join(stagingDir, 'package.json'))) {
|
|
133
|
+
generateMinimalPackageJson(stagingDir);
|
|
134
|
+
}
|
|
135
|
+
if (!existsSync(join(stagingDir, 'README.md'))) {
|
|
136
|
+
generateMinimalReadme(stagingDir, context.runId);
|
|
137
|
+
}
|
|
138
|
+
// 4. Deliver to project root (layout-aware target)
|
|
139
|
+
const targetPath = context.workspace.rootDir;
|
|
140
|
+
const { fileCount, totalSizeBytes } = deliverToTarget(stagingDir, targetPath);
|
|
141
|
+
// 5. Validate self-containment on project root
|
|
142
|
+
const selfContainment = validateSelfContainment(targetPath);
|
|
143
|
+
// 6. Create archive from staging (clean tree, not project root)
|
|
144
|
+
let archivePath;
|
|
145
|
+
try {
|
|
146
|
+
const archiveOutputPath = join(context.workspace.forgeDir, 'runs', context.runId, 'project.tgz');
|
|
147
|
+
const archiveResult = await createArchive(stagingDir, archiveOutputPath, 'tgz');
|
|
148
|
+
archivePath = archiveResult.archivePath;
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Archive creation failure is non-fatal
|
|
152
|
+
}
|
|
153
|
+
// 7. Build manifest
|
|
154
|
+
const manifest = {
|
|
155
|
+
runId: context.runId,
|
|
156
|
+
deliveredAt: new Date().toISOString(),
|
|
157
|
+
fileCount,
|
|
158
|
+
totalSizeBytes,
|
|
159
|
+
stages: stageEntries,
|
|
160
|
+
forgeVersion: FORGE_VERSION,
|
|
161
|
+
};
|
|
162
|
+
// 8. Write delivery.json to .forge/runs/{id}/ (Forge metadata, not project root)
|
|
163
|
+
const runsDir = join(context.workspace.forgeDir, 'runs', context.runId);
|
|
164
|
+
const manifestJson = JSON.stringify(manifest, null, 2);
|
|
165
|
+
writeFileSync(join(runsDir, 'delivery.json'), manifestJson, 'utf-8');
|
|
166
|
+
// 9. Build DeliveryResult
|
|
167
|
+
const deliveryResult = {
|
|
168
|
+
runId: context.runId,
|
|
169
|
+
targetPath,
|
|
170
|
+
archivePath,
|
|
171
|
+
manifest,
|
|
172
|
+
selfContainment,
|
|
173
|
+
};
|
|
174
|
+
// 10. Return pipeline output
|
|
175
|
+
const manifestContent = JSON.stringify(deliveryResult, null, 2);
|
|
176
|
+
return {
|
|
177
|
+
artifacts: [
|
|
178
|
+
{
|
|
179
|
+
filePath: 'delivery.json',
|
|
180
|
+
content: manifestContent,
|
|
181
|
+
contentHash: hashContent(manifestContent),
|
|
182
|
+
sizeBytes: Buffer.byteLength(manifestContent, 'utf-8'),
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
data: {
|
|
186
|
+
delivery: deliveryResult,
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=deliver-stage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deliver-stage.js","sourceRoot":"","sources":["../src/deliver-stage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delivery writer for the Forge delivery stage.
|
|
3
|
+
*
|
|
4
|
+
* Copies staged artifacts to the target delivery directory,
|
|
5
|
+
* preserving directory structure.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Deliver staged artifacts to the target directory.
|
|
9
|
+
*
|
|
10
|
+
* Copies all files from the staging directory to the target,
|
|
11
|
+
* preserving the directory structure.
|
|
12
|
+
*
|
|
13
|
+
* @param stagingDir - Source staging directory with merged artifacts
|
|
14
|
+
* @param targetPath - Target delivery directory
|
|
15
|
+
* @returns File count and total size in bytes
|
|
16
|
+
*/
|
|
17
|
+
export declare function deliverToTarget(stagingDir: string, targetPath: string): {
|
|
18
|
+
fileCount: number;
|
|
19
|
+
totalSizeBytes: number;
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=delivery-writer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-writer.d.ts","sourceRoot":"","sources":["../src/delivery-writer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuDH;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAE/C"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delivery writer for the Forge delivery stage.
|
|
3
|
+
*
|
|
4
|
+
* Copies staged artifacts to the target delivery directory,
|
|
5
|
+
* preserving directory structure.
|
|
6
|
+
*/
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { mkdirSync, readFileSync, writeFileSync, readdirSync, statSync, } from 'node:fs';
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Helpers
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
/**
|
|
13
|
+
* Recursively copy a directory, preserving structure.
|
|
14
|
+
*
|
|
15
|
+
* @param src - Source directory
|
|
16
|
+
* @param dest - Destination directory
|
|
17
|
+
* @returns File count and total size in bytes
|
|
18
|
+
*/
|
|
19
|
+
function copyDirRecursive(src, dest) {
|
|
20
|
+
mkdirSync(dest, { recursive: true });
|
|
21
|
+
let fileCount = 0;
|
|
22
|
+
let totalSizeBytes = 0;
|
|
23
|
+
const entries = readdirSync(src, { withFileTypes: true });
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
const srcPath = join(src, entry.name);
|
|
26
|
+
const destPath = join(dest, entry.name);
|
|
27
|
+
if (entry.isDirectory()) {
|
|
28
|
+
const sub = copyDirRecursive(srcPath, destPath);
|
|
29
|
+
fileCount += sub.fileCount;
|
|
30
|
+
totalSizeBytes += sub.totalSizeBytes;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
const content = readFileSync(srcPath);
|
|
34
|
+
writeFileSync(destPath, content);
|
|
35
|
+
const stat = statSync(srcPath);
|
|
36
|
+
fileCount++;
|
|
37
|
+
totalSizeBytes += stat.size;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return { fileCount, totalSizeBytes };
|
|
41
|
+
}
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Public API
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
/**
|
|
46
|
+
* Deliver staged artifacts to the target directory.
|
|
47
|
+
*
|
|
48
|
+
* Copies all files from the staging directory to the target,
|
|
49
|
+
* preserving the directory structure.
|
|
50
|
+
*
|
|
51
|
+
* @param stagingDir - Source staging directory with merged artifacts
|
|
52
|
+
* @param targetPath - Target delivery directory
|
|
53
|
+
* @returns File count and total size in bytes
|
|
54
|
+
*/
|
|
55
|
+
export function deliverToTarget(stagingDir, targetPath) {
|
|
56
|
+
return copyDirRecursive(stagingDir, targetPath);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=delivery-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-writer.js","sourceRoot":"","sources":["../src/delivery-writer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,SAAS,EACT,YAAY,EACZ,aAAa,EACb,WAAW,EACX,QAAQ,GACT,MAAM,SAAS,CAAC;AAEjB,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;;;;;GAMG;AACH,SAAS,gBAAgB,CACvB,GAAW,EACX,IAAY;IAEZ,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,cAAc,GAAG,CAAC,CAAC;IAEvB,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAExC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,gBAAgB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAChD,SAAS,IAAI,GAAG,CAAC,SAAS,CAAC;YAC3B,cAAc,IAAI,GAAG,CAAC,cAAc,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;YACtC,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACjC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC/B,SAAS,EAAE,CAAC;YACZ,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC;AACvC,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAC7B,UAAkB,EAClB,UAAkB;IAElB,OAAO,gBAAgB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delivery result formatters for the Forge CLI.
|
|
3
|
+
*
|
|
4
|
+
* Produces human-readable console output and JSON representations
|
|
5
|
+
* of the delivery result.
|
|
6
|
+
*/
|
|
7
|
+
import type { DeliveryResult } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Format a delivery result into human-readable console output.
|
|
10
|
+
*
|
|
11
|
+
* @param result - The delivery result to format
|
|
12
|
+
* @returns Multi-line formatted string
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatDeliveryResult(result: DeliveryResult): string;
|
|
15
|
+
/**
|
|
16
|
+
* Convert a delivery result to a JSON-serializable object.
|
|
17
|
+
*
|
|
18
|
+
* @param result - The delivery result
|
|
19
|
+
* @returns Plain object suitable for JSON output
|
|
20
|
+
*/
|
|
21
|
+
export declare function deliveryResultToJson(result: DeliveryResult): Record<string, unknown>;
|
|
22
|
+
//# sourceMappingURL=formatters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../src/formatters.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAoCjD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAyDnE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,cAAc,GACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delivery result formatters for the Forge CLI.
|
|
3
|
+
*
|
|
4
|
+
* Produces human-readable console output and JSON representations
|
|
5
|
+
* of the delivery result.
|
|
6
|
+
*/
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Helpers
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
/**
|
|
11
|
+
* Format byte size into a human-readable string.
|
|
12
|
+
*
|
|
13
|
+
* @param bytes - Size in bytes
|
|
14
|
+
* @returns Human-readable size string (e.g., "245 KB")
|
|
15
|
+
*/
|
|
16
|
+
function formatSize(bytes) {
|
|
17
|
+
if (bytes < 1024)
|
|
18
|
+
return `${String(bytes)} B`;
|
|
19
|
+
const kb = bytes / 1024;
|
|
20
|
+
if (kb < 1024)
|
|
21
|
+
return `${kb.toFixed(1)} KB`;
|
|
22
|
+
const mb = kb / 1024;
|
|
23
|
+
return `${mb.toFixed(1)} MB`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Pad or truncate a string to a fixed width.
|
|
27
|
+
*
|
|
28
|
+
* @param str - Input string
|
|
29
|
+
* @param width - Target width
|
|
30
|
+
* @returns Padded string
|
|
31
|
+
*/
|
|
32
|
+
function pad(str, width) {
|
|
33
|
+
if (str.length >= width)
|
|
34
|
+
return str.slice(0, width);
|
|
35
|
+
return str + ' '.repeat(width - str.length);
|
|
36
|
+
}
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Public API
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
/**
|
|
41
|
+
* Format a delivery result into human-readable console output.
|
|
42
|
+
*
|
|
43
|
+
* @param result - The delivery result to format
|
|
44
|
+
* @returns Multi-line formatted string
|
|
45
|
+
*/
|
|
46
|
+
export function formatDeliveryResult(result) {
|
|
47
|
+
const lines = [];
|
|
48
|
+
// Header box
|
|
49
|
+
lines.push('\u2554' + '\u2550'.repeat(44) + '\u2557');
|
|
50
|
+
lines.push('\u2551' + pad(' Delivery Complete', 44) + '\u2551');
|
|
51
|
+
lines.push('\u2560' + '\u2550'.repeat(44) + '\u2563');
|
|
52
|
+
lines.push('\u2551' + pad(` Run: ${result.runId}`, 44) + '\u2551');
|
|
53
|
+
lines.push('\u2551' +
|
|
54
|
+
pad(` Target: ${result.targetPath}`, 44) +
|
|
55
|
+
'\u2551');
|
|
56
|
+
lines.push('\u2551' +
|
|
57
|
+
pad(` Files: ${String(result.manifest.fileCount)}`, 44) +
|
|
58
|
+
'\u2551');
|
|
59
|
+
lines.push('\u2551' +
|
|
60
|
+
pad(` Size: ${formatSize(result.manifest.totalSizeBytes)}`, 44) +
|
|
61
|
+
'\u2551');
|
|
62
|
+
if (result.archivePath !== undefined) {
|
|
63
|
+
lines.push('\u2551' +
|
|
64
|
+
pad(` Archive: ${result.archivePath}`, 44) +
|
|
65
|
+
'\u2551');
|
|
66
|
+
}
|
|
67
|
+
lines.push('\u255A' + '\u2550'.repeat(44) + '\u255D');
|
|
68
|
+
// Self-containment checks
|
|
69
|
+
lines.push('');
|
|
70
|
+
lines.push('Self-Containment:');
|
|
71
|
+
for (const check of result.selfContainment.checks) {
|
|
72
|
+
const symbol = check.passed ? '\u2713' : '\u2717';
|
|
73
|
+
lines.push(` ${symbol} ${check.name}`);
|
|
74
|
+
}
|
|
75
|
+
// Stages included
|
|
76
|
+
lines.push('');
|
|
77
|
+
lines.push('Stages Included:');
|
|
78
|
+
for (const stage of result.manifest.stages) {
|
|
79
|
+
const count = stage.artifactCount === 1
|
|
80
|
+
? '1 file'
|
|
81
|
+
: `${String(stage.artifactCount)} files`;
|
|
82
|
+
lines.push(` ${pad(stage.stageName, 18)} ${count}`);
|
|
83
|
+
}
|
|
84
|
+
return lines.join('\n');
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Convert a delivery result to a JSON-serializable object.
|
|
88
|
+
*
|
|
89
|
+
* @param result - The delivery result
|
|
90
|
+
* @returns Plain object suitable for JSON output
|
|
91
|
+
*/
|
|
92
|
+
export function deliveryResultToJson(result) {
|
|
93
|
+
return {
|
|
94
|
+
runId: result.runId,
|
|
95
|
+
targetPath: result.targetPath,
|
|
96
|
+
archivePath: result.archivePath,
|
|
97
|
+
manifest: result.manifest,
|
|
98
|
+
selfContainment: result.selfContainment,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=formatters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatters.js","sourceRoot":"","sources":["../src/formatters.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hstm-labs/forge-deliverer — Output packaging and delivery for Forge.
|
|
3
|
+
*
|
|
4
|
+
* Provides the {@link DeliverStage} pipeline stage, artifact merging,
|
|
5
|
+
* self-containment validation, archiving, delivery writing, and
|
|
6
|
+
* result formatting.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
export type { DeliveryResult, DeliveryManifest, DeliveryStageEntry, SelfContainmentResult, SelfContainmentCheck, DeliveryOptions, } from './types.js';
|
|
11
|
+
export { DeliverStage } from './deliver-stage.js';
|
|
12
|
+
export { mergeArtifacts } from './artifact-merger.js';
|
|
13
|
+
export { validateSelfContainment } from './self-containment.js';
|
|
14
|
+
export { createArchive } from './archiver.js';
|
|
15
|
+
export { deliverToTarget } from './delivery-writer.js';
|
|
16
|
+
export { formatDeliveryResult, deliveryResultToJson } from './formatters.js';
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hstm-labs/forge-deliverer — Output packaging and delivery for Forge.
|
|
3
|
+
*
|
|
4
|
+
* Provides the {@link DeliverStage} pipeline stage, artifact merging,
|
|
5
|
+
* self-containment validation, archiving, delivery writing, and
|
|
6
|
+
* result formatting.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
export { DeliverStage } from './deliver-stage.js';
|
|
11
|
+
export { mergeArtifacts } from './artifact-merger.js';
|
|
12
|
+
export { validateSelfContainment } from './self-containment.js';
|
|
13
|
+
export { createArchive } from './archiver.js';
|
|
14
|
+
export { deliverToTarget } from './delivery-writer.js';
|
|
15
|
+
export { formatDeliveryResult, deliveryResultToJson } from './formatters.js';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAUH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-containment validator for the Forge delivery stage.
|
|
3
|
+
*
|
|
4
|
+
* Checks whether the staged project output is self-contained,
|
|
5
|
+
* including presence of package.json, build scripts, README,
|
|
6
|
+
* and absence of hardcoded Forge paths.
|
|
7
|
+
*/
|
|
8
|
+
import type { SelfContainmentResult } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Validate that a staged project directory is self-contained.
|
|
11
|
+
*
|
|
12
|
+
* Performs the following checks (non-blocking — warnings only):
|
|
13
|
+
* 1. `package.json` exists
|
|
14
|
+
* 2. Build scripts are defined in `package.json`
|
|
15
|
+
* 3. `README.md` exists
|
|
16
|
+
* 4. No hardcoded `.forge/` paths in generated code
|
|
17
|
+
*
|
|
18
|
+
* @param stagingDir - Path to the staging directory
|
|
19
|
+
* @returns Self-containment validation result
|
|
20
|
+
*/
|
|
21
|
+
export declare function validateSelfContainment(stagingDir: string): SelfContainmentResult;
|
|
22
|
+
//# sourceMappingURL=self-containment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"self-containment.d.ts","sourceRoot":"","sources":["../src/self-containment.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,qBAAqB,EAAwB,MAAM,YAAY,CAAC;AAiC9E;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,GACjB,qBAAqB,CA0FvB"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-containment validator for the Forge delivery stage.
|
|
3
|
+
*
|
|
4
|
+
* Checks whether the staged project output is self-contained,
|
|
5
|
+
* including presence of package.json, build scripts, README,
|
|
6
|
+
* and absence of hardcoded Forge paths.
|
|
7
|
+
*/
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Helpers
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
/**
|
|
14
|
+
* Recursively collect all file paths under a directory.
|
|
15
|
+
*
|
|
16
|
+
* @param dir - Directory to scan
|
|
17
|
+
* @param base - Base path for relative paths
|
|
18
|
+
* @returns Array of relative file paths
|
|
19
|
+
*/
|
|
20
|
+
function collectFiles(dir, base = dir) {
|
|
21
|
+
const results = [];
|
|
22
|
+
if (!existsSync(dir))
|
|
23
|
+
return results;
|
|
24
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
25
|
+
for (const entry of entries) {
|
|
26
|
+
const fullPath = join(dir, entry.name);
|
|
27
|
+
if (entry.isDirectory()) {
|
|
28
|
+
results.push(...collectFiles(fullPath, base));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
results.push(fullPath.slice(base.length + 1));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return results;
|
|
35
|
+
}
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Public API
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
/**
|
|
40
|
+
* Validate that a staged project directory is self-contained.
|
|
41
|
+
*
|
|
42
|
+
* Performs the following checks (non-blocking — warnings only):
|
|
43
|
+
* 1. `package.json` exists
|
|
44
|
+
* 2. Build scripts are defined in `package.json`
|
|
45
|
+
* 3. `README.md` exists
|
|
46
|
+
* 4. No hardcoded `.forge/` paths in generated code
|
|
47
|
+
*
|
|
48
|
+
* @param stagingDir - Path to the staging directory
|
|
49
|
+
* @returns Self-containment validation result
|
|
50
|
+
*/
|
|
51
|
+
export function validateSelfContainment(stagingDir) {
|
|
52
|
+
const checks = [];
|
|
53
|
+
// 1. package.json exists
|
|
54
|
+
const pkgJsonPath = join(stagingDir, 'package.json');
|
|
55
|
+
const hasPkgJson = existsSync(pkgJsonPath);
|
|
56
|
+
checks.push({
|
|
57
|
+
name: 'package.json present',
|
|
58
|
+
passed: hasPkgJson,
|
|
59
|
+
description: hasPkgJson
|
|
60
|
+
? 'package.json found in project root'
|
|
61
|
+
: 'package.json not found — project may not be installable',
|
|
62
|
+
});
|
|
63
|
+
// 2. Build scripts defined
|
|
64
|
+
let hasBuildScripts = false;
|
|
65
|
+
if (hasPkgJson) {
|
|
66
|
+
try {
|
|
67
|
+
const pkgContent = readFileSync(pkgJsonPath, 'utf-8');
|
|
68
|
+
const pkg = JSON.parse(pkgContent);
|
|
69
|
+
if (typeof pkg === 'object' &&
|
|
70
|
+
pkg !== null &&
|
|
71
|
+
'scripts' in pkg &&
|
|
72
|
+
typeof pkg['scripts'] === 'object') {
|
|
73
|
+
const scripts = pkg['scripts'];
|
|
74
|
+
hasBuildScripts =
|
|
75
|
+
'build' in scripts || 'start' in scripts || 'dev' in scripts;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Malformed package.json — treat as no build scripts
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
checks.push({
|
|
83
|
+
name: 'Build scripts defined',
|
|
84
|
+
passed: hasBuildScripts,
|
|
85
|
+
description: hasBuildScripts
|
|
86
|
+
? 'Build/start scripts found in package.json'
|
|
87
|
+
: 'No build or start scripts found in package.json',
|
|
88
|
+
});
|
|
89
|
+
// 3. README.md exists
|
|
90
|
+
const readmePath = join(stagingDir, 'README.md');
|
|
91
|
+
const hasReadme = existsSync(readmePath);
|
|
92
|
+
checks.push({
|
|
93
|
+
name: 'README.md present',
|
|
94
|
+
passed: hasReadme,
|
|
95
|
+
description: hasReadme
|
|
96
|
+
? 'README.md found in project root'
|
|
97
|
+
: 'README.md not found — project lacks documentation',
|
|
98
|
+
});
|
|
99
|
+
// 4. No hardcoded Forge paths
|
|
100
|
+
let forgePathFound = false;
|
|
101
|
+
const files = collectFiles(stagingDir);
|
|
102
|
+
for (const filePath of files) {
|
|
103
|
+
// Only scan text-like files
|
|
104
|
+
if (filePath.endsWith('.ts') ||
|
|
105
|
+
filePath.endsWith('.js') ||
|
|
106
|
+
filePath.endsWith('.json') ||
|
|
107
|
+
filePath.endsWith('.md') ||
|
|
108
|
+
filePath.endsWith('.yml') ||
|
|
109
|
+
filePath.endsWith('.yaml')) {
|
|
110
|
+
try {
|
|
111
|
+
const content = readFileSync(join(stagingDir, filePath), 'utf-8');
|
|
112
|
+
if (content.includes('.forge/')) {
|
|
113
|
+
forgePathFound = true;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Skip unreadable files
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
checks.push({
|
|
123
|
+
name: 'No hardcoded Forge paths',
|
|
124
|
+
passed: !forgePathFound,
|
|
125
|
+
description: forgePathFound
|
|
126
|
+
? 'References to .forge/ found in generated code'
|
|
127
|
+
: 'No hardcoded .forge/ paths detected',
|
|
128
|
+
});
|
|
129
|
+
// Self-containment is valid if core checks pass (package.json + README)
|
|
130
|
+
// Build scripts and forge paths are advisory
|
|
131
|
+
const valid = checks.every((c) => c.passed);
|
|
132
|
+
return { valid, checks };
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=self-containment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"self-containment.js","sourceRoot":"","sources":["../src/self-containment.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the Forge delivery stage.
|
|
3
|
+
*
|
|
4
|
+
* Defines the output structures for artifact packaging, self-containment
|
|
5
|
+
* validation, delivery manifests, and delivery options.
|
|
6
|
+
*/
|
|
7
|
+
/** Complete delivery result returned by the deliver stage. */
|
|
8
|
+
export interface DeliveryResult {
|
|
9
|
+
/** Run ID. */
|
|
10
|
+
runId: string;
|
|
11
|
+
/** Target delivery path. */
|
|
12
|
+
targetPath: string;
|
|
13
|
+
/** Archive file path (if created). */
|
|
14
|
+
archivePath?: string | undefined;
|
|
15
|
+
/** Delivery manifest. */
|
|
16
|
+
manifest: DeliveryManifest;
|
|
17
|
+
/** Self-containment validation result. */
|
|
18
|
+
selfContainment: SelfContainmentResult;
|
|
19
|
+
}
|
|
20
|
+
/** Delivery manifest written as delivery.json. */
|
|
21
|
+
export interface DeliveryManifest {
|
|
22
|
+
/** Run ID. */
|
|
23
|
+
runId: string;
|
|
24
|
+
/** ISO timestamp. */
|
|
25
|
+
deliveredAt: string;
|
|
26
|
+
/** Total files delivered. */
|
|
27
|
+
fileCount: number;
|
|
28
|
+
/** Total size in bytes. */
|
|
29
|
+
totalSizeBytes: number;
|
|
30
|
+
/** Stage artifacts included. */
|
|
31
|
+
stages: DeliveryStageEntry[];
|
|
32
|
+
/** Forge version. */
|
|
33
|
+
forgeVersion: string;
|
|
34
|
+
}
|
|
35
|
+
/** Entry for a single stage in the delivery manifest. */
|
|
36
|
+
export interface DeliveryStageEntry {
|
|
37
|
+
/** Stage name. */
|
|
38
|
+
stageName: string;
|
|
39
|
+
/** Number of artifacts from this stage. */
|
|
40
|
+
artifactCount: number;
|
|
41
|
+
/** Artifact file paths (relative). */
|
|
42
|
+
files: string[];
|
|
43
|
+
}
|
|
44
|
+
/** Result of self-containment validation checks. */
|
|
45
|
+
export interface SelfContainmentResult {
|
|
46
|
+
/** Whether the project passes self-containment checks. */
|
|
47
|
+
valid: boolean;
|
|
48
|
+
/** Checks performed. */
|
|
49
|
+
checks: SelfContainmentCheck[];
|
|
50
|
+
}
|
|
51
|
+
/** A single self-containment check result. */
|
|
52
|
+
export interface SelfContainmentCheck {
|
|
53
|
+
/** Check name. */
|
|
54
|
+
name: string;
|
|
55
|
+
/** Whether this check passed. */
|
|
56
|
+
passed: boolean;
|
|
57
|
+
/** Description of what was checked. */
|
|
58
|
+
description: string;
|
|
59
|
+
}
|
|
60
|
+
/** Options controlling delivery behavior. */
|
|
61
|
+
export interface DeliveryOptions {
|
|
62
|
+
/** Target directory for delivery (overrides config). */
|
|
63
|
+
target?: string | undefined;
|
|
64
|
+
/** Skip verification check. */
|
|
65
|
+
skipVerify?: boolean | undefined;
|
|
66
|
+
/** Create archive (tgz). */
|
|
67
|
+
archive?: boolean | undefined;
|
|
68
|
+
/** Archive format. */
|
|
69
|
+
archiveFormat?: 'tgz' | 'zip' | undefined;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC7B,cAAc;IACd,KAAK,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,yBAAyB;IACzB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,0CAA0C;IAC1C,eAAe,EAAE,qBAAqB,CAAC;CACxC;AAMD,kDAAkD;AAClD,MAAM,WAAW,gBAAgB;IAC/B,cAAc;IACd,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,qBAAqB;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,yDAAyD;AACzD,MAAM,WAAW,kBAAkB;IACjC,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,aAAa,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAMD,oDAAoD;AACpD,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,KAAK,EAAE,OAAO,CAAC;IACf,wBAAwB;IACxB,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC;AAED,8CAA8C;AAC9C,MAAM,WAAW,oBAAoB;IACnC,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;CACrB;AAMD,6CAA6C;AAC7C,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,+BAA+B;IAC/B,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,4BAA4B;IAC5B,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,sBAAsB;IACtB,aAAa,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC;CAC3C"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hstm-labs/forge-deliverer",
|
|
3
|
+
"version": "0.1.11",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@hstm-labs/forge-common": "0.1.11",
|
|
20
|
+
"@hstm-labs/forge-core": "0.1.11"
|
|
21
|
+
}
|
|
22
|
+
}
|