@tinycloud/cli 1.0.0-beta.19 → 1.0.0-beta.20
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/index.js +52 -20
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +43 -16
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -7735,6 +7735,17 @@ var __export2 = (target, all) => {
|
|
|
7735
7735
|
for (var name in all)
|
|
7736
7736
|
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
7737
7737
|
};
|
|
7738
|
+
var UNSAFE_FILENAME_CODE_POINT = /[\p{Cc}\p{Cf}\p{Cs}\u2028\u2029]/u;
|
|
7739
|
+
function hasUnsafeFilenameCodePoint(value) {
|
|
7740
|
+
return UNSAFE_FILENAME_CODE_POINT.test(value);
|
|
7741
|
+
}
|
|
7742
|
+
function canonicalShareFilename(value) {
|
|
7743
|
+
const canonical = value.normalize("NFC");
|
|
7744
|
+
if (canonical.length === 0 || canonical === "." || canonical === ".." || canonical.includes("/") || canonical.includes("\\") || hasUnsafeFilenameCodePoint(canonical)) {
|
|
7745
|
+
throw new TypeError("share filename is unsafe");
|
|
7746
|
+
}
|
|
7747
|
+
return canonical;
|
|
7748
|
+
}
|
|
7738
7749
|
var external_exports = {};
|
|
7739
7750
|
__export2(external_exports, {
|
|
7740
7751
|
BRAND: () => BRAND,
|
|
@@ -13908,8 +13919,17 @@ function normalizeShareTarget(target) {
|
|
|
13908
13919
|
function targetAuthorizationMethod(target) {
|
|
13909
13920
|
return authorizationMethodForTarget(target);
|
|
13910
13921
|
}
|
|
13922
|
+
function publishFilename(value) {
|
|
13923
|
+
try {
|
|
13924
|
+
return canonicalShareFilename(value);
|
|
13925
|
+
} catch {
|
|
13926
|
+
throw new SharePublishError("invalid-argument", hasUnsafeFilenameCodePoint(value) ? "filename contains control or invisible characters" : "filename must be one safe path segment");
|
|
13927
|
+
}
|
|
13928
|
+
}
|
|
13911
13929
|
async function publishTargetShare(input) {
|
|
13912
13930
|
const target = normalizeShareTarget(input.target);
|
|
13931
|
+
const filename = publishFilename(input.filename);
|
|
13932
|
+
const files = input.files?.map((file) => ({ ...file, filename: publishFilename(file.filename) }));
|
|
13913
13933
|
if (input.targetAdapter === void 0) {
|
|
13914
13934
|
if (target.kind === "bearer") throw new SharePublishError("authority-required", "native bearer publication requires an authenticated TinyCloud node");
|
|
13915
13935
|
return {
|
|
@@ -13935,9 +13955,6 @@ async function publishTargetShare(input) {
|
|
|
13935
13955
|
return bytes3;
|
|
13936
13956
|
})();
|
|
13937
13957
|
if (source.byteLength === 0) throw new SharePublishError("invalid-argument", "share content is empty");
|
|
13938
|
-
if (!input.filename || input.filename === "." || input.filename === ".." || /[/\\\u0000-\u001f\u007f]/.test(input.filename)) {
|
|
13939
|
-
throw new SharePublishError("invalid-argument", "filename must be one safe path segment");
|
|
13940
|
-
}
|
|
13941
13958
|
if (target.kind === "bearer" && input.allowBinary !== true) {
|
|
13942
13959
|
try {
|
|
13943
13960
|
new TextDecoder("utf-8", { fatal: true }).decode(source);
|
|
@@ -13945,10 +13962,10 @@ async function publishTargetShare(input) {
|
|
|
13945
13962
|
throw new SharePublishError("invalid-argument", "Markdown input must be valid UTF-8");
|
|
13946
13963
|
}
|
|
13947
13964
|
}
|
|
13948
|
-
const
|
|
13965
|
+
const publishFiles = files === void 0 || files.length === 0 ? [{ bytes: source }] : files;
|
|
13949
13966
|
const limit = input.maxBytes ?? SHARE_CONTENT_LIMIT;
|
|
13950
13967
|
let totalBytes = 0;
|
|
13951
|
-
for (const file of
|
|
13968
|
+
for (const file of publishFiles) {
|
|
13952
13969
|
totalBytes += file.bytes.byteLength;
|
|
13953
13970
|
if (!Number.isSafeInteger(totalBytes) || totalBytes > limit) {
|
|
13954
13971
|
throw new SharePublishError("max-bytes-exceeded", "share publication exceeds the combined byte limit");
|
|
@@ -13959,8 +13976,8 @@ async function publishTargetShare(input) {
|
|
|
13959
13976
|
if (!Number.isFinite(expiresAt.getTime()) || expiresAt.getTime() <= nowMs) throw new SharePublishError("invalid-argument", "expiresAt must be a valid future time");
|
|
13960
13977
|
return input.targetAdapter.publish({
|
|
13961
13978
|
source,
|
|
13962
|
-
filename
|
|
13963
|
-
...
|
|
13979
|
+
filename,
|
|
13980
|
+
...files === void 0 ? {} : { files },
|
|
13964
13981
|
...input.mediaType === void 0 ? {} : { mediaType: input.mediaType },
|
|
13965
13982
|
...input.resourceKind === void 0 ? {} : { resourceKind: input.resourceKind },
|
|
13966
13983
|
...input.actions === void 0 ? {} : { actions: input.actions },
|
|
@@ -14103,7 +14120,11 @@ function mailboxCredentialCommitment(target) {
|
|
|
14103
14120
|
};
|
|
14104
14121
|
}
|
|
14105
14122
|
function prepareAddressedShare(request) {
|
|
14106
|
-
|
|
14123
|
+
try {
|
|
14124
|
+
canonicalShareFilename(request.filename);
|
|
14125
|
+
} catch {
|
|
14126
|
+
throw new TypeError("addressed filename is invalid");
|
|
14127
|
+
}
|
|
14107
14128
|
if (request.actions.length === 0 || request.policyActions.length === 0) throw new TypeError("addressed share actions are empty");
|
|
14108
14129
|
const target = normalizeShareTarget(request.target);
|
|
14109
14130
|
if (target.kind === "bearer") throw new TypeError("addressed target is required");
|
|
@@ -14121,9 +14142,10 @@ function assertMailboxCommitment(commitment, target) {
|
|
|
14121
14142
|
if (canonicalize(commitment.profile) !== canonicalize(expected.profile) || canonicalize(commitment.credentialType) !== canonicalize(expected.credentialType) || commitment.descriptorDigest !== expected.descriptorDigest || commitment.issuerDid !== expected.issuerDid || commitment.issuerKid !== expected.issuerKid) throw new TypeError(`${label} shares require the ${label} credential profile`);
|
|
14122
14143
|
if (commitment.requirementDigest !== expected.requirementDigest) throw new TypeError(`credential requirement is not bound to the email ${target.kind === "email" ? "address" : "domain"}`);
|
|
14123
14144
|
}
|
|
14124
|
-
async function publishAddressedShare(
|
|
14125
|
-
assertSafeInput(
|
|
14126
|
-
const { target } = prepareAddressedShare(
|
|
14145
|
+
async function publishAddressedShare(input) {
|
|
14146
|
+
assertSafeInput(input);
|
|
14147
|
+
const { target } = prepareAddressedShare(input);
|
|
14148
|
+
const options = { ...input, filename: canonicalShareFilename(input.filename) };
|
|
14127
14149
|
if (target.kind !== "recipientDid") assertMailboxCommitment(options.credentialRequirement, target);
|
|
14128
14150
|
const expiry = rfc3339Seconds(options.expiresAt);
|
|
14129
14151
|
const matcher = targetMatcher(target);
|
|
@@ -15099,6 +15121,7 @@ import { constants } from "fs";
|
|
|
15099
15121
|
import { lstat, mkdir as mkdir2, mkdtemp, open as open2, readFile as readFile2, realpath, stat as stat2, link, rename as rename2, rm as rm3, unlink } from "fs/promises";
|
|
15100
15122
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
15101
15123
|
import { basename as basename2, join as join4, resolve, sep } from "path";
|
|
15124
|
+
init_errors();
|
|
15102
15125
|
var MAX_SHARE_STDIN_BYTES = 100 * 1024 * 1024;
|
|
15103
15126
|
var MAX_SHARE_URL_BYTES = 64 * 1024;
|
|
15104
15127
|
async function readBoundedStdin(limit = MAX_SHARE_STDIN_BYTES) {
|
|
@@ -15120,21 +15143,28 @@ async function readBoundedUrlStdin() {
|
|
|
15120
15143
|
return value;
|
|
15121
15144
|
}
|
|
15122
15145
|
function safeFilename(value) {
|
|
15123
|
-
|
|
15124
|
-
return value;
|
|
15146
|
+
return shareFilename(value);
|
|
15125
15147
|
}
|
|
15126
15148
|
function shareFilename(value) {
|
|
15127
|
-
|
|
15149
|
+
try {
|
|
15150
|
+
return canonicalShareFilename(value);
|
|
15151
|
+
} catch {
|
|
15152
|
+
throw new CLIError(
|
|
15153
|
+
"UNSAFE_FILENAME",
|
|
15154
|
+
hasUnsafeFilenameCodePoint(value) ? "filename contains control or invisible characters" : "filename must be one safe path segment",
|
|
15155
|
+
8
|
|
15156
|
+
);
|
|
15157
|
+
}
|
|
15158
|
+
}
|
|
15159
|
+
function shareInputFilename(input, name) {
|
|
15160
|
+
return shareFilename(name ?? (input === "-" ? "stdin.md" : basename2(resolve(input))));
|
|
15128
15161
|
}
|
|
15129
15162
|
async function readShareInput(input, name, limit = MAX_SHARE_STDIN_BYTES) {
|
|
15130
|
-
|
|
15131
|
-
|
|
15132
|
-
return { bytes: bytes2, filename: shareFilename(name ?? "stdin.md") };
|
|
15133
|
-
}
|
|
15163
|
+
const filename = shareInputFilename(input, name);
|
|
15164
|
+
if (input === "-") return { bytes: await readBoundedStdin(limit), filename };
|
|
15134
15165
|
const path = resolve(input);
|
|
15135
15166
|
const info = await stat2(path);
|
|
15136
15167
|
if (!info.isFile() || info.size > limit) throw new Error("MAX_BYTES_EXCEEDED");
|
|
15137
|
-
const filename = shareFilename(name ?? basename2(path));
|
|
15138
15168
|
const bytes = new Uint8Array(await readFile2(path));
|
|
15139
15169
|
if (bytes.byteLength > limit) throw new Error("MAX_BYTES_EXCEEDED");
|
|
15140
15170
|
return { bytes, filename };
|
|
@@ -15382,7 +15412,9 @@ function registerShareCommand(program2) {
|
|
|
15382
15412
|
const json = jsonOutput(options, command);
|
|
15383
15413
|
const maxBytes = byteLimit(options.maxBytes);
|
|
15384
15414
|
if (files.length === 0 || files.includes("-") && files.length > 1) throw new CLIError("INVALID_ARGUMENT", "stdin must be the only publish input", 2);
|
|
15385
|
-
const
|
|
15415
|
+
const name = files.length === 1 ? options.name : void 0;
|
|
15416
|
+
for (const file of files) shareInputFilename(file, name);
|
|
15417
|
+
const inputs = await Promise.all(files.map((file) => readShareInput(file, name, maxBytes)));
|
|
15386
15418
|
assertAggregateInputLimit(inputs, maxBytes);
|
|
15387
15419
|
const target = parseShareTarget(options.to);
|
|
15388
15420
|
const actions = requestedActions(options.action);
|