@marcoappio/marco-config 2.0.594 → 2.0.596

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.
@@ -108,8 +108,9 @@ declare const rowSchemas: {
108
108
  readonly $updatedAt: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>;
109
109
  }, undefined>;
110
110
  readonly credentials: v.StrictObjectSchema<{
111
+ readonly accountId: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 255, undefined>]>;
111
112
  readonly encryptedPayload: v.StringSchema<undefined>;
112
- readonly expiresAt: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>, undefined>;
113
+ readonly expiresAt: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>, undefined>;
113
114
  readonly provider: v.PicklistSchema<readonly ["GMAIL", "OUTLOOK", "IMAP"], undefined>;
114
115
  readonly userId: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 255, undefined>]>;
115
116
  readonly $createdAt: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>;
@@ -226,6 +227,24 @@ declare const rowSchemas: {
226
227
  readonly $tableId: v.LiteralSchema<"messages", undefined>;
227
228
  readonly $updatedAt: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>;
228
229
  }, undefined>;
230
+ readonly oauthStates: v.StrictObjectSchema<{
231
+ readonly accountId: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 255, undefined>]>;
232
+ readonly email: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.EmailAction<string, undefined>]>;
233
+ readonly expiresAt: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>;
234
+ readonly nonceHash: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 255, undefined>]>;
235
+ readonly operation: v.PicklistSchema<readonly ["CONNECT", "REPLACE"], undefined>;
236
+ readonly profile: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 255, undefined>]>;
237
+ readonly provider: v.PicklistSchema<readonly ["GMAIL", "OUTLOOK"], undefined>;
238
+ readonly returnUri: v.PicklistSchema<readonly ["https://app.marcoapp.io/onboarding/provider-selection", "https://app.staging.marcoapp.io/onboarding/provider-selection"], undefined>;
239
+ readonly userId: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 255, undefined>]>;
240
+ readonly $createdAt: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>;
241
+ readonly $databaseId: v.LiteralSchema<"marco", undefined>;
242
+ readonly $id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 36, undefined>, v.RegexAction<string, undefined>]>;
243
+ readonly $permissions: v.ArraySchema<v.StringSchema<undefined>, undefined>;
244
+ readonly $sequence: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 255, undefined>]>;
245
+ readonly $tableId: v.LiteralSchema<"oauth_states", undefined>;
246
+ readonly $updatedAt: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>;
247
+ }, undefined>;
229
248
  readonly profiles: v.StrictObjectSchema<{
230
249
  readonly avatarFileId: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 36, undefined>, v.RegexAction<string, undefined>]>, undefined>;
231
250
  readonly deletionRequestedAt: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>, undefined>;
@@ -376,6 +395,7 @@ export declare const appwrite: {
376
395
  readonly labels: "labels";
377
396
  readonly mailboxSyncStates: "mailbox_sync_states";
378
397
  readonly messages: "messages";
398
+ readonly oauthStates: "oauth_states";
379
399
  readonly profiles: "profiles";
380
400
  readonly pushNotificationTokens: "push_notification_tokens";
381
401
  readonly savedViews: "saved_views";
@@ -384,6 +404,12 @@ export declare const appwrite: {
384
404
  readonly watcherShards: "watcher_shards";
385
405
  };
386
406
  };
407
+ readonly oauth: {
408
+ readonly operations: readonly ["CONNECT", "REPLACE"];
409
+ readonly providers: readonly ["GMAIL", "OUTLOOK"];
410
+ readonly returnUriSchema: v.PicklistSchema<readonly ["https://app.marcoapp.io/onboarding/provider-selection", "https://app.staging.marcoapp.io/onboarding/provider-selection"], undefined>;
411
+ readonly returnUris: readonly ["https://app.marcoapp.io/onboarding/provider-selection", "https://app.staging.marcoapp.io/onboarding/provider-selection"];
412
+ };
387
413
  readonly rowSchemas: {
388
414
  readonly accounts: v.StrictObjectSchema<{
389
415
  readonly colour: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MaxLengthAction<string, 64, undefined>]>;
@@ -463,8 +489,9 @@ export declare const appwrite: {
463
489
  readonly $updatedAt: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>;
464
490
  }, undefined>;
465
491
  readonly credentials: v.StrictObjectSchema<{
492
+ readonly accountId: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 255, undefined>]>;
466
493
  readonly encryptedPayload: v.StringSchema<undefined>;
467
- readonly expiresAt: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>, undefined>;
494
+ readonly expiresAt: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>, undefined>;
468
495
  readonly provider: v.PicklistSchema<readonly ["GMAIL", "OUTLOOK", "IMAP"], undefined>;
469
496
  readonly userId: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 255, undefined>]>;
470
497
  readonly $createdAt: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>;
@@ -581,6 +608,24 @@ export declare const appwrite: {
581
608
  readonly $tableId: v.LiteralSchema<"messages", undefined>;
582
609
  readonly $updatedAt: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>;
583
610
  }, undefined>;
