@@ -80,11 +80,13 @@ function validateParentPath(
8080
8181 // Set pathError.path to last valid parent path component
8282 pathError . path = [ ] ;
83- while ( parentPath . length > 0 ) {
84- const segment = parentPath . shift ( ) ! ;
85- value = value [ segment ] ;
86- if ( ! isRecord ( value ) ) break ;
87- pathError . path . push ( segment ) ;
83+ if ( isRecord ( value ) ) {
84+ while ( parentPath . length > 0 ) {
85+ const segment = parentPath . shift ( ) ! ;
86+ value = value [ segment ] ;
87+ if ( ! isRecord ( value ) ) break ;
88+ pathError . path . push ( segment ) ;
89+ }
8890 }
8991
9092 return pathError ;
@@ -191,23 +193,60 @@ class TransactionReader implements ITransactionReader {
191193 return { ok : undefined , error : notFoundError } ;
192194 }
193195
194- const validationError = validateParentPath ( doc . get ( ) , address . path ) ;
195- if ( validationError ) {
196- return { ok : undefined , error : validationError } ;
196+ // Path-based logic
197+ if ( ! address . path . length ) {
198+ const notFoundError : INotFoundError = new Error (
199+ `Path must not be empty` ,
200+ ) as INotFoundError ;
201+ notFoundError . name = "NotFoundError" ;
202+ return { ok : undefined , error : notFoundError } ;
203+ }
204+ const [ first , ...rest ] = address . path ;
205+ if ( first === "value" ) {
206+ // Validate parent path exists and is a record for nested writes/reads
207+ const validationError = validateParentPath ( doc . get ( ) , rest ) ;
208+ if ( validationError ) {
209+ return { ok : undefined , error : validationError } ;
210+ }
211+ // Read from doc itself
212+ const value = doc . getAtPath ( rest ) ;
213+ const read : Read = {
214+ address,
215+ value,
216+ cause : refer ( "shim does not care" ) ,
217+ } ;
218+ this . log . addRead ( read ) ;
219+ return { ok : read } ;
220+ } else if ( first === "source" ) {
221+ // Only allow path length 1
222+ if ( rest . length > 0 ) {
223+ const notFoundError : INotFoundError = new Error (
224+ `Path beyond 'source' is not allowed` ,
225+ ) as INotFoundError ;
226+ notFoundError . name = "NotFoundError" ;
227+ return { ok : undefined , error : notFoundError } ;
228+ }
229+ // Return the URI of the sourceCell if it exists
230+ const sourceCell = doc . sourceCell ;
231+ let value : string | undefined = undefined ;
232+ if ( sourceCell ) {
233+ // Convert EntityId to URI string
234+ value = `of:${ JSON . parse ( JSON . stringify ( sourceCell . entityId ) ) [ "/" ] } ` ;
235+ }
236+ const read : Read = {
237+ address,
238+ value,
239+ cause : refer ( "shim does not care" ) ,
240+ } ;
241+ this . log . addRead ( read ) ;
242+ return { ok : read } ;
243+ } else {
244+ const notFoundError : INotFoundError = new Error (
245+ `Invalid first path element: ${ String ( first ) } ` ,
246+ ) as INotFoundError ;
247+ notFoundError . name = "NotFoundError" ;
248+ return { ok : undefined , error : notFoundError } ;
197249 }
198-
199- // Read the value at the specified path
200- const value = getValueAtPath ( doc . get ( ) , address . path ) ;
201-
202- // Create the read invariant
203- const read : Read = {
204- address,
205- value,
206- cause : refer ( "shim does not care" ) ,
207- } ;
208- this . log . addRead ( read ) ;
209-
210- return { ok : read } ;
211250 }
212251}
213252
@@ -264,35 +303,76 @@ class TransactionWriter extends TransactionReader
264303 throw new Error ( `Failed to get or create document: ${ address . id } ` ) ;
265304 }
266305
267- // For non-empty paths, check if document exists and is a record
268- if ( address . path . length > 0 ) {
269- if ( doc . get ( ) === undefined || ! isRecord ( doc . get ( ) ) ) {
306+ // Path-based logic
307+ if ( ! address . path . length ) {
308+ const notFoundError : INotFoundError = new Error (
309+ `Path must not be empty` ,
310+ ) as INotFoundError ;
311+ notFoundError . name = "NotFoundError" ;
312+ return { ok : undefined , error : notFoundError } ;
313+ }
314+ const [ first , ...rest ] = address . path ;
315+ if ( first === "value" ) {
316+ // Validate parent path exists and is a record for nested writes
317+ const validationError = validateParentPath ( doc . get ( ) , rest ) ;
318+ if ( validationError ) {
319+ return { ok : undefined , error : validationError } ;
320+ }
321+ // Write to doc itself
322+ doc . setAtPath ( rest , value ) ;
323+ const write : Write = {
324+ address,
325+ value,
326+ cause : refer ( address . id ) ,
327+ } ;
328+ this . log . addWrite ( write ) ;
329+ return { ok : write } ;
330+ } else if ( first === "source" ) {
331+ // Only allow path length 1
332+ if ( rest . length > 0 ) {
270333 const notFoundError : INotFoundError = new Error (
271- `Document not found or not a record: ${ address . id } ` ,
334+ `Path beyond 'source' is not allowed ` ,
272335 ) as INotFoundError ;
273336 notFoundError . name = "NotFoundError" ;
274337 return { ok : undefined , error : notFoundError } ;
275338 }
339+ // Value must be a URI string (of:...)
340+ if ( typeof value !== "string" || ! value . startsWith ( "of:" ) ) {
341+ const notFoundError : INotFoundError = new Error (
342+ `Value for 'source' must be a URI string (of:...)` ,
343+ ) as INotFoundError ;
344+ notFoundError . name = "NotFoundError" ;
345+ return { ok : undefined , error : notFoundError } ;
346+ }
347+ // Get the source doc in the same space
348+ const sourceEntityId = uriToEntityId ( value ) ;
349+ const sourceDoc = this . runtime . documentMap . getDocByEntityId (
350+ address . space ,
351+ sourceEntityId ,
352+ false ,
353+ ) ;
354+ if ( ! sourceDoc ) {
355+ const notFoundError : INotFoundError = new Error (
356+ `Source document not found: ${ value } ` ,
357+ ) as INotFoundError ;
358+ notFoundError . name = "NotFoundError" ;
359+ return { ok : undefined , error : notFoundError } ;
360+ }
361+ doc . sourceCell = sourceDoc ;
362+ const write : Write = {
363+ address,
364+ value,
365+ cause : refer ( address . id ) ,
366+ } ;
367+ this . log . addWrite ( write ) ;
368+ return { ok : write } ;
369+ } else {
370+ const notFoundError : INotFoundError = new Error (
371+ `Invalid first path element: ${ String ( first ) } ` ,
372+ ) as INotFoundError ;
373+ notFoundError . name = "NotFoundError" ;
374+ return { ok : undefined , error : notFoundError } ;
276375 }
277-
278- // Validate parent path exists and is a record for nested writes
279- const validationError = validateParentPath ( doc . get ( ) , address . path ) ;
280- if ( validationError ) {
281- return { ok : undefined , error : validationError } ;
282- }
283-
284- // Write the value at the specified path
285- doc . setAtPath ( address . path , value ) ;
286-
287- // Create the write invariant
288- const write : Write = {
289- address,
290- value,
291- cause : refer ( address . id ) ,
292- } ;
293- this . log . addWrite ( write ) ;
294-
295- return { ok : write } ;
296376 }
297377}
298378
0 commit comments