Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@ name: Integration UnitTest
on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
- develop
workflow_call:

jobs:
macos-browser-test:
runs-on: macos-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/setup-node@v6
with:
Expand All @@ -33,7 +32,6 @@ jobs:
runs-on: windows-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/setup-node@v6
with:
Expand Down
8 changes: 3 additions & 5 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@ name: Lint
on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
- develop
workflow_call:

jobs:
macos-browser-test:
runs-on: macos-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
Expand All @@ -24,13 +23,12 @@ jobs:
registry-url: "https://registry.npmjs.org"
- run: npm install -g npm@latest
- run: npm install
- run: npx eslint ./src/**/*.ts
- run: npm run lint

windows-browser-test:
runs-on: windows-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
Expand All @@ -39,4 +37,4 @@ jobs:
registry-url: "https://registry.npmjs.org"
- run: npm install -g npm@latest
- run: npm install
- run: npx eslint ./src/**/*.ts
- run: npm run lint
11 changes: 11 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,19 @@ permissions:
contents: read

jobs:
lint:
uses: ./.github/workflows/lint.yml

integration:
uses: ./.github/workflows/integration.yml

Comment on lines +13 to +18
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the newly added lint/integration jobs, the workflow-level permissions (including id-token: write) will also apply to those jobs unless overridden. Consider explicitly setting minimal permissions on the lint and integration jobs (e.g., contents: read) and/or moving id-token: write to only the publish job to avoid over-privileging the reusable workflows.

Copilot uses AI. Check for mistakes.
publish:
needs: [lint, integration]
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pull-requests: write is granted to the publish job, but the steps shown don't interact with PRs. If it's not required (e.g., for commenting/changelog automation), please remove it to keep the job permissions least-privilege.

Suggested change
pull-requests: write

Copilot uses AI. Check for mistakes.
id-token: write
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
Expand Down
20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@next2d/framework",
"description": "Next2D Framework is designed according to the principles of clean architecture, domain-driven development, test-driven development, and MVVM, with an emphasis on flexibility, scalability, and maintainability, and a design methodology that keeps each layer loosely coupled.",
"version": "4.0.0",
"version": "4.0.1",
"homepage": "https://next2d.app",
"bugs": "https://github.com/Next2D/Framework/issues/new",
"author": "Toshiyuki Ienaga <ienaga@next2d.app> (https://github.com/ienaga/)",
Expand Down Expand Up @@ -31,17 +31,17 @@
"url": "git+https://github.com/Next2D/Framework.git"
},
"devDependencies": {
"@eslint/eslintrc": "^3.3.3",
"@eslint/js": "^9.39.2",
"@types/node": "^25.2.0",
"@typescript-eslint/eslint-plugin": "^8.54.0",
"@typescript-eslint/parser": "^8.54.0",
"@eslint/eslintrc": "^3.3.4",
"@eslint/js": "^10.0.1",
"@types/node": "^25.3.3",
"@typescript-eslint/eslint-plugin": "^8.56.1",
"@typescript-eslint/parser": "^8.56.1",
"@vitest/coverage-v8": "^4.0.18",
"@vitest/web-worker": "^4.0.18",
"eslint": "^9.39.2",
"eslint-plugin-unused-imports": "^4.3.0",
"globals": "^17.3.0",
"jsdom": "^28.0.0",
"eslint": "^10.0.2",
"eslint-plugin-unused-imports": "^4.4.1",
"globals": "^17.4.0",
"jsdom": "^28.1.0",
"typescript": "^5.9.3",
"vite": "^7.3.1",
"vitest": "^4.0.18",
Expand Down
35 changes: 33 additions & 2 deletions specs/cn/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ Next2D Framework 可以作为单页应用程序通过 URL 控制场景。路由
### 使用缓存

设置 `cache: true` 会缓存数据。缓存的数据在画面转换中持久存在。
`app.getCache()` 返回 `Map<string, unknown>`,可通过每个请求的 `name` 键访问。

缓存使用要点:

- 如果同一键已存在,请求处理可优先复用缓存值。
- 缓存不会自动清理,不再需要时请显式使用 `delete` 或 `clear`。

```json
{
Expand Down Expand Up @@ -216,7 +222,15 @@ export class HomeDataCallback

### app.gotoView()

使用 `app.gotoView()` 进行画面转换:
使用 `app.gotoView(name?: string)` 进行画面转换。其返回 `Promise<void>`,可等待请求完成、View/ViewModel 重新绑定以及 `onEnter()` 执行完成。

`gotoView` 要点:

- `name` 参数类型是 `string`(可省略,默认值为 `""`)。
- `name` 使用 `routing.json` 的键,例如 `home`、`quest/list`。
- 支持附带 `?id=123` 这类查询字符串。
- 省略 `name` 时,会从当前 URL 解析目标路由(SPA 的 `popstate` 流程)。
- 开始新转换时会清空上一轮的 `response` 数据。

```typescript
import { app } from "@next2d/framework";
Expand Down Expand Up @@ -267,9 +281,26 @@ export class TopViewModel extends ViewModel
}
```

### app.getContext()

使用 `app.getContext()` 获取当前运行中的 `Context`。其中包含 `root`(根 `Sprite`)、`view`、`viewModel` 引用。转换过程中 `view` 与 `viewModel` 可能暂时为 `null`。
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

文档称 app.getContext() 可获取当前运行的 Context,但实际 $getContext() 在 Context 尚未初始化时会抛错("Context is not initialized. Call run() first.")。建议补充说明需要先启动应用/设置 Context,否则 getContext() 可能抛异常。

Suggested change
使用 `app.getContext()` 获取当前运行中的 `Context`。其中包含 `root`(根 `Sprite`)、`view``viewModel` 引用。转换过程中 `view``viewModel` 可能暂时为 `null`
使用 `app.getContext()` 获取当前运行中的 `Context`。其中包含 `root`(根 `Sprite`)、`view``viewModel` 引用。转换过程中 `view``viewModel` 可能暂时为 `null`注意:必须先通过 `app.run()` 等方式启动应用并完成 `Context` 初始化后再调用本方法;如果在 `Context` 尚未初始化时调用,`app.getContext()` 会抛出 `"Context is not initialized. Call run() first."` 异常。

Copilot uses AI. Check for mistakes.

```typescript
import { app } from "@next2d/framework";

