feat/orm-diffs
Laurin Weger 4 days ago
parent cce7dbfbe9
commit 2bdaa84438
No known key found for this signature in database
GPG Key ID: 9B372BB0B792770F
  1. 10
      engine/verifier/src/orm/handle_frontend_update.rs
  2. 1
      engine/verifier/src/orm/initialize.rs
  3. 20
      sdk/js/alien-deepsignals/test-map-type.js
  4. 28
      sdk/js/alien-deepsignals/test-set-map.js
  5. 25
      sdk/js/alien-deepsignals/test-set-paths.js
  6. 9
      sdk/js/examples/multi-framework-signals/src/app/pages/index.astro
  7. 136
      sdk/js/examples/multi-framework-signals/src/frontends/react/HelloWorld.tsx
  8. 7
      sdk/js/examples/multi-framework-signals/src/frontends/svelte/HelloWorld.svelte
  9. 21
      sdk/js/examples/multi-framework-signals/src/frontends/vue/HelloWorld.vue

@ -302,10 +302,7 @@ fn create_sparql_update_query_for_diff(
remove_statement, remove_statement,
wheres.join(" .\n ") wheres.join(" .\n ")
)); ));
log_info!( log_info!("[create_sparql_update_query_for_diff] Added delete query.");
"[create_sparql_update_query_for_diff] Added delete query #{}",
sparql_sub_queries.len()
);
// var_counter += 1; // Not necessary because not used afterwards. // var_counter += 1; // Not necessary because not used afterwards.
} }
// The actual INSERT. // The actual INSERT.
@ -315,10 +312,7 @@ fn create_sparql_update_query_for_diff(
add_statement, add_statement,
where_statements.join(". \n ") where_statements.join(". \n ")
)); ));
log_info!( log_info!("[create_sparql_update_query_for_diff] Added insert query.");
"[create_sparql_update_query_for_diff] Added insert query #{}",
sparql_sub_queries.len()
);
} }
} }

@ -62,7 +62,6 @@ impl Verifier {
.push(orm_subscription); .push(orm_subscription);
let orm_objects = self.create_orm_object_for_shape(nuri, session_id, &shape_type)?; let orm_objects = self.create_orm_object_for_shape(nuri, session_id, &shape_type)?;
// log_debug!("create_orm_object_for_shape return {:?}", orm_objects);
let _ = tx let _ = tx
.send(AppResponse::V0(AppResponseV0::OrmInitial(orm_objects))) .send(AppResponse::V0(AppResponseV0::OrmInitial(orm_objects)))

@ -0,0 +1,20 @@
import { deepSignal } from './dist/index.js';
const root = deepSignal({
mySet: new Set([
{ "@id": "obj1", value: 10 },
{ "@id": "obj2", value: 20 }
])
});
const result = root.mySet.values().map(entry => entry);
console.log('Type:', typeof result);
console.log('Constructor:', result.constructor.name);
console.log('Result:', result);
console.log('Has next?:', typeof result.next);
console.log('Is iterable?:', Symbol.iterator in result);
// Convert to array
const arr = Array.from(result);
console.log('Array:', arr);
console.log('First entry:', arr[0]);

@ -0,0 +1,28 @@
import { deepSignal, subscribeDeepMutations } from './dist/index.js';
const root = deepSignal({
mySet: new Set([
{ "@id": "obj1", value: 10 },
{ "@id": "obj2", value: 20 }
])
});
subscribeDeepMutations(root, (patches) => {
console.log('Patches:', JSON.stringify(patches, null, 2));
});
// Use .map() to get entries
const entries = root.mySet.values().map(entry => {
console.log('Entry:', entry);
return entry;
});
console.log('Got entries:', entries.length);
console.log('Modifying first entry...');
// Modify the first one
entries[0].value = 100;
setTimeout(() => {
console.log('Done');
}, 100);

@ -0,0 +1,25 @@
import { deepSignal, subscribeDeepMutations } from './dist/index.js';
const root = deepSignal({
mySet: new Set([
{ "@id": "obj1", value: 10 }
])
});
subscribeDeepMutations(root, (patches) => {
console.log('Patches:', JSON.stringify(patches, null, 2));
});
// Get the first entry from the set
const entries = root.mySet.values();
const firstEntry = entries.next().value;
console.log('First entry:', firstEntry);
console.log('Modifying value...');
// Modify it
firstEntry.value = 20;
setTimeout(() => {
console.log('Done');
}, 100);

@ -30,6 +30,7 @@ const title = "Multi-framework app";
initNg(ng, event.session); initNg(ng, event.session);
window.ng = ng; window.ng = ng;
window.session = event.session;
}, },
true, true,
[] []
@ -37,15 +38,15 @@ const title = "Multi-framework app";
</script> </script>
<Layout title={title}> <Layout title={title}>
<!-- <Highlight vue> <Highlight vue>
<VueRoot client:only /> <VueRoot client:only />
</Highlight> --> </Highlight>
<Highlight react> <Highlight react>
<ReactRoot client:only="react" /> <ReactRoot client:only="react" />
</Highlight> </Highlight>
<!--
<Highlight svelte> <Highlight svelte>
<SvelteRoot client:only /> <SvelteRoot client:only />
</Highlight> --> </Highlight>
</Layout> </Layout>

