@_nazmiforreal/flutter-ota 0.1.0 → 0.1.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/bin/flutter-patcher.js +37 -39
- package/dart-src/packages/cli-tools/.dart_tool/package_config.json +496 -0
- package/dart-src/packages/cli-tools/.dart_tool/package_graph.json +743 -0
- package/dart-src/packages/cli-tools/.gitkeep +0 -0
- package/dart-src/packages/cli-tools/bin/flutter_patcher.dart +9 -0
- package/dart-src/packages/cli-tools/lib/flutter_patcher_cli.dart +73 -0
- package/dart-src/packages/cli-tools/lib/src/backend.dart +162 -0
- package/dart-src/packages/cli-tools/lib/src/cli_base.dart +56 -0
- package/dart-src/packages/cli-tools/lib/src/commands/build.dart +99 -0
- package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +151 -0
- package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +140 -0
- package/dart-src/packages/cli-tools/lib/src/commands/config_command.dart +113 -0
- package/dart-src/packages/cli-tools/lib/src/commands/console.dart +55 -0
- package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +95 -0
- package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +48 -0
- package/dart-src/packages/cli-tools/lib/src/commands/fingerprint.dart +29 -0
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +82 -0
- package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +55 -0
- package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +132 -0
- package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +41 -0
- package/dart-src/packages/cli-tools/lib/src/config.dart +715 -0
- package/dart-src/packages/cli-tools/lib/src/operations.dart +206 -0
- package/dart-src/packages/cli-tools/lib/src/pack.dart +417 -0
- package/dart-src/packages/cli-tools/lib/src/sign.dart +60 -0
- package/dart-src/packages/cli-tools/lib/src/util.dart +86 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0001_hot-updater_init.sql +16 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0002_hot-updater_0.13.0.sql +8 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0003_hot-updater_0.18.0.sql +38 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0004_hot-updater_0.29.0.sql +11 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0005_hot-updater_0.31.0.sql +24 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/bundles.sql +42 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20250103114225_init.sql +228 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20250314000000_hot-updater_0.13.0.sql +134 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20250516000000_hot-updater_0.18.0.sql +194 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20251014000000_hot-updater_0.21.0.sql +184 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260401000000_hot-updater_0.29.0.sql +503 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260414000000_hot-updater_0.30.0.sql +50 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260422000000_hot-updater_0.31.0.sql +20 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260520014100_hot-updater_rls.sql +48 -0
- package/dart-src/packages/cli-tools/pubspec.lock +638 -0
- package/dart-src/packages/cli-tools/pubspec.yaml +37 -0
- package/dart-src/packages/core/lib/flutter_patcher_core.dart +29 -0
- package/dart-src/packages/core/lib/src/app_update_info.dart +111 -0
- package/dart-src/packages/core/lib/src/bundle.dart +191 -0
- package/dart-src/packages/core/lib/src/bundle_artifacts.dart +39 -0
- package/dart-src/packages/core/lib/src/bundle_patch_artifact.dart +47 -0
- package/dart-src/packages/core/lib/src/changed_asset.dart +77 -0
- package/dart-src/packages/core/lib/src/get_bundles_args.dart +78 -0
- package/dart-src/packages/core/lib/src/metadata.dart +31 -0
- package/dart-src/packages/core/lib/src/patch_info.dart +284 -0
- package/dart-src/packages/core/lib/src/platform.dart +15 -0
- package/dart-src/packages/core/lib/src/rollout.dart +209 -0
- package/dart-src/packages/core/lib/src/semver.dart +70 -0
- package/dart-src/packages/core/lib/src/semver_range.dart +358 -0
- package/dart-src/packages/core/lib/src/semver_version.dart +94 -0
- package/dart-src/packages/core/lib/src/status.dart +24 -0
- package/dart-src/packages/core/lib/src/strategy.dart +10 -0
- package/dart-src/packages/core/lib/src/update_bundle_params.dart +30 -0
- package/dart-src/packages/core/lib/src/uuid.dart +45 -0
- package/dart-src/packages/core/pubspec.lock +381 -0
- package/dart-src/packages/core/pubspec.yaml +9 -0
- package/dart-src/packages/core/tool/dbg.dart +6 -0
- package/dart-src/plugins/aws/.gitkeep +0 -0
- package/dart-src/plugins/aws/lib/flutter_patcher_aws.dart +12 -0
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +207 -0
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_signer.dart +130 -0
- package/dart-src/plugins/aws/lib/src/aws_config.dart +59 -0
- package/dart-src/plugins/aws/lib/src/aws_database.dart +198 -0
- package/dart-src/plugins/aws/lib/src/aws_lambda_edge_storage.dart +72 -0
- package/dart-src/plugins/aws/lib/src/aws_s3_client.dart +312 -0
- package/dart-src/plugins/aws/lib/src/aws_ssm_client.dart +125 -0
- package/dart-src/plugins/aws/lib/src/aws_storage.dart +20 -0
- package/dart-src/plugins/aws/lib/src/aws_storage_profile.dart +123 -0
- package/dart-src/plugins/aws/lib/src/with_cloudfront_signed_url.dart +136 -0
- package/dart-src/plugins/aws/pubspec.lock +435 -0
- package/dart-src/plugins/aws/pubspec.yaml +22 -0
- package/dart-src/plugins/cloudflare/.gitkeep +0 -0
- package/dart-src/plugins/cloudflare/lib/flutter_patcher_cloudflare.dart +18 -0
- package/dart-src/plugins/cloudflare/lib/src/cloudflare_worker_database.dart +29 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_build_where.dart +93 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_bundle_mapper.dart +131 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_client.dart +66 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_config.dart +30 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_database.dart +16 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_database_plugin.dart +314 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_config.dart +50 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_s3_client.dart +287 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_storage.dart +23 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_storage_profile.dart +110 -0
- package/dart-src/plugins/cloudflare/migrations/0001_hot-updater_init.sql +16 -0
- package/dart-src/plugins/cloudflare/migrations/0002_hot-updater_0.13.0.sql +8 -0
- package/dart-src/plugins/cloudflare/migrations/0003_hot-updater_0.18.0.sql +38 -0
- package/dart-src/plugins/cloudflare/migrations/0004_hot-updater_0.29.0.sql +11 -0
- package/dart-src/plugins/cloudflare/migrations/0005_hot-updater_0.31.0.sql +24 -0
- package/dart-src/plugins/cloudflare/pubspec.lock +411 -0
- package/dart-src/plugins/cloudflare/pubspec.yaml +20 -0
- package/dart-src/plugins/cloudflare/sql/bundles.sql +42 -0
- package/dart-src/plugins/plugin-core/lib/flutter_patcher_plugin_core.dart +42 -0
- package/dart-src/plugins/plugin-core/lib/src/asset_storage_layout.dart +94 -0
- package/dart-src/plugins/plugin-core/lib/src/bundle_storage_layout.dart +44 -0
- package/dart-src/plugins/plugin-core/lib/src/bundle_unit_of_work.dart +259 -0
- package/dart-src/plugins/plugin-core/lib/src/bundle_unit_of_work_store.dart +20 -0
- package/dart-src/plugins/plugin-core/lib/src/calculate_pagination.dart +29 -0
- package/dart-src/plugins/plugin-core/lib/src/compression_format.dart +63 -0
- package/dart-src/plugins/plugin-core/lib/src/content_addressed_assets.dart +22 -0
- package/dart-src/plugins/plugin-core/lib/src/create_blob_database_plugin.dart +853 -0
- package/dart-src/plugins/plugin-core/lib/src/create_database_plugin.dart +525 -0
- package/dart-src/plugins/plugin-core/lib/src/create_storage_key_builder.dart +15 -0
- package/dart-src/plugins/plugin-core/lib/src/create_storage_plugin.dart +130 -0
- package/dart-src/plugins/plugin-core/lib/src/filter_compatible_app_versions.dart +18 -0
- package/dart-src/plugins/plugin-core/lib/src/generate_min_bundle_id.dart +19 -0
- package/dart-src/plugins/plugin-core/lib/src/get_update_info.dart +241 -0
- package/dart-src/plugins/plugin-core/lib/src/is_object.dart +5 -0
- package/dart-src/plugins/plugin-core/lib/src/legacy_asset_storage_layout.dart +9 -0
- package/dart-src/plugins/plugin-core/lib/src/paginate_bundles.dart +112 -0
- package/dart-src/plugins/plugin-core/lib/src/parse_storage_uri.dart +44 -0
- package/dart-src/plugins/plugin-core/lib/src/query_bundles.dart +70 -0
- package/dart-src/plugins/plugin-core/lib/src/request_update_bundle_state.dart +62 -0
- package/dart-src/plugins/plugin-core/lib/src/resolve_update_info_from_bundles.dart +43 -0
- package/dart-src/plugins/plugin-core/lib/src/storage_profile.dart +27 -0
- package/dart-src/plugins/plugin-core/lib/src/types.dart +454 -0
- package/dart-src/plugins/plugin-core/lib/src/uuidv7.dart +41 -0
- package/dart-src/plugins/plugin-core/pubspec.lock +396 -0
- package/dart-src/plugins/plugin-core/pubspec.yaml +15 -0
- package/dart-src/plugins/postgres/.gitkeep +0 -0
- package/dart-src/plugins/postgres/lib/flutter_patcher_postgres.dart +17 -0
- package/dart-src/plugins/postgres/lib/src/postgres_bundle_mapper.dart +130 -0
- package/dart-src/plugins/postgres/lib/src/postgres_client.dart +90 -0
- package/dart-src/plugins/postgres/lib/src/postgres_config.dart +35 -0
- package/dart-src/plugins/postgres/lib/src/postgres_database.dart +302 -0
- package/dart-src/plugins/postgres/lib/src/postgres_get_update_info.dart +120 -0
- package/dart-src/plugins/postgres/lib/src/postgres_storage.dart +205 -0
- package/dart-src/plugins/postgres/lib/src/postgres_types.dart +132 -0
- package/dart-src/plugins/postgres/pubspec.lock +427 -0
- package/dart-src/plugins/postgres/pubspec.yaml +18 -0
- package/dart-src/plugins/postgres/sql/bundles.sql +45 -0
- package/dart-src/plugins/postgres/sql/get_target_app_version_list.sql +21 -0
- package/dart-src/plugins/postgres/sql/get_update_info.spec.ts +171 -0
- package/dart-src/plugins/postgres/sql/get_update_info_by_app_version.sql +126 -0
- package/dart-src/plugins/postgres/sql/get_update_info_by_fingerprint_hash.sql +125 -0
- package/dart-src/plugins/postgres/sql/hash_user_id.sql +30 -0
- package/dart-src/plugins/postgres/sql/is_device_eligible.sql +237 -0
- package/dart-src/plugins/postgres/sql/prepareSql.ts +11 -0
- package/dart-src/plugins/standalone/.gitkeep +0 -0
- package/dart-src/plugins/standalone/lib/flutter_patcher_standalone.dart +12 -0
- package/dart-src/plugins/standalone/lib/src/standalone_config.dart +107 -0
- package/dart-src/plugins/standalone/lib/src/standalone_database.dart +233 -0
- package/dart-src/plugins/standalone/lib/src/standalone_storage.dart +175 -0
- package/dart-src/plugins/standalone/pubspec.lock +411 -0
- package/dart-src/plugins/standalone/pubspec.yaml +18 -0
- package/dart-src/plugins/supabase/lib/edge.dart +10 -0
- package/dart-src/plugins/supabase/lib/flutter_patcher_supabase.dart +21 -0
- package/dart-src/plugins/supabase/lib/src/supabase_bundle_mapper.dart +129 -0
- package/dart-src/plugins/supabase/lib/src/supabase_client_adapter.dart +154 -0
- package/dart-src/plugins/supabase/lib/src/supabase_client_http.dart +516 -0
- package/dart-src/plugins/supabase/lib/src/supabase_config.dart +34 -0
- package/dart-src/plugins/supabase/lib/src/supabase_database.dart +422 -0
- package/dart-src/plugins/supabase/lib/src/supabase_edge_function_database.dart +32 -0
- package/dart-src/plugins/supabase/lib/src/supabase_edge_function_storage.dart +99 -0
- package/dart-src/plugins/supabase/lib/src/supabase_signed_url_batcher.dart +145 -0
- package/dart-src/plugins/supabase/lib/src/supabase_storage.dart +298 -0
- package/dart-src/plugins/supabase/lib/src/types.dart +130 -0
- package/dart-src/plugins/supabase/pubspec.lock +515 -0
- package/dart-src/plugins/supabase/pubspec.yaml +19 -0
- package/dart-src/plugins/supabase/supabase/edge-functions/index.ts +44 -0
- package/dart-src/plugins/supabase/supabase/edge-functions/runtime.docker.integration.spec.ts +968 -0
- package/dart-src/plugins/supabase/supabase/migrations/20250103114225_init.sql +228 -0
- package/dart-src/plugins/supabase/supabase/migrations/20250314000000_hot-updater_0.13.0.sql +134 -0
- package/dart-src/plugins/supabase/supabase/migrations/20250516000000_hot-updater_0.18.0.sql +194 -0
- package/dart-src/plugins/supabase/supabase/migrations/20251014000000_hot-updater_0.21.0.sql +184 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260401000000_hot-updater_0.29.0.sql +503 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260414000000_hot-updater_0.30.0.sql +50 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260422000000_hot-updater_0.31.0.sql +20 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260520014100_hot-updater_rls.sql +48 -0
- package/package.json +2 -1
- package/scripts/postinstall.js +32 -23
- /package/bin/{flutter-patcher-linux → flutter-patcher-linux-x64} +0 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/// Abstraction over the Supabase client used by the database + storage plugins.
|
|
2
|
+
///
|
|
3
|
+
/// Mirrors the TS `vi.mock("@supabase/supabase-js")` test seam: in production
|
|
4
|
+
/// this wraps the real `supabase` Dart package; in tests a mock implementation
|
|
5
|
+
/// is supplied via the plugin config's `clientFactory`.
|
|
6
|
+
library;
|
|
7
|
+
|
|
8
|
+
/// Minimal PostgREST query-builder surface used by the database plugin.
|
|
9
|
+
abstract class SupabaseQueryBuilderLike {
|
|
10
|
+
SupabaseFilterBuilderLike select(
|
|
11
|
+
String columns, {
|
|
12
|
+
bool? count,
|
|
13
|
+
bool? head,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
SupabaseFilterBuilderLike delete();
|
|
17
|
+
|
|
18
|
+
Future<dynamic> upsert(
|
|
19
|
+
dynamic values, {
|
|
20
|
+
String? onConflict,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/// Minimal PostgREST filter-builder surface.
|
|
25
|
+
abstract class SupabaseFilterBuilderLike {
|
|
26
|
+
SupabaseFilterBuilderLike eq(String column, Object? value);
|
|
27
|
+
SupabaseFilterBuilderLike gt(String column, Object? value);
|
|
28
|
+
SupabaseFilterBuilderLike gte(String column, Object? value);
|
|
29
|
+
SupabaseFilterBuilderLike lt(String column, Object? value);
|
|
30
|
+
SupabaseFilterBuilderLike lte(String column, Object? value);
|
|
31
|
+
SupabaseFilterBuilderLike in_(String column, List<Object?> values);
|
|
32
|
+
SupabaseFilterBuilderLike isFilter(String column, Object? value);
|
|
33
|
+
SupabaseFilterBuilderLike not(String column, String operator, Object? value);
|
|
34
|
+
SupabaseFilterBuilderLike order(String column, {bool? ascending});
|
|
35
|
+
SupabaseFilterBuilderLike limit(int value);
|
|
36
|
+
SupabaseFilterBuilderLike range(int from, int to);
|
|
37
|
+
SupabaseFilterBuilderLike single();
|
|
38
|
+
Future<SupabaseResponseLike> execute();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// The response shape returned by a query/filter builder.
|
|
42
|
+
abstract class SupabaseResponseLike {
|
|
43
|
+
Object? get data;
|
|
44
|
+
Object? get error;
|
|
45
|
+
int? get count;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Storage bucket surface used by the storage plugin.
|
|
49
|
+
abstract class SupabaseStorageBucketLike {
|
|
50
|
+
Future<SupabaseSignedUrlResult> createSignedUrl(
|
|
51
|
+
String path,
|
|
52
|
+
int expiresIn,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
Future<SupabaseSignedUrlListResult> createSignedUrls(
|
|
56
|
+
List<String> paths,
|
|
57
|
+
int expiresIn,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
Future<SupabaseUploadResult> upload(
|
|
61
|
+
String path,
|
|
62
|
+
List<int> fileBytes, {
|
|
63
|
+
String? contentType,
|
|
64
|
+
String? cacheControl,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
Future<SupabaseRemoveResult> remove(List<String> paths);
|
|
68
|
+
|
|
69
|
+
Future<SupabaseExistsResult> exists(String path);
|
|
70
|
+
|
|
71
|
+
Future<SupabaseDownloadResult> download(String path);
|
|
72
|
+
|
|
73
|
+
/// List objects under an optional prefix (used by storage GC).
|
|
74
|
+
Future<SupabaseListResult> list([String? prefix]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/// A list-objects result entry.
|
|
78
|
+
abstract class SupabaseStorageObject {
|
|
79
|
+
String get key;
|
|
80
|
+
int get size;
|
|
81
|
+
String? get lastModifiedAt;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// A list-objects result.
|
|
85
|
+
abstract class SupabaseListResult {
|
|
86
|
+
List<SupabaseStorageObject>? get data;
|
|
87
|
+
Object? get error;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// A signed URL result.
|
|
91
|
+
abstract class SupabaseSignedUrlResult {
|
|
92
|
+
String? get signedUrl;
|
|
93
|
+
Object? get error;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/// A batch signed URL result.
|
|
97
|
+
abstract class SupabaseSignedUrlListResult {
|
|
98
|
+
List<SupabaseSignedUrlResult>? get data;
|
|
99
|
+
Object? get error;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// An upload result.
|
|
103
|
+
abstract class SupabaseUploadResult {
|
|
104
|
+
Object? get data;
|
|
105
|
+
Object? get error;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/// A remove result.
|
|
109
|
+
abstract class SupabaseRemoveResult {
|
|
110
|
+
Object? get data;
|
|
111
|
+
Object? get error;
|
|
112
|
+
String? get message;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/// An exists result.
|
|
116
|
+
abstract class SupabaseExistsResult {
|
|
117
|
+
bool? get data;
|
|
118
|
+
Object? get error;
|
|
119
|
+
String? get message;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/// A download result.
|
|
123
|
+
abstract class SupabaseDownloadResult {
|
|
124
|
+
List<int>? get data;
|
|
125
|
+
Object? get error;
|
|
126
|
+
String? get message;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/// The storage client surface used by the storage plugin.
|
|
130
|
+
abstract class SupabaseStorageClientLike {
|
|
131
|
+
SupabaseStorageBucketLike from(String bucket);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// The top-level Supabase client surface used by both plugins.
|
|
135
|
+
abstract class SupabaseClientLike {
|
|
136
|
+
SupabaseQueryBuilderLike from(String table);
|
|
137
|
+
SupabaseStorageClientLike get storage;
|
|
138
|
+
Future<SupabaseRpcResponse> rpc(
|
|
139
|
+
String functionName, {
|
|
140
|
+
Map<String, dynamic>? params,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// An RPC response.
|
|
145
|
+
abstract class SupabaseRpcResponse {
|
|
146
|
+
Object? get data;
|
|
147
|
+
Object? get error;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/// Factory that builds a [SupabaseClientLike] from credentials.
|
|
151
|
+
typedef SupabaseClientFactory = SupabaseClientLike Function(
|
|
152
|
+
String url,
|
|
153
|
+
String key,
|
|
154
|
+
);
|
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
/// Production adapter implementing [SupabaseClientLike] over the Supabase
|
|
2
|
+
/// REST (PostgREST) API using plain `http` calls.
|
|
3
|
+
///
|
|
4
|
+
/// This is the Dart equivalent of `@supabase/supabase-js`'s `createClient`:
|
|
5
|
+
/// it talks to the same REST endpoints the JS client would, so the plugin
|
|
6
|
+
/// logic stays identical regardless of transport.
|
|
7
|
+
library;
|
|
8
|
+
|
|
9
|
+
import 'dart:convert';
|
|
10
|
+
|
|
11
|
+
import 'package:http/http.dart' as http;
|
|
12
|
+
|
|
13
|
+
import 'supabase_client_adapter.dart';
|
|
14
|
+
|
|
15
|
+
class _HttpResponse implements SupabaseResponseLike {
|
|
16
|
+
@override
|
|
17
|
+
final Object? data;
|
|
18
|
+
@override
|
|
19
|
+
final Object? error;
|
|
20
|
+
@override
|
|
21
|
+
final int? count;
|
|
22
|
+
|
|
23
|
+
const _HttpResponse(this.data, this.error, this.count);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class _FilterBuilder implements SupabaseFilterBuilderLike {
|
|
27
|
+
final String _baseUrl;
|
|
28
|
+
final String _table;
|
|
29
|
+
final Map<String, String> _query;
|
|
30
|
+
final Map<String, String> _headers;
|
|
31
|
+
final String _method;
|
|
32
|
+
final Object? _body;
|
|
33
|
+
|
|
34
|
+
_FilterBuilder({
|
|
35
|
+
required String baseUrl,
|
|
36
|
+
required String table,
|
|
37
|
+
required Map<String, String> headers,
|
|
38
|
+
Map<String, String>? query,
|
|
39
|
+
String method = 'GET',
|
|
40
|
+
Object? body,
|
|
41
|
+
}) : _baseUrl = baseUrl,
|
|
42
|
+
_table = table,
|
|
43
|
+
_headers = headers,
|
|
44
|
+
_query = query ?? {},
|
|
45
|
+
_method = method,
|
|
46
|
+
_body = body;
|
|
47
|
+
|
|
48
|
+
@override
|
|
49
|
+
SupabaseFilterBuilderLike eq(String column, Object? value) =>
|
|
50
|
+
_addFilter(column, 'eq', value);
|
|
51
|
+
|
|
52
|
+
@override
|
|
53
|
+
SupabaseFilterBuilderLike gt(String column, Object? value) =>
|
|
54
|
+
_addFilter(column, 'gt', value);
|
|
55
|
+
|
|
56
|
+
@override
|
|
57
|
+
SupabaseFilterBuilderLike gte(String column, Object? value) =>
|
|
58
|
+
_addFilter(column, 'gte', value);
|
|
59
|
+
|
|
60
|
+
@override
|
|
61
|
+
SupabaseFilterBuilderLike lt(String column, Object? value) =>
|
|
62
|
+
_addFilter(column, 'lt', value);
|
|
63
|
+
|
|
64
|
+
@override
|
|
65
|
+
SupabaseFilterBuilderLike lte(String column, Object? value) =>
|
|
66
|
+
_addFilter(column, 'lte', value);
|
|
67
|
+
|
|
68
|
+
@override
|
|
69
|
+
SupabaseFilterBuilderLike in_(String column, List<Object?> values) {
|
|
70
|
+
_query[column] = 'in.(${values.join(',')})';
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@override
|
|
75
|
+
SupabaseFilterBuilderLike isFilter(String column, Object? value) {
|
|
76
|
+
_query[column] = 'is.${value ?? 'null'}';
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@override
|
|
81
|
+
SupabaseFilterBuilderLike not(
|
|
82
|
+
String column,
|
|
83
|
+
String operator,
|
|
84
|
+
Object? value,
|
|
85
|
+
) {
|
|
86
|
+
_query[column] = 'not.$operator.${_enc(value)}';
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@override
|
|
91
|
+
SupabaseFilterBuilderLike order(String column, {bool? ascending}) {
|
|
92
|
+
_query['order'] = ascending == false ? '$column.desc' : '$column.asc';
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@override
|
|
97
|
+
SupabaseFilterBuilderLike limit(int value) {
|
|
98
|
+
_query['limit'] = value.toString();
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@override
|
|
103
|
+
SupabaseFilterBuilderLike range(int from, int to) {
|
|
104
|
+
_headers['Range'] = '$from-$to';
|
|
105
|
+
_headers['Range-Unit'] = 'items';
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@override
|
|
110
|
+
SupabaseFilterBuilderLike single() {
|
|
111
|
+
_headers['Accept'] = 'application/vnd.pgrst.object+json';
|
|
112
|
+
return this;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
SupabaseFilterBuilderLike _addFilter(
|
|
116
|
+
String column,
|
|
117
|
+
String op,
|
|
118
|
+
Object? value,
|
|
119
|
+
) {
|
|
120
|
+
_query[column] = '$op.${_enc(value)}';
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
String _enc(Object? value) => value == null ? 'null' : value.toString();
|
|
125
|
+
|
|
126
|
+
@override
|
|
127
|
+
Future<SupabaseResponseLike> execute() async {
|
|
128
|
+
final uri = Uri.parse('$_baseUrl/rest/v1/$_table')
|
|
129
|
+
.replace(queryParameters: _query.isEmpty ? null : _query);
|
|
130
|
+
final headers = <String, String>{
|
|
131
|
+
..._headers,
|
|
132
|
+
if (_method != 'GET' && _body != null)
|
|
133
|
+
'Content-Type': 'application/json',
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
late http.Response res;
|
|
137
|
+
switch (_method) {
|
|
138
|
+
case 'DELETE':
|
|
139
|
+
res = await http.delete(uri, headers: headers);
|
|
140
|
+
break;
|
|
141
|
+
case 'POST':
|
|
142
|
+
res = await http.post(
|
|
143
|
+
uri,
|
|
144
|
+
headers: headers,
|
|
145
|
+
body: _body == null ? null : jsonEncode(_body),
|
|
146
|
+
);
|
|
147
|
+
break;
|
|
148
|
+
case 'PATCH':
|
|
149
|
+
res = await http.patch(
|
|
150
|
+
uri,
|
|
151
|
+
headers: headers,
|
|
152
|
+
body: _body == null ? null : jsonEncode(_body),
|
|
153
|
+
);
|
|
154
|
+
break;
|
|
155
|
+
default:
|
|
156
|
+
res = await http.get(uri, headers: headers);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (res.statusCode >= 400) {
|
|
160
|
+
return _HttpResponse(null, jsonDecode(res.body), null);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
final body = res.body.isEmpty ? null : jsonDecode(res.body);
|
|
164
|
+
final countHeader = res.headers['content-range'];
|
|
165
|
+
int? count;
|
|
166
|
+
if (countHeader != null) {
|
|
167
|
+
final slash = countHeader.indexOf('/');
|
|
168
|
+
if (slash >= 0 && slash + 1 < countHeader.length) {
|
|
169
|
+
count = int.tryParse(countHeader.substring(slash + 1));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return _HttpResponse(body, null, count);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
class _QueryBuilder implements SupabaseQueryBuilderLike {
|
|
177
|
+
final String _baseUrl;
|
|
178
|
+
final String _table;
|
|
179
|
+
final Map<String, String> _headers;
|
|
180
|
+
|
|
181
|
+
_QueryBuilder(this._baseUrl, this._table, this._headers);
|
|
182
|
+
|
|
183
|
+
@override
|
|
184
|
+
SupabaseFilterBuilderLike select(
|
|
185
|
+
String columns, {
|
|
186
|
+
bool? count,
|
|
187
|
+
bool? head,
|
|
188
|
+
}) {
|
|
189
|
+
final headers = <String, String>{..._headers};
|
|
190
|
+
if (count == true) headers['Prefer'] = 'count=exact';
|
|
191
|
+
if (head == true) headers['Range'] = '0-0';
|
|
192
|
+
return _FilterBuilder(
|
|
193
|
+
baseUrl: _baseUrl,
|
|
194
|
+
table: _table,
|
|
195
|
+
headers: headers,
|
|
196
|
+
query: {'select': columns},
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
@override
|
|
201
|
+
SupabaseFilterBuilderLike delete() => _FilterBuilder(
|
|
202
|
+
baseUrl: _baseUrl,
|
|
203
|
+
table: _table,
|
|
204
|
+
headers: _headers,
|
|
205
|
+
method: 'DELETE',
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
@override
|
|
209
|
+
Future<dynamic> upsert(dynamic values, {String? onConflict}) async {
|
|
210
|
+
final uri = Uri.parse('$_baseUrl/rest/v1/$_table').replace(
|
|
211
|
+
queryParameters: onConflict != null
|
|
212
|
+
? {'on_conflict': onConflict}
|
|
213
|
+
: null,
|
|
214
|
+
);
|
|
215
|
+
final headers = <String, String>{
|
|
216
|
+
..._headers,
|
|
217
|
+
'Content-Type': 'application/json',
|
|
218
|
+
'Prefer': 'resolution=merge-duplicates',
|
|
219
|
+
};
|
|
220
|
+
final res = await http.post(
|
|
221
|
+
uri,
|
|
222
|
+
headers: headers,
|
|
223
|
+
body: jsonEncode(values),
|
|
224
|
+
);
|
|
225
|
+
if (res.statusCode >= 400) {
|
|
226
|
+
return _HttpResponse(null, jsonDecode(res.body), null);
|
|
227
|
+
}
|
|
228
|
+
return _HttpResponse(
|
|
229
|
+
res.body.isEmpty ? null : jsonDecode(res.body),
|
|
230
|
+
null,
|
|
231
|
+
null,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
class _StorageBucket implements SupabaseStorageBucketLike {
|
|
237
|
+
final String _baseUrl;
|
|
238
|
+
final String _bucket;
|
|
239
|
+
final Map<String, String> _headers;
|
|
240
|
+
|
|
241
|
+
_StorageBucket(this._baseUrl, this._bucket, this._headers);
|
|
242
|
+
|
|
243
|
+
@override
|
|
244
|
+
Future<SupabaseSignedUrlResult> createSignedUrl(
|
|
245
|
+
String path,
|
|
246
|
+
int expiresIn,
|
|
247
|
+
) async {
|
|
248
|
+
final uri = Uri.parse(
|
|
249
|
+
'$_baseUrl/storage/v1/object/sign/$_bucket/$path',
|
|
250
|
+
).replace(queryParameters: {'expiresIn': expiresIn.toString()});
|
|
251
|
+
final res = await http.post(uri, headers: _headers);
|
|
252
|
+
if (res.statusCode >= 400) {
|
|
253
|
+
return _MockSignedUrlResult(null, jsonDecode(res.body));
|
|
254
|
+
}
|
|
255
|
+
final body = jsonDecode(res.body) as Map<String, dynamic>;
|
|
256
|
+
return _MockSignedUrlResult(
|
|
257
|
+
'$_baseUrl/storage/v1/object/sign/$_bucket/$path?...${body['signedURL']}',
|
|
258
|
+
null);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
@override
|
|
262
|
+
Future<SupabaseSignedUrlListResult> createSignedUrls(
|
|
263
|
+
List<String> paths,
|
|
264
|
+
int expiresIn,
|
|
265
|
+
) async {
|
|
266
|
+
final uri = Uri.parse('$_baseUrl/storage/v1/object/sign/$_bucket');
|
|
267
|
+
final res = await http.post(
|
|
268
|
+
uri,
|
|
269
|
+
headers: {..._headers, 'Content-Type': 'application/json'},
|
|
270
|
+
body: jsonEncode({
|
|
271
|
+
'expiresIn': expiresIn,
|
|
272
|
+
'paths': paths,
|
|
273
|
+
}),
|
|
274
|
+
);
|
|
275
|
+
if (res.statusCode >= 400) {
|
|
276
|
+
return _MockSignedUrlListResult(null, jsonDecode(res.body));
|
|
277
|
+
}
|
|
278
|
+
final body = jsonDecode(res.body) as List<dynamic>;
|
|
279
|
+
return _MockSignedUrlListResult(
|
|
280
|
+
body
|
|
281
|
+
.map((e) => _MockSignedUrlResult(
|
|
282
|
+
e['signedURL'] as String?,
|
|
283
|
+
e['error'] as String?,
|
|
284
|
+
))
|
|
285
|
+
.toList(),
|
|
286
|
+
null,
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@override
|
|
291
|
+
Future<SupabaseUploadResult> upload(
|
|
292
|
+
String path,
|
|
293
|
+
List<int> fileBytes, {
|
|
294
|
+
String? contentType,
|
|
295
|
+
String? cacheControl,
|
|
296
|
+
}) async {
|
|
297
|
+
final uri = Uri.parse('$_baseUrl/storage/v1/object/$_bucket/$path');
|
|
298
|
+
final res = await http.post(
|
|
299
|
+
uri,
|
|
300
|
+
headers: {
|
|
301
|
+
..._headers,
|
|
302
|
+
'Content-Type': contentType ?? 'application/octet-stream',
|
|
303
|
+
if (cacheControl != null) 'Cache-Control': cacheControl,
|
|
304
|
+
},
|
|
305
|
+
body: fileBytes,
|
|
306
|
+
);
|
|
307
|
+
if (res.statusCode >= 400) {
|
|
308
|
+
return _MockUploadResult(null, jsonDecode(res.body));
|
|
309
|
+
}
|
|
310
|
+
return _MockUploadResult(jsonDecode(res.body), null);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
@override
|
|
314
|
+
Future<SupabaseRemoveResult> remove(List<String> paths) async {
|
|
315
|
+
final uri = Uri.parse('$_baseUrl/storage/v1/object/$_bucket');
|
|
316
|
+
final res = await http.delete(
|
|
317
|
+
uri,
|
|
318
|
+
headers: {..._headers, 'Content-Type': 'application/json'},
|
|
319
|
+
body: jsonEncode({'prefixes': paths}),
|
|
320
|
+
);
|
|
321
|
+
if (res.statusCode >= 400) {
|
|
322
|
+
final body = jsonDecode(res.body) as Map<String, dynamic>;
|
|
323
|
+
return _MockRemoveResult(null, body['message'] as String?);
|
|
324
|
+
}
|
|
325
|
+
return _MockRemoveResult(jsonDecode(res.body), null);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
@override
|
|
329
|
+
Future<SupabaseExistsResult> exists(String path) async {
|
|
330
|
+
final uri = Uri.parse(
|
|
331
|
+
'$_baseUrl/storage/v1/object/$_bucket/$path',
|
|
332
|
+
).replace(queryParameters: {'_method': 'HEAD'});
|
|
333
|
+
final res = await http.get(uri, headers: _headers);
|
|
334
|
+
if (res.statusCode == 404) {
|
|
335
|
+
return _MockExistsResult(false, null);
|
|
336
|
+
}
|
|
337
|
+
if (res.statusCode >= 400) {
|
|
338
|
+
final body = jsonDecode(res.body) as Map<String, dynamic>;
|
|
339
|
+
return _MockExistsResult(null, body['message'] as String?);
|
|
340
|
+
}
|
|
341
|
+
return _MockExistsResult(true, null);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
@override
|
|
345
|
+
Future<SupabaseDownloadResult> download(String path) async {
|
|
346
|
+
final uri = Uri.parse('$_baseUrl/storage/v1/object/$_bucket/$path');
|
|
347
|
+
final res = await http.get(uri, headers: _headers);
|
|
348
|
+
if (res.statusCode >= 400) {
|
|
349
|
+
final body = jsonDecode(res.body) as Map<String, dynamic>;
|
|
350
|
+
return _MockDownloadResult(null, body['message'] as String?);
|
|
351
|
+
}
|
|
352
|
+
return _MockDownloadResult(res.bodyBytes, null);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
@override
|
|
356
|
+
Future<SupabaseListResult> list([String? prefix]) async {
|
|
357
|
+
final uri = Uri.parse('$_baseUrl/storage/v1/object/list/$_bucket');
|
|
358
|
+
final res = await http.post(
|
|
359
|
+
uri,
|
|
360
|
+
headers: {..._headers, 'Content-Type': 'application/json'},
|
|
361
|
+
body: jsonEncode({'prefix': prefix ?? ''}),
|
|
362
|
+
);
|
|
363
|
+
if (res.statusCode >= 400) {
|
|
364
|
+
final body = jsonDecode(res.body) as Map<String, dynamic>;
|
|
365
|
+
return _MockListResult(null, body['message'] as String?);
|
|
366
|
+
}
|
|
367
|
+
final body = (res.body.isEmpty ? [] : jsonDecode(res.body)) as List<dynamic>;
|
|
368
|
+
return _MockListResult(
|
|
369
|
+
body
|
|
370
|
+
.map((e) => _MockStorageObject(e as Map<String, dynamic>))
|
|
371
|
+
.toList(),
|
|
372
|
+
null,
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
class _MockStorageObject implements SupabaseStorageObject {
|
|
378
|
+
_MockStorageObject(this._raw);
|
|
379
|
+
final Map<String, dynamic> _raw;
|
|
380
|
+
|
|
381
|
+
@override
|
|
382
|
+
String get key => _raw['name'] as String;
|
|
383
|
+
|
|
384
|
+
@override
|
|
385
|
+
int get size {
|
|
386
|
+
final meta = _raw['metadata'] as Map?;
|
|
387
|
+
final s = meta?['size'];
|
|
388
|
+
return s is num ? s.toInt() : 0;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
@override
|
|
392
|
+
String? get lastModifiedAt => _raw['updated_at'] as String?;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
class _MockListResult implements SupabaseListResult {
|
|
396
|
+
@override
|
|
397
|
+
final List<SupabaseStorageObject>? data;
|
|
398
|
+
@override
|
|
399
|
+
final Object? error;
|
|
400
|
+
const _MockListResult(this.data, this.error);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
class _MockSignedUrlResult implements SupabaseSignedUrlResult {
|
|
404
|
+
@override
|
|
405
|
+
final String? signedUrl;
|
|
406
|
+
@override
|
|
407
|
+
final Object? error;
|
|
408
|
+
const _MockSignedUrlResult(this.signedUrl, this.error);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
class _MockSignedUrlListResult implements SupabaseSignedUrlListResult {
|
|
412
|
+
@override
|
|
413
|
+
final List<SupabaseSignedUrlResult>? data;
|
|
414
|
+
@override
|
|
415
|
+
final Object? error;
|
|
416
|
+
const _MockSignedUrlListResult(this.data, this.error);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
class _MockUploadResult implements SupabaseUploadResult {
|
|
420
|
+
@override
|
|
421
|
+
final Object? data;
|
|
422
|
+
@override
|
|
423
|
+
final Object? error;
|
|
424
|
+
const _MockUploadResult(this.data, this.error);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
class _MockRemoveResult implements SupabaseRemoveResult {
|
|
428
|
+
@override
|
|
429
|
+
final Object? data;
|
|
430
|
+
@override
|
|
431
|
+
final Object? error;
|
|
432
|
+
@override
|
|
433
|
+
final String? message;
|
|
434
|
+
const _MockRemoveResult(this.data, this.message) : error = null;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
class _MockExistsResult implements SupabaseExistsResult {
|
|
438
|
+
@override
|
|
439
|
+
final bool? data;
|
|
440
|
+
@override
|
|
441
|
+
final Object? error;
|
|
442
|
+
@override
|
|
443
|
+
final String? message;
|
|
444
|
+
const _MockExistsResult(this.data, this.error) : message = null;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
class _MockDownloadResult implements SupabaseDownloadResult {
|
|
448
|
+
@override
|
|
449
|
+
final List<int>? data;
|
|
450
|
+
@override
|
|
451
|
+
final Object? error;
|
|
452
|
+
@override
|
|
453
|
+
final String? message;
|
|
454
|
+
const _MockDownloadResult(this.data, this.error) : message = null;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
class _StorageClient implements SupabaseStorageClientLike {
|
|
458
|
+
final String _baseUrl;
|
|
459
|
+
final Map<String, String> _headers;
|
|
460
|
+
_StorageClient(this._baseUrl, this._headers);
|
|
461
|
+
|
|
462
|
+
@override
|
|
463
|
+
SupabaseStorageBucketLike from(String bucket) =>
|
|
464
|
+
_StorageBucket(_baseUrl, bucket, _headers);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/// [SupabaseClientLike] implementation backed by the Supabase REST API.
|
|
468
|
+
class SupabaseHttpClient implements SupabaseClientLike {
|
|
469
|
+
final String _baseUrl;
|
|
470
|
+
final Map<String, String> _headers;
|
|
471
|
+
|
|
472
|
+
SupabaseHttpClient(String supabaseUrl, String key)
|
|
473
|
+
: _baseUrl = supabaseUrl,
|
|
474
|
+
_headers = {
|
|
475
|
+
'apikey': key,
|
|
476
|
+
'Authorization': 'Bearer $key',
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
@override
|
|
480
|
+
SupabaseQueryBuilderLike from(String table) =>
|
|
481
|
+
_QueryBuilder(_baseUrl, table, _headers);
|
|
482
|
+
|
|
483
|
+
@override
|
|
484
|
+
SupabaseStorageClientLike get storage =>
|
|
485
|
+
_StorageClient(_baseUrl, _headers);
|
|
486
|
+
|
|
487
|
+
@override
|
|
488
|
+
Future<SupabaseRpcResponse> rpc(
|
|
489
|
+
String functionName, {
|
|
490
|
+
Map<String, dynamic>? params,
|
|
491
|
+
}) async {
|
|
492
|
+
final uri = Uri.parse('$_baseUrl/rest/v1/rpc/$functionName');
|
|
493
|
+
final res = await http.post(
|
|
494
|
+
uri,
|
|
495
|
+
headers: {..._headers, 'Content-Type': 'application/json'},
|
|
496
|
+
body: params == null ? null : jsonEncode(params),
|
|
497
|
+
);
|
|
498
|
+
if (res.statusCode >= 400) {
|
|
499
|
+
return _RpcResponse(null, jsonDecode(res.body));
|
|
500
|
+
}
|
|
501
|
+
final body = res.body.isEmpty ? null : jsonDecode(res.body);
|
|
502
|
+
return _RpcResponse(body, null);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
class _RpcResponse implements SupabaseRpcResponse {
|
|
507
|
+
@override
|
|
508
|
+
final Object? data;
|
|
509
|
+
@override
|
|
510
|
+
final Object? error;
|
|
511
|
+
const _RpcResponse(this.data, this.error);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/// Default factory: builds a [SupabaseHttpClient] from credentials.
|
|
515
|
+
SupabaseClientLike createSupabaseHttpClient(String url, String key) =>
|
|
516
|
+
SupabaseHttpClient(url, key);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/// Faithful port of hot-updater `plugins/supabase/src/supabaseConfig.ts`.
|
|
2
|
+
library;
|
|
3
|
+
|
|
4
|
+
import 'supabase_client_adapter.dart';
|
|
5
|
+
|
|
6
|
+
/// Configuration requiring either a service role key or an anon key.
|
|
7
|
+
class SupabaseServiceRoleConfig {
|
|
8
|
+
final String supabaseUrl;
|
|
9
|
+
final String? supabaseServiceRoleKey;
|
|
10
|
+
final String? supabaseAnonKey;
|
|
11
|
+
|
|
12
|
+
/// Optional test seam: a factory that builds the [SupabaseClientLike] used by
|
|
13
|
+
/// the plugin. When omitted, [createSupabaseHttpClient] is used (REST API).
|
|
14
|
+
/// Mirrors the TS `vi.mock("@supabase/supabase-js")` test seam.
|
|
15
|
+
final SupabaseClientFactory? clientFactory;
|
|
16
|
+
|
|
17
|
+
const SupabaseServiceRoleConfig({
|
|
18
|
+
required this.supabaseUrl,
|
|
19
|
+
this.supabaseServiceRoleKey,
|
|
20
|
+
this.supabaseAnonKey,
|
|
21
|
+
this.clientFactory,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/// Resolve the best available key — service role preferred over anon.
|
|
25
|
+
String resolveKey() {
|
|
26
|
+
final key = supabaseServiceRoleKey ?? supabaseAnonKey;
|
|
27
|
+
if (key == null || key.isEmpty) {
|
|
28
|
+
throw StateError(
|
|
29
|
+
'Supabase service role key is required. Set supabaseServiceRoleKey.',
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
return key;
|
|
33
|
+
}
|
|
34
|
+
}
|