Skip to content

Commit d17d265

Browse files
authored
Merge pull request #34 from A3S-Lab/chore/sync-acl-unordered-blocks-20260720
docs: sync ACL unordered block normalization
2 parents 7ae25de + 9f9a4bb commit d17d265

10 files changed

Lines changed: 189 additions & 28 deletions

File tree

apps/docs/content/docs/cn/acl/generation-boundaries.mdx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,17 @@ document shape。
2727
`canonical_digest`,Node 使用 `canonicalBytes` / `canonicalDigest`
2828
Canonical output 使用 UTF-8、LF line endings、恰好一个 final newline、
2929
sorted portable attribute/object keys,并对 duplicate object keys 使用
30-
last-value-wins。Block、label、list 和 function-argument order 保持有意义。
30+
last-value-wins。这些 base APIs 会保留 block、label、list 和
31+
function-argument order。
32+
33+
完成 schema admission 后,Rust 的
34+
`canonical_bytes_with_schema` / `canonical_digest_with_schema` 和 Node 的
35+
`canonicalBytesWithSchema` / `canonicalDigestWithSchema` 可以递归规范化 schema
36+
rule 标记为 `unordered` 的同一种 declared block name 的 occurrences。
37+
Matching blocks 按 canonical UTF-8 bytes 比较,并且只替换原有 slots。其它
38+
names、unknown blocks、labels、lists、calls 和 unmarked blocks 仍然保持顺序。
39+
Schema-aware APIs 只把 schema 用作 normalization metadata,不会执行
40+
admission。
3141

3242
Finite numbers 使用 ECMAScript shortest-round-trip formatting,negative zero
3343
输出为 `0`。Unicode scalar sequences 不做 normalization。Digest 格式为
@@ -55,11 +65,14 @@ ACL 负责:
5565
- 从 AST 生成 text
5666
- value constructors 和 document builders
5767
- parser diagnostics
68+
- declarative document-shape schemas 和 bounded validation reports
69+
- base 和 schema-aware canonical bytes 与 digests
5870
- language-neutral config shape
5971

6072
宿主组件负责:
6173

62-
- 验证 block 或 attribute 是否允许
74+
- 定义每个 product boundary 使用的 trusted schema
75+
- 验证 document shape 之外的 domain rules
6376
- 执行 function calls
6477
- 解析 environment variables 或 secret-store references
6578
- 应用 defaults

apps/docs/content/docs/cn/acl/index.mdx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,19 @@ Rust crate 与 Node.js/TypeScript SDK,可以把 ACL 文本解析成 AST,也
2323
| 函数 | 支持把 `env(...)``concat(...)` 和自定义函数名表示为 first-class call values。 |
2424
| AST | `Document``Block`、typed values、builders、value constructors 和 `Value` accessors。 |
2525
| 生成 | Native ACL output、block-header labels、sorted attributes 和 objects、type-stable strings,以及 map generation。 |
26-
| 完整性 | Rust/Node byte-identical canonical UTF-8 和带 algorithm prefix 的 SHA-256 digests。 |
26+
| 完整性 | Rust/Node byte-identical canonical UTF-8、带 algorithm prefix 的 SHA-256 digests,以及对 declared unordered block occurrences 的 recursive schema-aware normalization|
2727
| SDK | Rust crate `a3s-acl`;Node package `@a3s-lab/acl`,带 TypeScript definitions。 |
2828

2929
## 从这里开始
3030

3131
- [Syntax](/cn/docs/acl/syntax) 说明 blocks、attributes、values、calls、
3232
comments 和 parser boundaries。
3333
- [Rust API](/cn/docs/acl/rust-api) 覆盖 bounded parsing、schema admission、
34-
multi-diagnostic collection、canonical digests、generation、builders 和 AST
35-
value helpers。
34+
multi-diagnostic collection、schema-aware canonical digests、generation、
35+
builders 和 AST value helpers。
3636
- [Node SDK](/cn/docs/acl/node-sdk) 覆盖 JavaScript/TypeScript parser、
37-
schema validator、native ACL generator、canonical APIs 和 builders。
37+
schema validator、native ACL generator、schema-aware canonical APIs 和
38+
builders。
3839
- [Generation And Boundaries](/cn/docs/acl/generation-boundaries) 说明 stable
3940
output、canonical bytes、secret handling 和 host-component ownership。
4041

