# KitJS — Hướng dẫn package bằng tiếng Việt

KitJS bổ sung state cục bộ, binding, event, điều kiện, danh sách và component
nhỏ vào HTML thông thường bằng các thuộc tính `data-kit-*`. Người dùng website
tĩnh chỉ cần tải đúng một classic browser script từ CDN; không cần frontend
compiler, virtual DOM, Go hoặc server Kitwork.

> **Bản build stable:** source và browser artifact trong checkout này mang
> version `1.0.0`. Chỉ ghi nhận trạng thái công khai trên npm/CDN và việc npm
> `latest` trỏ đúng bản này sau khi truy xuất độc lập; xem
> [`RELEASE_READINESS.md`](RELEASE_READINESS.md). Production nên pin exact
> version cùng SRI ở bên dưới.

Tài liệu này chỉ mô tả public API của package standalone chạy trong browser.
JITJS là hệ thống phân phối phía server của Kitwork, không phải API của package
KitJS và nằm ngoài phạm vi hướng dẫn này.

## 1. Bắt đầu với một script CDN

Để thử nhanh, URL jsDelivr rút gọn tự đi theo npm `latest` và có thể được
jsDelivr tự động minify:

```html
<script defer src="https://cdn.jsdelivr.net/npm/@kitwork/kitjs"></script>
```

URL này tự nâng cấp và byte có thể thay đổi. Không gắn SRI của `dist/kit.js`
vào URL rút gọn. Với production, dùng file readable được pin chính xác cùng
SRI tương ứng như ví dụ tiếp theo.

Dùng profile Kit khi link và form phải điều hướng theo cách bình thường của
browser:

```html
<!doctype html>
<html lang="vi">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>KitJS counter</title>
  <script
    defer
    src="https://cdn.jsdelivr.net/npm/@kitwork/kitjs@1.0.0/dist/kit.js"
    integrity="sha256-LXt1DK4QG43susUNwzTQp7H046G1/gONdOhBAcXVIZI="
    crossorigin="anonymous"></script>
</head>
<body>
  <section data-kit-scope="count: 0">
    <button type="button" data-kit-click="count--">−</button>
    <output data-kit-text="count">0</output>
    <button type="button" data-kit-click="count++">+</button>
  </section>
</body>
</html>
```

`data-kit-scope` tạo một boundary state nông. Các directive con dùng boundary
gần nhất; các phép ghi đồng bộ được gom vào một lượt render. UI cục bộ như ví
dụ trên không cần đăng ký component.

## 2. Chọn đúng một profile

| Profile | File | Điều hướng |
|---|---|---|
| **Kit** | `dist/kit.js` | Browser tải document mới theo cách bình thường |
| **Hydrate** | `dist/hydrate.kit.js` | Drive/Morph tùy chọn cho route tương thích, nếu không thì native fallback |

Hydrate đã chứa toàn bộ reactive runtime của Kit. Không tải cả hai profile
trong cùng một document.

Exact CDN tag của Hydrate:

```html
<script
  defer
  src="https://cdn.jsdelivr.net/npm/@kitwork/kitjs@1.0.0/dist/hydrate.kit.js"
  integrity="sha256-AbI+PkXOVgSxZDNiMjQCxehrcASWQuW+mJHrSmfS57k="
  crossorigin="anonymous"></script>
```

Nếu destination không đáp ứng hợp đồng tương thích, Hydrate giao việc điều
hướng lại cho browser trước khi thay đổi document hiện tại. Native reload là
hành vi fallback được hỗ trợ.

## 3. Scope và directive

Scope chỉ chứa dữ liệu, không chứa JavaScript tùy ý:

```html
<section data-kit-scope="count: 3; open: true; profile: { name: 'Ada' }">
  <button type="button" data-kit-click="open = !open">Ẩn/hiện</button>
  <p data-kit-show="open">
    <strong data-kit-text="profile.name">Ada</strong>
    đã bấm <output data-kit-text="count">3</output> lần.
  </p>
</section>
```

Có thể dùng dạng object:

```html
<section data-kit-scope="{ count: 3, open: true }"></section>
```

Field top-level dùng ASCII identifier. Value có thể là `null`, boolean, finite
number, string, array hoặc plain object. Scope declaration từ chối call,
property read, lambda và chương trình khởi tạo có hành vi.

Các directive chính:

| Mục đích | Ví dụ |
|---|---|
| State cục bộ | `data-kit-scope="count: 0; open: true"` |
| Text | `data-kit-text="user?.name ?? 'Guest'"` |
| Hiển thị | `data-kit-show="open"` |
| Attribute an toàn | `data-kit-bind="aria-expanded: open; disabled: busy"` |
| Class | `data-kit-class="open ? 'block opacity-100' : 'hidden opacity-0'"` |
| Style liên tục | `data-kit-style="width: progress + '%'; opacity: visible ? 1 : 0"` |
| Form state | `data-kit-model="name"` |
| Event | `data-kit-click:prevent="save()"` |
| Nhánh một element | `<p data-kit-if="ready">Sẵn sàng</p>` |
| Nhánh fragment | `<template data-kit-if="ready">...</template>` |
| Danh sách có key | `<template data-kit-for="item, i of items" data-kit-key="item.id">...</template>` |
| Bỏ ownership | `data-kit-ignore` |

