@lark-base-open/base-database-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +169 -0
  3. package/changelog/CHANGELOG-0.x.md +103 -0
  4. package/config-schema.json +28 -0
  5. package/dist/agent-guide.d.ts +18 -0
  6. package/dist/agent-guide.d.ts.map +1 -0
  7. package/dist/agent-guide.js +30 -0
  8. package/dist/agent-guide.js.map +1 -0
  9. package/dist/archive.d.ts +16 -0
  10. package/dist/archive.d.ts.map +1 -0
  11. package/dist/archive.js +162 -0
  12. package/dist/archive.js.map +1 -0
  13. package/dist/assets.d.ts +4 -0
  14. package/dist/assets.d.ts.map +1 -0
  15. package/dist/assets.js +6 -0
  16. package/dist/assets.js.map +1 -0
  17. package/dist/auth.d.ts +17 -0
  18. package/dist/auth.d.ts.map +1 -0
  19. package/dist/auth.js +63 -0
  20. package/dist/auth.js.map +1 -0
  21. package/dist/cli.d.ts +3 -0
  22. package/dist/cli.d.ts.map +1 -0
  23. package/dist/cli.js +15 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/config.d.ts +5 -0
  26. package/dist/config.d.ts.map +1 -0
  27. package/dist/config.js +50 -0
  28. package/dist/config.js.map +1 -0
  29. package/dist/database-skill.d.ts +22 -0
  30. package/dist/database-skill.d.ts.map +1 -0
  31. package/dist/database-skill.js +135 -0
  32. package/dist/database-skill.js.map +1 -0
  33. package/dist/deploy.d.ts +28 -0
  34. package/dist/deploy.d.ts.map +1 -0
  35. package/dist/deploy.js +476 -0
  36. package/dist/deploy.js.map +1 -0
  37. package/dist/environment.d.ts +12 -0
  38. package/dist/environment.d.ts.map +1 -0
  39. package/dist/environment.js +27 -0
  40. package/dist/environment.js.map +1 -0
  41. package/dist/function-schema.d.ts +3 -0
  42. package/dist/function-schema.d.ts.map +1 -0
  43. package/dist/function-schema.js +310 -0
  44. package/dist/function-schema.js.map +1 -0
  45. package/dist/init.d.ts +77 -0
  46. package/dist/init.d.ts.map +1 -0
  47. package/dist/init.js +418 -0
  48. package/dist/init.js.map +1 -0
  49. package/dist/lark-cli.d.ts +8 -0
  50. package/dist/lark-cli.d.ts.map +1 -0
  51. package/dist/lark-cli.js +228 -0
  52. package/dist/lark-cli.js.map +1 -0
  53. package/dist/npm.d.ts +13 -0
  54. package/dist/npm.d.ts.map +1 -0
  55. package/dist/npm.js +93 -0
  56. package/dist/npm.js.map +1 -0
  57. package/dist/page-title.d.ts +3 -0
  58. package/dist/page-title.d.ts.map +1 -0
  59. package/dist/page-title.js +47 -0
  60. package/dist/page-title.js.map +1 -0
  61. package/dist/preview-receipt.d.ts +23 -0
  62. package/dist/preview-receipt.d.ts.map +1 -0
  63. package/dist/preview-receipt.js +133 -0
  64. package/dist/preview-receipt.js.map +1 -0
  65. package/dist/process.d.ts +17 -0
  66. package/dist/process.d.ts.map +1 -0
  67. package/dist/process.js +53 -0
  68. package/dist/process.js.map +1 -0
  69. package/dist/program.d.ts +12 -0
  70. package/dist/program.d.ts.map +1 -0
  71. package/dist/program.js +204 -0
  72. package/dist/program.js.map +1 -0
  73. package/dist/pull.d.ts +18 -0
  74. package/dist/pull.d.ts.map +1 -0
  75. package/dist/pull.js +324 -0
  76. package/dist/pull.js.map +1 -0
  77. package/dist/skill.d.ts +7 -0
  78. package/dist/skill.d.ts.map +1 -0
  79. package/dist/skill.js +28 -0
  80. package/dist/skill.js.map +1 -0
  81. package/dist/types.d.ts +32 -0
  82. package/dist/types.d.ts.map +1 -0
  83. package/dist/types.js +2 -0
  84. package/dist/types.js.map +1 -0
  85. package/dist/workspace-fingerprint.d.ts +8 -0
  86. package/dist/workspace-fingerprint.d.ts.map +1 -0
  87. package/dist/workspace-fingerprint.js +64 -0
  88. package/dist/workspace-fingerprint.js.map +1 -0
  89. package/package.json +54 -0
  90. package/skills/lark-base-database/SKILL.md +307 -0
  91. package/skills/lark-base-database/references/base-capability-routing.md +81 -0
  92. package/skills/lark-base-database/references/base-sql-unsupported.md +42 -0
  93. package/skills/lark-base-database/references/database-capability-skill.md +24 -0
  94. package/skills/lark-base-database/references/development.md +642 -0
  95. package/skills/lark-base-database/references/file-transfer.md +275 -0
  96. package/skills/lark-base-database/references/permission-design.md +138 -0
  97. package/skills/lark-base-database/references/release.md +81 -0
  98. package/skills/lark-base-database/references/sql-function.md +276 -0
  99. package/skills/lark-base-database/references/style-guide.md +88 -0
  100. package/templates/database-capability/SKILL.md.tmpl +41 -0
  101. package/templates/database-starter/README.md +11 -0
  102. package/templates/database-starter/gitignore +3 -0
  103. package/templates/database-starter/page/.prettierignore +4 -0
  104. package/templates/database-starter/page/.prettierrc.json +7 -0
  105. package/templates/database-starter/page/README.md +29 -0
  106. package/templates/database-starter/page/index.html +24 -0
  107. package/templates/database-starter/page/package-lock.json +2675 -0
  108. package/templates/database-starter/page/package.json +44 -0
  109. package/templates/database-starter/page/public/favicon.png +0 -0
  110. package/templates/database-starter/page/src/App.tsx +18 -0
  111. package/templates/database-starter/page/src/components/UserSelectorEditor/index.tsx +267 -0
  112. package/templates/database-starter/page/src/components/UserSelectorPanel/index.css +32 -0
  113. package/templates/database-starter/page/src/components/UserSelectorPanel/index.test.ts +24 -0
  114. package/templates/database-starter/page/src/components/UserSelectorPanel/index.tsx +478 -0
  115. package/templates/database-starter/page/src/components/UserSelectorPanel/user-search.test.ts +175 -0
  116. package/templates/database-starter/page/src/components/UserSelectorPanel/user-search.ts +201 -0
  117. package/templates/database-starter/page/src/components/database-runtime-ready.tsx +21 -0
  118. package/templates/database-starter/page/src/components/database-user-menu/index.tsx +238 -0
  119. package/templates/database-starter/page/src/components/feedback/page-state.tsx +66 -0
  120. package/templates/database-starter/page/src/components/floating-button/chatbot-entry.test.ts +23 -0
  121. package/templates/database-starter/page/src/components/floating-button/chatbot-entry.tsx +873 -0
  122. package/templates/database-starter/page/src/components/floating-button/index.tsx +196 -0
  123. package/templates/database-starter/page/src/components/floating-button/local-chat-panel.test.ts +25 -0
  124. package/templates/database-starter/page/src/components/floating-button/local-chat-panel.tsx +197 -0
  125. package/templates/database-starter/page/src/components/floating-button/utils/current-user.ts +56 -0
  126. package/templates/database-starter/page/src/components/floating-button/utils/env.ts +18 -0
  127. package/templates/database-starter/page/src/components/floating-button/utils/project-meta.test.ts +79 -0
  128. package/templates/database-starter/page/src/components/floating-button/utils/project-meta.ts +252 -0
  129. package/templates/database-starter/page/src/components/ui/button.tsx +56 -0
  130. package/templates/database-starter/page/src/index.css +67 -0
  131. package/templates/database-starter/page/src/lib/cn.test.ts +9 -0
  132. package/templates/database-starter/page/src/lib/cn.ts +7 -0
  133. package/templates/database-starter/page/src/lib/database-runtime-attachment.test.ts +168 -0
  134. package/templates/database-starter/page/src/lib/database-runtime-context.test.ts +111 -0
  135. package/templates/database-starter/page/src/lib/database-runtime.test.ts +103 -0
  136. package/templates/database-starter/page/src/lib/database-runtime.ts +389 -0
  137. package/templates/database-starter/page/src/main.tsx +34 -0
  138. package/templates/database-starter/page/src/vite-env.d.ts +1 -0
  139. package/templates/database-starter/page/tsconfig.json +23 -0
  140. package/templates/database-starter/page/vite.config.ts +13 -0
  141. package/templates/database-starter/prettierignore +1 -0
  142. package/templates/database-starter/server-functions/.prettierignore +3 -0
  143. package/templates/database-starter/server-functions/.prettierrc.json +7 -0
  144. package/templates/database-starter/server-functions/README.md +11 -0
  145. package/templates/database-starter/server-functions/package-lock.json +1687 -0
  146. package/templates/database-starter/server-functions/package.json +22 -0
  147. package/templates/database-starter/server-functions/scripts/run-tsc.mjs +50 -0
  148. package/templates/database-starter/server-functions/tsconfig.json +20 -0
  149. package/templates/database-starter/sql/README.md +8 -0
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "page",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=20.19.0"
8
+ },
9
+ "scripts": {
10
+ "dev": "vite",
11
+ "build": "tsc -b && vite build",
12
+ "preview": "vite preview",
13
+ "check": "tsc -b --noEmit",
14
+ "format": "prettier --write .",
15
+ "format:check": "prettier --check .",
16
+ "lint": "oxlint src --deny-warnings",
17
+ "quality": "npm run format:check && npm run lint && npm run check && npm run test && npm run build",
18
+ "test": "vitest run"
19
+ },
20
+ "dependencies": {
21
+ "@lark-base-open/base-site-channel": "https://lf3-static.bytednsdoc.com/obj/eden-cn/vlseh7bfnuhps/@lark-base-open-base-site-channel-0.0.1-alpha.35.tgz",
22
+ "@lark-base-open/base-site-js": "https://lf3-static.bytednsdoc.com/obj/eden-cn/jjjpceh7nulojvhj/@lark-base-open-base-site-js-0.0.1-alpha.25.tgz",
23
+ "@radix-ui/react-slot": "^1.3.0",
24
+ "class-variance-authority": "^0.7.1",
25
+ "clsx": "^2.1.1",
26
+ "lucide-react": "^1.24.0",
27
+ "react": "^19.2.7",
28
+ "react-dom": "^19.2.7",
29
+ "tailwind-merge": "^3.6.0"
30
+ },
31
+ "devDependencies": {
32
+ "@tailwindcss/vite": "^4.3.2",
33
+ "@types/node": "^26.1.1",
34
+ "@types/react": "^19.2.17",
35
+ "@types/react-dom": "^19.2.3",
36
+ "@vitejs/plugin-react": "^6.0.3",
37
+ "oxlint": "^1.42.0",
38
+ "prettier": "^3.7.4",
39
+ "tailwindcss": "^4.3.2",
40
+ "typescript": "~7.0.2",
41
+ "vite": "^8.1.4",
42
+ "vitest": "^4.1.0"
43
+ }
44
+ }
@@ -0,0 +1,18 @@
1
+ export default function App() {
2
+ return (
3
+ <main className="flex min-h-screen items-center justify-center bg-background px-6 py-12 text-foreground">
4
+ <section className="w-full max-w-2xl rounded-3xl border border-border bg-card p-8 shadow-sm sm:p-12">
5
+ <p className="text-sm font-semibold tracking-[0.18em] text-primary uppercase">
6
+ Base Database
7
+ </p>
8
+ <h1 className="mt-4 text-3xl font-semibold tracking-tight text-balance sm:text-5xl">
9
+ 从真实业务任务开始构建
10
+ </h1>
11
+ <p className="mt-5 max-w-xl text-base leading-7 text-muted-foreground sm:text-lg">
12
+ 使用页面组件承载交互,通过受控 Function 连接 Base SQL 与
13
+ FaaS,并在交付前替换这段起始内容。
14
+ </p>
15
+ </section>
16
+ </main>
17
+ )
18
+ }
@@ -0,0 +1,267 @@
1
+ import React, {
2
+ useCallback,
3
+ useEffect,
4
+ useRef,
5
+ useState,
6
+ type FocusEventHandler,
7
+ type KeyboardEventHandler,
8
+ type MouseEventHandler,
9
+ } from 'react'
10
+
11
+ import { cn } from '../../lib/cn'
12
+ import UserSelectorPanel, {
13
+ type User,
14
+ type UserSelectorAction,
15
+ type UserSelectorMode,
16
+ type UserSelectorSearchAction,
17
+ type UserSelectorValue,
18
+ } from '../UserSelectorPanel'
19
+
20
+ export type UserSelectorEditorValue = UserSelectorValue
21
+ export type { UserSelectorMode } from '../UserSelectorPanel'
22
+
23
+ export interface UserSelectorEditorProps {
24
+ value: UserSelectorEditorValue | null | undefined
25
+ loading?: boolean
26
+ mode?: UserSelectorMode
27
+ onClick?: MouseEventHandler<HTMLDivElement>
28
+ onChange?: (value: UserSelectorEditorValue | undefined) => void
29
+ onSelect?: (user: User, meta: { option: User }) => void
30
+ onClear?: () => void
31
+ onRemove?: (user: User) => void
32
+ onSelectRecordCreator?: () => void
33
+ getUserDescription?: (user: User) => string | undefined
34
+ onDropdownVisibleChange?: (visible: boolean) => void
35
+ onSearch?: (value: string, meta: { action: UserSelectorSearchAction }) => void
36
+ onKeyDown?: KeyboardEventHandler<HTMLInputElement>
37
+ onFocus?: FocusEventHandler<HTMLButtonElement>
38
+ onBlur?: FocusEventHandler<HTMLButtonElement>
39
+ }
40
+
41
+ /**
42
+ * 展示受控成员值,并闭环管理 UserSelectorPanel 的打开、关闭与值回传。
43
+ */
44
+ export default function UserSelectorEditor(
45
+ props: UserSelectorEditorProps,
46
+ ): React.JSX.Element {
47
+ const {
48
+ value,
49
+ loading = false,
50
+ mode,
51
+ onClick,
52
+ onChange,
53
+ onSelect,
54
+ onClear,
55
+ onRemove,
56
+ onSelectRecordCreator,
57
+ getUserDescription,
58
+ onDropdownVisibleChange,
59
+ onSearch,
60
+ onKeyDown,
61
+ onFocus,
62
+ onBlur,
63
+ } = props
64
+ const [open, setOpen] = useState(false)
65
+ const rootRef = useRef<HTMLDivElement>(null)
66
+ const panelOpen = open
67
+ const selectedUsers = Array.isArray(value) ? value : value ? [value] : []
68
+ const isMultiple = mode === 'multiple'
69
+
70
+ const updateOpen = useCallback(
71
+ (nextOpen: boolean) => {
72
+ setOpen(nextOpen)
73
+ if (nextOpen !== open) onDropdownVisibleChange?.(nextOpen)
74
+ },
75
+ [onDropdownVisibleChange, open],
76
+ )
77
+
78
+ useEffect(() => {
79
+ if (!panelOpen) return
80
+
81
+ const handlePointerDown = (event: MouseEvent) => {
82
+ if (!rootRef.current?.contains(event.target as Node)) {
83
+ updateOpen(false)
84
+ }
85
+ }
86
+
87
+ document.addEventListener('mousedown', handlePointerDown)
88
+ return () => document.removeEventListener('mousedown', handlePointerDown)
89
+ }, [panelOpen, updateOpen])
90
+
91
+ const closePanel = () => {
92
+ updateOpen(false)
93
+ }
94
+
95
+ const updateValue = (nextValue: UserSelectorEditorValue | undefined) => {
96
+ onChange?.(nextValue)
97
+ }
98
+
99
+ const handlePanelChange = (
100
+ nextValue: UserSelectorEditorValue | undefined,
101
+ meta: { option: User | undefined; action: UserSelectorAction },
102
+ ) => {
103
+ updateValue(nextValue)
104
+ if (meta.action === 'Remove' && meta.option) onRemove?.(meta.option)
105
+ }
106
+
107
+ const handleTriggerClick: MouseEventHandler<HTMLDivElement> = (event) => {
108
+ onClick?.(event)
109
+ if (event.defaultPrevented) return
110
+
111
+ updateOpen(!open)
112
+ }
113
+
114
+ const renderAvatar = (user: User) => {
115
+ const avatarUrl = user.avatar.avatar_72 || user.avatar.avatar_240
116
+
117
+ return (
118
+ <span className="relative flex size-5 shrink-0 items-center justify-center overflow-hidden rounded-full bg-[#e8f0fe] text-[10px] leading-none font-medium text-[#1456f0]">
119
+ {user.name.slice(0, 1)}
120
+ {avatarUrl ? (
121
+ <img
122
+ alt=""
123
+ className="absolute inset-0 size-full object-cover"
124
+ onError={(event) => {
125
+ event.currentTarget.hidden = true
126
+ }}
127
+ src={avatarUrl}
128
+ />
129
+ ) : null}
130
+ </span>
131
+ )
132
+ }
133
+
134
+ return (
135
+ <div className="relative w-full" ref={rootRef}>
136
+ <div
137
+ className={cn(
138
+ 'flex min-h-8 w-full cursor-pointer items-center rounded-md border border-[#dee0e3] bg-white p-0 text-left text-sm leading-[22px] text-[#1f2329] outline-none transition-colors duration-200 hover:border-[#346df4]',
139
+ panelOpen && 'border-[#346df4]',
140
+ )}
141
+ onClick={handleTriggerClick}
142
+ >
143
+ <span
144
+ className={cn(
145
+ 'flex w-full items-center justify-center gap-2 rounded-md px-1.5 py-[3px]',
146
+ isMultiple && 'items-start',
147
+ )}
148
+ >
149
+ <span
150
+ className={cn(
151
+ 'flex min-h-6 min-w-0 flex-1 items-center gap-2 overflow-hidden',
152
+ isMultiple &&
153
+ 'flex-wrap content-start gap-x-2 gap-y-1 overflow-visible [&>*]:shrink-0',
154
+ )}
155
+ >
156
+ {selectedUsers.length > 0 ? (
157
+ selectedUsers.map((selectedUser) => (
158
+ <span
159
+ className="inline-flex h-6 max-w-full min-w-0 items-center gap-1 overflow-hidden rounded-full bg-transparent pr-1.5 pl-0.5 text-[13px] leading-6 text-[#1f2329] hover:bg-[rgb(194_212_255)]"
160
+ key={selectedUser.user_id}
161
+ >
162
+ <button
163
+ aria-expanded={panelOpen}
164
+ aria-haspopup="listbox"
165
+ className="flex h-full min-w-0 flex-[0_1_auto] items-center gap-1 border-0 bg-transparent p-0 text-left text-inherit outline-none"
166
+ onBlur={onBlur}
167
+ onFocus={onFocus}
168
+ type="button"
169
+ >
170
+ {renderAvatar(selectedUser)}
171
+ <span className="min-w-0 flex-[0_1_auto] truncate">
172
+ {selectedUser.name}
173
+ </span>
174
+ </button>
175
+ <button
176
+ aria-label={
177
+ isMultiple
178
+ ? `移除成员 ${selectedUser.name}`
179
+ : '清除已选成员'
180
+ }
181
+ className="relative ml-0 flex size-3.5 shrink-0 cursor-pointer items-center justify-center rounded-sm border-0 bg-transparent p-0 !text-[#646a73] outline-none hover:!text-[#0442d2]"
182
+ onClick={(event) => {
183
+ event.stopPropagation()
184
+ if (isMultiple) {
185
+ updateValue(
186
+ selectedUsers.filter(
187
+ (user) => user.user_id !== selectedUser.user_id,
188
+ ),
189
+ )
190
+ onRemove?.(selectedUser)
191
+ } else {
192
+ updateValue(undefined)
193
+ onClear?.()
194
+ }
195
+ }}
196
+ type="button"
197
+ >
198
+ <svg
199
+ aria-hidden="true"
200
+ data-icon="CloseSmallOutlined"
201
+ fill="none"
202
+ height="1em"
203
+ viewBox="0 0 24 24"
204
+ width="1em"
205
+ xmlns="http://www.w3.org/2000/svg"
206
+ >
207
+ <path
208
+ d="M5.636 5.636a1 1 0 0 0 0 1.414l4.95 4.95-4.95 4.95a1 1 0 1 0 1.414 1.414l4.95-4.95 4.95 4.95a1 1 0 0 0 1.414-1.414L13.414 12l4.95-4.95a1 1 0 0 0-1.415-1.414L12 10.586l-4.95-4.95a1 1 0 0 0-1.413 0Z"
209
+ fill="currentColor"
210
+ />
211
+ </svg>
212
+ </button>
213
+ </span>
214
+ ))
215
+ ) : (
216
+ <button
217
+ aria-label="选择成员"
218
+ aria-expanded={panelOpen}
219
+ aria-haspopup="listbox"
220
+ className="flex h-full min-w-0 flex-1 items-center gap-1 border-0 bg-transparent p-0 text-left text-inherit outline-none"
221
+ onBlur={onBlur}
222
+ onFocus={onFocus}
223
+ type="button"
224
+ >
225
+ <span className="min-w-0 flex-1 truncate text-[#8f959e]">
226
+ 选择成员
227
+ </span>
228
+ </button>
229
+ )}
230
+ </span>
231
+ <span
232
+ aria-hidden="true"
233
+ className={cn(
234
+ 'flex size-3 shrink-0 items-center justify-center text-[#646a73] transition-transform duration-200',
235
+ isMultiple && 'mt-1.5',
236
+ panelOpen && 'rotate-180',
237
+ )}
238
+ >
239
+ <svg className="block size-3" viewBox="0 0 24 24">
240
+ <path
241
+ d="m3.414 7.086-.707.707a1 1 0 0 0 0 1.414l7.778 7.778a2 2 0 0 0 2.829 0l7.778-7.778a1 1 0 0 0 0-1.414l-.707-.707a1 1 0 0 0-1.415 0l-7.07 7.07-7.072-7.07a1 1 0 0 0-1.414 0Z"
242
+ fill="currentColor"
243
+ />
244
+ </svg>
245
+ </span>
246
+ </span>
247
+ </div>
248
+
249
+ {panelOpen ? (
250
+ <div className="absolute top-[calc(100%+4px)] left-px z-20 w-[calc(100%-2px)]">
251
+ <UserSelectorPanel
252
+ getUserDescription={getUserDescription}
253
+ loading={loading}
254
+ mode={mode}
255
+ onChange={handlePanelChange}
256
+ onClose={closePanel}
257
+ onKeyDown={onKeyDown}
258
+ onSearch={onSearch}
259
+ onSelect={onSelect}
260
+ onSelectRecordCreator={onSelectRecordCreator}
261
+ value={value}
262
+ />
263
+ </div>
264
+ ) : null}
265
+ </div>
266
+ )
267
+ }
@@ -0,0 +1,32 @@
1
+ .user-selector-loading-spinner {
2
+ animation: user-selector-loading-rotate 1.2s linear infinite;
3
+ }
4
+
5
+ .user-selector-loading-spinner circle {
6
+ animation: user-selector-loading-dash 2.5s ease-in-out infinite;
7
+ stroke-dasharray: 1, 220;
8
+ stroke-dashoffset: 0;
9
+ }
10
+
11
+ @keyframes user-selector-loading-rotate {
12
+ to {
13
+ transform: rotate(1turn);
14
+ }
15
+ }
16
+
17
+ @keyframes user-selector-loading-dash {
18
+ 0% {
19
+ stroke-dasharray: 1, 220;
20
+ stroke-dashoffset: 0;
21
+ }
22
+
23
+ 50% {
24
+ stroke-dasharray: 150, 220;
25
+ stroke-dashoffset: -68px;
26
+ }
27
+
28
+ 100% {
29
+ stroke-dasharray: 150, 220;
30
+ stroke-dashoffset: -218px;
31
+ }
32
+ }
@@ -0,0 +1,24 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { isKeyboardEventComposing } from './index'
4
+
5
+ describe('isKeyboardEventComposing', () => {
6
+ it('recognizes native IME composition', () => {
7
+ expect(
8
+ isKeyboardEventComposing({ nativeEvent: { isComposing: true } }),
9
+ ).toBe(true)
10
+ })
11
+
12
+ it('recognizes the legacy IME key code', () => {
13
+ expect(isKeyboardEventComposing({ keyCode: 229 })).toBe(true)
14
+ })
15
+
16
+ it('allows normal keyboard navigation', () => {
17
+ expect(
18
+ isKeyboardEventComposing({
19
+ keyCode: 40,
20
+ nativeEvent: { isComposing: false },
21
+ }),
22
+ ).toBe(false)
23
+ })
24
+ })