const context = app.getContext();
const root = context.root;
```

## 获取响应数据

`requests` 的数据可以通过 `app.getResponse()` 获取:
`app.getResponse()` 返回 `Map<string, unknown>`。它保存当前一次转换中 `requests` 里定义了 `name` 的响应数据。

`getResponse` 要点:

- 它是一次 `gotoView` 周期内的临时数据容器。
- 开始下一次 `gotoView` 时会重置。
- 值类型为 `unknown`,使用前请做类型守卫或类型断言。

```typescript
import { app } from "@next2d/framework";
Expand Down
45 changes: 42 additions & 3 deletions specs/cn/view.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,15 @@ export class HomeView extends View<HomeViewModel>

## 画面转换

使用 `app.gotoView()` 进行画面转换。
使用 `app.gotoView(name?: string)` 进行画面转换。返回值是 `Promise<void>`,可用于等待完整的转换流程(请求执行、View/ViewModel 重新绑定、`onEnter()` 调用)。

`gotoView` 要点:

- `name` 参数类型是 `string`(可省略,默认值为 `""`)。
- `name` 传入 `routing.json` 的键(如 `home`、`quest/list`),也支持 `?id=123` 这类查询字符串。
- 省略 `name` 时,会从当前 URL 解析目标路由(常用于 SPA 的 `popstate` 流程)。
- 转换开始时会先清空上一画面的 `response`,然后将新请求结果按 `name` 键重新写入。
- 当 `config.json` 中设置 `all.spa: true` 时,普通转换会通过 `pushState` 更新浏览器历史。

```typescript
import { app } from "@next2d/framework";
Expand All @@ -321,9 +329,34 @@ export class NavigateToViewUseCase
}
```

## 获取上下文

`app.getContext()` 返回当前运行时的 `Context`,包含:
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

文档说明 app.getContext() 会返回当前 Context,但实现中 $getContext() 在上下文未初始化时会抛错("Context is not initialized. Call run() first.")。建议在此处补充前置条件:需要先启动应用/初始化 Context,否则 getContext() 可能抛出异常。