Directive lạ hoặc expression không hợp lệ sẽ fail closed.

### `if`, `show` và `for`

Dạng phổ biến của `1.0.0` đặt `if` trực tiếp trên element:

```html
<section data-kit-scope="ready: false">
  <button type="button" data-kit-click="ready = !ready">Chuyển trạng thái</button>
  <p data-kit-if="ready">Đã sẵn sàng.</p>
</section>
```

Dùng `template` khi cần nhiều node ngang hàng, không muốn wrapper, hoặc muốn
content inert cho đến lúc materialize:

```html
<template data-kit-if="ready">
  <h2>Đã sẵn sàng</h2>
  <p>Cả fragment mount cùng nhau.</p>
</template>
```

`data-kit-for` và `data-kit-key` chỉ dùng trên `template`. `data-kit-show` giữ
node đã mount và chỉ đổi visibility; `data-kit-if` dispose branch khi false.
Direct branch ban đầu true giữ nguyên authored host. Sau khi branch đã unmount,
lần true tiếp theo tạo node và component identity mới.

Element trực tiếp là DOM bình thường trước khi runtime chuẩn bị, nên browser có
thể paint hoặc bắt đầu tải resource. Client `if` không phải authorization,
secrecy, inertness hoặc bảo đảm không flash. Không đặt secret hay quyết định
phân quyền trong HTML phía client.

## 4. Event và expression

```html
<form data-kit-submit:prevent="save()">
  <input data-kit-keydown:escape="close()">
  <button data-kit-click:once="count++">Chạy một lần</button>
  <button data-kit-click:debounce(250)="search()">Tìm kiếm</button>
</form>
```

Event được hỗ trợ: `click`, `dblclick`, `submit`, `input`, `change`, `keydown`,
`keyup`, `pointerdown`, `pointerup`, `focusin`, `focusout`.

Modifier: `self`, `prevent`, `stop`, `once`, `outside`, `enter`, `escape`,
`debounce(ms)`.

Binding chỉ đọc. Action có thể gán hoặc dùng `++`/`--` với field top-level đã
tồn tại và có thể ghi. Nhiều phép ghi chỉ commit cùng nhau khi toàn bộ action
đồng bộ thành công.

Expression là một ngôn ngữ đóng, không phải JavaScript đầy đủ. Nó không có
declaration, loop, constructor, template literal, member assignment, browser
global hoặc đường thoát qua prototype. Runtime không dùng `eval()` hay
`Function` để chạy authored expression.

## 5. Component standalone

Chỉ dùng component khi boundary cần trusted method hoặc lifecycle. Component
của package CDN luôn được đăng ký trực tiếp bằng tên không có version:

```html
<section data-kit-component="counter">
  <button type="button" data-kit-click="increment()">Tăng</button>
  <output data-kit-text="count">0</output>
</section>

<script
  defer
  src="https://cdn.jsdelivr.net/npm/@kitwork/kitjs@1.0.0/dist/kit.js"
  integrity="sha256-LXt1DK4QG43susUNwzTQp7H046G1/gONdOhBAcXVIZI="
  crossorigin="anonymous"></script>
<script defer src="/assets/components.js"></script>
```

```js
// /assets/components.js
kit.component("counter", {
  count: 0,
  increment() {
    this.count += 1;
  }
});
```

Mỗi host nhận một shallow instance riêng. `data-kit-scope` trên cùng host có
thể seed field dữ liệu writable đã tồn tại nhưng không thể thêm field hoặc thay
method. `data-kit-as="$name"` tạo alias chỉ dùng trong action.

Nếu host gọi một component chưa được đăng ký, KitJS báo missing definition một
lần và giữ nguyên fallback DOM đã viết trong HTML. Nó không tải component từ
server và không reload trang. Một đăng ký trực tiếp đến sau vẫn có thể mount
những host phù hợp đang còn kết nối.

`init(context)` là hook tùy chọn cho trusted JavaScript. Frozen context có đúng
`host`, `owned(selector)`, `listen(...)`, `cleanup(fn)` và `afterRender(fn)`.
Component chỉ có data và method không cần `init`.

Public JavaScript API đầy đủ của package là:

```text
kit.version
kit.component(name, plainObject)
```

`globalThis.kit` đã được freeze. Package không có public compiler, renderer,
manual mount/destroy, plugin loader, navigation object hoặc service registry.
`data-kit-version` không được hỗ trợ và không có marker `data-kit-local`.

## 6. Hydrate và Drive

Drive chỉ tăng cường link cùng origin và form GET đủ điều kiện; nó không cần
router riêng và không chạy script lấy từ fetched HTML.

Để một nhóm route tương thích:

- Hydrate phải là classic external `defer` script và là con trực tiếp của
  `head`;
- mọi trang phải giữ nguyên resolved URL, vị trí, thứ tự và toàn bộ attribute
  của script;
