@superlayer/svelte 1.0.67

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.
@@ -0,0 +1,337 @@
1
+ import { txInit, init as core_init, coerceQuery, getInfiniteQueryInitialSnapshot, InstantError, } from '@superlayer/core';
2
+ import { InstantSvelteRoom, rooms } from './InstantSvelteRoom.svelte.js';
3
+ import version from './version.js';
4
+ const defaultState = {
5
+ isLoading: true,
6
+ data: undefined,
7
+ pageInfo: undefined,
8
+ error: undefined,
9
+ };
10
+ const defaultAuthState = {
11
+ isLoading: true,
12
+ user: undefined,
13
+ error: undefined,
14
+ };
15
+ function stateForResult(result) {
16
+ return {
17
+ isLoading: !Boolean(result),
18
+ data: undefined,
19
+ pageInfo: undefined,
20
+ error: undefined,
21
+ ...(result ? result : {}),
22
+ };
23
+ }
24
+ export class InstantSvelteDatabase {
25
+ tx = txInit();
26
+ auth;
27
+ storage;
28
+ streams;
29
+ core;
30
+ constructor(core) {
31
+ this.core = core;
32
+ this.auth = this.core.auth;
33
+ this.storage = this.core.storage;
34
+ this.streams = this.core.streams;
35
+ }
36
+ /**
37
+ * Returns a unique ID for a given `name`. It's stored in local storage,
38
+ * so you will get the same ID across sessions.
39
+ *
40
+ * @example
41
+ * const deviceId = await db.getLocalId('device');
42
+ */
43
+ getLocalId = (name) => {
44
+ return this.core.getLocalId(name);
45
+ };
46
+ /**
47
+ * Use this to write data! You can create, update, delete, and link objects
48
+ *
49
+ * @see https://interfacedb.com/docs/instaml
50
+ *
51
+ * @example
52
+ * const goalId = id();
53
+ * db.transact(db.tx.goals[goalId].update({title: "Get fit"}))
54
+ */
55
+ transact = (chunks) => {
56
+ return this.core.transact(chunks);
57
+ };
58
+ /**
59
+ * One time query for the logged in state.
60
+ *
61
+ * @see https://interfacedb.com/docs/auth
62
+ * @example
63
+ * const user = await db.getAuth();
64
+ * console.log('logged in as', user.email)
65
+ */
66
+ getAuth() {
67
+ return this.core.getAuth();
68
+ }
69
+ /**
70
+ * Use this for one-off queries.
71
+ * Returns local data if available, otherwise fetches from the server.
72
+ *
73
+ * @see https://interfacedb.com/docs/instaql
74
+ *
75
+ * @example
76
+ * const resp = await db.queryOnce({ goals: {} });
77
+ * console.log(resp.data.goals)
78
+ */
79
+ queryOnce = (query, opts) => {
80
+ return this.core.queryOnce(query, opts);
81
+ };
82
+ // -----------
83
+ // Svelte reactive hooks
84
+ /**
85
+ * Use this to query your data!
86
+ *
87
+ * @see https://interfacedb.com/docs/instaql
88
+ *
89
+ * @example
90
+ * // basic query
91
+ * const state = db.useQuery({ goals: {} });
92
+ * // state.isLoading, state.error, state.data
93
+ *
94
+ * @example
95
+ * // conditional query (pass a function that returns null to skip)
96
+ * const auth = db.useAuth();
97
+ * const state = db.useQuery(() =>
98
+ * auth.user ? { todos: { $: { where: { 'owner.id': auth.user.id } } } } : null
99
+ * );
100
+ *
101
+ * @example
102
+ * // reactive query (re-runs when $state variables change)
103
+ * let filter = $state<'all' | 'done'>('all');
104
+ * const state = db.useQuery(() => {
105
+ * if (filter === 'all') return { todos: {} };
106
+ * return { todos: { $: { where: { done: true } } } };
107
+ * });
108
+ */
109
+ useQuery = (query, opts) => {
110
+ let result = $state({
111
+ ...defaultState,
112
+ });
113
+ $effect(() => {
114
+ const resolvedQuery = typeof query === 'function' ? query() : query;
115
+ if (!resolvedQuery) {
116
+ result.isLoading = true;
117
+ result.data = undefined;
118
+ result.pageInfo = undefined;
119
+ result.error = undefined;
120
+ return;
121
+ }
122
+ let q = resolvedQuery;
123
+ if (opts && 'ruleParams' in opts) {
124
+ q = { $$ruleParams: opts['ruleParams'], ...q };
125
+ }
126
+ const coerced = coerceQuery(q);
127
+ const prev = this.core._reactor.getPreviousResult(coerced);
128
+ const prevState = stateForResult(prev);
129
+ result.isLoading = prevState.isLoading;
130
+ result.data = prevState.data;
131
+ result.pageInfo = prevState.pageInfo;
132
+ result.error = prevState.error;
133
+ const unsub = this.core.subscribeQuery(coerced, (r) => {
134
+ result.isLoading = false;
135
+ result.data = r.data;
136
+ result.pageInfo = r.pageInfo;
137
+ result.error = r.error;
138
+ });
139
+ return unsub;
140
+ });
141
+ return result;
142
+ };
143
+ /**
144
+ * Subscribe to a query and incrementally load more items.
145
+ *
146
+ * Only one top level namespace in the query is allowed.
147
+ *
148
+ * @see https://interfacedb.com/docs/instaql
149
+ *
150
+ * @example
151
+ * const state = db.useInfiniteQuery({
152
+ * posts: {
153
+ * $: {
154
+ * limit: 20,
155
+ * order: { createdAt: 'desc' },
156
+ * },
157
+ * },
158
+ * });
159
+ * // state.data, state.isLoading, state.error,
160
+ * // state.canLoadNextPage, state.loadNextPage()
161
+ */
162
+ useInfiniteQuery = (query, opts) => {
163
+ let sub = null;
164
+ let result = $state({
165
+ isLoading: true,
166
+ error: undefined,
167
+ data: undefined,
168
+ canLoadNextPage: false,
169
+ loadNextPage: () => sub?.loadNextPage(),
170
+ });
171
+ $effect(() => {
172
+ const resolvedQuery = typeof query === 'function' ? query() : query;
173
+ if (!resolvedQuery) {
174
+ sub = null;
175
+ result.isLoading = true;
176
+ result.error = undefined;
177
+ result.data = undefined;
178
+ result.canLoadNextPage = false;
179
+ return;
180
+ }
181
+ const snapshot = getInfiniteQueryInitialSnapshot(this.core, resolvedQuery, opts);
182
+ result.isLoading = !snapshot.data && !snapshot.error;
183
+ result.error = snapshot.error;
184
+ result.data = snapshot.data;
185
+ result.canLoadNextPage = snapshot.canLoadNextPage;
186
+ sub = this.core.subscribeInfiniteQuery(resolvedQuery, (resp) => {
187
+ result.isLoading = false;
188
+ result.data = resp.data;
189
+ result.error = resp.error;
190
+ result.canLoadNextPage = resp.canLoadNextPage;
191
+ }, opts);
192
+ return () => {
193
+ sub?.unsubscribe();
194
+ sub = null;
195
+ };
196
+ });
197
+ return result;
198
+ };
199
+ /**
200
+ * Listen for the logged in state. This is useful
201
+ * for deciding when to show a login screen.
202
+ *
203
+ * @see https://interfacedb.com/docs/auth
204
+ * @example
205
+ * const auth = db.useAuth();
206
+ * // auth.isLoading, auth.user, auth.error
207
+ */
208
+ useAuth = () => {
209
+ let result = $state(this.core._reactor._currentUserCached
210
+ ? { ...this.core._reactor._currentUserCached }
211
+ : { ...defaultAuthState });
212
+ $effect(() => {
213
+ const unsub = this.core.subscribeAuth((auth) => {
214
+ result.isLoading = false;
215
+ result.user = auth.user;
216
+ result.error = auth.error;
217
+ });
218
+ return unsub;
219
+ });
220
+ return result;
221
+ };
222
+ /**
223
+ * Subscribe to the currently logged in user.
224
+ * If the user is not logged in, this will throw an Error.
225
+ *
226
+ * @see https://interfacedb.com/docs/auth
227
+ * @example
228
+ * const user = db.useUser();
229
+ * // user.email, user.id, etc.
230
+ * // Throws if not logged in
231
+ */
232
+ useUser = () => {
233
+ const auth = this.useAuth();
234
+ // Return a proxy that always reads from the latest auth state
235
+ return new Proxy({}, {
236
+ get(_target, prop, receiver) {
237
+ if (!auth.user) {
238
+ throw new InstantError('useUser must be used within an auth-protected route');
239
+ }
240
+ return Reflect.get(auth.user, prop, receiver);
241
+ },
242
+ });
243
+ };
244
+ /**
245
+ * Listen for connection status changes to Instant.
246
+ *
247
+ * @see https://www.interfacedb.com/docs/patterns#connection-status
248
+ * @example
249
+ * const status = db.useConnectionStatus();
250
+ * // status.current
251
+ */
252
+ useConnectionStatus = () => {
253
+ let result = $state({
254
+ current: this.core._reactor.status,
255
+ });
256
+ $effect(() => {
257
+ const unsub = this.core.subscribeConnectionStatus((newStatus) => {
258
+ result.current = newStatus;
259
+ });
260
+ return unsub;
261
+ });
262
+ return result;
263
+ };
264
+ /**
265
+ * A hook that returns a unique ID for a given `name`. localIds are
266
+ * stored in local storage, so you will get the same ID across sessions.
267
+ *
268
+ * Initially returns `null`, and then loads the localId.
269
+ *
270
+ * @example
271
+ * const deviceId = db.useLocalId('device');
272
+ * // deviceId.current is null initially, then the ID string
273
+ */
274
+ useLocalId = (name) => {
275
+ let result = $state({ current: null });
276
+ $effect(() => {
277
+ let mounted = true;
278
+ this.getLocalId(name).then((id) => {
279
+ if (mounted) {
280
+ result.current = id;
281
+ }
282
+ });
283
+ return () => {
284
+ mounted = false;
285
+ };
286
+ });
287
+ return result;
288
+ };
289
+ /**
290
+ * Obtain a handle to a room, which allows you to listen to topics and presence data
291
+ *
292
+ * @see https://interfacedb.com/docs/presence-and-topics
293
+ *
294
+ * @example
295
+ * const room = db.room('chat', roomId);
296
+ * const presence = db.rooms.usePresence(room);
297
+ */
298
+ room(type = '_defaultRoomType', id = '_defaultRoomId') {
299
+ return new InstantSvelteRoom(this.core, type, id);
300
+ }
301
+ /**
302
+ * Hooks for working with rooms
303
+ *
304
+ * @see https://interfacedb.com/docs/presence-and-topics
305
+ *
306
+ * @example
307
+ * const room = db.room('chat', roomId);
308
+ * const presence = db.rooms.usePresence(room);
309
+ * const publish = db.rooms.usePublishTopic(room, 'emoji');
310
+ */
311
+ rooms = rooms;
312
+ }
313
+ // -----------
314
+ // init
315
+ /**
316
+ * The first step: init your application!
317
+ *
318
+ * Visit https://interfacedb.com/dash to get your `appId` :)
319
+ *
320
+ * @example
321
+ * import { init } from "@superlayer/svelte"
322
+ *
323
+ * const db = init({ appId: "my-app-id" })
324
+ *
325
+ * // You can also provide a schema for type safety and editor autocomplete!
326
+ *
327
+ * import { init } from "@superlayer/svelte"
328
+ * import schema from "../instant.schema.ts";
329
+ *
330
+ * const db = init({ appId: "my-app-id", schema })
331
+ */
332
+ export function init(config) {
333
+ const coreDb = core_init(config, undefined, undefined, {
334
+ '@instantdb/svelte': version,
335
+ });
336
+ return new InstantSvelteDatabase(coreDb);
337
+ }
@@ -0,0 +1,82 @@
1
+ import { type PresenceOpts, type PresenceResponse, type RoomSchemaShape, InstantCoreDatabase, InstantSchemaDef } from '@superlayer/core';
2
+ export type PresenceHandle<PresenceShape, Keys extends keyof PresenceShape> = PresenceResponse<PresenceShape, Keys> & {
3
+ publishPresence: (data: Partial<PresenceShape>) => void;
4
+ };
5
+ export type TypingIndicatorOpts = {
6
+ timeout?: number | null;
7
+ stopOnEnter?: boolean;
8
+ writeOnly?: boolean;
9
+ };
10
+ export type TypingIndicatorHandle<PresenceShape> = {
11
+ active: PresenceShape[];
12
+ setActive(active: boolean): void;
13
+ inputProps: {
14
+ onKeyDown: (e: KeyboardEvent) => void;
15
+ onBlur: () => void;
16
+ };
17
+ };
18
+ export declare const defaultActivityStopTimeout = 1000;
19
+ /**
20
+ * Listen for broadcasted events given a room and topic.
21
+ *
22
+ * @see https://interfacedb.com/docs/presence-and-topics
23
+ * @example
24
+ * const room = db.room('chats', roomId);
25
+ * db.rooms.useTopicEffect(room, 'emoji', (message, peer) => {
26
+ * console.log(peer.name, 'sent', message);
27
+ * });
28
+ */
29
+ export declare function useTopicEffect<RoomSchema extends RoomSchemaShape, RoomType extends keyof RoomSchema, TopicType extends keyof RoomSchema[RoomType]['topics']>(room: InstantSvelteRoom<any, RoomSchema, RoomType>, topic: TopicType, onEvent: (event: RoomSchema[RoomType]['topics'][TopicType], peer: RoomSchema[RoomType]['presence']) => any): void;
30
+ /**
31
+ * Broadcast an event to a room.
32
+ *
33
+ * @see https://interfacedb.com/docs/presence-and-topics
34
+ * @example
35
+ * const room = db.room('chat', roomId);
36
+ * const publishTopic = db.rooms.usePublishTopic(room, 'emoji');
37
+ * publishTopic({ emoji: "🔥" });
38
+ */
39
+ export declare function usePublishTopic<RoomSchema extends RoomSchemaShape, RoomType extends keyof RoomSchema, TopicType extends keyof RoomSchema[RoomType]['topics']>(room: InstantSvelteRoom<any, RoomSchema, RoomType>, topic: TopicType): (data: RoomSchema[RoomType]['topics'][TopicType]) => void;
40
+ /**
41
+ * Listen for peer's presence data in a room, and publish the current user's presence.
42
+ *
43
+ * @see https://interfacedb.com/docs/presence-and-topics
44
+ * @example
45
+ * const room = db.room('chat', roomId);
46
+ * const presence = db.rooms.usePresence(room, { keys: ["name", "avatar"] });
47
+ * // presence.peers, presence.isLoading, presence.publishPresence
48
+ */
49
+ export declare function usePresence<RoomSchema extends RoomSchemaShape, RoomType extends keyof RoomSchema, Keys extends keyof RoomSchema[RoomType]['presence']>(room: InstantSvelteRoom<any, RoomSchema, RoomType>, opts?: PresenceOpts<RoomSchema[RoomType]['presence'], Keys>): PresenceHandle<RoomSchema[RoomType]['presence'], Keys>;
50
+ /**
51
+ * Publishes presence data to a room
52
+ *
53
+ * @see https://interfacedb.com/docs/presence-and-topics
54
+ * @example
55
+ * const room = db.room('chat', roomId);
56
+ * db.rooms.useSyncPresence(room, { nickname });
57
+ */
58
+ export declare function useSyncPresence<RoomSchema extends RoomSchemaShape, RoomType extends keyof RoomSchema>(room: InstantSvelteRoom<any, RoomSchema, RoomType>, data: Partial<RoomSchema[RoomType]['presence']>, deps?: any[]): void;
59
+ /**
60
+ * Manage typing indicator state
61
+ *
62
+ * @see https://interfacedb.com/docs/presence-and-topics
63
+ * @example
64
+ * const room = db.room('chat', roomId);
65
+ * const typing = db.rooms.useTypingIndicator(room, 'chat-input');
66
+ * // typing.active, typing.setActive(bool), typing.inputProps
67
+ */
68
+ export declare function useTypingIndicator<RoomSchema extends RoomSchemaShape, RoomType extends keyof RoomSchema>(room: InstantSvelteRoom<any, RoomSchema, RoomType>, inputName: string, opts?: TypingIndicatorOpts): TypingIndicatorHandle<RoomSchema[RoomType]['presence']>;
69
+ export declare const rooms: {
70
+ useTopicEffect: typeof useTopicEffect;
71
+ usePublishTopic: typeof usePublishTopic;
72
+ usePresence: typeof usePresence;
73
+ useSyncPresence: typeof useSyncPresence;
74
+ useTypingIndicator: typeof useTypingIndicator;
75
+ };
76
+ export declare class InstantSvelteRoom<Schema extends InstantSchemaDef<any, any, any>, RoomSchema extends RoomSchemaShape, RoomType extends keyof RoomSchema> {
77
+ core: InstantCoreDatabase<Schema, boolean>;
78
+ type: RoomType;
79
+ id: string;
80
+ constructor(core: InstantCoreDatabase<Schema, boolean>, type: RoomType, id: string);
81
+ }
82
+ //# sourceMappingURL=InstantSvelteRoom.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InstantSvelteRoom.svelte.d.ts","sourceRoot":"","sources":["../src/lib/InstantSvelteRoom.svelte.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,196 @@
1
+ export const defaultActivityStopTimeout = 1_000;
2
+ // ------
3
+ // Topics
4
+ /**
5
+ * Listen for broadcasted events given a room and topic.
6
+ *
7
+ * @see https://interfacedb.com/docs/presence-and-topics
8
+ * @example
9
+ * const room = db.room('chats', roomId);
10
+ * db.rooms.useTopicEffect(room, 'emoji', (message, peer) => {
11
+ * console.log(peer.name, 'sent', message);
12
+ * });
13
+ */
14
+ export function useTopicEffect(room, topic, onEvent) {
15
+ $effect(() => {
16
+ const unsub = room.core._reactor.subscribeTopic(room.type, room.id, topic, (event, peer) => {
17
+ onEvent(event, peer);
18
+ });
19
+ return unsub;
20
+ });
21
+ }
22
+ /**
23
+ * Broadcast an event to a room.
24
+ *
25
+ * @see https://interfacedb.com/docs/presence-and-topics
26
+ * @example
27
+ * const room = db.room('chat', roomId);
28
+ * const publishTopic = db.rooms.usePublishTopic(room, 'emoji');
29
+ * publishTopic({ emoji: "🔥" });
30
+ */
31
+ export function usePublishTopic(room, topic) {
32
+ $effect(() => {
33
+ const unsub = room.core._reactor.joinRoom(room.type, room.id);
34
+ return unsub;
35
+ });
36
+ return (data) => {
37
+ room.core._reactor.publishTopic({
38
+ roomType: room.type,
39
+ roomId: room.id,
40
+ topic,
41
+ data,
42
+ });
43
+ };
44
+ }
45
+ // ---------
46
+ // Presence
47
+ /**
48
+ * Listen for peer's presence data in a room, and publish the current user's presence.
49
+ *
50
+ * @see https://interfacedb.com/docs/presence-and-topics
51
+ * @example
52
+ * const room = db.room('chat', roomId);
53
+ * const presence = db.rooms.usePresence(room, { keys: ["name", "avatar"] });
54
+ * // presence.peers, presence.isLoading, presence.publishPresence
55
+ */
56
+ export function usePresence(room, opts = {}) {
57
+ const initial = (room.core._reactor.getPresence(room.type, room.id, opts) ?? {
58
+ peers: {},
59
+ isLoading: true,
60
+ });
61
+ let result = $state({
62
+ ...initial,
63
+ publishPresence: (data) => {
64
+ room.core._reactor.publishPresence(room.type, room.id, data);
65
+ },
66
+ });
67
+ $effect(() => {
68
+ const unsub = room.core._reactor.subscribePresence(room.type, room.id, opts, (data) => {
69
+ result.peers = data.peers;
70
+ result.isLoading = data.isLoading;
71
+ if ('user' in data) {
72
+ result.user = data.user;
73
+ }
74
+ });
75
+ return unsub;
76
+ });
77
+ return result;
78
+ }
79
+ /**
80
+ * Publishes presence data to a room
81
+ *
82
+ * @see https://interfacedb.com/docs/presence-and-topics
83
+ * @example
84
+ * const room = db.room('chat', roomId);
85
+ * db.rooms.useSyncPresence(room, { nickname });
86
+ */
87
+ export function useSyncPresence(room, data, deps) {
88
+ $effect(() => {
89
+ const unsub = room.core._reactor.joinRoom(room.type, room.id, data);
90
+ return unsub;
91
+ });
92
+ $effect(() => {
93
+ if (deps) {
94
+ // Track deps by reading them
95
+ deps.forEach((d) => {
96
+ if (typeof d === 'function')
97
+ d();
98
+ });
99
+ }
100
+ else {
101
+ JSON.stringify(data);
102
+ }
103
+ room.core._reactor.publishPresence(room.type, room.id, data);
104
+ });
105
+ }
106
+ // -----------------
107
+ // Typing Indicator
108
+ /**
109
+ * Manage typing indicator state
110
+ *
111
+ * @see https://interfacedb.com/docs/presence-and-topics
112
+ * @example
113
+ * const room = db.room('chat', roomId);
114
+ * const typing = db.rooms.useTypingIndicator(room, 'chat-input');
115
+ * // typing.active, typing.setActive(bool), typing.inputProps
116
+ */
117
+ export function useTypingIndicator(room, inputName, opts = {}) {
118
+ let timeoutId = null;
119
+ const presence = rooms.usePresence(room, {
120
+ keys: [inputName],
121
+ });
122
+ let _active = $state([]);
123
+ $effect(() => {
124
+ if (opts?.writeOnly) {
125
+ _active = [];
126
+ return;
127
+ }
128
+ // Read presence to track it
129
+ const _peers = presence.peers;
130
+ const presenceSnapshot = room.core._reactor.getPresence(room.type, room.id);
131
+ _active = Object.values(presenceSnapshot?.peers ?? {}).filter((p) => p[inputName] === true);
132
+ });
133
+ const setActive = (isActive) => {
134
+ room.core._reactor.publishPresence(room.type, room.id, {
135
+ [inputName]: isActive ? true : null,
136
+ });
137
+ if (timeoutId) {
138
+ clearTimeout(timeoutId);
139
+ timeoutId = null;
140
+ }
141
+ if (!isActive)
142
+ return;
143
+ if (opts?.timeout === null || opts?.timeout === 0)
144
+ return;
145
+ timeoutId = setTimeout(() => {
146
+ room.core._reactor.publishPresence(room.type, room.id, {
147
+ [inputName]: null,
148
+ });
149
+ }, opts?.timeout ?? defaultActivityStopTimeout);
150
+ };
151
+ $effect(() => {
152
+ return () => {
153
+ if (timeoutId) {
154
+ clearTimeout(timeoutId);
155
+ timeoutId = null;
156
+ }
157
+ setActive(false);
158
+ };
159
+ });
160
+ const onKeyDown = (e) => {
161
+ const isEnter = opts?.stopOnEnter && e.key === 'Enter';
162
+ const isActive = !isEnter;
163
+ setActive(isActive);
164
+ };
165
+ const onBlur = () => {
166
+ setActive(false);
167
+ };
168
+ return {
169
+ get active() {
170
+ return _active;
171
+ },
172
+ setActive,
173
+ inputProps: { onKeyDown, onBlur },
174
+ };
175
+ }
176
+ // --------------
177
+ // Hooks namespace
178
+ export const rooms = {
179
+ useTopicEffect,
180
+ usePublishTopic,
181
+ usePresence,
182
+ useSyncPresence,
183
+ useTypingIndicator,
184
+ };
185
+ // ------------
186
+ // Class
187
+ export class InstantSvelteRoom {
188
+ core;
189
+ type;
190
+ id;
191
+ constructor(core, type, id) {
192
+ this.core = core;
193
+ this.type = type;
194
+ this.id = id;
195
+ }
196
+ }
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import type { InstantSchemaDef } from '@superlayer/core';
3
+ import type { InstantSvelteDatabase } from './InstantSvelteDatabase.svelte.js';
4
+ import type { Snippet } from 'svelte';
5
+
6
+ let { db, children }: {
7
+ db: InstantSvelteDatabase<InstantSchemaDef<any, any, any>>;
8
+ children: Snippet;
9
+ } = $props();
10
+
11
+ // svelte-ignore state_referenced_locally
12
+ const auth = db.useAuth();
13
+ </script>
14
+
15
+ {#if !auth.isLoading && !auth.error && auth.user}
16
+ {@render children()}
17
+ {/if}
@@ -0,0 +1,11 @@
1
+ import type { InstantSchemaDef } from '@superlayer/core';
2
+ import type { InstantSvelteDatabase } from './InstantSvelteDatabase.svelte.js';
3
+ import type { Snippet } from 'svelte';
4
+ type $$ComponentProps = {
5
+ db: InstantSvelteDatabase<InstantSchemaDef<any, any, any>>;
6
+ children: Snippet;
7
+ };
8
+ declare const SignedIn: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type SignedIn = ReturnType<typeof SignedIn>;
10
+ export default SignedIn;
11
+ //# sourceMappingURL=SignedIn.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignedIn.svelte.d.ts","sourceRoot":"","sources":["../src/lib/SignedIn.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAErC,KAAK,gBAAgB,GAAI;IACtB,EAAE,EAAE,qBAAqB,CAAC,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IAC3D,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAkBJ,QAAA,MAAM,QAAQ,sDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import type { InstantSchemaDef } from '@superlayer/core';
3
+ import type { InstantSvelteDatabase } from './InstantSvelteDatabase.svelte.js';
4
+ import type { Snippet } from 'svelte';
5
+
6
+ let { db, children }: {
7
+ db: InstantSvelteDatabase<InstantSchemaDef<any, any, any>>;
8
+ children: Snippet;
9
+ } = $props();
10
+
11
+ // svelte-ignore state_referenced_locally
12
+ const auth = db.useAuth();
13
+ </script>
14
+
15
+ {#if !auth.isLoading && !auth.error && !auth.user}
16
+ {@render children()}
17
+ {/if}
@@ -0,0 +1,11 @@
1
+ import type { InstantSchemaDef } from '@superlayer/core';
2
+ import type { InstantSvelteDatabase } from './InstantSvelteDatabase.svelte.js';
3
+ import type { Snippet } from 'svelte';
4
+ type $$ComponentProps = {
5
+ db: InstantSvelteDatabase<InstantSchemaDef<any, any, any>>;
6
+ children: Snippet;
7
+ };
8
+ declare const SignedOut: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type SignedOut = ReturnType<typeof SignedOut>;
10
+ export default SignedOut;
11
+ //# sourceMappingURL=SignedOut.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignedOut.svelte.d.ts","sourceRoot":"","sources":["../src/lib/SignedOut.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAErC,KAAK,gBAAgB,GAAI;IACtB,EAAE,EAAE,qBAAqB,CAAC,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IAC3D,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAkBJ,QAAA,MAAM,SAAS,sDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}