apps/docs/content/docs/cn/acl/node-sdk.mdx

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Node SDK
3-
description: "@a3s-lab/acl 的 JavaScript/TypeScript bounded parsing、schema admission、multi-diagnostics、native ACL generation、canonical digests 和 builders。"
3+
description: "@a3s-lab/acl 的 JavaScript/TypeScript bounded parsing、schema admission、multi-diagnostics、native generation、schema-aware canonical digests 和 builders。"
44
---
55

66
# Node SDK
@@ -52,6 +52,7 @@ const generated = generate(new DocumentBuilder().block(provider).build());
5252
| `Schema``AttributeSchema``BlockSchema``ValueSchema` | Closed-by-default document、block 和 recursive value rules 的 TypeScript definitions。 |
5353
| `generate(doc)` | 生成 labels 位于 block headers 的 native ACL text。 |
5454
| `canonicalBytes(doc)``canonicalDigest(doc)` | 生成 stable cross-SDK bytes 和带 algorithm prefix 的 SHA-256 digest。 |
55+
| `canonicalBytesWithSchema(doc, schema)``canonicalDigestWithSchema(doc, schema)` | 生成 bytes 或 digest 前,递归规范化 schema-declared unordered block occurrences。 |
5556
| `CanonicalError` | 对 non-finite numbers、non-scalar strings 和 non-portable identifiers 返回不回显值的错误。 |
5657
| `string``number``boolean``bool``nullValue``list``call` | Value constructors。 |
5758
| `BlockBuilder``DocumentBuilder` | Structured document builders。 |
@@ -124,6 +125,8 @@ Schemas 默认拒绝 undeclared attributes、blocks 和 object fields。它们
124125
required/optional attributes、block occurrence 和 label cardinalities,以及
125126
recursive `Any``String``Number``Bool``Null``List``Object`
126127
`Call``OneOf` rules。Extension points 必须使用相应的显式 allow flag。
128+
Block rule 的 `unordered: true` metadata 只影响 schema-aware
129+
canonicalization,不改变 validation。
127130

128131
Schema reports 使用 stable `acl.schema.*` codes 和
129132
`$.blocks.provider[0].attributes.api_key` 这样的 logical paths。Messages 和
@@ -146,6 +149,44 @@ alternate generator modes 和 generator options 均不支持。
146149
`canonicalDigest`。Canonical bytes 会保留 ordered ACL constructs,同时对
147150
semantically unordered attribute 和 object keys 排序。
148151

152+
完成 schema admission 后,如果 trusted schema 把 repeatable block
153+
occurrences 声明为 unordered,则使用 schema-aware APIs:
154+
155+
```typescript
156+
import {
157+
canonicalBytes,
158+
canonicalBytesWithSchema,
159+
canonicalDigestWithSchema,
160+
parse,
161+
validateDocument,
162+
type Schema,
163+
} from '@a3s-lab/acl';
164+
165+
const document = parse('provider "z" {}\nprovider "a" {}');
166+
const schema: Schema = {
167+
blocks: {
168+
provider: {
169+
labels: {min: 1, max: 1},
170+
unordered: true,
171+
},
172+
},
173+
};
174+
const report = validateDocument(document, schema);
175+
if (report.diagnostics.length > 0 || report.truncated) {
176+
throw new Error('document failed schema admission');
177+
}
178+
179+
const orderedBytes = canonicalBytes(document);
180+
const normalizedBytes = canonicalBytesWithSchema(document, schema);
181+
const digest = canonicalDigestWithSchema(document, schema);
182+
```
183+
184+
对于每个 body,normalization 会递归按照 canonical UTF-8 bytes 对每一种相同
185+
declared block name 的 occurrences 排序,同时保留它们原有的 positions。
186+
其它 block types、unknown blocks、labels、lists、calls 和 unmarked blocks
187+
仍然保持顺序。Schema-aware APIs 只把 schema 用作 normalization metadata,
188+
不会自行执行 admission。
189+
149190
## 边界
150191