@ -3,31 +3,102 @@ import { useShape } from "@ng-org/signals/react";
import flattenObject from "../utils/flattenObject"; import flattenObject from "../utils/flattenObject";
import { TestObjectShapeType } from "../../shapes/orm/testShape.shapeTypes"; import { TestObjectShapeType } from "../../shapes/orm/testShape.shapeTypes";
import { BasicShapeType } from "../../shapes/orm/basic.shapeTypes"; import { BasicShapeType } from "../../shapes/orm/basic.shapeTypes";
import type { ShapeType } from "@ng-org/shex-orm";
import type { Basic } from "../../shapes/orm/basic.typings";
const sparqlExampleData = `
PREFIX ex: <http://example.org/>
INSERT DATA {
<urn:test:obj1> a ex:TestObject ;
ex:stringValue "hello world" ;
ex:numValue 42 ;
ex:boolValue true ;
ex:arrayValue 1,2,3 ;
ex:objectValue <urn:test:id3> ;
ex:anotherObject <urn:test:id1>, <urn:test:id2> ;
ex:numOrStr "either" ;
ex:lit1Or2 "lit1" ;
ex:unrelated "some value" ;
ex:anotherUnrelated 4242 .
<urn:test:id3>
ex:nestedString "nested" ;
ex:nestedNum 7 ;
ex:nestedArray 5,6 .
<urn:test:id1>
ex:prop1 "one" ;
ex:prop2 1 .
<urn:test:id2>
ex:prop1 "two" ;
ex:prop2 2 .
<urn:test:obj2> a ex:TestObject ;
ex:stringValue "hello world #2" ;
ex:numValue 422 ;
ex:boolValue false ;
ex:arrayValue 4,5,6 ;
ex:objectValue <urn:test:id6> ;
ex:anotherObject <urn:test:id4>, <urn:test:id5> ;
ex:numOrStr 4 ;
ex:lit1Or2 "lit2" ;
ex:unrelated "some value2" ;
ex:anotherUnrelated 42422 .
<urn:test:id6>
ex:nestedString "nested2" ;
ex:nestedNum 72 ;
ex:nestedArray 7,8,9 .
<urn:test:id4>
ex:prop1 "one2" ;
ex:prop2 12 .
<urn:test:id5>
ex:prop1 "two2" ;
ex:prop2 22 .
<urn:basicObject4>
a <http://example.org/Basic> ;
ex:basicString "string of object 1" .
<urn:basicObject5>
a <http://example.org/Basic> ;
ex:basicString "string of object 2" .
}
`;
export function HelloWorldReact() { export function HelloWorldReact() {
const state = [...(useShape(BasicShapeType)?.entries() || [])][0]; const state = useShape(BasicShapeType);
// @ts-expect-error // @ts-expect-error
window.reactState = state; window.reactState = state;
console.log("react state", state);
if (!state) return <>Loading state</>; if (!state) return <div>Loading...</div>;
// Create a table from the state object: One column for keys, one for values, one with an input to change the value. // Create a table from the state object: One column for keys, one for values, one with an input to change the value.
return ( return (
<div> <div>
<p>Rendered in React</p> <p>Rendered in React</p>
{/* <button <button
onClick={() => { onClick={() => {
state.boolValue = !state.boolValue; window.ng.sparql_update(
state.numValue += 2; window.session.session_id,
sparqlExampleData,
"did:ng:" + window.session.private_store_id
);
}} }}
> >
click me to change multiple props Add example data
</button> */} </button>
<table border={1} cellPadding={5}> <div>
{state.values()?.map((ormObj) => (
<table border={1} cellPadding={5} key={ormObj["@id"]}>
<thead> <thead>
<tr> <tr>
<th>Key</th> <th>Key</th>
@ -52,18 +123,27 @@ export function HelloWorldReact() {
current[keys[keys.length - 1]] = value; current[keys[keys.length - 1]] = value;
}; };
const getNestedValue = (obj: any, path: string) => { const getNestedValue = (
obj: any,
path: string
) => {
return path return path
.split(".") .split(".")
.reduce((current, key) => current[key], obj); .reduce(
(current, key) => current[key],
obj
);
}; };
return flattenObject(state).map(([key, value]) => ( return flattenObject(ormObj).map(
([key, value]) => (
<tr key={key}> <tr key={key}>
<td>{key}</td> <td>{key}</td>
<td> <td>
{value instanceof Set {value instanceof Set
? Array.from(value).join(", ") ? Array.from(value).join(
", "
)
: Array.isArray(value) : Array.isArray(value)
? `[${value.join(", ")}]` ? `[${value.join(", ")}]`
: JSON.stringify(value)} : JSON.stringify(value)}
@ -81,7 +161,8 @@ export function HelloWorldReact() {
); );
}} }}
/> />
) : typeof value === "number" ? ( ) : typeof value ===
"number" ? (
<input <input
type="number" type="number"
value={value} value={value}
@ -89,11 +170,15 @@ export function HelloWorldReact() {
setNestedValue( setNestedValue(
state, state,
key, key,
Number(e.target.value) Number(
e.target
.value
)
); );
}} }}
/> />
) : typeof value === "boolean" ? ( ) : typeof value ===
"boolean" ? (
<input <input
type="checkbox" type="checkbox"
checked={value} checked={value}
@ -114,10 +199,15 @@ export function HelloWorldReact() {
state, state,
key key
); );
setNestedValue(state, key, [ setNestedValue(
state,
key,
[
...currentArray, ...currentArray,
currentArray.length + 1, currentArray.length +
]); 1,
]
);
}} }}
> >
Add Add
@ -130,7 +220,8 @@ export function HelloWorldReact() {
key key
); );
if ( if (
currentArray.length > 0 currentArray.length >
0
) { ) {
setNestedValue( setNestedValue(
state, state,
@ -188,10 +279,13 @@ export function HelloWorldReact() {
)} )}
</td> </td>
</tr> </tr>
)); )
);
})()} })()}
</tbody> </tbody>
</table> </table>
))}
</div>
</div> </div>
); );
} }

