diff --git a/package.json b/package.json index 7f4451e..06f7d54 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ }, "bugs": "https://github.com/hackmdio/hackmd-cli/issues", "dependencies": { - "@hackmd/api": "^2.5.0", + "@hackmd/api": "2.5.0-beta.20260430192828.99a5117", "@hackmd/oclif-plugin-autocomplete": "^2.1.9-fish", "@oclif/core": "2.8.2", "@oclif/plugin-help": "5.2.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4d5899c..0cde4ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@hackmd/api': - specifier: ^2.5.0 - version: 2.5.0 + specifier: 2.5.0-beta.20260430192828.99a5117 + version: 2.5.0-beta.20260430192828.99a5117 '@hackmd/oclif-plugin-autocomplete': specifier: ^2.1.9-fish version: 2.1.9-fish(@types/node@24.10.8)(typescript@5.9.3) @@ -219,8 +219,8 @@ packages: '@gar/promisify@1.1.3': resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} - '@hackmd/api@2.5.0': - resolution: {integrity: sha512-eG4COWt2odRwiUHgJc3vP73iUS2vuAN1ECpPbKF0kRNJt6YerlsAsf3TNBs/CfVuemsC3g7JeAZpIrT7gmsTRQ==} + '@hackmd/api@2.5.0-beta.20260430192828.99a5117': + resolution: {integrity: sha512-JhAxOqIU6L/Hc6QWw8oq/rYwNzkxKC0J0XBHu+N/zqJezvKSFhdSysCbhKsLuJpBXG5LsVM7ZpnLHuN3f8EOJA==} '@hackmd/oclif-plugin-autocomplete@2.1.9-fish': resolution: {integrity: sha512-wLiROF31bABA+Q9+blweBfythR6HuUANsdYql+MJNb+R1Z6/JJvyHvtAfA6u+mcT4quLJeU5xZ0timWgfDkhlw==} @@ -3979,7 +3979,7 @@ snapshots: '@gar/promisify@1.1.3': {} - '@hackmd/api@2.5.0': + '@hackmd/api@2.5.0-beta.20260430192828.99a5117': dependencies: axios: 1.13.2 tslib: 1.14.1 @@ -5594,7 +5594,7 @@ snapshots: eslint-config-xo: 0.49.0(eslint@9.39.2) eslint-config-xo-space: 0.35.0(eslint@9.39.2) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2))(eslint@9.39.2) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2))(eslint@9.39.2))(eslint@9.39.2) eslint-plugin-jsdoc: 50.8.0(eslint@9.39.2) eslint-plugin-mocha: 10.5.0(eslint@9.39.2) eslint-plugin-n: 17.23.2(eslint@9.39.2)(typescript@5.9.3) @@ -5646,7 +5646,7 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2))(eslint@9.39.2))(eslint@9.39.2) transitivePeerDependencies: - supports-color @@ -5674,7 +5674,7 @@ snapshots: eslint-utils: 2.1.0 regexpp: 3.2.0 - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2))(eslint@9.39.2))(eslint@9.39.2): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 diff --git a/src/commands/folders/create.ts b/src/commands/folders/create.ts new file mode 100644 index 0000000..f1aeb76 --- /dev/null +++ b/src/commands/folders/create.ts @@ -0,0 +1,67 @@ + +import type {CreateUserFolderBody} from '@hackmd/api' + +import {Flags, ux} from '@oclif/core' + +import HackMDCommand from '../../command' +import { + folderColor, + folderDescription, + folderIcon, + folderName, + parentFolderId, +} from '../../flags' + +export default class Create extends HackMDCommand { + static description = 'Create a folder' + static flags = { + color: folderColor, + description: folderDescription, + help: Flags.help({char: 'h'}), + icon: folderIcon, + name: folderName, + parentFolderId, + ...ux.table.flags(), + } + + async run() { + const {flags} = await this.parse(Create) + const {color, description, icon, name, parentFolderId} = flags + + if (!name) { + this.error('Flag name could not be empty') + } + + const payload: CreateUserFolderBody = { + color, + description, + icon, + name, + parentFolderId, + } + + try { + const APIClient = await this.getAPIClient() + const folder = await APIClient.createFolder(payload) + + ux.table([folder], { + color: {}, + description: {}, + icon: {}, + id: { + header: 'ID', + }, + name: {}, + parentFolderId: { + header: 'Parent Folder ID', + }, + }, { + printLine: this.log.bind(this), + ...flags, + }) + } catch (error) { + this.log('Create folder failed') + this.error(error as Error) + } + } +} diff --git a/src/commands/folders/delete.ts b/src/commands/folders/delete.ts new file mode 100644 index 0000000..1df1a3a --- /dev/null +++ b/src/commands/folders/delete.ts @@ -0,0 +1,29 @@ +import {Flags} from '@oclif/core' + +import HackMDCommand from '../../command' +import {folderId} from '../../flags' + +export default class Delete extends HackMDCommand { + static description = 'Delete a folder' + static flags = { + folderId, + help: Flags.help({char: 'h'}), + } + + async run() { + const {flags} = await this.parse(Delete) + const {folderId} = flags + + if (!folderId) { + this.error('Flag folderId could not be empty') + } + + try { + const APIClient = await this.getAPIClient() + await APIClient.deleteFolder(folderId) + } catch (error) { + this.log('Delete folder failed') + this.error(error as Error) + } + } +} diff --git a/src/commands/folders/index.ts b/src/commands/folders/index.ts new file mode 100644 index 0000000..578322a --- /dev/null +++ b/src/commands/folders/index.ts @@ -0,0 +1,41 @@ +import {Flags, ux} from '@oclif/core' + +import HackMDCommand from '../../command' +import {folderId} from '../../flags' + +export default class IndexCommand extends HackMDCommand { + static description = 'HackMD folders commands' + static flags = { + folderId, + help: Flags.help({char: 'h'}), + ...ux.table.flags(), + } + + async run() { + const {flags} = await this.parse(IndexCommand) + + try { + const APIClient = await this.getAPIClient() + const folders = flags.folderId ? [await APIClient.getFolder(flags.folderId)] : await APIClient.getFolderList() + + ux.table(folders, { + color: {}, + description: {}, + icon: {}, + id: { + header: 'ID', + }, + name: {}, + parentFolderId: { + header: 'Parent Folder ID', + }, + }, { + printLine: this.log.bind(this), + ...flags, + }) + } catch (error) { + this.log('Fetch folders failed') + this.error(error as Error) + } + } +} diff --git a/src/commands/folders/order.ts b/src/commands/folders/order.ts new file mode 100644 index 0000000..66caf52 --- /dev/null +++ b/src/commands/folders/order.ts @@ -0,0 +1,35 @@ +import {Flags} from '@oclif/core' + +import HackMDCommand from '../../command' +import {folderOrder} from '../../flags' +import {parseFolderOrder} from '../../utils' + +export default class Order extends HackMDCommand { + static description = 'Get or update folder order' + static flags = { + help: Flags.help({char: 'h'}), + order: folderOrder, + } + + async run() { + const {flags} = await this.parse(Order) + + try { + const APIClient = await this.getAPIClient() + + if (flags.order) { + await APIClient.updateFolderOrder({ + order: parseFolderOrder(flags.order), + }) + this.log('Folder order updated') + return + } + + const order = await APIClient.getFolderOrder() + this.log(JSON.stringify(order, null, 2)) + } catch (error) { + this.log('Update folder order failed') + this.error(error as Error) + } + } +} diff --git a/src/commands/folders/update.ts b/src/commands/folders/update.ts new file mode 100644 index 0000000..6b040c7 --- /dev/null +++ b/src/commands/folders/update.ts @@ -0,0 +1,68 @@ +import type {UpdateUserFolderBody} from '@hackmd/api' + +import {Flags, ux} from '@oclif/core' + +import HackMDCommand from '../../command' +import { + folderColor, + folderDescription, + folderIcon, + folderId, + folderName, + parentFolderId, +} from '../../flags' + +export default class Update extends HackMDCommand { + static description = 'Update folder' + static flags = { + color: folderColor, + description: folderDescription, + folderId, + help: Flags.help({char: 'h'}), + icon: folderIcon, + name: folderName, + parentFolderId, + ...ux.table.flags(), + } + + async run() { + const {flags} = await this.parse(Update) + const {color, description, folderId, icon, name, parentFolderId} = flags + + if (!folderId) { + this.error('Flag folderId could not be empty') + } + + const payload: UpdateUserFolderBody = { + color, + description, + icon, + name, + parentFolderId, + } + + try { + const APIClient = await this.getAPIClient() + const folder = await APIClient.updateFolder(folderId, payload) + + ux.table([folder], { + color: {}, + description: {}, + icon: {}, + id: { + header: 'ID', + }, + name: {}, + parentFolderId: { + header: 'Parent Folder ID', + }, + }, { + printLine: this.log.bind(this), + ...flags, + }) + } catch (error) { + this.log('Update folder failed') + this.error(error as Error) + } + } +} diff --git a/src/commands/notes/create.ts b/src/commands/notes/create.ts index f8f3ac9..a44246d 100644 --- a/src/commands/notes/create.ts +++ b/src/commands/notes/create.ts @@ -1,8 +1,9 @@ -import { +import type { CommentPermissionType, CreateNoteOptions, NotePermissionRole, -} from '@hackmd/api/dist/type' +} from '@hackmd/api' + import {Flags, ux} from '@oclif/core' import * as fs from 'node:fs' @@ -13,6 +14,7 @@ import { noteContent, notePermission, noteTitle, + parentFolderId, } from '../../flags' import {openEditor} from '../../open-editor' import {safeStdinRead, temporaryMD} from '../../utils' @@ -34,6 +36,7 @@ raUuSTetT5uQbqQfLnz9lA A new note gvfz2UB5THiKABQJQnLs6Q content: noteContent, editor, help: Flags.help({char: 'h'}), + parentFolderId, readPermission: notePermission, title: noteTitle, writePermission: notePermission, @@ -47,6 +50,7 @@ raUuSTetT5uQbqQfLnz9lA A new note gvfz2UB5THiKABQJQnLs6Q const options: CreateNoteOptions = { commentPermission: flags.commentPermission as CommentPermissionType, content: pipeString || flags.content, + parentFolderId: flags.parentFolderId, readPermission: flags.readPermission as NotePermissionRole, title: flags.title, writePermission: flags.writePermission as NotePermissionRole, diff --git a/src/commands/notes/update.ts b/src/commands/notes/update.ts index 5339507..c4b7d15 100644 --- a/src/commands/notes/update.ts +++ b/src/commands/notes/update.ts @@ -1,7 +1,9 @@ +import type {UpdateNoteOptions} from '@hackmd/api' + import {Flags} from '@oclif/core' import HackMDCommand from '../../command' -import {noteContent, noteId} from '../../flags' +import {noteContent, noteId, parentFolderId} from '../../flags' export default class Update extends HackMDCommand { static description = 'Update note content' @@ -12,19 +14,25 @@ export default class Update extends HackMDCommand { content: noteContent, help: Flags.help({char: 'h'}), noteId, + parentFolderId, } async run() { const {flags} = await this.parse(Update) - const {content, noteId} = flags + const {content, noteId, parentFolderId} = flags if (!noteId) { this.error('Flag noteId could not be empty') } + const payload: UpdateNoteOptions = { + content, + parentFolderId, + } + try { const APIClient = await this.getAPIClient() - await APIClient.updateNoteContent(noteId, content) + await APIClient.updateNote(noteId, payload) } catch (error) { this.log('Update note content failed') this.error(error as Error) diff --git a/src/commands/team-folders/create.ts b/src/commands/team-folders/create.ts new file mode 100644 index 0000000..b400c5b --- /dev/null +++ b/src/commands/team-folders/create.ts @@ -0,0 +1,72 @@ +import type {CreateTeamFolderBody} from '@hackmd/api' + +import {Flags, ux} from '@oclif/core' + +import HackMDCommand from '../../command' +import { + folderColor, + folderDescription, + folderIcon, + folderName, + parentFolderId, + teamPath, +} from '../../flags' + +export default class Create extends HackMDCommand { + static description = 'Create a team folder' + static flags = { + color: folderColor, + description: folderDescription, + help: Flags.help({char: 'h'}), + icon: folderIcon, + name: folderName, + parentFolderId, + teamPath, + ...ux.table.flags(), + } + + async run() { + const {flags} = await this.parse(Create) + const {color, description, icon, name, parentFolderId, teamPath} = flags + + if (!teamPath) { + this.error('Flag teamPath could not be empty') + } + + if (!name) { + this.error('Flag name could not be empty') + } + + const payload: CreateTeamFolderBody = { + color, + description, + icon, + name, + parentFolderId, + } + + try { + const APIClient = await this.getAPIClient() + const folder = await APIClient.createTeamFolder(teamPath, payload) + + ux.table([folder], { + color: {}, + description: {}, + icon: {}, + id: { + header: 'ID', + }, + name: {}, + parentFolderId: { + header: 'Parent Folder ID', + }, + }, { + printLine: this.log.bind(this), + ...flags, + }) + } catch (error) { + this.log('Create team folder failed') + this.error(error as Error) + } + } +} diff --git a/src/commands/team-folders/delete.ts b/src/commands/team-folders/delete.ts new file mode 100644 index 0000000..045137c --- /dev/null +++ b/src/commands/team-folders/delete.ts @@ -0,0 +1,34 @@ +import {Flags} from '@oclif/core' + +import HackMDCommand from '../../command' +import {folderId, teamPath} from '../../flags' + +export default class Delete extends HackMDCommand { + static description = 'Delete a team folder' + static flags = { + folderId, + help: Flags.help({char: 'h'}), + teamPath, + } + + async run() { + const {flags} = await this.parse(Delete) + const {folderId, teamPath} = flags + + if (!teamPath) { + this.error('Flag teamPath could not be empty') + } + + if (!folderId) { + this.error('Flag folderId could not be empty') + } + + try { + const APIClient = await this.getAPIClient() + await APIClient.deleteTeamFolder(teamPath, folderId) + } catch (error) { + this.log('Delete team folder failed') + this.error(error as Error) + } + } +} diff --git a/src/commands/team-folders/index.ts b/src/commands/team-folders/index.ts new file mode 100644 index 0000000..e7f246d --- /dev/null +++ b/src/commands/team-folders/index.ts @@ -0,0 +1,46 @@ +import {Flags, ux} from '@oclif/core' + +import HackMDCommand from '../../command' +import {folderId, teamPath} from '../../flags' + +export default class IndexCommand extends HackMDCommand { + static description = 'HackMD team folders commands' + static flags = { + folderId, + help: Flags.help({char: 'h'}), + teamPath, + ...ux.table.flags(), + } + + async run() { + const {flags} = await this.parse(IndexCommand) + + if (!flags.teamPath) { + this.error('Flag teamPath could not be empty') + } + + try { + const APIClient = await this.getAPIClient() + const folders = flags.folderId ? [await APIClient.getTeamFolder(flags.teamPath, flags.folderId)] : await APIClient.getTeamFolderList(flags.teamPath) + + ux.table(folders, { + color: {}, + description: {}, + icon: {}, + id: { + header: 'ID', + }, + name: {}, + parentFolderId: { + header: 'Parent Folder ID', + }, + }, { + printLine: this.log.bind(this), + ...flags, + }) + } catch (error) { + this.log('Fetch team folders failed') + this.error(error as Error) + } + } +} diff --git a/src/commands/team-folders/order.ts b/src/commands/team-folders/order.ts new file mode 100644 index 0000000..769e5d8 --- /dev/null +++ b/src/commands/team-folders/order.ts @@ -0,0 +1,40 @@ +import {Flags} from '@oclif/core' + +import HackMDCommand from '../../command' +import {folderOrder, teamPath} from '../../flags' +import {parseFolderOrder} from '../../utils' + +export default class Order extends HackMDCommand { + static description = 'Get or update team folder order' + static flags = { + help: Flags.help({char: 'h'}), + order: folderOrder, + teamPath, + } + + async run() { + const {flags} = await this.parse(Order) + + if (!flags.teamPath) { + this.error('Flag teamPath could not be empty') + } + + try { + const APIClient = await this.getAPIClient() + + if (flags.order) { + await APIClient.updateTeamFolderOrder(flags.teamPath, { + order: parseFolderOrder(flags.order), + }) + this.log('Team folder order updated') + return + } + + const order = await APIClient.getTeamFolderOrder(flags.teamPath) + this.log(JSON.stringify(order, null, 2)) + } catch (error) { + this.log('Update team folder order failed') + this.error(error as Error) + } + } +} diff --git a/src/commands/team-folders/update.ts b/src/commands/team-folders/update.ts new file mode 100644 index 0000000..e02ce66 --- /dev/null +++ b/src/commands/team-folders/update.ts @@ -0,0 +1,74 @@ +import type {UpdateTeamFolderBody} from '@hackmd/api' + +import {Flags, ux} from '@oclif/core' + +import HackMDCommand from '../../command' +import { + folderColor, + folderDescription, + folderIcon, + folderId, + folderName, + parentFolderId, + teamPath, +} from '../../flags' + +export default class Update extends HackMDCommand { + static description = 'Update team folder' + static flags = { + color: folderColor, + description: folderDescription, + folderId, + help: Flags.help({char: 'h'}), + icon: folderIcon, + name: folderName, + parentFolderId, + teamPath, + ...ux.table.flags(), + } + + async run() { + const {flags} = await this.parse(Update) + const {color, description, folderId, icon, name, parentFolderId, teamPath} = flags + + if (!teamPath) { + this.error('Flag teamPath could not be empty') + } + + if (!folderId) { + this.error('Flag folderId could not be empty') + } + + const payload: UpdateTeamFolderBody = { + color, + description, + icon, + name, + parentFolderId, + } + + try { + const APIClient = await this.getAPIClient() + const folder = await APIClient.updateTeamFolder(teamPath, folderId, payload) + + ux.table([folder], { + color: {}, + description: {}, + icon: {}, + id: { + header: 'ID', + }, + name: {}, + parentFolderId: { + header: 'Parent Folder ID', + }, + }, { + printLine: this.log.bind(this), + ...flags, + }) + } catch (error) { + this.log('Update team folder failed') + this.error(error as Error) + } + } +} diff --git a/src/commands/team-notes/create.ts b/src/commands/team-notes/create.ts index 137ac4b..d1f7853 100644 --- a/src/commands/team-notes/create.ts +++ b/src/commands/team-notes/create.ts @@ -1,10 +1,11 @@ -import {CommentPermissionType, CreateNoteOptions, NotePermissionRole} from '@hackmd/api/dist/type' +import type {CommentPermissionType, CreateNoteOptions, NotePermissionRole} from '@hackmd/api' + import {Flags, ux} from '@oclif/core' import fs from 'node:fs' import HackMDCommand from '../../command' import { - commentPermission, editor, noteContent, notePermission, noteTitle, teamPath, + commentPermission, editor, noteContent, notePermission, noteTitle, parentFolderId, teamPath, } from '../../flags' import {openEditor} from '../../open-editor' import {safeStdinRead, temporaryMD} from '../../utils' @@ -25,6 +26,7 @@ raUuSTetT5uQbqQfLnz9lA A new note gvfz2UB5THiKABQJQnLs6Q n content: noteContent, editor, help: Flags.help({char: 'h'}), + parentFolderId, readPermission: notePermission, teamPath, title: noteTitle, @@ -36,10 +38,11 @@ raUuSTetT5uQbqQfLnz9lA A new note gvfz2UB5THiKABQJQnLs6Q n const {flags} = await this.parse(Create) const pipeString = safeStdinRead() - const {commentPermission, content, readPermission, teamPath, title, writePermission} = flags + const {commentPermission, content, parentFolderId, readPermission, teamPath, title, writePermission} = flags const options: CreateNoteOptions = { commentPermission: commentPermission as CommentPermissionType, content: pipeString || content, + parentFolderId, readPermission: readPermission as NotePermissionRole, title, writePermission: writePermission as NotePermissionRole, diff --git a/src/commands/team-notes/update.ts b/src/commands/team-notes/update.ts index 983bd78..d560bf4 100644 --- a/src/commands/team-notes/update.ts +++ b/src/commands/team-notes/update.ts @@ -1,7 +1,11 @@ +import type {UpdateNoteOptions} from '@hackmd/api' + import {Flags} from '@oclif/core' import HackMDCommand from '../../command' -import {noteContent, noteId, teamPath} from '../../flags' +import { + noteContent, noteId, parentFolderId, teamPath, +} from '../../flags' export default class Update extends HackMDCommand { static description = 'Update team note content' @@ -12,12 +16,13 @@ export default class Update extends HackMDCommand { content: noteContent, help: Flags.help({char: 'h'}), noteId, + parentFolderId, teamPath, } async run() { const {flags} = await this.parse(Update) - const {content, noteId, teamPath} = flags + const {content, noteId, parentFolderId, teamPath} = flags if (!teamPath) { this.error('Flag teamPath could not be empty') @@ -27,9 +32,14 @@ export default class Update extends HackMDCommand { this.error('Flag noteId could not be empty') } + const payload: UpdateNoteOptions = { + content, + parentFolderId, + } + try { const APIClient = await this.getAPIClient() - await APIClient.updateTeamNoteContent(teamPath, noteId, content) + await APIClient.updateTeamNote(teamPath, noteId, payload) } catch (error) { this.log('Update team note content failed') this.error(error as Error) diff --git a/src/flags.ts b/src/flags.ts index b9a71ae..6915942 100644 --- a/src/flags.ts +++ b/src/flags.ts @@ -3,6 +3,10 @@ export const noteId = Flags.string({ description: 'HackMD note id', }) +export const folderId = Flags.string({ + description: 'HackMD folder id', +}) + export const teamPath = Flags.string({ description: 'HackMD team path', }) @@ -15,6 +19,30 @@ export const noteTitle = Flags.string({ description: 'new note title', }) +export const folderName = Flags.string({ + description: 'folder name', +}) + +export const folderDescription = Flags.string({ + description: 'folder description', +}) + +export const folderIcon = Flags.string({ + description: 'folder icon', +}) + +export const folderColor = Flags.string({ + description: 'folder color', +}) + +export const parentFolderId = Flags.string({ + description: 'parent folder id', +}) + +export const folderOrder = Flags.string({ + description: 'folder order JSON, e.g. {"root":["folder-id"]}', +}) + export const notePermission = Flags.string({ description: 'set note permission: owner, signed_in, guest', }) diff --git a/src/utils.ts b/src/utils.ts index eed6e0f..4dfcf5a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,5 @@ +import type {ApiFolderOrder} from '@hackmd/api' + import fs from 'fs-extra' import {homedir, tmpdir} from 'node:os' import path from 'node:path' @@ -44,3 +46,19 @@ export function temporaryMD() { return filePath } + +export function parseFolderOrder(order: string): ApiFolderOrder { + const parsed = JSON.parse(order) + + if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { + throw new Error('Folder order must be a JSON object') + } + + for (const [key, value] of Object.entries(parsed)) { + if (!Array.isArray(value) || !value.every(item => typeof item === 'string')) { + throw new Error(`Folder order entry "${key}" must be an array of folder ids`) + } + } + + return parsed as ApiFolderOrder +} diff --git a/test/smoke/cli.test.ts b/test/smoke/cli.test.ts index 84a296f..e18ccf5 100644 --- a/test/smoke/cli.test.ts +++ b/test/smoke/cli.test.ts @@ -206,6 +206,38 @@ describe('Smoke Tests: Built CLI Binary', () => { expect(result.stdout.toLowerCase()).to.include('logout') } }) + + it('should recognize folders command', async () => { + const result = await runCLI(['folders', '--help']) + expect(result.stdout || result.stderr).to.exist + if (result.code === 0) { + expect(result.stdout.toLowerCase()).to.include('folders') + } + }) + + it('should recognize folders create command', async () => { + const result = await runCLI(['folders', 'create', '--help']) + expect(result.stdout || result.stderr).to.exist + if (result.code === 0) { + expect(result.stdout.toLowerCase()).to.include('folder') + } + }) + + it('should recognize team-folders command', async () => { + const result = await runCLI(['team-folders', '--help']) + expect(result.stdout || result.stderr).to.exist + if (result.code === 0) { + expect(result.stdout.toLowerCase()).to.include('team') + } + }) + + it('should recognize team-folders create command', async () => { + const result = await runCLI(['team-folders', 'create', '--help']) + expect(result.stdout || result.stderr).to.exist + if (result.code === 0) { + expect(result.stdout.toLowerCase()).to.include('folder') + } + }) }) describe('Error handling', () => {