Suggested change
`app.getContext()` 返回当前运行时的 `Context`,包含:
`app.getContext()` 返回当前运行时的 `Context`,包含(前提是应用已启动且 Context 已初始化,否则该方法可能抛出异常)

Copilot uses AI. Check for mistakes.

- `root`:Stage 下的根 `Sprite`
- `view`:当前绑定的 View(转换中或启动初期可能为 `null`)
- `viewModel`:当前绑定的 ViewModel(转换中或启动初期可能为 `null`)

```typescript
import { app } from "@next2d/framework";

const context = app.getContext();
const root = context.root;

if (context.view && context.viewModel) {
// 访问当前 View / ViewModel
}
```

## 获取响应数据

`routing.json` 中 `requests` 的数据可以通过 `app.getResponse()` 获取。
`app.getResponse()` 返回 `Map<string, unknown>`。`routing.json` 的 `requests` 中设置了 `name` 的响应,会按当前一次画面转换写入该 Map。

`getResponse` 要点:

- 它是单次 `gotoView` 的临时数据容器。
- 开始下一次 `gotoView` 时,上一轮内容会被清空。
- 值类型为 `unknown`,业务侧应使用类型守卫或类型断言后再读取。

```typescript
import { app } from "@next2d/framework";
Expand All @@ -341,7 +374,13 @@ async initialize(): Promise<void>

## 获取缓存数据

`cache: true` 的数据可以通过 `app.getCache()` 获取。
`app.getCache()` 返回 `Map<string, unknown>`。在 `requests` 中设置 `cache: true` 的数据会跨画面保留,适合主数据等可复用数据。

`getCache` 要点:

- 同时具备 `cache: true` 和 `name` 的请求会进入缓存。
- 当相同键已存在时,请求处理可优先复用缓存值。
- 缓存不会自动清理;不再需要时请显式 `delete` 或 `clear`。

```typescript
import { app } from "@next2d/framework";
Expand Down
35 changes: 33 additions & 2 deletions specs/en/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ Enclose with `{{***}}` to get variables from `config.json`:
### Using Cache

Setting `cache: true` caches the data. Cached data persists through screen transitions.
`app.getCache()` returns `Map<string, unknown>`, and values are accessed by each request `name`.

Key points for cache usage:

- If the same key already exists, request processing can reuse cached values.
- Cache is not auto-cleared, so remove unused entries explicitly with `delete` or `clear`.

```json
{
Expand Down Expand Up @@ -216,7 +222,15 @@ export class HomeDataCallback

### app.gotoView()

Use `app.gotoView()` for screen transitions:
Use `app.gotoView(name?: string)` for screen transitions. It returns `Promise<void>` so you can await request completion, View/ViewModel rebind, and `onEnter()`.

Key points for `gotoView`:

- The `name` parameter type is `string` (optional, default is `""`).
- `name` is a `routing.json` key such as `home` or `quest/list`.
- You can include query strings such as `?id=123`.
- If `name` is omitted, the destination is resolved from the current URL (SPA `popstate` flow).
- Previous transition `response` data is cleared when a new transition starts.

```typescript
import { app } from "@next2d/framework";
Expand Down Expand Up @@ -267,9 +281,26 @@ export class TopViewModel extends ViewModel
}
```

### app.getContext()

Use `app.getContext()` to get the active `Context`. It provides references to `root` (root `Sprite`), `view`, and `viewModel`. During transitions, `view` and `viewModel` can temporarily be `null`.
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs say app.getContext() returns the active Context, but $getContext() throws when the context is not initialized ("Context is not initialized. Call run() first."). Please document that getContext() requires the app to have been started / context set, otherwise it can throw.

Suggested change
Use `app.getContext()` to get the active `Context`. It provides references to `root` (root `Sprite`), `view`, and `viewModel`. During transitions, `view` and `viewModel` can temporarily be `null`.
Use `app.getContext()` to get the active `Context`. It provides references to `root` (root `Sprite`), `view`, and `viewModel`. During transitions, `view` and `viewModel` can temporarily be `null`. If the application has not been started and the context has not been initialized (for example, before calling `run()`), `app.getContext()` will throw an error such as `"Context is not initialized. Call run() first."`.

Copilot uses AI. Check for mistakes.

```typescript
import { app } from "@next2d/framework";