611
+ readonly oauthStates: v.StrictObjectSchema<{
612
+ readonly accountId: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 255, undefined>]>;
613
+ readonly email: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.EmailAction<string, undefined>]>;
614
+ readonly expiresAt: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>;
615
+ readonly nonceHash: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 255, undefined>]>;
616
+ readonly operation: v.PicklistSchema<readonly ["CONNECT", "REPLACE"], undefined>;
617
+ readonly profile: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 255, undefined>]>;
618
+ readonly provider: v.PicklistSchema<readonly ["GMAIL", "OUTLOOK"], undefined>;
619
+ readonly returnUri: v.PicklistSchema<readonly ["https://app.marcoapp.io/onboarding/provider-selection", "https://app.staging.marcoapp.io/onboarding/provider-selection"], undefined>;
620
+ readonly userId: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 255, undefined>]>;
621
+ readonly $createdAt: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>;
622
+ readonly $databaseId: v.LiteralSchema<"marco", undefined>;
623
+ readonly $id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 36, undefined>, v.RegexAction<string, undefined>]>;
624
+ readonly $permissions: v.ArraySchema<v.StringSchema<undefined>, undefined>;
625
+ readonly $sequence: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 255, undefined>]>;
626
+ readonly $tableId: v.LiteralSchema<"oauth_states", undefined>;
627
+ readonly $updatedAt: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>;
628
+ }, undefined>;
584
629
  readonly profiles: v.StrictObjectSchema<{
585
630
  readonly avatarFileId: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 36, undefined>, v.RegexAction<string, undefined>]>, undefined>;
