@reidelsaltres/pureper 0.2.29 → 0.2.31
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.
|
@@ -4,6 +4,7 @@ export type ModuleStruct = {
|
|
|
4
4
|
description?: string;
|
|
5
5
|
icon?: string;
|
|
6
6
|
core?: boolean;
|
|
7
|
+
enabled?: boolean;
|
|
7
8
|
};
|
|
8
9
|
export default class Module {
|
|
9
10
|
readonly name: string;
|
|
@@ -11,6 +12,7 @@ export default class Module {
|
|
|
11
12
|
readonly icon?: string;
|
|
12
13
|
readonly core: boolean;
|
|
13
14
|
readonly enabled: Observable<boolean>;
|
|
15
|
+
readonly downloaded: Observable<boolean>;
|
|
14
16
|
private _registrations;
|
|
15
17
|
constructor(struct: ModuleStruct);
|
|
16
18
|
addRegistration(fn: () => Promise<void>): void;
|
|
@@ -26,10 +28,15 @@ export default class Module {
|
|
|
26
28
|
enable(): void;
|
|
27
29
|
disable(): void;
|
|
28
30
|
isActive(): boolean;
|
|
31
|
+
/** True if enabled but not downloaded — works only in the current session. */
|
|
32
|
+
get ephemeral(): boolean;
|
|
33
|
+
download(): void;
|
|
34
|
+
undownload(): void;
|
|
29
35
|
}
|
|
30
36
|
export declare class ModuleManager {
|
|
31
37
|
private static _modules;
|
|
32
38
|
private static readonly STORAGE_KEY;
|
|
39
|
+
private static readonly SESSION_KEY;
|
|
33
40
|
static register(struct: ModuleStruct): Module;
|
|
34
41
|
static get(name: string): Module | undefined;
|
|
35
42
|
static getAll(): Module[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Module.d.ts","sourceRoot":"","sources":["../../src/foundation/Module.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,mBAAmB,CAAC;AAG3C,MAAM,MAAM,YAAY,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Module.d.ts","sourceRoot":"","sources":["../../src/foundation/Module.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,mBAAmB,CAAC;AAG3C,MAAM,MAAM,YAAY,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,MAAM;IACvB,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrC,SAAgB,IAAI,CAAC,EAAE,MAAM,CAAC;IAC9B,SAAgB,IAAI,EAAE,OAAO,CAAC;IAC9B,SAAgB,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAC7C,SAAgB,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAEhD,OAAO,CAAC,cAAc,CAA+B;gBAElC,MAAM,EAAE,YAAY;IAShC,eAAe,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAI9C,gBAAgB,IAAI,aAAa,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAI7D;;;;;;OAMG;IACI,oBAAoB,CAAC,QAAQ,EAAE,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,IAAI;IAM7D,MAAM,IAAI,IAAI;IAMd,OAAO,IAAI,IAAI;IASf,QAAQ,IAAI,OAAO;IAI1B,8EAA8E;IAC9E,IAAW,SAAS,IAAI,OAAO,CAE9B;IAEM,QAAQ,IAAI,IAAI;IAKhB,UAAU,IAAI,IAAI;CAO5B;AAED,qBAAa,aAAa;IACtB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAkC;IACzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAoB;IACvD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAA4B;IAE/D,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM;IAU7C,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI5C,MAAM,CAAC,MAAM,IAAI,MAAM,EAAE;IAIzB,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAKtC;;;;;;OAMG;IACH,MAAM,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE;IA4BpC,MAAM,CAAC,YAAY,IAAI,IAAI;IAuB3B,MAAM,CAAC,YAAY,IAAI,IAAI;CA4C9B"}
|
package/out/foundation/Module.js
CHANGED
|
@@ -6,13 +6,15 @@ export default class Module {
|
|
|
6
6
|
icon;
|
|
7
7
|
core;
|
|
8
8
|
enabled;
|
|
9
|
+
downloaded;
|
|
9
10
|
_registrations = [];
|
|
10
11
|
constructor(struct) {
|
|
11
12
|
this.name = struct.name;
|
|
12
13
|
this.description = struct.description;
|
|
13
14
|
this.icon = struct.icon;
|
|
14
15
|
this.core = struct.core ?? false;
|
|
15
|
-
this.enabled = new Observable(this.core ? true :
|
|
16
|
+
this.enabled = new Observable(this.core ? true : (struct.enabled ?? false));
|
|
17
|
+
this.downloaded = new Observable(this.core ? true : false);
|
|
16
18
|
}
|
|
17
19
|
addRegistration(fn) {
|
|
18
20
|
this._registrations.push(fn);
|
|
@@ -50,10 +52,26 @@ export default class Module {
|
|
|
50
52
|
isActive() {
|
|
51
53
|
return this.enabled.getObject() === true;
|
|
52
54
|
}
|
|
55
|
+
/** True if enabled but not downloaded — works only in the current session. */
|
|
56
|
+
get ephemeral() {
|
|
57
|
+
return this.isActive() && !this.downloaded.getObject();
|
|
58
|
+
}
|
|
59
|
+
download() {
|
|
60
|
+
this.downloaded.setObject(true);
|
|
61
|
+
console.log(`[Module]: "${this.name}" downloaded`);
|
|
62
|
+
}
|
|
63
|
+
undownload() {
|
|
64
|
+
if (this.core) {
|
|
65
|
+
throw new Error(`[Module]: Cannot remove core module "${this.name}" from downloads`);
|
|
66
|
+
}
|
|
67
|
+
this.downloaded.setObject(false);
|
|
68
|
+
console.log(`[Module]: "${this.name}" removed from downloads`);
|
|
69
|
+
}
|
|
53
70
|
}
|
|
54
71
|
export class ModuleManager {
|
|
55
72
|
static _modules = new Map();
|
|
56
73
|
static STORAGE_KEY = "purper:modules";
|
|
74
|
+
static SESSION_KEY = "purper:modules:session";
|
|
57
75
|
static register(struct) {
|
|
58
76
|
if (this._modules.has(struct.name)) {
|
|
59
77
|
throw new Error(`[Module]: Module "${struct.name}" is already registered`);
|
|
@@ -105,14 +123,21 @@ export class ModuleManager {
|
|
|
105
123
|
return promises;
|
|
106
124
|
}
|
|
107
125
|
static persistState() {
|
|
108
|
-
const
|
|
126
|
+
const localState = {};
|
|
127
|
+
const sessionState = {};
|
|
109
128
|
for (const mod of this._modules.values()) {
|
|
110
|
-
if (
|
|
111
|
-
|
|
129
|
+
if (mod.core)
|
|
130
|
+
continue;
|
|
131
|
+
if (mod.downloaded.getObject()) {
|
|
132
|
+
localState[mod.name] = { enabled: mod.isActive(), downloaded: true };
|
|
133
|
+
}
|
|
134
|
+
else if (mod.isActive()) {
|
|
135
|
+
sessionState[mod.name] = { enabled: true };
|
|
112
136
|
}
|
|
113
137
|
}
|
|
114
138
|
try {
|
|
115
|
-
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(
|
|
139
|
+
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(localState));
|
|
140
|
+
sessionStorage.setItem(this.SESSION_KEY, JSON.stringify(sessionState));
|
|
116
141
|
console.log(`[Module]: Persisted module state`);
|
|
117
142
|
}
|
|
118
143
|
catch (e) {
|
|
@@ -120,21 +145,48 @@ export class ModuleManager {
|
|
|
120
145
|
}
|
|
121
146
|
}
|
|
122
147
|
static restoreState() {
|
|
148
|
+
// Restore from localStorage (downloaded modules)
|
|
123
149
|
try {
|
|
124
150
|
const raw = localStorage.getItem(this.STORAGE_KEY);
|
|
125
|
-
if (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
151
|
+
if (raw) {
|
|
152
|
+
const state = JSON.parse(raw);
|
|
153
|
+
for (const [name, data] of Object.entries(state)) {
|
|
154
|
+
const mod = this._modules.get(name);
|
|
155
|
+
if (mod && !mod.core) {
|
|
156
|
+
if (typeof data === 'boolean') {
|
|
157
|
+
// Old format: value is just a boolean for enabled state
|
|
158
|
+
mod.enabled.setObject(data);
|
|
159
|
+
console.log(`[Module]: Migrated old format "${name}" → enabled=${data}`);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (data.downloaded) {
|
|
163
|
+
mod.downloaded.setObject(true);
|
|
164
|
+
}
|
|
165
|
+
mod.enabled.setObject(data.enabled);
|
|
166
|
+
console.log(`[Module]: Restored "${name}" → downloaded=${data.downloaded}, enabled=${data.enabled}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (e) {
|
|
172
|
+
console.warn(`[Module]: Failed to restore localStorage state`, e);
|
|
173
|
+
}
|
|
174
|
+
// Restore from sessionStorage (ephemeral modules)
|
|
175
|
+
try {
|
|
176
|
+
const raw = sessionStorage.getItem(this.SESSION_KEY);
|
|
177
|
+
if (raw) {
|
|
178
|
+
const state = JSON.parse(raw);
|
|
179
|
+
for (const [name, data] of Object.entries(state)) {
|
|
180
|
+
const mod = this._modules.get(name);
|
|
181
|
+
if (mod && !mod.core && !mod.downloaded.getObject()) {
|
|
182
|
+
mod.enabled.setObject(data.enabled);
|
|
183
|
+
console.log(`[Module]: Restored ephemeral "${name}" → enabled=${data.enabled}`);
|
|
184
|
+
}
|
|
133
185
|
}
|
|
134
186
|
}
|
|
135
187
|
}
|
|
136
188
|
catch (e) {
|
|
137
|
-
console.warn(`[Module]: Failed to restore
|
|
189
|
+
console.warn(`[Module]: Failed to restore sessionStorage state`, e);
|
|
138
190
|
}
|
|
139
191
|
}
|
|
140
192
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Module.js","sourceRoot":"","sources":["../../src/foundation/Module.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"Module.js","sourceRoot":"","sources":["../../src/foundation/Module.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/foundation/Module.ts
CHANGED
|
@@ -6,6 +6,7 @@ export type ModuleStruct = {
|
|
|
6
6
|
description?: string;
|
|
7
7
|
icon?: string;
|
|
8
8
|
core?: boolean;
|
|
9
|
+
enabled?: boolean;
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
export default class Module {
|
|
@@ -14,6 +15,7 @@ export default class Module {
|
|
|
14
15
|
public readonly icon?: string;
|
|
15
16
|
public readonly core: boolean;
|
|
16
17
|
public readonly enabled: Observable<boolean>;
|
|
18
|
+
public readonly downloaded: Observable<boolean>;
|
|
17
19
|
|
|
18
20
|
private _registrations: (() => Promise<void>)[] = [];
|
|
19
21
|
|
|
@@ -22,7 +24,8 @@ export default class Module {
|
|
|
22
24
|
this.description = struct.description;
|
|
23
25
|
this.icon = struct.icon;
|
|
24
26
|
this.core = struct.core ?? false;
|
|
25
|
-
this.enabled = new Observable<boolean>(this.core ? true :
|
|
27
|
+
this.enabled = new Observable<boolean>(this.core ? true : (struct.enabled ?? false));
|
|
28
|
+
this.downloaded = new Observable<boolean>(this.core ? true : false);
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
public addRegistration(fn: () => Promise<void>): void {
|
|
@@ -64,11 +67,30 @@ export default class Module {
|
|
|
64
67
|
public isActive(): boolean {
|
|
65
68
|
return this.enabled.getObject() === true;
|
|
66
69
|
}
|
|
70
|
+
|
|
71
|
+
/** True if enabled but not downloaded — works only in the current session. */
|
|
72
|
+
public get ephemeral(): boolean {
|
|
73
|
+
return this.isActive() && !this.downloaded.getObject();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public download(): void {
|
|
77
|
+
this.downloaded.setObject(true);
|
|
78
|
+
console.log(`[Module]: "${this.name}" downloaded`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public undownload(): void {
|
|
82
|
+
if (this.core) {
|
|
83
|
+
throw new Error(`[Module]: Cannot remove core module "${this.name}" from downloads`);
|
|
84
|
+
}
|
|
85
|
+
this.downloaded.setObject(false);
|
|
86
|
+
console.log(`[Module]: "${this.name}" removed from downloads`);
|
|
87
|
+
}
|
|
67
88
|
}
|
|
68
89
|
|
|
69
90
|
export class ModuleManager {
|
|
70
91
|
private static _modules: Map<string, Module> = new Map();
|
|
71
92
|
private static readonly STORAGE_KEY = "purper:modules";
|
|
93
|
+
private static readonly SESSION_KEY = "purper:modules:session";
|
|
72
94
|
|
|
73
95
|
static register(struct: ModuleStruct): Module {
|
|
74
96
|
if (this._modules.has(struct.name)) {
|
|
@@ -129,14 +151,22 @@ export class ModuleManager {
|
|
|
129
151
|
}
|
|
130
152
|
|
|
131
153
|
static persistState(): void {
|
|
132
|
-
const
|
|
154
|
+
const localState: Record<string, { enabled: boolean, downloaded: boolean }> = {};
|
|
155
|
+
const sessionState: Record<string, { enabled: boolean }> = {};
|
|
156
|
+
|
|
133
157
|
for (const mod of this._modules.values()) {
|
|
134
|
-
if (
|
|
135
|
-
|
|
158
|
+
if (mod.core) continue;
|
|
159
|
+
|
|
160
|
+
if (mod.downloaded.getObject()) {
|
|
161
|
+
localState[mod.name] = { enabled: mod.isActive(), downloaded: true };
|
|
162
|
+
} else if (mod.isActive()) {
|
|
163
|
+
sessionState[mod.name] = { enabled: true };
|
|
136
164
|
}
|
|
137
165
|
}
|
|
166
|
+
|
|
138
167
|
try {
|
|
139
|
-
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(
|
|
168
|
+
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(localState));
|
|
169
|
+
sessionStorage.setItem(this.SESSION_KEY, JSON.stringify(sessionState));
|
|
140
170
|
console.log(`[Module]: Persisted module state`);
|
|
141
171
|
} catch (e) {
|
|
142
172
|
console.warn(`[Module]: Failed to persist module state`, e);
|
|
@@ -144,20 +174,47 @@ export class ModuleManager {
|
|
|
144
174
|
}
|
|
145
175
|
|
|
146
176
|
static restoreState(): void {
|
|
177
|
+
// Restore from localStorage (downloaded modules)
|
|
147
178
|
try {
|
|
148
179
|
const raw = localStorage.getItem(this.STORAGE_KEY);
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
180
|
+
if (raw) {
|
|
181
|
+
const state: Record<string, { enabled: boolean, downloaded: boolean } | boolean> = JSON.parse(raw);
|
|
182
|
+
for (const [name, data] of Object.entries(state)) {
|
|
183
|
+
const mod = this._modules.get(name);
|
|
184
|
+
if (mod && !mod.core) {
|
|
185
|
+
if (typeof data === 'boolean') {
|
|
186
|
+
// Old format: value is just a boolean for enabled state
|
|
187
|
+
mod.enabled.setObject(data);
|
|
188
|
+
console.log(`[Module]: Migrated old format "${name}" → enabled=${data}`);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (data.downloaded) {
|
|
192
|
+
mod.downloaded.setObject(true);
|
|
193
|
+
}
|
|
194
|
+
mod.enabled.setObject(data.enabled);
|
|
195
|
+
console.log(`[Module]: Restored "${name}" → downloaded=${data.downloaded}, enabled=${data.enabled}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} catch (e) {
|
|
200
|
+
console.warn(`[Module]: Failed to restore localStorage state`, e);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Restore from sessionStorage (ephemeral modules)
|
|
204
|
+
try {
|
|
205
|
+
const raw = sessionStorage.getItem(this.SESSION_KEY);
|
|
206
|
+
if (raw) {
|
|
207
|
+
const state: Record<string, { enabled: boolean }> = JSON.parse(raw);
|
|
208
|
+
for (const [name, data] of Object.entries(state)) {
|
|
209
|
+
const mod = this._modules.get(name);
|
|
210
|
+
if (mod && !mod.core && !mod.downloaded.getObject()) {
|
|
211
|
+
mod.enabled.setObject(data.enabled);
|
|
212
|
+
console.log(`[Module]: Restored ephemeral "${name}" → enabled=${data.enabled}`);
|
|
213
|
+
}
|
|
157
214
|
}
|
|
158
215
|
}
|
|
159
216
|
} catch (e) {
|
|
160
|
-
console.warn(`[Module]: Failed to restore
|
|
217
|
+
console.warn(`[Module]: Failed to restore sessionStorage state`, e);
|
|
161
218
|
}
|
|
162
219
|
}
|
|
163
220
|
}
|