@pome-sh/cli 0.21.12 → 0.21.14

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.
@@ -2,6 +2,7 @@ export { GMAIL_CHECKS } from './chunk-EU6SGN7A.js';
2
2
  import { gmailErrorEnvelope, gmailSeedSchema, parseSeed, defaultSeedState, notFound, invalidArgument, checkFault, unsupported, validateSearchQuery, SEARCH_MAILBOX_MESSAGE_BUDGET, GmailError, parseSearchQuery, CATEGORY_LABELS, parseDate, parseSize, KNOWN_FIELDS, parseDuration, assertHasOperator } from './chunk-NJ246QPJ.js';
3
3
  export { DEFAULT_GMAIL_AGENT_EMAIL, GmailError, SEARCH_MAILBOX_MESSAGE_BUDGET, agentPathInboxMailbox, defaultSeedState, gmailErrorEnvelope, gmailSeedSchema, loadSeedFromEnv, parseSearchQuery, parseSeed, validateSearchQuery } from './chunk-NJ246QPJ.js';
4
4
  import './chunk-NY55QTVQ.js';
5
+ import { integerInput, booleanInput, repeatedInput, declareRouteInputs, mountDeclaredRoute, UndeclaredInputError, MalformedBodyError } from './chunk-4MQULI7E.js';
5
6
  import { loadMcpToolFixture, deriveMcpToolTable, defineTwin, openTwinDatabase, createApp } from './chunk-TV5S6WQV.js';
6
7
  import './chunk-VBATFCWR.js';
7
8
  import './chunk-SG6ZTIMT.js';
@@ -2773,26 +2774,8 @@ function isBinary(value) {
2773
2774
  }
2774
2775
 
2775
2776
  // ../packages/twin-gmail/dist/src/rest-common.js
