create-qs 0.8.21 → 0.8.23

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.
package/README.md CHANGED
@@ -1,6 +1,94 @@
1
1
  # create-qs
2
2
 
3
- English | [简体中文](README.zh-CN.md) | [繁體中文](README.zh-TW.md)
3
+ <details>
4
+ <summary>简体中文</summary>
5
+
6
+ 用一条命令创建全栈 Quicksilver 项目。
7
+
8
+ ```bash
9
+ npm create qs
10
+ ```
11
+
12
+ 这一条命令请使用 npm,不要使用 pnpm。自 pnpm 11 起,`pnpm create` 会跳过发布不足 24 小时的版本(`minimumReleaseAge`),且不给出任何提示,当天发布的脚手架会被静默替换为较早的版本。npm 默认没有这一限制。项目创建完成后,其余操作一律使用 pnpm。
13
+
14
+ 脚手架发布在公网 npmjs 上,未做任何配置的机器即可直接运行。它会询问项目及其第一个模块的若干问题,然后生成一个可以直接运行的全栈项目,包括 Kotlin/Spring Boot 后端、Preact/TypeScript 前端、Gradle Wrapper,以及文件型 H2 开发数据库。
15
+
16
+ ### 环境要求
17
+
18
+ - **JDK 25**。后端按 Java 25 编译,版本更低时无法编译。
19
+ - **Node.js `>=22.13.0`**,这是 pnpm 11 要求的最低版本。
20
+ - **pnpm 11**,创建项目之后的所有开发工作都使用它。workspace 与 catalog 均依赖 pnpm,因此不支持 npm 与 yarn。
21
+ - **能够访问内部 Nexus**,见下一节。
22
+
23
+ ### 首次安装依赖之前
24
+
25
+ `create-qs` 是唯一发布到公网的包。平台的其余部分,包括 `@qs-platform/*`、`@qs-elements/*`、`@qs-charts/*` 三组 npm 包、Quicksilver 的 Gradle 插件以及全部 Maven 构件,都发布在内部 Nexus 上。内部 Nexus 的地址不对外公开,因此生成的项目不预置任何地址。请先向提供 Quicksilver 的一方获取地址与账户,然后在每台机器上配置一次。npm 的三个 scope 配置在 `~/.npmrc` 中,Gradle 的仓库地址与账户配置在 `~/.gradle/gradle.properties` 中。
26
+
27
+ 具体写法见生成项目中开发手册的环境准备一章,即 `.quicksilver/manual/zh-CN/00-setup.md`。该章随项目提交,首次安装依赖之前即可阅读,脚手架执行完毕时也会提示它的位置。
28
+
29
+ 然后启动项目:
30
+
31
+ ```bash
32
+ cd <项目名>
33
+ pnpm install
34
+ pnpm dev
35
+ ```
36
+
37
+ 首次执行 `pnpm dev` 时需要下载 Gradle 与后端依赖,并完成首次编译,耗时较长。下载的内容缓存在 `~/.gradle` 中,之后的启动会快很多。
38
+
39
+ 在浏览器中访问 <http://localhost:6286>,使用 `admin` / `123456` 登录。`pnpm install` 同时会在 `00-setup.md` 所在目录写入开发手册的其余章节。
40
+
41
+ ---
42
+
43
+ <br>
44
+
45
+ </details>
46
+
47
+ <details>
48
+ <summary>繁體中文</summary>
49
+
50
+ 用一個指令建立全端 Quicksilver 專案。
51
+
52
+ ```bash
53
+ npm create qs
54
+ ```
55
+
56
+ 這一個指令請使用 npm,不要使用 pnpm。自 pnpm 11 起,`pnpm create` 會略過發布不足 24 小時的版本(`minimumReleaseAge`),且不會給出任何提示,當天發布的鷹架會被默默替換為較早的版本。npm 預設沒有這項限制。專案建立完成後,其餘操作一律使用 pnpm。
57
+
58
+ 鷹架發布在公開的 npmjs 上,未做任何設定的電腦即可直接執行。它會詢問專案及其第一個模組的幾個問題,然後產生一個可以直接執行的全端專案,包括 Kotlin/Spring Boot 後端、Preact/TypeScript 前端、Gradle Wrapper,以及檔案型 H2 開發資料庫。
59
+
60
+ ### 環境需求
61
+
62
+ - **JDK 25**。後端依 Java 25 編譯,版本較低時無法編譯。
63
+ - **Node.js `>=22.13.0`**,這是 pnpm 11 要求的最低版本。
64
+ - **pnpm 11**,建立專案之後的所有開發工作都使用它。workspace 與 catalog 均依賴 pnpm,因此不支援 npm 與 yarn。
65
+ - **能夠存取內部 Nexus**,請參閱下一節。
66
+
67
+ ### 首次安裝相依套件之前
68
+
69
+ `create-qs` 是唯一發布到公開 npmjs 的套件。平台的其餘部分,包括 `@qs-platform/*`、`@qs-elements/*`、`@qs-charts/*` 三組 npm 套件、Quicksilver 的 Gradle 外掛程式以及全部 Maven 構件,都發布在內部 Nexus 上。內部 Nexus 的位址不對外公開,因此產生的專案不預設任何位址。請先向提供 Quicksilver 的一方取得位址與帳號,然後在每台電腦上設定一次。npm 的三個 scope 設定在 `~/.npmrc` 中,Gradle 的儲存庫位址與帳號設定在 `~/.gradle/gradle.properties` 中。
70
+
71
+ 具體寫法請參閱產生的專案中開發手冊的環境準備一章,即 `.quicksilver/manual/zh-TW/00-setup.md`。該章隨專案提交,首次安裝相依套件之前即可閱讀,鷹架執行完畢時也會提示它的位置。
72
+
73
+ 然後啟動專案:
74
+
75
+ ```bash
76
+ cd <專案名稱>
77
+ pnpm install
78
+ pnpm dev
79
+ ```
80
+
81
+ 首次執行 `pnpm dev` 時需要下載 Gradle 與後端相依套件,並完成首次編譯,耗時較長。下載的內容快取在 `~/.gradle` 中,之後的啟動會快很多。
82
+
83
+ 在瀏覽器中開啟 <http://localhost:6286>,使用 `admin` / `123456` 登入。`pnpm install` 同時會在 `00-setup.md` 所在目錄寫入開發手冊的其餘章節。
84
+
85
+ ---
86
+
87
+ <br>
88
+
89
+ </details>
90
+
91
+ <br>
4
92
 
