sveltedfire 0.0.8 → 0.1.3
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.
- package/README.md +14 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -1
- package/dist/sveltedfire/auth/AuthSig.d.ts +7 -0
- package/dist/sveltedfire/auth/AuthSig.js +1 -0
- package/dist/sveltedfire/auth/getAuthContext.d.ts +1 -0
- package/dist/sveltedfire/auth/getAuthContext.js +3 -0
- package/dist/sveltedfire/components/FireForm.svelte +54 -0
- package/dist/sveltedfire/components/FireForm.svelte.d.ts +10 -0
- package/dist/sveltedfire/components/SignedIn.svelte +4 -7
- package/dist/sveltedfire/components/SignedOut.svelte +4 -7
- package/dist/sveltedfire/components/SveltedAuth.svelte +43 -0
- package/dist/sveltedfire/components/SveltedAuth.svelte.d.ts +5 -0
- package/dist/sveltedfire/constants.d.ts +1 -0
- package/dist/sveltedfire/constants.js +1 -0
- package/dist/sveltedfire/utilities/fetchDocs.d.ts +5 -2
- package/dist/sveltedfire/utilities/fetchDocs.js +31 -9
- package/dist/sveltedfire/utilities/listenDocs.d.ts +6 -3
- package/dist/sveltedfire/utilities/listenDocs.js +31 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,6 +26,18 @@ This guarantees that firebase has been initialized prior to any data loading cal
|
|
|
26
26
|
|
|
27
27
|
### Authentication
|
|
28
28
|
|
|
29
|
+
In order to fully utilize the helper components sveltedfire contains, you must wrap your app at a high level with the `SveltedAuth` component. This initializes the firebase authentication system and provides a context that provides access to the current user. To access the auth context, use the `getAuthContext` helper. Additional keys can be injected onto the context by adding them as props on `SveltedAuth` with the prefix `extra_` and the value being a function that accepts user and token. This function will be called with user and token of null in the case of a logged out user to initialize default values. E.g., to add the key `claimLevel` to the sveltedAuth context:
|
|
30
|
+
|
|
31
|
+
```svelte
|
|
32
|
+
<script lang="ts">
|
|
33
|
+
import { SveltedAuth } from 'sveltedfire'
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<SveltedAuth extra_claimLevel={(user, token) => token!.claims.level || ''}>
|
|
37
|
+
...rest of application
|
|
38
|
+
</SveltedAuth>
|
|
39
|
+
```
|
|
40
|
+
|
|
29
41
|
You can use the SignedIn/SignoutOut components to selectively render based on the user's authentication state. In addition, the `signOut` and `signInWithGoogle` helpers are available. Note that the signInWithGoogle automatically performs a signInWithPopup. If you would rather use signInWithRedirect, you will need to implement that manually.
|
|
30
42
|
|
|
31
43
|
### Fetching/Querying
|
|
@@ -37,11 +49,12 @@ fetchDoc('pages', params.slug)
|
|
|
37
49
|
fetchDoc('pages', 'home', 'posts', params.postId)
|
|
38
50
|
```
|
|
39
51
|
|
|
40
|
-
To perform a query for one or more documents, use the `fetchDocs` method. This is a double-invoked function. The first invocation sets the collection path. The second invocation accepts either
|
|
52
|
+
To perform a query for one or more documents, use the `fetchDocs` method. This is a double-invoked function. The first invocation sets the collection path. The second invocation accepts either a variable number of arguments. It accepts three arguments specifically for a simpler form of a where condition. Otherwise, you can pass one or more firestore constraints (where, limit, orderBy, startAt, endAt) and up to one composite filter (and, or)
|
|
41
53
|
```typescript
|
|
42
54
|
fetchDocs('pages')() // fetch all pages
|
|
43
55
|
fetchDocs('pages')('public', '==', true) // fetch all public pages
|
|
44
56
|
fetchDocs('pages')(and(where('published_at', '>=', '2025-01-01'), where('published_at', '<=', '2025-01-31'))) // fetch all pages published in the month of January
|
|
57
|
+
fetchDocs('pages')(where('public', '==', true), limit(10), orderBy('published_at', 'desc'))
|
|
45
58
|
```
|
|
46
59
|
|
|
47
60
|
To listen for the snapshots of an object, use `listenDoc`. This takes the same parameters as `fetchDoc`, but returns a store. This should be used with rune syntax as below:
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,10 @@ export { listenDoc } from './sveltedfire/utilities/listenDoc.js';
|
|
|
7
7
|
export { listenDocs } from './sveltedfire/utilities/listenDocs.js';
|
|
8
8
|
export { kindlyFetchDoc } from './sveltedfire/utilities/kindlyFetchDoc.js';
|
|
9
9
|
export { kindlyFetchDocs } from './sveltedfire/utilities/kindlyFetchDocs.js';
|
|
10
|
+
export { type AuthSig } from './sveltedfire/auth/AuthSig.js';
|
|
11
|
+
export { getAuthContext } from './sveltedfire/auth/getAuthContext.js';
|
|
10
12
|
import SignedIn from './sveltedfire/components/SignedIn.svelte';
|
|
11
13
|
import SignedOut from './sveltedfire/components/SignedOut.svelte';
|
|
12
|
-
|
|
14
|
+
import SveltedAuth from "./sveltedfire/components/SveltedAuth.svelte";
|
|
15
|
+
import FireForm from "./sveltedfire/components/FireForm.svelte";
|
|
16
|
+
export { SignedIn, SignedOut, SveltedAuth, FireForm };
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,10 @@ export { listenDoc } from './sveltedfire/utilities/listenDoc.js';
|
|
|
8
8
|
export { listenDocs } from './sveltedfire/utilities/listenDocs.js';
|
|
9
9
|
export { kindlyFetchDoc } from './sveltedfire/utilities/kindlyFetchDoc.js';
|
|
10
10
|
export { kindlyFetchDocs } from './sveltedfire/utilities/kindlyFetchDocs.js';
|
|
11
|
+
export {} from './sveltedfire/auth/AuthSig.js';
|
|
12
|
+
export { getAuthContext } from './sveltedfire/auth/getAuthContext.js';
|
|
11
13
|
import SignedIn from './sveltedfire/components/SignedIn.svelte';
|
|
12
14
|
import SignedOut from './sveltedfire/components/SignedOut.svelte';
|
|
13
|
-
|
|
15
|
+
import SveltedAuth from "./sveltedfire/components/SveltedAuth.svelte";
|
|
16
|
+
import FireForm from "./sveltedfire/components/FireForm.svelte";
|
|
17
|
+
export { SignedIn, SignedOut, SveltedAuth, FireForm };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import {} from "firebase/auth";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getAuthContext: () => unknown;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getFirestore, collection, doc, getDoc, updateDoc, setDoc, addDoc, deleteField } from "firebase/firestore"
|
|
3
|
+
|
|
4
|
+
let { collectionName, docId = null, docIdField = null, onSubmit, children, beforeSubmit = null, ...rest } = $props()
|
|
5
|
+
const db = getFirestore()
|
|
6
|
+
|
|
7
|
+
const submitTheForm = async (ev: SubmitEvent) => {
|
|
8
|
+
console.log('Attempting to save')
|
|
9
|
+
ev.preventDefault()
|
|
10
|
+
const formData = new FormData(ev.target as HTMLFormElement)
|
|
11
|
+
let col = collection(db, collectionName)
|
|
12
|
+
let objData = Object.fromEntries(formData.entries())
|
|
13
|
+
if (beforeSubmit) {
|
|
14
|
+
objData = beforeSubmit(objData, ev)
|
|
15
|
+
}
|
|
16
|
+
Object.keys(objData).forEach(k => {
|
|
17
|
+
if (objData[k] === '_deleteme_') {
|
|
18
|
+
if (!k.match(/\[/)) {
|
|
19
|
+
objData[k] = deleteField()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
let matcher = k.match(/^(.*)\[(.*)\]$/)
|
|
23
|
+
if (matcher) {
|
|
24
|
+
if (!(matcher[1] in objData)) {
|
|
25
|
+
objData[matcher[1]] = []
|
|
26
|
+
}
|
|
27
|
+
if (objData[k] === '_deleteme_') {
|
|
28
|
+
console.log('Deletion override')
|
|
29
|
+
objData[matcher[1]] = deleteField()
|
|
30
|
+
} else {
|
|
31
|
+
objData[matcher[1]][parseInt(matcher[2], 10)] = objData[k]
|
|
32
|
+
}
|
|
33
|
+
delete objData[k]
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
console.log('Object data is', objData)
|
|
37
|
+
let docRefId
|
|
38
|
+
if (docId) {
|
|
39
|
+
await updateDoc(doc(col, docId), objData)
|
|
40
|
+
docRefId = docId
|
|
41
|
+
} else if (docIdField) {
|
|
42
|
+
await updateDoc(doc(col, formData.get(docIdField) as string), objData)
|
|
43
|
+
docRefId = formData.get(docIdField) as string
|
|
44
|
+
} else {
|
|
45
|
+
let docRef = await addDoc(col, objData)
|
|
46
|
+
docRefId = docRef.id
|
|
47
|
+
}
|
|
48
|
+
onSubmit(docRefId)
|
|
49
|
+
}
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<form onsubmit={submitTheForm} {...rest}>
|
|
53
|
+
{@render children()}
|
|
54
|
+
</form>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare const FireForm: import("svelte").Component<{
|
|
2
|
+
collectionName: any;
|
|
3
|
+
docId?: any;
|
|
4
|
+
docIdField?: any;
|
|
5
|
+
onSubmit: any;
|
|
6
|
+
children: any;
|
|
7
|
+
beforeSubmit?: any;
|
|
8
|
+
} & Record<string, any>, {}, "">;
|
|
9
|
+
type FireForm = ReturnType<typeof FireForm>;
|
|
10
|
+
export default FireForm;
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
const { children } = $props()
|
|
3
|
-
import {
|
|
3
|
+
import { getContext } from 'svelte';
|
|
4
|
+
import { AuthSig } from '../auth/AuthSig.js';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
let user = $state(null)
|
|
7
|
-
auth.onAuthStateChanged(u => {
|
|
8
|
-
user = u
|
|
9
|
-
})
|
|
6
|
+
let fullAuth = getContext<AuthSig>('sveltedAuth')
|
|
10
7
|
</script>
|
|
11
8
|
|
|
12
|
-
{#if
|
|
9
|
+
{#if fullAuth!.currentUser}
|
|
13
10
|
{@render children()}
|
|
14
11
|
{/if}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
const { children } = $props()
|
|
3
|
-
import {
|
|
3
|
+
import { getContext } from 'svelte';
|
|
4
|
+
import { type AuthSig } from '../auth/AuthSig.js';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
let user = $state(null)
|
|
7
|
-
auth.onAuthStateChanged(u => {
|
|
8
|
-
user = u
|
|
9
|
-
})
|
|
6
|
+
let fullAuth = getContext<AuthSig>('sveltedAuth')
|
|
10
7
|
</script>
|
|
11
8
|
|
|
12
|
-
{#if !
|
|
9
|
+
{#if fullAuth && !fullAuth!.currentUser}
|
|
13
10
|
{@render children()}
|
|
14
11
|
{/if}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { setContext } from 'svelte'
|
|
3
|
+
import { getAuth, type User } from 'firebase/auth'
|
|
4
|
+
import { signOut } from '../auth/signOut.js'
|
|
5
|
+
import { signInWithGoogle } from '../auth/signInWithGoogle.js'
|
|
6
|
+
import { type AuthSig } from '../auth/AuthSig.js';
|
|
7
|
+
import { AUTH_CONTEXT_NAME } from '../constants.js'
|
|
8
|
+
|
|
9
|
+
const { children, ...rest } = $props()
|
|
10
|
+
|
|
11
|
+
let additionalKeys: {[k: string]: any} = {}
|
|
12
|
+
Object.keys(rest).forEach(k => {
|
|
13
|
+
if (k.startsWith('extra_')) {
|
|
14
|
+
const actualKey = k.replace('^extra_', '')
|
|
15
|
+
additionalKeys[actualKey] = k
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const auth = getAuth()
|
|
20
|
+
|
|
21
|
+
let fullAuth = $state<AuthSig>({
|
|
22
|
+
currentUser: null,
|
|
23
|
+
signOut,
|
|
24
|
+
signInWithGoogle
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
auth.onAuthStateChanged(user => {
|
|
28
|
+
fullAuth.currentUser = user
|
|
29
|
+
Object.keys(additionalKeys).forEach((k: string) => {
|
|
30
|
+
fullAuth[k] = rest[additionalKeys[k]](null, null)
|
|
31
|
+
})
|
|
32
|
+
user?.getIdTokenResult().then(token => {
|
|
33
|
+
Object.keys(additionalKeys).forEach((k: string) => {
|
|
34
|
+
fullAuth[k] = rest[additionalKeys[k]](user, token)
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
setContext<AuthSig>(AUTH_CONTEXT_NAME, fullAuth)
|
|
40
|
+
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
{@render children()}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const AUTH_CONTEXT_NAME = "sveltedAuth";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const AUTH_CONTEXT_NAME = 'sveltedAuth';
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { QueryCompositeFilterConstraint } from "firebase/firestore";
|
|
2
|
-
|
|
1
|
+
import { QueryFieldFilterConstraint, QueryCompositeFilterConstraint, type QueryNonFilterConstraint, type WhereFilterOp } from "firebase/firestore";
|
|
2
|
+
type QueryFieldsType = QueryCompositeFilterConstraint | QueryFieldFilterConstraint | QueryNonFilterConstraint;
|
|
3
|
+
type TheFieldsType = string | QueryFieldsType | WhereFilterOp | number | boolean;
|
|
4
|
+
export declare const fetchDocs: (...collectionPath: Array<string>) => (...fields: TheFieldsType[]) => Promise<{
|
|
3
5
|
docs: {
|
|
4
6
|
_id: string;
|
|
5
7
|
id: string;
|
|
6
8
|
}[];
|
|
7
9
|
count: number;
|
|
8
10
|
}>;
|
|
11
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getFirestore, collection,
|
|
2
|
-
export const fetchDocs = (...collectionPath) => async (
|
|
1
|
+
import { getFirestore, collection, getDocs, QueryFieldFilterConstraint, where, query, QueryCompositeFilterConstraint, QueryConstraint, QueryOrderByConstraint, QueryLimitConstraint, QueryStartAtConstraint, QueryEndAtConstraint } from "firebase/firestore";
|
|
2
|
+
export const fetchDocs = (...collectionPath) => async (...fields) => {
|
|
3
3
|
const db = getFirestore();
|
|
4
4
|
let theRef;
|
|
5
5
|
if (collectionPath.length > 1) {
|
|
@@ -9,16 +9,38 @@ export const fetchDocs = (...collectionPath) => async (qOrField = null, comparat
|
|
|
9
9
|
theRef = collection(db, collectionPath[0]);
|
|
10
10
|
}
|
|
11
11
|
let theQuery = query(theRef);
|
|
12
|
-
if (
|
|
13
|
-
if (
|
|
14
|
-
theQuery = query(theRef, where(
|
|
12
|
+
if (fields.length > 0) {
|
|
13
|
+
if ((fields.length === 3) && (typeof fields[1] === "string")) {
|
|
14
|
+
theQuery = query(theRef, where(fields[0], fields[1], fields[2]));
|
|
15
15
|
}
|
|
16
16
|
else {
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const builtFields = [];
|
|
18
|
+
let compositeConstraint = null;
|
|
19
|
+
fields.forEach(k => {
|
|
20
|
+
if (k instanceof QueryCompositeFilterConstraint) {
|
|
21
|
+
compositeConstraint = k;
|
|
22
|
+
}
|
|
23
|
+
else if (k instanceof QueryFieldFilterConstraint) {
|
|
24
|
+
builtFields.push(k);
|
|
25
|
+
}
|
|
26
|
+
else if (k instanceof QueryOrderByConstraint) {
|
|
27
|
+
builtFields.push(k);
|
|
28
|
+
}
|
|
29
|
+
else if (k instanceof QueryLimitConstraint) {
|
|
30
|
+
builtFields.push(k);
|
|
31
|
+
}
|
|
32
|
+
else if (k instanceof QueryStartAtConstraint) {
|
|
33
|
+
builtFields.push(k);
|
|
34
|
+
}
|
|
35
|
+
else if (k instanceof QueryEndAtConstraint) {
|
|
36
|
+
builtFields.push(k);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
if (compositeConstraint) {
|
|
40
|
+
theQuery = query(theRef, compositeConstraint, ...builtFields);
|
|
19
41
|
}
|
|
20
|
-
else
|
|
21
|
-
theQuery = query(theRef,
|
|
42
|
+
else {
|
|
43
|
+
theQuery = query(theRef, ...builtFields);
|
|
22
44
|
}
|
|
23
45
|
}
|
|
24
46
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { QueryCompositeFilterConstraint } from "firebase/firestore";
|
|
1
|
+
import { QueryFieldFilterConstraint, QueryCompositeFilterConstraint, type WhereFilterOp, type QueryNonFilterConstraint } from "firebase/firestore";
|
|
2
2
|
import { type DocumentData } from "firebase/firestore";
|
|
3
|
-
|
|
3
|
+
type QueryFieldsType = QueryCompositeFilterConstraint | QueryFieldFilterConstraint | QueryNonFilterConstraint;
|
|
4
|
+
type TheFieldsType = string | QueryFieldsType | WhereFilterOp | number | boolean;
|
|
5
|
+
export declare const listenDocs: (...collectionPath: Array<string>) => (...fields: TheFieldsType[]) => import("svelte/store").Readable<{
|
|
4
6
|
docs: DocumentData[];
|
|
5
7
|
count: number;
|
|
6
|
-
} | null
|
|
8
|
+
} | null>;
|
|
9
|
+
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { getFirestore, collection, doc, getDocs, Query, QueryFieldFilterConstraint, where, query, QueryCompositeFilterConstraint } from "firebase/firestore";
|
|
1
|
+
import { getFirestore, collection, doc, getDocs, Query, QueryFieldFilterConstraint, where, query, QueryCompositeFilterConstraint, QueryConstraint, QueryOrderByConstraint, QueryLimitConstraint, QueryStartAtConstraint, QueryEndAtConstraint } from "firebase/firestore";
|
|
2
2
|
import { onSnapshot } from "firebase/firestore";
|
|
3
3
|
import { readable } from 'svelte/store';
|
|
4
|
-
export const listenDocs = (...collectionPath) =>
|
|
4
|
+
export const listenDocs = (...collectionPath) => (...fields) => {
|
|
5
5
|
const db = getFirestore();
|
|
6
6
|
let theRef;
|
|
7
7
|
if (collectionPath.length > 1) {
|
|
@@ -11,16 +11,38 @@ export const listenDocs = (...collectionPath) => async (qOrField = null, compara
|
|
|
11
11
|
theRef = collection(db, collectionPath[0]);
|
|
12
12
|
}
|
|
13
13
|
let theQuery = query(theRef);
|
|
14
|
-
if (
|
|
15
|
-
if (
|
|
16
|
-
theQuery = query(theRef, where(
|
|
14
|
+
if (fields.length > 0) {
|
|
15
|
+
if ((fields.length === 3) && (typeof fields[1] === "string")) {
|
|
16
|
+
theQuery = query(theRef, where(fields[0], fields[1], fields[2]));
|
|
17
17
|
}
|
|
18
18
|
else {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
const builtFields = [];
|
|
20
|
+
let compositeConstraint = null;
|
|
21
|
+
fields.forEach(k => {
|
|
22
|
+
if (k instanceof QueryCompositeFilterConstraint) {
|
|
23
|
+
compositeConstraint = k;
|
|
24
|
+
}
|
|
25
|
+
else if (k instanceof QueryFieldFilterConstraint) {
|
|
26
|
+
builtFields.push(k);
|
|
27
|
+
}
|
|
28
|
+
else if (k instanceof QueryOrderByConstraint) {
|
|
29
|
+
builtFields.push(k);
|
|
30
|
+
}
|
|
31
|
+
else if (k instanceof QueryLimitConstraint) {
|
|
32
|
+
builtFields.push(k);
|
|
33
|
+
}
|
|
34
|
+
else if (k instanceof QueryStartAtConstraint) {
|
|
35
|
+
builtFields.push(k);
|
|
36
|
+
}
|
|
37
|
+
else if (k instanceof QueryEndAtConstraint) {
|
|
38
|
+
builtFields.push(k);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
if (compositeConstraint) {
|
|
42
|
+
theQuery = query(theRef, compositeConstraint, ...builtFields);
|
|
21
43
|
}
|
|
22
|
-
else
|
|
23
|
-
theQuery = query(theRef,
|
|
44
|
+
else {
|
|
45
|
+
theQuery = query(theRef, ...builtFields);
|
|
24
46
|
}
|
|
25
47
|
}
|
|
26
48
|
}
|