@forgehive/forge-cli 0.2.11 → 0.2.13

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.
@@ -0,0 +1,26 @@
1
+ import archiver from 'archiver';
2
+ import fs from 'fs';
3
+ export declare const bytesToMB: (bytes: number) => string;
4
+ export declare const zip: import("@forgehive/task").TaskInstanceType<(argv: {
5
+ dir: string;
6
+ input: string;
7
+ output: string;
8
+ }, boundaries: import("@forgehive/task").WrappedBoundaries<{
9
+ createWriteStream: (outputPath: string) => Promise<fs.WriteStream>;
10
+ createArchiver: (format: "zip", options: {
11
+ zlib: {
12
+ level: number;
13
+ };
14
+ }) => Promise<archiver.Archiver>;
15
+ resolvePathDir: (dir: string, filename: string) => Promise<string>;
16
+ fileExists: (filePath: string) => Promise<boolean>;
17
+ }>) => Promise<unknown>, {
18
+ createWriteStream: (outputPath: string) => Promise<fs.WriteStream>;
19
+ createArchiver: (format: "zip", options: {
20
+ zlib: {
21
+ level: number;
22
+ };
23
+ }) => Promise<archiver.Archiver>;
24
+ resolvePathDir: (dir: string, filename: string) => Promise<string>;
25
+ fileExists: (filePath: string) => Promise<boolean>;
26
+ }>;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ // TASK: zip
3
+ // Run this task with:
4
+ // forge task:run bundle:zip --dir .builds/ --input dailyUpdate.js --output dailyUpdate.zip
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.zip = exports.bytesToMB = void 0;
10
+ const task_1 = require("@forgehive/task");
11
+ const schema_1 = require("@forgehive/schema");
12
+ const archiver_1 = __importDefault(require("archiver"));
13
+ const fs_1 = __importDefault(require("fs"));
14
+ const path_1 = __importDefault(require("path"));
15
+ const description = 'Zip a bundle file for distribution';
16
+ const schema = new schema_1.Schema({
17
+ dir: schema_1.Schema.string(),
18
+ input: schema_1.Schema.string(),
19
+ output: schema_1.Schema.string()
20
+ });
21
+ const boundaries = {
22
+ createWriteStream: async (outputPath) => {
23
+ return fs_1.default.createWriteStream(outputPath);
24
+ },
25
+ createArchiver: async (format, options) => {
26
+ return (0, archiver_1.default)(format, options);
27
+ },
28
+ resolvePathDir: async (dir, filename) => {
29
+ return path_1.default.resolve(dir, filename);
30
+ },
31
+ fileExists: async (filePath) => {
32
+ try {
33
+ await fs_1.default.promises.access(filePath);
34
+ return true;
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }
40
+ };
41
+ const bytesToMB = (bytes) => {
42
+ const MB = bytes / (1024 * 1024);
43
+ return `${MB.toFixed(2)} MB`;
44
+ };
45
+ exports.bytesToMB = bytesToMB;
46
+ exports.zip = (0, task_1.createTask)(schema, boundaries, async function ({ dir, input, output }, { createWriteStream, createArchiver, resolvePathDir, fileExists }) {
47
+ const outputPath = await resolvePathDir(dir, output);
48
+ const inputPath = await resolvePathDir(dir, input);
49
+ const inputMapPath = inputPath + '.map';
50
+ // Check if input file exists
51
+ const inputExists = await fileExists(inputPath);
52
+ if (!inputExists) {
53
+ throw new Error(`Input file does not exist: ${inputPath}`);
54
+ }
55
+ // Check if source map exists before creating Promise
56
+ const mapExists = await fileExists(inputMapPath);
57
+ // Handle async operations outside of Promise constructor
58
+ const outStream = await createWriteStream(outputPath);
59
+ const archive = await createArchiver('zip', {
60
+ zlib: { level: 9 } // Sets the compression level
61
+ });
62
+ return new Promise((resolve, reject) => {
63
+ archive.on('error', function (err) {
64
+ reject(err);
65
+ });
66
+ outStream.on('end', function () {
67
+ console.log('Data has been drained');
68
+ });
69
+ outStream.on('close', function () {
70
+ setTimeout(() => {
71
+ resolve({
72
+ output,
73
+ outputPath,
74
+ size: archive.pointer()
75
+ });
76
+ }, 100);
77
+ });
78
+ archive.on('warning', function (err) {
79
+ if (err.code === 'ENOENT') {
80
+ console.warn('ENOENT', err);
81
+ }
82
+ else {
83
+ reject(err);
84
+ }
85
+ });
86
+ archive.pipe(outStream);
87
+ // Add the main bundle file
88
+ archive.file(inputPath, { name: 'index.js' });
89
+ // Add source map if it exists
90
+ if (mapExists) {
91
+ archive.file(inputMapPath, { name: 'index.js.map' });
92
+ }
93
+ archive.finalize();
94
+ });
95
+ });
96
+ exports.zip.setDescription(description);
@@ -14,6 +14,11 @@ export declare const publish: import("@forgehive/task").TaskInstanceType<(argv:
14
14
  bundleLoad: (args: {
15
15
  bundlePath: string;
16
16
  }) => Promise<Promise<any>>;
17
+ bundleZip: (args: {
18
+ dir: string;
19
+ input: string;
20
+ output: string;
21
+ }) => Promise<Promise<unknown>>;
17
22
  readFileUtf8: (filePath: string) => Promise<string>;
18
23
  readFileBinary: (filePath: string) => Promise<Buffer>;
19
24
  publishTask: (data: any, profile: Profile) => Promise<any>;
@@ -35,6 +40,11 @@ export declare const publish: import("@forgehive/task").TaskInstanceType<(argv:
35
40
  bundleLoad: (args: {
36
41
  bundlePath: string;
37
42
  }) => Promise<Promise<any>>;
43
+ bundleZip: (args: {
44
+ dir: string;
45
+ input: string;
46
+ output: string;
47
+ }) => Promise<Promise<unknown>>;
38
48
  readFileUtf8: (filePath: string) => Promise<string>;
39
49
  readFileBinary: (filePath: string) => Promise<Buffer>;
40
50
  publishTask: (data: any, profile: Profile) => Promise<any>;
@@ -16,6 +16,7 @@ const os_1 = __importDefault(require("os"));
16
16
  const load_1 = require("../conf/load");
17
17
  const create_1 = require("../bundle/create");
18
18
  const load_2 = require("../bundle/load");
19
+ const zip_1 = require("../bundle/zip");
19
20
  const loadCurrent_1 = require("../auth/loadCurrent");
20
21
  const schema = new schema_1.Schema({
21
22
  descriptorName: schema_1.Schema.string()
@@ -28,6 +29,7 @@ const boundaries = {
28
29
  loadCurrentProfile: loadCurrent_1.loadCurrent.asBoundary(),
29
30
  bundleCreate: create_1.create.asBoundary(),
30
31
  bundleLoad: load_2.load.asBoundary(),
32
+ bundleZip: zip_1.zip.asBoundary(),
31
33
  readFileUtf8: async (filePath) => {
32
34
  return promises_1.default.readFile(filePath, 'utf-8');
33
35
  },
@@ -75,7 +77,7 @@ const boundaries = {
75
77
  return buildsPath;
76
78
  }
77
79
  };
78
- exports.publish = (0, task_1.createTask)(schema, boundaries, async function ({ descriptorName }, { getCwd, ensureBuildsFolder, loadConf, bundleCreate, bundleLoad, readFileUtf8, readFileBinary, publishTask, loadCurrentProfile, uploadBundleWithPresignedUrl }) {
80
+ exports.publish = (0, task_1.createTask)(schema, boundaries, async function ({ descriptorName }, { getCwd, ensureBuildsFolder, loadConf, bundleCreate, bundleLoad, bundleZip, readFileUtf8, readFileBinary, publishTask, loadCurrentProfile, uploadBundleWithPresignedUrl }) {
79
81
  const cwd = await getCwd();
80
82
  const forgeJson = await loadConf({});
81
83
  const profile = await loadCurrentProfile({});
@@ -87,12 +89,20 @@ exports.publish = (0, task_1.createTask)(schema, boundaries, async function ({ d
87
89
  const entryPoint = path_1.default.join(cwd, taskDescriptor.path);
88
90
  const buildsPath = await ensureBuildsFolder();
89
91
  const outputFile = path_1.default.join(buildsPath, `${descriptorName}.js`);
92
+ const zipFile = `${descriptorName}.zip`;
90
93
  // Bundle the task
91
94
  await bundleCreate({
92
95
  entryPoint,
93
96
  outputFile
94
97
  });
95
98
  console.log('Bundle created...');
99
+ // Zip the bundle
100
+ await bundleZip({
101
+ dir: buildsPath,
102
+ input: `${descriptorName}.js`,
103
+ output: zipFile
104
+ });
105
+ console.log('Bundle zipped...');
96
106
  // Load the bundled task
97
107
  const bundle = await bundleLoad({
98
108
  bundlePath: outputFile
@@ -105,13 +115,16 @@ exports.publish = (0, task_1.createTask)(schema, boundaries, async function ({ d
105
115
  const schemaDescriptor = schema.describe();
106
116
  // Read the task file content
107
117
  const sourceCode = await readFileUtf8(entryPoint);
108
- const bundleContent = await readFileBinary(outputFile);
118
+ // Read the zipped bundle instead of the raw bundle
119
+ const zipPath = path_1.default.join(buildsPath, zipFile);
120
+ const bundleContent = await readFileBinary(zipPath);
109
121
  // Get bundle size
110
122
  const bundleSize = bundleContent.length;
111
123
  // First, publish task metadata and get presigned URL for bundle upload
112
124
  const data = {
113
125
  ...taskDescriptor,
114
126
  taskName: descriptorName,
127
+ handler: taskDescriptor.handler,
115
128
  projectName,
116
129
  description,
117
130
  schemaDescriptor: JSON.stringify(schemaDescriptor),
@@ -122,9 +135,9 @@ exports.publish = (0, task_1.createTask)(schema, boundaries, async function ({ d
122
135
  // Publish metadata to hive api server
123
136
  console.log(`Publishing metadata and source code to ${profile.url}...`);
124
137
  const publishResponse = await publishTask(data, profile);
125
- // Upload bundle using the presigned URL
138
+ // Upload zipped bundle using the presigned URL
126
139
  if (publishResponse.bundleUploadUrl) {
127
- console.log('Uploading bundle...');
140
+ console.log('Uploading zipped bundle...');
128
141
  await uploadBundleWithPresignedUrl(publishResponse.bundleUploadUrl, bundleContent);
129
142
  return {
130
143
  descriptor: taskDescriptor,
package/forge.json CHANGED
@@ -85,6 +85,10 @@
85
85
  "fixture:download": {
86
86
  "path": "src/tasks/fixture/download.ts",
87
87
  "handler": "download"
88
+ },
89
+ "bundle:zip": {
90
+ "path": "src/tasks/bundle/zip.ts",
91
+ "handler": "zip"
88
92
  }
89
93
  },
90
94
  "runners": {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgehive/forge-cli",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "description": "TypeScript CLI application",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -13,24 +13,26 @@
13
13
  "@forgehive/record-tape": "^0.1.4",
14
14
  "@forgehive/runner": "^0.1.10",
15
15
  "@forgehive/schema": "^0.1.4",
16
- "@forgehive/task": "^0.1.11",
16
+ "@forgehive/task": "^0.1.13",
17
17
  "esbuild": "^0.25.0",
18
18
  "handlebars": "^4.7.8",
19
19
  "minimist": "^1.2.8"
20
20
  }
21
21
  },
22
22
  "dependencies": {
23
+ "archiver": "^7.0.1",
23
24
  "axios": "^1.8.4",
24
25
  "dotenv": "^16.5.0",
25
26
  "esbuild": "^0.25.0",
26
27
  "handlebars": "^4.7.8",
27
28
  "minimist": "^1.2.8",
28
- "@forgehive/runner": "0.1.10",
29
- "@forgehive/record-tape": "0.1.4",
30
- "@forgehive/schema": "0.1.4",
31
- "@forgehive/task": "0.1.11"
29
+ "@forgehive/record-tape": "0.1.6",
30
+ "@forgehive/runner": "0.1.12",
31
+ "@forgehive/task": "0.1.13",
32
+ "@forgehive/schema": "0.1.4"
32
33
  },
33
34
  "devDependencies": {
35
+ "@types/archiver": "^6.0.3",
34
36
  "@types/jest": "^29.5.3",
35
37
  "@types/minimist": "^1.2.5",
36
38
  "@types/node": "^20.4.5",
@@ -0,0 +1,109 @@
1
+ // TASK: zip
2
+ // Run this task with:
3
+ // forge task:run bundle:zip --dir .builds/ --input dailyUpdate.js --output dailyUpdate.zip
4
+
5
+ import { createTask } from '@forgehive/task'
6
+ import { Schema } from '@forgehive/schema'
7
+ import archiver from 'archiver'
8
+ import fs from 'fs'
9
+ import path from 'path'
10
+
11
+ const description = 'Zip a bundle file for distribution'
12
+
13
+ const schema = new Schema({
14
+ dir: Schema.string(),
15
+ input: Schema.string(),
16
+ output: Schema.string()
17
+ })
18
+
19
+ const boundaries = {
20
+ createWriteStream: async (outputPath: string): Promise<fs.WriteStream> => {
21
+ return fs.createWriteStream(outputPath)
22
+ },
23
+ createArchiver: async (format: 'zip', options: { zlib: { level: number } }): Promise<archiver.Archiver> => {
24
+ return archiver(format, options)
25
+ },
26
+ resolvePathDir: async (dir: string, filename: string): Promise<string> => {
27
+ return path.resolve(dir, filename)
28
+ },
29
+ fileExists: async (filePath: string): Promise<boolean> => {
30
+ try {
31
+ await fs.promises.access(filePath)
32
+ return true
33
+ } catch {
34
+ return false
35
+ }
36
+ }
37
+ }
38
+
39
+ export const bytesToMB = (bytes: number): string => {
40
+ const MB = bytes / (1024 * 1024)
41
+ return `${MB.toFixed(2)} MB`
42
+ }
43
+
44
+ export const zip = createTask(
45
+ schema,
46
+ boundaries,
47
+ async function ({ dir, input, output }, { createWriteStream, createArchiver, resolvePathDir, fileExists }) {
48
+ const outputPath = await resolvePathDir(dir, output)
49
+ const inputPath = await resolvePathDir(dir, input)
50
+ const inputMapPath = inputPath + '.map'
51
+
52
+ // Check if input file exists
53
+ const inputExists = await fileExists(inputPath)
54
+ if (!inputExists) {
55
+ throw new Error(`Input file does not exist: ${inputPath}`)
56
+ }
57
+
58
+ // Check if source map exists before creating Promise
59
+ const mapExists = await fileExists(inputMapPath)
60
+
61
+ // Handle async operations outside of Promise constructor
62
+ const outStream = await createWriteStream(outputPath)
63
+ const archive = await createArchiver('zip', {
64
+ zlib: { level: 9 } // Sets the compression level
65
+ })
66
+
67
+ return new Promise((resolve, reject) => {
68
+ archive.on('error', function (err: Error) {
69
+ reject(err)
70
+ })
71
+
72
+ outStream.on('end', function () {
73
+ console.log('Data has been drained')
74
+ })
75
+
76
+ outStream.on('close', function () {
77
+ setTimeout(() => {
78
+ resolve({
79
+ output,
80
+ outputPath,
81
+ size: archive.pointer()
82
+ })
83
+ }, 100)
84
+ })
85
+
86
+ archive.on('warning', function (err: archiver.ArchiverError) {
87
+ if (err.code === 'ENOENT') {
88
+ console.warn('ENOENT', err)
89
+ } else {
90
+ reject(err)
91
+ }
92
+ })
93
+
94
+ archive.pipe(outStream)
95
+
96
+ // Add the main bundle file
97
+ archive.file(inputPath, { name: 'index.js' })
98
+
99
+ // Add source map if it exists
100
+ if (mapExists) {
101
+ archive.file(inputMapPath, { name: 'index.js.map' })
102
+ }
103
+
104
+ archive.finalize()
105
+ })
106
+ }
107
+ )
108
+
109
+ zip.setDescription(description)
@@ -13,6 +13,7 @@ import os from 'os'
13
13
  import { load as loadConf } from '../conf/load'
14
14
  import { create as bundleCreate } from '../bundle/create'
15
15
  import { load as bundleLoad } from '../bundle/load'
16
+ import { zip as bundleZip } from '../bundle/zip'
16
17
  import { loadCurrent as loadCurrentProfile } from '../auth/loadCurrent'
17
18
  import { Profile } from '../types'
18
19
 
@@ -28,6 +29,7 @@ const boundaries = {
28
29
  loadCurrentProfile: loadCurrentProfile.asBoundary(),
29
30
  bundleCreate: bundleCreate.asBoundary(),
30
31
  bundleLoad: bundleLoad.asBoundary(),
32
+ bundleZip: bundleZip.asBoundary(),
31
33
  readFileUtf8: async (filePath: string): Promise<string> => {
32
34
  return fs.readFile(filePath, 'utf-8')
33
35
  },
@@ -88,6 +90,7 @@ export const publish = createTask(
88
90
  loadConf,
89
91
  bundleCreate,
90
92
  bundleLoad,
93
+ bundleZip,
91
94
  readFileUtf8,
92
95
  readFileBinary,
93
96
  publishTask,
@@ -108,6 +111,7 @@ export const publish = createTask(
108
111
  const entryPoint = path.join(cwd, taskDescriptor.path)
109
112
  const buildsPath = await ensureBuildsFolder()
110
113
  const outputFile = path.join(buildsPath, `${descriptorName}.js`)
114
+ const zipFile = `${descriptorName}.zip`
111
115
 
112
116
  // Bundle the task
113
117
  await bundleCreate({
@@ -117,6 +121,15 @@ export const publish = createTask(
117
121
 
118
122
  console.log('Bundle created...')
119
123
 
124
+ // Zip the bundle
125
+ await bundleZip({
126
+ dir: buildsPath,
127
+ input: `${descriptorName}.js`,
128
+ output: zipFile
129
+ })
130
+
131
+ console.log('Bundle zipped...')
132
+
120
133
  // Load the bundled task
121
134
  const bundle = await bundleLoad({
122
135
  bundlePath: outputFile
@@ -131,7 +144,9 @@ export const publish = createTask(
131
144
 
132
145
  // Read the task file content
133
146
  const sourceCode = await readFileUtf8(entryPoint)
134
- const bundleContent = await readFileBinary(outputFile)
147
+ // Read the zipped bundle instead of the raw bundle
148
+ const zipPath = path.join(buildsPath, zipFile)
149
+ const bundleContent = await readFileBinary(zipPath)
135
150
 
136
151
  // Get bundle size
137
152
  const bundleSize = bundleContent.length
@@ -140,6 +155,7 @@ export const publish = createTask(
140
155
  const data = {
141
156
  ...taskDescriptor,
142
157
  taskName: descriptorName,
158
+ handler: taskDescriptor.handler,
143
159
  projectName,
144
160
  description,
145
161
  schemaDescriptor: JSON.stringify(schemaDescriptor),
@@ -152,9 +168,9 @@ export const publish = createTask(
152
168
  console.log(`Publishing metadata and source code to ${profile.url}...`)
153
169
  const publishResponse = await publishTask(data, profile)
154
170
 
155
- // Upload bundle using the presigned URL
171
+ // Upload zipped bundle using the presigned URL
156
172
  if (publishResponse.bundleUploadUrl) {
157
- console.log('Uploading bundle...')
173
+ console.log('Uploading zipped bundle...')
158
174
  await uploadBundleWithPresignedUrl(
159
175
  publishResponse.bundleUploadUrl,
160
176
  bundleContent