@runhalo/engine 0.2.0 → 0.3.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.
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+ /**
3
+ * PII Sanitizer Scaffold
4
+ * Addresses: coppa-data-002 (HIGH)
5
+ * Generates middleware/utility to strip PII from URL parameters
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.piiSanitizerTemplate = void 0;
9
+ function generateReact(typescript) {
10
+ const ext = typescript ? 'ts' : 'js';
11
+ const typeAnnotations = typescript;
12
+ // Utility file (framework-agnostic, used by both React and Next.js)
13
+ const utility = `/**
14
+ * PII Sanitizer — strips personally identifiable information from URLs
15
+ *
16
+ * COPPA requires that children's personal information is not transmitted
17
+ * in URL parameters, query strings, or referrer headers. This utility
18
+ * detects and removes PII patterns from URLs before they are used.
19
+ *
20
+ * Common PII fields detected: email, name, phone, dob, ssn, address,
21
+ * birthday, age, school, parent, guardian.
22
+ *
23
+ * Usage:
24
+ * import { sanitizeUrl, containsPII } from './pii-sanitizer';
25
+ *
26
+ * const cleanUrl = sanitizeUrl('https://api.example.com/users?email=child@test.com&name=Bobby');
27
+ * // => 'https://api.example.com/users'
28
+ *
29
+ * if (containsPII(url)) {
30
+ * console.warn('URL contains PII — sanitizing before request');
31
+ * }
32
+ *
33
+ * Generated by Halo (runhalo.dev) — COPPA compliance scaffold
34
+ */
35
+
36
+ /** PII parameter patterns — case-insensitive matching */
37
+ const PII_PATTERNS${typeAnnotations ? ': readonly string[]' : ''} = [
38
+ 'email', 'e-mail', 'mail',
39
+ 'name', 'first_name', 'last_name', 'firstname', 'lastname', 'full_name', 'fullname',
40
+ 'username', 'user_name', 'display_name',
41
+ 'phone', 'telephone', 'tel', 'mobile', 'cell',
42
+ 'dob', 'date_of_birth', 'dateofbirth', 'birthday', 'birth_date', 'birthdate',
43
+ 'age',
44
+ 'ssn', 'social_security', 'national_id',
45
+ 'address', 'street', 'city', 'zip', 'zipcode', 'zip_code', 'postal',
46
+ 'school', 'school_name',
47
+ 'parent', 'guardian', 'parent_email', 'guardian_email',
48
+ 'child', 'child_name', 'kid',
49
+ 'password', 'passwd', 'pwd',
50
+ 'token', 'api_key', 'apikey', 'secret',
51
+ ]${typeAnnotations ? ' as const' : ''};
52
+
53
+ /**
54
+ * Check if a URL contains PII in its query parameters
55
+ */
56
+ export function containsPII(url${typeAnnotations ? ': string' : ''})${typeAnnotations ? ': boolean' : ''} {
57
+ try {
58
+ const parsed = new URL(url, 'https://placeholder.local');
59
+ const params = Array.from(parsed.searchParams.keys());
60
+ return params.some(param =>
61
+ PII_PATTERNS.some(pattern =>
62
+ param.toLowerCase().includes(pattern.toLowerCase())
63
+ )
64
+ );
65
+ } catch {
66
+ return false;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Remove PII parameters from a URL, preserving non-PII params
72
+ */
73
+ export function sanitizeUrl(url${typeAnnotations ? ': string' : ''})${typeAnnotations ? ': string' : ''} {
74
+ try {
75
+ const parsed = new URL(url, 'https://placeholder.local');
76
+ const toRemove${typeAnnotations ? ': string[]' : ''} = [];
77
+
78
+ parsed.searchParams.forEach((_, key) => {
79
+ if (PII_PATTERNS.some(pattern => key.toLowerCase().includes(pattern.toLowerCase()))) {
80
+ toRemove.push(key);
81
+ }
82
+ });
83
+
84
+ toRemove.forEach(key => parsed.searchParams.delete(key));
85
+
86
+ // If original was relative, return relative
87
+ if (!url.startsWith('http')) {
88
+ return parsed.pathname + parsed.search + parsed.hash;
89
+ }
90
+
91
+ return parsed.toString();
92
+ } catch {
93
+ return url;
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Extract PII fields from a URL (for logging/auditing)
99
+ * Returns the parameter names (NOT values) that match PII patterns
100
+ */
101
+ export function detectPIIFields(url${typeAnnotations ? ': string' : ''})${typeAnnotations ? ': string[]' : ''} {
102
+ try {
103
+ const parsed = new URL(url, 'https://placeholder.local');
104
+ return Array.from(parsed.searchParams.keys()).filter(key =>
105
+ PII_PATTERNS.some(pattern => key.toLowerCase().includes(pattern.toLowerCase()))
106
+ );
107
+ } catch {
108
+ return [];
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Middleware-style sanitizer for fetch/axios interceptors
114
+ *
115
+ * Usage with fetch:
116
+ * const originalFetch = window.fetch;
117
+ * window.fetch = (url, options) => {
118
+ * return originalFetch(sanitizeFetchUrl(url), options);
119
+ * };
120
+ */
121
+ export function sanitizeFetchUrl(input${typeAnnotations ? ': string | URL | Request' : ''})${typeAnnotations ? ': string | URL | Request' : ''} {
122
+ if (typeof input === 'string') {
123
+ return sanitizeUrl(input);
124
+ }
125
+ if (input instanceof URL) {
126
+ return new URL(sanitizeUrl(input.toString()));
127
+ }
128
+ // Request object — sanitize the URL
129
+ if (input instanceof Request) {
130
+ const cleanUrl = sanitizeUrl(input.url);
131
+ return new Request(cleanUrl, input);
132
+ }
133
+ return input;
134
+ }
135
+
136
+ export { PII_PATTERNS };
137
+ `;
138
+ return [
139
+ {
140
+ relativePath: `utils/pii-sanitizer.${ext}`,
141
+ content: utility,
142
+ description: 'PII detection and URL sanitization utility for COPPA compliance',
143
+ },
144
+ ];
145
+ }
146
+ function generatePlainJS() {
147
+ const code = `/**
148
+ * PII Sanitizer — strips personally identifiable information from URLs
149
+ *
150
+ * COPPA requires that children's personal information is not transmitted
151
+ * in URL parameters, query strings, or referrer headers.
152
+ *
153
+ * Usage:
154
+ * const { sanitizeUrl, containsPII } = require('./pii-sanitizer');
155
+ * const cleanUrl = sanitizeUrl('https://api.example.com/users?email=child@test.com');
156
+ *
157
+ * Express middleware:
158
+ * app.use(piiMiddleware());
159
+ *
160
+ * Generated by Halo (runhalo.dev) — COPPA compliance scaffold
161
+ */
162
+
163
+ const PII_PATTERNS = [
164
+ 'email', 'e-mail', 'mail',
165
+ 'name', 'first_name', 'last_name', 'firstname', 'lastname', 'full_name', 'fullname',
166
+ 'username', 'user_name', 'display_name',
167
+ 'phone', 'telephone', 'tel', 'mobile', 'cell',
168
+ 'dob', 'date_of_birth', 'dateofbirth', 'birthday', 'birth_date', 'birthdate',
169
+ 'age',
170
+ 'ssn', 'social_security', 'national_id',
171
+ 'address', 'street', 'city', 'zip', 'zipcode', 'zip_code', 'postal',
172
+ 'school', 'school_name',
173
+ 'parent', 'guardian', 'parent_email', 'guardian_email',
174
+ 'child', 'child_name', 'kid',
175
+ 'password', 'passwd', 'pwd',
176
+ 'token', 'api_key', 'apikey', 'secret',
177
+ ];
178
+
179
+ function containsPII(url) {
180
+ try {
181
+ const parsed = new URL(url, 'https://placeholder.local');
182
+ const params = Array.from(parsed.searchParams.keys());
183
+ return params.some(param =>
184
+ PII_PATTERNS.some(pattern =>
185
+ param.toLowerCase().includes(pattern.toLowerCase())
186
+ )
187
+ );
188
+ } catch {
189
+ return false;
190
+ }
191
+ }
192
+
193
+ function sanitizeUrl(url) {
194
+ try {
195
+ const parsed = new URL(url, 'https://placeholder.local');
196
+ const toRemove = [];
197
+
198
+ parsed.searchParams.forEach((_, key) => {
199
+ if (PII_PATTERNS.some(pattern => key.toLowerCase().includes(pattern.toLowerCase()))) {
200
+ toRemove.push(key);
201
+ }
202
+ });
203
+
204
+ toRemove.forEach(key => parsed.searchParams.delete(key));
205
+
206
+ if (!url.startsWith('http')) {
207
+ return parsed.pathname + parsed.search + parsed.hash;
208
+ }
209
+
210
+ return parsed.toString();
211
+ } catch {
212
+ return url;
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Express middleware — strips PII from query parameters
218
+ *
219
+ * Usage:
220
+ * const { piiMiddleware } = require('./pii-sanitizer');
221
+ * app.use(piiMiddleware());
222
+ */
223
+ function piiMiddleware() {
224
+ return function (req, res, next) {
225
+ const piiKeys = Object.keys(req.query).filter(key =>
226
+ PII_PATTERNS.some(pattern => key.toLowerCase().includes(pattern.toLowerCase()))
227
+ );
228
+
229
+ if (piiKeys.length > 0) {
230
+ console.warn('[Halo PII Guard] Stripped PII params from request:', piiKeys.join(', '));
231
+ piiKeys.forEach(key => delete req.query[key]);
232
+ }
233
+
234
+ next();
235
+ };
236
+ }
237
+
238
+ module.exports = { containsPII, sanitizeUrl, piiMiddleware, PII_PATTERNS };
239
+ `;
240
+ return [
241
+ {
242
+ relativePath: 'pii-sanitizer.js',
243
+ content: code,
244
+ description: 'PII sanitizer with Express middleware (vanilla JS/Node.js)',
245
+ },
246
+ ];
247
+ }
248
+ exports.piiSanitizerTemplate = {
249
+ scaffoldId: 'pii-sanitizer',
250
+ name: 'PII Sanitizer',
251
+ description: 'Strips personally identifiable information from URL parameters to prevent COPPA violations',
252
+ ruleIds: ['coppa-data-002'],
253
+ generate(framework, typescript) {
254
+ switch (framework) {
255
+ case 'react':
256
+ case 'nextjs':
257
+ return generateReact(typescript);
258
+ default:
259
+ return generatePlainJS();
260
+ }
261
+ },
262
+ };
263
+ //# sourceMappingURL=pii-sanitizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pii-sanitizer.js","sourceRoot":"","sources":["../../../src/scaffolds/templates/pii-sanitizer.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAKH,SAAS,aAAa,CAAC,UAAmB;IACxC,MAAM,GAAG,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACrC,MAAM,eAAe,GAAG,UAAU,CAAC;IAEnC,oEAAoE;IACpE,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;oBAwBE,eAAe,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;GAc7D,eAAe,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE;;;;;iCAKJ,eAAe,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,eAAe,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;iCAiBvE,eAAe,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,eAAe,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;;;oBAGnF,eAAe,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;qCAyBlB,eAAe,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,eAAe,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;wCAoBrE,eAAe,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,EAAE,IAAI,eAAe,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;CAgB7I,CAAC;IAEA,OAAO;QACL;YACE,YAAY,EAAE,uBAAuB,GAAG,EAAE;YAC1C,OAAO,EAAE,OAAO;YAChB,WAAW,EAAE,iEAAiE;SAC/E;KACF,CAAC;AACJ,CAAC;AAED,SAAS,eAAe;IACtB,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4Fd,CAAC;IAEA,OAAO;QACL;YACE,YAAY,EAAE,kBAAkB;YAChC,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,4DAA4D;SAC1E;KACF,CAAC;AACJ,CAAC;AAEY,QAAA,oBAAoB,GAAqB;IACpD,UAAU,EAAE,eAAe;IAC3B,IAAI,EAAE,eAAe;IACrB,WAAW,EAAE,4FAA4F;IACzG,OAAO,EAAE,CAAC,gBAAgB,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,UAAU;QAC5B,QAAQ,SAAS,EAAE,CAAC;YAClB,KAAK,OAAO,CAAC;YACb,KAAK,QAAQ;gBACX,OAAO,aAAa,CAAC,UAAU,CAAC,CAAC;YACnC;gBACE,OAAO,eAAe,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;CACF,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Data Retention Policy Scaffold
3
+ * Addresses: coppa-retention-005
4
+ * Generates data retention utility with cleanup script and deletion handler
5
+ */
6
+ import type { ScaffoldTemplate } from '../index';
7
+ export declare const retentionPolicyTemplate: ScaffoldTemplate;
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ /**
3
+ * Data Retention Policy Scaffold
4
+ * Addresses: coppa-retention-005
5
+ * Generates data retention utility with cleanup script and deletion handler
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.retentionPolicyTemplate = void 0;
9
+ function generateReact(typescript) {
10
+ const ext = typescript ? 'ts' : 'js';
11
+ const typeAnnotations = typescript;
12
+ const utility = `/**
13
+ * Data Retention Policy — COPPA-compliant data lifecycle management
14
+ *
15
+ * COPPA requires:
16
+ * - Data collected from children must be deleted when no longer needed
17
+ * - Parents can request deletion of their child's data at any time
18
+ * - Deletion must be completed within a reasonable timeframe (48-hour SLA recommended)
19
+ * - Only collect data that is reasonably necessary
20
+ *
21
+ * This utility provides:
22
+ * - Schema helpers for marking records with retention metadata
23
+ * - A cleanup function for purging expired data
24
+ * - A deletion request handler for parent-initiated deletion
25
+ *
26
+ * Usage:
27
+ * import { DataRetention, RETENTION_POLICIES } from './data-retention';
28
+ *
29
+ * // Mark a record with retention metadata
30
+ * const record = DataRetention.markForRetention(userData, 'user_profile');
31
+ *
32
+ * // Check for expired records
33
+ * const expired = DataRetention.isExpired(record);
34
+ *
35
+ * // Handle parent deletion request
36
+ * await DataRetention.handleDeletionRequest(userId);
37
+ *
38
+ * Generated by Halo (runhalo.dev) — COPPA compliance scaffold
39
+ */
40
+ ${typeAnnotations ? `
41
+ export interface RetentionPolicy {
42
+ /** Category of data */
43
+ category: string;
44
+ /** Maximum retention period in days */
45
+ maxRetentionDays: number;
46
+ /** Whether this data type is required for service operation */
47
+ essential: boolean;
48
+ /** Description for privacy policy */
49
+ description: string;
50
+ }
51
+
52
+ export interface RetentionMetadata {
53
+ /** When the data was created */
54
+ createdAt: string;
55
+ /** When the data should be deleted */
56
+ expiresAt: string;
57
+ /** Retention policy category applied */
58
+ retentionCategory: string;
59
+ /** Whether a deletion request is pending */
60
+ deletionRequested?: boolean;
61
+ /** When deletion was requested */
62
+ deletionRequestedAt?: string;
63
+ }
64
+
65
+ export interface DeletionRequest {
66
+ userId: string;
67
+ requestedAt: string;
68
+ /** COPPA SLA: must be completed within this deadline */
69
+ deadline: string;
70
+ status: 'pending' | 'processing' | 'completed' | 'failed';
71
+ categories: string[];
72
+ }
73
+ ` : ''}
74
+ /**
75
+ * Default retention policies — customize per your data categories.
76
+ * COPPA best practice: shorter retention = lower risk.
77
+ */
78
+ ${typeAnnotations ? 'export const RETENTION_POLICIES: Record<string, RetentionPolicy> = {' : 'const RETENTION_POLICIES = {'}
79
+ /** User profile data — retained while account is active + 30 day grace period */
80
+ user_profile: {
81
+ category: 'user_profile',
82
+ maxRetentionDays: 365,
83
+ essential: true,
84
+ description: 'Basic account information needed for service operation',
85
+ },
86
+ /** Session data — short-lived, purged after 24 hours */
87
+ session: {
88
+ category: 'session',
89
+ maxRetentionDays: 1,
90
+ essential: true,
91
+ description: 'Temporary session data for authentication',
92
+ },
93
+ /** Analytics/telemetry — aggregated, purged after 90 days */
94
+ analytics: {
95
+ category: 'analytics',
96
+ maxRetentionDays: 90,
97
+ essential: false,
98
+ description: 'Anonymous usage analytics for product improvement',
99
+ },
100
+ /** User-generated content — retained while account is active */
101
+ user_content: {
102
+ category: 'user_content',
103
+ maxRetentionDays: 365,
104
+ essential: false,
105
+ description: 'Content created by the user within the service',
106
+ },
107
+ /** Support/communication — retained for 180 days */
108
+ support: {
109
+ category: 'support',
110
+ maxRetentionDays: 180,
111
+ essential: false,
112
+ description: 'Customer support interactions and communications',
113
+ },
114
+ };
115
+
116
+ /** COPPA-recommended maximum time to process a deletion request (48 hours) */
117
+ const DELETION_SLA_HOURS = 48;
118
+
119
+ ${typeAnnotations ? 'export class DataRetention {' : 'class DataRetention {'}
120
+ /**
121
+ * Add retention metadata to a data record.
122
+ * Call this when creating or updating user data.
123
+ */
124
+ static markForRetention(
125
+ record${typeAnnotations ? ': Record<string, any>' : ''},
126
+ category${typeAnnotations ? ': string' : ''},
127
+ policyOverrides${typeAnnotations ? '?: Partial<RetentionPolicy>' : ''} = {}
128
+ )${typeAnnotations ? ': Record<string, any> & { _retention: RetentionMetadata }' : ''} {
129
+ const policy = { ...(RETENTION_POLICIES[category] || RETENTION_POLICIES.user_profile), ...policyOverrides };
130
+ const now = new Date();
131
+ const expiresAt = new Date(now.getTime() + policy.maxRetentionDays * 24 * 60 * 60 * 1000);
132
+
133
+ return {
134
+ ...record,
135
+ _retention: {
136
+ createdAt: now.toISOString(),
137
+ expiresAt: expiresAt.toISOString(),
138
+ retentionCategory: policy.category,
139
+ },
140
+ };
141
+ }
142
+
143
+ /**
144
+ * Check if a record has expired based on its retention metadata
145
+ */
146
+ static isExpired(record${typeAnnotations ? ': { _retention?: RetentionMetadata }' : ''})${typeAnnotations ? ': boolean' : ''} {
147
+ if (!record._retention?.expiresAt) return false;
148
+ return new Date() > new Date(record._retention.expiresAt);
149
+ }
150
+
151
+ /**
152
+ * Check if a deletion request is pending for a record
153
+ */
154
+ static isDeletionPending(record${typeAnnotations ? ': { _retention?: RetentionMetadata }' : ''})${typeAnnotations ? ': boolean' : ''} {
155
+ return record._retention?.deletionRequested === true;
156
+ }
157
+
158
+ /**
159
+ * Create a deletion request with COPPA SLA deadline.
160
+ *
161
+ * IMPORTANT: Implement the actual data deletion in your database layer.
162
+ * This function creates the request record — you must process it.
163
+ */
164
+ static createDeletionRequest(
165
+ userId${typeAnnotations ? ': string' : ''},
166
+ categories${typeAnnotations ? ': string[]' : ''} = Object.keys(RETENTION_POLICIES)
167
+ )${typeAnnotations ? ': DeletionRequest' : ''} {
168
+ const now = new Date();
169
+ const deadline = new Date(now.getTime() + DELETION_SLA_HOURS * 60 * 60 * 1000);
170
+
171
+ return {
172
+ userId,
173
+ requestedAt: now.toISOString(),
174
+ deadline: deadline.toISOString(), // 48-hour SLA
175
+ status: 'pending',
176
+ categories,
177
+ };
178
+ }
179
+
180
+ /**
181
+ * Get all retention policies (for privacy policy page generation)
182
+ */
183
+ static getPolicies()${typeAnnotations ? ': Record<string, RetentionPolicy>' : ''} {
184
+ return { ...RETENTION_POLICIES };
185
+ }
186
+
187
+ /**
188
+ * Filter an array of records, removing expired ones.
189
+ * Use this in a scheduled cleanup job.
190
+ *
191
+ * Example (cron job):
192
+ * const records = await db.query('SELECT * FROM user_data');
193
+ * const { active, expired } = DataRetention.partitionByExpiry(records);
194
+ * await db.deleteMany(expired.map(r => r.id));
195
+ */
196
+ static partitionByExpiry(records${typeAnnotations ? ': Array<{ _retention?: RetentionMetadata }>' : ''})${typeAnnotations ? ': { active: any[]; expired: any[] }' : ''} {
197
+ const active${typeAnnotations ? ': any[]' : ''} = [];
198
+ const expired${typeAnnotations ? ': any[]' : ''} = [];
199
+
200
+ for (const record of records) {
201
+ if (DataRetention.isExpired(record) || DataRetention.isDeletionPending(record)) {
202
+ expired.push(record);
203
+ } else {
204
+ active.push(record);
205
+ }
206
+ }
207
+
208
+ return { active, expired };
209
+ }
210
+ }
211
+
212
+ ${typeAnnotations ? '' : 'module.exports = { DataRetention, RETENTION_POLICIES, DELETION_SLA_HOURS };'}
213
+ ${typeAnnotations ? 'export { DELETION_SLA_HOURS };' : ''}
214
+ `;
215
+ return [
216
+ {
217
+ relativePath: `utils/data-retention.${ext}`,
218
+ content: utility,
219
+ description: 'COPPA-compliant data retention utility with cleanup and deletion request handling',
220
+ },
221
+ ];
222
+ }
223
+ function generatePlainJS() {
224
+ // Reuse the same logic but without TypeScript annotations
225
+ const files = generateReact(false);
226
+ // Adjust path for plain JS (no utils/ nesting)
227
+ return files.map(f => ({
228
+ ...f,
229
+ relativePath: 'data-retention.js',
230
+ }));
231
+ }
232
+ exports.retentionPolicyTemplate = {
233
+ scaffoldId: 'retention-policy',
234
+ name: 'Data Retention Policy',
235
+ description: 'COPPA-compliant data retention lifecycle management with 48-hour deletion SLA',
236
+ ruleIds: ['coppa-retention-005'],
237
+ generate(framework, typescript) {
238
+ switch (framework) {
239
+ case 'react':
240
+ case 'nextjs':
241
+ return generateReact(typescript);
242
+ default:
243
+ return generatePlainJS();
244
+ }
245
+ },
246
+ };
247
+ //# sourceMappingURL=retention-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention-policy.js","sourceRoot":"","sources":["../../../src/scaffolds/templates/retention-policy.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runhalo/engine",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Halo rule engine — COPPA 2.0 risk detection via tree-sitter AST analysis",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",