effect 4.0.0-beta.44 → 4.0.0-beta.46

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 (151) hide show
  1. package/dist/Equal.d.ts.map +1 -1
  2. package/dist/Equal.js +16 -0
  3. package/dist/Equal.js.map +1 -1
  4. package/dist/Hash.js +1 -1
  5. package/dist/Hash.js.map +1 -1
  6. package/dist/Semaphore.d.ts +1 -1
  7. package/dist/Semaphore.d.ts.map +1 -1
  8. package/dist/Semaphore.js +1 -3
  9. package/dist/Semaphore.js.map +1 -1
  10. package/dist/unstable/ai/McpServer.d.ts.map +1 -1
  11. package/dist/unstable/ai/McpServer.js +24 -21
  12. package/dist/unstable/ai/McpServer.js.map +1 -1
  13. package/dist/unstable/eventlog/Event.d.ts +0 -6
  14. package/dist/unstable/eventlog/Event.d.ts.map +1 -1
  15. package/dist/unstable/eventlog/Event.js +0 -5
  16. package/dist/unstable/eventlog/Event.js.map +1 -1
  17. package/dist/unstable/eventlog/EventGroup.d.ts +0 -2
  18. package/dist/unstable/eventlog/EventGroup.d.ts.map +1 -1
  19. package/dist/unstable/eventlog/EventGroup.js +0 -2
  20. package/dist/unstable/eventlog/EventGroup.js.map +1 -1
  21. package/dist/unstable/eventlog/EventJournal.d.ts +22 -5
  22. package/dist/unstable/eventlog/EventJournal.d.ts.map +1 -1
  23. package/dist/unstable/eventlog/EventJournal.js +126 -67
  24. package/dist/unstable/eventlog/EventJournal.js.map +1 -1
  25. package/dist/unstable/eventlog/EventLog.d.ts +88 -34
  26. package/dist/unstable/eventlog/EventLog.d.ts.map +1 -1
  27. package/dist/unstable/eventlog/EventLog.js +215 -141
  28. package/dist/unstable/eventlog/EventLog.js.map +1 -1
  29. package/dist/unstable/eventlog/EventLogEncryption.d.ts +9 -7
  30. package/dist/unstable/eventlog/EventLogEncryption.d.ts.map +1 -1
  31. package/dist/unstable/eventlog/EventLogEncryption.js +13 -15
  32. package/dist/unstable/eventlog/EventLogEncryption.js.map +1 -1
  33. package/dist/unstable/eventlog/EventLogMessage.d.ts +228 -0
  34. package/dist/unstable/eventlog/EventLogMessage.d.ts.map +1 -0
  35. package/dist/unstable/eventlog/EventLogMessage.js +214 -0
  36. package/dist/unstable/eventlog/EventLogMessage.js.map +1 -0
  37. package/dist/unstable/eventlog/EventLogRemote.d.ts +109 -194
  38. package/dist/unstable/eventlog/EventLogRemote.d.ts.map +1 -1
  39. package/dist/unstable/eventlog/EventLogRemote.js +165 -320
  40. package/dist/unstable/eventlog/EventLogRemote.js.map +1 -1
  41. package/dist/unstable/eventlog/EventLogServer.d.ts +25 -47
  42. package/dist/unstable/eventlog/EventLogServer.d.ts.map +1 -1
  43. package/dist/unstable/eventlog/EventLogServer.js +127 -198
  44. package/dist/unstable/eventlog/EventLogServer.js.map +1 -1
  45. package/dist/unstable/eventlog/EventLogServerEncrypted.d.ts +60 -0
  46. package/dist/unstable/eventlog/EventLogServerEncrypted.d.ts.map +1 -0
  47. package/dist/unstable/eventlog/EventLogServerEncrypted.js +166 -0
  48. package/dist/unstable/eventlog/EventLogServerEncrypted.js.map +1 -0
  49. package/dist/unstable/eventlog/EventLogServerUnencrypted.d.ts +183 -0
  50. package/dist/unstable/eventlog/EventLogServerUnencrypted.d.ts.map +1 -0
  51. package/dist/unstable/eventlog/EventLogServerUnencrypted.js +461 -0
  52. package/dist/unstable/eventlog/EventLogServerUnencrypted.js.map +1 -0
  53. package/dist/unstable/eventlog/EventLogSessionAuth.d.ts +117 -0
  54. package/dist/unstable/eventlog/EventLogSessionAuth.d.ts.map +1 -0
  55. package/dist/unstable/eventlog/EventLogSessionAuth.js +284 -0
  56. package/dist/unstable/eventlog/EventLogSessionAuth.js.map +1 -0
  57. package/dist/unstable/eventlog/{SqlEventLogJournal.d.ts → SqlEventJournal.d.ts} +2 -2
  58. package/dist/unstable/eventlog/SqlEventJournal.d.ts.map +1 -0
  59. package/dist/unstable/eventlog/{SqlEventLogJournal.js → SqlEventJournal.js} +20 -14
  60. package/dist/unstable/eventlog/SqlEventJournal.js.map +1 -0
  61. package/dist/unstable/eventlog/{SqlEventLogServer.d.ts → SqlEventLogServerEncrypted.d.ts} +5 -5
  62. package/dist/unstable/eventlog/SqlEventLogServerEncrypted.d.ts.map +1 -0
  63. package/dist/unstable/eventlog/{SqlEventLogServer.js → SqlEventLogServerEncrypted.js} +65 -24
  64. package/dist/unstable/eventlog/SqlEventLogServerEncrypted.js.map +1 -0
  65. package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.d.ts +25 -0
  66. package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.d.ts.map +1 -0
  67. package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.js +354 -0
  68. package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.js.map +1 -0
  69. package/dist/unstable/eventlog/index.d.ts +22 -2
  70. package/dist/unstable/eventlog/index.d.ts.map +1 -1
  71. package/dist/unstable/eventlog/index.js +22 -2
  72. package/dist/unstable/eventlog/index.js.map +1 -1
  73. package/dist/unstable/eventlog/internal/identityRootSecretDerivation.d.ts +2 -0
  74. package/dist/unstable/eventlog/internal/identityRootSecretDerivation.d.ts.map +1 -0
  75. package/dist/unstable/eventlog/internal/identityRootSecretDerivation.js +89 -0
  76. package/dist/unstable/eventlog/internal/identityRootSecretDerivation.js.map +1 -0
  77. package/dist/unstable/reactivity/AtomHttpApi.d.ts +1 -2
  78. package/dist/unstable/reactivity/AtomHttpApi.d.ts.map +1 -1
  79. package/dist/unstable/reactivity/AtomHttpApi.js +2 -2
  80. package/dist/unstable/reactivity/AtomHttpApi.js.map +1 -1
  81. package/dist/unstable/reactivity/AtomRpc.d.ts +1 -2
  82. package/dist/unstable/reactivity/AtomRpc.d.ts.map +1 -1
  83. package/dist/unstable/reactivity/AtomRpc.js +3 -3
  84. package/dist/unstable/reactivity/AtomRpc.js.map +1 -1
  85. package/dist/unstable/rpc/Rpc.d.ts +25 -4
  86. package/dist/unstable/rpc/Rpc.d.ts.map +1 -1
  87. package/dist/unstable/rpc/Rpc.js +26 -0
  88. package/dist/unstable/rpc/Rpc.js.map +1 -1
  89. package/dist/unstable/rpc/RpcClient.d.ts +3 -13
  90. package/dist/unstable/rpc/RpcClient.d.ts.map +1 -1
  91. package/dist/unstable/rpc/RpcClient.js +47 -23
  92. package/dist/unstable/rpc/RpcClient.js.map +1 -1
  93. package/dist/unstable/rpc/RpcGroup.d.ts +1 -1
  94. package/dist/unstable/rpc/RpcGroup.d.ts.map +1 -1
  95. package/dist/unstable/rpc/RpcMiddleware.d.ts +2 -2
  96. package/dist/unstable/rpc/RpcMiddleware.d.ts.map +1 -1
  97. package/dist/unstable/rpc/RpcServer.d.ts.map +1 -1
  98. package/dist/unstable/rpc/RpcServer.js +3 -2
  99. package/dist/unstable/rpc/RpcServer.js.map +1 -1
  100. package/dist/unstable/rpc/Utils.d.ts +6 -0
  101. package/dist/unstable/rpc/Utils.d.ts.map +1 -1
  102. package/dist/unstable/rpc/Utils.js +44 -0
  103. package/dist/unstable/rpc/Utils.js.map +1 -1
  104. package/dist/unstable/schema/Model.d.ts +2 -2
  105. package/dist/unstable/schema/Model.d.ts.map +1 -1
  106. package/dist/unstable/schema/Model.js +2 -4
  107. package/dist/unstable/schema/Model.js.map +1 -1
  108. package/dist/unstable/schema/VariantSchema.d.ts +1 -1
  109. package/dist/unstable/schema/VariantSchema.d.ts.map +1 -1
  110. package/dist/unstable/schema/VariantSchema.js +1 -12
  111. package/dist/unstable/schema/VariantSchema.js.map +1 -1
  112. package/dist/unstable/workers/Transferable.d.ts +1 -1
  113. package/dist/unstable/workers/Transferable.d.ts.map +1 -1
  114. package/dist/unstable/workers/Transferable.js +1 -1
  115. package/dist/unstable/workers/Transferable.js.map +1 -1
  116. package/package.json +1 -1
  117. package/src/Equal.ts +17 -0
  118. package/src/Hash.ts +2 -2
  119. package/src/Semaphore.ts +2 -4
  120. package/src/unstable/ai/McpServer.ts +24 -22
  121. package/src/unstable/eventlog/Event.ts +0 -8
  122. package/src/unstable/eventlog/EventGroup.ts +0 -4
  123. package/src/unstable/eventlog/EventJournal.ts +144 -76
  124. package/src/unstable/eventlog/EventLog.ts +342 -221
  125. package/src/unstable/eventlog/EventLogEncryption.ts +16 -30
  126. package/src/unstable/eventlog/EventLogMessage.ts +277 -0
  127. package/src/unstable/eventlog/EventLogRemote.ts +261 -408
  128. package/src/unstable/eventlog/EventLogServer.ts +182 -274
  129. package/src/unstable/eventlog/EventLogServerEncrypted.ts +206 -0
  130. package/src/unstable/eventlog/EventLogServerUnencrypted.ts +749 -0
  131. package/src/unstable/eventlog/EventLogSessionAuth.ts +437 -0
  132. package/src/unstable/eventlog/{SqlEventLogJournal.ts → SqlEventJournal.ts} +26 -18
  133. package/src/unstable/eventlog/{SqlEventLogServer.ts → SqlEventLogServerEncrypted.ts} +102 -40
  134. package/src/unstable/eventlog/SqlEventLogServerUnencrypted.ts +500 -0
  135. package/src/unstable/eventlog/index.ts +27 -2
  136. package/src/unstable/eventlog/internal/identityRootSecretDerivation.ts +153 -0
  137. package/src/unstable/reactivity/AtomHttpApi.ts +23 -8
  138. package/src/unstable/reactivity/AtomRpc.ts +16 -5
  139. package/src/unstable/rpc/Rpc.ts +42 -4
  140. package/src/unstable/rpc/RpcClient.ts +59 -24
  141. package/src/unstable/rpc/RpcGroup.ts +1 -1
  142. package/src/unstable/rpc/RpcMiddleware.ts +2 -2
  143. package/src/unstable/rpc/RpcServer.ts +5 -3
  144. package/src/unstable/rpc/Utils.ts +59 -0
  145. package/src/unstable/schema/Model.ts +4 -6
  146. package/src/unstable/schema/VariantSchema.ts +4 -17
  147. package/src/unstable/workers/Transferable.ts +9 -11
  148. package/dist/unstable/eventlog/SqlEventLogJournal.d.ts.map +0 -1
  149. package/dist/unstable/eventlog/SqlEventLogJournal.js.map +0 -1
  150. package/dist/unstable/eventlog/SqlEventLogServer.d.ts.map +0 -1
  151. package/dist/unstable/eventlog/SqlEventLogServer.js.map +0 -1
