monday-cli 0.5.0 → 0.6.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/CHANGELOG.md +277 -0
- package/README.md +96 -35
- package/dist/api/column-types.d.ts +48 -19
- package/dist/api/column-types.d.ts.map +1 -1
- package/dist/api/column-types.js +25 -11
- package/dist/api/column-types.js.map +1 -1
- package/dist/api/column-values.d.ts +17 -10
- package/dist/api/column-values.d.ts.map +1 -1
- package/dist/api/column-values.js +33 -19
- package/dist/api/column-values.js.map +1 -1
- package/dist/api/file-column-set.d.ts +507 -0
- package/dist/api/file-column-set.d.ts.map +1 -0
- package/dist/api/file-column-set.js +510 -0
- package/dist/api/file-column-set.js.map +1 -0
- package/dist/api/raw-write.d.ts +27 -17
- package/dist/api/raw-write.d.ts.map +1 -1
- package/dist/api/raw-write.js +40 -25
- package/dist/api/raw-write.js.map +1 -1
- package/dist/api/resolver-error-fold.d.ts +25 -0
- package/dist/api/resolver-error-fold.d.ts.map +1 -1
- package/dist/api/resolver-error-fold.js +56 -0
- package/dist/api/resolver-error-fold.js.map +1 -1
- package/dist/commands/board/column-create.d.ts +8 -3
- package/dist/commands/board/column-create.d.ts.map +1 -1
- package/dist/commands/board/column-create.js +16 -8
- package/dist/commands/board/column-create.js.map +1 -1
- package/dist/commands/item/create.d.ts.map +1 -1
- package/dist/commands/item/create.js +131 -33
- package/dist/commands/item/create.js.map +1 -1
- package/dist/commands/item/set.d.ts +33 -3
- package/dist/commands/item/set.d.ts.map +1 -1
- package/dist/commands/item/set.js +193 -15
- package/dist/commands/item/set.js.map +1 -1
- package/dist/commands/item/update.d.ts +34 -3
- package/dist/commands/item/update.d.ts.map +1 -1
- package/dist/commands/item/update.js +346 -67
- package/dist/commands/item/update.js.map +1 -1
- package/dist/commands/item/upload.d.ts.map +1 -1
- package/dist/commands/item/upload.js +16 -69
- package/dist/commands/item/upload.js.map +1 -1
- package/dist/commands/update/upload.d.ts.map +1 -1
- package/dist/commands/update/upload.js +9 -59
- package/dist/commands/update/upload.js.map +1 -1
- package/dist/utils/file-source.d.ts +93 -0
- package/dist/utils/file-source.d.ts.map +1 -0
- package/dist/utils/file-source.js +140 -0
- package/dist/utils/file-source.js.map +1 -0
- package/package.json +1 -1
|
@@ -96,9 +96,6 @@
|
|
|
96
96
|
* invalidation all land below.
|
|
97
97
|
*/
|
|
98
98
|
import { z } from 'zod';
|
|
99
|
-
import { stat as fsStat, access as fsAccess, readFile } from 'node:fs/promises';
|
|
100
|
-
import { constants as fsConstants } from 'node:fs';
|
|
101
|
-
import { resolve as resolvePath, basename } from 'node:path';
|
|
102
99
|
import { ensureSubcommand } from '../types.js';
|
|
103
100
|
import { parseArgv } from '../parse-argv.js';
|
|
104
101
|
import { ItemIdSchema, ColumnIdSchema } from '../../types/ids.js';
|
|
@@ -108,8 +105,8 @@ import { resolveColumnWithRefresh } from '../../api/columns.js';
|
|
|
108
105
|
import { lookupItemBoard } from '../../api/item-board-lookup.js';
|
|
109
106
|
import { invalidateBoard } from '../../api/cache.js';
|
|
110
107
|
import { foldResolverWarningsIntoError } from '../../api/resolver-error-fold.js';
|
|
111
|
-
import { ApiError
|
|
112
|
-
import {
|
|
108
|
+
import { ApiError } from '../../utils/errors.js';
|
|
109
|
+
import { precheckLocalFile, buildBlobFromPath, } from '../../utils/file-source.js';
|
|
113
110
|
import { emitMutation, emitDryRun } from '../emit.js';
|
|
114
111
|
const inputSchema = z
|
|
115
112
|
.object({
|
|
@@ -158,68 +155,15 @@ export const itemUploadCommand = {
|
|
|
158
155
|
file: fileArg,
|
|
159
156
|
column: opts.column,
|
|
160
157
|
});
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
// pre-check and `fs.readFile()` would throw a raw fs error
|
|
171
|
-
// mid-upload after wire calls).
|
|
172
|
-
const filePath = resolvePath(process.cwd(), parsed.file);
|
|
173
|
-
const filename = basename(filePath);
|
|
174
|
-
let fileSizeBytes;
|
|
175
|
-
try {
|
|
176
|
-
const stats = await fsStat(filePath);
|
|
177
|
-
if (!stats.isFile()) {
|
|
178
|
-
throw new UsageError(`<file> ${JSON.stringify(parsed.file)} is not a regular file ` +
|
|
179
|
-
`(resolved to ${JSON.stringify(filePath)}).`, {
|
|
180
|
-
details: {
|
|
181
|
-
reason: 'file_not_readable',
|
|
182
|
-
file_path: filePath,
|
|
183
|
-
hint: 'pass a path to a regular readable file; directories ' +
|
|
184
|
-
'and special files (sockets, devices) are rejected.',
|
|
185
|
-
},
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
await fsAccess(filePath, fsConstants.R_OK);
|
|
189
|
-
fileSizeBytes = stats.size;
|
|
190
|
-
}
|
|
191
|
-
catch (err) {
|
|
192
|
-
if (err instanceof UsageError) {
|
|
193
|
-
throw err;
|
|
194
|
-
}
|
|
195
|
-
const code = errorCode(err);
|
|
196
|
-
throw new UsageError(`<file> ${JSON.stringify(parsed.file)} cannot be read ` +
|
|
197
|
-
`(resolved to ${JSON.stringify(filePath)}): ` +
|
|
198
|
-
`${asError(err).message}.`, {
|
|
199
|
-
cause: err,
|
|
200
|
-
details: {
|
|
201
|
-
reason: 'file_not_readable',
|
|
202
|
-
file_path: filePath,
|
|
203
|
-
...(code === undefined ? {} : { errno_code: code }),
|
|
204
|
-
hint: 'check that the path exists, is readable by the current ' +
|
|
205
|
-
'user, and isn\'t a directory.',
|
|
206
|
-
},
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
if (fileSizeBytes === 0) {
|
|
210
|
-
throw new UsageError(`<file> ${JSON.stringify(parsed.file)} is empty (0 bytes); ` +
|
|
211
|
-
`Monday rejects empty uploads server-side.`, {
|
|
212
|
-
details: {
|
|
213
|
-
reason: 'file_empty',
|
|
214
|
-
file_path: filePath,
|
|
215
|
-
filename,
|
|
216
|
-
file_size_bytes: 0,
|
|
217
|
-
hint: 'Monday returns FILE_SIZE_LIMIT_EXCEEDED on empty ' +
|
|
218
|
-
'uploads. Provide a non-empty file or remove the upload ' +
|
|
219
|
-
'call.',
|
|
220
|
-
},
|
|
221
|
-
});
|
|
222
|
-
}
|
|
158
|
+
// Pre-check existence + type + read-permission + emptiness
|
|
159
|
+
// BEFORE resolveClient so a missing/unreadable-file error
|
|
160
|
+
// surfaces as usage_error (exit 1) rather than getting
|
|
161
|
+
// tangled up with config_error (exit 3) on a token miss
|
|
162
|
+
// OR firing AFTER `lookupItemBoard` /
|
|
163
|
+
// `resolveColumnWithRefresh` Monday wire calls (round-1
|
|
164
|
+
// P2-2 fix). Lifted at v0.6-M38 IMPL kickoff per
|
|
165
|
+
// R-v0.6-NEW-1 (3-consumer trigger).
|
|
166
|
+
const { filePath, filename, fileSizeBytes } = await precheckLocalFile(parsed.file);
|
|
223
167
|
const { client, globalFlags, apiVersion, multipart, toEmit } = resolveClient(ctx, program.opts());
|
|
224
168
|
if (globalFlags.dryRun) {
|
|
225
169
|
// D5 closure: dry-run is fs.stat()-backed (NOT a 0-byte
|
|
@@ -315,8 +259,11 @@ export const itemUploadCommand = {
|
|
|
315
259
|
// Read the file bytes into a Blob with a sniffed content-
|
|
316
260
|
// type. Done AFTER column-type validation so a non-`file`
|
|
317
261
|
// column rejection doesn't pay for the full read.
|
|
318
|
-
const
|
|
319
|
-
|
|
262
|
+
const file = await buildBlobFromPath({
|
|
263
|
+
filePath,
|
|
264
|
+
filename,
|
|
265
|
+
fileSizeBytes,
|
|
266
|
+
});
|
|
320
267
|
const result = await addFileToColumn({
|
|
321
268
|
client,
|
|
322
269
|
multipart,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../../src/commands/item/upload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgGG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../../src/commands/item/upload.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/commands/update/upload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/commands/update/upload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAGnE,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,qBAAqB,CAAC;AAQ7B,QAAA,MAAM,WAAW;;;kBAcN,CAAC;AAEZ,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAC7C,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,EAC3B,kBAAkB,CA+HnB,CAAC"}
|
|
@@ -58,16 +58,12 @@
|
|
|
58
58
|
* supports).
|
|
59
59
|
*/
|
|
60
60
|
import { z } from 'zod';
|
|
61
|
-
import { stat as fsStat, access as fsAccess, readFile } from 'node:fs/promises';
|
|
62
|
-
import { constants as fsConstants } from 'node:fs';
|
|
63
|
-
import { resolve as resolvePath, basename } from 'node:path';
|
|
64
61
|
import { ensureSubcommand } from '../types.js';
|
|
65
62
|
import { parseArgv } from '../parse-argv.js';
|
|
66
63
|
import { UpdateIdSchema } from '../../types/ids.js';
|
|
67
64
|
import { updateUploadOutputSchema, addFileToUpdate, } from '../../api/assets.js';
|
|
68
65
|
import { resolveClient } from '../../api/resolve-client.js';
|
|
69
|
-
import {
|
|
70
|
-
import { sniffContentType } from '../../utils/mime.js';
|
|
66
|
+
import { precheckLocalFile, buildBlobFromPath, } from '../../utils/file-source.js';
|
|
71
67
|
import { emitMutation, emitDryRun } from '../emit.js';
|
|
72
68
|
const inputSchema = z
|
|
73
69
|
.object({
|
|
@@ -116,58 +112,9 @@ export const updateUploadCommand = {
|
|
|
116
112
|
// Same fs.stat() + fs.access(R_OK) pre-check shape as
|
|
117
113
|
// `item upload` (round-1 P2-2 fix). Pre-resolveClient so a
|
|
118
114
|
// missing/unreadable-file error surfaces as usage_error
|
|
119
|
-
// (exit 1) before any token check.
|
|
120
|
-
|
|
121
|
-
const filename =
|
|
122
|
-
let fileSizeBytes;
|
|
123
|
-
try {
|
|
124
|
-
const stats = await fsStat(filePath);
|
|
125
|
-
if (!stats.isFile()) {
|
|
126
|
-
throw new UsageError(`<file> ${JSON.stringify(parsed.file)} is not a regular file ` +
|
|
127
|
-
`(resolved to ${JSON.stringify(filePath)}).`, {
|
|
128
|
-
details: {
|
|
129
|
-
reason: 'file_not_readable',
|
|
130
|
-
file_path: filePath,
|
|
131
|
-
hint: 'pass a path to a regular readable file; directories ' +
|
|
132
|
-
'and special files (sockets, devices) are rejected.',
|
|
133
|
-
},
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
await fsAccess(filePath, fsConstants.R_OK);
|
|
137
|
-
fileSizeBytes = stats.size;
|
|
138
|
-
}
|
|
139
|
-
catch (err) {
|
|
140
|
-
if (err instanceof UsageError) {
|
|
141
|
-
throw err;
|
|
142
|
-
}
|
|
143
|
-
const code = errorCode(err);
|
|
144
|
-
throw new UsageError(`<file> ${JSON.stringify(parsed.file)} cannot be read ` +
|
|
145
|
-
`(resolved to ${JSON.stringify(filePath)}): ` +
|
|
146
|
-
`${asError(err).message}.`, {
|
|
147
|
-
cause: err,
|
|
148
|
-
details: {
|
|
149
|
-
reason: 'file_not_readable',
|
|
150
|
-
file_path: filePath,
|
|
151
|
-
...(code === undefined ? {} : { errno_code: code }),
|
|
152
|
-
hint: 'check that the path exists, is readable by the current ' +
|
|
153
|
-
'user, and isn\'t a directory.',
|
|
154
|
-
},
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
if (fileSizeBytes === 0) {
|
|
158
|
-
throw new UsageError(`<file> ${JSON.stringify(parsed.file)} is empty (0 bytes); ` +
|
|
159
|
-
`Monday rejects empty uploads server-side.`, {
|
|
160
|
-
details: {
|
|
161
|
-
reason: 'file_empty',
|
|
162
|
-
file_path: filePath,
|
|
163
|
-
filename,
|
|
164
|
-
file_size_bytes: 0,
|
|
165
|
-
hint: 'Monday returns FILE_SIZE_LIMIT_EXCEEDED on empty ' +
|
|
166
|
-
'uploads. Provide a non-empty file or remove the upload ' +
|
|
167
|
-
'call.',
|
|
168
|
-
},
|
|
169
|
-
});
|
|
170
|
-
}
|
|
115
|
+
// (exit 1) before any token check. Lifted to a shared
|
|
116
|
+
// helper at v0.6-M38 IMPL kickoff per R-v0.6-NEW-1.
|
|
117
|
+
const { filePath, filename, fileSizeBytes } = await precheckLocalFile(parsed.file);
|
|
171
118
|
const { client, globalFlags, apiVersion, multipart, toEmit } = resolveClient(ctx, program.opts());
|
|
172
119
|
if (globalFlags.dryRun) {
|
|
173
120
|
// D5 closure mirror — dry-run is fs.stat()-backed; no
|
|
@@ -195,8 +142,11 @@ export const updateUploadCommand = {
|
|
|
195
142
|
});
|
|
196
143
|
return;
|
|
197
144
|
}
|
|
198
|
-
const
|
|
199
|
-
|
|
145
|
+
const file = await buildBlobFromPath({
|
|
146
|
+
filePath,
|
|
147
|
+
filename,
|
|
148
|
+
fileSizeBytes,
|
|
149
|
+
});
|
|
200
150
|
const result = await addFileToUpdate({
|
|
201
151
|
client,
|
|
202
152
|
multipart,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../../src/commands/update/upload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../../src/commands/update/upload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local-file pre-check + Blob-construction shared helpers for the
|
|
3
|
+
* multipart-upload surfaces. Lifted at v0.6-M38 IMPL kickoff per
|
|
4
|
+
* R-v0.6-NEW-1 (3-consumer trigger fires at IMPL: v0.4-M31's `monday
|
|
5
|
+
* item upload` action body + v0.4-M31's `monday update upload` action
|
|
6
|
+
* body + v0.6-M38's `executeFileColumnSet` runtime body). Mirrors
|
|
7
|
+
* R-NEW-29's M25 lift-ahead-of-feat cadence (extract the shared
|
|
8
|
+
* pattern BEFORE the feat commit that crystallizes the 3rd consumer
|
|
9
|
+
* so the feat diff stays focused on the behavioural change).
|
|
10
|
+
*
|
|
11
|
+
* **Two helpers, distinct fire points** — the M31 / M38 callers run
|
|
12
|
+
* the pre-check BEFORE `resolveClient` so a missing/unreadable-file
|
|
13
|
+
* error surfaces as `usage_error` (exit 1) rather than getting
|
|
14
|
+
* tangled up with `config_error` (exit 3) on a token miss; the Blob
|
|
15
|
+
* construction fires AFTER column-type validation so a non-`file`
|
|
16
|
+
* column rejection or an `item update` mutex rejection doesn't pay
|
|
17
|
+
* for the full read:
|
|
18
|
+
*
|
|
19
|
+
* 1. {@link precheckLocalFile} — `fs.stat()` + `fs.access(R_OK)` +
|
|
20
|
+
* non-empty check. Surfaces `usage_error` with `details.reason:
|
|
21
|
+
* 'file_not_readable'` (ENOENT / EACCES / path is a directory)
|
|
22
|
+
* or `'file_empty'` (zero bytes).
|
|
23
|
+
* 2. {@link buildBlobFromPath} — `fs/promises.readFile()` + Web
|
|
24
|
+
* `Blob` construction with the sniffed `Content-Type` from
|
|
25
|
+
* `sniffContentType(...)`. No additional error surfaces — read
|
|
26
|
+
* failures past the pre-check are TOCTOU-class (file removed
|
|
27
|
+
* between pre-check and read) and surface via the caller's
|
|
28
|
+
* catch-all.
|
|
29
|
+
*
|
|
30
|
+
* **Behaviour-preserving lift.** Byte-equivalent to the M31 inline
|
|
31
|
+
* copies the lift consolidates — same `usage_error` message prose,
|
|
32
|
+
* same `details.reason` discriminator + `errno_code` slot + hint
|
|
33
|
+
* text, same path resolution (`resolve(process.cwd(), rawPath)`)
|
|
34
|
+
* and basename (`basename(filePath)`). Integration tests for the
|
|
35
|
+
* 2 M31 consumers cover the pre-check error branches; this module
|
|
36
|
+
* adds direct unit tests for the helper + the buildBlobFromPath
|
|
37
|
+
* sibling.
|
|
38
|
+
*/
|
|
39
|
+
/**
|
|
40
|
+
* Result of a successful {@link precheckLocalFile} call. Carries the
|
|
41
|
+
* resolved absolute path (post `resolve(cwd, rawPath)`), the
|
|
42
|
+
* basename (Monday's wire `Asset.name` source), and the `fs.stat()`
|
|
43
|
+
* size in bytes.
|
|
44
|
+
*/
|
|
45
|
+
export interface LocalFilePrecheck {
|
|
46
|
+
/** Resolved absolute path. */
|
|
47
|
+
readonly filePath: string;
|
|
48
|
+
/** `basename(filePath)` — Monday's wire `Asset.name` source. */
|
|
49
|
+
readonly filename: string;
|
|
50
|
+
/** Local `fs.stat()` size in bytes (always `>= 1` post-check). */
|
|
51
|
+
readonly fileSizeBytes: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolves `rawPath` relative to `process.cwd()` and confirms the
|
|
55
|
+
* file is (a) a regular file (not a directory / socket / device),
|
|
56
|
+
* (b) readable by the current user, and (c) non-empty. Throws
|
|
57
|
+
* {@link UsageError} on any check failure with a `details.reason`
|
|
58
|
+
* discriminator:
|
|
59
|
+
*
|
|
60
|
+
* - `'file_not_readable'` — `ENOENT` / `EACCES` / path resolves
|
|
61
|
+
* to a directory or special file. `details.errno_code` echoes
|
|
62
|
+
* the underlying errno when one was attached to the Node fs
|
|
63
|
+
* error.
|
|
64
|
+
* - `'file_empty'` — file exists and is readable but has zero
|
|
65
|
+
* bytes. Monday rejects empty uploads server-side; the CLI
|
|
66
|
+
* surfaces the rejection with a clearer hint via the local
|
|
67
|
+
* pre-check.
|
|
68
|
+
*
|
|
69
|
+
* The Web `Blob` construction for the actual upload payload happens
|
|
70
|
+
* later via {@link buildBlobFromPath} — callers run the pre-check
|
|
71
|
+
* BEFORE the wire dispatch so a bad path doesn't burn a network
|
|
72
|
+
* round-trip, and run the Blob construction AFTER any column-type
|
|
73
|
+
* / mutex validation so a non-`file` column rejection doesn't pay
|
|
74
|
+
* for the full read.
|
|
75
|
+
*/
|
|
76
|
+
export declare const precheckLocalFile: (rawPath: string) => Promise<LocalFilePrecheck>;
|
|
77
|
+
/**
|
|
78
|
+
* Reads the file at `precheck.filePath` into memory and wraps it as
|
|
79
|
+
* a Web `Blob` with a `Content-Type` sniffed from the filename
|
|
80
|
+
* extension via {@link sniffContentType}. The Blob is the payload
|
|
81
|
+
* the multipart transport sends to Monday's wire `File!` scalar.
|
|
82
|
+
*
|
|
83
|
+
* Run AFTER {@link precheckLocalFile} so the path is known good and
|
|
84
|
+
* the size is known non-zero. Run AFTER column-type validation / M38
|
|
85
|
+
* mutex checks so a non-`file` column rejection or a mutex violation
|
|
86
|
+
* doesn't pay for the full read. The read uses `fs/promises.readFile`
|
|
87
|
+
* which buffers the entire payload into memory — Monday's per-file
|
|
88
|
+
* cap is plan-tier-dependent (typically 500 MB at standard tiers);
|
|
89
|
+
* callers don't pre-check size against a hardcoded ceiling because
|
|
90
|
+
* the cap isn't exposed via the GraphQL schema.
|
|
91
|
+
*/
|
|
92
|
+
export declare const buildBlobFromPath: (precheck: LocalFilePrecheck) => Promise<Blob>;
|
|
93
|
+
//# sourceMappingURL=file-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-source.d.ts","sourceRoot":"","sources":["../../src/utils/file-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAQH;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,8BAA8B;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,kEAAkE;IAClE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,KACd,OAAO,CAAC,iBAAiB,CAgE3B,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iBAAiB,GAC5B,UAAU,iBAAiB,KAC1B,OAAO,CAAC,IAAI,CAGd,CAAC"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local-file pre-check + Blob-construction shared helpers for the
|
|
3
|
+
* multipart-upload surfaces. Lifted at v0.6-M38 IMPL kickoff per
|
|
4
|
+
* R-v0.6-NEW-1 (3-consumer trigger fires at IMPL: v0.4-M31's `monday
|
|
5
|
+
* item upload` action body + v0.4-M31's `monday update upload` action
|
|
6
|
+
* body + v0.6-M38's `executeFileColumnSet` runtime body). Mirrors
|
|
7
|
+
* R-NEW-29's M25 lift-ahead-of-feat cadence (extract the shared
|
|
8
|
+
* pattern BEFORE the feat commit that crystallizes the 3rd consumer
|
|
9
|
+
* so the feat diff stays focused on the behavioural change).
|
|
10
|
+
*
|
|
11
|
+
* **Two helpers, distinct fire points** — the M31 / M38 callers run
|
|
12
|
+
* the pre-check BEFORE `resolveClient` so a missing/unreadable-file
|
|
13
|
+
* error surfaces as `usage_error` (exit 1) rather than getting
|
|
14
|
+
* tangled up with `config_error` (exit 3) on a token miss; the Blob
|
|
15
|
+
* construction fires AFTER column-type validation so a non-`file`
|
|
16
|
+
* column rejection or an `item update` mutex rejection doesn't pay
|
|
17
|
+
* for the full read:
|
|
18
|
+
*
|
|
19
|
+
* 1. {@link precheckLocalFile} — `fs.stat()` + `fs.access(R_OK)` +
|
|
20
|
+
* non-empty check. Surfaces `usage_error` with `details.reason:
|
|
21
|
+
* 'file_not_readable'` (ENOENT / EACCES / path is a directory)
|
|
22
|
+
* or `'file_empty'` (zero bytes).
|
|
23
|
+
* 2. {@link buildBlobFromPath} — `fs/promises.readFile()` + Web
|
|
24
|
+
* `Blob` construction with the sniffed `Content-Type` from
|
|
25
|
+
* `sniffContentType(...)`. No additional error surfaces — read
|
|
26
|
+
* failures past the pre-check are TOCTOU-class (file removed
|
|
27
|
+
* between pre-check and read) and surface via the caller's
|
|
28
|
+
* catch-all.
|
|
29
|
+
*
|
|
30
|
+
* **Behaviour-preserving lift.** Byte-equivalent to the M31 inline
|
|
31
|
+
* copies the lift consolidates — same `usage_error` message prose,
|
|
32
|
+
* same `details.reason` discriminator + `errno_code` slot + hint
|
|
33
|
+
* text, same path resolution (`resolve(process.cwd(), rawPath)`)
|
|
34
|
+
* and basename (`basename(filePath)`). Integration tests for the
|
|
35
|
+
* 2 M31 consumers cover the pre-check error branches; this module
|
|
36
|
+
* adds direct unit tests for the helper + the buildBlobFromPath
|
|
37
|
+
* sibling.
|
|
38
|
+
*/
|
|
39
|
+
import { stat as fsStat, access as fsAccess, readFile } from 'node:fs/promises';
|
|
40
|
+
import { constants as fsConstants } from 'node:fs';
|
|
41
|
+
import { resolve as resolvePath, basename } from 'node:path';
|
|
42
|
+
import { UsageError, asError, errorCode } from './errors.js';
|
|
43
|
+
import { sniffContentType } from './mime.js';
|
|
44
|
+
/**
|
|
45
|
+
* Resolves `rawPath` relative to `process.cwd()` and confirms the
|
|
46
|
+
* file is (a) a regular file (not a directory / socket / device),
|
|
47
|
+
* (b) readable by the current user, and (c) non-empty. Throws
|
|
48
|
+
* {@link UsageError} on any check failure with a `details.reason`
|
|
49
|
+
* discriminator:
|
|
50
|
+
*
|
|
51
|
+
* - `'file_not_readable'` — `ENOENT` / `EACCES` / path resolves
|
|
52
|
+
* to a directory or special file. `details.errno_code` echoes
|
|
53
|
+
* the underlying errno when one was attached to the Node fs
|
|
54
|
+
* error.
|
|
55
|
+
* - `'file_empty'` — file exists and is readable but has zero
|
|
56
|
+
* bytes. Monday rejects empty uploads server-side; the CLI
|
|
57
|
+
* surfaces the rejection with a clearer hint via the local
|
|
58
|
+
* pre-check.
|
|
59
|
+
*
|
|
60
|
+
* The Web `Blob` construction for the actual upload payload happens
|
|
61
|
+
* later via {@link buildBlobFromPath} — callers run the pre-check
|
|
62
|
+
* BEFORE the wire dispatch so a bad path doesn't burn a network
|
|
63
|
+
* round-trip, and run the Blob construction AFTER any column-type
|
|
64
|
+
* / mutex validation so a non-`file` column rejection doesn't pay
|
|
65
|
+
* for the full read.
|
|
66
|
+
*/
|
|
67
|
+
export const precheckLocalFile = async (rawPath) => {
|
|
68
|
+
const filePath = resolvePath(process.cwd(), rawPath);
|
|
69
|
+
const filename = basename(filePath);
|
|
70
|
+
let fileSizeBytes;
|
|
71
|
+
try {
|
|
72
|
+
const stats = await fsStat(filePath);
|
|
73
|
+
if (!stats.isFile()) {
|
|
74
|
+
throw new UsageError(`<file> ${JSON.stringify(rawPath)} is not a regular file ` +
|
|
75
|
+
`(resolved to ${JSON.stringify(filePath)}).`, {
|
|
76
|
+
details: {
|
|
77
|
+
reason: 'file_not_readable',
|
|
78
|
+
file_path: filePath,
|
|
79
|
+
hint: 'pass a path to a regular readable file; directories ' +
|
|
80
|
+
'and special files (sockets, devices) are rejected.',
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
await fsAccess(filePath, fsConstants.R_OK);
|
|
85
|
+
fileSizeBytes = stats.size;
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
if (err instanceof UsageError) {
|
|
89
|
+
throw err;
|
|
90
|
+
}
|
|
91
|
+
const code = errorCode(err);
|
|
92
|
+
throw new UsageError(`<file> ${JSON.stringify(rawPath)} cannot be read ` +
|
|
93
|
+
`(resolved to ${JSON.stringify(filePath)}): ` +
|
|
94
|
+
`${asError(err).message}.`, {
|
|
95
|
+
cause: err,
|
|
96
|
+
details: {
|
|
97
|
+
reason: 'file_not_readable',
|
|
98
|
+
file_path: filePath,
|
|
99
|
+
...(code === undefined ? {} : { errno_code: code }),
|
|
100
|
+
hint: 'check that the path exists, is readable by the current ' +
|
|
101
|
+
"user, and isn't a directory.",
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (fileSizeBytes === 0) {
|
|
106
|
+
throw new UsageError(`<file> ${JSON.stringify(rawPath)} is empty (0 bytes); ` +
|
|
107
|
+
`Monday rejects empty uploads server-side.`, {
|
|
108
|
+
details: {
|
|
109
|
+
reason: 'file_empty',
|
|
110
|
+
file_path: filePath,
|
|
111
|
+
filename,
|
|
112
|
+
file_size_bytes: 0,
|
|
113
|
+
hint: 'Monday returns FILE_SIZE_LIMIT_EXCEEDED on empty ' +
|
|
114
|
+
'uploads. Provide a non-empty file or remove the upload ' +
|
|
115
|
+
'call.',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return { filePath, filename, fileSizeBytes };
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Reads the file at `precheck.filePath` into memory and wraps it as
|
|
123
|
+
* a Web `Blob` with a `Content-Type` sniffed from the filename
|
|
124
|
+
* extension via {@link sniffContentType}. The Blob is the payload
|
|
125
|
+
* the multipart transport sends to Monday's wire `File!` scalar.
|
|
126
|
+
*
|
|
127
|
+
* Run AFTER {@link precheckLocalFile} so the path is known good and
|
|
128
|
+
* the size is known non-zero. Run AFTER column-type validation / M38
|
|
129
|
+
* mutex checks so a non-`file` column rejection or a mutex violation
|
|
130
|
+
* doesn't pay for the full read. The read uses `fs/promises.readFile`
|
|
131
|
+
* which buffers the entire payload into memory — Monday's per-file
|
|
132
|
+
* cap is plan-tier-dependent (typically 500 MB at standard tiers);
|
|
133
|
+
* callers don't pre-check size against a hardcoded ceiling because
|
|
134
|
+
* the cap isn't exposed via the GraphQL schema.
|
|
135
|
+
*/
|
|
136
|
+
export const buildBlobFromPath = async (precheck) => {
|
|
137
|
+
const bytes = await readFile(precheck.filePath);
|
|
138
|
+
return new Blob([bytes], { type: sniffContentType(precheck.filename) });
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=file-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-source.js","sourceRoot":"","sources":["../../src/utils/file-source.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "monday-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Agent-first CLI for Monday.com — read boards, file backlog items, transition statuses, and post comments from the terminal. Built for AI coding agents (Claude Code, Codex) with humans as a welcome second audience.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|