- authored executable script khác cũng phải giữ cùng external direct-head
  topology;
- script khác origin, kể cả jsDelivr, phải có SRI hợp lệ và không được dùng
  `data-kit-drive="stable"`.

Với file tự host cùng origin, có thể dùng `data-kit-drive="stable"` như một cam
kết của tác giả rằng URL, bytes, vị trí, thứ tự và attribute không đổi:

```html
<script defer src="/assets/hydrate.kit.js" data-kit-drive="stable"></script>
```

`stable` không kiểm tra nội dung. Production nên ưu tiên URL có version hoặc
content hash kèm SRI. Inline script, body script, module, import map,
speculation rule, `async`, `nomodule` hoặc thay đổi executable topology làm
destination không tương thích và browser sẽ điều hướng bình thường.

Vì vậy inline `kit.component(...)` dùng được với profile Kit nhưng khiến
Hydrate để browser reload. Nếu cần Drive, chuyển đăng ký component sang một file
external có exact tag giống nhau trên mọi route tương thích.

Quy tắc đó áp dụng cho mọi executable inline script, kể cả
`<script>console.log("hello world")</script>`. Hydrate không reload vì nội dung
script; nó chỉ không nhận quyền điều hướng bằng Drive, nên lần điều hướng tiếp
theo trở thành full document load bình thường do browser thực hiện.

Dùng `data-kit-drive="false"` trên link, form GET, submitter hoặc ancestor khi
một route phải luôn điều hướng bình thường.

Hydrate kiểm tra topology ban đầu trước khi intercept. Initial page không hợp
lệ giữ nguyên native navigation và phát một diagnostic. Destination không
tương thích fallback trước khi thay đổi title, head, history hoặc body. Response
trên 8 MiB, document sau parse trên 100.000 node hoặc sâu hơn 256 cũng native
fallback.

Same-document fragment vẫn do browser xử lý. Với cross-route tương thích,
Hydrate giữ fragment, focus, scroll, form, Back/Forward và cleanup component
theo browser contract.

`data-kit-ignore` là ownership/Morph boundary, không phải sanitizer.
`data-kit-retain="key"` giữ một registered component host standalone và live
state qua navigation tương thích; key phải unique, ổn định và không nằm trong
structural branch.

## 7. Ranh giới bảo mật

- Authored expression không truy cập `window`, `document`, native event hoặc
  trusted object `kit`.
- `data-kit-bind` chặn event handler, raw style, `srcdoc`, HTML replacement,
  URL scheme nguy hiểm và target `data-kit-*`.
- `data-kit-style` kiểm tra cả property map trước khi ghi.
- Hydrate không chạy script trong fetched HTML; trang không tương thích được
  giao cho browser document loader.
- Component definition là trusted application JavaScript và vẫn có quyền
  JavaScript bình thường của trang.

KitJS thu hẹp quyền của authored expression nhưng không phải HTML sanitizer
tổng quát và không tự sửa một ứng dụng không an toàn.

## 8. Identity của bản phát hành

Build xác định `1.0.0` trong checkout này tạo ra hai profile readable sau:

| Profile | Bytes | SHA-256 | SRI |
|---|---:|---|---|
| Kit | 206.607 | `2d7b750cae101b8decbac50dc334d0a7b1f4e3a1b5fe038d74e84101c5d52192` | `sha256-LXt1DK4QG43susUNwzTQp7H046G1/gONdOhBAcXVIZI=` |
| Hydrate | 314.424 | `01b23e3e45ce5604b1643362323402c5e86b70049642e5be9891eb4a67d2e7b9` | `sha256-AbI+PkXOVgSxZDNiMjQCxehrcASWQuW+mJHrSmfS57k=` |

Các identity local này chưa tự chứng minh artifact đã có công khai. Bằng chứng
phát hành bất biến của `1.0.0-rc.2` vẫn nằm trong
[`RELEASE_READINESS.md`](RELEASE_READINESS.md) cho tới khi tag stable, package,
CDN, signature và provenance được kiểm tra độc lập.

`1.0.0` chứa `data-kit-if` trực tiếp trên ordinary element, lần đầu được phát
hành trong `1.0.0-rc.2`. Artifact `1.0.0-rc.1` bất biến vẫn yêu cầu
`<template data-kit-if>`.

Tài liệu cho người dùng:

- [`README.md`](README.md): hướng dẫn package chính.
- [`STATIC_DEPLOYMENT.md`](STATIC_DEPLOYMENT.md): CDN, self-host, SRI, CSP và cache.
- [`KITJS_SPEC.md`](KITJS_SPEC.md): browser contract đầy đủ.
- [`examples/static-hydrate/README.md`](examples/static-hydrate/README.md): ví dụ Drive nâng cao.
- [`SUPPORT.md`](SUPPORT.md) và [`SECURITY.md`](SECURITY.md): hỗ trợ và báo cáo bảo mật.

Quy trình build, test và release chỉ dành cho maintainer được tách riêng trong
[`BUILDING.md`](BUILDING.md) và [`RELEASE_READINESS.md`](RELEASE_READINESS.md).
