@selvajs/supabase-provider 0.11.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.
Files changed (173) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +271 -0
  3. package/dist/auth/SupabaseAuthProvider.d.ts +81 -0
  4. package/dist/auth/SupabaseAuthProvider.d.ts.map +1 -0
  5. package/dist/auth/SupabaseAuthProvider.js +383 -0
  6. package/dist/auth/SupabaseAuthProvider.js.map +1 -0
  7. package/dist/auth/__tests__/conformance.test.d.ts +2 -0
  8. package/dist/auth/__tests__/conformance.test.d.ts.map +1 -0
  9. package/dist/auth/__tests__/conformance.test.js +76 -0
  10. package/dist/auth/__tests__/conformance.test.js.map +1 -0
  11. package/dist/auth/index.d.ts +3 -0
  12. package/dist/auth/index.d.ts.map +1 -0
  13. package/dist/auth/index.js +2 -0
  14. package/dist/auth/index.js.map +1 -0
  15. package/dist/data/SupabaseAuditQuery.d.ts +16 -0
  16. package/dist/data/SupabaseAuditQuery.d.ts.map +1 -0
  17. package/dist/data/SupabaseAuditQuery.js +83 -0
  18. package/dist/data/SupabaseAuditQuery.js.map +1 -0
  19. package/dist/data/SupabaseComputeServerStore.d.ts +26 -0
  20. package/dist/data/SupabaseComputeServerStore.d.ts.map +1 -0
  21. package/dist/data/SupabaseComputeServerStore.js +227 -0
  22. package/dist/data/SupabaseComputeServerStore.js.map +1 -0
  23. package/dist/data/SupabaseDataProvider.d.ts +55 -0
  24. package/dist/data/SupabaseDataProvider.d.ts.map +1 -0
  25. package/dist/data/SupabaseDataProvider.js +114 -0
  26. package/dist/data/SupabaseDataProvider.js.map +1 -0
  27. package/dist/data/SupabaseDefinitionStore.d.ts +39 -0
  28. package/dist/data/SupabaseDefinitionStore.d.ts.map +1 -0
  29. package/dist/data/SupabaseDefinitionStore.js +419 -0
  30. package/dist/data/SupabaseDefinitionStore.js.map +1 -0
  31. package/dist/data/SupabaseEventSink.d.ts +17 -0
  32. package/dist/data/SupabaseEventSink.d.ts.map +1 -0
  33. package/dist/data/SupabaseEventSink.js +31 -0
  34. package/dist/data/SupabaseEventSink.js.map +1 -0
  35. package/dist/data/SupabaseInviteStore.d.ts +22 -0
  36. package/dist/data/SupabaseInviteStore.d.ts.map +1 -0
  37. package/dist/data/SupabaseInviteStore.js +159 -0
  38. package/dist/data/SupabaseInviteStore.js.map +1 -0
  39. package/dist/data/SupabaseOrgStore.d.ts +33 -0
  40. package/dist/data/SupabaseOrgStore.d.ts.map +1 -0
  41. package/dist/data/SupabaseOrgStore.js +455 -0
  42. package/dist/data/SupabaseOrgStore.js.map +1 -0
  43. package/dist/data/SupabaseProjectStore.d.ts +29 -0
  44. package/dist/data/SupabaseProjectStore.d.ts.map +1 -0
  45. package/dist/data/SupabaseProjectStore.js +328 -0
  46. package/dist/data/SupabaseProjectStore.js.map +1 -0
  47. package/dist/data/SupabaseShareLinkStore.d.ts +26 -0
  48. package/dist/data/SupabaseShareLinkStore.d.ts.map +1 -0
  49. package/dist/data/SupabaseShareLinkStore.js +150 -0
  50. package/dist/data/SupabaseShareLinkStore.js.map +1 -0
  51. package/dist/data/__tests__/compute-server-conformance.test.d.ts +2 -0
  52. package/dist/data/__tests__/compute-server-conformance.test.d.ts.map +1 -0
  53. package/dist/data/__tests__/compute-server-conformance.test.js +40 -0
  54. package/dist/data/__tests__/compute-server-conformance.test.js.map +1 -0
  55. package/dist/data/__tests__/definition-conformance.test.d.ts +2 -0
  56. package/dist/data/__tests__/definition-conformance.test.d.ts.map +1 -0
  57. package/dist/data/__tests__/definition-conformance.test.js +85 -0
  58. package/dist/data/__tests__/definition-conformance.test.js.map +1 -0
  59. package/dist/data/__tests__/event-sink-conformance.test.d.ts +2 -0
  60. package/dist/data/__tests__/event-sink-conformance.test.d.ts.map +1 -0
  61. package/dist/data/__tests__/event-sink-conformance.test.js +27 -0
  62. package/dist/data/__tests__/event-sink-conformance.test.js.map +1 -0
  63. package/dist/data/__tests__/invite-conformance.test.d.ts +2 -0
  64. package/dist/data/__tests__/invite-conformance.test.d.ts.map +1 -0
  65. package/dist/data/__tests__/invite-conformance.test.js +45 -0
  66. package/dist/data/__tests__/invite-conformance.test.js.map +1 -0
  67. package/dist/data/__tests__/org-conformance.test.d.ts +2 -0
  68. package/dist/data/__tests__/org-conformance.test.d.ts.map +1 -0
  69. package/dist/data/__tests__/org-conformance.test.js +29 -0
  70. package/dist/data/__tests__/org-conformance.test.js.map +1 -0
  71. package/dist/data/__tests__/project-conformance.test.d.ts +2 -0
  72. package/dist/data/__tests__/project-conformance.test.d.ts.map +1 -0
  73. package/dist/data/__tests__/project-conformance.test.js +57 -0
  74. package/dist/data/__tests__/project-conformance.test.js.map +1 -0
  75. package/dist/data/__tests__/share-link-conformance.test.d.ts +2 -0
  76. package/dist/data/__tests__/share-link-conformance.test.d.ts.map +1 -0
  77. package/dist/data/__tests__/share-link-conformance.test.js +88 -0
  78. package/dist/data/__tests__/share-link-conformance.test.js.map +1 -0
  79. package/dist/data/__tests__/test-helpers.d.ts +53 -0
  80. package/dist/data/__tests__/test-helpers.d.ts.map +1 -0
  81. package/dist/data/__tests__/test-helpers.js +175 -0
  82. package/dist/data/__tests__/test-helpers.js.map +1 -0
  83. package/dist/data/client.d.ts +44 -0
  84. package/dist/data/client.d.ts.map +1 -0
  85. package/dist/data/client.js +40 -0
  86. package/dist/data/client.js.map +1 -0
  87. package/dist/data/index.d.ts +12 -0
  88. package/dist/data/index.d.ts.map +1 -0
  89. package/dist/data/index.js +11 -0
  90. package/dist/data/index.js.map +1 -0
  91. package/dist/data/pagination.d.ts +30 -0
  92. package/dist/data/pagination.d.ts.map +1 -0
  93. package/dist/data/pagination.js +53 -0
  94. package/dist/data/pagination.js.map +1 -0
  95. package/dist/index.d.ts +9 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +6 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/permissions/SupabasePlatformPermissionStore.d.ts +25 -0
  100. package/dist/permissions/SupabasePlatformPermissionStore.d.ts.map +1 -0
  101. package/dist/permissions/SupabasePlatformPermissionStore.js +153 -0
  102. package/dist/permissions/SupabasePlatformPermissionStore.js.map +1 -0
  103. package/dist/permissions/__tests__/conformance.test.d.ts +2 -0
  104. package/dist/permissions/__tests__/conformance.test.d.ts.map +1 -0
  105. package/dist/permissions/__tests__/conformance.test.js +27 -0
  106. package/dist/permissions/__tests__/conformance.test.js.map +1 -0
  107. package/dist/permissions/index.d.ts +2 -0
  108. package/dist/permissions/index.d.ts.map +1 -0
  109. package/dist/permissions/index.js +2 -0
  110. package/dist/permissions/index.js.map +1 -0
  111. package/dist/storage/SupabaseStorageProvider.d.ts +67 -0
  112. package/dist/storage/SupabaseStorageProvider.d.ts.map +1 -0
  113. package/dist/storage/SupabaseStorageProvider.js +136 -0
  114. package/dist/storage/SupabaseStorageProvider.js.map +1 -0
  115. package/dist/storage/__tests__/conformance.test.d.ts +2 -0
  116. package/dist/storage/__tests__/conformance.test.d.ts.map +1 -0
  117. package/dist/storage/__tests__/conformance.test.js +149 -0
  118. package/dist/storage/__tests__/conformance.test.js.map +1 -0
  119. package/dist/storage/index.d.ts +3 -0
  120. package/dist/storage/index.d.ts.map +1 -0
  121. package/dist/storage/index.js +2 -0
  122. package/dist/storage/index.js.map +1 -0
  123. package/dist/userProfile/SupabaseUserProfileProvider.d.ts +16 -0
  124. package/dist/userProfile/SupabaseUserProfileProvider.d.ts.map +1 -0
  125. package/dist/userProfile/SupabaseUserProfileProvider.js +148 -0
  126. package/dist/userProfile/SupabaseUserProfileProvider.js.map +1 -0
  127. package/dist/userProfile/__tests__/conformance.test.d.ts +2 -0
  128. package/dist/userProfile/__tests__/conformance.test.d.ts.map +1 -0
  129. package/dist/userProfile/__tests__/conformance.test.js +38 -0
  130. package/dist/userProfile/__tests__/conformance.test.js.map +1 -0
  131. package/dist/userProfile/index.d.ts +2 -0
  132. package/dist/userProfile/index.d.ts.map +1 -0
  133. package/dist/userProfile/index.js +2 -0
  134. package/dist/userProfile/index.js.map +1 -0
  135. package/package.json +75 -0
  136. package/src/auth/SupabaseAuthProvider.ts +464 -0
  137. package/src/auth/__tests__/conformance.test.ts +78 -0
  138. package/src/auth/index.ts +2 -0
  139. package/src/data/SupabaseAuditQuery.ts +103 -0
  140. package/src/data/SupabaseComputeServerStore.ts +262 -0
  141. package/src/data/SupabaseDataProvider.ts +125 -0
  142. package/src/data/SupabaseDefinitionStore.ts +504 -0
  143. package/src/data/SupabaseEventSink.ts +30 -0
  144. package/src/data/SupabaseInviteStore.ts +183 -0
  145. package/src/data/SupabaseOrgStore.ts +547 -0
  146. package/src/data/SupabaseProjectStore.ts +414 -0
  147. package/src/data/SupabaseShareLinkStore.ts +191 -0
  148. package/src/data/__tests__/compute-server-conformance.test.ts +40 -0
  149. package/src/data/__tests__/definition-conformance.test.ts +89 -0
  150. package/src/data/__tests__/event-sink-conformance.test.ts +32 -0
  151. package/src/data/__tests__/invite-conformance.test.ts +46 -0
  152. package/src/data/__tests__/org-conformance.test.ts +30 -0
  153. package/src/data/__tests__/project-conformance.test.ts +57 -0
  154. package/src/data/__tests__/share-link-conformance.test.ts +87 -0
  155. package/src/data/__tests__/test-helpers.ts +186 -0
  156. package/src/data/client.ts +86 -0
  157. package/src/data/index.ts +11 -0
  158. package/src/data/pagination.ts +68 -0
  159. package/src/index.ts +23 -0
  160. package/src/permissions/SupabasePlatformPermissionStore.ts +161 -0
  161. package/src/permissions/__tests__/conformance.test.ts +28 -0
  162. package/src/permissions/index.ts +1 -0
  163. package/src/storage/SupabaseStorageProvider.ts +177 -0
  164. package/src/storage/__tests__/conformance.test.ts +152 -0
  165. package/src/storage/index.ts +2 -0
  166. package/src/userProfile/SupabaseUserProfileProvider.ts +177 -0
  167. package/src/userProfile/__tests__/conformance.test.ts +39 -0
  168. package/src/userProfile/index.ts +1 -0
  169. package/supabase/.branches/_current_branch +1 -0
  170. package/supabase/.temp/cli-latest +1 -0
  171. package/supabase/config.toml +68 -0
  172. package/supabase/migrations/0001_initial.sql +1084 -0
  173. package/supabase/seed.sql +13 -0
