@rozenite/storage-plugin 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/devtools/assets/panel-CcThj-0Z.js +32 -0
  3. package/dist/devtools/assets/panel-DTYbHqnH.css +1 -0
  4. package/dist/devtools/panel.html +2 -2
  5. package/dist/react-native/cjs/package.json +3 -0
  6. package/dist/react-native/cjs/react-native.js +38 -0
  7. package/dist/react-native/cjs/src/react-native/adapters/async-storage.js +60 -0
  8. package/dist/react-native/cjs/src/react-native/adapters/index.js +10 -0
  9. package/dist/react-native/cjs/src/react-native/adapters/mmkv.js +143 -0
  10. package/dist/react-native/cjs/src/react-native/adapters/secure-storage.js +51 -0
  11. package/dist/react-native/cjs/src/react-native/entry-preview-pagination.js +198 -0
  12. package/dist/react-native/cjs/src/react-native/export-snapshot.js +70 -0
  13. package/dist/react-native/cjs/src/react-native/full-entry-request.js +72 -0
  14. package/dist/react-native/cjs/src/react-native/import.js +185 -0
  15. package/dist/react-native/cjs/src/react-native/storage-discovery.js +69 -0
  16. package/dist/react-native/cjs/src/react-native/storage-view.js +128 -0
  17. package/dist/react-native/cjs/src/react-native/useRozeniteStoragePlugin.js +145 -0
  18. package/dist/react-native/cjs/src/react-native/useStorageAgentTools.js +228 -0
  19. package/dist/react-native/cjs/src/shared/agent-tools.js +119 -0
  20. package/dist/react-native/cjs/src/shared/entry-preview.js +48 -0
  21. package/dist/react-native/cjs/src/shared/messaging.js +2 -0
  22. package/dist/react-native/cjs/src/shared/snapshot.js +185 -0
  23. package/dist/react-native/cjs/src/shared/types.js +13 -0
  24. package/dist/react-native/package.json +3 -0
  25. package/dist/react-native/react-native.d.ts +7 -0
  26. package/dist/react-native/react-native.js +40 -0
  27. package/dist/react-native/src/react-native/adapters/async-storage.d.ts +34 -0
  28. package/dist/react-native/src/react-native/adapters/async-storage.js +56 -0
  29. package/dist/react-native/src/react-native/adapters/index.d.ts +6 -0
  30. package/dist/react-native/src/react-native/adapters/index.js +3 -0
  31. package/dist/react-native/src/react-native/adapters/mmkv.d.ts +13 -0
  32. package/dist/react-native/src/react-native/adapters/mmkv.js +139 -0
  33. package/dist/react-native/src/react-native/adapters/secure-storage.d.ts +18 -0
  34. package/dist/react-native/src/react-native/adapters/secure-storage.js +47 -0
  35. package/dist/react-native/src/react-native/entry-preview-pagination.d.ts +7 -0
  36. package/dist/react-native/src/react-native/entry-preview-pagination.js +194 -0
  37. package/dist/react-native/src/react-native/export-snapshot.d.ts +5 -0
  38. package/dist/react-native/src/react-native/export-snapshot.js +66 -0
  39. package/dist/react-native/src/react-native/full-entry-request.d.ts +5 -0
  40. package/dist/react-native/src/react-native/full-entry-request.js +68 -0
  41. package/dist/react-native/src/react-native/import.d.ts +7 -0
  42. package/dist/react-native/src/react-native/import.js +180 -0
  43. package/dist/react-native/src/react-native/storage-discovery.d.ts +5 -0
  44. package/dist/react-native/src/react-native/storage-discovery.js +65 -0
  45. package/dist/react-native/src/react-native/storage-view.d.ts +19 -0
  46. package/dist/react-native/src/react-native/storage-view.js +123 -0
  47. package/dist/react-native/src/react-native/useRozeniteStoragePlugin.d.ts +6 -0
  48. package/dist/react-native/src/react-native/useRozeniteStoragePlugin.js +141 -0
  49. package/dist/react-native/src/react-native/useStorageAgentTools.d.ts +37 -0
  50. package/dist/react-native/src/react-native/useStorageAgentTools.js +223 -0
  51. package/dist/react-native/src/shared/agent-tools.d.ts +76 -0
  52. package/dist/react-native/src/shared/agent-tools.js +116 -0
  53. package/dist/react-native/src/shared/entry-preview.d.ts +4 -0
  54. package/dist/react-native/src/shared/entry-preview.js +44 -0
  55. package/dist/react-native/src/shared/messaging.d.ts +134 -0
  56. package/dist/react-native/src/shared/messaging.js +1 -0
  57. package/dist/react-native/src/shared/snapshot.d.ts +53 -0
  58. package/dist/react-native/src/shared/snapshot.js +179 -0
  59. package/dist/react-native/src/shared/types.d.ts +78 -0
  60. package/dist/react-native/src/shared/types.js +8 -0
  61. package/dist/rozenite.json +1 -1
  62. package/dist/sdk/package.json +3 -0
  63. package/dist/sdk/sdk.d.ts +44 -0
  64. package/dist/sdk/sdk.js +9 -0
  65. package/dist/sdk/src/shared/agent-tools.d.ts +76 -0
  66. package/dist/sdk/src/shared/agent-tools.js +119 -0
  67. package/dist/sdk/src/shared/types.d.ts +78 -0
  68. package/dist/sdk/src/shared/types.js +13 -0
  69. package/package.json +16 -16
  70. package/react-native.ts +8 -1
  71. package/rozenite.config.ts +1 -0
  72. package/src/__tests__/release-bundle.test.ts +32 -0
  73. package/dist/devtools/assets/panel-D5z1BY8U.js +0 -32
  74. package/dist/devtools/assets/panel-DVlWRg6d.css +0 -1
  75. package/dist/react-native/chunks/async-storage.require.cjs +0 -1
  76. package/dist/react-native/chunks/async-storage.require.js +0 -53
  77. package/dist/react-native/chunks/index.require.cjs +0 -1
  78. package/dist/react-native/chunks/index.require.js +0 -103
  79. package/dist/react-native/chunks/secure-storage.require.cjs +0 -1
  80. package/dist/react-native/chunks/secure-storage.require.js +0 -46
  81. package/dist/react-native/chunks/types.cjs +0 -1
  82. package/dist/react-native/chunks/types.js +0 -11
  83. package/dist/react-native/chunks/useRozeniteStoragePlugin.require.cjs +0 -1
  84. package/dist/react-native/chunks/useRozeniteStoragePlugin.require.js +0 -1037
  85. package/dist/react-native/index.cjs +0 -1
  86. package/dist/react-native/index.d.ts +0 -365
  87. package/dist/react-native/index.js +0 -28
  88. package/dist/sdk/index.cjs +0 -1
  89. package/dist/sdk/index.d.ts +0 -206
  90. package/dist/sdk/index.js +0 -122
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useStorageAgentTools = exports.createStorageAgentHandlers = void 0;
4
+ const agent_bridge_1 = require("@rozenite/agent-bridge");
5
+ const agent_shared_1 = require("@rozenite/agent-shared");
6
+ const agent_tools_1 = require("../shared/agent-tools");
7
+ const parseValueForType = (type, value) => {
8
+ if (type === 'string') {
9
+ if (typeof value !== 'string') {
10
+ throw new Error('Expected string value for type=string');
11
+ }
12
+ return value;
13
+ }
14
+ if (type === 'number') {
15
+ if (typeof value !== 'number' || Number.isNaN(value)) {
16
+ throw new Error('Expected number value for type=number');
17
+ }
18
+ return value;
19
+ }
20
+ if (type === 'boolean') {
21
+ if (typeof value !== 'boolean') {
22
+ throw new Error('Expected boolean value for type=boolean');
23
+ }
24
+ return value;
25
+ }
26
+ if (!Array.isArray(value) || !value.every((item) => Number.isInteger(item))) {
27
+ throw new Error('Expected number[] value for type=buffer');
28
+ }
29
+ return value;
30
+ };
31
+ const formatViewRef = (view) => `${view.target.adapterId}/${view.target.storageId}`;
32
+ const isSameTarget = (left, right) => left.adapterId === right.adapterId && left.storageId === right.storageId;
33
+ const encodeCursor = (cursor) => btoa(encodeURIComponent(JSON.stringify(cursor)));
34
+ const decodeCursor = (value) => {
35
+ try {
36
+ const cursor = JSON.parse(decodeURIComponent(atob(value)));
37
+ if (typeof cursor !== 'object' ||
38
+ cursor == null ||
39
+ Array.isArray(cursor) ||
40
+ cursor.version !== 1 ||
41
+ typeof cursor.target?.adapterId !== 'string' ||
42
+ typeof cursor.target?.storageId !== 'string' ||
43
+ !Number.isSafeInteger(cursor.offset) ||
44
+ cursor.offset < 0) {
45
+ throw new Error('Invalid cursor');
46
+ }
47
+ return cursor;
48
+ }
49
+ catch {
50
+ throw new Error('Invalid cursor. Run list-entries again without a cursor to restart pagination.');
51
+ }
52
+ };
53
+ const sanitizeLimit = (limit) => {
54
+ if (typeof limit !== 'number' ||
55
+ !Number.isFinite(limit) ||
56
+ !Number.isInteger(limit) ||
57
+ limit < 1) {
58
+ return agent_shared_1.DEFAULT_PAGE_LIMIT;
59
+ }
60
+ return Math.min(limit, agent_shared_1.MAX_PAGE_LIMIT);
61
+ };
62
+ const sanitizeOffset = (offset) => {
63
+ if (typeof offset !== 'number' ||
64
+ !Number.isFinite(offset) ||
65
+ !Number.isSafeInteger(offset) ||
66
+ offset < 0) {
67
+ return 0;
68
+ }
69
+ return offset;
70
+ };
71
+ const resolveStorage = (views, adapterId, storageId) => {
72
+ if (views.length === 0) {
73
+ throw new Error('No storages are registered.');
74
+ }
75
+ if (adapterId !== undefined || storageId !== undefined) {
76
+ const matches = views.filter((view) => (adapterId === undefined || view.target.adapterId === adapterId) &&
77
+ (storageId === undefined || view.target.storageId === storageId));
78
+ const qualifier = [
79
+ adapterId !== undefined && `adapterId="${adapterId}"`,
80
+ storageId !== undefined && `storageId="${storageId}"`,
81
+ ]
82
+ .filter(Boolean)
83
+ .join(', ');
84
+ if (matches.length === 0) {
85
+ throw new Error(`No storage matched ${qualifier}. Available: ${views.map(formatViewRef).join(', ')}`);
86
+ }
87
+ if (matches.length > 1) {
88
+ throw new Error(`Multiple storages matched ${qualifier}. Provide both adapterId and storageId. Available: ${matches.map(formatViewRef).join(', ')}`);
89
+ }
90
+ return matches[0];
91
+ }
92
+ if (views.length > 1) {
93
+ throw new Error(`Multiple storages detected. Provide adapterId and/or storageId. Available: ${views.map(formatViewRef).join(', ')}`);
94
+ }
95
+ return views[0];
96
+ };
97
+ const listStorageKeys = async (view, input) => {
98
+ const limit = sanitizeLimit(input.limit);
99
+ if (input.cursor !== undefined && input.offset !== undefined) {
100
+ throw new Error('Provide either cursor or offset, not both.');
101
+ }
102
+ const cursor = input.cursor === undefined ? undefined : decodeCursor(input.cursor);
103
+ if (cursor && !isSameTarget(cursor.target, view.target)) {
104
+ throw new Error('Cursor does not match the requested storage. Run list-entries again without a cursor to restart pagination.');
105
+ }
106
+ const allKeys = await view.getAllKeys();
107
+ const offset = cursor?.offset ?? sanitizeOffset(input.offset);
108
+ const end = Math.min(offset + limit, allKeys.length);
109
+ return {
110
+ adapterId: view.target.adapterId,
111
+ storageId: view.target.storageId,
112
+ total: allKeys.length,
113
+ items: allKeys.slice(offset, end).map((key) => ({ key })),
114
+ page: {
115
+ limit,
116
+ hasMore: end < allKeys.length,
117
+ ...(end < allKeys.length
118
+ ? {
119
+ nextCursor: encodeCursor({
120
+ version: 1,
121
+ target: view.target,
122
+ offset: end,
123
+ }),
124
+ }
125
+ : {}),
126
+ },
127
+ };
128
+ };
129
+ const createStorageAgentHandlers = (views) => ({
130
+ listStorages: async () => ({
131
+ storages: views.map((view) => ({
132
+ adapterId: view.target.adapterId,
133
+ storageId: view.target.storageId,
134
+ adapterName: view.adapterName,
135
+ storageName: view.storageName,
136
+ supportedTypes: view.capabilities.supportedTypes,
137
+ })),
138
+ }),
139
+ listEntries: async (input) => {
140
+ const view = resolveStorage(views, input.adapterId, input.storageId);
141
+ return listStorageKeys(view, input);
142
+ },
143
+ readEntry: async ({ adapterId, storageId, key, }) => {
144
+ const view = resolveStorage(views, adapterId, storageId);
145
+ const entry = await view.get(key);
146
+ if (!entry) {
147
+ throw new Error(`Key "${key}" not found in storage "${formatViewRef(view)}".`);
148
+ }
149
+ return {
150
+ adapterId: view.target.adapterId,
151
+ storageId: view.target.storageId,
152
+ entry,
153
+ };
154
+ },
155
+ });
156
+ exports.createStorageAgentHandlers = createStorageAgentHandlers;
157
+ const useStorageAgentTools = (views) => {
158
+ const handlers = (0, exports.createStorageAgentHandlers)(views);
159
+ (0, agent_bridge_1.useRozenitePluginAgentTool)({
160
+ pluginId: agent_tools_1.STORAGE_AGENT_PLUGIN_ID,
161
+ tool: agent_tools_1.storageToolDefinitions.listStorages,
162
+ handler: handlers.listStorages,
163
+ });
164
+ (0, agent_bridge_1.useRozenitePluginAgentTool)({
165
+ pluginId: agent_tools_1.STORAGE_AGENT_PLUGIN_ID,
166
+ tool: agent_tools_1.storageToolDefinitions.listEntries,
167
+ handler: handlers.listEntries,
168
+ });
169
+ (0, agent_bridge_1.useRozenitePluginAgentTool)({
170
+ pluginId: agent_tools_1.STORAGE_AGENT_PLUGIN_ID,
171
+ tool: agent_tools_1.storageToolDefinitions.readEntry,
172
+ handler: handlers.readEntry,
173
+ });
174
+ (0, agent_bridge_1.useRozenitePluginAgentTool)({
175
+ pluginId: agent_tools_1.STORAGE_AGENT_PLUGIN_ID,
176
+ tool: agent_tools_1.storageToolDefinitions.createEntry,
177
+ handler: async ({ adapterId, storageId, key, type, value }) => {
178
+ const view = resolveStorage(views, adapterId, storageId);
179
+ const existing = await view.get(key);
180
+ if (existing) {
181
+ throw new Error(`Key "${key}" already exists in storage "${formatViewRef(view)}". Use edit-entry instead.`);
182
+ }
183
+ const parsedValue = parseValueForType(type, value);
184
+ await view.set({ key, type, value: parsedValue });
185
+ return {
186
+ adapterId: view.target.adapterId,
187
+ storageId: view.target.storageId,
188
+ created: true,
189
+ key,
190
+ };
191
+ },
192
+ });
193
+ (0, agent_bridge_1.useRozenitePluginAgentTool)({
194
+ pluginId: agent_tools_1.STORAGE_AGENT_PLUGIN_ID,
195
+ tool: agent_tools_1.storageToolDefinitions.editEntry,
196
+ handler: async ({ adapterId, storageId, key, type, value }) => {
197
+ const view = resolveStorage(views, adapterId, storageId);
198
+ const existing = await view.get(key);
199
+ if (!existing) {
200
+ throw new Error(`Key "${key}" not found in storage "${formatViewRef(view)}". Use create-entry instead.`);
201
+ }
202
+ const parsedValue = parseValueForType(type, value);
203
+ await view.set({ key, type, value: parsedValue });
204
+ return {
205
+ adapterId: view.target.adapterId,
206
+ storageId: view.target.storageId,
207
+ edited: true,
208
+ key,
209
+ };
210
+ },
211
+ });
212
+ (0, agent_bridge_1.useRozenitePluginAgentTool)({
213
+ pluginId: agent_tools_1.STORAGE_AGENT_PLUGIN_ID,
214
+ tool: agent_tools_1.storageToolDefinitions.removeEntry,
215
+ handler: async ({ adapterId, storageId, key }) => {
216
+ const view = resolveStorage(views, adapterId, storageId);
217
+ const existed = !!(await view.get(key));
218
+ await view.delete(key);
219
+ return {
220
+ adapterId: view.target.adapterId,
221
+ storageId: view.target.storageId,
222
+ removed: existed,
223
+ key,
224
+ };
225
+ },
226
+ });
227
+ };
228
+ exports.useStorageAgentTools = useStorageAgentTools;
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.storageToolDefinitions = exports.STORAGE_AGENT_ENTRY_TYPES = exports.STORAGE_AGENT_PLUGIN_ID = void 0;
4
+ const agent_shared_1 = require("@rozenite/agent-shared");
5
+ const sharedStorageProperties = {
6
+ adapterId: {
7
+ type: 'string',
8
+ description: 'Storage adapter ID. Required when multiple adapters are configured.',
9
+ },
10
+ storageId: {
11
+ type: 'string',
12
+ description: 'Storage node ID within the adapter. Required when multiple storages are configured.',
13
+ },
14
+ };
15
+ exports.STORAGE_AGENT_PLUGIN_ID = '@rozenite/storage-plugin';
16
+ exports.STORAGE_AGENT_ENTRY_TYPES = [
17
+ 'string',
18
+ 'number',
19
+ 'boolean',
20
+ 'buffer',
21
+ ];
22
+ exports.storageToolDefinitions = {
23
+ listStorages: (0, agent_shared_1.defineAgentToolContract)({
24
+ name: 'list-storages',
25
+ description: 'List all storage adapters and their storage nodes currently available on the device, including supported entry types. Entry counts are omitted to avoid enumerating every storage.',
26
+ inputSchema: { type: 'object', properties: {} },
27
+ }),
28
+ listEntries: (0, agent_shared_1.definePaginatedAgentToolContract)({
29
+ name: 'list-entries',
30
+ description: 'List keys in a storage. This call intentionally does not return entry values to keep responses token-efficient.',
31
+ inputSchema: {
32
+ type: 'object',
33
+ properties: {
34
+ ...sharedStorageProperties,
35
+ limit: {
36
+ type: 'number',
37
+ description: 'Pagination size. Defaults to 20 and is capped at 100.',
38
+ },
39
+ cursor: {
40
+ type: 'string',
41
+ description: 'Opaque pagination cursor from a previous list-entries call.',
42
+ },
43
+ offset: {
44
+ type: 'number',
45
+ description: 'Deprecated initial pagination offset. Must be a non-negative integer and cannot be combined with cursor.',
46
+ },
47
+ },
48
+ },
49
+ pagination: {
50
+ kind: 'cursor',
51
+ fields: ['key'],
52
+ defaultFields: ['key'],
53
+ },
54
+ }),
55
+ readEntry: (0, agent_shared_1.defineAgentToolContract)({
56
+ name: 'read-entry',
57
+ description: 'Read a single storage entry value by key.',
58
+ inputSchema: {
59
+ type: 'object',
60
+ properties: {
61
+ ...sharedStorageProperties,
62
+ key: { type: 'string', description: 'Entry key.' },
63
+ },
64
+ required: ['key'],
65
+ },
66
+ }),
67
+ createEntry: (0, agent_shared_1.defineAgentToolContract)({
68
+ name: 'create-entry',
69
+ description: 'Create a new storage entry. Fails if the key already exists.',
70
+ inputSchema: {
71
+ type: 'object',
72
+ properties: {
73
+ ...sharedStorageProperties,
74
+ key: { type: 'string', description: 'Entry key.' },
75
+ type: {
76
+ type: 'string',
77
+ enum: [...exports.STORAGE_AGENT_ENTRY_TYPES],
78
+ description: 'Entry value type. Must be one of the supported types for the target storage (see list-storages).',
79
+ },
80
+ value: {
81
+ description: 'Entry value matching the provided type.',
82
+ },
83
+ },
84
+ required: ['key', 'type', 'value'],
85
+ },
86
+ }),
87
+ editEntry: (0, agent_shared_1.defineAgentToolContract)({
88
+ name: 'edit-entry',
89
+ description: 'Edit an existing storage entry. Fails if the key does not exist.',
90
+ inputSchema: {
91
+ type: 'object',
92
+ properties: {
93
+ ...sharedStorageProperties,
94
+ key: { type: 'string', description: 'Entry key.' },
95
+ type: {
96
+ type: 'string',
97
+ enum: [...exports.STORAGE_AGENT_ENTRY_TYPES],
98
+ description: 'Entry value type. Must be one of the supported types for the target storage (see list-storages).',
99
+ },
100
+ value: {
101
+ description: 'Entry value matching the provided type.',
102
+ },
103
+ },
104
+ required: ['key', 'type', 'value'],
105
+ },
106
+ }),
107
+ removeEntry: (0, agent_shared_1.defineAgentToolContract)({
108
+ name: 'remove-entry',
109
+ description: 'Remove a storage entry by key.',
110
+ inputSchema: {
111
+ type: 'object',
112
+ properties: {
113
+ ...sharedStorageProperties,
114
+ key: { type: 'string', description: 'Entry key.' },
115
+ },
116
+ required: ['key'],
117
+ },
118
+ }),
119
+ };
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toStorageEntryPreview = exports.MAX_BUFFER_PREVIEW_BYTES = exports.MAX_STRING_PREVIEW_CODE_POINTS = void 0;
4
+ exports.MAX_STRING_PREVIEW_CODE_POINTS = 256;
5
+ exports.MAX_BUFFER_PREVIEW_BYTES = 16;
6
+ const toHexPair = (byte) => byte.toString(16).toUpperCase().padStart(2, '0');
7
+ const truncateString = (value) => {
8
+ let preview = '';
9
+ let codePointCount = 0;
10
+ for (const codePoint of value) {
11
+ if (codePointCount === exports.MAX_STRING_PREVIEW_CODE_POINTS) {
12
+ return { preview, isTruncated: true };
13
+ }
14
+ preview += codePoint;
15
+ codePointCount += 1;
16
+ }
17
+ return { preview, isTruncated: false };
18
+ };
19
+ const toStorageEntryPreview = (entry) => {
20
+ if (entry.type === 'string') {
21
+ return {
22
+ key: entry.key,
23
+ type: entry.type,
24
+ ...truncateString(entry.value),
25
+ // JavaScript strings expose their length in UTF-16 code units.
26
+ valueSize: entry.value.length,
27
+ };
28
+ }
29
+ if (entry.type === 'buffer') {
30
+ const shownBytes = entry.value.slice(0, exports.MAX_BUFFER_PREVIEW_BYTES);
31
+ return {
32
+ key: entry.key,
33
+ type: entry.type,
34
+ preview: shownBytes.map(toHexPair).join(' '),
35
+ valueSize: entry.value.length,
36
+ isTruncated: entry.value.length > exports.MAX_BUFFER_PREVIEW_BYTES,
37
+ };
38
+ }
39
+ const preview = String(entry.value);
40
+ return {
41
+ key: entry.key,
42
+ type: entry.type,
43
+ preview,
44
+ valueSize: preview.length,
45
+ isTruncated: false,
46
+ };
47
+ };
48
+ exports.toStorageEntryPreview = toStorageEntryPreview;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.computePreview = exports.buildSnapshot = exports.parseSnapshot = void 0;
4
+ const types_1 = require("./types");
5
+ const PLUGIN_ID = '@rozenite/storage-plugin';
6
+ const SUPPORTED_VERSION = 1;
7
+ class ParseException extends Error {
8
+ constructor(path, message) {
9
+ super(message);
10
+ this.path = path;
11
+ }
12
+ }
13
+ const describe = (value) => {
14
+ if (value === null)
15
+ return 'null';
16
+ if (Array.isArray(value))
17
+ return 'array';
18
+ return typeof value;
19
+ };
20
+ const isPlainObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
21
+ const expectObject = (value, path) => {
22
+ if (!isPlainObject(value)) {
23
+ throw new ParseException(path, `Expected object, got ${describe(value)}`);
24
+ }
25
+ return value;
26
+ };
27
+ const expectString = (value, path) => {
28
+ if (typeof value !== 'string') {
29
+ throw new ParseException(path, `Expected string, got ${describe(value)}`);
30
+ }
31
+ return value;
32
+ };
33
+ const expectArray = (value, path) => {
34
+ if (!Array.isArray(value)) {
35
+ throw new ParseException(path, `Expected array, got ${describe(value)}`);
36
+ }
37
+ return value;
38
+ };
39
+ const expectEntryType = (value, path) => {
40
+ if (typeof value !== 'string' || !types_1.DEFAULT_SUPPORTED_TYPES.includes(value)) {
41
+ throw new ParseException(path, `Expected one of ${types_1.DEFAULT_SUPPORTED_TYPES.join(', ')}, got ${describe(value)}`);
42
+ }
43
+ return value;
44
+ };
45
+ const parseCapabilities = (raw, path) => {
46
+ const obj = expectObject(raw, path);
47
+ const supported = expectArray(obj.supportedTypes, `${path}.supportedTypes`);
48
+ const supportedTypes = supported.map((type, index) => expectEntryType(type, `${path}.supportedTypes[${index}]`));
49
+ return { supportedTypes };
50
+ };
51
+ const parseStorageMeta = (raw) => {
52
+ const obj = expectObject(raw, 'storage');
53
+ return {
54
+ adapterId: expectString(obj.adapterId, 'storage.adapterId'),
55
+ storageId: expectString(obj.storageId, 'storage.storageId'),
56
+ adapterName: expectString(obj.adapterName, 'storage.adapterName'),
57
+ storageName: expectString(obj.storageName, 'storage.storageName'),
58
+ capabilities: parseCapabilities(obj.capabilities, 'storage.capabilities'),
59
+ };
60
+ };
61
+ const parseEntry = (raw, path) => {
62
+ const obj = expectObject(raw, path);
63
+ const key = expectString(obj.key, `${path}.key`);
64
+ const type = expectEntryType(obj.type, `${path}.type`);
65
+ const valuePath = `${path}.value`;
66
+ switch (type) {
67
+ case 'string': {
68
+ const value = obj.value;
69
+ if (typeof value !== 'string') {
70
+ throw new ParseException(valuePath, `Expected string for type "string", got ${describe(value)}`);
71
+ }
72
+ return { key, type: 'string', value };
73
+ }
74
+ case 'number': {
75
+ const value = obj.value;
76
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
77
+ throw new ParseException(valuePath, `Expected finite number for type "number", got ${describe(value)}`);
78
+ }
79
+ return { key, type: 'number', value };
80
+ }
81
+ case 'boolean': {
82
+ const value = obj.value;
83
+ if (typeof value !== 'boolean') {
84
+ throw new ParseException(valuePath, `Expected boolean for type "boolean", got ${describe(value)}`);
85
+ }
86
+ return { key, type: 'boolean', value };
87
+ }
88
+ case 'buffer': {
89
+ const value = obj.value;
90
+ if (!Array.isArray(value)) {
91
+ throw new ParseException(valuePath, `Expected number[] for type "buffer", got ${describe(value)}`);
92
+ }
93
+ const bytes = value.map((byte, index) => {
94
+ if (typeof byte !== 'number' || !Number.isInteger(byte) || byte < 0 || byte > 255) {
95
+ throw new ParseException(`${valuePath}[${index}]`, `Expected uint8 (integer 0-255), got ${describe(byte)}`);
96
+ }
97
+ return byte;
98
+ });
99
+ return { key, type: 'buffer', value: bytes };
100
+ }
101
+ }
102
+ };
103
+ const parseSnapshot = (raw) => {
104
+ try {
105
+ const root = expectObject(raw, '$');
106
+ const version = root.version;
107
+ if (version !== SUPPORTED_VERSION) {
108
+ throw new ParseException('version', `Unsupported snapshot version: ${describe(version)} (this build supports version ${SUPPORTED_VERSION})`);
109
+ }
110
+ const plugin = expectString(root.plugin, 'plugin');
111
+ const createdAt = expectString(root.createdAt, 'createdAt');
112
+ const storage = parseStorageMeta(root.storage);
113
+ const rawEntries = expectArray(root.entries, 'entries');
114
+ const entries = rawEntries.map((entry, index) => parseEntry(entry, `entries[${index}]`));
115
+ return {
116
+ ok: true,
117
+ snapshot: {
118
+ version: SUPPORTED_VERSION,
119
+ plugin,
120
+ createdAt,
121
+ storage,
122
+ entries,
123
+ },
124
+ };
125
+ }
126
+ catch (error) {
127
+ if (error instanceof ParseException) {
128
+ return { ok: false, error: { path: error.path, message: error.message } };
129
+ }
130
+ throw error;
131
+ }
132
+ };
133
+ exports.parseSnapshot = parseSnapshot;
134
+ const buildSnapshot = (args) => ({
135
+ version: 1,
136
+ plugin: PLUGIN_ID,
137
+ createdAt: new Date().toISOString(),
138
+ storage: {
139
+ adapterId: args.target.adapterId,
140
+ storageId: args.target.storageId,
141
+ adapterName: args.adapterName,
142
+ storageName: args.storageName,
143
+ capabilities: args.capabilities,
144
+ },
145
+ entries: args.entries,
146
+ });
147
+ exports.buildSnapshot = buildSnapshot;
148
+ const computePreview = (snapshot, current) => {
149
+ const newKeys = [];
150
+ const overwriteKeys = [];
151
+ const skippedKeys = [];
152
+ const unsupportedTypes = [];
153
+ const acceptedEntryIndexes = [];
154
+ for (const [index, entry] of snapshot.entries.entries()) {
155
+ if (!(0, types_1.supportsType)(current.capabilities, entry.type)) {
156
+ unsupportedTypes.push({ key: entry.key, type: entry.type });
157
+ continue;
158
+ }
159
+ if (current.isBlacklisted(entry.key)) {
160
+ skippedKeys.push({ key: entry.key, reason: 'blacklist' });
161
+ continue;
162
+ }
163
+ if (current.entryKeys.has(entry.key)) {
164
+ overwriteKeys.push(entry.key);
165
+ }
166
+ else {
167
+ newKeys.push(entry.key);
168
+ }
169
+ acceptedEntryIndexes.push(index);
170
+ // Duplicate keys are applied in source order, so every accepted duplicate
171
+ // after the first overwrites the value that preceded it in this import.
172
+ current.entryKeys.add(entry.key);
173
+ }
174
+ const metadataMismatch = snapshot.storage.adapterId !== current.target.adapterId ||
175
+ snapshot.storage.storageId !== current.target.storageId;
176
+ return {
177
+ newKeys,
178
+ overwriteKeys,
179
+ skippedKeys,
180
+ unsupportedTypes,
181
+ acceptedEntryIndexes,
182
+ metadataMismatch,
183
+ };
184
+ };
185
+ exports.computePreview = computePreview;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.supportsType = exports.getStorageViewId = exports.DEFAULT_SUPPORTED_TYPES = void 0;
4
+ exports.DEFAULT_SUPPORTED_TYPES = [
5
+ 'string',
6
+ 'number',
7
+ 'boolean',
8
+ 'buffer',
9
+ ];
10
+ const getStorageViewId = ({ adapterId, storageId }) => `${adapterId}:${storageId}`;
11
+ exports.getStorageViewId = getStorageViewId;
12
+ const supportsType = (capabilities, type) => capabilities.supportedTypes.includes(type);
13
+ exports.supportsType = supportsType;
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,7 @@
1
+ export type { AsyncStorageInstanceConfig, AsyncStorageLike, CreateAsyncStorageAdapterOptions, CreateExpoAsyncStorageAdapterOptions, CreateExpoSecureStorageAdapterOptions, CreateMMKVStorageAdapterOptions, SecureStorageLike, } from './src/react-native/adapters';
2
+ export type { AsyncStorage, StorageAdapter, StorageCapabilities, StorageEntry, StorageEntryType, StorageEntryValue, StorageNode, SyncStorage, } from './src/shared/types';
3
+ export declare let createAsyncStorageAdapter: typeof import('./src/react-native/adapters').createAsyncStorageAdapter;
4
+ export declare let createExpoAsyncStorageAdapter: typeof import('./src/react-native/adapters').createExpoAsyncStorageAdapter;
5
+ export declare let createExpoSecureStorageAdapter: typeof import('./src/react-native/adapters').createExpoSecureStorageAdapter;
6
+ export declare let createMMKVStorageAdapter: typeof import('./src/react-native/adapters').createMMKVStorageAdapter;
7
+ export declare let useRozeniteStoragePlugin: typeof import('./src/react-native/useRozeniteStoragePlugin').useRozeniteStoragePlugin;
@@ -0,0 +1,40 @@
1
+ export let createAsyncStorageAdapter;
2
+ export let createExpoAsyncStorageAdapter;
3
+ export let createExpoSecureStorageAdapter;
4
+ export let createMMKVStorageAdapter;
5
+ export let useRozeniteStoragePlugin;
6
+ const isWeb = typeof window !== 'undefined' && window.navigator.product !== 'ReactNative';
7
+ const isDev = process.env.NODE_ENV !== 'production';
8
+ const isServer = typeof window === 'undefined' && typeof lynx === 'undefined';
9
+ const createNoopStorageAdapter = (options, defaultId, defaultName) => ({
10
+ id: options?.adapterId ?? defaultId,
11
+ name: options?.adapterName ?? defaultName,
12
+ storages: [],
13
+ });
14
+ if (!isDev || isServer) {
15
+ createAsyncStorageAdapter = ((options) => createNoopStorageAdapter(options, 'async-storage', 'AsyncStorage'));
16
+ createExpoAsyncStorageAdapter = createAsyncStorageAdapter;
17
+ createExpoSecureStorageAdapter = ((options) => createNoopStorageAdapter(options, 'expo-secure-store', 'Expo SecureStore'));
18
+ createMMKVStorageAdapter = ((options) => createNoopStorageAdapter(options, 'mmkv', 'MMKV'));
19
+ useRozeniteStoragePlugin = () => null;
20
+ }
21
+ else if (isWeb) {
22
+ const asyncStorageModule = require('./src/react-native/adapters/async-storage');
23
+ const secureStorageModule = require('./src/react-native/adapters/secure-storage');
24
+ createAsyncStorageAdapter = asyncStorageModule.createAsyncStorageAdapter;
25
+ createExpoAsyncStorageAdapter = asyncStorageModule.createExpoAsyncStorageAdapter;
26
+ createExpoSecureStorageAdapter = secureStorageModule.createExpoSecureStorageAdapter;
27
+ createMMKVStorageAdapter = ((options) => createNoopStorageAdapter(options, 'mmkv', 'MMKV'));
28
+ useRozeniteStoragePlugin =
29
+ require('./src/react-native/useRozeniteStoragePlugin').useRozeniteStoragePlugin;
30
+ }
31
+ else {
32
+ createAsyncStorageAdapter = require('./src/react-native/adapters').createAsyncStorageAdapter;
33
+ createExpoAsyncStorageAdapter =
34
+ require('./src/react-native/adapters').createExpoAsyncStorageAdapter;
35
+ createExpoSecureStorageAdapter =
36
+ require('./src/react-native/adapters').createExpoSecureStorageAdapter;
37
+ createMMKVStorageAdapter = require('./src/react-native/adapters').createMMKVStorageAdapter;
38
+ useRozeniteStoragePlugin =
39
+ require('./src/react-native/useRozeniteStoragePlugin').useRozeniteStoragePlugin;
40
+ }