151192
SDK 负责解析、生成、构造和验证 declarative ACL document shapes。它不会加载

apps/docs/content/docs/cn/acl/rust-api.mdx

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Rust API
3-
description: a3s-acl 的 Rust bounded parsing、schema admission、multi-diagnostics、canonical digests、native ACL generation、AST 和 builders。
3+
description: a3s-acl 的 Rust bounded parsing、schema admission、multi-diagnostics、schema-aware canonical digests、native ACL generation、AST 和 builders。
44
---
55

66
# Rust API
@@ -43,6 +43,7 @@ Public crate exports:
4343
| `generate` / `generate_acl` |`Document` 生成 ACL text。 |
4444
| `generate_from_map` |`HashMap<String, Value>` 生成 ACL text。 |
4545
| `canonical_bytes``canonical_digest` | 生成 stable cross-SDK bytes 和带 algorithm prefix 的 SHA-256 digest。 |
46+
| `canonical_bytes_with_schema``canonical_digest_with_schema` | 生成 bytes 或 digest 前,递归规范化 schema-declared unordered block occurrences。 |
4647
| `CanonicalError` | 拒绝 unsupported programmatic values 且不回显值。 |
4748
| `Lexer` / `Parser` | 高级 tokenization 和 parsing entry points。 |
4849

@@ -117,6 +118,8 @@ Schemas 默认拒绝 undeclared attributes、blocks 和 object fields。它们
117118
required/optional attributes、block occurrence 和 label cardinalities,以及
118119
recursive `Any``String``Number``Bool``Null``List``Object`
119120
`Call``OneOf` rules。Extension points 必须使用相应的显式 allow flag。
121+
`BlockSchema::unordered(true)` 会把 matching occurrences 标记为 schema-aware
122+
canonicalization 中的 semantically unordered;它不会改变 validation。
120123

121124
Schema reports 使用 stable `acl.schema.*` codes 和
122125
`$.blocks.provider[0].attributes.api_key` 这样的 logical paths。Messages 和
@@ -173,16 +176,39 @@ Generation 只有一个 native ACL contract:block labels 保留在 block heade
173176
## Canonical Bytes And Digests
174177

175178
```rust
176-
use a3s_acl::{canonical_bytes, canonical_digest};
179+
use a3s_acl::{
180+
canonical_bytes, canonical_bytes_with_schema, canonical_digest_with_schema,
181+
parse, validate_document, BlockSchema, Cardinality, Schema,
182+
};
183+
184+
let document = parse(
185+
"provider \"z\" {}\nprovider \"a\" {}",
186+
)?;
187+
let schema = Schema::new().block(
188+
"provider",
189+
BlockSchema::new(Schema::new())
190+
.labels(Cardinality::exactly(1))
191+
.unordered(true),
192+
);
193+
assert!(validate_document(&document, &schema).is_empty());
177194

178-
let bytes = canonical_bytes(&doc)?;
179-
let digest = canonical_digest(&doc)?;
195+
let ordered_bytes = canonical_bytes(&document)?;
196+
let normalized_bytes = canonical_bytes_with_schema(&document, &schema)?;
197+
let digest = canonical_digest_with_schema(&document, &schema)?;
198+
assert_ne!(ordered_bytes, normalized_bytes);
180199
assert!(digest.starts_with("sha256:"));
181200
```
182201

183202
签名、durable identity 和 cross-SDK comparisons 应使用 canonical APIs。
184203
Canonical bytes 会保留 ordered ACL constructs,同时对 semantically
185-
unordered attribute 和 object keys 排序。
204+
unordered attribute 和 object keys 排序。完成 schema admission 后,
205+
schema-aware APIs 还会对 rule 设置了 `unordered` 的每一种 declared block
206+
name 的 occurrences 排序。Normalization 会递归执行并比较 canonical UTF-8
207+
bytes,但保留 occurrences 的原有 positions,因此其它 block types 和 unknown
208+
blocks 不会移动。Labels、lists、calls 和 unmarked blocks 仍然保持顺序。
209+
210+
Schema-aware canonicalization 不会执行 schema admission。把 trusted schema
211+
用作 normalization metadata 前,需要单独验证 document。
186212

