@openpolicy/cli 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_cli-BVVbyDQ9.js +49 -0
- package/dist/_cli-BjVuFgXe.js +33 -0
- package/dist/_cli-C-Vx-Nop.js +58 -0
- package/dist/_cli-DrypJgn2.js +247 -0
- package/dist/_lib-9AbI1LBo.js +35 -0
- package/dist/_lib-9AbI1LBo.js.map +1 -0
- package/dist/_lib-D2I-M4OU.js +51 -0
- package/dist/_lib-D2I-M4OU.js.map +1 -0
- package/dist/_lib-D2LkleUQ.js +60 -0
- package/dist/_lib-D2LkleUQ.js.map +1 -0
- package/dist/_lib-MI0GKJMR.js +249 -0
- package/dist/_lib-MI0GKJMR.js.map +1 -0
- package/dist/cli +0 -0
- package/dist/cli.js +381 -2759
- package/dist/generate-ItFTJ-jj.js +60 -0
- package/dist/generate-ItFTJ-jj.js.map +1 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -961
- package/dist/index.js.map +1 -0
- package/dist/init-37NxG-6N.js +249 -0
- package/dist/init-37NxG-6N.js.map +1 -0
- package/dist/load-config-Bw8yiDkb.js +35 -0
- package/dist/load-config-Bw8yiDkb.js.map +1 -0
- package/dist/validate-DPge58Uv.js +51 -0
- package/dist/validate-DPge58Uv.js.map +1 -0
- package/package.json +4 -19
- package/src/cli.ts +0 -5
- package/src/commands/generate.test.ts +0 -19
- package/src/commands/generate.ts +0 -58
- package/src/commands/init.test.ts +0 -15
- package/src/commands/init.ts +0 -197
- package/src/commands/validate.test.ts +0 -15
- package/src/commands/validate.ts +0 -53
- package/src/index.test.ts +0 -29
- package/src/index.ts +0 -21
- package/src/utils/load-config.ts +0 -32
package/src/commands/init.ts
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
|
-
import type { Jurisdiction } from "@openpolicy/core";
|
|
3
|
-
import { defineCommand } from "citty";
|
|
4
|
-
import consola from "consola";
|
|
5
|
-
|
|
6
|
-
const DATA_CATEGORY_MAP: Record<string, { group: string; label: string }> = {
|
|
7
|
-
name: { group: "Personal Information", label: "Full name" },
|
|
8
|
-
email: { group: "Personal Information", label: "Email address" },
|
|
9
|
-
ip_address: { group: "Technical Data", label: "IP address" },
|
|
10
|
-
device_info: { group: "Technical Data", label: "Device type and browser" },
|
|
11
|
-
location: { group: "Location Data", label: "Approximate location" },
|
|
12
|
-
payment_info: { group: "Financial Data", label: "Payment card details" },
|
|
13
|
-
usage_data: { group: "Usage Data", label: "Pages visited and features used" },
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
function toJurisdictions(choice: string): Jurisdiction[] {
|
|
17
|
-
if (choice === "gdpr") return ["eu"];
|
|
18
|
-
if (choice === "ccpa") return ["ca"];
|
|
19
|
-
if (choice === "both") return ["eu", "ca"];
|
|
20
|
-
return ["us"];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function toDataCollected(categories: string[]): Record<string, string[]> {
|
|
24
|
-
const groups: Record<string, string[]> = {};
|
|
25
|
-
for (const cat of categories) {
|
|
26
|
-
const mapping = DATA_CATEGORY_MAP[cat];
|
|
27
|
-
if (!mapping) continue;
|
|
28
|
-
groups[mapping.group] = [...(groups[mapping.group] ?? []), mapping.label];
|
|
29
|
-
}
|
|
30
|
-
return Object.keys(groups).length > 0
|
|
31
|
-
? groups
|
|
32
|
-
: { "Personal Information": ["Email address"] };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function toUserRights(jurisdictions: Jurisdiction[]): string[] {
|
|
36
|
-
const rights = new Set<string>(["access", "erasure"]);
|
|
37
|
-
if (jurisdictions.includes("eu")) {
|
|
38
|
-
for (const r of [
|
|
39
|
-
"rectification",
|
|
40
|
-
"portability",
|
|
41
|
-
"restriction",
|
|
42
|
-
"objection",
|
|
43
|
-
])
|
|
44
|
-
rights.add(r);
|
|
45
|
-
}
|
|
46
|
-
if (jurisdictions.includes("ca")) {
|
|
47
|
-
for (const r of ["opt_out_sale", "non_discrimination"]) rights.add(r);
|
|
48
|
-
}
|
|
49
|
-
return Array.from(rights);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function renderConfig(values: {
|
|
53
|
-
companyName: string;
|
|
54
|
-
legalName: string;
|
|
55
|
-
address: string;
|
|
56
|
-
contact: string;
|
|
57
|
-
jurisdictions: Jurisdiction[];
|
|
58
|
-
dataCollected: Record<string, string[]>;
|
|
59
|
-
legalBasis: string;
|
|
60
|
-
hasCookies: boolean;
|
|
61
|
-
userRights: string[];
|
|
62
|
-
}): string {
|
|
63
|
-
const dataLines = Object.entries(values.dataCollected)
|
|
64
|
-
.map(([k, v]) => ` ${JSON.stringify(k)}: ${JSON.stringify(v)},`)
|
|
65
|
-
.join("\n");
|
|
66
|
-
|
|
67
|
-
const today = new Date().toISOString().slice(0, 10);
|
|
68
|
-
|
|
69
|
-
return `import { definePrivacyPolicy } from "@openpolicy/sdk";
|
|
70
|
-
|
|
71
|
-
export default definePrivacyPolicy({
|
|
72
|
-
effectiveDate: "${today}",
|
|
73
|
-
company: {
|
|
74
|
-
name: ${JSON.stringify(values.companyName)},
|
|
75
|
-
legalName: ${JSON.stringify(values.legalName)},
|
|
76
|
-
address: ${JSON.stringify(values.address)},
|
|
77
|
-
contact: ${JSON.stringify(values.contact)},
|
|
78
|
-
},
|
|
79
|
-
dataCollected: {
|
|
80
|
-
${dataLines}
|
|
81
|
-
},
|
|
82
|
-
legalBasis: ${JSON.stringify(values.legalBasis)},
|
|
83
|
-
retention: {
|
|
84
|
-
"All personal data": "As long as necessary for the purposes described in this policy",
|
|
85
|
-
},
|
|
86
|
-
cookies: {
|
|
87
|
-
essential: true,
|
|
88
|
-
analytics: ${values.hasCookies},
|
|
89
|
-
marketing: false,
|
|
90
|
-
},
|
|
91
|
-
thirdParties: [],
|
|
92
|
-
userRights: ${JSON.stringify(values.userRights)},
|
|
93
|
-
jurisdictions: ${JSON.stringify(values.jurisdictions)},
|
|
94
|
-
});
|
|
95
|
-
`;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export const initCommand = defineCommand({
|
|
99
|
-
meta: {
|
|
100
|
-
name: "init",
|
|
101
|
-
description: "Interactively create a policy config file",
|
|
102
|
-
},
|
|
103
|
-
args: {
|
|
104
|
-
out: {
|
|
105
|
-
type: "string",
|
|
106
|
-
description: "Output path for generated config",
|
|
107
|
-
default: "./policy.config.ts",
|
|
108
|
-
},
|
|
109
|
-
yes: {
|
|
110
|
-
type: "boolean",
|
|
111
|
-
description: "Skip prompts and use defaults (CI mode)",
|
|
112
|
-
default: false,
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
async run({ args }) {
|
|
116
|
-
consola.start("OpenPolicy init wizard");
|
|
117
|
-
|
|
118
|
-
const companyName = String(
|
|
119
|
-
await consola.prompt("Company name?", { type: "text", cancel: "reject" }),
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
const legalName = String(
|
|
123
|
-
await consola.prompt("Legal entity name?", {
|
|
124
|
-
type: "text",
|
|
125
|
-
cancel: "reject",
|
|
126
|
-
initial: companyName,
|
|
127
|
-
}),
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
const address = String(
|
|
131
|
-
await consola.prompt("Company address?", {
|
|
132
|
-
type: "text",
|
|
133
|
-
cancel: "reject",
|
|
134
|
-
}),
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
const contact = String(
|
|
138
|
-
await consola.prompt("Privacy contact email?", {
|
|
139
|
-
type: "text",
|
|
140
|
-
cancel: "reject",
|
|
141
|
-
}),
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
const jurisdictionChoice = String(
|
|
145
|
-
await consola.prompt("Jurisdiction?", {
|
|
146
|
-
type: "select",
|
|
147
|
-
cancel: "reject",
|
|
148
|
-
options: ["gdpr", "ccpa", "both"],
|
|
149
|
-
}),
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
const dataCategories = (await consola.prompt("Data categories collected?", {
|
|
153
|
-
type: "multiselect",
|
|
154
|
-
cancel: "reject",
|
|
155
|
-
options: [
|
|
156
|
-
"name",
|
|
157
|
-
"email",
|
|
158
|
-
"ip_address",
|
|
159
|
-
"device_info",
|
|
160
|
-
"location",
|
|
161
|
-
"payment_info",
|
|
162
|
-
"usage_data",
|
|
163
|
-
],
|
|
164
|
-
})) as string[];
|
|
165
|
-
|
|
166
|
-
const hasCookies = Boolean(
|
|
167
|
-
await consola.prompt("Does your app use cookies?", {
|
|
168
|
-
type: "confirm",
|
|
169
|
-
cancel: "reject",
|
|
170
|
-
initial: true,
|
|
171
|
-
}),
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
const jurisdictions = toJurisdictions(jurisdictionChoice);
|
|
175
|
-
const dataCollected = toDataCollected(dataCategories);
|
|
176
|
-
const userRights = toUserRights(jurisdictions);
|
|
177
|
-
const legalBasis = jurisdictions.includes("eu")
|
|
178
|
-
? "Legitimate interests and consent"
|
|
179
|
-
: "";
|
|
180
|
-
|
|
181
|
-
const source = renderConfig({
|
|
182
|
-
companyName,
|
|
183
|
-
legalName,
|
|
184
|
-
address,
|
|
185
|
-
contact,
|
|
186
|
-
jurisdictions,
|
|
187
|
-
dataCollected,
|
|
188
|
-
legalBasis,
|
|
189
|
-
hasCookies,
|
|
190
|
-
userRights,
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
const outPath = resolve(args.out ?? "./policy.config.ts");
|
|
194
|
-
await Bun.write(outPath, source);
|
|
195
|
-
consola.success(`Config written to ${outPath}`);
|
|
196
|
-
},
|
|
197
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "bun:test";
|
|
2
|
-
import type { ArgsDef, CommandMeta } from "citty";
|
|
3
|
-
import { validateCommand } from "./validate";
|
|
4
|
-
|
|
5
|
-
test("validateCommand has correct name", () => {
|
|
6
|
-
expect((validateCommand.meta as CommandMeta)?.name).toBe("validate");
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
test("validateCommand has config positional arg", () => {
|
|
10
|
-
expect((validateCommand.args as ArgsDef)?.config?.type).toBe("positional");
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test("validateCommand has jurisdiction arg", () => {
|
|
14
|
-
expect((validateCommand.args as ArgsDef)?.jurisdiction?.type).toBe("string");
|
|
15
|
-
});
|
package/src/commands/validate.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { PrivacyPolicyConfig } from "@openpolicy/core";
|
|
2
|
-
import { validatePrivacyPolicy } from "@openpolicy/core";
|
|
3
|
-
import { defineCommand } from "citty";
|
|
4
|
-
import consola from "consola";
|
|
5
|
-
import { loadConfig } from "../utils/load-config";
|
|
6
|
-
|
|
7
|
-
export const validateCommand = defineCommand({
|
|
8
|
-
meta: {
|
|
9
|
-
name: "validate",
|
|
10
|
-
description: "Validate a policy config for compliance",
|
|
11
|
-
},
|
|
12
|
-
args: {
|
|
13
|
-
config: {
|
|
14
|
-
type: "positional",
|
|
15
|
-
description: "Path to policy config file",
|
|
16
|
-
default: "./policy.config.ts",
|
|
17
|
-
},
|
|
18
|
-
jurisdiction: {
|
|
19
|
-
type: "string",
|
|
20
|
-
description: "Jurisdiction to validate against: gdpr, ccpa, or all",
|
|
21
|
-
default: "all",
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
async run({ args }) {
|
|
25
|
-
const configPath = args.config ?? "./policy.config.ts";
|
|
26
|
-
|
|
27
|
-
consola.start(`Validating ${configPath}`);
|
|
28
|
-
|
|
29
|
-
const config = await loadConfig(configPath);
|
|
30
|
-
const issues = validatePrivacyPolicy(config as PrivacyPolicyConfig);
|
|
31
|
-
|
|
32
|
-
if (issues.length === 0) {
|
|
33
|
-
consola.success("Config is valid — no issues found.");
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
for (const issue of issues) {
|
|
38
|
-
if (issue.level === "error") {
|
|
39
|
-
consola.error(issue.message);
|
|
40
|
-
} else {
|
|
41
|
-
consola.warn(issue.message);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const errors = issues.filter((i) => i.level === "error");
|
|
46
|
-
if (errors.length > 0) {
|
|
47
|
-
consola.fail(`Validation failed with ${errors.length} error(s).`);
|
|
48
|
-
process.exit(1);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
consola.success("Validation passed with warnings.");
|
|
52
|
-
},
|
|
53
|
-
});
|
package/src/index.test.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "bun:test";
|
|
2
|
-
import type { CommandMeta, SubCommandsDef } from "citty";
|
|
3
|
-
import { mainCommand, run } from "./index";
|
|
4
|
-
|
|
5
|
-
test("run is a function", () => {
|
|
6
|
-
expect(typeof run).toBe("function");
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
test("mainCommand has correct name", () => {
|
|
10
|
-
expect((mainCommand.meta as CommandMeta)?.name).toBe("openpolicy");
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test("mainCommand has init subcommand", () => {
|
|
14
|
-
expect(typeof (mainCommand.subCommands as SubCommandsDef)?.init).toBe(
|
|
15
|
-
"function",
|
|
16
|
-
);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
test("mainCommand has generate subcommand", () => {
|
|
20
|
-
expect(typeof (mainCommand.subCommands as SubCommandsDef)?.generate).toBe(
|
|
21
|
-
"function",
|
|
22
|
-
);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test("mainCommand has validate subcommand", () => {
|
|
26
|
-
expect(typeof (mainCommand.subCommands as SubCommandsDef)?.validate).toBe(
|
|
27
|
-
"function",
|
|
28
|
-
);
|
|
29
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { defineCommand } from "citty";
|
|
2
|
-
|
|
3
|
-
export const mainCommand = defineCommand({
|
|
4
|
-
meta: {
|
|
5
|
-
name: "openpolicy",
|
|
6
|
-
version: "0.0.1",
|
|
7
|
-
description: "Generate and validate privacy policy documents",
|
|
8
|
-
},
|
|
9
|
-
subCommands: {
|
|
10
|
-
init: () => import("./commands/init").then((m) => m.initCommand),
|
|
11
|
-
generate: () =>
|
|
12
|
-
import("./commands/generate").then((m) => m.generateCommand),
|
|
13
|
-
validate: () =>
|
|
14
|
-
import("./commands/validate").then((m) => m.validateCommand),
|
|
15
|
-
},
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
export async function run() {
|
|
19
|
-
const { runMain } = await import("citty");
|
|
20
|
-
await runMain(mainCommand);
|
|
21
|
-
}
|
package/src/utils/load-config.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
|
-
import consola from "consola";
|
|
3
|
-
|
|
4
|
-
export async function loadConfig(configPath: string): Promise<unknown> {
|
|
5
|
-
const absPath = resolve(configPath);
|
|
6
|
-
|
|
7
|
-
if (!(await Bun.file(absPath).exists())) {
|
|
8
|
-
consola.error(`Config file not found: ${absPath}`);
|
|
9
|
-
process.exit(1);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
let mod: unknown;
|
|
13
|
-
try {
|
|
14
|
-
mod = await import(absPath);
|
|
15
|
-
} catch (err) {
|
|
16
|
-
consola.error(`Failed to load config: ${absPath}`);
|
|
17
|
-
consola.error(err);
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const config =
|
|
22
|
-
(mod as Record<string, unknown>)["default"] ??
|
|
23
|
-
(mod as Record<string, unknown>)["module.exports"] ??
|
|
24
|
-
mod;
|
|
25
|
-
|
|
26
|
-
if (config === null || config === undefined || typeof config !== "object") {
|
|
27
|
-
consola.error(`Config must export a non-null object: ${absPath}`);
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return config;
|
|
32
|
-
}
|