diff --git a/README.md b/README.md index 5c8f233..0b890b7 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,27 @@ To run the Node.js example: The example includes detailed comments and demonstrates best practices for using the HackMD API client. +### Book Mode Conference Note Example + +The `examples/book-mode-conference/` directory contains a TypeScript example for creating a "book mode" conference note system: + +- **Book Mode Notes**: Creates a master note that links to all session notes +- **Bulk Note Creation**: Automatically creates individual notes for each conference session +- **TypeScript Implementation**: Full type safety with tsx support for direct execution +- **Configurable Templates**: Customizable note templates and conference settings +- **Hierarchical Organization**: Sessions organized by day and time in the main book +- **Error Handling**: Graceful handling of API failures during bulk operations + +To run the book mode conference example: + +1. Navigate to the example directory: `cd examples/book-mode-conference` +2. Follow the setup instructions in [examples/book-mode-conference/README.md](./examples/book-mode-conference/README.md) +3. Customize the configuration constants and session data +4. Set your HackMD access token +5. Run `npm start` + +This example demonstrates advanced usage patterns including bulk operations, team note management, and creating interconnected note structures for conferences or events. + ## LICENSE MIT diff --git a/examples/book-mode-conference/.env.example b/examples/book-mode-conference/.env.example new file mode 100644 index 0000000..5b7704b --- /dev/null +++ b/examples/book-mode-conference/.env.example @@ -0,0 +1,8 @@ +# HackMD API Configuration +# Get your access token from: https://hackmd.io/@hackmd-api/developer-portal + +# Required: Your HackMD access token +HACKMD_ACCESS_TOKEN=your_access_token_here + +# Optional: HackMD API endpoint (defaults to https://api.hackmd.io/v1) +HACKMD_API_ENDPOINT=https://api.hackmd.io/v1 \ No newline at end of file diff --git a/examples/book-mode-conference/README.md b/examples/book-mode-conference/README.md new file mode 100644 index 0000000..5b5f20b --- /dev/null +++ b/examples/book-mode-conference/README.md @@ -0,0 +1,288 @@ +# Book Mode Conference Note Generator + +This example demonstrates how to create a "book mode" conference note system using the HackMD API. Book mode is a Markdown note that contains organized links to each session note page, making it easy for conference attendees to navigate between different session notes. + +## What This Example Does + +The script performs the following actions: + +1. **Loads Session Data**: Reads conference session information from `sessions.json` +2. **Creates Individual Session Notes**: For each session, creates a dedicated HackMD note with: + - Session title and speaker information + - Embedded announcement note + - Sections for notes, discussion, and related links + - Appropriate tags and permissions +3. **Generates Main Book Note**: Creates a master note that: + - Contains welcome information and useful links + - Organizes all session notes by day and time + - Provides easy navigation to all sessions + - Serves as a central hub for the conference + +## Features + +- **TypeScript Implementation**: Written in TypeScript with full type safety +- **Configurable Constants**: All configuration is centralized at the top of the file +- **Comprehensive Comments**: Well-documented code explaining each section +- **Error Handling**: Graceful handling of API failures +- **tsx Support**: Can be run directly without compilation using tsx +- **Modular Design**: Functions are exportable for potential reuse +- **Flexible Session Data**: Supports various session types and multilingual content + +## Setup + +### Prerequisites + +- Node.js (version 16 or higher) +- A HackMD account with API access +- Access to a HackMD team (for creating team notes) + +### Installation + +1. **Build the main HackMD API package** (if not already done): + ```bash + cd ../../nodejs + npm install + npm run build + cd ../examples/book-mode-conference + ``` + +2. **Install dependencies**: + ```bash + npm install + ``` + +3. **Configure your HackMD access token**: + + **Option A: Environment Variable** + ```bash + # For Unix/Linux/macOS + export HACKMD_ACCESS_TOKEN=your_access_token_here + + # For Windows PowerShell + $env:HACKMD_ACCESS_TOKEN="your_access_token_here" + ``` + + **Option B: .env File** + ```bash + cp .env.example .env + # Edit .env and add your access token + ``` + + You can get your access token from the [HackMD API documentation](https://hackmd.io/@hackmd-api/developer-portal). + +### Configuration + +Before running the script, you may want to customize the configuration constants at the top of `index.ts`: + +#### Essential Configuration + +```typescript +// HackMD announcement note to embed in each session note +const ANNOUNCEMENT_NOTE = '@DevOpsDay/rkO2jyLMlg' + +// Team path where notes will be created +const TEAM_PATH = 'DevOpsDay' + +// Conference details +const CONFERENCE_CONFIG = { + name: 'DevOpsDays Taipei 2025', + website: 'https://devopsdays.tw/', + community: 'https://www.facebook.com/groups/DevOpsTaiwan/', + tags: 'DevOpsDays Taipei 2025' +} +``` + +#### Session Data Format + +The script expects session data in `sessions.json` with the following structure: + +```json +[ + { + "id": "session-001", + "title": "Session Title", + "speaker": [ + { + "speaker": { + "public_name": "Speaker Name" + } + } + ], + "session_type": "talk", + "started_at": "2025-03-15T09:00:00Z", + "finished_at": "2025-03-15T09:30:00Z", + "tags": ["tag1", "tag2"], + "classroom": { + "tw_name": "會議室名稱", + "en_name": "Room Name" + }, + "language": "en", + "difficulty": "General" + } +] +``` + +## Running the Example + +### Development Mode (with file watching) +```bash +npm run dev +``` + +### Production Mode +```bash +npm start +``` + +### Direct Execution with tsx +```bash +npx tsx index.ts +``` + +## Sample Session Data + +The included `sessions.json` contains sample conference session data with: + +- **Multiple session types**: keynotes, talks, workshops +- **Multi-day schedule**: Sessions across different days +- **Bilingual support**: English and Traditional Chinese sessions +- **Various difficulty levels**: General, Beginner, Intermediate, Advanced +- **Multiple speakers**: Examples of single and multiple speaker sessions + +## Generated Output + +The script will create: + +1. **Individual Session Notes**: Each with a dedicated HackMD note containing: + - Session title with speaker names + - Embedded announcement note + - Sections for collaborative note-taking + - Discussion area + - Related links + +2. **Main Conference Book**: A master note containing: + - Conference welcome information + - Organized schedule with links to all session notes + - Quick navigation by day and time + - Useful conference resources + +### Example Output + +``` +=== Creating Individual Session Notes === +✓ Created note for: Welcome to DevOpsDays - John Doe +✓ Created note for: Introduction to CI/CD - Jane Smith +✓ Created note for: Advanced Kubernetes Operations - Alex Chen & Sarah Wilson +... + +=== Session URLs === +[ + { + "id": "session-001", + "url": "https://hackmd.io/abc123", + "title": "Welcome to DevOpsDays - John Doe" + }, + ... +] + +=== Main Conference Book Created === +✓ Book URL: https://hackmd.io/xyz789 +🎉 Book mode conference notes created successfully! +📚 Main book contains links to 6 session notes +``` + +## Customization + +### Modifying Note Templates + +You can customize the session note template by modifying the `generateSessionNoteContent` function: + +```typescript +function generateSessionNoteContent(session: ProcessedSession): string { + return `# ${session.title} + +{%hackmd ${ANNOUNCEMENT_NOTE} %} + +## Your Custom Section +> Add your custom content here + +## ${SESSION_NOTE_CONFIG.sections.notes} +> ${SESSION_NOTE_CONFIG.sections.notesDescription} + +// ... rest of template +` +} +``` + +### Changing the Book Structure + +The book organization can be modified by changing the nesting keys in the main function: + +```typescript +// Current: organize by day, then by start time +const nestedSessions = nest(sessionList.filter(s => s.noteUrl !== 'error'), ['day', 'startTime']) + +// Alternative: organize by session type, then by day +const nestedSessions = nest(sessionList.filter(s => s.noteUrl !== 'error'), ['sessionType', 'day']) +``` + +### Adding Additional Metadata + +You can extend the session data structure and processing by: + +1. Adding new fields to the `ProcessedSession` interface +2. Updating the `loadAndProcessSessions` function to process new fields +3. Modifying the note templates to include the new information + +## Error Handling + +The script includes comprehensive error handling: + +- **Missing Environment Variables**: Clear error messages with setup instructions +- **Missing Session File**: Helpful error message with expected file location +- **API Failures**: Individual session note failures don't stop the entire process +- **Network Issues**: The HackMD API client includes built-in retry logic + +## Troubleshooting + +### Common Issues + +**"HACKMD_ACCESS_TOKEN environment variable is not set"** +- Solution: Set your access token using one of the methods in the Setup section + +**"Sessions file not found"** +- Solution: Ensure `sessions.json` exists in the same directory as `index.ts` + +**"Failed to create note for [session]"** +- Check your team permissions +- Verify the team path is correct +- Ensure your access token has team note creation permissions + +**"Failed to create main book"** +- Same troubleshooting steps as individual notes +- Check that you have sufficient API quota remaining + +### Development Tips + +1. **Start Small**: Test with a few sessions first by modifying `sessions.json` +2. **Check Permissions**: Ensure your HackMD team allows note creation +3. **Monitor Rate Limits**: The script includes built-in retry logic, but be mindful of API limits +4. **Backup Data**: Consider backing up important notes before running the script + +## API Features Demonstrated + +This example showcases several HackMD API features: + +- **Team Note Creation**: Creating notes within a team context +- **Permission Management**: Setting read/write permissions for notes +- **Content Templates**: Using consistent note structures +- **Bulk Operations**: Creating multiple notes programmatically +- **Error Handling**: Graceful handling of API errors + +## License + +This example is part of the HackMD API client and is licensed under the MIT License. + +## Contributing + +If you have suggestions for improving this example or find bugs, please open an issue or submit a pull request to the main repository. \ No newline at end of file diff --git a/examples/book-mode-conference/index.ts b/examples/book-mode-conference/index.ts new file mode 100644 index 0000000..5c1b0e8 --- /dev/null +++ b/examples/book-mode-conference/index.ts @@ -0,0 +1,413 @@ +#!/usr/bin/env tsx +/** + * Book Mode Conference Note Generator + * + * This script generates a "book mode" conference note system using HackMD API. + * It creates individual notes for each session and a main book note that links to all sessions. + * + * Book mode is a Markdown note that contains organized links to each session note page, + * making it easy for conference attendees to navigate between different session notes. + * + * Prerequisites: + * - HackMD access token (set in HACKMD_ACCESS_TOKEN environment variable) + * - Team path where notes will be created + * - Session data in JSON format + */ + +'use strict' + +// Load environment variables from .env file in project root +import dotenv from 'dotenv' +dotenv.config() + +import _ from 'lodash' +import moment from 'moment' +import { API } from '@hackmd/api' +import fs from 'fs' +import path from 'path' +import { fileURLToPath } from 'url' + +// Get the current directory for ES modules +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + +// ========================================== +// CONFIGURATION CONSTANTS +// ========================================== + +/** + * HackMD announcement note short ID to be embedded in each session note + * This note typically contains conference-wide announcements or information + */ +const ANNOUNCEMENT_NOTE = '@DevOpsDay/rkO2jyLMlg' + +/** + * Team path where all notes will be created + * This should be your HackMD team's unique identifier + */ +const TEAM_PATH = 'DevOpsDay' + +/** + * Conference details for the main book note + */ +const CONFERENCE_CONFIG = { + name: 'DevOpsDays Taipei 2025', + website: 'https://devopsdays.tw/', + community: 'https://www.facebook.com/groups/DevOpsTaiwan/', + tags: 'DevOpsDays Taipei 2025' +} + +/** + * Session note template configuration + */ +const SESSION_NOTE_CONFIG = { + // Default content sections for each session note + sections: { + notes: '筆記區', + notesDescription: '從這開始記錄你的筆記', + discussion: '討論區', + discussionDescription: '歡迎在此進行討論', + links: '相關連結' + } +} + +/** + * Main book note configuration + */ +const BOOK_NOTE_CONFIG = { + welcomeNote: '/@DevOpsDay/ry9DnJIfel', + hackmdQuickStart: 'https://hackmd.io/s/BJvtP4zGX', + hackmdMeetingFeatures: 'https://hackmd.io/s/BJHWlNQMX' +} + +// ========================================== +// TYPE DEFINITIONS +// ========================================== + +/** + * Define permission constants (equivalent to the API enums) + * These mirror the NotePermissionRole enum from the API + */ +const NotePermissionRole = { + OWNER: 'owner', + SIGNED_IN: 'signed_in', + GUEST: 'guest' +} as const + +type NotePermissionRoleType = typeof NotePermissionRole[keyof typeof NotePermissionRole] + +/** + * Raw session data structure from JSON file + */ +interface RawSession { + id: string + title: string + speaker: Array<{ + speaker: { + public_name: string + } + }> + session_type: string | null + started_at: string + finished_at: string + tags?: string[] + classroom?: { + tw_name?: string + en_name?: string + } + language?: string + difficulty?: string +} + +/** + * Processed session data structure + */ +interface ProcessedSession { + id: string + title: string + tags: string[] + startDate: number + day: string + startTime: string + endTime: string + sessionType: string + classroom: string + language: string + difficulty: string + noteUrl?: string +} + +/** + * Session URL reference for output + */ +interface SessionUrl { + id: string + url: string + title: string +} + +// ========================================== +// UTILITY FUNCTIONS +// ========================================== + +/** + * Creates a nested object structure from an array using specified keys + * This is used to organize sessions by day and time for the book structure + * + * @param seq - Array of items to nest + * @param keys - Array of property names to use for nesting levels + * @returns Nested object structure + */ +function nest(seq: any[], keys: string[]): any { + if (!keys.length) return seq + + const [first, ...rest] = keys + return _.mapValues(_.groupBy(seq, first), function (value) { + return nest(value, rest) + }) +} + +/** + * Extracts the HackMD host URL from the API endpoint + * This is used to generate correct note URLs for display + * + * @returns The HackMD host URL + */ +function getHackMDHost(): string { + const apiEndpoint = process.env.HACKMD_API_ENDPOINT || 'https://hackmd.io' + try { + const url = new URL(apiEndpoint) + return `${url.protocol}//${url.host}` + } catch (error) { + console.warn('Failed to parse HACKMD_API_ENDPOINT, falling back to https://hackmd.io') + return 'https://hackmd.io' + } +} + +/** + * Loads and processes session data from JSON file + * Filters out sessions with null session types and enriches data + * + * @returns Array of processed session data + */ +function loadAndProcessSessions(): ProcessedSession[] { + const sessionsPath = path.join(__dirname, 'sessions.json') + + if (!fs.existsSync(sessionsPath)) { + throw new Error(`Sessions file not found: ${sessionsPath}`) + } + + const rawSessions: RawSession[] = JSON.parse(fs.readFileSync(sessionsPath, 'utf8')) + + return rawSessions + .filter(s => s.session_type && s.session_type !== null) // Filter out null session types + .map(s => { + // Combine speaker names with ampersand separator + const speakers = s.speaker.map(speaker => { + return speaker.speaker.public_name + }).join(' & ') + + return { + id: s.id, + title: s.title + (speakers ? " - " + speakers : ""), + tags: s.tags || [], + startDate: moment(s.started_at).valueOf(), + day: moment(s.started_at).format('MM/DD'), + startTime: moment(s.started_at).format('HH:mm'), + endTime: moment(s.finished_at).format('HH:mm'), + sessionType: s.session_type!, // We already filtered out null values above + classroom: s.classroom?.tw_name || s.classroom?.en_name || 'TBD', + language: s.language || 'en', + difficulty: s.difficulty || 'General' + } + }) + .sort((a, b) => (a.startDate - b.startDate)) // Sort by start time +} + +/** + * Generates the content for a session note + * + * @param session - The session data + * @returns Formatted markdown content for the session note + */ +function generateSessionNoteContent(session: ProcessedSession): string { + return `# ${session.title} + +{%hackmd ${ANNOUNCEMENT_NOTE} %} + +## ${SESSION_NOTE_CONFIG.sections.notes} +> ${SESSION_NOTE_CONFIG.sections.notesDescription} + +## ${SESSION_NOTE_CONFIG.sections.discussion} +> ${SESSION_NOTE_CONFIG.sections.discussionDescription} + +## ${SESSION_NOTE_CONFIG.sections.links} +- [${CONFERENCE_CONFIG.name} 官方網站](${CONFERENCE_CONFIG.website}) + +###### tags: \`${CONFERENCE_CONFIG.tags}\` +` +} + +/** + * Generates the hierarchical book content from nested session data + * + * @param sessions - Nested session data organized by day/time + * @param layer - Current nesting level (for header depth) + * @returns Formatted markdown content for the book section + */ +function generateBookContent(sessions: any, layer: number): string { + const days = Object.keys(sessions).sort() + let content = "" + + if (Array.isArray(sessions[days[0]])) { + // This is the leaf level (sessions) - flatten all sessions and sort chronologically + let allSessions: ProcessedSession[] = [] + for (let timeSlot of days) { + allSessions = allSessions.concat(sessions[timeSlot]) + } + // Sort all sessions by start time + const sortedSessions = _.sortBy(allSessions, ['startTime']) + + for (let session of sortedSessions) { + if (session.noteUrl && session.noteUrl !== 'error') { + content += `- ${session.startTime} ~ ${session.endTime} [${session.title}](/${session.noteUrl}) (${session.classroom})\n` + } + } + return content + } else { + // This is a grouping level + for (let day of days) { + content += `${new Array(layer).fill("#").join("")} ${day}\n\n` + content += generateBookContent(sessions[day], layer + 1) + } + return content + } +} + +/** + * Generates the main conference book note content + * + * @param bookContent - The hierarchical session content + * @returns Formatted markdown content for the main book note + */ +function generateMainBookContent(bookContent: string): string { + return `${CONFERENCE_CONFIG.name} 共同筆記 +=== + +## 歡迎來到 ${CONFERENCE_CONFIG.name}! + +- [歡迎來到 DevOpsDays!](${BOOK_NOTE_CONFIG.welcomeNote}) +- [${CONFERENCE_CONFIG.name} 官方網站](${CONFERENCE_CONFIG.website}) [target=_blank] +- [HackMD 快速入門](${BOOK_NOTE_CONFIG.hackmdQuickStart}) +- [HackMD 會議功能介紹](${BOOK_NOTE_CONFIG.hackmdMeetingFeatures}) + +## 議程筆記 + +${bookContent} + +## 相關資源 + +- [DevOps Taiwan Community](${CONFERENCE_CONFIG.community}) +- [活動照片分享區](#) +- [問題回饋](#) + +###### tags: \`${CONFERENCE_CONFIG.tags}\` +` +} + +// ========================================== +// MAIN EXECUTION LOGIC +// ========================================== + +/** + * Main function that orchestrates the entire book mode note creation process + */ +async function main(): Promise { + // Validate required environment variables + if (!process.env.HACKMD_ACCESS_TOKEN) { + console.error('Error: HACKMD_ACCESS_TOKEN environment variable is not set.') + console.error('Please set your HackMD access token using one of these methods:') + console.error('1. Create a .env file with HACKMD_ACCESS_TOKEN=your_token_here') + console.error('2. Set the environment variable directly: export HACKMD_ACCESS_TOKEN=your_token_here') + process.exit(1) + } + + // Initialize API client + const api = new API(process.env.HACKMD_ACCESS_TOKEN, process.env.HACKMD_API_ENDPOINT) + + // Load and process session data + console.log('Loading session data...') + const sessionList = loadAndProcessSessions() + console.log(`Processing ${sessionList.length} sessions...`) + + // Create individual session notes + console.log('\n=== Creating Individual Session Notes ===') + for (let data of sessionList) { + const noteContent = generateSessionNoteContent(data) + + const noteData = { + title: data.title, + content: noteContent, + readPermission: NotePermissionRole.GUEST as any, + writePermission: NotePermissionRole.SIGNED_IN as any + } + + try { + const note = await api.createTeamNote(TEAM_PATH, noteData) + data.noteUrl = note.shortId + console.log(`✓ Created note for: ${data.title}`) + } catch (error: any) { + console.error(`✗ Failed to create note for ${data.title}:`, error.message) + data.noteUrl = 'error' + } + } + + // Output session URLs for reference + const hackmdHost = getHackMDHost() + const sessionUrls: SessionUrl[] = sessionList + .filter(s => s.noteUrl !== 'error') + .map(s => ({ + id: s.id, + url: `${hackmdHost}/${s.noteUrl}`, + title: s.title + })) + + console.log('\n=== Session URLs ===') + console.log(JSON.stringify(sessionUrls, null, 2)) + + // Create nested structure for the main book + const nestedSessions = nest(sessionList.filter(s => s.noteUrl !== 'error'), ['day', 'startTime']) + const bookContent = generateBookContent(nestedSessions, 1) + + // Create main conference book + console.log('\n=== Creating Main Conference Book ===') + const mainBookContent = generateMainBookContent(bookContent) + + try { + const mainBook = await api.createTeamNote(TEAM_PATH, { + title: `${CONFERENCE_CONFIG.name} 共同筆記`, + content: mainBookContent, + readPermission: NotePermissionRole.GUEST as any, + writePermission: NotePermissionRole.SIGNED_IN as any + }) + + console.log('\n=== Main Conference Book Created ===') + console.log(`✓ Book URL: ${hackmdHost}/${mainBook.shortId}`) + console.log('\n🎉 Book mode conference notes created successfully!') + console.log(`📚 Main book contains links to ${sessionUrls.length} session notes`) + } catch (error: any) { + console.error('✗ Failed to create main book:', error.message) + } +} + +// ========================================== +// SCRIPT EXECUTION +// ========================================== + +// Run the script when executed directly +if (import.meta.url === `file://${process.argv[1]}`) { + main().catch(console.error) +} + +// Export functions for potential module usage +export { main, generateBookContent, loadAndProcessSessions, generateSessionNoteContent } \ No newline at end of file diff --git a/examples/book-mode-conference/package-lock.json b/examples/book-mode-conference/package-lock.json new file mode 100644 index 0000000..839b6fc --- /dev/null +++ b/examples/book-mode-conference/package-lock.json @@ -0,0 +1,646 @@ +{ + "name": "hackmd-api-book-mode-conference-example", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "hackmd-api-book-mode-conference-example", + "version": "1.0.0", + "dependencies": { + "@hackmd/api": "file:../../nodejs", + "dotenv": "^16.4.5", + "lodash": "^4.17.21", + "moment": "^2.29.4" + }, + "devDependencies": { + "@types/lodash": "^4.14.202", + "@types/node": "^20.10.6", + "tsx": "^4.7.0", + "typescript": "^5.3.3" + } + }, + "../../nodejs": { + "name": "@hackmd/api", + "version": "2.4.0", + "license": "MIT", + "dependencies": { + "axios": "^1.8.4", + "tslib": "^1.14.1" + }, + "devDependencies": { + "@faker-js/faker": "^7.6.0", + "@rollup/plugin-commonjs": "^28.0.3", + "@rollup/plugin-node-resolve": "^16.0.1", + "@rollup/plugin-typescript": "^12.1.2", + "@types/eslint": "^8.21.0", + "@types/jest": "^29.4.0", + "@types/node": "^13.11.1", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "dotenv": "^16.0.3", + "eslint": "^8.57.1", + "jest": "^29.4.2", + "msw": "^2.7.3", + "rimraf": "^4.1.2", + "rollup": "^4.41.1", + "ts-jest": "^29.0.5", + "ts-node": "^8.8.2", + "typescript": "^4.9.5" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", + "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@hackmd/api": { + "resolved": "../../nodejs", + "link": true + }, + "node_modules/@types/lodash": { + "version": "4.17.18", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.18.tgz", + "integrity": "sha512-KJ65INaxqxmU6EoCiJmRPZC9H9RVWCRd349tXM2M3O5NA7cY6YL7c0bHAHQ93NOfTObEQ004kd2QVHs/r0+m4g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.1.tgz", + "integrity": "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/esbuild": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.5" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/tsx": { + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", + "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/examples/book-mode-conference/package.json b/examples/book-mode-conference/package.json new file mode 100644 index 0000000..6debafc --- /dev/null +++ b/examples/book-mode-conference/package.json @@ -0,0 +1,23 @@ +{ + "name": "hackmd-api-book-mode-conference-example", + "version": "1.0.0", + "description": "Example for creating a book mode conference note with HackMD API", + "main": "index.ts", + "type": "module", + "scripts": { + "start": "tsx index.ts", + "dev": "tsx watch index.ts" + }, + "dependencies": { + "@hackmd/api": "file:../../nodejs", + "dotenv": "^16.4.5", + "lodash": "^4.17.21", + "moment": "^2.29.4" + }, + "devDependencies": { + "@types/lodash": "^4.14.202", + "@types/node": "^20.10.6", + "tsx": "^4.7.0", + "typescript": "^5.3.3" + } +} \ No newline at end of file diff --git a/examples/book-mode-conference/sessions.json b/examples/book-mode-conference/sessions.json new file mode 100644 index 0000000..829ad2f --- /dev/null +++ b/examples/book-mode-conference/sessions.json @@ -0,0 +1,133 @@ +[ + { + "id": "session-001", + "title": "Welcome to DevOpsDays", + "speaker": [ + { + "speaker": { + "public_name": "John Doe" + } + } + ], + "session_type": "keynote", + "started_at": "2025-03-15T09:00:00Z", + "finished_at": "2025-03-15T09:30:00Z", + "tags": ["welcome", "keynote"], + "classroom": { + "tw_name": "主舞台", + "en_name": "Main Stage" + }, + "language": "en", + "difficulty": "General" + }, + { + "id": "session-002", + "title": "Introduction to CI/CD", + "speaker": [ + { + "speaker": { + "public_name": "Jane Smith" + } + } + ], + "session_type": "talk", + "started_at": "2025-03-15T10:00:00Z", + "finished_at": "2025-03-15T10:45:00Z", + "tags": ["ci", "cd", "automation"], + "classroom": { + "tw_name": "A會議室", + "en_name": "Room A" + }, + "language": "en", + "difficulty": "Beginner" + }, + { + "id": "session-003", + "title": "Advanced Kubernetes Operations", + "speaker": [ + { + "speaker": { + "public_name": "Alex Chen" + } + }, + { + "speaker": { + "public_name": "Sarah Wilson" + } + } + ], + "session_type": "workshop", + "started_at": "2025-03-15T11:00:00Z", + "finished_at": "2025-03-15T12:00:00Z", + "tags": ["kubernetes", "containers", "orchestration"], + "classroom": { + "tw_name": "B會議室", + "en_name": "Room B" + }, + "language": "en", + "difficulty": "Advanced" + }, + { + "id": "session-004", + "title": "DevOps Culture and Practices", + "speaker": [ + { + "speaker": { + "public_name": "Mike Johnson" + } + } + ], + "session_type": "talk", + "started_at": "2025-03-15T14:00:00Z", + "finished_at": "2025-03-15T14:45:00Z", + "tags": ["culture", "practices", "team"], + "classroom": { + "tw_name": "主舞台", + "en_name": "Main Stage" + }, + "language": "en", + "difficulty": "General" + }, + { + "id": "session-005", + "title": "監控與可觀測性", + "speaker": [ + { + "speaker": { + "public_name": "林小明" + } + } + ], + "session_type": "talk", + "started_at": "2025-03-16T09:30:00Z", + "finished_at": "2025-03-16T10:15:00Z", + "tags": ["monitoring", "observability"], + "classroom": { + "tw_name": "A會議室", + "en_name": "Room A" + }, + "language": "zh-TW", + "difficulty": "Intermediate" + }, + { + "id": "session-006", + "title": "Security in DevOps Pipeline", + "speaker": [ + { + "speaker": { + "public_name": "Emma Davis" + } + } + ], + "session_type": "workshop", + "started_at": "2025-03-16T10:30:00Z", + "finished_at": "2025-03-16T12:00:00Z", + "tags": ["security", "devsecops", "pipeline"], + "classroom": { + "tw_name": "C會議室", + "en_name": "Room C" + }, + "language": "en", + "difficulty": "Intermediate" + } +] \ No newline at end of file diff --git a/examples/book-mode-conference/tsconfig.json b/examples/book-mode-conference/tsconfig.json new file mode 100644 index 0000000..2301721 --- /dev/null +++ b/examples/book-mode-conference/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "node", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "outDir": "./dist", + "rootDir": "./", + "resolveJsonModule": true + }, + "include": ["*.ts"], + "exclude": ["node_modules", "dist"] +} \ No newline at end of file diff --git a/examples/nodejs/package-lock.json b/examples/nodejs/package-lock.json index 165b707..617f983 100644 --- a/examples/nodejs/package-lock.json +++ b/examples/nodejs/package-lock.json @@ -22,16 +22,20 @@ }, "devDependencies": { "@faker-js/faker": "^7.6.0", + "@rollup/plugin-commonjs": "^28.0.3", + "@rollup/plugin-node-resolve": "^16.0.1", + "@rollup/plugin-typescript": "^12.1.2", "@types/eslint": "^8.21.0", "@types/jest": "^29.4.0", "@types/node": "^13.11.1", - "@typescript-eslint/eslint-plugin": "^5.52.0", - "@typescript-eslint/parser": "^5.52.0", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", "dotenv": "^16.0.3", - "eslint": "^8.9.0", + "eslint": "^8.57.1", "jest": "^29.4.2", "msw": "^2.7.3", "rimraf": "^4.1.2", + "rollup": "^4.41.1", "ts-jest": "^29.0.5", "ts-node": "^8.8.2", "typescript": "^4.9.5"