@intentius/chant-lexicon-cedar 0.44.8
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 +190 -0
- package/dist/avp/ambient.d.ts +54 -0
- package/dist/avp/ambient.d.ts.map +1 -0
- package/dist/avp/client.d.ts +127 -0
- package/dist/avp/client.d.ts.map +1 -0
- package/dist/avp/describe-resources.d.ts +42 -0
- package/dist/avp/describe-resources.d.ts.map +1 -0
- package/dist/avp/embed.d.ts +120 -0
- package/dist/avp/embed.d.ts.map +1 -0
- package/dist/avp/live-export.d.ts +94 -0
- package/dist/avp/live-export.d.ts.map +1 -0
- package/dist/avp/ownership.d.ts +97 -0
- package/dist/avp/ownership.d.ts.map +1 -0
- package/dist/avp/statement.d.ts +22 -0
- package/dist/avp/statement.d.ts.map +1 -0
- package/dist/avp/store.d.ts +82 -0
- package/dist/avp/store.d.ts.map +1 -0
- package/dist/avp/testdata/mock-transport.d.ts +55 -0
- package/dist/avp/testdata/mock-transport.d.ts.map +1 -0
- package/dist/codegen/docs-cli.d.ts +3 -0
- package/dist/codegen/docs-cli.d.ts.map +1 -0
- package/dist/codegen/docs.d.ts +26 -0
- package/dist/codegen/docs.d.ts.map +1 -0
- package/dist/codegen/emit.d.ts +85 -0
- package/dist/codegen/emit.d.ts.map +1 -0
- package/dist/codegen/generate-cli.d.ts +3 -0
- package/dist/codegen/generate-cli.d.ts.map +1 -0
- package/dist/codegen/generate.d.ts +37 -0
- package/dist/codegen/generate.d.ts.map +1 -0
- package/dist/codegen/naming.d.ts +48 -0
- package/dist/codegen/naming.d.ts.map +1 -0
- package/dist/codegen/package.d.ts +10 -0
- package/dist/codegen/package.d.ts.map +1 -0
- package/dist/composites/deny-by-default-set.d.ts +58 -0
- package/dist/composites/deny-by-default-set.d.ts.map +1 -0
- package/dist/composites/index.d.ts +12 -0
- package/dist/composites/index.d.ts.map +1 -0
- package/dist/composites/owner-can-manage.d.ts +48 -0
- package/dist/composites/owner-can-manage.d.ts.map +1 -0
- package/dist/config.d.ts +102 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/coverage.d.ts +59 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/detect.d.ts +18 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/generated/index.d.ts +251 -0
- package/dist/generated/index.d.ts.map +1 -0
- package/dist/generated/runtime.d.ts +2 -0
- package/dist/generated/runtime.d.ts.map +1 -0
- package/dist/import/adapter.d.ts +21 -0
- package/dist/import/adapter.d.ts.map +1 -0
- package/dist/import/clause-text.d.ts +38 -0
- package/dist/import/clause-text.d.ts.map +1 -0
- package/dist/import/generator.d.ts +53 -0
- package/dist/import/generator.d.ts.map +1 -0
- package/dist/import/parser.d.ts +57 -0
- package/dist/import/parser.d.ts.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/init-templates.d.ts +32 -0
- package/dist/init-templates.d.ts.map +1 -0
- package/dist/integrity.json +22 -0
- package/dist/lint/audit-catalog.d.ts +29 -0
- package/dist/lint/audit-catalog.d.ts.map +1 -0
- package/dist/lint/post-synth/cedar-helpers.d.ts +97 -0
- package/dist/lint/post-synth/cedar-helpers.d.ts.map +1 -0
- package/dist/lint/post-synth/cedc010.d.ts +17 -0
- package/dist/lint/post-synth/cedc010.d.ts.map +1 -0
- package/dist/lint/post-synth/cedc011.d.ts +17 -0
- package/dist/lint/post-synth/cedc011.d.ts.map +1 -0
- package/dist/lint/post-synth/cedc012.d.ts +21 -0
- package/dist/lint/post-synth/cedc012.d.ts.map +1 -0
- package/dist/lint/post-synth/cedc013.d.ts +20 -0
- package/dist/lint/post-synth/cedc013.d.ts.map +1 -0
- package/dist/lint/post-synth/cedc014.d.ts +19 -0
- package/dist/lint/post-synth/cedc014.d.ts.map +1 -0
- package/dist/lint/post-synth/cede010.d.ts +34 -0
- package/dist/lint/post-synth/cede010.d.ts.map +1 -0
- package/dist/lint/post-synth/cede011.d.ts +22 -0
- package/dist/lint/post-synth/cede011.d.ts.map +1 -0
- package/dist/lint/post-synth/ceds010.d.ts +24 -0
- package/dist/lint/post-synth/ceds010.d.ts.map +1 -0
- package/dist/lint/post-synth/ceds011.d.ts +19 -0
- package/dist/lint/post-synth/ceds011.d.ts.map +1 -0
- package/dist/lint/post-synth/ceds012.d.ts +17 -0
- package/dist/lint/post-synth/ceds012.d.ts.map +1 -0
- package/dist/lint/post-synth/index.d.ts +3 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -0
- package/dist/lint/post-synth/wasm-helpers.d.ts +108 -0
- package/dist/lint/post-synth/wasm-helpers.d.ts.map +1 -0
- package/dist/lint/rules/index.d.ts +10 -0
- package/dist/lint/rules/index.d.ts.map +1 -0
- package/dist/lint/rules/policy-shape.d.ts +31 -0
- package/dist/lint/rules/policy-shape.d.ts.map +1 -0
- package/dist/lsp/completions.d.ts +24 -0
- package/dist/lsp/completions.d.ts.map +1 -0
- package/dist/lsp/hover.d.ts +11 -0
- package/dist/lsp/hover.d.ts.map +1 -0
- package/dist/lsp/registry.d.ts +45 -0
- package/dist/lsp/registry.d.ts.map +1 -0
- package/dist/manifest.json +8 -0
- package/dist/mcp/index.d.ts +34 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/policy-coverage.d.ts +81 -0
- package/dist/mcp/policy-coverage.d.ts.map +1 -0
- package/dist/meta.json +699 -0
- package/dist/okf/index.md +37 -0
- package/dist/okf/rules/CEDC001.md +11 -0
- package/dist/okf/rules/CEDC010.md +11 -0
- package/dist/okf/rules/CEDC011.md +15 -0
- package/dist/okf/rules/CEDC012.md +11 -0
- package/dist/okf/rules/CEDC013.md +15 -0
- package/dist/okf/rules/CEDC014.md +15 -0
- package/dist/okf/rules/CEDE010.md +15 -0
- package/dist/okf/rules/CEDE011.md +15 -0
- package/dist/okf/rules/CEDS010.md +15 -0
- package/dist/okf/rules/CEDS011.md +11 -0
- package/dist/okf/rules/CEDS012.md +15 -0
- package/dist/okf/types/AdminAction.md +14 -0
- package/dist/okf/types/Application.md +14 -0
- package/dist/okf/types/ApproveAction.md +13 -0
- package/dist/okf/types/CommentAction.md +14 -0
- package/dist/okf/types/CreateAction.md +14 -0
- package/dist/okf/types/DeleteAction.md +14 -0
- package/dist/okf/types/Document.md +18 -0
- package/dist/okf/types/Folder.md +15 -0
- package/dist/okf/types/Group.md +14 -0
- package/dist/okf/types/ListAction.md +14 -0
- package/dist/okf/types/Policy.md +29 -0
- package/dist/okf/types/ReadAction.md +14 -0
- package/dist/okf/types/ServiceAccount.md +15 -0
- package/dist/okf/types/ShareAction.md +14 -0
- package/dist/okf/types/Team.md +14 -0
- package/dist/okf/types/User.md +18 -0
- package/dist/okf/types/WriteAction.md +14 -0
- package/dist/package-cli.d.ts +3 -0
- package/dist/package-cli.d.ts.map +1 -0
- package/dist/plugin.d.ts +8 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/rules/cedar-helpers.ts +209 -0
- package/dist/rules/cedc010.ts +80 -0
- package/dist/rules/cedc011.ts +47 -0
- package/dist/rules/cedc012.ts +64 -0
- package/dist/rules/cedc013.ts +68 -0
- package/dist/rules/cedc014.ts +73 -0
- package/dist/rules/cede010.ts +89 -0
- package/dist/rules/cede011.ts +58 -0
- package/dist/rules/ceds010.ts +57 -0
- package/dist/rules/ceds011.ts +42 -0
- package/dist/rules/ceds012.ts +50 -0
- package/dist/rules/policy-shape.ts +148 -0
- package/dist/rules/wasm-helpers.ts +315 -0
- package/dist/serializer.d.ts +138 -0
- package/dist/serializer.d.ts.map +1 -0
- package/dist/spec/fetch.d.ts +71 -0
- package/dist/spec/fetch.d.ts.map +1 -0
- package/dist/spec/parse.d.ts +113 -0
- package/dist/spec/parse.d.ts.map +1 -0
- package/dist/spec/pin.d.ts +116 -0
- package/dist/spec/pin.d.ts.map +1 -0
- package/dist/spec/pinned-names.json +18 -0
- package/dist/spec/wasm.d.ts +143 -0
- package/dist/spec/wasm.d.ts.map +1 -0
- package/dist/types/index.d.ts +219 -0
- package/dist/validate-cli.d.ts +3 -0
- package/dist/validate-cli.d.ts.map +1 -0
- package/dist/validate.d.ts +25 -0
- package/dist/validate.d.ts.map +1 -0
- package/package.json +75 -0
- package/src/avp/OWNERSHIP.md +125 -0
- package/src/avp/ambient.test.ts +113 -0
- package/src/avp/ambient.ts +124 -0
- package/src/avp/client.ts +310 -0
- package/src/avp/describe-resources.test.ts +316 -0
- package/src/avp/describe-resources.ts +215 -0
- package/src/avp/embed.test.ts +114 -0
- package/src/avp/embed.ts +190 -0
- package/src/avp/live-export.test.ts +232 -0
- package/src/avp/live-export.ts +185 -0
- package/src/avp/ownership.test.ts +101 -0
- package/src/avp/ownership.ts +170 -0
- package/src/avp/statement.ts +50 -0
- package/src/avp/store.ts +152 -0
- package/src/avp/testdata/mock-transport.ts +147 -0
- package/src/codegen/docs-cli.ts +7 -0
- package/src/codegen/docs.ts +873 -0
- package/src/codegen/emit.ts +496 -0
- package/src/codegen/generate-cli.ts +18 -0
- package/src/codegen/generate.test.ts +128 -0
- package/src/codegen/generate.ts +123 -0
- package/src/codegen/naming.ts +101 -0
- package/src/codegen/package.ts +52 -0
- package/src/composites/composites.test.ts +206 -0
- package/src/composites/deny-by-default-set.ts +98 -0
- package/src/composites/index.ts +13 -0
- package/src/composites/owner-can-manage.ts +80 -0
- package/src/config-namespace.test.ts +44 -0
- package/src/config.test.ts +82 -0
- package/src/config.ts +119 -0
- package/src/coverage.test.ts +61 -0
- package/src/coverage.ts +166 -0
- package/src/detect.test.ts +64 -0
- package/src/detect.ts +59 -0
- package/src/generated/index.d.ts +219 -0
- package/src/generated/index.ts +279 -0
- package/src/generated/lexicon-cedar.json +699 -0
- package/src/generated/runtime.ts +2 -0
- package/src/import/adapter.ts +63 -0
- package/src/import/clause-text.ts +178 -0
- package/src/import/generator.test.ts +133 -0
- package/src/import/generator.ts +219 -0
- package/src/import/parser.test.ts +265 -0
- package/src/import/parser.ts +352 -0
- package/src/import/roundtrip.test.ts +127 -0
- package/src/import/testdata/full.cedar +36 -0
- package/src/import/testdata/full.cedar.json +238 -0
- package/src/import/testdata/realistic.cedar +35 -0
- package/src/import/testdata/simple.cedar +6 -0
- package/src/index.ts +94 -0
- package/src/init-templates.test.ts +158 -0
- package/src/init-templates.ts +358 -0
- package/src/lint/audit-catalog.ts +130 -0
- package/src/lint/post-synth/cedar-helpers.ts +209 -0
- package/src/lint/post-synth/cedc010.ts +80 -0
- package/src/lint/post-synth/cedc011.ts +47 -0
- package/src/lint/post-synth/cedc012.ts +64 -0
- package/src/lint/post-synth/cedc013.ts +68 -0
- package/src/lint/post-synth/cedc014.ts +73 -0
- package/src/lint/post-synth/cede010.ts +89 -0
- package/src/lint/post-synth/cede011.ts +58 -0
- package/src/lint/post-synth/ceds010.ts +57 -0
- package/src/lint/post-synth/ceds011.ts +42 -0
- package/src/lint/post-synth/ceds012.ts +50 -0
- package/src/lint/post-synth/index.ts +25 -0
- package/src/lint/post-synth/post-synth.test.ts +474 -0
- package/src/lint/post-synth/wasm-helpers.ts +315 -0
- package/src/lint/rules/index.ts +12 -0
- package/src/lint/rules/policy-shape.test.ts +90 -0
- package/src/lint/rules/policy-shape.ts +148 -0
- package/src/lsp/completions.test.ts +111 -0
- package/src/lsp/completions.ts +55 -0
- package/src/lsp/hover.test.ts +82 -0
- package/src/lsp/hover.ts +75 -0
- package/src/lsp/registry.ts +74 -0
- package/src/mcp/index.ts +103 -0
- package/src/mcp/policy-coverage.test.ts +176 -0
- package/src/mcp/policy-coverage.ts +205 -0
- package/src/package-cli.ts +22 -0
- package/src/plugin.test.ts +115 -0
- package/src/plugin.ts +312 -0
- package/src/serializer.test.ts +502 -0
- package/src/serializer.ts +335 -0
- package/src/skills/chant-cedar-authoring.md +180 -0
- package/src/skills/chant-cedar-avp-embedding.md +125 -0
- package/src/skills/chant-cedar-meta-policy.md +119 -0
- package/src/spec/default-schema.cedarschema +108 -0
- package/src/spec/fetch.ts +107 -0
- package/src/spec/parse.test.ts +123 -0
- package/src/spec/parse.ts +253 -0
- package/src/spec/pin.test.ts +88 -0
- package/src/spec/pin.ts +243 -0
- package/src/spec/pinned-names.json +18 -0
- package/src/spec/wasm.ts +283 -0
- package/src/validate-cli.ts +8 -0
- package/src/validate.ts +85 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn a `.cedarschema` into the flat declaration list the emitters read.
|
|
3
|
+
*
|
|
4
|
+
* The canonical input is the human-readable syntax and the canonical
|
|
5
|
+
* intermediate is `schemaToJsonWithResolvedTypes`' output (#1648 §4). That
|
|
6
|
+
* choice buys one thing that matters here: `schemaToJson` emits
|
|
7
|
+
* `{type:"EntityOrCommon", name}` for every named type, which cannot
|
|
8
|
+
* distinguish an entity reference from a primitive from a common-type alias,
|
|
9
|
+
* so an emitter reading it would have to re-implement Cedar's own name
|
|
10
|
+
* resolution. The resolved form hands back `{type:"Entity", name:"App::User"}`
|
|
11
|
+
* for references and bare primitives for the rest.
|
|
12
|
+
*
|
|
13
|
+
* What it does *not* resolve is common types in attribute position: those come
|
|
14
|
+
* back as a bare qualified name (`{type:"App::TagSet"}`) with the definition
|
|
15
|
+
* still in `commonTypes`. {@link resolveTypeRef} closes that last hop, so
|
|
16
|
+
* nothing downstream ever sees an alias.
|
|
17
|
+
*
|
|
18
|
+
* One declaration per entity type and one per action, each carrying its own
|
|
19
|
+
* fully-qualified `typeName` — which is what lets `NamingStrategy` contest
|
|
20
|
+
* short names across namespaces the same way it does for every other lexicon.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { ParsedResult } from "@intentius/chant/codegen/generate";
|
|
24
|
+
import { resolveSchema, type ResolvedNamespace, type ResolvedSchemaJson, type ResolvedType } from "./wasm";
|
|
25
|
+
|
|
26
|
+
// ── Types ─────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
/** A Cedar type, with common-type aliases already collapsed. */
|
|
29
|
+
export type CedarTypeRef =
|
|
30
|
+
| { kind: "primitive"; cedar: "String" | "Long" | "Bool"; ts: "string" | "number" | "boolean" }
|
|
31
|
+
| { kind: "entity"; entityType: string }
|
|
32
|
+
| { kind: "set"; element: CedarTypeRef }
|
|
33
|
+
| { kind: "record"; attributes: CedarAttribute[] }
|
|
34
|
+
/** `ipaddr`, `decimal`, `datetime`, `duration` — carried as strings in TS. */
|
|
35
|
+
| { kind: "extension"; name: string }
|
|
36
|
+
/** An enumerated entity type, or anything the resolver left unrecognized. */
|
|
37
|
+
| { kind: "opaque"; name: string };
|
|
38
|
+
|
|
39
|
+
export interface CedarAttribute {
|
|
40
|
+
name: string;
|
|
41
|
+
required: boolean;
|
|
42
|
+
type: CedarTypeRef;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface CedarDeclBase extends ParsedResult {
|
|
46
|
+
/** Fully-qualified: `App::User`, or `App::Action::"read"`. */
|
|
47
|
+
typeName: string;
|
|
48
|
+
/** Namespace the declaration lives in; `""` for the empty namespace. */
|
|
49
|
+
namespace: string;
|
|
50
|
+
propertyTypes: Array<{ name: string }>;
|
|
51
|
+
enums: unknown[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface CedarEntityDecl extends CedarDeclBase {
|
|
55
|
+
kind: "entity";
|
|
56
|
+
/** Unqualified name as written in the schema. */
|
|
57
|
+
shortName: string;
|
|
58
|
+
/** Fully-qualified parents from `in [...]`. */
|
|
59
|
+
memberOfTypes: string[];
|
|
60
|
+
attributes: CedarAttribute[];
|
|
61
|
+
/** Present for an enumerated entity (`entity Env enum ["prod","dev"]`). */
|
|
62
|
+
enumValues?: string[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface CedarActionDecl extends CedarDeclBase {
|
|
66
|
+
kind: "action";
|
|
67
|
+
/** The action id as written (`read`). */
|
|
68
|
+
actionId: string;
|
|
69
|
+
principalTypes: string[];
|
|
70
|
+
resourceTypes: string[];
|
|
71
|
+
context: CedarAttribute[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type CedarDecl = CedarEntityDecl | CedarActionDecl;
|
|
75
|
+
|
|
76
|
+
export interface ParsedSchema {
|
|
77
|
+
decls: CedarDecl[];
|
|
78
|
+
/** The resolved JSON, kept for the content pin and for coverage reporting. */
|
|
79
|
+
resolved: ResolvedSchemaJson;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ── Parsing ───────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Parse a `.cedarschema` into declarations plus the resolved JSON it came from.
|
|
86
|
+
*
|
|
87
|
+
* Throws with the wasm's own message on a schema that does not parse — which
|
|
88
|
+
* is the right failure for a project schema that is malformed, and reads far
|
|
89
|
+
* better than the alternative, since Cedar's parse errors carry `did you mean`
|
|
90
|
+
* suggestions.
|
|
91
|
+
*/
|
|
92
|
+
export function parseCedarSchema(schemaText: string): ParsedSchema {
|
|
93
|
+
const result = resolveSchema(schemaText);
|
|
94
|
+
if (!result.ok) {
|
|
95
|
+
throw new Error(`cedar: schema did not resolve — ${result.error}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const resolved = result.value;
|
|
99
|
+
const decls: CedarDecl[] = [];
|
|
100
|
+
|
|
101
|
+
for (const namespace of Object.keys(resolved).sort()) {
|
|
102
|
+
decls.push(...declarationsFor(namespace, resolved[namespace]));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { decls, resolved };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* `parseSchema` for `generatePipeline`. One buffer in, many declarations out.
|
|
110
|
+
*/
|
|
111
|
+
export function parseSchema(_typeName: string, data: Buffer): CedarDecl[] {
|
|
112
|
+
return parseCedarSchema(data.toString("utf-8")).decls;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function declarationsFor(namespace: string, body: ResolvedNamespace | undefined): CedarDecl[] {
|
|
116
|
+
if (!body) return [];
|
|
117
|
+
const commonTypes = body.commonTypes ?? {};
|
|
118
|
+
const decls: CedarDecl[] = [];
|
|
119
|
+
|
|
120
|
+
for (const shortName of Object.keys(body.entityTypes ?? {}).sort()) {
|
|
121
|
+
const entity = body.entityTypes![shortName];
|
|
122
|
+
const typeName = qualify(namespace, shortName);
|
|
123
|
+
const attributes = recordAttributes(entity.shape, commonTypes, namespace);
|
|
124
|
+
|
|
125
|
+
decls.push({
|
|
126
|
+
kind: "entity",
|
|
127
|
+
typeName,
|
|
128
|
+
namespace,
|
|
129
|
+
shortName,
|
|
130
|
+
memberOfTypes: [...(entity.memberOfTypes ?? [])].sort(),
|
|
131
|
+
attributes,
|
|
132
|
+
...(entity.enum ? { enumValues: [...entity.enum] } : {}),
|
|
133
|
+
// One "property type" per entity: its attribute record. Nested anonymous
|
|
134
|
+
// records are inlined rather than named, so this stays 1:1.
|
|
135
|
+
propertyTypes: [{ name: `${shortName}_Attributes` }],
|
|
136
|
+
enums: entity.enum ? [entity.enum] : [],
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
for (const actionId of Object.keys(body.actions ?? {}).sort()) {
|
|
141
|
+
const action = body.actions![actionId];
|
|
142
|
+
const appliesTo = action.appliesTo ?? {};
|
|
143
|
+
|
|
144
|
+
decls.push({
|
|
145
|
+
kind: "action",
|
|
146
|
+
typeName: actionUid(namespace, actionId),
|
|
147
|
+
namespace,
|
|
148
|
+
actionId,
|
|
149
|
+
principalTypes: [...(appliesTo.principalTypes ?? [])].sort(),
|
|
150
|
+
resourceTypes: [...(appliesTo.resourceTypes ?? [])].sort(),
|
|
151
|
+
context: recordAttributes(appliesTo.context, commonTypes, namespace),
|
|
152
|
+
propertyTypes: [{ name: `${actionId}_Context` }],
|
|
153
|
+
enums: [],
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return decls;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** `App` + `User` → `App::User`; the empty namespace yields the bare name. */
|
|
161
|
+
export function qualify(namespace: string, shortName: string): string {
|
|
162
|
+
return namespace ? `${namespace}::${shortName}` : shortName;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** The entity UID form an action is referenced by in a policy. */
|
|
166
|
+
export function actionUid(namespace: string, actionId: string): string {
|
|
167
|
+
return namespace ? `${namespace}::Action::"${actionId}"` : `Action::"${actionId}"`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function recordAttributes(
|
|
171
|
+
shape: ResolvedType | undefined,
|
|
172
|
+
commonTypes: Record<string, ResolvedType>,
|
|
173
|
+
namespace: string,
|
|
174
|
+
): CedarAttribute[] {
|
|
175
|
+
if (!shape || shape.type !== "Record" || !shape.attributes) return [];
|
|
176
|
+
return Object.keys(shape.attributes)
|
|
177
|
+
.sort()
|
|
178
|
+
.map((name) => {
|
|
179
|
+
const attr = shape.attributes![name];
|
|
180
|
+
return {
|
|
181
|
+
name,
|
|
182
|
+
// Cedar attributes are required unless the schema says otherwise, so
|
|
183
|
+
// `required` absent means required — the opposite of JSON Schema.
|
|
184
|
+
required: attr.required !== false,
|
|
185
|
+
type: resolveTypeRef(attr, commonTypes, namespace),
|
|
186
|
+
};
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const PRIMITIVES: Record<string, { cedar: "String" | "Long" | "Bool"; ts: "string" | "number" | "boolean" }> = {
|
|
191
|
+
String: { cedar: "String", ts: "string" },
|
|
192
|
+
Long: { cedar: "Long", ts: "number" },
|
|
193
|
+
Bool: { cedar: "Bool", ts: "boolean" },
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const EXTENSIONS = new Set(["ipaddr", "decimal", "datetime", "duration"]);
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Collapse one resolved type node, following common-type aliases.
|
|
200
|
+
*
|
|
201
|
+
* `seen` guards a schema whose common types reference each other — Cedar
|
|
202
|
+
* rejects true cycles, but a malformed one should not spin here.
|
|
203
|
+
*/
|
|
204
|
+
export function resolveTypeRef(
|
|
205
|
+
node: ResolvedType,
|
|
206
|
+
commonTypes: Record<string, ResolvedType>,
|
|
207
|
+
namespace: string,
|
|
208
|
+
seen: ReadonlySet<string> = new Set(),
|
|
209
|
+
): CedarTypeRef {
|
|
210
|
+
const primitive = PRIMITIVES[node.type];
|
|
211
|
+
if (primitive) return { kind: "primitive", ...primitive };
|
|
212
|
+
|
|
213
|
+
if (node.type === "Entity") {
|
|
214
|
+
return { kind: "entity", entityType: node.name ?? "" };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (node.type === "Set") {
|
|
218
|
+
const element = node.element
|
|
219
|
+
? resolveTypeRef(node.element, commonTypes, namespace, seen)
|
|
220
|
+
: ({ kind: "opaque", name: "Set" } as CedarTypeRef);
|
|
221
|
+
return { kind: "set", element };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (node.type === "Record") {
|
|
225
|
+
const attributes = Object.keys(node.attributes ?? {})
|
|
226
|
+
.sort()
|
|
227
|
+
.map((name) => {
|
|
228
|
+
const attr = node.attributes![name];
|
|
229
|
+
return {
|
|
230
|
+
name,
|
|
231
|
+
required: attr.required !== false,
|
|
232
|
+
type: resolveTypeRef(attr, commonTypes, namespace, seen),
|
|
233
|
+
};
|
|
234
|
+
});
|
|
235
|
+
return { kind: "record", attributes };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (EXTENSIONS.has(node.type)) {
|
|
239
|
+
return { kind: "extension", name: node.type };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// `EntityOrCommon` should not survive resolution, but a defensive branch
|
|
243
|
+
// costs nothing and keeps a future wasm version from emitting `unknown`.
|
|
244
|
+
const named = node.type === "EntityOrCommon" ? (node.name ?? "") : node.type;
|
|
245
|
+
|
|
246
|
+
const commonKey = named.startsWith(`${namespace}::`) ? named.slice(namespace.length + 2) : named;
|
|
247
|
+
const definition = commonTypes[commonKey];
|
|
248
|
+
if (definition && !seen.has(named)) {
|
|
249
|
+
return resolveTypeRef(definition, commonTypes, namespace, new Set([...seen, named]));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return { kind: "opaque", name: named };
|
|
253
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import {
|
|
6
|
+
assertPinnedLangVersion,
|
|
7
|
+
assertPinnedSchema,
|
|
8
|
+
CEDAR_LANG_VERSION,
|
|
9
|
+
CEDAR_SCHEMA_PIN,
|
|
10
|
+
CEDAR_WASM_VERSION,
|
|
11
|
+
PINNED_SCHEMA_NAMES,
|
|
12
|
+
resolvedSchemaDigest,
|
|
13
|
+
schemaDrift,
|
|
14
|
+
} from "./pin";
|
|
15
|
+
import { langVersion, packageVersion } from "./wasm";
|
|
16
|
+
import { defaultSchemaPath } from "./fetch";
|
|
17
|
+
import { parseCedarSchema } from "./parse";
|
|
18
|
+
|
|
19
|
+
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "..");
|
|
20
|
+
|
|
21
|
+
describe("version pin", () => {
|
|
22
|
+
it("matches the installed cedar-wasm", () => {
|
|
23
|
+
expect(packageVersion()).toBe(CEDAR_WASM_VERSION);
|
|
24
|
+
expect(langVersion()).toBe(CEDAR_LANG_VERSION);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("matches the version in package.json, which is what npm installs", () => {
|
|
28
|
+
const pkg = JSON.parse(readFileSync(join(repoRoot, "lexicons", "cedar", "package.json"), "utf-8")) as {
|
|
29
|
+
dependencies?: Record<string, string>;
|
|
30
|
+
};
|
|
31
|
+
expect(pkg.dependencies?.["@cedar-policy/cedar-wasm"]).toBe(CEDAR_WASM_VERSION);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("refuses a language version other than the pinned one", () => {
|
|
35
|
+
expect(() => assertPinnedLangVersion({ actual: "4.6", env: {} })).toThrow(/language version mismatch/);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("proceeds with a warning under the accept env var", () => {
|
|
39
|
+
const warnings: string[] = [];
|
|
40
|
+
assertPinnedLangVersion({
|
|
41
|
+
actual: "4.6",
|
|
42
|
+
env: { CHANT_ACCEPT_CEDAR_LANG: "1" },
|
|
43
|
+
warn: (m) => warnings.push(m),
|
|
44
|
+
});
|
|
45
|
+
expect(warnings).toHaveLength(1);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("passes on a match", () => {
|
|
49
|
+
expect(() => assertPinnedLangVersion()).not.toThrow();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe("content pin", () => {
|
|
54
|
+
const { decls, resolved } = parseCedarSchema(readFileSync(defaultSchemaPath(), "utf-8"));
|
|
55
|
+
const names = decls.map((d) => d.typeName).sort();
|
|
56
|
+
|
|
57
|
+
it("matches the bundled default schema", () => {
|
|
58
|
+
expect(resolvedSchemaDigest(resolved)).toBe(CEDAR_SCHEMA_PIN.digest);
|
|
59
|
+
expect(names).toEqual([...PINNED_SCHEMA_NAMES]);
|
|
60
|
+
expect(CEDAR_SCHEMA_PIN.declarations).toBe(names.length);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("digests the resolved JSON, so reformatting the schema text does not move it", () => {
|
|
64
|
+
const reformatted = readFileSync(defaultSchemaPath(), "utf-8").replace(/\n/g, "\n\n");
|
|
65
|
+
expect(resolvedSchemaDigest(parseCedarSchema(reformatted).resolved)).toBe(CEDAR_SCHEMA_PIN.digest);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("names what moved when it moves", () => {
|
|
69
|
+
const drift = schemaDrift({ changed: true }, [...names, "App::Ghost"], names);
|
|
70
|
+
expect(drift?.added).toEqual(["App::Ghost"]);
|
|
71
|
+
expect(drift?.removed).toEqual([]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("refuses rather than regenerating against a schema nobody chose", () => {
|
|
75
|
+
expect(() => assertPinnedSchema({ changed: true }, names, { env: {} })).toThrow(
|
|
76
|
+
/no longer resolves to the pinned JSON/,
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("warns instead under the accept env var", () => {
|
|
81
|
+
const warnings: string[] = [];
|
|
82
|
+
assertPinnedSchema({ changed: true }, names, {
|
|
83
|
+
env: { CHANT_ACCEPT_CEDAR_SCHEMA: "1" },
|
|
84
|
+
warn: (m) => warnings.push(m),
|
|
85
|
+
});
|
|
86
|
+
expect(warnings[0]).toMatch(/digest: "sha256:/);
|
|
87
|
+
});
|
|
88
|
+
});
|
package/src/spec/pin.ts
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What this lexicon pins, and why it pins two different things (#1650, #1390).
|
|
3
|
+
*
|
|
4
|
+
* ## The version pin
|
|
5
|
+
*
|
|
6
|
+
* `@cedar-policy/cedar-wasm` and the Cedar *language* move independently: the
|
|
7
|
+
* package is 4.12.0, the grammar it implements is 4.5 (#1648 §0). A package
|
|
8
|
+
* bump that leaves the language at 4.5 cannot change what parses, so the
|
|
9
|
+
* language version is the value worth asserting at generation time and the
|
|
10
|
+
* package version is the one `upstreamPin` bumps. Both are recorded.
|
|
11
|
+
*
|
|
12
|
+
* The runtime assert is chant #1390's spirit applied to a dependency rather
|
|
13
|
+
* than to a downloaded spec: generation refuses when the installed package
|
|
14
|
+
* implements a different language version than the one whose resolved output
|
|
15
|
+
* produced the committed artifacts. Releases are roughly monthly, so this will
|
|
16
|
+
* fire — and when it does, the answer is to look at the new resolved JSON, not
|
|
17
|
+
* to publish types generated from a grammar nobody checked.
|
|
18
|
+
*
|
|
19
|
+
* ## The content pin
|
|
20
|
+
*
|
|
21
|
+
* The bundled default schema (`default-schema.cedarschema`) is what generation
|
|
22
|
+
* reads in this repo and in a fresh checkout, so it decides the published
|
|
23
|
+
* surface. There is no upstream URL to pin, so the pin is over content: a
|
|
24
|
+
* sha256 over the *resolved* JSON — the thing the emitters actually read —
|
|
25
|
+
* plus the covered entity-type and action names committed beside it.
|
|
26
|
+
*
|
|
27
|
+
* Resolved-JSON serialization is byte-deterministic (Rust `BTreeMap` ordering,
|
|
28
|
+
* 1 distinct serialization over 25 runs, #1648 §5.2), so the digest is stable.
|
|
29
|
+
* It moves when the default schema is edited, and — the case the pin exists for
|
|
30
|
+
* — when a cedar-wasm upgrade resolves the same schema differently. Both change
|
|
31
|
+
* the generated types, and neither is visible in a diff otherwise, because
|
|
32
|
+
* `src/generated/` is not committed.
|
|
33
|
+
*
|
|
34
|
+
* A user's own project schema is not pinned. It is their input, they own its
|
|
35
|
+
* churn, and refusing to generate because they edited their own schema would be
|
|
36
|
+
* absurd.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import { createHash } from "crypto";
|
|
40
|
+
import pinnedNames from "./pinned-names.json" with { type: "json" };
|
|
41
|
+
import { langVersion } from "./wasm";
|
|
42
|
+
|
|
43
|
+
/** The pinned `@cedar-policy/cedar-wasm` version — what `upstreamPin` bumps. */
|
|
44
|
+
export const CEDAR_WASM_VERSION = "4.12.0";
|
|
45
|
+
|
|
46
|
+
/** The Cedar *language* version that package implements (`getCedarLangVersion()`). */
|
|
47
|
+
export const CEDAR_LANG_VERSION = "4.5";
|
|
48
|
+
|
|
49
|
+
/** Env var that proceeds past a language-version mismatch for one run. */
|
|
50
|
+
export const ACCEPT_LANG_ENV = "CHANT_ACCEPT_CEDAR_LANG";
|
|
51
|
+
|
|
52
|
+
/** Env var that accepts a moved default-schema digest, printing the new pin. */
|
|
53
|
+
export const ACCEPT_SCHEMA_ENV = "CHANT_ACCEPT_CEDAR_SCHEMA";
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The entity types and action UIDs the pinned default schema resolved to.
|
|
57
|
+
*
|
|
58
|
+
* Committed beside the digest for the same reason aws commits its type names:
|
|
59
|
+
* without the list, accepting a new pin is one opaque hash replacing another,
|
|
60
|
+
* and the generated artifacts cannot stand in because they are gitignored.
|
|
61
|
+
*/
|
|
62
|
+
export const PINNED_SCHEMA_NAMES: readonly string[] = pinnedNames as string[];
|
|
63
|
+
|
|
64
|
+
export interface SchemaPin {
|
|
65
|
+
/** `sha256:…` over the resolved schema JSON. */
|
|
66
|
+
readonly digest: string;
|
|
67
|
+
/** How many entity types and actions the pinned schema declared. */
|
|
68
|
+
readonly declarations: number;
|
|
69
|
+
/** ISO date the pin was accepted, so a diff reads as a decision. */
|
|
70
|
+
readonly accepted: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The accepted default schema.
|
|
75
|
+
*
|
|
76
|
+
* To move it: run generation, read the refusal, confirm the delta is one you
|
|
77
|
+
* want, and paste the printed block here in its own commit.
|
|
78
|
+
*/
|
|
79
|
+
export const CEDAR_SCHEMA_PIN: SchemaPin = {
|
|
80
|
+
digest: "sha256:8f86a4e4fd6845c245c84d226277741c41d29da6a173f01477bda3a662dd3ec9",
|
|
81
|
+
declarations: 16,
|
|
82
|
+
accepted: "2026-08-10",
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Digest the resolved schema JSON.
|
|
87
|
+
*
|
|
88
|
+
* Hashes the canonical serialization the wasm produced, not the authored
|
|
89
|
+
* `.cedarschema` text: reformatting a schema, or reordering its entity types,
|
|
90
|
+
* changes the text and not one byte of what the emitters read.
|
|
91
|
+
*/
|
|
92
|
+
export function resolvedSchemaDigest(resolved: unknown): string {
|
|
93
|
+
return `sha256:${createHash("sha256").update(JSON.stringify(resolved)).digest("hex")}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** What moved between the pinned schema and the one just resolved. */
|
|
97
|
+
export interface SchemaDrift {
|
|
98
|
+
digest: string;
|
|
99
|
+
declarations: number;
|
|
100
|
+
added: string[];
|
|
101
|
+
removed: string[];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Compare a freshly resolved schema against a pin. `null` when it matches. */
|
|
105
|
+
export function schemaDrift(
|
|
106
|
+
resolved: unknown,
|
|
107
|
+
names: readonly string[],
|
|
108
|
+
pinnedNamesList: readonly string[] = PINNED_SCHEMA_NAMES,
|
|
109
|
+
pin: SchemaPin = CEDAR_SCHEMA_PIN,
|
|
110
|
+
): SchemaDrift | null {
|
|
111
|
+
const digest = resolvedSchemaDigest(resolved);
|
|
112
|
+
if (digest === pin.digest) return null;
|
|
113
|
+
|
|
114
|
+
const current = new Set(names);
|
|
115
|
+
const pinned = new Set(pinnedNamesList);
|
|
116
|
+
return {
|
|
117
|
+
digest,
|
|
118
|
+
declarations: current.size,
|
|
119
|
+
added: [...current].filter((n) => !pinned.has(n)).sort(),
|
|
120
|
+
removed: [...pinned].filter((n) => !current.has(n)).sort(),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** The report a mismatch produces. */
|
|
125
|
+
export function schemaDriftMessage(drift: SchemaDrift, pin: SchemaPin = CEDAR_SCHEMA_PIN): string {
|
|
126
|
+
const delta = drift.declarations - pin.declarations;
|
|
127
|
+
const countLine =
|
|
128
|
+
delta === 0
|
|
129
|
+
? `${drift.declarations} declarations, unchanged in count`
|
|
130
|
+
: `${drift.declarations} declarations, ${delta > 0 ? "+" : ""}${delta} against the pin`;
|
|
131
|
+
|
|
132
|
+
const lines = [
|
|
133
|
+
"The bundled default Cedar schema no longer resolves to the pinned JSON.",
|
|
134
|
+
"",
|
|
135
|
+
` pinned ${pin.digest} (${pin.declarations} declarations, accepted ${pin.accepted})`,
|
|
136
|
+
` resolved ${drift.digest} (${countLine})`,
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
if (drift.added.length > 0) {
|
|
140
|
+
lines.push(` added ${summarize(drift.added)}`);
|
|
141
|
+
}
|
|
142
|
+
if (drift.removed.length > 0) {
|
|
143
|
+
lines.push(` removed ${summarize(drift.removed)}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
lines.push(
|
|
147
|
+
"",
|
|
148
|
+
"Either the schema was edited or cedar-wasm resolves it differently than",
|
|
149
|
+
"the version the committed surface was generated from. Both rewrite the",
|
|
150
|
+
"generated types, and src/generated/ is not committed, so neither shows up",
|
|
151
|
+
"in a diff on its own.",
|
|
152
|
+
"",
|
|
153
|
+
"Confirm the delta is one you want and update lexicons/cedar/src/spec/pin.ts",
|
|
154
|
+
"and pinned-names.json, in their own commit:",
|
|
155
|
+
"",
|
|
156
|
+
` digest: "${drift.digest}",`,
|
|
157
|
+
` declarations: ${drift.declarations},`,
|
|
158
|
+
` accepted: "<today>",`,
|
|
159
|
+
"",
|
|
160
|
+
`Or re-run with ${ACCEPT_SCHEMA_ENV}=1 to proceed this once and print the same block.`,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
return lines.join("\n");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function summarize(names: string[]): string {
|
|
167
|
+
return `${names.slice(0, 5).join(", ")}${names.length > 5 ? ` (+${names.length - 5} more)` : ""}`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Refuse a default schema whose resolved JSON does not match the pin.
|
|
172
|
+
*
|
|
173
|
+
* Unlike aws, this one refuses on any digest move rather than only on a
|
|
174
|
+
* declaration-set change. It can afford to: the input is a file in this repo
|
|
175
|
+
* and a dependency at a pinned version, neither of which changes under the
|
|
176
|
+
* build's feet the way a republished CloudFormation archive does.
|
|
177
|
+
*/
|
|
178
|
+
export function assertPinnedSchema(
|
|
179
|
+
resolved: unknown,
|
|
180
|
+
names: readonly string[],
|
|
181
|
+
options: {
|
|
182
|
+
pin?: SchemaPin;
|
|
183
|
+
pinnedNames?: readonly string[];
|
|
184
|
+
env?: NodeJS.ProcessEnv;
|
|
185
|
+
warn?: (message: string) => void;
|
|
186
|
+
} = {},
|
|
187
|
+
): void {
|
|
188
|
+
const pin = options.pin ?? CEDAR_SCHEMA_PIN;
|
|
189
|
+
const drift = schemaDrift(resolved, names, options.pinnedNames ?? PINNED_SCHEMA_NAMES, pin);
|
|
190
|
+
if (!drift) return;
|
|
191
|
+
|
|
192
|
+
const env = options.env ?? process.env;
|
|
193
|
+
const warn = options.warn ?? ((m: string) => console.error(m));
|
|
194
|
+
const message = schemaDriftMessage(drift, pin);
|
|
195
|
+
|
|
196
|
+
if (env[ACCEPT_SCHEMA_ENV]) {
|
|
197
|
+
warn(message);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
throw new Error(message);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Refuse to generate against a Cedar language version other than the pinned one.
|
|
205
|
+
*
|
|
206
|
+
* chant #1390's rule, one level up the supply chain: a lexicon does not emit
|
|
207
|
+
* types derived from a grammar nobody chose. `getCedarLangVersion()` is the
|
|
208
|
+
* right probe because it is the number that decides what parses — the package
|
|
209
|
+
* version moves monthly and mostly does not.
|
|
210
|
+
*/
|
|
211
|
+
export function assertPinnedLangVersion(
|
|
212
|
+
options: {
|
|
213
|
+
actual?: string;
|
|
214
|
+
expected?: string;
|
|
215
|
+
env?: NodeJS.ProcessEnv;
|
|
216
|
+
warn?: (message: string) => void;
|
|
217
|
+
} = {},
|
|
218
|
+
): void {
|
|
219
|
+
const expected = options.expected ?? CEDAR_LANG_VERSION;
|
|
220
|
+
const actual = options.actual ?? langVersion();
|
|
221
|
+
if (actual === expected) return;
|
|
222
|
+
|
|
223
|
+
const message = [
|
|
224
|
+
`Cedar language version mismatch: cedar-wasm reports ${actual}, this lexicon pins ${expected}.`,
|
|
225
|
+
"",
|
|
226
|
+
"The installed @cedar-policy/cedar-wasm implements a different grammar than",
|
|
227
|
+
"the one the generated entity and action types were derived from. Generation",
|
|
228
|
+
"refuses rather than emitting a surface nobody reviewed.",
|
|
229
|
+
"",
|
|
230
|
+
"To accept it: check the resolved schema JSON for shape changes, update",
|
|
231
|
+
`CEDAR_LANG_VERSION (and CEDAR_WASM_VERSION) in lexicons/cedar/src/spec/pin.ts,`,
|
|
232
|
+
"and refresh the schema pin in the same commit.",
|
|
233
|
+
"",
|
|
234
|
+
`Or re-run with ${ACCEPT_LANG_ENV}=1 to proceed this once.`,
|
|
235
|
+
].join("\n");
|
|
236
|
+
|
|
237
|
+
const env = options.env ?? process.env;
|
|
238
|
+
if (env[ACCEPT_LANG_ENV]) {
|
|
239
|
+
(options.warn ?? ((m: string) => console.error(m)))(message);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
throw new Error(message);
|
|
243
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[
|
|
2
|
+
"App::Action::\"admin\"",
|
|
3
|
+
"App::Action::\"approve\"",
|
|
4
|
+
"App::Action::\"comment\"",
|
|
5
|
+
"App::Action::\"create\"",
|
|
6
|
+
"App::Action::\"delete\"",
|
|
7
|
+
"App::Action::\"list\"",
|
|
8
|
+
"App::Action::\"read\"",
|
|
9
|
+
"App::Action::\"share\"",
|
|
10
|
+
"App::Action::\"write\"",
|
|
11
|
+
"App::Application",
|
|
12
|
+
"App::Document",
|
|
13
|
+
"App::Folder",
|
|
14
|
+
"App::Group",
|
|
15
|
+
"App::ServiceAccount",
|
|
16
|
+
"App::Team",
|
|
17
|
+
"App::User"
|
|
18
|
+
]
|