5
93
  Create a full-stack Quicksilver project with a single command.
6
94
 
@@ -47,5 +135,9 @@ pnpm install
47
135
  pnpm dev
48
136
  ```
49
137
 
138
+ The first `pnpm dev` downloads Gradle and the backend dependencies and compiles the backend for
139
+ the first time, so it takes a while. The downloads are cached in `~/.gradle`, and later starts are
140
+ much faster.
141
+
50
142
  Open <http://localhost:6286> and sign in as `admin` / `123456`. `pnpm install` also writes the
51
143
  other chapters of the handbook next to `00-setup.md`.
package/index.js CHANGED
@@ -1688,26 +1688,27 @@ var PlaceholderProcessor = class extends BaseProcessor {
1688
1688
  });
1689
1689
  }
1690
1690
  };
1691
- var SORT_ORDER = 3;
1691
+ var DAILY_MENU_ID = "7277da04-764d-46d6-8c9d-038d120c4852";
1692
+ var ORG_MENU_ID = "8ac26074-f08e-485c-839e-53969aeca4fd";
1693
+ var ORG_MODULE = "quicksilver.org";
1692
1694
  var ICON = "quicksilver:module-work";
1693
- function renderModuleMenu(name) {
1695
+ function renderModuleMenu(name, platformModules) {
1694
1696
  if (!name) {
1695
1697
  return { text: "" };
1696
1698
  }
1697
1699
  const menuId = randomUUID();
1700
+ const withOrg = platformModules.includes(ORG_MODULE);
1701
+ const anchor = withOrg ? ORG_MENU_ID : DAILY_MENU_ID;
1698
1702
  const text = [
1699
- "",
1700
1703
  "// The module's top-level menu: a first-level entry in the left nav that this",
1701
1704
  "// module's pages hang under.",
1702
- `// Shift the existing top-level entries down by one and take slot ${SORT_ORDER}: after`,
1703
- "// the built-in \u65E5\u5E38 (1) and, when the org module is installed, \u7EC4\u7EC7 (2); the remaining",
1704
- "// built-in ones sit beyond 1000.",
1705
- `{ @sql: "update ac_web_menu set sort_order = sort_order + 1 where parent_id is null and sort_order >= ${SORT_ORDER}" }`,
1706
- "",
1707
1705
  "{ $insert: 'ac.web_menu' }",
1708
- `{ id: '${menuId}', name: ${literal(name)}, icon: '${ICON}', sort_order: ${SORT_ORDER} }`,
1709
- // 模板里 MODULE_MENU 占位符是最后一个字符,没有换行跟在后面——不建菜单时文件就干净地
1710
- // 停在模块那一行,建了才由这里补上收尾的换行
1706
+ `{ id: '${menuId}', name: ${literal(name)}, icon: '${ICON}' }`,
1707
+ "",
1708
+ `// Place it right after the built-in ${withOrg ? "\u7EC4\u7EC7" : "\u65E5\u5E38"} menu, ahead of \u5B9A\u5236 and \u8FD0\u7EF4.`,
1709
+ `{ $place: 'menu', menu: '${menuId}', after: '${anchor}' }`,
1710
+ // 模板里 rows/menus.jsons 只有 MODULE_MENU 占位符,后面没有换行。不建菜单时文件为空,
1711
+ // 建了才由这里补上收尾的换行
1711
1712
  ""
1712
1713
  ].join("\n");
1713
1714
  return { text, menuId };
