@instantdb/core 0.22.167 → 0.22.168

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.
@@ -0,0 +1,134 @@
1
+ import { expect } from 'vitest';
2
+ import { i } from '../../src';
3
+ import { makeE2ETest, apiUrl } from './utils/e2e';
4
+
5
+ const schema = i.schema({
6
+ entities: {
7
+ $users: i.entity({
8
+ email: i.string().unique().indexed().optional(),
9
+ username: i.string().unique().indexed().optional(),
10
+ displayName: i.string().optional(),
11
+ }),
12
+ },
13
+ });
14
+
15
+ async function generateMagicCode(
16
+ appId: string,
17
+ adminToken: string,
18
+ email: string,
19
+ ): Promise<string> {
20
+ const res = await fetch(`${apiUrl}/admin/magic_code`, {
21
+ method: 'POST',
22
+ headers: {
23
+ 'Content-Type': 'application/json',
24
+ 'app-id': appId,
25
+ authorization: `Bearer ${adminToken}`,
26
+ },
27
+ body: JSON.stringify({ email }),
28
+ });
29
+ const data = await res.json();
30
+ return data.code;
31
+ }
32
+
33
+ const authTest = makeE2ETest({ schema });
34
+
35
+ authTest(
36
+ 'new user with extraFields gets fields written and created=true',
37
+ async ({ db, appId, adminToken }) => {
38
+ const email = `new-${Date.now()}@test.com`;
39
+
40
+ const code = await generateMagicCode(appId, adminToken, email);
41
+ const res = await db.auth.signInWithMagicCode({
42
+ email,
43
+ code,
44
+ extraFields: { username: 'cool_user', displayName: 'Cool User' },
45
+ });
46
+
47
+ expect(res.created).toBe(true);
48
+
49
+ const { data } = await db.queryOnce({ $users: {} });
50
+ const user = data.$users.find((u: any) => u.email === email);
51
+ expect(user).toBeDefined();
52
+ expect(user!.username).toBe('cool_user');
53
+ expect(user!.displayName).toBe('Cool User');
54
+ },
55
+ );
56
+
57
+ authTest(
58
+ 'returning user gets created=false',
59
+ async ({ db, appId, adminToken }) => {
60
+ const email = `returning-${Date.now()}@test.com`;
61
+
62
+ // First sign in -- creates user
63
+ const code1 = await generateMagicCode(appId, adminToken, email);
64
+ const res1 = await db.auth.signInWithMagicCode({ email, code: code1 });
65
+ expect(res1.created).toBe(true);
66
+
67
+ // Second sign in -- existing user
68
+ const code2 = await generateMagicCode(appId, adminToken, email);
69
+ const res2 = await db.auth.signInWithMagicCode({ email, code: code2 });
70
+ expect(res2.created).toBe(false);
71
+ },
72
+ );
73
+
74
+ authTest(
75
+ 'sign in without extraFields works (backwards compat)',
76
+ async ({ db, appId, adminToken }) => {
77
+ const email = `compat-${Date.now()}@test.com`;
78
+
79
+ const code = await generateMagicCode(appId, adminToken, email);
80
+ const res = await db.auth.signInWithMagicCode({ email, code });
81
+
82
+ expect(res.user).toBeDefined();
83
+ expect(res.user.email).toBe(email);
84
+ },
85
+ );
86
+
87
+ authTest(
88
+ 'admin verify_magic_code returns { user, created } for checkMagicCode',
89
+ async ({ db: _db, appId, adminToken }) => {
90
+ const email = `admin-consume-${Date.now()}@test.com`;
91
+ const code = await generateMagicCode(appId, adminToken, email);
92
+
93
+ // Hit the admin endpoint directly (same as admin SDK checkMagicCode)
94
+ const res = await fetch(
95
+ `${apiUrl}/admin/verify_magic_code?app_id=${appId}`,
96
+ {
97
+ method: 'POST',
98
+ headers: {
99
+ 'Content-Type': 'application/json',
100
+ authorization: `Bearer ${adminToken}`,
101
+ },
102
+ body: JSON.stringify({
103
+ email,
104
+ code,
105
+ 'extra-fields': { username: 'admin_user' },
106
+ }),
107
+ },
108
+ );
109
+ const data = await res.json();
110
+
111
+ // Response should have user nested (not splatted) and created flag
112
+ expect(data.user).toBeDefined();
113
+ expect(data.user.email).toBe(email);
114
+ expect(data.created).toBe(true);
115
+
116
+ // Second call -- existing user
117
+ const code2 = await generateMagicCode(appId, adminToken, email);
118
+ const res2 = await fetch(
119
+ `${apiUrl}/admin/verify_magic_code?app_id=${appId}`,
120
+ {
121
+ method: 'POST',
122
+ headers: {
123
+ 'Content-Type': 'application/json',
124
+ authorization: `Bearer ${adminToken}`,
125
+ },
126
+ body: JSON.stringify({ email, code: code2 }),
127
+ },
128
+ );
129
+ const data2 = await res2.json();
130
+
131
+ expect(data2.user).toBeDefined();
132
+ expect(data2.created).toBe(false);
133
+ },
134
+ );
@@ -6,18 +6,19 @@ import {
6
6
  InstantSchemaDef,
7
7
  } from '../../../src';
