@rdlabo/workers-hono-kit 0.12.0-beta.pr48.sha371f5792ce8a → 0.12.2
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rdlabo/workers-hono-kit",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -108,8 +108,8 @@
|
|
|
108
108
|
},
|
|
109
109
|
"peerDependencies": {
|
|
110
110
|
"@hono/zod-validator": "^0.8.0",
|
|
111
|
-
"@rdlabo/workers-mysql": "0.
|
|
112
|
-
"@rdlabo/workers-timezone": "0.
|
|
111
|
+
"@rdlabo/workers-mysql": "^0.12.2",
|
|
112
|
+
"@rdlabo/workers-timezone": "^0.12.2",
|
|
113
113
|
"ai": "^6.0.0",
|
|
114
114
|
"ai-gateway-provider": "^3.1.0",
|
|
115
115
|
"aws4fetch": "^1.0.20",
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return the safe tarball basename from `npm pack --json` output.
|
|
3
|
+
*
|
|
4
|
+
* npm 11 emits an array of pack records. npm 12 with `--workspace` emits an
|
|
5
|
+
* object keyed by package name. Both shapes are accepted; the caller must
|
|
6
|
+
* supply the exact workspace package name to select.
|
|
7
|
+
*
|
|
8
|
+
* @param {unknown} packJson Parsed `npm pack --json` stdout.
|
|
9
|
+
* @param {string} workspaceName Exact package name expected in the output.
|
|
10
|
+
* @returns {string} Basename of the packed `.tgz` (no directory separators).
|
|
11
|
+
*/
|
|
12
|
+
export function parseNpmPackFilename(packJson, workspaceName) {
|
|
13
|
+
if (typeof workspaceName !== 'string' || workspaceName.length === 0) {
|
|
14
|
+
throw new Error('Expected a non-empty workspace package name');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const records = collectPackRecords(packJson);
|
|
18
|
+
const matches = records.filter((record) => record.name === workspaceName);
|
|
19
|
+
|
|
20
|
+
if (matches.length === 0) {
|
|
21
|
+
throw new Error(`npm pack JSON is missing a record for ${workspaceName}`);
|
|
22
|
+
}
|
|
23
|
+
if (matches.length > 1) {
|
|
24
|
+
throw new Error(`npm pack JSON has ${matches.length} records for ${workspaceName}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const { filename } = matches[0];
|
|
28
|
+
if (!isSafeTarballBasename(filename)) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`npm pack JSON for ${workspaceName} has an unsafe or invalid filename: ${JSON.stringify(filename)}`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return filename;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {unknown} packJson
|
|
38
|
+
* @returns {Array<{ name?: unknown, filename?: unknown }>}
|
|
39
|
+
*/
|
|
40
|
+
function collectPackRecords(packJson) {
|
|
41
|
+
if (Array.isArray(packJson)) {
|
|
42
|
+
return packJson.map(asPackRecord);
|
|
43
|
+
}
|
|
44
|
+
if (packJson !== null && typeof packJson === 'object') {
|
|
45
|
+
return Object.values(packJson).map(asPackRecord);
|
|
46
|
+
}
|
|
47
|
+
throw new Error(`npm pack JSON must be an array or object, received ${describeValue(packJson)}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {unknown} value
|
|
52
|
+
* @returns {{ name?: unknown, filename?: unknown }}
|
|
53
|
+
*/
|
|
54
|
+
function asPackRecord(value) {
|
|
55
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
56
|
+
throw new Error(`npm pack JSON contains an invalid record: ${describeValue(value)}`);
|
|
57
|
+
}
|
|
58
|
+
return /** @type {{ name?: unknown, filename?: unknown }} */ (value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @param {unknown} filename
|
|
63
|
+
* @returns {filename is string}
|
|
64
|
+
*/
|
|
65
|
+
function isSafeTarballBasename(filename) {
|
|
66
|
+
return (
|
|
67
|
+
typeof filename === 'string' &&
|
|
68
|
+
filename.length > 4 &&
|
|
69
|
+
filename.endsWith('.tgz') &&
|
|
70
|
+
!filename.includes('/') &&
|
|
71
|
+
!filename.includes('\\') &&
|
|
72
|
+
!filename.includes('..')
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @param {unknown} value
|
|
78
|
+
* @returns {string}
|
|
79
|
+
*/
|
|
80
|
+
function describeValue(value) {
|
|
81
|
+
if (value === null) return 'null';
|
|
82
|
+
if (Array.isArray(value)) return 'array';
|
|
83
|
+
return typeof value;
|
|
84
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { test } from 'node:test';
|
|
3
|
+
import { parseNpmPackFilename } from './parse-npm-pack-json.mjs';
|
|
4
|
+
|
|
5
|
+
const workspace = '@rdlabo/workers-timezone';
|
|
6
|
+
const filename = 'rdlabo-workers-timezone-0.12.1.tgz';
|
|
7
|
+
const record = {
|
|
8
|
+
name: workspace,
|
|
9
|
+
filename,
|
|
10
|
+
packed: '/tmp/rdlabo-workers-timezone-0.12.1.tgz',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
test('parses npm 11 array pack JSON', () => {
|
|
14
|
+
assert.equal(parseNpmPackFilename([record], workspace), filename);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('parses npm 12 object pack JSON keyed by package name', () => {
|
|
18
|
+
assert.equal(parseNpmPackFilename({ [workspace]: record }, workspace), filename);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('rejects malformed pack JSON shapes', () => {
|
|
22
|
+
assert.throws(() => parseNpmPackFilename(null, workspace), /array or object/);
|
|
23
|
+
assert.throws(() => parseNpmPackFilename('nope', workspace), /array or object/);
|
|
24
|
+
assert.throws(() => parseNpmPackFilename([{ name: workspace }], workspace), /unsafe or invalid filename/);
|
|
25
|
+
assert.throws(() => parseNpmPackFilename([null], workspace), /invalid record/);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('rejects a mismatched workspace name', () => {
|
|
29
|
+
assert.throws(
|
|
30
|
+
() => parseNpmPackFilename([record], '@rdlabo/workers-mysql'),
|
|
31
|
+
/missing a record for @rdlabo\/workers-mysql/,
|
|
32
|
+
);
|
|
33
|
+
assert.throws(
|
|
34
|
+
() => parseNpmPackFilename({ [workspace]: record }, '@rdlabo/workers-mysql'),
|
|
35
|
+
/missing a record for @rdlabo\/workers-mysql/,
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('rejects ambiguous multiple matching records', () => {
|
|
40
|
+
assert.throws(() => parseNpmPackFilename([record, { ...record }], workspace), /2 records/);
|
|
41
|
+
assert.throws(
|
|
42
|
+
() =>
|
|
43
|
+
parseNpmPackFilename(
|
|
44
|
+
{
|
|
45
|
+
[workspace]: record,
|
|
46
|
+
'@rdlabo/workers-timezone-dup': { ...record },
|
|
47
|
+
},
|
|
48
|
+
workspace,
|
|
49
|
+
),
|
|
50
|
+
/2 records/,
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('rejects unsafe tarball filenames', () => {
|
|
55
|
+
for (const unsafe of [
|
|
56
|
+
'../rdlabo-workers-timezone-0.12.1.tgz',
|
|
57
|
+
'dir/rdlabo-workers-timezone-0.12.1.tgz',
|
|
58
|
+
'dir\\rdlabo-workers-timezone-0.12.1.tgz',
|
|
59
|
+
'rdlabo-workers-timezone-0.12.1.tar.gz',
|
|
60
|
+
'',
|
|
61
|
+
12,
|
|
62
|
+
]) {
|
|
63
|
+
assert.throws(
|
|
64
|
+
() => parseNpmPackFilename([{ name: workspace, filename: unsafe }], workspace),
|
|
65
|
+
/unsafe or invalid filename/,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
@@ -3,6 +3,7 @@ import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:
|
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join, resolve } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { parseNpmPackFilename } from './parse-npm-pack-json.mjs';
|
|
6
7
|
|
|
7
8
|
const root = resolve(fileURLToPath(new URL('../../..', import.meta.url)));
|
|
8
9
|
const temporaryDirectory = mkdtempSync(join(tmpdir(), 'workers-workspace-smoke-'));
|
|
@@ -20,7 +21,7 @@ function pack(workspace) {
|
|
|
20
21
|
env: commandEnvironment,
|
|
21
22
|
}),
|
|
22
23
|
);
|
|
23
|
-
return join(temporaryDirectory, result
|
|
24
|
+
return join(temporaryDirectory, parseNpmPackFilename(result, workspace));
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
try {
|