const context = app.getContext();
const root = context.root;
```

## Getting Response Data

Data from `requests` can be retrieved with `app.getResponse()`:
`app.getResponse()` returns `Map<string, unknown>`. It stores response values whose `name` is defined in `requests` for the current transition.

Key points for `getResponse`:

- It is a temporary store for one `gotoView` cycle.
- The map is reset when the next `gotoView` starts.
- Values are `unknown`; use type guards or type assertions before use.

```typescript
import { app } from "@next2d/framework";
Expand Down
45 changes: 42 additions & 3 deletions specs/en/view.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,15 @@ export class HomeView extends View<HomeViewModel>

## Screen Transition

Use `app.gotoView()` for screen transitions.
Use `app.gotoView(name?: string)` for screen transitions. It returns `Promise<void>`, so you can await the full transition flow (request execution, View/ViewModel rebind, and `onEnter()`).

Key points for `gotoView`:

- The `name` parameter type is `string` (optional, default is `""`).
- Pass a `routing.json` key such as `home` or `quest/list`. Query strings like `?id=123` are also supported.
- If `name` is omitted, the destination is resolved from the current URL (used in SPA `popstate` flows).
- At transition start, the previous `response` map is cleared, then new request results are stored by their `name` keys.
- When `all.spa: true` in `config.json`, normal transitions update browser history via `pushState`.

```typescript
import { app } from "@next2d/framework";
Expand All @@ -321,9 +329,34 @@ export class NavigateToViewUseCase
}
```

## Getting Context

`app.getContext()` returns the current runtime `Context`. It includes:

- `root`: Root `Sprite` under Stage
- `view`: Currently bound View (can be `null` during transition or right after startup)
- `viewModel`: Currently bound ViewModel (can be `null` during transition or right after startup)

Comment on lines +334 to +339
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs state that app.getContext() returns the current Context, but in the implementation $getContext() throws if the context hasn't been initialized yet ("Context is not initialized. Call run() first."). Please mention this precondition so readers know getContext() can throw early in app startup.

Suggested change
`app.getContext()` returns the current runtime `Context`. It includes:
- `root`: Root `Sprite` under Stage
- `view`: Currently bound View (can be `null` during transition or right after startup)
- `viewModel`: Currently bound ViewModel (can be `null` during transition or right after startup)
`app.getContext()` returns the current runtime `Context` once the application has been initialized with `app.run()`. It includes:
- `root`: Root `Sprite` under Stage
- `view`: Currently bound View (can be `null` during transition or right after startup)
- `viewModel`: Currently bound ViewModel (can be `null` during transition or right after startup)
> Note: If you call `app.getContext()` before the context has been initialized (that is, before `app.run()` has completed), it will throw an error: `"Context is not initialized. Call run() first."`.

Copilot uses AI. Check for mistakes.
```typescript
import { app } from "@next2d/framework";

const context = app.getContext();
const root = context.root;

if (context.view && context.viewModel) {
// Access current View / ViewModel
}
```

## Getting Response Data

Data from `requests` in `routing.json` can be retrieved with `app.getResponse()`.
`app.getResponse()` returns `Map<string, unknown>`. Response values whose `name` is defined in `routing.json` `requests` are stored for the current transition.

Key points for `getResponse`:

- It is a per-transition temporary store for data fetched in one `gotoView`.
- The map is cleared when the next `gotoView` starts.
- Values are `unknown`, so consumers should apply type guards or type assertions.

```typescript
import { app } from "@next2d/framework";
Expand All @@ -341,7 +374,13 @@ async initialize(): Promise<void>

## Getting Cache Data

Data with `cache: true` can be retrieved with `app.getCache()`.
`app.getCache()` returns `Map<string, unknown>`. Data from `requests` with `cache: true` is kept across transitions, which is useful for reusable data such as master data.

Key points for `getCache`:

- Requests with both `cache: true` and `name` are eligible for cache storage.
- If the same key already exists, request processing can reuse cached data.
- Cache is not auto-cleared; manage it explicitly with `delete` or `clear` when needed.

```typescript
import { app } from "@next2d/framework";
Expand Down
Loading