@selvajs/supabase-provider 0.14.3 → 0.14.4-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/LICENSE +1 -1
  2. package/dist/auth/SupabaseAuthProvider.d.ts +57 -0
  3. package/dist/auth/SupabaseAuthProvider.d.ts.map +1 -1
  4. package/dist/auth/SupabaseAuthProvider.js +123 -18
  5. package/dist/auth/SupabaseAuthProvider.js.map +1 -1
  6. package/dist/data/SupabaseComputeServerStore.d.ts +34 -2
  7. package/dist/data/SupabaseComputeServerStore.d.ts.map +1 -1
  8. package/dist/data/SupabaseComputeServerStore.js +146 -39
  9. package/dist/data/SupabaseComputeServerStore.js.map +1 -1
  10. package/dist/data/SupabaseDataProvider.d.ts +18 -3
  11. package/dist/data/SupabaseDataProvider.d.ts.map +1 -1
  12. package/dist/data/SupabaseDataProvider.js +83 -18
  13. package/dist/data/SupabaseDataProvider.js.map +1 -1
  14. package/dist/data/SupabaseDefinitionStore.d.ts +3 -1
  15. package/dist/data/SupabaseDefinitionStore.d.ts.map +1 -1
  16. package/dist/data/SupabaseDefinitionStore.js +94 -67
  17. package/dist/data/SupabaseDefinitionStore.js.map +1 -1
  18. package/dist/data/SupabaseInviteStore.d.ts.map +1 -1
  19. package/dist/data/SupabaseInviteStore.js +11 -22
  20. package/dist/data/SupabaseInviteStore.js.map +1 -1
  21. package/dist/data/SupabaseOrgStore.d.ts.map +1 -1
  22. package/dist/data/SupabaseOrgStore.js +49 -86
  23. package/dist/data/SupabaseOrgStore.js.map +1 -1
  24. package/dist/data/SupabaseProjectStore.d.ts.map +1 -1
  25. package/dist/data/SupabaseProjectStore.js +51 -70
  26. package/dist/data/SupabaseProjectStore.js.map +1 -1
  27. package/dist/data/SupabaseShareLinkStore.d.ts.map +1 -1
  28. package/dist/data/SupabaseShareLinkStore.js +13 -27
  29. package/dist/data/SupabaseShareLinkStore.js.map +1 -1
  30. package/dist/data/client.d.ts +35 -3
  31. package/dist/data/client.d.ts.map +1 -1
  32. package/dist/data/client.js +67 -16
  33. package/dist/data/client.js.map +1 -1
  34. package/dist/data/errors.d.ts +2 -0
  35. package/dist/data/errors.d.ts.map +1 -0
  36. package/dist/data/errors.js +23 -0
  37. package/dist/data/errors.js.map +1 -0
  38. package/dist/data/index.d.ts +8 -2
  39. package/dist/data/index.d.ts.map +1 -1
  40. package/dist/data/index.js +6 -1
  41. package/dist/data/index.js.map +1 -1
  42. package/dist/data/migrationHead.d.ts +14 -0
  43. package/dist/data/migrationHead.d.ts.map +1 -0
  44. package/dist/data/migrationHead.js +14 -0
  45. package/dist/data/migrationHead.js.map +1 -0
  46. package/dist/data/query.d.ts +20 -0
  47. package/dist/data/query.d.ts.map +1 -0
  48. package/dist/data/query.js +13 -0
  49. package/dist/data/query.js.map +1 -0
  50. package/dist/data/rowStamp.d.ts +38 -0
  51. package/dist/data/rowStamp.d.ts.map +1 -0
  52. package/dist/data/rowStamp.js +43 -0
  53. package/dist/data/rowStamp.js.map +1 -0
  54. package/dist/index.d.ts +2 -2
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +1 -1
  57. package/dist/index.js.map +1 -1
  58. package/dist/permissions/SupabasePlatformPermissionStore.d.ts +5 -3
  59. package/dist/permissions/SupabasePlatformPermissionStore.d.ts.map +1 -1
  60. package/dist/permissions/SupabasePlatformPermissionStore.js +17 -30
  61. package/dist/permissions/SupabasePlatformPermissionStore.js.map +1 -1
  62. package/dist/storage/SupabaseStorageProvider.d.ts.map +1 -1
  63. package/dist/storage/SupabaseStorageProvider.js +3 -0
  64. package/dist/storage/SupabaseStorageProvider.js.map +1 -1
  65. package/package.json +13 -14
  66. package/supabase/.branches/_current_branch +1 -0
  67. package/supabase/.temp/cli-latest +1 -0
  68. package/supabase/migrations/20260711120000_selva_definition_schema_version.sql +18 -0
  69. package/supabase/migrations/20260712120000_user_profiles_disabled_and_gin.sql +95 -0
  70. package/supabase/migrations/20260712123000_selva_migration_head_fn.sql +38 -0
  71. package/supabase/migrations/20260713120000_selva_next_version_number.sql +59 -0
  72. package/supabase/migrations/20260713140000_selva_solve_cache_limit.sql +15 -0
  73. package/dist/auth/__tests__/conformance.test.d.ts +0 -2
  74. package/dist/auth/__tests__/conformance.test.d.ts.map +0 -1
  75. package/dist/auth/__tests__/conformance.test.js +0 -76
  76. package/dist/auth/__tests__/conformance.test.js.map +0 -1
  77. package/dist/data/__tests__/compute-server-conformance.test.d.ts +0 -2
  78. package/dist/data/__tests__/compute-server-conformance.test.d.ts.map +0 -1
  79. package/dist/data/__tests__/compute-server-conformance.test.js +0 -40
  80. package/dist/data/__tests__/compute-server-conformance.test.js.map +0 -1
  81. package/dist/data/__tests__/definition-conformance.test.d.ts +0 -2
  82. package/dist/data/__tests__/definition-conformance.test.d.ts.map +0 -1
  83. package/dist/data/__tests__/definition-conformance.test.js +0 -85
  84. package/dist/data/__tests__/definition-conformance.test.js.map +0 -1
  85. package/dist/data/__tests__/event-sink-conformance.test.d.ts +0 -2
  86. package/dist/data/__tests__/event-sink-conformance.test.d.ts.map +0 -1
  87. package/dist/data/__tests__/event-sink-conformance.test.js +0 -27
  88. package/dist/data/__tests__/event-sink-conformance.test.js.map +0 -1
  89. package/dist/data/__tests__/invite-conformance.test.d.ts +0 -2
  90. package/dist/data/__tests__/invite-conformance.test.d.ts.map +0 -1
  91. package/dist/data/__tests__/invite-conformance.test.js +0 -45
  92. package/dist/data/__tests__/invite-conformance.test.js.map +0 -1
  93. package/dist/data/__tests__/org-conformance.test.d.ts +0 -2
  94. package/dist/data/__tests__/org-conformance.test.d.ts.map +0 -1
  95. package/dist/data/__tests__/org-conformance.test.js +0 -29
  96. package/dist/data/__tests__/org-conformance.test.js.map +0 -1
  97. package/dist/data/__tests__/project-conformance.test.d.ts +0 -2
  98. package/dist/data/__tests__/project-conformance.test.d.ts.map +0 -1
  99. package/dist/data/__tests__/project-conformance.test.js +0 -57
  100. package/dist/data/__tests__/project-conformance.test.js.map +0 -1
  101. package/dist/data/__tests__/share-link-conformance.test.d.ts +0 -2
  102. package/dist/data/__tests__/share-link-conformance.test.d.ts.map +0 -1
  103. package/dist/data/__tests__/share-link-conformance.test.js +0 -88
  104. package/dist/data/__tests__/share-link-conformance.test.js.map +0 -1
  105. package/dist/data/__tests__/solve-metric-sink-conformance.test.d.ts +0 -2
  106. package/dist/data/__tests__/solve-metric-sink-conformance.test.d.ts.map +0 -1
  107. package/dist/data/__tests__/solve-metric-sink-conformance.test.js +0 -52
  108. package/dist/data/__tests__/solve-metric-sink-conformance.test.js.map +0 -1
  109. package/dist/data/__tests__/test-helpers.d.ts +0 -52
  110. package/dist/data/__tests__/test-helpers.d.ts.map +0 -1
  111. package/dist/data/__tests__/test-helpers.js +0 -177
  112. package/dist/data/__tests__/test-helpers.js.map +0 -1
  113. package/dist/permissions/__tests__/conformance.test.d.ts +0 -2
  114. package/dist/permissions/__tests__/conformance.test.d.ts.map +0 -1
  115. package/dist/permissions/__tests__/conformance.test.js +0 -27
  116. package/dist/permissions/__tests__/conformance.test.js.map +0 -1
  117. package/dist/storage/__tests__/conformance.test.d.ts +0 -2
  118. package/dist/storage/__tests__/conformance.test.d.ts.map +0 -1
  119. package/dist/storage/__tests__/conformance.test.js +0 -149
  120. package/dist/storage/__tests__/conformance.test.js.map +0 -1
  121. package/dist/userProfile/__tests__/conformance.test.d.ts +0 -2
  122. package/dist/userProfile/__tests__/conformance.test.d.ts.map +0 -1
  123. package/dist/userProfile/__tests__/conformance.test.js +0 -38
  124. package/dist/userProfile/__tests__/conformance.test.js.map +0 -1
  125. package/src/auth/SupabaseAuthProvider.ts +0 -464
  126. package/src/auth/__tests__/conformance.test.ts +0 -78
  127. package/src/auth/index.ts +0 -2
  128. package/src/cli/sync-migrations.ts +0 -133
  129. package/src/data/SupabaseAuditQuery.ts +0 -103
  130. package/src/data/SupabaseComputeServerStore.ts +0 -262
  131. package/src/data/SupabaseDataProvider.ts +0 -133
  132. package/src/data/SupabaseDefinitionStore.ts +0 -520
  133. package/src/data/SupabaseEventSink.ts +0 -30
  134. package/src/data/SupabaseInviteStore.ts +0 -183
  135. package/src/data/SupabaseOrgStore.ts +0 -552
  136. package/src/data/SupabaseProjectStore.ts +0 -454
  137. package/src/data/SupabaseShareLinkStore.ts +0 -191
  138. package/src/data/SupabaseSolveMetricSink.ts +0 -38
  139. package/src/data/__tests__/compute-server-conformance.test.ts +0 -40
  140. package/src/data/__tests__/definition-conformance.test.ts +0 -89
  141. package/src/data/__tests__/event-sink-conformance.test.ts +0 -32
  142. package/src/data/__tests__/invite-conformance.test.ts +0 -46
  143. package/src/data/__tests__/org-conformance.test.ts +0 -30
  144. package/src/data/__tests__/project-conformance.test.ts +0 -57
  145. package/src/data/__tests__/share-link-conformance.test.ts +0 -87
  146. package/src/data/__tests__/solve-metric-sink-conformance.test.ts +0 -59
  147. package/src/data/__tests__/test-helpers.ts +0 -188
  148. package/src/data/client.ts +0 -98
  149. package/src/data/index.ts +0 -12
  150. package/src/data/pagination.ts +0 -68
  151. package/src/index.ts +0 -24
  152. package/src/permissions/SupabasePlatformPermissionStore.ts +0 -161
  153. package/src/permissions/__tests__/conformance.test.ts +0 -28
  154. package/src/permissions/index.ts +0 -1
  155. package/src/storage/SupabaseStorageProvider.ts +0 -182
  156. package/src/storage/__tests__/conformance.test.ts +0 -152
  157. package/src/storage/index.ts +0 -2
  158. package/src/userProfile/SupabaseUserProfileProvider.ts +0 -177
  159. package/src/userProfile/__tests__/conformance.test.ts +0 -39
  160. package/src/userProfile/index.ts +0 -1
