@medicine-wheel/consent-lifecycle 0.2.2
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 +84 -0
- package/dist/alerts.d.ts +74 -0
- package/dist/alerts.d.ts.map +1 -0
- package/dist/alerts.js +152 -0
- package/dist/alerts.js.map +1 -0
- package/dist/cascade.d.ts +48 -0
- package/dist/cascade.d.ts.map +1 -0
- package/dist/cascade.js +97 -0
- package/dist/cascade.js.map +1 -0
- package/dist/ceremony.d.ts +26 -0
- package/dist/ceremony.d.ts.map +1 -0
- package/dist/ceremony.js +81 -0
- package/dist/ceremony.js.map +1 -0
- package/dist/community.d.ts +57 -0
- package/dist/community.d.ts.map +1 -0
- package/dist/community.js +99 -0
- package/dist/community.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle.d.ts +45 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +175 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/schemas.d.ts +298 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +66 -0
- package/dist/schemas.js.map +1 -0
- package/dist/scope.d.ts +56 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +114 -0
- package/dist/scope.js.map +1 -0
- package/dist/types.d.ts +97 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# @medicine-wheel/consent-lifecycle
|
|
2
|
+
|
|
3
|
+
Ongoing relational consent lifecycle for the Medicine Wheel Developer Suite.
|
|
4
|
+
|
|
5
|
+
> Consent as a living relational obligation, not a boolean checkbox.
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Transforms consent from a boolean checkbox into a living relational obligation with lifecycle tracking, renewal, renegotiation, and community-level consent protocols.
|
|
10
|
+
|
|
11
|
+
Wilson's relational accountability means consent is not an event — it's a *relationship*. "Once you are in relationship, you are responsible for that relationship's wellbeing." Consent must be maintained, renewed, and can be withdrawn — with cascading effects on all dependent relations.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @medicine-wheel/consent-lifecycle
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Key Concepts
|
|
20
|
+
|
|
21
|
+
### ConsentState
|
|
22
|
+
|
|
23
|
+
Consent moves through a lifecycle of states:
|
|
24
|
+
- `pending` — consent requested but not yet granted
|
|
25
|
+
- `granted` — consent given but not yet ceremonialized
|
|
26
|
+
- `active` — consent is active and honored through ceremony
|
|
27
|
+
- `renewal-needed` — consent approaching expiration
|
|
28
|
+
- `expired` — consent has lapsed
|
|
29
|
+
- `renegotiating` — scope is being renegotiated
|
|
30
|
+
- `withdrawn` — consent has been withdrawn (terminal)
|
|
31
|
+
|
|
32
|
+
### Cascading Effects
|
|
33
|
+
|
|
34
|
+
When consent is withdrawn or scope changes, all dependent relations are affected. The cascade module computes these effects and ensures nothing falls through the cracks.
|
|
35
|
+
|
|
36
|
+
### Community Consent
|
|
37
|
+
|
|
38
|
+
Community-level consent transcends individual consent. It requires collective decision-making through consensus mechanisms and may require Elder endorsement.
|
|
39
|
+
|
|
40
|
+
## API
|
|
41
|
+
|
|
42
|
+
### Lifecycle Module
|
|
43
|
+
|
|
44
|
+
- `grantConsent(record)` — initial grant
|
|
45
|
+
- `renewConsent(record)` — renew existing consent
|
|
46
|
+
- `renegotiateConsent(record, newScope)` — change scope
|
|
47
|
+
- `withdrawConsent(record, reason)` — withdraw with cascading effects
|
|
48
|
+
- `checkConsentHealth(record)` — health assessment
|
|
49
|
+
|
|
50
|
+
### Scope Module
|
|
51
|
+
|
|
52
|
+
- `defineScope(description, dataTypes, purposes)` — create scope
|
|
53
|
+
- `narrowScope(scope, restrictions)` — add restrictions
|
|
54
|
+
- `widenScope(scope, additions)` — expand scope (requires re-consent)
|
|
55
|
+
- `scopeIncludes(scope, query)` — check if action is within scope
|
|
56
|
+
|
|
57
|
+
### Ceremony Module
|
|
58
|
+
|
|
59
|
+
- `consentCeremony(record, participants)` — record consent ceremony
|
|
60
|
+
- `consentRenewalCeremony(record, participants)` — renewal ceremony
|
|
61
|
+
|
|
62
|
+
### Community Module
|
|
63
|
+
|
|
64
|
+
- `communityConsent(community, scope)` — community-level consent
|
|
65
|
+
- `collectiveDecision(voices)` — consensus mechanism
|
|
66
|
+
- `elderApproval(elderId, record)` — Elder endorsement
|
|
67
|
+
|
|
68
|
+
### Cascade Module
|
|
69
|
+
|
|
70
|
+
- `onWithdrawal(record)` — compute cascading effects
|
|
71
|
+
- `propagateScopeChange(record, newScope)` — update dependent relations
|
|
72
|
+
- `findDependentRelations(record, allRecords)` — find dependencies
|
|
73
|
+
|
|
74
|
+
### Alerts Module
|
|
75
|
+
|
|
76
|
+
- `consentStaleAlert(record)` — warn when consent needs renewal
|
|
77
|
+
- `renewalDue(records)` — find all records needing renewal
|
|
78
|
+
- `scopeMismatch(record, action)` — detect action outside scope
|
|
79
|
+
- `healthCheck(records)` — batch health check
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
|
|
83
|
+
MIT — IAIP Collaborative, Shawinigan, QC
|
|
84
|
+
|
package/dist/alerts.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @medicine-wheel/consent-lifecycle — Alerts Module
|
|
3
|
+
*
|
|
4
|
+
* Consent health monitoring: stale consent warnings, renewal
|
|
5
|
+
* due dates, scope mismatches, and batch health checks.
|
|
6
|
+
* Consent that goes unmonitored decays — these alerts prevent that.
|
|
7
|
+
*/
|
|
8
|
+
import type { ConsentRecord } from './types.js';
|
|
9
|
+
import type { ConsentHealthResult } from './lifecycle.js';
|
|
10
|
+
/**
|
|
11
|
+
* Check whether a consent record is stale and needs renewal.
|
|
12
|
+
* Returns a warning if consent is approaching or past expiration.
|
|
13
|
+
*/
|
|
14
|
+
export declare function consentStaleAlert(record: ConsentRecord): StaleAlert | null;
|
|
15
|
+
/**
|
|
16
|
+
* Find all consent records that need renewal.
|
|
17
|
+
* Returns records sorted by urgency (most urgent first).
|
|
18
|
+
*/
|
|
19
|
+
export declare function renewalDue(records: ConsentRecord[]): RenewalDueResult;
|
|
20
|
+
/**
|
|
21
|
+
* Detect whether an action falls outside the consent scope.
|
|
22
|
+
* Returns a mismatch report if the action is not covered.
|
|
23
|
+
*/
|
|
24
|
+
export declare function scopeMismatch(record: ConsentRecord, action: ActionDescription): ScopeMismatchResult;
|
|
25
|
+
/**
|
|
26
|
+
* Batch health check across all consent records.
|
|
27
|
+
* Returns a comprehensive health report for the entire consent landscape.
|
|
28
|
+
*/
|
|
29
|
+
export declare function healthCheck(records: ConsentRecord[]): BatchHealthResult;
|
|
30
|
+
/** Stale consent alert */
|
|
31
|
+
export interface StaleAlert {
|
|
32
|
+
recordId: string;
|
|
33
|
+
severity: 'info' | 'warning' | 'critical';
|
|
34
|
+
message: string;
|
|
35
|
+
daysUntilExpiry: number | null;
|
|
36
|
+
}
|
|
37
|
+
/** A single renewal item */
|
|
38
|
+
export interface RenewalItem {
|
|
39
|
+
recordId: string;
|
|
40
|
+
grantor: string;
|
|
41
|
+
grantee: string;
|
|
42
|
+
severity: 'info' | 'warning' | 'critical';
|
|
43
|
+
daysUntilExpiry: number | null;
|
|
44
|
+
message: string;
|
|
45
|
+
}
|
|
46
|
+
/** Result of renewal due check */
|
|
47
|
+
export interface RenewalDueResult {
|
|
48
|
+
totalDue: number;
|
|
49
|
+
items: RenewalItem[];
|
|
50
|
+
summary: string;
|
|
51
|
+
}
|
|
52
|
+
/** Description of an action to check against scope */
|
|
53
|
+
export interface ActionDescription {
|
|
54
|
+
dataType: string;
|
|
55
|
+
purpose: string;
|
|
56
|
+
}
|
|
57
|
+
/** Result of scope mismatch check */
|
|
58
|
+
export interface ScopeMismatchResult {
|
|
59
|
+
mismatch: boolean;
|
|
60
|
+
issues: string[];
|
|
61
|
+
recommendation: string;
|
|
62
|
+
}
|
|
63
|
+
/** Batch health check result */
|
|
64
|
+
export interface BatchHealthResult {
|
|
65
|
+
total: number;
|
|
66
|
+
healthy: number;
|
|
67
|
+
unhealthy: number;
|
|
68
|
+
results: Array<{
|
|
69
|
+
recordId: string;
|
|
70
|
+
health: ConsentHealthResult;
|
|
71
|
+
}>;
|
|
72
|
+
summary: string;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=alerts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../src/alerts.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,UAAU,GAAG,IAAI,CAgD1E;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,gBAAgB,CA8BrE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,iBAAiB,GACxB,mBAAmB,CAmCrB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,iBAAiB,CAmBvE;AAID,0BAA0B;AAC1B,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,4BAA4B;AAC5B,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;IAC1C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,kCAAkC;AAClC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,sDAAsD;AACtD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qCAAqC;AACrC,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,gCAAgC;AAChC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,mBAAmB,CAAA;KAAE,CAAC,CAAC;IAClE,OAAO,EAAE,MAAM,CAAC;CACjB"}
|
package/dist/alerts.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @medicine-wheel/consent-lifecycle — Alerts Module
|
|
4
|
+
*
|
|
5
|
+
* Consent health monitoring: stale consent warnings, renewal
|
|
6
|
+
* due dates, scope mismatches, and batch health checks.
|
|
7
|
+
* Consent that goes unmonitored decays — these alerts prevent that.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.consentStaleAlert = consentStaleAlert;
|
|
11
|
+
exports.renewalDue = renewalDue;
|
|
12
|
+
exports.scopeMismatch = scopeMismatch;
|
|
13
|
+
exports.healthCheck = healthCheck;
|
|
14
|
+
const lifecycle_js_1 = require("./lifecycle.js");
|
|
15
|
+
/**
|
|
16
|
+
* Check whether a consent record is stale and needs renewal.
|
|
17
|
+
* Returns a warning if consent is approaching or past expiration.
|
|
18
|
+
*/
|
|
19
|
+
function consentStaleAlert(record) {
|
|
20
|
+
if (record.state === 'withdrawn')
|
|
21
|
+
return null;
|
|
22
|
+
if (!record.expiresAt) {
|
|
23
|
+
// No expiration — check if consent has never been renewed
|
|
24
|
+
if (record.state === 'granted' && !record.renewedAt) {
|
|
25
|
+
return {
|
|
26
|
+
recordId: record.id,
|
|
27
|
+
severity: 'info',
|
|
28
|
+
message: `Consent '${record.id}' has been granted but never renewed or activated through ceremony.`,
|
|
29
|
+
daysUntilExpiry: null,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const now = Date.now();
|
|
35
|
+
const expiryTime = new Date(record.expiresAt).getTime();
|
|
36
|
+
const daysUntilExpiry = Math.ceil((expiryTime - now) / 86400000);
|
|
37
|
+
if (daysUntilExpiry <= 0) {
|
|
38
|
+
return {
|
|
39
|
+
recordId: record.id,
|
|
40
|
+
severity: 'critical',
|
|
41
|
+
message: `Consent '${record.id}' has EXPIRED. Immediate renewal required.`,
|
|
42
|
+
daysUntilExpiry,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (daysUntilExpiry <= 7) {
|
|
46
|
+
return {
|
|
47
|
+
recordId: record.id,
|
|
48
|
+
severity: 'warning',
|
|
49
|
+
message: `Consent '${record.id}' expires in ${daysUntilExpiry} day(s). Schedule renewal ceremony.`,
|
|
50
|
+
daysUntilExpiry,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (daysUntilExpiry <= 30) {
|
|
54
|
+
return {
|
|
55
|
+
recordId: record.id,
|
|
56
|
+
severity: 'info',
|
|
57
|
+
message: `Consent '${record.id}' expires in ${daysUntilExpiry} day(s). Plan for renewal.`,
|
|
58
|
+
daysUntilExpiry,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Find all consent records that need renewal.
|
|
65
|
+
* Returns records sorted by urgency (most urgent first).
|
|
66
|
+
*/
|
|
67
|
+
function renewalDue(records) {
|
|
68
|
+
const due = [];
|
|
69
|
+
for (const record of records) {
|
|
70
|
+
if (record.state === 'withdrawn')
|
|
71
|
+
continue;
|
|
72
|
+
const alert = consentStaleAlert(record);
|
|
73
|
+
if (alert) {
|
|
74
|
+
due.push({
|
|
75
|
+
recordId: record.id,
|
|
76
|
+
grantor: record.grantor,
|
|
77
|
+
grantee: record.grantee,
|
|
78
|
+
severity: alert.severity,
|
|
79
|
+
daysUntilExpiry: alert.daysUntilExpiry,
|
|
80
|
+
message: alert.message,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Sort by urgency: critical first, then warning, then info
|
|
85
|
+
const severityOrder = { critical: 0, warning: 1, info: 2 };
|
|
86
|
+
due.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
|
|
87
|
+
return {
|
|
88
|
+
totalDue: due.length,
|
|
89
|
+
items: due,
|
|
90
|
+
summary: due.length === 0
|
|
91
|
+
? 'No consent records need renewal.'
|
|
92
|
+
: `${due.length} consent record(s) need attention: ${due.filter((d) => d.severity === 'critical').length} critical, ${due.filter((d) => d.severity === 'warning').length} warning.`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Detect whether an action falls outside the consent scope.
|
|
97
|
+
* Returns a mismatch report if the action is not covered.
|
|
98
|
+
*/
|
|
99
|
+
function scopeMismatch(record, action) {
|
|
100
|
+
const issues = [];
|
|
101
|
+
// Check state first
|
|
102
|
+
if (record.state !== 'active' && record.state !== 'granted') {
|
|
103
|
+
issues.push(`Consent is in '${record.state}' state — not active.`);
|
|
104
|
+
}
|
|
105
|
+
// Check data type
|
|
106
|
+
if (!record.scope.dataTypes.some((dt) => dt === action.dataType || dt === '*')) {
|
|
107
|
+
issues.push(`Data type '${action.dataType}' not in consented data types.`);
|
|
108
|
+
}
|
|
109
|
+
// Check purpose
|
|
110
|
+
if (!record.scope.purposes.some((p) => p === action.purpose || p === '*')) {
|
|
111
|
+
issues.push(`Purpose '${action.purpose}' not in consented purposes.`);
|
|
112
|
+
}
|
|
113
|
+
// Check restrictions
|
|
114
|
+
for (const restriction of record.scope.restrictions) {
|
|
115
|
+
if (restriction.toLowerCase().includes(action.dataType.toLowerCase()) ||
|
|
116
|
+
restriction.toLowerCase().includes(action.purpose.toLowerCase())) {
|
|
117
|
+
issues.push(`Action may violate restriction: '${restriction}'.`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
mismatch: issues.length > 0,
|
|
122
|
+
issues,
|
|
123
|
+
recommendation: issues.length > 0
|
|
124
|
+
? 'This action exceeds the current consent scope. Renegotiate consent before proceeding.'
|
|
125
|
+
: 'Action is within consent scope.',
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Batch health check across all consent records.
|
|
130
|
+
* Returns a comprehensive health report for the entire consent landscape.
|
|
131
|
+
*/
|
|
132
|
+
function healthCheck(records) {
|
|
133
|
+
const results = [];
|
|
134
|
+
let healthyCount = 0;
|
|
135
|
+
let unhealthyCount = 0;
|
|
136
|
+
for (const record of records) {
|
|
137
|
+
const health = (0, lifecycle_js_1.checkConsentHealth)(record);
|
|
138
|
+
results.push({ recordId: record.id, health });
|
|
139
|
+
if (health.healthy)
|
|
140
|
+
healthyCount++;
|
|
141
|
+
else
|
|
142
|
+
unhealthyCount++;
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
total: records.length,
|
|
146
|
+
healthy: healthyCount,
|
|
147
|
+
unhealthy: unhealthyCount,
|
|
148
|
+
results,
|
|
149
|
+
summary: `${healthyCount}/${records.length} consent records healthy. ${unhealthyCount} need attention.`,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=alerts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alerts.js","sourceRoot":"","sources":["../src/alerts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @medicine-wheel/consent-lifecycle — Cascade Module
|
|
3
|
+
*
|
|
4
|
+
* Manages cascading effects of consent changes. When consent
|
|
5
|
+
* is withdrawn or scope changes, all dependent relations must
|
|
6
|
+
* be updated. This honors the interconnected nature of consent.
|
|
7
|
+
*/
|
|
8
|
+
import type { ConsentRecord, ConsentScope, ConsentCascade } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Compute the cascading effects of consent withdrawal.
|
|
11
|
+
* All dependent relations must be notified and updated.
|
|
12
|
+
*/
|
|
13
|
+
export declare function onWithdrawal(record: ConsentRecord): ConsentCascade;
|
|
14
|
+
/**
|
|
15
|
+
* Propagate a scope change to all dependent relations.
|
|
16
|
+
* Determines whether dependent relations need review or narrowing.
|
|
17
|
+
*/
|
|
18
|
+
export declare function propagateScopeChange(record: ConsentRecord, newScope: ConsentScope): ScopeChangeResult;
|
|
19
|
+
/**
|
|
20
|
+
* Find all relations that depend on a given consent record.
|
|
21
|
+
* Returns the dependent relation IDs with their dependency nature.
|
|
22
|
+
*/
|
|
23
|
+
export declare function findDependentRelations(record: ConsentRecord, allRecords: ConsentRecord[]): DependencyResult;
|
|
24
|
+
/** Details of how a dependent relation is affected */
|
|
25
|
+
export interface AffectedRelation {
|
|
26
|
+
relationId: string;
|
|
27
|
+
action: 'narrow' | 'review' | 'withdraw' | 'expire';
|
|
28
|
+
reason: string;
|
|
29
|
+
}
|
|
30
|
+
/** Result of scope change propagation */
|
|
31
|
+
export interface ScopeChangeResult {
|
|
32
|
+
cascade: ConsentCascade;
|
|
33
|
+
narrowed: boolean;
|
|
34
|
+
widened: boolean;
|
|
35
|
+
affectedRelations: AffectedRelation[];
|
|
36
|
+
requiresReconsent: boolean;
|
|
37
|
+
summary: string;
|
|
38
|
+
}
|
|
39
|
+
/** Result of dependency analysis */
|
|
40
|
+
export interface DependencyResult {
|
|
41
|
+
directCount: number;
|
|
42
|
+
transitiveCount: number;
|
|
43
|
+
directDependents: string[];
|
|
44
|
+
transitiveDependents: string[];
|
|
45
|
+
totalAffected: number;
|
|
46
|
+
summary: string;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=cascade.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cascade.d.ts","sourceRoot":"","sources":["../src/cascade.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,cAAc,CAMlE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,YAAY,GACrB,iBAAiB,CA8BnB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,aAAa,EAAE,GAC1B,gBAAgB,CA2BlB;AAID,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,CAAC;IACpD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,yCAAyC;AACzC,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;IACtC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,oCAAoC;AACpC,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB"}
|
package/dist/cascade.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @medicine-wheel/consent-lifecycle — Cascade Module
|
|
4
|
+
*
|
|
5
|
+
* Manages cascading effects of consent changes. When consent
|
|
6
|
+
* is withdrawn or scope changes, all dependent relations must
|
|
7
|
+
* be updated. This honors the interconnected nature of consent.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.onWithdrawal = onWithdrawal;
|
|
11
|
+
exports.propagateScopeChange = propagateScopeChange;
|
|
12
|
+
exports.findDependentRelations = findDependentRelations;
|
|
13
|
+
/**
|
|
14
|
+
* Compute the cascading effects of consent withdrawal.
|
|
15
|
+
* All dependent relations must be notified and updated.
|
|
16
|
+
*/
|
|
17
|
+
function onWithdrawal(record) {
|
|
18
|
+
return {
|
|
19
|
+
triggerId: record.id,
|
|
20
|
+
affected: [...record.dependentRelations],
|
|
21
|
+
action: 'withdraw',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Propagate a scope change to all dependent relations.
|
|
26
|
+
* Determines whether dependent relations need review or narrowing.
|
|
27
|
+
*/
|
|
28
|
+
function propagateScopeChange(record, newScope) {
|
|
29
|
+
const narrowed = isNarrower(record.scope, newScope);
|
|
30
|
+
const widened = isWider(record.scope, newScope);
|
|
31
|
+
const cascade = {
|
|
32
|
+
triggerId: record.id,
|
|
33
|
+
affected: [...record.dependentRelations],
|
|
34
|
+
action: narrowed ? 'narrow' : 'review',
|
|
35
|
+
};
|
|
36
|
+
const affectedDetails = record.dependentRelations.map((relId) => ({
|
|
37
|
+
relationId: relId,
|
|
38
|
+
action: narrowed ? 'narrow' : 'review',
|
|
39
|
+
reason: narrowed
|
|
40
|
+
? 'Parent consent scope narrowed — dependent scope must be reviewed.'
|
|
41
|
+
: 'Parent consent scope changed — dependent relations need review.',
|
|
42
|
+
}));
|
|
43
|
+
return {
|
|
44
|
+
cascade,
|
|
45
|
+
narrowed,
|
|
46
|
+
widened,
|
|
47
|
+
affectedRelations: affectedDetails,
|
|
48
|
+
requiresReconsent: widened,
|
|
49
|
+
summary: record.dependentRelations.length === 0
|
|
50
|
+
? 'No dependent relations affected by scope change.'
|
|
51
|
+
: `${record.dependentRelations.length} dependent relation(s) affected. Action: ${cascade.action}.`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Find all relations that depend on a given consent record.
|
|
56
|
+
* Returns the dependent relation IDs with their dependency nature.
|
|
57
|
+
*/
|
|
58
|
+
function findDependentRelations(record, allRecords) {
|
|
59
|
+
// Direct dependents
|
|
60
|
+
const directDependents = record.dependentRelations;
|
|
61
|
+
// Transitive dependents: relations that depend on our dependents
|
|
62
|
+
const transitiveDependents = [];
|
|
63
|
+
for (const depId of directDependents) {
|
|
64
|
+
const depRecord = allRecords.find((r) => r.id === depId);
|
|
65
|
+
if (depRecord) {
|
|
66
|
+
for (const transId of depRecord.dependentRelations) {
|
|
67
|
+
if (!directDependents.includes(transId) && !transitiveDependents.includes(transId)) {
|
|
68
|
+
transitiveDependents.push(transId);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
directCount: directDependents.length,
|
|
75
|
+
transitiveCount: transitiveDependents.length,
|
|
76
|
+
directDependents,
|
|
77
|
+
transitiveDependents,
|
|
78
|
+
totalAffected: directDependents.length + transitiveDependents.length,
|
|
79
|
+
summary: directDependents.length === 0
|
|
80
|
+
? 'No dependent relations found.'
|
|
81
|
+
: `${directDependents.length} direct and ${transitiveDependents.length} transitive dependent(s).`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// ── Internal Helpers ────────────────────────────────────────────────────────
|
|
85
|
+
function isNarrower(oldScope, newScope) {
|
|
86
|
+
const fewerTypes = newScope.dataTypes.length < oldScope.dataTypes.length;
|
|
87
|
+
const fewerPurposes = newScope.purposes.length < oldScope.purposes.length;
|
|
88
|
+
const moreRestrictions = newScope.restrictions.length > oldScope.restrictions.length;
|
|
89
|
+
return fewerTypes || fewerPurposes || moreRestrictions;
|
|
90
|
+
}
|
|
91
|
+
function isWider(oldScope, newScope) {
|
|
92
|
+
const moreTypes = newScope.dataTypes.some((t) => !oldScope.dataTypes.includes(t));
|
|
93
|
+
const morePurposes = newScope.purposes.some((p) => !oldScope.purposes.includes(p));
|
|
94
|
+
const fewerRestrictions = oldScope.restrictions.some((r) => !newScope.restrictions.includes(r));
|
|
95
|
+
return moreTypes || morePurposes || fewerRestrictions;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=cascade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cascade.js","sourceRoot":"","sources":["../src/cascade.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @medicine-wheel/consent-lifecycle — Ceremony Module
|
|
3
|
+
*
|
|
4
|
+
* Records consent ceremonies — the relational acts that formalize
|
|
5
|
+
* consent granting, renewal, and withdrawal. Ceremony honors
|
|
6
|
+
* the relational nature of consent.
|
|
7
|
+
*/
|
|
8
|
+
import type { ConsentRecord } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Record a consent ceremony for initial consent granting.
|
|
11
|
+
* Transitions the consent record to 'active' state and logs
|
|
12
|
+
* the ceremony with participants and witnesses.
|
|
13
|
+
*/
|
|
14
|
+
export declare function consentCeremony(record: ConsentRecord, participants: string[], options?: ConsentCeremonyOptions): ConsentRecord;
|
|
15
|
+
/**
|
|
16
|
+
* Record a consent renewal ceremony.
|
|
17
|
+
* Resets the consent expiration and transitions to 'active' state.
|
|
18
|
+
*/
|
|
19
|
+
export declare function consentRenewalCeremony(record: ConsentRecord, participants: string[], options?: ConsentCeremonyOptions): ConsentRecord;
|
|
20
|
+
/** Options for consent ceremony recording */
|
|
21
|
+
export interface ConsentCeremonyOptions {
|
|
22
|
+
witnessedBy?: string[];
|
|
23
|
+
notes?: string;
|
|
24
|
+
ceremonyId?: string;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=ceremony.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceremony.d.ts","sourceRoot":"","sources":["../src/ceremony.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,aAAa,EAId,MAAM,YAAY,CAAC;AAEpB;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,aAAa,EACrB,YAAY,EAAE,MAAM,EAAE,EACtB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,aAAa,CA4Bf;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,aAAa,EACrB,YAAY,EAAE,MAAM,EAAE,EACtB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,aAAa,CAwCf;AAID,6CAA6C;AAC7C,MAAM,WAAW,sBAAsB;IACrC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB"}
|
package/dist/ceremony.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @medicine-wheel/consent-lifecycle — Ceremony Module
|
|
4
|
+
*
|
|
5
|
+
* Records consent ceremonies — the relational acts that formalize
|
|
6
|
+
* consent granting, renewal, and withdrawal. Ceremony honors
|
|
7
|
+
* the relational nature of consent.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.consentCeremony = consentCeremony;
|
|
11
|
+
exports.consentRenewalCeremony = consentRenewalCeremony;
|
|
12
|
+
/**
|
|
13
|
+
* Record a consent ceremony for initial consent granting.
|
|
14
|
+
* Transitions the consent record to 'active' state and logs
|
|
15
|
+
* the ceremony with participants and witnesses.
|
|
16
|
+
*/
|
|
17
|
+
function consentCeremony(record, participants, options) {
|
|
18
|
+
const now = new Date().toISOString();
|
|
19
|
+
const ceremony = {
|
|
20
|
+
type: 'initial',
|
|
21
|
+
timestamp: now,
|
|
22
|
+
participants,
|
|
23
|
+
witnessedBy: options?.witnessedBy ?? [],
|
|
24
|
+
outcome: 'active',
|
|
25
|
+
notes: options?.notes,
|
|
26
|
+
ceremonyId: options?.ceremonyId,
|
|
27
|
+
};
|
|
28
|
+
const change = {
|
|
29
|
+
from: record.state,
|
|
30
|
+
to: 'active',
|
|
31
|
+
reason: 'Consent ceremony conducted — consent is now active.',
|
|
32
|
+
timestamp: now,
|
|
33
|
+
initiatedBy: record.grantor,
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
...record,
|
|
37
|
+
state: 'active',
|
|
38
|
+
grantedAt: record.grantedAt ?? now,
|
|
39
|
+
ceremonies: [...record.ceremonies, ceremony],
|
|
40
|
+
history: [...record.history, change],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Record a consent renewal ceremony.
|
|
45
|
+
* Resets the consent expiration and transitions to 'active' state.
|
|
46
|
+
*/
|
|
47
|
+
function consentRenewalCeremony(record, participants, options) {
|
|
48
|
+
const validStates = ['active', 'granted', 'renewal-needed'];
|
|
49
|
+
if (!validStates.includes(record.state)) {
|
|
50
|
+
throw new Error(`Cannot conduct renewal ceremony for consent in state '${record.state}'.`);
|
|
51
|
+
}
|
|
52
|
+
const now = new Date().toISOString();
|
|
53
|
+
const ceremony = {
|
|
54
|
+
type: 'renewal',
|
|
55
|
+
timestamp: now,
|
|
56
|
+
participants,
|
|
57
|
+
witnessedBy: options?.witnessedBy ?? [],
|
|
58
|
+
outcome: 'active',
|
|
59
|
+
notes: options?.notes,
|
|
60
|
+
ceremonyId: options?.ceremonyId,
|
|
61
|
+
};
|
|
62
|
+
const change = {
|
|
63
|
+
from: record.state,
|
|
64
|
+
to: 'active',
|
|
65
|
+
reason: 'Consent renewal ceremony conducted.',
|
|
66
|
+
timestamp: now,
|
|
67
|
+
initiatedBy: record.grantor,
|
|
68
|
+
};
|
|
69
|
+
const expiresAt = record.renewalInterval
|
|
70
|
+
? new Date(Date.now() + record.renewalInterval * 86400000).toISOString()
|
|
71
|
+
: record.expiresAt;
|
|
72
|
+
return {
|
|
73
|
+
...record,
|
|
74
|
+
state: 'active',
|
|
75
|
+
renewedAt: now,
|
|
76
|
+
expiresAt,
|
|
77
|
+
ceremonies: [...record.ceremonies, ceremony],
|
|
78
|
+
history: [...record.history, change],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=ceremony.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceremony.js","sourceRoot":"","sources":["../src/ceremony.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AAcH,0CAgCC;AAMD,wDA4CC;AAvFD;;;;GAIG;AACH,SAAgB,eAAe,CAC7B,MAAqB,EACrB,YAAsB,EACtB,OAAgC;IAEhC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAErC,MAAM,QAAQ,GAAoB;QAChC,IAAI,EAAE,SAAS;QACf,SAAS,EAAE,GAAG;QACd,YAAY;QACZ,WAAW,EAAE,OAAO,EAAE,WAAW,IAAI,EAAE;QACvC,OAAO,EAAE,QAAQ;QACjB,KAAK,EAAE,OAAO,EAAE,KAAK;QACrB,UAAU,EAAE,OAAO,EAAE,UAAU;KAChC,CAAC;IAEF,MAAM,MAAM,GAAuB;QACjC,IAAI,EAAE,MAAM,CAAC,KAAK;QAClB,EAAE,EAAE,QAAQ;QACZ,MAAM,EAAE,qDAAqD;QAC7D,SAAS,EAAE,GAAG;QACd,WAAW,EAAE,MAAM,CAAC,OAAO;KAC5B,CAAC;IAEF,OAAO;QACL,GAAG,MAAM;QACT,KAAK,EAAE,QAAQ;QACf,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,GAAG;QAClC,UAAU,EAAE,CAAC,GAAG,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC;QAC5C,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC;KACrC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAgB,sBAAsB,CACpC,MAAqB,EACrB,YAAsB,EACtB,OAAgC;IAEhC,MAAM,WAAW,GAAmB,CAAC,QAAQ,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC;IAC5E,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CACb,yDAAyD,MAAM,CAAC,KAAK,IAAI,CAC1E,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAErC,MAAM,QAAQ,GAAoB;QAChC,IAAI,EAAE,SAAS;QACf,SAAS,EAAE,GAAG;QACd,YAAY;QACZ,WAAW,EAAE,OAAO,EAAE,WAAW,IAAI,EAAE;QACvC,OAAO,EAAE,QAAQ;QACjB,KAAK,EAAE,OAAO,EAAE,KAAK;QACrB,UAAU,EAAE,OAAO,EAAE,UAAU;KAChC,CAAC;IAEF,MAAM,MAAM,GAAuB;QACjC,IAAI,EAAE,MAAM,CAAC,KAAK;QAClB,EAAE,EAAE,QAAQ;QACZ,MAAM,EAAE,qCAAqC;QAC7C,SAAS,EAAE,GAAG;QACd,WAAW,EAAE,MAAM,CAAC,OAAO;KAC5B,CAAC;IAEF,MAAM,SAAS,GAAG,MAAM,CAAC,eAAe;QACtC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,eAAe,GAAG,QAAQ,CAAC,CAAC,WAAW,EAAE;QACxE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC;IAErB,OAAO;QACL,GAAG,MAAM;QACT,KAAK,EAAE,QAAQ;QACf,SAAS,EAAE,GAAG;QACd,SAAS;QACT,UAAU,EAAE,CAAC,GAAG,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC;QAC5C,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC;KACrC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @medicine-wheel/consent-lifecycle — Community Module
|
|
3
|
+
*
|
|
4
|
+
* Community-level consent operations: collective consent,
|
|
5
|
+
* consensus mechanisms, and Elder endorsement.
|
|
6
|
+
* Community consent transcends individual consent — it represents
|
|
7
|
+
* the collective voice of the community.
|
|
8
|
+
*/
|
|
9
|
+
import type { ConsentRecord, ConsentScope } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Create a community-level consent record.
|
|
12
|
+
* Community consent represents collective agreement, not just
|
|
13
|
+
* individual grantor/grantee relations.
|
|
14
|
+
*/
|
|
15
|
+
export declare function communityConsent(community: CommunityInfo, scope: ConsentScope): ConsentRecord;
|
|
16
|
+
/**
|
|
17
|
+
* Consensus mechanism for collective decision-making.
|
|
18
|
+
* Collects voices and determines whether consensus is reached.
|
|
19
|
+
*/
|
|
20
|
+
export declare function collectiveDecision(voices: CommunityVoice[]): ConsensusResult;
|
|
21
|
+
/**
|
|
22
|
+
* Record Elder approval for a consent record.
|
|
23
|
+
* Elder endorsement carries special weight in Indigenous governance.
|
|
24
|
+
*/
|
|
25
|
+
export declare function elderApproval(elderId: string, record: ConsentRecord, options?: ElderApprovalOptions): ConsentRecord;
|
|
26
|
+
/** Information about the community granting consent */
|
|
27
|
+
export interface CommunityInfo {
|
|
28
|
+
name: string;
|
|
29
|
+
grantee: string;
|
|
30
|
+
initiatedBy: string;
|
|
31
|
+
}
|
|
32
|
+
/** A community member's voice in collective decision-making */
|
|
33
|
+
export interface CommunityVoice {
|
|
34
|
+
name: string;
|
|
35
|
+
role: string;
|
|
36
|
+
position: 'approve' | 'object' | 'abstain';
|
|
37
|
+
reason?: string;
|
|
38
|
+
}
|
|
39
|
+
/** Result of a collective consensus process */
|
|
40
|
+
export interface ConsensusResult {
|
|
41
|
+
consensusReached: boolean;
|
|
42
|
+
approvalCount: number;
|
|
43
|
+
objectionCount: number;
|
|
44
|
+
abstainCount: number;
|
|
45
|
+
totalVoices: number;
|
|
46
|
+
objections?: Array<{
|
|
47
|
+
voice: string;
|
|
48
|
+
reason: string;
|
|
49
|
+
}>;
|
|
50
|
+
summary: string;
|
|
51
|
+
}
|
|
52
|
+
/** Options for Elder approval */
|
|
53
|
+
export interface ElderApprovalOptions {
|
|
54
|
+
blessing?: string;
|
|
55
|
+
conditions?: string[];
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=community.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"community.d.ts","sourceRoot":"","sources":["../src/community.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EAEb,MAAM,YAAY,CAAC;AAEpB;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,YAAY,GAClB,aAAa,CA2Bf;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,eAAe,CAiC5E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,aAAa,EACrB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,aAAa,CAef;AAID,uDAAuD;AACvD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,+CAA+C;AAC/C,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,iCAAiC;AACjC,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB"}
|