@responsive-privacy/astro 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 draftlab.org
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # @responsive-privacy/astro
2
+
3
+ Astro integration for [Responsive Privacy](https://github.com/draftlab-org/responsive-privacy) — build-time PII protection for static sites. Filters content fields at build time based on a 5-level privacy taxonomy.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add @responsive-privacy/core @responsive-privacy/astro
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ ### 1. Configure field mappings
14
+
15
+ Create `responsive-privacy.config.ts` in your project root:
16
+
17
+ ```typescript
18
+ import { defineConfig } from '@responsive-privacy/core';
19
+
20
+ export default defineConfig({
21
+ collections: {
22
+ team: {
23
+ fields: {
24
+ name: 'ID-01', // Full Name — visible at Level 2+
25
+ photo: 'ID-02', // Photo — visible at Level 2+
26
+ role: 'ID-03', // Job Title — visible at Level 1+
27
+ bio: 'ID-04', // Biography — visible at Level 3+
28
+ email: 'CV-01', // Email — visible at Level 4 only
29
+ department: 'OR-01', // Department — visible at Level 1+
30
+ },
31
+ },
32
+ },
33
+ });
34
+ ```
35
+
36
+ ### 2. Add the integration
37
+
38
+ ```javascript
39
+ // astro.config.mjs
40
+ import { responsivePrivacy } from '@responsive-privacy/astro';
41
+ import privacyConfig from './responsive-privacy.config';
42
+
43
+ export default defineConfig({
44
+ integrations: [responsivePrivacy(privacyConfig)],
45
+ });
46
+ ```
47
+
48
+ ### 3. Filter content in templates
49
+
50
+ ```astro
51
+ ---
52
+ import { getCollection } from 'astro:content';
53
+ import { filterCollection } from '@responsive-privacy/astro/helpers';
54
+ import privacyConfig from '../responsive-privacy.config';
55
+
56
+ const rawTeam = await getCollection('team');
57
+ const team = filterCollection('team', rawTeam, privacyConfig);
58
+ ---
59
+
60
+ {team.map((member) => (
61
+ <div>
62
+ <h3>{member.data.name}</h3>
63
+ {member.data.role && <p>{member.data.role}</p>}
64
+ {member.data.email && <a href={`mailto:${member.data.email}`}>Email</a>}
65
+ </div>
66
+ ))}
67
+ ```
68
+
69
+ ### 4. Build at different levels
70
+
71
+ ```bash
72
+ PRIVACY_LEVEL=4 astro build # Full transparency (default)
73
+ PRIVACY_LEVEL=1 astro build # Role only — names replaced
74
+ PRIVACY_LEVEL=0 astro build # Complete anonymity
75
+ ```
76
+
77
+ ## Template Helpers
78
+
79
+ Imported from `@responsive-privacy/astro/helpers`:
80
+
81
+ - **`filterCollection(name, entries, config, level?)`** — filter all entries in a collection, returns entries with transformed `data` and `_privacy` metadata
82
+ - **`filterEntry(name, entry, config, level?)`** — filter a single entry
83
+ - **`shouldShow(name, fieldName, config, level?)`** — check if a field would be visible (for conditional rendering)
84
+ - **`getPrivacyStatus(config, level?)`** — get current level info for display (level, name, description, isReduced)
85
+
86
+ ## Virtual Module
87
+
88
+ The integration also provides a `virtual:responsive-privacy` module with the config baked in at build time. Add to your `env.d.ts`:
89
+
90
+ ```typescript
91
+ /// <reference types="@responsive-privacy/astro/virtual" />
92
+ ```
93
+
94
+ Then import directly without passing config:
95
+
96
+ ```typescript
97
+ import { PRIVACY_LEVEL, isVisible, isFieldVisible } from 'virtual:responsive-privacy';
98
+ ```
99
+
100
+ ## Integration Options
101
+
102
+ ```typescript
103
+ responsivePrivacy({
104
+ ...config,
105
+ level: 2, // Override level (instead of reading PRIVACY_LEVEL env var)
106
+ verbose: false, // Suppress build log output (default: true)
107
+ })
108
+ ```
109
+
110
+ ## License
111
+
112
+ MIT
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Astro Template Helpers
3
+ *
4
+ * Utility functions for use directly in .astro component frontmatter.
5
+ * These wrap the core transformer to work naturally with Astro content collections.
6
+ *
7
+ * Usage in an .astro file:
8
+ *
9
+ * ---
10
+ * import { filterCollection, filterEntry } from '@responsive-privacy/astro/helpers';
11
+ * import { getCollection } from 'astro:content';
12
+ * import privacyConfig from '../responsive-privacy.config';
13
+ *
14
+ * const rawTeam = await getCollection('team');
15
+ * const team = filterCollection('team', rawTeam, privacyConfig);
16
+ * ---
17
+ */
18
+ import { type ResponsivePrivacyConfig, type PrivacyLevel, type TransformResult } from '@responsive-privacy/core';
19
+ /**
20
+ * Filter a single content collection entry's data fields.
21
+ *
22
+ * Returns the entry with its `data` object transformed according
23
+ * to the current privacy level.
24
+ *
25
+ * @param collectionName - Name of the Astro content collection
26
+ * @param entry - A single collection entry (with `.data` and other Astro fields)
27
+ * @param config - The responsive privacy config
28
+ * @param level - Optional override for privacy level
29
+ */
30
+ export declare function filterEntry<T extends {
31
+ data: Record<string, unknown>;
32
+ }>(collectionName: string, entry: T, config: ResponsivePrivacyConfig, level?: PrivacyLevel): T & {
33
+ _privacy: TransformResult;
34
+ };
35
+ /**
36
+ * Filter an entire content collection.
37
+ *
38
+ * Returns all entries with their data transformed, plus attaches
39
+ * privacy metadata for use in templates.
40
+ *
41
+ * @param collectionName - Name of the Astro content collection
42
+ * @param entries - Array of collection entries from getCollection()
43
+ * @param config - The responsive privacy config
44
+ * @param level - Optional override for privacy level
45
+ */
46
+ export declare function filterCollection<T extends {
47
+ data: Record<string, unknown>;
48
+ }>(collectionName: string, entries: T[], config: ResponsivePrivacyConfig, level?: PrivacyLevel): Array<T & {
49
+ _privacy: TransformResult;
50
+ }>;
51
+ /**
52
+ * Check if a specific field would be visible at the current privacy level.
53
+ *
54
+ * Useful for conditional rendering in templates:
55
+ *
56
+ * {shouldShow('team', 'email', config) && <a href={`mailto:${member.data.email}`}>Email</a>}
57
+ */
58
+ export declare function shouldShow(collectionName: string, fieldName: string, config: ResponsivePrivacyConfig, level?: PrivacyLevel): boolean;
59
+ /**
60
+ * Get the current privacy level and its metadata.
61
+ * Useful for displaying privacy status in templates.
62
+ */
63
+ export declare function getPrivacyStatus(config: ResponsivePrivacyConfig, level?: PrivacyLevel): {
64
+ level: PrivacyLevel;
65
+ name: string;
66
+ description: string;
67
+ isReduced: boolean;
68
+ };
69
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EAEjB,KAAK,eAAe,EAKrB,MAAM,0BAA0B,CAAC;AAElC;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EACrE,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,uBAAuB,EAC/B,KAAK,CAAC,EAAE,YAAY,GACnB,CAAC,GAAG;IAAE,QAAQ,EAAE,eAAe,CAAA;CAAE,CASnC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAC1E,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,CAAC,EAAE,EACZ,MAAM,EAAE,uBAAuB,EAC/B,KAAK,CAAC,EAAE,YAAY,GACnB,KAAK,CAAC,CAAC,GAAG;IAAE,QAAQ,EAAE,eAAe,CAAA;CAAE,CAAC,CAW1C;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,uBAAuB,EAC/B,KAAK,CAAC,EAAE,YAAY,GACnB,OAAO,CAaT;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,uBAAuB,EAAE,KAAK,CAAC,EAAE,YAAY;;;;;EAUrF"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Astro Template Helpers
3
+ *
4
+ * Utility functions for use directly in .astro component frontmatter.
5
+ * These wrap the core transformer to work naturally with Astro content collections.
6
+ *
7
+ * Usage in an .astro file:
8
+ *
9
+ * ---
10
+ * import { filterCollection, filterEntry } from '@responsive-privacy/astro/helpers';
11
+ * import { getCollection } from 'astro:content';
12
+ * import privacyConfig from '../responsive-privacy.config';
13
+ *
14
+ * const rawTeam = await getCollection('team');
15
+ * const team = filterCollection('team', rawTeam, privacyConfig);
16
+ * ---
17
+ */
18
+ import { createContext, readPrivacyLevel, transformEntry, } from '@responsive-privacy/core';
19
+ /**
20
+ * Filter a single content collection entry's data fields.
21
+ *
22
+ * Returns the entry with its `data` object transformed according
23
+ * to the current privacy level.
24
+ *
25
+ * @param collectionName - Name of the Astro content collection
26
+ * @param entry - A single collection entry (with `.data` and other Astro fields)
27
+ * @param config - The responsive privacy config
28
+ * @param level - Optional override for privacy level
29
+ */
30
+ export function filterEntry(collectionName, entry, config, level) {
31
+ const ctx = createContext(config, level ?? readPrivacyLevel());
32
+ const result = transformEntry(collectionName, entry.data, ctx);
33
+ return {
34
+ ...entry,
35
+ data: result.data,
36
+ _privacy: result,
37
+ };
38
+ }
39
+ /**
40
+ * Filter an entire content collection.
41
+ *
42
+ * Returns all entries with their data transformed, plus attaches
43
+ * privacy metadata for use in templates.
44
+ *
45
+ * @param collectionName - Name of the Astro content collection
46
+ * @param entries - Array of collection entries from getCollection()
47
+ * @param config - The responsive privacy config
48
+ * @param level - Optional override for privacy level
49
+ */
50
+ export function filterCollection(collectionName, entries, config, level) {
51
+ const ctx = createContext(config, level ?? readPrivacyLevel());
52
+ return entries.map((entry) => {
53
+ const result = transformEntry(collectionName, entry.data, ctx);
54
+ return {
55
+ ...entry,
56
+ data: result.data,
57
+ _privacy: result,
58
+ };
59
+ });
60
+ }
61
+ /**
62
+ * Check if a specific field would be visible at the current privacy level.
63
+ *
64
+ * Useful for conditional rendering in templates:
65
+ *
66
+ * {shouldShow('team', 'email', config) && <a href={`mailto:${member.data.email}`}>Email</a>}
67
+ */
68
+ export function shouldShow(collectionName, fieldName, config, level) {
69
+ const ctx = createContext(config, level ?? readPrivacyLevel());
70
+ const collection = ctx.config.collections[collectionName];
71
+ if (!collection)
72
+ return true;
73
+ const attributeId = collection.fields[fieldName];
74
+ if (!attributeId)
75
+ return true;
76
+ const attr = ctx.config.attributes[attributeId];
77
+ if (!attr)
78
+ return true;
79
+ return ctx.currentLevel >= attr.threshold;
80
+ }
81
+ /**
82
+ * Get the current privacy level and its metadata.
83
+ * Useful for displaying privacy status in templates.
84
+ */
85
+ export function getPrivacyStatus(config, level) {
86
+ const ctx = createContext(config, level ?? readPrivacyLevel());
87
+ const levelDef = ctx.config.levels.find((l) => l.level === ctx.currentLevel);
88
+ return {
89
+ level: ctx.currentLevel,
90
+ name: levelDef?.name ?? 'Unknown',
91
+ description: levelDef?.description ?? '',
92
+ isReduced: ctx.currentLevel < 4,
93
+ };
94
+ }
95
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAKL,aAAa,EACb,gBAAgB,EAChB,cAAc,GAEf,MAAM,0BAA0B,CAAC;AAElC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CACzB,cAAsB,EACtB,KAAQ,EACR,MAA+B,EAC/B,KAAoB;IAEpB,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,EAAE,KAAK,IAAI,gBAAgB,EAAE,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,cAAc,CAAC,cAAc,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAE/D,OAAO;QACL,GAAG,KAAK;QACR,IAAI,EAAE,MAAM,CAAC,IAAiB;QAC9B,QAAQ,EAAE,MAAM;KACjB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAC9B,cAAsB,EACtB,OAAY,EACZ,MAA+B,EAC/B,KAAoB;IAEpB,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,EAAE,KAAK,IAAI,gBAAgB,EAAE,CAAC,CAAC;IAE/D,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,cAAc,CAAC,cAAc,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC/D,OAAO;YACL,GAAG,KAAK;YACR,IAAI,EAAE,MAAM,CAAC,IAAiB;YAC9B,QAAQ,EAAE,MAAM;SACjB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CACxB,cAAsB,EACtB,SAAiB,EACjB,MAA+B,EAC/B,KAAoB;IAEpB,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,EAAE,KAAK,IAAI,gBAAgB,EAAE,CAAC,CAAC;IAC/D,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;IAE1D,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAE7B,MAAM,WAAW,GAAG,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACjD,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAE9B,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAChD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,OAAO,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,SAAS,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAA+B,EAAE,KAAoB;IACpF,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,EAAE,KAAK,IAAI,gBAAgB,EAAE,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,YAAY,CAAC,CAAC;IAE7E,OAAO;QACL,KAAK,EAAE,GAAG,CAAC,YAAY;QACvB,IAAI,EAAE,QAAQ,EAAE,IAAI,IAAI,SAAS;QACjC,WAAW,EAAE,QAAQ,EAAE,WAAW,IAAI,EAAE;QACxC,SAAS,EAAE,GAAG,CAAC,YAAY,GAAG,CAAC;KAChC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Astro Integration for Responsive Privacy
3
+ *
4
+ * Hooks into Astro's build lifecycle to:
5
+ * 1. Read the PRIVACY_LEVEL from environment
6
+ * 2. Log build summary showing what will be hidden
7
+ * 3. Provide the privacy context to components via Vite virtual module
8
+ *
9
+ * Usage in astro.config.mjs:
10
+ *
11
+ * import { responsivePrivacy } from '@responsive-privacy/astro';
12
+ * import privacyConfig from './responsive-privacy.config';
13
+ *
14
+ * export default defineConfig({
15
+ * integrations: [responsivePrivacy(privacyConfig)],
16
+ * });
17
+ */
18
+ import type { AstroIntegration } from 'astro';
19
+ import { type ResponsivePrivacyConfig, type PrivacyLevel } from '@responsive-privacy/core';
20
+ export interface ResponsivePrivacyOptions extends ResponsivePrivacyConfig {
21
+ /**
22
+ * Override the privacy level instead of reading from PRIVACY_LEVEL env var.
23
+ * Useful for testing or for passing from a build script.
24
+ */
25
+ level?: PrivacyLevel;
26
+ /**
27
+ * Print build summary to console. Defaults to true.
28
+ */
29
+ verbose?: boolean;
30
+ }
31
+ export declare function responsivePrivacy(options: ResponsivePrivacyOptions): AstroIntegration;
32
+ export { defineConfig, resolveConfig, createContext, readPrivacyLevel, transformEntry, transformCollection, buildSummary, isAttributeVisible, } from '@responsive-privacy/core';
33
+ export type { ResponsivePrivacyConfig, PrivacyLevel, PrivacyContext, TransformResult, AttributeDefinition, } from '@responsive-privacy/core';
34
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EAIlB,MAAM,0BAA0B,CAAC;AAKlC,MAAM,WAAW,wBAAyB,SAAQ,uBAAuB;IACvE;;;OAGG;IACH,KAAK,CAAC,EAAE,YAAY,CAAC;IAErB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,wBAAwB,GAChC,gBAAgB,CA8DlB;AAgED,OAAO,EACL,YAAY,EACZ,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EACV,uBAAuB,EACvB,YAAY,EACZ,cAAc,EACd,eAAe,EACf,mBAAmB,GACpB,MAAM,0BAA0B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Astro Integration for Responsive Privacy
3
+ *
4
+ * Hooks into Astro's build lifecycle to:
5
+ * 1. Read the PRIVACY_LEVEL from environment
6
+ * 2. Log build summary showing what will be hidden
7
+ * 3. Provide the privacy context to components via Vite virtual module
8
+ *
9
+ * Usage in astro.config.mjs:
10
+ *
11
+ * import { responsivePrivacy } from '@responsive-privacy/astro';
12
+ * import privacyConfig from './responsive-privacy.config';
13
+ *
14
+ * export default defineConfig({
15
+ * integrations: [responsivePrivacy(privacyConfig)],
16
+ * });
17
+ */
18
+ import { createContext, readPrivacyLevel, resolveConfig, } from '@responsive-privacy/core';
19
+ const VIRTUAL_MODULE_ID = 'virtual:responsive-privacy';
20
+ const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID;
21
+ export function responsivePrivacy(options) {
22
+ const { level, verbose = true, ...config } = options;
23
+ const currentLevel = level ?? readPrivacyLevel();
24
+ const resolved = resolveConfig(config);
25
+ const ctx = createContext(config, currentLevel);
26
+ const levelDef = resolved.levels.find((l) => l.level === currentLevel);
27
+ return {
28
+ name: 'responsive-privacy',
29
+ hooks: {
30
+ 'astro:config:setup': ({ updateConfig, logger }) => {
31
+ if (verbose) {
32
+ logger.info(`Privacy Level: ${currentLevel} — ${levelDef?.name ?? 'Unknown'}`);
33
+ if (currentLevel < 4) {
34
+ logger.warn(`Building with reduced visibility. Some content will be hidden or redacted.`);
35
+ }
36
+ }
37
+ // Inject a Vite plugin that provides the virtual module
38
+ updateConfig({
39
+ vite: {
40
+ plugins: [
41
+ {
42
+ name: 'responsive-privacy-virtual',
43
+ resolveId(id) {
44
+ if (id === VIRTUAL_MODULE_ID) {
45
+ return RESOLVED_VIRTUAL_MODULE_ID;
46
+ }
47
+ },
48
+ load(id) {
49
+ if (id === RESOLVED_VIRTUAL_MODULE_ID) {
50
+ return generateVirtualModule(currentLevel, resolved);
51
+ }
52
+ },
53
+ },
54
+ ],
55
+ },
56
+ });
57
+ },
58
+ 'astro:build:start': ({ logger }) => {
59
+ if (verbose && currentLevel < 4) {
60
+ // Log which attribute categories will be affected
61
+ const hiddenAttrs = Object.entries(resolved.attributes)
62
+ .filter(([_, attr]) => attr.threshold > currentLevel)
63
+ .map(([id, attr]) => `${id} (${attr.name})`);
64
+ if (hiddenAttrs.length > 0) {
65
+ logger.info(`Attributes hidden at Level ${currentLevel}:`);
66
+ for (const attr of hiddenAttrs) {
67
+ logger.info(` ⬛ ${attr}`);
68
+ }
69
+ }
70
+ }
71
+ },
72
+ },
73
+ };
74
+ }
75
+ /**
76
+ * Generate the virtual module code that components can import.
77
+ */
78
+ function generateVirtualModule(currentLevel, config) {
79
+ // Serialize just what templates need — keep it lightweight
80
+ const serializedAttributes = JSON.stringify(Object.fromEntries(Object.entries(config.attributes).map(([id, attr]) => [
81
+ id,
82
+ { threshold: attr.threshold, redaction: attr.redaction ?? 'omit', redactedValue: attr.redactedValue },
83
+ ])));
84
+ const serializedCollections = JSON.stringify(config.collections);
85
+ return `
86
+ // Auto-generated by @responsive-privacy/astro
87
+ // Privacy Level: ${currentLevel}
88
+
89
+ export const PRIVACY_LEVEL = ${currentLevel};
90
+ export const LEVEL_NAME = ${JSON.stringify(config.levels.find(l => l.level === currentLevel)?.name ?? 'Unknown')};
91
+
92
+ const _attributes = ${serializedAttributes};
93
+ const _collections = ${serializedCollections};
94
+
95
+ /**
96
+ * Check if a specific attribute ID is visible at the current build level.
97
+ */
98
+ export function isVisible(attributeId) {
99
+ const attr = _attributes[attributeId];
100
+ if (!attr) return true;
101
+ return ${currentLevel} >= attr.threshold;
102
+ }
103
+
104
+ /**
105
+ * Check if a specific field in a collection is visible.
106
+ */
107
+ export function isFieldVisible(collectionName, fieldName) {
108
+ const collection = _collections[collectionName];
109
+ if (!collection) return true;
110
+ const attributeId = collection.fields[fieldName];
111
+ if (!attributeId) return true;
112
+ return isVisible(attributeId);
113
+ }
114
+
115
+ /**
116
+ * Get the redacted value for an attribute, or undefined if omitted.
117
+ */
118
+ export function redactedValueFor(attributeId) {
119
+ const attr = _attributes[attributeId];
120
+ if (!attr) return undefined;
121
+ if (attr.redaction === 'replace') return attr.redactedValue ?? '[Hidden]';
122
+ return undefined;
123
+ }
124
+ `;
125
+ }
126
+ // Re-export core types and functions for convenience
127
+ export { defineConfig, resolveConfig, createContext, readPrivacyLevel, transformEntry, transformCollection, buildSummary, isAttributeVisible, } from '@responsive-privacy/core';
128
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@responsive-privacy/astro",
3
+ "version": "0.1.0",
4
+ "description": "Astro integration for responsive privacy — build-time PII protection for static sites",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ },
14
+ "./helpers": {
15
+ "import": "./dist/helpers.js",
16
+ "types": "./dist/helpers.d.ts"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "dependencies": {
23
+ "@responsive-privacy/core": "0.1.0"
24
+ },
25
+ "peerDependencies": {
26
+ "astro": ">=4.0.0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^22.0.0",
30
+ "astro": "^5.0.0",
31
+ "typescript": "^5.5.0"
32
+ },
33
+ "scripts": {
34
+ "build": "tsc"
35
+ }
36
+ }