@superzerodev/core 0.1.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/adapter.d.ts +127 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +16 -0
- package/dist/adapter.js.map +1 -0
- package/dist/errors.d.ts +158 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +309 -0
- package/dist/errors.js.map +1 -0
- package/dist/executor.d.ts +58 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +157 -0
- package/dist/executor.js.map +1 -0
- package/dist/graph.d.ts +19 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/graph.js +92 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/planner.d.ts +24 -0
- package/dist/planner.d.ts.map +1 -0
- package/dist/planner.js +56 -0
- package/dist/planner.js.map +1 -0
- package/dist/registry.d.ts +18 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +38 -0
- package/dist/registry.js.map +1 -0
- package/dist/source-archive.d.ts +85 -0
- package/dist/source-archive.d.ts.map +1 -0
- package/dist/source-archive.js +398 -0
- package/dist/source-archive.js.map +1 -0
- package/dist/types.d.ts +95 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* デプロイするソースの受け渡し(CLI → Control Plane)
|
|
3
|
+
*
|
|
4
|
+
* ★ ここが無いと、遠隔の顧客は誰もアプリを載せられない。
|
|
5
|
+
*
|
|
6
|
+
* 以前は CLI が「宣言ファイルのあるディレクトリの絶対パス」を文字列で送り、
|
|
7
|
+
* Control Plane がそれを *自分の* ファイルシステムのパスとして開いていた。
|
|
8
|
+
* CLI とサーバーが同じホストに居るときだけ通る作りで、顧客の Mac から
|
|
9
|
+
* api.superzero.dev を叩くと `ENOENT ... scandir '/Users/...'` で落ちていた。
|
|
10
|
+
* 受け入れ確認が通っていたのは、CLI をサーバー上で走らせていたため。
|
|
11
|
+
*
|
|
12
|
+
* 加えて、そのパスは無検証でサーバーの fs に渡っていた。認証済みの顧客が
|
|
13
|
+
* `/etc/superzero` を指定すれば、基盤の秘密が自分のコンテナイメージに入った。
|
|
14
|
+
* ソースを送る形にすると、この穴は構造的に消える(パスを受け取らなくなる)。
|
|
15
|
+
*
|
|
16
|
+
* ★ 外部依存を足さない。tar は自分で書く。
|
|
17
|
+
* 顧客の手元で走るコードなので、依存が増えるほど「入らない」が増える。
|
|
18
|
+
* 形式は ustar(長い名前は GNU の 'L' エントリ)なので、
|
|
19
|
+
* `tar -tzf` でそのまま中身を確認できる。デバッグ手段を捨てない。
|
|
20
|
+
*
|
|
21
|
+
* ★ macOS の tar を呼ばない。
|
|
22
|
+
* 呼ぶと拡張属性が `._<name>` という別ファイルとして混ざり、Linux 側の
|
|
23
|
+
* Nixpacks が "stream did not contain valid UTF-8" で落ちる
|
|
24
|
+
* (docs/05-development.md の「macOS の tar が AppleDouble を混ぜる」)。
|
|
25
|
+
* 自分で書けば構造的に混ざらない。既にディスク上にある `._*` は明示的に外す。
|
|
26
|
+
*/
|
|
27
|
+
import { spawn } from 'node:child_process';
|
|
28
|
+
import { mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises';
|
|
29
|
+
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
30
|
+
import { gunzipSync, gzipSync } from 'node:zlib';
|
|
31
|
+
import { sourceInvalidArchive, sourceTooLarge } from './errors.js';
|
|
32
|
+
/**
|
|
33
|
+
* ビルドに関係しないので送らない。ソースの指紋(fingerprintOf)の
|
|
34
|
+
* 除外リストと同じ集合であること。ここがずれると、送っていないファイルを
|
|
35
|
+
* サーバーが数えようとして指紋が食い違う。
|
|
36
|
+
*/
|
|
37
|
+
export const DEFAULT_IGNORED_ENTRIES = new Set([
|
|
38
|
+
'.git',
|
|
39
|
+
'node_modules',
|
|
40
|
+
'dist',
|
|
41
|
+
'build',
|
|
42
|
+
'.next',
|
|
43
|
+
'.turbo',
|
|
44
|
+
'target',
|
|
45
|
+
'__pycache__',
|
|
46
|
+
'.venv',
|
|
47
|
+
'.DS_Store',
|
|
48
|
+
]);
|
|
49
|
+
/**
|
|
50
|
+
* 名前だけで落とせるものを落とす。
|
|
51
|
+
*
|
|
52
|
+
* ★ `._*`(AppleDouble)は集合ではなく前置きで判定する。
|
|
53
|
+
* 名前が実行時に決まるので Set には入れられない。
|
|
54
|
+
*/
|
|
55
|
+
export function isIgnoredEntryName(name) {
|
|
56
|
+
return DEFAULT_IGNORED_ENTRIES.has(name) || name.startsWith('._');
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 上限。node_modules を外したアプリのソースとしては十分に広い。
|
|
60
|
+
* 超えるのはたいてい「送るべきでないもの」が混ざっている合図なので、
|
|
61
|
+
* 黙って通さずエラーにして気づかせる。
|
|
62
|
+
*/
|
|
63
|
+
export const DEFAULT_SOURCE_LIMITS = {
|
|
64
|
+
maxFiles: 20_000,
|
|
65
|
+
maxBytes: 100 * 1024 * 1024,
|
|
66
|
+
};
|
|
67
|
+
// --- 収集 --------------------------------------------------------------------
|
|
68
|
+
/**
|
|
69
|
+
* 送るファイルを選ぶ。
|
|
70
|
+
*
|
|
71
|
+
* ★ .gitignore の解釈は git 自身にやらせる。
|
|
72
|
+
* 自前のマッチャを書くと、git と挙動がずれたときに
|
|
73
|
+
* 「なぜかファイルが上がらない/上がる」という原因の分かりにくい事故になる。
|
|
74
|
+
* git が無い・リポジトリでない場合だけ、既定の除外リストで歩く。
|
|
75
|
+
*
|
|
76
|
+
* ★ どちらの経路でも DEFAULT_IGNORED_ENTRIES は最後に必ず適用する。
|
|
77
|
+
* node_modules を commit している相手でも送らないため。
|
|
78
|
+
*/
|
|
79
|
+
async function collectFiles(root, options) {
|
|
80
|
+
if (options.useGit !== false) {
|
|
81
|
+
const tracked = await gitListFiles(root, options.gitTimeoutMs ?? 10_000);
|
|
82
|
+
if (tracked)
|
|
83
|
+
return { files: tracked.filter(notIgnored), selection: 'git' };
|
|
84
|
+
}
|
|
85
|
+
const walked = await walk(root, root);
|
|
86
|
+
return { files: walked.filter(notIgnored), selection: 'walk' };
|
|
87
|
+
}
|
|
88
|
+
function notIgnored(relPath) {
|
|
89
|
+
return !relPath.split('/').some(isIgnoredEntryName);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* `git ls-files -co --exclude-standard` の結果を返す。
|
|
93
|
+
* リポジトリでない・git が無い・失敗した場合は null(呼び出し側が歩く)。
|
|
94
|
+
*/
|
|
95
|
+
async function gitListFiles(root, timeoutMs) {
|
|
96
|
+
const out = await runGit(['-c', 'core.quotepath=off', 'ls-files', '--cached', '--others', '--exclude-standard', '-z'], root, timeoutMs);
|
|
97
|
+
if (out === null)
|
|
98
|
+
return null;
|
|
99
|
+
const names = out.split('\0').filter((n) => n.length > 0);
|
|
100
|
+
const files = [];
|
|
101
|
+
for (const name of names) {
|
|
102
|
+
// ★ --cached には「削除済みだがまだ commit していない」ファイルも出る。
|
|
103
|
+
// stat して実体のあるものだけにする。無いものを送ろうとすると落ちる。
|
|
104
|
+
try {
|
|
105
|
+
const s = await stat(join(root, name));
|
|
106
|
+
if (s.isFile())
|
|
107
|
+
files.push(name);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// 消えているファイルは送らない
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return files;
|
|
114
|
+
}
|
|
115
|
+
function runGit(args, cwd, timeoutMs) {
|
|
116
|
+
return new Promise((resolvePromise) => {
|
|
117
|
+
let child;
|
|
118
|
+
try {
|
|
119
|
+
child = spawn('git', args, { cwd, stdio: ['ignore', 'pipe', 'ignore'] });
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
resolvePromise(null);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
let stdout = '';
|
|
126
|
+
let settled = false;
|
|
127
|
+
const finish = (value) => {
|
|
128
|
+
if (settled)
|
|
129
|
+
return;
|
|
130
|
+
settled = true;
|
|
131
|
+
clearTimeout(timer);
|
|
132
|
+
resolvePromise(value);
|
|
133
|
+
};
|
|
134
|
+
const timer = setTimeout(() => {
|
|
135
|
+
child.kill('SIGKILL');
|
|
136
|
+
finish(null);
|
|
137
|
+
}, timeoutMs);
|
|
138
|
+
child.stdout.on('data', (c) => {
|
|
139
|
+
stdout += c.toString('utf8');
|
|
140
|
+
});
|
|
141
|
+
// git が入っていない環境ではここに来る。歩く経路へ落とす
|
|
142
|
+
child.on('error', () => finish(null));
|
|
143
|
+
child.on('close', (code) => finish(code === 0 ? stdout : null));
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/** 既定の除外リストで歩く。fingerprintOf と同じ規則にすること */
|
|
147
|
+
async function walk(root, dir) {
|
|
148
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
149
|
+
const out = [];
|
|
150
|
+
for (const entry of entries) {
|
|
151
|
+
if (isIgnoredEntryName(entry.name))
|
|
152
|
+
continue;
|
|
153
|
+
const full = join(dir, entry.name);
|
|
154
|
+
if (entry.isDirectory()) {
|
|
155
|
+
out.push(...(await walk(root, full)));
|
|
156
|
+
}
|
|
157
|
+
else if (entry.isFile()) {
|
|
158
|
+
out.push(toPosix(relative(root, full)));
|
|
159
|
+
}
|
|
160
|
+
else if (entry.isSymbolicLink()) {
|
|
161
|
+
// 実体が通常ファイルなら中身を送る(ディレクトリは辿らない。循環を避ける)
|
|
162
|
+
try {
|
|
163
|
+
const s = await stat(full);
|
|
164
|
+
if (s.isFile())
|
|
165
|
+
out.push(toPosix(relative(root, full)));
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// 壊れたリンクは無視する
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return out;
|
|
173
|
+
}
|
|
174
|
+
function toPosix(p) {
|
|
175
|
+
return p.split(sep).join('/');
|
|
176
|
+
}
|
|
177
|
+
// --- 詰める ------------------------------------------------------------------
|
|
178
|
+
export async function packSource(rootDir, options = {}) {
|
|
179
|
+
const limits = options.limits ?? DEFAULT_SOURCE_LIMITS;
|
|
180
|
+
const root = resolve(rootDir);
|
|
181
|
+
const { files, selection } = await collectFiles(root, options);
|
|
182
|
+
files.sort();
|
|
183
|
+
if (files.length > limits.maxFiles) {
|
|
184
|
+
throw sourceTooLarge(`${files.length} files`, `${limits.maxFiles} files`);
|
|
185
|
+
}
|
|
186
|
+
const chunks = [];
|
|
187
|
+
let bytes = 0;
|
|
188
|
+
for (const relPath of files) {
|
|
189
|
+
const full = join(root, relPath);
|
|
190
|
+
// ★ シンボリックリンクは実体を送る。リンクのまま送ると、
|
|
191
|
+
// 展開先に無い場所を指す壊れたリンクになる(fingerprintOf も実体を見ている)
|
|
192
|
+
const info = await stat(full);
|
|
193
|
+
bytes += info.size;
|
|
194
|
+
if (bytes > limits.maxBytes) {
|
|
195
|
+
throw sourceTooLarge(formatBytes(bytes), formatBytes(limits.maxBytes));
|
|
196
|
+
}
|
|
197
|
+
const content = await readFile(full);
|
|
198
|
+
const mode = (info.mode & 0o111) !== 0 ? 0o755 : 0o644;
|
|
199
|
+
chunks.push(...entryBlocks(relPath, content, mode, Math.floor(info.mtimeMs / 1000)));
|
|
200
|
+
}
|
|
201
|
+
// 終端は 512 バイトのゼロブロック2つ
|
|
202
|
+
chunks.push(Buffer.alloc(BLOCK * 2));
|
|
203
|
+
return {
|
|
204
|
+
archive: gzipSync(Buffer.concat(chunks)),
|
|
205
|
+
fileCount: files.length,
|
|
206
|
+
bytes,
|
|
207
|
+
selection,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const BLOCK = 512;
|
|
211
|
+
function entryBlocks(name, content, mode, mtime) {
|
|
212
|
+
const blocks = [];
|
|
213
|
+
/**
|
|
214
|
+
* ★ 100 バイトを超える名前は GNU の長ファイル名エントリで先に送る。
|
|
215
|
+
* ustar の prefix 分割だけだと 255 バイトで詰まり、
|
|
216
|
+
* 深いモノレポで「なぜか特定のファイルだけ送れない」が起きる。
|
|
217
|
+
*/
|
|
218
|
+
const nameBytes = Buffer.from(name, 'utf8');
|
|
219
|
+
if (nameBytes.length > 100) {
|
|
220
|
+
const payload = Buffer.concat([nameBytes, Buffer.from([0])]);
|
|
221
|
+
blocks.push(header('././@LongLink', payload.length, 0o644, 0, 'L'));
|
|
222
|
+
blocks.push(pad(payload));
|
|
223
|
+
}
|
|
224
|
+
blocks.push(header(name, content.length, mode, mtime, '0'));
|
|
225
|
+
blocks.push(pad(content));
|
|
226
|
+
return blocks;
|
|
227
|
+
}
|
|
228
|
+
function pad(data) {
|
|
229
|
+
const remainder = data.length % BLOCK;
|
|
230
|
+
if (remainder === 0)
|
|
231
|
+
return data;
|
|
232
|
+
return Buffer.concat([data, Buffer.alloc(BLOCK - remainder)]);
|
|
233
|
+
}
|
|
234
|
+
function header(name, size, mode, mtime, typeflag) {
|
|
235
|
+
const buf = Buffer.alloc(BLOCK);
|
|
236
|
+
// 100 バイトに収まらない分は切り詰める。実際の名前は 'L' エントリが持っている
|
|
237
|
+
buf.write(name.slice(0, 100), 0, 100, 'utf8');
|
|
238
|
+
buf.write(octal(mode, 8), 100, 8, 'ascii');
|
|
239
|
+
buf.write(octal(0, 8), 108, 8, 'ascii'); // uid
|
|
240
|
+
buf.write(octal(0, 8), 116, 8, 'ascii'); // gid
|
|
241
|
+
buf.write(octal(size, 12), 124, 12, 'ascii');
|
|
242
|
+
buf.write(octal(mtime, 12), 136, 12, 'ascii');
|
|
243
|
+
buf.write(' ', 148, 8, 'ascii'); // chksum は空白で埋めてから計算する
|
|
244
|
+
buf.write(typeflag, 156, 1, 'ascii');
|
|
245
|
+
buf.write('ustar\0', 257, 6, 'ascii');
|
|
246
|
+
buf.write('00', 263, 2, 'ascii');
|
|
247
|
+
let sum = 0;
|
|
248
|
+
for (const byte of buf)
|
|
249
|
+
sum += byte;
|
|
250
|
+
buf.write(`${sum.toString(8).padStart(6, '0')}\0 `, 148, 8, 'ascii');
|
|
251
|
+
return buf;
|
|
252
|
+
}
|
|
253
|
+
function octal(value, width) {
|
|
254
|
+
return `${value.toString(8).padStart(width - 1, '0')}\0`;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* アーカイブを destDir に展開する。
|
|
258
|
+
*
|
|
259
|
+
* ★ 中身は顧客が作ったもの。信用しない。
|
|
260
|
+
* 絶対パス・`..`・シンボリックリンク・デバイスファイルは全て拒否する。
|
|
261
|
+
* 通したら、顧客が Control Plane のホストの任意の場所に書けることになる。
|
|
262
|
+
*
|
|
263
|
+
* ★ 上限は「宣言されたサイズ」ではなく「実際に展開したバイト数」で数える。
|
|
264
|
+
* ヘッダの数字を信じると、小さいと宣言した巨大なアーカイブを通してしまう。
|
|
265
|
+
*/
|
|
266
|
+
export async function unpackSource(archive, destDir, limits = DEFAULT_SOURCE_LIMITS) {
|
|
267
|
+
const root = resolve(destDir);
|
|
268
|
+
let raw;
|
|
269
|
+
try {
|
|
270
|
+
// ★ gzip の展開そのものにも上限をかける。ここが無いと、
|
|
271
|
+
// 数十 KB の圧縮データで数 GB のメモリを掴まされる
|
|
272
|
+
raw = gunzipSync(archive, { maxOutputLength: limits.maxBytes + BLOCK * 4 });
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
276
|
+
if (message.includes('maxOutputLength') || message.includes('Buffer')) {
|
|
277
|
+
throw sourceTooLarge('more than the limit', formatBytes(limits.maxBytes));
|
|
278
|
+
}
|
|
279
|
+
throw sourceInvalidArchive(`the upload is not a valid gzip stream (${message})`);
|
|
280
|
+
}
|
|
281
|
+
await mkdir(root, { recursive: true, mode: 0o700 });
|
|
282
|
+
let offset = 0;
|
|
283
|
+
let longName;
|
|
284
|
+
let fileCount = 0;
|
|
285
|
+
let bytes = 0;
|
|
286
|
+
while (offset + BLOCK <= raw.length) {
|
|
287
|
+
const head = raw.subarray(offset, offset + BLOCK);
|
|
288
|
+
offset += BLOCK;
|
|
289
|
+
if (isZeroBlock(head))
|
|
290
|
+
break;
|
|
291
|
+
verifyChecksum(head);
|
|
292
|
+
const typeflag = String.fromCharCode(head[156] ?? 0);
|
|
293
|
+
const size = parseOctal(head, 124, 12);
|
|
294
|
+
const end = offset + Math.ceil(size / BLOCK) * BLOCK;
|
|
295
|
+
if (end > raw.length)
|
|
296
|
+
throw sourceInvalidArchive('the archive ends in the middle of an entry');
|
|
297
|
+
const data = raw.subarray(offset, offset + size);
|
|
298
|
+
offset = end;
|
|
299
|
+
if (typeflag === 'L') {
|
|
300
|
+
// GNU の長ファイル名。次のエントリの名前になる
|
|
301
|
+
longName = data.toString('utf8').replace(/\0+$/, '');
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
const name = longName ?? readName(head);
|
|
305
|
+
longName = undefined;
|
|
306
|
+
// ディレクトリは中のファイルを書くときに作る。空ディレクトリは送っていない
|
|
307
|
+
if (typeflag === '5')
|
|
308
|
+
continue;
|
|
309
|
+
if (typeflag !== '0' && typeflag !== '\0') {
|
|
310
|
+
throw sourceInvalidArchive(`the archive contains an entry of type "${typeflag}" (${name}); ` +
|
|
311
|
+
'only regular files are accepted');
|
|
312
|
+
}
|
|
313
|
+
fileCount += 1;
|
|
314
|
+
bytes += size;
|
|
315
|
+
if (fileCount > limits.maxFiles) {
|
|
316
|
+
throw sourceTooLarge(`${fileCount} files`, `${limits.maxFiles} files`);
|
|
317
|
+
}
|
|
318
|
+
if (bytes > limits.maxBytes) {
|
|
319
|
+
throw sourceTooLarge(formatBytes(bytes), formatBytes(limits.maxBytes));
|
|
320
|
+
}
|
|
321
|
+
const full = safeJoin(root, name);
|
|
322
|
+
await mkdir(dirname(full), { recursive: true });
|
|
323
|
+
const mode = (parseOctal(head, 100, 8) & 0o111) !== 0 ? 0o755 : 0o644;
|
|
324
|
+
await writeFile(full, data, { mode });
|
|
325
|
+
}
|
|
326
|
+
return { fileCount, bytes };
|
|
327
|
+
}
|
|
328
|
+
function readName(head) {
|
|
329
|
+
const name = cstring(head, 0, 100);
|
|
330
|
+
const prefix = cstring(head, 345, 155);
|
|
331
|
+
return prefix ? `${prefix}/${name}` : name;
|
|
332
|
+
}
|
|
333
|
+
function cstring(buf, offset, length) {
|
|
334
|
+
const slice = buf.subarray(offset, offset + length);
|
|
335
|
+
const nul = slice.indexOf(0);
|
|
336
|
+
return slice.subarray(0, nul === -1 ? slice.length : nul).toString('utf8');
|
|
337
|
+
}
|
|
338
|
+
function isZeroBlock(block) {
|
|
339
|
+
for (const byte of block)
|
|
340
|
+
if (byte !== 0)
|
|
341
|
+
return false;
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
function verifyChecksum(head) {
|
|
345
|
+
const declared = parseOctal(head, 148, 8);
|
|
346
|
+
let sum = 0;
|
|
347
|
+
for (let i = 0; i < BLOCK; i += 1) {
|
|
348
|
+
// chksum の 8 バイトは空白として数える約束
|
|
349
|
+
sum += i >= 148 && i < 156 ? 0x20 : (head[i] ?? 0);
|
|
350
|
+
}
|
|
351
|
+
if (sum !== declared) {
|
|
352
|
+
throw sourceInvalidArchive('a tar header failed its checksum; the upload is corrupt');
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function parseOctal(buf, offset, length) {
|
|
356
|
+
const text = cstring(buf, offset, length).trim();
|
|
357
|
+
if (text === '')
|
|
358
|
+
return 0;
|
|
359
|
+
const value = Number.parseInt(text, 8);
|
|
360
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
361
|
+
throw sourceInvalidArchive('a tar header contains a malformed numeric field');
|
|
362
|
+
}
|
|
363
|
+
return value;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* アーカイブ内のパスを展開先へ落とす。
|
|
367
|
+
* 少しでも根の外を指しうるものは、直せる形のエラーにして拒否する。
|
|
368
|
+
*/
|
|
369
|
+
function safeJoin(root, name) {
|
|
370
|
+
const cleaned = name.replace(/\\/g, '/');
|
|
371
|
+
if (cleaned === '' || cleaned.startsWith('/') || /^[a-zA-Z]:/.test(cleaned)) {
|
|
372
|
+
throw sourceInvalidArchive(`the archive contains an absolute path (${name})`);
|
|
373
|
+
}
|
|
374
|
+
const segments = cleaned.split('/').filter((s) => s !== '' && s !== '.');
|
|
375
|
+
if (segments.some((s) => s === '..')) {
|
|
376
|
+
throw sourceInvalidArchive(`the archive contains a path that escapes the root (${name})`);
|
|
377
|
+
}
|
|
378
|
+
if (segments.some((s) => s.includes('\0'))) {
|
|
379
|
+
throw sourceInvalidArchive(`the archive contains a path with a NUL byte (${name})`);
|
|
380
|
+
}
|
|
381
|
+
if (segments.length === 0) {
|
|
382
|
+
throw sourceInvalidArchive(`the archive contains an entry with no usable name (${name})`);
|
|
383
|
+
}
|
|
384
|
+
const full = resolve(root, ...segments);
|
|
385
|
+
// ここまでで弾けているはずだが、最後にもう一度確かめる。安いので確かめる
|
|
386
|
+
if (full !== root && !full.startsWith(root + sep)) {
|
|
387
|
+
throw sourceInvalidArchive(`the archive contains a path that escapes the root (${name})`);
|
|
388
|
+
}
|
|
389
|
+
return full;
|
|
390
|
+
}
|
|
391
|
+
export function formatBytes(bytes) {
|
|
392
|
+
if (bytes < 1024)
|
|
393
|
+
return `${bytes} B`;
|
|
394
|
+
if (bytes < 1024 * 1024)
|
|
395
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
396
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
397
|
+
}
|
|
398
|
+
//# sourceMappingURL=source-archive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-archive.js","sourceRoot":"","sources":["../src/source-archive.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* リソースモデルと Change 型(P0設計 §5.2 / §5.3)
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* P0 で扱うリソース種別。
|
|
6
|
+
* Storage は P0 スコープ外(D9)。Realtime / Queue は P2。
|
|
7
|
+
*/
|
|
8
|
+
export declare const RESOURCE_KINDS: readonly ["compute", "database", "auth", "push", "email", "sms"];
|
|
9
|
+
export type ResourceKind = (typeof RESOURCE_KINDS)[number];
|
|
10
|
+
export type ResourceStatus = 'creating' | 'ready' | 'failed' | 'deleting';
|
|
11
|
+
/**
|
|
12
|
+
* リソースの自然キー。
|
|
13
|
+
* Control Plane DB の UNIQUE (environment_id, kind, logical_name) と対応し、
|
|
14
|
+
* これが冪等性(PE-1)の実装上の担保になる。
|
|
15
|
+
*/
|
|
16
|
+
export declare function resourceKey(kind: ResourceKind, logicalName: string): string;
|
|
17
|
+
/** 単一リソースしか持たない種別で使う既定の論理名 */
|
|
18
|
+
export declare const DEFAULT_LOGICAL_NAME = "default";
|
|
19
|
+
/**
|
|
20
|
+
* actual state — Control Plane DB の resources テーブル1行に対応する。
|
|
21
|
+
* TState はアダプタが自由に定義する(プロバイダ側のIDや設定を入れる)。
|
|
22
|
+
*/
|
|
23
|
+
export interface ResourceRecord<TState = unknown> {
|
|
24
|
+
id: string;
|
|
25
|
+
environmentId: string;
|
|
26
|
+
kind: ResourceKind;
|
|
27
|
+
logicalName: string;
|
|
28
|
+
/** 'docker' | 'neon' | 'twilio' | 'ses' ... */
|
|
29
|
+
provider: string;
|
|
30
|
+
/** プロバイダ側のID。作成前は null */
|
|
31
|
+
externalId: string | null;
|
|
32
|
+
state: TState;
|
|
33
|
+
status: ResourceStatus;
|
|
34
|
+
}
|
|
35
|
+
export type ChangeAction = 'create' | 'update' | 'destroy' | 'noop';
|
|
36
|
+
/** plan の差分表示用。人間にもエージェントにも読ませる */
|
|
37
|
+
export interface FieldDiff {
|
|
38
|
+
path: string;
|
|
39
|
+
before: unknown;
|
|
40
|
+
after: unknown;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 1リソースに対する1操作。
|
|
44
|
+
* Executor はこれを依存順に実行し、1件ごとに DB へコミットする(PE-3)。
|
|
45
|
+
*/
|
|
46
|
+
export interface Change<TSpec = unknown> {
|
|
47
|
+
/** resourceKey(kind, logicalName) と一致する自然キー */
|
|
48
|
+
key: string;
|
|
49
|
+
action: ChangeAction;
|
|
50
|
+
kind: ResourceKind;
|
|
51
|
+
logicalName: string;
|
|
52
|
+
provider: string;
|
|
53
|
+
/** desired spec。destroy の場合は null */
|
|
54
|
+
spec: TSpec | null;
|
|
55
|
+
/** 依存する他 Change の key。トポロジカルソートに使う(PE-2) */
|
|
56
|
+
dependsOn: string[];
|
|
57
|
+
/**
|
|
58
|
+
* データ喪失を伴うか。
|
|
59
|
+
* true の場合 --allow-destructive なしでは Planner が拒否する(PE-4)
|
|
60
|
+
*/
|
|
61
|
+
destructive: boolean;
|
|
62
|
+
/** plan 出力に出す1行説明 */
|
|
63
|
+
summary: string;
|
|
64
|
+
diff?: FieldDiff[];
|
|
65
|
+
}
|
|
66
|
+
/** plan の結果全体 */
|
|
67
|
+
export interface Plan {
|
|
68
|
+
changes: Change[];
|
|
69
|
+
/** 依存順に並べ替え済みか */
|
|
70
|
+
ordered: boolean;
|
|
71
|
+
/** destructive な Change の key 一覧 */
|
|
72
|
+
destructiveKeys: string[];
|
|
73
|
+
}
|
|
74
|
+
export declare function isNoop(plan: Plan): boolean;
|
|
75
|
+
/** 自己検証API(§5.10)の1項目 */
|
|
76
|
+
export interface VerifyCheck {
|
|
77
|
+
name: string;
|
|
78
|
+
ok: boolean;
|
|
79
|
+
durationMs?: number;
|
|
80
|
+
/** 失敗時のみ。SuperzeroError.toJSON() の形 */
|
|
81
|
+
error?: unknown;
|
|
82
|
+
/**
|
|
83
|
+
* 実施しなかった理由。設定上そもそも確認できない場合に入る。
|
|
84
|
+
*
|
|
85
|
+
* ★ 実施できないチェックを黙って一覧から消してはいけない。
|
|
86
|
+
* 消すと「全部緑だった」に見え、確認していない範囲が
|
|
87
|
+
* 確認済みとして扱われる。ok は true にせず、理由を書いて残す。
|
|
88
|
+
*/
|
|
89
|
+
skipped?: string;
|
|
90
|
+
}
|
|
91
|
+
export interface VerifyResult {
|
|
92
|
+
ok: boolean;
|
|
93
|
+
checks: VerifyCheck[];
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,eAAO,MAAM,cAAc,kEAAmE,CAAA;AAC9F,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAE1D,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAA;AAEzE;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED,8BAA8B;AAC9B,eAAO,MAAM,oBAAoB,YAAY,CAAA;AAE7C;;;GAGG;AACH,MAAM,WAAW,cAAc,CAAC,MAAM,GAAG,OAAO;IAC9C,EAAE,EAAE,MAAM,CAAA;IACV,aAAa,EAAE,MAAM,CAAA;IACrB,IAAI,EAAE,YAAY,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAA;IAChB,0BAA0B;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,cAAc,CAAA;CACvB;AAED,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAA;AAEnE,mCAAmC;AACnC,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,OAAO,CAAA;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,MAAM,CAAC,KAAK,GAAG,OAAO;IACrC,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,YAAY,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,qCAAqC;IACrC,IAAI,EAAE,KAAK,GAAG,IAAI,CAAA;IAClB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB;;;OAGG;IACH,WAAW,EAAE,OAAO,CAAA;IACpB,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,SAAS,EAAE,CAAA;CACnB;AAED,iBAAiB;AACjB,MAAM,WAAW,IAAI;IACnB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,kBAAkB;IAClB,OAAO,EAAE,OAAO,CAAA;IAChB,oCAAoC;IACpC,eAAe,EAAE,MAAM,EAAE,CAAA;CAC1B;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE1C;AAED,yBAAyB;AACzB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,OAAO,CAAA;IACX,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uCAAuC;IACvC,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,EAAE,WAAW,EAAE,CAAA;CACtB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* リソースモデルと Change 型(P0設計 §5.2 / §5.3)
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* P0 で扱うリソース種別。
|
|
6
|
+
* Storage は P0 スコープ外(D9)。Realtime / Queue は P2。
|
|
7
|
+
*/
|
|
8
|
+
export const RESOURCE_KINDS = ['compute', 'database', 'auth', 'push', 'email', 'sms'];
|
|
9
|
+
/**
|
|
10
|
+
* リソースの自然キー。
|
|
11
|
+
* Control Plane DB の UNIQUE (environment_id, kind, logical_name) と対応し、
|
|
12
|
+
* これが冪等性(PE-1)の実装上の担保になる。
|
|
13
|
+
*/
|
|
14
|
+
export function resourceKey(kind, logicalName) {
|
|
15
|
+
return `${kind}:${logicalName}`;
|
|
16
|
+
}
|
|
17
|
+
/** 単一リソースしか持たない種別で使う既定の論理名 */
|
|
18
|
+
export const DEFAULT_LOGICAL_NAME = 'default';
|
|
19
|
+
export function isNoop(plan) {
|
|
20
|
+
return plan.changes.every((c) => c.action === 'noop');
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAU,CAAA;AAK9F;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,IAAkB,EAAE,WAAmB;IACjE,OAAO,GAAG,IAAI,IAAI,WAAW,EAAE,CAAA;AACjC,CAAC;AAED,8BAA8B;AAC9B,MAAM,CAAC,MAAM,oBAAoB,GAAG,SAAS,CAAA;AA8D7C,MAAM,UAAU,MAAM,CAAC,IAAU;IAC/B,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAA;AACvD,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@superzerodev/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"!dist/__tests__"
|
|
16
|
+
],
|
|
17
|
+
"description": "Core model for Superzero: normalized errors, resources, planner and executor.",
|
|
18
|
+
"license": "UNLICENSED",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/mashihara/superzero.git",
|
|
22
|
+
"directory": "packages/core"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc -p tsconfig.json",
|
|
32
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
33
|
+
"test": "tsc -p tsconfig.json && node --test \"dist/__tests__/*.test.js\""
|
|
34
|
+
}
|
|
35
|
+
}
|