@@ -4,15 +4,15 @@
4
4
  import * as Effect from "../../Effect.ts"
5
5
  import * as Layer from "../../Layer.ts"
6
6
  import * as PubSub from "../../PubSub.ts"
7
- import * as Queue from "../../Queue.ts"
8
7
  import * as RcMap from "../../RcMap.ts"
9
8
  import * as Schema from "../../Schema.ts"
10
9
  import type * as Scope from "../../Scope.ts"
10
+ import * as Stream from "../../Stream.ts"
11
11
  import * as SqlClient from "../sql/SqlClient.ts"
12
12
  import type * as SqlError from "../sql/SqlError.ts"
13
13
  import { EntryId, makeRemoteIdUnsafe, type RemoteId } from "./EventJournal.ts"
14
14
  import * as EventLogEncryption from "./EventLogEncryption.ts"
15
- import * as EventLogServer from "./EventLogServer.ts"
15
+ import * as EventLogServerEncrypted from "./EventLogServerEncrypted.ts"
16
16
 
17
17
  /**
18
18
  * @since 4.0.0
@@ -23,7 +23,7 @@ export const makeStorage = (options?: {
23
23
  readonly remoteIdTable?: string
24
24
  readonly insertBatchSize?: number
25
25
  }): Effect.Effect<
26
- EventLogServer.Storage["Service"],
26
+ EventLogServerEncrypted.Storage["Service"],
27
27
  SqlError.SqlError,
28
28
  SqlClient.SqlClient | EventLogEncryption.EventLogEncryption | Scope.Scope
29
29
  > =>
@@ -33,9 +33,11 @@ export const makeStorage = (options?: {
33
33
 
34
34
  const tablePrefix = options?.entryTablePrefix ?? "effect_events"
35
35
  const remoteIdTable = options?.remoteIdTable ?? "effect_remote_id"
36
+ const sessionAuthBindingsTable = `${tablePrefix}_session_auth_bindings`
36
37
  const insertBatchSize = options?.insertBatchSize ?? 200
37
38
 
38
39
  const remoteIdTableSql = sql(remoteIdTable)
40
+ const sessionAuthBindingsTableSql = sql(sessionAuthBindingsTable)
39
41
 
40
42
  yield* sql.onDialectOrElse({
41
43
  pg: () =>
@@ -60,6 +62,33 @@ export const makeStorage = (options?: {
60
62
  )`
61
63
  })
62
64
 
65
+ yield* sql.onDialectOrElse({
66
+ pg: () =>
67
+ sql`
68
+ CREATE TABLE IF NOT EXISTS ${sessionAuthBindingsTableSql} (
69
+ public_key TEXT PRIMARY KEY,
70
+ signing_public_key BYTEA NOT NULL
71
+ )`,
72
+ mysql: () =>
73
+ sql`
74
+ CREATE TABLE IF NOT EXISTS ${sessionAuthBindingsTableSql} (
75
+ public_key VARCHAR(191) PRIMARY KEY,
76
+ signing_public_key BINARY(32) NOT NULL
77
+ )`,
78
+ mssql: () =>
79
+ sql`
80
+ CREATE TABLE IF NOT EXISTS ${sessionAuthBindingsTableSql} (
81
+ public_key NVARCHAR(191) PRIMARY KEY,
82
+ signing_public_key VARBINARY(32) NOT NULL
83
+ )`,
84
+ orElse: () =>
85
+ sql`
86
+ CREATE TABLE IF NOT EXISTS ${sessionAuthBindingsTableSql} (
87
+ public_key TEXT PRIMARY KEY,
88
+ signing_public_key BLOB NOT NULL
89
+ )`
90
+ })
91
+
63
92
  const remoteId = yield* sql<{ remote_id: Uint8Array }>`SELECT remote_id FROM ${remoteIdTableSql}`.pipe(
64
93
  Effect.flatMap((results) => {
65
94
  if (results.length > 0) {
@@ -74,9 +103,9 @@ export const makeStorage = (options?: {
74
103
  )
75
104
 
76
105
  const resources = yield* RcMap.make({
77
- lookup: Effect.fnUntraced(function*(publicKey: string) {
78
- const publicKeyHash = (yield* encryptions.sha256String(new TextEncoder().encode(publicKey))).slice(0, 16)
79
- const table = `${tablePrefix}_${publicKeyHash}`
106
+ lookup: Effect.fnUntraced(function*(scopeKey: string) {
107
+ const scopeHash = (yield* encryptions.sha256String(new TextEncoder().encode(scopeKey))).slice(0, 16)
108
+ const table = `${tablePrefix}_${scopeHash}`
80
109
  const tableSql = sql(table)
81
110
 
82
111
  yield* sql.onDialectOrElse({
@@ -123,12 +152,43 @@ export const makeStorage = (options?: {
123
152
  idleTimeToLive: "5 minutes"
124
153
  })
125
154
 
126
- return EventLogServer.Storage.of({
155
+ const getSessionAuthBinding = (publicKey: string) =>
156
+ sql`
157
+ SELECT public_key, signing_public_key
158
+ FROM ${sessionAuthBindingsTableSql}
159
+ WHERE public_key = ${publicKey}
160
+ `.pipe(
161
+ Effect.flatMap(decodeSessionAuthBindings),
162
+ Effect.map((rows) => {
163
+ const row = rows[0]
164
+ return row === undefined ? undefined : row.signing_public_key as Uint8Array<ArrayBuffer>
165
+ }),
166
+ Effect.orDie
167
+ )
168
+
169
+ return EventLogServerEncrypted.Storage.of({
127
170
  getId: Effect.succeed(remoteId),
171
+ getOrCreateSessionAuthBinding: Effect.fnUntraced(
172
+ function*(publicKey, signingPublicKey) {
173
+ const existing = yield* getSessionAuthBinding(publicKey)
174
+ if (existing !== undefined) {
175
+ return existing
176
+ }
177
+ return yield* sql`
178
+ INSERT INTO ${sessionAuthBindingsTableSql} (public_key, signing_public_key)
179
+ VALUES (${publicKey}, ${signingPublicKey})
180
+ `.pipe(
181
+ Effect.as(signingPublicKey)
182
+ )
183
+ },
184
+ sql.withTransaction,
185
+ Effect.orDie
186
+ ),
128
187
  write: Effect.fnUntraced(
129
- function*(publicKey, entries) {
188
+ function*(publicKey, storeId, entries) {
130
189
  if (entries.length === 0) return []
131
- const { pubsub, table } = yield* RcMap.get(resources, publicKey)
190
+ const scopeKey = makeEncryptedScopeKey(publicKey, storeId)
191
+ const { pubsub, table } = yield* RcMap.get(resources, scopeKey)
132
192
  const forInsert: Array<{
133
193
  readonly ids: Array<EntryId>
134
194
  readonly entries: Array<{
@@ -170,36 +230,21 @@ export const makeStorage = (options?: {
170
230
  Effect.orDie,
171
231
  Effect.scoped
172
232
  ),
173
- entries: Effect.fnUntraced(
174
- function*(publicKey, startSequence) {
175
- const { table } = yield* RcMap.get(resources, publicKey)
176
- return yield* sql`SELECT * FROM ${sql(table)} WHERE sequence >= ${startSequence} ORDER BY sequence ASC`.pipe(
233
+ changes: Effect.fnUntraced(
234
+ function*(publicKey, storeId, startSequence) {
235
+ const scopeKey = makeEncryptedScopeKey(publicKey, storeId)
236
+ const { pubsub, table } = yield* RcMap.get(resources, scopeKey)
237
+ const subscription = yield* PubSub.subscribe(pubsub)
238
+ const initial = yield* sql`
239
+ SELECT * FROM ${sql(table)} WHERE sequence >= ${startSequence} ORDER BY sequence ASC
240
+ `.pipe(
177
241
  Effect.flatMap(decodeEntries)
178
242
  )
243
+ return Stream.fromArray(initial).pipe(Stream.concat(Stream.fromSubscription(subscription)))
179
244
  },
180
245
  Effect.orDie,
181
- Effect.scoped
182
- ),
183
- changes: Effect.fnUntraced(function*(publicKey, startSequence) {
184
- const { pubsub, table } = yield* RcMap.get(resources, publicKey)
185
- const queue = yield* Queue.make<EventLogEncryption.EncryptedRemoteEntry>()
186
- const subscription = yield* PubSub.subscribe(pubsub)
187
- const initial = yield* sql`
188
- SELECT * FROM ${sql(table)} WHERE sequence >= ${startSequence} ORDER BY sequence ASC
189
- `.pipe(
190
- Effect.flatMap(decodeEntries)
191
- )
192
- yield* Queue.offerAll(queue, initial)
193
- yield* PubSub.takeAll(subscription).pipe(
194
- Effect.flatMap((entries) =>
195
- Queue.offerAll(queue, entries.filter((entry) => entry.sequence >= startSequence))
196
- ),
197
- Effect.forever,
198
- Effect.forkScoped
199
- )
200
- yield* Effect.addFinalizer(() => Queue.shutdown(queue))
201
- return Queue.asDequeue(queue)
202
- }, Effect.orDie)
246
+ Stream.unwrap
247
+ )
203
248
  })
204
249
  })
205
250
 
@@ -212,13 +257,21 @@ const EncryptedRemoteEntrySql = Schema.Struct({
212
257
 
213
258
  type EncryptedRemoteEntrySql = Schema.Schema.Type<typeof EncryptedRemoteEntrySql>
214
259
 
260
+ const SessionAuthBindingSql = Schema.Struct({
261
+ public_key: Schema.String,
262
+ signing_public_key: Schema.Uint8Array
263
+ })
264
+
265
+ type SessionAuthBindingSql = Schema.Schema.Type<typeof SessionAuthBindingSql>
266
+
215
267
  const decodeEntryRows = Schema.decodeUnknownEffect(Schema.Array(EncryptedRemoteEntrySql))
268
+ const decodeSessionAuthBindingRows = Schema.decodeUnknownEffect(Schema.Array(SessionAuthBindingSql))
216
269
 
217
270
  const toEncryptedRemoteEntry = (row: EncryptedRemoteEntrySql): EventLogEncryption.EncryptedRemoteEntry => ({
218
271
  sequence: row.sequence,
219
- iv: row.iv,
272
+ iv: row.iv as Uint8Array<ArrayBuffer>,
220
273
  entryId: row.entry_id,
221
- encryptedEntry: row.encrypted_entry
274
+ encryptedEntry: row.encrypted_entry as Uint8Array<ArrayBuffer>
222
275
  })
223
276
 
224
277
  const decodeEntries = (
@@ -226,6 +279,10 @@ const decodeEntries = (
226
279
  ): Effect.Effect<ReadonlyArray<EventLogEncryption.EncryptedRemoteEntry>, Schema.SchemaError> =>
227
280
  decodeEntryRows(rows).pipe(Effect.map((entries) => entries.map(toEncryptedRemoteEntry)))
228
281
 
282
+ const decodeSessionAuthBindings = (
283
+ rows: unknown
284
+ ): Effect.Effect<ReadonlyArray<SessionAuthBindingSql>, Schema.SchemaError> => decodeSessionAuthBindingRows(rows)
285
+
229
286
  /**
230
287
  * @since 4.0.0
231
288
  * @category layers
@@ -235,10 +292,10 @@ export const layerStorage = (options?: {
235
292
  readonly remoteIdTable?: string
236
293
  readonly insertBatchSize?: number
237
294
  }): Layer.Layer<
238
- EventLogServer.Storage,
295
+ EventLogServerEncrypted.Storage,
239
296
  SqlError.SqlError,
240
297
  SqlClient.SqlClient | EventLogEncryption.EventLogEncryption
241
- > => Layer.effect(EventLogServer.Storage)(makeStorage(options))
298
+ > => Layer.effect(EventLogServerEncrypted.Storage)(makeStorage(options))
242
299
 
243
300
  /**
244
301
  * @since 4.0.0
@@ -248,7 +305,12 @@ export const layerStorageSubtle = (options?: {
248
305
  readonly entryTablePrefix?: string
249
306
  readonly remoteIdTable?: string
250
307
  readonly insertBatchSize?: number
251
- }): Layer.Layer<EventLogServer.Storage, SqlError.SqlError, SqlClient.SqlClient> =>
308
+ }): Layer.Layer<EventLogServerEncrypted.Storage, SqlError.SqlError, SqlClient.SqlClient> =>
252
309
  layerStorage(options).pipe(
253
310
  Layer.provide(EventLogEncryption.layerSubtle)
254
311
  )
312
+
313
+ const makeEncryptedScopeKey = (
314
+ publicKey: string,
315
+ storeId: string
316
+ ): string => `${publicKey}/${storeId}`