8
8
 
9
- // @ts-ignore
10
- const apiUrl = import.meta.env.VITE_INSTANT_DEV
11
- ? 'http://localhost:8888'
12
- : // @ts-ignore
13
- import.meta.env.VITE_INSTANT_API_URL || 'https://api.instantdb.com';
9
+ // __DEV_LOCAL_PORT__ is set by vitest.config.ts.
10
+ // This allows us to run tests against mulutple checkouts
11
+ // If CI=1 then __DEV_LOCAL_PORT__ will be falsey and tests will hit prod.
12
+ // Otherwise they will hit localhost at the specified port.
13
+ declare const __DEV_LOCAL_PORT__: number;
14
14
 
15
- // @ts-ignore
16
- const websocketURI = import.meta.env.VITE_INSTANT_DEV
17
- ? 'ws://localhost:8888/runtime/session'
18
- : // @ts-ignore
19
- import.meta.env.VITE_INSTANT_WEBSOCKET_URI ||
20
- 'wss://api.instantdb.com/runtime/session';
15
+ const apiUrl = __DEV_LOCAL_PORT__
16
+ ? `http://localhost:${__DEV_LOCAL_PORT__}`
17
+ : 'https://api.instantdb.com';
18
+
19
+ const websocketURI = __DEV_LOCAL_PORT__
20
+ ? `ws://localhost:${__DEV_LOCAL_PORT__}/runtime/session`
21
+ : 'wss://api.instantdb.com/runtime/session';
21
22
 
22
23
  // Make a factory function that returns a typed test instance