187213
## Notes
188214

apps/docs/content/docs/en/acl/generation-boundaries.mdx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,16 @@ Use `canonical_bytes` / `canonical_digest` in Rust and `canonicalBytes` /
2828
`canonicalDigest` in Node when configuration is signed, stored, or compared.
2929
Canonical output uses UTF-8, LF line endings, exactly one final newline, sorted
3030
portable attribute and object keys, and last-value-wins duplicate object keys.
31-
Block, label, list, and function-argument order remains significant.
31+
These base APIs preserve block, label, list, and function-argument order.
32+
33+
After schema admission, Rust
34+
`canonical_bytes_with_schema` / `canonical_digest_with_schema` and Node
35+
`canonicalBytesWithSchema` / `canonicalDigestWithSchema` can recursively
36+
normalize occurrences of a declared block name whose schema rule marks it
37+
`unordered`. Matching blocks are compared by canonical UTF-8 bytes and replace
38+
only their original slots. Other names, unknown blocks, labels, lists, calls,
39+
and unmarked blocks remain ordered. The schema-aware APIs use the schema only
40+
as normalization metadata; they do not perform admission.
3241

3342
Finite numbers use ECMAScript shortest-round-trip formatting, including `0` for
3443
negative zero. Unicode scalar sequences are preserved without normalization.
@@ -58,11 +67,14 @@ ACL owns:
5867
- generating text from AST
5968
- value constructors and document builders
6069
- parser diagnostics
70+
- declarative document-shape schemas and bounded validation reports
71+
- base and schema-aware canonical bytes and digests
6172
- language-neutral config shape
6273

6374
The host component owns:
6475

65-
- validating whether a block or attribute is allowed
76+
- defining the trusted schema at each product boundary
77+
- validating domain rules beyond document shape
6678
- evaluating function calls
6779
- resolving environment variables or secret-store references
6880
- applying defaults

apps/docs/content/docs/en/acl/index.mdx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,19 @@ is `@a3s-lab/acl` `0.2.2`. The crate repository metadata points to
2323
| Functions | First-class call values such as `env(...)`, `concat(...)`, and custom function names. |
2424
| AST | `Document`, `Block`, typed values, builders, value constructors, and `Value` accessors. |
2525
| Generation | Native ACL output, block-header labels, sorted attributes and objects, type-stable strings, and map generation. |
26-
| Integrity | Byte-identical Rust/Node canonical UTF-8 and algorithm-prefixed SHA-256 digests. |
26+
| Integrity | Byte-identical Rust/Node canonical UTF-8, algorithm-prefixed SHA-256 digests, and recursive schema-aware normalization for declared unordered block occurrences. |
2727
| SDKs | Rust crate `a3s-acl`; Node package `@a3s-lab/acl` with TypeScript definitions. |
2828

2929
## Start Here
3030

3131
- [Syntax](/docs/acl/syntax) documents blocks, attributes, values, calls,
3232
comments, and parser boundaries.
3333
- [Rust API](/docs/acl/rust-api) covers bounded parsing, schema admission,
34-
multi-diagnostic collection, canonical digests, generation, builders, and
35-
AST value helpers.
34+
multi-diagnostic collection, schema-aware canonical digests, generation,
35+
builders, and AST value helpers.
3636
- [Node SDK](/docs/acl/node-sdk) covers the JavaScript/TypeScript parser,
37-
schema validator, native ACL generator, canonical APIs, and builders.
37+
schema validator, native ACL generator, schema-aware canonical APIs, and
38+
builders.
3839
- [Generation And Boundaries](/docs/acl/generation-boundaries) explains stable
3940
output, canonical bytes, secret handling, and host-component ownership.
4041

