@peac/disc 0.12.13 → 0.12.14
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 +61 -27
- package/dist/index.cjs +133 -131
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +77 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +131 -130
- package/dist/index.js.map +1 -1
- package/dist/parser.d.ts +53 -6
- package/dist/parser.d.ts.map +1 -1
- package/dist/types.d.ts +47 -18
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @peac/disc
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Thin loader / validator and remote fetcher for `peac.txt` policy documents.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,37 +10,65 @@ pnpm add @peac/disc
|
|
|
10
10
|
|
|
11
11
|
## What It Does
|
|
12
12
|
|
|
13
|
-
`@peac/disc`
|
|
13
|
+
`@peac/disc` is a thin wrapper around
|
|
14
|
+
[`@peac/policy-kit`](../policy-kit) specialized for the `peac.txt`
|
|
15
|
+
discovery surface. It:
|
|
16
|
+
|
|
17
|
+
- fetches `/.well-known/peac.txt` with `discover(origin)`,
|
|
18
|
+
- delegates parsing of `peac-policy/0.1` YAML / JSON bytes to
|
|
19
|
+
`@peac/policy-kit.parsePolicyDocument`,
|
|
20
|
+
- returns a tolerant `ParseResult` (rather than throwing) that carries a
|
|
21
|
+
validated `PolicyDocument`, error messages, and advisory warnings,
|
|
22
|
+
- tolerates legacy key-discovery lines (`verify:`, `public_keys:`, `jwks:`)
|
|
23
|
+
in older example documents by stripping them before validation and
|
|
24
|
+
surfacing a structured `PEAC_LEGACY_PEAC_TXT_KEY_FIELD`
|
|
25
|
+
`DeprecationWarning`.
|
|
26
|
+
|
|
27
|
+
`peac.txt` is the **policy-document surface** per
|
|
28
|
+
`docs/specs/PEAC-TXT.md`. It is **not** a key discovery surface.
|
|
29
|
+
Cryptographic key resolution uses the normative chain
|
|
30
|
+
`iss` -> `/.well-known/peac-issuer.json` -> `jwks_uri` -> JWKS (see
|
|
31
|
+
`docs/specs/PEAC-ISSUER.md`). For that flow, use `parseIssuerConfig` /
|
|
32
|
+
`fetchIssuerConfig` from `@peac/protocol`.
|
|
14
33
|
|
|
15
34
|
## How Do I Use It?
|
|
16
35
|
|
|
17
|
-
### Parse a
|
|
36
|
+
### Parse a policy document
|
|
18
37
|
|
|
19
38
|
```typescript
|
|
20
|
-
import { parse
|
|
39
|
+
import { parse } from '@peac/disc';
|
|
21
40
|
|
|
22
41
|
const result = parse(`
|
|
23
|
-
version: peac/0.1
|
|
24
|
-
|
|
25
|
-
|
|
42
|
+
version: 'peac-policy/0.1'
|
|
43
|
+
defaults:
|
|
44
|
+
decision: deny
|
|
45
|
+
rules:
|
|
46
|
+
- name: allow-verified-agents
|
|
47
|
+
subject:
|
|
48
|
+
type: agent
|
|
49
|
+
labels: [verified]
|
|
50
|
+
purpose: inference
|
|
51
|
+
decision: allow
|
|
26
52
|
`);
|
|
27
53
|
|
|
28
54
|
if (result.valid) {
|
|
29
|
-
console.log(result.
|
|
55
|
+
console.log(result.data?.version); // 'peac-policy/0.1'
|
|
56
|
+
console.log(result.data?.rules[0].decision); // 'allow'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (result.warnings) {
|
|
60
|
+
// e.g. legacy key-discovery lines were stripped on parse
|
|
61
|
+
console.warn(result.warnings);
|
|
30
62
|
}
|
|
31
63
|
```
|
|
32
64
|
|
|
33
|
-
###
|
|
65
|
+
### Emit a policy document as YAML
|
|
34
66
|
|
|
35
67
|
```typescript
|
|
36
68
|
import { emit } from '@peac/disc';
|
|
69
|
+
import { createExamplePolicy } from '@peac/policy-kit';
|
|
37
70
|
|
|
38
|
-
const
|
|
39
|
-
version: 'peac/0.1',
|
|
40
|
-
issuer: 'https://example.com',
|
|
41
|
-
jwks_uri: 'https://example.com/.well-known/jwks.json',
|
|
42
|
-
});
|
|
43
|
-
|
|
71
|
+
const yaml = emit(createExamplePolicy());
|
|
44
72
|
// Serve at /.well-known/peac.txt
|
|
45
73
|
```
|
|
46
74
|
|
|
@@ -51,25 +79,30 @@ import { discover, WELL_KNOWN_PATH } from '@peac/disc';
|
|
|
51
79
|
|
|
52
80
|
const result = await discover('https://example.com');
|
|
53
81
|
if (result.valid) {
|
|
54
|
-
console.log(result.
|
|
82
|
+
console.log(result.data);
|
|
55
83
|
}
|
|
56
|
-
|
|
57
84
|
console.log(WELL_KNOWN_PATH); // '/.well-known/peac.txt'
|
|
58
85
|
```
|
|
59
86
|
|
|
60
|
-
|
|
87
|
+
The caller's user-agent is taken from the `PEAC_USER_AGENT` environment
|
|
88
|
+
variable when present; otherwise `peac-disc` is used. `@peac/disc` does
|
|
89
|
+
not hard-code a package version in the user-agent (runtime-visible
|
|
90
|
+
version constants belong in release tooling).
|
|
61
91
|
|
|
62
|
-
|
|
63
|
-
- `@peac/protocol` (Layer 3): Issuer resolution during receipt verification
|
|
64
|
-
- `@peac/jwks-cache`: Fetches JWKS from the URI discovered via `peac.txt`
|
|
92
|
+
## Limits
|
|
65
93
|
|
|
66
|
-
|
|
94
|
+
Per `docs/specs/PEAC-TXT.md` §6.1, remote documents larger than
|
|
95
|
+
**256 KiB** are rejected by `discover()`. Nesting depth, array length,
|
|
96
|
+
and string length limits are enforced by the underlying
|
|
97
|
+
`@peac/policy-kit` validator.
|
|
67
98
|
|
|
68
|
-
|
|
99
|
+
## Integrates With
|
|
69
100
|
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
|
|
101
|
+
- `@peac/policy-kit`: canonical compiler / parser / evaluator for
|
|
102
|
+
`peac-policy/0.1` documents. `@peac/disc.parse` delegates to
|
|
103
|
+
`@peac/policy-kit.parsePolicyDocument`.
|
|
104
|
+
- `@peac/protocol`: `parseIssuerConfig` / `fetchIssuerConfig` for the
|
|
105
|
+
normative key-discovery chain.
|
|
73
106
|
|
|
74
107
|
## License
|
|
75
108
|
|
|
@@ -77,6 +110,7 @@ Apache-2.0
|
|
|
77
110
|
|
|
78
111
|
---
|
|
79
112
|
|
|
80
|
-
PEAC Protocol is an open source project stewarded by Originary and community
|
|
113
|
+
PEAC Protocol is an open source project stewarded by Originary and community
|
|
114
|
+
contributors.
|
|
81
115
|
|
|
82
116
|
[Docs](https://www.peacprotocol.org) | [GitHub](https://github.com/peacprotocol/peac) | [Originary](https://www.originary.xyz)
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var policyKit = require('@peac/policy-kit');
|
|
4
|
+
|
|
3
5
|
var __defProp = Object.defineProperty;
|
|
4
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
7
|
var __esm = (fn, res) => function __init() {
|
|
@@ -13,162 +15,153 @@ var __export = (target, all) => {
|
|
|
13
15
|
// src/parser.ts
|
|
14
16
|
var parser_exports = {};
|
|
15
17
|
__export(parser_exports, {
|
|
18
|
+
__resetLegacyWarningForTests: () => __resetLegacyWarningForTests,
|
|
16
19
|
emit: () => emit,
|
|
17
20
|
parse: () => parse,
|
|
18
21
|
validate: () => validate
|
|
19
22
|
});
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
function fireLegacyWarning(field) {
|
|
24
|
+
if (legacyWarningFired) return;
|
|
25
|
+
legacyWarningFired = true;
|
|
26
|
+
if (typeof process !== "undefined" && typeof process.emitWarning === "function") {
|
|
27
|
+
process.emitWarning(
|
|
28
|
+
`peac.txt legacy key-discovery field "${field}" is deprecated and ignored. peac.txt is a policy-document surface (docs/specs/PEAC-TXT.md). Key resolution uses iss -> /.well-known/peac-issuer.json -> jwks_uri -> JWKS.`,
|
|
29
|
+
{ code: "PEAC_LEGACY_PEAC_TXT_KEY_FIELD", type: "DeprecationWarning" }
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function __resetLegacyWarningForTests() {
|
|
34
|
+
legacyWarningFired = false;
|
|
35
|
+
}
|
|
36
|
+
function collectLegacyWarnings(text) {
|
|
37
|
+
const warnings = [];
|
|
38
|
+
let firstField = null;
|
|
39
|
+
const lines = text.split(/\r?\n/);
|
|
40
|
+
lines.forEach((line, idx) => {
|
|
41
|
+
const match = line.match(/^\s*(verify|public_keys|jwks)\s*:/);
|
|
42
|
+
if (match) {
|
|
43
|
+
if (firstField === null) firstField = match[1];
|
|
44
|
+
warnings.push(
|
|
45
|
+
`Line ${idx + 1}: legacy key-discovery field "${match[1]}" ignored (peac.txt is policy-only; use peac-issuer.json for keys)`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
return { warnings, firstField };
|
|
50
|
+
}
|
|
51
|
+
function parse(text) {
|
|
52
|
+
const warnings = [];
|
|
53
|
+
if (text.trim().length === 0) {
|
|
26
54
|
return {
|
|
27
55
|
valid: false,
|
|
28
|
-
errors: [
|
|
29
|
-
lineCount: lines.length
|
|
56
|
+
errors: ["Empty policy document. Expected peac-policy/0.1 YAML or JSON."]
|
|
30
57
|
};
|
|
31
58
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
switch (field) {
|
|
40
|
-
case "preferences":
|
|
41
|
-
case "access_control":
|
|
42
|
-
case "provenance":
|
|
43
|
-
case "verify":
|
|
44
|
-
data[field] = match[1].trim();
|
|
45
|
-
break;
|
|
46
|
-
case "receipts":
|
|
47
|
-
data.receipts = match[1];
|
|
48
|
-
break;
|
|
49
|
-
case "payments":
|
|
50
|
-
data.payments = parseArray(match[1]);
|
|
51
|
-
break;
|
|
52
|
-
case "public_keys":
|
|
53
|
-
data.public_keys = parsePublicKeys(match[1]);
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
56
|
-
} catch (error) {
|
|
57
|
-
errors.push(
|
|
58
|
-
`Line ${index + 1}: ${error instanceof Error ? error.message : String(error)}`
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
break;
|
|
62
|
-
}
|
|
59
|
+
const hasLegacyLines = LEGACY_KEY_LINE.test(text);
|
|
60
|
+
try {
|
|
61
|
+
const data = policyKit.parsePolicyDocument(text);
|
|
62
|
+
if (hasLegacyLines) {
|
|
63
|
+
const legacy = collectLegacyWarnings(text);
|
|
64
|
+
warnings.push(...legacy.warnings);
|
|
65
|
+
if (legacy.firstField) fireLegacyWarning(legacy.firstField);
|
|
63
66
|
}
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
return {
|
|
68
|
+
valid: true,
|
|
69
|
+
data,
|
|
70
|
+
warnings: warnings.length > 0 ? warnings : void 0
|
|
71
|
+
};
|
|
72
|
+
} catch (firstErr) {
|
|
73
|
+
if (!hasLegacyLines) {
|
|
74
|
+
return failure(firstErr, warnings);
|
|
66
75
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
throw new Error(`Invalid public key format: ${keyString}`);
|
|
76
|
+
const legacy = collectLegacyWarnings(text);
|
|
77
|
+
warnings.push(...legacy.warnings);
|
|
78
|
+
if (legacy.firstField) fireLegacyWarning(legacy.firstField);
|
|
79
|
+
const stripped = text.replace(LEGACY_KEY_FULL_LINE_GLOBAL, "");
|
|
80
|
+
if (stripped.trim().length === 0) {
|
|
81
|
+
return {
|
|
82
|
+
valid: false,
|
|
83
|
+
errors: ["Empty policy document after stripping legacy key-discovery lines."],
|
|
84
|
+
warnings: warnings.length > 0 ? warnings : void 0
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const data = policyKit.parsePolicyDocument(stripped);
|
|
89
|
+
return {
|
|
90
|
+
valid: true,
|
|
91
|
+
data,
|
|
92
|
+
warnings: warnings.length > 0 ? warnings : void 0
|
|
93
|
+
};
|
|
94
|
+
} catch (retryErr) {
|
|
95
|
+
return failure(retryErr, warnings);
|
|
88
96
|
}
|
|
89
|
-
keys.push({
|
|
90
|
-
kid: keyMatch[1],
|
|
91
|
-
alg: keyMatch[2],
|
|
92
|
-
key: keyMatch[3]
|
|
93
|
-
});
|
|
94
97
|
}
|
|
95
|
-
return keys;
|
|
96
98
|
}
|
|
97
|
-
function
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
function failure(err, warnings) {
|
|
100
|
+
if (err instanceof policyKit.PolicyValidationError || err instanceof policyKit.PolicyLoadError) {
|
|
101
|
+
return {
|
|
102
|
+
valid: false,
|
|
103
|
+
errors: [err.message],
|
|
104
|
+
warnings: warnings.length > 0 ? warnings : void 0
|
|
105
|
+
};
|
|
102
106
|
}
|
|
107
|
+
return {
|
|
108
|
+
valid: false,
|
|
109
|
+
errors: [err instanceof Error ? err.message : String(err)],
|
|
110
|
+
warnings: warnings.length > 0 ? warnings : void 0
|
|
111
|
+
};
|
|
103
112
|
}
|
|
104
|
-
function emit(
|
|
105
|
-
|
|
106
|
-
if (data.preferences) {
|
|
107
|
-
lines.push(`preferences: ${data.preferences}`);
|
|
108
|
-
}
|
|
109
|
-
if (data.access_control) {
|
|
110
|
-
lines.push(`access_control: ${data.access_control}`);
|
|
111
|
-
}
|
|
112
|
-
if (data.payments && data.payments.length > 0) {
|
|
113
|
-
data.payments.forEach((p) => assertSafeFieldValue(p, "payment"));
|
|
114
|
-
const paymentsStr = data.payments.map((p) => `"${p}"`).join(", ");
|
|
115
|
-
lines.push(`payments: [${paymentsStr}]`);
|
|
116
|
-
}
|
|
117
|
-
if (data.provenance) {
|
|
118
|
-
lines.push(`provenance: ${data.provenance}`);
|
|
119
|
-
}
|
|
120
|
-
if (data.receipts) {
|
|
121
|
-
lines.push(`receipts: ${data.receipts}`);
|
|
122
|
-
}
|
|
123
|
-
if (data.verify) {
|
|
124
|
-
lines.push(`verify: ${data.verify}`);
|
|
125
|
-
}
|
|
126
|
-
if (data.public_keys && data.public_keys.length > 0) {
|
|
127
|
-
data.public_keys.forEach((k) => {
|
|
128
|
-
assertSafeFieldValue(k.kid, "kid");
|
|
129
|
-
assertSafeFieldValue(k.alg, "alg");
|
|
130
|
-
assertSafeFieldValue(k.key, "key");
|
|
131
|
-
});
|
|
132
|
-
const keysStr = data.public_keys.map((k) => `"${k.kid}:${k.alg}:${k.key}"`).join(", ");
|
|
133
|
-
lines.push(`public_keys: [${keysStr}]`);
|
|
134
|
-
}
|
|
135
|
-
if (lines.length > MAX_LINES) {
|
|
136
|
-
throw new Error(`Generated peac.txt exceeds ${MAX_LINES} lines: ${lines.length}`);
|
|
137
|
-
}
|
|
138
|
-
return lines.join("\n");
|
|
113
|
+
function emit(doc) {
|
|
114
|
+
return policyKit.serializePolicyYaml(doc);
|
|
139
115
|
}
|
|
140
|
-
function validate(
|
|
141
|
-
|
|
142
|
-
return result.valid;
|
|
116
|
+
function validate(text) {
|
|
117
|
+
return parse(text).valid;
|
|
143
118
|
}
|
|
144
|
-
var
|
|
119
|
+
var LEGACY_KEY_LINE, LEGACY_KEY_FULL_LINE_GLOBAL, legacyWarningFired;
|
|
145
120
|
var init_parser = __esm({
|
|
146
121
|
"src/parser.ts"() {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
access_control: /^access_control:\s*(.+)$/,
|
|
151
|
-
payments: /^payments:\s*\[([^\]]+)\]$/,
|
|
152
|
-
provenance: /^provenance:\s*(.+)$/,
|
|
153
|
-
receipts: /^receipts:\s*(required|optional)$/,
|
|
154
|
-
verify: /^verify:\s*(.+)$/,
|
|
155
|
-
public_keys: /^public_keys:\s*\[([^\]]+)\]$/
|
|
156
|
-
};
|
|
157
|
-
UNSAFE_FIELD_CHARS = /["\n\r\x00-\x1f:[\]]/;
|
|
122
|
+
LEGACY_KEY_LINE = /^\s*(verify|public_keys|jwks)\s*:/m;
|
|
123
|
+
LEGACY_KEY_FULL_LINE_GLOBAL = /^\s*(verify|public_keys|jwks)\s*:.*\r?\n?/gm;
|
|
124
|
+
legacyWarningFired = false;
|
|
158
125
|
}
|
|
159
126
|
});
|
|
160
127
|
|
|
161
128
|
// src/index.ts
|
|
162
129
|
init_parser();
|
|
163
|
-
var
|
|
164
|
-
var MAX_LINES2 = 20;
|
|
130
|
+
var MAX_BYTES = 262144;
|
|
165
131
|
var WELL_KNOWN_PATH = "/.well-known/peac.txt";
|
|
166
|
-
var
|
|
167
|
-
|
|
132
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
133
|
+
var DEFAULT_USER_AGENT = "peac-disc";
|
|
134
|
+
async function discover(origin, options = {}) {
|
|
135
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
136
|
+
if (typeof fetchImpl !== "function") {
|
|
137
|
+
return {
|
|
138
|
+
valid: false,
|
|
139
|
+
errors: [
|
|
140
|
+
"Discovery failed: no fetch implementation available (supply options.fetchImpl or provide a global fetch)"
|
|
141
|
+
]
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const envUa = typeof process !== "undefined" && typeof process.env === "object" ? process.env.PEAC_USER_AGENT : void 0;
|
|
145
|
+
const userAgent = options.userAgent ?? envUa ?? DEFAULT_USER_AGENT;
|
|
146
|
+
const maxBytes = options.maxBytes ?? MAX_BYTES;
|
|
147
|
+
const redirect = options.redirect ?? "error";
|
|
148
|
+
let localController;
|
|
149
|
+
let timer;
|
|
150
|
+
let signal = options.signal;
|
|
151
|
+
if (!signal) {
|
|
152
|
+
localController = new AbortController();
|
|
153
|
+
signal = localController.signal;
|
|
154
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
155
|
+
if (timeoutMs > 0 && Number.isFinite(timeoutMs)) {
|
|
156
|
+
timer = setTimeout(() => localController?.abort(), timeoutMs);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
168
159
|
try {
|
|
169
160
|
const url = new URL(WELL_KNOWN_PATH, origin);
|
|
170
|
-
const response = await
|
|
171
|
-
headers: { "User-Agent":
|
|
161
|
+
const response = await fetchImpl(url.toString(), {
|
|
162
|
+
headers: { "User-Agent": userAgent },
|
|
163
|
+
signal,
|
|
164
|
+
redirect
|
|
172
165
|
});
|
|
173
166
|
if (!response.ok) {
|
|
174
167
|
return {
|
|
@@ -177,6 +170,12 @@ async function discover(origin) {
|
|
|
177
170
|
};
|
|
178
171
|
}
|
|
179
172
|
const content = await response.text();
|
|
173
|
+
if (content.length > maxBytes) {
|
|
174
|
+
return {
|
|
175
|
+
valid: false,
|
|
176
|
+
errors: [`peac.txt exceeds ${maxBytes} bytes (got ${content.length})`]
|
|
177
|
+
};
|
|
178
|
+
}
|
|
180
179
|
const { parse: parse2 } = await Promise.resolve().then(() => (init_parser(), parser_exports));
|
|
181
180
|
return parse2(content);
|
|
182
181
|
} catch (error) {
|
|
@@ -184,12 +183,15 @@ async function discover(origin) {
|
|
|
184
183
|
valid: false,
|
|
185
184
|
errors: [`Discovery failed: ${error instanceof Error ? error.message : String(error)}`]
|
|
186
185
|
};
|
|
186
|
+
} finally {
|
|
187
|
+
if (timer) clearTimeout(timer);
|
|
187
188
|
}
|
|
188
189
|
}
|
|
189
190
|
|
|
190
|
-
exports.
|
|
191
|
-
exports.
|
|
191
|
+
exports.DEFAULT_TIMEOUT_MS = DEFAULT_TIMEOUT_MS;
|
|
192
|
+
exports.MAX_BYTES = MAX_BYTES;
|
|
192
193
|
exports.WELL_KNOWN_PATH = WELL_KNOWN_PATH;
|
|
194
|
+
exports.__resetLegacyWarningForTests = __resetLegacyWarningForTests;
|
|
193
195
|
exports.discover = discover;
|
|
194
196
|
exports.emit = emit;
|
|
195
197
|
exports.parse = parse;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/parser.ts","../src/index.ts"],"names":["MAX_LINES","parse"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @peac/disc/parser - peac.txt parser with ≤20 lines enforcement\n * ABNF-compliant discovery document parsing\n */\n\nimport type { PeacDiscovery, ParseResult, PublicKeyInfo, ValidationOptions } from './types.js';\n\nconst MAX_LINES = 20;\nconst FIELD_PATTERNS = {\n preferences: /^preferences:\\s*(.+)$/,\n access_control: /^access_control:\\s*(.+)$/,\n payments: /^payments:\\s*\\[([^\\]]+)\\]$/,\n provenance: /^provenance:\\s*(.+)$/,\n receipts: /^receipts:\\s*(required|optional)$/,\n verify: /^verify:\\s*(.+)$/,\n public_keys: /^public_keys:\\s*\\[([^\\]]+)\\]$/,\n};\n\nexport function parse(content: string, options: ValidationOptions = {}): ParseResult {\n const maxLines = options.maxLines ?? MAX_LINES;\n const errors: string[] = [];\n const data: PeacDiscovery = {};\n\n const lines = content\n .split('\\n')\n .map((l) => l.trim())\n .filter((l) => l && !l.startsWith('#'));\n\n // Enforce line limit\n if (lines.length > maxLines) {\n return {\n valid: false,\n errors: [`Line limit exceeded: ${lines.length} > ${maxLines}`],\n lineCount: lines.length,\n };\n }\n\n // Parse each line\n for (const [index, line] of lines.entries()) {\n let matched = false;\n\n for (const [field, pattern] of Object.entries(FIELD_PATTERNS)) {\n const match = line.match(pattern);\n if (match) {\n matched = true;\n try {\n switch (field) {\n case 'preferences':\n case 'access_control':\n case 'provenance':\n case 'verify':\n data[field] = match[1].trim();\n break;\n case 'receipts':\n data.receipts = match[1] as 'required' | 'optional';\n break;\n case 'payments':\n data.payments = parseArray(match[1]);\n break;\n case 'public_keys':\n data.public_keys = parsePublicKeys(match[1]);\n break;\n }\n } catch (error) {\n errors.push(\n `Line ${index + 1}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n break;\n }\n }\n\n if (!matched) {\n errors.push(`Line ${index + 1}: Invalid format: ${line}`);\n }\n }\n\n // Validate required fields\n if (!data.verify) {\n errors.push('Missing required field: verify');\n }\n\n return {\n valid: errors.length === 0,\n data: errors.length === 0 ? data : undefined,\n errors: errors.length > 0 ? errors : undefined,\n lineCount: lines.length,\n };\n}\n\nfunction parseArray(content: string): string[] {\n return content\n .split(',')\n .map((item) => item.trim().replace(/^[\"']|[\"']$/g, ''))\n .filter((item) => item.length > 0);\n}\n\nfunction parsePublicKeys(content: string): PublicKeyInfo[] {\n const keyStrings = parseArray(content);\n const keys: PublicKeyInfo[] = [];\n\n for (const keyString of keyStrings) {\n const keyMatch = keyString.match(/^([^:]+):([^:]+):(.+)$/);\n if (!keyMatch) {\n throw new Error(`Invalid public key format: ${keyString}`);\n }\n\n keys.push({\n kid: keyMatch[1],\n alg: keyMatch[2],\n key: keyMatch[3],\n });\n }\n\n return keys;\n}\n\n/** Characters that break peac.txt wire format: quotes, colons, brackets, control chars */\nconst UNSAFE_FIELD_CHARS = /[\"\\n\\r\\x00-\\x1f:[\\]]/;\n\nfunction assertSafeFieldValue(value: string, fieldName: string): void {\n if (UNSAFE_FIELD_CHARS.test(value)) {\n throw new Error(\n `Invalid ${fieldName}: must not contain quotes, colons, brackets, newlines, or control characters`\n );\n }\n}\n\nexport function emit(data: PeacDiscovery): string {\n const lines: string[] = [];\n\n if (data.preferences) {\n lines.push(`preferences: ${data.preferences}`);\n }\n\n if (data.access_control) {\n lines.push(`access_control: ${data.access_control}`);\n }\n\n if (data.payments && data.payments.length > 0) {\n data.payments.forEach((p) => assertSafeFieldValue(p, 'payment'));\n const paymentsStr = data.payments.map((p) => `\"${p}\"`).join(', ');\n lines.push(`payments: [${paymentsStr}]`);\n }\n\n if (data.provenance) {\n lines.push(`provenance: ${data.provenance}`);\n }\n\n if (data.receipts) {\n lines.push(`receipts: ${data.receipts}`);\n }\n\n if (data.verify) {\n lines.push(`verify: ${data.verify}`);\n }\n\n if (data.public_keys && data.public_keys.length > 0) {\n data.public_keys.forEach((k) => {\n assertSafeFieldValue(k.kid, 'kid');\n assertSafeFieldValue(k.alg, 'alg');\n assertSafeFieldValue(k.key, 'key');\n });\n const keysStr = data.public_keys.map((k) => `\"${k.kid}:${k.alg}:${k.key}\"`).join(', ');\n lines.push(`public_keys: [${keysStr}]`);\n }\n\n // Enforce line limit during emission\n if (lines.length > MAX_LINES) {\n throw new Error(`Generated peac.txt exceeds ${MAX_LINES} lines: ${lines.length}`);\n }\n\n return lines.join('\\n');\n}\n\nexport function validate(content: string): boolean {\n const result = parse(content);\n return result.valid;\n}\n","/**\n * @peac/disc - PEAC discovery with ≤20 lines enforcement\n * ABNF-compliant .well-known/peac.txt parser and generator\n */\n\nexport { parse, emit, validate } from './parser.js';\nexport type { PeacDiscovery, PublicKeyInfo, ParseResult, ValidationOptions } from './types.js';\n\n// Constants\nexport const VERSION = '0.9.15';\nexport const MAX_LINES = 20;\nexport const WELL_KNOWN_PATH = '/.well-known/peac.txt';\n\nconst UA = `PEAC/${VERSION} (+https://www.peacprotocol.org)`;\n\n// Convenience function for fetching and parsing discovery documents\nexport async function discover(origin: string): Promise<import('./types.js').ParseResult> {\n try {\n const url = new URL(WELL_KNOWN_PATH, origin);\n const response = await fetch(url.toString(), {\n headers: { 'User-Agent': UA },\n });\n\n if (!response.ok) {\n return {\n valid: false,\n errors: [`HTTP ${response.status}: ${response.statusText}`],\n };\n }\n\n const content = await response.text();\n const { parse } = await import('./parser.js');\n return parse(content);\n } catch (error) {\n return {\n valid: false,\n errors: [`Discovery failed: ${error instanceof Error ? error.message : String(error)}`],\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/parser.ts","../src/index.ts"],"names":["parsePolicyDocument","PolicyValidationError","PolicyLoadError","serializePolicyYaml","parse"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @peac/disc/parser - thin peac.txt policy-document loader/validator.\n *\n * peac.txt is a POLICY DOCUMENT surface per docs/specs/PEAC-TXT.md.\n * Full parsing is delegated to `@peac/policy-kit.parsePolicyDocument`;\n * this module only provides:\n *\n * - format detection (YAML vs JSON) per PEAC-TXT.md §5.1\n * - a tolerant `parse()` wrapper that returns a structured `ParseResult`\n * (rather than throwing)\n * - structured warnings for legacy key-discovery lines (`verify`,\n * `public_keys`, `jwks`) that appeared in pre-v0.12.14 peac.txt\n * examples. Those lines are NEVER honored here: key discovery flows\n * through `iss` -> /.well-known/peac-issuer.json -> `jwks_uri` -> JWKS\n * (docs/specs/PEAC-ISSUER.md).\n * - a tiny `emit()` helper that serializes a `PolicyDocument` via\n * `@peac/policy-kit.serializePolicyYaml`\n *\n * @see docs/specs/PEAC-TXT.md\n * @see docs/specs/PEAC-ISSUER.md\n */\n\nimport {\n PolicyLoadError,\n PolicyValidationError,\n parsePolicyDocument,\n serializePolicyYaml,\n type PolicyDocument,\n} from '@peac/policy-kit';\nimport type { ParseResult } from './types.js';\n\nconst LEGACY_KEY_LINE = /^\\s*(verify|public_keys|jwks)\\s*:/m;\n/** Strips the whole line (including the trailing newline) when matched. */\nconst LEGACY_KEY_FULL_LINE_GLOBAL = /^\\s*(verify|public_keys|jwks)\\s*:.*\\r?\\n?/gm;\n\nlet legacyWarningFired = false;\n\nfunction fireLegacyWarning(field: string): void {\n if (legacyWarningFired) return;\n legacyWarningFired = true;\n if (typeof process !== 'undefined' && typeof process.emitWarning === 'function') {\n process.emitWarning(\n `peac.txt legacy key-discovery field \"${field}\" is deprecated and ignored. ` +\n `peac.txt is a policy-document surface (docs/specs/PEAC-TXT.md). ` +\n `Key resolution uses iss -> /.well-known/peac-issuer.json -> jwks_uri -> JWKS.`,\n { code: 'PEAC_LEGACY_PEAC_TXT_KEY_FIELD', type: 'DeprecationWarning' }\n );\n }\n}\n\n/**\n * Reset the one-shot legacy-warning flag. Exposed for tests that need to\n * observe the warning more than once per process.\n */\nexport function __resetLegacyWarningForTests(): void {\n legacyWarningFired = false;\n}\n\nfunction collectLegacyWarnings(text: string): { warnings: string[]; firstField: string | null } {\n const warnings: string[] = [];\n let firstField: string | null = null;\n const lines = text.split(/\\r?\\n/);\n lines.forEach((line, idx) => {\n const match = line.match(/^\\s*(verify|public_keys|jwks)\\s*:/);\n if (match) {\n if (firstField === null) firstField = match[1];\n warnings.push(\n `Line ${idx + 1}: legacy key-discovery field \"${match[1]}\" ignored ` +\n `(peac.txt is policy-only; use peac-issuer.json for keys)`\n );\n }\n });\n return { warnings, firstField };\n}\n\n/**\n * Parse a peac.txt policy document. Accepts YAML or JSON per\n * `docs/specs/PEAC-TXT.md` §5.1. On success, `data` is the validated\n * `peac-policy/0.1` `PolicyDocument`.\n *\n * Legacy key-discovery lines (`verify:`, `public_keys:`, `jwks:`) are\n * tolerated via a two-pass strategy: first the raw bytes are handed to\n * `parsePolicyDocument`; if validation fails AND top-level legacy lines\n * are present, the legacy lines are stripped and parsing is retried\n * once. Detected legacy lines are listed in `warnings` and surface a\n * structured `PEAC_LEGACY_PEAC_TXT_KEY_FIELD` `DeprecationWarning` once\n * per process. They never populate the parsed result. This preserves\n * the original bytes when a policy document happens to mention those\n * tokens inside comments, block scalars, or rule text.\n */\nexport function parse(text: string): ParseResult {\n const warnings: string[] = [];\n\n if (text.trim().length === 0) {\n return {\n valid: false,\n errors: ['Empty policy document. Expected peac-policy/0.1 YAML or JSON.'],\n };\n }\n\n const hasLegacyLines = LEGACY_KEY_LINE.test(text);\n\n // First pass: try the raw bytes. If they parse cleanly, legacy-looking\n // substrings inside block scalars / comments / rule text are not\n // mutated.\n try {\n const data = parsePolicyDocument(text);\n if (hasLegacyLines) {\n const legacy = collectLegacyWarnings(text);\n warnings.push(...legacy.warnings);\n if (legacy.firstField) fireLegacyWarning(legacy.firstField);\n }\n return {\n valid: true,\n data,\n warnings: warnings.length > 0 ? warnings : undefined,\n };\n } catch (firstErr) {\n if (!hasLegacyLines) {\n return failure(firstErr, warnings);\n }\n // Second pass: strip top-level legacy key-discovery lines and retry.\n const legacy = collectLegacyWarnings(text);\n warnings.push(...legacy.warnings);\n if (legacy.firstField) fireLegacyWarning(legacy.firstField);\n\n const stripped = text.replace(LEGACY_KEY_FULL_LINE_GLOBAL, '');\n if (stripped.trim().length === 0) {\n return {\n valid: false,\n errors: ['Empty policy document after stripping legacy key-discovery lines.'],\n warnings: warnings.length > 0 ? warnings : undefined,\n };\n }\n try {\n const data = parsePolicyDocument(stripped);\n return {\n valid: true,\n data,\n warnings: warnings.length > 0 ? warnings : undefined,\n };\n } catch (retryErr) {\n return failure(retryErr, warnings);\n }\n }\n}\n\nfunction failure(err: unknown, warnings: string[]): ParseResult {\n if (err instanceof PolicyValidationError || err instanceof PolicyLoadError) {\n return {\n valid: false,\n errors: [err.message],\n warnings: warnings.length > 0 ? warnings : undefined,\n };\n }\n return {\n valid: false,\n errors: [err instanceof Error ? err.message : String(err)],\n warnings: warnings.length > 0 ? warnings : undefined,\n };\n}\n\n/**\n * Serialize a `peac-policy/0.1` `PolicyDocument` as YAML suitable for\n * serving at `/.well-known/peac.txt`. Delegates to\n * `@peac/policy-kit.serializePolicyYaml`.\n */\nexport function emit(doc: PolicyDocument): string {\n return serializePolicyYaml(doc);\n}\n\n/**\n * Convenience predicate: returns `true` iff `text` parses as a valid\n * `peac-policy/0.1` document.\n */\nexport function validate(text: string): boolean {\n return parse(text).valid;\n}\n","/**\n * @peac/disc - thin peac.txt policy-document loader/validator and remote fetcher.\n *\n * peac.txt is a POLICY DOCUMENT surface per docs/specs/PEAC-TXT.md. Full\n * parsing is delegated to `@peac/policy-kit.parsePolicyDocument`; this\n * package provides a tolerant `parse()` wrapper, a `discover()` remote\n * fetcher, and structured warnings for legacy key-discovery lines that\n * appeared in older peac.txt examples.\n *\n * `peac.txt` is NOT a key discovery surface. Cryptographic key resolution\n * flows through `iss` -> /.well-known/peac-issuer.json -> `jwks_uri` -> JWKS\n * (docs/specs/PEAC-ISSUER.md). Callers that need key material MUST use\n * `parseIssuerConfig` / `fetchIssuerConfig` from `@peac/protocol`.\n *\n * Legacy key-discovery lines (`verify:`, `public_keys:`, `jwks:`) are\n * tolerated on parse: they are stripped before validation, surfaced on\n * `ParseResult.warnings`, and fire a structured `process.emitWarning`\n * with code `PEAC_LEGACY_PEAC_TXT_KEY_FIELD` once per process.\n */\n\nexport { parse, emit, validate, __resetLegacyWarningForTests } from './parser.js';\nexport type { ParseResult, ValidationOptions, PolicyDocument } from './types.js';\n\n/**\n * @deprecated Retained for one minor to keep existing import paths\n * compiling. peac.txt policy documents now resolve to a `PolicyDocument`\n * (re-exported from `@peac/policy-kit`). Removal target: next cleanup\n * release.\n */\nexport type { PeacDiscovery, PublicKeyInfo } from './types.js';\n\nexport const MAX_BYTES = 262144; // 256 KiB, per docs/specs/PEAC-TXT.md §6.1\nexport const WELL_KNOWN_PATH = '/.well-known/peac.txt';\nexport const DEFAULT_TIMEOUT_MS = 5000;\nconst DEFAULT_USER_AGENT = 'peac-disc';\n\n/**\n * Minimal fetch signature accepted by `discover`. `undici` / `node-fetch` /\n * browser / test-double implementations all satisfy this.\n */\nexport type DiscoverFetch = (\n input: string,\n init?: {\n headers?: Record<string, string>;\n signal?: AbortSignal;\n redirect?: 'follow' | 'error' | 'manual';\n }\n) => Promise<{\n ok: boolean;\n status: number;\n statusText: string;\n text(): Promise<string>;\n}>;\n\nexport interface DiscoverOptions {\n /**\n * HTTP fetch implementation. Defaults to the ambient `fetch` global.\n * Supply `undici.fetch`, a test double, or a policy-wrapping client as\n * needed.\n */\n fetchImpl?: DiscoverFetch;\n /**\n * User-agent string. Precedence (highest first): this option, the\n * `PEAC_USER_AGENT` environment variable, then `peac-disc`.\n */\n userAgent?: string;\n /** Caller-supplied abort signal. */\n signal?: AbortSignal;\n /**\n * Millisecond timeout. Ignored when `signal` is supplied (the caller\n * owns cancellation). Defaults to 5_000 when neither is provided.\n */\n timeoutMs?: number;\n /** Override the 256 KiB body cap from docs/specs/PEAC-TXT.md §6.1. */\n maxBytes?: number;\n /**\n * Redirect policy. Defaults to `error` to avoid cross-origin surprise\n * on a well-known discovery endpoint; set to `follow` explicitly if the\n * deployment relies on server-side redirects.\n */\n redirect?: 'follow' | 'error' | 'manual';\n}\n\n/**\n * Fetch and parse a remote peac.txt policy document. On success returns a\n * `ParseResult` whose `data` is the validated `peac-policy/0.1`\n * `PolicyDocument`. Legacy key-discovery lines in the fetched bytes are\n * surfaced as warnings but never populate `data`.\n */\nexport async function discover(\n origin: string,\n options: DiscoverOptions = {}\n): Promise<import('./types.js').ParseResult> {\n const fetchImpl = (options.fetchImpl ?? (globalThis as { fetch?: DiscoverFetch }).fetch) as\n | DiscoverFetch\n | undefined;\n if (typeof fetchImpl !== 'function') {\n return {\n valid: false,\n errors: [\n 'Discovery failed: no fetch implementation available (supply options.fetchImpl or provide a global fetch)',\n ],\n };\n }\n\n const envUa =\n typeof process !== 'undefined' && typeof process.env === 'object'\n ? process.env.PEAC_USER_AGENT\n : undefined;\n const userAgent = options.userAgent ?? envUa ?? DEFAULT_USER_AGENT;\n const maxBytes = options.maxBytes ?? MAX_BYTES;\n const redirect = options.redirect ?? 'error';\n\n // If the caller did not supply an AbortSignal, install a local one driven by timeoutMs.\n let localController: AbortController | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let signal = options.signal;\n if (!signal) {\n localController = new AbortController();\n signal = localController.signal;\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n if (timeoutMs > 0 && Number.isFinite(timeoutMs)) {\n timer = setTimeout(() => localController?.abort(), timeoutMs);\n }\n }\n\n try {\n const url = new URL(WELL_KNOWN_PATH, origin);\n const response = await fetchImpl(url.toString(), {\n headers: { 'User-Agent': userAgent },\n signal,\n redirect,\n });\n\n if (!response.ok) {\n return {\n valid: false,\n errors: [`HTTP ${response.status}: ${response.statusText}`],\n };\n }\n\n const content = await response.text();\n if (content.length > maxBytes) {\n return {\n valid: false,\n errors: [`peac.txt exceeds ${maxBytes} bytes (got ${content.length})`],\n };\n }\n const { parse } = await import('./parser.js');\n return parse(content);\n } catch (error) {\n return {\n valid: false,\n errors: [`Discovery failed: ${error instanceof Error ? error.message : String(error)}`],\n };\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,81 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @peac/disc -
|
|
3
|
-
*
|
|
2
|
+
* @peac/disc - thin peac.txt policy-document loader/validator and remote fetcher.
|
|
3
|
+
*
|
|
4
|
+
* peac.txt is a POLICY DOCUMENT surface per docs/specs/PEAC-TXT.md. Full
|
|
5
|
+
* parsing is delegated to `@peac/policy-kit.parsePolicyDocument`; this
|
|
6
|
+
* package provides a tolerant `parse()` wrapper, a `discover()` remote
|
|
7
|
+
* fetcher, and structured warnings for legacy key-discovery lines that
|
|
8
|
+
* appeared in older peac.txt examples.
|
|
9
|
+
*
|
|
10
|
+
* `peac.txt` is NOT a key discovery surface. Cryptographic key resolution
|
|
11
|
+
* flows through `iss` -> /.well-known/peac-issuer.json -> `jwks_uri` -> JWKS
|
|
12
|
+
* (docs/specs/PEAC-ISSUER.md). Callers that need key material MUST use
|
|
13
|
+
* `parseIssuerConfig` / `fetchIssuerConfig` from `@peac/protocol`.
|
|
14
|
+
*
|
|
15
|
+
* Legacy key-discovery lines (`verify:`, `public_keys:`, `jwks:`) are
|
|
16
|
+
* tolerated on parse: they are stripped before validation, surfaced on
|
|
17
|
+
* `ParseResult.warnings`, and fire a structured `process.emitWarning`
|
|
18
|
+
* with code `PEAC_LEGACY_PEAC_TXT_KEY_FIELD` once per process.
|
|
4
19
|
*/
|
|
5
|
-
export { parse, emit, validate } from './parser.js';
|
|
6
|
-
export type {
|
|
7
|
-
|
|
8
|
-
|
|
20
|
+
export { parse, emit, validate, __resetLegacyWarningForTests } from './parser.js';
|
|
21
|
+
export type { ParseResult, ValidationOptions, PolicyDocument } from './types.js';
|
|
22
|
+
/**
|
|
23
|
+
* @deprecated Retained for one minor to keep existing import paths
|
|
24
|
+
* compiling. peac.txt policy documents now resolve to a `PolicyDocument`
|
|
25
|
+
* (re-exported from `@peac/policy-kit`). Removal target: next cleanup
|
|
26
|
+
* release.
|
|
27
|
+
*/
|
|
28
|
+
export type { PeacDiscovery, PublicKeyInfo } from './types.js';
|
|
29
|
+
export declare const MAX_BYTES = 262144;
|
|
9
30
|
export declare const WELL_KNOWN_PATH = "/.well-known/peac.txt";
|
|
10
|
-
export declare
|
|
31
|
+
export declare const DEFAULT_TIMEOUT_MS = 5000;
|
|
32
|
+
/**
|
|
33
|
+
* Minimal fetch signature accepted by `discover`. `undici` / `node-fetch` /
|
|
34
|
+
* browser / test-double implementations all satisfy this.
|
|
35
|
+
*/
|
|
36
|
+
export type DiscoverFetch = (input: string, init?: {
|
|
37
|
+
headers?: Record<string, string>;
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
redirect?: 'follow' | 'error' | 'manual';
|
|
40
|
+
}) => Promise<{
|
|
41
|
+
ok: boolean;
|
|
42
|
+
status: number;
|
|
43
|
+
statusText: string;
|
|
44
|
+
text(): Promise<string>;
|
|
45
|
+
}>;
|
|
46
|
+
export interface DiscoverOptions {
|
|
47
|
+
/**
|
|
48
|
+
* HTTP fetch implementation. Defaults to the ambient `fetch` global.
|
|
49
|
+
* Supply `undici.fetch`, a test double, or a policy-wrapping client as
|
|
50
|
+
* needed.
|
|
51
|
+
*/
|
|
52
|
+
fetchImpl?: DiscoverFetch;
|
|
53
|
+
/**
|
|
54
|
+
* User-agent string. Precedence (highest first): this option, the
|
|
55
|
+
* `PEAC_USER_AGENT` environment variable, then `peac-disc`.
|
|
56
|
+
*/
|
|
57
|
+
userAgent?: string;
|
|
58
|
+
/** Caller-supplied abort signal. */
|
|
59
|
+
signal?: AbortSignal;
|
|
60
|
+
/**
|
|
61
|
+
* Millisecond timeout. Ignored when `signal` is supplied (the caller
|
|
62
|
+
* owns cancellation). Defaults to 5_000 when neither is provided.
|
|
63
|
+
*/
|
|
64
|
+
timeoutMs?: number;
|
|
65
|
+
/** Override the 256 KiB body cap from docs/specs/PEAC-TXT.md §6.1. */
|
|
66
|
+
maxBytes?: number;
|
|
67
|
+
/**
|
|
68
|
+
* Redirect policy. Defaults to `error` to avoid cross-origin surprise
|
|
69
|
+
* on a well-known discovery endpoint; set to `follow` explicitly if the
|
|
70
|
+
* deployment relies on server-side redirects.
|
|
71
|
+
*/
|
|
72
|
+
redirect?: 'follow' | 'error' | 'manual';
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Fetch and parse a remote peac.txt policy document. On success returns a
|
|
76
|
+
* `ParseResult` whose `data` is the validated `peac-policy/0.1`
|
|
77
|
+
* `PolicyDocument`. Legacy key-discovery lines in the fetched bytes are
|
|
78
|
+
* surfaced as warnings but never populate `data`.
|
|
79
|
+
*/
|
|
80
|
+
export declare function discover(origin: string, options?: DiscoverOptions): Promise<import('./types.js').ParseResult>;
|
|
11
81
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAClF,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjF;;;;;GAKG;AACH,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE/D,eAAO,MAAM,SAAS,SAAS,CAAC;AAChC,eAAO,MAAM,eAAe,0BAA0B,CAAC;AACvD,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAGvC;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;IACL,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;CAC1C,KACE,OAAO,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACzB,CAAC,CAAC;AAEH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;CAC1C;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,OAAO,YAAY,EAAE,WAAW,CAAC,CAkE3C"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { parsePolicyDocument, PolicyValidationError, PolicyLoadError, serializePolicyYaml } from '@peac/policy-kit';
|
|
2
|
+
|
|
1
3
|
var __defProp = Object.defineProperty;
|
|
2
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
5
|
var __esm = (fn, res) => function __init() {
|
|
@@ -11,162 +13,153 @@ var __export = (target, all) => {
|
|
|
11
13
|
// src/parser.ts
|
|
12
14
|
var parser_exports = {};
|
|
13
15
|
__export(parser_exports, {
|
|
16
|
+
__resetLegacyWarningForTests: () => __resetLegacyWarningForTests,
|
|
14
17
|
emit: () => emit,
|
|
15
18
|
parse: () => parse,
|
|
16
19
|
validate: () => validate
|
|
17
20
|
});
|
|
18
|
-
function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
function fireLegacyWarning(field) {
|
|
22
|
+
if (legacyWarningFired) return;
|
|
23
|
+
legacyWarningFired = true;
|
|
24
|
+
if (typeof process !== "undefined" && typeof process.emitWarning === "function") {
|
|
25
|
+
process.emitWarning(
|
|
26
|
+
`peac.txt legacy key-discovery field "${field}" is deprecated and ignored. peac.txt is a policy-document surface (docs/specs/PEAC-TXT.md). Key resolution uses iss -> /.well-known/peac-issuer.json -> jwks_uri -> JWKS.`,
|
|
27
|
+
{ code: "PEAC_LEGACY_PEAC_TXT_KEY_FIELD", type: "DeprecationWarning" }
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function __resetLegacyWarningForTests() {
|
|
32
|
+
legacyWarningFired = false;
|
|
33
|
+
}
|
|
34
|
+
function collectLegacyWarnings(text) {
|
|
35
|
+
const warnings = [];
|
|
36
|
+
let firstField = null;
|
|
37
|
+
const lines = text.split(/\r?\n/);
|
|
38
|
+
lines.forEach((line, idx) => {
|
|
39
|
+
const match = line.match(/^\s*(verify|public_keys|jwks)\s*:/);
|
|
40
|
+
if (match) {
|
|
41
|
+
if (firstField === null) firstField = match[1];
|
|
42
|
+
warnings.push(
|
|
43
|
+
`Line ${idx + 1}: legacy key-discovery field "${match[1]}" ignored (peac.txt is policy-only; use peac-issuer.json for keys)`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
return { warnings, firstField };
|
|
48
|
+
}
|
|
49
|
+
function parse(text) {
|
|
50
|
+
const warnings = [];
|
|
51
|
+
if (text.trim().length === 0) {
|
|
24
52
|
return {
|
|
25
53
|
valid: false,
|
|
26
|
-
errors: [
|
|
27
|
-
lineCount: lines.length
|
|
54
|
+
errors: ["Empty policy document. Expected peac-policy/0.1 YAML or JSON."]
|
|
28
55
|
};
|
|
29
56
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
switch (field) {
|
|
38
|
-
case "preferences":
|
|
39
|
-
case "access_control":
|
|
40
|
-
case "provenance":
|
|
41
|
-
case "verify":
|
|
42
|
-
data[field] = match[1].trim();
|
|
43
|
-
break;
|
|
44
|
-
case "receipts":
|
|
45
|
-
data.receipts = match[1];
|
|
46
|
-
break;
|
|
47
|
-
case "payments":
|
|
48
|
-
data.payments = parseArray(match[1]);
|
|
49
|
-
break;
|
|
50
|
-
case "public_keys":
|
|
51
|
-
data.public_keys = parsePublicKeys(match[1]);
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
} catch (error) {
|
|
55
|
-
errors.push(
|
|
56
|
-
`Line ${index + 1}: ${error instanceof Error ? error.message : String(error)}`
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
57
|
+
const hasLegacyLines = LEGACY_KEY_LINE.test(text);
|
|
58
|
+
try {
|
|
59
|
+
const data = parsePolicyDocument(text);
|
|
60
|
+
if (hasLegacyLines) {
|
|
61
|
+
const legacy = collectLegacyWarnings(text);
|
|
62
|
+
warnings.push(...legacy.warnings);
|
|
63
|
+
if (legacy.firstField) fireLegacyWarning(legacy.firstField);
|
|
61
64
|
}
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
return {
|
|
66
|
+
valid: true,
|
|
67
|
+
data,
|
|
68
|
+
warnings: warnings.length > 0 ? warnings : void 0
|
|
69
|
+
};
|
|
70
|
+
} catch (firstErr) {
|
|
71
|
+
if (!hasLegacyLines) {
|
|
72
|
+
return failure(firstErr, warnings);
|
|
64
73
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
throw new Error(`Invalid public key format: ${keyString}`);
|
|
74
|
+
const legacy = collectLegacyWarnings(text);
|
|
75
|
+
warnings.push(...legacy.warnings);
|
|
76
|
+
if (legacy.firstField) fireLegacyWarning(legacy.firstField);
|
|
77
|
+
const stripped = text.replace(LEGACY_KEY_FULL_LINE_GLOBAL, "");
|
|
78
|
+
if (stripped.trim().length === 0) {
|
|
79
|
+
return {
|
|
80
|
+
valid: false,
|
|
81
|
+
errors: ["Empty policy document after stripping legacy key-discovery lines."],
|
|
82
|
+
warnings: warnings.length > 0 ? warnings : void 0
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const data = parsePolicyDocument(stripped);
|
|
87
|
+
return {
|
|
88
|
+
valid: true,
|
|
89
|
+
data,
|
|
90
|
+
warnings: warnings.length > 0 ? warnings : void 0
|
|
91
|
+
};
|
|
92
|
+
} catch (retryErr) {
|
|
93
|
+
return failure(retryErr, warnings);
|
|
86
94
|
}
|
|
87
|
-
keys.push({
|
|
88
|
-
kid: keyMatch[1],
|
|
89
|
-
alg: keyMatch[2],
|
|
90
|
-
key: keyMatch[3]
|
|
91
|
-
});
|
|
92
95
|
}
|
|
93
|
-
return keys;
|
|
94
96
|
}
|
|
95
|
-
function
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
function failure(err, warnings) {
|
|
98
|
+
if (err instanceof PolicyValidationError || err instanceof PolicyLoadError) {
|
|
99
|
+
return {
|
|
100
|
+
valid: false,
|
|
101
|
+
errors: [err.message],
|
|
102
|
+
warnings: warnings.length > 0 ? warnings : void 0
|
|
103
|
+
};
|
|
100
104
|
}
|
|
105
|
+
return {
|
|
106
|
+
valid: false,
|
|
107
|
+
errors: [err instanceof Error ? err.message : String(err)],
|
|
108
|
+
warnings: warnings.length > 0 ? warnings : void 0
|
|
109
|
+
};
|
|
101
110
|
}
|
|
102
|
-
function emit(
|
|
103
|
-
|
|
104
|
-
if (data.preferences) {
|
|
105
|
-
lines.push(`preferences: ${data.preferences}`);
|
|
106
|
-
}
|
|
107
|
-
if (data.access_control) {
|
|
108
|
-
lines.push(`access_control: ${data.access_control}`);
|
|
109
|
-
}
|
|
110
|
-
if (data.payments && data.payments.length > 0) {
|
|
111
|
-
data.payments.forEach((p) => assertSafeFieldValue(p, "payment"));
|
|
112
|
-
const paymentsStr = data.payments.map((p) => `"${p}"`).join(", ");
|
|
113
|
-
lines.push(`payments: [${paymentsStr}]`);
|
|
114
|
-
}
|
|
115
|
-
if (data.provenance) {
|
|
116
|
-
lines.push(`provenance: ${data.provenance}`);
|
|
117
|
-
}
|
|
118
|
-
if (data.receipts) {
|
|
119
|
-
lines.push(`receipts: ${data.receipts}`);
|
|
120
|
-
}
|
|
121
|
-
if (data.verify) {
|
|
122
|
-
lines.push(`verify: ${data.verify}`);
|
|
123
|
-
}
|
|
124
|
-
if (data.public_keys && data.public_keys.length > 0) {
|
|
125
|
-
data.public_keys.forEach((k) => {
|
|
126
|
-
assertSafeFieldValue(k.kid, "kid");
|
|
127
|
-
assertSafeFieldValue(k.alg, "alg");
|
|
128
|
-
assertSafeFieldValue(k.key, "key");
|
|
129
|
-
});
|
|
130
|
-
const keysStr = data.public_keys.map((k) => `"${k.kid}:${k.alg}:${k.key}"`).join(", ");
|
|
131
|
-
lines.push(`public_keys: [${keysStr}]`);
|
|
132
|
-
}
|
|
133
|
-
if (lines.length > MAX_LINES) {
|
|
134
|
-
throw new Error(`Generated peac.txt exceeds ${MAX_LINES} lines: ${lines.length}`);
|
|
135
|
-
}
|
|
136
|
-
return lines.join("\n");
|
|
111
|
+
function emit(doc) {
|
|
112
|
+
return serializePolicyYaml(doc);
|
|
137
113
|
}
|
|
138
|
-
function validate(
|
|
139
|
-
|
|
140
|
-
return result.valid;
|
|
114
|
+
function validate(text) {
|
|
115
|
+
return parse(text).valid;
|
|
141
116
|
}
|
|
142
|
-
var
|
|
117
|
+
var LEGACY_KEY_LINE, LEGACY_KEY_FULL_LINE_GLOBAL, legacyWarningFired;
|
|
143
118
|
var init_parser = __esm({
|
|
144
119
|
"src/parser.ts"() {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
access_control: /^access_control:\s*(.+)$/,
|
|
149
|
-
payments: /^payments:\s*\[([^\]]+)\]$/,
|
|
150
|
-
provenance: /^provenance:\s*(.+)$/,
|
|
151
|
-
receipts: /^receipts:\s*(required|optional)$/,
|
|
152
|
-
verify: /^verify:\s*(.+)$/,
|
|
153
|
-
public_keys: /^public_keys:\s*\[([^\]]+)\]$/
|
|
154
|
-
};
|
|
155
|
-
UNSAFE_FIELD_CHARS = /["\n\r\x00-\x1f:[\]]/;
|
|
120
|
+
LEGACY_KEY_LINE = /^\s*(verify|public_keys|jwks)\s*:/m;
|
|
121
|
+
LEGACY_KEY_FULL_LINE_GLOBAL = /^\s*(verify|public_keys|jwks)\s*:.*\r?\n?/gm;
|
|
122
|
+
legacyWarningFired = false;
|
|
156
123
|
}
|
|
157
124
|
});
|
|
158
125
|
|
|
159
126
|
// src/index.ts
|
|
160
127
|
init_parser();
|
|
161
|
-
var
|
|
162
|
-
var MAX_LINES2 = 20;
|
|
128
|
+
var MAX_BYTES = 262144;
|
|
163
129
|
var WELL_KNOWN_PATH = "/.well-known/peac.txt";
|
|
164
|
-
var
|
|
165
|
-
|
|
130
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
131
|
+
var DEFAULT_USER_AGENT = "peac-disc";
|
|
132
|
+
async function discover(origin, options = {}) {
|
|
133
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
134
|
+
if (typeof fetchImpl !== "function") {
|
|
135
|
+
return {
|
|
136
|
+
valid: false,
|
|
137
|
+
errors: [
|
|
138
|
+
"Discovery failed: no fetch implementation available (supply options.fetchImpl or provide a global fetch)"
|
|
139
|
+
]
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
const envUa = typeof process !== "undefined" && typeof process.env === "object" ? process.env.PEAC_USER_AGENT : void 0;
|
|
143
|
+
const userAgent = options.userAgent ?? envUa ?? DEFAULT_USER_AGENT;
|
|
144
|
+
const maxBytes = options.maxBytes ?? MAX_BYTES;
|
|
145
|
+
const redirect = options.redirect ?? "error";
|
|
146
|
+
let localController;
|
|
147
|
+
let timer;
|
|
148
|
+
let signal = options.signal;
|
|
149
|
+
if (!signal) {
|
|
150
|
+
localController = new AbortController();
|
|
151
|
+
signal = localController.signal;
|
|
152
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
153
|
+
if (timeoutMs > 0 && Number.isFinite(timeoutMs)) {
|
|
154
|
+
timer = setTimeout(() => localController?.abort(), timeoutMs);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
166
157
|
try {
|
|
167
158
|
const url = new URL(WELL_KNOWN_PATH, origin);
|
|
168
|
-
const response = await
|
|
169
|
-
headers: { "User-Agent":
|
|
159
|
+
const response = await fetchImpl(url.toString(), {
|
|
160
|
+
headers: { "User-Agent": userAgent },
|
|
161
|
+
signal,
|
|
162
|
+
redirect
|
|
170
163
|
});
|
|
171
164
|
if (!response.ok) {
|
|
172
165
|
return {
|
|
@@ -175,6 +168,12 @@ async function discover(origin) {
|
|
|
175
168
|
};
|
|
176
169
|
}
|
|
177
170
|
const content = await response.text();
|
|
171
|
+
if (content.length > maxBytes) {
|
|
172
|
+
return {
|
|
173
|
+
valid: false,
|
|
174
|
+
errors: [`peac.txt exceeds ${maxBytes} bytes (got ${content.length})`]
|
|
175
|
+
};
|
|
176
|
+
}
|
|
178
177
|
const { parse: parse2 } = await Promise.resolve().then(() => (init_parser(), parser_exports));
|
|
179
178
|
return parse2(content);
|
|
180
179
|
} catch (error) {
|
|
@@ -182,9 +181,11 @@ async function discover(origin) {
|
|
|
182
181
|
valid: false,
|
|
183
182
|
errors: [`Discovery failed: ${error instanceof Error ? error.message : String(error)}`]
|
|
184
183
|
};
|
|
184
|
+
} finally {
|
|
185
|
+
if (timer) clearTimeout(timer);
|
|
185
186
|
}
|
|
186
187
|
}
|
|
187
188
|
|
|
188
|
-
export {
|
|
189
|
+
export { DEFAULT_TIMEOUT_MS, MAX_BYTES, WELL_KNOWN_PATH, __resetLegacyWarningForTests, discover, emit, parse, validate };
|
|
189
190
|
//# sourceMappingURL=index.js.map
|
|
190
191
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/parser.ts","../src/index.ts"],"names":["MAX_LINES","parse"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @peac/disc/parser - peac.txt parser with ≤20 lines enforcement\n * ABNF-compliant discovery document parsing\n */\n\nimport type { PeacDiscovery, ParseResult, PublicKeyInfo, ValidationOptions } from './types.js';\n\nconst MAX_LINES = 20;\nconst FIELD_PATTERNS = {\n preferences: /^preferences:\\s*(.+)$/,\n access_control: /^access_control:\\s*(.+)$/,\n payments: /^payments:\\s*\\[([^\\]]+)\\]$/,\n provenance: /^provenance:\\s*(.+)$/,\n receipts: /^receipts:\\s*(required|optional)$/,\n verify: /^verify:\\s*(.+)$/,\n public_keys: /^public_keys:\\s*\\[([^\\]]+)\\]$/,\n};\n\nexport function parse(content: string, options: ValidationOptions = {}): ParseResult {\n const maxLines = options.maxLines ?? MAX_LINES;\n const errors: string[] = [];\n const data: PeacDiscovery = {};\n\n const lines = content\n .split('\\n')\n .map((l) => l.trim())\n .filter((l) => l && !l.startsWith('#'));\n\n // Enforce line limit\n if (lines.length > maxLines) {\n return {\n valid: false,\n errors: [`Line limit exceeded: ${lines.length} > ${maxLines}`],\n lineCount: lines.length,\n };\n }\n\n // Parse each line\n for (const [index, line] of lines.entries()) {\n let matched = false;\n\n for (const [field, pattern] of Object.entries(FIELD_PATTERNS)) {\n const match = line.match(pattern);\n if (match) {\n matched = true;\n try {\n switch (field) {\n case 'preferences':\n case 'access_control':\n case 'provenance':\n case 'verify':\n data[field] = match[1].trim();\n break;\n case 'receipts':\n data.receipts = match[1] as 'required' | 'optional';\n break;\n case 'payments':\n data.payments = parseArray(match[1]);\n break;\n case 'public_keys':\n data.public_keys = parsePublicKeys(match[1]);\n break;\n }\n } catch (error) {\n errors.push(\n `Line ${index + 1}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n break;\n }\n }\n\n if (!matched) {\n errors.push(`Line ${index + 1}: Invalid format: ${line}`);\n }\n }\n\n // Validate required fields\n if (!data.verify) {\n errors.push('Missing required field: verify');\n }\n\n return {\n valid: errors.length === 0,\n data: errors.length === 0 ? data : undefined,\n errors: errors.length > 0 ? errors : undefined,\n lineCount: lines.length,\n };\n}\n\nfunction parseArray(content: string): string[] {\n return content\n .split(',')\n .map((item) => item.trim().replace(/^[\"']|[\"']$/g, ''))\n .filter((item) => item.length > 0);\n}\n\nfunction parsePublicKeys(content: string): PublicKeyInfo[] {\n const keyStrings = parseArray(content);\n const keys: PublicKeyInfo[] = [];\n\n for (const keyString of keyStrings) {\n const keyMatch = keyString.match(/^([^:]+):([^:]+):(.+)$/);\n if (!keyMatch) {\n throw new Error(`Invalid public key format: ${keyString}`);\n }\n\n keys.push({\n kid: keyMatch[1],\n alg: keyMatch[2],\n key: keyMatch[3],\n });\n }\n\n return keys;\n}\n\n/** Characters that break peac.txt wire format: quotes, colons, brackets, control chars */\nconst UNSAFE_FIELD_CHARS = /[\"\\n\\r\\x00-\\x1f:[\\]]/;\n\nfunction assertSafeFieldValue(value: string, fieldName: string): void {\n if (UNSAFE_FIELD_CHARS.test(value)) {\n throw new Error(\n `Invalid ${fieldName}: must not contain quotes, colons, brackets, newlines, or control characters`\n );\n }\n}\n\nexport function emit(data: PeacDiscovery): string {\n const lines: string[] = [];\n\n if (data.preferences) {\n lines.push(`preferences: ${data.preferences}`);\n }\n\n if (data.access_control) {\n lines.push(`access_control: ${data.access_control}`);\n }\n\n if (data.payments && data.payments.length > 0) {\n data.payments.forEach((p) => assertSafeFieldValue(p, 'payment'));\n const paymentsStr = data.payments.map((p) => `\"${p}\"`).join(', ');\n lines.push(`payments: [${paymentsStr}]`);\n }\n\n if (data.provenance) {\n lines.push(`provenance: ${data.provenance}`);\n }\n\n if (data.receipts) {\n lines.push(`receipts: ${data.receipts}`);\n }\n\n if (data.verify) {\n lines.push(`verify: ${data.verify}`);\n }\n\n if (data.public_keys && data.public_keys.length > 0) {\n data.public_keys.forEach((k) => {\n assertSafeFieldValue(k.kid, 'kid');\n assertSafeFieldValue(k.alg, 'alg');\n assertSafeFieldValue(k.key, 'key');\n });\n const keysStr = data.public_keys.map((k) => `\"${k.kid}:${k.alg}:${k.key}\"`).join(', ');\n lines.push(`public_keys: [${keysStr}]`);\n }\n\n // Enforce line limit during emission\n if (lines.length > MAX_LINES) {\n throw new Error(`Generated peac.txt exceeds ${MAX_LINES} lines: ${lines.length}`);\n }\n\n return lines.join('\\n');\n}\n\nexport function validate(content: string): boolean {\n const result = parse(content);\n return result.valid;\n}\n","/**\n * @peac/disc - PEAC discovery with ≤20 lines enforcement\n * ABNF-compliant .well-known/peac.txt parser and generator\n */\n\nexport { parse, emit, validate } from './parser.js';\nexport type { PeacDiscovery, PublicKeyInfo, ParseResult, ValidationOptions } from './types.js';\n\n// Constants\nexport const VERSION = '0.9.15';\nexport const MAX_LINES = 20;\nexport const WELL_KNOWN_PATH = '/.well-known/peac.txt';\n\nconst UA = `PEAC/${VERSION} (+https://www.peacprotocol.org)`;\n\n// Convenience function for fetching and parsing discovery documents\nexport async function discover(origin: string): Promise<import('./types.js').ParseResult> {\n try {\n const url = new URL(WELL_KNOWN_PATH, origin);\n const response = await fetch(url.toString(), {\n headers: { 'User-Agent': UA },\n });\n\n if (!response.ok) {\n return {\n valid: false,\n errors: [`HTTP ${response.status}: ${response.statusText}`],\n };\n }\n\n const content = await response.text();\n const { parse } = await import('./parser.js');\n return parse(content);\n } catch (error) {\n return {\n valid: false,\n errors: [`Discovery failed: ${error instanceof Error ? error.message : String(error)}`],\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/parser.ts","../src/index.ts"],"names":["parse"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @peac/disc/parser - thin peac.txt policy-document loader/validator.\n *\n * peac.txt is a POLICY DOCUMENT surface per docs/specs/PEAC-TXT.md.\n * Full parsing is delegated to `@peac/policy-kit.parsePolicyDocument`;\n * this module only provides:\n *\n * - format detection (YAML vs JSON) per PEAC-TXT.md §5.1\n * - a tolerant `parse()` wrapper that returns a structured `ParseResult`\n * (rather than throwing)\n * - structured warnings for legacy key-discovery lines (`verify`,\n * `public_keys`, `jwks`) that appeared in pre-v0.12.14 peac.txt\n * examples. Those lines are NEVER honored here: key discovery flows\n * through `iss` -> /.well-known/peac-issuer.json -> `jwks_uri` -> JWKS\n * (docs/specs/PEAC-ISSUER.md).\n * - a tiny `emit()` helper that serializes a `PolicyDocument` via\n * `@peac/policy-kit.serializePolicyYaml`\n *\n * @see docs/specs/PEAC-TXT.md\n * @see docs/specs/PEAC-ISSUER.md\n */\n\nimport {\n PolicyLoadError,\n PolicyValidationError,\n parsePolicyDocument,\n serializePolicyYaml,\n type PolicyDocument,\n} from '@peac/policy-kit';\nimport type { ParseResult } from './types.js';\n\nconst LEGACY_KEY_LINE = /^\\s*(verify|public_keys|jwks)\\s*:/m;\n/** Strips the whole line (including the trailing newline) when matched. */\nconst LEGACY_KEY_FULL_LINE_GLOBAL = /^\\s*(verify|public_keys|jwks)\\s*:.*\\r?\\n?/gm;\n\nlet legacyWarningFired = false;\n\nfunction fireLegacyWarning(field: string): void {\n if (legacyWarningFired) return;\n legacyWarningFired = true;\n if (typeof process !== 'undefined' && typeof process.emitWarning === 'function') {\n process.emitWarning(\n `peac.txt legacy key-discovery field \"${field}\" is deprecated and ignored. ` +\n `peac.txt is a policy-document surface (docs/specs/PEAC-TXT.md). ` +\n `Key resolution uses iss -> /.well-known/peac-issuer.json -> jwks_uri -> JWKS.`,\n { code: 'PEAC_LEGACY_PEAC_TXT_KEY_FIELD', type: 'DeprecationWarning' }\n );\n }\n}\n\n/**\n * Reset the one-shot legacy-warning flag. Exposed for tests that need to\n * observe the warning more than once per process.\n */\nexport function __resetLegacyWarningForTests(): void {\n legacyWarningFired = false;\n}\n\nfunction collectLegacyWarnings(text: string): { warnings: string[]; firstField: string | null } {\n const warnings: string[] = [];\n let firstField: string | null = null;\n const lines = text.split(/\\r?\\n/);\n lines.forEach((line, idx) => {\n const match = line.match(/^\\s*(verify|public_keys|jwks)\\s*:/);\n if (match) {\n if (firstField === null) firstField = match[1];\n warnings.push(\n `Line ${idx + 1}: legacy key-discovery field \"${match[1]}\" ignored ` +\n `(peac.txt is policy-only; use peac-issuer.json for keys)`\n );\n }\n });\n return { warnings, firstField };\n}\n\n/**\n * Parse a peac.txt policy document. Accepts YAML or JSON per\n * `docs/specs/PEAC-TXT.md` §5.1. On success, `data` is the validated\n * `peac-policy/0.1` `PolicyDocument`.\n *\n * Legacy key-discovery lines (`verify:`, `public_keys:`, `jwks:`) are\n * tolerated via a two-pass strategy: first the raw bytes are handed to\n * `parsePolicyDocument`; if validation fails AND top-level legacy lines\n * are present, the legacy lines are stripped and parsing is retried\n * once. Detected legacy lines are listed in `warnings` and surface a\n * structured `PEAC_LEGACY_PEAC_TXT_KEY_FIELD` `DeprecationWarning` once\n * per process. They never populate the parsed result. This preserves\n * the original bytes when a policy document happens to mention those\n * tokens inside comments, block scalars, or rule text.\n */\nexport function parse(text: string): ParseResult {\n const warnings: string[] = [];\n\n if (text.trim().length === 0) {\n return {\n valid: false,\n errors: ['Empty policy document. Expected peac-policy/0.1 YAML or JSON.'],\n };\n }\n\n const hasLegacyLines = LEGACY_KEY_LINE.test(text);\n\n // First pass: try the raw bytes. If they parse cleanly, legacy-looking\n // substrings inside block scalars / comments / rule text are not\n // mutated.\n try {\n const data = parsePolicyDocument(text);\n if (hasLegacyLines) {\n const legacy = collectLegacyWarnings(text);\n warnings.push(...legacy.warnings);\n if (legacy.firstField) fireLegacyWarning(legacy.firstField);\n }\n return {\n valid: true,\n data,\n warnings: warnings.length > 0 ? warnings : undefined,\n };\n } catch (firstErr) {\n if (!hasLegacyLines) {\n return failure(firstErr, warnings);\n }\n // Second pass: strip top-level legacy key-discovery lines and retry.\n const legacy = collectLegacyWarnings(text);\n warnings.push(...legacy.warnings);\n if (legacy.firstField) fireLegacyWarning(legacy.firstField);\n\n const stripped = text.replace(LEGACY_KEY_FULL_LINE_GLOBAL, '');\n if (stripped.trim().length === 0) {\n return {\n valid: false,\n errors: ['Empty policy document after stripping legacy key-discovery lines.'],\n warnings: warnings.length > 0 ? warnings : undefined,\n };\n }\n try {\n const data = parsePolicyDocument(stripped);\n return {\n valid: true,\n data,\n warnings: warnings.length > 0 ? warnings : undefined,\n };\n } catch (retryErr) {\n return failure(retryErr, warnings);\n }\n }\n}\n\nfunction failure(err: unknown, warnings: string[]): ParseResult {\n if (err instanceof PolicyValidationError || err instanceof PolicyLoadError) {\n return {\n valid: false,\n errors: [err.message],\n warnings: warnings.length > 0 ? warnings : undefined,\n };\n }\n return {\n valid: false,\n errors: [err instanceof Error ? err.message : String(err)],\n warnings: warnings.length > 0 ? warnings : undefined,\n };\n}\n\n/**\n * Serialize a `peac-policy/0.1` `PolicyDocument` as YAML suitable for\n * serving at `/.well-known/peac.txt`. Delegates to\n * `@peac/policy-kit.serializePolicyYaml`.\n */\nexport function emit(doc: PolicyDocument): string {\n return serializePolicyYaml(doc);\n}\n\n/**\n * Convenience predicate: returns `true` iff `text` parses as a valid\n * `peac-policy/0.1` document.\n */\nexport function validate(text: string): boolean {\n return parse(text).valid;\n}\n","/**\n * @peac/disc - thin peac.txt policy-document loader/validator and remote fetcher.\n *\n * peac.txt is a POLICY DOCUMENT surface per docs/specs/PEAC-TXT.md. Full\n * parsing is delegated to `@peac/policy-kit.parsePolicyDocument`; this\n * package provides a tolerant `parse()` wrapper, a `discover()` remote\n * fetcher, and structured warnings for legacy key-discovery lines that\n * appeared in older peac.txt examples.\n *\n * `peac.txt` is NOT a key discovery surface. Cryptographic key resolution\n * flows through `iss` -> /.well-known/peac-issuer.json -> `jwks_uri` -> JWKS\n * (docs/specs/PEAC-ISSUER.md). Callers that need key material MUST use\n * `parseIssuerConfig` / `fetchIssuerConfig` from `@peac/protocol`.\n *\n * Legacy key-discovery lines (`verify:`, `public_keys:`, `jwks:`) are\n * tolerated on parse: they are stripped before validation, surfaced on\n * `ParseResult.warnings`, and fire a structured `process.emitWarning`\n * with code `PEAC_LEGACY_PEAC_TXT_KEY_FIELD` once per process.\n */\n\nexport { parse, emit, validate, __resetLegacyWarningForTests } from './parser.js';\nexport type { ParseResult, ValidationOptions, PolicyDocument } from './types.js';\n\n/**\n * @deprecated Retained for one minor to keep existing import paths\n * compiling. peac.txt policy documents now resolve to a `PolicyDocument`\n * (re-exported from `@peac/policy-kit`). Removal target: next cleanup\n * release.\n */\nexport type { PeacDiscovery, PublicKeyInfo } from './types.js';\n\nexport const MAX_BYTES = 262144; // 256 KiB, per docs/specs/PEAC-TXT.md §6.1\nexport const WELL_KNOWN_PATH = '/.well-known/peac.txt';\nexport const DEFAULT_TIMEOUT_MS = 5000;\nconst DEFAULT_USER_AGENT = 'peac-disc';\n\n/**\n * Minimal fetch signature accepted by `discover`. `undici` / `node-fetch` /\n * browser / test-double implementations all satisfy this.\n */\nexport type DiscoverFetch = (\n input: string,\n init?: {\n headers?: Record<string, string>;\n signal?: AbortSignal;\n redirect?: 'follow' | 'error' | 'manual';\n }\n) => Promise<{\n ok: boolean;\n status: number;\n statusText: string;\n text(): Promise<string>;\n}>;\n\nexport interface DiscoverOptions {\n /**\n * HTTP fetch implementation. Defaults to the ambient `fetch` global.\n * Supply `undici.fetch`, a test double, or a policy-wrapping client as\n * needed.\n */\n fetchImpl?: DiscoverFetch;\n /**\n * User-agent string. Precedence (highest first): this option, the\n * `PEAC_USER_AGENT` environment variable, then `peac-disc`.\n */\n userAgent?: string;\n /** Caller-supplied abort signal. */\n signal?: AbortSignal;\n /**\n * Millisecond timeout. Ignored when `signal` is supplied (the caller\n * owns cancellation). Defaults to 5_000 when neither is provided.\n */\n timeoutMs?: number;\n /** Override the 256 KiB body cap from docs/specs/PEAC-TXT.md §6.1. */\n maxBytes?: number;\n /**\n * Redirect policy. Defaults to `error` to avoid cross-origin surprise\n * on a well-known discovery endpoint; set to `follow` explicitly if the\n * deployment relies on server-side redirects.\n */\n redirect?: 'follow' | 'error' | 'manual';\n}\n\n/**\n * Fetch and parse a remote peac.txt policy document. On success returns a\n * `ParseResult` whose `data` is the validated `peac-policy/0.1`\n * `PolicyDocument`. Legacy key-discovery lines in the fetched bytes are\n * surfaced as warnings but never populate `data`.\n */\nexport async function discover(\n origin: string,\n options: DiscoverOptions = {}\n): Promise<import('./types.js').ParseResult> {\n const fetchImpl = (options.fetchImpl ?? (globalThis as { fetch?: DiscoverFetch }).fetch) as\n | DiscoverFetch\n | undefined;\n if (typeof fetchImpl !== 'function') {\n return {\n valid: false,\n errors: [\n 'Discovery failed: no fetch implementation available (supply options.fetchImpl or provide a global fetch)',\n ],\n };\n }\n\n const envUa =\n typeof process !== 'undefined' && typeof process.env === 'object'\n ? process.env.PEAC_USER_AGENT\n : undefined;\n const userAgent = options.userAgent ?? envUa ?? DEFAULT_USER_AGENT;\n const maxBytes = options.maxBytes ?? MAX_BYTES;\n const redirect = options.redirect ?? 'error';\n\n // If the caller did not supply an AbortSignal, install a local one driven by timeoutMs.\n let localController: AbortController | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let signal = options.signal;\n if (!signal) {\n localController = new AbortController();\n signal = localController.signal;\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n if (timeoutMs > 0 && Number.isFinite(timeoutMs)) {\n timer = setTimeout(() => localController?.abort(), timeoutMs);\n }\n }\n\n try {\n const url = new URL(WELL_KNOWN_PATH, origin);\n const response = await fetchImpl(url.toString(), {\n headers: { 'User-Agent': userAgent },\n signal,\n redirect,\n });\n\n if (!response.ok) {\n return {\n valid: false,\n errors: [`HTTP ${response.status}: ${response.statusText}`],\n };\n }\n\n const content = await response.text();\n if (content.length > maxBytes) {\n return {\n valid: false,\n errors: [`peac.txt exceeds ${maxBytes} bytes (got ${content.length})`],\n };\n }\n const { parse } = await import('./parser.js');\n return parse(content);\n } catch (error) {\n return {\n valid: false,\n errors: [`Discovery failed: ${error instanceof Error ? error.message : String(error)}`],\n };\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n"]}
|
package/dist/parser.d.ts
CHANGED
|
@@ -1,9 +1,56 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @peac/disc/parser - peac.txt
|
|
3
|
-
*
|
|
2
|
+
* @peac/disc/parser - thin peac.txt policy-document loader/validator.
|
|
3
|
+
*
|
|
4
|
+
* peac.txt is a POLICY DOCUMENT surface per docs/specs/PEAC-TXT.md.
|
|
5
|
+
* Full parsing is delegated to `@peac/policy-kit.parsePolicyDocument`;
|
|
6
|
+
* this module only provides:
|
|
7
|
+
*
|
|
8
|
+
* - format detection (YAML vs JSON) per PEAC-TXT.md §5.1
|
|
9
|
+
* - a tolerant `parse()` wrapper that returns a structured `ParseResult`
|
|
10
|
+
* (rather than throwing)
|
|
11
|
+
* - structured warnings for legacy key-discovery lines (`verify`,
|
|
12
|
+
* `public_keys`, `jwks`) that appeared in pre-v0.12.14 peac.txt
|
|
13
|
+
* examples. Those lines are NEVER honored here: key discovery flows
|
|
14
|
+
* through `iss` -> /.well-known/peac-issuer.json -> `jwks_uri` -> JWKS
|
|
15
|
+
* (docs/specs/PEAC-ISSUER.md).
|
|
16
|
+
* - a tiny `emit()` helper that serializes a `PolicyDocument` via
|
|
17
|
+
* `@peac/policy-kit.serializePolicyYaml`
|
|
18
|
+
*
|
|
19
|
+
* @see docs/specs/PEAC-TXT.md
|
|
20
|
+
* @see docs/specs/PEAC-ISSUER.md
|
|
4
21
|
*/
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
22
|
+
import { type PolicyDocument } from '@peac/policy-kit';
|
|
23
|
+
import type { ParseResult } from './types.js';
|
|
24
|
+
/**
|
|
25
|
+
* Reset the one-shot legacy-warning flag. Exposed for tests that need to
|
|
26
|
+
* observe the warning more than once per process.
|
|
27
|
+
*/
|
|
28
|
+
export declare function __resetLegacyWarningForTests(): void;
|
|
29
|
+
/**
|
|
30
|
+
* Parse a peac.txt policy document. Accepts YAML or JSON per
|
|
31
|
+
* `docs/specs/PEAC-TXT.md` §5.1. On success, `data` is the validated
|
|
32
|
+
* `peac-policy/0.1` `PolicyDocument`.
|
|
33
|
+
*
|
|
34
|
+
* Legacy key-discovery lines (`verify:`, `public_keys:`, `jwks:`) are
|
|
35
|
+
* tolerated via a two-pass strategy: first the raw bytes are handed to
|
|
36
|
+
* `parsePolicyDocument`; if validation fails AND top-level legacy lines
|
|
37
|
+
* are present, the legacy lines are stripped and parsing is retried
|
|
38
|
+
* once. Detected legacy lines are listed in `warnings` and surface a
|
|
39
|
+
* structured `PEAC_LEGACY_PEAC_TXT_KEY_FIELD` `DeprecationWarning` once
|
|
40
|
+
* per process. They never populate the parsed result. This preserves
|
|
41
|
+
* the original bytes when a policy document happens to mention those
|
|
42
|
+
* tokens inside comments, block scalars, or rule text.
|
|
43
|
+
*/
|
|
44
|
+
export declare function parse(text: string): ParseResult;
|
|
45
|
+
/**
|
|
46
|
+
* Serialize a `peac-policy/0.1` `PolicyDocument` as YAML suitable for
|
|
47
|
+
* serving at `/.well-known/peac.txt`. Delegates to
|
|
48
|
+
* `@peac/policy-kit.serializePolicyYaml`.
|
|
49
|
+
*/
|
|
50
|
+
export declare function emit(doc: PolicyDocument): string;
|
|
51
|
+
/**
|
|
52
|
+
* Convenience predicate: returns `true` iff `text` parses as a valid
|
|
53
|
+
* `peac-policy/0.1` document.
|
|
54
|
+
*/
|
|
55
|
+
export declare function validate(text: string): boolean;
|
|
9
56
|
//# sourceMappingURL=parser.d.ts.map
|
package/dist/parser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAqB9C;;;GAGG;AACH,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD;AAmBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAuD/C;AAiBD;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,CAEhD;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9C"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,28 +1,57 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @peac/disc/types - Discovery types for .well-known/peac.txt
|
|
2
|
+
* @peac/disc/types - Discovery result types for .well-known/peac.txt
|
|
3
|
+
*
|
|
4
|
+
* peac.txt is a POLICY DOCUMENT surface per docs/specs/PEAC-TXT.md.
|
|
5
|
+
* It is NOT a key discovery surface. Key resolution uses the normative
|
|
6
|
+
* chain: `iss` -> /.well-known/peac-issuer.json -> `jwks_uri` -> JWKS.
|
|
7
|
+
*
|
|
8
|
+
* `@peac/disc` is a thin loader/validator over peac-policy/0.1 bytes
|
|
9
|
+
* (YAML or JSON). Full parsing is delegated to
|
|
10
|
+
* `@peac/policy-kit.parsePolicyDocument`; the canonical `PolicyDocument`
|
|
11
|
+
* type is re-exported from `@peac/policy-kit`.
|
|
12
|
+
*
|
|
13
|
+
* @see docs/specs/PEAC-TXT.md
|
|
14
|
+
* @see docs/specs/PEAC-ISSUER.md
|
|
3
15
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
16
|
+
import type { PolicyDocument } from '@peac/policy-kit';
|
|
17
|
+
export type { PolicyDocument };
|
|
18
|
+
/**
|
|
19
|
+
* Result of `parse()`. On success, `data` is the validated
|
|
20
|
+
* `peac-policy/0.1` `PolicyDocument`. On failure, `errors` explains why.
|
|
21
|
+
* `warnings` carries non-fatal advisories; in particular, legacy
|
|
22
|
+
* key-discovery lines (`verify`, `public_keys`, `jwks`) that appeared in
|
|
23
|
+
* older peac.txt examples are listed here and also fire a structured
|
|
24
|
+
* `process.emitWarning` with code `PEAC_LEGACY_PEAC_TXT_KEY_FIELD`
|
|
25
|
+
* once per process.
|
|
26
|
+
*/
|
|
27
|
+
export interface ParseResult {
|
|
28
|
+
valid: boolean;
|
|
29
|
+
data?: PolicyDocument;
|
|
30
|
+
errors?: string[];
|
|
31
|
+
warnings?: string[];
|
|
32
|
+
}
|
|
33
|
+
export interface ValidationOptions {
|
|
34
|
+
/** Soft line cap for advisory purposes (default 100, per PEAC-TXT.md §6.2). */
|
|
35
|
+
recommendedMaxLines?: number;
|
|
12
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* @deprecated Key discovery via peac.txt is retired. Use the normative
|
|
39
|
+
* discovery chain `iss` -> /.well-known/peac-issuer.json -> `jwks_uri` ->
|
|
40
|
+
* JWKS instead. This type is retained for one minor to keep existing import
|
|
41
|
+
* paths compiling. Removal target: next cleanup release.
|
|
42
|
+
*/
|
|
13
43
|
export interface PublicKeyInfo {
|
|
14
44
|
kid: string;
|
|
15
45
|
alg: string;
|
|
16
46
|
key: string;
|
|
17
47
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export interface
|
|
25
|
-
|
|
26
|
-
strictAbnf?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* @deprecated pre-v0.12.14 alias for the legacy line-based PEAC discovery
|
|
50
|
+
* shape. Use the `ParseResult.data` (`PolicyDocument`) returned by `parse()`.
|
|
51
|
+
* Retained for one minor so existing import paths keep compiling; removal
|
|
52
|
+
* target: next cleanup release.
|
|
53
|
+
*/
|
|
54
|
+
export interface PeacDiscovery {
|
|
55
|
+
[key: string]: unknown;
|
|
27
56
|
}
|
|
28
57
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,YAAY,EAAE,cAAc,EAAE,CAAC;AAE/B;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peac/disc",
|
|
3
|
-
"version": "0.12.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.12.14",
|
|
4
|
+
"description": "Thin loader / validator and remote fetcher for peac.txt policy documents (peac-policy/0.1; docs/specs/PEAC-TXT.md)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
"README.md",
|
|
21
21
|
"LICENSE"
|
|
22
22
|
],
|
|
23
|
-
"dependencies": {
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@peac/policy-kit": "0.12.14"
|
|
25
|
+
},
|
|
24
26
|
"devDependencies": {
|
|
25
27
|
"typescript": "^5.0.0",
|
|
26
28
|
"tsup": "^8.0.0"
|