@nebulr-group/bridge-svelte 0.1.0-beta.1

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/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 thebridgedev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+
package/README.md ADDED
@@ -0,0 +1,61 @@
1
+
2
+ ## @nebulr-group/bridge-svelte
3
+
4
+
5
+ Bridge Svelte library. Add Bridge auth, feature flags, and payments to your SvelteKit 2 + Svelte 5 apps.
6
+
7
+ ### Install
8
+
9
+ ```bash
10
+ npm i @nebulr-group/bridge-svelte
11
+ ```
12
+
13
+ ### Usage
14
+
15
+ See the `demo/` app in the monorepo for end-to-end wiring.
16
+
17
+ ### Build
18
+
19
+
20
+ ```bash
21
+ npm run build
22
+ ```
23
+
24
+ Artifacts are emitted to `dist/` via `svelte-package`.
25
+
26
+ ### Release (branch-protected main)
27
+
28
+ ```bash
29
+ # 1) Create release branch
30
+ git checkout -b release/v0.1.0-beta.1
31
+ git push -u origin release/v0.1.0-beta.1
32
+
33
+ # 2) Open a PR: release/v0.1.0-beta.1 -> main, approve and merge
34
+
35
+ # 3) After merge to main, tag and push
36
+ git checkout main && git pull
37
+ git tag v0.1.0-beta.1
38
+ git push origin v0.1.0-beta.1
39
+
40
+ # 4) Monitor GitHub Actions "Publish to npm"
41
+ ```
42
+
43
+ ### Commit signing (required)
44
+
45
+ Ensure your commits are verified before opening PRs:
46
+
47
+ ```bash
48
+ # Option A: SSH signing (recommended)
49
+ git config --global gpg.format ssh
50
+ git config --global user.signingkey ~/.ssh/id_ed25519.pub
51
+ git config --global commit.gpgsign true
52
+
53
+ # Option B: GPG signing
54
+ gpg --full-generate-key
55
+ gpg --list-secret-keys --keyid-format=long
56
+ git config --global user.signingkey <KEY_ID>
57
+ git config --global commit.gpgsign true
58
+ ```
59
+
60
+ ### License
61
+ MIT © thebridgedev
@@ -0,0 +1,32 @@
1
+ export type PublicRoutePattern = string | RegExp;
2
+ export type FlagRequirement = string | {
3
+ any: string[];
4
+ } | {
5
+ all: string[];
6
+ };
7
+ export type RouteRule = {
8
+ match: string | RegExp;
9
+ public?: boolean;
10
+ featureFlag?: FlagRequirement;
11
+ redirectTo?: string;
12
+ };
13
+ export interface RouteGuardConfig {
14
+ rules: RouteRule[];
15
+ defaultAccess?: 'public' | 'protected';
16
+ }
17
+ export declare function createRouteGuard(): {
18
+ isPublicRoute: (pathname: string) => boolean;
19
+ isProtectedRoute: (pathname: string) => boolean;
20
+ shouldRedirectToLogin: (pathname: string) => boolean;
21
+ checkRouteRestrictions: (pathname: string) => Promise<string | null>;
22
+ getLoginRedirect: () => string;
23
+ getNavigationDecision(pathname: string): Promise<{
24
+ type: "allow";
25
+ } | {
26
+ type: "login";
27
+ loginUrl: string;
28
+ } | {
29
+ type: "redirect";
30
+ to: string;
31
+ }>;
32
+ };
@@ -0,0 +1,100 @@
1
+ import { get } from 'svelte/store';
2
+ import { getRouteGuardConfig } from '../client/stores/config.store.js';
3
+ import { isFeatureEnabled } from '../shared/feature-flag.js';
4
+ import { logger } from '../shared/logger.js';
5
+ import { auth } from '../shared/services/auth.service.js';
6
+ const { isAubridgenticated, createLoginUrl } = auth;
7
+ function escapeRegex(text) {
8
+ return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
9
+ }
10
+ function toRegExp(pattern) {
11
+ if (pattern instanceof RegExp)
12
+ return pattern;
13
+ // Support simple wildcard '*'
14
+ const hasWildcard = pattern.includes('*');
15
+ if (!hasWildcard) {
16
+ return new RegExp(`^${escapeRegex(pattern)}$`);
17
+ }
18
+ const escaped = escapeRegex(pattern).replace(/\\\*/g, '.*');
19
+ return new RegExp(`^${escaped}$`);
20
+ }
21
+ function findMatchingRule(pathname, config) {
22
+ for (const rule of config.rules) {
23
+ if (toRegExp(rule.match).test(pathname)) {
24
+ return rule;
25
+ }
26
+ }
27
+ return null;
28
+ }
29
+ async function evaluateFlagRequirement(req) {
30
+ if (typeof req === 'string') {
31
+ return isFeatureEnabled(req);
32
+ }
33
+ if ('any' in req) {
34
+ const results = await Promise.all(req.any.map((f) => isFeatureEnabled(f)));
35
+ return results.some(Boolean);
36
+ }
37
+ if ('all' in req) {
38
+ const results = await Promise.all(req.all.map((f) => isFeatureEnabled(f)));
39
+ return results.every(Boolean);
40
+ }
41
+ return true;
42
+ }
43
+ export function createRouteGuard() {
44
+ const config = getRouteGuardConfig();
45
+ function isPublicRoute(pathname) {
46
+ const rule = findMatchingRule(pathname, config);
47
+ if (rule) {
48
+ logger.debug(`[route-guard] path ${pathname} is ${rule.public ? 'public' : 'protected'} by bridge rule ${rule.match}`);
49
+ return !!rule.public;
50
+ }
51
+ logger.debug(`[route-guard] path ${pathname} is ${config.defaultAccess === 'public' ? 'public' : 'protected'} by bridge default access ${config.defaultAccess}`);
52
+ const isPublicByDefault = (config.defaultAccess ?? 'protected') === 'public';
53
+ return isPublicByDefault;
54
+ }
55
+ function isProtectedRoute(pathname) {
56
+ return !isPublicRoute(pathname);
57
+ }
58
+ function shouldRedirectToLogin(pathname) {
59
+ const isProtected = isProtectedRoute(pathname);
60
+ const aubridgenticated = get(isAubridgenticated);
61
+ if (isProtectedRoute(pathname) && !get(isAubridgenticated)) {
62
+ logger.debug(`[route-guard] path ${pathname} is protected and user is not aubridgenticated`);
63
+ return true;
64
+ }
65
+ logger.debug(`[route-guard] path ${pathname} is ${isProtected ? 'protected' : 'public'} and user ${aubridgenticated ? 'aubridgenticated' : 'not aubridgenticated'}`);
66
+ return false;
67
+ }
68
+ async function checkRouteRestrictions(pathname) {
69
+ const rule = findMatchingRule(pathname, config);
70
+ if (!rule)
71
+ return null;
72
+ if (rule.featureFlag) {
73
+ const ok = await evaluateFlagRequirement(rule.featureFlag);
74
+ logger.debug(`[route-guard] path ${pathname} is restricted by bridge feature flag ${rule.featureFlag} and flag requirment evaluated to ${ok}`);
75
+ if (!ok)
76
+ return rule.redirectTo ?? '/';
77
+ }
78
+ return null;
79
+ }
80
+ function getLoginRedirect() {
81
+ return createLoginUrl();
82
+ }
83
+ return {
84
+ isPublicRoute,
85
+ isProtectedRoute,
86
+ shouldRedirectToLogin,
87
+ checkRouteRestrictions,
88
+ getLoginRedirect,
89
+ async getNavigationDecision(pathname) {
90
+ if (shouldRedirectToLogin(pathname)) {
91
+ return { type: 'login', loginUrl: getLoginRedirect() };
92
+ }
93
+ const redirectTo = await checkRouteRestrictions(pathname);
94
+ if (redirectTo) {
95
+ return { type: 'redirect', to: redirectTo };
96
+ }
97
+ return { type: 'allow' };
98
+ }
99
+ };
100
+ }
@@ -0,0 +1,3 @@
1
+ import type { RouteGuardConfig } from '../auth/route-guard.js';
2
+ import type { BridgeConfig } from '../shared/types/config.js';
3
+ export declare function bridgeBootstrap(url: URL, config: BridgeConfig | string, routeConfig?: RouteGuardConfig): Promise<void>;
@@ -0,0 +1,49 @@
1
+ // src/lib/bridge/bootstrap.ts
2
+ import { redirect } from '@sveltejs/kit';
3
+ import { createRouteGuard } from '../auth/route-guard.js';
4
+ import { featureFlags } from '../shared/feature-flag.js';
5
+ import { logger } from '../shared/logger.js';
6
+ import { auth, maybeRefreshNow } from '../shared/services/auth.service.js';
7
+ import { bridgeConfig } from './stores/config.store.js';
8
+ export async function bridgeBootstrap(url, config, routeConfig = { rules: [], defaultAccess: 'protected' }) {
9
+ const finalConfig = typeof config === 'string' ? { appId: config } : config;
10
+ // 1. Initialize configuration (synchronously)
11
+ bridgeConfig.initConfig(finalConfig, routeConfig);
12
+ // 1a. If we're on bridge OAuth callback route, let bridge dedicated route handle it
13
+ try {
14
+ const callbackPath = finalConfig.callbackUrl ? new URL(finalConfig.callbackUrl).pathname : null;
15
+ if (callbackPath && url.pathname === callbackPath) {
16
+ logger.debug('[bridgeBootstrap] callback route detected, skipping bootstrap flow');
17
+ const { handleCallback } = auth;
18
+ const code = url.searchParams.get('code');
19
+ if (code) {
20
+ try {
21
+ await handleCallback(code);
22
+ // Redirect bridge user to bridge home page after bridge callback is handled
23
+ window.location.href = '/';
24
+ }
25
+ catch (err) {
26
+ logger.error('Auth callback error:', err);
27
+ }
28
+ }
29
+ }
30
+ }
31
+ catch (e) {
32
+ logger.warn('[bridgeBootstrap] failed parsing callbackUrl', e);
33
+ }
34
+ // 2. Ensure tokens are fresh if needed
35
+ await maybeRefreshNow();
36
+ // 3. Load feature flags before guard if your guard depends on bridgem
37
+ await featureFlags.refresh();
38
+ // 4. Handle route guarding and redirects
39
+ const guard = createRouteGuard();
40
+ const decision = await guard.getNavigationDecision(url.pathname);
41
+ logger.debug('[bridgeBootstrap] navigation decision', decision);
42
+ if (decision.type === 'login') {
43
+ throw redirect(303, auth.createLoginUrl());
44
+ }
45
+ if (decision.type === 'redirect' && url.pathname !== decision.to) {
46
+ throw redirect(303, decision.to);
47
+ }
48
+ logger.debug('[bridgeBootstrap] in bridge end');
49
+ }
@@ -0,0 +1,41 @@
1
+ <script lang="ts">
2
+ import { beforeNavigate } from '$app/navigation';
3
+ import { onMount } from 'svelte';
4
+ import { createRouteGuard } from '../auth/route-guard.js';
5
+ import { auth, startAutoRefresh } from '../shared/services/auth.service.js';
6
+
7
+ // Props: require config to be passed by consumer, now including onBootstrapComplete
8
+ let {onBootstrapComplete }: {
9
+
10
+ onBootstrapComplete?: () => void
11
+ } = $props();
12
+
13
+ const { login } = auth;
14
+ const guard = createRouteGuard();
15
+
16
+ async function handleRoute(pathname: string, cancel?: () => void) {
17
+ const decision = await guard.getNavigationDecision(pathname);
18
+ if (decision.type === 'login') {
19
+ if (cancel) cancel();
20
+ login();
21
+ return;
22
+ }
23
+ if (decision.type === 'redirect' && window.location.pathname !== decision.to) {
24
+ if (cancel) cancel();
25
+ window.location.href = decision.to;
26
+ return;
27
+ }
28
+ }
29
+
30
+ onMount(async () => {
31
+ startAutoRefresh();
32
+
33
+ // Signal completion
34
+ if (onBootstrapComplete) onBootstrapComplete();
35
+ });
36
+
37
+ beforeNavigate(async ({ to, cancel }) => {
38
+ if (!to) return;
39
+ await handleRoute(to.url.pathname, cancel);
40
+ });
41
+ </script>
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onBootstrapComplete?: () => void;
3
+ };
4
+ declare const BridgeBootstrap: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type BridgeBootstrap = ReturnType<typeof BridgeBootstrap>;
6
+ export default BridgeBootstrap;
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { onMount } from 'svelte';
4
+ import { isFeatureEnabled } from '../../shared/feature-flag.js';
5
+
6
+ let { flagName, forceLive = false, negate = false, children }: { flagName: string; forceLive?: boolean; negate?: boolean; children?: Snippet } = $props();
7
+
8
+ let enabled = $state(false);
9
+
10
+ let shouldRender = $derived(() => negate ? !enabled : enabled);
11
+
12
+ onMount(async () => {
13
+ enabled = await isFeatureEnabled(flagName, forceLive);
14
+ });
15
+ </script>
16
+
17
+ {#if shouldRender}
18
+ {#if children}
19
+ {@render children()}
20
+ {/if}
21
+ {/if}
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ flagName: string;
4
+ forceLive?: boolean;
5
+ negate?: boolean;
6
+ children?: Snippet;
7
+ };
8
+ declare const FeatureFlag: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type FeatureFlag = ReturnType<typeof FeatureFlag>;
10
+ export default FeatureFlag;
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import { auth } from '../../../shared/services/auth.service';
3
+ const { isAubridgenticated, login } = auth;
4
+ </script>
5
+
6
+ <button onclick={() => login()} class="login-button">
7
+ Login with Bridge
8
+ </button>
9
+
10
+ <style>
11
+ .login-button {
12
+ display: inline-block;
13
+ padding: 0.5rem 1rem;
14
+ background-color: #3b82f6;
15
+ color: white;
16
+ border-radius: 0.25rem;
17
+ border: none;
18
+ cursor: pointer;
19
+ }
20
+ </style>
@@ -0,0 +1,18 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const Login: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type Login = InstanceType<typeof Login>;
18
+ export default Login;
@@ -0,0 +1,131 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import { logger } from '../../../shared/logger.js';
4
+ import { auth } from '../../../shared/services/auth.service.js';
5
+ import { getConfig } from '../../stores/config.store.js';
6
+
7
+ let iframeUrl: string | null = null;
8
+ let error: string | null = null;
9
+ let isLoading = true;
10
+
11
+ async function getHandoverCode(accessToken: string) {
12
+ const config = getConfig();
13
+ const authBaseUrl = config.authBaseUrl;
14
+ const appId = config.appId;
15
+
16
+ try {
17
+ const response = await fetch(
18
+ `${authBaseUrl}/handover/code/${appId}`,
19
+ {
20
+ method: 'POST',
21
+ headers: {
22
+ 'Content-Type': 'application/json',
23
+ },
24
+ body: JSON.stringify({ accessToken }),
25
+ }
26
+ );
27
+
28
+ if (!response.ok) {
29
+ const errorText = await response.text();
30
+ logger.error(`Failed to get handover code: ${response.status} ${response.statusText}`, errorText);
31
+ throw new Error(`Failed to get handover code: ${response.statusText}`);
32
+ }
33
+
34
+ const data = await response.json();
35
+ if (!data.code) {
36
+ logger.error('No handover code in response:', data);
37
+ throw new Error('Failed to get handover code: No code in response');
38
+ }
39
+
40
+ // Create bridge team management URL with bridge handover code
41
+ const baseUrl = config.teamManagementUrl;
42
+ return `${baseUrl}?code=${data.code}`;
43
+ } catch (err) {
44
+ logger.error('Error getting handover code:', err);
45
+ throw err;
46
+ }
47
+ }
48
+
49
+ onMount(async () => {
50
+ logger.debug('TeamManagement onMount');
51
+ try {
52
+ if (!auth.isAubridgenticated) {
53
+ logger.debug('TeamManagement onMount: User is not aubridgenticated');
54
+ throw new Error('User must be aubridgenticated to access team management');
55
+ }
56
+ const token = auth.getToken();
57
+ // Get bridge access token from your auth store
58
+ const accessToken = token?.accessToken;
59
+ if (!accessToken) {
60
+ throw new Error('No access token available');
61
+ }
62
+
63
+ iframeUrl = await getHandoverCode(accessToken);
64
+ } catch (err) {
65
+ error = err instanceof Error ? err.message : 'Failed to load team management';
66
+ } finally {
67
+ isLoading = false;
68
+ }
69
+ });
70
+ </script>
71
+
72
+ <div class="team-management-container">
73
+ {#if isLoading}
74
+ <div class="loading">Loading team management...</div>
75
+ {:else if error}
76
+ <div class="error">
77
+ <h3>Error</h3>
78
+ <p>{error}</p>
79
+ </div>
80
+ {:else if iframeUrl}
81
+ <iframe
82
+ src={iframeUrl}
83
+ title="Team Management"
84
+ class="team-management-iframe"
85
+ allow="clipboard-read; clipboard-write"
86
+ ></iframe>
87
+ {/if}
88
+ </div>
89
+
90
+ <style>
91
+ .team-management-container {
92
+ width: 100%;
93
+ height: 100%;
94
+ min-height: 600px;
95
+ position: relative;
96
+ }
97
+
98
+ .team-management-iframe {
99
+ width: 100%;
100
+ height: 100%;
101
+ border: none;
102
+ min-height: 600px;
103
+ }
104
+
105
+ .loading {
106
+ display: flex;
107
+ align-items: center;
108
+ justify-content: center;
109
+ height: 100%;
110
+ min-height: 600px;
111
+ color: #4b5563;
112
+ }
113
+
114
+ .error {
115
+ padding: 1rem;
116
+ background-color: #fee2e2;
117
+ border: 1px solid #ef4444;
118
+ border-radius: 0.25rem;
119
+ color: #dc2626;
120
+ margin: 1rem;
121
+ }
122
+
123
+ .error h3 {
124
+ margin: 0 0 0.5rem 0;
125
+ font-size: 1.125rem;
126
+ }
127
+
128
+ .error p {
129
+ margin: 0;
130
+ }
131
+ </style>
@@ -0,0 +1,18 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const TeamManagement: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type TeamManagement = InstanceType<typeof TeamManagement>;
18
+ export default TeamManagement;
@@ -0,0 +1,6 @@
1
+ import type { BridgeConfig } from '../../shared/types/config';
2
+ export declare const bridgeConfig: import("svelte/store").Writable<BridgeConfig | null>;
3
+ export declare const readonlyConfig: import("svelte/store").Readable<BridgeConfig | null>;
4
+ export declare const configReady: import("svelte/store").Readable<boolean>;
5
+ export declare function initConfig(config: BridgeConfig): void;
6
+ export declare function getConfig(): BridgeConfig;
@@ -0,0 +1,60 @@
1
+ // src/lib/config/bridgeConfig.ts
2
+ import { derived, writable } from 'svelte/store';
3
+ import { logger } from '../../shared/logger.js';
4
+ const DEFAULT_CONFIG = {
5
+ authBaseUrl: 'https://auth.nblocks.cloud',
6
+ backendlessBaseUrl: 'https://backendless.nblocks.cloud',
7
+ teamManagementUrl: 'https://backendless.nblocks.cloud/user-management-portal/users',
8
+ defaultRedirectRoute: '/',
9
+ loginRoute: '/login',
10
+ debug: false
11
+ };
12
+ const { subscribe, set, update } = writable({
13
+ config: null,
14
+ routeConfig: null,
15
+ loaded: false
16
+ });
17
+ export const bridgeConfig = {
18
+ subscribe,
19
+ initConfig: (config, routeConfig) => {
20
+ if (!config?.appId) {
21
+ throw new Error('Bridge appId is required but was not provided in bridge configuration.');
22
+ }
23
+ const merged = {
24
+ ...DEFAULT_CONFIG,
25
+ ...config
26
+ };
27
+ // Set bridge full config and mark it as loaded
28
+ set({
29
+ config: merged,
30
+ routeConfig: routeConfig || null,
31
+ loaded: true
32
+ });
33
+ // This will only print when debug is true
34
+ logger.debug('[config] initialized', merged);
35
+ }
36
+ };
37
+ // Convenience derived stores
38
+ export const configReady = derived(bridgeConfig, ($state) => $state.loaded);
39
+ export const readonlyConfig = derived(bridgeConfig, ($state) => $state.config);
40
+ // Synchronous access to latest config value
41
+ let _currentConfig = null;
42
+ let _currentRouteConfig = null;
43
+ bridgeConfig.subscribe(($state) => {
44
+ if ($state.loaded) {
45
+ _currentConfig = $state.config;
46
+ _currentRouteConfig = $state.routeConfig;
47
+ }
48
+ });
49
+ export function getConfig() {
50
+ if (!_currentConfig) {
51
+ throw new Error('Config has not been initialized. Call initConfig(...) early in app startup.');
52
+ }
53
+ return _currentConfig;
54
+ }
55
+ export function getRouteGuardConfig() {
56
+ if (!_currentRouteConfig) {
57
+ throw new Error('RouteGuardConfig has not been initialized. Call initConfig(...) early in app startup.');
58
+ }
59
+ return _currentRouteConfig;
60
+ }
@@ -0,0 +1,10 @@
1
+ export interface Profile {
2
+ username: string;
3
+ email: string;
4
+ }
5
+ export declare const profileState: import("svelte/store").Writable<{
6
+ profile: Profile | null;
7
+ isLoading: boolean;
8
+ error: string | null;
9
+ }>;
10
+ export declare function fetchProfile(): Promise<void>;
@@ -0,0 +1,29 @@
1
+ import { writable } from 'svelte/store';
2
+ import { bridgeConfig } from './config.store.js';
3
+ // Create a writable store for profile state
4
+ export const profileState = writable({
5
+ profile: null,
6
+ isLoading: true,
7
+ error: null
8
+ });
9
+ // Initialize profile when config changes
10
+ bridgeConfig.subscribe((config) => {
11
+ if (config) {
12
+ // TODO: Initialize profile service
13
+ }
14
+ });
15
+ export async function fetchProfile() {
16
+ profileState.update(state => ({ ...state, isLoading: true, error: null }));
17
+ try {
18
+ // TODO: Implement profile fetching
19
+ const profile = null; // Replace with actual profile fetch
20
+ profileState.update(state => ({ ...state, profile }));
21
+ }
22
+ catch (err) {
23
+ const errorMessage = err instanceof Error ? err.message : 'Failed to fetch profile';
24
+ profileState.update(state => ({ ...state, error: errorMessage }));
25
+ }
26
+ finally {
27
+ profileState.update(state => ({ ...state, isLoading: false }));
28
+ }
29
+ }
@@ -0,0 +1,13 @@
1
+ export * from './client/BridgeBootstrap.js';
2
+ export * from './client/stores/config.store.js';
3
+ export * from './client/stores/profile.store.js';
4
+ export { default as BridgeBootstrap, default as BridgeProvider } from './client/BridgeBootstrap.svelte';
5
+ export { default as Login } from './client/components/auth/Login.svelte';
6
+ export { default as FeatureFlag } from './client/components/FeatureFlag.svelte';
7
+ export { default as TeamManagement } from './client/components/team/TeamManagement.svelte';
8
+ export * from './shared/feature-flag.js';
9
+ export * from './auth/route-guard.js';
10
+ export * from './shared/profile.js';
11
+ export * from './shared/services/auth.service.js';
12
+ export * from './shared/types/config.js';
13
+ export { logger } from './shared/logger.js';
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ // Core stores and setup
2
+ export * from './client/BridgeBootstrap.js';
3
+ export * from './client/stores/config.store.js';
4
+ export * from './client/stores/profile.store.js';
5
+ // Components (Svelte components must have `export default`)
6
+ export { default as BridgeBootstrap, default as BridgeProvider } from './client/BridgeBootstrap.svelte';
7
+ export { default as Login } from './client/components/auth/Login.svelte';
8
+ export { default as FeatureFlag } from './client/components/FeatureFlag.svelte';
9
+ export { default as TeamManagement } from './client/components/team/TeamManagement.svelte';
10
+ // Feature flags
11
+ export * from './shared/feature-flag.js';
12
+ // Auth route guards
13
+ export * from './auth/route-guard.js';
14
+ // Types
15
+ export * from './shared/profile.js'; // If this exists
16
+ export * from './shared/services/auth.service.js';
17
+ export * from './shared/types/config.js';
18
+ // Logger
19
+ export { logger } from './shared/logger.js';
@@ -0,0 +1,6 @@
1
+ export declare function loadFeatureFlags(): Promise<void>;
2
+ export declare function isFeatureEnabled(flag: string, forceLive?: boolean): Promise<boolean>;
3
+ export declare const featureFlags: {
4
+ flags: import("svelte/store").Writable<Record<string, boolean>>;
5
+ refresh: typeof loadFeatureFlags;
6
+ };
@@ -0,0 +1,64 @@
1
+ import { get, writable } from 'svelte/store';
2
+ import { getConfig } from '../client/stores/config.store.js';
3
+ import { logger } from './logger.js';
4
+ import { auth } from './services/auth.service.js';
5
+ const cacheValidityMs = 5 * 60 * 1000;
6
+ const cachedFlags = writable({});
7
+ let lastFetchTime = 0;
8
+ export async function loadFeatureFlags() {
9
+ const tokens = get(auth.token);
10
+ const appId = tokens?.appId ?? getConfig().appId;
11
+ const accessToken = tokens?.accessToken;
12
+ const backendlessBaseUrl = getConfig().backendlessBaseUrl;
13
+ if (!appId)
14
+ return;
15
+ const url = `${backendlessBaseUrl}/flags/bulkEvaluate/${appId}`;
16
+ const body = accessToken ? { accessToken } : {};
17
+ const res = await fetch(url, {
18
+ method: 'POST',
19
+ headers: { 'Content-Type': 'application/json' },
20
+ body: JSON.stringify(body)
21
+ });
22
+ if (!res.ok)
23
+ throw new Error('Failed to load feature flags');
24
+ const data = await res.json();
25
+ const flags = data.flags.reduce((acc, { flag, evaluation }) => {
26
+ acc[flag] = evaluation?.enabled ?? false;
27
+ return acc;
28
+ }, {});
29
+ cachedFlags.set(flags);
30
+ lastFetchTime = Date.now();
31
+ }
32
+ export async function isFeatureEnabled(flag, forceLive = false) {
33
+ const tokens = get(auth.token);
34
+ const appId = tokens?.appId ?? getConfig().appId;
35
+ const accessToken = tokens?.accessToken;
36
+ const backendlessBaseUrl = getConfig().backendlessBaseUrl;
37
+ if (!appId)
38
+ return false;
39
+ logger.debug(`[feature-flag] is flag:${flag}: enabled: ${get(cachedFlags)[flag]}`);
40
+ if (!forceLive && Date.now() - lastFetchTime < cacheValidityMs) {
41
+ return get(cachedFlags)[flag] ?? false;
42
+ }
43
+ if (!forceLive)
44
+ await loadFeatureFlags();
45
+ if (forceLive) {
46
+ const url = `${backendlessBaseUrl}/flags/evaluate/${appId}/${flag}`;
47
+ const body = accessToken ? { accessToken } : {};
48
+ const res = await fetch(url, {
49
+ method: 'POST',
50
+ headers: { 'Content-Type': 'application/json' },
51
+ body: JSON.stringify(body)
52
+ });
53
+ if (!res.ok)
54
+ return get(cachedFlags)[flag] ?? false;
55
+ const { enabled } = await res.json();
56
+ cachedFlags.update(f => ({ ...f, [flag]: enabled }));
57
+ return enabled ?? false;
58
+ }
59
+ return get(cachedFlags)[flag] ?? false;
60
+ }
61
+ export const featureFlags = {
62
+ flags: cachedFlags,
63
+ refresh: loadFeatureFlags
64
+ };
@@ -0,0 +1,17 @@
1
+ type LogMethod = (...args: unknown[]) => void;
2
+ export declare const logger: {
3
+ readonly debug: (...args: unknown[]) => void;
4
+ readonly log: (...args: unknown[]) => void;
5
+ readonly info: (...args: unknown[]) => void;
6
+ readonly warn: (...args: unknown[]) => void;
7
+ readonly error: (...args: unknown[]) => void;
8
+ readonly withPrefix: (prefix: string) => {
9
+ readonly debug: (...args: unknown[]) => void;
10
+ readonly log: (...args: unknown[]) => void;
11
+ readonly info: (...args: unknown[]) => void;
12
+ readonly warn: LogMethod;
13
+ readonly error: LogMethod;
14
+ };
15
+ };
16
+ export type Logger = typeof logger;
17
+ export {};
@@ -0,0 +1,38 @@
1
+ import { getConfig } from '../client/stores/config.store.js';
2
+ function createPrefixed(method, prefix) {
3
+ return (...args) => method(prefix, ...args);
4
+ }
5
+ function isDebugEnabled() {
6
+ try {
7
+ const cfg = getConfig();
8
+ return !!cfg.debug;
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
14
+ export const logger = {
15
+ debug: (...args) => {
16
+ if (isDebugEnabled())
17
+ console.log(...args);
18
+ },
19
+ log: (...args) => {
20
+ if (isDebugEnabled())
21
+ console.log(...args);
22
+ },
23
+ info: (...args) => {
24
+ if (isDebugEnabled())
25
+ console.info(...args);
26
+ },
27
+ warn: (...args) => console.warn(...args),
28
+ error: (...args) => console.error(...args),
29
+ withPrefix(prefix) {
30
+ return {
31
+ debug: (...args) => logger.debug(prefix, ...args),
32
+ log: (...args) => logger.log(prefix, ...args),
33
+ info: (...args) => logger.info(prefix, ...args),
34
+ warn: createPrefixed(console.warn, prefix),
35
+ error: createPrefixed(console.error, prefix)
36
+ };
37
+ }
38
+ };
@@ -0,0 +1,47 @@
1
+ export interface IDToken {
2
+ sub: string;
3
+ preferred_username: string;
4
+ email: string;
5
+ email_verified: boolean;
6
+ name: string;
7
+ family_name?: string;
8
+ given_name?: string;
9
+ locale?: string;
10
+ onboarded?: boolean;
11
+ multi_tenant?: boolean;
12
+ tenant_id?: string;
13
+ tenant_name?: string;
14
+ tenant_locale?: string;
15
+ tenant_logo?: string;
16
+ tenant_onboarded?: boolean;
17
+ }
18
+ export interface Profile {
19
+ id: string;
20
+ username: string;
21
+ email: string;
22
+ emailVerified: boolean;
23
+ fullName: string;
24
+ familyName?: string;
25
+ givenName?: string;
26
+ locale?: string;
27
+ onboarded?: boolean;
28
+ multiTenantAccess?: boolean;
29
+ tenant?: {
30
+ id: string;
31
+ name: string;
32
+ locale?: string;
33
+ logo?: string;
34
+ onboarded?: boolean;
35
+ };
36
+ }
37
+ declare function updateProfile(idToken: string | null): Promise<void>;
38
+ export declare const profileStore: {
39
+ profile: import("svelte/store").Writable<Profile | null | undefined>;
40
+ error: import("svelte/store").Writable<string | null>;
41
+ updateProfile: typeof updateProfile;
42
+ isOnboarded: import("svelte/store").Readable<boolean>;
43
+ hasMultiTenantAccess: import("svelte/store").Readable<boolean>;
44
+ getProfile: () => Profile | null | undefined;
45
+ clear: () => void;
46
+ };
47
+ export {};
@@ -0,0 +1,98 @@
1
+ // src/lib/auth/profile.ts
2
+ import { createRemoteJWKSet, errors as joseErrors, jwtVerify } from 'jose';
3
+ import { derived, get, writable } from 'svelte/store';
4
+ import { getConfig } from '../client/stores/config.store.js';
5
+ import { auth } from './services/auth.service.js';
6
+ const profile = writable(undefined);
7
+ const error = writable(null);
8
+ function transformIDToken(payload) {
9
+ return {
10
+ id: payload.sub,
11
+ username: payload.preferred_username,
12
+ email: payload.email,
13
+ emailVerified: payload.email_verified,
14
+ fullName: payload.name,
15
+ familyName: payload.family_name,
16
+ givenName: payload.given_name,
17
+ locale: payload.locale,
18
+ onboarded: payload.onboarded,
19
+ multiTenantAccess: payload.multi_tenant,
20
+ tenant: payload.tenant_id
21
+ ? {
22
+ id: payload.tenant_id,
23
+ name: payload.tenant_name || '',
24
+ locale: payload.tenant_locale,
25
+ logo: payload.tenant_logo,
26
+ onboarded: payload.tenant_onboarded,
27
+ }
28
+ : undefined,
29
+ };
30
+ }
31
+ let jwks = null;
32
+ let expectedIssuer = null;
33
+ let expectedAudience = null;
34
+ function ensureVerifier() {
35
+ const config = getConfig();
36
+ if (!jwks || expectedIssuer !== config.authBaseUrl || expectedAudience !== config.appId) {
37
+ jwks = createRemoteJWKSet(new URL(`${config.authBaseUrl}/.well-known/jwks.json`));
38
+ expectedIssuer = config.authBaseUrl;
39
+ expectedAudience = config.appId;
40
+ }
41
+ }
42
+ async function verifyToken(idToken) {
43
+ try {
44
+ ensureVerifier();
45
+ const { payload } = await jwtVerify(idToken, jwks, {
46
+ issuer: expectedIssuer,
47
+ audience: expectedAudience,
48
+ });
49
+ return transformIDToken(payload);
50
+ }
51
+ catch (err) {
52
+ if (err instanceof joseErrors.JWTExpired) {
53
+ error.set('Token expired');
54
+ }
55
+ else if (err instanceof joseErrors.JWTInvalid) {
56
+ error.set('Invalid token');
57
+ }
58
+ else if (err instanceof joseErrors.JWKSNoMatchingKey) {
59
+ error.set('JWKS error');
60
+ }
61
+ else {
62
+ error.set('Token verification failed');
63
+ }
64
+ profile.set(null);
65
+ return null;
66
+ }
67
+ }
68
+ async function updateProfile(idToken) {
69
+ profile.set(undefined);
70
+ if (!idToken) {
71
+ profile.set(null);
72
+ error.set(null);
73
+ return;
74
+ }
75
+ const result = await verifyToken(idToken);
76
+ profile.set(result);
77
+ if (result)
78
+ error.set(null);
79
+ }
80
+ // 🔁 Auto-sync profile with token
81
+ auth.token.subscribe(($token) => {
82
+ const idToken = $token?.idToken || null;
83
+ updateProfile(idToken);
84
+ });
85
+ const isOnboarded = derived(profile, ($profile) => $profile?.onboarded ?? false);
86
+ const hasMultiTenantAccess = derived(profile, ($profile) => $profile?.multiTenantAccess ?? false);
87
+ export const profileStore = {
88
+ profile,
89
+ error,
90
+ updateProfile,
91
+ isOnboarded,
92
+ hasMultiTenantAccess,
93
+ getProfile: () => get(profile),
94
+ clear: () => {
95
+ profile.set(null);
96
+ error.set(null);
97
+ },
98
+ };
@@ -0,0 +1,24 @@
1
+ import type { TokenSet } from './types/config';
2
+ declare function clearTokens(): void;
3
+ declare function login(options?: {
4
+ redirectUri?: string;
5
+ }): Promise<void>;
6
+ declare function createLoginUrl(options?: {
7
+ redirectUri?: string;
8
+ }): string;
9
+ declare function handleCallback(code: string): Promise<void>;
10
+ declare function refreshToken(refreshToken: string): Promise<TokenSet | null>;
11
+ export declare const auth: {
12
+ token: import("svelte/store").Writable<any>;
13
+ isAubridgenticated: import("svelte/store").Readable<boolean>;
14
+ isLoading: import("svelte/store").Writable<boolean>;
15
+ error: import("svelte/store").Writable<string | null>;
16
+ login: typeof login;
17
+ logout: typeof clearTokens;
18
+ handleCallback: typeof handleCallback;
19
+ refreshToken: typeof refreshToken;
20
+ createLoginUrl: typeof createLoginUrl;
21
+ getToken: () => any;
22
+ };
23
+ export { };
24
+
@@ -0,0 +1,209 @@
1
+ // src/lib/auth.ts
2
+ import { browser } from '$app/environment';
3
+ import { derived, get, writable } from 'svelte/store';
4
+ import { getConfig } from '../../client/stores/config.store.js';
5
+ import { logger } from '../logger.js';
6
+ const TOKEN_KEY = 'bridge_tokens';
7
+ const tokenStore = writable(null);
8
+ const isLoading = writable(true);
9
+ const error = writable(null);
10
+ let refreshInterval = null;
11
+ const REFRESH_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes
12
+ // Load from localStorage on first load
13
+ if (browser) {
14
+ try {
15
+ const raw = localStorage.getItem(TOKEN_KEY);
16
+ if (raw)
17
+ tokenStore.set(JSON.parse(raw));
18
+ }
19
+ catch (e) {
20
+ logger.error('Failed to load tokens from storage', e);
21
+ }
22
+ finally {
23
+ isLoading.set(false);
24
+ }
25
+ }
26
+ // --- API ---
27
+ function setTokens(tokens) {
28
+ tokenStore.set(tokens);
29
+ if (browser)
30
+ localStorage.setItem(TOKEN_KEY, JSON.stringify(tokens));
31
+ scheduleTokenRefresh();
32
+ }
33
+ function clearTokens() {
34
+ tokenStore.set(null);
35
+ if (browser)
36
+ localStorage.removeItem(TOKEN_KEY);
37
+ stopAutoRefresh();
38
+ }
39
+ async function login(options = {}) {
40
+ const loginUrl = createLoginUrl(options);
41
+ if (browser) {
42
+ window.location.href = loginUrl;
43
+ }
44
+ else {
45
+ throw new Error('Login not supported in this environment');
46
+ }
47
+ }
48
+ function createLoginUrl(options = {}) {
49
+ const config = getConfig();
50
+ const redirectUri = options.redirectUri ?? config.callbackUrl;
51
+ const base = `${config.authBaseUrl}/url/login/${config.appId}`;
52
+ return redirectUri ? `${base}?redirect_uri=${encodeURIComponent(redirectUri)}` : base;
53
+ }
54
+ async function handleCallback(code) {
55
+ const config = getConfig();
56
+ const url = `${config.authBaseUrl}/token/code/${config.appId}`;
57
+ const response = await fetch(url, {
58
+ method: 'POST',
59
+ headers: { 'Content-Type': 'application/json' },
60
+ body: JSON.stringify({
61
+ code,
62
+ ...(config.callbackUrl ? { redirect_uri: config.callbackUrl } : {})
63
+ })
64
+ });
65
+ if (!response.ok) {
66
+ let errorMessage = 'Failed to exchange code for tokens';
67
+ try {
68
+ const errorData = await response.json();
69
+ if (errorData && errorData.message) {
70
+ errorMessage = `Failed to exchange code for tokens: ${errorData.message}`;
71
+ }
72
+ else if (errorData && typeof errorData === 'string') {
73
+ errorMessage = `Failed to exchange code for tokens: ${errorData}`;
74
+ }
75
+ }
76
+ catch (e) {
77
+ // If parsing JSON fails, use bridge generic message or response status text
78
+ errorMessage = `Failed to exchange code for tokens: ${response.statusText || 'Unknown error'}`;
79
+ }
80
+ throw new Error(errorMessage);
81
+ }
82
+ const data = await response.json();
83
+ setTokens({
84
+ accessToken: data.access_token,
85
+ refreshToken: data.refresh_token,
86
+ idToken: data.id_token
87
+ });
88
+ }
89
+ async function refreshToken(refreshToken) {
90
+ const config = getConfig();
91
+ try {
92
+ const url = `${config.authBaseUrl}/token`;
93
+ const response = await fetch(url, {
94
+ method: 'POST',
95
+ headers: { 'Content-Type': 'application/json' },
96
+ body: JSON.stringify({
97
+ client_id: config.appId,
98
+ grant_type: 'refresh_token',
99
+ refresh_token: refreshToken
100
+ })
101
+ });
102
+ if (!response.ok)
103
+ return null;
104
+ const data = await response.json();
105
+ const tokens = {
106
+ accessToken: data.access_token,
107
+ refreshToken: data.refresh_token,
108
+ idToken: data.id_token
109
+ };
110
+ setTokens(tokens);
111
+ return tokens;
112
+ }
113
+ catch (e) {
114
+ logger.error('Failed to refresh token', e);
115
+ return null;
116
+ }
117
+ }
118
+ function getTokenExpiry(token) {
119
+ try {
120
+ const payload = JSON.parse(atob(token.split('.')[1]));
121
+ return payload.exp * 1000;
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ }
127
+ function shouldRefreshNow(accessToken) {
128
+ if (!accessToken)
129
+ return false;
130
+ const exp = getTokenExpiry(accessToken);
131
+ if (!exp)
132
+ return false;
133
+ const timeUntilExpiry = exp - Date.now();
134
+ return timeUntilExpiry <= REFRESH_THRESHOLD_MS;
135
+ }
136
+ function scheduleTokenRefresh() {
137
+ if (!browser)
138
+ return;
139
+ const current = get(tokenStore);
140
+ const accessToken = current?.accessToken ?? null;
141
+ const exp = accessToken ? getTokenExpiry(accessToken) : null;
142
+ if (!exp)
143
+ return;
144
+ const timeUntilExpiry = exp - Date.now();
145
+ logger.debug('[auth] timeUntilExpiry and refresh threshold', timeUntilExpiry, REFRESH_THRESHOLD_MS);
146
+ if (shouldRefreshNow(accessToken)) {
147
+ logger.debug('[auth] refreshing now');
148
+ refreshNow();
149
+ }
150
+ else {
151
+ const checkIn = Math.max(timeUntilExpiry - REFRESH_THRESHOLD_MS, 10000);
152
+ refreshInterval = setTimeout(refreshNow, checkIn);
153
+ }
154
+ }
155
+ async function refreshNow() {
156
+ const current = get(tokenStore);
157
+ if (!current?.refreshToken)
158
+ return;
159
+ logger.debug('🔄 Attempting token refresh...');
160
+ const newTokens = await refreshToken(current.refreshToken);
161
+ if (newTokens) {
162
+ logger.debug('✅ Token refreshed');
163
+ setTokens(newTokens);
164
+ }
165
+ else {
166
+ logger.warn('❌ Token refresh failed');
167
+ clearTokens();
168
+ }
169
+ }
170
+ function startAutoRefresh() {
171
+ stopAutoRefresh();
172
+ scheduleTokenRefresh();
173
+ }
174
+ function stopAutoRefresh() {
175
+ if (refreshInterval)
176
+ clearTimeout(refreshInterval);
177
+ refreshInterval = null;
178
+ }
179
+ export async function maybeRefreshNow() {
180
+ const current = get(tokenStore);
181
+ const accessToken = current?.accessToken ?? null;
182
+ const refresh = current?.refreshToken ?? null;
183
+ if (!accessToken || !refresh)
184
+ return !!accessToken;
185
+ if (shouldRefreshNow(accessToken)) {
186
+ const newTokens = await refreshToken(refresh);
187
+ if (newTokens)
188
+ return true;
189
+ clearTokens();
190
+ return false;
191
+ }
192
+ return true;
193
+ }
194
+ // --- Derived values ---
195
+ const isAubridgenticated = derived(tokenStore, $t => !!$t?.accessToken);
196
+ // --- Exports ---
197
+ export const auth = {
198
+ token: tokenStore,
199
+ isAubridgenticated,
200
+ isLoading,
201
+ error,
202
+ login,
203
+ logout: clearTokens,
204
+ handleCallback,
205
+ refreshToken,
206
+ createLoginUrl,
207
+ getToken: () => get(tokenStore)
208
+ };
209
+ export { startAutoRefresh, stopAutoRefresh };
@@ -0,0 +1,47 @@
1
+ export interface BridgeConfig {
2
+ /**
3
+ * Your Bridge application ID
4
+ * @required
5
+ */
6
+ appId: string;
7
+ /**
8
+ * The URL to redirect to after successful login
9
+ * @default The current origin + '/auth/callback'
10
+ */
11
+ callbackUrl?: string;
12
+ /**
13
+ * The base URL for Bridge auth services
14
+ * @default 'https://auth.nblocks.cloud'
15
+ */
16
+ authBaseUrl?: string;
17
+ /**
18
+ * The base URL for Bridge backendless services
19
+ * @default 'https://backendless.nblocks.cloud'
20
+ */
21
+ backendlessBaseUrl?: string;
22
+ /**
23
+ * Route to redirect to after login
24
+ * @default '/'
25
+ */
26
+ defaultRedirectRoute?: string;
27
+ /**
28
+ * Route to redirect to when aubridgentication fails
29
+ * @default '/login'
30
+ */
31
+ loginRoute?: string;
32
+ /**
33
+ * URL for bridge team management portal
34
+ * @default 'https://backendless.nblocks.cloud'
35
+ */
36
+ teamManagementUrl?: string;
37
+ /**
38
+ * Debug mode
39
+ * @default false
40
+ */
41
+ debug?: boolean;
42
+ }
43
+ export interface TokenSet {
44
+ accessToken: string;
45
+ refreshToken: string;
46
+ idToken: string;
47
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@nebulr-group/bridge-svelte",
3
+ "version": "0.1.0-beta.1",
4
+
5
+ "description": "Bridge Svelte library, This library helps you to add bridge authentication and feature flags, and payments to your svelte application.",
6
+ "author": "Iman Pouya",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/thebridgedev/bridge-svelte.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/thebridgedev/bridge-svelte/issues"
14
+ },
15
+ "homepage": "https://github.com/thebridgedev/bridge-svelte#readme",
16
+ "scripts": {
17
+ "dev": "vite dev",
18
+ "build": "bun run prepack",
19
+ "preview": "vite preview",
20
+ "prepare": "svelte-kit sync || echo ''",
21
+ "prepack": "svelte-kit sync && svelte-package && publint",
22
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
23
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
24
+ "package": "bun run prepack && bun pm pack --destination ../"
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "sideEffects": [
32
+ "**/*.css"
33
+ ],
34
+ "type": "module",
35
+ "svelte": "./dist/index.js",
36
+ "types": "./dist/index.d.ts",
37
+ "exports": {
38
+ ".": {
39
+ "types": "./dist/index.d.ts",
40
+ "svelte": "./dist/index.js",
41
+ "import": "./dist/index.js"
42
+ }
43
+ },
44
+ "dependencies": {
45
+ "jose": "^6.0.10"
46
+ },
47
+ "peerDependencies": {
48
+ "svelte": "^5.0.0",
49
+ "@sveltejs/kit": "^2.0.0"
50
+ },
51
+ "devDependencies": {
52
+ "@sveltejs/kit": "^2.16.0",
53
+ "@sveltejs/package": "^2.0.0",
54
+ "@sveltejs/vite-plugin-svelte": "^5.0.0",
55
+ "publint": "^0.3.2",
56
+ "svelte": "^5.0.0",
57
+ "svelte-check": "^4.0.0",
58
+ "typescript": "^5.0.0",
59
+ "vite": "^6.2.6"
60
+ },
61
+ "keywords": ["svelte", "bridge", "nebulr", "auth", "feature-flags","payments","stripe","saas control center"]
62
+ }
63
+