@platforma-sdk/model 1.52.7 → 1.53.1
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/dist/block_migrations.cjs +145 -45
- package/dist/block_migrations.cjs.map +1 -1
- package/dist/block_migrations.d.ts +88 -26
- package/dist/block_migrations.d.ts.map +1 -1
- package/dist/block_migrations.js +142 -47
- package/dist/block_migrations.js.map +1 -1
- package/dist/block_storage.cjs +13 -6
- package/dist/block_storage.cjs.map +1 -1
- package/dist/block_storage.d.ts +16 -11
- package/dist/block_storage.d.ts.map +1 -1
- package/dist/block_storage.js +13 -7
- package/dist/block_storage.js.map +1 -1
- package/dist/block_storage_vm.cjs +15 -14
- package/dist/block_storage_vm.cjs.map +1 -1
- package/dist/block_storage_vm.d.ts +1 -1
- package/dist/block_storage_vm.d.ts.map +1 -1
- package/dist/block_storage_vm.js +16 -15
- package/dist/block_storage_vm.js.map +1 -1
- package/dist/components/PlMultiSequenceAlignment.cjs.map +1 -1
- package/dist/components/PlMultiSequenceAlignment.d.ts +5 -3
- package/dist/components/PlMultiSequenceAlignment.d.ts.map +1 -1
- package/dist/components/PlMultiSequenceAlignment.js.map +1 -1
- package/dist/index.cjs +6 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/package.json.cjs +1 -1
- package/dist/package.json.js +1 -1
- package/package.json +7 -7
- package/src/block_migrations.test.ts +170 -0
- package/src/block_migrations.ts +207 -62
- package/src/block_storage.test.ts +23 -22
- package/src/block_storage.ts +23 -16
- package/src/block_storage_vm.ts +21 -19
- package/src/components/PlMultiSequenceAlignment.ts +12 -8
- package/src/index.ts +8 -1
package/dist/block_migrations.js
CHANGED
|
@@ -1,23 +1,85 @@
|
|
|
1
1
|
import { tryRegisterCallback } from './internal.js';
|
|
2
|
-
import { createBlockStorage } from './block_storage.js';
|
|
2
|
+
import { DATA_MODEL_DEFAULT_VERSION, createBlockStorage } from './block_storage.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Helper to define version keys with literal type inference and runtime validation.
|
|
6
|
+
* - Validates that all version values are unique
|
|
7
|
+
* - Eliminates need for `as const` assertion
|
|
8
|
+
*
|
|
9
|
+
* @throws Error if duplicate version values are found
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const Version = defineDataVersions({
|
|
13
|
+
* Initial: 'v1',
|
|
14
|
+
* AddedLabels: 'v2',
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* type VersionedData = {
|
|
18
|
+
* [Version.Initial]: DataV1;
|
|
19
|
+
* [Version.AddedLabels]: DataV2;
|
|
20
|
+
* };
|
|
21
|
+
*/
|
|
22
|
+
function defineDataVersions(versions) {
|
|
23
|
+
const values = Object.values(versions);
|
|
24
|
+
const emptyKeys = Object.keys(versions).filter((key) => versions[key] === '');
|
|
25
|
+
if (emptyKeys.length > 0) {
|
|
26
|
+
throw new Error(`Version values must be non-empty strings (empty: ${emptyKeys.join(', ')})`);
|
|
27
|
+
}
|
|
28
|
+
const unique = new Set(values);
|
|
29
|
+
if (unique.size !== values.length) {
|
|
30
|
+
const duplicates = values.filter((v, i) => values.indexOf(v) !== i);
|
|
31
|
+
throw new Error(`Duplicate version values: ${[...new Set(duplicates)].join(', ')}`);
|
|
32
|
+
}
|
|
33
|
+
return versions;
|
|
34
|
+
}
|
|
35
|
+
/** Create a DataVersioned wrapper with correct shape */
|
|
36
|
+
function makeDataVersioned(version, data) {
|
|
37
|
+
return { version, data };
|
|
38
|
+
}
|
|
39
|
+
/** Thrown by recover() to signal unrecoverable data. */
|
|
40
|
+
class DataUnrecoverableError extends Error {
|
|
41
|
+
name = 'DataUnrecoverableError';
|
|
42
|
+
constructor(dataVersion) {
|
|
43
|
+
super(`Unknown version '${dataVersion}'`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function isDataUnrecoverableError(error) {
|
|
47
|
+
return error instanceof Error && error.name === 'DataUnrecoverableError';
|
|
48
|
+
}
|
|
49
|
+
/** Default recover function for unknown versions */
|
|
50
|
+
const defaultRecover = (version, _data) => {
|
|
51
|
+
throw new DataUnrecoverableError(version);
|
|
52
|
+
};
|
|
4
53
|
/** Internal builder for chaining migrations */
|
|
5
54
|
class DataModelBuilder {
|
|
55
|
+
versionChain;
|
|
6
56
|
migrationSteps;
|
|
7
|
-
|
|
57
|
+
recoverFn;
|
|
58
|
+
constructor(versionChain, steps = [], recoverFn) {
|
|
59
|
+
this.versionChain = versionChain;
|
|
8
60
|
this.migrationSteps = steps;
|
|
61
|
+
this.recoverFn = recoverFn;
|
|
9
62
|
}
|
|
10
|
-
/** Start a migration chain from an initial
|
|
11
|
-
static from() {
|
|
12
|
-
return new DataModelBuilder();
|
|
63
|
+
/** Start a migration chain from an initial version */
|
|
64
|
+
static from(initialVersion) {
|
|
65
|
+
return new DataModelBuilder([initialVersion]);
|
|
66
|
+
}
|
|
67
|
+
/** Add a migration step to the target version */
|
|
68
|
+
migrate(nextVersion, fn) {
|
|
69
|
+
if (this.versionChain.includes(nextVersion)) {
|
|
70
|
+
throw new Error(`Duplicate version '${nextVersion}' in migration chain`);
|
|
71
|
+
}
|
|
72
|
+
const fromVersion = this.versionChain[this.versionChain.length - 1];
|
|
73
|
+
const step = { fromVersion, toVersion: nextVersion, migrate: fn };
|
|
74
|
+
return new DataModelBuilder([...this.versionChain, nextVersion], [...this.migrationSteps, step]);
|
|
13
75
|
}
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
return new DataModelBuilder([...this.migrationSteps, fn
|
|
76
|
+
/** Set recovery handler for unknown or unsupported versions */
|
|
77
|
+
recover(fn) {
|
|
78
|
+
return new DataModelBuilder([...this.versionChain], [...this.migrationSteps], fn);
|
|
17
79
|
}
|
|
18
80
|
/** Finalize with initial data, creating the DataModel */
|
|
19
81
|
create(initialData, ..._) {
|
|
20
|
-
return DataModel._fromBuilder(this.migrationSteps, initialData);
|
|
82
|
+
return DataModel._fromBuilder(this.versionChain, this.migrationSteps, initialData, this.recoverFn);
|
|
21
83
|
}
|
|
22
84
|
}
|
|
23
85
|
/**
|
|
@@ -32,37 +94,61 @@ class DataModelBuilder {
|
|
|
32
94
|
* }));
|
|
33
95
|
*
|
|
34
96
|
* // Data model with migrations
|
|
97
|
+
* const Version = defineDataVersions({
|
|
98
|
+
* V1: 'v1',
|
|
99
|
+
* V2: 'v2',
|
|
100
|
+
* V3: 'v3',
|
|
101
|
+
* });
|
|
102
|
+
*
|
|
103
|
+
* type VersionedData = {
|
|
104
|
+
* [Version.V1]: { numbers: number[] };
|
|
105
|
+
* [Version.V2]: { numbers: number[]; labels: string[] };
|
|
106
|
+
* [Version.V3]: { numbers: number[]; labels: string[]; description: string };
|
|
107
|
+
* };
|
|
108
|
+
*
|
|
35
109
|
* const dataModel = DataModel
|
|
36
|
-
* .from<
|
|
37
|
-
* .migrate((data) => ({ ...data, labels: [] }))
|
|
38
|
-
* .migrate((data) => ({ ...data, description: '' }))
|
|
39
|
-
* .
|
|
110
|
+
* .from<VersionedData>(Version.V1)
|
|
111
|
+
* .migrate(Version.V2, (data) => ({ ...data, labels: [] }))
|
|
112
|
+
* .migrate(Version.V3, (data) => ({ ...data, description: '' }))
|
|
113
|
+
* .recover((version, data) => {
|
|
114
|
+
* if (version === 'legacy' && typeof data === 'object' && data !== null && 'numbers' in data) {
|
|
115
|
+
* return { numbers: (data as { numbers: number[] }).numbers, labels: [], description: '' };
|
|
116
|
+
* }
|
|
117
|
+
* return defaultRecover(version, data);
|
|
118
|
+
* })
|
|
119
|
+
* .create(() => ({ numbers: [], labels: [], description: '' }));
|
|
40
120
|
*/
|
|
41
121
|
class DataModel {
|
|
122
|
+
versionChain;
|
|
42
123
|
steps;
|
|
43
|
-
|
|
44
|
-
|
|
124
|
+
initialDataFn;
|
|
125
|
+
recoverFn;
|
|
126
|
+
constructor(versionChain, steps, initialData, recover = defaultRecover) {
|
|
127
|
+
if (versionChain.length === 0) {
|
|
128
|
+
throw new Error('DataModel requires at least one version key');
|
|
129
|
+
}
|
|
130
|
+
this.versionChain = versionChain;
|
|
45
131
|
this.steps = steps;
|
|
46
|
-
this.
|
|
132
|
+
this.initialDataFn = initialData;
|
|
133
|
+
this.recoverFn = recover;
|
|
47
134
|
}
|
|
48
135
|
/** Start a migration chain from an initial type */
|
|
49
|
-
static from() {
|
|
50
|
-
return DataModelBuilder.from();
|
|
136
|
+
static from(initialVersion) {
|
|
137
|
+
return DataModelBuilder.from(initialVersion);
|
|
51
138
|
}
|
|
52
139
|
/** Create a data model with just initial data (no migrations) */
|
|
53
|
-
static create(initialData) {
|
|
54
|
-
return new DataModel([], initialData);
|
|
140
|
+
static create(initialData, version = DATA_MODEL_DEFAULT_VERSION) {
|
|
141
|
+
return new DataModel([version], [], initialData);
|
|
55
142
|
}
|
|
56
143
|
/** Create from builder (internal use) */
|
|
57
|
-
static _fromBuilder(steps, initialData) {
|
|
58
|
-
return new DataModel(steps, initialData);
|
|
144
|
+
static _fromBuilder(versionChain, steps, initialData, recover) {
|
|
145
|
+
return new DataModel(versionChain, steps, initialData, recover);
|
|
59
146
|
}
|
|
60
147
|
/**
|
|
61
|
-
* Latest version
|
|
62
|
-
* Version 1 = initial state, each migration adds 1.
|
|
148
|
+
* Latest version key.
|
|
63
149
|
*/
|
|
64
150
|
get version() {
|
|
65
|
-
return this.
|
|
151
|
+
return this.versionChain[this.versionChain.length - 1];
|
|
66
152
|
}
|
|
67
153
|
/** Number of migration steps */
|
|
68
154
|
get migrationCount() {
|
|
@@ -70,43 +156,52 @@ class DataModel {
|
|
|
70
156
|
}
|
|
71
157
|
/** Get initial data */
|
|
72
158
|
initialData() {
|
|
73
|
-
return this.
|
|
159
|
+
return this.initialDataFn();
|
|
74
160
|
}
|
|
75
161
|
/** Get default data wrapped with current version */
|
|
76
162
|
getDefaultData() {
|
|
77
|
-
return
|
|
163
|
+
return makeDataVersioned(this.version, this.initialDataFn());
|
|
164
|
+
}
|
|
165
|
+
recoverFrom(data, version) {
|
|
166
|
+
try {
|
|
167
|
+
return { version: this.version, data: this.recoverFn(version, data) };
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
if (isDataUnrecoverableError(error)) {
|
|
171
|
+
return { ...this.getDefaultData(), warning: error.message };
|
|
172
|
+
}
|
|
173
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
174
|
+
return {
|
|
175
|
+
...this.getDefaultData(),
|
|
176
|
+
warning: `Recover failed for version '${version}': ${errorMessage}`,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
78
179
|
}
|
|
79
180
|
/**
|
|
80
|
-
*
|
|
181
|
+
* Migrate versioned data from any version to the latest.
|
|
81
182
|
* Applies only the migrations needed (skips already-applied ones).
|
|
82
183
|
* If a migration fails, returns default data with a warning.
|
|
83
184
|
*/
|
|
84
|
-
|
|
185
|
+
migrate(versioned) {
|
|
85
186
|
const { version: fromVersion, data } = versioned;
|
|
86
|
-
if (fromVersion > this.version) {
|
|
87
|
-
throw new Error(`Cannot downgrade from version ${fromVersion} to ${this.version}`);
|
|
88
|
-
}
|
|
89
187
|
if (fromVersion === this.version) {
|
|
90
188
|
return { version: this.version, data: data };
|
|
91
189
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const migrationsToApply = this.steps.slice(startIndex);
|
|
190
|
+
const startIndex = this.versionChain.indexOf(fromVersion);
|
|
191
|
+
if (startIndex < 0) {
|
|
192
|
+
return this.recoverFrom(data, fromVersion);
|
|
193
|
+
}
|
|
97
194
|
let currentData = data;
|
|
98
|
-
for (let i =
|
|
99
|
-
const
|
|
100
|
-
const fromVer = stepIndex + 1;
|
|
101
|
-
const toVer = stepIndex + 2;
|
|
195
|
+
for (let i = startIndex; i < this.steps.length; i++) {
|
|
196
|
+
const step = this.steps[i];
|
|
102
197
|
try {
|
|
103
|
-
currentData =
|
|
198
|
+
currentData = step.migrate(currentData);
|
|
104
199
|
}
|
|
105
200
|
catch (error) {
|
|
106
201
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
107
202
|
return {
|
|
108
203
|
...this.getDefaultData(),
|
|
109
|
-
warning: `Migration
|
|
204
|
+
warning: `Migration ${step.fromVersion}→${step.toVersion} failed: ${errorMessage}`,
|
|
110
205
|
};
|
|
111
206
|
}
|
|
112
207
|
}
|
|
@@ -118,12 +213,12 @@ class DataModel {
|
|
|
118
213
|
*
|
|
119
214
|
* All callbacks are prefixed with `__pl_` to indicate internal SDK use:
|
|
120
215
|
* - `__pl_data_initial`: returns initial data for new blocks
|
|
121
|
-
* - `
|
|
216
|
+
* - `__pl_data_migrate`: migrates versioned data from any version to latest
|
|
122
217
|
* - `__pl_storage_initial`: returns initial BlockStorage as JSON string
|
|
123
218
|
*/
|
|
124
219
|
registerCallbacks() {
|
|
125
|
-
tryRegisterCallback('__pl_data_initial', () => this.
|
|
126
|
-
tryRegisterCallback('
|
|
220
|
+
tryRegisterCallback('__pl_data_initial', () => this.initialDataFn());
|
|
221
|
+
tryRegisterCallback('__pl_data_migrate', (versioned) => this.migrate(versioned));
|
|
127
222
|
tryRegisterCallback('__pl_storage_initial', () => {
|
|
128
223
|
const { version, data } = this.getDefaultData();
|
|
129
224
|
const storage = createBlockStorage(data, version);
|
|
@@ -132,5 +227,5 @@ class DataModel {
|
|
|
132
227
|
}
|
|
133
228
|
}
|
|
134
229
|
|
|
135
|
-
export { DataModel };
|
|
230
|
+
export { DataModel, DataUnrecoverableError, defaultRecover, defineDataVersions, isDataUnrecoverableError, makeDataVersioned };
|
|
136
231
|
//# sourceMappingURL=block_migrations.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"block_migrations.js","sources":["../src/block_migrations.ts"],"sourcesContent":["import { tryRegisterCallback } from './internal';\nimport { createBlockStorage } from './block_storage';\n\nexport type MigrationFn<From, To> = (prev: Readonly<From>) => To;\n\n/** Versioned data wrapper for persistence */\nexport type Versioned<T> = {\n version: number;\n data: T;\n};\n\n/** Result of upgrade operation, may include warning if migration failed */\nexport type UpgradeResult<T> = Versioned<T> & {\n warning?: string;\n};\n\n/** Internal builder for chaining migrations */\nclass DataModelBuilder<State> {\n private readonly migrationSteps: Array<(x: unknown) => unknown>;\n\n private constructor(steps: Array<(x: unknown) => unknown> = []) {\n this.migrationSteps = steps;\n }\n\n /** Start a migration chain from an initial type */\n static from<T = unknown>(): DataModelBuilder<T> {\n return new DataModelBuilder<T>();\n }\n\n /** Add a migration step */\n migrate<Next>(fn: MigrationFn<State, Next>): DataModelBuilder<Next> {\n return new DataModelBuilder<Next>([...this.migrationSteps, fn as any]);\n }\n\n /** Finalize with initial data, creating the DataModel */\n create<S>(\n initialData: () => S,\n ..._: [State] extends [S] ? [] : [never]\n ): DataModel<S> {\n return DataModel._fromBuilder<S>(this.migrationSteps, initialData);\n }\n}\n\n/**\n * DataModel defines the block's data structure, initial values, and migrations.\n * Used by BlockModelV3 to manage data state.\n *\n * @example\n * // Simple data model (no migrations)\n * const dataModel = DataModel.create<BlockData>(() => ({\n * numbers: [],\n * labels: [],\n * }));\n *\n * // Data model with migrations\n * const dataModel = DataModel\n * .from<V1>()\n * .migrate((data) => ({ ...data, labels: [] })) // v1 → v2\n * .migrate((data) => ({ ...data, description: '' })) // v2 → v3\n * .create<BlockData>(() => ({ numbers: [], labels: [], description: '' }));\n */\nexport class DataModel<State> {\n private readonly steps: Array<(x: unknown) => unknown>;\n private readonly _initialData: () => State;\n\n private constructor(steps: Array<(x: unknown) => unknown>, initialData: () => State) {\n this.steps = steps;\n this._initialData = initialData;\n }\n\n /** Start a migration chain from an initial type */\n static from<S>(): DataModelBuilder<S> {\n return DataModelBuilder.from<S>();\n }\n\n /** Create a data model with just initial data (no migrations) */\n static create<S>(initialData: () => S): DataModel<S> {\n return new DataModel<S>([], initialData);\n }\n\n /** Create from builder (internal use) */\n static _fromBuilder<S>(\n steps: Array<(x: unknown) => unknown>,\n initialData: () => S,\n ): DataModel<S> {\n return new DataModel<S>(steps, initialData);\n }\n\n /**\n * Latest version number.\n * Version 1 = initial state, each migration adds 1.\n */\n get version(): number {\n return this.steps.length + 1;\n }\n\n /** Number of migration steps */\n get migrationCount(): number {\n return this.steps.length;\n }\n\n /** Get initial data */\n initialData(): State {\n return this._initialData();\n }\n\n /** Get default data wrapped with current version */\n getDefaultData(): Versioned<State> {\n return { version: this.version, data: this._initialData() };\n }\n\n /**\n * Upgrade versioned data from any version to the latest.\n * Applies only the migrations needed (skips already-applied ones).\n * If a migration fails, returns default data with a warning.\n */\n upgrade(versioned: Versioned<unknown>): UpgradeResult<State> {\n const { version: fromVersion, data } = versioned;\n\n if (fromVersion > this.version) {\n throw new Error(\n `Cannot downgrade from version ${fromVersion} to ${this.version}`,\n );\n }\n\n if (fromVersion === this.version) {\n return { version: this.version, data: data as State };\n }\n\n // Apply migrations starting from (fromVersion - 1) index\n // Version 1 -> no migrations applied yet -> start at index 0\n // Version 2 -> migration[0] already applied -> start at index 1\n const startIndex = fromVersion - 1;\n const migrationsToApply = this.steps.slice(startIndex);\n\n let currentData: unknown = data;\n for (let i = 0; i < migrationsToApply.length; i++) {\n const stepIndex = startIndex + i;\n const fromVer = stepIndex + 1;\n const toVer = stepIndex + 2;\n try {\n currentData = migrationsToApply[i](currentData);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n ...this.getDefaultData(),\n warning: `Migration v${fromVer}→v${toVer} failed: ${errorMessage}`,\n };\n }\n }\n\n return { version: this.version, data: currentData as State };\n }\n\n /**\n * Register callbacks for use in the VM.\n * Called by BlockModelV3.create() to set up internal callbacks.\n *\n * All callbacks are prefixed with `__pl_` to indicate internal SDK use:\n * - `__pl_data_initial`: returns initial data for new blocks\n * - `__pl_data_upgrade`: upgrades versioned data from any version to latest\n * - `__pl_storage_initial`: returns initial BlockStorage as JSON string\n */\n registerCallbacks(): void {\n tryRegisterCallback('__pl_data_initial', () => this._initialData());\n tryRegisterCallback('__pl_data_upgrade', (versioned: Versioned<unknown>) => this.upgrade(versioned));\n tryRegisterCallback('__pl_storage_initial', () => {\n const { version, data } = this.getDefaultData();\n const storage = createBlockStorage(data, version);\n return JSON.stringify(storage);\n });\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"block_migrations.js","sources":["../src/block_migrations.ts"],"sourcesContent":["import { tryRegisterCallback } from './internal';\nimport { createBlockStorage, DATA_MODEL_DEFAULT_VERSION } from './block_storage';\n\nexport type DataVersionKey = string;\nexport type DataVersionMap = Record<string, unknown>;\nexport type DataMigrateFn<From, To> = (prev: Readonly<From>) => To;\nexport type DataCreateFn<T> = () => T;\nexport type DataRecoverFn<T> = (version: DataVersionKey, data: unknown) => T;\n\n/**\n * Helper to define version keys with literal type inference and runtime validation.\n * - Validates that all version values are unique\n * - Eliminates need for `as const` assertion\n *\n * @throws Error if duplicate version values are found\n *\n * @example\n * const Version = defineDataVersions({\n * Initial: 'v1',\n * AddedLabels: 'v2',\n * });\n *\n * type VersionedData = {\n * [Version.Initial]: DataV1;\n * [Version.AddedLabels]: DataV2;\n * };\n */\nexport function defineDataVersions<const T extends Record<string, string>>(versions: T): T {\n const values = Object.values(versions);\n const emptyKeys = Object.keys(versions).filter((key) => versions[key] === '');\n if (emptyKeys.length > 0) {\n throw new Error(`Version values must be non-empty strings (empty: ${emptyKeys.join(', ')})`);\n }\n const unique = new Set(values);\n if (unique.size !== values.length) {\n const duplicates = values.filter((v, i) => values.indexOf(v) !== i);\n throw new Error(`Duplicate version values: ${[...new Set(duplicates)].join(', ')}`);\n }\n return versions;\n}\n\n/** Versioned data wrapper for persistence */\nexport type DataVersioned<T> = {\n version: DataVersionKey;\n data: T;\n};\n\n/** Create a DataVersioned wrapper with correct shape */\nexport function makeDataVersioned<T>(version: DataVersionKey, data: T): DataVersioned<T> {\n return { version, data };\n}\n\n/** Result of migration operation, may include warning if migration failed */\nexport type DataMigrationResult<T> = DataVersioned<T> & {\n warning?: string;\n};\n\n/** Thrown by recover() to signal unrecoverable data. */\nexport class DataUnrecoverableError extends Error {\n name = 'DataUnrecoverableError';\n constructor(dataVersion: DataVersionKey) {\n super(`Unknown version '${dataVersion}'`);\n }\n}\n\nexport function isDataUnrecoverableError(error: unknown): error is DataUnrecoverableError {\n return error instanceof Error && error.name === 'DataUnrecoverableError';\n}\n\ntype MigrationStep = {\n fromVersion: DataVersionKey;\n toVersion: DataVersionKey;\n migrate: (data: unknown) => unknown;\n};\n\n/** Default recover function for unknown versions */\nexport const defaultRecover: DataRecoverFn<never> = (version, _data) => {\n throw new DataUnrecoverableError(version);\n};\n\n/** Internal builder for chaining migrations */\nclass DataModelBuilder<\n VersionedData extends DataVersionMap,\n CurrentVersion extends keyof VersionedData & string,\n> {\n private readonly versionChain: DataVersionKey[];\n private readonly migrationSteps: MigrationStep[];\n private readonly recoverFn?: DataRecoverFn<VersionedData[CurrentVersion]>;\n\n private constructor(\n versionChain: DataVersionKey[],\n steps: MigrationStep[] = [],\n recoverFn?: DataRecoverFn<VersionedData[CurrentVersion]>,\n ) {\n this.versionChain = versionChain;\n this.migrationSteps = steps;\n this.recoverFn = recoverFn;\n }\n\n /** Start a migration chain from an initial version */\n static from<\n VersionedData extends DataVersionMap,\n InitialVersion extends keyof VersionedData & string = keyof VersionedData & string,\n >(initialVersion: InitialVersion): DataModelBuilder<VersionedData, InitialVersion> {\n return new DataModelBuilder<VersionedData, InitialVersion>([initialVersion]);\n }\n\n /** Add a migration step to the target version */\n migrate<NextVersion extends keyof VersionedData & string>(\n nextVersion: NextVersion,\n fn: DataMigrateFn<VersionedData[CurrentVersion], VersionedData[NextVersion]>,\n ): DataModelBuilder<VersionedData, NextVersion> {\n if (this.versionChain.includes(nextVersion)) {\n throw new Error(`Duplicate version '${nextVersion}' in migration chain`);\n }\n const fromVersion = this.versionChain[this.versionChain.length - 1];\n const step: MigrationStep = { fromVersion, toVersion: nextVersion, migrate: fn as (data: unknown) => unknown };\n return new DataModelBuilder<VersionedData, NextVersion>(\n [...this.versionChain, nextVersion],\n [...this.migrationSteps, step],\n );\n }\n\n /** Set recovery handler for unknown or unsupported versions */\n recover(\n fn: DataRecoverFn<VersionedData[CurrentVersion]>,\n ): DataModelBuilder<VersionedData, CurrentVersion> {\n return new DataModelBuilder<VersionedData, CurrentVersion>(\n [...this.versionChain],\n [...this.migrationSteps],\n fn,\n );\n }\n\n /** Finalize with initial data, creating the DataModel */\n create<S extends VersionedData[CurrentVersion]>(\n initialData: DataCreateFn<S>,\n ..._: [VersionedData[CurrentVersion]] extends [S] ? [] : [never]\n ): DataModel<S> {\n return DataModel._fromBuilder<S>(\n this.versionChain,\n this.migrationSteps,\n initialData,\n this.recoverFn as DataRecoverFn<S> | undefined,\n );\n }\n}\n\n/**\n * DataModel defines the block's data structure, initial values, and migrations.\n * Used by BlockModelV3 to manage data state.\n *\n * @example\n * // Simple data model (no migrations)\n * const dataModel = DataModel.create<BlockData>(() => ({\n * numbers: [],\n * labels: [],\n * }));\n *\n * // Data model with migrations\n * const Version = defineDataVersions({\n * V1: 'v1',\n * V2: 'v2',\n * V3: 'v3',\n * });\n *\n * type VersionedData = {\n * [Version.V1]: { numbers: number[] };\n * [Version.V2]: { numbers: number[]; labels: string[] };\n * [Version.V3]: { numbers: number[]; labels: string[]; description: string };\n * };\n *\n * const dataModel = DataModel\n * .from<VersionedData>(Version.V1)\n * .migrate(Version.V2, (data) => ({ ...data, labels: [] }))\n * .migrate(Version.V3, (data) => ({ ...data, description: '' }))\n * .recover((version, data) => {\n * if (version === 'legacy' && typeof data === 'object' && data !== null && 'numbers' in data) {\n * return { numbers: (data as { numbers: number[] }).numbers, labels: [], description: '' };\n * }\n * return defaultRecover(version, data);\n * })\n * .create(() => ({ numbers: [], labels: [], description: '' }));\n */\nexport class DataModel<State> {\n private readonly versionChain: DataVersionKey[];\n private readonly steps: MigrationStep[];\n private readonly initialDataFn: () => State;\n private readonly recoverFn: DataRecoverFn<State>;\n\n private constructor(\n versionChain: DataVersionKey[],\n steps: MigrationStep[],\n initialData: () => State,\n recover: DataRecoverFn<State> = defaultRecover as DataRecoverFn<State>,\n ) {\n if (versionChain.length === 0) {\n throw new Error('DataModel requires at least one version key');\n }\n this.versionChain = versionChain;\n this.steps = steps;\n this.initialDataFn = initialData;\n this.recoverFn = recover;\n }\n\n /** Start a migration chain from an initial type */\n static from<\n VersionedData extends DataVersionMap,\n InitialVersion extends keyof VersionedData & string = keyof VersionedData & string,\n >(initialVersion: InitialVersion): DataModelBuilder<VersionedData, InitialVersion> {\n return DataModelBuilder.from<VersionedData, InitialVersion>(initialVersion);\n }\n\n /** Create a data model with just initial data (no migrations) */\n static create<S>(initialData: () => S, version: DataVersionKey = DATA_MODEL_DEFAULT_VERSION): DataModel<S> {\n return new DataModel<S>([version], [], initialData);\n }\n\n /** Create from builder (internal use) */\n static _fromBuilder<S>(\n versionChain: DataVersionKey[],\n steps: MigrationStep[],\n initialData: () => S,\n recover?: DataRecoverFn<S>,\n ): DataModel<S> {\n return new DataModel<S>(versionChain, steps, initialData, recover);\n }\n\n /**\n * Latest version key.\n */\n get version(): DataVersionKey {\n return this.versionChain[this.versionChain.length - 1];\n }\n\n /** Number of migration steps */\n get migrationCount(): number {\n return this.steps.length;\n }\n\n /** Get initial data */\n initialData(): State {\n return this.initialDataFn();\n }\n\n /** Get default data wrapped with current version */\n getDefaultData(): DataVersioned<State> {\n return makeDataVersioned(this.version, this.initialDataFn());\n }\n\n private recoverFrom(data: unknown, version: DataVersionKey): DataMigrationResult<State> {\n try {\n return { version: this.version, data: this.recoverFn(version, data) };\n } catch (error) {\n if (isDataUnrecoverableError(error)) {\n return { ...this.getDefaultData(), warning: error.message };\n }\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n ...this.getDefaultData(),\n warning: `Recover failed for version '${version}': ${errorMessage}`,\n };\n }\n }\n\n /**\n * Migrate versioned data from any version to the latest.\n * Applies only the migrations needed (skips already-applied ones).\n * If a migration fails, returns default data with a warning.\n */\n migrate(versioned: DataVersioned<unknown>): DataMigrationResult<State> {\n const { version: fromVersion, data } = versioned;\n\n if (fromVersion === this.version) {\n return { version: this.version, data: data as State };\n }\n\n const startIndex = this.versionChain.indexOf(fromVersion);\n if (startIndex < 0) {\n return this.recoverFrom(data, fromVersion);\n }\n\n let currentData: unknown = data;\n for (let i = startIndex; i < this.steps.length; i++) {\n const step = this.steps[i];\n try {\n currentData = step.migrate(currentData);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n ...this.getDefaultData(),\n warning: `Migration ${step.fromVersion}→${step.toVersion} failed: ${errorMessage}`,\n };\n }\n }\n\n return { version: this.version, data: currentData as State };\n }\n\n /**\n * Register callbacks for use in the VM.\n * Called by BlockModelV3.create() to set up internal callbacks.\n *\n * All callbacks are prefixed with `__pl_` to indicate internal SDK use:\n * - `__pl_data_initial`: returns initial data for new blocks\n * - `__pl_data_migrate`: migrates versioned data from any version to latest\n * - `__pl_storage_initial`: returns initial BlockStorage as JSON string\n */\n registerCallbacks(): void {\n tryRegisterCallback('__pl_data_initial', () => this.initialDataFn());\n tryRegisterCallback('__pl_data_migrate', (versioned: DataVersioned<unknown>) => this.migrate(versioned));\n tryRegisterCallback('__pl_storage_initial', () => {\n const { version, data } = this.getDefaultData();\n const storage = createBlockStorage(data, version);\n return JSON.stringify(storage);\n });\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/block_storage.cjs
CHANGED
|
@@ -23,6 +23,11 @@ const BLOCK_STORAGE_KEY = '__pl_a7f3e2b9__';
|
|
|
23
23
|
* Increment this when the storage structure itself changes (not block state migrations).
|
|
24
24
|
*/
|
|
25
25
|
const BLOCK_STORAGE_SCHEMA_VERSION = 'v1';
|
|
26
|
+
/**
|
|
27
|
+
* Default data version for new blocks without migrations.
|
|
28
|
+
* Unique identifier ensures blocks are created via DataModel API.
|
|
29
|
+
*/
|
|
30
|
+
const DATA_MODEL_DEFAULT_VERSION = '__pl_v1_d4e8f2a1__';
|
|
26
31
|
/**
|
|
27
32
|
* Type guard to check if a value is a valid BlockStorage object.
|
|
28
33
|
* Checks for the discriminator key and valid schema version.
|
|
@@ -42,10 +47,10 @@ function isBlockStorage(value) {
|
|
|
42
47
|
* Creates a BlockStorage with the given initial data
|
|
43
48
|
*
|
|
44
49
|
* @param initialData - The initial data value (defaults to empty object)
|
|
45
|
-
* @param version - The initial data version (defaults to
|
|
50
|
+
* @param version - The initial data version key (defaults to DATA_MODEL_DEFAULT_VERSION)
|
|
46
51
|
* @returns A new BlockStorage instance with discriminator key
|
|
47
52
|
*/
|
|
48
|
-
function createBlockStorage(initialData = {}, version =
|
|
53
|
+
function createBlockStorage(initialData = {}, version = DATA_MODEL_DEFAULT_VERSION) {
|
|
49
54
|
return {
|
|
50
55
|
[BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,
|
|
51
56
|
__dataVersion: version,
|
|
@@ -62,10 +67,11 @@ function createBlockStorage(initialData = {}, version = 1) {
|
|
|
62
67
|
*/
|
|
63
68
|
function normalizeBlockStorage(raw) {
|
|
64
69
|
if (isBlockStorage(raw)) {
|
|
65
|
-
|
|
70
|
+
const storage = raw;
|
|
71
|
+
return { ...storage, __dataVersion: String(storage.__dataVersion) };
|
|
66
72
|
}
|
|
67
73
|
// Legacy format: raw is the state directly
|
|
68
|
-
return createBlockStorage(raw
|
|
74
|
+
return createBlockStorage(raw);
|
|
69
75
|
}
|
|
70
76
|
// =============================================================================
|
|
71
77
|
// Data Access & Update Functions
|
|
@@ -114,7 +120,7 @@ function updateStorageData(storage, payload) {
|
|
|
114
120
|
* Gets the data version from BlockStorage
|
|
115
121
|
*
|
|
116
122
|
* @param storage - The BlockStorage instance
|
|
117
|
-
* @returns The data version
|
|
123
|
+
* @returns The data version key
|
|
118
124
|
*/
|
|
119
125
|
function getStorageDataVersion(storage) {
|
|
120
126
|
return storage.__dataVersion;
|
|
@@ -123,7 +129,7 @@ function getStorageDataVersion(storage) {
|
|
|
123
129
|
* Updates the data version in BlockStorage (immutable)
|
|
124
130
|
*
|
|
125
131
|
* @param storage - The current BlockStorage
|
|
126
|
-
* @param version - The new version
|
|
132
|
+
* @param version - The new version key
|
|
127
133
|
* @returns A new BlockStorage with updated version
|
|
128
134
|
*/
|
|
129
135
|
function updateStorageDataVersion(storage, version) {
|
|
@@ -225,6 +231,7 @@ function mergeBlockStorageHandlers(customHandlers) {
|
|
|
225
231
|
|
|
226
232
|
exports.BLOCK_STORAGE_KEY = BLOCK_STORAGE_KEY;
|
|
227
233
|
exports.BLOCK_STORAGE_SCHEMA_VERSION = BLOCK_STORAGE_SCHEMA_VERSION;
|
|
234
|
+
exports.DATA_MODEL_DEFAULT_VERSION = DATA_MODEL_DEFAULT_VERSION;
|
|
228
235
|
exports.createBlockStorage = createBlockStorage;
|
|
229
236
|
exports.defaultBlockStorageHandlers = defaultBlockStorageHandlers;
|
|
230
237
|
exports.deriveDataFromStorage = deriveDataFromStorage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"block_storage.cjs","sources":["../src/block_storage.ts"],"sourcesContent":["/**\n * BlockStorage - Typed storage abstraction for block persistent data.\n *\n * This module provides:\n * - A typed structure for block storage with versioning and plugin support\n * - Utility functions for manipulating storage\n * - Handler interfaces for model-level customization\n *\n * @module block_storage\n */\n\n// =============================================================================\n// Core Types\n// =============================================================================\n\n/**\n * Discriminator key for BlockStorage format detection.\n * This unique hash-based key identifies data as BlockStorage vs legacy formats.\n */\nexport const BLOCK_STORAGE_KEY = '__pl_a7f3e2b9__';\n\n/**\n * Current BlockStorage schema version.\n * Increment this when the storage structure itself changes (not block state migrations).\n */\nexport const BLOCK_STORAGE_SCHEMA_VERSION = 'v1';\n\n/**\n * Type for valid schema versions\n */\nexport type BlockStorageSchemaVersion = 'v1'; // Add 'v2', 'v3', etc. as schema evolves\n\n/**\n * Plugin key type - keys starting with `@plugin/` are reserved for plugin data\n */\nexport type PluginKey = `@plugin/${string}`;\n\n/**\n * Core BlockStorage type that holds:\n * - __pl_a7f3e2b9__: Schema version (discriminator key identifies BlockStorage format)\n * - __dataVersion: Version number for block data migrations\n * - __data: The block's user-facing data (state)\n * - @plugin/*: Optional plugin-specific data\n */\nexport type BlockStorage<TState = unknown> = {\n /** Schema version - the key itself is the discriminator */\n readonly [BLOCK_STORAGE_KEY]: BlockStorageSchemaVersion;\n /** Version of the block data, used for migrations */\n __dataVersion: number;\n /** The block's user-facing data (state) */\n __data: TState;\n} & {\n /** Plugin-specific data, keyed by `@plugin/<pluginName>` */\n [K in PluginKey]?: unknown;\n};\n\n/**\n * Type guard to check if a value is a valid BlockStorage object.\n * Checks for the discriminator key and valid schema version.\n */\nexport function isBlockStorage(value: unknown): value is BlockStorage {\n if (value === null || typeof value !== 'object') return false;\n const obj = value as Record<string, unknown>;\n const schemaVersion = obj[BLOCK_STORAGE_KEY];\n // Currently only 'v1' is valid, but this allows future versions\n return schemaVersion === 'v1'; // Add more versions as schema evolves\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Creates a BlockStorage with the given initial data\n *\n * @param initialData - The initial data value (defaults to empty object)\n * @param version - The initial data version (defaults to 1)\n * @returns A new BlockStorage instance with discriminator key\n */\nexport function createBlockStorage<TState = unknown>(\n initialData: TState = {} as TState,\n version: number = 1,\n): BlockStorage<TState> {\n return {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: version,\n __data: initialData,\n };\n}\n\n/**\n * Normalizes raw storage data to BlockStorage format.\n * If the input is already a BlockStorage, returns it as-is.\n * If the input is legacy format (raw state), wraps it in BlockStorage structure.\n *\n * @param raw - Raw storage data (may be legacy format or BlockStorage)\n * @returns Normalized BlockStorage\n */\nexport function normalizeBlockStorage<TState = unknown>(raw: unknown): BlockStorage<TState> {\n if (isBlockStorage(raw)) {\n return raw as BlockStorage<TState>;\n }\n // Legacy format: raw is the state directly\n return createBlockStorage(raw as TState, 1);\n}\n\n// =============================================================================\n// Data Access & Update Functions\n// =============================================================================\n\n/**\n * Gets the data from BlockStorage\n *\n * @param storage - The BlockStorage instance\n * @returns The data value\n */\nexport function getStorageData<TState>(storage: BlockStorage<TState>): TState {\n return storage.__data;\n}\n\n/**\n * Derives data from raw block storage.\n * This function is meant to be called from sdk/ui-vue to extract\n * user-facing data from the raw storage returned by the middle layer.\n *\n * The middle layer returns raw storage (opaque to it), and the UI\n * uses this function to derive the actual data value.\n *\n * @param rawStorage - Raw storage data from middle layer (may be any format)\n * @returns The extracted data value, or undefined if storage is undefined/null\n */\nexport function deriveDataFromStorage<TData = unknown>(\n rawStorage: unknown,\n): TData {\n // Normalize to BlockStorage format (handles legacy formats too)\n const storage = normalizeBlockStorage<TData>(rawStorage);\n return getStorageData(storage);\n}\n\n/** Payload for storage mutation operations. SDK defines specific operations. */\nexport type MutateStoragePayload<T = unknown> = { operation: 'update-data'; value: T };\n\n/**\n * Updates the data in BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param payload - The update payload with operation and value\n * @returns A new BlockStorage with updated data\n */\nexport function updateStorageData<TValue = unknown>(\n storage: BlockStorage<TValue>,\n payload: MutateStoragePayload<TValue>,\n): BlockStorage<TValue> {\n switch (payload.operation) {\n case 'update-data':\n return { ...storage, __data: payload.value };\n default:\n throw new Error(`Unknown storage operation: ${(payload as { operation: string }).operation}`);\n }\n}\n\n/**\n * Gets the data version from BlockStorage\n *\n * @param storage - The BlockStorage instance\n * @returns The data version number\n */\nexport function getStorageDataVersion(storage: BlockStorage): number {\n return storage.__dataVersion;\n}\n\n/**\n * Updates the data version in BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param version - The new version number\n * @returns A new BlockStorage with updated version\n */\nexport function updateStorageDataVersion<TState>(\n storage: BlockStorage<TState>,\n version: number,\n): BlockStorage<TState> {\n return { ...storage, __dataVersion: version };\n}\n\n/**\n * Storage debug view returned by __pl_storage_debugView callback.\n * Used by developer tools to display block storage info.\n */\nexport interface StorageDebugView {\n /** Current data version (1-based, starts at 1) */\n dataVersion: number;\n /** Raw data payload stored in BlockStorage */\n data: unknown;\n}\n\n// =============================================================================\n// Plugin Data Functions\n// =============================================================================\n\n/**\n * Gets plugin-specific data from BlockStorage\n *\n * @param storage - The BlockStorage instance\n * @param pluginName - The plugin name (without `@plugin/` prefix)\n * @returns The plugin data or undefined if not set\n */\nexport function getPluginData<TData = unknown>(\n storage: BlockStorage,\n pluginName: string,\n): TData | undefined {\n const key: PluginKey = `@plugin/${pluginName}`;\n return storage[key] as TData | undefined;\n}\n\n/**\n * Sets plugin-specific data in BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param pluginName - The plugin name (without `@plugin/` prefix)\n * @param data - The plugin data to store\n * @returns A new BlockStorage with updated plugin data\n */\nexport function setPluginData<TState>(\n storage: BlockStorage<TState>,\n pluginName: string,\n data: unknown,\n): BlockStorage<TState> {\n const key: PluginKey = `@plugin/${pluginName}`;\n return { ...storage, [key]: data };\n}\n\n/**\n * Removes plugin-specific data from BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param pluginName - The plugin name (without `@plugin/` prefix)\n * @returns A new BlockStorage with the plugin data removed\n */\nexport function removePluginData<TState>(\n storage: BlockStorage<TState>,\n pluginName: string,\n): BlockStorage<TState> {\n const key: PluginKey = `@plugin/${pluginName}`;\n const { [key]: _, ...rest } = storage;\n return rest as BlockStorage<TState>;\n}\n\n/**\n * Gets all plugin names that have data stored\n *\n * @param storage - The BlockStorage instance\n * @returns Array of plugin names (without `@plugin/` prefix)\n */\nexport function getPluginNames(storage: BlockStorage): string[] {\n return Object.keys(storage)\n .filter((key): key is PluginKey => key.startsWith('@plugin/'))\n .map((key) => key.slice('@plugin/'.length));\n}\n\n// =============================================================================\n// Generic Storage Access\n// =============================================================================\n\n/**\n * Gets a value from BlockStorage by key\n *\n * @param storage - The BlockStorage instance\n * @param key - The key to retrieve\n * @returns The value at the given key\n */\nexport function getFromStorage<\n TState,\n K extends keyof BlockStorage<TState>,\n>(storage: BlockStorage<TState>, key: K): BlockStorage<TState>[K] {\n return storage[key];\n}\n\n/**\n * Updates a value in BlockStorage by key (immutable)\n *\n * @param storage - The current BlockStorage\n * @param key - The key to update\n * @param value - The new value\n * @returns A new BlockStorage with the updated value\n */\nexport function updateStorage<\n TState,\n K extends keyof BlockStorage<TState>,\n>(\n storage: BlockStorage<TState>,\n key: K,\n value: BlockStorage<TState>[K],\n): BlockStorage<TState> {\n return { ...storage, [key]: value };\n}\n\n// =============================================================================\n// Storage Handlers (for Phase 2 - Model-Level Customization)\n// =============================================================================\n\n/**\n * Interface for model-configurable storage operations.\n * These handlers allow block models to customize how storage is managed.\n */\nexport interface BlockStorageHandlers<TState = unknown> {\n /**\n * Called when setState is invoked - transforms the new state before storing.\n * Default behavior: replaces the state directly.\n *\n * @param currentStorage - The current BlockStorage\n * @param newState - The new state being set\n * @returns The updated BlockStorage\n */\n transformStateForStorage?: (\n currentStorage: BlockStorage<TState>,\n newState: TState,\n ) => BlockStorage<TState>;\n\n /**\n * Called when reading state for args derivation.\n * Default behavior: returns the state directly.\n *\n * @param storage - The current BlockStorage\n * @returns The state to use for args derivation\n */\n deriveStateForArgs?: (storage: BlockStorage<TState>) => TState;\n\n /**\n * Called during storage schema migration.\n * Default behavior: updates stateVersion only.\n *\n * @param oldStorage - The storage before migration\n * @param fromVersion - The version migrating from\n * @param toVersion - The version migrating to\n * @returns The migrated BlockStorage\n */\n migrateStorage?: (\n oldStorage: BlockStorage<TState>,\n fromVersion: number,\n toVersion: number,\n ) => BlockStorage<TState>;\n}\n\n/**\n * Default implementations of storage handlers\n */\nexport const defaultBlockStorageHandlers: Required<BlockStorageHandlers<unknown>> = {\n transformStateForStorage: <TState>(\n storage: BlockStorage<TState>,\n newState: TState,\n ): BlockStorage<TState> => updateStorageData(storage, { operation: 'update-data', value: newState }),\n\n deriveStateForArgs: <TState>(storage: BlockStorage<TState>): TState => getStorageData(storage),\n\n migrateStorage: <TState>(\n storage: BlockStorage<TState>,\n _fromVersion: number,\n toVersion: number,\n ): BlockStorage<TState> => updateStorageDataVersion(storage, toVersion),\n};\n\n/**\n * Merges custom handlers with defaults\n *\n * @param customHandlers - Custom handlers to merge\n * @returns Complete handlers with defaults for missing functions\n */\nexport function mergeBlockStorageHandlers<TState>(\n customHandlers?: BlockStorageHandlers<TState>,\n): Required<BlockStorageHandlers<TState>> {\n return {\n ...defaultBlockStorageHandlers,\n ...customHandlers,\n } as Required<BlockStorageHandlers<TState>>;\n}\n"],"names":[],"mappings":";;AAAA;;;;;;;;;AASG;AAEH;AACA;AACA;AAEA;;;AAGG;AACI,MAAM,iBAAiB,GAAG;AAEjC;;;AAGG;AACI,MAAM,4BAA4B,GAAG;AA+B5C;;;AAGG;AACG,SAAU,cAAc,CAAC,KAAc,EAAA;AAC3C,IAAA,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;AAAE,QAAA,OAAO,KAAK;IAC7D,MAAM,GAAG,GAAG,KAAgC;AAC5C,IAAA,MAAM,aAAa,GAAG,GAAG,CAAC,iBAAiB,CAAC;;AAE5C,IAAA,OAAO,aAAa,KAAK,IAAI,CAAC;AAChC;AAEA;AACA;AACA;AAEA;;;;;;AAMG;SACa,kBAAkB,CAChC,cAAsB,EAAY,EAClC,UAAkB,CAAC,EAAA;IAEnB,OAAO;QACL,CAAC,iBAAiB,GAAG,4BAA4B;AACjD,QAAA,aAAa,EAAE,OAAO;AACtB,QAAA,MAAM,EAAE,WAAW;KACpB;AACH;AAEA;;;;;;;AAOG;AACG,SAAU,qBAAqB,CAAmB,GAAY,EAAA;AAClE,IAAA,IAAI,cAAc,CAAC,GAAG,CAAC,EAAE;AACvB,QAAA,OAAO,GAA2B;IACpC;;AAEA,IAAA,OAAO,kBAAkB,CAAC,GAAa,EAAE,CAAC,CAAC;AAC7C;AAEA;AACA;AACA;AAEA;;;;;AAKG;AACG,SAAU,cAAc,CAAS,OAA6B,EAAA;IAClE,OAAO,OAAO,CAAC,MAAM;AACvB;AAEA;;;;;;;;;;AAUG;AACG,SAAU,qBAAqB,CACnC,UAAmB,EAAA;;AAGnB,IAAA,MAAM,OAAO,GAAG,qBAAqB,CAAQ,UAAU,CAAC;AACxD,IAAA,OAAO,cAAc,CAAC,OAAO,CAAC;AAChC;AAKA;;;;;;AAMG;AACG,SAAU,iBAAiB,CAC/B,OAA6B,EAC7B,OAAqC,EAAA;AAErC,IAAA,QAAQ,OAAO,CAAC,SAAS;AACvB,QAAA,KAAK,aAAa;YAChB,OAAO,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,KAAK,EAAE;AAC9C,QAAA;YACE,MAAM,IAAI,KAAK,CAAC,CAAA,2BAAA,EAA+B,OAAiC,CAAC,SAAS,CAAA,CAAE,CAAC;;AAEnG;AAEA;;;;;AAKG;AACG,SAAU,qBAAqB,CAAC,OAAqB,EAAA;IACzD,OAAO,OAAO,CAAC,aAAa;AAC9B;AAEA;;;;;;AAMG;AACG,SAAU,wBAAwB,CACtC,OAA6B,EAC7B,OAAe,EAAA;IAEf,OAAO,EAAE,GAAG,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE;AAC/C;AAaA;AACA;AACA;AAEA;;;;;;AAMG;AACG,SAAU,aAAa,CAC3B,OAAqB,EACrB,UAAkB,EAAA;AAElB,IAAA,MAAM,GAAG,GAAc,CAAA,QAAA,EAAW,UAAU,EAAE;AAC9C,IAAA,OAAO,OAAO,CAAC,GAAG,CAAsB;AAC1C;AAEA;;;;;;;AAOG;SACa,aAAa,CAC3B,OAA6B,EAC7B,UAAkB,EAClB,IAAa,EAAA;AAEb,IAAA,MAAM,GAAG,GAAc,CAAA,QAAA,EAAW,UAAU,EAAE;IAC9C,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC,GAAG,GAAG,IAAI,EAAE;AACpC;AAEA;;;;;;AAMG;AACG,SAAU,gBAAgB,CAC9B,OAA6B,EAC7B,UAAkB,EAAA;AAElB,IAAA,MAAM,GAAG,GAAc,CAAA,QAAA,EAAW,UAAU,EAAE;AAC9C,IAAA,MAAM,EAAE,CAAC,GAAG,GAAG,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO;AACrC,IAAA,OAAO,IAA4B;AACrC;AAEA;;;;;AAKG;AACG,SAAU,cAAc,CAAC,OAAqB,EAAA;AAClD,IAAA,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO;AACvB,SAAA,MAAM,CAAC,CAAC,GAAG,KAAuB,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC;AAC5D,SAAA,GAAG,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AAC/C;AAEA;AACA;AACA;AAEA;;;;;;AAMG;AACG,SAAU,cAAc,CAG5B,OAA6B,EAAE,GAAM,EAAA;AACrC,IAAA,OAAO,OAAO,CAAC,GAAG,CAAC;AACrB;AAEA;;;;;;;AAOG;SACa,aAAa,CAI3B,OAA6B,EAC7B,GAAM,EACN,KAA8B,EAAA;IAE9B,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC,GAAG,GAAG,KAAK,EAAE;AACrC;AAiDA;;AAEG;AACI,MAAM,2BAA2B,GAA4C;IAClF,wBAAwB,EAAE,CACxB,OAA6B,EAC7B,QAAgB,KACS,iBAAiB,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAEpG,kBAAkB,EAAE,CAAS,OAA6B,KAAa,cAAc,CAAC,OAAO,CAAC;AAE9F,IAAA,cAAc,EAAE,CACd,OAA6B,EAC7B,YAAoB,EACpB,SAAiB,KACQ,wBAAwB,CAAC,OAAO,EAAE,SAAS,CAAC;;AAGzE;;;;;AAKG;AACG,SAAU,yBAAyB,CACvC,cAA6C,EAAA;IAE7C,OAAO;AACL,QAAA,GAAG,2BAA2B;AAC9B,QAAA,GAAG,cAAc;KACwB;AAC7C;;;;;;;;;;;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"block_storage.cjs","sources":["../src/block_storage.ts"],"sourcesContent":["/**\n * BlockStorage - Typed storage abstraction for block persistent data.\n *\n * This module provides:\n * - A typed structure for block storage with versioning and plugin support\n * - Utility functions for manipulating storage\n * - Handler interfaces for model-level customization\n *\n * @module block_storage\n */\n\n// =============================================================================\n// Core Types\n// =============================================================================\n\n/**\n * Discriminator key for BlockStorage format detection.\n * This unique hash-based key identifies data as BlockStorage vs legacy formats.\n */\nexport const BLOCK_STORAGE_KEY = '__pl_a7f3e2b9__';\n\n/**\n * Current BlockStorage schema version.\n * Increment this when the storage structure itself changes (not block state migrations).\n */\nexport const BLOCK_STORAGE_SCHEMA_VERSION = 'v1';\n\n/**\n * Default data version for new blocks without migrations.\n * Unique identifier ensures blocks are created via DataModel API.\n */\nexport const DATA_MODEL_DEFAULT_VERSION = '__pl_v1_d4e8f2a1__';\n\n/**\n * Type for valid schema versions\n */\nexport type BlockStorageSchemaVersion = 'v1'; // Add 'v2', 'v3', etc. as schema evolves\n\n/**\n * Plugin key type - keys starting with `@plugin/` are reserved for plugin data\n */\nexport type PluginKey = `@plugin/${string}`;\n\n/**\n * Core BlockStorage type that holds:\n * - __pl_a7f3e2b9__: Schema version (discriminator key identifies BlockStorage format)\n * - __dataVersion: Version key for block data migrations\n * - __data: The block's user-facing data (state)\n * - @plugin/*: Optional plugin-specific data\n */\nexport type BlockStorage<TState = unknown> = {\n /** Schema version - the key itself is the discriminator */\n readonly [BLOCK_STORAGE_KEY]: BlockStorageSchemaVersion;\n /** Version of the block data, used for migrations */\n __dataVersion: string;\n /** The block's user-facing data (state) */\n __data: TState;\n} & {\n /** Plugin-specific data, keyed by `@plugin/<pluginName>` */\n [K in PluginKey]?: unknown;\n};\n\n/**\n * Type guard to check if a value is a valid BlockStorage object.\n * Checks for the discriminator key and valid schema version.\n */\nexport function isBlockStorage(value: unknown): value is BlockStorage {\n if (value === null || typeof value !== 'object') return false;\n const obj = value as Record<string, unknown>;\n const schemaVersion = obj[BLOCK_STORAGE_KEY];\n // Currently only 'v1' is valid, but this allows future versions\n return schemaVersion === 'v1'; // Add more versions as schema evolves\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Creates a BlockStorage with the given initial data\n *\n * @param initialData - The initial data value (defaults to empty object)\n * @param version - The initial data version key (defaults to DATA_MODEL_DEFAULT_VERSION)\n * @returns A new BlockStorage instance with discriminator key\n */\nexport function createBlockStorage<TState = unknown>(\n initialData: TState = {} as TState,\n version: string = DATA_MODEL_DEFAULT_VERSION,\n): BlockStorage<TState> {\n return {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: version,\n __data: initialData,\n };\n}\n\n/**\n * Normalizes raw storage data to BlockStorage format.\n * If the input is already a BlockStorage, returns it as-is.\n * If the input is legacy format (raw state), wraps it in BlockStorage structure.\n *\n * @param raw - Raw storage data (may be legacy format or BlockStorage)\n * @returns Normalized BlockStorage\n */\nexport function normalizeBlockStorage<TState = unknown>(raw: unknown): BlockStorage<TState> {\n if (isBlockStorage(raw)) {\n const storage = raw as BlockStorage<TState>;\n return { ...storage, __dataVersion: String(storage.__dataVersion) };\n }\n // Legacy format: raw is the state directly\n return createBlockStorage(raw as TState);\n}\n\n// =============================================================================\n// Data Access & Update Functions\n// =============================================================================\n\n/**\n * Gets the data from BlockStorage\n *\n * @param storage - The BlockStorage instance\n * @returns The data value\n */\nexport function getStorageData<TState>(storage: BlockStorage<TState>): TState {\n return storage.__data;\n}\n\n/**\n * Derives data from raw block storage.\n * This function is meant to be called from sdk/ui-vue to extract\n * user-facing data from the raw storage returned by the middle layer.\n *\n * The middle layer returns raw storage (opaque to it), and the UI\n * uses this function to derive the actual data value.\n *\n * @param rawStorage - Raw storage data from middle layer (may be any format)\n * @returns The extracted data value, or undefined if storage is undefined/null\n */\nexport function deriveDataFromStorage<TData = unknown>(\n rawStorage: unknown,\n): TData {\n // Normalize to BlockStorage format (handles legacy formats too)\n const storage = normalizeBlockStorage<TData>(rawStorage);\n return getStorageData(storage);\n}\n\n/** Payload for storage mutation operations. SDK defines specific operations. */\nexport type MutateStoragePayload<T = unknown> = { operation: 'update-data'; value: T };\n\n/**\n * Updates the data in BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param payload - The update payload with operation and value\n * @returns A new BlockStorage with updated data\n */\nexport function updateStorageData<TValue = unknown>(\n storage: BlockStorage<TValue>,\n payload: MutateStoragePayload<TValue>,\n): BlockStorage<TValue> {\n switch (payload.operation) {\n case 'update-data':\n return { ...storage, __data: payload.value };\n default:\n throw new Error(`Unknown storage operation: ${(payload as { operation: string }).operation}`);\n }\n}\n\n/**\n * Gets the data version from BlockStorage\n *\n * @param storage - The BlockStorage instance\n * @returns The data version key\n */\nexport function getStorageDataVersion(storage: BlockStorage): string {\n return storage.__dataVersion;\n}\n\n/**\n * Updates the data version in BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param version - The new version key\n * @returns A new BlockStorage with updated version\n */\nexport function updateStorageDataVersion<TState>(\n storage: BlockStorage<TState>,\n version: string,\n): BlockStorage<TState> {\n return { ...storage, __dataVersion: version };\n}\n\n/**\n * Storage debug view returned by __pl_storage_debugView callback.\n * Used by developer tools to display block storage info.\n */\nexport interface StorageDebugView {\n /** Current data version key */\n dataVersion: string;\n /** Raw data payload stored in BlockStorage */\n data: unknown;\n}\n\n// =============================================================================\n// Plugin Data Functions\n// =============================================================================\n\n/**\n * Gets plugin-specific data from BlockStorage\n *\n * @param storage - The BlockStorage instance\n * @param pluginName - The plugin name (without `@plugin/` prefix)\n * @returns The plugin data or undefined if not set\n */\nexport function getPluginData<TData = unknown>(\n storage: BlockStorage,\n pluginName: string,\n): TData | undefined {\n const key: PluginKey = `@plugin/${pluginName}`;\n return storage[key] as TData | undefined;\n}\n\n/**\n * Sets plugin-specific data in BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param pluginName - The plugin name (without `@plugin/` prefix)\n * @param data - The plugin data to store\n * @returns A new BlockStorage with updated plugin data\n */\nexport function setPluginData<TState>(\n storage: BlockStorage<TState>,\n pluginName: string,\n data: unknown,\n): BlockStorage<TState> {\n const key: PluginKey = `@plugin/${pluginName}`;\n return { ...storage, [key]: data };\n}\n\n/**\n * Removes plugin-specific data from BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param pluginName - The plugin name (without `@plugin/` prefix)\n * @returns A new BlockStorage with the plugin data removed\n */\nexport function removePluginData<TState>(\n storage: BlockStorage<TState>,\n pluginName: string,\n): BlockStorage<TState> {\n const key: PluginKey = `@plugin/${pluginName}`;\n const { [key]: _, ...rest } = storage;\n return rest as BlockStorage<TState>;\n}\n\n/**\n * Gets all plugin names that have data stored\n *\n * @param storage - The BlockStorage instance\n * @returns Array of plugin names (without `@plugin/` prefix)\n */\nexport function getPluginNames(storage: BlockStorage): string[] {\n return Object.keys(storage)\n .filter((key): key is PluginKey => key.startsWith('@plugin/'))\n .map((key) => key.slice('@plugin/'.length));\n}\n\n// =============================================================================\n// Generic Storage Access\n// =============================================================================\n\n/**\n * Gets a value from BlockStorage by key\n *\n * @param storage - The BlockStorage instance\n * @param key - The key to retrieve\n * @returns The value at the given key\n */\nexport function getFromStorage<\n TState,\n K extends keyof BlockStorage<TState>,\n>(storage: BlockStorage<TState>, key: K): BlockStorage<TState>[K] {\n return storage[key];\n}\n\n/**\n * Updates a value in BlockStorage by key (immutable)\n *\n * @param storage - The current BlockStorage\n * @param key - The key to update\n * @param value - The new value\n * @returns A new BlockStorage with the updated value\n */\nexport function updateStorage<\n TState,\n K extends keyof BlockStorage<TState>,\n>(\n storage: BlockStorage<TState>,\n key: K,\n value: BlockStorage<TState>[K],\n): BlockStorage<TState> {\n return { ...storage, [key]: value };\n}\n\n// =============================================================================\n// Storage Handlers (for Phase 2 - Model-Level Customization)\n// =============================================================================\n\n/**\n * Interface for model-configurable storage operations.\n * These handlers allow block models to customize how storage is managed.\n */\nexport interface BlockStorageHandlers<TState = unknown> {\n /**\n * Called when setState is invoked - transforms the new state before storing.\n * Default behavior: replaces the state directly.\n *\n * @param currentStorage - The current BlockStorage\n * @param newState - The new state being set\n * @returns The updated BlockStorage\n */\n transformStateForStorage?: (\n currentStorage: BlockStorage<TState>,\n newState: TState,\n ) => BlockStorage<TState>;\n\n /**\n * Called when reading state for args derivation.\n * Default behavior: returns the state directly.\n *\n * @param storage - The current BlockStorage\n * @returns The state to use for args derivation\n */\n deriveStateForArgs?: (storage: BlockStorage<TState>) => TState;\n\n /**\n * Called during storage schema migration.\n * Default behavior: updates stateVersion only.\n *\n * @param oldStorage - The storage before migration\n * @param fromVersion - The version migrating from\n * @param toVersion - The version migrating to\n * @returns The migrated BlockStorage\n */\n migrateStorage?: (\n oldStorage: BlockStorage<TState>,\n fromVersion: string,\n toVersion: string,\n ) => BlockStorage<TState>;\n}\n\n/**\n * Default implementations of storage handlers\n */\nexport const defaultBlockStorageHandlers: Required<BlockStorageHandlers<unknown>> = {\n transformStateForStorage: <TState>(\n storage: BlockStorage<TState>,\n newState: TState,\n ): BlockStorage<TState> => updateStorageData(storage, { operation: 'update-data', value: newState }),\n\n deriveStateForArgs: <TState>(storage: BlockStorage<TState>): TState => getStorageData(storage),\n\n migrateStorage: <TState>(\n storage: BlockStorage<TState>,\n _fromVersion: string,\n toVersion: string,\n ): BlockStorage<TState> => updateStorageDataVersion(storage, toVersion),\n};\n\n/**\n * Merges custom handlers with defaults\n *\n * @param customHandlers - Custom handlers to merge\n * @returns Complete handlers with defaults for missing functions\n */\nexport function mergeBlockStorageHandlers<TState>(\n customHandlers?: BlockStorageHandlers<TState>,\n): Required<BlockStorageHandlers<TState>> {\n return {\n ...defaultBlockStorageHandlers,\n ...customHandlers,\n } as Required<BlockStorageHandlers<TState>>;\n}\n"],"names":[],"mappings":"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"}
|
package/dist/block_storage.d.ts
CHANGED
|
@@ -18,6 +18,11 @@ export declare const BLOCK_STORAGE_KEY = "__pl_a7f3e2b9__";
|
|
|
18
18
|
* Increment this when the storage structure itself changes (not block state migrations).
|
|
19
19
|
*/
|
|
20
20
|
export declare const BLOCK_STORAGE_SCHEMA_VERSION = "v1";
|
|
21
|
+
/**
|
|
22
|
+
* Default data version for new blocks without migrations.
|
|
23
|
+
* Unique identifier ensures blocks are created via DataModel API.
|
|
24
|
+
*/
|
|
25
|
+
export declare const DATA_MODEL_DEFAULT_VERSION = "__pl_v1_d4e8f2a1__";
|
|
21
26
|
/**
|
|
22
27
|
* Type for valid schema versions
|
|
23
28
|
*/
|
|
@@ -29,7 +34,7 @@ export type PluginKey = `@plugin/${string}`;
|
|
|
29
34
|
/**
|
|
30
35
|
* Core BlockStorage type that holds:
|
|
31
36
|
* - __pl_a7f3e2b9__: Schema version (discriminator key identifies BlockStorage format)
|
|
32
|
-
* - __dataVersion: Version
|
|
37
|
+
* - __dataVersion: Version key for block data migrations
|
|
33
38
|
* - __data: The block's user-facing data (state)
|
|
34
39
|
* - @plugin/*: Optional plugin-specific data
|
|
35
40
|
*/
|
|
@@ -37,7 +42,7 @@ export type BlockStorage<TState = unknown> = {
|
|
|
37
42
|
/** Schema version - the key itself is the discriminator */
|
|
38
43
|
readonly [BLOCK_STORAGE_KEY]: BlockStorageSchemaVersion;
|
|
39
44
|
/** Version of the block data, used for migrations */
|
|
40
|
-
__dataVersion:
|
|
45
|
+
__dataVersion: string;
|
|
41
46
|
/** The block's user-facing data (state) */
|
|
42
47
|
__data: TState;
|
|
43
48
|
} & {
|
|
@@ -52,10 +57,10 @@ export declare function isBlockStorage(value: unknown): value is BlockStorage;
|
|
|
52
57
|
* Creates a BlockStorage with the given initial data
|
|
53
58
|
*
|
|
54
59
|
* @param initialData - The initial data value (defaults to empty object)
|
|
55
|
-
* @param version - The initial data version (defaults to
|
|
60
|
+
* @param version - The initial data version key (defaults to DATA_MODEL_DEFAULT_VERSION)
|
|
56
61
|
* @returns A new BlockStorage instance with discriminator key
|
|
57
62
|
*/
|
|
58
|
-
export declare function createBlockStorage<TState = unknown>(initialData?: TState, version?:
|
|
63
|
+
export declare function createBlockStorage<TState = unknown>(initialData?: TState, version?: string): BlockStorage<TState>;
|
|
59
64
|
/**
|
|
60
65
|
* Normalizes raw storage data to BlockStorage format.
|
|
61
66
|
* If the input is already a BlockStorage, returns it as-is.
|
|
@@ -101,24 +106,24 @@ export declare function updateStorageData<TValue = unknown>(storage: BlockStorag
|
|
|
101
106
|
* Gets the data version from BlockStorage
|
|
102
107
|
*
|
|
103
108
|
* @param storage - The BlockStorage instance
|
|
104
|
-
* @returns The data version
|
|
109
|
+
* @returns The data version key
|
|
105
110
|
*/
|
|
106
|
-
export declare function getStorageDataVersion(storage: BlockStorage):
|
|
111
|
+
export declare function getStorageDataVersion(storage: BlockStorage): string;
|
|
107
112
|
/**
|
|
108
113
|
* Updates the data version in BlockStorage (immutable)
|
|
109
114
|
*
|
|
110
115
|
* @param storage - The current BlockStorage
|
|
111
|
-
* @param version - The new version
|
|
116
|
+
* @param version - The new version key
|
|
112
117
|
* @returns A new BlockStorage with updated version
|
|
113
118
|
*/
|
|
114
|
-
export declare function updateStorageDataVersion<TState>(storage: BlockStorage<TState>, version:
|
|
119
|
+
export declare function updateStorageDataVersion<TState>(storage: BlockStorage<TState>, version: string): BlockStorage<TState>;
|
|
115
120
|
/**
|
|
116
121
|
* Storage debug view returned by __pl_storage_debugView callback.
|
|
117
122
|
* Used by developer tools to display block storage info.
|
|
118
123
|
*/
|
|
119
124
|
export interface StorageDebugView {
|
|
120
|
-
/** Current data version
|
|
121
|
-
dataVersion:
|
|
125
|
+
/** Current data version key */
|
|
126
|
+
dataVersion: string;
|
|
122
127
|
/** Raw data payload stored in BlockStorage */
|
|
123
128
|
data: unknown;
|
|
124
129
|
}
|
|
@@ -202,7 +207,7 @@ export interface BlockStorageHandlers<TState = unknown> {
|
|
|
202
207
|
* @param toVersion - The version migrating to
|
|
203
208
|
* @returns The migrated BlockStorage
|
|
204
209
|
*/
|
|
205
|
-
migrateStorage?: (oldStorage: BlockStorage<TState>, fromVersion:
|
|
210
|
+
migrateStorage?: (oldStorage: BlockStorage<TState>, fromVersion: string, toVersion: string) => BlockStorage<TState>;
|
|
206
211
|
}
|
|
207
212
|
/**
|
|
208
213
|
* Default implementations of storage handlers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"block_storage.d.ts","sourceRoot":"","sources":["../src/block_storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH;;;GAGG;AACH,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,WAAW,MAAM,EAAE,CAAC;AAE5C;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,CAAC,MAAM,GAAG,OAAO,IAAI;IAC3C,2DAA2D;IAC3D,QAAQ,CAAC,CAAC,iBAAiB,CAAC,EAAE,yBAAyB,CAAC;IACxD,qDAAqD;IACrD,aAAa,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG;KAED,CAAC,IAAI,SAAS,CAAC,CAAC,EAAE,OAAO;CAC3B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAMpE;AAMD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAG,OAAO,EACjD,WAAW,GAAE,MAAqB,EAClC,OAAO,GAAE,
|
|
1
|
+
{"version":3,"file":"block_storage.d.ts","sourceRoot":"","sources":["../src/block_storage.ts"],"names":[],"mappings":"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"}
|
package/dist/block_storage.js
CHANGED
|
@@ -21,6 +21,11 @@ const BLOCK_STORAGE_KEY = '__pl_a7f3e2b9__';
|
|
|
21
21
|
* Increment this when the storage structure itself changes (not block state migrations).
|
|
22
22
|
*/
|
|
23
23
|
const BLOCK_STORAGE_SCHEMA_VERSION = 'v1';
|
|
24
|
+
/**
|
|
25
|
+
* Default data version for new blocks without migrations.
|
|
26
|
+
* Unique identifier ensures blocks are created via DataModel API.
|
|
27
|
+
*/
|
|
28
|
+
const DATA_MODEL_DEFAULT_VERSION = '__pl_v1_d4e8f2a1__';
|
|
24
29
|
/**
|
|
25
30
|
* Type guard to check if a value is a valid BlockStorage object.
|
|
26
31
|
* Checks for the discriminator key and valid schema version.
|
|
@@ -40,10 +45,10 @@ function isBlockStorage(value) {
|
|
|
40
45
|
* Creates a BlockStorage with the given initial data
|
|
41
46
|
*
|
|
42
47
|
* @param initialData - The initial data value (defaults to empty object)
|
|
43
|
-
* @param version - The initial data version (defaults to
|
|
48
|
+
* @param version - The initial data version key (defaults to DATA_MODEL_DEFAULT_VERSION)
|
|
44
49
|
* @returns A new BlockStorage instance with discriminator key
|
|
45
50
|
*/
|
|
46
|
-
function createBlockStorage(initialData = {}, version =
|
|
51
|
+
function createBlockStorage(initialData = {}, version = DATA_MODEL_DEFAULT_VERSION) {
|
|
47
52
|
return {
|
|
48
53
|
[BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,
|
|
49
54
|
__dataVersion: version,
|
|
@@ -60,10 +65,11 @@ function createBlockStorage(initialData = {}, version = 1) {
|
|
|
60
65
|
*/
|
|
61
66
|
function normalizeBlockStorage(raw) {
|
|
62
67
|
if (isBlockStorage(raw)) {
|
|
63
|
-
|
|
68
|
+
const storage = raw;
|
|
69
|
+
return { ...storage, __dataVersion: String(storage.__dataVersion) };
|
|
64
70
|
}
|
|
65
71
|
// Legacy format: raw is the state directly
|
|
66
|
-
return createBlockStorage(raw
|
|
72
|
+
return createBlockStorage(raw);
|
|
67
73
|
}
|
|
68
74
|
// =============================================================================
|
|
69
75
|
// Data Access & Update Functions
|
|
@@ -112,7 +118,7 @@ function updateStorageData(storage, payload) {
|
|
|
112
118
|
* Gets the data version from BlockStorage
|
|
113
119
|
*
|
|
114
120
|
* @param storage - The BlockStorage instance
|
|
115
|
-
* @returns The data version
|
|
121
|
+
* @returns The data version key
|
|
116
122
|
*/
|
|
117
123
|
function getStorageDataVersion(storage) {
|
|
118
124
|
return storage.__dataVersion;
|
|
@@ -121,7 +127,7 @@ function getStorageDataVersion(storage) {
|
|
|
121
127
|
* Updates the data version in BlockStorage (immutable)
|
|
122
128
|
*
|
|
123
129
|
* @param storage - The current BlockStorage
|
|
124
|
-
* @param version - The new version
|
|
130
|
+
* @param version - The new version key
|
|
125
131
|
* @returns A new BlockStorage with updated version
|
|
126
132
|
*/
|
|
127
133
|
function updateStorageDataVersion(storage, version) {
|
|
@@ -221,5 +227,5 @@ function mergeBlockStorageHandlers(customHandlers) {
|
|
|
221
227
|
};
|
|
222
228
|
}
|
|
223
229
|
|
|
224
|
-
export { BLOCK_STORAGE_KEY, BLOCK_STORAGE_SCHEMA_VERSION, createBlockStorage, defaultBlockStorageHandlers, deriveDataFromStorage, getFromStorage, getPluginData, getPluginNames, getStorageData, getStorageDataVersion, isBlockStorage, mergeBlockStorageHandlers, normalizeBlockStorage, removePluginData, setPluginData, updateStorage, updateStorageData, updateStorageDataVersion };
|
|
230
|
+
export { BLOCK_STORAGE_KEY, BLOCK_STORAGE_SCHEMA_VERSION, DATA_MODEL_DEFAULT_VERSION, createBlockStorage, defaultBlockStorageHandlers, deriveDataFromStorage, getFromStorage, getPluginData, getPluginNames, getStorageData, getStorageDataVersion, isBlockStorage, mergeBlockStorageHandlers, normalizeBlockStorage, removePluginData, setPluginData, updateStorage, updateStorageData, updateStorageDataVersion };
|
|
225
231
|
//# sourceMappingURL=block_storage.js.map
|