use rest-api-client
This commit is contained in:
337
AGENTS.md
337
AGENTS.md
@@ -1,149 +1,114 @@
|
|||||||
# AGENTS.md
|
# AGENTS.md
|
||||||
|
|
||||||
Kintone Customize Manager 项目开发指南。
|
Kintone Customize Manager - Electron + React 应用,用于管理 Kintone 自定义资源。
|
||||||
|
|
||||||
## 1. 构建命令
|
## 1. 构建命令
|
||||||
|
|
||||||
### 开发
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 启动开发服务器(HMR + 热重载)
|
# 开发(HMR + 热重载)
|
||||||
npm run dev
|
npm run dev
|
||||||
|
|
||||||
# 类型检查
|
# 类型检查
|
||||||
npx tsc --noEmit
|
npx tsc --noEmit
|
||||||
```
|
|
||||||
|
|
||||||
### 构建
|
# 构建
|
||||||
|
|
||||||
```bash
|
|
||||||
# 构建生产版本
|
|
||||||
npm run build
|
npm run build
|
||||||
|
|
||||||
# 打包应用
|
# 打包
|
||||||
npm run package:win # Windows
|
npm run package:win # Windows
|
||||||
npm run package:mac # macOS
|
npm run package:mac # macOS
|
||||||
npm run package:linux # Linux
|
npm run package:linux # Linux
|
||||||
|
|
||||||
|
# 代码质量
|
||||||
|
npm run lint # ESLint 检查
|
||||||
|
npm run format # Prettier 格式化
|
||||||
```
|
```
|
||||||
|
|
||||||
### 代码质量
|
**注意**: 无测试框架,项目暂无测试文件。
|
||||||
|
|
||||||
```bash
|
## 2. 项目架构
|
||||||
# ESLint 检查
|
|
||||||
npm run lint
|
|
||||||
|
|
||||||
# 格式化代码
|
|
||||||
npm run format
|
|
||||||
```
|
|
||||||
|
|
||||||
## 2. 项目结构
|
|
||||||
|
|
||||||
```
|
```
|
||||||
kintone-customize-manager/
|
src/
|
||||||
├── src/
|
├── main/ # Electron 主进程
|
||||||
│ ├── main/ # Electron 主进程
|
│ ├── index.ts # 入口,创建窗口
|
||||||
│ │ ├── index.ts # 主进程入口
|
│ ├── ipc-handlers.ts # IPC 处理器(所有通信入口)
|
||||||
│ │ ├── ipc-handlers.ts # IPC 通信处理
|
│ ├── storage.ts # 文件存储 + 密码加密
|
||||||
│ │ ├── storage.ts # 文件系统操作
|
│ └── kintone-api.ts # Kintone REST API 封装
|
||||||
│ │ ├── kintone-api.ts # Kintone API 封装
|
├── preload/ # Preload 脚本
|
||||||
│ │ ├── updater.ts # 自动更新逻辑
|
│ ├── index.ts # 暴露 API 到渲染进程
|
||||||
│ │ └── config.ts # 配置管理
|
│ └── index.d.ts # 类型声明
|
||||||
│ ├── preload/ # Preload 脚本
|
└── renderer/ # React 渲染进程
|
||||||
│ │ └── index.ts # 暴露 API 到渲染进程
|
└── src/
|
||||||
│ └── renderer/ # React 渲染进程
|
├── main.tsx # React 入口
|
||||||
│ └── src/
|
├── App.tsx # 根组件
|
||||||
│ ├── main.tsx # React 入口
|
├── components/ # React 组件
|
||||||
│ ├── App.tsx # 根组件
|
├── stores/ # Zustand Stores
|
||||||
│ ├── components/ # React 组件
|
└── types/ # TypeScript 类型
|
||||||
│ ├── hooks/ # 自定义 Hooks
|
|
||||||
│ ├── stores/ # Zustand Stores
|
|
||||||
│ ├── utils/ # 工具函数
|
|
||||||
│ └── types/ # TypeScript 类型
|
|
||||||
├── resources/ # 应用资源(图标等)
|
|
||||||
└── build/ # 构建配置
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 3. 路径别名
|
## 3. 路径别名
|
||||||
|
|
||||||
| 别名 | 路径 |
|
| 别名 | 路径 |
|
||||||
|------|------|
|
| ------------- | -------------------- |
|
||||||
| `@renderer/*` | `src/renderer/src/*` |
|
| `@renderer/*` | `src/renderer/src/*` |
|
||||||
| `@main/*` | `src/main/*` |
|
| `@main/*` | `src/main/*` |
|
||||||
| `@preload/*` | `src/preload/*` |
|
| `@preload/*` | `src/preload/*` |
|
||||||
|
|
||||||
使用示例:
|
|
||||||
```typescript
|
|
||||||
import { useStore } from '@renderer/stores'
|
|
||||||
import { ipcHandler } from '@main/ipc-handlers'
|
|
||||||
```
|
|
||||||
|
|
||||||
## 4. 代码风格
|
## 4. 代码风格
|
||||||
|
|
||||||
### 导入顺序
|
### 导入顺序
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 1. Node.js 内置模块
|
// 1. Node.js 内置模块
|
||||||
import { join } from 'path'
|
import { join } from "path";
|
||||||
import { app, BrowserWindow } from 'electron'
|
import { app, BrowserWindow } from "electron";
|
||||||
|
|
||||||
// 2. 第三方库
|
// 2. 第三方库
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect } from "react";
|
||||||
import { Button, Layout } from 'antd'
|
import { Button, Layout } from "antd";
|
||||||
|
|
||||||
// 3. 项目内部模块(使用别名)
|
// 3. 项目内部模块(别名)
|
||||||
import { useDomainStore } from '@renderer/stores'
|
import { useDomainStore } from "@renderer/stores";
|
||||||
import { formatDate } from '@renderer/utils'
|
|
||||||
|
|
||||||
// 4. 相对导入
|
// 4. 相对导入
|
||||||
import './styles.css'
|
import "./styles.css";
|
||||||
```
|
```
|
||||||
|
|
||||||
### 命名规范
|
### 命名规范
|
||||||
|
|
||||||
| 类型 | 规范 | 示例 |
|
- 组件文件/名: `PascalCase` (e.g., `DomainManager.tsx`)
|
||||||
|------|------|------|
|
- 工具函数文件: `camelCase` (e.g., `formatDate.ts`)
|
||||||
| 组件文件 | PascalCase | `DomainManager.tsx` |
|
- Store 文件: `camelCase + Store` (e.g., `domainStore.ts`)
|
||||||
| 工具函数文件 | camelCase | `formatDate.ts` |
|
- 函数/变量: `camelCase` (e.g., `handleSubmit`)
|
||||||
| Store 文件 | camelCase + Store | `domainStore.ts` |
|
- 常量: `UPPER_SNAKE_CASE` (e.g., `MAX_FILE_SIZE`)
|
||||||
| 类型文件 | camelCase | `types.ts` |
|
- 类型/接口: `PascalCase` (e.g., `DomainConfig`)
|
||||||
| 组件名 | PascalCase | `DomainManager` |
|
|
||||||
| 函数/变量 | camelCase | `handleSubmit` |
|
|
||||||
| 常量 | UPPER_SNAKE_CASE | `MAX_FILE_SIZE` |
|
|
||||||
| 类型/接口 | PascalCase | `DomainConfig` |
|
|
||||||
|
|
||||||
### TypeScript 规范
|
### TypeScript 规范
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 显式类型定义
|
// 显式类型定义,避免 any
|
||||||
interface DomainConfig {
|
interface DomainConfig {
|
||||||
id: string
|
id: string;
|
||||||
name: string
|
name: string;
|
||||||
domain: string
|
authType: "password" | "api_token"; // 使用字面量联合类型
|
||||||
username: string
|
|
||||||
authType: 'password' | 'api_token'
|
|
||||||
createdAt: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 函数返回类型
|
// 异步函数返回 Promise
|
||||||
function createWindow(): void { }
|
|
||||||
|
|
||||||
// 异步函数
|
|
||||||
async function fetchDomains(): Promise<Domain[]> {}
|
async function fetchDomains(): Promise<Domain[]> {}
|
||||||
|
|
||||||
// 避免使用 any,使用 unknown 或具体类型
|
// 使用类型守卫处理 unknown
|
||||||
function parseResponse(data: unknown): DomainConfig {
|
function parseResponse(data: unknown): DomainConfig {
|
||||||
// 类型守卫
|
if (typeof data !== "object" || data === null) {
|
||||||
if (typeof data !== 'object' || data === null) {
|
throw new Error("Invalid response");
|
||||||
throw new Error('Invalid response')
|
|
||||||
}
|
}
|
||||||
return data as DomainConfig
|
return data as DomainConfig;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### React 组件规范
|
### React 组件规范
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 函数组件优先
|
|
||||||
interface DomainListProps {
|
interface DomainListProps {
|
||||||
domains: Domain[]
|
domains: Domain[]
|
||||||
onSelect: (domain: Domain) => void
|
onSelect: (domain: Domain) => void
|
||||||
@@ -153,19 +118,13 @@ function DomainList({ domains, onSelect }: DomainListProps) {
|
|||||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||||
|
|
||||||
// Hooks 放在组件顶部
|
// Hooks 放在组件顶部
|
||||||
const { token } = theme.useToken()
|
|
||||||
|
|
||||||
// 事件处理函数使用 useCallback
|
// 事件处理函数使用 useCallback
|
||||||
const handleClick = useCallback((domain: Domain) => {
|
const handleClick = useCallback((domain: Domain) => {
|
||||||
setSelectedId(domain.id)
|
setSelectedId(domain.id)
|
||||||
onSelect(domain)
|
onSelect(domain)
|
||||||
}, [onSelect])
|
}, [onSelect])
|
||||||
|
|
||||||
return (
|
return <div>...</div>
|
||||||
<div>
|
|
||||||
{/* JSX */}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DomainList
|
export default DomainList
|
||||||
@@ -174,123 +133,65 @@ export default DomainList
|
|||||||
### Zustand Store 规范
|
### Zustand Store 规范
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { create } from 'zustand'
|
import { create } from "zustand";
|
||||||
import { persist } from 'zustand/middleware'
|
import { persist } from "zustand/middleware";
|
||||||
|
|
||||||
interface DomainState {
|
interface DomainState {
|
||||||
domains: Domain[]
|
domains: Domain[];
|
||||||
currentDomain: Domain | null
|
addDomain: (domain: Domain) => void;
|
||||||
addDomain: (domain: Domain) => void
|
|
||||||
removeDomain: (id: string) => void
|
|
||||||
setCurrentDomain: (domain: Domain | null) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useDomainStore = create<DomainState>()(
|
export const useDomainStore = create<DomainState>()(
|
||||||
persist(
|
persist(
|
||||||
(set) => ({
|
(set) => ({
|
||||||
domains: [],
|
domains: [],
|
||||||
currentDomain: null,
|
addDomain: (domain) =>
|
||||||
addDomain: (domain) => set((state) => ({
|
set((state) => ({
|
||||||
domains: [...state.domains, domain]
|
domains: [...state.domains, domain],
|
||||||
})),
|
})),
|
||||||
removeDomain: (id) => set((state) => ({
|
|
||||||
domains: state.domains.filter(d => d.id !== id)
|
|
||||||
})),
|
|
||||||
setCurrentDomain: (domain) => set({ currentDomain: domain })
|
|
||||||
}),
|
}),
|
||||||
{ name: 'domain-storage' }
|
{ name: "domain-storage" },
|
||||||
)
|
),
|
||||||
)
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
## 5. 错误处理
|
## 5. IPC 通信规范
|
||||||
|
|
||||||
### 主进程错误处理
|
### Result 模式(所有 IPC 返回)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// IPC 处理错误
|
type Result<T> = { success: true; data: T } | { success: false; error: string };
|
||||||
ipcMain.handle('fetch-domains', async () => {
|
|
||||||
try {
|
|
||||||
const domains = await fetchDomainsFromApi()
|
|
||||||
return { success: true, data: domains }
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to fetch domains:', error)
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: error instanceof Error ? error.message : 'Unknown error'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 渲染进程错误处理
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// 使用 Result 模式
|
|
||||||
type Result<T> =
|
|
||||||
| { success: true; data: T }
|
|
||||||
| { success: false; error: string }
|
|
||||||
|
|
||||||
async function handleFetch(): Promise<Result<Domain[]>> {
|
|
||||||
try {
|
|
||||||
const result = await window.api.fetchDomains()
|
|
||||||
if (!result.success) {
|
|
||||||
message.error(result.error)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
} catch (error) {
|
|
||||||
const errorMsg = error instanceof Error ? error.message : 'Unknown error'
|
|
||||||
message.error(errorMsg)
|
|
||||||
return { success: false, error: errorMsg }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 6. IPC 通信规范
|
|
||||||
|
|
||||||
### Preload 暴露 API
|
### Preload 暴露 API
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// preload/index.ts
|
|
||||||
const api = {
|
const api = {
|
||||||
// 使用 invoke 进行双向通信
|
// 双向通信使用 invoke
|
||||||
fetchDomains: () => ipcRenderer.invoke('fetch-domains'),
|
fetchDomains: () => ipcRenderer.invoke("fetch-domains"),
|
||||||
|
// 单向通信使用 send
|
||||||
|
notify: (message: string) => ipcRenderer.send("notify", message),
|
||||||
|
};
|
||||||
|
|
||||||
// 使用 send 进行单向通信
|
contextBridge.exposeInMainWorld("api", api);
|
||||||
notify: (message: string) => ipcRenderer.send('notify', message),
|
|
||||||
|
|
||||||
// 监听事件
|
|
||||||
onUpdate: (callback: (info: UpdateInfo) => void) =>
|
|
||||||
ipcRenderer.on('update-available', (_, info) => callback(info))
|
|
||||||
}
|
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld('api', api)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 类型定义
|
### 类型定义
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// preload/index.d.ts
|
// preload/index.d.ts
|
||||||
interface ElectronAPI {
|
|
||||||
fetchDomains: () => Promise<Result<Domain[]>>
|
|
||||||
notify: (message: string) => void
|
|
||||||
onUpdate: (callback: (info: UpdateInfo) => void) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
electron: ElectronAPI
|
api: ElectronAPI;
|
||||||
api: ElectronAPI
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 7. UI 组件规范
|
## 6. UI 组件规范
|
||||||
|
|
||||||
### 使用 LobeHub UI + Ant Design
|
使用 **Ant Design 6** + **antd-style**(不用 Tailwind):
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 使用 antd-style 进行样式
|
|
||||||
import { createStyles } from 'antd-style'
|
import { createStyles } from 'antd-style'
|
||||||
|
|
||||||
const useStyles = createStyles(({ token, css }) => ({
|
const useStyles = createStyles(({ token, css }) => ({
|
||||||
@@ -307,59 +208,85 @@ function MyComponent() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 国际化
|
国际化:使用中文默认 `import zhCN from 'antd/locale/zh_CN'`
|
||||||
|
|
||||||
```typescript
|
## 7. 安全规范
|
||||||
// 使用中文默认
|
|
||||||
import zhCN from 'antd/locale/zh_CN'
|
|
||||||
|
|
||||||
<ConfigProvider locale={zhCN}>
|
|
||||||
<App />
|
|
||||||
</ConfigProvider>
|
|
||||||
```
|
|
||||||
|
|
||||||
## 8. 安全规范
|
|
||||||
|
|
||||||
### 密码存储
|
### 密码存储
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 使用 safeStorage 加密存储
|
import { safeStorage } from "electron";
|
||||||
import { safeStorage } from 'electron'
|
|
||||||
|
|
||||||
// 加密
|
// 加密
|
||||||
const encrypted = safeStorage.encryptString(password)
|
const encrypted = safeStorage.encryptString(password);
|
||||||
|
|
||||||
// 解密
|
// 解密
|
||||||
const decrypted = safeStorage.decryptString(encrypted)
|
const decrypted = safeStorage.decryptString(encrypted);
|
||||||
```
|
```
|
||||||
|
|
||||||
### CSP 配置
|
### WebPreferences
|
||||||
|
|
||||||
```html
|
必须启用:`contextIsolation: true`, `nodeIntegration: false`, `sandbox: false`
|
||||||
<meta http-equiv="Content-Security-Policy"
|
|
||||||
content="default-src 'self';
|
## 8. 错误处理
|
||||||
script-src 'self';
|
|
||||||
style-src 'self' 'unsafe-inline';
|
```typescript
|
||||||
img-src 'self' data: https:;
|
// 主进程 IPC
|
||||||
font-src 'self' data:;" />
|
ipcMain.handle("fetch-domains", async () => {
|
||||||
|
try {
|
||||||
|
const domains = await fetchDomainsFromApi();
|
||||||
|
return { success: true, data: domains };
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : "Unknown error",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 渲染进程调用
|
||||||
|
const result = await window.api.fetchDomains();
|
||||||
|
if (!result.success) {
|
||||||
|
message.error(result.error);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 9. fnm 环境配置
|
## 9. fnm 环境配置
|
||||||
|
|
||||||
所有 npm/npx 命令需要先加载 fnm 环境:
|
所有 npm/npx 命令需加载 fnm 环境:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 方式一:使用 wrapper 脚本
|
|
||||||
~/.config/opencode/node-fnm-wrapper.sh npm run dev
|
|
||||||
|
|
||||||
# 方式二:手动加载
|
|
||||||
eval "$(fnm env --use-on-cd)" && npm run dev
|
eval "$(fnm env --use-on-cd)" && npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## 10. 注意事项
|
## 10. 注意事项
|
||||||
|
|
||||||
1. **ESM Only**: LobeHub UI 仅支持 ESM,确保 `tsconfig.json` 中 `"module": "ESNext"`
|
1. **ESM Only**: LobeHub UI 仅支持 ESM
|
||||||
2. **React 19**: 必须使用 `@types/react@^19.0.0` 和 `@types/react-dom@^19.0.0`
|
2. **React 19**: 使用 `@types/react@^19.0.0`
|
||||||
3. **CSS 方案**: 使用 `antd-style`,不使用 Tailwind CSS
|
3. **CSS 方案**: 使用 `antd-style`,禁止 Tailwind
|
||||||
4. **Context Isolation**: 必须启用 `contextIsolation: true`
|
4. **禁止 `as any`**: 使用类型守卫或 `unknown`
|
||||||
5. **禁止类型断言**: 避免使用 `as any`,优先使用类型守卫
|
5. **函数组件优先**: 禁止 class 组件
|
||||||
|
|
||||||
|
## 11. MVP Phase - Breaking Changes
|
||||||
|
|
||||||
|
**This is MVP phase - breaking changes are acceptable for better design.** However, you MUST:
|
||||||
|
|
||||||
|
- **Explain what will break and why**: Document which components/APIs/workflows will be affected
|
||||||
|
- **Compare old vs new approach**: Show the differences and improvements
|
||||||
|
- **Document the tradeoffs**: What are the pros and cons of this change
|
||||||
|
- **Ask for confirmation**: If the change is significant (affects multiple modules or core architecture)
|
||||||
|
|
||||||
|
**Examples of acceptable breaking changes during MVP**:
|
||||||
|
|
||||||
|
- Refactoring data structures for better type safety
|
||||||
|
- Changing IPC communication patterns
|
||||||
|
- Restructuring component hierarchy
|
||||||
|
- Modifying store architecture
|
||||||
|
- Updating API interfaces
|
||||||
|
|
||||||
|
**Process for breaking changes**:
|
||||||
|
|
||||||
|
1. Identify the change and its impact scope
|
||||||
|
2. Document the breaking change in code comments
|
||||||
|
3. Explain the reasoning and benefits
|
||||||
|
4. If significant, ask user for confirmation before implementing
|
||||||
|
5. Update related documentation after implementation
|
||||||
|
|||||||
123
package-lock.json
generated
123
package-lock.json
generated
@@ -16,6 +16,7 @@
|
|||||||
"@codemirror/view": "^6.36.0",
|
"@codemirror/view": "^6.36.0",
|
||||||
"@electron-toolkit/preload": "^3.0.1",
|
"@electron-toolkit/preload": "^3.0.1",
|
||||||
"@electron-toolkit/utils": "^3.0.0",
|
"@electron-toolkit/utils": "^3.0.0",
|
||||||
|
"@kintone/rest-api-client": "^6.1.2",
|
||||||
"@lobehub/ui": "^5.5.0",
|
"@lobehub/ui": "^5.5.0",
|
||||||
"@uiw/react-codemirror": "^4.23.0",
|
"@uiw/react-codemirror": "^4.23.0",
|
||||||
"antd": "^6.1.0",
|
"antd": "^6.1.0",
|
||||||
@@ -2383,6 +2384,35 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@kintone/rest-api-client": {
|
||||||
|
"version": "6.1.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@kintone/rest-api-client/-/rest-api-client-6.1.2.tgz",
|
||||||
|
"integrity": "sha512-/6RMKD/cNg8KOfQDXLOTzKkfG+EAKuU6czgVF5b5c4Na9uFB9BENmdg7e1eV3qNwszfIt7UpONfT3apCiw2R3Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.12.2",
|
||||||
|
"core-js": "^3.46.0",
|
||||||
|
"form-data": "^4.0.5",
|
||||||
|
"js-base64": "^3.7.8",
|
||||||
|
"mime": "^3.0.0",
|
||||||
|
"qs": "^6.14.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@kintone/rest-api-client/node_modules/mime": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/mime/-/mime-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"mime": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@lezer/common": {
|
"node_modules/@lezer/common": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmmirror.com/@lezer/common/-/common-1.5.1.tgz",
|
"resolved": "https://registry.npmmirror.com/@lezer/common/-/common-1.5.1.tgz",
|
||||||
@@ -5909,7 +5939,6 @@
|
|||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
|
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/at-least-node": {
|
"node_modules/at-least-node": {
|
||||||
@@ -5957,6 +5986,17 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.13.6",
|
||||||
|
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.6.tgz",
|
||||||
|
"integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.11",
|
||||||
|
"form-data": "^4.0.5",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/babel-plugin-macros": {
|
"node_modules/babel-plugin-macros": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmmirror.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
||||||
@@ -6355,7 +6395,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
@@ -6369,7 +6408,6 @@
|
|||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz",
|
"resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz",
|
||||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind-apply-helpers": "^1.0.2",
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
@@ -6707,7 +6745,6 @@
|
|||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"delayed-stream": "~1.0.0"
|
"delayed-stream": "~1.0.0"
|
||||||
@@ -6841,6 +6878,17 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/core-js": {
|
||||||
|
"version": "3.48.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.48.0.tgz",
|
||||||
|
"integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/core-js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/core-util-is": {
|
"node_modules/core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||||
@@ -7671,7 +7719,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
@@ -7919,7 +7966,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind-apply-helpers": "^1.0.1",
|
"call-bind-apply-helpers": "^1.0.1",
|
||||||
@@ -8409,7 +8455,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -8419,7 +8464,6 @@
|
|||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
|
||||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -8458,7 +8502,6 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0"
|
"es-errors": "^1.3.0"
|
||||||
@@ -8471,7 +8514,6 @@
|
|||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
@@ -9253,6 +9295,26 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.11",
|
||||||
|
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||||
|
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/for-each": {
|
"node_modules/for-each": {
|
||||||
"version": "0.3.5",
|
"version": "0.3.5",
|
||||||
"resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz",
|
"resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz",
|
||||||
@@ -9312,7 +9374,6 @@
|
|||||||
"version": "4.0.5",
|
"version": "4.0.5",
|
||||||
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz",
|
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz",
|
||||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
@@ -9487,7 +9548,6 @@
|
|||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind-apply-helpers": "^1.0.2",
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
@@ -9512,7 +9572,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
|
||||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dunder-proto": "^1.0.1",
|
"dunder-proto": "^1.0.1",
|
||||||
@@ -9704,7 +9763,6 @@
|
|||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
|
||||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -9806,7 +9864,6 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -9819,7 +9876,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has-symbols": "^1.0.3"
|
"has-symbols": "^1.0.3"
|
||||||
@@ -10991,6 +11047,12 @@
|
|||||||
"jiti": "lib/jiti-cli.mjs"
|
"jiti": "lib/jiti-cli.mjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/js-base64": {
|
||||||
|
"version": "3.7.8",
|
||||||
|
"resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.8.tgz",
|
||||||
|
"integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/js-cookie": {
|
"node_modules/js-cookie": {
|
||||||
"version": "3.0.5",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz",
|
"resolved": "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz",
|
||||||
@@ -11480,7 +11542,6 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -12684,7 +12745,6 @@
|
|||||||
"version": "1.52.0",
|
"version": "1.52.0",
|
||||||
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
|
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
@@ -12694,7 +12754,6 @@
|
|||||||
"version": "2.1.35",
|
"version": "2.1.35",
|
||||||
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
|
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mime-db": "1.52.0"
|
"mime-db": "1.52.0"
|
||||||
@@ -13206,7 +13265,6 @@
|
|||||||
"version": "1.13.4",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz",
|
"resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -13890,6 +13948,12 @@
|
|||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/pump": {
|
"node_modules/pump": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.4.tgz",
|
"resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.4.tgz",
|
||||||
@@ -13910,6 +13974,21 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/qs": {
|
||||||
|
"version": "6.15.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/qs/-/qs-6.15.0.tgz",
|
||||||
|
"integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"side-channel": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/query-string": {
|
"node_modules/query-string": {
|
||||||
"version": "9.3.1",
|
"version": "9.3.1",
|
||||||
"resolved": "https://registry.npmmirror.com/query-string/-/query-string-9.3.1.tgz",
|
"resolved": "https://registry.npmmirror.com/query-string/-/query-string-9.3.1.tgz",
|
||||||
@@ -15314,7 +15393,6 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz",
|
||||||
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
@@ -15334,7 +15412,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
||||||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
@@ -15351,7 +15428,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
||||||
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bound": "^1.0.2",
|
"call-bound": "^1.0.2",
|
||||||
@@ -15370,7 +15446,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
||||||
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bound": "^1.0.2",
|
"call-bound": "^1.0.2",
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
"@codemirror/view": "^6.36.0",
|
"@codemirror/view": "^6.36.0",
|
||||||
"@electron-toolkit/preload": "^3.0.1",
|
"@electron-toolkit/preload": "^3.0.1",
|
||||||
"@electron-toolkit/utils": "^3.0.0",
|
"@electron-toolkit/utils": "^3.0.0",
|
||||||
|
"@kintone/rest-api-client": "^6.1.2",
|
||||||
"@lobehub/ui": "^5.5.0",
|
"@lobehub/ui": "^5.5.0",
|
||||||
"@uiw/react-codemirror": "^4.23.0",
|
"@uiw/react-codemirror": "^4.23.0",
|
||||||
"antd": "^6.1.0",
|
"antd": "^6.1.0",
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
/**
|
import { KintoneRestAPIClient } from "@kintone/rest-api-client";
|
||||||
* Kintone REST API Client
|
import type { KintoneRestAPIError } from "@kintone/rest-api-client";
|
||||||
* Handles authentication and API calls to Kintone
|
|
||||||
* Based on REQUIREMENTS.md:331-345, 502-522
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { DomainWithPassword } from "@renderer/types/domain";
|
import type { DomainWithPassword } from "@renderer/types/domain";
|
||||||
import type {
|
import type {
|
||||||
KintoneSpace,
|
KintoneSpace,
|
||||||
@@ -12,10 +8,10 @@ import type {
|
|||||||
FileContent,
|
FileContent,
|
||||||
AppCustomizationConfig,
|
AppCustomizationConfig,
|
||||||
KintoneApiError,
|
KintoneApiError,
|
||||||
|
JSFileConfig,
|
||||||
|
CSSFileConfig,
|
||||||
} from "@renderer/types/kintone";
|
} from "@renderer/types/kintone";
|
||||||
|
|
||||||
const REQUEST_TIMEOUT = 30000; // 30 seconds
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom error class for Kintone API errors
|
* Custom error class for Kintone API errors
|
||||||
*/
|
*/
|
||||||
@@ -41,446 +37,256 @@ export class KintoneError extends Error {
|
|||||||
* Kintone REST API Client
|
* Kintone REST API Client
|
||||||
*/
|
*/
|
||||||
export class KintoneClient {
|
export class KintoneClient {
|
||||||
private baseUrl: string;
|
private client: KintoneRestAPIClient;
|
||||||
private headers: Headers;
|
|
||||||
private domain: string;
|
private domain: string;
|
||||||
|
|
||||||
constructor(domainConfig: DomainWithPassword) {
|
constructor(domainConfig: DomainWithPassword) {
|
||||||
this.domain = domainConfig.domain;
|
this.domain = domainConfig.domain;
|
||||||
this.baseUrl = `https://${domainConfig.domain}/k/v1/`;
|
|
||||||
this.headers = new Headers({
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (domainConfig.authType === "api_token") {
|
const auth =
|
||||||
// API Token authentication
|
domainConfig.authType === "api_token"
|
||||||
this.headers.set("X-Cybozu-API-Token", domainConfig.apiToken || "");
|
? { apiToken: domainConfig.apiToken || "" }
|
||||||
} else {
|
: {
|
||||||
// Password authentication (Basic Auth)
|
username: domainConfig.username,
|
||||||
const credentials = Buffer.from(
|
password: domainConfig.password,
|
||||||
`${domainConfig.username}:${domainConfig.password}`,
|
};
|
||||||
).toString("base64");
|
|
||||||
this.headers.set("X-Cybozu-Authorization", credentials);
|
this.client = new KintoneRestAPIClient({
|
||||||
}
|
baseUrl: `https://${domainConfig.domain}`,
|
||||||
|
auth,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private convertError(error: unknown): KintoneError {
|
||||||
* Make an API request with timeout
|
if (error && typeof error === "object" && "code" in error) {
|
||||||
*/
|
const apiError = error as KintoneRestAPIError;
|
||||||
private async request<T>(
|
return new KintoneError(
|
||||||
endpoint: string,
|
apiError.message,
|
||||||
options: RequestInit = {},
|
{ code: apiError.code, message: apiError.message, id: apiError.id },
|
||||||
): Promise<T> {
|
apiError.status,
|
||||||
const controller = new AbortController();
|
|
||||||
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const url = this.baseUrl + endpoint;
|
|
||||||
const response = await fetch(url, {
|
|
||||||
...options,
|
|
||||||
headers: this.headers,
|
|
||||||
signal: controller.signal,
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const apiError = data as KintoneApiError;
|
|
||||||
throw new KintoneError(
|
|
||||||
apiError.message || `API request failed: ${response.status}`,
|
|
||||||
apiError,
|
|
||||||
response.status,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data as T;
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof KintoneError) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
if (error.name === "AbortError") {
|
return new KintoneError(error.message);
|
||||||
throw new KintoneError("Request timeout");
|
|
||||||
}
|
}
|
||||||
throw new KintoneError(`Network error: ${error.message}`);
|
|
||||||
|
return new KintoneError("Unknown error occurred");
|
||||||
}
|
}
|
||||||
throw new KintoneError("Unknown error occurred");
|
|
||||||
} finally {
|
private async withErrorHandling<T>(operation: () => Promise<T>): Promise<T> {
|
||||||
clearTimeout(timeoutId);
|
try {
|
||||||
|
return await operation();
|
||||||
|
} catch (error) {
|
||||||
|
throw this.convertError(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Space APIs ====================
|
private mapSpace(space: {
|
||||||
|
id: string | number;
|
||||||
/**
|
|
||||||
* Get list of spaces
|
|
||||||
* GET /k/v1/space.json
|
|
||||||
*/
|
|
||||||
async getSpaces(): Promise<KintoneSpace[]> {
|
|
||||||
interface SpacesResponse {
|
|
||||||
spaces: Array<{
|
|
||||||
id: string;
|
|
||||||
name: string;
|
name: string;
|
||||||
code: string;
|
code: string;
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
creator?: { code: string; name: string };
|
creator?: { code: string; name: string };
|
||||||
}>;
|
}): KintoneSpace {
|
||||||
}
|
return {
|
||||||
|
id: String(space.id),
|
||||||
const response = await this.request<SpacesResponse>("space.json");
|
|
||||||
|
|
||||||
return response.spaces.map((space) => ({
|
|
||||||
id: space.id,
|
|
||||||
name: space.name,
|
name: space.name,
|
||||||
code: space.code,
|
code: space.code,
|
||||||
createdAt: space.createdAt,
|
createdAt: space.createdAt,
|
||||||
creator: space.creator,
|
creator: space.creator
|
||||||
}));
|
? { code: space.creator.code, name: space.creator.name }
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== App APIs ====================
|
private mapApp(app: {
|
||||||
|
appId: string | number;
|
||||||
/**
|
|
||||||
* Get list of apps, optionally filtered by space
|
|
||||||
* GET /k/v1/apps.json?space={spaceId}
|
|
||||||
*/
|
|
||||||
async getApps(spaceId?: string): Promise<KintoneApp[]> {
|
|
||||||
interface AppsResponse {
|
|
||||||
apps: Array<{
|
|
||||||
appId: string;
|
|
||||||
name: string;
|
name: string;
|
||||||
code?: string;
|
code?: string;
|
||||||
spaceId?: string;
|
spaceId?: string | number;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
creator?: { code: string; name: string };
|
creator?: { code: string; name: string };
|
||||||
modifiedAt?: string;
|
modifiedAt?: string;
|
||||||
modifier?: { code: string; name: string };
|
modifier?: { code: string; name: string };
|
||||||
}>;
|
}): KintoneApp {
|
||||||
}
|
return {
|
||||||
|
appId: String(app.appId),
|
||||||
let endpoint = "apps.json";
|
|
||||||
if (spaceId) {
|
|
||||||
endpoint += `?space=${spaceId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await this.request<AppsResponse>(endpoint);
|
|
||||||
|
|
||||||
return response.apps.map((app) => ({
|
|
||||||
appId: app.appId,
|
|
||||||
name: app.name,
|
name: app.name,
|
||||||
code: app.code,
|
code: app.code,
|
||||||
spaceId: app.spaceId,
|
spaceId: app.spaceId ? String(app.spaceId) : undefined,
|
||||||
createdAt: app.createdAt,
|
createdAt: app.createdAt,
|
||||||
creator: app.creator,
|
creator: app.creator,
|
||||||
modifiedAt: app.modifiedAt,
|
modifiedAt: app.modifiedAt,
|
||||||
modifier: app.modifier,
|
modifier: app.modifier,
|
||||||
}));
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapResource(resource: {
|
||||||
|
type: string;
|
||||||
|
file?: { fileKey: string; name: string };
|
||||||
|
url?: string;
|
||||||
|
}): JSFileConfig | CSSFileConfig {
|
||||||
|
return {
|
||||||
|
type: resource.type as "FILE" | "URL",
|
||||||
|
file: resource.file
|
||||||
|
? { fileKey: resource.file.fileKey, name: resource.file.name, size: 0 }
|
||||||
|
: undefined,
|
||||||
|
url: resource.url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildCustomizeSection(
|
||||||
|
js?: JSFileConfig[],
|
||||||
|
css?: CSSFileConfig[],
|
||||||
|
):
|
||||||
|
| { js?: CustomizeResourceItem[]; css?: CustomizeResourceItem[] }
|
||||||
|
| undefined {
|
||||||
|
if (!js && !css) return undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
js: js?.map((item) => ({
|
||||||
|
type: item.type,
|
||||||
|
file: item.file ? { fileKey: item.file.fileKey } : undefined,
|
||||||
|
url: item.url,
|
||||||
|
})),
|
||||||
|
css: css?.map((item) => ({
|
||||||
|
type: item.type,
|
||||||
|
file: item.file ? { fileKey: item.file.fileKey } : undefined,
|
||||||
|
url: item.url,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== Space APIs ====================
|
||||||
|
|
||||||
|
async getSpaces(): Promise<KintoneSpace[]> {
|
||||||
|
return this.withErrorHandling(async () => {
|
||||||
|
const response = await this.client.space.getSpaces();
|
||||||
|
return response.spaces.map((space) => this.mapSpace(space));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== App APIs ====================
|
||||||
|
|
||||||
|
async getApps(spaceId?: string): Promise<KintoneApp[]> {
|
||||||
|
return this.withErrorHandling(async () => {
|
||||||
|
const params = spaceId ? { spaceId } : {};
|
||||||
|
const response = await this.client.app.getApps(params);
|
||||||
|
return response.apps.map((app) => this.mapApp(app));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get app details including customization config
|
|
||||||
* GET /k/v1/app.json?app={appId}
|
|
||||||
*/
|
|
||||||
async getAppDetail(appId: string): Promise<AppDetail> {
|
async getAppDetail(appId: string): Promise<AppDetail> {
|
||||||
interface AppResponse {
|
return this.withErrorHandling(async () => {
|
||||||
appId: string;
|
const [appInfo, customizeInfo] = await Promise.all([
|
||||||
name: string;
|
this.client.app.getApp({ id: appId }),
|
||||||
code?: string;
|
this.client.app.getAppCustomize({ app: appId }),
|
||||||
description?: string;
|
]);
|
||||||
spaceId?: string;
|
|
||||||
createdAt: string;
|
|
||||||
creator: { code: string; name: string };
|
|
||||||
modifiedAt: string;
|
|
||||||
modifier: { code: string; name: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AppCustomizeResponse {
|
|
||||||
appId: string;
|
|
||||||
scope: string;
|
|
||||||
desktop: {
|
|
||||||
js?: Array<{
|
|
||||||
type: string;
|
|
||||||
file?: { fileKey: string; name: string };
|
|
||||||
url?: string;
|
|
||||||
}>;
|
|
||||||
css?: Array<{
|
|
||||||
type: string;
|
|
||||||
file?: { fileKey: string; name: string };
|
|
||||||
url?: string;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
mobile: {
|
|
||||||
js?: Array<{
|
|
||||||
type: string;
|
|
||||||
file?: { fileKey: string; name: string };
|
|
||||||
url?: string;
|
|
||||||
}>;
|
|
||||||
css?: Array<{
|
|
||||||
type: string;
|
|
||||||
file?: { fileKey: string; name: string };
|
|
||||||
url?: string;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get basic app info
|
|
||||||
const appInfo = await this.request<AppResponse>(`app.json?app=${appId}`);
|
|
||||||
|
|
||||||
// Get customization config
|
|
||||||
const customizeInfo = await this.request<AppCustomizeResponse>(
|
|
||||||
`app/customize.json?app=${appId}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Transform customization config
|
|
||||||
const customization: AppCustomizationConfig = {
|
const customization: AppCustomizationConfig = {
|
||||||
javascript: {
|
javascript: {
|
||||||
pc:
|
pc:
|
||||||
customizeInfo.desktop?.js?.map((js) => ({
|
customizeInfo.desktop?.js?.map((js) => this.mapResource(js)) || [],
|
||||||
type: js.type as "FILE" | "URL",
|
|
||||||
file: js.file
|
|
||||||
? { fileKey: js.file.fileKey, name: js.file.name, size: 0 }
|
|
||||||
: undefined,
|
|
||||||
url: js.url,
|
|
||||||
})) || [],
|
|
||||||
mobile:
|
mobile:
|
||||||
customizeInfo.mobile?.js?.map((js) => ({
|
customizeInfo.mobile?.js?.map((js) => this.mapResource(js)) || [],
|
||||||
type: js.type as "FILE" | "URL",
|
|
||||||
file: js.file
|
|
||||||
? { fileKey: js.file.fileKey, name: js.file.name, size: 0 }
|
|
||||||
: undefined,
|
|
||||||
url: js.url,
|
|
||||||
})) || [],
|
|
||||||
},
|
},
|
||||||
stylesheet: {
|
stylesheet: {
|
||||||
pc:
|
pc:
|
||||||
customizeInfo.desktop?.css?.map((css) => ({
|
customizeInfo.desktop?.css?.map((css) => this.mapResource(css)) ||
|
||||||
type: css.type as "FILE" | "URL",
|
[],
|
||||||
file: css.file
|
|
||||||
? { fileKey: css.file.fileKey, name: css.file.name, size: 0 }
|
|
||||||
: undefined,
|
|
||||||
url: css.url,
|
|
||||||
})) || [],
|
|
||||||
mobile:
|
mobile:
|
||||||
customizeInfo.mobile?.css?.map((css) => ({
|
customizeInfo.mobile?.css?.map((css) => this.mapResource(css)) ||
|
||||||
type: css.type as "FILE" | "URL",
|
[],
|
||||||
file: css.file
|
|
||||||
? { fileKey: css.file.fileKey, name: css.file.name, size: 0 }
|
|
||||||
: undefined,
|
|
||||||
url: css.url,
|
|
||||||
})) || [],
|
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
appId: appInfo.appId,
|
appId: String(appInfo.appId),
|
||||||
name: appInfo.name,
|
name: appInfo.name,
|
||||||
code: appInfo.code,
|
code: appInfo.code,
|
||||||
description: appInfo.description,
|
description: appInfo.description,
|
||||||
spaceId: appInfo.spaceId,
|
spaceId: appInfo.spaceId ? String(appInfo.spaceId) : undefined,
|
||||||
createdAt: appInfo.createdAt,
|
createdAt: appInfo.createdAt,
|
||||||
creator: appInfo.creator,
|
creator: appInfo.creator,
|
||||||
modifiedAt: appInfo.modifiedAt,
|
modifiedAt: appInfo.modifiedAt,
|
||||||
modifier: appInfo.modifier,
|
modifier: appInfo.modifier,
|
||||||
customization,
|
customization,
|
||||||
};
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== File APIs ====================
|
// ==================== File APIs ====================
|
||||||
|
|
||||||
/**
|
|
||||||
* Get file content from Kintone
|
|
||||||
* GET /k/v1/file.json?fileKey={fileKey}
|
|
||||||
*/
|
|
||||||
async getFileContent(fileKey: string): Promise<FileContent> {
|
async getFileContent(fileKey: string): Promise<FileContent> {
|
||||||
const controller = new AbortController();
|
return this.withErrorHandling(async () => {
|
||||||
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);
|
const data = await this.client.file.downloadFile({ fileKey });
|
||||||
|
const buffer = Buffer.from(data);
|
||||||
try {
|
const content = buffer.toString("base64");
|
||||||
const url = `${this.baseUrl}file.json?fileKey=${fileKey}`;
|
|
||||||
const response = await fetch(url, {
|
|
||||||
headers: this.headers,
|
|
||||||
signal: controller.signal,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const error = await response.json();
|
|
||||||
throw new KintoneError(
|
|
||||||
error.message || "Failed to get file",
|
|
||||||
error,
|
|
||||||
response.status,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const contentType =
|
|
||||||
response.headers.get("content-type") || "application/octet-stream";
|
|
||||||
const arrayBuffer = await response.arrayBuffer();
|
|
||||||
const content = Buffer.from(arrayBuffer).toString("base64");
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fileKey,
|
fileKey,
|
||||||
name: fileKey, // Kintone doesn't return filename in file API
|
name: fileKey,
|
||||||
size: arrayBuffer.byteLength,
|
size: buffer.byteLength,
|
||||||
mimeType: contentType,
|
mimeType: "application/octet-stream",
|
||||||
content,
|
content,
|
||||||
};
|
};
|
||||||
} finally {
|
});
|
||||||
clearTimeout(timeoutId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload a file to Kintone
|
|
||||||
* POST /k/v1/file.json (multipart/form-data)
|
|
||||||
*/
|
|
||||||
async uploadFile(
|
async uploadFile(
|
||||||
content: string | Buffer,
|
content: string | Buffer,
|
||||||
fileName: string,
|
fileName: string,
|
||||||
mimeType: string = "application/javascript",
|
_mimeType?: string,
|
||||||
): Promise<{ fileKey: string }> {
|
): Promise<{ fileKey: string }> {
|
||||||
const controller = new AbortController();
|
return this.withErrorHandling(async () => {
|
||||||
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);
|
const response = await this.client.file.uploadFile({
|
||||||
|
file: { name: fileName, data: content },
|
||||||
try {
|
});
|
||||||
const url = `${this.baseUrl}file.json`;
|
return { fileKey: response.fileKey };
|
||||||
|
|
||||||
// Create form data
|
|
||||||
const formData = new FormData();
|
|
||||||
const blob = new Blob(
|
|
||||||
[typeof content === "string" ? content : Buffer.from(content)],
|
|
||||||
{ type: mimeType },
|
|
||||||
);
|
|
||||||
formData.append("file", blob, fileName);
|
|
||||||
|
|
||||||
// Remove Content-Type header to let browser set it with boundary
|
|
||||||
const uploadHeaders = new Headers(this.headers);
|
|
||||||
uploadHeaders.delete("Content-Type");
|
|
||||||
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "POST",
|
|
||||||
headers: uploadHeaders,
|
|
||||||
body: formData,
|
|
||||||
signal: controller.signal,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const error = await response.json();
|
|
||||||
throw new KintoneError(
|
|
||||||
error.message || "Failed to upload file",
|
|
||||||
error,
|
|
||||||
response.status,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
return { fileKey: data.fileKey };
|
|
||||||
} finally {
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Deploy APIs ====================
|
// ==================== Deploy APIs ====================
|
||||||
|
|
||||||
/**
|
|
||||||
* Update app customization config
|
|
||||||
* PUT /k/v1/app/customize.json
|
|
||||||
*/
|
|
||||||
async updateAppCustomize(
|
async updateAppCustomize(
|
||||||
appId: string,
|
appId: string,
|
||||||
config: AppCustomizationConfig,
|
config: AppCustomizationConfig,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
interface CustomizeUpdateRequest {
|
return this.withErrorHandling(async () => {
|
||||||
app: string;
|
await this.client.app.updateAppCustomize({
|
||||||
desktop?: {
|
|
||||||
js?: Array<{ type: string; file?: { fileKey: string }; url?: string }>;
|
|
||||||
css?: Array<{ type: string; file?: { fileKey: string }; url?: string }>;
|
|
||||||
};
|
|
||||||
mobile?: {
|
|
||||||
js?: Array<{ type: string; file?: { fileKey: string }; url?: string }>;
|
|
||||||
css?: Array<{ type: string; file?: { fileKey: string }; url?: string }>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestBody: CustomizeUpdateRequest = {
|
|
||||||
app: appId,
|
app: appId,
|
||||||
desktop: {
|
desktop: this.buildCustomizeSection(
|
||||||
js: config.javascript?.pc
|
config.javascript?.pc,
|
||||||
?.map((js) => ({
|
config.stylesheet?.pc,
|
||||||
type: js.type,
|
),
|
||||||
file: js.file ? { fileKey: js.file.fileKey } : undefined,
|
mobile: this.buildCustomizeSection(
|
||||||
url: js.url,
|
config.javascript?.mobile,
|
||||||
}))
|
config.stylesheet?.mobile,
|
||||||
.filter(Boolean),
|
),
|
||||||
css: config.stylesheet?.pc
|
scope: "ALL",
|
||||||
?.map((css) => ({
|
});
|
||||||
type: css.type,
|
|
||||||
file: css.file ? { fileKey: css.file.fileKey } : undefined,
|
|
||||||
url: css.url,
|
|
||||||
}))
|
|
||||||
.filter(Boolean),
|
|
||||||
},
|
|
||||||
mobile: {
|
|
||||||
js: config.javascript?.mobile
|
|
||||||
?.map((js) => ({
|
|
||||||
type: js.type,
|
|
||||||
file: js.file ? { fileKey: js.file.fileKey } : undefined,
|
|
||||||
url: js.url,
|
|
||||||
}))
|
|
||||||
.filter(Boolean),
|
|
||||||
css: config.stylesheet?.mobile
|
|
||||||
?.map((css) => ({
|
|
||||||
type: css.type,
|
|
||||||
file: css.file ? { fileKey: css.file.fileKey } : undefined,
|
|
||||||
url: css.url,
|
|
||||||
}))
|
|
||||||
.filter(Boolean),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
await this.request("app/customize.json", {
|
|
||||||
method: "PUT",
|
|
||||||
body: JSON.stringify(requestBody),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Deploy app changes
|
|
||||||
* POST /k/v1/preview/app/deploy.json
|
|
||||||
*/
|
|
||||||
async deployApp(appId: string): Promise<void> {
|
async deployApp(appId: string): Promise<void> {
|
||||||
await this.request("preview/app/deploy.json", {
|
return this.withErrorHandling(async () => {
|
||||||
method: "POST",
|
await this.client.app.deployApp({ apps: [{ app: appId }] });
|
||||||
body: JSON.stringify({ apps: [{ app: appId }] }),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get deploy status
|
|
||||||
* GET /k/v1/preview/app/deploy.json?app={appId}
|
|
||||||
*/
|
|
||||||
async getDeployStatus(
|
async getDeployStatus(
|
||||||
appId: string,
|
appId: string,
|
||||||
): Promise<"PROCESSING" | "SUCCESS" | "FAIL" | "CANCEL"> {
|
): Promise<"PROCESSING" | "SUCCESS" | "FAIL" | "CANCEL"> {
|
||||||
interface DeployStatusResponse {
|
return this.withErrorHandling(async () => {
|
||||||
apps: Array<{
|
const response = await this.client.app.getDeployStatus({ app: appId });
|
||||||
app: string;
|
return response.apps[0]?.status || "FAIL";
|
||||||
status: "PROCESSING" | "SUCCESS" | "FAIL" | "CANCEL";
|
});
|
||||||
}>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await this.request<DeployStatusResponse>(
|
|
||||||
`preview/app/deploy.json?app=${appId}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
const appStatus = response.apps.find((a) => a.app === appId);
|
|
||||||
return appStatus?.status || "FAIL";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Utility Methods ====================
|
// ==================== Utility Methods ====================
|
||||||
|
|
||||||
/**
|
|
||||||
* Test connection to Kintone
|
|
||||||
*/
|
|
||||||
async testConnection(): Promise<{ success: boolean; error?: string }> {
|
async testConnection(): Promise<{ success: boolean; error?: string }> {
|
||||||
try {
|
try {
|
||||||
await this.getApps();
|
await this.getApps();
|
||||||
@@ -494,15 +300,17 @@ export class KintoneClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the domain name
|
|
||||||
*/
|
|
||||||
getDomain(): string {
|
getDomain(): string {
|
||||||
return this.domain;
|
return this.domain;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export factory function for convenience
|
type CustomizeResourceItem = {
|
||||||
|
type: string;
|
||||||
|
file?: { fileKey: string };
|
||||||
|
url?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export function createKintoneClient(domain: DomainWithPassword): KintoneClient {
|
export function createKintoneClient(domain: DomainWithPassword): KintoneClient {
|
||||||
return new KintoneClient(domain);
|
return new KintoneClient(domain);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user