@@ -1803,10 +1804,10 @@ async function processFiles(options) {
1803
1804
  variables.set("WEB_PACKAGE_NAME", options.webModulePackage);
1804
1805
  variables.set("API_PACKAGE", options.apiPackage);
1805
1806
  variables.set("API_ARTIFACT_ID", options.apiArtifactId);
1806
- variables.set("QUICKSILVER_VERSION", "0.8.21");
1807
+ variables.set("QUICKSILVER_VERSION", "0.8.23");
1807
1808
  variables.set("MODULE_ID", randomUUID());
1808
1809
  variables.set("DB_PREFIX", toDbPrefix(options.name));
1809
- const menu = renderModuleMenu(options.moduleMenu);
1810
+ const menu = renderModuleMenu(options.moduleMenu, options.platformModules);
1810
1811
  variables.set("MODULE_MENU", menu.text);
1811
1812
  variables.set("MODULE_DEFAULT_MENU", menu.menuId ? `default_menu_id: '${menu.menuId}', ` : "");
1812
1813
  for (const [key, value] of Object.entries(buildPlatformModuleVariables(options.platformModules))) {
@@ -1865,7 +1866,7 @@ if (process.argv.some((arg) => arg === "--version" || arg === "-v")) {
1865
1866
  console.log(getCliVersion());
1866
1867
  process.exit(0);
1867
1868
  }
1868
- var program = new Command().name("create-qs").description("Create a new Quicksilver application").version("0.8.21", "-v, --version").argument("[project-name]", "Name of the project").option("-c, --module-code <code>", "Full module code (e.g., power_crm.sales)").option("-d, --module-directory <directory>", "Module directory name (e.g., sales)").option("-n, --module-namespace <code>", "Prefix of unit/table, defaults to the initials of the module code (e.g., pcs)").option("-p, --api-package <package>", "JVM package for Kotlin files (e.g., com.company.crm)").option("-a, --api-artifact-id <artifactId>", "Module artifact ID in repository (e.g., power-crm-module-sales)").option("-w, --web-module-package <package>", "Web module package name (e.g., @powercrm/module-sales-web)").option("-m, --modules <codes>", "Modules to depend on, comma separated (e.g., quicksilver.org)").option("--module-menu <name>", "Create the module's top-level menu in the left nav, with this label").option("--no-module-menu", "Do not create a top-level menu").option("-y, --yes", "Skip prompts and use defaults").action(async (projectName, options) => {
1869
+ var program = new Command().name("create-qs").description("Create a new Quicksilver application").version("0.8.23", "-v, --version").argument("[project-name]", "Name of the project").option("-c, --module-code <code>", "Full module code (e.g., power_crm.sales)").option("-d, --module-directory <directory>", "Module directory name (e.g., sales)").option("-n, --module-namespace <code>", "Prefix of unit/table, defaults to the initials of the module code (e.g., pcs)").option("-p, --api-package <package>", "JVM package for Kotlin files (e.g., com.company.crm)").option("-a, --api-artifact-id <artifactId>", "Module artifact ID in repository (e.g., power-crm-module-sales)").option("-w, --web-module-package <package>", "Web module package name (e.g., @powercrm/module-sales-web)").option("-m, --modules <codes>", "Modules to depend on, comma separated (e.g., quicksilver.org)").option("--module-menu <name>", "Create the module's top-level menu in the left nav, with this label").option("--no-module-menu", "Do not create a top-level menu").option("-y, --yes", "Skip prompts and use defaults").action(async (projectName, options) => {
1869
1870
  applyPromptTexts();
1870
1871
  console.log(chalk4.cyan(`
1871
1872
  ${t("banner")}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-qs",
3
- "version": "0.8.21",
3
+ "version": "0.8.23",
4
4
  "description": "Create a full-stack Quicksilver project with a single command",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,9 +9,7 @@
9
9
  "files": [
10
10
  "index.js",
11
11
  "template/**/*",
12
- "README.md",
13
- "README.zh-CN.md",
14
- "README.zh-TW.md"
12
+ "README.md"
15
13
  ],
16
14
  "keywords": [
17
15
  "quicksilver",
@@ -86,9 +86,11 @@ pnpm i
86
86
  pnpm dev
87
87
  ```
88
88
 
89
+ The first `pnpm dev` downloads Gradle and the backend dependencies and compiles the backend for the first time, so it takes a while. The downloads are cached in `~/.gradle`, and later starts are much faster.
90
+
89
91
  Open <http://localhost:6286> in a browser and sign in as `admin` / `123456`.
90
92
 
91
- > This account is **development seed data** in the core module's `init.jsons`, and its password is restored every time the development database is rebuilt. **Change it before deployment.**
93
+ > This account is **development seed data** in the core module's init data files, and its password is restored whenever the development database is rebuilt. **Change it before deployment.**
92
94
 
93
95
  The backend provides two health check addresses:
94
96
  - <http://localhost:6288/api/metrics/status> returns UP as soon as the service starts accepting requests
@@ -115,7 +117,7 @@ power-crm/
115
117
  ├── modules/sales/ Business module, where most day-to-day development happens
116
118
  │ ├── module.yaml Module identity: id, code, namespaces, dependencies, web package name
117
119
  │ ├── api/ Backend (Kotlin + Spring Boot)
118
- │ │ └── src/main/resources/QUICKSILVER-INF/data/init.jsons ← initial data
120
+ │ │ └── data/init/ ← initial data: tables.jsons (optional), units/ (one file per unit) and rows/
119
121
  │ └── web/ Frontend (Preact + TypeScript)
120
122
  │ └── src/module.ts Registry of pages, plugins, components, styles and icons
121
123
  ├── run/
@@ -145,7 +147,7 @@ Keep the following two points in mind:
145
147
 
146
148
  After dependencies are installed, the other chapters of the manual appear in this directory. Start with the [table of contents](README.md), then read [01 Getting Started](01-getting-started.md), which explains the options chosen when creating a project, the common commands, and how to switch databases.
147
149
 
148
- On first use, run `pnpm qs sources` once. It downloads the platform's API documentation, `init.jsons` and the frontend `.d.ts` files into `.quicksilver/generated/`, which later chapters of the manual refer to in many places.
150
+ On first use, run `pnpm qs sources` once. It downloads the platform's API documentation, data files and the frontend `.d.ts` files into `.quicksilver/generated/`, which later chapters of the manual refer to in many places.
149
151
 
150
152
  ## Optional configuration
151
153
 
@@ -86,9 +86,11 @@ pnpm i
86
86
  pnpm dev
87
87
  ```
88
88
 
89
+ 首次执行 `pnpm dev` 时需要下载 Gradle 与后端依赖,并完成首次编译,耗时较长。下载的内容缓存在 `~/.gradle` 中,之后的启动会快很多。
90
+
89
91
  在浏览器中访问 <http://localhost:6286>,使用 `admin` / `123456` 登录。
90
92
 
91
- > 该账户是 core 模块 `init.jsons` 中的**开发种子数据**,开发数据库每次重建后都会恢复为此密码。**部署前必须修改。**
93
+ > 该账户是 core 模块 init 数据文件中的**开发种子数据**,重建开发数据库后恢复为此密码。**部署前必须修改。**
92
94
 
93
95
  后端提供两个健康检查地址:
94
96
  - <http://localhost:6288/api/metrics/status> 在服务开始接受请求后即返回 UP
@@ -115,7 +117,7 @@ power-crm/
115
117
  ├── modules/sales/ 业务模块,日常开发主要在此进行
116
118
  │ ├── module.yaml 模块标识,包括 id、code、namespaces、依赖、Web 包名
117
119
  │ ├── api/ 后端(Kotlin + Spring Boot)
118
- │ │ └── src/main/resources/QUICKSILVER-INF/data/init.jsons ← 初始化数据
120
+ │ │ └── data/init/ ← 初始化数据:tables.jsons(可选)、units/(每个单元一个文件)与 rows/
119
121
  │ └── web/ 前端(Preact + TypeScript)
120
122
  │ └── src/module.ts 页面、插件、组件、样式、图标的注册表
121
123
  ├── run/
@@ -145,7 +147,7 @@ power-crm/
145
147
 
146
148
  安装依赖后,本目录下会出现手册的其余章节。建议先阅读[手册目录](README.md),再阅读 [01 快速入门](01-getting-started.md),其中介绍创建项目时各选项的含义、常用命令以及如何切换数据库。
147
149
 
148
- 首次使用时建议执行一次 `pnpm qs sources`,将平台的接口文档、`init.jsons` 与前端 `.d.ts` 下载到 `.quicksilver/generated/`,手册后续多处会引用其中的内容。
150
+ 首次使用时建议执行一次 `pnpm qs sources`,将平台的接口文档、数据文件与前端 `.d.ts` 下载到 `.quicksilver/generated/`,手册后续多处会引用其中的内容。
149
151
 
150
152
  ## 可选配置
151
153
 
@@ -86,9 +86,11 @@ pnpm i
86
86
  pnpm dev
87
87
  ```
88
88
 
89
+ 首次執行 `pnpm dev` 時需要下載 Gradle 與後端相依套件,並完成首次編譯,耗時較長。下載的內容快取在 `~/.gradle` 中,之後的啟動會快很多。
90
+
89
91
  在瀏覽器中開啟 <http://localhost:6286>,使用 `admin` / `123456` 登入。
90
92
 
91
- > 此帳號是 core 模組 `init.jsons` 中的**開發用種子資料**,開發資料庫每次重建後都會恢復為此密碼。**部署前必須修改。**
93
+ > 此帳號是 core 模組 init 資料檔中的**開發用種子資料**,重建開發資料庫後恢復為此密碼。**部署前必須修改。**
92
94
 
93
95
  後端提供兩個健康檢查位址:
94
96
  - <http://localhost:6288/api/metrics/status> 在服務開始接受請求後即回傳 UP
@@ -115,7 +117,7 @@ power-crm/
115
117
  ├── modules/sales/ 業務模組,日常開發主要在此進行
116
118
  │ ├── module.yaml 模組識別,包括 id、code、namespaces、相依、Web 套件名稱
117
119
  │ ├── api/ 後端(Kotlin + Spring Boot)
118
- │ │ └── src/main/resources/QUICKSILVER-INF/data/init.jsons ← 初始化資料
120
+ │ │ └── data/init/ ← 初始化資料:tables.jsons(可選)、units/(每個單元一個檔案)與 rows/
119
121
  │ └── web/ 前端(Preact + TypeScript)
120
122
  │ └── src/module.ts 頁面、外掛、元件、樣式、圖示的註冊表
121
123
  ├── run/
@@ -145,7 +147,7 @@ power-crm/
145
147
 
146
148
  安裝相依套件後,本目錄下會出現手冊的其餘章節。建議先閱讀[手冊目錄](README.md),再閱讀 [01 快速入門](01-getting-started.md),其中介紹建立專案時各選項的含義、常用指令以及如何切換資料庫。
147
149
 
148
- 首次使用時建議執行一次 `pnpm qs sources`,將平台的 API 文件、`init.jsons` 與前端 `.d.ts` 下載到 `.quicksilver/generated/`,手冊後續多處會引用其中的內容。
150
+ 首次使用時建議執行一次 `pnpm qs sources`,將平台的 API 文件、資料檔與前端 `.d.ts` 下載到 `.quicksilver/generated/`,手冊後續多處會引用其中的內容。
149
151
 
150
152
  ## 選用設定
151
153
 
@@ -120,11 +120,11 @@ index in the handbook's `references/architecture.md`, which every install refres
120
120
  | `pnpm qs dev [services]` | You want only some of the services (api / web / app) rather than the full set |
121
121
  | `pnpm qs gradle <tasks...>` | Run Gradle tasks; unrecognized options are passed straight through to Gradle |
122
122
  | `pnpm qs new uuid [n]` | You need a new id for a jsons row — **mint one, never copy one from elsewhere** (a duplicated id fails far away from where it was introduced) |
123
- | `pnpm qs new unit [code]` | Add a business unit: appends `$add: 'ac.unit'` to init.jsons with freshly minted ids for the fields, the list, the form and the five standard pages. `--owner account` adds four permissions, a row-level grant on the default account role, and a Service that stamps the owner column. `--menu '日常/基础功能'` hangs the list page under that menu; the row alone does not make the menu **visible** — some role still has to hold the unit's read permission. Run it with no arguments to be asked for all of the above (tree, ownership, Kotlin layers, menu) and get the equivalent command line back |
124
- | `pnpm qs new jsons [version]` | Add an upgrade script **and** raise `ac.module`'s `data_version` above it — skip the second half and a brand-new database will run the upgrade script as well |
123
+ | `pnpm qs new unit [code]` | Add a business unit: creates the unit file `data/init/units/<code>.jsons` holding the `$add: 'ac.unit'` block with freshly minted ids for the fields, the list, the form and the five standard pages; an entity unit also declares its four CRUD permissions there. `--owner account` adds row-level grants on the default account role to `data/init/rows/roles.jsons` and a Service that stamps the owner column. `--menu '日常/基础功能'` appends a row to `data/init/rows/menus.jsons` that hangs the list page under that menu; the row alone does not make the menu **visible** — some role still has to hold the unit's read permission. The same content is appended to the module's draft `data/upgrade/draft.jsons`, so installed environments get the unit too. Run it with no arguments to be asked for all of the above (tree, ownership, Kotlin layers, menu) and get the equivalent command line back |
124
+ | `pnpm qs data freeze` | Rename every module draft `data/upgrade/draft.jsons` with changes to the next upgrade file of the product version, `data/upgrade/<product version>/<version>.jsons`, and start a new draft. Write each data change into init and into the draft at the same time. Drafts are for development only: run this on the main branch before deploying to an environment that is kept, long-lived test environments included, then build the package again. A package built while a draft has changes is a draft package, for throwaway environments only. Later changes go into the draft again: changes to an upgrade file do not reach environments that have already run it |
125
125
  | `pnpm qs build-vendor` | You changed a vendor dependency and need it rebundled into the single-file ESM used by the import map |
126
126
  | `pnpm qs i18n extract` | You changed Chinese copy inside a jsons file and need each module's `i18n.tsv` refreshed |
127
- | `pnpm qs sources` | **You need to see how Quicksilver itself does something** — drops the API docs, sources or decompiled classes, and every module's `init.jsons`, into `.quicksilver/generated/` (see below) |
127
+ | `pnpm qs sources` | **You need to see how Quicksilver itself does something** — drops the API docs, sources or decompiled classes, and every module's data files (`init/`, `demo/`, `upgrade/`), into `.quicksilver/generated/` (see below) |
128
128
  | `pnpm qs docs` | Refresh `.agents/skills/qs-*` and `.quicksilver/manual/` from the installed CLI version. `pnpm install` runs it whenever it installs something, so you only need it when those directories are missing |
129
129
  | `pnpm qs upgrade <version>` | Upgrade the platform: sets `quicksilver.version` in gradle.properties, `catalogs.quicksilver` and every other managed version to what that version's create-qs would write, then refreshes skills and the manual. Hand edits to managed values are set back. See `.quicksilver/manual/zh-CN/10-upgrading.md` |
130
130
  | `pnpm qs test list` | You want to know which integration test profiles exist |
@@ -175,9 +175,10 @@ you are unsure what a target would actually do.
175
175
  **An AI agent starts `pnpm dev:h2`, never `pnpm dev`.**
176
176
 
177
177
  `pnpm dev` (web 6286 / api 6288) belongs to the person you are working with. It may well be running
178
- already, and it is backed by the H2 file database `run/api/local/data/h2/database1` with
179
- `auto-init.drop-first: true` — starting or restarting it **drops and rebuilds that database**. So
180
- taking it over does not just steal a port, it throws away whatever state they were looking at.
178
+ already, and it is backed by the H2 file database `run/api/local/data/h2/database1`, which keeps
179
+ their data between starts: with `auto-init.draft-sync: true`, every start runs the upgrade drafts that
180
+ changed since the last one. Starting or restarting it yourself interrupts them and runs your
181
+ half-finished drafts against their data, so taking it over does not just steal a port.
181
182
 
182
183
  `pnpm dev:h2` (6210 / 6211) is the isolated instance: its own database (`database2`), its own log
183
184
  directory and its own Atomikos directory, so it can run side by side with theirs. The other dialect
@@ -203,8 +204,8 @@ that ships with Quicksilver:
203
204
  |---|---|
204
205
  | `admin` | `123456` |
205
206
 
206
- It is a development account seeded by core's `init.jsons`, so it comes back to this password every
207
- time the development database is rebuilt. `pnpm qs test verify` signs in with it too, for the
207
+ It is a development account seeded by core's init data files, so it comes back to this password
208
+ whenever the development database is rebuilt. `pnpm qs test verify` signs in with it too, for the
208
209
  platform's own specs and for yours under `test/e2e/scenarios/`, which start out signed in as `admin`
209
210
  (override it with the `E2E_ADMIN_USER` / `E2E_ADMIN_PASSWORD` environment variables).
210
211
 
@@ -222,8 +223,18 @@ For the default H2 profiles that means a file — `run/api/local/data/h2/databas
222
223
  `database2` for `pnpm dev:h2` — which you can open with any H2 client. For the other dialects,
223
224
  connect with your usual client using the host, port, user and password from that same file.
224
225
 
225
- Remember that `auto-init.drop-first: true` rebuilds the database on every start: **`init.jsons` is
226
- the only source of truth**, and anything you insert by hand is gone at the next restart.
226
+ The development database is kept between starts. With `auto-init.draft-sync: true`, every start runs
227
+ each module draft `data/upgrade/draft.jsons` that changed since the last start, and data entered by hand
228
+ or through the UI stays. **Write every data change into the draft as well as into `data/init/`**: a
229
+ change made only in `data/init/` reaches neither this database nor installed environments, and the
230
+ startup log lists such files. The two do not always hold the same commands. A removal takes the rows
231
+ out of `data/init/` and writes `$delete`, `@drop_column` or `@drop_table` into the draft. A fix to
232
+ existing data, such as filling a new column, goes into the draft only. Changes to `data/demo/` stay
233
+ out of the draft, since demo data runs on fresh installs only. Imperative commands in a draft (`@sql`,
234
+ `$update`, `$delete` ...) run once per `$key`; to run a changed one again, give it a new `$key`.
235
+ `$place` is the exception: it runs again whenever its draft runs. To rebuild the database from the
236
+ data files under `data/init/` and `data/demo/`, set `drop-first: true` in a profile override for one
237
+ start, then set it back.
227
238
 
228
239
  ## Development skills
229
240
 
@@ -234,7 +245,7 @@ and where to confirm each step":
234
245
  |---|---|---|
235
246
  | `qs-unit` | Adding or reworking a business unit: `$add: 'ac.unit'` table and field definitions, list / form metadata, the Kotlin model / dao / service / controller, role permissions and menus | [.agents/skills/qs-unit/SKILL.md](.agents/skills/qs-unit/SKILL.md) |
236
247
  | `qs-page` | Adding or reworking a page: the page class, registration in `module.ts`, the `ac.page` rows, the backend `xxx.page` preparation endpoint, page plugins | [.agents/skills/qs-page/SKILL.md](.agents/skills/qs-page/SKILL.md) |
237
- | `qs-jsons` | Writing or changing `init.jsons` / upgrade jsons: creating tables, defining units, inserting seed data, writing `ac.text` copy | [.agents/skills/qs-jsons/SKILL.md](.agents/skills/qs-jsons/SKILL.md) |
248
+ | `qs-jsons` | Writing or changing init data files / upgrade files: creating tables, defining units, inserting seed data, writing `ac.text` copy | [.agents/skills/qs-jsons/SKILL.md](.agents/skills/qs-jsons/SKILL.md) |
238
249
 
239
250
  Each skill's `examples/` directory holds complete samples you can copy from. The conventions they
240
251
  cite live in the handbook's `references/` (`qs-jsons` carries a `references/` of its own as well).
@@ -249,7 +260,7 @@ pnpm qs sources
249
260
  ```
250
261
 
251
262
  It fills `.quicksilver/generated/` with three things: `api/` holds the backend API documentation, sources or
252
- decompiled classes (including core's own `init.jsons`), `web/` holds the frontend packages' type
263
+ decompiled classes (including core's own init data files), `web/` holds the frontend packages' type
253
264
  declarations and readable sources (including the property and event types of the `<q-*>` elements),
254
265
  and `README.md` explains what each directory contains. `.quicksilver/generated/` is not under version
255
266
  control, so you can delete and re-fetch it at any time.
@@ -58,7 +58,7 @@ pnpm-debug.log*
58
58
  # therefore wins over every profile file.
59
59
 
60
60
  # Auto-generated files
61
- /modules/*/api/src/main/resources/QUICKSILVER-INF/module.json
61
+ /modules/*/api/src/main/resources/QS-INF/module.json
62
62
 
63
63
  # Kotlin tooling
64
64
  /.kotlin/
@@ -0,0 +1,11 @@
1
+ # Demo data
2
+
3
+ This folder holds demo data, rows that exist only to show and test the product, such as sample customers and orders. Production installs do not need them.
4
+
5
+ - Put the rows in `.jsons` files in this folder or in subfolders of it. Other files, such as this README, are skipped.
6
+ - On a fresh install of the module, the files run after everything in `data/init/`, sorted by name level by level with each subfolder run where it sorts, and they can refer to rows written there.
7
+ - Development and test profiles load demo data, as set by `quicksilver.datasource.auto-init.demo-data` in `run/api/config/application-dev.yaml`. On a production install the installer asks first, and does not load it by default.
8
+ - A module that is already installed never loads demo data. Draft sync does not run these files either, so an existing database does not pick up files added or changed here. To load them, set `drop-first: true` for one start, then set it back to `false`. The rebuild clears the whole development database, including rows entered through the UI.
9
+ - Do not copy demo rows into `data/upgrade/draft.jsons`. Upgrades never load demo data.
10
+
11
+ The rules for data files are in section 10 of [`.agents/skills/qs-jsons/references/json-command.md`](../../../../../.agents/skills/qs-jsons/references/json-command.md), and in the developer manual at [`.quicksilver/manual/en-US/06-data.md`](../../../../../.quicksilver/manual/en-US/06-data.md). Both are written by `pnpm install`.
@@ -0,0 +1,15 @@
1
+ // This file is the default place for the module's rows, and its first command is the module row
2
+ // below. When one kind of rows grows into a topic of its own, move it to a file of its own in rows/,
3
+ // named with a plural noun and with underscores between words. The usual names follow.
4
+ //
5
+ // menus.jsons menus
6
+ // roles.jsons roles and grants
7
+ // dictionaries.jsons dictionaries shared by several units
8
+ // texts.jsons texts (ac.text)
9
+ // error_messages.jsons error messages (ac.error_message)
10
+ // settings.jsons setting groups and setting definitions
11
+ //
12
+ // pnpm qs new unit appends to menus.jsons and roles.jsons. The files in rows/ have no ordering
13
+ // dependencies between them, and each row is written by one file only.
14
+
15
+ { $insert: 'ac.module', id: '{{MODULE_ID}}', name: '{{MODULE_DIRECTORY}}', code: '{{MODULE_CODE}}', icon: 'quicksilver:module-work', {{MODULE_DEFAULT_MENU}}}
@@ -0,0 +1 @@
1
+ // Upgrade draft. Write data changes here along with the matching init changes. pnpm qs data freeze renames it to a versioned upgrade file.
@@ -9,18 +9,20 @@ import org.junit.jupiter.api.Test
9
9
  import org.springframework.beans.factory.annotation.Autowired
10
10
 
11
11
  /**
12
- * 环境冒烟:Spring 上下文起得来,且 provisioning 真的把元数据灌进去了。
12
+ * Environment smoke test: the Spring context starts, and provisioning has actually loaded the metadata.
13
13
  *
14
- * 它也是「jsons 能正常装载进对应表」的测试覆盖,单元不必各自再写。测试上下文启动时执行全部
15
- * init.jsons,任何一条命令失败,上下文就无法启动,本类与其它测试一并失败。
14
+ * It also covers loading the data files into their tables, so units need no tests of their own for that. The
15
+ * test context runs every init data file on startup. When any command fails, the context cannot start, and this
16
+ * class fails together with every other test.
16
17
  *
17
- * 它同时是 `run/api/config/logback-test.xml` 的守卫。测试树里一个 `@Test` 都没有时 Spring 根本
18
- * 不会启动,日志配置写坏了(比如引用一个 Spring Boot 已删除的 converter)也发现不了——那种错
19
- * 会让**任何**测试在上下文初始化阶段就抛 `LogbackLoggingSystem` 的
20
- * `DynamicClassLoadingException`,报错内容跟被测代码毫无关系,很难往回查。
18
+ * It also guards `run/api/config/logback-test.xml`. Without a single `@Test` in the test tree, Spring never
19
+ * starts, and a broken logging configuration (for example one that references a converter Spring Boot has
20
+ * removed) goes unnoticed. Such an error makes **every** test throw `DynamicClassLoadingException` from
21
+ * `LogbackLoggingSystem` while the context initializes, with a message unrelated to the code under test, which
22
+ * is hard to trace back.
21
23
  *
22
- * 断言故意用裸 JDBC 而不是 `DatabaseInspectors`:只依赖 core 自己的 `Databases`,不押注
23
- * lib-database 是否传递到下游的测试 classpath 上。
24
+ * The assertion uses plain JDBC instead of `DatabaseInspectors` on purpose. It depends only on the `Databases`
25
+ * of core, and does not rely on lib-database being on the test classpath of downstream projects.
24
26
  */
25
27
  class SmokeTests: QsBaseTests() {
26
28
  @Autowired
@@ -40,12 +42,13 @@ class SmokeTests: QsBaseTests() {
40
42
  }
41
43
 
42
44
  /**
43
- * 全部单元(含平台单元)的元数据自检都没有问题。
45
+ * Every unit, platform units included, passes the metadata self-check.
44
46
  *
45
- * 自检在启动时发现问题只记一条 error 日志,服务照常启动,本用例把它变成测试失败。用
46
- * `$add: 'ac.unit'` 新建的单元过不了自检时 provisioning 已经中止,本用例额外拦住的是新建之后
47
- * 被其它写法改坏的单元,例如直接改写 `ac_unit_field` 的 jsons。自检只核对元数据本身(表名、
48
- * 字段、ID 字段、树单元的语义列),不与物理表比对。
47
+ * When the self-check finds a problem on startup, it only logs an error and the service starts as usual. This
48
+ * test turns that into a test failure. A unit created with `$add: 'ac.unit'` that fails the self-check already
49
+ * stops provisioning, so what this test adds is catching a unit broken afterwards by other writes, for example
50
+ * data files that rewrite `ac_unit_field` directly. The self-check covers the metadata only (table name,
51
+ * fields, ID fields, the semantic columns of tree units), and does not compare it with the physical table.
49
52
  */
50
53
  @Test
51
54
  fun `every unit passes the metadata self-check`() {
@@ -21,7 +21,7 @@
21
21
  "// --- build subprojects -------------------------------------------": "",
22
22
  "build": "pnpm build:api && pnpm build:web",
23
23
  "build:api": "quicksilver gradle build",
24
- "build:web": "pnpm -r --filter './modules/*/web' build",
24
+ "build:web": "pnpm -r --fail-if-no-match --filter './modules/*/web' build",
25
25
  "build:release": "pnpm build:web",
26
26
  "// --- utils -------------------------------------------------------": "",
27
27
  "i18n:extract": "quicksilver i18n extract",
@@ -51,10 +51,10 @@
51
51
  "// --- release (build is automatically included) -------------------": "",
52
52
  "release": "qs release --target=internal",
53
53
  "// --- clean -------------------------------------------------------": "",
54
- "clean:all": "pnpm clean:api && pnpm -r clean && pnpm clean:root",
54
+ "clean:all": "pnpm clean:api && pnpm -r run clean && pnpm clean:root",
55
55
  "clean:api": "quicksilver gradle clean",
56
56
  "clean:root": "shx rm -rf build/release build/test",
57
- "clean:web": "pnpm -r --filter './modules/*/web' --filter './run/web' clean",
57
+ "clean:web": "pnpm -r --filter './modules/*/web' --filter './run/web' run clean",
58
58
  "// --- lifecycle hooks (run automatically on install) --------------": "",
59
59
  "postinstall": "quicksilver docs && playwright install chromium"
60
60
  },
@@ -11,6 +11,11 @@ allowBuilds:
11
11
 
12
12
  engineStrict: true
13
13
 
14
+ # The scripts in package.json use POSIX sh syntax (single-quoted arguments, `VAR=value command`).
15
+ # On Windows, pnpm runs scripts with cmd.exe by default, which supports neither. The shell emulator
16
+ # runs them the same way on every platform.
17
+ shellEmulator: true
18
+
14
19
  minimumReleaseAgeExclude:
15
20
  - '@qs-elements/*'
16
21
  - '@qs-charts/*'
@@ -23,10 +28,10 @@ catalogs:
23
28
  "@qs-elements/web": "1.0.158"
24
29
  "@qs-elements/web-preact": "1.0.158"
25
30
  quicksilver:
26
- "@qs-platform/cli": "0.8.21"
27
- "@qs-platform/preset-vite-web": "0.8.21"
28
- "@qs-platform/web-module-core": "0.8.21"
29
- "@qs-platform/web-module-org": "0.8.21"
31
+ "@qs-platform/cli": "0.8.23"
32
+ "@qs-platform/preset-vite-web": "0.8.23"
33
+ "@qs-platform/web-module-core": "0.8.23"
34
+ "@qs-platform/web-module-org": "0.8.23"
30
35
  tooling:
31
36
  "@types/node": "^26.4.0"
32
37
  "sass": "^1.103.1"
@@ -21,6 +21,9 @@
21
21
  # An empty URL means the target is not configured yet: publishing fails with an error rather
22
22
  # than falling back to a default registry.
23
23
  #
24
+ # Maven artifacts never carry a draft with changes (data/upgrade/draft.jsons): the release refuses
25
+ # them on every target. Run `pnpm qs data freeze` on the main branch and commit before releasing.
26
+ #
24
27
  # Paths under `packages` are relative to this file — or, for the local/ copy, to the parent of
25
28
  # local/ — and may contain globs.
26
29
 
@@ -8,7 +8,7 @@ startup; nothing in this directory ships to your users.
8
8
  | File | What it is |
9
9
  |---|---|
10
10
  | `application.yaml` | Base configuration: ports, CORS, cache, upload, Atomikos. **Declares no datasources** — those live in the profile files below |
11
- | `application-dev.yaml` | The `dev` profile, active by default. Datasources for local development, plus `auto-init` and `strict-check` |
11
+ | `application-dev.yaml` | The `dev` profile, active by default. Datasources for local development, plus `auto-init` (with `draft-sync` on) and `fail-on-unregistered-data-source` |
12
12
  | `application-<dialect>.yaml` | One overlay per database dialect (`h2`, `postgresql`, `mariadb`, `mssql`, `oracle`). Each carries its own datasources, log directory and Atomikos log directory, so several can run side by side |
13
13
  | `application-test.yaml` | Used by `modules/*/api` tests. In-memory H2, wiped on every run |
14
14
  | `logback.xml` / `logback-test.xml` | Logging |
@@ -56,11 +56,20 @@ Add `runtimeOnly("com.mysql:mysql-connector-j")` to `run/api/build.gradle.kts`,
56
56
  `application-mysql.yaml` overlay and a `dev:mysql` script of your own, modelled on the other
57
57
  dialects. Every other dialect listed above ships with its driver.
58
58
 
59
+ ## The dev database
60
+
61
+ The `dev` profile turns on `draft-sync` and turns off `drop-first`. Every start runs the upgrade
62
+ drafts that changed since the last start, and keeps the data already in the dev database. To
63
+ rebuild the database from init and demo data, set `drop-first: true` for one start and then set it
64
+ back. Switching `draft-sync` on or off also needs one such rebuild: the two modes record drafts
65
+ differently, and startup refuses a database built in the other mode.
66
+
59
67
  ## Local edits
60
68
 
61
69
  `application-dev.yaml` is committed, but **by convention your local edits to it are not** — a
62
- different database host, `drop-first` turned off for a while, a different `strict-check`. Commit it
63
- only when you are deliberately changing the team default, and say so in the commit message.
70
+ different database host, `drop-first` turned on for a rebuild, a different
71
+ `fail-on-unregistered-data-source`. Commit it only when you are deliberately changing the team
72
+ default, and say so in the commit message.
64
73
 
65
74
  For an override that never enters version control, point `QUICKSILVER_CONFIG_LOCATION` at a config
66
75
  directory of your own: it is appended to the tail of `spring.config.location`, so it wins over
@@ -17,9 +17,29 @@ quicksilver:
17
17
  url: "jdbc:h2:./local/data/h2/database1;DB_CLOSE_ON_EXIT=FALSE;DATABASE_TO_UPPER=false;MODE=MySQL"
18
18
  user: sa
19
19
  password: "sa"
20
- strict-check: true
20
+ # Each ac_table row records the datasource its table lives in. When a row names a code that is
21
+ # not listed under items, true stops startup with an error and false only logs a warning. Unset,
22
+ # it is false. Keep it true in development so that a datasource removed from this file is
23
+ # noticed at the next start.
24
+ fail-on-unregistered-data-source: true
21
25
  auto-init:
26
+ # Install and upgrade the modules at every start by running their data files. Takes effect
27
+ # only when quicksilver.application.mode is development. In production the installer does it.
22
28
  enabled: true
23
- drop-first: true
29
+ # Drop the tables and views that Quicksilver manages before provisioning, so the database is
30
+ # rebuilt from init and demo data. Set drop-first: true for one start, then set it back to false.
31
+ drop-first: false
32
+ # Draft sync. Every start runs the upgrade drafts that changed since the last start. Data in
33
+ # the dev database is kept, including rows entered through the UI. Switching draft-sync on or
34
+ # off needs one rebuild with drop-first.
35
+ draft-sync: true
36
+ # Demo data: on a fresh install of a module, the .jsons files in its data/demo/ run after its
37
+ # init. A database that already exists does not pick up demo files added later, rebuild it with
38
+ # drop-first as described above.
39
+ demo-data: true
24
40
  webhook:
41
+ # Webhook targets that resolve to a private or loopback address (10.0.0.0/8, 127.0.0.0/8, ...)
42
+ # are refused unless the address falls in one of these CIDR ranges. The dev profile allows
43
+ # 127.0.0.1 so that a receiver running on this machine can be tested. Link-local addresses,
44
+ # which include the cloud metadata endpoint, are always refused whatever is listed here.
25
45
  restricted-exceptions: ["127.0.0.1/32"]
package/README.zh-CN.md DELETED
@@ -1,36 +0,0 @@
1
- # create-qs
2
-
3
- [English](README.md) | 简体中文 | [繁體中文](README.zh-TW.md)
4
-
5
- 用一条命令创建全栈 Quicksilver 项目。
6
-
7
- ```bash
8
- npm create qs
9
- ```
10
-
11
- 这一条命令请使用 npm,不要使用 pnpm。自 pnpm 11 起,`pnpm create` 会跳过发布不足 24 小时的版本(`minimumReleaseAge`),且不给出任何提示,当天发布的脚手架会被静默替换为较早的版本。npm 默认没有这一限制。项目创建完成后,其余操作一律使用 pnpm。
12
-
13
- 脚手架发布在公网 npmjs 上,未做任何配置的机器即可直接运行。它会询问项目及其第一个模块的若干问题,然后生成一个可以直接运行的全栈项目,包括 Kotlin/Spring Boot 后端、Preact/TypeScript 前端、Gradle Wrapper,以及文件型 H2 开发数据库。
14
-
15
- ## 环境要求
16
-
17
- - **JDK 25**。后端按 Java 25 编译,版本更低时无法编译。
18
- - **Node.js `>=22.13.0`**,这是 pnpm 11 要求的最低版本。
19
- - **pnpm 11**,创建项目之后的所有开发工作都使用它。workspace 与 catalog 均依赖 pnpm,因此不支持 npm 与 yarn。
20
- - **能够访问内部 Nexus**,见下一节。
21
-
22
- ## 首次安装依赖之前
23
-
24
- `create-qs` 是唯一发布到公网的包。平台的其余部分,包括 `@qs-platform/*`、`@qs-elements/*`、`@qs-charts/*` 三组 npm 包、Quicksilver 的 Gradle 插件以及全部 Maven 构件,都发布在内部 Nexus 上。内部 Nexus 的地址不对外公开,因此生成的项目不预置任何地址。请先向提供 Quicksilver 的一方获取地址与账户,然后在每台机器上配置一次。npm 的三个 scope 配置在 `~/.npmrc` 中,Gradle 的仓库地址与账户配置在 `~/.gradle/gradle.properties` 中。
25
-
26
- 具体写法见生成项目中开发手册的环境准备一章,即 `.quicksilver/manual/zh-CN/00-setup.md`。该章随项目提交,首次安装依赖之前即可阅读,脚手架执行完毕时也会提示它的位置。
27
-
28
- 然后启动项目:
29
-
30
- ```bash
31
- cd <项目名>
32
- pnpm install
33
- pnpm dev
34
- ```
35
-
36
- 在浏览器中访问 <http://localhost:6286>,使用 `admin` / `123456` 登录。`pnpm install` 同时会在 `00-setup.md` 所在目录写入开发手册的其余章节。
package/README.zh-TW.md DELETED
@@ -1,36 +0,0 @@
1
- # create-qs
2
-
3
- [English](README.md) | [简体中文](README.zh-CN.md) | 繁體中文
4
-
5
- 用一個指令建立全端 Quicksilver 專案。
6
-
7
- ```bash
8
- npm create qs
9
- ```
10
-
11
- 這一個指令請使用 npm,不要使用 pnpm。自 pnpm 11 起,`pnpm create` 會略過發布不足 24 小時的版本(`minimumReleaseAge`),且不會給出任何提示,當天發布的鷹架會被默默替換為較早的版本。npm 預設沒有這項限制。專案建立完成後,其餘操作一律使用 pnpm。
12
-
13
- 鷹架發布在公開的 npmjs 上,未做任何設定的電腦即可直接執行。它會詢問專案及其第一個模組的幾個問題,然後產生一個可以直接執行的全端專案,包括 Kotlin/Spring Boot 後端、Preact/TypeScript 前端、Gradle Wrapper,以及檔案型 H2 開發資料庫。
14
-
15
- ## 環境需求
16
-
17
- - **JDK 25**。後端依 Java 25 編譯,版本較低時無法編譯。
18
- - **Node.js `>=22.13.0`**,這是 pnpm 11 要求的最低版本。
19
- - **pnpm 11**,建立專案之後的所有開發工作都使用它。workspace 與 catalog 均依賴 pnpm,因此不支援 npm 與 yarn。
20
- - **能夠存取內部 Nexus**,請參閱下一節。
21
-
22
- ## 首次安裝相依套件之前
23
-
24
- `create-qs` 是唯一發布到公開 npmjs 的套件。平台的其餘部分,包括 `@qs-platform/*`、`@qs-elements/*`、`@qs-charts/*` 三組 npm 套件、Quicksilver 的 Gradle 外掛程式以及全部 Maven 構件,都發布在內部 Nexus 上。內部 Nexus 的位址不對外公開,因此產生的專案不預設任何位址。請先向提供 Quicksilver 的一方取得位址與帳號,然後在每台電腦上設定一次。npm 的三個 scope 設定在 `~/.npmrc` 中,Gradle 的儲存庫位址與帳號設定在 `~/.gradle/gradle.properties` 中。
25
-
26
- 具體寫法請參閱產生的專案中開發手冊的環境準備一章,即 `.quicksilver/manual/zh-TW/00-setup.md`。該章隨專案提交,首次安裝相依套件之前即可閱讀,鷹架執行完畢時也會提示它的位置。
27
-
28
- 然後啟動專案:
29
-
30
- ```bash
31
- cd <專案名稱>
32
- pnpm install
33
- pnpm dev
34
- ```
35
-
36
- 在瀏覽器中開啟 <http://localhost:6286>,使用 `admin` / `123456` 登入。`pnpm install` 同時會在 `00-setup.md` 所在目錄寫入開發手冊的其餘章節。
@@ -1,2 +0,0 @@
1
- { $insert: 'ac.module', id: '{{MODULE_ID}}', name: '{{MODULE_DIRECTORY}}', code: '{{MODULE_CODE}}', icon: 'quicksilver:module-work', {{MODULE_DEFAULT_MENU}}data_version: '0.0.1' }
2
- {{MODULE_MENU}}