@openpolicy/core 0.0.2 → 0.0.3

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/package.json CHANGED
@@ -1,12 +1,27 @@
1
1
  {
2
2
  "name": "@openpolicy/core",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
+ "description": "Core package for OpenPolicy",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/openpolicy/openpolicy",
10
+ "directory": "packages/core"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "README.md"
15
+ ],
5
16
  "exports": {
6
- ".": "./src/index.ts"
17
+ ".": {
18
+ "import": "./dist/index.js",
19
+ "types": "./dist/index.d.ts"
20
+ }
7
21
  },
8
22
  "scripts": {
9
- "build": "bun build ./src/index.ts --outdir dist",
23
+ "dev": "bun build ./src/index.ts --outdir dist --watch",
24
+ "build": "bun build ./src/index.ts --outdir dist && tsc --emitDeclarationOnly",
10
25
  "check-types": "tsc --noEmit",
11
26
  "test": "bun test"
12
27
  },
@@ -1,6 +0,0 @@
1
-
2
- $ bun build ./src/index.ts --outdir dist
3
- Bundled 14 modules in 4ms
4
-
5
- index.js 9.17 KB (entry point)
6
-
@@ -1 +0,0 @@
1
- $ tsc --noEmit
@@ -1,18 +0,0 @@
1
- $ bun test
2
- bun test v1.2.21 (7c45ed97)
3
-
4
- src/index.test.ts:
5
- (pass) compilePolicy routes privacy input to markdown [1.32ms]
6
-
7
- src/privacy.test.ts:
8
- (pass) default markdown output contains company name [0.07ms]
9
- (pass) US-only config: no legal-basis or gdpr-supplement sections [0.04ms]
10
- (pass) EU config: legal-basis + gdpr-supplement sections present [0.19ms]
11
- (pass) CA config: ccpa-supplement section present [0.03ms]
12
- (pass) multi-jurisdiction (us + eu + ca): all jurisdiction sections present [0.01ms]
13
- (pass) requesting pdf format throws 'not yet implemented' [0.42ms]
14
-
15
- 7 pass
16
- 0 fail
17
- 17 expect() calls
18
- Ran 7 tests across 2 files. [68.00ms]
package/CHANGELOG.md DELETED
@@ -1,7 +0,0 @@
1
- # @openpolicy/core
2
-
3
- ## 0.0.2
4
-
5
- ### Patch Changes
6
-
7
- - Fixes
package/src/index.test.ts DELETED
@@ -1,28 +0,0 @@
1
- import { expect, test } from "bun:test";
2
- import { compilePolicy } from "./index";
3
- import type { PolicyInput } from "./types";
4
-
5
- const input: PolicyInput = {
6
- type: "privacy",
7
- effectiveDate: "2026-01-01",
8
- company: {
9
- name: "Acme Inc.",
10
- legalName: "Acme Corporation",
11
- address: "123 Main St, Springfield, USA",
12
- contact: "privacy@acme.com",
13
- },
14
- dataCollected: { "Account Information": ["Name", "Email"] },
15
- legalBasis: "Legitimate interests",
16
- retention: { "Account data": "Until deletion" },
17
- cookies: { essential: true, analytics: false, marketing: false },
18
- thirdParties: [],
19
- userRights: ["access"],
20
- jurisdictions: ["us"],
21
- };
22
-
23
- test("compilePolicy routes privacy input to markdown", () => {
24
- const results = compilePolicy(input);
25
- expect(results).toBeArray();
26
- expect(results[0]?.format).toBe("markdown");
27
- expect(results[0]?.content).toContain("Acme Inc.");
28
- });
package/src/index.ts DELETED
@@ -1,23 +0,0 @@
1
- export { compilePrivacyPolicy } from "./privacy";
2
- export type {
3
- CompileOptions,
4
- Jurisdiction,
5
- OutputFormat,
6
- PolicyInput,
7
- PolicySection,
8
- PrivacyPolicyConfig,
9
- ValidationIssue,
10
- } from "./types";
11
- export { validatePrivacyPolicy } from "./validate";
12
-
13
- import { compilePrivacyPolicy } from "./privacy";
14
- import type { CompileOptions, PolicyInput } from "./types";
15
-
16
- export function compilePolicy(input: PolicyInput, options?: CompileOptions) {
17
- switch (input.type) {
18
- case "privacy": {
19
- const { type: _, ...config } = input;
20
- return compilePrivacyPolicy(config, options);
21
- }
22
- }
23
- }
@@ -1,80 +0,0 @@
1
- import { expect, test } from "bun:test";
2
- import { compilePrivacyPolicy } from "./privacy";
3
- import type { PrivacyPolicyConfig } from "./types";
4
-
5
- const baseConfig: PrivacyPolicyConfig = {
6
- effectiveDate: "2026-01-01",
7
- company: {
8
- name: "Acme Inc.",
9
- legalName: "Acme Corporation",
10
- address: "123 Main St, Springfield, USA",
11
- contact: "privacy@acme.com",
12
- },
13
- dataCollected: {
14
- "Account Information": ["Name", "Email address"],
15
- "Usage Data": ["IP address", "Browser type"],
16
- },
17
- legalBasis: "Legitimate interests and consent",
18
- retention: {
19
- "Account data": "Until account deletion",
20
- "Usage logs": "90 days",
21
- },
22
- cookies: { essential: true, analytics: true, marketing: false },
23
- thirdParties: [{ name: "Stripe", purpose: "Payment processing" }],
24
- userRights: ["access", "erasure", "portability"],
25
- jurisdictions: ["us"],
26
- };
27
-
28
- test("default markdown output contains company name", () => {
29
- const results = compilePrivacyPolicy(baseConfig);
30
- expect(results[0]?.format).toBe("markdown");
31
- expect(results[0]?.content).toContain("Acme Inc.");
32
- });
33
-
34
- test("US-only config: no legal-basis or gdpr-supplement sections", () => {
35
- const results = compilePrivacyPolicy(baseConfig);
36
- const ids = results[0]?.sections.map((s) => s.id);
37
- expect(ids).not.toContain("legal-basis");
38
- expect(ids).not.toContain("gdpr-supplement");
39
- expect(ids).not.toContain("ccpa-supplement");
40
- });
41
-
42
- test("EU config: legal-basis + gdpr-supplement sections present", () => {
43
- const config: PrivacyPolicyConfig = { ...baseConfig, jurisdictions: ["eu"] };
44
- const results = compilePrivacyPolicy(config);
45
- const ids = results[0]?.sections.map((s) => s.id);
46
- expect(ids).toContain("legal-basis");
47
- expect(ids).toContain("gdpr-supplement");
48
- });
49
-
50
- test("CA config: ccpa-supplement section present", () => {
51
- const config: PrivacyPolicyConfig = { ...baseConfig, jurisdictions: ["ca"] };
52
- const results = compilePrivacyPolicy(config);
53
- const ids = results[0]?.sections.map((s) => s.id);
54
- expect(ids).toContain("ccpa-supplement");
55
- expect(ids).not.toContain("legal-basis");
56
- expect(ids).not.toContain("gdpr-supplement");
57
- });
58
-
59
- test("multi-jurisdiction (us + eu + ca): all jurisdiction sections present", () => {
60
- const config: PrivacyPolicyConfig = {
61
- ...baseConfig,
62
- jurisdictions: ["us", "eu", "ca"],
63
- };
64
- const results = compilePrivacyPolicy(config);
65
-
66
- if (results.length === 0) {
67
- throw new Error("No results");
68
- }
69
-
70
- const ids = results[0]?.sections.map((s) => s.id);
71
- expect(ids).toContain("legal-basis");
72
- expect(ids).toContain("gdpr-supplement");
73
- expect(ids).toContain("ccpa-supplement");
74
- });
75
-
76
- test("requesting pdf format throws 'not yet implemented'", () => {
77
- expect(() => compilePrivacyPolicy(baseConfig, { formats: ["pdf"] })).toThrow(
78
- "not yet implemented",
79
- );
80
- });
package/src/privacy.ts DELETED
@@ -1,54 +0,0 @@
1
- import { renderMarkdown } from "./renderers/markdown";
2
- import { buildCcpaSupplement } from "./templates/privacy/ccpa-supplement";
3
- import { buildContact } from "./templates/privacy/contact";
4
- import { buildCookies } from "./templates/privacy/cookies";
5
- import { buildDataCollected } from "./templates/privacy/data-collected";
6
- import { buildDataRetention } from "./templates/privacy/data-retention";
7
- import { buildGdprSupplement } from "./templates/privacy/gdpr-supplement";
8
- import { buildIntroduction } from "./templates/privacy/introduction";
9
- import { buildLegalBasis } from "./templates/privacy/legal-basis";
10
- import { buildThirdParties } from "./templates/privacy/third-parties";
11
- import { buildUserRights } from "./templates/privacy/user-rights";
12
- import type {
13
- CompileOptions,
14
- OutputFormat,
15
- PolicySection,
16
- PrivacyPolicyConfig,
17
- } from "./types";
18
-
19
- const SECTION_BUILDERS: ((
20
- config: PrivacyPolicyConfig,
21
- ) => PolicySection | null)[] = [
22
- buildIntroduction,
23
- buildDataCollected,
24
- buildLegalBasis,
25
- buildDataRetention,
26
- buildCookies,
27
- buildThirdParties,
28
- buildUserRights,
29
- buildGdprSupplement,
30
- buildCcpaSupplement,
31
- buildContact,
32
- ];
33
-
34
- export function compilePrivacyPolicy(
35
- config: PrivacyPolicyConfig,
36
- options: CompileOptions = { formats: ["markdown"] },
37
- ): { format: OutputFormat; content: string; sections: PolicySection[] }[] {
38
- const sections = SECTION_BUILDERS.map((builder) => builder(config)).filter(
39
- (s): s is PolicySection => s !== null,
40
- );
41
-
42
- return options.formats.map((format) => {
43
- switch (format) {
44
- case "markdown":
45
- return { format, content: renderMarkdown(sections), sections };
46
- case "pdf":
47
- throw new Error("pdf format is not yet implemented");
48
- case "jsx":
49
- throw new Error("jsx format is not yet implemented");
50
- default:
51
- throw new Error(`Unsupported format: ${format}`);
52
- }
53
- });
54
- }
@@ -1,7 +0,0 @@
1
- import type { PolicySection } from "../types";
2
-
3
- export function renderMarkdown(sections: PolicySection[]): string {
4
- return sections
5
- .map((section) => `## ${section.title}\n\n${section.body}`)
6
- .join("\n\n---\n\n");
7
- }
@@ -1,23 +0,0 @@
1
- import type { PolicySection, PrivacyPolicyConfig } from "../../types";
2
-
3
- export function buildCcpaSupplement(
4
- config: PrivacyPolicyConfig,
5
- ): PolicySection | null {
6
- if (!config.jurisdictions.includes("ca")) return null;
7
-
8
- return {
9
- id: "ccpa-supplement",
10
- title: "California Privacy Rights (CCPA)",
11
- body: `This section applies to California residents under the California Consumer Privacy Act (CCPA) and the California Privacy Rights Act (CPRA).
12
-
13
- **Categories of Personal Information Collected:** We collect the categories of personal information described in the "Information We Collect" section above.
14
-
15
- **Your California Rights:**
16
- - **Right to Know:** You may request information about the personal information we have collected about you, including the categories of sources, the business purpose for collection, and the categories of third parties with whom we share information.
17
- - **Right to Delete:** You may request deletion of personal information we have collected from you, subject to certain exceptions.
18
- - **Right to Opt-Out:** You may opt out of the sale or sharing of your personal information.
19
- - **Right to Non-Discrimination:** We will not discriminate against you for exercising your California privacy rights.
20
-
21
- To exercise your rights, contact us at ${config.company.contact}.`,
22
- };
23
- }
@@ -1,14 +0,0 @@
1
- import type { PolicySection, PrivacyPolicyConfig } from "../../types";
2
-
3
- export function buildContact(config: PrivacyPolicyConfig): PolicySection {
4
- return {
5
- id: "contact",
6
- title: "Contact Us",
7
- body: `If you have questions or concerns about this Privacy Policy or our data practices, please contact us:
8
-
9
- **${config.company.legalName}**
10
- ${config.company.address}
11
-
12
- Email: ${config.company.contact}`,
13
- };
14
- }
@@ -1,28 +0,0 @@
1
- import type { PolicySection, PrivacyPolicyConfig } from "../../types";
2
-
3
- export function buildCookies(config: PrivacyPolicyConfig): PolicySection {
4
- const enabled: string[] = [];
5
- if (config.cookies.essential)
6
- enabled.push(
7
- "**Essential cookies** — required for the service to function",
8
- );
9
- if (config.cookies.analytics)
10
- enabled.push(
11
- "**Analytics cookies** — help us understand how visitors interact with our service",
12
- );
13
- if (config.cookies.marketing)
14
- enabled.push(
15
- "**Marketing cookies** — used to deliver relevant advertisements",
16
- );
17
-
18
- const body =
19
- enabled.length > 0
20
- ? `We use the following types of cookies and tracking technologies:\n\n${enabled.map((e) => `- ${e}`).join("\n")}`
21
- : "We do not use cookies or tracking technologies on our service.";
22
-
23
- return {
24
- id: "cookies",
25
- title: "Cookies and Tracking",
26
- body,
27
- };
28
- }
@@ -1,15 +0,0 @@
1
- import type { PolicySection, PrivacyPolicyConfig } from "../../types";
2
-
3
- export function buildDataCollected(config: PrivacyPolicyConfig): PolicySection {
4
- const entries = Object.entries(config.dataCollected);
5
- const lines = entries.map(([category, items]) => {
6
- const itemList = items.map((item) => ` - ${item}`).join("\n");
7
- return `**${category}:**\n${itemList}`;
8
- });
9
-
10
- return {
11
- id: "data-collected",
12
- title: "Information We Collect",
13
- body: `We collect the following categories of information:\n\n${lines.join("\n\n")}`,
14
- };
15
- }
@@ -1,14 +0,0 @@
1
- import type { PolicySection, PrivacyPolicyConfig } from "../../types";
2
-
3
- export function buildDataRetention(config: PrivacyPolicyConfig): PolicySection {
4
- const entries = Object.entries(config.retention);
5
- const lines = entries.map(
6
- ([category, period]) => `- **${category}:** ${period}`,
7
- );
8
-
9
- return {
10
- id: "data-retention",
11
- title: "Data Retention",
12
- body: `We retain your information for the following periods:\n\n${lines.join("\n")}`,
13
- };
14
- }
@@ -1,19 +0,0 @@
1
- import type { PolicySection, PrivacyPolicyConfig } from "../../types";
2
-
3
- export function buildGdprSupplement(
4
- config: PrivacyPolicyConfig,
5
- ): PolicySection | null {
6
- if (!config.jurisdictions.includes("eu")) return null;
7
-
8
- return {
9
- id: "gdpr-supplement",
10
- title: "GDPR Supplemental Disclosures",
11
- body: `This section applies to individuals in the European Economic Area (EEA) under the General Data Protection Regulation (GDPR).
12
-
13
- **Data Controller:** ${config.company.legalName}, ${config.company.address}
14
-
15
- **Your GDPR Rights:** In addition to the rights listed above, you have the right to lodge a complaint with your local data protection authority if you believe we have not handled your data in accordance with applicable law.
16
-
17
- **International Transfers:** If we transfer your personal data outside the EEA, we ensure adequate safeguards are in place in accordance with GDPR requirements.`,
18
- };
19
- }
@@ -1,13 +0,0 @@
1
- import type { PolicySection, PrivacyPolicyConfig } from "../../types";
2
-
3
- export function buildIntroduction(config: PrivacyPolicyConfig): PolicySection {
4
- return {
5
- id: "introduction",
6
- title: "Introduction",
7
- body: `This Privacy Policy describes how ${config.company.name} ("we", "us", or "our") collects, uses, and shares information about you when you use our services.
8
-
9
- **Effective Date:** ${config.effectiveDate}
10
-
11
- If you have questions about this policy, please contact us at ${config.company.contact}.`,
12
- };
13
- }
@@ -1,13 +0,0 @@
1
- import type { PolicySection, PrivacyPolicyConfig } from "../../types";
2
-
3
- export function buildLegalBasis(
4
- config: PrivacyPolicyConfig,
5
- ): PolicySection | null {
6
- if (!config.jurisdictions.includes("eu")) return null;
7
-
8
- return {
9
- id: "legal-basis",
10
- title: "Legal Basis for Processing",
11
- body: `We process your personal data under the following legal basis:\n\n${config.legalBasis}`,
12
- };
13
- }
@@ -1,18 +0,0 @@
1
- import type { PolicySection, PrivacyPolicyConfig } from "../../types";
2
-
3
- export function buildThirdParties(config: PrivacyPolicyConfig): PolicySection {
4
- const lines = config.thirdParties.map(
5
- (tp) => `- **${tp.name}** — ${tp.purpose}`,
6
- );
7
-
8
- const body =
9
- lines.length > 0
10
- ? `We share data with the following third-party services:\n\n${lines.join("\n")}`
11
- : "We do not share your data with third-party services.";
12
-
13
- return {
14
- id: "third-parties",
15
- title: "Third-Party Services",
16
- body,
17
- };
18
- }
@@ -1,26 +0,0 @@
1
- import type { PolicySection, PrivacyPolicyConfig } from "../../types";
2
-
3
- const RIGHTS_LABELS: Record<string, string> = {
4
- access: "Right to access your personal data",
5
- rectification: "Right to correct inaccurate data",
6
- erasure: "Right to request deletion of your data",
7
- portability: "Right to receive your data in a portable format",
8
- restriction: "Right to restrict how we process your data",
9
- objection: "Right to object to processing",
10
- opt_out_sale: "Right to opt out of the sale of your personal information",
11
- non_discrimination:
12
- "Right to non-discriminatory treatment for exercising your rights",
13
- };
14
-
15
- export function buildUserRights(config: PrivacyPolicyConfig): PolicySection {
16
- const lines = config.userRights.map((right) => {
17
- const label = RIGHTS_LABELS[right] ?? right;
18
- return `- ${label}`;
19
- });
20
-
21
- return {
22
- id: "user-rights",
23
- title: "Your Rights",
24
- body: `You have the following rights regarding your personal data:\n\n${lines.join("\n")}`,
25
- };
26
- }
package/src/types.ts DELETED
@@ -1,35 +0,0 @@
1
- export type OutputFormat = "markdown" | "pdf" | "jsx";
2
-
3
- export type CompileOptions = { formats: OutputFormat[] };
4
-
5
- export type PolicySection = {
6
- id: string;
7
- title: string;
8
- body: string;
9
- };
10
-
11
- export type Jurisdiction = "us" | "eu" | "ca" | "au" | "nz" | "other";
12
-
13
- export type PrivacyPolicyConfig = {
14
- effectiveDate: string;
15
- company: {
16
- name: string;
17
- legalName: string;
18
- address: string;
19
- contact: string;
20
- };
21
- dataCollected: Record<string, string[]>;
22
- legalBasis: string;
23
- retention: Record<string, string>;
24
- cookies: { essential: boolean; analytics: boolean; marketing: boolean };
25
- thirdParties: { name: string; purpose: string }[];
26
- userRights: string[];
27
- jurisdictions: Jurisdiction[];
28
- };
29
-
30
- export type PolicyInput = { type: "privacy" } & PrivacyPolicyConfig;
31
-
32
- export type ValidationIssue = {
33
- level: "error" | "warning";
34
- message: string;
35
- };
package/src/validate.ts DELETED
@@ -1,67 +0,0 @@
1
- import type { PrivacyPolicyConfig, ValidationIssue } from "./types";
2
-
3
- export function validatePrivacyPolicy(
4
- config: PrivacyPolicyConfig,
5
- ): ValidationIssue[] {
6
- const issues: ValidationIssue[] = [];
7
-
8
- // Required fields
9
- if (!config.effectiveDate)
10
- issues.push({ level: "error", message: "effectiveDate is required" });
11
- if (!config.company.name)
12
- issues.push({ level: "error", message: "company.name is required" });
13
- if (!config.company.legalName)
14
- issues.push({ level: "error", message: "company.legalName is required" });
15
- if (!config.company.address)
16
- issues.push({ level: "error", message: "company.address is required" });
17
- if (!config.company.contact)
18
- issues.push({ level: "error", message: "company.contact is required" });
19
- if (Object.keys(config.dataCollected).length === 0)
20
- issues.push({
21
- level: "error",
22
- message: "dataCollected must have at least one entry",
23
- });
24
- if (config.userRights.length === 0)
25
- issues.push({
26
- level: "warning",
27
- message: "userRights is empty — consider listing applicable rights",
28
- });
29
-
30
- // GDPR checks
31
- if (config.jurisdictions.includes("eu")) {
32
- if (!config.legalBasis)
33
- issues.push({ level: "error", message: "GDPR requires a legalBasis" });
34
- for (const right of [
35
- "access",
36
- "rectification",
37
- "erasure",
38
- "portability",
39
- "restriction",
40
- "objection",
41
- ]) {
42
- if (!config.userRights.includes(right))
43
- issues.push({
44
- level: "warning",
45
- message: `GDPR recommends including the "${right}" right`,
46
- });
47
- }
48
- }
49
-
50
- // CCPA checks
51
- if (config.jurisdictions.includes("ca")) {
52
- for (const right of [
53
- "access",
54
- "erasure",
55
- "opt_out_sale",
56
- "non_discrimination",
57
- ]) {
58
- if (!config.userRights.includes(right))
59
- issues.push({
60
- level: "warning",
61
- message: `CCPA recommends including the "${right}" right`,
62
- });
63
- }
64
- }
65
-
66
- return issues;
67
- }
package/tsconfig.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "extends": "@openpolicy/tooling/base",
3
- "include": ["src"]
4
- }