23
24
  export function makeE2ETest<Schema extends InstantSchemaDef<any, any, any>>({
@@ -31,6 +32,8 @@ export function makeE2ETest<Schema extends InstantSchemaDef<any, any, any>>({
31
32
  }) {
32
33
  return baseTest.extend<{
33
34
  db: InstantCoreDatabase<Schema, false>;
35
+ appId: string;
36
+ adminToken: string;
34
37
  }>({
35
38
  db: async ({ task, signal }, use) => {
36
39
  const response = await fetch(`${apiUrl}/dash/apps/ephemeral`, {
@@ -49,9 +52,18 @@ export function makeE2ETest<Schema extends InstantSchemaDef<any, any, any>>({
49
52
  websocketURI,
50
53
  schema,
51
54
  });
55
+ (db as any)._testApp = app;
52
56
  await use(db);
53
57
  },
58
+ appId: async ({ db }, use) => {
59
+ await use((db as any)._testApp.id);
60
+ },
61
+ adminToken: async ({ db }, use) => {
62
+ await use((db as any)._testApp['admin-token']);
63
+ },
54
64
  });
55
65
  }
56
66
 
67
+ export { apiUrl };
68
+
57
69
  export const e2eTest = makeE2ETest({});
@@ -291,10 +291,7 @@ export default class Reactor<RoomSchema extends import("./presence.ts").RoomSche
291
291
  sendMagicCode({ email }: {
292
292
  email: any;
293
293
  }): Promise<authAPI.SendMagicCodeResponse>;
294
- signInWithMagicCode({ email, code }: {
295
- email: any;
296
- code: any;
297
- }): Promise<authAPI.VerifyResponse>;
294
+ signInWithMagicCode(params: any): Promise<authAPI.CheckMagicCodeResponse>;
298
295
  signInWithCustomToken(authToken: any): Promise<authAPI.VerifyResponse>;
299
296
  signInAsGuest(): Promise<authAPI.VerifyResponse>;
300
297
  potentiallyInvalidateToken(currentUser: any, opts: any): void;
@@ -305,33 +302,39 @@ export default class Reactor<RoomSchema extends import("./presence.ts").RoomSche
305
302
  * @param {Object} params - The parameters to create the authorization URL.
306
303
  * @param {string} params.clientName - The name of the client requesting authorization.
307
304
  * @param {string} params.redirectURL - The URL to redirect users to after authorization.
305
+ * @param {Record<string, any>} [params.extraFields] - Extra fields to write to $users on creation
308
306
  * @returns {string} The created authorization URL.
309
307
  */
310
- createAuthorizationURL({ clientName, redirectURL }: {
308
+ createAuthorizationURL({ clientName, redirectURL, extraFields }: {
311
309
  clientName: string;
312
310
  redirectURL: string;
311
+ extraFields?: Record<string, any> | undefined;
313
312
  }): string;
314
313
  /**
315
314
  * @param {Object} params
316
315
  * @param {string} params.code - The code received from the OAuth service.
317
316
  * @param {string} [params.codeVerifier] - The code verifier used to generate the code challenge.
317
+ * @param {Record<string, any>} [params.extraFields] - Extra fields to write to $users on creation
318
318
  */
319
- exchangeCodeForToken({ code, codeVerifier }: {
319
+ exchangeCodeForToken({ code, codeVerifier, extraFields }: {
320
320
  code: string;
321
321
  codeVerifier?: string | undefined;
322
- }): Promise<authAPI.VerifyResponse>;
322
+ extraFields?: Record<string, any> | undefined;
323
+ }): Promise<authAPI.CheckMagicCodeResponse>;
323
324
  issuerURI(): string;
324
325
  /**
325
326
  * @param {Object} params
326
327
  * @param {string} params.clientName - The name of the client requesting authorization.
327
328
  * @param {string} params.idToken - The id_token from the external service
328
329
  * @param {string | null | undefined} [params.nonce] - The nonce used when requesting the id_token from the external service
330
+ * @param {Record<string, any>} [params.extraFields] - Extra fields to write to $users on creation
329
331
  */
330
- signInWithIdToken({ idToken, clientName, nonce }: {
332
+ signInWithIdToken(params: {
331
333
  clientName: string;
332
334
  idToken: string;
333
335
  nonce?: string | null | undefined;
334
- }): Promise<authAPI.VerifyResponse>;
336
+ extraFields?: Record<string, any> | undefined;
337
+ }): Promise<authAPI.CheckMagicCodeResponse>;
335
338
  /**
336
339
  * @param {string} roomType
337
340
  * @param {string} roomId
@@ -1 +1 @@
1
- {"version":3,"file":"Reactor.d.ts","sourceRoot":"","sources":["../../src/Reactor.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Reactor.d.ts","sourceRoot":"","sources":["../../src/Reactor.js"],"names":[],"mappings":"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"}
@@ -91,6 +91,8 @@ const defaultConfig = {
91
91
  };
92
92
  // Param that the backend adds if this is an oauth redirect
93
93
  const OAUTH_REDIRECT_PARAM = '_instant_oauth_redirect';
94
+ const OAUTH_EXTRA_FIELDS_ID_PARAM = '_instant_extra_fields_id';
95
+ const oauthExtraFieldsKey = 'oauthExtraFields';
94
96
  const currentUserKey = `currentUser`;
95
97
  /**
96
98
  * @param {Object} config
@@ -1552,6 +1554,7 @@ class Reactor {
1552
1554
  if (url.searchParams.get(OAUTH_REDIRECT_PARAM)) {
1553
1555
  const startUrl = url.toString();
1554
1556
  url.searchParams.delete(OAUTH_REDIRECT_PARAM);
1557
+ url.searchParams.delete(OAUTH_EXTRA_FIELDS_ID_PARAM);
1555
1558
  url.searchParams.delete('code');
1556
1559
  url.searchParams.delete('error');
1557
1560
  const newPath = url.pathname +
@@ -1614,8 +1617,20 @@ class Reactor {
1614
1617
  if (!code) {
1615
1618
  return null;
1616
1619
  }
1620
+ const extraFieldsId = params.get(OAUTH_EXTRA_FIELDS_ID_PARAM);
1617
1621
  this._replaceUrlAfterOAuth();
1618
1622
  try {
1623
+ let extraFields;
1624
+ const stored = await this.kv.waitForKeyToLoad(oauthExtraFieldsKey);
1625
+ if (extraFieldsId && stored) {
1626
+ extraFields = stored[extraFieldsId];
1627
+ }
1628
+ // Clean up all stored extraFields after login
1629
+ if (stored) {
1630
+ this.kv.updateInPlace((prev) => {
1631
+ delete prev[oauthExtraFieldsKey];
1632
+ });
1633
+ }
1619
1634
  const currentUser = await this._getCurrentUser();
1620
1635
  const isGuest = currentUser?.type === 'guest';
1621
1636
  const { user } = await authAPI.exchangeCodeForToken({
@@ -1623,6 +1638,7 @@ class Reactor {
1623
1638
  appId: this.config.appId,
1624
1639
  code,
1625
1640
  refreshToken: isGuest ? currentUser.refresh_token : undefined,
1641
+ extraFields,
1626
1642
  });
1627
1643
  this.setCurrentUser(user);
1628
1644
  return null;
@@ -1840,15 +1856,16 @@ class Reactor {
1840
1856
  email: email,
1841
1857
  });
1842
1858
  }
1843
- async signInWithMagicCode({ email, code }) {
1859
+ async signInWithMagicCode(params) {
1844
1860
  const currentUser = await this.getCurrentUser();
1845
1861
  const isGuest = currentUser?.user?.type === 'guest';
1846
- const res = await authAPI.verifyMagicCode({
1862
+ const res = await authAPI.checkMagicCode({
1847
1863
  apiURI: this.config.apiURI,
1848
1864
  appId: this.config.appId,
1849
- email,
1850
- code,
1865
+ email: params.email,
1866
+ code: params.code,
1851
1867
  refreshToken: isGuest ? currentUser?.user?.refresh_token : undefined,
1868
+ extraFields: params.extraFields,
1852
1869
  });
1853
1870
  await this.changeCurrentUser(res.user);
1854
1871
  return res;
@@ -1902,18 +1919,35 @@ class Reactor {
1902
1919
  * @param {Object} params - The parameters to create the authorization URL.
1903
1920
  * @param {string} params.clientName - The name of the client requesting authorization.
1904
1921
  * @param {string} params.redirectURL - The URL to redirect users to after authorization.
1922
+ * @param {Record<string, any>} [params.extraFields] - Extra fields to write to $users on creation
1905
1923
  * @returns {string} The created authorization URL.
1906
1924
  */
1907
- createAuthorizationURL({ clientName, redirectURL }) {
1925
+ createAuthorizationURL({ clientName, redirectURL, extraFields }) {
1908
1926
  const { apiURI, appId } = this.config;
1909
- return `${apiURI}/runtime/oauth/start?app_id=${appId}&client_name=${clientName}&redirect_uri=${redirectURL}`;
1927
+ let finalRedirectURL = redirectURL;
1928
+ if (extraFields) {
1929
+ // Store extraFields under a unique ID so multiple
1930
+ // createAuthorizationURL calls don't overwrite each other.
1931
+ // The ID is passed through the redirect URL and used
1932
+ // by _oauthLoginInit to retrieve the right extraFields.
1933
+ // All entries are cleaned up after login.
1934
+ const extraFieldsId = `${Math.random().toString(36).slice(2)}`;
1935
+ this.kv.updateInPlace((prev) => {
1936
+ const stored = prev[oauthExtraFieldsKey] || {};
1937
+ stored[extraFieldsId] = extraFields;
1938
+ prev[oauthExtraFieldsKey] = stored;
1939
+ });
1940
+ finalRedirectURL = `${redirectURL}${redirectURL.includes('?') ? '&' : '?'}${OAUTH_EXTRA_FIELDS_ID_PARAM}=${extraFieldsId}`;
1941
+ }
1942
+ return `${apiURI}/runtime/oauth/start?app_id=${appId}&client_name=${clientName}&redirect_uri=${encodeURIComponent(finalRedirectURL)}`;
1910
1943
  }
1911
1944
  /**
1912
1945
  * @param {Object} params
1913
1946
  * @param {string} params.code - The code received from the OAuth service.
1914
1947
  * @param {string} [params.codeVerifier] - The code verifier used to generate the code challenge.
1948
+ * @param {Record<string, any>} [params.extraFields] - Extra fields to write to $users on creation
1915
1949
  */
1916
- async exchangeCodeForToken({ code, codeVerifier }) {
1950
+ async exchangeCodeForToken({ code, codeVerifier, extraFields }) {
1917
1951
  const currentUser = await this.getCurrentUser();
1918
1952
  const isGuest = currentUser?.user?.type === 'guest';
1919
1953
  const res = await authAPI.exchangeCodeForToken({
@@ -1922,6 +1956,7 @@ class Reactor {
1922
1956
  code: code,
1923
1957
  codeVerifier,
1924
1958
  refreshToken: isGuest ? currentUser?.user?.refresh_token : undefined,
1959
+ extraFields,
1925
1960
  });
1926
1961
  await this.changeCurrentUser(res.user);
1927
1962
  return res;
@@ -1935,17 +1970,19 @@ class Reactor {
1935
1970
  * @param {string} params.clientName - The name of the client requesting authorization.
1936
1971
  * @param {string} params.idToken - The id_token from the external service
1937
1972
  * @param {string | null | undefined} [params.nonce] - The nonce used when requesting the id_token from the external service
1973
+ * @param {Record<string, any>} [params.extraFields] - Extra fields to write to $users on creation
1938
1974
  */
1939
- async signInWithIdToken({ idToken, clientName, nonce }) {
1975
+ async signInWithIdToken(params) {
1940
1976
  const currentUser = await this.getCurrentUser();
1941
1977
  const refreshToken = currentUser?.user?.refresh_token;
1942
1978
  const res = await authAPI.signInWithIdToken({
1943
1979
  apiURI: this.config.apiURI,
1944
1980
  appId: this.config.appId,
1945
- idToken,
1946
- clientName,
1947
- nonce,
1981
+ idToken: params.idToken,
1982
+ clientName: params.clientName,
1983
+ nonce: params.nonce,
1948
1984
  refreshToken,
1985
+ extraFields: params.extraFields,
1949
1986
  });
1950
1987
  await this.changeCurrentUser(res.user);
1951
1988
  return res;