fix doc_subscribe stream

master
Niko PLP 1 day ago
parent a81692cd80
commit b1e76c20af
  1. 18
      helpers/app-auth/src/main.ts
  2. 25
      helpers/net-auth/src/main.ts
  3. 27
      helpers/nextgraphweb/src/index.ts
  4. 19
      ng-sdk-js/example-webapp-react/README.md
  5. 1173
      ng-sdk-js/example-webapp-react/package-lock.json
  6. 7
      ng-sdk-js/example-webapp-react/package.json
  7. 82
      ng-sdk-js/example-webapp-react/src/.ldo/container.context.ts
  8. 124
      ng-sdk-js/example-webapp-react/src/.ldo/container.schema.ts
  9. 19
      ng-sdk-js/example-webapp-react/src/.ldo/container.shapeTypes.ts
  10. 44
      ng-sdk-js/example-webapp-react/src/.ldo/container.typings.ts
  11. 24
      ng-sdk-js/example-webapp-react/src/.shapes/container.shex
  12. 6
      ng-sdk-js/example-webapp-react/src/App.tsx
  13. 20
      ng-sdk-js/example-webapp-react/src/Contact.tsx
  14. 2
      ng-sdk-js/example-webapp-react/src/Header.tsx
  15. 4
      ng-sdk-js/example-webapp-react/src/createBrowserNGReactMethods.tsx

@ -67,6 +67,23 @@ window.addEventListener("message", async (event)=>{
writer.close(); writer.close();
} }
} else if ( method === "doc_subscribe" ) {
let args = event.data.args;
console.log("processing doc_subscribe...",method, args);
args.push((callbacked)=> {
writer.write({stream:true, ret:callbacked});
});
// TODO: deal with cancel and end of stream (call writer.close())
try {
let cancel_function = await Reflect.apply(web_api[method], null, args);
} catch (e) {
writer.write({ok:false, ret:e});
writer.close();
}
} else { } else {
// forwarding to ng // forwarding to ng
@ -80,7 +97,6 @@ window.addEventListener("message", async (event)=>{
writer.close(); writer.close();
} }
} }
}, false); }, false);

