@openpolicy/cli 0.0.10 → 0.0.12
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 +1 -0
- package/dist/cli.js +153 -294
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -41,6 +41,7 @@ openpolicy generate ./terms.config.ts --format markdown --out ./public/po
|
|
|
41
41
|
| `--format` | `markdown` | Comma-separated output formats: `markdown`, `html` |
|
|
42
42
|
| `--out` | `./public/policies` | Output directory |
|
|
43
43
|
| `--type` | auto-detected | Override policy type: `privacy` or `terms` |
|
|
44
|
+
| `--watch` | `false` | Watch config files and regenerate on changes |
|
|
44
45
|
|
|
45
46
|
Policy type is auto-detected from the filename — files containing `"terms"` compile as terms of service.
|
|
46
47
|
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { defineCommand, runMain } from "citty";
|
|
3
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
4
|
import { join, resolve } from "node:path";
|
|
4
5
|
import consola from "consola";
|
|
5
|
-
import {
|
|
6
|
-
import { compilePolicy, validatePrivacyPolicy, validateTermsOfService } from "@openpolicy/core";
|
|
7
|
-
import { existsSync } from "node:fs";
|
|
6
|
+
import { existsSync, watch } from "node:fs";
|
|
7
|
+
import { compilePolicy, expandOpenPolicyConfig, isOpenPolicyConfig, validateCookiePolicy, validatePrivacyPolicy, validateTermsOfService } from "@openpolicy/core";
|
|
8
8
|
//#region \0rolldown/runtime.js
|
|
9
9
|
var __defProp = Object.defineProperty;
|
|
10
10
|
var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -18,270 +18,121 @@ var __exportAll = (all, no_symbols) => {
|
|
|
18
18
|
return target;
|
|
19
19
|
};
|
|
20
20
|
//#endregion
|
|
21
|
+
//#region package.json
|
|
22
|
+
var version = "0.0.12";
|
|
23
|
+
//#endregion
|
|
21
24
|
//#region src/commands/init.ts
|
|
22
|
-
var init_exports = /* @__PURE__ */ __exportAll({
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (choice === "ccpa") return ["ca"];
|
|
26
|
-
if (choice === "both") return ["eu", "ca"];
|
|
27
|
-
return ["us"];
|
|
28
|
-
}
|
|
29
|
-
function toDataCollected(categories) {
|
|
30
|
-
const groups = {};
|
|
31
|
-
for (const cat of categories) {
|
|
32
|
-
const mapping = DATA_CATEGORY_MAP[cat];
|
|
33
|
-
if (!mapping) continue;
|
|
34
|
-
groups[mapping.group] = [...groups[mapping.group] ?? [], mapping.label];
|
|
35
|
-
}
|
|
36
|
-
return Object.keys(groups).length > 0 ? groups : { "Personal Information": ["Email address"] };
|
|
37
|
-
}
|
|
38
|
-
function toUserRights(jurisdictions) {
|
|
39
|
-
const rights = new Set(["access", "erasure"]);
|
|
40
|
-
if (jurisdictions.includes("eu")) for (const r of [
|
|
41
|
-
"rectification",
|
|
42
|
-
"portability",
|
|
43
|
-
"restriction",
|
|
44
|
-
"objection"
|
|
45
|
-
]) rights.add(r);
|
|
46
|
-
if (jurisdictions.includes("ca")) for (const r of ["opt_out_sale", "non_discrimination"]) rights.add(r);
|
|
47
|
-
return Array.from(rights);
|
|
48
|
-
}
|
|
49
|
-
function renderPrivacyConfig(values) {
|
|
50
|
-
const dataLines = Object.entries(values.dataCollected).map(([k, v]) => ` ${JSON.stringify(k)}: ${JSON.stringify(v)},`).join("\n");
|
|
51
|
-
return `import { definePrivacyPolicy } from "@openpolicy/sdk";
|
|
52
|
-
|
|
53
|
-
export default definePrivacyPolicy({
|
|
54
|
-
effectiveDate: "${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}",
|
|
55
|
-
company: {
|
|
56
|
-
name: ${JSON.stringify(values.companyName)},
|
|
57
|
-
legalName: ${JSON.stringify(values.legalName)},
|
|
58
|
-
address: ${JSON.stringify(values.address)},
|
|
59
|
-
contact: ${JSON.stringify(values.contact)},
|
|
60
|
-
},
|
|
61
|
-
dataCollected: {
|
|
62
|
-
${dataLines}
|
|
63
|
-
},
|
|
64
|
-
legalBasis: ${JSON.stringify(values.legalBasis)},
|
|
65
|
-
retention: {
|
|
66
|
-
"All personal data": "As long as necessary for the purposes described in this policy",
|
|
67
|
-
},
|
|
68
|
-
cookies: {
|
|
69
|
-
essential: true,
|
|
70
|
-
analytics: ${values.hasCookies},
|
|
71
|
-
marketing: false,
|
|
72
|
-
},
|
|
73
|
-
thirdParties: [],
|
|
74
|
-
userRights: ${JSON.stringify(values.userRights)},
|
|
75
|
-
jurisdictions: ${JSON.stringify(values.jurisdictions)},
|
|
25
|
+
var init_exports = /* @__PURE__ */ __exportAll({
|
|
26
|
+
getOpenPolicyTemplate: () => getOpenPolicyTemplate,
|
|
27
|
+
initCommand: () => initCommand
|
|
76
28
|
});
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return `import { defineTermsOfService } from "@openpolicy/sdk";
|
|
29
|
+
function getOpenPolicyTemplate(companyName, contactEmail, policies) {
|
|
30
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
31
|
+
return `import { defineConfig } from "@openpolicy/sdk";
|
|
81
32
|
|
|
82
|
-
export default
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
eligibility: {
|
|
94
|
-
minimumAge: 13,
|
|
95
|
-
},
|
|
96
|
-
accounts: {
|
|
97
|
-
registrationRequired: false,
|
|
98
|
-
userResponsibleForCredentials: true,
|
|
99
|
-
companyCanTerminate: true,
|
|
100
|
-
},
|
|
101
|
-
prohibitedUses: [
|
|
102
|
-
"Violating any applicable laws or regulations",
|
|
103
|
-
"Infringing on intellectual property rights",
|
|
104
|
-
"Transmitting harmful or malicious content",
|
|
105
|
-
],
|
|
106
|
-
intellectualProperty: {
|
|
107
|
-
companyOwnsService: true,
|
|
108
|
-
usersMayNotCopy: true,
|
|
109
|
-
},
|
|
110
|
-
termination: {
|
|
111
|
-
companyCanTerminate: true,
|
|
112
|
-
userCanTerminate: true,
|
|
113
|
-
},
|
|
114
|
-
disclaimers: {
|
|
115
|
-
serviceProvidedAsIs: true,
|
|
116
|
-
noWarranties: true,
|
|
117
|
-
},
|
|
118
|
-
limitationOfLiability: {
|
|
119
|
-
excludesIndirectDamages: true,
|
|
120
|
-
},
|
|
121
|
-
governingLaw: {
|
|
122
|
-
jurisdiction: ${JSON.stringify(values.jurisdiction)},
|
|
123
|
-
},
|
|
124
|
-
changesPolicy: {
|
|
125
|
-
noticeMethod: "email or prominent notice on our website",
|
|
126
|
-
noticePeriodDays: 30,
|
|
127
|
-
},
|
|
128
|
-
});
|
|
129
|
-
`;
|
|
130
|
-
}
|
|
131
|
-
var DATA_CATEGORY_MAP, initCommand;
|
|
132
|
-
var init_init = __esmMin((() => {
|
|
133
|
-
DATA_CATEGORY_MAP = {
|
|
134
|
-
name: {
|
|
135
|
-
group: "Personal Information",
|
|
136
|
-
label: "Full name"
|
|
33
|
+
export default defineConfig({
|
|
34
|
+
company: {
|
|
35
|
+
name: "${companyName}",
|
|
36
|
+
legalName: "${companyName}",
|
|
37
|
+
address: "",
|
|
38
|
+
contact: "${contactEmail}",
|
|
39
|
+
},${policies.includes("privacy") ? `
|
|
40
|
+
privacy: {
|
|
41
|
+
effectiveDate: "${today}",
|
|
42
|
+
dataCollected: {
|
|
43
|
+
"Personal Information": ["Email address"],
|
|
137
44
|
},
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
45
|
+
legalBasis: "Legitimate interests",
|
|
46
|
+
retention: {
|
|
47
|
+
"All personal data": "As long as necessary for the purposes described in this policy",
|
|
141
48
|
},
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
49
|
+
cookies: { essential: true, analytics: false, marketing: false },
|
|
50
|
+
thirdParties: [],
|
|
51
|
+
userRights: ["access", "erasure"],
|
|
52
|
+
jurisdictions: ["us"],
|
|
53
|
+
},` : ""}${policies.includes("terms") ? `
|
|
54
|
+
terms: {
|
|
55
|
+
effectiveDate: "${today}",
|
|
56
|
+
acceptance: { methods: ["using the service", "creating an account"] },
|
|
57
|
+
eligibility: { minimumAge: 13 },
|
|
58
|
+
accounts: {
|
|
59
|
+
registrationRequired: false,
|
|
60
|
+
userResponsibleForCredentials: true,
|
|
61
|
+
companyCanTerminate: true,
|
|
145
62
|
},
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
63
|
+
prohibitedUses: [
|
|
64
|
+
"Violating any applicable laws or regulations",
|
|
65
|
+
"Infringing on intellectual property rights",
|
|
66
|
+
"Transmitting harmful or malicious content",
|
|
67
|
+
],
|
|
68
|
+
intellectualProperty: { companyOwnsService: true, usersMayNotCopy: true },
|
|
69
|
+
termination: { companyCanTerminate: true, userCanTerminate: true },
|
|
70
|
+
disclaimers: { serviceProvidedAsIs: true, noWarranties: true },
|
|
71
|
+
limitationOfLiability: { excludesIndirectDamages: true },
|
|
72
|
+
governingLaw: { jurisdiction: "Delaware, USA" },
|
|
73
|
+
changesPolicy: {
|
|
74
|
+
noticeMethod: "email or prominent notice on our website",
|
|
75
|
+
noticePeriodDays: 30,
|
|
149
76
|
},
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
},
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
};
|
|
77
|
+
},` : ""}${policies.includes("cookie") ? `
|
|
78
|
+
cookie: {
|
|
79
|
+
effectiveDate: "${today}",
|
|
80
|
+
cookies: { essential: true, analytics: false, functional: false, marketing: false },
|
|
81
|
+
jurisdictions: ["us"],
|
|
82
|
+
},` : ""}
|
|
83
|
+
});
|
|
84
|
+
`;
|
|
85
|
+
}
|
|
86
|
+
var initCommand;
|
|
87
|
+
var init_init = __esmMin((() => {
|
|
163
88
|
initCommand = defineCommand({
|
|
164
89
|
meta: {
|
|
165
90
|
name: "init",
|
|
166
91
|
description: "Interactively create a policy config file"
|
|
167
92
|
},
|
|
168
|
-
args: {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
},
|
|
174
|
-
yes: {
|
|
175
|
-
type: "boolean",
|
|
176
|
-
description: "Skip prompts and use defaults (CI mode)",
|
|
177
|
-
default: false
|
|
178
|
-
},
|
|
179
|
-
type: {
|
|
180
|
-
type: "string",
|
|
181
|
-
description: "Policy type: \"privacy\" or \"terms\"",
|
|
182
|
-
default: "privacy"
|
|
183
|
-
}
|
|
184
|
-
},
|
|
93
|
+
args: { out: {
|
|
94
|
+
type: "string",
|
|
95
|
+
description: "Output path for generated config",
|
|
96
|
+
default: "openpolicy.ts"
|
|
97
|
+
} },
|
|
185
98
|
async run({ args }) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
consola.
|
|
189
|
-
const companyName = String(await consola.prompt("Company name?", {
|
|
190
|
-
type: "text",
|
|
191
|
-
cancel: "reject"
|
|
192
|
-
}));
|
|
193
|
-
const legalName = String(await consola.prompt("Legal entity name?", {
|
|
194
|
-
type: "text",
|
|
195
|
-
cancel: "reject",
|
|
196
|
-
initial: companyName
|
|
197
|
-
}));
|
|
198
|
-
const address = String(await consola.prompt("Company address?", {
|
|
99
|
+
consola.box("Welcome to OpenPolicy\nGenerate privacy policies, terms of service, and cookie policies from a single config file.");
|
|
100
|
+
consola.start("Let's get you set up.");
|
|
101
|
+
const source = getOpenPolicyTemplate(String(await consola.prompt("Company name?", {
|
|
199
102
|
type: "text",
|
|
200
103
|
cancel: "reject"
|
|
201
|
-
}))
|
|
202
|
-
const contact = String(await consola.prompt(policyType === "terms" ? "Legal contact email?" : "Privacy contact email?", {
|
|
104
|
+
})), String(await consola.prompt("Contact email?", {
|
|
203
105
|
type: "text",
|
|
204
106
|
cancel: "reject"
|
|
107
|
+
})), await consola.prompt("Which policies do you need?", {
|
|
108
|
+
type: "multiselect",
|
|
109
|
+
cancel: "reject",
|
|
110
|
+
options: [
|
|
111
|
+
"privacy",
|
|
112
|
+
"terms",
|
|
113
|
+
"cookie"
|
|
114
|
+
]
|
|
205
115
|
}));
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
companyName,
|
|
209
|
-
legalName,
|
|
210
|
-
address,
|
|
211
|
-
contact,
|
|
212
|
-
jurisdiction: String(await consola.prompt("Governing law jurisdiction? (e.g. Delaware, USA)", {
|
|
213
|
-
type: "text",
|
|
214
|
-
cancel: "reject",
|
|
215
|
-
initial: "Delaware, USA"
|
|
216
|
-
}))
|
|
217
|
-
});
|
|
218
|
-
else {
|
|
219
|
-
const jurisdictionChoice = String(await consola.prompt("Jurisdiction?", {
|
|
220
|
-
type: "select",
|
|
221
|
-
cancel: "reject",
|
|
222
|
-
options: [
|
|
223
|
-
"gdpr",
|
|
224
|
-
"ccpa",
|
|
225
|
-
"both"
|
|
226
|
-
]
|
|
227
|
-
}));
|
|
228
|
-
const dataCategories = await consola.prompt("Data categories collected?", {
|
|
229
|
-
type: "multiselect",
|
|
230
|
-
cancel: "reject",
|
|
231
|
-
options: [
|
|
232
|
-
"name",
|
|
233
|
-
"email",
|
|
234
|
-
"ip_address",
|
|
235
|
-
"device_info",
|
|
236
|
-
"location",
|
|
237
|
-
"payment_info",
|
|
238
|
-
"usage_data"
|
|
239
|
-
]
|
|
240
|
-
});
|
|
241
|
-
const hasCookies = Boolean(await consola.prompt("Does your app use cookies?", {
|
|
242
|
-
type: "confirm",
|
|
243
|
-
cancel: "reject",
|
|
244
|
-
initial: true
|
|
245
|
-
}));
|
|
246
|
-
const jurisdictions = toJurisdictions(jurisdictionChoice);
|
|
247
|
-
const dataCollected = toDataCollected(dataCategories);
|
|
248
|
-
const userRights = toUserRights(jurisdictions);
|
|
249
|
-
source = renderPrivacyConfig({
|
|
250
|
-
companyName,
|
|
251
|
-
legalName,
|
|
252
|
-
address,
|
|
253
|
-
contact,
|
|
254
|
-
jurisdictions,
|
|
255
|
-
dataCollected,
|
|
256
|
-
legalBasis: jurisdictions.includes("eu") ? "Legitimate interests and consent" : "",
|
|
257
|
-
hasCookies,
|
|
258
|
-
userRights
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
const outPath = resolve(args.out || defaultOut);
|
|
262
|
-
await Bun.write(outPath, source);
|
|
116
|
+
const outPath = resolve(args.out);
|
|
117
|
+
await writeFile(outPath, source);
|
|
263
118
|
consola.success(`Config written to ${outPath}`);
|
|
119
|
+
consola.info(`Open ${outPath} and fill in your company's details — address, legal name, and any policy-specific fields.`);
|
|
120
|
+
consola.info(`When you're ready, run:\n\n openpolicy generate ${args.out}\n\nto compile your policies to HTML or Markdown.`);
|
|
264
121
|
}
|
|
265
122
|
});
|
|
266
123
|
}));
|
|
267
124
|
//#endregion
|
|
268
|
-
//#region src/utils/detect-type.ts
|
|
269
|
-
function detectType(explicitType, configPath) {
|
|
270
|
-
if (explicitType === "privacy" || explicitType === "terms") return explicitType;
|
|
271
|
-
return configPath.toLowerCase().includes("terms") ? "terms" : "privacy";
|
|
272
|
-
}
|
|
273
|
-
var init_detect_type = __esmMin((() => {}));
|
|
274
|
-
//#endregion
|
|
275
125
|
//#region src/utils/load-config.ts
|
|
276
|
-
async function loadConfig(configPath) {
|
|
126
|
+
async function loadConfig(configPath, bustCache = false) {
|
|
277
127
|
const absPath = resolve(configPath);
|
|
278
128
|
if (!existsSync(absPath)) {
|
|
279
129
|
consola.error(`Config file not found: ${absPath}`);
|
|
280
130
|
process.exit(1);
|
|
281
131
|
}
|
|
132
|
+
const importPath = bustCache ? `${absPath}?t=${Date.now()}` : absPath;
|
|
282
133
|
let mod;
|
|
283
134
|
try {
|
|
284
|
-
mod = await import(
|
|
135
|
+
mod = await import(importPath);
|
|
285
136
|
} catch (err) {
|
|
286
137
|
consola.error(`Failed to load config: ${absPath}`);
|
|
287
138
|
consola.error(err);
|
|
@@ -298,9 +149,31 @@ var init_load_config = __esmMin((() => {}));
|
|
|
298
149
|
//#endregion
|
|
299
150
|
//#region src/commands/generate.ts
|
|
300
151
|
var generate_exports = /* @__PURE__ */ __exportAll({ generateCommand: () => generateCommand });
|
|
152
|
+
async function generateFromConfig(configPath, formats, outDir, bustCache = false) {
|
|
153
|
+
const config = await loadConfig(configPath, bustCache);
|
|
154
|
+
if (isOpenPolicyConfig(config)) {
|
|
155
|
+
const inputs = expandOpenPolicyConfig(config);
|
|
156
|
+
if (inputs.length === 0) {
|
|
157
|
+
consola.warn(`Unified config has no privacy or terms sections: ${configPath}`);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
await mkdir(outDir, { recursive: true });
|
|
161
|
+
for (const input of inputs) {
|
|
162
|
+
const outputFilename = input.type === "terms" ? "terms-of-service" : input.type === "cookie" ? "cookie-policy" : "privacy-policy";
|
|
163
|
+
consola.start(`Generating ${input.type} policy from ${configPath} → formats: ${formats.join(", ")}`);
|
|
164
|
+
const results = compilePolicy(input, { formats });
|
|
165
|
+
for (const result of results) {
|
|
166
|
+
const outPath = join(outDir, `${outputFilename}.${result.format === "markdown" ? "md" : result.format}`);
|
|
167
|
+
await writeFile(outPath, result.content, "utf-8");
|
|
168
|
+
consola.success(`Written: ${outPath}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
throw new Error(`[openpolicy] Config must use defineConfig() (OpenPolicyConfig): ${configPath}`);
|
|
174
|
+
}
|
|
301
175
|
var generateCommand;
|
|
302
176
|
var init_generate = __esmMin((() => {
|
|
303
|
-
init_detect_type();
|
|
304
177
|
init_load_config();
|
|
305
178
|
generateCommand = defineCommand({
|
|
306
179
|
meta: {
|
|
@@ -310,8 +183,8 @@ var init_generate = __esmMin((() => {
|
|
|
310
183
|
args: {
|
|
311
184
|
config: {
|
|
312
185
|
type: "positional",
|
|
313
|
-
description: "Path
|
|
314
|
-
default: "./
|
|
186
|
+
description: "Path to policy config file",
|
|
187
|
+
default: "./openpolicy.ts"
|
|
315
188
|
},
|
|
316
189
|
format: {
|
|
317
190
|
type: "string",
|
|
@@ -323,44 +196,33 @@ var init_generate = __esmMin((() => {
|
|
|
323
196
|
description: "Output directory",
|
|
324
197
|
default: "./output"
|
|
325
198
|
},
|
|
326
|
-
|
|
327
|
-
type: "
|
|
328
|
-
description: "
|
|
329
|
-
default:
|
|
199
|
+
watch: {
|
|
200
|
+
type: "boolean",
|
|
201
|
+
description: "Watch config file and regenerate on changes",
|
|
202
|
+
default: false
|
|
330
203
|
}
|
|
331
204
|
},
|
|
332
205
|
async run({ args }) {
|
|
333
|
-
const formats =
|
|
334
|
-
const outDir = args.out
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
if (!await access(configPath).then(() => true).catch(() => false)) {
|
|
339
|
-
if (isMulti) {
|
|
340
|
-
consola.warn(`Config not found, skipping: ${configPath}`);
|
|
341
|
-
continue;
|
|
342
|
-
}
|
|
343
|
-
throw new Error(`Config not found: ${configPath}`);
|
|
344
|
-
}
|
|
345
|
-
const policyType = detectType(args.type || void 0, configPath);
|
|
346
|
-
consola.start(`Generating ${policyType} policy from ${configPath} → formats: ${formats.join(", ")}`);
|
|
347
|
-
const config = await loadConfig(configPath);
|
|
348
|
-
const outputFilename = policyType === "terms" ? "terms-of-service" : "privacy-policy";
|
|
349
|
-
const results = compilePolicy(policyType === "terms" ? {
|
|
350
|
-
type: "terms",
|
|
351
|
-
...config
|
|
352
|
-
} : {
|
|
353
|
-
type: "privacy",
|
|
354
|
-
...config
|
|
355
|
-
}, { formats });
|
|
356
|
-
await mkdir(outDir, { recursive: true });
|
|
357
|
-
for (const result of results) {
|
|
358
|
-
const outPath = join(outDir, `${outputFilename}.${result.format === "markdown" ? "md" : result.format}`);
|
|
359
|
-
await writeFile(outPath, result.content, "utf-8");
|
|
360
|
-
consola.success(`Written: ${outPath}`);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
206
|
+
const formats = args.format.split(",").map((f) => f.trim()).filter(Boolean);
|
|
207
|
+
const outDir = args.out;
|
|
208
|
+
const configPath = args.config;
|
|
209
|
+
if (!existsSync(configPath)) throw new Error(`Config not found: ${configPath}`);
|
|
210
|
+
await generateFromConfig(configPath, formats, outDir);
|
|
363
211
|
consola.success(`Policy generation complete → ${outDir}`);
|
|
212
|
+
if (args.watch) {
|
|
213
|
+
consola.info("Watching for changes...");
|
|
214
|
+
let debounceTimer = null;
|
|
215
|
+
watch(configPath, () => {
|
|
216
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
217
|
+
debounceTimer = setTimeout(async () => {
|
|
218
|
+
try {
|
|
219
|
+
await generateFromConfig(configPath, formats, outDir, true);
|
|
220
|
+
} catch (err) {
|
|
221
|
+
consola.error(`Error regenerating ${configPath}:`, err);
|
|
222
|
+
}
|
|
223
|
+
}, 100);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
364
226
|
}
|
|
365
227
|
});
|
|
366
228
|
}));
|
|
@@ -369,7 +231,6 @@ var init_generate = __esmMin((() => {
|
|
|
369
231
|
var validate_exports = /* @__PURE__ */ __exportAll({ validateCommand: () => validateCommand });
|
|
370
232
|
var validateCommand;
|
|
371
233
|
var init_validate = __esmMin((() => {
|
|
372
|
-
init_detect_type();
|
|
373
234
|
init_load_config();
|
|
374
235
|
validateCommand = defineCommand({
|
|
375
236
|
meta: {
|
|
@@ -386,31 +247,29 @@ var init_validate = __esmMin((() => {
|
|
|
386
247
|
type: "string",
|
|
387
248
|
description: "Jurisdiction to validate against: gdpr, ccpa, or all",
|
|
388
249
|
default: "all"
|
|
389
|
-
},
|
|
390
|
-
type: {
|
|
391
|
-
type: "string",
|
|
392
|
-
description: "Policy type: \"privacy\" or \"terms\" (auto-detected from filename if omitted)",
|
|
393
|
-
default: ""
|
|
394
250
|
}
|
|
395
251
|
},
|
|
396
252
|
async run({ args }) {
|
|
397
253
|
const configPath = args.config ?? "./policy.config.ts";
|
|
398
|
-
const policyType = detectType(args.type || void 0, configPath);
|
|
399
|
-
consola.start(`Validating ${policyType} policy: ${configPath}`);
|
|
400
254
|
const config = await loadConfig(configPath);
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
255
|
+
if (!isOpenPolicyConfig(config)) throw new Error(`[openpolicy] Config must use defineConfig() (OpenPolicyConfig): ${configPath}`);
|
|
256
|
+
const inputs = expandOpenPolicyConfig(config);
|
|
257
|
+
let totalErrors = 0;
|
|
258
|
+
for (const input of inputs) {
|
|
259
|
+
consola.start(`Validating ${input.type} policy: ${configPath}`);
|
|
260
|
+
const issues = input.type === "terms" ? validateTermsOfService(input) : input.type === "cookie" ? validateCookiePolicy(input) : validatePrivacyPolicy(input);
|
|
261
|
+
if (issues.length === 0) {
|
|
262
|
+
consola.success(`${input.type}: no issues found.`);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
for (const issue of issues) if (issue.level === "error") consola.error(issue.message);
|
|
266
|
+
else consola.warn(issue.message);
|
|
267
|
+
const errors = issues.filter((i) => i.level === "error");
|
|
268
|
+
totalErrors += errors.length;
|
|
269
|
+
if (errors.length > 0) consola.fail(`${input.type}: validation failed with ${errors.length} error(s).`);
|
|
270
|
+
else consola.success(`${input.type}: validation passed with warnings.`);
|
|
412
271
|
}
|
|
413
|
-
|
|
272
|
+
if (totalErrors > 0) process.exit(1);
|
|
414
273
|
}
|
|
415
274
|
});
|
|
416
275
|
}));
|
|
@@ -419,7 +278,7 @@ var init_validate = __esmMin((() => {
|
|
|
419
278
|
runMain(defineCommand({
|
|
420
279
|
meta: {
|
|
421
280
|
name: "openpolicy",
|
|
422
|
-
version
|
|
281
|
+
version,
|
|
423
282
|
description: "Generate and validate privacy policy documents"
|
|
424
283
|
},
|
|
425
284
|
subCommands: {
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openpolicy/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI for generating and validating OpenPolicy privacy policy documents",
|
|
6
|
-
"license": "
|
|
6
|
+
"license": "GPL-3.0-only",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "https://github.com/jamiedavenport/openpolicy",
|