@tantainnovative/create-ndpr 0.1.0 → 0.4.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,317 @@
1
+ /**
2
+ * Express — Cross-Border Transfer Router
3
+ * Generated by create-ndpr for: {{ORG_NAME}}
4
+ * DPO: {{DPO_EMAIL}}
5
+ *
6
+ * NDPA §41–43 — transfers of personal data outside Nigeria require
7
+ * adequate safeguards and, in some cases, NDPC approval.
8
+ *
9
+ * Routes:
10
+ * GET /cross-border — List transfers (optional ?riskLevel= or ?destinationCountry= filter)
11
+ * GET /cross-border/:id — Get a single transfer record
12
+ * POST /cross-border — Register a new cross-border transfer
13
+ * PUT /cross-border/:id — Update an existing transfer record
14
+ * DELETE /cross-border/:id — Delete a transfer record
15
+ *
16
+ * Usage:
17
+ * import { crossBorderRouter } from './routes/cross-border';
18
+ * app.use('/api/cross-border', crossBorderRouter);
19
+ */
20
+
21
+ import { Router } from 'express';
22
+ // {{#if ORM=prisma}}
23
+ import { PrismaClient } from '@prisma/client';
24
+
25
+ const prisma = new PrismaClient();
26
+ // {{/if}}
27
+ // {{#if ORM=drizzle}}
28
+ import { db } from '@/drizzle';
29
+ import { crossBorderTransferRecords, complianceAuditLog } from '@/drizzle/ndpr-schema';
30
+ import { eq, and, desc, type SQL } from 'drizzle-orm';
31
+ // {{/if}}
32
+ // {{#if ORM=none}}
33
+ // TODO ({{ORG_NAME}}): wire this to your persistent store of choice. The
34
+ // in-memory map below is enough to develop against locally but DOES NOT
35
+ // satisfy NDPA Section 44 (record-keeping) or the cross-border register
36
+ // NDPC inspects under Sections 41–43. Replace `crossBorderStore`/`auditLog`
37
+ // with your DB / KV / API.
38
+ interface CrossBorderTransferRecord {
39
+ id: string;
40
+ destinationCountry: string;
41
+ recipientName: string;
42
+ transferMechanism: string;
43
+ safeguards: string;
44
+ dataCategories: string[];
45
+ adequacyStatus: string;
46
+ ndpcApprovalRequired: boolean;
47
+ ndpcApprovalReference: string | null;
48
+ riskLevel: string;
49
+ createdAt: Date;
50
+ updatedAt: Date;
51
+ }
52
+ const crossBorderStore = new Map<string, CrossBorderTransferRecord>();
53
+ const auditLog: Array<{ id: string; module: string; action: string; entityId: string; at: Date }> = [];
54
+ function newId() { return crypto.randomUUID(); }
55
+ // {{/if}}
56
+
57
+ export const crossBorderRouter = Router();
58
+
59
+ // GET /cross-border?riskLevel=high&destinationCountry=US
60
+ crossBorderRouter.get('/', async (req, res) => {
61
+ const { riskLevel, destinationCountry } = req.query;
62
+ const riskLevelFilter = typeof riskLevel === 'string' ? riskLevel : undefined;
63
+ const destinationCountryFilter = typeof destinationCountry === 'string' ? destinationCountry : undefined;
64
+
65
+ // {{#if ORM=prisma}}
66
+ const where: Record<string, string> = {};
67
+ if (riskLevelFilter) where.riskLevel = riskLevelFilter;
68
+ if (destinationCountryFilter) where.destinationCountry = destinationCountryFilter;
69
+
70
+ const records = await prisma.crossBorderTransferRecord.findMany({
71
+ where: Object.keys(where).length > 0 ? where : undefined,
72
+ orderBy: { createdAt: 'desc' },
73
+ });
74
+ // {{/if}}
75
+ // {{#if ORM=drizzle}}
76
+ const filters: SQL[] = [];
77
+ if (riskLevelFilter) filters.push(eq(crossBorderTransferRecords.riskLevel, riskLevelFilter));
78
+ if (destinationCountryFilter) filters.push(eq(crossBorderTransferRecords.destinationCountry, destinationCountryFilter));
79
+
80
+ const records = filters.length > 0
81
+ ? await db.select().from(crossBorderTransferRecords).where(and(...filters)).orderBy(desc(crossBorderTransferRecords.createdAt))
82
+ : await db.select().from(crossBorderTransferRecords).orderBy(desc(crossBorderTransferRecords.createdAt));
83
+ // {{/if}}
84
+ // {{#if ORM=none}}
85
+ const records = [...crossBorderStore.values()]
86
+ .filter((r) => (riskLevelFilter ? r.riskLevel === riskLevelFilter : true))
87
+ .filter((r) => (destinationCountryFilter ? r.destinationCountry === destinationCountryFilter : true))
88
+ .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
89
+ // {{/if}}
90
+
91
+ return res.json(records);
92
+ });
93
+
94
+ // GET /cross-border/:id
95
+ crossBorderRouter.get('/:id', async (req, res) => {
96
+ // {{#if ORM=prisma}}
97
+ const record = await prisma.crossBorderTransferRecord.findUnique({ where: { id: req.params.id } });
98
+ // {{/if}}
99
+ // {{#if ORM=drizzle}}
100
+ const [record] = await db.select().from(crossBorderTransferRecords).where(eq(crossBorderTransferRecords.id, req.params.id)).limit(1);
101
+ // {{/if}}
102
+ // {{#if ORM=none}}
103
+ const record = crossBorderStore.get(req.params.id) ?? null;
104
+ // {{/if}}
105
+
106
+ if (!record) {
107
+ return res.status(404).json({ error: 'Cross-border transfer record not found' });
108
+ }
109
+
110
+ return res.json(record);
111
+ });
112
+
113
+ // POST /cross-border
114
+ crossBorderRouter.post('/', async (req, res) => {
115
+ const {
116
+ destinationCountry,
117
+ recipientName,
118
+ transferMechanism,
119
+ safeguards,
120
+ dataCategories,
121
+ adequacyStatus,
122
+ ndpcApprovalRequired,
123
+ ndpcApprovalReference,
124
+ riskLevel,
125
+ } = req.body;
126
+
127
+ if (
128
+ !destinationCountry ||
129
+ !recipientName ||
130
+ !transferMechanism ||
131
+ !safeguards ||
132
+ !dataCategories ||
133
+ !adequacyStatus ||
134
+ !riskLevel
135
+ ) {
136
+ return res.status(400).json({
137
+ error:
138
+ 'destinationCountry, recipientName, transferMechanism, safeguards, dataCategories, adequacyStatus, and riskLevel are required',
139
+ });
140
+ }
141
+
142
+ if (!Array.isArray(dataCategories)) {
143
+ return res.status(400).json({ error: 'dataCategories must be an array' });
144
+ }
145
+
146
+ // {{#if ORM=prisma}}
147
+ const record = await prisma.crossBorderTransferRecord.create({
148
+ data: {
149
+ destinationCountry,
150
+ recipientName,
151
+ transferMechanism,
152
+ safeguards,
153
+ dataCategories,
154
+ adequacyStatus,
155
+ ndpcApprovalRequired: ndpcApprovalRequired ?? false,
156
+ ndpcApprovalReference: ndpcApprovalReference ?? null,
157
+ riskLevel,
158
+ },
159
+ });
160
+
161
+ await prisma.complianceAuditLog.create({
162
+ data: {
163
+ module: 'cross-border',
164
+ action: 'created',
165
+ entityId: record.id,
166
+ entityType: 'CrossBorderTransferRecord',
167
+ changes: { destinationCountry, recipientName, riskLevel },
168
+ },
169
+ });
170
+ // {{/if}}
171
+ // {{#if ORM=drizzle}}
172
+ const [record] = await db.insert(crossBorderTransferRecords).values({
173
+ destinationCountry,
174
+ recipientName,
175
+ transferMechanism,
176
+ safeguards,
177
+ dataCategories,
178
+ adequacyStatus,
179
+ ndpcApprovalRequired: ndpcApprovalRequired ?? false,
180
+ ndpcApprovalReference: ndpcApprovalReference ?? null,
181
+ riskLevel,
182
+ }).returning();
183
+
184
+ await db.insert(complianceAuditLog).values({
185
+ module: 'cross-border',
186
+ action: 'created',
187
+ entityId: record.id,
188
+ entityType: 'CrossBorderTransferRecord',
189
+ changes: { destinationCountry, recipientName, riskLevel },
190
+ });
191
+ // {{/if}}
192
+ // {{#if ORM=none}}
193
+ const now = new Date();
194
+ const record: CrossBorderTransferRecord = {
195
+ id: newId(),
196
+ destinationCountry,
197
+ recipientName,
198
+ transferMechanism,
199
+ safeguards,
200
+ dataCategories,
201
+ adequacyStatus,
202
+ ndpcApprovalRequired: ndpcApprovalRequired ?? false,
203
+ ndpcApprovalReference: ndpcApprovalReference ?? null,
204
+ riskLevel,
205
+ createdAt: now,
206
+ updatedAt: now,
207
+ };
208
+ crossBorderStore.set(record.id, record);
209
+ auditLog.push({ id: newId(), module: 'cross-border', action: 'created', entityId: record.id, at: new Date() });
210
+ // {{/if}}
211
+
212
+ return res.status(201).json(record);
213
+ });
214
+
215
+ // PUT /cross-border/:id
216
+ crossBorderRouter.put('/:id', async (req, res) => {
217
+ const { id } = req.params;
218
+
219
+ // {{#if ORM=prisma}}
220
+ const existing = await prisma.crossBorderTransferRecord.findUnique({ where: { id } });
221
+ if (!existing) {
222
+ return res.status(404).json({ error: 'Cross-border transfer record not found' });
223
+ }
224
+
225
+ const record = await prisma.crossBorderTransferRecord.update({
226
+ where: { id },
227
+ data: req.body,
228
+ });
229
+
230
+ await prisma.complianceAuditLog.create({
231
+ data: {
232
+ module: 'cross-border',
233
+ action: 'updated',
234
+ entityId: record.id,
235
+ entityType: 'CrossBorderTransferRecord',
236
+ changes: req.body,
237
+ },
238
+ });
239
+ // {{/if}}
240
+ // {{#if ORM=drizzle}}
241
+ const [existing] = await db.select().from(crossBorderTransferRecords).where(eq(crossBorderTransferRecords.id, id)).limit(1);
242
+ if (!existing) {
243
+ return res.status(404).json({ error: 'Cross-border transfer record not found' });
244
+ }
245
+
246
+ const [record] = await db.update(crossBorderTransferRecords).set(req.body).where(eq(crossBorderTransferRecords.id, id)).returning();
247
+
248
+ await db.insert(complianceAuditLog).values({
249
+ module: 'cross-border',
250
+ action: 'updated',
251
+ entityId: record.id,
252
+ entityType: 'CrossBorderTransferRecord',
253
+ changes: req.body,
254
+ });
255
+ // {{/if}}
256
+ // {{#if ORM=none}}
257
+ const existing = crossBorderStore.get(id);
258
+ if (!existing) {
259
+ return res.status(404).json({ error: 'Cross-border transfer record not found' });
260
+ }
261
+ const record: CrossBorderTransferRecord = { ...existing, ...req.body, id, updatedAt: new Date() };
262
+ crossBorderStore.set(id, record);
263
+ auditLog.push({ id: newId(), module: 'cross-border', action: 'updated', entityId: record.id, at: new Date() });
264
+ // {{/if}}
265
+
266
+ return res.json(record);
267
+ });
268
+
269
+ // DELETE /cross-border/:id
270
+ crossBorderRouter.delete('/:id', async (req, res) => {
271
+ const { id } = req.params;
272
+
273
+ // {{#if ORM=prisma}}
274
+ const existing = await prisma.crossBorderTransferRecord.findUnique({ where: { id } });
275
+ if (!existing) {
276
+ return res.status(404).json({ error: 'Cross-border transfer record not found' });
277
+ }
278
+
279
+ await prisma.crossBorderTransferRecord.delete({ where: { id } });
280
+
281
+ await prisma.complianceAuditLog.create({
282
+ data: {
283
+ module: 'cross-border',
284
+ action: 'deleted',
285
+ entityId: id,
286
+ entityType: 'CrossBorderTransferRecord',
287
+ changes: { destinationCountry: existing.destinationCountry, recipientName: existing.recipientName },
288
+ },
289
+ });
290
+ // {{/if}}
291
+ // {{#if ORM=drizzle}}
292
+ const [existing] = await db.select().from(crossBorderTransferRecords).where(eq(crossBorderTransferRecords.id, id)).limit(1);
293
+ if (!existing) {
294
+ return res.status(404).json({ error: 'Cross-border transfer record not found' });
295
+ }
296
+
297
+ await db.delete(crossBorderTransferRecords).where(eq(crossBorderTransferRecords.id, id));
298
+
299
+ await db.insert(complianceAuditLog).values({
300
+ module: 'cross-border',
301
+ action: 'deleted',
302
+ entityId: id,
303
+ entityType: 'CrossBorderTransferRecord',
304
+ changes: { destinationCountry: existing.destinationCountry, recipientName: existing.recipientName },
305
+ });
306
+ // {{/if}}
307
+ // {{#if ORM=none}}
308
+ const existing = crossBorderStore.get(id);
309
+ if (!existing) {
310
+ return res.status(404).json({ error: 'Cross-border transfer record not found' });
311
+ }
312
+ crossBorderStore.delete(id);
313
+ auditLog.push({ id: newId(), module: 'cross-border', action: 'deleted', entityId: id, at: new Date() });
314
+ // {{/if}}
315
+
316
+ return res.json({ success: true });
317
+ });
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Express — DPIA (Data Protection Impact Assessment) Router
3
+ * Generated by create-ndpr for: {{ORG_NAME}}
4
+ * DPO: {{DPO_EMAIL}}
5
+ *
6
+ * NDPA §28 — a DPIA must be conducted where processing is likely
7
+ * to result in a high risk to the rights and freedoms of data subjects.
8
+ *
9
+ * Routes:
10
+ * GET /dpia — List DPIA records (optional ?status= filter)
11
+ * GET /dpia/:id — Get a single DPIA record
12
+ * POST /dpia — Create a new DPIA record
13
+ * PUT /dpia/:id — Update an existing DPIA record
14
+ * DELETE /dpia/:id — Delete a DPIA record
15
+ *
16
+ * Usage:
17
+ * import { dpiaRouter } from './routes/dpia';
18
+ * app.use('/api/dpia', dpiaRouter);
19
+ */
20
+
21
+ import { Router } from 'express';
22
+ // {{#if ORM=prisma}}
23
+ import { PrismaClient } from '@prisma/client';
24
+
25
+ const prisma = new PrismaClient();
26
+ // {{/if}}
27
+ // {{#if ORM=drizzle}}
28
+ import { db } from '@/drizzle';
29
+ import { dpiaRecords, complianceAuditLog } from '@/drizzle/ndpr-schema';
30
+ import { eq, desc } from 'drizzle-orm';
31
+ // {{/if}}
32
+ // {{#if ORM=none}}
33
+ // TODO ({{ORG_NAME}}): wire this to your persistent store of choice. The
34
+ // in-memory map below is enough to develop against locally but DOES NOT
35
+ // satisfy NDPA Section 44 (record-keeping). Replace `dpiaStore`/`auditLog`
36
+ // with your DB / KV / API.
37
+ interface DPIARecord {
38
+ id: string;
39
+ projectName: string;
40
+ description: string;
41
+ dpiaData: unknown;
42
+ overallRisk: string;
43
+ score: number;
44
+ status: string;
45
+ conductedBy: string;
46
+ approvedBy: string | null;
47
+ createdAt: Date;
48
+ updatedAt: Date;
49
+ }
50
+ const dpiaStore = new Map<string, DPIARecord>();
51
+ const auditLog: Array<{ id: string; module: string; action: string; entityId: string; at: Date }> = [];
52
+ function newId() { return crypto.randomUUID(); }
53
+ // {{/if}}
54
+
55
+ export const dpiaRouter = Router();
56
+
57
+ // GET /dpia?status=draft
58
+ dpiaRouter.get('/', async (req, res) => {
59
+ const { status } = req.query;
60
+ const statusFilter = typeof status === 'string' ? status : undefined;
61
+
62
+ // {{#if ORM=prisma}}
63
+ const records = await prisma.dPIARecord.findMany({
64
+ where: statusFilter ? { status: statusFilter } : undefined,
65
+ orderBy: { createdAt: 'desc' },
66
+ });
67
+ // {{/if}}
68
+ // {{#if ORM=drizzle}}
69
+ const records = statusFilter
70
+ ? await db.select().from(dpiaRecords).where(eq(dpiaRecords.status, statusFilter)).orderBy(desc(dpiaRecords.createdAt))
71
+ : await db.select().from(dpiaRecords).orderBy(desc(dpiaRecords.createdAt));
72
+ // {{/if}}
73
+ // {{#if ORM=none}}
74
+ const records = [...dpiaStore.values()]
75
+ .filter((r) => (statusFilter ? r.status === statusFilter : true))
76
+ .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
77
+ // {{/if}}
78
+
79
+ return res.json(records);
80
+ });
81
+
82
+ // GET /dpia/:id
83
+ dpiaRouter.get('/:id', async (req, res) => {
84
+ // {{#if ORM=prisma}}
85
+ const record = await prisma.dPIARecord.findUnique({ where: { id: req.params.id } });
86
+ // {{/if}}
87
+ // {{#if ORM=drizzle}}
88
+ const [record] = await db.select().from(dpiaRecords).where(eq(dpiaRecords.id, req.params.id)).limit(1);
89
+ // {{/if}}
90
+ // {{#if ORM=none}}
91
+ const record = dpiaStore.get(req.params.id) ?? null;
92
+ // {{/if}}
93
+
94
+ if (!record) {
95
+ return res.status(404).json({ error: 'DPIA record not found' });
96
+ }
97
+
98
+ return res.json(record);
99
+ });
100
+
101
+ // POST /dpia
102
+ dpiaRouter.post('/', async (req, res) => {
103
+ const {
104
+ projectName,
105
+ description,
106
+ dpiaData,
107
+ overallRisk,
108
+ score,
109
+ conductedBy,
110
+ approvedBy,
111
+ } = req.body;
112
+
113
+ if (!projectName || !description || !dpiaData || !overallRisk || score == null || !conductedBy) {
114
+ return res.status(400).json({
115
+ error: 'projectName, description, dpiaData, overallRisk, score, and conductedBy are required',
116
+ });
117
+ }
118
+
119
+ // {{#if ORM=prisma}}
120
+ const record = await prisma.dPIARecord.create({
121
+ data: {
122
+ projectName,
123
+ description,
124
+ dpiaData,
125
+ overallRisk,
126
+ score,
127
+ status: 'draft',
128
+ conductedBy,
129
+ approvedBy: approvedBy ?? null,
130
+ },
131
+ });
132
+
133
+ await prisma.complianceAuditLog.create({
134
+ data: {
135
+ module: 'dpia',
136
+ action: 'created',
137
+ entityId: record.id,
138
+ entityType: 'DPIARecord',
139
+ changes: { projectName, overallRisk, score, status: 'draft' },
140
+ },
141
+ });
142
+ // {{/if}}
143
+ // {{#if ORM=drizzle}}
144
+ const [record] = await db.insert(dpiaRecords).values({
145
+ projectName,
146
+ description,
147
+ dpiaData,
148
+ overallRisk,
149
+ score,
150
+ status: 'draft',
151
+ conductedBy,
152
+ approvedBy: approvedBy ?? null,
153
+ }).returning();
154
+
155
+ await db.insert(complianceAuditLog).values({
156
+ module: 'dpia',
157
+ action: 'created',
158
+ entityId: record.id,
159
+ entityType: 'DPIARecord',
160
+ changes: { projectName, overallRisk, score, status: 'draft' },
161
+ });
162
+ // {{/if}}
163
+ // {{#if ORM=none}}
164
+ const now = new Date();
165
+ const record: DPIARecord = {
166
+ id: newId(),
167
+ projectName,
168
+ description,
169
+ dpiaData,
170
+ overallRisk,
171
+ score,
172
+ status: 'draft',
173
+ conductedBy,
174
+ approvedBy: approvedBy ?? null,
175
+ createdAt: now,
176
+ updatedAt: now,
177
+ };
178
+ dpiaStore.set(record.id, record);
179
+ auditLog.push({ id: newId(), module: 'dpia', action: 'created', entityId: record.id, at: new Date() });
180
+ // {{/if}}
181
+
182
+ return res.status(201).json(record);
183
+ });
184
+
185
+ // PUT /dpia/:id
186
+ dpiaRouter.put('/:id', async (req, res) => {
187
+ const { id } = req.params;
188
+
189
+ // {{#if ORM=prisma}}
190
+ const existing = await prisma.dPIARecord.findUnique({ where: { id } });
191
+ if (!existing) {
192
+ return res.status(404).json({ error: 'DPIA record not found' });
193
+ }
194
+
195
+ const record = await prisma.dPIARecord.update({
196
+ where: { id },
197
+ data: req.body,
198
+ });
199
+
200
+ await prisma.complianceAuditLog.create({
201
+ data: {
202
+ module: 'dpia',
203
+ action: 'updated',
204
+ entityId: record.id,
205
+ entityType: 'DPIARecord',
206
+ changes: req.body,
207
+ },
208
+ });
209
+ // {{/if}}
210
+ // {{#if ORM=drizzle}}
211
+ const [existing] = await db.select().from(dpiaRecords).where(eq(dpiaRecords.id, id)).limit(1);
212
+ if (!existing) {
213
+ return res.status(404).json({ error: 'DPIA record not found' });
214
+ }
215
+
216
+ const [record] = await db.update(dpiaRecords).set(req.body).where(eq(dpiaRecords.id, id)).returning();
217
+
218
+ await db.insert(complianceAuditLog).values({
219
+ module: 'dpia',
220
+ action: 'updated',
221
+ entityId: record.id,
222
+ entityType: 'DPIARecord',
223
+ changes: req.body,
224
+ });
225
+ // {{/if}}
226
+ // {{#if ORM=none}}
227
+ const existing = dpiaStore.get(id);
228
+ if (!existing) {
229
+ return res.status(404).json({ error: 'DPIA record not found' });
230
+ }
231
+ const record: DPIARecord = { ...existing, ...req.body, id, updatedAt: new Date() };
232
+ dpiaStore.set(id, record);
233
+ auditLog.push({ id: newId(), module: 'dpia', action: 'updated', entityId: record.id, at: new Date() });
234
+ // {{/if}}
235
+
236
+ return res.json(record);
237
+ });
238
+
239
+ // DELETE /dpia/:id
240
+ dpiaRouter.delete('/:id', async (req, res) => {
241
+ const { id } = req.params;
242
+
243
+ // {{#if ORM=prisma}}
244
+ const existing = await prisma.dPIARecord.findUnique({ where: { id } });
245
+ if (!existing) {
246
+ return res.status(404).json({ error: 'DPIA record not found' });
247
+ }
248
+
249
+ await prisma.dPIARecord.delete({ where: { id } });
250
+
251
+ await prisma.complianceAuditLog.create({
252
+ data: {
253
+ module: 'dpia',
254
+ action: 'deleted',
255
+ entityId: id,
256
+ entityType: 'DPIARecord',
257
+ changes: { projectName: existing.projectName },
258
+ },
259
+ });
260
+ // {{/if}}
261
+ // {{#if ORM=drizzle}}
262
+ const [existing] = await db.select().from(dpiaRecords).where(eq(dpiaRecords.id, id)).limit(1);
263
+ if (!existing) {
264
+ return res.status(404).json({ error: 'DPIA record not found' });
265
+ }
266
+
267
+ await db.delete(dpiaRecords).where(eq(dpiaRecords.id, id));
268
+
269
+ await db.insert(complianceAuditLog).values({
270
+ module: 'dpia',
271
+ action: 'deleted',
272
+ entityId: id,
273
+ entityType: 'DPIARecord',
274
+ changes: { projectName: existing.projectName },
275
+ });
276
+ // {{/if}}
277
+ // {{#if ORM=none}}
278
+ const existing = dpiaStore.get(id);
279
+ if (!existing) {
280
+ return res.status(404).json({ error: 'DPIA record not found' });
281
+ }
282
+ dpiaStore.delete(id);
283
+ auditLog.push({ id: newId(), module: 'dpia', action: 'deleted', entityId: id, at: new Date() });
284
+ // {{/if}}
285
+
286
+ return res.json({ success: true });
287
+ });