diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..cf4fafc8de --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,108 @@ +# AGENTS.md for Bot Framework Web Chat + +## Coding + +### General + +- Unless stated otherwise, avoid Node.js +- Apply our latest coding style to every file changed +- Avoid spaghetti code: on new feature with a similar existing feature, refactor existing one before writing new feature + - This does not applies to test code +- Avoid global pollution, do not modify `window` object +- Markdown are always space-indented with 3 spaces +- Use `/prettierrc.yml` to format +- Sort names in property bags +- Avoid one-use variable, inline them instead +- More new lines to separate code with different responsibilities + - Multiple lines of code that can run in random order, put them into a group, no need new line between them + - Otherwise, lines of code that depends on each other in a specific order, separate them with new line +- Prefer `&&` over if-statement if it is a oneliner +- Importing relative file should always include file extension +- Prefer truthy/false check like `!array.length` over `array.length === 0` +- Prefer `numValue` over `valueCount` for "how many values are there" +- Prefer uppercase for acronyms instead of Pascal case, e.g. `getURL()` over `getUrl()` + - The only exception is `id`, e.g. `getId()` over `getID()` +- Use fewer shorthands, only allow `min`, `max`, `num` + +### Design + +- Use immutability as much as possible, add `object.freeze()` +- Follow W3C convention for API design + - Extends `EventTarget` when eventing is needed, do not use Node.js `on`/`off` + - Consider `IntersectionObserver`-like pattern for allow userland to subscribe to live changes +- Allow ESNext code that is polyfilled by `core-js-pure` or `iter-fest` package + +### Typing + +- TypeScript is best-effort checking, use `valibot` for strict type checking +- Use `valibot` for runtime type checker, never use `zod` + - Assume all externally exported functions will receive unsafe/invalid input, always check with `valibot` +- Avoid `any` +- Avoid `as`, use `valibot` instead + - If absolutely needed, use `satisifes X as Y` to make sure it is `X` before force casting to `Y` +- Use as few `unknown` as possible +- All optional properties must be `undefined`-able, i.e. use `value?: number | undefined` over `value?: number` +- For functions exported outside of current file, make sure all arguments and return value are typed +- If need to look inside the object to check for types, use `valibot` + - E.g. `if (obj && typeof obj === 'object' && 'value' in obj && typeof obj.value === 'string')` should be replaced with `safeParse(object({ value: string }), obj).success` +- Use `{ readonly value: string }` instead of `Readonly<{ value: string }>` +- Use as much `readonly` as possible + +### React template + +```tsx +import { reactNode, validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import React from 'react'; +import { number, object, pipe, readonly } from 'valibot'; + +// Use valibot to validate props. +const MyComponentPropsSchema = pipe( + object({ + children: optional(reactNode()) + }), + readonly() +); + +type MyComponentProps = InferInput; + +// Use big function instead of arrow function. +function MyComponent(props: MyComponentProps) { + // Call `validateProps` to get a validated props. + const { children } = validateProps(MyComponentPropsSchema, props); + + // Use `<>` instead of ``. + // Never export `children` by itself, always wrap with `<>`. + return <>{children}; +} + +// Set `displayName`. +MyComponent.displayName = 'MyComponent'; + +// Export as memoized/exotic component, export as default. +export default memo(MyComponent); + +// Export both schema and typing. +export { MyComponentPropsSchema, type MyComponentProps }; +``` + +## Testing instructions + +- Practice test-driven development +- Build and run Docker to host WebDriver-controlled browser instances + - Build container by `docker compose -f docker-compose-wsl2.yml build --build-arg REGISTRY=mcr.microsoft.com` + - Start container by `docker compose -f docker-compose-wsl2.yml up --detach --scale chrome=2` +- Run `curl http://localhost:4444/wd/hub/status` and wait until `.value.ready` become `true` +- Run `npm test`, or `npm test -- --testPathPattern test-html-file.html` to focus on one +- Test files are HTML files under `/__tests__/html2/`, they are not JS +- Follow other HTML files under the same folder on how to write tests +- Page conditions, page elements, and page objects can be found at `/packages/test/page-object/src/globals` +- Use `expect` instead of `assert` +- Use `@testduet/given-when-then` package instead of xUnit style `describe`/`before`/`test`/`after` +- Prefer integration/end-to-end testing than unit testing +- Use as realistic setup as possible, such as using `msw` than mocking calls + +## PR instructions + +- Run new test and all of them must be green +- Run `npm run precommit` to make sure it pass all linting process +- Add changelog entry to `CHANGELOG.md`, follow our existing format diff --git a/HUMAN.md b/HUMAN.md new file mode 100644 index 0000000000..59f9fe2de8 --- /dev/null +++ b/HUMAN.md @@ -0,0 +1,39 @@ +# HUMAN.md for Bot Framework Web Chat + +## Coding + +### Package management + +- When adding dependencies, use `npm install` + - Do not add it to workspace root + - If it is an existing package, must use consistent version: either use existing version for the package or consider bumping every dependents to latest +- Unless stated otherwise, avoid Node.js packages or polyfills + - Use `ReadableStream`, `WritableStream`, `TransformStream`, instead of Node.js `buffer` + - Use Web Cryptography instead of Node.js `crypto` + - Do not use `fs`-like or `net`-like packages + - Do not use any Browserify-like packages +- Unless stated otherwise, verify newly added packages and transient packages must be either platform-neutral, browser-specific, or React-specific +- Do not add external/publishing packages unless explicitly requested +- Always prefix internal/non-publishing packages with `@msinternal/` to prevent package squatting + +### Platform complexity level + +This table list expect platform complexity in the running environment. + +| Package family | Platform complexity | Neutral | React Native | Full Browser | React | Node.js | +| ------------------------ | ------------------- | ------- | ------------ | ------------ | ----- | ------- | +| `base` | 100 - Neutral | ✅ | ❌ | ❌ | ❌ | ❌ | +| `core` | 100 - Neutral | ✅ | ❌ | ❌ | ❌ | ❌ | +| `api` | 200 - React Native | ✅ | ✅ | ❌ | ❌ | ❌ | +| `react-*` | 200 - React Native | ✅ | ✅ | ❌ | ❌ | ❌ | +| `redux-*` | 200 - React Native | ✅ | ✅ | ❌ | ❌ | ❌ | +| `component` | 300 - React (HTML) | ✅ | ✅ | ✅ | ✅ | ❌ | +| `bundle` | 300 - React (HTML) | ✅ | ✅ | ✅ | ✅ | ❌ | +| `fluent-theme`/`*-theme` | 300 - React (HTML) | ✅ | ✅ | ✅ | ✅ | ❌ | +| `support` | 100 - Neutral | ✅ | ❌ | ❌ | ❌ | ❌ | + +Descriptions of platform complexity: + +- 100 - Neutral: JavaScript engine only, logic only, UI-agnostic, minimal +- 200 - React Native: browser-like but not full browser, mobile app or web app +- 300 - React (HTML): full browser, web app diff --git a/packages/api-graph/AGENTS.md b/packages/api-graph/AGENTS.md new file mode 100644 index 0000000000..f351067a40 --- /dev/null +++ b/packages/api-graph/AGENTS.md @@ -0,0 +1,3 @@ +# AGENTS.md + +Refer to `/packages/api/AGENTS.md`. diff --git a/packages/api-middleware/AGENTS.md b/packages/api-middleware/AGENTS.md new file mode 100644 index 0000000000..f351067a40 --- /dev/null +++ b/packages/api-middleware/AGENTS.md @@ -0,0 +1,3 @@ +# AGENTS.md + +Refer to `/packages/api/AGENTS.md`. diff --git a/packages/api/AGENTS.md b/packages/api/AGENTS.md new file mode 100644 index 0000000000..f241e88c95 --- /dev/null +++ b/packages/api/AGENTS.md @@ -0,0 +1,5 @@ +# AGENTS.md + +## Coding + +- The code could run on React Native, no browser-specific technologies diff --git a/packages/base/AGENTS.md b/packages/base/AGENTS.md new file mode 100644 index 0000000000..27c1f03546 --- /dev/null +++ b/packages/base/AGENTS.md @@ -0,0 +1,5 @@ +# AGENTS.md + +## Coding + +- Stay platform neutral, no browser technologies, no Node.js, no React diff --git a/packages/core-debug-api/AGENTS.md b/packages/core-debug-api/AGENTS.md new file mode 100644 index 0000000000..852e5a183f --- /dev/null +++ b/packages/core-debug-api/AGENTS.md @@ -0,0 +1,3 @@ +# AGENTS.md + +Refer to `/packages/core/AGENTS.md`. diff --git a/packages/core-graph/AGENTS.md b/packages/core-graph/AGENTS.md new file mode 100644 index 0000000000..852e5a183f --- /dev/null +++ b/packages/core-graph/AGENTS.md @@ -0,0 +1,3 @@ +# AGENTS.md + +Refer to `/packages/core/AGENTS.md`. diff --git a/packages/core/AGENTS.md b/packages/core/AGENTS.md new file mode 100644 index 0000000000..27c1f03546 --- /dev/null +++ b/packages/core/AGENTS.md @@ -0,0 +1,5 @@ +# AGENTS.md + +## Coding + +- Stay platform neutral, no browser technologies, no Node.js, no React