@neurocode-ai/enterprise 1.18.8

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.
@@ -0,0 +1,292 @@
1
+ import { describe, expect, test } from "bun:test"
2
+ import { Share } from "../../src/core/share"
3
+ import { Storage } from "../../src/core/storage"
4
+ import { Identifier } from "@neurocode-ai/core/util/identifier"
5
+
6
+ describe.concurrent("core.share", () => {
7
+ test("should create a share", async () => {
8
+ const sessionID = Identifier.descending()
9
+ const share = await Share.create({ sessionID })
10
+
11
+ expect(share.sessionID).toBe(sessionID)
12
+ expect(share.secret).toBeDefined()
13
+
14
+ await Share.remove({ id: share.id, secret: share.secret })
15
+ })
16
+
17
+ test("should remove a share as admin", async () => {
18
+ const share = await Share.create({ sessionID: Identifier.descending() })
19
+
20
+ await Share.removeAdmin({ id: share.id })
21
+
22
+ expect(await Share.get(share.id)).toBeUndefined()
23
+ })
24
+
25
+ test("should sync data to a share", async () => {
26
+ const sessionID = Identifier.descending()
27
+ const share = await Share.create({ sessionID })
28
+
29
+ const data: Share.Data[] = [
30
+ {
31
+ type: "part",
32
+ data: { id: "part1", sessionID, messageID: "msg1", type: "text", text: "Hello" },
33
+ },
34
+ ]
35
+
36
+ await Share.sync({
37
+ share: { id: share.id, secret: share.secret },
38
+ data,
39
+ })
40
+
41
+ const snapshot = await Storage.read<{ data: Share.Data[] }>(["share_snapshot", share.id])
42
+ expect(snapshot?.data).toHaveLength(1)
43
+
44
+ await Share.remove({ id: share.id, secret: share.secret })
45
+ })
46
+
47
+ test("should sync multiple batches of data", async () => {
48
+ const sessionID = Identifier.descending()
49
+ const share = await Share.create({ sessionID })
50
+
51
+ const data1: Share.Data[] = [
52
+ {
53
+ type: "part",
54
+ data: { id: "part1", sessionID, messageID: "msg1", type: "text", text: "Hello" },
55
+ },
56
+ ]
57
+
58
+ const data2: Share.Data[] = [
59
+ {
60
+ type: "part",
61
+ data: { id: "part2", sessionID, messageID: "msg1", type: "text", text: "World" },
62
+ },
63
+ ]
64
+
65
+ await Share.sync({
66
+ share: { id: share.id, secret: share.secret },
67
+ data: data1,
68
+ })
69
+
70
+ await Share.sync({
71
+ share: { id: share.id, secret: share.secret },
72
+ data: data2,
73
+ })
74
+
75
+ const snapshot = await Storage.read<{ data: Share.Data[] }>(["share_snapshot", share.id])
76
+ expect(snapshot?.data).toHaveLength(2)
77
+
78
+ await Share.remove({ id: share.id, secret: share.secret })
79
+ })
80
+
81
+ test("should retrieve synced data", async () => {
82
+ const sessionID = Identifier.descending()
83
+ const share = await Share.create({ sessionID })
84
+
85
+ const data: Share.Data[] = [
86
+ {
87
+ type: "part",
88
+ data: { id: "part1", sessionID, messageID: "msg1", type: "text", text: "Hello" },
89
+ },
90
+ {
91
+ type: "part",
92
+ data: { id: "part2", sessionID, messageID: "msg1", type: "text", text: "World" },
93
+ },
94
+ ]
95
+
96
+ await Share.sync({
97
+ share: { id: share.id, secret: share.secret },
98
+ data,
99
+ })
100
+
101
+ const result = await Share.data(share.id)
102
+
103
+ expect(result.length).toBe(2)
104
+ expect(result[0].type).toBe("part")
105
+ expect(result[1].type).toBe("part")
106
+
107
+ await Share.remove({ id: share.id, secret: share.secret })
108
+ })
109
+
110
+ test("should retrieve data from multiple syncs", async () => {
111
+ const sessionID = Identifier.descending()
112
+ const share = await Share.create({ sessionID })
113
+
114
+ const data1: Share.Data[] = [
115
+ {
116
+ type: "part",
117
+ data: { id: "part1", sessionID, messageID: "msg1", type: "text", text: "Hello" },
118
+ },
119
+ ]
120
+
121
+ const data2: Share.Data[] = [
122
+ {
123
+ type: "part",
124
+ data: { id: "part2", sessionID, messageID: "msg2", type: "text", text: "World" },
125
+ },
126
+ ]
127
+
128
+ const data3: Share.Data[] = [
129
+ { type: "part", data: { id: "part3", sessionID, messageID: "msg3", type: "text", text: "!" } },
130
+ ]
131
+
132
+ await Share.sync({
133
+ share: { id: share.id, secret: share.secret },
134
+ data: data1,
135
+ })
136
+
137
+ await Share.sync({
138
+ share: { id: share.id, secret: share.secret },
139
+ data: data2,
140
+ })
141
+
142
+ await Share.sync({
143
+ share: { id: share.id, secret: share.secret },
144
+ data: data3,
145
+ })
146
+
147
+ const result = await Share.data(share.id)
148
+
149
+ expect(result.length).toBe(3)
150
+ const parts = result.filter((d) => d.type === "part")
151
+ expect(parts.length).toBe(3)
152
+
153
+ await Share.remove({ id: share.id, secret: share.secret })
154
+ })
155
+
156
+ test("should return latest data when syncing duplicate parts", async () => {
157
+ const sessionID = Identifier.descending()
158
+ const share = await Share.create({ sessionID })
159
+
160
+ const data1: Share.Data[] = [
161
+ {
162
+ type: "part",
163
+ data: { id: "part1", sessionID, messageID: "msg1", type: "text", text: "Hello" },
164
+ },
165
+ ]
166
+
167
+ const data2: Share.Data[] = [
168
+ {
169
+ type: "part",
170
+ data: { id: "part1", sessionID, messageID: "msg1", type: "text", text: "Hello Updated" },
171
+ },
172
+ ]
173
+
174
+ await Share.sync({
175
+ share: { id: share.id, secret: share.secret },
176
+ data: data1,
177
+ })
178
+
179
+ await Share.sync({
180
+ share: { id: share.id, secret: share.secret },
181
+ data: data2,
182
+ })
183
+
184
+ const result = await Share.data(share.id)
185
+
186
+ expect(result.length).toBe(1)
187
+ const [first] = result
188
+ expect(first.type).toBe("part")
189
+ expect(first.type === "part" && first.data.type === "text" && first.data.text).toBe("Hello Updated")
190
+
191
+ await Share.remove({ id: share.id, secret: share.secret })
192
+ })
193
+
194
+ test("should return empty array for share with no data", async () => {
195
+ const sessionID = Identifier.descending()
196
+ const share = await Share.create({ sessionID })
197
+
198
+ const result = await Share.data(share.id)
199
+
200
+ expect(result).toEqual([])
201
+
202
+ await Share.remove({ id: share.id, secret: share.secret })
203
+ })
204
+
205
+ test("should migrate legacy event data into the snapshot", async () => {
206
+ const sessionID = Identifier.descending()
207
+ const share = await Share.create({ sessionID })
208
+ const data: Share.Data[] = [
209
+ {
210
+ type: "part",
211
+ data: { id: "part1", sessionID, messageID: "msg1", type: "text", text: "Hello" },
212
+ },
213
+ ]
214
+
215
+ await Storage.remove(["share_snapshot", share.id])
216
+ await Storage.write(["share_event", share.id, Identifier.descending()], data)
217
+
218
+ const result = await Share.data(share.id)
219
+ const snapshot = await Storage.read<{ data: Share.Data[] }>(["share_snapshot", share.id])
220
+
221
+ expect(result).toHaveLength(1)
222
+ expect(snapshot?.data).toHaveLength(1)
223
+
224
+ await Share.remove({ id: share.id, secret: share.secret })
225
+ })
226
+
227
+ test("should throw error for invalid secret", async () => {
228
+ const sessionID = Identifier.descending()
229
+ const share = await Share.create({ sessionID })
230
+
231
+ const data: Share.Data[] = [
232
+ {
233
+ type: "part",
234
+ data: { id: "part1", sessionID, messageID: "msg1", type: "text", text: "Test" },
235
+ },
236
+ ]
237
+
238
+ expect(async () => {
239
+ await Share.sync({
240
+ share: { id: share.id, secret: "invalid-secret" },
241
+ data,
242
+ })
243
+ }).toThrow()
244
+
245
+ await Share.remove({ id: share.id, secret: share.secret })
246
+ })
247
+
248
+ test("should throw error for non-existent share", async () => {
249
+ const sessionID = Identifier.descending()
250
+ const data: Share.Data[] = [
251
+ {
252
+ type: "part",
253
+ data: { id: "part1", sessionID, messageID: "msg1", type: "text", text: "Test" },
254
+ },
255
+ ]
256
+
257
+ expect(async () => {
258
+ await Share.sync({
259
+ share: { id: "non-existent-id", secret: "some-secret" },
260
+ data,
261
+ })
262
+ }).toThrow()
263
+ })
264
+
265
+ test("should handle different data types", async () => {
266
+ const sessionID = Identifier.descending()
267
+ const share = await Share.create({ sessionID })
268
+
269
+ const data: Share.Data[] = [
270
+ { type: "session", data: { id: sessionID, status: "running" } as any },
271
+ { type: "message", data: { id: "msg1", sessionID } as any },
272
+ {
273
+ type: "part",
274
+ data: { id: "part1", sessionID, messageID: "msg1", type: "text", text: "Hello" },
275
+ },
276
+ ]
277
+
278
+ await Share.sync({
279
+ share: { id: share.id, secret: share.secret },
280
+ data,
281
+ })
282
+
283
+ const result = await Share.data(share.id)
284
+
285
+ expect(result.length).toBe(3)
286
+ expect(result.some((d) => d.type === "session")).toBe(true)
287
+ expect(result.some((d) => d.type === "message")).toBe(true)
288
+ expect(result.some((d) => d.type === "part")).toBe(true)
289
+
290
+ await Share.remove({ id: share.id, secret: share.secret })
291
+ })
292
+ })
@@ -0,0 +1,64 @@
1
+ import { describe, expect, test, afterAll } from "bun:test"
2
+ import { Storage } from "../../src/core/storage"
3
+
4
+ describe("core.storage", () => {
5
+ test("should list files with after and before range", async () => {
6
+ await Storage.write(["test", "users", "user1"], { name: "user1" })
7
+ await Storage.write(["test", "users", "user2"], { name: "user2" })
8
+ await Storage.write(["test", "users", "user3"], { name: "user3" })
9
+ await Storage.write(["test", "users", "user4"], { name: "user4" })
10
+ await Storage.write(["test", "users", "user5"], { name: "user5" })
11
+
12
+ const result = await Storage.list({ prefix: ["test", "users"], after: "user2", before: "user4" })
13
+
14
+ expect(result).toEqual([["test", "users", "user3"]])
15
+ })
16
+
17
+ test("should list files with after only", async () => {
18
+ const result = await Storage.list({ prefix: ["test", "users"], after: "user3" })
19
+
20
+ expect(result).toEqual([
21
+ ["test", "users", "user4"],
22
+ ["test", "users", "user5"],
23
+ ])
24
+ })
25
+
26
+ test("should list files with limit", async () => {
27
+ const result = await Storage.list({ prefix: ["test", "users"], limit: 3 })
28
+
29
+ expect(result).toEqual([
30
+ ["test", "users", "user1"],
31
+ ["test", "users", "user2"],
32
+ ["test", "users", "user3"],
33
+ ])
34
+ })
35
+
36
+ test("should list all files without prefix", async () => {
37
+ const result = await Storage.list()
38
+
39
+ expect(result.length).toBeGreaterThan(0)
40
+ })
41
+
42
+ test("should list all files with prefix", async () => {
43
+ const result = await Storage.list({ prefix: ["test", "users"] })
44
+
45
+ expect(result).toEqual([
46
+ ["test", "users", "user1"],
47
+ ["test", "users", "user2"],
48
+ ["test", "users", "user3"],
49
+ ["test", "users", "user4"],
50
+ ["test", "users", "user5"],
51
+ ])
52
+ })
53
+
54
+ afterAll(async () => {
55
+ const testFiles = await Storage.list({ prefix: ["test"] })
56
+
57
+ for (const file of testFiles) {
58
+ await Storage.remove(file)
59
+ }
60
+
61
+ const remainingFiles = await Storage.list({ prefix: ["test"] })
62
+ expect(remainingFiles).toEqual([])
63
+ })
64
+ })
package/test-debug.ts ADDED
@@ -0,0 +1,40 @@
1
+ import { Share } from "./src/core/share"
2
+ import { Storage } from "./src/core/storage"
3
+
4
+ async function test() {
5
+ const shareInfo = await Share.create({ sessionID: "test-debug-" + Date.now() })
6
+
7
+ const batch1: Share.Data[] = [
8
+ { type: "part", data: { id: "part1", sessionID: "session1", messageID: "msg1", type: "text", text: "Hello" } },
9
+ ]
10
+
11
+ const batch2: Share.Data[] = [
12
+ {
13
+ type: "part",
14
+ data: { id: "part1", sessionID: "session1", messageID: "msg1", type: "text", text: "Hello Updated" },
15
+ },
16
+ ]
17
+
18
+ await Share.sync({
19
+ share: { id: shareInfo.id, secret: shareInfo.secret },
20
+ data: batch1,
21
+ })
22
+
23
+ await Share.sync({
24
+ share: { id: shareInfo.id, secret: shareInfo.secret },
25
+ data: batch2,
26
+ })
27
+
28
+ const events = await Storage.list({ prefix: ["share_event", shareInfo.id] })
29
+ console.log("Events (raw):", events)
30
+ console.log("Events (reversed):", events.toReversed())
31
+
32
+ for (const event of events.toReversed()) {
33
+ const data = await Storage.read(event)
34
+ console.log("Event data (reversed order):", event, data)
35
+ }
36
+
37
+ await Share.remove({ id: shareInfo.id, secret: shareInfo.secret })
38
+ }
39
+
40
+ void test()
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "skipLibCheck": true,
7
+ "allowSyntheticDefaultImports": true,
8
+ "esModuleInterop": true,
9
+ "jsx": "preserve",
10
+ "jsxImportSource": "solid-js",
11
+ "allowJs": true,
12
+ "noEmit": true,
13
+ "strict": true,
14
+ "types": ["@cloudflare/workers-types", "vite/client", "bun"],
15
+ "isolatedModules": true,
16
+ "paths": {
17
+ "~/*": ["./src/*"]
18
+ }
19
+ }
20
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { defineConfig, PluginOption } from "vite"
2
+ import { solidStart } from "@solidjs/start/config"
3
+ import { nitro } from "nitro/vite"
4
+ import tailwindcss from "@tailwindcss/vite"
5
+
6
+ const nitroConfig: any = (() => {
7
+ const target = process.env.NEUROCODE_DEPLOYMENT_TARGET
8
+ if (target === "cloudflare") {
9
+ return {
10
+ compatibilityDate: "2024-09-19",
11
+ preset: "cloudflare-module",
12
+ cloudflare: {
13
+ nodeCompat: true,
14
+ },
15
+ }
16
+ }
17
+ return {}
18
+ })()
19
+
20
+ export default defineConfig({
21
+ plugins: [
22
+ tailwindcss(),
23
+ solidStart() as PluginOption,
24
+ nitro({
25
+ ...nitroConfig,
26
+ baseURL: process.env.NEUROCODE_BASE_URL,
27
+ }),
28
+ ],
29
+ server: {
30
+ host: "0.0.0.0",
31
+ allowedHosts: true,
32
+ port: 3002,
33
+ },
34
+ worker: {
35
+ format: "es",
36
+ },
37
+ })