First commit
This commit is contained in:
commit
7d13a3ea8b
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
dist/
|
||||||
|
node_modules
|
29
package.json
Normal file
29
package.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "solid-pb",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"default": "./dist/index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "tsc -w",
|
||||||
|
"build": "tsc -w",
|
||||||
|
"prepublish": "tsc"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nedpals/pbf": "^1.3.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"pocketbase": "^0.25.1",
|
||||||
|
"solid-js": "^1.9.4",
|
||||||
|
"zod": "^3.24.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.7.3"
|
||||||
|
}
|
||||||
|
}
|
15
src/context.ts
Normal file
15
src/context.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { createContext, useContext } from "solid-js";
|
||||||
|
import PocketBaseClient from "pocketbase";
|
||||||
|
|
||||||
|
|
||||||
|
const PBContext = createContext<PocketBaseClient>();
|
||||||
|
|
||||||
|
export const usePB = () => {
|
||||||
|
const ctx = useContext(PBContext)
|
||||||
|
if (!ctx) throw new Error("This hook must be used within a PBContext.Provider!");
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default PBContext;
|
159
src/hooks.ts
Normal file
159
src/hooks.ts
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
import { createEffect, onCleanup } from "solid-js";
|
||||||
|
import { createStore } from "solid-js/store";
|
||||||
|
import { RecordModel, RecordSubscription } from "pocketbase";
|
||||||
|
import * as pbf from "@nedpals/pbf";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { usePB } from "./context.js";
|
||||||
|
|
||||||
|
interface IUseRecordOptions {
|
||||||
|
realtime?: boolean;
|
||||||
|
validator?: z.AnyZodObject;
|
||||||
|
expand?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useRecord<T>(collection: string, id: () => string, options?: IUseRecordOptions) {
|
||||||
|
const pb = usePB();
|
||||||
|
const requestKey = crypto.randomUUID();
|
||||||
|
|
||||||
|
const [record, setRecord] = createStore<{
|
||||||
|
loading: boolean,
|
||||||
|
record: T & RecordModel | null,
|
||||||
|
error: Error | null,
|
||||||
|
}>({
|
||||||
|
loading: true,
|
||||||
|
error: null,
|
||||||
|
record: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const v = options?.validator ?? ({ parse: (i) => id });
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const cid = id();
|
||||||
|
|
||||||
|
const setRecordChecked: typeof setRecord = (...args) => {
|
||||||
|
if (cid !== id()) return;
|
||||||
|
(setRecord as any)(...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
setRecordChecked("loading", () => true)
|
||||||
|
setRecordChecked("error", () => null)
|
||||||
|
pb.collection(collection).getOne(cid, {
|
||||||
|
expand: options?.expand ? options.expand.join(",") : undefined,
|
||||||
|
requestKey,
|
||||||
|
}).then((value) => {
|
||||||
|
setRecordChecked("record", () => v.parse(value) as T & RecordModel)
|
||||||
|
}).catch((error) => {
|
||||||
|
setRecordChecked("error", () => error as Error)
|
||||||
|
}).finally(() => {
|
||||||
|
setRecordChecked("loading", () => false)
|
||||||
|
});
|
||||||
|
|
||||||
|
const sub = pb.realtime.subscribe(collection + "/" + cid, (value) => {
|
||||||
|
//TODO: Check what actions exist and if some needs special handling
|
||||||
|
setRecordChecked("record", () => v.parse(value.record) as T & RecordModel)
|
||||||
|
}, {
|
||||||
|
query: {
|
||||||
|
expand: options?.expand ? options.expand.join(",") : undefined,
|
||||||
|
requestKey: requestKey + "-rt",
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sub.catch(err => { console.error("Error in realtime subscription", err) });
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
pb.cancelRequest(requestKey);
|
||||||
|
pb.cancelRequest(requestKey + "-rt");
|
||||||
|
sub.then(s => s());
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IUseRecordsOptions {
|
||||||
|
realtime?: boolean;
|
||||||
|
validator?: z.AnyZodObject;
|
||||||
|
filter?: () => pbf.MaybeFilter<pbf.Filter> | undefined,
|
||||||
|
expand?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useRecords<T>(collection: string, options: IUseRecordsOptions = { expand: [] }) {
|
||||||
|
const pb = usePB();
|
||||||
|
const requestKey = crypto.randomUUID();
|
||||||
|
|
||||||
|
const fstr = () => options.filter && options.filter() ? pbf.stringify(options.filter()) : undefined;
|
||||||
|
const v = options.validator ?? ({ parse: (i) => i });
|
||||||
|
|
||||||
|
const [records, setRecords] = createStore<{
|
||||||
|
loading: boolean,
|
||||||
|
records: (T & RecordModel)[],
|
||||||
|
error: Error | null,
|
||||||
|
}>({
|
||||||
|
records: [],
|
||||||
|
loading: true,
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const cf = fstr();
|
||||||
|
|
||||||
|
const setRecordChecked: typeof setRecords = (...args) => {
|
||||||
|
if (cf !== fstr()) return;
|
||||||
|
(setRecords as any)(...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
setRecordChecked("loading", () => true)
|
||||||
|
setRecordChecked("error", () => null)
|
||||||
|
|
||||||
|
pb.collection(collection).getFullList({
|
||||||
|
requestKey,
|
||||||
|
filter: cf,
|
||||||
|
expand: options?.expand ? options.expand.join(",") : undefined,
|
||||||
|
}).then((values) => {
|
||||||
|
setRecordChecked("records", () => values.map(value => v.parse(value) as T & RecordModel))
|
||||||
|
}).catch((error) => {
|
||||||
|
setRecordChecked("error", () => error as Error)
|
||||||
|
}).finally(() => {
|
||||||
|
setRecordChecked("loading", () => false)
|
||||||
|
});
|
||||||
|
|
||||||
|
const sub = pb.realtime.subscribe(collection, (event: RecordSubscription<T & RecordModel>) => {
|
||||||
|
console.log("Event", event);
|
||||||
|
const rec = v.parse(event.record) as T & RecordModel;
|
||||||
|
switch (event.action) {
|
||||||
|
case "create":
|
||||||
|
setRecordChecked("records", (records) => [...records, rec]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "update":
|
||||||
|
setRecordChecked("records", (records) => records.map(r => r.id === rec.id ? rec : r));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "delete":
|
||||||
|
setRecordChecked("records", (records) => records.filter(r => r.id !== rec.id));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.log("Unknown action", event.action);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
requestKey: requestKey + "-rt",
|
||||||
|
query: {
|
||||||
|
filter: cf,
|
||||||
|
expand: options?.expand ? options.expand.join(",") : undefined,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sub.then(unsub => { console.log("Subscribed to realtime changes!"); });
|
||||||
|
sub.catch(err => { console.error("Error in realtime subscription", err) });
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
pb.cancelRequest(requestKey);
|
||||||
|
pb.cancelRequest(requestKey + "-rt");
|
||||||
|
sub.then(s => s());
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
return records;
|
||||||
|
}
|
3
src/index.ts
Normal file
3
src/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export { default as PBContext, usePB } from "./context.js";
|
||||||
|
export { useRecord, useRecords } from "./hooks.js";
|
||||||
|
|
12
tsconfig.json
Normal file
12
tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "nodenext",
|
||||||
|
"target": "ESNext",
|
||||||
|
"declaration": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
47
yarn.lock
Normal file
47
yarn.lock
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@nedpals/pbf@^1.3.2":
|
||||||
|
version "1.3.2"
|
||||||
|
resolved "https://npm.hibas123.de/@nedpals/pbf/-/pbf-1.3.2.tgz#53b00c65103fbaa90c217ea0a4183119d9f9817e"
|
||||||
|
integrity sha512-vTEUIbVF8AiufclVzor2HWMoTrnIZn74NTu+B7HlNlB+QR9HEq/9Rf/hufX9QOt54iOoJukP2x3ASsDUKl48MQ==
|
||||||
|
|
||||||
|
csstype@^3.1.0:
|
||||||
|
version "3.1.3"
|
||||||
|
resolved "https://npm.hibas123.de/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
|
||||||
|
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
|
||||||
|
|
||||||
|
pocketbase@^0.25.1:
|
||||||
|
version "0.25.1"
|
||||||
|
resolved "https://npm.hibas123.de/pocketbase/-/pocketbase-0.25.1.tgz#e2bb606d2e39a992d9b328d67a24a9879ba7337d"
|
||||||
|
integrity sha512-2IH0KLI/qMNR/E17C7BGWX2FxW7Tead+igLHOWZ45P56v/NyVT18Jnmddeft+3qWWGL1Hog2F8bc4orWV/+Fcg==
|
||||||
|
|
||||||
|
seroval-plugins@^1.1.0:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://npm.hibas123.de/seroval-plugins/-/seroval-plugins-1.2.1.tgz#fa535e70ade8af553634b2b5c80d8a6fd8c2ff72"
|
||||||
|
integrity sha512-H5vs53+39+x4Udwp4J5rNZfgFuA+Lt+uU+09w1gYBVWomtAl98B+E9w7yC05Xc81/HgLvJdlyqJbU0fJCKCmdw==
|
||||||
|
|
||||||
|
seroval@^1.1.0:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://npm.hibas123.de/seroval/-/seroval-1.2.1.tgz#fc671d63445923ab64f7abaf3967c83901382f40"
|
||||||
|
integrity sha512-yBxFFs3zmkvKNmR0pFSU//rIsYjuX418TnlDmc2weaq5XFDqDIV/NOMPBoLrbxjLH42p4UzRuXHryXh9dYcKcw==
|
||||||
|
|
||||||
|
solid-js@^1.9.4:
|
||||||
|
version "1.9.4"
|
||||||
|
resolved "https://npm.hibas123.de/solid-js/-/solid-js-1.9.4.tgz#da9b5645f10875a631d93335cd50525ff36b6c27"
|
||||||
|
integrity sha512-ipQl8FJ31bFUoBNScDQTG3BjN6+9Rg+Q+f10bUbnO6EOTTf5NGerJeHc7wyu5I4RMHEl/WwZwUmy/PTRgxxZ8g==
|
||||||
|
dependencies:
|
||||||
|
csstype "^3.1.0"
|
||||||
|
seroval "^1.1.0"
|
||||||
|
seroval-plugins "^1.1.0"
|
||||||
|
|
||||||
|
typescript@^5.7.3:
|
||||||
|
version "5.7.3"
|
||||||
|
resolved "https://npm.hibas123.de/typescript/-/typescript-5.7.3.tgz#919b44a7dbb8583a9b856d162be24a54bf80073e"
|
||||||
|
integrity sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==
|
||||||
|
|
||||||
|
zod@^3.24.2:
|
||||||
|
version "3.24.2"
|
||||||
|
resolved "https://npm.hibas123.de/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3"
|
||||||
|
integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==
|
Loading…
x
Reference in New Issue
Block a user