@mr.dj2u/library-registry 0.2.0 → 0.3.0
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 +3 -0
- package/assets/mds/env/convex.env.example +1 -0
- package/assets/mds/env/firebase.env.example +6 -0
- package/assets/mds/env/supabase.env.example +3 -0
- package/assets/mds/project/auth-base.md +17 -0
- package/assets/mds/project/auth-convex.md +33 -0
- package/assets/mds/project/auth-firebase.md +22 -0
- package/assets/mds/project/auth-supabase.md +46 -0
- package/assets/mds/src/app/(auth)/reset-password.tsx +5 -0
- package/assets/mds/src/app/(auth)/sign-in.tsx +5 -0
- package/assets/mds/src/app/(auth)/sign-up.tsx +5 -0
- package/assets/mds/src/app/legal/updates.tsx +1 -0
- package/assets/mds/src/app/onboarding/complete.tsx +2 -0
- package/assets/mds/src/app/onboarding/features.tsx +1 -0
- package/assets/mds/src/app/onboarding/legal.tsx +2 -0
- package/assets/mds/src/app/onboarding.tsx +1 -1
- package/assets/mds/src/app/settings.tsx +10 -1
- package/assets/mds/src/db/adapter.ts +195 -0
- package/assets/mds/src/db/firebase.ts +77 -0
- package/assets/mds/src/db/index.firebase.ts +19 -0
- package/assets/mds/src/db/index.supabase.ts +22 -0
- package/assets/mds/src/db/supabase.ts +337 -0
- package/assets/mds/src/features/auth/adapters/base-auth-adapter.tsx +82 -0
- package/assets/mds/src/features/auth/adapters/convex-auth-adapter.tsx +156 -0
- package/assets/mds/src/features/auth/adapters/firebase-auth-adapter.tsx +147 -0
- package/assets/mds/src/features/auth/adapters/supabase-auth-adapter.tsx +166 -0
- package/assets/mds/src/features/auth/auth-guard-logic.ts +19 -0
- package/assets/mds/src/features/auth/auth-guard.tsx +77 -0
- package/assets/mds/src/features/auth/auth-provider.tsx +60 -0
- package/assets/mds/src/features/auth/auth-screen.tsx +265 -0
- package/assets/mds/src/features/auth/auth-types.ts +45 -0
- package/assets/mds/src/features/exposition/data-screen.tsx +4 -2
- package/assets/mds/src/features/exposition/expo-sdk-56-screen.tsx +119 -76
- package/assets/mds/src/features/exposition/exposition-screen.tsx +10 -11
- package/assets/mds/src/features/exposition/stylist-screen.tsx +62 -11
- package/assets/mds/src/features/legal/legal-acceptance-adapter.ts +190 -0
- package/assets/mds/src/features/legal/legal-acceptance-config.ts +79 -0
- package/assets/mds/src/features/legal/legal-agreement-screen.tsx +9 -4
- package/assets/mds/src/features/legal/legal-document-modal.tsx +7 -3
- package/assets/mds/src/features/legal/legal-document-view.tsx +1 -0
- package/assets/mds/src/features/legal/legal-documents.ts +17 -3
- package/assets/mds/src/features/legal/legal-update-screen.tsx +297 -0
- package/assets/mds/src/features/legal/use-legal-acceptance.ts +1 -38
- package/assets/mds/src/features/onboarding/complete-screen.tsx +138 -0
- package/assets/mds/src/features/onboarding/features-screen.tsx +129 -0
- package/assets/mds/src/features/onboarding/legal-review-screen.tsx +242 -0
- package/assets/mds/src/features/onboarding/onboarding-config-with-legal.ts +104 -0
- package/assets/mds/src/features/onboarding/onboarding-config.ts +104 -0
- package/assets/mds/src/features/onboarding/onboarding-persistence-sync.tsx +1 -0
- package/assets/mds/src/features/onboarding/welcome-screen.tsx +130 -0
- package/assets/mds/src/features/onboarding-state/adapters/memory-onboarding-state-adapter.ts +15 -0
- package/assets/mds/src/features/onboarding-state/adapters/supabase-onboarding-state-adapter.ts +45 -0
- package/assets/mds/src/features/onboarding-state/adapters/zustand-onboarding-state-adapter.ts +16 -0
- package/assets/mds/src/features/onboarding-state/adapters/zustand-supabase-onboarding-state-adapter.ts +53 -0
- package/assets/mds/src/features/onboarding-state/onboarding-state-core.ts +108 -0
- package/assets/mds/src/features/onboarding-state/onboarding-state-memory.ts +106 -0
- package/assets/mds/src/features/onboarding-state/onboarding-state-supabase.ts +246 -0
- package/assets/mds/src/features/onboarding-state/onboarding-state-types.ts +64 -0
- package/assets/mds/src/features/onboarding-state/onboarding-state-zustand-supabase.ts +120 -0
- package/assets/mds/src/features/onboarding-state/onboarding-state-zustand.ts +71 -0
- package/assets/mds/src/features/onboarding-state/onboarding-state.ts +22 -0
- package/assets/mds/src/features/onboarding-state/onboarding-store.ts +39 -0
- package/assets/mds/src/features/settings/settings-screen-logic.ts +122 -0
- package/assets/mds/src/features/settings/settings-screen.tsx +274 -28
- package/assets/mds/src/services/convex.ts +40 -0
- package/assets/mds/src/services/firebase.ts +70 -0
- package/assets/mds/src/services/supabase.ts +50 -0
- package/assets/mds/src/theme/color-utils.ts +42 -0
- package/assets/mds/src/theme/provider.tsx +15 -5
- package/assets/mds/src/types/database.ts +13 -0
- package/assets/mds/supabase/migrations/0001_mds_auth_onboarding.sql +107 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +444 -29
- package/dist/catalog.js.map +1 -1
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/assets/mds/src/app/onboarding/account-setup.tsx +0 -1
- package/assets/mds/src/app/onboarding/agreement.tsx +0 -1
- package/assets/mds/src/app/onboarding/terms.tsx +0 -1
- package/assets/mds/src/features/onboarding/account-setup-screen.tsx +0 -54
- package/assets/mds/src/features/onboarding/agreement-screen.tsx +0 -6
- package/assets/mds/src/features/onboarding/components/legal-document-view.tsx +0 -103
- package/assets/mds/src/features/onboarding/legal-documents.ts +0 -80
- package/assets/mds/src/features/onboarding/onboarding-screen.tsx +0 -167
- package/assets/mds/src/features/onboarding/terms-screen.tsx +0 -6
package/README.md
CHANGED
|
@@ -40,3 +40,6 @@ explicitly declared in an asset's `contentTokens` metadata. V1 declares only
|
|
|
40
40
|
|
|
41
41
|
The registry does not track installation history and does not implement
|
|
42
42
|
authentication. See `THIRD_PARTY_NOTICES.md` for snapshot provenance.
|
|
43
|
+
|
|
44
|
+
See `docs/database-adapter.md` for the generated app database adapter contract
|
|
45
|
+
and provider variant expectations.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
EXPO_PUBLIC_CONVEX_URL=
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Auth Setup
|
|
2
|
+
|
|
3
|
+
This app includes the provider-neutral MDS auth shell.
|
|
4
|
+
|
|
5
|
+
## What Was Added
|
|
6
|
+
|
|
7
|
+
- Sign-in, sign-up, and password reset routes under the auth route group.
|
|
8
|
+
- Shared auth form UI in `src/features/auth`.
|
|
9
|
+
- `AuthProvider` and `useAuth`.
|
|
10
|
+
- A base in-memory adapter at `src/features/auth/auth-adapter.ts`.
|
|
11
|
+
|
|
12
|
+
## Before Production
|
|
13
|
+
|
|
14
|
+
- Replace the base adapter with a real auth provider.
|
|
15
|
+
- Persist sessions outside process memory.
|
|
16
|
+
- Guard protected app routes in the root Expo Router layout.
|
|
17
|
+
- Store legal acceptance and onboarding completion in user-scoped storage when those flows matter for compliance.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Convex Auth Setup
|
|
2
|
+
|
|
3
|
+
This app includes the MDS auth shell wired to Convex React Native and Convex Auth. This variant is experimental.
|
|
4
|
+
|
|
5
|
+
## Environment
|
|
6
|
+
|
|
7
|
+
Install the generated app dependencies, then initialize Convex:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx convex dev
|
|
11
|
+
npx @convex-dev/auth
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Set the public Convex deployment URL for the Expo app:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
EXPO_PUBLIC_CONVEX_URL=
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Password Provider Snippet
|
|
21
|
+
|
|
22
|
+
After Convex generates its `convex/` folder and `_generated` files, configure password auth in the Convex backend:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { convexAuth } from '@convex-dev/auth/server';
|
|
26
|
+
import { Password } from '@convex-dev/auth/providers/Password';
|
|
27
|
+
|
|
28
|
+
export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
|
|
29
|
+
providers: [Password],
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Convex Auth is beta. Confirm the current Convex Auth setup docs before treating this path as production infrastructure.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Firebase Auth Setup
|
|
2
|
+
|
|
3
|
+
This app includes the MDS auth shell wired to Firebase Authentication through the Firebase JS SDK.
|
|
4
|
+
|
|
5
|
+
## Environment
|
|
6
|
+
|
|
7
|
+
Register a web app in Firebase, enable Email/Password authentication, then set:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
EXPO_PUBLIC_FIREBASE_API_KEY=
|
|
11
|
+
EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN=
|
|
12
|
+
EXPO_PUBLIC_FIREBASE_PROJECT_ID=
|
|
13
|
+
EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET=
|
|
14
|
+
EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
|
|
15
|
+
EXPO_PUBLIC_FIREBASE_APP_ID=
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Notes
|
|
19
|
+
|
|
20
|
+
- This variant uses the Firebase JS SDK so it works with Expo Go and universal Expo apps.
|
|
21
|
+
- React Native Firebase requires native code and a development build, so it is intentionally outside this source-copy variant.
|
|
22
|
+
- Legal acceptance and onboarding completion default to local app state in this branch. Add Firestore or another backend before using Firebase-authenticated legal audit trails in production.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Supabase Auth Setup
|
|
2
|
+
|
|
3
|
+
This app includes the MDS auth shell wired to Supabase Auth.
|
|
4
|
+
|
|
5
|
+
## Environment
|
|
6
|
+
|
|
7
|
+
The scaffold includes `.env.example` with the required client-safe Supabase variables. Copy it to `.env.local` and fill in your values before running the app:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
EXPO_PUBLIC_SUPABASE_URL=
|
|
11
|
+
EXPO_PUBLIC_SUPABASE_PUBLISHABLE_KEY=
|
|
12
|
+
EXPO_PUBLIC_SUPABASE_KEY=
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`EXPO_PUBLIC_SUPABASE_KEY` and `EXPO_PUBLIC_SUPABASE_ANON_KEY` are still accepted as fallbacks for older projects.
|
|
16
|
+
|
|
17
|
+
## Database
|
|
18
|
+
|
|
19
|
+
Apply `supabase/migrations/0001_mds_auth_onboarding.sql` to the Supabase project before relying on onboarding or legal acceptance persistence.
|
|
20
|
+
|
|
21
|
+
Onboarding completion and legal acceptance are written through MDS adapters, not by the auth screens. When Zustand is also selected, Zustand is only a local cache and pending queue. Supabase `user_onboarding_state` and `user_legal_acceptances` remain the source of truth. Legal rows are insert-only and scoped to `auth.uid()`.
|
|
22
|
+
|
|
23
|
+
Do not treat pre-auth local legal acceptance as a hosted audit record. Hosted apps should write legal acceptance after sign-in, typically through `/legal/updates`.
|
|
24
|
+
|
|
25
|
+
The Expo client env values are not enough to run migrations. To apply SQL, use one of these:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Linked Supabase project. Requires a Supabase CLI login/access token.
|
|
29
|
+
npx supabase link --project-ref <project-ref>
|
|
30
|
+
npx supabase migration up --linked
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Direct database connection. Requires the Postgres connection string/password from Supabase.
|
|
35
|
+
npx supabase migration up --db-url "<postgres-connection-string>"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Use separate Supabase projects for test/staging and production. Never put service-role keys, database passwords, or other Supabase secrets in Expo client code.
|
|
39
|
+
|
|
40
|
+
`EXPO_PUBLIC_SUPABASE_URL` and `EXPO_PUBLIC_SUPABASE_PUBLISHABLE_KEY` are client-visible configuration values, not database admin credentials. Generated apps include a populated, ignored `.env.local` for the smoke Supabase project plus a blank tracked `.env.example` template for replacing those values when you move to another backend project.
|
|
41
|
+
|
|
42
|
+
## Notes
|
|
43
|
+
|
|
44
|
+
- New Supabase projects usually require email confirmation before the first session appears.
|
|
45
|
+
- OAuth and magic-link auth need app deep links and are left as follow-up integration work.
|
|
46
|
+
- Row level security is the production boundary for client-visible Supabase tables.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@/features/legal/legal-update-screen';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@/features/onboarding/features-screen';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default } from '@/features/onboarding/
|
|
1
|
+
export { default } from '@/features/onboarding/welcome-screen';
|
|
@@ -1 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import SettingsScreen, { createPlaceholderAuthAdapter } from '@/features/settings/settings-screen';
|
|
2
|
+
|
|
3
|
+
export default function SettingsRoute() {
|
|
4
|
+
return (
|
|
5
|
+
<SettingsScreen
|
|
6
|
+
auth={createPlaceholderAuthAdapter()}
|
|
7
|
+
legalUrls={{ terms: '/terms', privacy: '/privacy' }}
|
|
8
|
+
/>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
export type DatabaseTableRow = Record<string, unknown>;
|
|
2
|
+
export type DatabaseSchema = Record<string, DatabaseTableRow>;
|
|
3
|
+
export type DatabaseTableName<Schema extends DatabaseSchema> = Extract<keyof Schema, string>;
|
|
4
|
+
export type DatabaseRow<
|
|
5
|
+
Schema extends DatabaseSchema,
|
|
6
|
+
Table extends DatabaseTableName<Schema>,
|
|
7
|
+
> = Schema[Table];
|
|
8
|
+
export type DatabaseInsert<
|
|
9
|
+
Schema extends DatabaseSchema,
|
|
10
|
+
Table extends DatabaseTableName<Schema>,
|
|
11
|
+
> = Partial<DatabaseRow<Schema, Table>>;
|
|
12
|
+
export type DatabaseUpdate<
|
|
13
|
+
Schema extends DatabaseSchema,
|
|
14
|
+
Table extends DatabaseTableName<Schema>,
|
|
15
|
+
> = Partial<DatabaseRow<Schema, Table>>;
|
|
16
|
+
|
|
17
|
+
export type DatabaseFilterOperator =
|
|
18
|
+
| 'eq'
|
|
19
|
+
| 'neq'
|
|
20
|
+
| 'gt'
|
|
21
|
+
| 'gte'
|
|
22
|
+
| 'lt'
|
|
23
|
+
| 'lte'
|
|
24
|
+
| 'in'
|
|
25
|
+
| 'like'
|
|
26
|
+
| 'ilike'
|
|
27
|
+
| 'is';
|
|
28
|
+
|
|
29
|
+
export type DatabaseErrorCode =
|
|
30
|
+
| 'not_found'
|
|
31
|
+
| 'conflict'
|
|
32
|
+
| 'timeout'
|
|
33
|
+
| 'unauthorized'
|
|
34
|
+
| 'validation'
|
|
35
|
+
| 'unsupported'
|
|
36
|
+
| 'unknown';
|
|
37
|
+
|
|
38
|
+
export type DatabaseMutationType = 'insert' | 'update' | 'upsert' | 'delete';
|
|
39
|
+
export type DatabaseChangeType = 'insert' | 'update' | 'delete' | '*';
|
|
40
|
+
|
|
41
|
+
export interface DatabaseCapabilities {
|
|
42
|
+
transactions: boolean;
|
|
43
|
+
subscriptions: boolean;
|
|
44
|
+
rls?: boolean;
|
|
45
|
+
authIntegration?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface DatabaseAdapterMetadata {
|
|
49
|
+
name: string;
|
|
50
|
+
version: string;
|
|
51
|
+
capabilities: DatabaseCapabilities;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface DatabaseFilter<Row extends DatabaseTableRow> {
|
|
55
|
+
column: Extract<keyof Row, string>;
|
|
56
|
+
operator?: DatabaseFilterOperator;
|
|
57
|
+
value: unknown;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface DatabaseQueryInput<
|
|
61
|
+
Schema extends DatabaseSchema,
|
|
62
|
+
Table extends DatabaseTableName<Schema>,
|
|
63
|
+
> {
|
|
64
|
+
table: Table;
|
|
65
|
+
select?: string | readonly Extract<keyof DatabaseRow<Schema, Table>, string>[];
|
|
66
|
+
filters?: readonly DatabaseFilter<DatabaseRow<Schema, Table>>[];
|
|
67
|
+
limit?: number;
|
|
68
|
+
orderBy?: {
|
|
69
|
+
column: Extract<keyof DatabaseRow<Schema, Table>, string>;
|
|
70
|
+
ascending?: boolean;
|
|
71
|
+
};
|
|
72
|
+
single?: boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface DatabaseMutationInput<
|
|
76
|
+
Schema extends DatabaseSchema,
|
|
77
|
+
Table extends DatabaseTableName<Schema>,
|
|
78
|
+
> {
|
|
79
|
+
table: Table;
|
|
80
|
+
type: DatabaseMutationType;
|
|
81
|
+
values?: DatabaseInsert<Schema, Table> | readonly DatabaseInsert<Schema, Table>[];
|
|
82
|
+
filters?: readonly DatabaseFilter<DatabaseRow<Schema, Table>>[];
|
|
83
|
+
select?: string | readonly Extract<keyof DatabaseRow<Schema, Table>, string>[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface DatabaseMutationResult<Row extends DatabaseTableRow> {
|
|
87
|
+
rows: Row[];
|
|
88
|
+
count: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface DatabaseSubscribeInput<
|
|
92
|
+
Schema extends DatabaseSchema,
|
|
93
|
+
Table extends DatabaseTableName<Schema>,
|
|
94
|
+
> {
|
|
95
|
+
table: Table;
|
|
96
|
+
event?: DatabaseChangeType;
|
|
97
|
+
filters?: readonly DatabaseFilter<DatabaseRow<Schema, Table>>[];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface DatabaseChangeEvent<Row extends DatabaseTableRow> {
|
|
101
|
+
type: Exclude<DatabaseChangeType, '*'>;
|
|
102
|
+
table: string;
|
|
103
|
+
row: Row | null;
|
|
104
|
+
oldRow?: Partial<Row> | null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export type DatabaseUnsubscribe = () => void;
|
|
108
|
+
|
|
109
|
+
export interface DatabaseAdapter<Schema extends DatabaseSchema = DatabaseSchema>
|
|
110
|
+
extends DatabaseAdapterMetadata {
|
|
111
|
+
query<Table extends DatabaseTableName<Schema>>(
|
|
112
|
+
input: DatabaseQueryInput<Schema, Table>,
|
|
113
|
+
): Promise<DatabaseRow<Schema, Table>[]>;
|
|
114
|
+
mutate<Table extends DatabaseTableName<Schema>>(
|
|
115
|
+
input: DatabaseMutationInput<Schema, Table>,
|
|
116
|
+
): Promise<DatabaseMutationResult<DatabaseRow<Schema, Table>>>;
|
|
117
|
+
transaction<Result>(fn: (client: DatabaseAdapter<Schema>) => Promise<Result>): Promise<Result>;
|
|
118
|
+
subscribe<Table extends DatabaseTableName<Schema>>(
|
|
119
|
+
input: DatabaseSubscribeInput<Schema, Table>,
|
|
120
|
+
onChange: (event: DatabaseChangeEvent<DatabaseRow<Schema, Table>>) => void,
|
|
121
|
+
onError?: (error: DatabaseAdapterError) => void,
|
|
122
|
+
): DatabaseUnsubscribe;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface DatabaseAdapterErrorOptions {
|
|
126
|
+
cause?: unknown;
|
|
127
|
+
providerCode?: string;
|
|
128
|
+
retryable?: boolean;
|
|
129
|
+
table?: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export class DatabaseAdapterError extends Error {
|
|
133
|
+
readonly code: DatabaseErrorCode;
|
|
134
|
+
readonly providerCode?: string;
|
|
135
|
+
readonly retryable: boolean;
|
|
136
|
+
readonly table?: string;
|
|
137
|
+
|
|
138
|
+
constructor(code: DatabaseErrorCode, message: string, options: DatabaseAdapterErrorOptions = {}) {
|
|
139
|
+
super(message);
|
|
140
|
+
this.name = 'DatabaseAdapterError';
|
|
141
|
+
this.code = code;
|
|
142
|
+
this.providerCode = options.providerCode;
|
|
143
|
+
this.retryable = options.retryable ?? code === 'timeout';
|
|
144
|
+
this.table = options.table;
|
|
145
|
+
if (options.cause !== undefined) {
|
|
146
|
+
this.cause = options.cause;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export class DatabaseNotFoundError extends DatabaseAdapterError {
|
|
152
|
+
constructor(message = 'Database row was not found.', options?: DatabaseAdapterErrorOptions) {
|
|
153
|
+
super('not_found', message, options);
|
|
154
|
+
this.name = 'DatabaseNotFoundError';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export class DatabaseConflictError extends DatabaseAdapterError {
|
|
159
|
+
constructor(message = 'Database write conflicted with existing data.', options?: DatabaseAdapterErrorOptions) {
|
|
160
|
+
super('conflict', message, options);
|
|
161
|
+
this.name = 'DatabaseConflictError';
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export class DatabaseTimeoutError extends DatabaseAdapterError {
|
|
166
|
+
constructor(message = 'Database operation timed out.', options?: DatabaseAdapterErrorOptions) {
|
|
167
|
+
super('timeout', message, { ...options, retryable: true });
|
|
168
|
+
this.name = 'DatabaseTimeoutError';
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export class DatabaseUnauthorizedError extends DatabaseAdapterError {
|
|
173
|
+
constructor(message = 'Database operation is not authorized.', options?: DatabaseAdapterErrorOptions) {
|
|
174
|
+
super('unauthorized', message, options);
|
|
175
|
+
this.name = 'DatabaseUnauthorizedError';
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export class DatabaseValidationError extends DatabaseAdapterError {
|
|
180
|
+
constructor(message = 'Database operation input is invalid.', options?: DatabaseAdapterErrorOptions) {
|
|
181
|
+
super('validation', message, options);
|
|
182
|
+
this.name = 'DatabaseValidationError';
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export class DatabaseUnsupportedError extends DatabaseAdapterError {
|
|
187
|
+
constructor(message = 'Database operation is not supported by this adapter.', options?: DatabaseAdapterErrorOptions) {
|
|
188
|
+
super('unsupported', message, options);
|
|
189
|
+
this.name = 'DatabaseUnsupportedError';
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function isDatabaseAdapterError(error: unknown): error is DatabaseAdapterError {
|
|
194
|
+
return error instanceof DatabaseAdapterError;
|
|
195
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DatabaseAdapterError,
|
|
3
|
+
DatabaseUnsupportedError,
|
|
4
|
+
type DatabaseAdapter,
|
|
5
|
+
type DatabaseChangeEvent,
|
|
6
|
+
type DatabaseMutationInput,
|
|
7
|
+
type DatabaseMutationResult,
|
|
8
|
+
type DatabaseQueryInput,
|
|
9
|
+
type DatabaseSchema,
|
|
10
|
+
type DatabaseSubscribeInput,
|
|
11
|
+
type DatabaseTableName,
|
|
12
|
+
} from './adapter';
|
|
13
|
+
|
|
14
|
+
export type FirebaseDatabaseClient = unknown;
|
|
15
|
+
export type FirebaseDatabaseClientFactory = () => FirebaseDatabaseClient;
|
|
16
|
+
|
|
17
|
+
export interface FirebaseDatabaseAdapterOptions {
|
|
18
|
+
version?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function createFirebaseDatabaseAdapter<Schema extends DatabaseSchema>(
|
|
22
|
+
getClient: FirebaseDatabaseClientFactory,
|
|
23
|
+
options: FirebaseDatabaseAdapterOptions = {},
|
|
24
|
+
): DatabaseAdapter<Schema> {
|
|
25
|
+
void getClient;
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
name: 'firebase',
|
|
29
|
+
version: options.version ?? '0.1.0',
|
|
30
|
+
capabilities: {
|
|
31
|
+
transactions: false,
|
|
32
|
+
subscriptions: false,
|
|
33
|
+
rls: false,
|
|
34
|
+
authIntegration: false,
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
async query<Table extends DatabaseTableName<Schema>>(
|
|
38
|
+
input: DatabaseQueryInput<Schema, Table>,
|
|
39
|
+
): Promise<Schema[Table][]> {
|
|
40
|
+
void input;
|
|
41
|
+
throw new DatabaseUnsupportedError(
|
|
42
|
+
'Firebase database reads are a generated skeleton. Implement Firestore collection/query mapping for this app schema before use.',
|
|
43
|
+
);
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
async mutate<Table extends DatabaseTableName<Schema>>(
|
|
47
|
+
input: DatabaseMutationInput<Schema, Table>,
|
|
48
|
+
): Promise<DatabaseMutationResult<Schema[Table]>> {
|
|
49
|
+
void input;
|
|
50
|
+
throw new DatabaseUnsupportedError(
|
|
51
|
+
'Firebase database writes are a generated skeleton. Implement addDoc, setDoc, updateDoc, or deleteDoc mapping for this app schema before use.',
|
|
52
|
+
);
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
async transaction<Result>(fn: (client: DatabaseAdapter<Schema>) => Promise<Result>): Promise<Result> {
|
|
56
|
+
void fn;
|
|
57
|
+
throw new DatabaseUnsupportedError(
|
|
58
|
+
'Firebase transaction support is not wired in this generated skeleton. Use runTransaction after defining document paths for your schema.',
|
|
59
|
+
);
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
subscribe<Table extends DatabaseTableName<Schema>>(
|
|
63
|
+
input: DatabaseSubscribeInput<Schema, Table>,
|
|
64
|
+
onChange: (event: DatabaseChangeEvent<Schema[Table]>) => void,
|
|
65
|
+
onError?: (error: DatabaseAdapterError) => void,
|
|
66
|
+
) {
|
|
67
|
+
void input;
|
|
68
|
+
void onChange;
|
|
69
|
+
const error = new DatabaseUnsupportedError(
|
|
70
|
+
'Firebase subscriptions are a generated skeleton. Wire Firestore onSnapshot once collection paths and auth rules are defined.',
|
|
71
|
+
);
|
|
72
|
+
onError?.(error);
|
|
73
|
+
return () => {};
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getFirebaseDb } from '../services/firebase';
|
|
2
|
+
import { createFirebaseDatabaseAdapter } from './firebase';
|
|
3
|
+
|
|
4
|
+
import type { DatabaseAdapter } from './adapter';
|
|
5
|
+
import type { AppDatabase } from '../types/database';
|
|
6
|
+
|
|
7
|
+
let adapter: DatabaseAdapter<AppDatabase> | null = null;
|
|
8
|
+
|
|
9
|
+
export function getAdapter(type: 'firebase' = 'firebase'): DatabaseAdapter<AppDatabase> {
|
|
10
|
+
if (type !== 'firebase') {
|
|
11
|
+
throw new Error(`Unsupported database adapter: ${type}`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
adapter ??= createFirebaseDatabaseAdapter<AppDatabase>(() => getFirebaseDb());
|
|
15
|
+
return adapter;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const database = getAdapter;
|
|
19
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { getSupabaseClient } from '../services/supabase';
|
|
2
|
+
import { createSupabaseDatabaseAdapter } from './supabase';
|
|
3
|
+
|
|
4
|
+
import type { DatabaseAdapter } from './adapter';
|
|
5
|
+
import type { AppDatabase } from '../types/database';
|
|
6
|
+
import type { SupabaseDatabaseClientFactory } from './supabase';
|
|
7
|
+
|
|
8
|
+
let adapter: DatabaseAdapter<AppDatabase> | null = null;
|
|
9
|
+
|
|
10
|
+
export function getAdapter(type: 'supabase' = 'supabase'): DatabaseAdapter<AppDatabase> {
|
|
11
|
+
if (type !== 'supabase') {
|
|
12
|
+
throw new Error(`Unsupported database adapter: ${type}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
adapter ??= createSupabaseDatabaseAdapter<AppDatabase>(
|
|
16
|
+
() => getSupabaseClient() as unknown as ReturnType<SupabaseDatabaseClientFactory>,
|
|
17
|
+
);
|
|
18
|
+
return adapter;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const database = getAdapter;
|
|
22
|
+
|