2776
- function emailFromContext(c) {
2777
- return resolveUserEmail(routeParam(c, "userId"), c.get("session"));
2778
- }
2779
- function routeParam(c, name) {
2780
- const value = c.req.param(name);
2781
- if (!value)
2782
- invalidArgument(`Missing path parameter: ${name}`);
2783
- return value;
2784
- }
2785
- async function readJsonObject(c) {
2786
- try {
2787
- const value = await c.req.json();
2788
- if (!value || typeof value !== "object" || Array.isArray(value))
2789
- invalidArgument("Invalid request body");
2790
- return value;
2791
- } catch (error) {
2792
- if (error instanceof GmailError)
2793
- throw error;
2794
- invalidArgument("Invalid JSON payload received.");
2795
- }
2777
+ function emailFrom(userId, c) {
2778
+ return resolveUserEmail(userId, c.get("session"));
2796
2779
  }
2797
2780
  function stringField(body, name, required = false) {
2798
2781
  const value = body[name];
@@ -2811,54 +2794,26 @@ function stringArray(body, name, limit = 100) {
2811
2794
  }
2812
2795
  return [...new Set(value)];
2813
2796
  }
2814
- function objectField(body, name, required = false) {
2815
- const value = body[name];
2816
- if (value === void 0 && !required)
2817
- return void 0;
2818
- if (!value || typeof value !== "object" || Array.isArray(value))
2819
- invalidArgument(`Invalid ${name}`);
2820
- return value;
2821
- }
2822
- function booleanQuery(c, name, fallback = false) {
2823
- const value = c.req.query(name);
2824
- if (value === void 0)
2825
- return fallback;
2826
- if (value === "true")
2827
- return true;
2828
- if (value === "false")
2829
- return false;
2830
- invalidArgument(`Invalid value for ${name}`);
2831
- }
2832
- function numberQuery(c, name, fallback, max) {
2833
- const raw = c.req.query(name);
2834
- if (raw === void 0)
2835
- return fallback;
2836
- const value = Number(raw);
2837
- if (!Number.isInteger(value) || value < 1 || value > max)
2838
- invalidArgument(`Invalid value for ${name}`);
2839
- return value;
2840
- }
2841
- function repeatedQuery(c, name) {
2842
- return new URL(c.req.url).searchParams.getAll(name);
2843
- }
2844
- function messageFormat(c, allowRaw = true) {
2845
- const raw = (c.req.query("format") ?? "full").toLowerCase();
2846
- const allowed = allowRaw ? ["minimal", "full", "raw", "metadata"] : ["minimal", "full", "metadata"];
2847
- if (!allowed.includes(raw))
2848
- invalidArgument("Invalid format");
2849
- return raw;
2850
- }
2851
- function rejectUnsupportedQuery(c, names) {
2852
- for (const name of names) {
2853
- if (booleanQuery(c, name, false))
2797
+ function rejectUnsupportedFlags(flags) {
2798
+ for (const [name, value] of Object.entries(flags)) {
2799
+ if (value)
2854
2800
  unsupported(`${name}=true is not supported by the Gmail twin`);
2855
2801
  }
2856
2802
  }
2803
+ function rejectResumable(uploadType) {
2804
+ if (uploadType === "resumable")
2805
+ unsupported("Resumable Gmail uploads are not supported");
2806
+ }
2857
2807
  function rejectClassification(body) {
2858
2808
  if (body.addClassificationLabels !== void 0 || body.removeClassificationLabelIds !== void 0) {
2859
2809
  unsupported("Gmail classification labels require Google Drive Labels and are not supported");
2860
2810
  }
2861
2811
  }
2812
+ function rejectClassificationValues(resource) {
2813
+ if (resource.classificationLabelValues !== void 0) {
2814
+ unsupported("Gmail classification labels require Google Drive Labels and are not supported");
2815
+ }
2816
+ }
2862
2817
  function paginate2(items, options) {
2863
2818
  const offset = options.pageToken ? decodePageToken(options.pageToken, options.binding, options.snapshot) : 0;
2864
2819
  if (offset > items.length)
@@ -2879,146 +2834,407 @@ function asInputError(fn) {
2879
2834
  invalidArgument(error instanceof Error ? error.message : "Invalid request");
2880
2835
  }
2881
2836
  }
2882
-
2883
- // ../packages/twin-gmail/dist/src/rest-upload.js
2884
- async function readMessageWrite(c, draftEnvelope = false) {
2885
- if (isResumable(c))
2886
- unsupported("Resumable Gmail uploads are not supported");
2887
- const contentType = c.req.header("content-type") ?? "";
2888
- if (/^multipart\/related\b/i.test(contentType)) {
2889
- const { metadata, media } = await readMultipart(c, contentType);
2890
- const resource2 = draftEnvelope ? objectField(metadata, "message") ?? metadata : metadata;
2891
- rejectMessageExtensions(resource2);
2892
- return {
2893
- raw: media,
2894
- threadId: stringField(resource2, "threadId"),
2895
- id: stringField(metadata, "id"),
2896
- labelIds: stringList(resource2, "labelIds")
2897
- };
2898
- }
2899
- if (!/^application\/json\b/i.test(contentType) && !/^text\/json\b/i.test(contentType)) {
2900
- const bytes = Buffer.from(await c.req.arrayBuffer());
2901
- if (!bytes.length)
2902
- invalidArgument("MIME message is empty");
2903
- return { raw: bytes };
2904
- }
2905
- const body = await readJsonObject(c);
2906
- const resource = draftEnvelope ? objectField(body, "message", true) : body;
2907
- rejectMessageExtensions(resource);
2908
- return {
2909
- raw: stringField(resource, "raw", true),
2910
- threadId: stringField(resource, "threadId"),
2911
- id: stringField(body, "id"),
2912
- labelIds: stringList(resource, "labelIds")
2913
- };
2914
- }
2915
- function rejectMessageExtensions(resource) {
2916
- if (resource.classificationLabelValues !== void 0) {
2917
- unsupported("Gmail classification labels require Google Drive Labels and are not supported");
2918
- }
2919
- }
2920
- function stringList(body, name) {
2921
- const value = body[name];
2922
- if (value === void 0)
2923
- return void 0;
2924
- if (!Array.isArray(value) || value.length > 100 || value.some((item) => typeof item !== "string")) {
2925
- invalidArgument(`Invalid ${name}`);
2926
- }
2927
- return [...new Set(value)];
2928
- }
2929
- async function readDraftSend(c) {
2930
- const contentType = c.req.header("content-type") ?? "";
2931
- if (/^application\/json\b/i.test(contentType) || /^text\/json\b/i.test(contentType)) {
2932
- const body = await readJsonObject(c);
2933
- const id = stringField(body, "id");
2934
- const message = objectField(body, "message");
2935
- if (!id && !message)
2936
- invalidArgument("Draft id is required");
2937
- return {
2938
- id,
2939
- ...message ? {
2940
- message: {
2941
- raw: stringField(message, "raw", true),
2942
- threadId: stringField(message, "threadId")
2943
- }
2944
- } : {}
2945
- };
2946
- }
2947
- return { message: await readMessageWrite(c, false) };
2948
- }
2949
- function isResumable(c) {
2950
- return c.req.path.startsWith("/resumable/") || c.req.query("uploadType") === "resumable";
2951
- }
2952
- async function readMultipart(c, contentType) {
2953
- const boundary = contentType.match(/boundary=(?:"([^"]+)"|([^;\s]+))/i)?.slice(1).find(Boolean);
2954
- if (!boundary)
2955
- invalidArgument("Multipart upload is missing a boundary");
2956
- const bytes = Buffer.from(await c.req.arrayBuffer());
2957
- const parts = splitMultipart2(bytes, boundary);
2958
- if (parts.length !== 2)
2959
- invalidArgument("Multipart upload must contain metadata and MIME media");
2960
- let metadata;
2961
- try {
2962
- metadata = JSON.parse(parts[0].body.toString("utf8"));
2963
- } catch {
2964
- invalidArgument("Invalid multipart metadata JSON");
2965
- }
2966
- if (!metadata || typeof metadata !== "object" || Array.isArray(metadata))
2967
- invalidArgument("Invalid multipart metadata JSON");
2968
- if (!parts[1].body.length)
2969
- invalidArgument("MIME message is empty");
2970
- return { metadata, media: parts[1].body };
2971
- }
2972
- function splitMultipart2(bytes, boundary) {
2973
- return asInputError(() => {
2974
- const marker = Buffer.from(`--${boundary}`);
2975
- const endMarker = Buffer.from(`--${boundary}--`);
2976
- const parts = [];
2977
- let cursor = 0;
2978
- while (cursor < bytes.length) {
2979
- const start = bytes.indexOf(marker, cursor);
2980
- if (start < 0 || bytes.subarray(start, start + endMarker.length).equals(endMarker))
2981
- break;
2982
- const lineEnd = bytes.indexOf(Buffer.from("\n"), start + marker.length);
2983
- if (lineEnd < 0)
2984
- throw new Error("Malformed multipart upload");
2985
- const next = bytes.indexOf(marker, lineEnd + 1);
2986
- if (next < 0)
2987
- throw new Error("Malformed multipart upload");
2988
- let chunk = bytes.subarray(lineEnd + 1, next);
2989
- while (chunk.length && (chunk.at(-1) === 10 || chunk.at(-1) === 13))
2990
- chunk = chunk.subarray(0, -1);
2991
- const crlf = chunk.indexOf(Buffer.from("\r\n\r\n"));
2992
- const lf = chunk.indexOf(Buffer.from("\n\n"));
2993
- const separator = crlf >= 0 ? { index: crlf, length: 4 } : { index: lf, length: 2 };
2994
- if (separator.index < 0)
2995
- throw new Error("Malformed multipart upload");
2996
- parts.push({
2997
- headers: chunk.subarray(0, separator.index).toString("latin1"),
2998
- body: Buffer.from(chunk.subarray(separator.index + separator.length))
2999
- });
3000
- cursor = next;
2837
+ var USERS = "/gmail/v1/users/:userId";
2838
+ var MESSAGES = `${USERS}/messages`;
2839
+ var MESSAGES_UPLOAD = "/upload/gmail/v1/users/:userId/messages";
2840
+ var DRAFTS = `${USERS}/drafts`;
2841
+ var DRAFTS_UPLOAD = "/upload/gmail/v1/users/:userId/drafts";
2842
+ var RESUMABLE = "/resumable/upload/gmail/v1/users/:userId";
2843
+ var USER = { userId: z.string().min(1) };
2844
+ var USER_ITEM = { ...USER, id: z.string().min(1) };
2845
+ var LIST_QUERY = {
2846
+ maxResults: integerInput({ min: 1, max: 500 }).default(100),
2847
+ pageToken: z.string().optional()
2848
+ };
2849
+ var SEARCH_QUERY = {
2850
+ q: z.string().default(""),
2851
+ includeSpamTrash: booleanInput.default(false)
2852
+ };
2853
+ var UPLOAD_QUERY = { uploadType: z.string().optional() };
2854
+ var MESSAGE_FORMAT_QUERY = {
2855
+ format: z.enum(["minimal", "full", "raw", "metadata"]).default("full"),
2856
+ metadataHeaders: repeatedInput()
2857
+ };
2858
+ var THREAD_FORMAT_QUERY = {
2859
+ format: z.enum(["minimal", "full", "metadata"]).default("full"),
2860
+ metadataHeaders: repeatedInput()
2861
+ };
2862
+ var unique = (values) => [...new Set(values)];
2863
+ var stringListInput = (max) => z.array(z.string()).max(max).transform(unique).default([]);
2864
+ var optionalStringListInput = (max) => z.array(z.string()).max(max).transform(unique).optional();
2865
+ var RAW_INPUT = z.union([
2866
+ z.string().min(1),
2867
+ z.instanceof(Uint8Array).refine((bytes) => bytes.byteLength > 0)
2868
+ ]);
2869
+ var CLASSIFICATION_BODY = {
2870
+ addClassificationLabels: z.unknown().optional(),
2871
+ removeClassificationLabelIds: z.unknown().optional()
2872
+ };
2873
+ var LABEL_MODIFY_BODY = {
2874
+ addLabelIds: stringListInput(100),
2875
+ removeLabelIds: stringListInput(100)
2876
+ };
2877
+ var MESSAGE_RESOURCE = {
2878
+ raw: RAW_INPUT,
2879
+ threadId: z.string().optional(),
2880
+ labelIds: optionalStringListInput(100),
2881
+ /** Accepted, then answered 501. */
2882
+ classificationLabelValues: z.unknown().optional()
2883
+ };
2884
+ var MESSAGE_WRITE_BODY = { ...MESSAGE_RESOURCE, id: z.string().optional() };
2885
+ var DRAFT_WRITE_BODY = {
2886
+ message: z.object(MESSAGE_RESOURCE),
2887
+ id: z.string().optional()
2888
+ };
2889
+ var DRAFT_UPDATE_BODY = {
2890
+ message: z.object(MESSAGE_RESOURCE),
2891
+ id: z.string().optional()
2892
+ };
2893
+ var DRAFT_SEND_BODY = {
2894
+ id: z.string().optional(),
2895
+ message: z.object({ raw: RAW_INPUT, threadId: z.string().optional() }).optional()
2896
+ };
2897
+ var LABEL_BODY = {
2898
+ color: z.object({ textColor: z.string().optional(), backgroundColor: z.string().optional() }).optional(),
2899
+ type: z.unknown().optional(),
2900
+ labelListVisibility: z.unknown().optional(),
2901
+ messageListVisibility: z.unknown().optional()
2902
+ };
2903
+ var jsonObjectInput = z.record(z.string(), z.unknown());
2904
+ var MESSAGE_SEND_SHAPE = {
2905
+ method: "POST",
2906
+ pathParams: USER,
2907
+ query: UPLOAD_QUERY,
2908
+ bodyEncoding: "media",
2909
+ mediaField: "raw",
2910
+ body: MESSAGE_WRITE_BODY
2911
+ };
2912
+ var MESSAGE_IMPORT_SHAPE = {
2913
+ method: "POST",
2914
+ pathParams: USER,
2915
+ query: {
2916
+ ...UPLOAD_QUERY,
2917
+ deleted: booleanInput.default(false),
2918
+ processForCalendar: booleanInput.default(false),
2919
+ neverMarkSpam: booleanInput.default(false),
2920
+ internalDateSource: z.enum(["receivedTime", "dateHeader"]).default("dateHeader")
2921
+ },
2922
+ bodyEncoding: "media",
2923
+ mediaField: "raw",
2924
+ body: MESSAGE_WRITE_BODY
2925
+ };
2926
+ var MESSAGE_INSERT_SHAPE = {
2927
+ method: "POST",
2928
+ pathParams: USER,
2929
+ query: {
2930
+ ...UPLOAD_QUERY,
2931
+ deleted: booleanInput.default(false),
2932
+ internalDateSource: z.enum(["receivedTime", "dateHeader"]).default("receivedTime")
2933
+ },
2934
+ bodyEncoding: "media",
2935
+ mediaField: "raw",
2936
+ body: MESSAGE_WRITE_BODY
2937
+ };
2938
+ var DRAFT_CREATE_SHAPE = {
2939
+ method: "POST",
2940
+ pathParams: USER,
2941
+ query: UPLOAD_QUERY,
2942
+ bodyEncoding: "media",
2943
+ mediaField: "message.raw",
2944
+ body: DRAFT_WRITE_BODY
2945
+ };
2946
+ var DRAFT_SEND_SHAPE = {
2947
+ method: "POST",
2948
+ pathParams: USER,
2949
+ query: UPLOAD_QUERY,
2950
+ bodyEncoding: "media",
2951
+ mediaField: "message.raw",
2952
+ body: DRAFT_SEND_BODY
2953
+ };
2954
+ var DRAFT_UPDATE_SHAPE = {
2955
+ method: "PUT",
2956
+ pathParams: USER_ITEM,
2957
+ query: UPLOAD_QUERY,
2958
+ bodyEncoding: "media",
2959
+ mediaField: "message.raw",
2960
+ body: DRAFT_UPDATE_BODY
2961
+ };
2962
+ var REFUSED = { method: "POST", pathParams: USER };
2963
+ var REFUSED_PUT = { method: "PUT", pathParams: USER };
2964
+ var REFUSED_ITEM = { method: "POST", pathParams: USER_ITEM };
2965
+ var REFUSED_ITEM_PUT = { method: "PUT", pathParams: USER_ITEM };
2966
+ var GMAIL_ROUTES = {
2967
+ // messages
2968
+ listMessages: declareRouteInputs({
2969
+ method: "GET",
2970
+ path: MESSAGES,
2971
+ pathParams: USER,
2972
+ query: { ...SEARCH_QUERY, ...LIST_QUERY, labelIds: repeatedInput() }
2973
+ }),
2974
+ batchModifyMessages: declareRouteInputs({
2975
+ method: "POST",
2976
+ path: `${MESSAGES}/batchModify`,
2977
+ pathParams: USER,
2978
+ bodyEncoding: "json",
2979
+ body: {
2980
+ ids: stringListInput(1e3),
2981
+ ...LABEL_MODIFY_BODY,
2982
+ ...CLASSIFICATION_BODY
3001
2983
  }
3002
- return parts;
3003
- });
3004
- }
2984
+ }),
2985
+ batchDeleteMessages: declareRouteInputs({
2986
+ method: "POST",
2987
+ path: `${MESSAGES}/batchDelete`,
2988
+ pathParams: USER,
2989
+ bodyEncoding: "json",
2990
+ body: { ids: stringListInput(1e3) }
2991
+ }),
2992
+ sendMessage: declareRouteInputs({ ...MESSAGE_SEND_SHAPE, path: `${MESSAGES}/send` }),
2993
+ sendMessageUpload: declareRouteInputs({
2994
+ ...MESSAGE_SEND_SHAPE,
2995
+ path: `${MESSAGES_UPLOAD}/send`
2996
+ }),
2997
+ importMessage: declareRouteInputs({ ...MESSAGE_IMPORT_SHAPE, path: `${MESSAGES}/import` }),
2998
+ importMessageUpload: declareRouteInputs({
2999
+ ...MESSAGE_IMPORT_SHAPE,
3000
+ path: `${MESSAGES_UPLOAD}/import`
3001
+ }),
3002
+ insertMessage: declareRouteInputs({ ...MESSAGE_INSERT_SHAPE, path: MESSAGES }),
3003
+ insertMessageUpload: declareRouteInputs({ ...MESSAGE_INSERT_SHAPE, path: MESSAGES_UPLOAD }),
3004
+ getMessage: declareRouteInputs({
3005
+ method: "GET",
3006
+ path: `${MESSAGES}/:id`,
3007
+ pathParams: USER_ITEM,
3008
+ query: MESSAGE_FORMAT_QUERY
3009
+ }),
3010
+ modifyMessage: declareRouteInputs({
3011
+ method: "POST",
3012
+ path: `${MESSAGES}/:id/modify`,
3013
+ pathParams: USER_ITEM,
3014
+ bodyEncoding: "json",
3015
+ body: { ...LABEL_MODIFY_BODY, ...CLASSIFICATION_BODY }
3016
+ }),
3017
+ trashMessage: declareRouteInputs({
3018
+ method: "POST",
3019
+ path: `${MESSAGES}/:id/trash`,
3020
+ pathParams: USER_ITEM
3021
+ }),
3022
+ untrashMessage: declareRouteInputs({
3023
+ method: "POST",
3024
+ path: `${MESSAGES}/:id/untrash`,
3025
+ pathParams: USER_ITEM
3026
+ }),
3027
+ deleteMessage: declareRouteInputs({
3028
+ method: "DELETE",
3029
+ path: `${MESSAGES}/:id`,
3030
+ pathParams: USER_ITEM
3031
+ }),
3032
+ getAttachment: declareRouteInputs({
3033
+ method: "GET",
3034
+ path: `${MESSAGES}/:messageId/attachments/:id`,
3035
+ pathParams: { ...USER_ITEM, messageId: z.string().min(1) }
3036
+ }),
3037
+ resumableInsertMessage: declareRouteInputs({ ...REFUSED, path: `${RESUMABLE}/messages` }),
3038
+ resumableInsertMessagePut: declareRouteInputs({
3039
+ ...REFUSED_PUT,
3040
+ path: `${RESUMABLE}/messages`
3041
+ }),
3042
+ resumableSendMessage: declareRouteInputs({ ...REFUSED, path: `${RESUMABLE}/messages/send` }),
3043
+ resumableSendMessagePut: declareRouteInputs({
3044
+ ...REFUSED_PUT,
3045
+ path: `${RESUMABLE}/messages/send`
3046
+ }),
3047
+ resumableImportMessage: declareRouteInputs({
3048
+ ...REFUSED,
3049
+ path: `${RESUMABLE}/messages/import`
3050
+ }),
3051
+ resumableImportMessagePut: declareRouteInputs({
3052
+ ...REFUSED_PUT,
3053
+ path: `${RESUMABLE}/messages/import`
3054
+ }),
3055
+ // drafts
3056
+ listDrafts: declareRouteInputs({
3057
+ method: "GET",
3058
+ path: DRAFTS,
3059
+ pathParams: USER,
3060
+ query: { ...SEARCH_QUERY, ...LIST_QUERY }
3061
+ }),
3062
+ createDraft: declareRouteInputs({ ...DRAFT_CREATE_SHAPE, path: DRAFTS }),
3063
+ createDraftUpload: declareRouteInputs({ ...DRAFT_CREATE_SHAPE, path: DRAFTS_UPLOAD }),
3064
+ sendDraft: declareRouteInputs({ ...DRAFT_SEND_SHAPE, path: `${DRAFTS}/send` }),
3065
+ sendDraftUpload: declareRouteInputs({ ...DRAFT_SEND_SHAPE, path: `${DRAFTS_UPLOAD}/send` }),
3066
+ getDraft: declareRouteInputs({
3067
+ method: "GET",
3068
+ path: `${DRAFTS}/:id`,
3069
+ pathParams: USER_ITEM,
3070
+ query: { format: MESSAGE_FORMAT_QUERY.format }
3071
+ }),
3072
+ updateDraft: declareRouteInputs({ ...DRAFT_UPDATE_SHAPE, path: `${DRAFTS}/:id` }),
3073
+ updateDraftUpload: declareRouteInputs({
3074
+ ...DRAFT_UPDATE_SHAPE,
3075
+ path: `${DRAFTS_UPLOAD}/:id`
3076
+ }),
3077
+ deleteDraft: declareRouteInputs({
3078
+ method: "DELETE",
3079
+ path: `${DRAFTS}/:id`,
3080
+ pathParams: USER_ITEM
3081
+ }),
3082
+ resumableCreateDraft: declareRouteInputs({ ...REFUSED, path: `${RESUMABLE}/drafts` }),
3083
+ resumableCreateDraftPut: declareRouteInputs({ ...REFUSED_PUT, path: `${RESUMABLE}/drafts` }),
3084
+ resumableSendDraft: declareRouteInputs({ ...REFUSED, path: `${RESUMABLE}/drafts/send` }),
3085
+ resumableSendDraftPut: declareRouteInputs({
3086
+ ...REFUSED_PUT,
3087
+ path: `${RESUMABLE}/drafts/send`
3088
+ }),
3089
+ resumableUpdateDraft: declareRouteInputs({ ...REFUSED_ITEM, path: `${RESUMABLE}/drafts/:id` }),
3090
+ resumableUpdateDraftPut: declareRouteInputs({
3091
+ ...REFUSED_ITEM_PUT,
3092
+ path: `${RESUMABLE}/drafts/:id`
3093
+ }),
3094
+ // profile, threads
3095
+ getProfile: declareRouteInputs({ method: "GET", path: `${USERS}/profile`, pathParams: USER }),
3096
+ listThreads: declareRouteInputs({
3097
+ method: "GET",
3098
+ path: `${USERS}/threads`,
3099
+ pathParams: USER,
3100
+ query: { ...SEARCH_QUERY, ...LIST_QUERY, labelIds: repeatedInput() }
3101
+ }),
3102
+ getThread: declareRouteInputs({
3103
+ method: "GET",
3104
+ path: `${USERS}/threads/:id`,
3105
+ pathParams: USER_ITEM,
3106
+ query: THREAD_FORMAT_QUERY
3107
+ }),
3108
+ modifyThread: declareRouteInputs({
3109
+ method: "POST",
3110
+ path: `${USERS}/threads/:id/modify`,
3111
+ pathParams: USER_ITEM,
3112
+ bodyEncoding: "json",
3113
+ body: LABEL_MODIFY_BODY
3114
+ }),
3115
+ trashThread: declareRouteInputs({
3116
+ method: "POST",
3117
+ path: `${USERS}/threads/:id/trash`,
3118
+ pathParams: USER_ITEM
3119
+ }),
3120
+ untrashThread: declareRouteInputs({
3121
+ method: "POST",
3122
+ path: `${USERS}/threads/:id/untrash`,
3123
+ pathParams: USER_ITEM
3124
+ }),
3125
+ deleteThread: declareRouteInputs({
3126
+ method: "DELETE",
3127
+ path: `${USERS}/threads/:id`,
3128
+ pathParams: USER_ITEM
3129
+ }),
3130
+ // labels
3131
+ listLabels: declareRouteInputs({ method: "GET", path: `${USERS}/labels`, pathParams: USER }),
3132
+ getLabel: declareRouteInputs({
3133
+ method: "GET",
3134
+ path: `${USERS}/labels/:id`,
3135
+ pathParams: USER_ITEM
3136
+ }),
3137
+ createLabel: declareRouteInputs({
3138
+ method: "POST",
3139
+ path: `${USERS}/labels`,
3140
+ pathParams: USER,
3141
+ bodyEncoding: "json",
3142
+ body: { name: z.string().min(1), ...LABEL_BODY }
3143
+ }),
3144
+ updateLabel: declareRouteInputs({
3145
+ method: "PUT",
3146
+ path: `${USERS}/labels/:id`,
3147
+ pathParams: USER_ITEM,
3148
+ bodyEncoding: "json",
3149
+ body: { name: z.string().min(1), ...LABEL_BODY }
3150
+ }),
3151
+ patchLabel: declareRouteInputs({
3152
+ method: "PATCH",
3153
+ path: `${USERS}/labels/:id`,
3154
+ pathParams: USER_ITEM,
3155
+ bodyEncoding: "json",
3156
+ // `stringField(body, "name")` let `""` through to the domain, which answers
3157
+ // "Label name is required"; `.min(1)` here would move that error.
3158
+ body: { name: z.string().optional(), ...LABEL_BODY }
3159
+ }),
3160
+ deleteLabel: declareRouteInputs({
3161
+ method: "DELETE",
3162
+ path: `${USERS}/labels/:id`,
3163
+ pathParams: USER_ITEM
3164
+ }),
3165
+ // history
3166
+ listHistory: declareRouteInputs({
3167
+ method: "GET",
3168
+ path: `${USERS}/history`,
3169
+ pathParams: USER,
3170
+ query: {
3171
+ ...LIST_QUERY,
3172
+ startHistoryId: z.string().min(1),
3173
+ historyTypes: repeatedInput(),
3174
+ labelId: z.string().optional()
3175
+ }
3176
+ }),
3177
+ // settings
3178
+ listFilters: declareRouteInputs({
3179
+ method: "GET",
3180
+ path: `${USERS}/settings/filters`,
3181
+ pathParams: USER
3182
+ }),
3183
+ getFilter: declareRouteInputs({
3184
+ method: "GET",
3185
+ path: `${USERS}/settings/filters/:id`,
3186
+ pathParams: USER_ITEM
3187
+ }),
3188
+ createFilter: declareRouteInputs({
3189
+ method: "POST",
3190
+ path: `${USERS}/settings/filters`,
3191
+ pathParams: USER,
3192
+ bodyEncoding: "json",
3193
+ body: { criteria: jsonObjectInput.optional(), action: jsonObjectInput.optional() }
3194
+ }),
3195
+ deleteFilter: declareRouteInputs({
3196
+ method: "DELETE",
3197
+ path: `${USERS}/settings/filters/:id`,
3198
+ pathParams: USER_ITEM
3199
+ }),
3200
+ listForwardingAddresses: declareRouteInputs({
3201
+ method: "GET",
3202
+ path: `${USERS}/settings/forwardingAddresses`,
3203
+ pathParams: USER
3204
+ }),
3205
+ getForwardingAddress: declareRouteInputs({
3206
+ method: "GET",
3207
+ path: `${USERS}/settings/forwardingAddresses/:forwardingEmail`,
3208
+ pathParams: { ...USER, forwardingEmail: z.string().min(1) }
3209
+ }),
3210
+ listSendAs: declareRouteInputs({
3211
+ method: "GET",
3212
+ path: `${USERS}/settings/sendAs`,
3213
+ pathParams: USER
3214
+ }),
3215
+ getSendAs: declareRouteInputs({
3216
+ method: "GET",
3217
+ path: `${USERS}/settings/sendAs/:sendAsEmail`,
3218
+ pathParams: { ...USER, sendAsEmail: z.string().min(1) }
3219
+ }),
3220
+ // Pub/Sub
3221
+ watch: declareRouteInputs({ ...REFUSED, path: `${USERS}/watch` }),
3222
+ stop: declareRouteInputs({ ...REFUSED, path: `${USERS}/stop` })
3223
+ };
3005
3224
 
3006
3225
  // ../packages/twin-gmail/dist/src/rest-routes-drafts.js
3007
- var BASE = "/gmail/v1/users/:userId/drafts";
3008
- var UPLOAD = "/upload/gmail/v1/users/:userId/drafts";
3226
+ var RESUMABLE2 = "Resumable Gmail uploads are not supported";
3009
3227
  function registerDraftRoutes(app, kit) {
3010
3228
  const { serializers, domain } = kit;
3011
- app.get(BASE, kit.read((c) => {
3012
- const email2 = emailFromContext(c);
3013
- const query = c.req.query("q") ?? "";
3014
- const includeSpamTrash = booleanQuery(c, "includeSpamTrash");
3015
- const drafts2 = asInputError(() => domain.drafts(email2, query, includeSpamTrash));
3016
- const maxResults = numberQuery(c, "maxResults", 100, 500);
3229
+ kit.read(app, GMAIL_ROUTES.listDrafts, ({ path, query }, c) => {
3230
+ const email2 = emailFrom(path.userId, c);
3231
+ const { q, includeSpamTrash } = query;
3232
+ const drafts2 = asInputError(() => domain.drafts(email2, q, includeSpamTrash));
3017
3233
  const snapshot = domain.currentHistoryIdFor(email2);
3018
- const binding = normalizeListBinding("drafts.list", email2, { query, includeSpamTrash });
3234
+ const binding = normalizeListBinding("drafts.list", email2, { query: q, includeSpamTrash });
3019
3235
  const { page, nextPageToken } = paginate2(drafts2, {
3020
- maxResults,
3021
- pageToken: c.req.query("pageToken"),
3236
+ maxResults: query.maxResults,
3237
+ pageToken: query.pageToken,
3022
3238
  binding,
3023
3239
  snapshot
3024
3240
  });
@@ -3034,53 +3250,62 @@ function registerDraftRoutes(app, kit) {
3034
3250
  ...nextPageToken ? { nextPageToken } : {}
3035
3251
  }
3036
3252
  };
3037
- }));
3038
- const create = kit.write(async (c) => {
3039
- const email2 = emailFromContext(c);
3040
- const input = await readMessageWrite(c, true);
3041
- const draft2 = asInputError(() => domain.createDraft(email2, input.raw, { threadId: input.threadId }));
3042
- return { body: serializers.draft(email2, draft2, "full") };
3043
3253
  });
3044
- app.post(BASE, create);
3045
- app.post(UPLOAD, create);
3046
- const send = kit.write(async (c) => {
3047
- const email2 = emailFromContext(c);
3048
- const input = await readDraftSend(c);
3049
- if (input.id) {
3050
- if (input.message) {
3051
- asInputError(() => domain.updateDraft(email2, input.id, input.message.raw, { threadId: input.message.threadId }));
3254
+ const create = ({ path, query, body }, c) => {
3255
+ rejectResumable(query.uploadType);
3256
+ rejectClassificationValues(body.message);
3257
+ const email2 = emailFrom(path.userId, c);
3258
+ const draft2 = asInputError(() => domain.createDraft(email2, body.message.raw, { threadId: body.message.threadId }));
3259
+ return { body: serializers.draft(email2, draft2, "full") };
3260
+ };
3261
+ kit.write(app, GMAIL_ROUTES.createDraft, create);
3262
+ kit.write(app, GMAIL_ROUTES.createDraftUpload, create);
3263
+ const send = ({ path, query, body }, c) => {
3264
+ if (body.message?.raw instanceof Uint8Array)
3265
+ rejectResumable(query.uploadType);
3266
+ if (!body.id && !body.message)
3267
+ invalidArgument("Draft id is required");
3268
+ const email2 = emailFrom(path.userId, c);
3269
+ if (body.id) {
3270
+ if (body.message) {
3271
+ const message2 = body.message;
3272
+ asInputError(() => domain.updateDraft(email2, body.id, message2.raw, { threadId: message2.threadId }));
3052
3273
  }
3053
- const sent2 = asInputError(() => domain.sendDraft(email2, input.id));
3274
+ const sent2 = asInputError(() => domain.sendDraft(email2, body.id));
3054
3275
  return { body: serializers.message(email2, sent2.sender, "full") };
3055
3276
  }
3056
- const sent = asInputError(() => domain.sendMessage(email2, input.message.raw, { threadId: input.message.threadId }));
3277
+ const message = body.message;
3278
+ const sent = asInputError(() => domain.sendMessage(email2, message.raw, { threadId: message.threadId }));
3057
3279
  return { body: serializers.message(email2, sent.sender, "full") };
3280
+ };
3281
+ kit.write(app, GMAIL_ROUTES.sendDraft, send);
3282
+ kit.write(app, GMAIL_ROUTES.sendDraftUpload, send);
3283
+ kit.read(app, GMAIL_ROUTES.getDraft, ({ path, query }, c) => {
3284
+ const email2 = emailFrom(path.userId, c);
3285
+ return { body: serializers.draft(email2, domain.draft(email2, path.id), query.format) };
3058
3286
  });
3059
- app.post(`${BASE}/send`, send);
3060
- app.post(`${UPLOAD}/send`, send);
3061
- app.get(`${BASE}/:id`, kit.read((c) => {
3062
- const email2 = emailFromContext(c);
3063
- return { body: serializers.draft(email2, domain.draft(email2, routeParam(c, "id")), messageFormat(c)) };
3064
- }));
3065
- const update = kit.write(async (c) => {
3066
- const email2 = emailFromContext(c);
3067
- const input = await readMessageWrite(c, true);
3068
- const draft2 = asInputError(() => domain.updateDraft(email2, routeParam(c, "id"), input.raw, { threadId: input.threadId }));
3287
+ const update = ({ path, query, body }, c) => {
3288
+ rejectResumable(query.uploadType);
3289
+ rejectClassificationValues(body.message);
3290
+ const email2 = emailFrom(path.userId, c);
3291
+ const draft2 = asInputError(() => domain.updateDraft(email2, path.id, body.message.raw, { threadId: body.message.threadId }));
3069
3292
  return { body: serializers.draft(email2, draft2, "full") };
3070
- });
3071
- app.put(`${BASE}/:id`, update);
3072
- app.put(`${UPLOAD}/:id`, update);
3073
- app.delete(`${BASE}/:id`, kit.write((c) => {
3074
- domain.deleteDraft(emailFromContext(c), routeParam(c, "id"));
3293
+ };
3294
+ kit.write(app, GMAIL_ROUTES.updateDraft, update);
3295
+ kit.write(app, GMAIL_ROUTES.updateDraftUpload, update);
3296
+ kit.write(app, GMAIL_ROUTES.deleteDraft, ({ path }, c) => {
3297
+ domain.deleteDraft(emailFrom(path.userId, c), path.id);
3075
3298
  return { status: 204, body: null };
3076
- }));
3077
- for (const path of [
3078
- "/resumable/upload/gmail/v1/users/:userId/drafts",
3079
- "/resumable/upload/gmail/v1/users/:userId/drafts/send",
3080
- "/resumable/upload/gmail/v1/users/:userId/drafts/:id"
3299
+ });
3300
+ for (const declaration of [
3301
+ GMAIL_ROUTES.resumableCreateDraft,
3302
+ GMAIL_ROUTES.resumableCreateDraftPut,
3303
+ GMAIL_ROUTES.resumableSendDraft,
3304
+ GMAIL_ROUTES.resumableSendDraftPut,
3305
+ GMAIL_ROUTES.resumableUpdateDraft,
3306
+ GMAIL_ROUTES.resumableUpdateDraftPut
3081
3307
  ]) {
3082
- app.post(path, kit.unsupported("Resumable Gmail uploads are not supported"));
3083
- app.put(path, kit.unsupported("Resumable Gmail uploads are not supported"));
3308
+ kit.unsupported(app, declaration, RESUMABLE2);
3084
3309
  }
3085
3310
  }
3086
3311
 
@@ -3240,16 +3465,17 @@ var GmailRouteKit = class {
3240
3465
  get domain() {
3241
3466
  return this.context.domain;
3242
3467
  }
3243
- read(fn) {
3244
- return this.context.recorder.handle({ mutation: false }, async (c) => {
3245
- const result = await fn(c);
3468
+ read(app, declaration, fn) {
3469
+ mountDeclaredRoute(app, declaration, this.context.recorder.handle({ mutation: false }, async (c) => {
3470
+ const result = await fn(await parseDeclared(declaration, c), c);
3246
3471
  return { status: result.status ?? 200, body: result.body, mutation: false };
3247
- });
3472
+ }));
3248
3473
  }
3249
- write(fn) {
3250
- return this.context.recorder.handle({ mutation: true }, async (c) => {
3474
+ write(app, declaration, fn) {
3475
+ mountDeclaredRoute(app, declaration, this.context.recorder.handle({ mutation: true }, async (c) => {
3476
+ const input = await parseDeclared(declaration, c);
3251
3477
  const before = this.context.domain.exportState();
3252
- const result = await fn(c);
3478
+ const result = await fn(input, c);
3253
3479
  const wantsMutation = result.mutation ?? true;
3254
3480
  const delta = wantsMutation ? gmailStateDelta(before, this.context.domain.exportState()) : null;
3255
3481
  const mutation = wantsMutation && delta !== null;
@@ -3259,10 +3485,19 @@ var GmailRouteKit = class {
3259
3485
  mutation,
3260
3486
  delta
3261
3487
  };
3262
- });
3488
+ }));
3263
3489
  }
3264
- unsupported(message) {
3265
- return this.context.recorder.handle({ mutation: false, fidelity: "unsupported" }, () => ({
3490
+ /**
3491
+ * A route the twin serves only to answer 501.
3492
+ *
3493
+ * It does NOT parse its declaration: "this operation is not implemented" is
3494
+ * the honest answer to a well-formed resumable-upload or Pub/Sub request, and
3495
+ * parsing first would turn a real `users.watch` body into a 400 about an
3496
+ * undeclared parameter. The declarations exist so the surface is published
3497
+ * and so the mount point still has exactly one home.
3498
+ */
3499
+ unsupported(app, declaration, message) {
3500
+ mountDeclaredRoute(app, declaration, this.context.recorder.handle({ mutation: false, fidelity: "unsupported" }, () => ({
3266
3501
  status: 501,
3267
3502
  body: {
3268
3503
  error: {
@@ -3273,32 +3508,40 @@ var GmailRouteKit = class {
3273
3508
  }
3274
3509
  },
3275
3510
  mutation: false
3276
- }));
3511
+ })));
3277
3512
  }
3278
3513
  };
3514
+ async function parseDeclared(declaration, c) {
3515
+ try {
3516
+ return await declaration.parse(c.req);
3517
+ } catch (error) {
3518
+ if (error instanceof UndeclaredInputError) {
3519
+ invalidArgument(`Invalid ${error.location} parameter: ${error.first}`);
3520
+ }
3521
+ if (error instanceof MalformedBodyError)
3522
+ invalidArgument("Invalid JSON payload received.");
3523
+ throw error;
3524
+ }
3525
+ }
3279
3526
 
3280
3527
  // ../packages/twin-gmail/dist/src/rest-routes-messages.js
3281
- var BASE2 = "/gmail/v1/users/:userId/messages";
3282
- var UPLOAD2 = "/upload/gmail/v1/users/:userId/messages";
3528
+ var RESUMABLE3 = "Resumable Gmail uploads are not supported";
3283
3529
  function registerMessageRoutes(app, kit) {
3284
3530
  const { serializers, domain } = kit;
3285
- app.get(BASE2, kit.read((c) => {
3286
- const email2 = emailFromContext(c);
3287
- const query = c.req.query("q") ?? "";
3288
- const includeSpamTrash = booleanQuery(c, "includeSpamTrash");
3289
- const labelIds2 = repeatedQuery(c, "labelIds");
3290
- let messages = asInputError(() => domain.searchMessages(email2, query, { includeTrash: includeSpamTrash }));
3291
- if (!/\bin:draft\b/i.test(query))
3531
+ kit.read(app, GMAIL_ROUTES.listMessages, ({ path, query }, c) => {
3532
+ const email2 = emailFrom(path.userId, c);
3533
+ const { q, includeSpamTrash, labelIds: labelIds2 } = query;
3534
+ let messages = asInputError(() => domain.searchMessages(email2, q, { includeTrash: includeSpamTrash }));
3535
+ if (!/\bin:draft\b/i.test(q))
3292
3536
  messages = messages.filter((message) => !message.labelIds.includes("DRAFT"));
3293
3537
  if (labelIds2.length) {
3294
3538
  messages = messages.filter((message) => labelIds2.every((labelId) => message.labelIds.includes(labelId)));
3295
3539
  }
3296
- const maxResults = numberQuery(c, "maxResults", 100, 500);
3297
3540
  const snapshot = domain.currentHistoryIdFor(email2);
3298
- const binding = normalizeListBinding("messages.list", email2, { query, includeSpamTrash, labelIds: labelIds2 });
3541
+ const binding = normalizeListBinding("messages.list", email2, { query: q, includeSpamTrash, labelIds: labelIds2 });
3299
3542
  const { page, nextPageToken } = paginate2(messages, {
3300
- maxResults,
3301
- pageToken: c.req.query("pageToken"),
3543
+ maxResults: query.maxResults,
3544
+ pageToken: query.pageToken,
3302
3545
  binding,
3303
3546
  snapshot
3304
3547
  });
@@ -3309,135 +3552,118 @@ function registerMessageRoutes(app, kit) {
3309
3552
  ...nextPageToken ? { nextPageToken } : {}
3310
3553
  }
3311
3554
  };
3312
- }));
3313
- app.post(`${BASE2}/batchModify`, kit.write(async (c) => {
3314
- const email2 = emailFromContext(c);
3315
- const body = await readJsonObject(c);
3555
+ });
3556
+ kit.write(app, GMAIL_ROUTES.batchModifyMessages, ({ path, body }, c) => {
3557
+ const email2 = emailFrom(path.userId, c);
3316
3558
  rejectClassification(body);
3317
- const ids = stringArray(body, "ids", 1e3);
3318
- if (!ids.length)
3559
+ if (!body.ids.length)
3319
3560
  invalidArgument("ids is required");
3320
- const add = stringArray(body, "addLabelIds");
3321
- const remove = stringArray(body, "removeLabelIds");
3322
3561
  domain.db.transaction(() => {
3323
- for (const id of ids)
3324
- domain.modifyMessageLabels(email2, id, add, remove);
3562
+ for (const id of body.ids)
3563
+ domain.modifyMessageLabels(email2, id, body.addLabelIds, body.removeLabelIds);
3325
3564
  }).immediate();
3326
3565
  return { body: {} };
3327
- }));
3328
- app.post(`${BASE2}/batchDelete`, kit.write(async (c) => {
3329
- const email2 = emailFromContext(c);
3330
- const body = await readJsonObject(c);
3331
- const ids = stringArray(body, "ids", 1e3);
3332
- if (!ids.length)
3566
+ });
3567
+ kit.write(app, GMAIL_ROUTES.batchDeleteMessages, ({ path, body }, c) => {
3568
+ const email2 = emailFrom(path.userId, c);
3569
+ if (!body.ids.length)
3333
3570
  invalidArgument("ids is required");
3334
- domain.batchDeleteMessages(email2, ids);
3571
+ domain.batchDeleteMessages(email2, body.ids);
3335
3572
  return { status: 204, body: null };
3336
- }));
3337
- const send = kit.write(async (c) => {
3338
- const email2 = emailFromContext(c);
3339
- const input = await readMessageWrite(c);
3340
- const result = asInputError(() => domain.sendMessage(email2, input.raw, { threadId: input.threadId }));
3341
- return { body: serializers.message(email2, result.sender, "full") };
3342
3573
  });
3343
- app.post(`${BASE2}/send`, send);
3344
- app.post(`${UPLOAD2}/send`, send);
3345
- const importMessage = kit.write(async (c) => {
3346
- const email2 = emailFromContext(c);
3347
- rejectUnsupportedQuery(c, ["deleted", "processForCalendar"]);
3348
- const source = internalDateSource(c, "dateHeader");
3349
- booleanQuery(c, "neverMarkSpam");
3350
- const input = await readMessageWrite(c);
3351
- const inserted = asInputError(() => domain.insertMessage(email2, input.raw, {
3352
- threadId: input.threadId,
3353
- labels: input.labelIds,
3574
+ const send = ({ path, query, body }, c) => {
3575
+ rejectResumable(query.uploadType);
3576
+ rejectClassificationValues(body);
3577
+ const email2 = emailFrom(path.userId, c);
3578
+ const result = asInputError(() => domain.sendMessage(email2, body.raw, { threadId: body.threadId }));
3579
+ return { body: serializers.message(email2, result.sender, "full") };
3580
+ };
3581
+ kit.write(app, GMAIL_ROUTES.sendMessage, send);
3582
+ kit.write(app, GMAIL_ROUTES.sendMessageUpload, send);
3583
+ const importMessage = ({ path, query, body }, c) => {
3584
+ rejectResumable(query.uploadType);
3585
+ rejectUnsupportedFlags({ deleted: query.deleted, processForCalendar: query.processForCalendar });
3586
+ rejectClassificationValues(body);
3587
+ const email2 = emailFrom(path.userId, c);
3588
+ const inserted = asInputError(() => domain.insertMessage(email2, body.raw, {
3589
+ threadId: body.threadId,
3590
+ labels: body.labelIds,
3354
3591
  incoming: true
3355
3592
  }));
3356
- const message = domain.applyInternalDateSource(email2, inserted.id, source);
3593
+ const message = domain.applyInternalDateSource(email2, inserted.id, query.internalDateSource);
3357
3594
  return { body: serializers.message(email2, message, "full") };
3358
- });
3359
- app.post(`${BASE2}/import`, importMessage);
3360
- app.post(`${UPLOAD2}/import`, importMessage);
3361
- const insert = kit.write(async (c) => {
3362
- const email2 = emailFromContext(c);
3363
- rejectUnsupportedQuery(c, ["deleted"]);
3364
- const source = internalDateSource(c, "receivedTime");
3365
- const input = await readMessageWrite(c);
3366
- const inserted = asInputError(() => domain.insertMessage(email2, input.raw, {
3367
- threadId: input.threadId,
3368
- labels: input.labelIds
3369
- }));
3370
- const message = domain.applyInternalDateSource(email2, inserted.id, source);
3595
+ };
3596
+ kit.write(app, GMAIL_ROUTES.importMessage, importMessage);
3597
+ kit.write(app, GMAIL_ROUTES.importMessageUpload, importMessage);
3598
+ const insert = ({ path, query, body }, c) => {
3599
+ rejectResumable(query.uploadType);
3600
+ rejectUnsupportedFlags({ deleted: query.deleted });
3601
+ rejectClassificationValues(body);
3602
+ const email2 = emailFrom(path.userId, c);
3603
+ const inserted = asInputError(() => domain.insertMessage(email2, body.raw, { threadId: body.threadId, labels: body.labelIds }));
3604
+ const message = domain.applyInternalDateSource(email2, inserted.id, query.internalDateSource);
3371
3605
  return { body: serializers.message(email2, message, "full") };
3606
+ };
3607
+ kit.write(app, GMAIL_ROUTES.insertMessage, insert);
3608
+ kit.write(app, GMAIL_ROUTES.insertMessageUpload, insert);
3609
+ kit.read(app, GMAIL_ROUTES.getMessage, ({ path, query }, c) => {
3610
+ const email2 = emailFrom(path.userId, c);
3611
+ const message = domain.getMessage(email2, path.id);
3612
+ return { body: serializers.message(email2, message, query.format, query.metadataHeaders) };
3372
3613
  });
3373
- app.post(BASE2, insert);
3374
- app.post(UPLOAD2, insert);
3375
- app.get(`${BASE2}/:id`, kit.read((c) => {
3376
- const email2 = emailFromContext(c);
3377
- const format = messageFormat(c);
3378
- const message = domain.getMessage(email2, routeParam(c, "id"));
3379
- return { body: serializers.message(email2, message, format, repeatedQuery(c, "metadataHeaders")) };
3380
- }));
3381
- app.post(`${BASE2}/:id/modify`, kit.write(async (c) => {
3382
- const email2 = emailFromContext(c);
3383
- const body = await readJsonObject(c);
3614
+ kit.write(app, GMAIL_ROUTES.modifyMessage, ({ path, body }, c) => {
3615
+ const email2 = emailFrom(path.userId, c);
3384
3616
  rejectClassification(body);
3385
- const message = domain.modifyMessageLabels(email2, routeParam(c, "id"), stringArray(body, "addLabelIds"), stringArray(body, "removeLabelIds"));
3617
+ const message = domain.modifyMessageLabels(email2, path.id, body.addLabelIds, body.removeLabelIds);
3386
3618
  return { body: serializers.message(email2, message, "minimal") };
3387
- }));
3388
- app.post(`${BASE2}/:id/trash`, kit.write((c) => {
3389
- const email2 = emailFromContext(c);
3390
- const message = domain.modifyMessageLabels(email2, routeParam(c, "id"), ["TRASH"], ["INBOX"]);
3619
+ });
3620
+ kit.write(app, GMAIL_ROUTES.trashMessage, ({ path }, c) => {
3621
+ const email2 = emailFrom(path.userId, c);
3622
+ const message = domain.modifyMessageLabels(email2, path.id, ["TRASH"], ["INBOX"]);
3391
3623
  return { body: serializers.message(email2, message, "minimal") };
3392
- }));
3393
- app.post(`${BASE2}/:id/untrash`, kit.write((c) => {
3394
- const email2 = emailFromContext(c);
3395
- const message = domain.modifyMessageLabels(email2, routeParam(c, "id"), [], ["TRASH"]);
3624
+ });
3625
+ kit.write(app, GMAIL_ROUTES.untrashMessage, ({ path }, c) => {
3626
+ const email2 = emailFrom(path.userId, c);
3627
+ const message = domain.modifyMessageLabels(email2, path.id, [], ["TRASH"]);
3396
3628
  return { body: serializers.message(email2, message, "minimal") };
3397
- }));
3398
- app.delete(`${BASE2}/:id`, kit.write((c) => {
3399
- domain.deleteMessage(emailFromContext(c), routeParam(c, "id"));
3629
+ });
3630
+ kit.write(app, GMAIL_ROUTES.deleteMessage, ({ path }, c) => {
3631
+ domain.deleteMessage(emailFrom(path.userId, c), path.id);
3400
3632
  return { status: 204, body: null };
3633
+ });
3634
+ kit.read(app, GMAIL_ROUTES.getAttachment, ({ path }, c) => ({
3635
+ body: domain.attachment(emailFrom(path.userId, c), path.messageId, path.id)
3401
3636
  }));
3402
- app.get(`${BASE2}/:messageId/attachments/:id`, kit.read((c) => ({
3403
- body: domain.attachment(emailFromContext(c), routeParam(c, "messageId"), routeParam(c, "id"))
3404
- })));
3405
- for (const path of [
3406
- "/resumable/upload/gmail/v1/users/:userId/messages",
3407
- "/resumable/upload/gmail/v1/users/:userId/messages/send",
3408
- "/resumable/upload/gmail/v1/users/:userId/messages/import"
3637
+ for (const declaration of [
3638
+ GMAIL_ROUTES.resumableInsertMessage,
3639
+ GMAIL_ROUTES.resumableInsertMessagePut,
3640
+ GMAIL_ROUTES.resumableSendMessage,
3641
+ GMAIL_ROUTES.resumableSendMessagePut,
3642
+ GMAIL_ROUTES.resumableImportMessage,
3643
+ GMAIL_ROUTES.resumableImportMessagePut
3409
3644
  ]) {
3410
- app.post(path, kit.unsupported("Resumable Gmail uploads are not supported"));
3411
- app.put(path, kit.unsupported("Resumable Gmail uploads are not supported"));
3645
+ kit.unsupported(app, declaration, RESUMABLE3);
3412
3646
  }
3413
3647
  }
3414
- function internalDateSource(c, fallback) {
3415
- const value = c.req.query("internalDateSource") ?? fallback;
3416
- if (value !== "receivedTime" && value !== "dateHeader")
3417
- invalidArgument("Invalid internalDateSource");
3418
- return value;
3419
- }
3420
3648
 
3421
3649
  // ../packages/twin-gmail/dist/src/rest-routes-resources.js
3422
- var USERS = "/gmail/v1/users/:userId";
3423
3650
  function registerResourceRoutes(app, kit) {
3424
3651
  const { serializers, domain } = kit;
3425
- app.get(`${USERS}/profile`, kit.read((c) => ({ body: domain.profile(emailFromContext(c)) })));
3426
- app.get(`${USERS}/threads`, kit.read((c) => {
3427
- const email2 = emailFromContext(c);
3428
- const query = c.req.query("q") ?? "";
3429
- const includeSpamTrash = booleanQuery(c, "includeSpamTrash");
3430
- const labelIds2 = repeatedQuery(c, "labelIds");
3431
- let threads = asInputError(() => domain.searchThreads(email2, query, { includeTrash: includeSpamTrash }));
3652
+ kit.read(app, GMAIL_ROUTES.getProfile, ({ path }, c) => ({
3653
+ body: domain.profile(emailFrom(path.userId, c))
3654
+ }));
3655
+ kit.read(app, GMAIL_ROUTES.listThreads, ({ path, query }, c) => {
3656
+ const email2 = emailFrom(path.userId, c);
3657
+ const { q, includeSpamTrash, labelIds: labelIds2 } = query;
3658
+ let threads = asInputError(() => domain.searchThreads(email2, q, { includeTrash: includeSpamTrash }));
3432
3659
  if (labelIds2.length) {
3433
3660
  threads = threads.filter((thread) => labelIds2.every((label2) => thread.labelIds.includes(label2)));
3434
3661
  }
3435
- const maxResults = numberQuery(c, "maxResults", 100, 500);
3436
3662
  const snapshot = domain.currentHistoryIdFor(email2);
3437
- const binding = normalizeListBinding("threads.list", email2, { query, includeSpamTrash, labelIds: labelIds2 });
3663
+ const binding = normalizeListBinding("threads.list", email2, { query: q, includeSpamTrash, labelIds: labelIds2 });
3438
3664
  const { page, nextPageToken } = paginate2(threads, {
3439
- maxResults,
3440
- pageToken: c.req.query("pageToken"),
3665
+ maxResults: query.maxResults,
3666
+ pageToken: query.pageToken,
3441
3667
  binding,
3442
3668
  snapshot
3443
3669
  });
@@ -3454,85 +3680,79 @@ function registerResourceRoutes(app, kit) {
3454
3680
  ...nextPageToken ? { nextPageToken } : {}
3455
3681
  }
3456
3682
  };
3457
- }));
3458
- app.get(`${USERS}/threads/:id`, kit.read((c) => {
3459
- const email2 = emailFromContext(c);
3460
- const format = messageFormat(c, false);
3683
+ });
3684
+ kit.read(app, GMAIL_ROUTES.getThread, ({ path, query }, c) => {
3685
+ const email2 = emailFrom(path.userId, c);
3461
3686
  return {
3462
- body: serializers.thread(email2, domain.getThread(email2, routeParam(c, "id")), format, repeatedQuery(c, "metadataHeaders"))
3687
+ body: serializers.thread(email2, domain.getThread(email2, path.id), query.format, query.metadataHeaders)
3463
3688
  };
3464
- }));
3465
- app.post(`${USERS}/threads/:id/modify`, kit.write(async (c) => {
3466
- const email2 = emailFromContext(c);
3467
- const body = await readJsonObject(c);
3468
- const thread = domain.modifyThreadLabels(email2, routeParam(c, "id"), stringArray(body, "addLabelIds"), stringArray(body, "removeLabelIds"));
3689
+ });
3690
+ kit.write(app, GMAIL_ROUTES.modifyThread, ({ path, body }, c) => {
3691
+ const email2 = emailFrom(path.userId, c);
3692
+ const thread = domain.modifyThreadLabels(email2, path.id, body.addLabelIds, body.removeLabelIds);
3469
3693
  return { body: serializers.thread(email2, thread, "minimal") };
3470
- }));
3471
- app.post(`${USERS}/threads/:id/trash`, kit.write((c) => {
3472
- const email2 = emailFromContext(c);
3694
+ });
3695
+ kit.write(app, GMAIL_ROUTES.trashThread, ({ path }, c) => {
3696
+ const email2 = emailFrom(path.userId, c);
3473
3697
  return {
3474
- body: serializers.thread(email2, domain.modifyThreadLabels(email2, routeParam(c, "id"), ["TRASH"], ["INBOX"]), "minimal")
3698
+ body: serializers.thread(email2, domain.modifyThreadLabels(email2, path.id, ["TRASH"], ["INBOX"]), "minimal")
3475
3699
  };
3476
- }));
3477
- app.post(`${USERS}/threads/:id/untrash`, kit.write((c) => {
3478
- const email2 = emailFromContext(c);
3700
+ });
3701
+ kit.write(app, GMAIL_ROUTES.untrashThread, ({ path }, c) => {
3702
+ const email2 = emailFrom(path.userId, c);
3479
3703
  return {
3480
- body: serializers.thread(email2, domain.modifyThreadLabels(email2, routeParam(c, "id"), [], ["TRASH"]), "minimal")
3704
+ body: serializers.thread(email2, domain.modifyThreadLabels(email2, path.id, [], ["TRASH"]), "minimal")
3481
3705
  };
3482
- }));
3483
- app.delete(`${USERS}/threads/:id`, kit.write((c) => {
3484
- domain.deleteThread(emailFromContext(c), routeParam(c, "id"));
3706
+ });
3707
+ kit.write(app, GMAIL_ROUTES.deleteThread, ({ path }, c) => {
3708
+ domain.deleteThread(emailFrom(path.userId, c), path.id);
3485
3709
  return { status: 204, body: null };
3710
+ });
3711
+ kit.read(app, GMAIL_ROUTES.listLabels, ({ path }, c) => ({
3712
+ body: { labels: domain.labels(emailFrom(path.userId, c)).map(labelSummary) }
3713
+ }));
3714
+ kit.read(app, GMAIL_ROUTES.getLabel, ({ path }, c) => ({
3715
+ body: labelDetail(domain.label(emailFrom(path.userId, c), path.id))
3486
3716
  }));
3487
- app.get(`${USERS}/labels`, kit.read((c) => ({ body: { labels: domain.labels(emailFromContext(c)).map(labelSummary) } })));
3488
- app.get(`${USERS}/labels/:id`, kit.read((c) => ({ body: labelDetail(domain.label(emailFromContext(c), routeParam(c, "id"))) })));
3489
- app.post(`${USERS}/labels`, kit.write(async (c) => {
3490
- const email2 = emailFromContext(c);
3491
- const body = await readJsonObject(c);
3717
+ kit.write(app, GMAIL_ROUTES.createLabel, ({ path, body }, c) => {
3718
+ const email2 = emailFrom(path.userId, c);
3492
3719
  if (body.type !== void 0 && body.type !== "user")
3493
3720
  invalidArgument("Only user labels can be created");
3494
- const created = domain.createLabel(email2, stringField(body, "name", true), colorInput(body));
3721
+ const created = domain.createLabel(email2, body.name, body.color);
3495
3722
  return { body: labelDetail(domain.label(email2, created.id)) };
3496
- }));
3497
- app.put(`${USERS}/labels/:id`, kit.write(async (c) => {
3498
- const body = await readJsonObject(c);
3499
- const label2 = domain.updateLabel(emailFromContext(c), routeParam(c, "id"), { name: stringField(body, "name", true), color: colorInput(body) }, true);
3723
+ });
3724
+ kit.write(app, GMAIL_ROUTES.updateLabel, ({ path, body }, c) => {
3725
+ const label2 = domain.updateLabel(emailFrom(path.userId, c), path.id, { name: body.name, color: body.color }, true);
3500
3726
  return { body: labelDetail(label2) };
3501
- }));
3502
- app.patch(`${USERS}/labels/:id`, kit.write(async (c) => {
3503
- const body = await readJsonObject(c);
3504
- const label2 = domain.updateLabel(emailFromContext(c), routeParam(c, "id"), { name: stringField(body, "name"), color: colorInput(body) }, false);
3727
+ });
3728
+ kit.write(app, GMAIL_ROUTES.patchLabel, ({ path, body }, c) => {
3729
+ const label2 = domain.updateLabel(emailFrom(path.userId, c), path.id, { name: body.name, color: body.color }, false);
3505
3730
  return { body: labelDetail(label2) };
3506
- }));
3507
- app.delete(`${USERS}/labels/:id`, kit.write((c) => {
3508
- domain.deleteLabel(emailFromContext(c), routeParam(c, "id"));
3731
+ });
3732
+ kit.write(app, GMAIL_ROUTES.deleteLabel, ({ path }, c) => {
3733
+ domain.deleteLabel(emailFrom(path.userId, c), path.id);
3509
3734
  return { status: 204, body: null };
3510
- }));
3735
+ });
3511
3736
  registerHistory(app, kit);
3512
3737
  registerSettings(app, kit);
3513
- app.post(`${USERS}/watch`, kit.unsupported("users.watch requires Pub/Sub and is not supported"));
3514
- app.post(`${USERS}/stop`, kit.unsupported("users.stop requires Pub/Sub and is not supported"));
3738
+ kit.unsupported(app, GMAIL_ROUTES.watch, "users.watch requires Pub/Sub and is not supported");
3739
+ kit.unsupported(app, GMAIL_ROUTES.stop, "users.stop requires Pub/Sub and is not supported");
3515
3740
  }
3516
3741
  function registerHistory(app, kit) {
3517
- app.get(`${USERS}/history`, kit.read((c) => {
3518
- const email2 = emailFromContext(c);
3519
- const startHistoryId = c.req.query("startHistoryId");
3520
- if (!startHistoryId)
3521
- invalidArgument("startHistoryId is required");
3522
- const historyTypes = repeatedQuery(c, "historyTypes");
3742
+ kit.read(app, GMAIL_ROUTES.listHistory, ({ path, query }, c) => {
3743
+ const email2 = emailFrom(path.userId, c);
3744
+ const { startHistoryId, historyTypes, labelId } = query;
3523
3745
  const allowed = /* @__PURE__ */ new Set(["messageAdded", "messageDeleted", "labelAdded", "labelRemoved"]);
3524
3746
  if (historyTypes.some((type) => !allowed.has(type)))
3525
3747
  invalidArgument("Invalid historyTypes");
3526
3748
  const result = kit.context.domain.listHistory(email2, startHistoryId, {
3527
3749
  types: historyTypes.length ? historyTypes : void 0
3528
3750
  });
3529
- const labelId = c.req.query("labelId");
3530
3751
  const resources = result.history.filter((event) => !labelId || event.labelIds.includes(labelId)).map(historyResource).filter((item) => item !== null);
3531
- const maxResults = numberQuery(c, "maxResults", 100, 500);
3532
3752
  const binding = normalizeListBinding("history.list", email2, { startHistoryId, historyTypes, labelId });
3533
3753
  const { page, nextPageToken } = paginate2(resources, {
3534
- maxResults,
3535
- pageToken: c.req.query("pageToken"),
3754
+ maxResults: query.maxResults,
3755
+ pageToken: query.pageToken,
3536
3756
  binding,
3537
3757
  snapshot: result.historyId
3538
3758
  });
@@ -3543,39 +3763,35 @@ function registerHistory(app, kit) {
3543
3763
  ...nextPageToken ? { nextPageToken } : {}
3544
3764
  }
3545
3765
  };
3546
- }));
3766
+ });
3547
3767
  }
3548
3768
  function registerSettings(app, kit) {
3549
3769
  const { domain } = kit;
3550
- app.get(`${USERS}/settings/filters`, kit.read((c) => ({ body: { filter: domain.filters(emailFromContext(c)) } })));
3551
- app.get(`${USERS}/settings/filters/:id`, kit.read((c) => ({ body: domain.filter(emailFromContext(c), routeParam(c, "id")) })));
3552
- app.post(`${USERS}/settings/filters`, kit.write(async (c) => {
3553
- const body = await readJsonObject(c);
3554
- return {
3555
- body: asInputError(() => domain.createFilter(emailFromContext(c), filterCriteria(objectField(body, "criteria") ?? {}), filterAction(objectField(body, "action") ?? {})))
3556
- };
3770
+ kit.read(app, GMAIL_ROUTES.listFilters, ({ path }, c) => ({
3771
+ body: { filter: domain.filters(emailFrom(path.userId, c)) }
3557
3772
  }));
3558
- app.delete(`${USERS}/settings/filters/:id`, kit.write((c) => {
3559
- domain.deleteFilter(emailFromContext(c), routeParam(c, "id"));
3773
+ kit.read(app, GMAIL_ROUTES.getFilter, ({ path }, c) => ({
3774
+ body: domain.filter(emailFrom(path.userId, c), path.id)
3775
+ }));
3776
+ kit.write(app, GMAIL_ROUTES.createFilter, ({ path, body }, c) => ({
3777
+ body: asInputError(() => domain.createFilter(emailFrom(path.userId, c), filterCriteria(body.criteria ?? {}), filterAction(body.action ?? {})))
3778
+ }));
3779
+ kit.write(app, GMAIL_ROUTES.deleteFilter, ({ path }, c) => {
3780
+ domain.deleteFilter(emailFrom(path.userId, c), path.id);
3560
3781
  return { status: 204, body: null };
3782
+ });
3783
+ kit.read(app, GMAIL_ROUTES.listForwardingAddresses, ({ path }, c) => ({
3784
+ body: { forwardingAddresses: domain.forwardingAddresses(emailFrom(path.userId, c)) }
3785
+ }));
3786
+ kit.read(app, GMAIL_ROUTES.getForwardingAddress, ({ path }, c) => ({
3787
+ body: domain.forwardingAddress(emailFrom(path.userId, c), decodeURIComponent(path.forwardingEmail))
3788
+ }));
3789
+ kit.read(app, GMAIL_ROUTES.listSendAs, ({ path }, c) => ({
3790
+ body: { sendAs: domain.sendAs(emailFrom(path.userId, c)) }
3791
+ }));
3792
+ kit.read(app, GMAIL_ROUTES.getSendAs, ({ path }, c) => ({
3793
+ body: domain.sendAsAddress(emailFrom(path.userId, c), decodeURIComponent(path.sendAsEmail))
3561
3794
  }));
3562
- app.get(`${USERS}/settings/forwardingAddresses`, kit.read((c) => ({ body: { forwardingAddresses: domain.forwardingAddresses(emailFromContext(c)) } })));
3563
- app.get(`${USERS}/settings/forwardingAddresses/:forwardingEmail`, kit.read((c) => ({
3564
- body: domain.forwardingAddress(emailFromContext(c), decodeURIComponent(routeParam(c, "forwardingEmail")))
3565
- })));
3566
- app.get(`${USERS}/settings/sendAs`, kit.read((c) => ({ body: { sendAs: domain.sendAs(emailFromContext(c)) } })));
3567
- app.get(`${USERS}/settings/sendAs/:sendAsEmail`, kit.read((c) => ({
3568
- body: domain.sendAsAddress(emailFromContext(c), decodeURIComponent(routeParam(c, "sendAsEmail")))
3569
- })));
3570
- }
3571
- function colorInput(body) {
3572
- const color = objectField(body, "color");
3573
- if (!color)
3574
- return void 0;
3575
- return {
3576
- textColor: stringField(color, "textColor"),
3577
- backgroundColor: stringField(color, "backgroundColor")
3578
- };
3579
3795
  }
3580
3796
  function filterCriteria(body) {
3581
3797
  const criteria = {};