openxiangda 2.0.0-alpha.5 → 2.0.0-alpha.9
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/dist/internal/web-build-verifier.d.ts +8 -0
- package/dist/internal/web-build-verifier.d.ts.map +1 -0
- package/dist/internal/web-build-verifier.js +63 -0
- package/dist/internal/web-build-verifier.js.map +1 -0
- package/dist/testing.d.ts +1 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +1 -0
- package/dist/testing.js.map +1 -1
- package/package.json +5 -5
- package/skills/manifest.json +1 -1
- package/skills/openxiangda-v2/SKILL.md +3 -3
- package/skills/openxiangda-v2/references/data-authz.md +1 -1
- package/skills/openxiangda-v2/references/workspace.md +1 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface OpenXiangdaWebBuildVerification {
|
|
2
|
+
assetFiles: number;
|
|
3
|
+
totalBytes: number;
|
|
4
|
+
totalJavaScriptGzipBytes: number;
|
|
5
|
+
initialJavaScriptGzipBytes: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function verifyOpenXiangdaWebBuild(distInput: string | URL): OpenXiangdaWebBuildVerification;
|
|
8
|
+
//# sourceMappingURL=web-build-verifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-build-verifier.d.ts","sourceRoot":"","sources":["../../src/internal/web-build-verifier.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,+BAA+B;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,wBAAwB,EAAE,MAAM,CAAC;IACjC,0BAA0B,EAAE,MAAM,CAAC;CACpC;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,GAAG,GAAG,GACtB,+BAA+B,CA0EjC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { gzipSync } from 'node:zlib';
|
|
5
|
+
const MAX_ASSET_FILES = 512;
|
|
6
|
+
const MAX_HTML_BYTES = 100_000;
|
|
7
|
+
const MAX_TOTAL_BYTES = 2_650_000;
|
|
8
|
+
const MAX_TOTAL_JAVASCRIPT_GZIP_BYTES = 800_000;
|
|
9
|
+
const MAX_INITIAL_JAVASCRIPT_GZIP_BYTES = 600_000;
|
|
10
|
+
export function verifyOpenXiangdaWebBuild(distInput) {
|
|
11
|
+
const dist = resolve(typeof distInput === 'string' ? distInput : fileURLToPath(distInput));
|
|
12
|
+
const assets = join(dist, 'assets');
|
|
13
|
+
const entries = readdirSync(assets, { withFileTypes: true });
|
|
14
|
+
if (entries.length > MAX_ASSET_FILES) {
|
|
15
|
+
throw new Error(`WEB_ASSET_FILE_COUNT_EXCEEDED:${entries.length}>${MAX_ASSET_FILES}`);
|
|
16
|
+
}
|
|
17
|
+
if (entries.some(entry => !entry.isFile())) {
|
|
18
|
+
throw new Error('WEB_ASSET_TREE_INVALID');
|
|
19
|
+
}
|
|
20
|
+
const names = entries.map(entry => entry.name);
|
|
21
|
+
const files = names.map(name => join(assets, name));
|
|
22
|
+
const sizes = files.map(file => statSync(file).size);
|
|
23
|
+
const totalBytes = sizes.reduce((total, size) => total + size, 0);
|
|
24
|
+
if (totalBytes > MAX_TOTAL_BYTES) {
|
|
25
|
+
throw new Error(`WEB_DIST_BUDGET_EXCEEDED:${totalBytes}>${MAX_TOTAL_BYTES}`);
|
|
26
|
+
}
|
|
27
|
+
if (names.some(name => name.endsWith('.map'))) {
|
|
28
|
+
throw new Error('WEB_SOURCE_MAP_FORBIDDEN');
|
|
29
|
+
}
|
|
30
|
+
const javascript = files.filter(file => file.endsWith('.js'));
|
|
31
|
+
const totalJavaScriptGzipBytes = javascript.reduce((total, file) => total + gzipSync(readFileSync(file), { level: 9 }).byteLength, 0);
|
|
32
|
+
if (totalJavaScriptGzipBytes > MAX_TOTAL_JAVASCRIPT_GZIP_BYTES) {
|
|
33
|
+
throw new Error(`WEB_JS_GZIP_BUDGET_EXCEEDED:${totalJavaScriptGzipBytes}>${MAX_TOTAL_JAVASCRIPT_GZIP_BYTES}`);
|
|
34
|
+
}
|
|
35
|
+
const htmlPath = join(dist, 'index.html');
|
|
36
|
+
const htmlBytes = statSync(htmlPath).size;
|
|
37
|
+
if (htmlBytes > MAX_HTML_BYTES) {
|
|
38
|
+
throw new Error(`WEB_HTML_BUDGET_EXCEEDED:${htmlBytes}>${MAX_HTML_BYTES}`);
|
|
39
|
+
}
|
|
40
|
+
const html = readFileSync(htmlPath, 'utf8');
|
|
41
|
+
if (!html.includes('<div id="root"></div>')) {
|
|
42
|
+
throw new Error('WEB_ROOT_MISSING');
|
|
43
|
+
}
|
|
44
|
+
const initialNames = [
|
|
45
|
+
...html.matchAll(/(?:src|href)="\.\/assets\/([^"]+\.js)"/g),
|
|
46
|
+
].map(match => match[1]);
|
|
47
|
+
const initialJavaScriptGzipBytes = [...new Set(initialNames)].reduce((total, name) => {
|
|
48
|
+
if (!names.includes(name))
|
|
49
|
+
throw new Error(`WEB_INITIAL_ASSET_MISSING:${name}`);
|
|
50
|
+
return (total +
|
|
51
|
+
gzipSync(readFileSync(join(assets, name)), { level: 9 }).byteLength);
|
|
52
|
+
}, 0);
|
|
53
|
+
if (initialJavaScriptGzipBytes > MAX_INITIAL_JAVASCRIPT_GZIP_BYTES) {
|
|
54
|
+
throw new Error(`WEB_INITIAL_JS_GZIP_BUDGET_EXCEEDED:${initialJavaScriptGzipBytes}>${MAX_INITIAL_JAVASCRIPT_GZIP_BYTES}`);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
assetFiles: entries.length,
|
|
58
|
+
totalBytes,
|
|
59
|
+
totalJavaScriptGzipBytes,
|
|
60
|
+
initialJavaScriptGzipBytes,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=web-build-verifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-build-verifier.js","sourceRoot":"","sources":["../../src/internal/web-build-verifier.ts"],"names":[],"mappings":"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"}
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from 'openxiangda-devkit-core/testing';
|
|
2
2
|
export { OpenXiangdaApplicationServices, verifySealedAppPackage, } from 'openxiangda-devkit-core';
|
|
3
3
|
export { sha256Digest } from 'openxiangda-contracts';
|
|
4
|
+
export { verifyOpenXiangdaWebBuild, type OpenXiangdaWebBuildVerification, } from './internal/web-build-verifier.js';
|
|
4
5
|
//# sourceMappingURL=testing.d.ts.map
|
package/dist/testing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC;AAChD,OAAO,EACL,8BAA8B,EAC9B,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC;AAChD,OAAO,EACL,8BAA8B,EAC9B,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EACL,yBAAyB,EACzB,KAAK,+BAA+B,GACrC,MAAM,kCAAkC,CAAC"}
|
package/dist/testing.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from 'openxiangda-devkit-core/testing';
|
|
2
2
|
export { OpenXiangdaApplicationServices, verifySealedAppPackage, } from 'openxiangda-devkit-core';
|
|
3
3
|
export { sha256Digest } from 'openxiangda-contracts';
|
|
4
|
+
export { verifyOpenXiangdaWebBuild, } from './internal/web-build-verifier.js';
|
|
4
5
|
//# sourceMappingURL=testing.js.map
|
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC;AAChD,OAAO,EACL,8BAA8B,EAC9B,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC;AAChD,OAAO,EACL,8BAA8B,EAC9B,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EACL,yBAAyB,GAE1B,MAAM,kCAAkC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openxiangda",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.9",
|
|
4
4
|
"description": "Unified OpenXiangda 2.0 CLI, SDK, React runtime, NestJS integration and AI skill distribution.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"dayjs": "1.11.18",
|
|
51
51
|
"docx-preview": "0.3.7",
|
|
52
|
-
"openxiangda-cli": "2.0.0-alpha.
|
|
52
|
+
"openxiangda-cli": "2.0.0-alpha.97",
|
|
53
53
|
"openxiangda-contracts": "2.0.0-alpha.46",
|
|
54
|
-
"openxiangda-devkit-core": "2.0.0-alpha.
|
|
55
|
-
"openxiangda-mcp": "2.0.0-alpha.
|
|
54
|
+
"openxiangda-devkit-core": "2.0.0-alpha.60",
|
|
55
|
+
"openxiangda-mcp": "2.0.0-alpha.60",
|
|
56
56
|
"openxiangda-nest": "2.0.0-alpha.55",
|
|
57
|
-
"openxiangda-skill-kit": "2.0.0-alpha.
|
|
57
|
+
"openxiangda-skill-kit": "2.0.0-alpha.78",
|
|
58
58
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
package/skills/manifest.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
{
|
|
5
5
|
"name": "openxiangda-v2",
|
|
6
6
|
"description": "Use when researching, designing, building, testing, or delivering a complete OpenXiangda 2.0 application.",
|
|
7
|
-
"sha256": "
|
|
7
|
+
"sha256": "5545fe41b6afa06ee11eda13eeb372a4baf0ea448224a681d044952db743dd82"
|
|
8
8
|
}
|
|
9
9
|
]
|
|
10
10
|
}
|
|
@@ -10,14 +10,14 @@ Treat requirement discovery, architecture, authorization, development, testing a
|
|
|
10
10
|
Before a workspace exists, use this Skill's exact npm version:
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
pnpm dlx openxiangda@2.0.0-alpha.
|
|
14
|
-
pnpm dlx openxiangda@2.0.0-alpha.
|
|
13
|
+
pnpm dlx openxiangda@2.0.0-alpha.9 login --base-url <platform>
|
|
14
|
+
pnpm dlx openxiangda@2.0.0-alpha.9 create <directory>
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Inside an application, use only its locked executable through `pnpm openxiangda`. Never invoke a bare global `openxiangda`, because that executable may belong to stable 1.x. Moving tags such as `latest` and `alpha` are forbidden. To install or refresh this same Skill from the package, run:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
pnpm dlx openxiangda@2.0.0-alpha.
|
|
20
|
+
pnpm dlx openxiangda@2.0.0-alpha.9 skill install --force
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Use this order:
|
|
@@ -65,7 +65,7 @@ without `access` inherits the resource read/create/update capability. Each
|
|
|
65
65
|
access array is all-of; `false` is explicit deny. The same arrays drive the
|
|
66
66
|
generated UI and platform field policies.
|
|
67
67
|
|
|
68
|
-
Field types are semantic, not PostgreSQL storage aliases. Use the catalog in the generated `AGENTS.md`: for example `text.short`, `number.integer`, `user.single`, `department.multiple`, `resource-ref.single`, `file`, `address` and `subtable`. The compiler alone chooses storage columns and constraints. Reference fields store complete labeled snapshots; resource references additionally declare their source and snapshot fields. File limits exist only under `file`; `maxCount` owns the single/multiple bound and `maxSizeMb` owns the per-file size bound. There is no `file.multiple` key.
|
|
68
|
+
Field types are semantic, not PostgreSQL storage aliases. Use the catalog in the generated `AGENTS.md`: for example `text.short`, `number.integer`, `user.single`, `department.multiple`, `resource-ref.single`, `file`, `address` and `subtable`. The compiler alone chooses storage columns and constraints. Reference fields store complete labeled snapshots; resource references additionally declare their source and snapshot fields. A resource source `labelField` must point to `text.short` or `text.long`; a `serial-number` field can be listed in `searchFields`, `descriptionFields` or `snapshotFields`, but it is not a display label. File limits exist only under `file`; `maxCount` owns the single/multiple bound and `maxSizeMb` owns the per-file size bound. There is no `file.multiple` key.
|
|
69
69
|
|
|
70
70
|
Do not author raw schema or storage words as field types. `string`, `text`,
|
|
71
71
|
`integer`, `decimal`, `boolean`, `date`, `datetime`, `uuid`, `json` and `file`
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
- Declare every custom operation capability in `authz.capabilities` with `kind: 'backend'`, then reference that same code from the operation and its allowed roles. Generated resource CRUD capabilities do not go in this catalog.
|
|
14
14
|
- Visitor duplicate protection uses `createVisitorReservation({ duplicateMatch: { fieldCode: submittedValue }, ... })`. `duplicateMatch` is a non-empty value map, never a field-name array, and no mutable pre-read is allowed.
|
|
15
15
|
- Desktop and mobile pages share values, validation and authorization, but use separate renderers. Options, members, departments and resource references store their complete display snapshots; attachments, images and signatures use platform-managed file references.
|
|
16
|
-
- `option.*`, `user.*`, `department.*` and `resource-ref.*` values never collapse to scalar IDs. Single values store one labeled snapshot and multiple values store snapshot arrays. `location` accepts only exact WGS84 coordinates captured by DingTalk or browser geolocation; it has no manual input or `manual` source. Roles that consume directory-backed fields explicitly include `app:<app-code>:directory:read`.
|
|
16
|
+
- `option.*`, `user.*`, `department.*` and `resource-ref.*` values never collapse to scalar IDs. Single values store one labeled snapshot and multiple values store snapshot arrays. A resource source `labelField` must be `text.short` or `text.long`; `serial-number` is allowed in source search, description and snapshot fields, but not as the label. `location` accepts only exact WGS84 coordinates captured by DingTalk or browser geolocation; it has no manual input or `manual` source. Roles that consume directory-backed fields explicitly include `app:<app-code>:directory:read`.
|
|
17
17
|
- Derive role grants with `resourceCapabilityCodes(appCode, resourceCode)`. Declare current-user rows only with `currentUserDataPolicy(...)`; do not invent operators, values or alternate current-user spellings.
|
|
18
18
|
- Do not add compatibility aliases, migration branches or silent fallbacks for an earlier 2.0 alpha contract. Replace an incorrect contract and regenerate the application.
|
|
19
19
|
- Standard Workflow and Notification Hub are optional 2.0 modules. Enable them only through the canonical `openxiangda.config.ts` declarations and generated clients; never copy 1.x workflow/message code, tables, APIs, templates or callbacks into this workspace. Keep ordinary CRUD and application-specific state machines independent of them.
|