@forgehive/forge-cli 0.2.14 → 0.3.0
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/dist/runner.js +3 -1
- package/dist/tasks/auth/add.js +23 -19
- package/dist/tasks/auth/list.js +20 -16
- package/dist/tasks/auth/load.js +19 -15
- package/dist/tasks/auth/loadCurrent.js +13 -9
- package/dist/tasks/auth/remove.js +30 -26
- package/dist/tasks/auth/switch.js +19 -15
- package/dist/tasks/bundle/create.js +16 -12
- package/dist/tasks/bundle/fingerprint.d.ts +36 -0
- package/dist/tasks/bundle/fingerprint.js +164 -0
- package/dist/tasks/bundle/load.js +9 -5
- package/dist/tasks/bundle/zip.js +49 -45
- package/dist/tasks/conf/info.js +23 -19
- package/dist/tasks/conf/load.js +8 -4
- package/dist/tasks/fixture/download.js +40 -36
- package/dist/tasks/init.js +35 -31
- package/dist/tasks/runner/bundle.js +34 -30
- package/dist/tasks/runner/create.js +28 -24
- package/dist/tasks/runner/remove.js +22 -18
- package/dist/tasks/task/createTask.js +35 -28
- package/dist/tasks/task/describe.js +85 -81
- package/dist/tasks/task/download.js +63 -59
- package/dist/tasks/task/fingerprint.d.ts +26 -0
- package/dist/tasks/task/fingerprint.js +87 -0
- package/dist/tasks/task/list.js +27 -23
- package/dist/tasks/task/publish.js +72 -68
- package/dist/tasks/task/remove.js +24 -20
- package/dist/tasks/task/replay.js +94 -90
- package/dist/tasks/task/run.js +84 -79
- package/dist/test/tasks/create.test.js +6 -5
- package/dist/utils/taskAnalysis.d.ts +21 -0
- package/dist/utils/taskAnalysis.js +380 -0
- package/forge.json +12 -0
- package/logs/task:fingerprint.log +10 -0
- package/package.json +7 -7
- package/specs/fingerprint.md +380 -0
- package/src/runner.ts +3 -1
- package/src/tasks/README.md +13 -13
- package/src/tasks/auth/add.ts +3 -3
- package/src/tasks/auth/list.ts +3 -3
- package/src/tasks/auth/load.ts +3 -3
- package/src/tasks/auth/loadCurrent.ts +3 -3
- package/src/tasks/auth/remove.ts +3 -3
- package/src/tasks/auth/switch.ts +3 -3
- package/src/tasks/bundle/README.md +7 -7
- package/src/tasks/bundle/create.ts +4 -4
- package/src/tasks/bundle/fingerprint.ts +218 -0
- package/src/tasks/bundle/load.ts +4 -4
- package/src/tasks/bundle/zip.ts +3 -3
- package/src/tasks/conf/info.ts +3 -3
- package/src/tasks/conf/load.ts +3 -3
- package/src/tasks/fixture/download.ts +3 -3
- package/src/tasks/init.ts +3 -3
- package/src/tasks/runner/bundle.ts +3 -3
- package/src/tasks/runner/create.ts +3 -3
- package/src/tasks/runner/remove.ts +3 -3
- package/src/tasks/task/createTask.ts +10 -7
- package/src/tasks/task/describe.ts +3 -3
- package/src/tasks/task/download.ts +3 -3
- package/src/tasks/task/fingerprint.ts +107 -0
- package/src/tasks/task/list.ts +3 -3
- package/src/tasks/task/publish.ts +3 -3
- package/src/tasks/task/remove.ts +3 -3
- package/src/tasks/task/replay.ts +3 -3
- package/src/tasks/task/run.ts +5 -4
- package/src/test/tasks/create.test.ts +9 -9
- package/src/utils/taskAnalysis.ts +419 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgehive/forge-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "TypeScript CLI application",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
"publishConfig": {
|
|
11
11
|
"access": "public",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@forgehive/record-tape": "^0.
|
|
14
|
-
"@forgehive/runner": "^0.
|
|
13
|
+
"@forgehive/record-tape": "^0.2.0",
|
|
14
|
+
"@forgehive/runner": "^0.2.0",
|
|
15
15
|
"@forgehive/schema": "^0.1.4",
|
|
16
|
-
"@forgehive/task": "^0.
|
|
16
|
+
"@forgehive/task": "^0.2.0",
|
|
17
17
|
"esbuild": "^0.25.0",
|
|
18
18
|
"handlebars": "^4.7.8",
|
|
19
19
|
"minimist": "^1.2.8"
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
"esbuild": "^0.25.0",
|
|
27
27
|
"handlebars": "^4.7.8",
|
|
28
28
|
"minimist": "^1.2.8",
|
|
29
|
-
"@forgehive/record-tape": "0.
|
|
30
|
-
"@forgehive/
|
|
29
|
+
"@forgehive/record-tape": "0.2.0",
|
|
30
|
+
"@forgehive/task": "0.2.0",
|
|
31
31
|
"@forgehive/schema": "0.1.4",
|
|
32
|
-
"@forgehive/
|
|
32
|
+
"@forgehive/runner": "0.2.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/archiver": "^6.0.3",
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# Task Fingerprinting System Specification
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This specification describes a build-time code generation system that extracts comprehensive type information ("fingerprints") from TypeScript tasks during the esbuild bundling process. The system provides complete introspection capabilities for tasks created with the `@forgehive/task` library.
|
|
6
|
+
|
|
7
|
+
## Problem Statement
|
|
8
|
+
|
|
9
|
+
Currently, TypeScript task functions created with `createTask` have limited runtime introspection capabilities. We need a way to:
|
|
10
|
+
|
|
11
|
+
1. Extract complete type information about task inputs, outputs, and boundaries
|
|
12
|
+
2. Generate comprehensive task fingerprints during build time
|
|
13
|
+
3. Provide zero-runtime-overhead type introspection
|
|
14
|
+
4. Enable tooling, documentation generation, and runtime validation
|
|
15
|
+
5. Detect changes in task interfaces through hash-based fingerprinting
|
|
16
|
+
|
|
17
|
+
## Architecture
|
|
18
|
+
|
|
19
|
+
### Core Components
|
|
20
|
+
|
|
21
|
+
1. **esbuild Plugin**: `taskFingerprintPlugin` - Analyzes TypeScript AST during bundling
|
|
22
|
+
2. **Type Extractor**: Processes `createTask` calls and extracts type information
|
|
23
|
+
3. **Fingerprint Generator**: Creates comprehensive task metadata
|
|
24
|
+
4. **Bundle Task**: Enhanced version of existing bundle creation with fingerprinting
|
|
25
|
+
|
|
26
|
+
### Data Flow
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
TypeScript Source Files
|
|
30
|
+
↓
|
|
31
|
+
esbuild Plugin (AST Analysis)
|
|
32
|
+
↓
|
|
33
|
+
Task Discovery & Type Extraction
|
|
34
|
+
↓
|
|
35
|
+
Fingerprint Generation
|
|
36
|
+
↓
|
|
37
|
+
JSON Output + Bundle Creation
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Technical Requirements
|
|
41
|
+
|
|
42
|
+
### Dependencies
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import * as esbuild from 'esbuild'
|
|
46
|
+
import * as ts from 'typescript'
|
|
47
|
+
import * as fs from 'fs'
|
|
48
|
+
import * as path from 'path'
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Core Interfaces
|
|
52
|
+
|
|
53
|
+
#### TaskFingerprint
|
|
54
|
+
```typescript
|
|
55
|
+
interface TaskFingerprint {
|
|
56
|
+
name: string
|
|
57
|
+
description?: string
|
|
58
|
+
location: {
|
|
59
|
+
file: string
|
|
60
|
+
line: number
|
|
61
|
+
column: number
|
|
62
|
+
}
|
|
63
|
+
inputSchema: {
|
|
64
|
+
type: string
|
|
65
|
+
properties: Record<string, any>
|
|
66
|
+
}
|
|
67
|
+
outputType: string
|
|
68
|
+
boundaries: Record<string, {
|
|
69
|
+
inputTypes: string[]
|
|
70
|
+
outputType: string
|
|
71
|
+
signature: string
|
|
72
|
+
}>
|
|
73
|
+
functionSource: string
|
|
74
|
+
hash: string
|
|
75
|
+
metadata: {
|
|
76
|
+
extractedAt: string
|
|
77
|
+
version: string
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### FingerprintResult
|
|
83
|
+
```typescript
|
|
84
|
+
interface FingerprintResult {
|
|
85
|
+
tasks: TaskFingerprint[]
|
|
86
|
+
buildInfo: {
|
|
87
|
+
entryPoint: string
|
|
88
|
+
outputFile: string
|
|
89
|
+
fingerprintsFile?: string
|
|
90
|
+
totalTasks: number
|
|
91
|
+
buildTimestamp: string
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Implementation Specification
|
|
97
|
+
|
|
98
|
+
### 1. esbuild Plugin (`taskFingerprintPlugin`)
|
|
99
|
+
|
|
100
|
+
**Purpose**: Integrate TypeScript AST analysis into the esbuild bundling process
|
|
101
|
+
|
|
102
|
+
**Key Functions**:
|
|
103
|
+
- `onLoad`: Analyze `.ts` files for `createTask` calls
|
|
104
|
+
- `onEnd`: Generate and write fingerprint JSON file
|
|
105
|
+
- Handle TypeScript compiler integration
|
|
106
|
+
- Extract comprehensive type information
|
|
107
|
+
|
|
108
|
+
**Configuration**:
|
|
109
|
+
```typescript
|
|
110
|
+
interface PluginOptions {
|
|
111
|
+
tsConfigPath: string // Path to tsconfig.json
|
|
112
|
+
outputPath?: string // Output file for fingerprints
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 2. Task Discovery Algorithm
|
|
117
|
+
|
|
118
|
+
**Detection Pattern**: Look for these patterns in TypeScript AST:
|
|
119
|
+
```typescript
|
|
120
|
+
// Direct calls
|
|
121
|
+
createTask(schema, boundaries, fn)
|
|
122
|
+
|
|
123
|
+
// Exported assignments
|
|
124
|
+
export const taskName = createTask(schema, boundaries, fn)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**AST Node Types to Process**:
|
|
128
|
+
- `ts.CallExpression` with identifier "createTask"
|
|
129
|
+
- `ts.VariableStatement` with exported createTask assignments
|
|
130
|
+
|
|
131
|
+
### 3. Type Extraction Specifications
|
|
132
|
+
|
|
133
|
+
#### Schema Analysis
|
|
134
|
+
- Detect `new Schema({ ... })` patterns
|
|
135
|
+
- Extract property definitions and types
|
|
136
|
+
- Handle Schema methods: `.string()`, `.number()`, `.boolean()`, `.optional()`, `.default()`
|
|
137
|
+
- Generate JSON Schema-compatible output
|
|
138
|
+
|
|
139
|
+
#### Boundary Analysis
|
|
140
|
+
- Extract function signatures from boundary object
|
|
141
|
+
- Parse parameter types and return types
|
|
142
|
+
- Handle async functions and Promise return types
|
|
143
|
+
- Truncate function source for readability (200 chars max)
|
|
144
|
+
|
|
145
|
+
#### Return Type Analysis
|
|
146
|
+
- Extract TypeScript return type annotations
|
|
147
|
+
- Parse Promise wrapper types
|
|
148
|
+
- Infer types from return statements when annotations missing
|
|
149
|
+
- Handle complex object return types
|
|
150
|
+
|
|
151
|
+
### 4. Hash Generation Algorithm
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
function generateHash(input: string): string {
|
|
155
|
+
// Simple hash function for change detection
|
|
156
|
+
let hash = 0
|
|
157
|
+
for (let i = 0; i < input.length; i++) {
|
|
158
|
+
const char = input.charCodeAt(i)
|
|
159
|
+
hash = ((hash << 5) - hash) + char
|
|
160
|
+
hash = hash & hash // Convert to 32-bit integer
|
|
161
|
+
}
|
|
162
|
+
return Math.abs(hash).toString(36)
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Hash Input**: Combination of task name, function source, and input schema
|
|
167
|
+
|
|
168
|
+
## Task Configuration Schema
|
|
169
|
+
|
|
170
|
+
### Enhanced Bundle Task Schema
|
|
171
|
+
```typescript
|
|
172
|
+
const schema = new Schema({
|
|
173
|
+
entryPoint: Schema.string(),
|
|
174
|
+
outputFile: Schema.string(),
|
|
175
|
+
generateFingerprints: Schema.boolean().optional().default(true),
|
|
176
|
+
fingerprintsOutputFile: Schema.string().optional(),
|
|
177
|
+
tsConfigPath: Schema.string().optional().default('tsconfig.json')
|
|
178
|
+
})
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Boundaries
|
|
182
|
+
```typescript
|
|
183
|
+
const boundaries = {
|
|
184
|
+
readFile: async (filePath: string): Promise<string> => { /* fs.readFile */ },
|
|
185
|
+
writeFile: async (filePath: string, content: string): Promise<void> => { /* fs.writeFile */ },
|
|
186
|
+
findTsFiles: async (dir: string): Promise<string[]> => { /* glob search */ }
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Output Specifications
|
|
191
|
+
|
|
192
|
+
### Generated Fingerprint File Structure
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"tasks": [
|
|
197
|
+
{
|
|
198
|
+
"name": "getPrice",
|
|
199
|
+
"description": "Fetches stock price for a given ticker",
|
|
200
|
+
"location": {
|
|
201
|
+
"file": "src/tasks/price.ts",
|
|
202
|
+
"line": 15,
|
|
203
|
+
"column": 23
|
|
204
|
+
},
|
|
205
|
+
"inputSchema": {
|
|
206
|
+
"type": "object",
|
|
207
|
+
"properties": {
|
|
208
|
+
"ticker": { "type": "string" }
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
"outputType": "Promise<{ ticker: string, price: number }>",
|
|
212
|
+
"boundaries": {
|
|
213
|
+
"fetchStockPrice": {
|
|
214
|
+
"inputTypes": ["string"],
|
|
215
|
+
"outputType": "Promise<{ price: number }>",
|
|
216
|
+
"signature": "async (ticker: string): Promise<{ price: number }> => {...}"
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
"functionSource": "async function ({ ticker }, { fetchStockPrice }) { ... }",
|
|
220
|
+
"hash": "abc123def",
|
|
221
|
+
"metadata": {
|
|
222
|
+
"extractedAt": "2025-01-01T12:00:00.000Z",
|
|
223
|
+
"version": "1.0.0"
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
],
|
|
227
|
+
"buildInfo": {
|
|
228
|
+
"entryPoint": "src/index.ts",
|
|
229
|
+
"outputFile": "dist/bundle.js",
|
|
230
|
+
"fingerprintsFile": "dist/bundle.fingerprints.json",
|
|
231
|
+
"totalTasks": 5,
|
|
232
|
+
"buildTimestamp": "2025-01-01T12:00:00.000Z"
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Task Return Value
|
|
238
|
+
```typescript
|
|
239
|
+
{
|
|
240
|
+
outputFile: string,
|
|
241
|
+
fingerprintsFile?: string,
|
|
242
|
+
errors: number,
|
|
243
|
+
warnings: number,
|
|
244
|
+
taskFingerprints?: {
|
|
245
|
+
totalTasks: number,
|
|
246
|
+
tasks: Array<{
|
|
247
|
+
name: string,
|
|
248
|
+
inputType: string,
|
|
249
|
+
outputType: string,
|
|
250
|
+
boundaryCount: number,
|
|
251
|
+
hash: string
|
|
252
|
+
}>
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Usage Patterns
|
|
258
|
+
|
|
259
|
+
### CLI Integration
|
|
260
|
+
```bash
|
|
261
|
+
# Generate bundle with fingerprints
|
|
262
|
+
shadow-cli bundle:createWithFingerprints \
|
|
263
|
+
--entryPoint src/index.ts \
|
|
264
|
+
--outputFile dist/bundle.js
|
|
265
|
+
|
|
266
|
+
# Custom fingerprints location
|
|
267
|
+
shadow-cli bundle:createWithFingerprints \
|
|
268
|
+
--entryPoint src/index.ts \
|
|
269
|
+
--outputFile dist/bundle.js \
|
|
270
|
+
--fingerprintsOutputFile dist/task-types.json
|
|
271
|
+
|
|
272
|
+
# Disable fingerprints
|
|
273
|
+
shadow-cli bundle:createWithFingerprints \
|
|
274
|
+
--entryPoint src/index.ts \
|
|
275
|
+
--outputFile dist/bundle.js \
|
|
276
|
+
--generateFingerprints false
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Programmatic Usage
|
|
280
|
+
```typescript
|
|
281
|
+
import { createWithFingerprints } from './create-with-fingerprints'
|
|
282
|
+
|
|
283
|
+
const result = await createWithFingerprints.run({
|
|
284
|
+
entryPoint: 'src/index.ts',
|
|
285
|
+
outputFile: 'dist/bundle.js',
|
|
286
|
+
generateFingerprints: true
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
console.log(`Generated ${result.taskFingerprints?.totalTasks} task fingerprints`)
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Error Handling
|
|
293
|
+
|
|
294
|
+
### Plugin Error Scenarios
|
|
295
|
+
1. **TypeScript Parsing Errors**: Log warning, continue processing other files
|
|
296
|
+
2. **Invalid createTask Calls**: Skip malformed calls, log warnings
|
|
297
|
+
3. **File System Errors**: Fail gracefully, provide meaningful error messages
|
|
298
|
+
4. **TypeScript Compiler Issues**: Use fallback type extraction methods
|
|
299
|
+
|
|
300
|
+
### Graceful Degradation
|
|
301
|
+
- If TypeScript analysis fails, include raw source code snippets
|
|
302
|
+
- Provide partial fingerprints when complete type information unavailable
|
|
303
|
+
- Continue build process even if fingerprint generation fails
|
|
304
|
+
|
|
305
|
+
## Performance Considerations
|
|
306
|
+
|
|
307
|
+
### Optimization Strategies
|
|
308
|
+
1. **File Filtering**: Only analyze files containing "createTask"
|
|
309
|
+
2. **Caching**: Avoid re-processing identical files
|
|
310
|
+
3. **Lazy Loading**: Import TypeScript compiler only when needed
|
|
311
|
+
4. **Parallel Processing**: Process multiple files concurrently where possible
|
|
312
|
+
|
|
313
|
+
### Memory Management
|
|
314
|
+
- Limit function source code storage (truncate long functions)
|
|
315
|
+
- Clean up TypeScript compiler instances after use
|
|
316
|
+
- Stream large file operations when possible
|
|
317
|
+
|
|
318
|
+
## Testing Strategy
|
|
319
|
+
|
|
320
|
+
### Unit Tests Required
|
|
321
|
+
1. **AST Parsing**: Test extraction from various createTask patterns
|
|
322
|
+
2. **Type Analysis**: Verify schema, boundary, and return type extraction
|
|
323
|
+
3. **Hash Generation**: Ensure consistent hash generation
|
|
324
|
+
4. **Plugin Integration**: Test esbuild plugin lifecycle
|
|
325
|
+
|
|
326
|
+
### Integration Tests
|
|
327
|
+
1. **End-to-End**: Full bundle creation with fingerprint generation
|
|
328
|
+
2. **Real Projects**: Test against actual task codebases
|
|
329
|
+
3. **Error Scenarios**: Verify graceful handling of malformed code
|
|
330
|
+
|
|
331
|
+
### Test Data Requirements
|
|
332
|
+
```typescript
|
|
333
|
+
// Sample task for testing
|
|
334
|
+
export const testTask = createTask(
|
|
335
|
+
new Schema({
|
|
336
|
+
input: Schema.string(),
|
|
337
|
+
count: Schema.number().optional()
|
|
338
|
+
}),
|
|
339
|
+
{
|
|
340
|
+
apiCall: async (data: string): Promise<{ result: number }> => ({ result: 42 })
|
|
341
|
+
},
|
|
342
|
+
async function ({ input, count }, { apiCall }): Promise<{ output: string }> {
|
|
343
|
+
const { result } = await apiCall(input)
|
|
344
|
+
return { output: `${input}-${result}` }
|
|
345
|
+
}
|
|
346
|
+
)
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Future Enhancements
|
|
350
|
+
|
|
351
|
+
### Planned Features
|
|
352
|
+
1. **Watch Mode**: Incremental fingerprint updates during development
|
|
353
|
+
2. **Validation**: Runtime validation using generated fingerprints
|
|
354
|
+
3. **Documentation**: Auto-generate API docs from fingerprints
|
|
355
|
+
4. **IDE Integration**: TypeScript language service plugin
|
|
356
|
+
5. **Diff Detection**: Compare fingerprints across versions
|
|
357
|
+
|
|
358
|
+
### Extension Points
|
|
359
|
+
1. **Custom Extractors**: Plugin system for custom type extraction
|
|
360
|
+
2. **Output Formats**: Support for different output formats (YAML, XML)
|
|
361
|
+
3. **Integration Hooks**: Webhooks for fingerprint updates
|
|
362
|
+
4. **Analysis Tools**: CLI tools for fingerprint analysis and comparison
|
|
363
|
+
|
|
364
|
+
## Implementation Checklist
|
|
365
|
+
|
|
366
|
+
- [ ] Implement `taskFingerprintPlugin` esbuild plugin
|
|
367
|
+
- [ ] Create TypeScript AST analysis functions
|
|
368
|
+
- [ ] Implement schema extraction logic
|
|
369
|
+
- [ ] Implement boundary analysis logic
|
|
370
|
+
- [ ] Implement return type extraction
|
|
371
|
+
- [ ] Create hash generation function
|
|
372
|
+
- [ ] Implement enhanced bundle task
|
|
373
|
+
- [ ] Add error handling and logging
|
|
374
|
+
- [ ] Write comprehensive tests
|
|
375
|
+
- [ ] Create CLI integration
|
|
376
|
+
- [ ] Generate documentation
|
|
377
|
+
- [ ] Performance optimization
|
|
378
|
+
- [ ] Add configuration validation
|
|
379
|
+
|
|
380
|
+
This specification provides a complete blueprint for implementing a sophisticated task fingerprinting system that operates at build time and provides comprehensive type introspection capabilities for TypeScript tasks.
|
package/src/runner.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { download as downloadTask } from './tasks/task/download'
|
|
|
17
17
|
import { replay as replayTask } from './tasks/task/replay'
|
|
18
18
|
import { list as listTasks } from './tasks/task/list'
|
|
19
19
|
import { describe as describeTask } from './tasks/task/describe'
|
|
20
|
+
import { fingerprint as fingerprintTask } from './tasks/task/fingerprint'
|
|
20
21
|
|
|
21
22
|
import { create as createRunner } from './tasks/runner/create'
|
|
22
23
|
import { remove as removeRunner } from './tasks/runner/remove'
|
|
@@ -56,6 +57,7 @@ runner.load('task:download', downloadTask)
|
|
|
56
57
|
runner.load('task:replay', replayTask)
|
|
57
58
|
runner.load('task:list', listTasks)
|
|
58
59
|
runner.load('task:describe', describeTask)
|
|
60
|
+
runner.load('task:fingerprint', fingerprintTask)
|
|
59
61
|
|
|
60
62
|
// Runner commands
|
|
61
63
|
runner.load('runner:create', createRunner)
|
|
@@ -89,7 +91,7 @@ runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
|
|
|
89
91
|
try {
|
|
90
92
|
let result
|
|
91
93
|
|
|
92
|
-
const commandsWithDescriptor = ['task:create', 'task:remove', 'task:publish', 'task:describe']
|
|
94
|
+
const commandsWithDescriptor = ['task:create', 'task:remove', 'task:publish', 'task:describe', 'task:fingerprint']
|
|
93
95
|
const commandsWithRunner = ['runner:create', 'runner:remove']
|
|
94
96
|
|
|
95
97
|
if (commandsWithDescriptor.includes(taskName)) {
|
package/src/tasks/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Forge CLI Task Guidelines
|
|
2
2
|
|
|
3
|
-
This document outlines the standard patterns and best practices for creating tasks in the
|
|
3
|
+
This document outlines the standard patterns and best practices for creating tasks in the Forge CLI.
|
|
4
4
|
|
|
5
5
|
## Task Structure
|
|
6
6
|
|
|
@@ -9,10 +9,10 @@ All tasks should follow this standard structure:
|
|
|
9
9
|
```typescript
|
|
10
10
|
// TASK: [task_name]
|
|
11
11
|
// Run this task with:
|
|
12
|
-
//
|
|
12
|
+
// forge task:run [namespace]:[task_name]
|
|
13
13
|
|
|
14
|
-
import { createTask } from '@
|
|
15
|
-
import { Schema } from '@
|
|
14
|
+
import { createTask } from '@forgehive/task'
|
|
15
|
+
import { Schema } from '@forgehive/schema'
|
|
16
16
|
// Import any additional dependencies
|
|
17
17
|
|
|
18
18
|
// Define the schema for task arguments
|
|
@@ -33,7 +33,7 @@ export const taskName = createTask(
|
|
|
33
33
|
boundaries,
|
|
34
34
|
async function ({ /* destructured parameters */ }) {
|
|
35
35
|
// Task implementation
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
// Return appropriate result
|
|
38
38
|
return result
|
|
39
39
|
}
|
|
@@ -45,8 +45,8 @@ export const taskName = createTask(
|
|
|
45
45
|
### Bundle Creation Task
|
|
46
46
|
|
|
47
47
|
```typescript
|
|
48
|
-
import { createTask } from '@
|
|
49
|
-
import { Schema } from '@
|
|
48
|
+
import { createTask } from '@forgehive/task'
|
|
49
|
+
import { Schema } from '@forgehive/schema'
|
|
50
50
|
import esbuild from 'esbuild'
|
|
51
51
|
|
|
52
52
|
const schema = new Schema({
|
|
@@ -80,8 +80,8 @@ export const create = createTask(
|
|
|
80
80
|
### Bundle Loading Task
|
|
81
81
|
|
|
82
82
|
```typescript
|
|
83
|
-
import { createTask } from '@
|
|
84
|
-
import { Schema } from '@
|
|
83
|
+
import { createTask } from '@forgehive/task'
|
|
84
|
+
import { Schema } from '@forgehive/schema'
|
|
85
85
|
|
|
86
86
|
const schema = new Schema({
|
|
87
87
|
bundlePath: Schema.string()
|
|
@@ -129,11 +129,11 @@ Common schema types include:
|
|
|
129
129
|
Tasks can be run using the CLI:
|
|
130
130
|
|
|
131
131
|
```bash
|
|
132
|
-
|
|
132
|
+
forge task:run [namespace]:[task_name] --paramName=value
|
|
133
133
|
```
|
|
134
134
|
|
|
135
135
|
For example:
|
|
136
136
|
|
|
137
137
|
```bash
|
|
138
|
-
|
|
139
|
-
```
|
|
138
|
+
forge task:run bundle:create --entryPoint=src/index.ts --outputFile=dist/bundle.js
|
|
139
|
+
```
|
package/src/tasks/auth/add.ts
CHANGED
|
@@ -27,10 +27,10 @@ const boundaries = {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export const add = createTask(
|
|
30
|
+
export const add = createTask({
|
|
31
31
|
schema,
|
|
32
32
|
boundaries,
|
|
33
|
-
async function ({ name, apiKey, apiSecret, url }, { loadProfiles, persistProfiles }) {
|
|
33
|
+
fn: async function ({ name, apiKey, apiSecret, url }, { loadProfiles, persistProfiles }) {
|
|
34
34
|
const profiles = await loadProfiles({})
|
|
35
35
|
|
|
36
36
|
// Check if profile with same name already exists
|
|
@@ -54,4 +54,4 @@ export const add = createTask(
|
|
|
54
54
|
message: `Profile '${name}' added and set as default`
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
)
|
|
57
|
+
})
|
package/src/tasks/auth/list.ts
CHANGED
|
@@ -14,10 +14,10 @@ const boundaries = {
|
|
|
14
14
|
loadProfiles: loadProfiles.asBoundary()
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export const list = createTask(
|
|
17
|
+
export const list = createTask({
|
|
18
18
|
schema,
|
|
19
19
|
boundaries,
|
|
20
|
-
async function (_argv, { loadProfiles }) {
|
|
20
|
+
fn: async function (_argv, { loadProfiles }) {
|
|
21
21
|
const profiles: Profiles = await loadProfiles({})
|
|
22
22
|
|
|
23
23
|
if (profiles.profiles.length === 0) {
|
|
@@ -40,4 +40,4 @@ export const list = createTask(
|
|
|
40
40
|
default: profiles.default
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
)
|
|
43
|
+
})
|
package/src/tasks/auth/load.ts
CHANGED
|
@@ -26,10 +26,10 @@ const boundaries = {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export const load = createTask(
|
|
29
|
+
export const load = createTask({
|
|
30
30
|
schema,
|
|
31
31
|
boundaries,
|
|
32
|
-
async function (argv, { ensureBuildsFolder }) {
|
|
32
|
+
fn: async function (argv, { ensureBuildsFolder }) {
|
|
33
33
|
const buildsPath = await ensureBuildsFolder()
|
|
34
34
|
|
|
35
35
|
let profiles: Profiles = {
|
|
@@ -48,4 +48,4 @@ export const load = createTask(
|
|
|
48
48
|
|
|
49
49
|
return profiles
|
|
50
50
|
}
|
|
51
|
-
)
|
|
51
|
+
})
|
|
@@ -14,10 +14,10 @@ const boundaries = {
|
|
|
14
14
|
loadProfiles: loadProfiles.asBoundary()
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export const loadCurrent = createTask(
|
|
17
|
+
export const loadCurrent = createTask({
|
|
18
18
|
schema,
|
|
19
19
|
boundaries,
|
|
20
|
-
async function (_argv, { loadProfiles }): Promise<Profile> {
|
|
20
|
+
fn: async function (_argv, { loadProfiles }): Promise<Profile> {
|
|
21
21
|
const profiles = await loadProfiles({})
|
|
22
22
|
|
|
23
23
|
if (!profiles.default || profiles.default === '') {
|
|
@@ -32,4 +32,4 @@ export const loadCurrent = createTask(
|
|
|
32
32
|
|
|
33
33
|
return { ...defaultProfile, name: profiles.default }
|
|
34
34
|
}
|
|
35
|
-
)
|
|
35
|
+
})
|
package/src/tasks/auth/remove.ts
CHANGED
|
@@ -24,10 +24,10 @@ const boundaries = {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export const remove = createTask(
|
|
27
|
+
export const remove = createTask({
|
|
28
28
|
schema,
|
|
29
29
|
boundaries,
|
|
30
|
-
async function ({ profileName }, { loadProfiles, persistProfiles }) {
|
|
30
|
+
fn: async function ({ profileName }, { loadProfiles, persistProfiles }) {
|
|
31
31
|
const profiles = await loadProfiles({})
|
|
32
32
|
|
|
33
33
|
// Check if profile exists
|
|
@@ -63,4 +63,4 @@ export const remove = createTask(
|
|
|
63
63
|
message: `Profile "${profileName}" has been removed.`
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
-
)
|
|
66
|
+
})
|
package/src/tasks/auth/switch.ts
CHANGED
|
@@ -24,10 +24,10 @@ const boundaries = {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export const switchProfile = createTask(
|
|
27
|
+
export const switchProfile = createTask({
|
|
28
28
|
schema,
|
|
29
29
|
boundaries,
|
|
30
|
-
async function ({ profileName }, { loadProfiles, persistProfiles }) {
|
|
30
|
+
fn: async function ({ profileName }, { loadProfiles, persistProfiles }) {
|
|
31
31
|
// Load profiles
|
|
32
32
|
const profiles = await loadProfiles({})
|
|
33
33
|
|
|
@@ -50,4 +50,4 @@ export const switchProfile = createTask(
|
|
|
50
50
|
default: profileName
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
-
)
|
|
53
|
+
})
|
|
@@ -17,7 +17,7 @@ Creates a bundled JavaScript file using esbuild.
|
|
|
17
17
|
|
|
18
18
|
**Example Usage:**
|
|
19
19
|
```bash
|
|
20
|
-
|
|
20
|
+
forge task:run bundle:create --entryPoint=src/index.ts --outputFile=dist/bundle.js
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
**Implementation Details:**
|
|
@@ -38,7 +38,7 @@ Dynamically loads a JavaScript bundle and returns its default export.
|
|
|
38
38
|
|
|
39
39
|
**Example Usage:**
|
|
40
40
|
```bash
|
|
41
|
-
|
|
41
|
+
forge task:run bundle:load --bundlePath=dist/bundle.js
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
**Implementation Details:**
|
|
@@ -51,10 +51,10 @@ shadow-cli bundle:load --bundlePath=dist/bundle.js
|
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
53
|
# First, create the bundle
|
|
54
|
-
|
|
54
|
+
forge task:run bundle:create --entryPoint=src/index.ts --outputFile=dist/bundle.js
|
|
55
55
|
|
|
56
56
|
# Then, load the bundle
|
|
57
|
-
|
|
57
|
+
forge task:run bundle:load --bundlePath=dist/bundle.js
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
### Using in Scripts
|
|
@@ -71,13 +71,13 @@ async function buildAndLoad() {
|
|
|
71
71
|
entryPoint: 'src/index.ts',
|
|
72
72
|
outputFile: 'dist/bundle.js'
|
|
73
73
|
})
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
// Load bundle
|
|
76
76
|
const bundleExport = await loadBundle({
|
|
77
77
|
bundlePath: 'dist/bundle.js'
|
|
78
78
|
})
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
// Use the loaded bundle
|
|
81
81
|
return bundleExport
|
|
82
82
|
}
|
|
83
|
-
```
|
|
83
|
+
```
|