@ -32,6 +32,26 @@ async function rpc( method:string, args?: any) : Promise<any> {
return ret.value; return ret.value;
} }
async function rpc_stream( method:string, args: any, writer:WritableStreamDefaultWriter<any>) {
const { readable, writablePort } = new RemoteReadableStream();
(<any>window).ng_broker_selected.postMessage({ method, args, port: writablePort }, (<any>window).ng_iframe_origin, [writablePort]);
const reader = readable.getReader();
for (var msg; msg = await reader.read(); ) {
if (msg.done) {
writer.close();
break;
}
if (msg.value.error) {
writer.write(msg.value);
writer.close();
break;
} else if (msg.value.stream) {
writer.write(msg.value);
}
// TODO: deal with end of stream
}
}
const AUTH_HOME = "#/"; const AUTH_HOME = "#/";
// const AUTH_USER_PANEL = "#/user"; // const AUTH_USER_PANEL = "#/user";
// const AUTH_USER_ACCOUNTS = "#/user/accounts"; // const AUTH_USER_ACCOUNTS = "#/user/accounts";
@ -74,6 +94,11 @@ window.addEventListener("message", async (event)=>{
writer.write(await rpc("login")); writer.write(await rpc("login"));
writer.close(); writer.close();
} }
} else if ( method === "doc_subscribe" ) {
console.log("net forward doc_subscribe to app", method, event.data.args)
await rpc_stream(method, event.data.args, writer);
} else { } else {
console.log("net forward to app", method, event.data.args) console.log("net forward to app", method, event.data.args)
// forward to app auth iframe // forward to app auth iframe

@ -136,6 +136,31 @@ function hide_nextgraph_auth() {
async function rpc( method:string, args?: any) : Promise<any> { async function rpc( method:string, args?: any) : Promise<any> {
const { readable, writablePort } = new RemoteReadableStream(); const { readable, writablePort } = new RemoteReadableStream();
console.log("POSTING",method, args);
if (method==="doc_subscribe") {
let callback = args[2];
let new_args = [args[0],args[1]];
initialized?.postMessage({ method, args:new_args, port: writablePort }, iframe_config.origin, [writablePort]);
const reader = readable.getReader();
let unsub = new Promise(async (resolve)=> {
resolve(()=>{
// unsub function that does nothing.
//TODO: implement it
});
for (var msg; msg = await reader.read(); ) {
if (msg.done) break;
if (msg.value.error) {
throw new Error(msg.value.ret);
} else if (msg.value.stream) {
(callback)(msg.value.ret);
}
// TODO: deal with end of stream
}
});
return unsub;
} else {
initialized?.postMessage({ method, args, port: writablePort }, iframe_config.origin, [writablePort]); initialized?.postMessage({ method, args, port: writablePort }, iframe_config.origin, [writablePort]);
const reader = readable.getReader(); const reader = readable.getReader();
let ret = await reader.read(); let ret = await reader.read();
@ -147,6 +172,8 @@ async function rpc( method:string, args?: any) : Promise<any> {
throw new Error(ret.value.ret); throw new Error(ret.value.ret);
} }
}
const handler = { const handler = {
async apply(_target: object, path: PropertyKey[], _caller: any, args?: any) :Promise<any> { async apply(_target: object, path: PropertyKey[], _caller: any, args?: any) :Promise<any> {
if (initialized === null) { if (initialized === null) {

@ -39,26 +39,9 @@ Due to the way `npm link` works, you will have to run this command again, after
Open this URL in browser : [http://localhost:5173](http://localhost:5173) Open this URL in browser : [http://localhost:5173](http://localhost:5173)
See the example code in [src/main.tsx](./src/main.tsx) See the example code in [src/main.tsx](./src/App.tsx)
## For usage in your project
call :
```javascript
import {default as ng, init} from "nextgraphweb";
await init( location.origin, (event) => {
// callback
// once you receive event.status == "loggedin"
// you can use the full API
}
, true // singleton: boolean (will your app create many docs in the system, or should it be launched as a unique instance)
, []); //list of AccessRequests (for now, leave this empty)
await ng.login(); // this will return false at the first attempt. but it will open the wallet login page so the user can login.
// if you call it again later once the user has logged in already, it will return true, and nothing more will happen
```
## License ## License

File diff suppressed because it is too large Load Diff

@ -7,10 +7,12 @@
"dev": "vite", "dev": "vite",
"build": "tsc -b && vite build", "build": "tsc -b && vite build",
"lint": "eslint .", "lint": "eslint .",
"preview": "vite preview" "preview": "vite preview",
"build:ldo": "ldo build --input src/.shapes --output src/.ldo"
}, },
"dependencies": { "dependencies": {
"@ldo/connected-nextgraph": "^1.0.0-alpha.7", "@ldo/connected-nextgraph": "^1.0.0-alpha.7",
"@ldo/ldo": "^1.0.0-alpha.3",
"@ldo/react": "^1.0.0-alpha.3", "@ldo/react": "^1.0.0-alpha.3",
"nextgraphweb": "^0.1.1-alpha.1", "nextgraphweb": "^0.1.1-alpha.1",
"react": "^19.0.0", "react": "^19.0.0",
@ -18,8 +20,11 @@
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.22.0", "@eslint/js": "^9.22.0",
"@ldo/cli": "^1.0.0-alpha.3",
"@types/jsonld": "^1.5.15",
"@types/react": "^19.0.10", "@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4", "@types/react-dom": "^19.0.4",
"@types/shexj": "^2.1.7",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"eslint": "^9.22.0", "eslint": "^9.22.0",

@ -0,0 +1,82 @@
import { LdoJsonldContext } from "@ldo/ldo";
/**
* =============================================================================
* containerContext: JSONLD Context for container
* =============================================================================
*/
export const containerContext: LdoJsonldContext = {
type: {
"@id": "@type",
"@isCollection": true,
},
Container: {
"@id": "http://www.w3.org/ns/ldp#Container",
"@context": {
type: {
"@id": "@type",
"@isCollection": true,
},
modified: {
"@id": "http://purl.org/dc/terms/modified",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
contains: {
"@id": "http://www.w3.org/ns/ldp#contains",
"@type": "@id",
"@isCollection": true,
},
mtime: {
"@id": "http://www.w3.org/ns/posix/stat#mtime",
"@type": "http://www.w3.org/2001/XMLSchema#decimal",
},
size: {
"@id": "http://www.w3.org/ns/posix/stat#size",
"@type": "http://www.w3.org/2001/XMLSchema#integer",
},
},
},
Resource: {
"@id": "http://www.w3.org/ns/ldp#Resource",
"@context": {
type: {
"@id": "@type",
"@isCollection": true,
},
modified: {
"@id": "http://purl.org/dc/terms/modified",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
contains: {
"@id": "http://www.w3.org/ns/ldp#contains",
"@type": "@id",
"@isCollection": true,
},
mtime: {
"@id": "http://www.w3.org/ns/posix/stat#mtime",
"@type": "http://www.w3.org/2001/XMLSchema#decimal",
},
size: {
"@id": "http://www.w3.org/ns/posix/stat#size",
"@type": "http://www.w3.org/2001/XMLSchema#integer",
},
},
},
modified: {
"@id": "http://purl.org/dc/terms/modified",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
contains: {
"@id": "http://www.w3.org/ns/ldp#contains",
"@type": "@id",
"@isCollection": true,
},
mtime: {
"@id": "http://www.w3.org/ns/posix/stat#mtime",
"@type": "http://www.w3.org/2001/XMLSchema#decimal",
},
size: {
"@id": "http://www.w3.org/ns/posix/stat#size",
"@type": "http://www.w3.org/2001/XMLSchema#integer",
},
};

@ -0,0 +1,124 @@
import { Schema } from "shexj";
/**
* =============================================================================
* containerSchema: ShexJ Schema for container
* =============================================================================
*/
export const containerSchema: Schema = {
type: "Schema",
shapes: [
{
id: "http://www.w3.org/ns/lddps#Container",
type: "ShapeDecl",
shapeExpr: {
type: "Shape",
expression: {
id: "http://www.w3.org/ns/lddps#ContainerShape",
type: "EachOf",
expressions: [
{
type: "TripleConstraint",
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
valueExpr: {
type: "NodeConstraint",
values: [
"http://www.w3.org/ns/ldp#Container",
"http://www.w3.org/ns/ldp#Resource",
],
},
min: 0,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "A container",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://purl.org/dc/terms/modified",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "Date modified",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/ldp#contains",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
min: 0,
max: -1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "Defines a Resource",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/posix/stat#mtime",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#decimal",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "?",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://www.w3.org/ns/posix/stat#size",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#integer",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "size of this container",
},
},
],
},
],
},
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"],
},
},
],
};

@ -0,0 +1,19 @@
import { ShapeType } from "@ldo/ldo";
import { containerSchema } from "./container.schema";
import { containerContext } from "./container.context";
import { Container } from "./container.typings";
/**
* =============================================================================
* LDO ShapeTypes container
* =============================================================================
*/
/**
* Container ShapeType
*/
export const ContainerShapeType: ShapeType<Container> = {
schema: containerSchema,
shape: "http://www.w3.org/ns/lddps#Container",
context: containerContext,
};

@ -0,0 +1,44 @@
import { LdoJsonldContext, LdSet } from "@ldo/ldo";
/**
* =============================================================================
* Typescript Typings for container
* =============================================================================
*/
/**
* Container Type
*/
export interface Container {
"@id"?: string;
"@context"?: LdoJsonldContext;
/**
* A container
*/
type?: LdSet<
| {
"@id": "Container";
}
| {
"@id": "Resource";
}
>;
/**
* Date modified
*/
modified?: string;
/**
* Defines a Resource
*/
contains?: LdSet<{
"@id": string;
}>;
/**
* ?
*/
mtime?: number;
/**
* size of this container
*/
size?: number;
}

@ -0,0 +1,24 @@
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX ldp: <http://www.w3.org/ns/ldp#>
PREFIX ldps: <http://www.w3.org/ns/lddps#>
PREFIX dct: <http://purl.org/dc/terms/>
PREFIX stat: <http://www.w3.org/ns/posix/stat#>
PREFIX tur: <http://www.w3.org/ns/iana/media-types/text/turtle#>
PREFIX pim: <http://www.w3.org/ns/pim/space#>
ldps:Container EXTRA a {
$ldps:ContainerShape (
a [ ldp:Container ldp:Resource ]*
// rdfs:comment "A container";
dct:modified xsd:string?
// rdfs:comment "Date modified";
ldp:contains IRI *
// rdfs:comment "Defines a Resource";
stat:mtime xsd:decimal?
// rdfs:comment "?";
stat:size xsd:integer?
// rdfs:comment "size of this container";
)
}

@ -2,7 +2,7 @@ import { useState } from 'react'
import React, { FunctionComponent } from 'react'; import React, { FunctionComponent } from 'react';
import { Header } from './Header'; import { Header } from './Header';
import { Contact } from './Contact'; import { Contact } from './Contact';
import { BrowserNGLdoProvider } from './reactMethods'; import { BrowserNGLdoProvider, useNextGraphAuth } from './reactMethods';
import './App.css' import './App.css'
@ -20,11 +20,15 @@ import "../../../common/src/styles.css";
// } // }
const App: FunctionComponent = () => { const App: FunctionComponent = () => {
//const { session } = useNextGraphAuth();
return ( return (
<div className="App"> <div className="App">
<BrowserNGLdoProvider> <BrowserNGLdoProvider>
<Header /> <Header />
<Contact /> <Contact />
</BrowserNGLdoProvider> </BrowserNGLdoProvider>
</div> </div>
); );

@ -1,12 +1,22 @@
import { FunctionComponent } from "react"; import { FunctionComponent } from "react";
import { useNextGraphAuth } from "./reactMethods"; import { useNextGraphAuth } from "./reactMethods";
import { ContainerShapeType } from "./.ldo/container.shapeTypes.ts";
import { useSubscribeToResource, useResource, useSubject } from "./reactMethods.ts";
export const Contact: FunctionComponent = () => { export const Contact: FunctionComponent = () => {
const { session } = useNextGraphAuth(); const { session } = useNextGraphAuth();
let myContainer;
let container_overlay;
if (session.sessionId) {
useResource("did:ng:"+session.privateStoreId);
container_overlay = session.privateStoreId.substring(46);
console.log(container_overlay);
myContainer = useSubject(ContainerShapeType, "did:ng:"+(session.privateStoreId.substring(0,46)));
}
if (!session.sessionId) return <></>; if (!session.sessionId) return <></>;
return (
<div> return <>{myContainer.contains?.map((contained) => <p className="mb-5" style={{overflowWrap:"anywhere"}} key={contained["@id"]}>{contained["@id"]}</p>)}</>;
<p>Contact</p>
</div>
);
}; };

@ -11,7 +11,7 @@ export const Header: FunctionComponent = () => {
{session.sessionId ? ( {session.sessionId ? (
// If the session is logged in // If the session is logged in
<div className="p-1 text-center fixed top-0 left-0 right-0" style={{zIndex:1000, height:'36px', backgroundColor:'rgb(73, 114, 165)'}}> <div className="p-1 text-white text-center fixed top-0 left-0 right-0" style={{zIndex:1000, height:'36px', backgroundColor:'rgb(73, 114, 165)'}}>
You are logged in. <span className="font-bold clickable" onClick={logout}> Log out</span> You are logged in. <span className="font-bold clickable" onClick={logout}> Log out</span>
</div> </div>
) : ( ) : (

@ -83,13 +83,17 @@ export function createBrowserNGReactMethods(
const nextGraphAuthFunctions = useMemo( const nextGraphAuthFunctions = useMemo(
() => ({ () => ({
runInitialAuthCheck,
login, login,
logout, logout,
session, session,
ranInitialAuthCheck,
}), }),
[ [
login, login,
logout, logout,
ranInitialAuthCheck,
runInitialAuthCheck,
session, session,
], ],
); );

Loading…
Cancel
Save