monday-cli 0.4.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 +645 -0
- package/README.md +173 -34
- package/dist/api/column-types.d.ts +48 -17
- package/dist/api/column-types.d.ts.map +1 -1
- package/dist/api/column-types.js +25 -9
- package/dist/api/column-types.js.map +1 -1
- package/dist/api/column-values.d.ts +17 -9
- package/dist/api/column-values.d.ts.map +1 -1
- package/dist/api/column-values.js +33 -18
- package/dist/api/column-values.js.map +1 -1
- package/dist/api/documents.d.ts +1136 -3
- package/dist/api/documents.d.ts.map +1 -1
- package/dist/api/documents.js +1828 -3
- package/dist/api/documents.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 -16
- package/dist/api/raw-write.d.ts.map +1 -1
- package/dist/api/raw-write.js +40 -24
- 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/api/teams.d.ts +657 -0
- package/dist/api/teams.d.ts.map +1 -0
- package/dist/api/teams.js +880 -0
- package/dist/api/teams.js.map +1 -0
- 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/doc/append-markdown.d.ts +117 -0
- package/dist/commands/doc/append-markdown.d.ts.map +1 -0
- package/dist/commands/doc/append-markdown.js +253 -0
- package/dist/commands/doc/append-markdown.js.map +1 -0
- package/dist/commands/doc/block-create.d.ts +114 -0
- package/dist/commands/doc/block-create.d.ts.map +1 -0
- package/dist/commands/doc/block-create.js +206 -0
- package/dist/commands/doc/block-create.js.map +1 -0
- package/dist/commands/doc/block-delete.d.ts +72 -0
- package/dist/commands/doc/block-delete.d.ts.map +1 -0
- package/dist/commands/doc/block-delete.js +161 -0
- package/dist/commands/doc/block-delete.js.map +1 -0
- package/dist/commands/doc/block-update.d.ts +75 -0
- package/dist/commands/doc/block-update.d.ts.map +1 -0
- package/dist/commands/doc/block-update.js +162 -0
- package/dist/commands/doc/block-update.js.map +1 -0
- package/dist/commands/doc/create-in-workspace.d.ts +76 -0
- package/dist/commands/doc/create-in-workspace.d.ts.map +1 -0
- package/dist/commands/doc/create-in-workspace.js +164 -0
- package/dist/commands/doc/create-in-workspace.js.map +1 -0
- package/dist/commands/doc/create-on-column.d.ts +71 -0
- package/dist/commands/doc/create-on-column.d.ts.map +1 -0
- package/dist/commands/doc/create-on-column.js +146 -0
- package/dist/commands/doc/create-on-column.js.map +1 -0
- package/dist/commands/doc/delete.d.ts +68 -0
- package/dist/commands/doc/delete.d.ts.map +1 -0
- package/dist/commands/doc/delete.js +146 -0
- package/dist/commands/doc/delete.js.map +1 -0
- package/dist/commands/doc/duplicate.d.ts +101 -0
- package/dist/commands/doc/duplicate.d.ts.map +1 -0
- package/dist/commands/doc/duplicate.js +191 -0
- package/dist/commands/doc/duplicate.js.map +1 -0
- package/dist/commands/doc/import-html.d.ts +125 -0
- package/dist/commands/doc/import-html.d.ts.map +1 -0
- package/dist/commands/doc/import-html.js +273 -0
- package/dist/commands/doc/import-html.js.map +1 -0
- package/dist/commands/doc/list.d.ts +6 -3
- package/dist/commands/doc/list.d.ts.map +1 -1
- package/dist/commands/doc/list.js +17 -48
- package/dist/commands/doc/list.js.map +1 -1
- package/dist/commands/doc/rename.d.ts +60 -0
- package/dist/commands/doc/rename.d.ts.map +1 -0
- package/dist/commands/doc/rename.js +135 -0
- package/dist/commands/doc/rename.js.map +1 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +116 -0
- package/dist/commands/index.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/create.d.ts.map +1 -1
- package/dist/commands/update/create.js +6 -4
- package/dist/commands/update/create.js.map +1 -1
- package/dist/commands/update/edit.d.ts +4 -2
- package/dist/commands/update/edit.d.ts.map +1 -1
- package/dist/commands/update/edit.js +10 -6
- package/dist/commands/update/edit.js.map +1 -1
- package/dist/commands/update/reply.d.ts +4 -2
- package/dist/commands/update/reply.d.ts.map +1 -1
- package/dist/commands/update/reply.js +10 -6
- package/dist/commands/update/reply.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/commands/user/_team-membership.d.ts +10 -0
- package/dist/commands/user/_team-membership.d.ts.map +1 -0
- package/dist/commands/user/_team-membership.js +88 -0
- package/dist/commands/user/_team-membership.js.map +1 -0
- package/dist/commands/user/team-add-members.d.ts +81 -0
- package/dist/commands/user/team-add-members.d.ts.map +1 -0
- package/dist/commands/user/team-add-members.js +186 -0
- package/dist/commands/user/team-add-members.js.map +1 -0
- package/dist/commands/user/team-create.d.ts +82 -0
- package/dist/commands/user/team-create.d.ts.map +1 -0
- package/dist/commands/user/team-create.js +206 -0
- package/dist/commands/user/team-create.js.map +1 -0
- package/dist/commands/user/team-delete.d.ts +56 -0
- package/dist/commands/user/team-delete.d.ts.map +1 -0
- package/dist/commands/user/team-delete.js +137 -0
- package/dist/commands/user/team-delete.js.map +1 -0
- package/dist/commands/user/team-get.d.ts +41 -0
- package/dist/commands/user/team-get.d.ts.map +1 -0
- package/dist/commands/user/team-get.js +87 -0
- package/dist/commands/user/team-get.js.map +1 -0
- package/dist/commands/user/team-list.d.ts +39 -0
- package/dist/commands/user/team-list.d.ts.map +1 -0
- package/dist/commands/user/team-list.js +90 -0
- package/dist/commands/user/team-list.js.map +1 -0
- package/dist/commands/user/team-remove-members.d.ts +71 -0
- package/dist/commands/user/team-remove-members.d.ts.map +1 -0
- package/dist/commands/user/team-remove-members.js +176 -0
- package/dist/commands/user/team-remove-members.js.map +1 -0
- package/dist/types/ids.d.ts +6 -0
- package/dist/types/ids.d.ts.map +1 -1
- package/dist/types/ids.js +46 -5
- package/dist/types/ids.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/dist/utils/parse-brand-list.d.ts +95 -0
- package/dist/utils/parse-brand-list.d.ts.map +1 -0
- package/dist/utils/parse-brand-list.js +96 -0
- package/dist/utils/parse-brand-list.js.map +1 -0
- package/dist/utils/source-content.d.ts +93 -0
- package/dist/utils/source-content.d.ts.map +1 -0
- package/dist/utils/source-content.js +120 -0
- package/dist/utils/source-content.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `monday user team-remove-members <tid> --users <id,...>
|
|
3
|
+
* [--dry-run]` — remove one or more users from a team
|
|
4
|
+
* (`cli-design.md` §4.3 USER section + §13 v0.5 entry;
|
|
5
|
+
* `v0.5-plan.md` §3 M34).
|
|
6
|
+
*
|
|
7
|
+
* **Wire shape.** Single `remove_users_from_team(team_id,
|
|
8
|
+
* user_ids)` round-trip via {@link removeUsersFromTeam}
|
|
9
|
+
* against `mutation RemoveUsersFromTeam` with `operationName:
|
|
10
|
+
* 'RemoveUsersFromTeam'` (R-NEW-37 W2 audit-point). Monday
|
|
11
|
+
* returns `ChangeTeamMembershipsResult { failed_users:
|
|
12
|
+
* [User!], successful_users: [User!] }` — same wire-level
|
|
13
|
+
* partial-success envelope as add-members. The action body
|
|
14
|
+
* wraps this into the §6.1 universal partial-success shape
|
|
15
|
+
* `data: { operation: "remove_users_from_team", team_id,
|
|
16
|
+
* results: [{ok, user_id, ...}] }` (D5 closure).
|
|
17
|
+
*
|
|
18
|
+
* **Argv shape.**
|
|
19
|
+
*
|
|
20
|
+
* - `<teamId>` — positional `TeamId`. Required, brand-
|
|
21
|
+
* validated at parse boundary.
|
|
22
|
+
* - `--users <id,...>` — required, comma-separated numeric
|
|
23
|
+
* user IDs (maps to wire `user_ids: [ID!]!`). Each entry
|
|
24
|
+
* brand-validated via {@link UserIdSchema} through the
|
|
25
|
+
* lifted {@link parseBrandedListArg} helper (R-NEW-70
|
|
26
|
+
* consumer #4 post-lift).
|
|
27
|
+
*
|
|
28
|
+
* **Output envelope.** Same shape as
|
|
29
|
+
* {@link teamAddMembersCommand} but with `operation:
|
|
30
|
+
* 'remove_users_from_team'` so agents that key off the
|
|
31
|
+
* operation literal can dispatch the right post-mutation
|
|
32
|
+
* recovery flow.
|
|
33
|
+
*
|
|
34
|
+
* **Wire-vs-CLI semantics asymmetry.** Same generic-
|
|
35
|
+
* `membership_failed`-code asymmetry as team-add-members; see
|
|
36
|
+
* `teamMembershipResultSchema` JSDoc in `src/api/teams.ts` for
|
|
37
|
+
* the canonical note + cross-link to `docs/architecture.md`'s
|
|
38
|
+
* "Wire-vs-CLI semantics documentation conventions" section.
|
|
39
|
+
*
|
|
40
|
+
* **Dry-run shape** per cli-design §6.4 mutation-dry-run
|
|
41
|
+
* variant. SINGLE planned operation entry `{operation:
|
|
42
|
+
* 'remove_users_from_team', team_id, user_ids: [...]}` with
|
|
43
|
+
* `user_ids` echoing the input argv order — Monday's wire is
|
|
44
|
+
* a single-shot bulk call (`remove_users_from_team(team_id,
|
|
45
|
+
* user_ids: [ID!]!)`), NOT a per-user fan-out like
|
|
46
|
+
* `monday workspace remove-users`. No preflight read fires;
|
|
47
|
+
* argv-derived. `meta.source: 'none'`.
|
|
48
|
+
*
|
|
49
|
+
* **Idempotent: yes** — Monday is no-op on a re-remove (the
|
|
50
|
+
* user already being out of the team surfaces in
|
|
51
|
+
* `successful_users[]` per Monday's wire convention).
|
|
52
|
+
*
|
|
53
|
+
* **Admin-permission-sensitive.** Non-admin callers surface
|
|
54
|
+
* `forbidden`.
|
|
55
|
+
*
|
|
56
|
+
* **Runtime body landed at v0.5-M34 IMPL.** Mirrors the
|
|
57
|
+
* `team-add-members` cadence verbatim modulo the `operation`
|
|
58
|
+
* literal — argv + `--users` parse → resolveClient → dry-run
|
|
59
|
+
* or live dispatch via {@link removeUsersFromTeam} → shared
|
|
60
|
+
* {@link projectMembershipResults} → `emitMutation`.
|
|
61
|
+
*/
|
|
62
|
+
import { z } from 'zod';
|
|
63
|
+
import { ensureSubcommand } from '../types.js';
|
|
64
|
+
import { parseArgv } from '../parse-argv.js';
|
|
65
|
+
import { emitDryRun, emitMutation } from '../emit.js';
|
|
66
|
+
import { resolveClient } from '../../api/resolve-client.js';
|
|
67
|
+
import { TeamIdSchema, UserIdSchema } from '../../types/ids.js';
|
|
68
|
+
import { parseBrandedListArg } from '../../utils/parse-brand-list.js';
|
|
69
|
+
import { removeUsersFromTeam, teamRemoveMembersOutputSchema, } from '../../api/teams.js';
|
|
70
|
+
import { projectMembershipResults } from './_team-membership.js';
|
|
71
|
+
const inputSchema = z
|
|
72
|
+
.object({
|
|
73
|
+
teamId: TeamIdSchema,
|
|
74
|
+
users: z.string().min(1, '--users must not be empty'),
|
|
75
|
+
})
|
|
76
|
+
.strict();
|
|
77
|
+
export const teamRemoveMembersCommand = {
|
|
78
|
+
name: 'user.team-remove-members',
|
|
79
|
+
summary: 'Remove users from a team (partial-success envelope)',
|
|
80
|
+
examples: [
|
|
81
|
+
'monday user team-remove-members 12345 --users 67890',
|
|
82
|
+
'monday user team-remove-members 12345 --users 67890,67891',
|
|
83
|
+
'monday user team-remove-members 12345 --users 67890 --dry-run --json',
|
|
84
|
+
],
|
|
85
|
+
// Re-removing an already-removed user is a no-op on Monday's
|
|
86
|
+
// wire; mark idempotent so agents can retry on transient
|
|
87
|
+
// failure.
|
|
88
|
+
idempotent: true,
|
|
89
|
+
inputSchema,
|
|
90
|
+
outputSchema: teamRemoveMembersOutputSchema,
|
|
91
|
+
attach: (program, ctx) => {
|
|
92
|
+
const noun = ensureSubcommand(program, 'user', 'User commands');
|
|
93
|
+
noun
|
|
94
|
+
.command('team-remove-members <teamId>')
|
|
95
|
+
.description(teamRemoveMembersCommand.summary)
|
|
96
|
+
.requiredOption('--users <list>', 'Comma-separated numeric user IDs to remove (maps to wire `user_ids: [ID!]!`).')
|
|
97
|
+
.addHelpText('after', [
|
|
98
|
+
'',
|
|
99
|
+
'Examples:',
|
|
100
|
+
...teamRemoveMembersCommand.examples.map((e) => ` ${e}`),
|
|
101
|
+
'',
|
|
102
|
+
'Notes:',
|
|
103
|
+
' - Envelope is per-cli-design §6.1 partial-success (`results: [{user_id, ok, ...}]`).',
|
|
104
|
+
' - Re-removing an absent member is a no-op (surfaces as `successful_users[]`).',
|
|
105
|
+
'',
|
|
106
|
+
].join('\n'))
|
|
107
|
+
.action(async (teamIdArg, opts) => {
|
|
108
|
+
const parsed = parseArgv(teamRemoveMembersCommand.inputSchema, {
|
|
109
|
+
teamId: teamIdArg,
|
|
110
|
+
...opts,
|
|
111
|
+
});
|
|
112
|
+
// Parse `--users` once at the boundary so a malformed
|
|
113
|
+
// user ID surfaces `usage_error` ahead of any wire call.
|
|
114
|
+
// Lifted helper at R-NEW-70 (consumer #4 post-lift).
|
|
115
|
+
const userIds = parseBrandedListArg(parsed.users, UserIdSchema, {
|
|
116
|
+
flagName: '--users',
|
|
117
|
+
entryDescription: 'numeric user ID',
|
|
118
|
+
hint: 'user IDs are numeric (e.g. 67890)',
|
|
119
|
+
emptyEntryHint: 'e.g. --users 67890,67891 — no leading, trailing, or ' +
|
|
120
|
+
'duplicate commas',
|
|
121
|
+
});
|
|
122
|
+
const { client, globalFlags, apiVersion } = resolveClient(ctx, program.opts());
|
|
123
|
+
if (globalFlags.dryRun) {
|
|
124
|
+
// Minimal dry-run shape per cli-design §6.4 — single
|
|
125
|
+
// planned operation echoing what the live wire call
|
|
126
|
+
// would send. No preflight read fires; `meta.source:
|
|
127
|
+
// 'none'`.
|
|
128
|
+
emitDryRun({
|
|
129
|
+
ctx,
|
|
130
|
+
programOpts: program.opts(),
|
|
131
|
+
plannedChanges: [
|
|
132
|
+
{
|
|
133
|
+
operation: 'remove_users_from_team',
|
|
134
|
+
team_id: parsed.teamId,
|
|
135
|
+
user_ids: [...userIds],
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
source: 'none',
|
|
139
|
+
cacheAgeSeconds: null,
|
|
140
|
+
warnings: [],
|
|
141
|
+
apiVersion,
|
|
142
|
+
});
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const result = await removeUsersFromTeam({
|
|
146
|
+
client,
|
|
147
|
+
teamId: parsed.teamId,
|
|
148
|
+
userIds,
|
|
149
|
+
});
|
|
150
|
+
const results = projectMembershipResults({
|
|
151
|
+
inputUserIds: userIds,
|
|
152
|
+
failedUsers: result.failedUsers,
|
|
153
|
+
successfulUsers: result.successfulUsers,
|
|
154
|
+
operation: 'remove_users_from_team',
|
|
155
|
+
teamId: parsed.teamId,
|
|
156
|
+
});
|
|
157
|
+
const data = {
|
|
158
|
+
operation: 'remove_users_from_team',
|
|
159
|
+
team_id: parsed.teamId,
|
|
160
|
+
results: [...results],
|
|
161
|
+
};
|
|
162
|
+
emitMutation({
|
|
163
|
+
ctx,
|
|
164
|
+
data,
|
|
165
|
+
schema: teamRemoveMembersCommand.outputSchema,
|
|
166
|
+
programOpts: program.opts(),
|
|
167
|
+
warnings: [],
|
|
168
|
+
source: result.source,
|
|
169
|
+
cacheAgeSeconds: result.cacheAgeSeconds,
|
|
170
|
+
complexity: result.complexity,
|
|
171
|
+
apiVersion,
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
//# sourceMappingURL=team-remove-members.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team-remove-members.js","sourceRoot":"","sources":["../../../src/commands/user/team-remove-members.ts"],"names":[],"mappings":"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"}
|
package/dist/types/ids.d.ts
CHANGED
|
@@ -28,8 +28,11 @@ export declare const WorkspaceIdSchema: z.core.$ZodBranded<z.ZodString, "Workspa
|
|
|
28
28
|
export declare const UpdateIdSchema: z.core.$ZodBranded<z.ZodString, "UpdateId", "out">;
|
|
29
29
|
export declare const WebhookIdSchema: z.core.$ZodBranded<z.ZodString, "WebhookId", "out">;
|
|
30
30
|
export declare const DocIdSchema: z.core.$ZodBranded<z.ZodString, "DocId", "out">;
|
|
31
|
+
export declare const TeamIdSchema: z.core.$ZodBranded<z.ZodString, "TeamId", "out">;
|
|
32
|
+
export declare const DocFolderIdSchema: z.core.$ZodBranded<z.ZodString, "DocFolderId", "out">;
|
|
31
33
|
export declare const ColumnIdSchema: z.core.$ZodBranded<z.ZodString, "ColumnId", "out">;
|
|
32
34
|
export declare const GroupIdSchema: z.core.$ZodBranded<z.ZodString, "GroupId", "out">;
|
|
35
|
+
export declare const DocBlockIdSchema: z.core.$ZodBranded<z.ZodString, "DocBlockId", "out">;
|
|
33
36
|
export type BoardId = z.infer<typeof BoardIdSchema>;
|
|
34
37
|
export type ItemId = z.infer<typeof ItemIdSchema>;
|
|
35
38
|
export type ColumnId = z.infer<typeof ColumnIdSchema>;
|
|
@@ -39,4 +42,7 @@ export type WorkspaceId = z.infer<typeof WorkspaceIdSchema>;
|
|
|
39
42
|
export type UpdateId = z.infer<typeof UpdateIdSchema>;
|
|
40
43
|
export type WebhookId = z.infer<typeof WebhookIdSchema>;
|
|
41
44
|
export type DocId = z.infer<typeof DocIdSchema>;
|
|
45
|
+
export type TeamId = z.infer<typeof TeamIdSchema>;
|
|
46
|
+
export type DocFolderId = z.infer<typeof DocFolderIdSchema>;
|
|
47
|
+
export type DocBlockId = z.infer<typeof DocBlockIdSchema>;
|
|
42
48
|
//# sourceMappingURL=ids.d.ts.map
|
package/dist/types/ids.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../src/types/ids.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../src/types/ids.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiCxB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,uBAAuB,QAAoB,CAAC;AAEzD,eAAO,MAAM,aAAa,mDAAqC,CAAC;AAChE,eAAO,MAAM,YAAY,kDAAoC,CAAC;AAC9D,eAAO,MAAM,YAAY,kDAAoC,CAAC;AAC9D,eAAO,MAAM,iBAAiB,uDAAyC,CAAC;AACxE,eAAO,MAAM,cAAc,oDAAsC,CAAC;AAClE,eAAO,MAAM,eAAe,qDAAuC,CAAC;AAOpE,eAAO,MAAM,WAAW,iDAAmC,CAAC;AAQ5D,eAAO,MAAM,YAAY,kDAAoC,CAAC;AAS9D,eAAO,MAAM,iBAAiB,uDAAyC,CAAC;AAIxE,eAAO,MAAM,cAAc,oDAAmC,CAAC;AAC/D,eAAO,MAAM,aAAa,mDAAkC,CAAC;AAc7D,eAAO,MAAM,gBAAgB,sDAAqC,CAAC;AAEnE,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AACpD,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAClD,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AACtD,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AACpD,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAClD,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC5D,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AACtD,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AACxD,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAChD,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAClD,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC5D,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|
package/dist/types/ids.js
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
/**
|
|
3
|
-
* Branded zod schemas for the
|
|
4
|
-
* adds `
|
|
3
|
+
* Branded zod schemas for the twelve ID kinds Monday surfaces
|
|
4
|
+
* (v0.5-M36 adds `DocBlockId` alongside the v0.5-M35 `DocFolderId`,
|
|
5
|
+
* the v0.5-M34 `TeamId`, and nine v0.1+M27+M32 brands). Brands make
|
|
5
6
|
* `BoardId`/`ItemId`/etc. nominally distinct at the type level even
|
|
6
|
-
* though
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* though most are numeric strings on the wire — passing a `BoardId`
|
|
8
|
+
* where an `ItemId` is wanted becomes a compile error, which is the
|
|
9
|
+
* whole point.
|
|
9
10
|
*
|
|
10
11
|
* Monday's numeric IDs exceed `Number.MAX_SAFE_INTEGER` for older
|
|
11
12
|
* accounts, so we keep them as decimal strings everywhere — argv
|
|
12
13
|
* already arrives as strings, GraphQL responses already arrive as
|
|
13
14
|
* strings, and the wire format stays stable through the CLI.
|
|
15
|
+
*
|
|
16
|
+
* Three ID kinds are non-numeric (opaque string IDs): `ColumnId`,
|
|
17
|
+
* `GroupId`, and `DocBlockId`. These use a non-empty-string base
|
|
18
|
+
* regex (`slugIdSchema`) rather than the numeric `^\d+$` regex.
|
|
19
|
+
* Column IDs and group IDs are lower-snake-case slugs (`status_4`,
|
|
20
|
+
* `topics`); doc-block IDs are opaque strings minted by Monday's
|
|
21
|
+
* workdocs surface — pin only "non-empty" at the brand boundary so
|
|
22
|
+
* the type system catches absence without mis-matching Monday's
|
|
23
|
+
* actual character class (UUID, base62, or any other future shape).
|
|
14
24
|
*/
|
|
15
25
|
const numericIdSchema = z
|
|
16
26
|
.string()
|
|
@@ -53,8 +63,39 @@ export const WebhookIdSchema = numericIdSchema.brand();
|
|
|
53
63
|
// `BoardId`/`ItemId`/etc. so the type system catches an `--workspace
|
|
54
64
|
// <did>` slip at compile time.
|
|
55
65
|
export const DocIdSchema = numericIdSchema.brand();
|
|
66
|
+
// M34 (v0.5) — team writer surface (`monday user team-*`). Monday's
|
|
67
|
+
// `Team.id` is `ID!` on the wire (numeric on every observed account
|
|
68
|
+
// at API `2026-01`; empirical probe at `scripts/probe/v0.5-team-
|
|
69
|
+
// mutations.ts` 2026-05-15). Brand keeps `TeamId` distinct from
|
|
70
|
+
// `UserId` so the type system catches a `--users <tid>` slip at
|
|
71
|
+
// compile time (an easy mistake to make against `team-add-members
|
|
72
|
+
// <tid> --users <id,...>` where both args are numeric).
|
|
73
|
+
export const TeamIdSchema = numericIdSchema.brand();
|
|
74
|
+
// M35 (v0.5) — doc-level CRUD surface (`monday doc create-in-
|
|
75
|
+
// workspace --folder <fid>`). Monday's `Document.doc_folder_id` is
|
|
76
|
+
// `ID, nullable` on the wire (M32 probe) + `CreateDocWorkspaceInput.
|
|
77
|
+
// folder_id` accepts the same `ID` shape. Brand keeps `DocFolderId`
|
|
78
|
+
// distinct from `WorkspaceId` so the type system catches a slip at
|
|
79
|
+
// the `create-in-workspace --workspace <wid> --folder <fid>` call
|
|
80
|
+
// site at compile time (both args are numeric IDs but address
|
|
81
|
+
// distinct Monday entities — workspace vs nested-folder).
|
|
82
|
+
export const DocFolderIdSchema = numericIdSchema.brand();
|
|
56
83
|
// Column and group IDs are stable lower-snake-case slugs ("status_4",
|
|
57
84
|
// "topics") — not numeric. Validate as non-empty strings only.
|
|
58
85
|
export const ColumnIdSchema = slugIdSchema.brand();
|
|
59
86
|
export const GroupIdSchema = slugIdSchema.brand();
|
|
87
|
+
// M36 (v0.5) — per-block CRUD surface (`monday doc block-create` /
|
|
88
|
+
// `block-update` / `block-delete`). Monday's `DocumentBlock.id` is
|
|
89
|
+
// `String!` on the wire (NOT `ID` — empirical probe at
|
|
90
|
+
// `scripts/probe/v0.5-doc-mutations.ts` + `v0.5-inputs-and-results.ts`
|
|
91
|
+
// 2026-05-15; the same `String!` shape applies to the `block_id`
|
|
92
|
+
// arg slot on `update_doc_block` / `delete_doc_block` and to
|
|
93
|
+
// `DocumentBlockIdOnly.id`). The block IDs Monday mints are opaque
|
|
94
|
+
// — UUID-shaped in practice today, but the wire contract pins only
|
|
95
|
+
// "non-empty string", so the brand uses `slugIdSchema` like
|
|
96
|
+
// `ColumnId` / `GroupId` rather than the numeric `^\d+$` regex.
|
|
97
|
+
// Distinct from `DocId` (numeric — wire `ID!`) so the type system
|
|
98
|
+
// catches a `--parent <did>` slip at compile time (e.g.
|
|
99
|
+
// `block-create <docId> --parent <bidNotDid>`).
|
|
100
|
+
export const DocBlockIdSchema = slugIdSchema.brand();
|
|
60
101
|
//# sourceMappingURL=ids.js.map
|
package/dist/types/ids.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ids.js","sourceRoot":"","sources":["../../src/types/ids.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB
|
|
1
|
+
{"version":3,"file":"ids.js","sourceRoot":"","sources":["../../src/types/ids.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,eAAe,GAAG,CAAC;KACtB,MAAM,EAAE;KACR,KAAK,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC,CAAC;AAEzD,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE;IACrC,OAAO,EAAE,yBAAyB;CACnC,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,iBAAiB,CAAC;AAEzD,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC,KAAK,EAAa,CAAC;AAChE,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC,KAAK,EAAY,CAAC;AAC9D,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC,KAAK,EAAY,CAAC;AAC9D,MAAM,CAAC,MAAM,iBAAiB,GAAG,eAAe,CAAC,KAAK,EAAiB,CAAC;AACxE,MAAM,CAAC,MAAM,cAAc,GAAG,eAAe,CAAC,KAAK,EAAc,CAAC;AAClE,MAAM,CAAC,MAAM,eAAe,GAAG,eAAe,CAAC,KAAK,EAAe,CAAC;AACpE,uEAAuE;AACvE,gEAAgE;AAChE,mEAAmE;AACnE,0DAA0D;AAC1D,qEAAqE;AACrE,+BAA+B;AAC/B,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAC,KAAK,EAAW,CAAC;AAC5D,oEAAoE;AACpE,oEAAoE;AACpE,iEAAiE;AACjE,gEAAgE;AAChE,gEAAgE;AAChE,kEAAkE;AAClE,wDAAwD;AACxD,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC,KAAK,EAAY,CAAC;AAC9D,8DAA8D;AAC9D,mEAAmE;AACnE,qEAAqE;AACrE,oEAAoE;AACpE,mEAAmE;AACnE,kEAAkE;AAClE,8DAA8D;AAC9D,0DAA0D;AAC1D,MAAM,CAAC,MAAM,iBAAiB,GAAG,eAAe,CAAC,KAAK,EAAiB,CAAC;AAExE,sEAAsE;AACtE,+DAA+D;AAC/D,MAAM,CAAC,MAAM,cAAc,GAAG,YAAY,CAAC,KAAK,EAAc,CAAC;AAC/D,MAAM,CAAC,MAAM,aAAa,GAAG,YAAY,CAAC,KAAK,EAAa,CAAC;AAC7D,mEAAmE;AACnE,mEAAmE;AACnE,uDAAuD;AACvD,uEAAuE;AACvE,iEAAiE;AACjE,6DAA6D;AAC7D,mEAAmE;AACnE,mEAAmE;AACnE,4DAA4D;AAC5D,gEAAgE;AAChE,kEAAkE;AAClE,wDAAwD;AACxD,gDAAgD;AAChD,MAAM,CAAC,MAAM,gBAAgB,GAAG,YAAY,CAAC,KAAK,EAAgB,CAAC"}
|
|
@@ -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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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comma-separated branded-ID list argv parser (R-NEW-70 lift,
|
|
3
|
+
* v0.5-M34 pre-flight kickoff — ahead-of-feat per R-NEW-29 M25
|
|
4
|
+
* cadence; `v0.5-plan.md` §22 R-NEW-70 entry).
|
|
5
|
+
*
|
|
6
|
+
* Three sites in `src/commands/*` consume a `--<flag> <id,...>`
|
|
7
|
+
* comma-separated list of branded IDs at the argv-parse boundary +
|
|
8
|
+
* reject malformed entries as `usage_error`:
|
|
9
|
+
*
|
|
10
|
+
* - `monday workspace add-users` / `workspace remove-users` /
|
|
11
|
+
* `board add-users` already use the mixed numeric-or-email
|
|
12
|
+
* {@link parseUsersArg} helper (different shape — accepts
|
|
13
|
+
* emails + numeric IDs; NOT a consumer of this helper).
|
|
14
|
+
* - `monday doc list --workspace <wid,...>`
|
|
15
|
+
* (`src/commands/doc/list.ts`, M32 pre-flight — 1st pure-brand-list
|
|
16
|
+
* consumer; pre-lift `parseWorkspaceListArg` inline copy).
|
|
17
|
+
* - `monday user team-create --users <id,...>` / `team-add-members
|
|
18
|
+
* <tid> --users <id,...>` / `team-remove-members <tid> --users
|
|
19
|
+
* <id,...>` (`src/commands/user/team-*.ts`, M34 pre-flight —
|
|
20
|
+
* 3rd-5th consumers; would each duplicate the ~35-line inline
|
|
21
|
+
* pattern without this lift).
|
|
22
|
+
*
|
|
23
|
+
* Each pre-lift site shared the outer-split + trim + empty-entry +
|
|
24
|
+
* per-entry brand-validation outline; the per-call sites carried
|
|
25
|
+
* verb-specific error-message strings + detail keys + hint text. M34
|
|
26
|
+
* pre-flight pushed the count to 4 (1 migrated + 3 new), crossing the
|
|
27
|
+
* R7/R8 3-consumer threshold; this lift ships AHEAD of the M34
|
|
28
|
+
* pre-flight feat commit mirroring R-NEW-29's M25 cadence (lift
|
|
29
|
+
* commit lands first, feat commit consumes the lifted helper, Codex
|
|
30
|
+
* review then sees the lifted shape).
|
|
31
|
+
*
|
|
32
|
+
* **Why this is NOT folded into {@link parseUsersArg}.** The mixed
|
|
33
|
+
* numeric-or-email shape inflates the helper's signature with an
|
|
34
|
+
* `email_kind` discriminator + a directory-cache leg's source-
|
|
35
|
+
* aggregator coupling that's load-bearing in the fan-out path but
|
|
36
|
+
* irrelevant to a pure brand-list parse. Keeping the two shapes as
|
|
37
|
+
* sibling helpers keeps the pure-brand-list call sites readable and
|
|
38
|
+
* the mixed-mode fan-out call sites tightly typed.
|
|
39
|
+
*/
|
|
40
|
+
import type { z } from 'zod';
|
|
41
|
+
export interface ParseBrandedListArgOptions {
|
|
42
|
+
/**
|
|
43
|
+
* The argv flag name including the leading dashes (e.g.
|
|
44
|
+
* `'--workspace'`, `'--users'`). Surfaces verbatim into
|
|
45
|
+
* `error.message` so the agent sees which flag rejected.
|
|
46
|
+
*/
|
|
47
|
+
readonly flagName: string;
|
|
48
|
+
/**
|
|
49
|
+
* Short noun phrase describing one entry (e.g. `'numeric workspace
|
|
50
|
+
* ID'`, `'numeric user ID'`). Surfaces into the per-entry
|
|
51
|
+
* rejection message: `"--<flag> entry "<token>" is not a
|
|
52
|
+
* <entryDescription>"`.
|
|
53
|
+
*/
|
|
54
|
+
readonly entryDescription: string;
|
|
55
|
+
/**
|
|
56
|
+
* Free-form hint surfaced into `error.details.hint` on per-entry
|
|
57
|
+
* brand-validation failure (e.g. `'workspace IDs are numeric
|
|
58
|
+
* (e.g. 12345)'`). Same hint used for both the empty-entry
|
|
59
|
+
* rejection (where it falls back to a generic "no leading,
|
|
60
|
+
* trailing, or duplicate commas" message if {@link emptyEntryHint}
|
|
61
|
+
* is unset) and the per-entry rejection.
|
|
62
|
+
*/
|
|
63
|
+
readonly hint: string;
|
|
64
|
+
/**
|
|
65
|
+
* Optional hint override for the empty-entry rejection path
|
|
66
|
+
* (trailing comma / leading comma / double comma). Defaults to
|
|
67
|
+
* `"e.g. ${flagName} <id-or-token>,<id-or-token> — no leading,
|
|
68
|
+
* trailing, or duplicate commas"` when unset, which matches the
|
|
69
|
+
* pre-lift `parseWorkspaceListArg` shape verbatim.
|
|
70
|
+
*/
|
|
71
|
+
readonly emptyEntryHint?: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Splits a comma-separated argv string into an array of brand-
|
|
75
|
+
* validated typed IDs. Whitespace around commas is trimmed. Empty
|
|
76
|
+
* entries (trailing comma, leading comma, double comma) reject
|
|
77
|
+
* with `usage_error.details.hint`; non-conforming entries reject
|
|
78
|
+
* via the supplied zod brand schema (`details.issues[]` carries
|
|
79
|
+
* the per-issue path/message + `details.argv_value` echoes the
|
|
80
|
+
* raw input so agents can correlate retries against the original
|
|
81
|
+
* argv).
|
|
82
|
+
*
|
|
83
|
+
* The brand-validation step uses `safeParse` so the zod error
|
|
84
|
+
* doesn't bubble through the runner's catch-all as
|
|
85
|
+
* `internal_error` (per `validation.md`'s "Never bubble raw
|
|
86
|
+
* ZodError out of a parse boundary" rule). Caller wraps with the
|
|
87
|
+
* verb's outer `parseArgv` for the rest of the argv surface.
|
|
88
|
+
*
|
|
89
|
+
* Empty input strings (`raw === ''`) reject at the per-verb input
|
|
90
|
+
* schema's `.min(1)` check BEFORE reaching this helper — the
|
|
91
|
+
* helper assumes a non-empty raw string. The empty-`raw` defensive
|
|
92
|
+
* branch is c8-ignored as unreachable from production.
|
|
93
|
+
*/
|
|
94
|
+
export declare const parseBrandedListArg: <T>(raw: string, brandSchema: z.ZodType<T>, options: ParseBrandedListArgOptions) => readonly T[];
|
|
95
|
+
//# sourceMappingURL=parse-brand-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-brand-list.d.ts","sourceRoot":"","sources":["../../src/utils/parse-brand-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAG7B,MAAM,WAAW,0BAA0B;IACzC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,EACnC,KAAK,MAAM,EACX,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EACzB,SAAS,0BAA0B,KAClC,SAAS,CAAC,EAwCZ,CAAC"}
|