@@ -1,40 +0,0 @@
1
- import { describe, beforeEach, it } from 'vitest';
2
- import { runComputeServerStoreConformance } from '@selvajs/platform/testing';
3
- import { SupabaseComputeServerStore } from '../SupabaseComputeServerStore.js';
4
- import { readEnv, resetAllData, seedUser } from './test-helpers.js';
5
-
6
- const envCtx = readEnv();
7
-
8
- if (!envCtx) {
9
- describe.skip('SupabaseComputeServerStore (skipped: no live stack)', () => {
10
- it('populate packages/providers/supabase/.env.test with Supabase creds to run these tests', () => {});
11
- });
12
- } else {
13
- describe('SupabaseComputeServerStore', () => {
14
- beforeEach(async () => {
15
- await resetAllData(envCtx);
16
- });
17
-
18
- runComputeServerStoreConformance({
19
- name: 'SupabaseComputeServerStore',
20
- createStore: () => new SupabaseComputeServerStore(envCtx.bundle),
21
- seedOrg: async (orgId) => {
22
- // `compute_servers.owner_org_id` and `compute_server_org_defaults.org_id`
23
- // FK to `orgs(id)`; the suite invents an `orgId` per org-scoped test,
24
- // so we materialize the parent org here. Owner is a throwaway user
25
- // (the org-scoped tests use SYSTEM_CONTEXT for writes anyway).
26
- const { userId } = await seedUser(envCtx, '');
27
- const now = new Date().toISOString();
28
- const { error } = await envCtx.adminClient.from('orgs').insert({
29
- id: orgId,
30
- name: 'Compute Test Org',
31
- slug: `compute-${orgId.slice(0, 8)}`,
32
- owner_id: userId,
33
- created_at: now,
34
- updated_at: now
35
- });
36
- if (error) throw error;
37
- }
38
- });
39
- });
40
- }
@@ -1,89 +0,0 @@
1
- import { describe, beforeEach, it } from 'vitest';
2
- import { runDefinitionStoreConformance } from '@selvajs/platform/testing';
3
- import { DEFAULT_ORG_PERMISSIONS } from '@selvajs/platform';
4
- import { SupabaseDefinitionStore } from '../SupabaseDefinitionStore.js';
5
- import { readEnv, resetAllData, seedUser } from './test-helpers.js';
6
-
7
- const envCtx = readEnv();
8
-
9
- if (!envCtx) {
10
- describe.skip('SupabaseDefinitionStore (skipped: no live stack)', () => {
11
- it('populate packages/providers/supabase/.env.test with Supabase creds to run these tests', () => {});
12
- });
13
- } else {
14
- describe('SupabaseDefinitionStore', () => {
15
- beforeEach(async () => {
16
- await resetAllData(envCtx);
17
- });
18
-
19
- runDefinitionStoreConformance({
20
- name: 'SupabaseDefinitionStore',
21
- createStore: () => new SupabaseDefinitionStore(envCtx.bundle),
22
- createScope: async () => {
23
- // Seed: 1 owner + 1 secondary user + 1 org + 2 projects. Definitions
24
- // created in the suite FK to one of those two projects.
25
- const { userId: ownerId, sessionToken: ownerSessionToken } = await seedUser(envCtx, '');
26
- const { userId: secondaryUserId, sessionToken: secondaryUserSessionToken } = await seedUser(
27
- envCtx,
28
- ''
29
- );
30
- const orgId = crypto.randomUUID();
31
- const projectId = crypto.randomUUID();
32
- const secondaryProjectId = crypto.randomUUID();
33
- const now = new Date().toISOString();
34
- const admin = envCtx.adminClient;
35
-
36
- const { error: orgError } = await admin.from('orgs').insert({
37
- id: orgId,
38
- name: 'Definition Test Org',
39
- slug: `def-test-${orgId.slice(0, 8)}`,
40
- owner_id: ownerId,
41
- created_at: now,
42
- updated_at: now
43
- });
44
- if (orgError) throw orgError;
45
- const { error: memberError } = await admin.from('org_members').insert({
46
- org_id: orgId,
47
- user_id: ownerId,
48
- role: 'owner',
49
- permissions: [...DEFAULT_ORG_PERMISSIONS.owner],
50
- joined_at: now
51
- });
52
- if (memberError) throw memberError;
53
-
54
- const { error: p1Error } = await admin.from('projects').insert([
55
- {
56
- id: projectId,
57
- org_id: orgId,
58
- name: 'Project A',
59
- slug: `proj-a-${projectId.slice(0, 8)}`,
60
- visibility: 'public',
61
- owner_id: ownerId,
62
- created_at: now,
63
- updated_at: now
64
- },
65
- {
66
- id: secondaryProjectId,
67
- org_id: orgId,
68
- name: 'Project B',
69
- slug: `proj-b-${secondaryProjectId.slice(0, 8)}`,
70
- visibility: 'public',
71
- owner_id: ownerId,
72
- created_at: now,
73
- updated_at: now
74
- }
75
- ]);
76
- if (p1Error) throw p1Error;
77
-
78
- return {
79
- ownerId,
80
- ownerSessionToken,
81
- projectId,
82
- secondaryProjectId,
83
- secondaryUserId,
84
- secondaryUserSessionToken
85
- };
86
- }
87
- });
88
- });
89
- }
@@ -1,32 +0,0 @@
1
- import { describe, beforeEach, it } from 'vitest';
2
- import { runEventSinkConformance, type RecordingEventSink } from '@selvajs/platform/testing';
3
- import { SupabaseDataProvider } from '../SupabaseDataProvider.js';
4
- import { readEnv, resetAllData, seedUser } from './test-helpers.js';
5
-
6
- const envCtx = readEnv();
7
-
8
- if (!envCtx) {
9
- describe.skip('SupabaseDataProvider event-sink (skipped: no live stack)', () => {
10
- it('populate packages/providers/supabase/.env.test with Supabase creds to run these tests', () => {});
11
- });
12
- } else {
13
- describe('SupabaseDataProvider event-sink', () => {
14
- beforeEach(async () => {
15
- await resetAllData(envCtx);
16
- });
17
-
18
- runEventSinkConformance({
19
- name: 'SupabaseDataProvider',
20
- createProvider: (sink: RecordingEventSink) =>
21
- SupabaseDataProvider.create(
22
- {
23
- supabaseUrl: envCtx.url,
24
- anonKey: envCtx.anonKey,
25
- serviceRoleKey: envCtx.serviceRoleKey
26
- },
27
- sink
28
- ),
29
- createActorId: () => seedUser(envCtx, '')
30
- });
31
- });
32
- }
@@ -1,46 +0,0 @@
1
- import { describe, beforeEach, it } from 'vitest';
2
- import { runInviteStoreConformance } from '@selvajs/platform/testing';
3
- import { DEFAULT_ORG_PERMISSIONS } from '@selvajs/platform';
4
- import { SupabaseInviteStore } from '../SupabaseInviteStore.js';
5
- import { readEnv, resetAllData, seedUser } from './test-helpers.js';
6
-
7
- const envCtx = readEnv();
8
-
9
- if (!envCtx) {
10
- describe.skip('SupabaseInviteStore (skipped: no live stack)', () => {
11
- it('populate packages/providers/supabase/.env.test with Supabase creds to run these tests', () => {});
12
- });
13
- } else {
14
- describe('SupabaseInviteStore', () => {
15
- beforeEach(async () => {
16
- await resetAllData(envCtx);
17
- });
18
-
19
- runInviteStoreConformance({
20
- name: 'SupabaseInviteStore',
21
- createStore: () => new SupabaseInviteStore(envCtx.bundle),
22
- createScope: async () => {
23
- const { userId: adminId, sessionToken: adminSessionToken } = await seedUser(envCtx, '');
24
- const orgId = crypto.randomUUID();
25
- const now = new Date().toISOString();
26
- await envCtx.adminClient.from('orgs').insert({
27
- id: orgId,
28
- name: 'Invite Test Org',
29
- slug: `invite-test-${orgId.slice(0, 8)}`,
30
- owner_id: adminId,
31
- created_at: now,
32
- updated_at: now
33
- });
34
- await envCtx.adminClient.from('org_members').insert({
35
- org_id: orgId,
36
- user_id: adminId,
37
- role: 'owner',
38
- permissions: [...DEFAULT_ORG_PERMISSIONS.owner],
39
- joined_at: now
40
- });
41
- return { adminId, adminSessionToken, orgId };
42
- },
43
- seedUser: (id) => seedUser(envCtx, id)
44
- });
45
- });
46
- }
@@ -1,30 +0,0 @@
1
- import { describe, beforeEach, it } from 'vitest';
2
- import { runOrgStoreConformance } from '@selvajs/platform/testing';
3
- import { SupabaseOrgStore } from '../SupabaseOrgStore.js';
4
- import { SupabaseInviteStore } from '../SupabaseInviteStore.js';
5
- import { SupabaseComputeServerStore } from '../SupabaseComputeServerStore.js';
6
- import { readEnv, resetAllData, seedUser } from './test-helpers.js';
7
-
8
- const envCtx = readEnv();
9
-
10
- if (!envCtx) {
11
- describe.skip('SupabaseOrgStore (skipped: no live stack)', () => {
12
- it('populate packages/providers/supabase/.env.test with Supabase creds to run these tests', () => {});
13
- });
14
- } else {
15
- describe('SupabaseOrgStore', () => {
16
- beforeEach(async () => {
17
- await resetAllData(envCtx);
18
- });
19
-
20
- runOrgStoreConformance({
21
- name: 'SupabaseOrgStore',
22
- createStore: () => new SupabaseOrgStore(envCtx.bundle),
23
- seedUser: (id) => seedUser(envCtx, id),
24
- createCompanionStores: () => ({
25
- invites: new SupabaseInviteStore(envCtx.bundle),
26
- computeServer: new SupabaseComputeServerStore(envCtx.bundle)
27
- })
28
- });
29
- });
30
- }
@@ -1,57 +0,0 @@
1
- import { describe, beforeEach, it } from 'vitest';
2
- import { runProjectStoreConformance } from '@selvajs/platform/testing';
3
- import { DEFAULT_ORG_PERMISSIONS } from '@selvajs/platform';
4
- import { SupabaseProjectStore } from '../SupabaseProjectStore.js';
5
- import { readEnv, resetAllData, seedUser } from './test-helpers.js';
6
-
7
- const envCtx = readEnv();
8
-
9
- if (!envCtx) {
10
- describe.skip('SupabaseProjectStore (skipped: no live stack)', () => {
11
- it('populate packages/providers/supabase/.env.test with Supabase creds to run these tests', () => {});
12
- });
13
- } else {
14
- describe('SupabaseProjectStore', () => {
15
- beforeEach(async () => {
16
- await resetAllData(envCtx);
17
- });
18
-
19
- runProjectStoreConformance({
20
- name: 'SupabaseProjectStore',
21
- createStore: async () => {
22
- // Seed an owner + an org so the project tests have an anchor.
23
- const { userId: ownerId, sessionToken: ownerSessionToken } = await seedUser(envCtx, '');
24
- const orgId = crypto.randomUUID();
25
- const now = new Date().toISOString();
26
- const admin = envCtx.adminClient;
27
- const { error: orgError } = await admin.from('orgs').insert({
28
- id: orgId,
29
- name: 'Conformance Org',
30
- slug: `conformance-${orgId.slice(0, 8)}`,
31
- owner_id: ownerId,
32
- created_at: now,
33
- updated_at: now
34
- });
35
- if (orgError) throw orgError;
36
- // And an owner membership with `manage_projects` so project
37
- // creates pass the `has_org_permission` policy under RLS.
38
- const { error: memberError } = await admin.from('org_members').insert({
39
- org_id: orgId,
40
- user_id: ownerId,
41
- role: 'owner',
42
- permissions: [...DEFAULT_ORG_PERMISSIONS.owner],
43
- joined_at: now
44
- });
45
- if (memberError) throw memberError;
46
-
47
- return {
48
- store: new SupabaseProjectStore(envCtx.bundle),
49
- orgId,
50
- ownerId,
51
- ownerSessionToken
52
- };
53
- },
54
- seedUser: (id) => seedUser(envCtx, id)
55
- });
56
- });
57
- }
@@ -1,87 +0,0 @@
1
- import { describe, beforeEach, it } from 'vitest';
2
- import { runShareLinkStoreConformance } from '@selvajs/platform/testing';
3
- import { DEFAULT_ORG_PERMISSIONS } from '@selvajs/platform';
4
- import { SupabaseShareLinkStore } from '../SupabaseShareLinkStore.js';
5
- import { readEnv, resetAllData, seedUser } from './test-helpers.js';
6
-
7
- const envCtx = readEnv();
8
-
9
- if (!envCtx) {
10
- describe.skip('SupabaseShareLinkStore (skipped: no live stack)', () => {
11
- it('populate packages/providers/supabase/.env.test with Supabase creds to run these tests', () => {});
12
- });
13
- } else {
14
- describe('SupabaseShareLinkStore', () => {
15
- beforeEach(async () => {
16
- await resetAllData(envCtx);
17
- });
18
-
19
- runShareLinkStoreConformance({
20
- name: 'SupabaseShareLinkStore',
21
- createStore: () => new SupabaseShareLinkStore(envCtx.bundle),
22
- createScope: async () => {
23
- // Seed: 1 owner + 1 org + 1 project + 2 definitions. Share links
24
- // FK to definitions, definitions FK to projects, projects FK to orgs.
25
- const { userId: ownerId, sessionToken: ownerSessionToken } = await seedUser(envCtx, '');
26
- const orgId = crypto.randomUUID();
27
- const projectId = crypto.randomUUID();
28
- const definitionId = crypto.randomUUID();
29
- const otherDefinitionId = crypto.randomUUID();
30
- const now = new Date().toISOString();
31
- const admin = envCtx.adminClient;
32
-
33
- const { error: orgError } = await admin.from('orgs').insert({
34
- id: orgId,
35
- name: 'Share Link Test Org',
36
- slug: `sl-test-${orgId.slice(0, 8)}`,
37
- owner_id: ownerId,
38
- created_at: now,
39
- updated_at: now
40
- });
41
- if (orgError) throw orgError;
42
- const { error: memberError } = await admin.from('org_members').insert({
43
- org_id: orgId,
44
- user_id: ownerId,
45
- role: 'owner',
46
- permissions: [...DEFAULT_ORG_PERMISSIONS.owner],
47
- joined_at: now
48
- });
49
- if (memberError) throw memberError;
50
- const { error: projError } = await admin.from('projects').insert({
51
- id: projectId,
52
- org_id: orgId,
53
- name: 'Project',
54
- slug: `proj-${projectId.slice(0, 8)}`,
55
- visibility: 'public',
56
- owner_id: ownerId,
57
- created_at: now,
58
- updated_at: now
59
- });
60
- if (projError) throw projError;
61
- const { error: defError } = await admin.from('definitions').insert([
62
- {
63
- guid: definitionId,
64
- project_id: projectId,
65
- owner_id: ownerId,
66
- display_name: 'Def A',
67
- status: 'published',
68
- created_at: now,
69
- updated_at: now
70
- },
71
- {
72
- guid: otherDefinitionId,
73
- project_id: projectId,
74
- owner_id: ownerId,
75
- display_name: 'Def B',
76
- status: 'published',
77
- created_at: now,
78
- updated_at: now
79
- }
80
- ]);
81
- if (defError) throw defError;
82
-
83
- return { ownerId, ownerSessionToken, definitionId, otherDefinitionId };
84
- }
85
- });
86
- });
87
- }
@@ -1,59 +0,0 @@
1
- import { describe, beforeEach, it } from 'vitest';
2
- import { runSolveMetricSinkConformance, type RecordedSolveMetric } from '@selvajs/platform/testing';
3
- import { SupabaseSolveMetricSink } from '../SupabaseSolveMetricSink.js';
4
- import { buildClientBundle } from '../client.js';
5
- import { readEnv, resetAllData } from './test-helpers.js';
6
-
7
- const envCtx = readEnv();
8
-
9
- if (!envCtx) {
10
- describe.skip('SupabaseSolveMetricSink (skipped: no live stack)', () => {
11
- it('populate packages/providers/supabase/.env.test with Supabase creds to run these tests', () => {});
12
- });
13
- } else {
14
- const ctx = envCtx;
15
- describe('SupabaseSolveMetricSink', () => {
16
- beforeEach(async () => {
17
- await resetAllData(ctx);
18
- });
19
-
20
- runSolveMetricSinkConformance({
21
- name: 'SupabaseSolveMetricSink',
22
- createSink: () => new SupabaseSolveMetricSink(ctx.bundle),
23
- readRows: async () => {
24
- const { data, error } = await ctx.adminClient
25
- .from('solve_metrics')
26
- .select(
27
- 'actor_id, definition_url, definition_id, version_id, channel, org_id, duration_ms, ok, failure_kind, error_count, warning_count'
28
- )
29
- .order('occurred_at', { ascending: true });
30
- if (error) throw new Error(`solve_metrics read failed: ${error.message}`);
31
- return (data ?? []).map(
32
- (r): RecordedSolveMetric => ({
33
- actorId: r.actor_id,
34
- definitionUrl: r.definition_url,
35
- definitionId: r.definition_id,
36
- versionId: r.version_id,
37
- channel: r.channel,
38
- orgId: r.org_id,
39
- durationMs: r.duration_ms,
40
- ok: r.ok,
41
- failureKind: r.failure_kind,
42
- errorCount: r.error_count,
43
- warningCount: r.warning_count
44
- })
45
- );
46
- },
47
- // Point the sink at a bundle whose service key is bogus, so the insert
48
- // fails. record() must still resolve (contract: never throw).
49
- createFailingSink: () =>
50
- new SupabaseSolveMetricSink(
51
- buildClientBundle({
52
- supabaseUrl: ctx.url,
53
- anonKey: ctx.anonKey,
54
- serviceRoleKey: 'invalid-service-role-key'
55
- })
56
- )
57
- });
58
- });
59
- }
@@ -1,188 +0,0 @@
1
- /**
2
- * Shared test helpers for Supabase data conformance tests.
3
- *
4
- * Every suite starts with a fully wiped DB so tests are isolated. We use the
5
- * service-role client for both — seeding users in `auth.users` and wiping
6
- * every relevant table.
7
- */
8
-
9
- import { createClient } from '@supabase/supabase-js';
10
- import { buildClientBundle, type ClientBundle, type SelvaSchemaClient } from '../client.js';
11
-
12
- export interface TestContext {
13
- url: string;
14
- anonKey: string;
15
- serviceRoleKey: string;
16
- adminClient: SelvaSchemaClient;
17
- bundle: ClientBundle;
18
- }
19
-
20
- export function readEnv(): TestContext | null {
21
- const url = process.env.SUPABASE_URL;
22
- const anonKey = process.env.SUPABASE_ANON_KEY;
23
- const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
24
- if (!url || !anonKey || !serviceRoleKey) return null;
25
- // `.env.test` is checked in with local-Supabase defaults so contributors can
26
- // `npx supabase start` and run the suite. But `createClient` transitively
27
- // loads `@supabase/realtime-js`, which on Node < 22 throws at construction
28
- // without a native WebSocket. Swallow that so the suite cleanly skips
29
- // instead of crashing test collection.
30
- let adminClient: SelvaSchemaClient;
31
- try {
32
- adminClient = createClient(url, serviceRoleKey, {
33
- db: { schema: 'selva' },
34
- auth: { persistSession: false, autoRefreshToken: false }
35
- });
36
- } catch (err) {
37
- const msg = err instanceof Error ? err.message : String(err);
38
- if (msg.includes('WebSocket')) return null;
39
- throw err;
40
- }
41
- const bundle = buildClientBundle({ supabaseUrl: url, anonKey, serviceRoleKey });
42
- return { url, anonKey, serviceRoleKey, adminClient, bundle };
43
- }
44
-
45
- /**
46
- * Reset every data table this adapter owns. Leaves Supabase-managed tables
47
- * (`auth.users`, etc.) alone except via `deleteAllAuthUsers` below.
48
- *
49
- * Order-sensitive because some FKs don't cascade: we go bottom up from
50
- * dependents to parents.
51
- */
52
- export async function resetAllData(ctx: TestContext): Promise<void> {
53
- await truncate(ctx, 'solve_metrics');
54
- await truncate(ctx, 'share_links');
55
- await truncate(ctx, 'invites');
56
- // Versions FK to definitions ON DELETE CASCADE, but `definitions` can't
57
- // be wiped while live/draft pointers reference versions (ON DELETE
58
- // RESTRICT, spec §6). Null the pointers, drop versions, then drop defs.
59
- await ctx.adminClient
60
- .from('definitions')
61
- .update({ live_version_id: null, draft_version_id: null })
62
- .not('guid', 'is', null);
63
- await truncate(ctx, 'definition_versions');
64
- await truncate(ctx, 'definitions');
65
- await truncate(ctx, 'project_members');
66
- // compute defaults have a platform_default sibling — reset all three.
67
- await truncate(ctx, 'compute_server_org_defaults');
68
- await truncate(ctx, 'compute_server_shares');
69
- await resetComputePlatformDefault(ctx);
70
- await truncate(ctx, 'compute_servers');
71
- await truncate(ctx, 'org_members');
72
- await truncate(ctx, 'orgs');
73
- await truncate(ctx, 'user_profiles');
74
- await deleteAllAuthUsers(ctx);
75
- }
76
-
77
- async function resetComputePlatformDefault(ctx: TestContext): Promise<void> {
78
- // Singleton row — clear `default_server_id` rather than deleting.
79
- const { error } = await ctx.adminClient
80
- .from('compute_server_platform_default')
81
- .update({ default_server_id: null })
82
- .eq('singleton', true);
83
- if (error) throw error;
84
- }
85
-
86
- async function truncate(ctx: TestContext, table: string): Promise<void> {
87
- const { error } = await ctx.adminClient
88
- .from(table)
89
- .delete()
90
- .neq(columnForDeleteAll(table), '00000000-0000-0000-0000-000000000000');
91
- if (error) throw error;
92
- }
93
-
94
- function columnForDeleteAll(table: string): string {
95
- if (table === 'org_members') return 'user_id';
96
- if (table === 'project_members') return 'user_id';
97
- if (table === 'user_profiles') return 'user_id';
98
- if (table === 'definitions') return 'guid';
99
- if (table === 'definition_versions') return 'id';
100
- if (table === 'compute_server_org_defaults') return 'org_id';
101
- if (table === 'compute_server_shares') return 'server_id';
102
- return 'id';
103
- }
104
-
105
- async function deleteAllAuthUsers(ctx: TestContext): Promise<void> {
106
- const { data, error } = await ctx.adminClient.auth.admin.listUsers({ perPage: 1000 });
107
- if (error) throw error;
108
- for (const user of data.users) {
109
- const { error: delError } = await ctx.adminClient.auth.admin.deleteUser(user.id);
110
- if (delError) throw delError;
111
- }
112
- }
113
-
114
- /**
115
- * Seed an `auth.users` row, mark the user as `instance_admin`, sign in, and
116
- * return the real id + access token. `auth.admin.createUser` doesn't accept a
117
- * caller-supplied id — Supabase generates one. The conformance suite calls us
118
- * with a *suggested* id (for adapters that honor it); we ignore it and return
119
- * GoTrue's real id.
120
- *
121
- * The instance_admin grant is what makes RLS open up for the conformance
122
- * tests: every policy in the schema accepts `is_instance_admin()` as a
123
- * universal pass, mirroring the `instance_admin` default in `makeCtx`.
124
- *
125
- * The session token is what makes RLS *see* the user at all. Without it, the
126
- * Supabase client falls back to the anon role and every write fails.
127
- */
128
- export async function seedUser(
129
- ctx: TestContext,
130
- suggestedId: string
131
- ): Promise<{ userId: string; sessionToken: string }> {
132
- return seedUserCore(ctx, suggestedId, { promoteToInstanceAdmin: true });
133
- }
134
-
135
- /**
136
- * Variant of `seedUser` that leaves `platform_permissions` empty. Use this
137
- * when a test specifically asserts on a fresh user's permission state (e.g.
138
- * the platform-permission store conformance suite).
139
- */
140
- export async function seedPlainUser(
141
- ctx: TestContext,
142
- suggestedId: string
143
- ): Promise<{ userId: string; sessionToken: string }> {
144
- return seedUserCore(ctx, suggestedId, { promoteToInstanceAdmin: false });
145
- }
146
-
147
- async function seedUserCore(
148
- ctx: TestContext,
149
- _suggestedId: string,
150
- opts: { promoteToInstanceAdmin: boolean }
151
- ): Promise<{ userId: string; sessionToken: string }> {
152
- // Unique email per seed so repeated calls don't collide. GoTrue requires
153
- // an email even for password-less users.
154
- const email = `conformance-${crypto.randomUUID()}@conformance.test`;
155
- const password = 'conformance-test-password-1234';
156
- const { data, error } = await ctx.adminClient.auth.admin.createUser({
157
- email,
158
- password,
159
- email_confirm: true
160
- });
161
- if (error) throw error;
162
- const userId = data.user.id;
163
-
164
- if (opts.promoteToInstanceAdmin) {
165
- // Trigger auto-created the user_profiles row; promote to instance_admin
166
- // so every RLS policy treats this user as fully authorized. Mirrors the
167
- // `instance_admin` default in `makeCtx`.
168
- const { error: promoteError } = await ctx.adminClient
169
- .from('user_profiles')
170
- .update({ platform_permissions: ['instance_admin'] })
171
- .eq('user_id', userId);
172
- if (promoteError) throw promoteError;
173
- }
174
-
175
- // Sign in to get an access token. The conformance suite stuffs this into
176
- // `adapterContext.sessionToken` and our client.ts uses it to scope the
177
- // request to this user instead of falling back to anon.
178
- const signInClient = createClient(ctx.url, ctx.anonKey, {
179
- auth: { persistSession: false, autoRefreshToken: false }
180
- });
181
- const { data: session, error: signInError } = await signInClient.auth.signInWithPassword({
182
- email,
183
- password
184
- });
185
- if (signInError) throw signInError;
186
- if (!session.session) throw new Error('seedUser: signInWithPassword returned no session');
187
- return { userId, sessionToken: session.session.access_token };
188
- }