@walkthru-earth/objex 0.1.0 → 1.1.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 +9 -2
- package/dist/components/browser/FileBrowser.svelte +53 -41
- package/dist/components/browser/FileRow.svelte +8 -3
- package/dist/components/browser/FileTreeSidebar.svelte +2 -4
- package/dist/components/layout/AboutSheet.svelte +126 -0
- package/dist/components/layout/AboutSheet.svelte.d.ts +6 -0
- package/dist/components/layout/ConnectionDialog.svelte +186 -138
- package/dist/components/layout/ConnectionDialog.svelte.d.ts +1 -0
- package/dist/components/layout/Sidebar.svelte +19 -3
- package/dist/components/layout/TabBar.svelte +4 -7
- package/dist/components/viewers/CodeViewer.svelte +17 -9
- package/dist/components/viewers/ImageViewer.svelte +6 -16
- package/dist/components/viewers/MarkdownViewer.svelte +8 -16
- package/dist/components/viewers/MediaViewer.svelte +6 -17
- package/dist/components/viewers/ModelViewer.svelte +4 -2
- package/dist/components/viewers/NotebookViewer.svelte +90 -40
- package/dist/components/viewers/PdfViewer.svelte +5 -3
- package/dist/components/viewers/RawViewer.svelte +4 -2
- package/dist/components/viewers/TableGrid.svelte +3 -2
- package/dist/components/viewers/ZarrMapViewer.svelte +334 -40
- package/dist/components/viewers/ZarrMapViewer.svelte.d.ts +3 -8
- package/dist/components/viewers/ZarrViewer.svelte +459 -178
- package/dist/components/viewers/map/AttributeTable.svelte +1 -6
- package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte +2 -6
- package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte +96 -22
- package/dist/constants.d.ts +28 -0
- package/dist/constants.js +34 -0
- package/dist/file-icons/index.js +6 -0
- package/dist/i18n/ar.js +34 -0
- package/dist/i18n/en.js +34 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.js +16 -1
- package/dist/query/wasm.js +5 -4
- package/dist/storage/browser-cloud.d.ts +7 -0
- package/dist/storage/browser-cloud.js +74 -7
- package/dist/storage/providers.d.ts +53 -0
- package/dist/storage/providers.js +318 -0
- package/dist/stores/connections.svelte.js +8 -34
- package/dist/stores/files.svelte.d.ts +1 -6
- package/dist/stores/files.svelte.js +4 -36
- package/dist/stores/query-history.svelte.js +5 -28
- package/dist/stores/settings.svelte.d.ts +1 -0
- package/dist/stores/settings.svelte.js +11 -31
- package/dist/types.d.ts +2 -2
- package/dist/utils/clipboard.d.ts +13 -0
- package/dist/utils/clipboard.js +38 -0
- package/dist/utils/cloud-url.d.ts +27 -0
- package/dist/utils/cloud-url.js +61 -0
- package/dist/utils/error.d.ts +8 -0
- package/dist/utils/error.js +12 -0
- package/dist/utils/export.d.ts +22 -2
- package/dist/utils/export.js +35 -10
- package/dist/utils/file-sort.d.ts +20 -0
- package/dist/utils/file-sort.js +41 -0
- package/dist/utils/format.d.ts +10 -0
- package/dist/utils/format.js +22 -0
- package/dist/utils/host-detection.js +78 -18
- package/dist/utils/local-storage.d.ts +16 -0
- package/dist/utils/local-storage.js +37 -0
- package/dist/utils/notebook.d.ts +59 -0
- package/dist/utils/notebook.js +211 -0
- package/dist/utils/parquet-metadata.js +1 -1
- package/dist/utils/pmtiles-tile.js +2 -1
- package/dist/utils/pmtiles.js +2 -1
- package/dist/utils/storage-url.d.ts +1 -1
- package/dist/utils/storage-url.js +82 -24
- package/dist/utils/url-state.js +2 -7
- package/dist/utils/url.d.ts +0 -2
- package/dist/utils/url.js +3 -29
- package/dist/utils/zarr.d.ts +60 -20
- package/dist/utils/zarr.js +450 -103
- package/package.json +66 -54
- package/dist/assets/favicon.svg +0 -17
- package/dist/components/CLAUDE.md +0 -44
- package/dist/components/viewers/CLAUDE.md +0 -60
- package/dist/file-icons/CLAUDE.md +0 -21
- package/dist/i18n/CLAUDE.md +0 -19
- package/dist/query/CLAUDE.md +0 -22
- package/dist/storage/CLAUDE.md +0 -23
- package/dist/stores/CLAUDE.md +0 -29
- package/dist/types/notebookjs.d.ts +0 -14
- package/dist/utils/CLAUDE.md +0 -54
- package/dist/utils/analytics.d.ts +0 -10
- package/dist/utils/analytics.js +0 -38
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider registry — single source of truth for all cloud storage providers.
|
|
3
|
+
*
|
|
4
|
+
* Centralizes endpoint patterns, regions, auth methods, and UI metadata.
|
|
5
|
+
* Used by ConnectionDialog, browser-cloud adapter, host-detection, url-state, etc.
|
|
6
|
+
*/
|
|
7
|
+
export type ProviderId = 's3' | 'gcs' | 'r2' | 'minio' | 'azure' | 'storj' | 'b2' | 'digitalocean' | 'wasabi' | 'contabo' | 'hetzner' | 'linode' | 'ovhcloud';
|
|
8
|
+
export interface ProviderRegion {
|
|
9
|
+
code: string;
|
|
10
|
+
label: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ProviderDef {
|
|
13
|
+
/** Display label in the UI. */
|
|
14
|
+
label: string;
|
|
15
|
+
/** Short description shown as helper text. */
|
|
16
|
+
description: string;
|
|
17
|
+
/** Auth method used by this provider. */
|
|
18
|
+
authMethod: 'sigv4' | 'sas-token';
|
|
19
|
+
/** Whether the region field is relevant for this provider. */
|
|
20
|
+
needsRegion: boolean;
|
|
21
|
+
/** Whether the endpoint field is required. */
|
|
22
|
+
needsEndpoint: boolean;
|
|
23
|
+
/** Default region when creating a new connection. */
|
|
24
|
+
defaultRegion: string;
|
|
25
|
+
/**
|
|
26
|
+
* Endpoint template with `{region}` placeholder.
|
|
27
|
+
* If null, the user must provide a custom endpoint (e.g. MinIO).
|
|
28
|
+
* If a fixed string (no `{region}`), it's always the same (e.g. GCS).
|
|
29
|
+
*/
|
|
30
|
+
endpointTemplate: string | null;
|
|
31
|
+
/** Known regions with labels. Empty = free-form region input. */
|
|
32
|
+
regions: ProviderRegion[];
|
|
33
|
+
/** Bucket label override (e.g. Azure uses "Container"). */
|
|
34
|
+
bucketLabel?: string;
|
|
35
|
+
/** Default endpoint placeholder shown in the input. */
|
|
36
|
+
endpointPlaceholder: string;
|
|
37
|
+
/** URI schemes that map to this provider (lowercase, without "://"). */
|
|
38
|
+
schemes: string[];
|
|
39
|
+
}
|
|
40
|
+
export declare const PROVIDERS: Record<ProviderId, ProviderDef>;
|
|
41
|
+
/** All provider IDs, ordered for the UI. */
|
|
42
|
+
export declare const PROVIDER_IDS: ProviderId[];
|
|
43
|
+
/** Get provider def, falling back to S3 for unknown. */
|
|
44
|
+
export declare function getProvider(id: string): ProviderDef;
|
|
45
|
+
/** Build endpoint URL from template + region. */
|
|
46
|
+
export declare function buildEndpointFromTemplate(id: ProviderId, region: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Build the base URL for API requests (endpoint + bucket).
|
|
49
|
+
* Used by browser-cloud adapter and url-state.
|
|
50
|
+
*/
|
|
51
|
+
export declare function buildProviderBaseUrl(provider: ProviderId, endpoint: string, bucket: string, region: string): string;
|
|
52
|
+
/** Check if a provider uses the GCS JSON API (not S3 XML). */
|
|
53
|
+
export declare function isGcsProvider(provider: string, endpoint: string): boolean;
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider registry — single source of truth for all cloud storage providers.
|
|
3
|
+
*
|
|
4
|
+
* Centralizes endpoint patterns, regions, auth methods, and UI metadata.
|
|
5
|
+
* Used by ConnectionDialog, browser-cloud adapter, host-detection, url-state, etc.
|
|
6
|
+
*/
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Registry
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
export const PROVIDERS = {
|
|
11
|
+
s3: {
|
|
12
|
+
label: 'AWS S3',
|
|
13
|
+
description: 'Amazon S3 or any S3-compatible service',
|
|
14
|
+
authMethod: 'sigv4',
|
|
15
|
+
needsRegion: true,
|
|
16
|
+
needsEndpoint: false,
|
|
17
|
+
defaultRegion: 'us-east-1',
|
|
18
|
+
endpointTemplate: null,
|
|
19
|
+
regions: [
|
|
20
|
+
{ code: 'us-east-1', label: 'US East (N. Virginia)' },
|
|
21
|
+
{ code: 'us-east-2', label: 'US East (Ohio)' },
|
|
22
|
+
{ code: 'us-west-1', label: 'US West (N. California)' },
|
|
23
|
+
{ code: 'us-west-2', label: 'US West (Oregon)' },
|
|
24
|
+
{ code: 'eu-west-1', label: 'EU (Ireland)' },
|
|
25
|
+
{ code: 'eu-west-2', label: 'EU (London)' },
|
|
26
|
+
{ code: 'eu-west-3', label: 'EU (Paris)' },
|
|
27
|
+
{ code: 'eu-central-1', label: 'EU (Frankfurt)' },
|
|
28
|
+
{ code: 'eu-central-2', label: 'EU (Zurich)' },
|
|
29
|
+
{ code: 'eu-north-1', label: 'EU (Stockholm)' },
|
|
30
|
+
{ code: 'eu-south-1', label: 'EU (Milan)' },
|
|
31
|
+
{ code: 'eu-south-2', label: 'EU (Spain)' },
|
|
32
|
+
{ code: 'ap-northeast-1', label: 'Asia Pacific (Tokyo)' },
|
|
33
|
+
{ code: 'ap-northeast-2', label: 'Asia Pacific (Seoul)' },
|
|
34
|
+
{ code: 'ap-northeast-3', label: 'Asia Pacific (Osaka)' },
|
|
35
|
+
{ code: 'ap-southeast-1', label: 'Asia Pacific (Singapore)' },
|
|
36
|
+
{ code: 'ap-southeast-2', label: 'Asia Pacific (Sydney)' },
|
|
37
|
+
{ code: 'ap-southeast-3', label: 'Asia Pacific (Jakarta)' },
|
|
38
|
+
{ code: 'ap-south-1', label: 'Asia Pacific (Mumbai)' },
|
|
39
|
+
{ code: 'ap-south-2', label: 'Asia Pacific (Hyderabad)' },
|
|
40
|
+
{ code: 'ap-east-1', label: 'Asia Pacific (Hong Kong)' },
|
|
41
|
+
{ code: 'sa-east-1', label: 'South America (São Paulo)' },
|
|
42
|
+
{ code: 'ca-central-1', label: 'Canada (Central)' },
|
|
43
|
+
{ code: 'ca-west-1', label: 'Canada (Calgary)' },
|
|
44
|
+
{ code: 'me-south-1', label: 'Middle East (Bahrain)' },
|
|
45
|
+
{ code: 'me-central-1', label: 'Middle East (UAE)' },
|
|
46
|
+
{ code: 'af-south-1', label: 'Africa (Cape Town)' },
|
|
47
|
+
{ code: 'il-central-1', label: 'Israel (Tel Aviv)' }
|
|
48
|
+
],
|
|
49
|
+
endpointPlaceholder: 'Leave empty for AWS, or enter custom S3 endpoint',
|
|
50
|
+
schemes: ['s3', 's3a', 's3n', 'aws']
|
|
51
|
+
},
|
|
52
|
+
gcs: {
|
|
53
|
+
label: 'Google Cloud',
|
|
54
|
+
description: 'Google Cloud Storage',
|
|
55
|
+
authMethod: 'sigv4',
|
|
56
|
+
needsRegion: false,
|
|
57
|
+
needsEndpoint: false,
|
|
58
|
+
defaultRegion: 'auto',
|
|
59
|
+
endpointTemplate: 'https://storage.googleapis.com',
|
|
60
|
+
regions: [],
|
|
61
|
+
endpointPlaceholder: 'https://storage.googleapis.com',
|
|
62
|
+
schemes: ['gs', 'gcs']
|
|
63
|
+
},
|
|
64
|
+
r2: {
|
|
65
|
+
label: 'Cloudflare R2',
|
|
66
|
+
description: 'Cloudflare R2 Storage',
|
|
67
|
+
authMethod: 'sigv4',
|
|
68
|
+
needsRegion: false,
|
|
69
|
+
needsEndpoint: true,
|
|
70
|
+
defaultRegion: 'auto',
|
|
71
|
+
endpointTemplate: null,
|
|
72
|
+
regions: [],
|
|
73
|
+
endpointPlaceholder: 'https://<account-id>.r2.cloudflarestorage.com',
|
|
74
|
+
schemes: ['r2']
|
|
75
|
+
},
|
|
76
|
+
azure: {
|
|
77
|
+
label: 'Azure',
|
|
78
|
+
description: 'Azure Blob Storage',
|
|
79
|
+
authMethod: 'sas-token',
|
|
80
|
+
needsRegion: false,
|
|
81
|
+
needsEndpoint: true,
|
|
82
|
+
defaultRegion: '',
|
|
83
|
+
endpointTemplate: null,
|
|
84
|
+
regions: [],
|
|
85
|
+
bucketLabel: 'Container',
|
|
86
|
+
endpointPlaceholder: 'https://<account>.blob.core.windows.net',
|
|
87
|
+
schemes: ['azure', 'az', 'abfs', 'abfss', 'wasbs', 'adl']
|
|
88
|
+
},
|
|
89
|
+
minio: {
|
|
90
|
+
label: 'MinIO',
|
|
91
|
+
description: 'Self-hosted MinIO or S3-compatible',
|
|
92
|
+
authMethod: 'sigv4',
|
|
93
|
+
needsRegion: false,
|
|
94
|
+
needsEndpoint: true,
|
|
95
|
+
defaultRegion: 'us-east-1',
|
|
96
|
+
endpointTemplate: null,
|
|
97
|
+
regions: [],
|
|
98
|
+
endpointPlaceholder: 'https://minio.example.com or http://localhost:9000',
|
|
99
|
+
schemes: []
|
|
100
|
+
},
|
|
101
|
+
storj: {
|
|
102
|
+
label: 'Storj',
|
|
103
|
+
description: 'Storj Decentralized Cloud',
|
|
104
|
+
authMethod: 'sigv4',
|
|
105
|
+
needsRegion: false,
|
|
106
|
+
needsEndpoint: false,
|
|
107
|
+
defaultRegion: 'us1',
|
|
108
|
+
endpointTemplate: 'https://gateway.storjshare.io',
|
|
109
|
+
regions: [
|
|
110
|
+
{ code: 'us1', label: 'US1' },
|
|
111
|
+
{ code: 'eu1', label: 'EU1' },
|
|
112
|
+
{ code: 'ap1', label: 'AP1' }
|
|
113
|
+
],
|
|
114
|
+
endpointPlaceholder: 'https://gateway.storjshare.io',
|
|
115
|
+
schemes: ['storj', 'sj']
|
|
116
|
+
},
|
|
117
|
+
b2: {
|
|
118
|
+
label: 'Backblaze B2',
|
|
119
|
+
description: 'Backblaze B2 Cloud Storage',
|
|
120
|
+
authMethod: 'sigv4',
|
|
121
|
+
needsRegion: true,
|
|
122
|
+
needsEndpoint: false,
|
|
123
|
+
defaultRegion: 'us-west-004',
|
|
124
|
+
endpointTemplate: 'https://s3.{region}.backblazeb2.com',
|
|
125
|
+
regions: [
|
|
126
|
+
{ code: 'us-west-000', label: 'US West (Sacramento)' },
|
|
127
|
+
{ code: 'us-west-001', label: 'US West (Stockton)' },
|
|
128
|
+
{ code: 'us-west-002', label: 'US West (Phoenix)' },
|
|
129
|
+
{ code: 'us-west-004', label: 'US West' },
|
|
130
|
+
{ code: 'us-east-005', label: 'US East (Reston)' },
|
|
131
|
+
{ code: 'eu-central-003', label: 'EU Central (Amsterdam)' },
|
|
132
|
+
{ code: 'ca-central-001', label: 'Canada (Toronto)' }
|
|
133
|
+
],
|
|
134
|
+
endpointPlaceholder: 'https://s3.us-west-004.backblazeb2.com',
|
|
135
|
+
schemes: []
|
|
136
|
+
},
|
|
137
|
+
digitalocean: {
|
|
138
|
+
label: 'DigitalOcean',
|
|
139
|
+
description: 'DigitalOcean Spaces',
|
|
140
|
+
authMethod: 'sigv4',
|
|
141
|
+
needsRegion: true,
|
|
142
|
+
needsEndpoint: false,
|
|
143
|
+
defaultRegion: 'nyc3',
|
|
144
|
+
endpointTemplate: 'https://{region}.digitaloceanspaces.com',
|
|
145
|
+
regions: [
|
|
146
|
+
{ code: 'nyc3', label: 'New York 3' },
|
|
147
|
+
{ code: 'sfo3', label: 'San Francisco 3' },
|
|
148
|
+
{ code: 'ams3', label: 'Amsterdam 3' },
|
|
149
|
+
{ code: 'sgp1', label: 'Singapore 1' },
|
|
150
|
+
{ code: 'lon1', label: 'London 1' },
|
|
151
|
+
{ code: 'fra1', label: 'Frankfurt 1' },
|
|
152
|
+
{ code: 'tor1', label: 'Toronto 1' },
|
|
153
|
+
{ code: 'blr1', label: 'Bangalore 1' },
|
|
154
|
+
{ code: 'syd1', label: 'Sydney 1' }
|
|
155
|
+
],
|
|
156
|
+
endpointPlaceholder: 'https://nyc3.digitaloceanspaces.com',
|
|
157
|
+
schemes: []
|
|
158
|
+
},
|
|
159
|
+
wasabi: {
|
|
160
|
+
label: 'Wasabi',
|
|
161
|
+
description: 'Wasabi Hot Cloud Storage',
|
|
162
|
+
authMethod: 'sigv4',
|
|
163
|
+
needsRegion: true,
|
|
164
|
+
needsEndpoint: false,
|
|
165
|
+
defaultRegion: 'us-east-1',
|
|
166
|
+
endpointTemplate: 'https://s3.{region}.wasabisys.com',
|
|
167
|
+
regions: [
|
|
168
|
+
{ code: 'us-east-1', label: 'US East 1 (Virginia)' },
|
|
169
|
+
{ code: 'us-east-2', label: 'US East 2 (Virginia)' },
|
|
170
|
+
{ code: 'us-central-1', label: 'US Central 1 (Texas)' },
|
|
171
|
+
{ code: 'us-west-1', label: 'US West 1 (Oregon)' },
|
|
172
|
+
{ code: 'eu-central-1', label: 'EU Central 1 (Amsterdam)' },
|
|
173
|
+
{ code: 'eu-central-2', label: 'EU Central 2 (Frankfurt)' },
|
|
174
|
+
{ code: 'eu-west-1', label: 'EU West 1 (London)' },
|
|
175
|
+
{ code: 'eu-west-2', label: 'EU West 2 (Paris)' },
|
|
176
|
+
{ code: 'ap-northeast-1', label: 'AP Northeast 1 (Tokyo)' },
|
|
177
|
+
{ code: 'ap-northeast-2', label: 'AP Northeast 2 (Osaka)' },
|
|
178
|
+
{ code: 'ap-southeast-1', label: 'AP Southeast 1 (Singapore)' },
|
|
179
|
+
{ code: 'ap-southeast-2', label: 'AP Southeast 2 (Sydney)' },
|
|
180
|
+
{ code: 'ca-central-1', label: 'Canada (Toronto)' }
|
|
181
|
+
],
|
|
182
|
+
endpointPlaceholder: 'https://s3.us-east-1.wasabisys.com',
|
|
183
|
+
schemes: []
|
|
184
|
+
},
|
|
185
|
+
contabo: {
|
|
186
|
+
label: 'Contabo',
|
|
187
|
+
description: 'Contabo Object Storage',
|
|
188
|
+
authMethod: 'sigv4',
|
|
189
|
+
needsRegion: true,
|
|
190
|
+
needsEndpoint: false,
|
|
191
|
+
defaultRegion: 'eu2',
|
|
192
|
+
endpointTemplate: 'https://{region}.contabostorage.com',
|
|
193
|
+
regions: [
|
|
194
|
+
{ code: 'eu2', label: 'European Union' },
|
|
195
|
+
{ code: 'usc1', label: 'US Central' },
|
|
196
|
+
{ code: 'sin1', label: 'Singapore' }
|
|
197
|
+
],
|
|
198
|
+
endpointPlaceholder: 'https://eu2.contabostorage.com',
|
|
199
|
+
schemes: []
|
|
200
|
+
},
|
|
201
|
+
hetzner: {
|
|
202
|
+
label: 'Hetzner',
|
|
203
|
+
description: 'Hetzner Object Storage',
|
|
204
|
+
authMethod: 'sigv4',
|
|
205
|
+
needsRegion: true,
|
|
206
|
+
needsEndpoint: false,
|
|
207
|
+
defaultRegion: 'fsn1',
|
|
208
|
+
endpointTemplate: 'https://{region}.your-objectstorage.com',
|
|
209
|
+
regions: [
|
|
210
|
+
{ code: 'fsn1', label: 'Falkenstein, DE' },
|
|
211
|
+
{ code: 'nbg1', label: 'Nuremberg, DE' },
|
|
212
|
+
{ code: 'hel1', label: 'Helsinki, FI' }
|
|
213
|
+
],
|
|
214
|
+
endpointPlaceholder: 'https://fsn1.your-objectstorage.com',
|
|
215
|
+
schemes: []
|
|
216
|
+
},
|
|
217
|
+
linode: {
|
|
218
|
+
label: 'Linode / Akamai',
|
|
219
|
+
description: 'Akamai / Linode Object Storage',
|
|
220
|
+
authMethod: 'sigv4',
|
|
221
|
+
needsRegion: true,
|
|
222
|
+
needsEndpoint: false,
|
|
223
|
+
defaultRegion: 'us-east-1',
|
|
224
|
+
endpointTemplate: 'https://{region}.linodeobjects.com',
|
|
225
|
+
regions: [
|
|
226
|
+
{ code: 'us-east-1', label: 'Newark, NJ' },
|
|
227
|
+
{ code: 'us-southeast-1', label: 'Atlanta, GA' },
|
|
228
|
+
{ code: 'us-ord-1', label: 'Chicago, IL' },
|
|
229
|
+
{ code: 'us-iad-1', label: 'Washington, DC' },
|
|
230
|
+
{ code: 'us-lax-1', label: 'Los Angeles, CA' },
|
|
231
|
+
{ code: 'us-sea-1', label: 'Seattle, WA' },
|
|
232
|
+
{ code: 'us-mia-1', label: 'Miami, FL' },
|
|
233
|
+
{ code: 'eu-central-1', label: 'Frankfurt, DE' },
|
|
234
|
+
{ code: 'nl-ams-1', label: 'Amsterdam, NL' },
|
|
235
|
+
{ code: 'gb-lon-1', label: 'London, UK' },
|
|
236
|
+
{ code: 'fr-par-1', label: 'Paris, FR' },
|
|
237
|
+
{ code: 'ap-south-1', label: 'Singapore' },
|
|
238
|
+
{ code: 'jp-osa-1', label: 'Osaka, JP' },
|
|
239
|
+
{ code: 'au-mel-1', label: 'Melbourne, AU' },
|
|
240
|
+
{ code: 'br-gru-1', label: 'São Paulo, BR' },
|
|
241
|
+
{ code: 'in-maa-1', label: 'Chennai, IN' },
|
|
242
|
+
{ code: 'id-cgk-1', label: 'Jakarta, ID' },
|
|
243
|
+
{ code: 'it-mil-1', label: 'Milan, IT' },
|
|
244
|
+
{ code: 'se-sto-1', label: 'Stockholm, SE' }
|
|
245
|
+
],
|
|
246
|
+
endpointPlaceholder: 'https://us-east-1.linodeobjects.com',
|
|
247
|
+
schemes: []
|
|
248
|
+
},
|
|
249
|
+
ovhcloud: {
|
|
250
|
+
label: 'OVHcloud',
|
|
251
|
+
description: 'OVHcloud Object Storage',
|
|
252
|
+
authMethod: 'sigv4',
|
|
253
|
+
needsRegion: true,
|
|
254
|
+
needsEndpoint: false,
|
|
255
|
+
defaultRegion: 'gra',
|
|
256
|
+
endpointTemplate: 'https://s3.{region}.io.cloud.ovh.net',
|
|
257
|
+
regions: [
|
|
258
|
+
{ code: 'gra', label: 'Gravelines, FR' },
|
|
259
|
+
{ code: 'sbg', label: 'Strasbourg, FR' },
|
|
260
|
+
{ code: 'bhs', label: 'Beauharnois, CA' },
|
|
261
|
+
{ code: 'de', label: 'Frankfurt, DE' },
|
|
262
|
+
{ code: 'uk', label: 'London, UK' },
|
|
263
|
+
{ code: 'waw', label: 'Warsaw, PL' }
|
|
264
|
+
],
|
|
265
|
+
endpointPlaceholder: 'https://s3.gra.io.cloud.ovh.net',
|
|
266
|
+
schemes: []
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
// ---------------------------------------------------------------------------
|
|
270
|
+
// Helpers
|
|
271
|
+
// ---------------------------------------------------------------------------
|
|
272
|
+
/** All provider IDs, ordered for the UI. */
|
|
273
|
+
export const PROVIDER_IDS = [
|
|
274
|
+
's3',
|
|
275
|
+
'gcs',
|
|
276
|
+
'r2',
|
|
277
|
+
'azure',
|
|
278
|
+
'b2',
|
|
279
|
+
'digitalocean',
|
|
280
|
+
'wasabi',
|
|
281
|
+
'storj',
|
|
282
|
+
'hetzner',
|
|
283
|
+
'contabo',
|
|
284
|
+
'linode',
|
|
285
|
+
'ovhcloud',
|
|
286
|
+
'minio'
|
|
287
|
+
];
|
|
288
|
+
/** Get provider def, falling back to S3 for unknown. */
|
|
289
|
+
export function getProvider(id) {
|
|
290
|
+
return PROVIDERS[id] ?? PROVIDERS.s3;
|
|
291
|
+
}
|
|
292
|
+
/** Build endpoint URL from template + region. */
|
|
293
|
+
export function buildEndpointFromTemplate(id, region) {
|
|
294
|
+
const def = PROVIDERS[id];
|
|
295
|
+
if (!def?.endpointTemplate)
|
|
296
|
+
return '';
|
|
297
|
+
return def.endpointTemplate.replace('{region}', region);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Build the base URL for API requests (endpoint + bucket).
|
|
301
|
+
* Used by browser-cloud adapter and url-state.
|
|
302
|
+
*/
|
|
303
|
+
export function buildProviderBaseUrl(provider, endpoint, bucket, region) {
|
|
304
|
+
if (endpoint) {
|
|
305
|
+
return `${endpoint.replace(/\/$/, '')}/${bucket}`;
|
|
306
|
+
}
|
|
307
|
+
const def = PROVIDERS[provider];
|
|
308
|
+
if (def?.endpointTemplate) {
|
|
309
|
+
const resolved = def.endpointTemplate.replace('{region}', region || def.defaultRegion);
|
|
310
|
+
return `${resolved}/${bucket}`;
|
|
311
|
+
}
|
|
312
|
+
// Fallback: AWS S3 path-style
|
|
313
|
+
return `https://s3.${region || 'us-east-1'}.amazonaws.com/${bucket}`;
|
|
314
|
+
}
|
|
315
|
+
/** Check if a provider uses the GCS JSON API (not S3 XML). */
|
|
316
|
+
export function isGcsProvider(provider, endpoint) {
|
|
317
|
+
return provider === 'gcs' || (!!endpoint && /storage\.googleapis\.com/i.test(endpoint));
|
|
318
|
+
}
|
|
@@ -1,32 +1,6 @@
|
|
|
1
|
+
import { STORAGE_KEYS } from '../constants.js';
|
|
2
|
+
import { loadFromStorage, persistToStorage } from '../utils/local-storage.js';
|
|
1
3
|
import { credentialStore, storeToNative } from './credentials.svelte.js';
|
|
2
|
-
const CONNECTIONS_KEY = 'obstore-explore-connections';
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
// localStorage helpers
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
function loadFromLocalStorage() {
|
|
7
|
-
if (typeof window === 'undefined')
|
|
8
|
-
return [];
|
|
9
|
-
try {
|
|
10
|
-
const raw = localStorage.getItem(CONNECTIONS_KEY);
|
|
11
|
-
if (raw) {
|
|
12
|
-
return JSON.parse(raw);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
// ignore parse errors
|
|
17
|
-
}
|
|
18
|
-
return [];
|
|
19
|
-
}
|
|
20
|
-
function persistToLocalStorage(connections) {
|
|
21
|
-
if (typeof window === 'undefined')
|
|
22
|
-
return;
|
|
23
|
-
try {
|
|
24
|
-
localStorage.setItem(CONNECTIONS_KEY, JSON.stringify(connections));
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
// ignore storage errors
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
4
|
// ---------------------------------------------------------------------------
|
|
31
5
|
// Store
|
|
32
6
|
// ---------------------------------------------------------------------------
|
|
@@ -48,7 +22,7 @@ function createConnectionsStore() {
|
|
|
48
22
|
async load() {
|
|
49
23
|
if (loaded)
|
|
50
24
|
return;
|
|
51
|
-
connections =
|
|
25
|
+
connections = loadFromStorage(STORAGE_KEYS.CONNECTIONS, []);
|
|
52
26
|
loaded = true;
|
|
53
27
|
},
|
|
54
28
|
/**
|
|
@@ -75,7 +49,7 @@ function createConnectionsStore() {
|
|
|
75
49
|
rootPrefix: config.rootPrefix
|
|
76
50
|
};
|
|
77
51
|
connections = [...connections, conn];
|
|
78
|
-
|
|
52
|
+
persistToStorage(STORAGE_KEYS.CONNECTIONS, connections);
|
|
79
53
|
// Store credentials in memory (never persisted to localStorage).
|
|
80
54
|
if (!config.anonymous) {
|
|
81
55
|
if (config.sas_token) {
|
|
@@ -114,7 +88,7 @@ function createConnectionsStore() {
|
|
|
114
88
|
rootPrefix: config.rootPrefix
|
|
115
89
|
};
|
|
116
90
|
connections = [...connections];
|
|
117
|
-
|
|
91
|
+
persistToStorage(STORAGE_KEYS.CONNECTIONS, connections);
|
|
118
92
|
// Invalidate cached adapter for this connection
|
|
119
93
|
import('../storage/index.js').then(({ clearAdapterCache }) => clearAdapterCache(id));
|
|
120
94
|
// Update in-memory credentials.
|
|
@@ -148,7 +122,7 @@ function createConnectionsStore() {
|
|
|
148
122
|
async remove(id) {
|
|
149
123
|
const before = connections.length;
|
|
150
124
|
connections = connections.filter((c) => c.id !== id);
|
|
151
|
-
|
|
125
|
+
persistToStorage(STORAGE_KEYS.CONNECTIONS, connections);
|
|
152
126
|
credentialStore.remove(id);
|
|
153
127
|
// Invalidate cached adapter for this connection
|
|
154
128
|
import('../storage/index.js').then(({ clearAdapterCache }) => clearAdapterCache(id));
|
|
@@ -158,7 +132,7 @@ function createConnectionsStore() {
|
|
|
158
132
|
* Test whether a connection is reachable via a lightweight list.
|
|
159
133
|
*/
|
|
160
134
|
async test(id) {
|
|
161
|
-
const { getAdapter } = await import(
|
|
135
|
+
const { getAdapter } = await import('../storage/index.js');
|
|
162
136
|
const adapter = getAdapter('remote', id);
|
|
163
137
|
await adapter.list('');
|
|
164
138
|
return true;
|
|
@@ -200,7 +174,7 @@ function createConnectionsStore() {
|
|
|
200
174
|
}
|
|
201
175
|
}
|
|
202
176
|
try {
|
|
203
|
-
const { getAdapter } = await import(
|
|
177
|
+
const { getAdapter } = await import('../storage/index.js');
|
|
204
178
|
const adapter = getAdapter('remote', tempId);
|
|
205
179
|
await adapter.list(config.rootPrefix || '');
|
|
206
180
|
return true;
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import type { FileEntry } from '../types.js';
|
|
2
|
-
|
|
3
|
-
export type SortDirection = 'asc' | 'desc';
|
|
4
|
-
export interface SortConfig {
|
|
5
|
-
field: SortField;
|
|
6
|
-
direction: SortDirection;
|
|
7
|
-
}
|
|
2
|
+
import { type SortConfig, type SortField } from '../utils/file-sort.js';
|
|
8
3
|
export declare const fileStore: {
|
|
9
4
|
readonly entries: FileEntry[];
|
|
10
5
|
readonly currentPath: string;
|
|
@@ -1,27 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
const sorted = [...entries];
|
|
3
|
-
const dir = config.direction === 'asc' ? 1 : -1;
|
|
4
|
-
sorted.sort((a, b) => {
|
|
5
|
-
// Directories always come first
|
|
6
|
-
if (a.is_dir && !b.is_dir)
|
|
7
|
-
return -1;
|
|
8
|
-
if (!a.is_dir && b.is_dir)
|
|
9
|
-
return 1;
|
|
10
|
-
switch (config.field) {
|
|
11
|
-
case 'name':
|
|
12
|
-
return dir * a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });
|
|
13
|
-
case 'size':
|
|
14
|
-
return dir * (a.size - b.size);
|
|
15
|
-
case 'modified':
|
|
16
|
-
return dir * (a.modified - b.modified);
|
|
17
|
-
case 'extension':
|
|
18
|
-
return dir * a.extension.localeCompare(b.extension, undefined, { sensitivity: 'base' });
|
|
19
|
-
default:
|
|
20
|
-
return 0;
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
return sorted;
|
|
24
|
-
}
|
|
1
|
+
import { sortFileEntries, toggleSortField } from '../utils/file-sort.js';
|
|
25
2
|
function createFilesStore() {
|
|
26
3
|
let files = $state([]);
|
|
27
4
|
let currentPath = $state('');
|
|
@@ -45,7 +22,7 @@ function createFilesStore() {
|
|
|
45
22
|
return sortConfig;
|
|
46
23
|
},
|
|
47
24
|
setFiles(entries) {
|
|
48
|
-
files =
|
|
25
|
+
files = sortFileEntries(entries, sortConfig);
|
|
49
26
|
error = null;
|
|
50
27
|
},
|
|
51
28
|
setPath(path) {
|
|
@@ -58,17 +35,8 @@ function createFilesStore() {
|
|
|
58
35
|
error = message;
|
|
59
36
|
},
|
|
60
37
|
sort(field) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
sortConfig = {
|
|
64
|
-
field,
|
|
65
|
-
direction: sortConfig.direction === 'asc' ? 'desc' : 'asc'
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
sortConfig = { field, direction: 'asc' };
|
|
70
|
-
}
|
|
71
|
-
files = sortEntries(files, sortConfig);
|
|
38
|
+
sortConfig = toggleSortField(sortConfig, field);
|
|
39
|
+
files = sortFileEntries(files, sortConfig);
|
|
72
40
|
}
|
|
73
41
|
};
|
|
74
42
|
}
|
|
@@ -1,32 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
function loadEntries() {
|
|
4
|
-
if (typeof window === 'undefined')
|
|
5
|
-
return [];
|
|
6
|
-
try {
|
|
7
|
-
const raw = localStorage.getItem(STORAGE_KEY);
|
|
8
|
-
if (raw)
|
|
9
|
-
return JSON.parse(raw);
|
|
10
|
-
}
|
|
11
|
-
catch {
|
|
12
|
-
// ignore parse errors
|
|
13
|
-
}
|
|
14
|
-
return [];
|
|
15
|
-
}
|
|
16
|
-
function persistEntries(entries) {
|
|
17
|
-
if (typeof window === 'undefined')
|
|
18
|
-
return;
|
|
19
|
-
try {
|
|
20
|
-
localStorage.setItem(STORAGE_KEY, JSON.stringify(entries));
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
// ignore storage errors
|
|
24
|
-
}
|
|
25
|
-
}
|
|
1
|
+
import { MAX_QUERY_HISTORY_ENTRIES, STORAGE_KEYS } from '../constants.js';
|
|
2
|
+
import { loadFromStorage, persistToStorage } from '../utils/local-storage.js';
|
|
26
3
|
function createQueryHistoryStore() {
|
|
27
|
-
let entries = $state(
|
|
4
|
+
let entries = $state(loadFromStorage(STORAGE_KEYS.QUERY_HISTORY, []));
|
|
28
5
|
function save() {
|
|
29
|
-
|
|
6
|
+
persistToStorage(STORAGE_KEYS.QUERY_HISTORY, entries);
|
|
30
7
|
}
|
|
31
8
|
return {
|
|
32
9
|
get entries() {
|
|
@@ -37,7 +14,7 @@ function createQueryHistoryStore() {
|
|
|
37
14
|
...entry,
|
|
38
15
|
id: crypto.randomUUID()
|
|
39
16
|
};
|
|
40
|
-
entries = [newEntry, ...entries].slice(0,
|
|
17
|
+
entries = [newEntry, ...entries].slice(0, MAX_QUERY_HISTORY_ENTRIES);
|
|
41
18
|
save();
|
|
42
19
|
},
|
|
43
20
|
remove(id) {
|
|
@@ -1,36 +1,16 @@
|
|
|
1
|
+
import { STORAGE_KEYS } from '../constants.js';
|
|
1
2
|
import { setLocale } from '../i18n/index.svelte.js';
|
|
2
|
-
|
|
3
|
+
import { loadFromStorage, persistToStorage } from '../utils/local-storage.js';
|
|
4
|
+
const SETTINGS_DEFAULTS = { theme: 'system', locale: 'en', featureLimit: 1000 };
|
|
3
5
|
function loadSettings() {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const parsed = JSON.parse(raw);
|
|
11
|
-
return {
|
|
12
|
-
theme: parsed.theme ?? 'system',
|
|
13
|
-
locale: parsed.locale ?? 'en',
|
|
14
|
-
featureLimit: parsed.featureLimit ?? 100
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
// ignore parse errors
|
|
20
|
-
}
|
|
21
|
-
return { theme: 'system', locale: 'en', featureLimit: 1000 };
|
|
22
|
-
}
|
|
23
|
-
function persistSettings(settings) {
|
|
24
|
-
if (typeof window === 'undefined')
|
|
25
|
-
return;
|
|
26
|
-
try {
|
|
27
|
-
localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
// ignore storage errors
|
|
31
|
-
}
|
|
6
|
+
const stored = loadFromStorage(STORAGE_KEYS.SETTINGS, {});
|
|
7
|
+
return {
|
|
8
|
+
theme: stored.theme ?? SETTINGS_DEFAULTS.theme,
|
|
9
|
+
locale: stored.locale ?? SETTINGS_DEFAULTS.locale,
|
|
10
|
+
featureLimit: stored.featureLimit ?? SETTINGS_DEFAULTS.featureLimit
|
|
11
|
+
};
|
|
32
12
|
}
|
|
33
|
-
function resolveTheme(theme) {
|
|
13
|
+
export function resolveTheme(theme) {
|
|
34
14
|
if (theme !== 'system')
|
|
35
15
|
return theme;
|
|
36
16
|
if (typeof window === 'undefined')
|
|
@@ -51,7 +31,7 @@ function createSettingsStore() {
|
|
|
51
31
|
document.documentElement.lang = initial.locale;
|
|
52
32
|
}
|
|
53
33
|
function persist() {
|
|
54
|
-
|
|
34
|
+
persistToStorage(STORAGE_KEYS.SETTINGS, { theme, locale, featureLimit });
|
|
55
35
|
}
|
|
56
36
|
function applyTheme(t) {
|
|
57
37
|
theme = t;
|
package/dist/types.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface FileEntry {
|
|
|
9
9
|
export interface Connection {
|
|
10
10
|
id: string;
|
|
11
11
|
name: string;
|
|
12
|
-
provider:
|
|
12
|
+
provider: string;
|
|
13
13
|
endpoint: string;
|
|
14
14
|
bucket: string;
|
|
15
15
|
region: string;
|
|
@@ -19,7 +19,7 @@ export interface Connection {
|
|
|
19
19
|
}
|
|
20
20
|
export interface ConnectionConfig {
|
|
21
21
|
name: string;
|
|
22
|
-
provider:
|
|
22
|
+
provider: string;
|
|
23
23
|
endpoint: string;
|
|
24
24
|
bucket: string;
|
|
25
25
|
region: string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy text to clipboard and run a feedback callback for COPY_FEEDBACK_MS.
|
|
3
|
+
* Silently catches clipboard errors (e.g. insecure context).
|
|
4
|
+
*
|
|
5
|
+
* @returns true if copy succeeded, false otherwise.
|
|
6
|
+
*/
|
|
7
|
+
export declare function copyToClipboard(text: string, onFeedback?: (copied: boolean) => void): Promise<boolean>;
|
|
8
|
+
/**
|
|
9
|
+
* Wire click-to-copy on all elements matching `selector` inside `root`.
|
|
10
|
+
* Each element must have `data-code` (URI-encoded) with the text to copy.
|
|
11
|
+
* Adds/removes a `copied` CSS class for visual feedback.
|
|
12
|
+
*/
|
|
13
|
+
export declare function wireCodeCopyButtons(root: Element, selector: string): void;
|