586
631
  readonly deletionRequestedAt: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>, undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"appwrite.d.ts","sourceRoot":"","sources":["../../src/appwrite/appwrite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7C,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAgB5B,QAAA,MAAM,qBAAqB;;;;;aAKzB,CAAA;AAEF,QAAA,MAAM,mBAAmB;;;;;eASxB,CAAA;AACD,QAAA,MAAM,uBAAuB;;;;;;;aAO3B,CAAA;AAEF,QAAA,MAAM,sBAAsB;;;;aAI1B,CAAA;AAGF,MAAM,MAAM,uBAAuB,GAAG,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,qBAAqB,CAAC,CAAC,CAAA;AAC/F,MAAM,MAAM,qBAAqB,GAAG,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAA;AAC3F,MAAM,MAAM,yBAAyB,GAAG,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,uBAAuB,CAAC,CAAC,CAAA;AACnG,MAAM,MAAM,wBAAwB,GAAG,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAA;AAmDjG,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0MN,CAAA;AAEV,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWX,CAAA;AAEV,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,UAAU,CAAA;AACtD,MAAM,MAAM,WAAW,CAAC,MAAM,SAAS,gBAAgB,IAAI,CAAC,CAAC,WAAW,CAAC,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"appwrite.d.ts","sourceRoot":"","sources":["../../src/appwrite/appwrite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7C,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAgB5B,QAAA,MAAM,qBAAqB;;;;;aAKzB,CAAA;AAEF,QAAA,MAAM,mBAAmB;;;;;eASxB,CAAA;AACD,QAAA,MAAM,uBAAuB;;;;;;;aAO3B,CAAA;AAEF,QAAA,MAAM,sBAAsB;;;;aAI1B,CAAA;AAGF,MAAM,MAAM,uBAAuB,GAAG,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,qBAAqB,CAAC,CAAC,CAAA;AAC/F,MAAM,MAAM,qBAAqB,GAAG,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAA;AAC3F,MAAM,MAAM,yBAAyB,GAAG,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,uBAAuB,CAAC,CAAC,CAAA;AACnG,MAAM,MAAM,wBAAwB,GAAG,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAA;AA2DjG,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuNN,CAAA;AAEV,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiBX,CAAA;AAEV,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,UAAU,CAAA;AACtD,MAAM,MAAM,WAAW,CAAC,MAAM,SAAS,gBAAgB,IAAI,CAAC,CAAC,WAAW,CAAC,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA"}
@@ -43,6 +43,13 @@ const draftTypes = ['COMPOSE', 'REPLY', 'FORWARD'];
43
43
  const jobStates = ['PENDING', 'RUNNING', 'SUCCEEDED', 'FAILED', 'CANCELLED'];
44
44
  const labelSpecialUses = ['ALL', 'ARCHIVE', 'DRAFTS', 'INBOX', 'JUNK', 'SENT', 'STARRED', 'TRASH'];
45
45
  const mailboxSyncStates = ['INITIAL', 'SYNCING', 'IDLE', 'ERROR'];
46
+ const oauthOperations = ['CONNECT', 'REPLACE'];
47
+ const oauthProviders = ['GMAIL', 'OUTLOOK'];
48
+ const oauthReturnUris = [
49
+ 'https://app.marcoapp.io/onboarding/provider-selection',
50
+ 'https://app.staging.marcoapp.io/onboarding/provider-selection',
51
+ ];
52
+ const oauthReturnUriSchema = v.picklist(oauthReturnUris);
46
53
  const watcherShardStates = ['ACTIVE', 'IDLE', 'STOPPED', 'FAILED'];
47
54
  const appwriteIds = {
48
55
  bucket: 'marco-files',
@@ -62,6 +69,7 @@ const appwriteIds = {
62
69
  labels: 'labels',
63
70
  mailboxSyncStates: 'mailbox_sync_states',
64
71
  messages: 'messages',
72
+ oauthStates: 'oauth_states',
65
73
  profiles: 'profiles',
66
74
  pushNotificationTokens: 'push_notification_tokens',
67
75
  savedViews: 'saved_views',
@@ -135,8 +143,9 @@ const rowSchemas = {
135
143
  }),
136
144
  credentials: v.strictObject({
137
145
  ...wireMetadata(appwriteIds.tables.credentials),
146
+ accountId: identifierSchema,
138
147
  encryptedPayload: v.string(),
139
- expiresAt: v.optional(timestampSchema),
148
+ expiresAt: v.nullable(timestampSchema),
140
149
  provider: v.picklist(accountProviders),
141
150
  userId: identifierSchema,
142
151
  }),
@@ -216,6 +225,18 @@ const rowSchemas = {
216
225
  threadId: identifierSchema,
217
226
  userId: identifierSchema,
218
227
  }),
228
+ oauthStates: v.strictObject({
229
+ ...wireMetadata(appwriteIds.tables.oauthStates),
230
+ accountId: identifierSchema,
231
+ email: emailSchema,
232
+ expiresAt: timestampSchema,
233
+ nonceHash: identifierSchema,
234
+ operation: v.picklist(oauthOperations),
235
+ profile: identifierSchema,
236
+ provider: v.picklist(oauthProviders),
237
+ returnUri: oauthReturnUriSchema,
238
+ userId: identifierSchema,
239
+ }),
219
240
  profiles: v.strictObject({
220
241
  ...wireMetadata(appwriteIds.tables.profiles),
221
242
  avatarFileId: v.nullable(appwriteRowIdSchema),
@@ -292,5 +313,11 @@ export const appwrite = {
292
313
  messageRecipients: messageRecipientsSchema,
293
314
  },
294
315
  ids: appwriteIds,
316
+ oauth: {
317
+ operations: oauthOperations,
318
+ providers: oauthProviders,
319
+ returnUriSchema: oauthReturnUriSchema,
320
+ returnUris: oauthReturnUris,
321
+ },
295
322
  rowSchemas,
296
323
  };
@@ -66,7 +66,9 @@ const validRows = {
66
66
  credentials: {
67
67
  ...metadata,
68
68
  $tableId: 'credentials',
69
+ accountId: 'account-1',
69
70
  encryptedPayload: 'encrypted-payload',
71
+ expiresAt: null,
70
72
  provider: 'GMAIL',
71
73
  userId: 'user-1',
72
74
  },
@@ -151,6 +153,19 @@ const validRows = {
151
153
  threadId: 'thread-1',
152
154
  userId: 'user-1',
153
155
  },
156
+ oauthStates: {
157
+ ...metadata,
158
+ $tableId: 'oauth_states',
159
+ accountId: 'account-1',
160
+ email: 'marco@example.com',
161
+ expiresAt: '2026-07-15T00:10:00.000Z',
162
+ nonceHash: 'test-nonce-hash-1',
163
+ operation: 'CONNECT',
164
+ profile: 'user-1',
165
+ provider: 'GMAIL',
166
+ returnUri: 'https://app.marcoapp.io/onboarding/provider-selection',
167
+ userId: 'user-1',
168
+ },
154
169
  profiles: {
155
170
  ...metadata,
156
171
  $tableId: 'profiles',
@@ -239,6 +254,7 @@ describe('appwrite', () => {
239
254
  labels: 'labels',
240
255
  mailboxSyncStates: 'mailbox_sync_states',
241
256
  messages: 'messages',
257
+ oauthStates: 'oauth_states',
242
258
  profiles: 'profiles',
243
259
  pushNotificationTokens: 'push_notification_tokens',
244
260
  savedViews: 'saved_views',
@@ -248,6 +264,16 @@ describe('appwrite', () => {
248
264
  },
249
265
  });
250
266
  });
267
+ it('exports the shared OAuth state contract', () => {
268
+ expect(appwrite.oauth).toMatchObject({
269
+ operations: ['CONNECT', 'REPLACE'],
270
+ providers: ['GMAIL', 'OUTLOOK'],
271
+ returnUris: [
272
+ 'https://app.marcoapp.io/onboarding/provider-selection',
273
+ 'https://app.staging.marcoapp.io/onboarding/provider-selection',
274
+ ],
275
+ });
276
+ });
251
277
  const rowTestCases = Object.keys(validRows);
252
278
  for (const table of rowTestCases) {
253
279
  it(`accepts the ${table} wire row`, () => {
@@ -258,6 +284,34 @@ describe('appwrite', () => {
258
284
  expect(v.safeParse(appwrite.rowSchemas[table], rowWithoutId).success).toBe(false);
259
285
  });
260
286
  }
287
+ const validOAuthStates = [
288
+ { expected: true, operation: 'CONNECT', provider: 'GMAIL' },
289
+ { expected: true, operation: 'REPLACE', provider: 'OUTLOOK' },
290
+ ];
291
+ for (const { expected, operation, provider } of validOAuthStates) {
292
+ it(`accepts the ${operation} ${provider} OAuth state`, () => {
293
+ expect(v.safeParse(appwrite.rowSchemas.oauthStates, {
294
+ ...validRows.oauthStates,
295
+ operation,
296
+ provider,
297
+ }).success).toBe(expected);
298
+ });
299
+ }
300
+ const invalidOAuthStates = [
301
+ { description: 'an unknown operation', expected: false, value: { operation: 'UNKNOWN' } },
302
+ { description: 'an unknown provider', expected: false, value: { provider: 'UNKNOWN' } },
303
+ { description: 'a malformed return URL', expected: false, value: { returnUri: 'not-a-url' } },
304
+ {
305
+ description: 'an unapproved return destination',
306
+ expected: false,
307
+ value: { returnUri: 'https://attacker.example/onboarding/provider-selection' },
308
+ },
309
+ ];
310
+ for (const { description, expected, value } of invalidOAuthStates) {
311
+ it(`rejects an OAuth state with ${description}`, () => {
312
+ expect(v.safeParse(appwrite.rowSchemas.oauthStates, { ...validRows.oauthStates, ...value }).success).toBe(expected);
313
+ });
314
+ }
261
315
  const invalidSequences = [
262
316
  { description: 'numeric', value: 1 },
263
317
  { description: 'empty', value: '' },
@@ -1,6 +1,6 @@
1
1
  import * as v from 'valibot';
2
2
  export declare const appwriteAvatar: {
3
- createFileId: (userId: string, sha256: string) => string;
3
+ createFileId: (commandId: string) => string;
4
4
  };
5
5
  export declare const appwriteCommands: {
6
6
  readonly errorCodes: readonly ["AUTHENTICATION_FAILED", "AUTHORIZATION_FAILED", "COMMAND_CONFLICT", "COMMAND_NOT_FOUND", "INVALID_COMMAND", "PROVIDER_REJECTED", "TRANSIENT_FAILURE", "INTERNAL_ERROR"];
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/appwrite/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAuB5B,eAAO,MAAM,cAAc;2BACF,MAAM,UAAU,MAAM,KAAG,MAAM;CAEvD,CAAA;AAgQD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CASnB,CAAA;AAEV,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,gBAAgB,CAAC,WAAW,CAAC,CAAA;AACrF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,gBAAgB,CAAC,aAAa,CAAC,CAAA;AACzF,MAAM,MAAM,mBAAmB,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAA;AAChE,MAAM,MAAM,sBAAsB,CAAC,KAAK,SAAS,mBAAmB,IAAI,OAAO,CAC7E,sBAAsB,EACtB;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,CAChB,CAAC,SAAS,CAAC,CAAA;AACZ,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,gBAAgB,CAAC,cAAc,CAAC,CAAA;AAC3F,MAAM,MAAM,oBAAoB,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAA"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/appwrite/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAiB5B,eAAO,MAAM,cAAc;8BACC,MAAM,KAAG,MAAM;CAC1C,CAAA;AAgQD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CASnB,CAAA;AAEV,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,gBAAgB,CAAC,WAAW,CAAC,CAAA;AACrF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,gBAAgB,CAAC,aAAa,CAAC,CAAA;AACzF,MAAM,MAAM,mBAAmB,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAA;AAChE,MAAM,MAAM,sBAAsB,CAAC,KAAK,SAAS,mBAAmB,IAAI,OAAO,CAC7E,sBAAsB,EACtB;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,CAChB,CAAC,SAAS,CAAC,CAAA;AACZ,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,gBAAgB,CAAC,cAAc,CAAC,CAAA;AAC3F,MAAM,MAAM,oBAAoB,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAA"}
@@ -8,9 +8,8 @@ const timestampSchema = v.pipe(v.string(), v.isoTimestamp());
8
8
  const appwriteThreadCommandBatchSize = 100;
9
9
  const threadIdsSchema = v.pipe(v.array(id.threadId.required()), v.minLength(1), v.maxLength(appwriteThreadCommandBatchSize));
10
10
  const avatarSha256Schema = v.pipe(v.string(), v.regex(/^[a-f0-9]{64}$/));
11
- const appwriteUserIdSchema = v.pipe(v.string(), v.minLength(1), v.maxLength(20), v.regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/));
12
11
  export const appwriteAvatar = {
13
- createFileId: (userId, sha256) => `avatar_${v.parse(appwriteUserIdSchema, userId)}${v.parse(avatarSha256Schema, sha256).slice(0, 9)}`,
12
+ createFileId: (commandId) => v.parse(commandIdSchema, commandId),
14
13
  };
15
14
  const commandKinds = {
16
15
  accountDelete: 'account.delete',
@@ -7,7 +7,6 @@ const commandMetadata = {
7
7
  version: 1,
8
8
  };
9
9
  const avatarSha256 = 'a'.repeat(64);
10
- const avatarUserId = 'avatarowner123456789';
11
10
  const commandFixtures = [
12
11
  {
13
12
  command: {
@@ -291,12 +290,12 @@ describe('appwriteCommands', () => {
291
290
  'INTERNAL_ERROR',
292
291
  ]);
293
292
  });
294
- it('derives a user-scoped content-addressed avatar file ID within Appwrite limits', () => {
295
- expect(appwriteAvatar.createFileId(avatarUserId, avatarSha256)).toBe(`avatar_${avatarUserId}${avatarSha256.slice(0, 9)}`);
293
+ it('derives a command-scoped avatar file ID within Appwrite limits', () => {
294
+ expect(appwriteAvatar.createFileId(commandMetadata.id)).toBe(commandMetadata.id);
295
+ expect(appwriteAvatar.createFileId('0197f3d4-1b22-7abc-8def-123456789abd')).not.toBe(appwriteAvatar.createFileId(commandMetadata.id));
296
296
  });
297
- it('rejects an invalid avatar input before deriving a file ID', () => {
298
- expect(() => appwriteAvatar.createFileId(avatarUserId, 'not-a-sha256')).toThrow();
299
- expect(() => appwriteAvatar.createFileId('invalid!user', avatarSha256)).toThrow();
297
+ it('rejects an invalid command ID before deriving an avatar file ID', () => {
298
+ expect(() => appwriteAvatar.createFileId('not-a-command-id')).toThrow();
300
299
  });
301
300
  it('rejects a thread command larger than one bounded worker page', () => {
302
301
  expect(v.safeParse(appwriteCommands.requestSchema, {
@@ -15,9 +15,10 @@ export declare const appwriteFunctions: {
15
15
  requestSchema: v.StrictObjectSchema<{
16
16
  readonly accountId: v.NonOptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>, v.MaxLengthAction<string, number, undefined>, v.BrandAction<string, "AccountId">]>, undefined>;
17
17
  readonly connection: v.VariantSchema<"kind", [v.StrictObjectSchema<{
18
+ readonly email: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.EmailAction<string, undefined>]>;
18
19
  readonly kind: v.LiteralSchema<"OAUTH", undefined>;
19
20
  readonly provider: v.PicklistSchema<["GMAIL", "OUTLOOK"], undefined>;
20
- readonly redirectUri: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.UrlAction<string, undefined>]>;
21
+ readonly returnUri: v.PicklistSchema<readonly ["https://app.marcoapp.io/onboarding/provider-selection", "https://app.staging.marcoapp.io/onboarding/provider-selection"], undefined>;
21
22
  }, undefined>, v.StrictObjectSchema<{
22
23
  readonly imap: v.StrictObjectSchema<{
23
24
  readonly host: v.NonOptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>, v.MaxLengthAction<string, number, undefined>]>, undefined>;
@@ -65,9 +66,10 @@ export declare const appwriteFunctions: {
65
66
  path: "/accounts/connect";
66
67
  requestSchema: v.StrictObjectSchema<{
67
68
  readonly connection: v.VariantSchema<"kind", [v.StrictObjectSchema<{
69
+ readonly email: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.EmailAction<string, undefined>]>;
68
70
  readonly kind: v.LiteralSchema<"OAUTH", undefined>;
69
71
  readonly provider: v.PicklistSchema<["GMAIL", "OUTLOOK"], undefined>;
70
- readonly redirectUri: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.UrlAction<string, undefined>]>;
72
+ readonly returnUri: v.PicklistSchema<readonly ["https://app.marcoapp.io/onboarding/provider-selection", "https://app.staging.marcoapp.io/onboarding/provider-selection"], undefined>;
71
73
  }, undefined>, v.StrictObjectSchema<{
72
74
  readonly imap: v.StrictObjectSchema<{
73
75
  readonly host: v.NonOptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>, v.MaxLengthAction<string, number, undefined>]>, undefined>;
@@ -443,7 +445,7 @@ export declare const appwriteFunctions: {
443
445
  }, undefined>;
444
446
  context: "webhook";
445
447
  method: "GET";
446
- path: "/webhooks/oauth";
448
+ path: "/v1/wh/oauth/callback";
447
449
  requestSchema: v.StrictObjectSchema<{
448
450
  readonly code: v.NonOptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>, v.MaxLengthAction<string, number, undefined>]>, undefined>;
449
451
  readonly state: v.NonOptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>, v.MaxLengthAction<string, number, undefined>]>, undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../../src/appwrite/functions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAQ5B,KAAK,uBAAuB,GAAG,eAAe,GAAG,UAAU,GAAG,SAAS,CAAA;AAqJvE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWpB,CAAA;AAEV,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,iBAAiB,CAAC,WAAW,CAAC,CAAA;AACvF,YAAY,EAAE,uBAAuB,EAAE,CAAA;AACvC,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,QAAQ,CAAA;AACnD,MAAM,MAAM,yBAAyB,CAAC,SAAS,SAAS,oBAAoB,IAAI,SAAS,SAAS,oBAAoB,GAClH,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,GACrD,KAAK,CAAA;AACT,MAAM,MAAM,qBAAqB,CAC/B,SAAS,SAAS,oBAAoB,EACtC,MAAM,SAAS,yBAAyB,CAAC,SAAS,CAAC,IACjD,SAAS,SAAS,oBAAoB,GACtC,MAAM,SAAS,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,GAClE,CAAC,OAAO,iBAAiB,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GACvD,KAAK,GACP,KAAK,CAAA;AACT,MAAM,MAAM,uBAAuB,CACjC,SAAS,SAAS,oBAAoB,EACtC,MAAM,SAAS,yBAAyB,CAAC,SAAS,CAAC,IACjD,qBAAqB,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS;IAAE,aAAa,EAAE,MAAM,OAAO,SAAS,CAAC,CAAC,aAAa,CAAA;CAAE,GACzG,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,GACtB,KAAK,CAAA;AACT,MAAM,MAAM,wBAAwB,CAClC,SAAS,SAAS,oBAAoB,EACtC,MAAM,SAAS,yBAAyB,CAAC,SAAS,CAAC,IACjD,qBAAqB,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS;IAAE,cAAc,EAAE,MAAM,OAAO,SAAS,CAAC,CAAC,aAAa,CAAA;CAAE,GAC1G,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,GACtB,KAAK,CAAA"}
1
+ {"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../../src/appwrite/functions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAS5B,KAAK,uBAAuB,GAAG,eAAe,GAAG,UAAU,GAAG,SAAS,CAAA;AAsJvE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWpB,CAAA;AAEV,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,iBAAiB,CAAC,WAAW,CAAC,CAAA;AACvF,YAAY,EAAE,uBAAuB,EAAE,CAAA;AACvC,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,QAAQ,CAAA;AACnD,MAAM,MAAM,yBAAyB,CAAC,SAAS,SAAS,oBAAoB,IAAI,SAAS,SAAS,oBAAoB,GAClH,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,GACrD,KAAK,CAAA;AACT,MAAM,MAAM,qBAAqB,CAC/B,SAAS,SAAS,oBAAoB,EACtC,MAAM,SAAS,yBAAyB,CAAC,SAAS,CAAC,IACjD,SAAS,SAAS,oBAAoB,GACtC,MAAM,SAAS,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,GAClE,CAAC,OAAO,iBAAiB,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GACvD,KAAK,GACP,KAAK,CAAA;AACT,MAAM,MAAM,uBAAuB,CACjC,SAAS,SAAS,oBAAoB,EACtC,MAAM,SAAS,yBAAyB,CAAC,SAAS,CAAC,IACjD,qBAAqB,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS;IAAE,aAAa,EAAE,MAAM,OAAO,SAAS,CAAC,CAAC,aAAa,CAAA;CAAE,GACzG,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,GACtB,KAAK,CAAA;AACT,MAAM,MAAM,wBAAwB,CAClC,SAAS,SAAS,oBAAoB,EACtC,MAAM,SAAS,yBAAyB,CAAC,SAAS,CAAC,IACjD,qBAAqB,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS;IAAE,cAAc,EAAE,MAAM,OAAO,SAAS,CAAC,CAAC,aAAa,CAAA;CAAE,GAC1G,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,GACtB,KAAK,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import * as v from 'valibot';
2
+ import { oauth } from '../constants/oauth';
2
3
  import { id } from '../schemas/id';
3
4
  import { string } from '../schemas/string';
4
5
  import { appwrite } from './appwrite';
@@ -29,9 +30,10 @@ const providerCredentialSchema = v.strictObject({
29
30
  });
30
31
  const connectionSchema = v.variant('kind', [
31
32
  v.strictObject({
33
+ email: v.pipe(v.string(), v.email()),
32
34
  kind: v.literal('OAUTH'),
33
35
  provider: v.picklist(['GMAIL', 'OUTLOOK']),
34
- redirectUri: v.pipe(v.string(), v.url()),
36
+ returnUri: appwrite.oauth.returnUriSchema,
35
37
  }),
36
38
  v.strictObject({
37
39
  imap: providerCredentialSchema,
@@ -85,7 +87,7 @@ const apiRoutes = {
85
87
  oauthWebhook: createRoute({
86
88
  context: 'webhook',
87
89
  method: 'GET',
88
- path: '/webhooks/oauth',
90
+ path: oauth.redirectPath,
89
91
  requestSchema: v.strictObject({
90
92
  code: string.required(4096),
91
93
  state: string.required(4096),
@@ -49,7 +49,14 @@ const routeFixtures = [
49
49
  },
50
50
  {
51
51
  name: 'account connect',
52
- request: { connection: { kind: 'OAUTH', provider: 'GMAIL', redirectUri: 'https://marco.app/oauth' } },
52
+ request: {
53
+ connection: {
54
+ email: 'test-user@example.com',
55
+ kind: 'OAUTH',
56
+ provider: 'GMAIL',
57
+ returnUri: 'https://app.marcoapp.io/onboarding/provider-selection',
58
+ },
59
+ },
53
60
  response: {
54
61
  accountId: 'account_1',
55
62
  authorizationUrl: 'https://accounts.google.com/o/oauth2/auth',
@@ -119,7 +126,7 @@ describe('appwriteFunctions', () => {
119
126
  { context: 'authenticated', functionId: 'marco-api', method: 'PUT', path: '/accounts/connection' },
120
127
  { context: 'authenticated', functionId: 'marco-api', method: 'POST', path: '/push-tokens' },
121
128
  { context: 'authenticated', functionId: 'marco-api', method: 'DELETE', path: '/account/delete' },
122
- { context: 'webhook', functionId: 'marco-api', method: 'GET', path: '/webhooks/oauth' },
129
+ { context: 'webhook', functionId: 'marco-api', method: 'GET', path: '/v1/wh/oauth/callback' },
123
130
  { context: 'internal', functionId: 'marco-worker', method: 'POST', path: '/' },
124
131
  { context: 'internal', functionId: 'marco-worker', method: 'POST', path: '/jobs/run' },
125
132
  { context: 'internal', functionId: 'marco-worker', method: 'POST', path: '/watchers/run' },
@@ -151,4 +158,55 @@ describe('appwriteFunctions', () => {
151
158
  retryable: false,
152
159
  }).success).toBe(false);
153
160
  });
161
+ const invalidOAuthConnections = [
162
+ {
163
+ connection: {
164
+ kind: 'OAUTH',
165
+ provider: 'GMAIL',
166
+ returnUri: 'https://app.marcoapp.io/onboarding/provider-selection',
167
+ },
168
+ description: 'a missing mailbox email',
169
+ expected: false,
170
+ },
171
+ {
172
+ connection: {
173
+ email: 'not-an-email',
174
+ kind: 'OAUTH',
175
+ provider: 'GMAIL',
176
+ returnUri: 'https://app.marcoapp.io/onboarding/provider-selection',
177
+ },
178
+ description: 'a malformed mailbox email',
179
+ expected: false,
180
+ },
181
+ {
182
+ connection: {
183
+ email: 'test-user@example.com',
184
+ kind: 'OAUTH',
185
+ provider: 'GMAIL',
186
+ returnUri: 'https://attacker.example/onboarding/provider-selection',
187
+ },
188
+ description: 'an unapproved return destination',
189
+ expected: false,
190
+ },
191
+ ];
192
+ const oauthConnectionRoutes = [
193
+ {
194
+ description: 'connect',
195
+ includesAccountId: false,
196
+ requestSchema: appwriteFunctions.api.routes.accountsConnect.requestSchema,
197
+ },
198
+ {
199
+ description: 'replacement',
200
+ includesAccountId: true,
201
+ requestSchema: appwriteFunctions.api.routes.accountConnection.requestSchema,
202
+ },
203
+ ];
204
+ for (const { connection, description, expected } of invalidOAuthConnections) {
205
+ for (const { description: routeDescription, includesAccountId, requestSchema } of oauthConnectionRoutes) {
206
+ it(`rejects ${routeDescription} OAuth connection with ${description}`, () => {
207
+ const request = includesAccountId ? { accountId: 'account_1', connection } : { connection };
208
+ expect(v.safeParse(requestSchema, request).success).toBe(expected);
209
+ });
210
+ }
211
+ }
154
212
  });
@@ -2,6 +2,10 @@ import type { SubscriptionTier } from './types';
2
2
  export type MarcoEnvironment = 'production' | 'staging' | 'development';
3
3
  export declare const MARCO_ENV: MarcoEnvironment;
4
4
  type MarcoPublicConfig = {
5
+ appwrite: {
6
+ endpoint: string;
7
+ projectId: string;
8
+ };
5
9
  marco: {
6
10
  api: {
7
11
  baseUrl: string;
@@ -1 +1 @@
1
- {"version":3,"file":"marcoPublicConfig.d.ts","sourceRoot":"","sources":["../src/marcoPublicConfig.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,SAAS,GAAG,aAAa,CAAA;AAEvE,eAAO,MAAM,SAAS,kBAGP,CAAA;AAEf,KAAK,iBAAiB,GAAG;IACvB,KAAK,EAAE;QACL,GAAG,EAAE;YACH,OAAO,EAAE,MAAM,CAAA;SAChB,CAAA;QACD,GAAG,EAAE;YACH,OAAO,EAAE,MAAM,CAAA;SAChB,CAAA;QACD,GAAG,EAAE;YACH,OAAO,EAAE,MAAM,CAAA;YACf,UAAU,EAAE,MAAM,CAAA;SACnB,CAAA;QACD,EAAE,EAAE;YACF,KAAK,EAAE,MAAM,CAAA;YACb,IAAI,EAAE,MAAM,CAAA;YACZ,GAAG,EAAE,MAAM,CAAA;SACZ,CAAA;KACF,CAAA;IACD,KAAK,EAAE;QACL,MAAM,EAAE;YACN,QAAQ,EAAE,MAAM,CAAA;SACjB,CAAA;QACD,OAAO,EAAE;YACP,QAAQ,EAAE,MAAM,CAAA;YAChB,QAAQ,EAAE,MAAM,CAAA;SACjB,CAAA;KACF,CAAA;IACD,MAAM,EAAE;QACN,QAAQ,EAAE;aACP,GAAG,IAAI,gBAAgB,GAAG;gBACzB,EAAE,EAAE,MAAM,CAAA;gBACV,QAAQ,EAAE;oBACR,OAAO,EAAE,MAAM,CAAA;oBACf,MAAM,EAAE,MAAM,CAAA;iBACf,CAAA;aACF;SACF,CAAA;KACF,CAAA;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAA;KACjB,CAAA;CACF,CAAA;AAmID,eAAO,MAAM,iBAAiB,SAAS,gBAAgB,sBAAmC,CAAA"}
1
+ {"version":3,"file":"marcoPublicConfig.d.ts","sourceRoot":"","sources":["../src/marcoPublicConfig.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,SAAS,GAAG,aAAa,CAAA;AAEvE,eAAO,MAAM,SAAS,kBAGP,CAAA;AAEf,KAAK,iBAAiB,GAAG;IACvB,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAA;QAChB,SAAS,EAAE,MAAM,CAAA;KAClB,CAAA;IACD,KAAK,EAAE;QACL,GAAG,EAAE;YACH,OAAO,EAAE,MAAM,CAAA;SAChB,CAAA;QACD,GAAG,EAAE;YACH,OAAO,EAAE,MAAM,CAAA;SAChB,CAAA;QACD,GAAG,EAAE;YACH,OAAO,EAAE,MAAM,CAAA;YACf,UAAU,EAAE,MAAM,CAAA;SACnB,CAAA;QACD,EAAE,EAAE;YACF,KAAK,EAAE,MAAM,CAAA;YACb,IAAI,EAAE,MAAM,CAAA;YACZ,GAAG,EAAE,MAAM,CAAA;SACZ,CAAA;KACF,CAAA;IACD,KAAK,EAAE;QACL,MAAM,EAAE;YACN,QAAQ,EAAE,MAAM,CAAA;SACjB,CAAA;QACD,OAAO,EAAE;YACP,QAAQ,EAAE,MAAM,CAAA;YAChB,QAAQ,EAAE,MAAM,CAAA;SACjB,CAAA;KACF,CAAA;IACD,MAAM,EAAE;QACN,QAAQ,EAAE;aACP,GAAG,IAAI,gBAAgB,GAAG;gBACzB,EAAE,EAAE,MAAM,CAAA;gBACV,QAAQ,EAAE;oBACR,OAAO,EAAE,MAAM,CAAA;oBACf,MAAM,EAAE,MAAM,CAAA;iBACf,CAAA;aACF;SACF,CAAA;KACF,CAAA;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAA;KACjB,CAAA;CACF,CAAA;AA+ID,eAAO,MAAM,iBAAiB,SAAS,gBAAgB,sBAAmC,CAAA"}
@@ -3,6 +3,10 @@ export const MARCO_ENV = process.env.MARCO_ENV ||
3
3
  'development';
4
4
  const configMap = {
5
5
  development: {
6
+ appwrite: {
7
+ endpoint: 'https://nyc.cloud.appwrite.io/v1',
8
+ projectId: 'marco',
9
+ },
6
10
  marco: {
7
11
  api: {
8
12
  baseUrl: 'https://api.staging.marcoapp.io',
@@ -45,6 +49,10 @@ const configMap = {
45
49
  },
46
50
  },
47
51
  production: {
52
+ appwrite: {
53
+ endpoint: 'https://nyc.cloud.appwrite.io/v1',
54
+ projectId: 'marco',
55
+ },
48
56
  marco: {
49
57
  api: {
50
58
  baseUrl: 'https://api.marcoapp.io',
@@ -87,6 +95,10 @@ const configMap = {
87
95
  },
88
96
  },
89
97
  staging: {
98
+ appwrite: {
99
+ endpoint: 'https://nyc.cloud.appwrite.io/v1',
100
+ projectId: 'marco',
101
+ },
90
102
  marco: {
91
103
  api: {
92
104
  baseUrl: 'https://api.staging.marcoapp.io',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marcoappio/marco-config",
3
- "version": "2.0.594",
3
+ "version": "2.0.596",
4
4
  "author": "team@marcoapp.io",
5
5
  "main": "dist/index.js",
6
6
  "repository": "git@github.com:marcoappio/marco-config.git",