@@ -0,0 +1,27 @@
1
+ import { describe, beforeEach, it } from 'vitest';
2
+ import { runPlatformPermissionStoreConformance } from '@selvajs/platform/testing';
3
+ import { SupabasePlatformPermissionStore } from '../SupabasePlatformPermissionStore.js';
4
+ import { readEnv, resetAllData, seedPlainUser } from '../../data/__tests__/test-helpers.js';
5
+ const envCtx = readEnv();
6
+ if (!envCtx) {
7
+ describe.skip('SupabasePlatformPermissionStore (skipped: no live stack)', () => {
8
+ it('populate packages/providers/supabase/.env.test with Supabase creds to run these tests', () => { });
9
+ });
10
+ }
11
+ else {
12
+ describe('SupabasePlatformPermissionStore', () => {
13
+ beforeEach(async () => {
14
+ await resetAllData(envCtx);
15
+ });
16
+ runPlatformPermissionStoreConformance({
17
+ name: 'SupabasePlatformPermissionStore',
18
+ createStore: async () => {
19
+ return {
20
+ store: new SupabasePlatformPermissionStore(envCtx.bundle),
21
+ seedUser: async () => (await seedPlainUser(envCtx, '')).userId
22
+ };
23
+ }
24
+ });
25
+ });
26
+ }
27
+ //# sourceMappingURL=conformance.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformance.test.js","sourceRoot":"","sources":["../../../src/permissions/__tests__/conformance.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,qCAAqC,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,+BAA+B,EAAE,MAAM,uCAAuC,CAAC;AACxF,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAE5F,MAAM,MAAM,GAAG,OAAO,EAAE,CAAC;AAEzB,IAAI,CAAC,MAAM,EAAE,CAAC;IACb,QAAQ,CAAC,IAAI,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAC9E,EAAE,CAAC,uFAAuF,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACvG,CAAC,CAAC,CAAC;AACJ,CAAC;KAAM,CAAC;IACP,QAAQ,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAChD,UAAU,CAAC,KAAK,IAAI,EAAE;YACrB,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,qCAAqC,CAAC;YACrC,IAAI,EAAE,iCAAiC;YACvC,WAAW,EAAE,KAAK,IAAI,EAAE;gBACvB,OAAO;oBACN,KAAK,EAAE,IAAI,+BAA+B,CAAC,MAAM,CAAC,MAAM,CAAC;oBACzD,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM;iBAC9D,CAAC;YACH,CAAC;SACD,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { SupabasePlatformPermissionStore } from './SupabasePlatformPermissionStore.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/permissions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,sCAAsC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { SupabasePlatformPermissionStore } from './SupabasePlatformPermissionStore.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/permissions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,sCAAsC,CAAC"}
@@ -0,0 +1,67 @@
1
+ import type { IStorageProvider } from '@selvajs/platform/storage';
2
+ /**
3
+ * Storage backend for Selva on Supabase Storage.
4
+ *
5
+ * Uses two buckets:
6
+ * - {publicBucket} (public): cover images, archive thumbnails, anything
7
+ * CDN-safe. `getPublicUrl` returns the direct CDN URL.
8
+ * - {privateBucket} (private): .gh / .ghx source files. `getPublicUrl`
9
+ * returns the authenticated proxy URL (`${privateUrlPrefix}/{path}`) —
10
+ * the consuming app must have a route at that prefix that authenticates
11
+ * the request and streams the bytes via `get()`.
12
+ *
13
+ * Path routing: any path whose basename matches `definition.{ext}` is
14
+ * considered private. Covers (`cover.webp`) and archives (`archive/{ref}`)
15
+ * stay public. `definitionPaths` from @selvajs/platform produces exactly these
16
+ * names, so the routing is implicit — no caller has to think about buckets.
17
+ */
18
+ export interface SupabaseStorageProviderConfig {
19
+ /** Supabase project URL (e.g. http://127.0.0.1:54321 for local stack). */
20
+ supabaseUrl: string;
21
+ /**
22
+ * Service-role key. Required because the provider runs server-side and
23
+ * must bypass RLS for server-internal actions (admin uploads, janitor
24
+ * cleanup). The provider is never used directly from the browser.
25
+ */
26
+ serviceRoleKey: string;
27
+ /** Bucket for CDN-readable objects. Default: "selva-public". */
28
+ publicBucket?: string;
29
+ /** Bucket for authenticated-only objects. Default: "selva-private". */
30
+ privateBucket?: string;
31
+ /**
32
+ * URL prefix the app exposes for authenticated file downloads. Used by
33
+ * `getPublicUrl` for private-bucket paths. Default: "/api/files".
34
+ * The matching SvelteKit route is expected to call `get()` and stream
35
+ * bytes back after an auth check.
36
+ */
37
+ privateUrlPrefix?: string;
38
+ }
39
+ export declare class SupabaseStorageProvider implements IStorageProvider {
40
+ private readonly client;
41
+ private readonly publicBucket;
42
+ private readonly privateBucket;
43
+ private readonly publicBaseUrl;
44
+ private readonly privateUrlPrefix;
45
+ static fromEnv(env: Record<string, string | undefined>): SupabaseStorageProvider;
46
+ constructor(config: SupabaseStorageProviderConfig);
47
+ /**
48
+ * Route a storage path to a bucket. Any `.gh`/`.ghx` source file is
49
+ * private — confidentiality is determined by extension, not location, so
50
+ * a path-scheme rename (e.g. `definition.gh` → `versions/v1.gh`) can't
51
+ * silently move source files into the public bucket. Everything else
52
+ * (covers, archives, thumbnails) is public.
53
+ */
54
+ private bucketFor;
55
+ get(storagePath: string): Promise<Uint8Array | null>;
56
+ put(storagePath: string, data: Uint8Array, contentType?: string): Promise<void>;
57
+ delete(storagePath: string): Promise<void>;
58
+ deletePrefix(prefix: string): Promise<void>;
59
+ private deletePrefixInBucket;
60
+ /**
61
+ * Recursively list every key under a prefix. `storage-js`'s `list` API
62
+ * only returns the immediate children of a "folder", so we DFS.
63
+ */
64
+ private listAllKeys;
65
+ getPublicUrl(storagePath: string): string;
66
+ }
67
+ //# sourceMappingURL=SupabaseStorageProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SupabaseStorageProvider.d.ts","sourceRoot":"","sources":["../../src/storage/SupabaseStorageProvider.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,6BAA6B;IAC7C,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,uBAAwB,YAAW,gBAAgB;IAC/D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAE1C,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,uBAAuB;gBAcpE,MAAM,EAAE,6BAA6B;IAWjD;;;;;;OAMG;IACH,OAAO,CAAC,SAAS;IAIX,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAcpD,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc/E,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO1C,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YASnC,oBAAoB;IAYlC;;;OAGG;YACW,WAAW;IAuBzB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;CAOzC"}
@@ -0,0 +1,136 @@
1
+ import { createClient } from '@supabase/supabase-js';
2
+ import { transcodeImageIfNeeded } from '@selvajs/platform/storage';
3
+ export class SupabaseStorageProvider {
4
+ client;
5
+ publicBucket;
6
+ privateBucket;
7
+ publicBaseUrl;
8
+ privateUrlPrefix;
9
+ static fromEnv(env) {
10
+ const supabaseUrl = env.SUPABASE_URL;
11
+ const serviceRoleKey = env.SUPABASE_SERVICE_ROLE_KEY;
12
+ if (!supabaseUrl)
13
+ throw new Error('Missing required env var: SUPABASE_URL');
14
+ if (!serviceRoleKey)
15
+ throw new Error('Missing required env var: SUPABASE_SERVICE_ROLE_KEY');
16
+ return new SupabaseStorageProvider({
17
+ supabaseUrl,
18
+ serviceRoleKey,
19
+ publicBucket: env.SUPABASE_PUBLIC_BUCKET,
20
+ privateBucket: env.SUPABASE_PRIVATE_BUCKET,
21
+ privateUrlPrefix: env.SUPABASE_PRIVATE_URL_PREFIX
22
+ });
23
+ }
24
+ constructor(config) {
25
+ this.client = createClient(config.supabaseUrl, config.serviceRoleKey, {
26
+ auth: { persistSession: false, autoRefreshToken: false }
27
+ });
28
+ this.publicBucket = config.publicBucket ?? 'selva-public';
29
+ this.privateBucket = config.privateBucket ?? 'selva-private';
30
+ // Direct CDN URL format: {supabaseUrl}/storage/v1/object/public/{bucket}/{path}
31
+ this.publicBaseUrl = `${config.supabaseUrl.replace(/\/$/, '')}/storage/v1/object/public/${this.publicBucket}`;
32
+ this.privateUrlPrefix = (config.privateUrlPrefix ?? '/api/files').replace(/\/$/, '');
33
+ }
34
+ /**
35
+ * Route a storage path to a bucket. Any `.gh`/`.ghx` source file is
36
+ * private — confidentiality is determined by extension, not location, so
37
+ * a path-scheme rename (e.g. `definition.gh` → `versions/v1.gh`) can't
38
+ * silently move source files into the public bucket. Everything else
39
+ * (covers, archives, thumbnails) is public.
40
+ */
41
+ bucketFor(storagePath) {
42
+ return /\.(gh|ghx)$/i.test(storagePath) ? this.privateBucket : this.publicBucket;
43
+ }
44
+ async get(storagePath) {
45
+ const bucket = this.bucketFor(storagePath);
46
+ const { data, error } = await this.client.storage.from(bucket).download(storagePath);
47
+ if (error) {
48
+ // storage-js surfaces 404 as an error with message "Object not found".
49
+ // Normalize any "not found" to null — we don't have a stable status code.
50
+ const msg = error.message ?? '';
51
+ if (/not found/i.test(msg) || /no such key/i.test(msg))
52
+ return null;
53
+ throw error;
54
+ }
55
+ const buffer = await data.arrayBuffer();
56
+ return new Uint8Array(buffer);
57
+ }
58
+ async put(storagePath, data, contentType) {
59
+ // Normalize images through the shared helper so this provider produces
60
+ // the same bytes / extensions as LocalStorageProvider for the same input.
61
+ const transcoded = await transcodeImageIfNeeded(data, contentType, storagePath);
62
+ const bucket = this.bucketFor(transcoded.path);
63
+ const { error } = await this.client.storage
64
+ .from(bucket)
65
+ .upload(transcoded.path, transcoded.data, {
66
+ contentType: transcoded.contentType,
67
+ upsert: true
68
+ });
69
+ if (error)
70
+ throw error;
71
+ }
72
+ async delete(storagePath) {
73
+ const bucket = this.bucketFor(storagePath);
74
+ const { error } = await this.client.storage.from(bucket).remove([storagePath]);
75
+ // remove() does not error for missing paths — it just returns an empty array.
76
+ if (error)
77
+ throw error;
78
+ }
79
+ async deletePrefix(prefix) {
80
+ // Hit both buckets: a definition's GUID prefix may have files in each.
81
+ // Scoped by the trailing slash in the prefix, so siblings aren't touched.
82
+ await Promise.all([
83
+ this.deletePrefixInBucket(this.publicBucket, prefix),
84
+ this.deletePrefixInBucket(this.privateBucket, prefix)
85
+ ]);
86
+ }
87
+ async deletePrefixInBucket(bucket, prefix) {
88
+ const normalized = prefix.replace(/\/+$/, '');
89
+ const keys = await this.listAllKeys(bucket, normalized);
90
+ if (keys.length === 0)
91
+ return;
92
+ // `remove` accepts up to 1000 paths; batch just in case.
93
+ for (let i = 0; i < keys.length; i += 1000) {
94
+ const slice = keys.slice(i, i + 1000);
95
+ const { error } = await this.client.storage.from(bucket).remove(slice);
96
+ if (error)
97
+ throw error;
98
+ }
99
+ }
100
+ /**
101
+ * Recursively list every key under a prefix. `storage-js`'s `list` API
102
+ * only returns the immediate children of a "folder", so we DFS.
103
+ */
104
+ async listAllKeys(bucket, prefix) {
105
+ const out = [];
106
+ const stack = [prefix];
107
+ while (stack.length > 0) {
108
+ const dir = stack.pop();
109
+ const { data, error } = await this.client.storage.from(bucket).list(dir, {
110
+ limit: 1000,
111
+ sortBy: { column: 'name', order: 'asc' }
112
+ });
113
+ if (error)
114
+ throw error;
115
+ for (const entry of data ?? []) {
116
+ const full = dir ? `${dir}/${entry.name}` : entry.name;
117
+ // Folder entries have `id === null` in storage-js.
118
+ if (entry.id === null) {
119
+ stack.push(full);
120
+ }
121
+ else {
122
+ out.push(full);
123
+ }
124
+ }
125
+ }
126
+ return out;
127
+ }
128
+ getPublicUrl(storagePath) {
129
+ if (this.bucketFor(storagePath) === this.publicBucket) {
130
+ return `${this.publicBaseUrl}/${storagePath}`;
131
+ }
132
+ // Private files: the app's authenticated proxy route. See `privateUrlPrefix`.
133
+ return `${this.privateUrlPrefix}/${storagePath}`;
134
+ }
135
+ }
136
+ //# sourceMappingURL=SupabaseStorageProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SupabaseStorageProvider.js","sourceRoot":"","sources":["../../src/storage/SupabaseStorageProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=conformance.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformance.test.d.ts","sourceRoot":"","sources":["../../../src/storage/__tests__/conformance.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,149 @@
1
+ import { describe, beforeEach, it, expect } from 'vitest';
2
+ import { createClient } from '@supabase/supabase-js';
3
+ import { runStorageProviderConformance } from '@selvajs/platform/testing';
4
+ import { definitionPaths } from '@selvajs/platform';
5
+ import { SupabaseStorageProvider } from '../SupabaseStorageProvider.js';
6
+ const SUPABASE_URL = process.env.SUPABASE_URL;
7
+ const SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY;
8
+ const PUBLIC_BUCKET = process.env.SUPABASE_PUBLIC_BUCKET ?? 'selva-public';
9
+ const PRIVATE_BUCKET = process.env.SUPABASE_PRIVATE_BUCKET ?? 'selva-private';
10
+ // The conformance suite hits a live Supabase stack. If creds aren't present
11
+ // (e.g. in CI without Docker, or a clean clone pre-`supabase start`), skip
12
+ // the suite with a single explanatory test so the run surfaces the reason.
13
+ // `createClient` transitively loads `@supabase/realtime-js`, which on Node < 22
14
+ // throws at construction without native WebSocket — treat that as "no stack"
15
+ // rather than crashing the suite during collection.
16
+ function tryBuildAdminClient() {
17
+ if (!SUPABASE_URL || !SERVICE_ROLE_KEY)
18
+ return null;
19
+ try {
20
+ return createClient(SUPABASE_URL, SERVICE_ROLE_KEY, {
21
+ auth: { persistSession: false, autoRefreshToken: false }
22
+ });
23
+ }
24
+ catch (err) {
25
+ const msg = err instanceof Error ? err.message : String(err);
26
+ if (msg.includes('WebSocket'))
27
+ return null;
28
+ throw err;
29
+ }
30
+ }
31
+ const _adminClient = tryBuildAdminClient();
32
+ if (_adminClient === null) {
33
+ describe.skip('SupabaseStorageProvider (skipped: no live stack)', () => {
34
+ it('populate packages/providers/supabase/.env.test with SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY (from `npx supabase start`) to run these tests', () => { });
35
+ });
36
+ }
37
+ else {
38
+ // Re-bind in this branch so closures below see the non-null narrowed type.
39
+ const adminClient = _adminClient;
40
+ /**
41
+ * Delete every object under a prefix in a bucket. The conformance suite
42
+ * assumes fresh state per test; Supabase Storage is shared across runs,
43
+ * so we reset both buckets before each test.
44
+ */
45
+ async function emptyBucket(bucket) {
46
+ const keys = [];
47
+ const stack = [''];
48
+ while (stack.length > 0) {
49
+ const dir = stack.pop();
50
+ const { data, error } = await adminClient.storage.from(bucket).list(dir, {
51
+ limit: 1000,
52
+ sortBy: { column: 'name', order: 'asc' }
53
+ });
54
+ if (error)
55
+ throw error;
56
+ for (const entry of data ?? []) {
57
+ const full = dir ? `${dir}/${entry.name}` : entry.name;
58
+ if (entry.id === null)
59
+ stack.push(full);
60
+ else
61
+ keys.push(full);
62
+ }
63
+ }
64
+ if (keys.length === 0)
65
+ return;
66
+ for (let i = 0; i < keys.length; i += 1000) {
67
+ const { error } = await adminClient.storage.from(bucket).remove(keys.slice(i, i + 1000));
68
+ if (error)
69
+ throw error;
70
+ }
71
+ }
72
+ describe('SupabaseStorageProvider', () => {
73
+ beforeEach(async () => {
74
+ await Promise.all([emptyBucket(PUBLIC_BUCKET), emptyBucket(PRIVATE_BUCKET)]);
75
+ });
76
+ runStorageProviderConformance({
77
+ name: 'SupabaseStorageProvider',
78
+ createStorage: () => new SupabaseStorageProvider({
79
+ supabaseUrl: SUPABASE_URL,
80
+ serviceRoleKey: SERVICE_ROLE_KEY,
81
+ publicBucket: PUBLIC_BUCKET,
82
+ privateBucket: PRIVATE_BUCKET
83
+ })
84
+ });
85
+ // ============================================================================
86
+ // Bucket routing — Supabase-specific (the conformance suite is provider-
87
+ // agnostic and can't see which bucket a file lands in). Without these tests
88
+ // a regex regression in `bucketFor` could silently move .gh source files
89
+ // into the public/CDN bucket.
90
+ // ============================================================================
91
+ describe('bucket routing', () => {
92
+ const storage = new SupabaseStorageProvider({
93
+ supabaseUrl: SUPABASE_URL,
94
+ serviceRoleKey: SERVICE_ROLE_KEY,
95
+ publicBucket: PUBLIC_BUCKET,
96
+ privateBucket: PRIVATE_BUCKET
97
+ });
98
+ async function existsInBucket(bucket, path) {
99
+ const { data, error } = await adminClient.storage.from(bucket).download(path);
100
+ if (error)
101
+ return false;
102
+ return Boolean(data);
103
+ }
104
+ it('versioned .gh files land in the private bucket', async () => {
105
+ const guid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
106
+ const path = definitionPaths.version(guid, 1, 'gh');
107
+ await storage.put(path, new TextEncoder().encode('GH_BYTES'));
108
+ expect(await existsInBucket(PRIVATE_BUCKET, path)).toBe(true);
109
+ expect(await existsInBucket(PUBLIC_BUCKET, path)).toBe(false);
110
+ });
111
+ it('versioned .ghx files land in the private bucket', async () => {
112
+ const guid = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb';
113
+ const path = definitionPaths.version(guid, 3, 'ghx');
114
+ await storage.put(path, new TextEncoder().encode('GHX_BYTES'));
115
+ expect(await existsInBucket(PRIVATE_BUCKET, path)).toBe(true);
116
+ expect(await existsInBucket(PUBLIC_BUCKET, path)).toBe(false);
117
+ });
118
+ it('cover images land in the public bucket', async () => {
119
+ const guid = 'cccccccc-cccc-cccc-cccc-cccccccccccc';
120
+ const path = definitionPaths.image(guid);
121
+ // Real 1×1 transparent PNG. The provider runs every image through
122
+ // transcodeImageIfNeeded → sharp, so a hand-crafted byte stub
123
+ // would fail the decode. Routing (the test's actual concern) is
124
+ // independent of which format the input arrives in.
125
+ const tinyPng = Uint8Array.from([
126
+ 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1, 8,
127
+ 6, 0, 0, 0, 31, 21, 196, 137, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 3, 232, 0, 0, 3, 232, 1,
128
+ 181, 123, 82, 107, 0, 0, 0, 13, 73, 68, 65, 84, 120, 156, 99, 96, 96, 96, 96, 0, 0, 0, 5,
129
+ 0, 1, 165, 246, 69, 64, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130
130
+ ]);
131
+ await storage.put(path, tinyPng, 'image/png');
132
+ expect(await existsInBucket(PUBLIC_BUCKET, path)).toBe(true);
133
+ expect(await existsInBucket(PRIVATE_BUCKET, path)).toBe(false);
134
+ });
135
+ it('getPublicUrl returns the proxy prefix for private files', () => {
136
+ const path = definitionPaths.version('guid-1', 1, 'gh');
137
+ const url = storage.getPublicUrl(path);
138
+ expect(url.startsWith('/api/files/')).toBe(true);
139
+ expect(url).not.toContain('/storage/v1/object/public/');
140
+ });
141
+ it('getPublicUrl returns the CDN URL for public files', () => {
142
+ const path = definitionPaths.image('guid-1');
143
+ const url = storage.getPublicUrl(path);
144
+ expect(url).toContain(`/storage/v1/object/public/${PUBLIC_BUCKET}/`);
145
+ });
146
+ });
147
+ });
148
+ }
149
+ //# sourceMappingURL=conformance.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformance.test.js","sourceRoot":"","sources":["../../../src/storage/__tests__/conformance.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { SupabaseStorageProvider } from './SupabaseStorageProvider.js';
2
+ export type { SupabaseStorageProviderConfig } from './SupabaseStorageProvider.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,YAAY,EAAE,6BAA6B,EAAE,MAAM,8BAA8B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { SupabaseStorageProvider } from './SupabaseStorageProvider.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { IUserProfileStore, RecentRun, RequestContext, UserManagementResult, UserProfile } from '@selvajs/platform';
2
+ import type { ClientBundle } from '../data/client.js';
3
+ export declare class SupabaseUserProfileProvider implements IUserProfileStore {
4
+ private readonly clients;
5
+ constructor(clients: ClientBundle);
6
+ private client;
7
+ getProfile(ctx: RequestContext, userId: string): Promise<UserProfile | null>;
8
+ getProfiles(_ctx: RequestContext, userIds: readonly string[]): Promise<UserProfile[]>;
9
+ updateProfile(ctx: RequestContext, userId: string, patch: {
10
+ displayName?: string;
11
+ }): Promise<UserManagementResult>;
12
+ starDefinition(ctx: RequestContext, userId: string, definitionId: string): Promise<UserManagementResult>;
13
+ unstarDefinition(ctx: RequestContext, userId: string, definitionId: string): Promise<UserManagementResult>;
14
+ recordRun(ctx: RequestContext, userId: string, run: RecentRun): Promise<UserManagementResult>;
15
+ }
16
+ //# sourceMappingURL=SupabaseUserProfileProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SupabaseUserProfileProvider.d.ts","sourceRoot":"","sources":["../../src/userProfile/SupabaseUserProfileProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,iBAAiB,EACjB,SAAS,EACT,cAAc,EACd,oBAAoB,EACpB,WAAW,EACX,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAetD,qBAAa,2BAA4B,YAAW,iBAAiB;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,YAAY;IAElD,OAAO,CAAC,MAAM;IAIR,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAW5E,WAAW,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAYrF,aAAa,CAClB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAC7B,OAAO,CAAC,oBAAoB,CAAC;IAe1B,cAAc,CACnB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GAClB,OAAO,CAAC,oBAAoB,CAAC;IAmB1B,gBAAgB,CACrB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GAClB,OAAO,CAAC,oBAAoB,CAAC;IAgB1B,SAAS,CACd,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,SAAS,GACZ,OAAO,CAAC,oBAAoB,CAAC;CAiBhC"}
@@ -0,0 +1,148 @@
1
+ import { ProviderError, hasPermission } from '@selvajs/platform';
2
+ /**
3
+ * User profile backed by `public.user_profiles` (one row per `auth.users.id`).
4
+ * The row is auto-seeded by the `handle_new_auth_user` trigger on signup, so
5
+ * the store never inserts — only reads/updates.
6
+ *
7
+ * Uses the service-role client throughout (RLS on `user_profiles` is
8
+ * coarse-grained — server-side flows like the hooks.server.ts profile load
9
+ * need access regardless of the caller's identity). The `assertCanAccess`
10
+ * check enforces "you can only see/modify your own profile" at the app layer
11
+ * to match the `IUserProfileStore` contract.
12
+ */
13
+ const MAX_RECENT_RUNS = 20;
14
+ export class SupabaseUserProfileProvider {
15
+ clients;
16
+ constructor(clients) {
17
+ this.clients = clients;
18
+ }
19
+ client() {
20
+ return this.clients.serviceClient;
21
+ }
22
+ async getProfile(ctx, userId) {
23
+ assertCanAccess(ctx, userId);
24
+ const { data, error } = await this.client()
25
+ .from('user_profiles')
26
+ .select('user_id, display_name, starred_definitions, recent_runs')
27
+ .eq('user_id', userId)
28
+ .maybeSingle();
29
+ if (error)
30
+ throw mapError(error);
31
+ return data ? rowToProfile(data) : null;
32
+ }
33
+ async getProfiles(_ctx, userIds) {
34
+ // Batch read serves display-name lookups across the UI; scoping to one
35
+ // user defeats the purpose. No assertCanAccess.
36
+ if (userIds.length === 0)
37
+ return [];
38
+ const { data, error } = await this.client()
39
+ .from('user_profiles')
40
+ .select('user_id, display_name, starred_definitions, recent_runs')
41
+ .in('user_id', [...userIds]);
42
+ if (error)
43
+ throw mapError(error);
44
+ return (data ?? []).map(rowToProfile);
45
+ }
46
+ async updateProfile(ctx, userId, patch) {
47
+ assertCanAccess(ctx, userId);
48
+ const row = {};
49
+ if (patch.displayName !== undefined)
50
+ row.display_name = patch.displayName;
51
+ if (Object.keys(row).length === 0)
52
+ return 'ok';
53
+ const { data, error } = await this.client()
54
+ .from('user_profiles')
55
+ .update(row)
56
+ .eq('user_id', userId)
57
+ .select('user_id');
58
+ if (error)
59
+ throw mapError(error);
60
+ return data && data.length > 0 ? 'ok' : 'not_found';
61
+ }
62
+ async starDefinition(ctx, userId, definitionId) {
63
+ assertCanAccess(ctx, userId);
64
+ // Read-modify-write is fine for the local scale here; if concurrent
65
+ // starring ever becomes a hotspot, a SECURITY DEFINER RPC using
66
+ // `array_append` + `array_distinct` closes the race.
67
+ const existing = await this.getProfile(ctx, userId);
68
+ if (!existing)
69
+ return 'not_found';
70
+ if (existing.starredDefinitions.includes(definitionId))
71
+ return 'ok';
72
+ const next = [...existing.starredDefinitions, definitionId];
73
+ const { data, error } = await this.client()
74
+ .from('user_profiles')
75
+ .update({ starred_definitions: next })
76
+ .eq('user_id', userId)
77
+ .select('user_id');
78
+ if (error)
79
+ throw mapError(error);
80
+ return data && data.length > 0 ? 'ok' : 'not_found';
81
+ }
82
+ async unstarDefinition(ctx, userId, definitionId) {
83
+ assertCanAccess(ctx, userId);
84
+ const existing = await this.getProfile(ctx, userId);
85
+ if (!existing)
86
+ return 'not_found';
87
+ if (!existing.starredDefinitions.includes(definitionId))
88
+ return 'ok';
89
+ const next = existing.starredDefinitions.filter((id) => id !== definitionId);
90
+ const { data, error } = await this.client()
91
+ .from('user_profiles')
92
+ .update({ starred_definitions: next })
93
+ .eq('user_id', userId)
94
+ .select('user_id');
95
+ if (error)
96
+ throw mapError(error);
97
+ return data && data.length > 0 ? 'ok' : 'not_found';
98
+ }
99
+ async recordRun(ctx, userId, run) {
100
+ assertCanAccess(ctx, userId);
101
+ const existing = await this.getProfile(ctx, userId);
102
+ if (!existing)
103
+ return 'not_found';
104
+ // Prepend newest, de-dupe by definitionId, cap at MAX_RECENT_RUNS.
105
+ const filtered = existing.recentRuns.filter((r) => r.definitionId !== run.definitionId);
106
+ const next = [run, ...filtered].slice(0, MAX_RECENT_RUNS);
107
+ const { data, error } = await this.client()
108
+ .from('user_profiles')
109
+ .update({ recent_runs: next })
110
+ .eq('user_id', userId)
111
+ .select('user_id');
112
+ if (error)
113
+ throw mapError(error);
114
+ return data && data.length > 0 ? 'ok' : 'not_found';
115
+ }
116
+ }
117
+ function assertCanAccess(ctx, userId) {
118
+ if (ctx.system)
119
+ return;
120
+ if (ctx.userId === userId)
121
+ return;
122
+ if (hasPermission(ctx, 'instance_admin'))
123
+ return;
124
+ throw new ProviderError('Forbidden: cannot access another user’s profile', 403);
125
+ }
126
+ function rowToProfile(row) {
127
+ return {
128
+ userId: row.user_id,
129
+ displayName: row.display_name ?? undefined,
130
+ starredDefinitions: row.starred_definitions ?? [],
131
+ recentRuns: row.recent_runs ?? []
132
+ };
133
+ }
134
+ function mapError(e) {
135
+ const pg = e;
136
+ if (pg?.code === '23505')
137
+ return new ProviderError(pg.message ?? 'Duplicate record', 409);
138
+ if (pg?.code === '23503')
139
+ return new ProviderError(pg.message ?? 'Foreign key violation', 409);
140
+ if (e instanceof Error)
141
+ return e;
142
+ if (e && typeof e === 'object') {
143
+ const obj = e;
144
+ return new Error(obj.code ? `[${obj.code}] ${obj.message ?? ''}` : (obj.message ?? String(e)));
145
+ }
146
+ return new Error(String(e));
147
+ }
148
+ //# sourceMappingURL=SupabaseUserProfileProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SupabaseUserProfileProvider.js","sourceRoot":"","sources":["../../src/userProfile/SupabaseUserProfileProvider.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGjE;;;;;;;;;;GAUG;AACH,MAAM,eAAe,GAAG,EAAE,CAAC;AAE3B,MAAM,OAAO,2BAA2B;IACV;IAA7B,YAA6B,OAAqB;QAArB,YAAO,GAAP,OAAO,CAAc;IAAG,CAAC;IAE9C,MAAM;QACb,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,GAAmB,EAAE,MAAc;QACnD,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE;aACzC,IAAI,CAAC,eAAe,CAAC;aACrB,MAAM,CAAC,yDAAyD,CAAC;aACjE,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC;aACrB,WAAW,EAAE,CAAC;QAChB,IAAI,KAAK;YAAE,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,IAAoB,EAAE,OAA0B;QACjE,uEAAuE;QACvE,gDAAgD;QAChD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QACpC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE;aACzC,IAAI,CAAC,eAAe,CAAC;aACrB,MAAM,CAAC,yDAAyD,CAAC;aACjE,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;QAC9B,IAAI,KAAK;YAAE,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC;QACjC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,aAAa,CAClB,GAAmB,EACnB,MAAc,EACd,KAA+B;QAE/B,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS;YAAE,GAAG,CAAC,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC;QAC1E,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAE/C,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE;aACzC,IAAI,CAAC,eAAe,CAAC;aACrB,MAAM,CAAC,GAAG,CAAC;aACX,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC;aACrB,MAAM,CAAC,SAAS,CAAC,CAAC;QACpB,IAAI,KAAK;YAAE,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC;QACjC,OAAO,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,cAAc,CACnB,GAAmB,EACnB,MAAc,EACd,YAAoB;QAEpB,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC7B,oEAAoE;QACpE,gEAAgE;QAChE,qDAAqD;QACrD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACpD,IAAI,CAAC,QAAQ;YAAE,OAAO,WAAW,CAAC;QAClC,IAAI,QAAQ,CAAC,kBAAkB,CAAC,QAAQ,CAAC,YAAY,CAAC;YAAE,OAAO,IAAI,CAAC;QAEpE,MAAM,IAAI,GAAG,CAAC,GAAG,QAAQ,CAAC,kBAAkB,EAAE,YAAY,CAAC,CAAC;QAC5D,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE;aACzC,IAAI,CAAC,eAAe,CAAC;aACrB,MAAM,CAAC,EAAE,mBAAmB,EAAE,IAAI,EAAE,CAAC;aACrC,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC;aACrB,MAAM,CAAC,SAAS,CAAC,CAAC;QACpB,IAAI,KAAK;YAAE,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC;QACjC,OAAO,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,gBAAgB,CACrB,GAAmB,EACnB,MAAc,EACd,YAAoB;QAEpB,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACpD,IAAI,CAAC,QAAQ;YAAE,OAAO,WAAW,CAAC;QAClC,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,QAAQ,CAAC,YAAY,CAAC;YAAE,OAAO,IAAI,CAAC;QAErE,MAAM,IAAI,GAAG,QAAQ,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,YAAY,CAAC,CAAC;QAC7E,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE;aACzC,IAAI,CAAC,eAAe,CAAC;aACrB,MAAM,CAAC,EAAE,mBAAmB,EAAE,IAAI,EAAE,CAAC;aACrC,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC;aACrB,MAAM,CAAC,SAAS,CAAC,CAAC;QACpB,IAAI,KAAK;YAAE,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC;QACjC,OAAO,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,SAAS,CACd,GAAmB,EACnB,MAAc,EACd,GAAc;QAEd,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACpD,IAAI,CAAC,QAAQ;YAAE,OAAO,WAAW,CAAC;QAElC,mEAAmE;QACnE,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,GAAG,CAAC,YAAY,CAAC,CAAC;QACxF,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC;QAE1D,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE;aACzC,IAAI,CAAC,eAAe,CAAC;aACrB,MAAM,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;aAC7B,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC;aACrB,MAAM,CAAC,SAAS,CAAC,CAAC;QACpB,IAAI,KAAK;YAAE,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC;QACjC,OAAO,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC;IACrD,CAAC;CACD;AAED,SAAS,eAAe,CAAC,GAAmB,EAAE,MAAc;IAC3D,IAAI,GAAG,CAAC,MAAM;QAAE,OAAO;IACvB,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO;IAClC,IAAI,aAAa,CAAC,GAAG,EAAE,gBAAgB,CAAC;QAAE,OAAO;IACjD,MAAM,IAAI,aAAa,CAAC,iDAAiD,EAAE,GAAG,CAAC,CAAC;AACjF,CAAC;AASD,SAAS,YAAY,CAAC,GAAe;IACpC,OAAO;QACN,MAAM,EAAE,GAAG,CAAC,OAAO;QACnB,WAAW,EAAE,GAAG,CAAC,YAAY,IAAI,SAAS;QAC1C,kBAAkB,EAAE,GAAG,CAAC,mBAAmB,IAAI,EAAE;QACjD,UAAU,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE;KACjC,CAAC;AACH,CAAC;AAOD,SAAS,QAAQ,CAAC,CAAU;IAC3B,MAAM,EAAE,GAAG,CAAmB,CAAC;IAC/B,IAAI,EAAE,EAAE,IAAI,KAAK,OAAO;QAAE,OAAO,IAAI,aAAa,CAAC,EAAE,CAAC,OAAO,IAAI,kBAAkB,EAAE,GAAG,CAAC,CAAC;IAC1F,IAAI,EAAE,EAAE,IAAI,KAAK,OAAO;QAAE,OAAO,IAAI,aAAa,CAAC,EAAE,CAAC,OAAO,IAAI,uBAAuB,EAAE,GAAG,CAAC,CAAC;IAC/F,IAAI,CAAC,YAAY,KAAK;QAAE,OAAO,CAAC,CAAC;IACjC,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,CAAwC,CAAC;QACrD,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChG,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=conformance.test.d.ts.map