@ -19,9 +19,9 @@
} }
cur[keys[keys.length - 1]] = value; cur[keys[keys.length - 1]] = value;
} }
const flatEntries = $derived( const flattenedObjects = $derived(
$shapeObject $shapeObject
? $shapeObject.entries().map((o) => flattenObject(o)[0] || ({} as any)) ? $shapeObject.values().map((o) => flattenObject(o)[0] || ({} as any))
: [] : []
); );
$effect(() => { $effect(() => {
@ -32,6 +32,8 @@
{#if $shapeObject} {#if $shapeObject}
<div> <div>
<p>Rendered in Svelte</p> <p>Rendered in Svelte</p>
{#each flattenedObjects as flatEntries}
<table border="1" cellpadding="5"> <table border="1" cellpadding="5">
<thead> <thead>
<tr> <tr>
@ -118,6 +120,7 @@
{/each} {/each}
</tbody> </tbody>
</table> </table>
{/each}
</div> </div>
{:else} {:else}
<p>Loading state</p> <p>Loading state</p>

@ -5,24 +5,21 @@ import flattenObject from "../utils/flattenObject";
import { TestObjectShapeType } from "../../shapes/orm/testShape.shapeTypes"; import { TestObjectShapeType } from "../../shapes/orm/testShape.shapeTypes";
// Acquire deep signal object (proxy) for a shape; scope second arg left empty string for parity // Acquire deep signal object (proxy) for a shape; scope second arg left empty string for parity
const shapeObj = useShape(TestObjectShapeType); const shapeObjects = useShape(TestObjectShapeType);
// Expose for devtools exploration // Expose for devtools exploration
// @ts-ignore // @ts-ignore
window.vueState = shapeObj; window.vueState = shapeObjects;
const flatEntries = computed(() => flattenObject(shapeObj));
</script> </script>
<template> <template>
<div class="vue"> <div class="vue">
<p>Rendered in Vue</p> <p>Rendered in Vue</p>
<template v-if="shapeObj && 'type' in shapeObj"> <template v-if="shapeObjects">
<!-- Direct property access --> <template v-for="obj in shapeObjects" :key="obj">
<input type="text" v-model="shapeObj.type" /> <template v-for="flatEntries in [flattenObject(obj)]">
<input type="text" v-model="shapeObj.objectValue.nestedString" />
<!-- Property access through object recursion --> <!-- Property access through object recursion -->
<table <table
border="1" border="1"
@ -65,7 +62,7 @@ const flatEntries = computed(() => flattenObject(shapeObj));
<template v-if="path.indexOf('.') === -1"> <template v-if="path.indexOf('.') === -1">
<input <input
type="text" type="text"
v-model="shapeObj[key]" v-model="obj[key]"
/> />
</template> </template>
<template v-else> <template v-else>
@ -87,7 +84,7 @@ const flatEntries = computed(() => flattenObject(shapeObj));
<template v-if="path.indexOf('.') === -1"> <template v-if="path.indexOf('.') === -1">
<input <input
type="number" type="number"
v-model="shapeObj[key]" v-model="obj[key]"
/> />
</template> </template>
<template v-else> <template v-else>
@ -108,7 +105,7 @@ const flatEntries = computed(() => flattenObject(shapeObj));
<template v-if="path.indexOf('.') === -1"> <template v-if="path.indexOf('.') === -1">
<input <input
type="checkbox" type="checkbox"
v-model="shapeObj[key]" v-model="obj[key]"
/> />
</template> </template>
<template v-else> <template v-else>
@ -215,6 +212,8 @@ const flatEntries = computed(() => flattenObject(shapeObj));
</tbody> </tbody>
</table> </table>
</template> </template>
</template>
</template>
<template v-else> <template v-else>
<p>Loading state</p> <p>Loading state</p>
</template> </template>

Loading…
Cancel
Save