@standardagents/vue 0.14.1 → 0.15.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/index.cjs +123 -4
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +123 -4
- package/package.json +4 -3
package/dist/index.cjs
CHANGED
|
@@ -33,6 +33,29 @@ function useStandardAgents() {
|
|
|
33
33
|
return context;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
// src/utils/id.ts
|
|
37
|
+
function createClientId() {
|
|
38
|
+
const cryptoRef = globalThis.crypto;
|
|
39
|
+
if (typeof cryptoRef?.randomUUID === "function") {
|
|
40
|
+
return cryptoRef.randomUUID();
|
|
41
|
+
}
|
|
42
|
+
if (typeof cryptoRef?.getRandomValues === "function") {
|
|
43
|
+
const bytes = new Uint8Array(16);
|
|
44
|
+
cryptoRef.getRandomValues(bytes);
|
|
45
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
46
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
47
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0"));
|
|
48
|
+
return [
|
|
49
|
+
hex.slice(0, 4).join(""),
|
|
50
|
+
hex.slice(4, 6).join(""),
|
|
51
|
+
hex.slice(6, 8).join(""),
|
|
52
|
+
hex.slice(8, 10).join(""),
|
|
53
|
+
hex.slice(10, 16).join("")
|
|
54
|
+
].join("-");
|
|
55
|
+
}
|
|
56
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
36
59
|
// src/composables/useThreadSetup.ts
|
|
37
60
|
var uploadManager = new client.FileUploadManager();
|
|
38
61
|
function fileToBase64(file) {
|
|
@@ -69,6 +92,9 @@ function useThreadSetup(threadIdInput, options = {}) {
|
|
|
69
92
|
includeSilent
|
|
70
93
|
};
|
|
71
94
|
const messages = vue.ref([]);
|
|
95
|
+
const paginatedMessagesLoaded = vue.ref(0);
|
|
96
|
+
const hasMoreMessages = vue.ref(false);
|
|
97
|
+
const isLoadingOlderMessages = vue.ref(false);
|
|
72
98
|
const status = vue.ref("disconnected");
|
|
73
99
|
const isLoading = vue.ref(preload);
|
|
74
100
|
const error = vue.ref(null);
|
|
@@ -105,18 +131,104 @@ function useThreadSetup(threadIdInput, options = {}) {
|
|
|
105
131
|
}
|
|
106
132
|
return messages.value;
|
|
107
133
|
});
|
|
134
|
+
const MESSAGE_PAGE_SIZE = 100;
|
|
135
|
+
const mergeMessages = (olderMessages, newerMessages) => {
|
|
136
|
+
const messageMap = /* @__PURE__ */ new Map();
|
|
137
|
+
for (const message of olderMessages) {
|
|
138
|
+
messageMap.set(message.id, message);
|
|
139
|
+
}
|
|
140
|
+
for (const message of newerMessages) {
|
|
141
|
+
messageMap.set(message.id, message);
|
|
142
|
+
}
|
|
143
|
+
return Array.from(messageMap.values()).sort((a, b) => {
|
|
144
|
+
if (a.created_at !== b.created_at) return a.created_at - b.created_at;
|
|
145
|
+
return a.id.localeCompare(b.id);
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
const hasCompleteOldestWorkblockBoundary = () => {
|
|
149
|
+
const firstMessage = messages.value[0];
|
|
150
|
+
if (!firstMessage) return true;
|
|
151
|
+
if (firstMessage.role === "tool") return false;
|
|
152
|
+
if (firstMessage.role === "assistant" && firstMessage.tool_calls) return false;
|
|
153
|
+
return true;
|
|
154
|
+
};
|
|
108
155
|
async function loadMessages() {
|
|
109
156
|
isLoading.value = true;
|
|
110
157
|
error.value = null;
|
|
111
158
|
try {
|
|
112
|
-
const
|
|
113
|
-
|
|
159
|
+
const page = await client$1.getMessagesPage(threadId.value, {
|
|
160
|
+
limit: MESSAGE_PAGE_SIZE,
|
|
161
|
+
offset: 0,
|
|
162
|
+
depth,
|
|
163
|
+
includeSilent
|
|
164
|
+
});
|
|
165
|
+
messages.value = page.messages;
|
|
166
|
+
paginatedMessagesLoaded.value = page.messages.length;
|
|
167
|
+
hasMoreMessages.value = page.hasMore;
|
|
114
168
|
} catch (err) {
|
|
115
169
|
error.value = err instanceof Error ? err : new Error(String(err));
|
|
116
170
|
} finally {
|
|
117
171
|
isLoading.value = false;
|
|
118
172
|
}
|
|
119
173
|
}
|
|
174
|
+
async function loadOlderMessages() {
|
|
175
|
+
if (isLoadingOlderMessages.value || isLoading.value || !hasMoreMessages.value) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
isLoadingOlderMessages.value = true;
|
|
179
|
+
error.value = null;
|
|
180
|
+
try {
|
|
181
|
+
let loadedAny = false;
|
|
182
|
+
let keepLoading = true;
|
|
183
|
+
while (keepLoading && hasMoreMessages.value) {
|
|
184
|
+
const page = await client$1.getMessagesPage(threadId.value, {
|
|
185
|
+
limit: MESSAGE_PAGE_SIZE,
|
|
186
|
+
offset: paginatedMessagesLoaded.value,
|
|
187
|
+
depth,
|
|
188
|
+
includeSilent
|
|
189
|
+
});
|
|
190
|
+
if (page.messages.length === 0) {
|
|
191
|
+
hasMoreMessages.value = false;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
messages.value = mergeMessages(page.messages, messages.value);
|
|
195
|
+
paginatedMessagesLoaded.value += page.messages.length;
|
|
196
|
+
hasMoreMessages.value = page.hasMore;
|
|
197
|
+
loadedAny = true;
|
|
198
|
+
keepLoading = !hasCompleteOldestWorkblockBoundary();
|
|
199
|
+
}
|
|
200
|
+
return loadedAny;
|
|
201
|
+
} catch (err) {
|
|
202
|
+
error.value = err instanceof Error ? err : new Error(String(err));
|
|
203
|
+
return false;
|
|
204
|
+
} finally {
|
|
205
|
+
isLoadingOlderMessages.value = false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
async function loadMessagesByIds(ids, options2 = {}) {
|
|
209
|
+
const uniqueIds = Array.from(new Set(ids.map((id) => id.trim()).filter(Boolean)));
|
|
210
|
+
if (uniqueIds.length === 0) return true;
|
|
211
|
+
try {
|
|
212
|
+
let loadedAny = false;
|
|
213
|
+
for (let i = 0; i < uniqueIds.length; i += 200) {
|
|
214
|
+
const chunk = uniqueIds.slice(i, i + 200);
|
|
215
|
+
const page = await client$1.getMessagesPage(threadId.value, {
|
|
216
|
+
ids: chunk,
|
|
217
|
+
depth,
|
|
218
|
+
includeSilent,
|
|
219
|
+
includeWorkblocks: options2.includeWorkblocks
|
|
220
|
+
});
|
|
221
|
+
if (page.messages.length > 0) {
|
|
222
|
+
messages.value = mergeMessages(page.messages, messages.value);
|
|
223
|
+
loadedAny = true;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return loadedAny;
|
|
227
|
+
} catch (err) {
|
|
228
|
+
error.value = err instanceof Error ? err : new Error(String(err));
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
120
232
|
async function loadFiles() {
|
|
121
233
|
try {
|
|
122
234
|
const fileList = await client$1.listFiles(threadId.value);
|
|
@@ -228,7 +340,7 @@ function useThreadSetup(threadIdInput, options = {}) {
|
|
|
228
340
|
connectionManager.connect();
|
|
229
341
|
}
|
|
230
342
|
async function sendMessage(payload) {
|
|
231
|
-
const optimisticId = `optimistic-${
|
|
343
|
+
const optimisticId = `optimistic-${createClientId()}`;
|
|
232
344
|
const optimisticAttachments = attachments.value.map((a) => ({
|
|
233
345
|
id: a.id,
|
|
234
346
|
type: "file",
|
|
@@ -342,7 +454,7 @@ function useThreadSetup(threadIdInput, options = {}) {
|
|
|
342
454
|
const newAttachments = filesToAdd.map((file) => {
|
|
343
455
|
const isImage = file.type.startsWith("image/");
|
|
344
456
|
return {
|
|
345
|
-
id:
|
|
457
|
+
id: createClientId(),
|
|
346
458
|
file,
|
|
347
459
|
name: file.name,
|
|
348
460
|
mimeType: file.type,
|
|
@@ -390,6 +502,9 @@ function useThreadSetup(threadIdInput, options = {}) {
|
|
|
390
502
|
if (newThreadId !== oldThreadId) {
|
|
391
503
|
disconnect();
|
|
392
504
|
messages.value = [];
|
|
505
|
+
paginatedMessagesLoaded.value = 0;
|
|
506
|
+
hasMoreMessages.value = false;
|
|
507
|
+
isLoadingOlderMessages.value = false;
|
|
393
508
|
error.value = null;
|
|
394
509
|
pendingFiles.value = [];
|
|
395
510
|
serverFiles.value = [];
|
|
@@ -422,6 +537,10 @@ function useThreadSetup(threadIdInput, options = {}) {
|
|
|
422
537
|
threadId,
|
|
423
538
|
options: resolvedOptions,
|
|
424
539
|
messages,
|
|
540
|
+
hasMoreMessages,
|
|
541
|
+
isLoadingOlderMessages,
|
|
542
|
+
loadOlderMessages,
|
|
543
|
+
loadMessagesByIds,
|
|
425
544
|
workblocks,
|
|
426
545
|
subagentBlocks,
|
|
427
546
|
groupedMessages,
|
package/dist/index.d.cts
CHANGED
|
@@ -47,6 +47,16 @@ interface ThreadContext {
|
|
|
47
47
|
options: ThreadProviderOptions;
|
|
48
48
|
/** Current messages in the thread */
|
|
49
49
|
messages: Ref<_standardagents_client.Message[]>;
|
|
50
|
+
/** Whether older messages are available */
|
|
51
|
+
hasMoreMessages: Ref<boolean>;
|
|
52
|
+
/** Whether older messages are currently loading */
|
|
53
|
+
isLoadingOlderMessages: Ref<boolean>;
|
|
54
|
+
/** Load the next older message page */
|
|
55
|
+
loadOlderMessages: () => Promise<boolean>;
|
|
56
|
+
/** Load specific messages, optionally expanded to complete workblocks */
|
|
57
|
+
loadMessagesByIds: (ids: string[], options?: {
|
|
58
|
+
includeWorkblocks?: boolean;
|
|
59
|
+
}) => Promise<boolean>;
|
|
50
60
|
/** Messages transformed to workblocks (if useWorkblocks is true) */
|
|
51
61
|
workblocks: ComputedRef<_standardagents_client.ThreadMessage[]>;
|
|
52
62
|
/** Messages transformed to subagent blocks (if useSubagentBlocks is true) */
|
package/dist/index.d.ts
CHANGED
|
@@ -47,6 +47,16 @@ interface ThreadContext {
|
|
|
47
47
|
options: ThreadProviderOptions;
|
|
48
48
|
/** Current messages in the thread */
|
|
49
49
|
messages: Ref<_standardagents_client.Message[]>;
|
|
50
|
+
/** Whether older messages are available */
|
|
51
|
+
hasMoreMessages: Ref<boolean>;
|
|
52
|
+
/** Whether older messages are currently loading */
|
|
53
|
+
isLoadingOlderMessages: Ref<boolean>;
|
|
54
|
+
/** Load the next older message page */
|
|
55
|
+
loadOlderMessages: () => Promise<boolean>;
|
|
56
|
+
/** Load specific messages, optionally expanded to complete workblocks */
|
|
57
|
+
loadMessagesByIds: (ids: string[], options?: {
|
|
58
|
+
includeWorkblocks?: boolean;
|
|
59
|
+
}) => Promise<boolean>;
|
|
50
60
|
/** Messages transformed to workblocks (if useWorkblocks is true) */
|
|
51
61
|
workblocks: ComputedRef<_standardagents_client.ThreadMessage[]>;
|
|
52
62
|
/** Messages transformed to subagent blocks (if useSubagentBlocks is true) */
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,29 @@ function useStandardAgents() {
|
|
|
32
32
|
return context;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
// src/utils/id.ts
|
|
36
|
+
function createClientId() {
|
|
37
|
+
const cryptoRef = globalThis.crypto;
|
|
38
|
+
if (typeof cryptoRef?.randomUUID === "function") {
|
|
39
|
+
return cryptoRef.randomUUID();
|
|
40
|
+
}
|
|
41
|
+
if (typeof cryptoRef?.getRandomValues === "function") {
|
|
42
|
+
const bytes = new Uint8Array(16);
|
|
43
|
+
cryptoRef.getRandomValues(bytes);
|
|
44
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
45
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
46
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0"));
|
|
47
|
+
return [
|
|
48
|
+
hex.slice(0, 4).join(""),
|
|
49
|
+
hex.slice(4, 6).join(""),
|
|
50
|
+
hex.slice(6, 8).join(""),
|
|
51
|
+
hex.slice(8, 10).join(""),
|
|
52
|
+
hex.slice(10, 16).join("")
|
|
53
|
+
].join("-");
|
|
54
|
+
}
|
|
55
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
35
58
|
// src/composables/useThreadSetup.ts
|
|
36
59
|
var uploadManager = new FileUploadManager();
|
|
37
60
|
function fileToBase64(file) {
|
|
@@ -68,6 +91,9 @@ function useThreadSetup(threadIdInput, options = {}) {
|
|
|
68
91
|
includeSilent
|
|
69
92
|
};
|
|
70
93
|
const messages = ref([]);
|
|
94
|
+
const paginatedMessagesLoaded = ref(0);
|
|
95
|
+
const hasMoreMessages = ref(false);
|
|
96
|
+
const isLoadingOlderMessages = ref(false);
|
|
71
97
|
const status = ref("disconnected");
|
|
72
98
|
const isLoading = ref(preload);
|
|
73
99
|
const error = ref(null);
|
|
@@ -104,18 +130,104 @@ function useThreadSetup(threadIdInput, options = {}) {
|
|
|
104
130
|
}
|
|
105
131
|
return messages.value;
|
|
106
132
|
});
|
|
133
|
+
const MESSAGE_PAGE_SIZE = 100;
|
|
134
|
+
const mergeMessages = (olderMessages, newerMessages) => {
|
|
135
|
+
const messageMap = /* @__PURE__ */ new Map();
|
|
136
|
+
for (const message of olderMessages) {
|
|
137
|
+
messageMap.set(message.id, message);
|
|
138
|
+
}
|
|
139
|
+
for (const message of newerMessages) {
|
|
140
|
+
messageMap.set(message.id, message);
|
|
141
|
+
}
|
|
142
|
+
return Array.from(messageMap.values()).sort((a, b) => {
|
|
143
|
+
if (a.created_at !== b.created_at) return a.created_at - b.created_at;
|
|
144
|
+
return a.id.localeCompare(b.id);
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
const hasCompleteOldestWorkblockBoundary = () => {
|
|
148
|
+
const firstMessage = messages.value[0];
|
|
149
|
+
if (!firstMessage) return true;
|
|
150
|
+
if (firstMessage.role === "tool") return false;
|
|
151
|
+
if (firstMessage.role === "assistant" && firstMessage.tool_calls) return false;
|
|
152
|
+
return true;
|
|
153
|
+
};
|
|
107
154
|
async function loadMessages() {
|
|
108
155
|
isLoading.value = true;
|
|
109
156
|
error.value = null;
|
|
110
157
|
try {
|
|
111
|
-
const
|
|
112
|
-
|
|
158
|
+
const page = await client.getMessagesPage(threadId.value, {
|
|
159
|
+
limit: MESSAGE_PAGE_SIZE,
|
|
160
|
+
offset: 0,
|
|
161
|
+
depth,
|
|
162
|
+
includeSilent
|
|
163
|
+
});
|
|
164
|
+
messages.value = page.messages;
|
|
165
|
+
paginatedMessagesLoaded.value = page.messages.length;
|
|
166
|
+
hasMoreMessages.value = page.hasMore;
|
|
113
167
|
} catch (err) {
|
|
114
168
|
error.value = err instanceof Error ? err : new Error(String(err));
|
|
115
169
|
} finally {
|
|
116
170
|
isLoading.value = false;
|
|
117
171
|
}
|
|
118
172
|
}
|
|
173
|
+
async function loadOlderMessages() {
|
|
174
|
+
if (isLoadingOlderMessages.value || isLoading.value || !hasMoreMessages.value) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
isLoadingOlderMessages.value = true;
|
|
178
|
+
error.value = null;
|
|
179
|
+
try {
|
|
180
|
+
let loadedAny = false;
|
|
181
|
+
let keepLoading = true;
|
|
182
|
+
while (keepLoading && hasMoreMessages.value) {
|
|
183
|
+
const page = await client.getMessagesPage(threadId.value, {
|
|
184
|
+
limit: MESSAGE_PAGE_SIZE,
|
|
185
|
+
offset: paginatedMessagesLoaded.value,
|
|
186
|
+
depth,
|
|
187
|
+
includeSilent
|
|
188
|
+
});
|
|
189
|
+
if (page.messages.length === 0) {
|
|
190
|
+
hasMoreMessages.value = false;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
messages.value = mergeMessages(page.messages, messages.value);
|
|
194
|
+
paginatedMessagesLoaded.value += page.messages.length;
|
|
195
|
+
hasMoreMessages.value = page.hasMore;
|
|
196
|
+
loadedAny = true;
|
|
197
|
+
keepLoading = !hasCompleteOldestWorkblockBoundary();
|
|
198
|
+
}
|
|
199
|
+
return loadedAny;
|
|
200
|
+
} catch (err) {
|
|
201
|
+
error.value = err instanceof Error ? err : new Error(String(err));
|
|
202
|
+
return false;
|
|
203
|
+
} finally {
|
|
204
|
+
isLoadingOlderMessages.value = false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async function loadMessagesByIds(ids, options2 = {}) {
|
|
208
|
+
const uniqueIds = Array.from(new Set(ids.map((id) => id.trim()).filter(Boolean)));
|
|
209
|
+
if (uniqueIds.length === 0) return true;
|
|
210
|
+
try {
|
|
211
|
+
let loadedAny = false;
|
|
212
|
+
for (let i = 0; i < uniqueIds.length; i += 200) {
|
|
213
|
+
const chunk = uniqueIds.slice(i, i + 200);
|
|
214
|
+
const page = await client.getMessagesPage(threadId.value, {
|
|
215
|
+
ids: chunk,
|
|
216
|
+
depth,
|
|
217
|
+
includeSilent,
|
|
218
|
+
includeWorkblocks: options2.includeWorkblocks
|
|
219
|
+
});
|
|
220
|
+
if (page.messages.length > 0) {
|
|
221
|
+
messages.value = mergeMessages(page.messages, messages.value);
|
|
222
|
+
loadedAny = true;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return loadedAny;
|
|
226
|
+
} catch (err) {
|
|
227
|
+
error.value = err instanceof Error ? err : new Error(String(err));
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
119
231
|
async function loadFiles() {
|
|
120
232
|
try {
|
|
121
233
|
const fileList = await client.listFiles(threadId.value);
|
|
@@ -227,7 +339,7 @@ function useThreadSetup(threadIdInput, options = {}) {
|
|
|
227
339
|
connectionManager.connect();
|
|
228
340
|
}
|
|
229
341
|
async function sendMessage(payload) {
|
|
230
|
-
const optimisticId = `optimistic-${
|
|
342
|
+
const optimisticId = `optimistic-${createClientId()}`;
|
|
231
343
|
const optimisticAttachments = attachments.value.map((a) => ({
|
|
232
344
|
id: a.id,
|
|
233
345
|
type: "file",
|
|
@@ -341,7 +453,7 @@ function useThreadSetup(threadIdInput, options = {}) {
|
|
|
341
453
|
const newAttachments = filesToAdd.map((file) => {
|
|
342
454
|
const isImage = file.type.startsWith("image/");
|
|
343
455
|
return {
|
|
344
|
-
id:
|
|
456
|
+
id: createClientId(),
|
|
345
457
|
file,
|
|
346
458
|
name: file.name,
|
|
347
459
|
mimeType: file.type,
|
|
@@ -389,6 +501,9 @@ function useThreadSetup(threadIdInput, options = {}) {
|
|
|
389
501
|
if (newThreadId !== oldThreadId) {
|
|
390
502
|
disconnect();
|
|
391
503
|
messages.value = [];
|
|
504
|
+
paginatedMessagesLoaded.value = 0;
|
|
505
|
+
hasMoreMessages.value = false;
|
|
506
|
+
isLoadingOlderMessages.value = false;
|
|
392
507
|
error.value = null;
|
|
393
508
|
pendingFiles.value = [];
|
|
394
509
|
serverFiles.value = [];
|
|
@@ -421,6 +536,10 @@ function useThreadSetup(threadIdInput, options = {}) {
|
|
|
421
536
|
threadId,
|
|
422
537
|
options: resolvedOptions,
|
|
423
538
|
messages,
|
|
539
|
+
hasMoreMessages,
|
|
540
|
+
isLoadingOlderMessages,
|
|
541
|
+
loadOlderMessages,
|
|
542
|
+
loadMessagesByIds,
|
|
424
543
|
workblocks,
|
|
425
544
|
subagentBlocks,
|
|
426
545
|
groupedMessages,
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@standardagents/vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
4
|
+
"private": false,
|
|
4
5
|
"description": "Vue SDK for Standard Agents",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"main": "./dist/index.cjs",
|
|
@@ -22,7 +23,7 @@
|
|
|
22
23
|
"dist"
|
|
23
24
|
],
|
|
24
25
|
"dependencies": {
|
|
25
|
-
"@standardagents/client": "0.
|
|
26
|
+
"@standardagents/client": "0.15.1"
|
|
26
27
|
},
|
|
27
28
|
"peerDependencies": {
|
|
28
29
|
"vue": "^3.3.0"
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
"vue": "^3.5.13"
|
|
40
41
|
},
|
|
41
42
|
"publishConfig": {
|
|
42
|
-
"access": "
|
|
43
|
+
"access": "public",
|
|
43
44
|
"registry": "https://registry.npmjs.org/"
|
|
44
45
|
},
|
|
45
46
|
"keywords": [
|