apps/docs/content/docs/en/acl/node-sdk.mdx

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Node SDK
3-
description: JavaScript and TypeScript bounded parsing, schema admission, multi-diagnostics, native ACL generation, canonical digests, and builders in @a3s-lab/acl.
3+
description: JavaScript and TypeScript bounded parsing, schema admission, multi-diagnostics, native generation, schema-aware canonical digests, and builders in @a3s-lab/acl.
44
---
55

66
# Node SDK
@@ -52,6 +52,7 @@ const generated = generate(new DocumentBuilder().block(provider).build());
5252
| `Schema`, `AttributeSchema`, `BlockSchema`, `ValueSchema` | TypeScript definitions for closed-by-default document, block, and recursive value rules. |
5353
| `generate(doc)` | Generate native ACL text with labels in block headers. |
5454
| `canonicalBytes(doc)`, `canonicalDigest(doc)` | Produce stable cross-SDK bytes and an algorithm-prefixed SHA-256 digest. |
55+
| `canonicalBytesWithSchema(doc, schema)`, `canonicalDigestWithSchema(doc, schema)` | Recursively normalize schema-declared unordered block occurrences before producing bytes or a digest. |
5556
| `CanonicalError` | Report non-finite numbers, non-scalar strings, and non-portable identifiers without echoing values. |
5657
| `string`, `number`, `boolean`, `bool`, `nullValue`, `list`, `call` | Value constructors. |
5758
| `BlockBuilder`, `DocumentBuilder` | Structured document builders. |
@@ -124,7 +125,8 @@ Schemas reject undeclared attributes, blocks, and object fields by default.
124125
They can declare required or optional attributes, block occurrence and label
125126
cardinalities, and recursive `Any`, `String`, `Number`, `Bool`, `Null`, `List`,
126127
`Object`, `Call`, and `OneOf` rules. Extension points require the corresponding
127-
explicit allow flag.
128+
explicit allow flag. A block rule's `unordered: true` metadata affects only
129+
schema-aware canonicalization, not validation.
128130

129131
Schema reports use stable `acl.schema.*` codes and logical paths such as
130132
`$.blocks.provider[0].attributes.api_key`. Messages and paths never include
@@ -147,6 +149,44 @@ Use `canonicalBytes` or `canonicalDigest` for signatures, durable identity, and
147149
cross-SDK comparisons. Canonical bytes preserve ordered ACL constructs while
148150
sorting semantically unordered attribute and object keys.
149151

152+
After schema admission, use the schema-aware APIs when a trusted schema
153+
declares repeatable block occurrences as unordered:
154+
155+
```typescript
156+
import {
157+
canonicalBytes,
158+
canonicalBytesWithSchema,
159+
canonicalDigestWithSchema,
160+
parse,
161+
validateDocument,
162+
type Schema,
163+
} from '@a3s-lab/acl';
164+
165+
const document = parse('provider "z" {}\nprovider "a" {}');
166+
const schema: Schema = {
167+
blocks: {
168+
provider: {
169+
labels: {min: 1, max: 1},
170+
unordered: true,
171+
},
172+
},
173+
};
174+
const report = validateDocument(document, schema);
175+
if (report.diagnostics.length > 0 || report.truncated) {
176+
throw new Error('document failed schema admission');
177+
}
178+
179+
const orderedBytes = canonicalBytes(document);
180+
const normalizedBytes = canonicalBytesWithSchema(document, schema);
181+
const digest = canonicalDigestWithSchema(document, schema);
182+
```
183+
184+
For each body, normalization recursively sorts occurrences of each same
185+
declared block name by canonical UTF-8 bytes while retaining their existing
186+
positions. Other block types, unknown blocks, labels, lists, calls, and
187+
unmarked blocks remain ordered. The schema-aware APIs use the schema only as
188+
normalization metadata and do not perform admission themselves.
189+
150190
## Boundary
151191

152192
The SDK parses, generates, builds, and validates declarative ACL document

0 commit comments

Comments
 (0)