create-hedgeboard 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +82 -12
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -93,18 +93,85 @@ function fetchBuffer(url, headers = {}) {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
// ---------------------------------------------------------------------------
|
|
96
|
-
//
|
|
96
|
+
// Recursive directory copy (pure JS, no shell — replaces cp -r)
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
function copyDirRecursive(src, dest) {
|
|
100
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
101
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
102
|
+
const srcPath = path.join(src, entry.name);
|
|
103
|
+
const destPath = path.join(dest, entry.name);
|
|
104
|
+
if (entry.isDirectory()) {
|
|
105
|
+
copyDirRecursive(srcPath, destPath);
|
|
106
|
+
} else {
|
|
107
|
+
fs.copyFileSync(srcPath, destPath);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// ZIP extraction (pure JS — uses only Node built-in zlib, no dependencies)
|
|
97
114
|
// ---------------------------------------------------------------------------
|
|
98
115
|
|
|
99
116
|
function extractZip(zipBuffer, destDir) {
|
|
100
|
-
|
|
101
|
-
// For robustness in production, consider using a proper zip library
|
|
102
|
-
const { execSync } = require("child_process");
|
|
103
|
-
const tmpZip = path.join(require("os").tmpdir(), `hedgeboard-${Date.now()}.zip`);
|
|
104
|
-
fs.writeFileSync(tmpZip, zipBuffer);
|
|
117
|
+
const zlib = require("zlib");
|
|
105
118
|
fs.mkdirSync(destDir, { recursive: true });
|
|
106
|
-
|
|
107
|
-
|
|
119
|
+
|
|
120
|
+
// Parse ZIP central directory (from end-of-central-directory record)
|
|
121
|
+
const buf = Buffer.isBuffer(zipBuffer) ? zipBuffer : Buffer.from(zipBuffer);
|
|
122
|
+
let offset = 0;
|
|
123
|
+
|
|
124
|
+
while (offset < buf.length - 4) {
|
|
125
|
+
// Local file header signature = 0x04034b50
|
|
126
|
+
const sig = buf.readUInt32LE(offset);
|
|
127
|
+
if (sig !== 0x04034b50) break;
|
|
128
|
+
|
|
129
|
+
const method = buf.readUInt16LE(offset + 8);
|
|
130
|
+
const compSize = buf.readUInt32LE(offset + 18);
|
|
131
|
+
const uncompSize = buf.readUInt32LE(offset + 22);
|
|
132
|
+
const nameLen = buf.readUInt16LE(offset + 26);
|
|
133
|
+
const extraLen = buf.readUInt16LE(offset + 28);
|
|
134
|
+
const nameStart = offset + 30;
|
|
135
|
+
const name = buf.toString("utf8", nameStart, nameStart + nameLen);
|
|
136
|
+
const dataStart = nameStart + nameLen + extraLen;
|
|
137
|
+
|
|
138
|
+
offset = dataStart + compSize;
|
|
139
|
+
|
|
140
|
+
// Skip directories (with path traversal guard — P3-9)
|
|
141
|
+
if (name.endsWith("/")) {
|
|
142
|
+
const dirPath = path.join(destDir, name);
|
|
143
|
+
if (!dirPath.startsWith(path.resolve(destDir) + path.sep) &&
|
|
144
|
+
dirPath !== path.resolve(destDir)) {
|
|
145
|
+
console.warn(` Skipping ${name}: path traversal detected`);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Path traversal guard — prevent ../../.bashrc attacks (P0-8)
|
|
153
|
+
const filePath = path.join(destDir, name);
|
|
154
|
+
if (!filePath.startsWith(path.resolve(destDir) + path.sep) &&
|
|
155
|
+
filePath !== path.resolve(destDir)) {
|
|
156
|
+
console.warn(` Skipping ${name}: path traversal detected`);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
160
|
+
|
|
161
|
+
const rawData = buf.subarray(dataStart, dataStart + compSize);
|
|
162
|
+
|
|
163
|
+
if (method === 0) {
|
|
164
|
+
// STORE — no compression
|
|
165
|
+
fs.writeFileSync(filePath, rawData);
|
|
166
|
+
} else if (method === 8) {
|
|
167
|
+
// DEFLATE — use raw inflate (no zlib header)
|
|
168
|
+
const inflated = zlib.inflateRawSync(rawData);
|
|
169
|
+
fs.writeFileSync(filePath, inflated);
|
|
170
|
+
} else {
|
|
171
|
+
// Unsupported method — skip
|
|
172
|
+
console.warn(` Skipping ${name}: unsupported compression method ${method}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
108
175
|
}
|
|
109
176
|
|
|
110
177
|
// ---------------------------------------------------------------------------
|
|
@@ -139,7 +206,7 @@ ${c.dim} ██████╔╝██║ ██║███████
|
|
|
139
206
|
${c.dim} ██╔══██╗██║ ██║██╔══██║██╔══██╗██║ ██║${c.reset}
|
|
140
207
|
${c.dim} ██████╔╝╚██████╔╝██║ ██║██║ ██║██████╔╝${c.reset}
|
|
141
208
|
${c.dim} ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ${c.reset}
|
|
142
|
-
${c.dim} AI-Native Financial Intelligence — v1.0
|
|
209
|
+
${c.dim} AI-Native Financial Intelligence — v1.3.0${c.reset}
|
|
143
210
|
`);
|
|
144
211
|
}
|
|
145
212
|
|
|
@@ -156,7 +223,9 @@ async function main() {
|
|
|
156
223
|
if (key) {
|
|
157
224
|
process.stdout.write(` ${c.gray}◆${c.reset} Validating API key ${c.dim}...${c.reset} `);
|
|
158
225
|
try {
|
|
159
|
-
const resp = await fetchBuffer(`${APP_URL}/api/data-sources
|
|
226
|
+
const resp = await fetchBuffer(`${APP_URL}/api/data-sources`, {
|
|
227
|
+
"x-api-key": key,
|
|
228
|
+
});
|
|
160
229
|
const data = JSON.parse(resp.toString());
|
|
161
230
|
if (data.error) throw new Error(data.error);
|
|
162
231
|
console.log(`${c.green}✓${c.reset}`);
|
|
@@ -189,8 +258,8 @@ async function main() {
|
|
|
189
258
|
if (fs.existsSync(extractedDir)) {
|
|
190
259
|
fs.mkdirSync(path.dirname(absDir), { recursive: true });
|
|
191
260
|
if (fs.existsSync(absDir)) {
|
|
192
|
-
|
|
193
|
-
|
|
261
|
+
// Merge into existing dir — pure JS, no shell (P3-8)
|
|
262
|
+
copyDirRecursive(extractedDir, absDir);
|
|
194
263
|
} else {
|
|
195
264
|
fs.renameSync(extractedDir, absDir);
|
|
196
265
|
}
|
|
@@ -239,6 +308,7 @@ ${c.dim} ───────────────────────
|
|
|
239
308
|
${c.gray}├──${c.reset} ${c.white}viz/${c.reset} ${c.dim}Dashboard templates & charts${c.reset}
|
|
240
309
|
${c.gray}├──${c.reset} ${c.white}brand/${c.reset} ${c.dim}Your brand settings${c.reset}
|
|
241
310
|
${c.gray}├──${c.reset} ${c.white}modules/${c.reset} ${c.dim}356 analysis recipes + indexes${c.reset}
|
|
311
|
+
${c.gray}├──${c.reset} ${c.white}docs/${c.reset} ${c.dim}Data catalog — 35 providers, 15 categories${c.reset}
|
|
242
312
|
${c.gray}└──${c.reset} ${c.white}output/${c.reset} ${c.dim}Generated dashboards land here${c.reset}
|
|
243
313
|
|
|
244
314
|
${c.dim} ─────────────────────────────────────${c.reset}
|