@particle-academy/gmail-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,68 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@particle-academy/gmail-ui`, `@particle-academy/gmail-js`,
4
+ `particle-academy/gmail-php` and `fancy-gmail`.
5
+
6
+ The four packages share one version, because they are generated from one
7
+ `provider/` definition and a version that meant something different in each
8
+ would be a version nobody could reason about.
9
+
10
+ ## [0.1.0] — 2026-08-23
11
+
12
+ First release. Provider nine, and the second Google connector.
13
+
14
+ ### Added
15
+
16
+ - `message_send` — send an email from a connected account.
17
+ `POST /gmail/v1/users/{userId}/messages/send`.
18
+ - A faker for it, so the node runs on a canvas without sending mail.
19
+
20
+ ### Gmail does not take `to`, `subject` and `body`
21
+
22
+ It takes **one opaque string**. Quoting Google's own discovery document, the
23
+ `Message.raw` field is *"the entire email message in an RFC 2822 formatted and
24
+ base64url encoded string"*.
25
+
26
+ Nobody hand-writes RFC 2822, so a connector exposing `raw` directly would be
27
+ useless. The generated code composes the message from the fields you fill —
28
+ which is the first time the vocabulary has built **one value out of several**.
29
+
30
+ **base64url is not base64.** `+` and `/` become `-` and `_`, and the `=` padding
31
+ is stripped. A runtime reaching for its standard base64 function produces a
32
+ string Gmail rejects — or, when the message's bytes happen to avoid the two
33
+ substituted characters, one it *accepts*, right up until an address contains the
34
+ byte that differs. The parity suite proves all three runtimes emit the same
35
+ bytes for the same message.
36
+
37
+ ### A header cannot inject another header
38
+
39
+ A subject carrying a newline would otherwise add headers of the sender's
40
+ choosing — `Bcc:` being the interesting one — from a field a web form fills. The
41
+ composer **refuses** such a value rather than sanitising it, because silently
42
+ dropping half of somebody's subject line is its own surprise.
43
+
44
+ An empty optional header is omitted entirely, rather than sent as `Cc:` with
45
+ nothing after it.
46
+
47
+ ### The scope is `gmail.send`, and that was a real choice
48
+
49
+ The discovery document lists `https://mail.google.com/` **first** for this
50
+ method — full read, write and delete across the entire mailbox. Taking the first
51
+ entry would let an automation delete somebody's mail in order to send one
52
+ message. `gmail.send` cannot read a single message.
53
+
54
+ ### No sandbox, and no idempotency
55
+
56
+ `sandbox` is `none` — **checked**. There is one API and a test mailbox is a real
57
+ mailbox: anything this sends is delivered, to a real person.
58
+
59
+ `messages.send` takes no idempotency key. So it is `unsafe-to-replay` with no
60
+ way to make it safe, and a retried durable run sends a **second email** — the
61
+ least recallable side effect in this estate so far.
62
+
63
+ ### Access tokens last one hour
64
+
65
+ Google's do. A connection that is never refreshed stops working within the day,
66
+ which is why `refreshToken` is required rather than optional.
67
+
68
+ [0.1.0]: https://github.com/Fancy-Friends/gmail/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Particle Academy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Gmail
2
+
3
+ Gmail for [fancy-flow][flow] — as **four imported, versioned packages**, one
4
+ per runtime. Not vendored source: a copy cannot be upgraded, and third-party APIs
5
+ change.
6
+
7
+ [flow]: https://github.com/Particle-Academy/fancy-flow
8
+
9
+ | Runtime | Package | Install |
10
+ |---|---|---|
11
+ | Authoring surface (every host) | `@particle-academy/gmail-ui` | `npm install @particle-academy/gmail-ui` |
12
+ | Node | `@particle-academy/gmail-js` | `npm install @particle-academy/gmail-js` |
13
+ | PHP 8.4+ | `particle-academy/gmail-php` | `composer require particle-academy/gmail-php` |
14
+ | Python 3.11+ | `fancy-gmail` | `pip install fancy-gmail` |
15
+
16
+ The `ui` package is the editor surface and is React on every host — a PHP or
17
+ Python project installs it *and* its own runtime package, and never the `js` one.
18
+
19
+ ## What it costs you
20
+
21
+ One dependency: `@particle-academy/fancy-connector-core` (or
22
+ `particle-academy/fancy-connector-core` on Composer), which the `js` and `php`
23
+ packages pull in themselves. The Python package has **zero** runtime
24
+ dependencies.
25
+
26
+ **No Gmail SDK.** Plain HTTP, deliberately: a vendor SDK is third-party code
27
+ subject to the kit's full approval bar, and one per provider is hundreds of
28
+ dependencies nobody is tracking.
29
+
30
+ ## Run it before you have credentials
31
+
32
+ Every operation ships a **faker**, whether or not Gmail has a sandbox. Set a
33
+ node's mode to `fake` and it returns the shape Gmail actually publishes — the
34
+ same field names, deterministically — so you can wire the downstream nodes before
35
+ touching an account, a key, or a network.
36
+
37
+ ## This repository is generated
38
+
39
+ `provider/` is the source. Everything under `packages/` is emitted from it and
40
+ **must not be hand-edited** — CI regenerates and diffs on every push, and the
41
+ next protocol sync destroys anything it finds. See [`AGENTS.md`](AGENTS.md).
42
+
43
+ ## Two namespaces, which do not match on purpose
44
+
45
+ The repo is `github.com/Fancy-Friends/gmail`; the packages publish under
46
+ `particle-academy`. Nothing derives one from the other — the names come from
47
+ weaver's `friends.json` and nowhere else.
48
+
49
+ ## Licence
50
+
51
+ MIT.
package/dist/index.cjs ADDED
@@ -0,0 +1,145 @@
1
+ 'use strict';
2
+
3
+ var connectors = require('@particle-academy/fancy-flow/connectors');
4
+
5
+ // src/service.ts
6
+ var CONNECTOR_API_VERSION = 1;
7
+ var GMAIL_SERVICE = {
8
+ service: "gmail",
9
+ serviceTitle: "Gmail",
10
+ domain: "email",
11
+ sandbox: "none"
12
+ };
13
+ var GMAIL_CREDENTIALS = [
14
+ {
15
+ "key": "clientId",
16
+ "label": "OAuth client ID",
17
+ "scope": "provider",
18
+ "secret": false,
19
+ "help": "From Google Cloud Console -> APIs & Services -> Credentials. ONE value for the whole installation, not per connected account."
20
+ },
21
+ {
22
+ "key": "clientSecret",
23
+ "label": "OAuth client secret",
24
+ "scope": "provider",
25
+ "secret": true,
26
+ "help": "The client secret for the same OAuth app. One value for the whole installation."
27
+ },
28
+ {
29
+ "key": "accessToken",
30
+ "label": "Access token",
31
+ "scope": "account",
32
+ "secret": true,
33
+ "help": "Per connected Google account, and it expires after ONE HOUR. The host refreshes it with the refresh token."
34
+ },
35
+ {
36
+ "key": "refreshToken",
37
+ "label": "Refresh token",
38
+ "scope": "account",
39
+ "secret": true,
40
+ "help": "Per connected Google account. Google issues one only when the consent request asks for offline access; without it the connection dies within the hour."
41
+ }
42
+ ];
43
+ var GMAIL_OAUTH = {
44
+ "flow": "authorization_code",
45
+ "authorizeUrl": "https://accounts.google.com/o/oauth2/v2/auth",
46
+ "tokenUrl": "https://oauth2.googleapis.com/token",
47
+ "scopes": [
48
+ "https://www.googleapis.com/auth/gmail.send"
49
+ ],
50
+ "accessTokenCredential": "accessToken",
51
+ "refreshTokenCredential": "refreshToken",
52
+ "accessTokenTtlSeconds": 3600
53
+ };
54
+ function gmailMeta(role, operation, docs) {
55
+ return { ...GMAIL_SERVICE, role, operation, docs };
56
+ }
57
+ var GMAIL_MESSAGE_KIND = "@particle-academy/gmail_message";
58
+ var GMAIL_MESSAGE_OPERATION = "message_send";
59
+ var GMAIL_MESSAGE_META = gmailMeta("action", "send an email", "https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.messages/send");
60
+ var GMAIL_MESSAGE_OUTPUT = [
61
+ {
62
+ "path": "data.id",
63
+ "type": "string",
64
+ "description": "Gmail's id for the sent message."
65
+ },
66
+ {
67
+ "path": "data.threadId",
68
+ "type": "string",
69
+ "description": "The thread it landed in. Gmail groups by subject and references, so a reply to an existing conversation shares this."
70
+ },
71
+ {
72
+ "path": "data.labelIds",
73
+ "type": "array",
74
+ "description": "Labels Gmail applied, typically SENT."
75
+ }
76
+ ];
77
+ var gmailMessageKind = connectors.defineConnectorKind(GMAIL_MESSAGE_META, {
78
+ name: GMAIL_MESSAGE_KIND,
79
+ aliases: ["gmail_message"],
80
+ label: "Gmail message",
81
+ description: "Send an email from a connected Gmail account.",
82
+ inputs: [{ id: "in" }],
83
+ outputs: [{ id: "out" }],
84
+ sideEffects: "unsafe-to-replay",
85
+ outputShape: GMAIL_MESSAGE_OUTPUT,
86
+ configSchema: [
87
+ {
88
+ "type": "text",
89
+ "key": "userId",
90
+ "label": "Send as",
91
+ "required": true,
92
+ "default": "me",
93
+ "description": "Whose mailbox to send from. `me` is the connected account and is almost always what you want."
94
+ },
95
+ {
96
+ "type": "text",
97
+ "key": "to",
98
+ "label": "To",
99
+ "required": true,
100
+ "description": "Recipient address. Several can be given, comma separated, exactly as an email client accepts them."
101
+ },
102
+ {
103
+ "type": "text",
104
+ "key": "cc",
105
+ "label": "Cc",
106
+ "description": "Copied recipients, comma separated. Left empty the header is omitted entirely rather than sent blank."
107
+ },
108
+ {
109
+ "type": "text",
110
+ "key": "subject",
111
+ "label": "Subject",
112
+ "required": true
113
+ },
114
+ {
115
+ "type": "textarea",
116
+ "key": "body",
117
+ "label": "Message",
118
+ "required": true,
119
+ "description": "The plain-text body. It is sent as text/plain; UTF-8, so accented characters and emoji survive."
120
+ }
121
+ ],
122
+ defaultConfig: {
123
+ "mode": "auto"
124
+ },
125
+ renderBody: ({ config }) => connectors.summarize(GMAIL_MESSAGE_META, config, "send an email")
126
+ });
127
+
128
+ // src/index.ts
129
+ var GMAIL_KINDS = [
130
+ gmailMessageKind
131
+ ];
132
+
133
+ exports.CONNECTOR_API_VERSION = CONNECTOR_API_VERSION;
134
+ exports.GMAIL_CREDENTIALS = GMAIL_CREDENTIALS;
135
+ exports.GMAIL_KINDS = GMAIL_KINDS;
136
+ exports.GMAIL_MESSAGE_KIND = GMAIL_MESSAGE_KIND;
137
+ exports.GMAIL_MESSAGE_META = GMAIL_MESSAGE_META;
138
+ exports.GMAIL_MESSAGE_OPERATION = GMAIL_MESSAGE_OPERATION;
139
+ exports.GMAIL_MESSAGE_OUTPUT = GMAIL_MESSAGE_OUTPUT;
140
+ exports.GMAIL_OAUTH = GMAIL_OAUTH;
141
+ exports.GMAIL_SERVICE = GMAIL_SERVICE;
142
+ exports.gmailMessageKind = gmailMessageKind;
143
+ exports.gmailMeta = gmailMeta;
144
+ //# sourceMappingURL=index.cjs.map
145
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/service.ts","../src/kinds/message-send.ts","../src/index.ts"],"names":["defineConnectorKind","summarize"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/manifest.json by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/manifest.json (or weaver's template/) and regenerate:\n *\n * npm run provider -- gmail\n */\n\n/**\n * Gmail's identity on the authoring surface, shared by every Gmail node.\n *\n * This file must import nothing from the js package: a PHP or Python project\n * installs the ui package and never that one, and the import would be a\n * dangling module the moment it did.\n *\n * ## The sandbox trap\n *\n * Gmail has no sandbox. A send is a real email to a real address -- point this\n * at your own inbox before pointing it at anybody else's. The faker is the\n * only way to develop against it without delivering mail.\n */\n\nimport type { ConnectorMeta } from \"@particle-academy/fancy-flow/connectors\";\n\n/**\n * The connector API version this package was GENERATED against.\n *\n * A literal, never imported — an imported constant lets an upgrade rewrite the\n * very claim it exists to detect.\n */\nexport const CONNECTOR_API_VERSION = 1;\n\n/** The parts of a connector's identity that belong to the SERVICE, not the node. */\nexport const GMAIL_SERVICE = {\n service: \"gmail\",\n serviceTitle: \"Gmail\",\n domain: \"email\",\n sandbox: \"none\",\n} as const satisfies Pick<ConnectorMeta, \"service\" | \"serviceTitle\" | \"domain\" | \"sandbox\">;\n\n/** The credentials a Gmail connection holds. */\nexport const GMAIL_CREDENTIALS = [\n {\n \"key\": \"clientId\",\n \"label\": \"OAuth client ID\",\n \"scope\": \"provider\",\n \"secret\": false,\n \"help\": \"From Google Cloud Console -> APIs & Services -> Credentials. ONE value for the whole installation, not per connected account.\"\n },\n {\n \"key\": \"clientSecret\",\n \"label\": \"OAuth client secret\",\n \"scope\": \"provider\",\n \"secret\": true,\n \"help\": \"The client secret for the same OAuth app. One value for the whole installation.\"\n },\n {\n \"key\": \"accessToken\",\n \"label\": \"Access token\",\n \"scope\": \"account\",\n \"secret\": true,\n \"help\": \"Per connected Google account, and it expires after ONE HOUR. The host refreshes it with the refresh token.\"\n },\n {\n \"key\": \"refreshToken\",\n \"label\": \"Refresh token\",\n \"scope\": \"account\",\n \"secret\": true,\n \"help\": \"Per connected Google account. Google issues one only when the consent request asks for offline access; without it the connection dies within the hour.\"\n }\n] as const;\n\n/**\n * The OAuth2 exchange Gmail requires — DECLARED here, performed by the host.\n *\n * A consent screen needs a browser, a redirect URI and somewhere to persist\n * the result, and all three belong to the host; a package that ran the dance\n * itself would have to own a web server. So this says precisely enough for a\n * host to do it.\n *\n * The access token lasts 3600 seconds. A host that never refreshes will work\n * all afternoon and be broken by morning, which is why the lifetime is stated\n * rather than left to be discovered.\n */\nexport const GMAIL_OAUTH = {\n \"flow\": \"authorization_code\",\n \"authorizeUrl\": \"https://accounts.google.com/o/oauth2/v2/auth\",\n \"tokenUrl\": \"https://oauth2.googleapis.com/token\",\n \"scopes\": [\n \"https://www.googleapis.com/auth/gmail.send\"\n ],\n \"accessTokenCredential\": \"accessToken\",\n \"refreshTokenCredential\": \"refreshToken\",\n \"accessTokenTtlSeconds\": 3600\n} as const;\n\n/** Build a Gmail node's connector metadata from the operation it performs. */\nexport function gmailMeta(\n role: ConnectorMeta[\"role\"],\n operation: string,\n docs: string,\n): ConnectorMeta {\n return { ...GMAIL_SERVICE, role, operation, docs };\n}\n","/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/actions/message-send.json by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/actions/message-send.json (or weaver's template/) and regenerate:\n *\n * npm run provider -- gmail\n */\n\n/**\n * Gmail message — Send an email from a connected Gmail account.\n *\n * https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.messages/send\n *\n * `unsafe-to-replay`.\n */\n\nimport type { NodeKindDefinition } from \"@particle-academy/fancy-flow/engine\";\nimport { defineConnectorKind, summarize, type OutputField } from \"@particle-academy/fancy-flow/connectors\";\nimport { gmailMeta } from \"../service.js\";\n\nexport const GMAIL_MESSAGE_KIND = \"@particle-academy/gmail_message\";\nexport const GMAIL_MESSAGE_OPERATION = \"message_send\";\n\nexport const GMAIL_MESSAGE_META = gmailMeta(\"action\", \"send an email\", \"https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.messages/send\");\n\n/**\n * What this node emits — the \"ingredients\" a downstream node can reference.\n *\n * fancy-flow reads `outputShape` off the kind and offers it in the variable\n * picker, so declaring it is the whole of the work: an author configuring the\n * next node picks `{{ $json.data.id }}` off a list instead of typing a path\n * and hoping.\n */\nexport const GMAIL_MESSAGE_OUTPUT: OutputField[] = [\n {\n \"path\": \"data.id\",\n \"type\": \"string\",\n \"description\": \"Gmail's id for the sent message.\"\n },\n {\n \"path\": \"data.threadId\",\n \"type\": \"string\",\n \"description\": \"The thread it landed in. Gmail groups by subject and references, so a reply to an existing conversation shares this.\"\n },\n {\n \"path\": \"data.labelIds\",\n \"type\": \"array\",\n \"description\": \"Labels Gmail applied, typically SENT.\"\n }\n];\n\nexport const gmailMessageKind: NodeKindDefinition = defineConnectorKind(GMAIL_MESSAGE_META, {\n name: GMAIL_MESSAGE_KIND,\n aliases: [\"gmail_message\"],\n label: \"Gmail message\",\n description: \"Send an email from a connected Gmail account.\",\n inputs: [{ id: \"in\" }],\n outputs: [{ id: \"out\" }],\n sideEffects: \"unsafe-to-replay\",\n outputShape: GMAIL_MESSAGE_OUTPUT,\n configSchema: [\n {\n \"type\": \"text\",\n \"key\": \"userId\",\n \"label\": \"Send as\",\n \"required\": true,\n \"default\": \"me\",\n \"description\": \"Whose mailbox to send from. `me` is the connected account and is almost always what you want.\"\n },\n {\n \"type\": \"text\",\n \"key\": \"to\",\n \"label\": \"To\",\n \"required\": true,\n \"description\": \"Recipient address. Several can be given, comma separated, exactly as an email client accepts them.\"\n },\n {\n \"type\": \"text\",\n \"key\": \"cc\",\n \"label\": \"Cc\",\n \"description\": \"Copied recipients, comma separated. Left empty the header is omitted entirely rather than sent blank.\"\n },\n {\n \"type\": \"text\",\n \"key\": \"subject\",\n \"label\": \"Subject\",\n \"required\": true\n },\n {\n \"type\": \"textarea\",\n \"key\": \"body\",\n \"label\": \"Message\",\n \"required\": true,\n \"description\": \"The plain-text body. It is sent as text/plain; UTF-8, so accented characters and emoji survive.\"\n }\n ],\n defaultConfig: {\n \"mode\": \"auto\"\n },\n renderBody: ({ config }) =>\n summarize(GMAIL_MESSAGE_META, config as Record<string, unknown>, \"send an email\"),\n});\n","/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/manifest.json by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/manifest.json (or weaver's template/) and regenerate:\n *\n * npm run provider -- gmail\n */\n\n/**\n * Gmail's node kinds for fancy-flow.\n *\n * Install this on every host. The TypeScript executors live in the js\n * package's `./flow` subpath; PHP and Python hosts run their own and need only\n * this.\n */\n\nexport * from \"./service.js\";\nexport * from \"./kinds/message-send.js\";\n\nimport type { NodeKindDefinition } from \"@particle-academy/fancy-flow/engine\";\nimport { gmailMessageKind } from \"./kinds/message-send.js\";\n\n/** Every Gmail kind, for a host that registers the lot. */\nexport const GMAIL_KINDS: NodeKindDefinition[] = [\n gmailMessageKind,\n];\n"]}
@@ -0,0 +1,128 @@
1
+ import * as _particle_academy_fancy_flow_connectors from '@particle-academy/fancy-flow/connectors';
2
+ import { ConnectorMeta, OutputField } from '@particle-academy/fancy-flow/connectors';
3
+ import { NodeKindDefinition } from '@particle-academy/fancy-flow/engine';
4
+
5
+ /**
6
+ * GENERATED FILE — do not edit.
7
+ *
8
+ * Emitted from provider/manifest.json by weaver's generator.
9
+ * A hand-edit here is destroyed by the next protocol sync, which is worse than
10
+ * being rejected, because it works until it silently does not. Fix
11
+ * provider/manifest.json (or weaver's template/) and regenerate:
12
+ *
13
+ * npm run provider -- gmail
14
+ */
15
+ /**
16
+ * Gmail's identity on the authoring surface, shared by every Gmail node.
17
+ *
18
+ * This file must import nothing from the js package: a PHP or Python project
19
+ * installs the ui package and never that one, and the import would be a
20
+ * dangling module the moment it did.
21
+ *
22
+ * ## The sandbox trap
23
+ *
24
+ * Gmail has no sandbox. A send is a real email to a real address -- point this
25
+ * at your own inbox before pointing it at anybody else's. The faker is the
26
+ * only way to develop against it without delivering mail.
27
+ */
28
+
29
+ /**
30
+ * The connector API version this package was GENERATED against.
31
+ *
32
+ * A literal, never imported — an imported constant lets an upgrade rewrite the
33
+ * very claim it exists to detect.
34
+ */
35
+ declare const CONNECTOR_API_VERSION = 1;
36
+ /** The parts of a connector's identity that belong to the SERVICE, not the node. */
37
+ declare const GMAIL_SERVICE: {
38
+ readonly service: "gmail";
39
+ readonly serviceTitle: "Gmail";
40
+ readonly domain: "email";
41
+ readonly sandbox: "none";
42
+ };
43
+ /** The credentials a Gmail connection holds. */
44
+ declare const GMAIL_CREDENTIALS: readonly [{
45
+ readonly key: "clientId";
46
+ readonly label: "OAuth client ID";
47
+ readonly scope: "provider";
48
+ readonly secret: false;
49
+ readonly help: "From Google Cloud Console -> APIs & Services -> Credentials. ONE value for the whole installation, not per connected account.";
50
+ }, {
51
+ readonly key: "clientSecret";
52
+ readonly label: "OAuth client secret";
53
+ readonly scope: "provider";
54
+ readonly secret: true;
55
+ readonly help: "The client secret for the same OAuth app. One value for the whole installation.";
56
+ }, {
57
+ readonly key: "accessToken";
58
+ readonly label: "Access token";
59
+ readonly scope: "account";
60
+ readonly secret: true;
61
+ readonly help: "Per connected Google account, and it expires after ONE HOUR. The host refreshes it with the refresh token.";
62
+ }, {
63
+ readonly key: "refreshToken";
64
+ readonly label: "Refresh token";
65
+ readonly scope: "account";
66
+ readonly secret: true;
67
+ readonly help: "Per connected Google account. Google issues one only when the consent request asks for offline access; without it the connection dies within the hour.";
68
+ }];
69
+ /**
70
+ * The OAuth2 exchange Gmail requires — DECLARED here, performed by the host.
71
+ *
72
+ * A consent screen needs a browser, a redirect URI and somewhere to persist
73
+ * the result, and all three belong to the host; a package that ran the dance
74
+ * itself would have to own a web server. So this says precisely enough for a
75
+ * host to do it.
76
+ *
77
+ * The access token lasts 3600 seconds. A host that never refreshes will work
78
+ * all afternoon and be broken by morning, which is why the lifetime is stated
79
+ * rather than left to be discovered.
80
+ */
81
+ declare const GMAIL_OAUTH: {
82
+ readonly flow: "authorization_code";
83
+ readonly authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth";
84
+ readonly tokenUrl: "https://oauth2.googleapis.com/token";
85
+ readonly scopes: readonly ["https://www.googleapis.com/auth/gmail.send"];
86
+ readonly accessTokenCredential: "accessToken";
87
+ readonly refreshTokenCredential: "refreshToken";
88
+ readonly accessTokenTtlSeconds: 3600;
89
+ };
90
+ /** Build a Gmail node's connector metadata from the operation it performs. */
91
+ declare function gmailMeta(role: ConnectorMeta["role"], operation: string, docs: string): ConnectorMeta;
92
+
93
+ declare const GMAIL_MESSAGE_KIND = "@particle-academy/gmail_message";
94
+ declare const GMAIL_MESSAGE_OPERATION = "message_send";
95
+ declare const GMAIL_MESSAGE_META: _particle_academy_fancy_flow_connectors.ConnectorMeta;
96
+ /**
97
+ * What this node emits — the "ingredients" a downstream node can reference.
98
+ *
99
+ * fancy-flow reads `outputShape` off the kind and offers it in the variable
100
+ * picker, so declaring it is the whole of the work: an author configuring the
101
+ * next node picks `{{ $json.data.id }}` off a list instead of typing a path
102
+ * and hoping.
103
+ */
104
+ declare const GMAIL_MESSAGE_OUTPUT: OutputField[];
105
+ declare const gmailMessageKind: NodeKindDefinition;
106
+
107
+ /**
108
+ * GENERATED FILE — do not edit.
109
+ *
110
+ * Emitted from provider/manifest.json by weaver's generator.
111
+ * A hand-edit here is destroyed by the next protocol sync, which is worse than
112
+ * being rejected, because it works until it silently does not. Fix
113
+ * provider/manifest.json (or weaver's template/) and regenerate:
114
+ *
115
+ * npm run provider -- gmail
116
+ */
117
+ /**
118
+ * Gmail's node kinds for fancy-flow.
119
+ *
120
+ * Install this on every host. The TypeScript executors live in the js
121
+ * package's `./flow` subpath; PHP and Python hosts run their own and need only
122
+ * this.
123
+ */
124
+
125
+ /** Every Gmail kind, for a host that registers the lot. */
126
+ declare const GMAIL_KINDS: NodeKindDefinition[];
127
+
128
+ export { CONNECTOR_API_VERSION, GMAIL_CREDENTIALS, GMAIL_KINDS, GMAIL_MESSAGE_KIND, GMAIL_MESSAGE_META, GMAIL_MESSAGE_OPERATION, GMAIL_MESSAGE_OUTPUT, GMAIL_OAUTH, GMAIL_SERVICE, gmailMessageKind, gmailMeta };
@@ -0,0 +1,128 @@
1
+ import * as _particle_academy_fancy_flow_connectors from '@particle-academy/fancy-flow/connectors';
2
+ import { ConnectorMeta, OutputField } from '@particle-academy/fancy-flow/connectors';
3
+ import { NodeKindDefinition } from '@particle-academy/fancy-flow/engine';
4
+
5
+ /**
6
+ * GENERATED FILE — do not edit.
7
+ *
8
+ * Emitted from provider/manifest.json by weaver's generator.
9
+ * A hand-edit here is destroyed by the next protocol sync, which is worse than
10
+ * being rejected, because it works until it silently does not. Fix
11
+ * provider/manifest.json (or weaver's template/) and regenerate:
12
+ *
13
+ * npm run provider -- gmail
14
+ */
15
+ /**
16
+ * Gmail's identity on the authoring surface, shared by every Gmail node.
17
+ *
18
+ * This file must import nothing from the js package: a PHP or Python project
19
+ * installs the ui package and never that one, and the import would be a
20
+ * dangling module the moment it did.
21
+ *
22
+ * ## The sandbox trap
23
+ *
24
+ * Gmail has no sandbox. A send is a real email to a real address -- point this
25
+ * at your own inbox before pointing it at anybody else's. The faker is the
26
+ * only way to develop against it without delivering mail.
27
+ */
28
+
29
+ /**
30
+ * The connector API version this package was GENERATED against.
31
+ *
32
+ * A literal, never imported — an imported constant lets an upgrade rewrite the
33
+ * very claim it exists to detect.
34
+ */
35
+ declare const CONNECTOR_API_VERSION = 1;
36
+ /** The parts of a connector's identity that belong to the SERVICE, not the node. */
37
+ declare const GMAIL_SERVICE: {
38
+ readonly service: "gmail";
39
+ readonly serviceTitle: "Gmail";
40
+ readonly domain: "email";
41
+ readonly sandbox: "none";
42
+ };
43
+ /** The credentials a Gmail connection holds. */
44
+ declare const GMAIL_CREDENTIALS: readonly [{
45
+ readonly key: "clientId";
46
+ readonly label: "OAuth client ID";
47
+ readonly scope: "provider";
48
+ readonly secret: false;
49
+ readonly help: "From Google Cloud Console -> APIs & Services -> Credentials. ONE value for the whole installation, not per connected account.";
50
+ }, {
51
+ readonly key: "clientSecret";
52
+ readonly label: "OAuth client secret";
53
+ readonly scope: "provider";
54
+ readonly secret: true;
55
+ readonly help: "The client secret for the same OAuth app. One value for the whole installation.";
56
+ }, {
57
+ readonly key: "accessToken";
58
+ readonly label: "Access token";
59
+ readonly scope: "account";
60
+ readonly secret: true;
61
+ readonly help: "Per connected Google account, and it expires after ONE HOUR. The host refreshes it with the refresh token.";
62
+ }, {
63
+ readonly key: "refreshToken";
64
+ readonly label: "Refresh token";
65
+ readonly scope: "account";
66
+ readonly secret: true;
67
+ readonly help: "Per connected Google account. Google issues one only when the consent request asks for offline access; without it the connection dies within the hour.";
68
+ }];
69
+ /**
70
+ * The OAuth2 exchange Gmail requires — DECLARED here, performed by the host.
71
+ *
72
+ * A consent screen needs a browser, a redirect URI and somewhere to persist
73
+ * the result, and all three belong to the host; a package that ran the dance
74
+ * itself would have to own a web server. So this says precisely enough for a
75
+ * host to do it.
76
+ *
77
+ * The access token lasts 3600 seconds. A host that never refreshes will work
78
+ * all afternoon and be broken by morning, which is why the lifetime is stated
79
+ * rather than left to be discovered.
80
+ */
81
+ declare const GMAIL_OAUTH: {
82
+ readonly flow: "authorization_code";
83
+ readonly authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth";
84
+ readonly tokenUrl: "https://oauth2.googleapis.com/token";
85
+ readonly scopes: readonly ["https://www.googleapis.com/auth/gmail.send"];
86
+ readonly accessTokenCredential: "accessToken";
87
+ readonly refreshTokenCredential: "refreshToken";
88
+ readonly accessTokenTtlSeconds: 3600;
89
+ };
90
+ /** Build a Gmail node's connector metadata from the operation it performs. */
91
+ declare function gmailMeta(role: ConnectorMeta["role"], operation: string, docs: string): ConnectorMeta;
92
+
93
+ declare const GMAIL_MESSAGE_KIND = "@particle-academy/gmail_message";
94
+ declare const GMAIL_MESSAGE_OPERATION = "message_send";
95
+ declare const GMAIL_MESSAGE_META: _particle_academy_fancy_flow_connectors.ConnectorMeta;
96
+ /**
97
+ * What this node emits — the "ingredients" a downstream node can reference.
98
+ *
99
+ * fancy-flow reads `outputShape` off the kind and offers it in the variable
100
+ * picker, so declaring it is the whole of the work: an author configuring the
101
+ * next node picks `{{ $json.data.id }}` off a list instead of typing a path
102
+ * and hoping.
103
+ */
104
+ declare const GMAIL_MESSAGE_OUTPUT: OutputField[];
105
+ declare const gmailMessageKind: NodeKindDefinition;
106
+
107
+ /**
108
+ * GENERATED FILE — do not edit.
109
+ *
110
+ * Emitted from provider/manifest.json by weaver's generator.
111
+ * A hand-edit here is destroyed by the next protocol sync, which is worse than
112
+ * being rejected, because it works until it silently does not. Fix
113
+ * provider/manifest.json (or weaver's template/) and regenerate:
114
+ *
115
+ * npm run provider -- gmail
116
+ */
117
+ /**
118
+ * Gmail's node kinds for fancy-flow.
119
+ *
120
+ * Install this on every host. The TypeScript executors live in the js
121
+ * package's `./flow` subpath; PHP and Python hosts run their own and need only
122
+ * this.
123
+ */
124
+
125
+ /** Every Gmail kind, for a host that registers the lot. */
126
+ declare const GMAIL_KINDS: NodeKindDefinition[];
127
+
128
+ export { CONNECTOR_API_VERSION, GMAIL_CREDENTIALS, GMAIL_KINDS, GMAIL_MESSAGE_KIND, GMAIL_MESSAGE_META, GMAIL_MESSAGE_OPERATION, GMAIL_MESSAGE_OUTPUT, GMAIL_OAUTH, GMAIL_SERVICE, gmailMessageKind, gmailMeta };
package/dist/index.js ADDED
@@ -0,0 +1,133 @@
1
+ import { defineConnectorKind, summarize } from '@particle-academy/fancy-flow/connectors';
2
+
3
+ // src/service.ts
4
+ var CONNECTOR_API_VERSION = 1;
5
+ var GMAIL_SERVICE = {
6
+ service: "gmail",
7
+ serviceTitle: "Gmail",
8
+ domain: "email",
9
+ sandbox: "none"
10
+ };
11
+ var GMAIL_CREDENTIALS = [
12
+ {
13
+ "key": "clientId",
14
+ "label": "OAuth client ID",
15
+ "scope": "provider",
16
+ "secret": false,
17
+ "help": "From Google Cloud Console -> APIs & Services -> Credentials. ONE value for the whole installation, not per connected account."
18
+ },
19
+ {
20
+ "key": "clientSecret",
21
+ "label": "OAuth client secret",
22
+ "scope": "provider",
23
+ "secret": true,
24
+ "help": "The client secret for the same OAuth app. One value for the whole installation."
25
+ },
26
+ {
27
+ "key": "accessToken",
28
+ "label": "Access token",
29
+ "scope": "account",
30
+ "secret": true,
31
+ "help": "Per connected Google account, and it expires after ONE HOUR. The host refreshes it with the refresh token."
32
+ },
33
+ {
34
+ "key": "refreshToken",
35
+ "label": "Refresh token",
36
+ "scope": "account",
37
+ "secret": true,
38
+ "help": "Per connected Google account. Google issues one only when the consent request asks for offline access; without it the connection dies within the hour."
39
+ }
40
+ ];
41
+ var GMAIL_OAUTH = {
42
+ "flow": "authorization_code",
43
+ "authorizeUrl": "https://accounts.google.com/o/oauth2/v2/auth",
44
+ "tokenUrl": "https://oauth2.googleapis.com/token",
45
+ "scopes": [
46
+ "https://www.googleapis.com/auth/gmail.send"
47
+ ],
48
+ "accessTokenCredential": "accessToken",
49
+ "refreshTokenCredential": "refreshToken",
50
+ "accessTokenTtlSeconds": 3600
51
+ };
52
+ function gmailMeta(role, operation, docs) {
53
+ return { ...GMAIL_SERVICE, role, operation, docs };
54
+ }
55
+ var GMAIL_MESSAGE_KIND = "@particle-academy/gmail_message";
56
+ var GMAIL_MESSAGE_OPERATION = "message_send";
57
+ var GMAIL_MESSAGE_META = gmailMeta("action", "send an email", "https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.messages/send");
58
+ var GMAIL_MESSAGE_OUTPUT = [
59
+ {
60
+ "path": "data.id",
61
+ "type": "string",
62
+ "description": "Gmail's id for the sent message."
63
+ },
64
+ {
65
+ "path": "data.threadId",
66
+ "type": "string",
67
+ "description": "The thread it landed in. Gmail groups by subject and references, so a reply to an existing conversation shares this."
68
+ },
69
+ {
70
+ "path": "data.labelIds",
71
+ "type": "array",
72
+ "description": "Labels Gmail applied, typically SENT."
73
+ }
74
+ ];
75
+ var gmailMessageKind = defineConnectorKind(GMAIL_MESSAGE_META, {
76
+ name: GMAIL_MESSAGE_KIND,
77
+ aliases: ["gmail_message"],
78
+ label: "Gmail message",
79
+ description: "Send an email from a connected Gmail account.",
80
+ inputs: [{ id: "in" }],
81
+ outputs: [{ id: "out" }],
82
+ sideEffects: "unsafe-to-replay",
83
+ outputShape: GMAIL_MESSAGE_OUTPUT,
84
+ configSchema: [
85
+ {
86
+ "type": "text",
87
+ "key": "userId",
88
+ "label": "Send as",
89
+ "required": true,
90
+ "default": "me",
91
+ "description": "Whose mailbox to send from. `me` is the connected account and is almost always what you want."
92
+ },
93
+ {
94
+ "type": "text",
95
+ "key": "to",
96
+ "label": "To",
97
+ "required": true,
98
+ "description": "Recipient address. Several can be given, comma separated, exactly as an email client accepts them."
99
+ },
100
+ {
101
+ "type": "text",
102
+ "key": "cc",
103
+ "label": "Cc",
104
+ "description": "Copied recipients, comma separated. Left empty the header is omitted entirely rather than sent blank."
105
+ },
106
+ {
107
+ "type": "text",
108
+ "key": "subject",
109
+ "label": "Subject",
110
+ "required": true
111
+ },
112
+ {
113
+ "type": "textarea",
114
+ "key": "body",
115
+ "label": "Message",
116
+ "required": true,
117
+ "description": "The plain-text body. It is sent as text/plain; UTF-8, so accented characters and emoji survive."
118
+ }
119
+ ],
120
+ defaultConfig: {
121
+ "mode": "auto"
122
+ },
123
+ renderBody: ({ config }) => summarize(GMAIL_MESSAGE_META, config, "send an email")
124
+ });
125
+
126
+ // src/index.ts
127
+ var GMAIL_KINDS = [
128
+ gmailMessageKind
129
+ ];
130
+
131
+ export { CONNECTOR_API_VERSION, GMAIL_CREDENTIALS, GMAIL_KINDS, GMAIL_MESSAGE_KIND, GMAIL_MESSAGE_META, GMAIL_MESSAGE_OPERATION, GMAIL_MESSAGE_OUTPUT, GMAIL_OAUTH, GMAIL_SERVICE, gmailMessageKind, gmailMeta };
132
+ //# sourceMappingURL=index.js.map
133
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/service.ts","../src/kinds/message-send.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/manifest.json by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/manifest.json (or weaver's template/) and regenerate:\n *\n * npm run provider -- gmail\n */\n\n/**\n * Gmail's identity on the authoring surface, shared by every Gmail node.\n *\n * This file must import nothing from the js package: a PHP or Python project\n * installs the ui package and never that one, and the import would be a\n * dangling module the moment it did.\n *\n * ## The sandbox trap\n *\n * Gmail has no sandbox. A send is a real email to a real address -- point this\n * at your own inbox before pointing it at anybody else's. The faker is the\n * only way to develop against it without delivering mail.\n */\n\nimport type { ConnectorMeta } from \"@particle-academy/fancy-flow/connectors\";\n\n/**\n * The connector API version this package was GENERATED against.\n *\n * A literal, never imported — an imported constant lets an upgrade rewrite the\n * very claim it exists to detect.\n */\nexport const CONNECTOR_API_VERSION = 1;\n\n/** The parts of a connector's identity that belong to the SERVICE, not the node. */\nexport const GMAIL_SERVICE = {\n service: \"gmail\",\n serviceTitle: \"Gmail\",\n domain: \"email\",\n sandbox: \"none\",\n} as const satisfies Pick<ConnectorMeta, \"service\" | \"serviceTitle\" | \"domain\" | \"sandbox\">;\n\n/** The credentials a Gmail connection holds. */\nexport const GMAIL_CREDENTIALS = [\n {\n \"key\": \"clientId\",\n \"label\": \"OAuth client ID\",\n \"scope\": \"provider\",\n \"secret\": false,\n \"help\": \"From Google Cloud Console -> APIs & Services -> Credentials. ONE value for the whole installation, not per connected account.\"\n },\n {\n \"key\": \"clientSecret\",\n \"label\": \"OAuth client secret\",\n \"scope\": \"provider\",\n \"secret\": true,\n \"help\": \"The client secret for the same OAuth app. One value for the whole installation.\"\n },\n {\n \"key\": \"accessToken\",\n \"label\": \"Access token\",\n \"scope\": \"account\",\n \"secret\": true,\n \"help\": \"Per connected Google account, and it expires after ONE HOUR. The host refreshes it with the refresh token.\"\n },\n {\n \"key\": \"refreshToken\",\n \"label\": \"Refresh token\",\n \"scope\": \"account\",\n \"secret\": true,\n \"help\": \"Per connected Google account. Google issues one only when the consent request asks for offline access; without it the connection dies within the hour.\"\n }\n] as const;\n\n/**\n * The OAuth2 exchange Gmail requires — DECLARED here, performed by the host.\n *\n * A consent screen needs a browser, a redirect URI and somewhere to persist\n * the result, and all three belong to the host; a package that ran the dance\n * itself would have to own a web server. So this says precisely enough for a\n * host to do it.\n *\n * The access token lasts 3600 seconds. A host that never refreshes will work\n * all afternoon and be broken by morning, which is why the lifetime is stated\n * rather than left to be discovered.\n */\nexport const GMAIL_OAUTH = {\n \"flow\": \"authorization_code\",\n \"authorizeUrl\": \"https://accounts.google.com/o/oauth2/v2/auth\",\n \"tokenUrl\": \"https://oauth2.googleapis.com/token\",\n \"scopes\": [\n \"https://www.googleapis.com/auth/gmail.send\"\n ],\n \"accessTokenCredential\": \"accessToken\",\n \"refreshTokenCredential\": \"refreshToken\",\n \"accessTokenTtlSeconds\": 3600\n} as const;\n\n/** Build a Gmail node's connector metadata from the operation it performs. */\nexport function gmailMeta(\n role: ConnectorMeta[\"role\"],\n operation: string,\n docs: string,\n): ConnectorMeta {\n return { ...GMAIL_SERVICE, role, operation, docs };\n}\n","/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/actions/message-send.json by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/actions/message-send.json (or weaver's template/) and regenerate:\n *\n * npm run provider -- gmail\n */\n\n/**\n * Gmail message — Send an email from a connected Gmail account.\n *\n * https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.messages/send\n *\n * `unsafe-to-replay`.\n */\n\nimport type { NodeKindDefinition } from \"@particle-academy/fancy-flow/engine\";\nimport { defineConnectorKind, summarize, type OutputField } from \"@particle-academy/fancy-flow/connectors\";\nimport { gmailMeta } from \"../service.js\";\n\nexport const GMAIL_MESSAGE_KIND = \"@particle-academy/gmail_message\";\nexport const GMAIL_MESSAGE_OPERATION = \"message_send\";\n\nexport const GMAIL_MESSAGE_META = gmailMeta(\"action\", \"send an email\", \"https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.messages/send\");\n\n/**\n * What this node emits — the \"ingredients\" a downstream node can reference.\n *\n * fancy-flow reads `outputShape` off the kind and offers it in the variable\n * picker, so declaring it is the whole of the work: an author configuring the\n * next node picks `{{ $json.data.id }}` off a list instead of typing a path\n * and hoping.\n */\nexport const GMAIL_MESSAGE_OUTPUT: OutputField[] = [\n {\n \"path\": \"data.id\",\n \"type\": \"string\",\n \"description\": \"Gmail's id for the sent message.\"\n },\n {\n \"path\": \"data.threadId\",\n \"type\": \"string\",\n \"description\": \"The thread it landed in. Gmail groups by subject and references, so a reply to an existing conversation shares this.\"\n },\n {\n \"path\": \"data.labelIds\",\n \"type\": \"array\",\n \"description\": \"Labels Gmail applied, typically SENT.\"\n }\n];\n\nexport const gmailMessageKind: NodeKindDefinition = defineConnectorKind(GMAIL_MESSAGE_META, {\n name: GMAIL_MESSAGE_KIND,\n aliases: [\"gmail_message\"],\n label: \"Gmail message\",\n description: \"Send an email from a connected Gmail account.\",\n inputs: [{ id: \"in\" }],\n outputs: [{ id: \"out\" }],\n sideEffects: \"unsafe-to-replay\",\n outputShape: GMAIL_MESSAGE_OUTPUT,\n configSchema: [\n {\n \"type\": \"text\",\n \"key\": \"userId\",\n \"label\": \"Send as\",\n \"required\": true,\n \"default\": \"me\",\n \"description\": \"Whose mailbox to send from. `me` is the connected account and is almost always what you want.\"\n },\n {\n \"type\": \"text\",\n \"key\": \"to\",\n \"label\": \"To\",\n \"required\": true,\n \"description\": \"Recipient address. Several can be given, comma separated, exactly as an email client accepts them.\"\n },\n {\n \"type\": \"text\",\n \"key\": \"cc\",\n \"label\": \"Cc\",\n \"description\": \"Copied recipients, comma separated. Left empty the header is omitted entirely rather than sent blank.\"\n },\n {\n \"type\": \"text\",\n \"key\": \"subject\",\n \"label\": \"Subject\",\n \"required\": true\n },\n {\n \"type\": \"textarea\",\n \"key\": \"body\",\n \"label\": \"Message\",\n \"required\": true,\n \"description\": \"The plain-text body. It is sent as text/plain; UTF-8, so accented characters and emoji survive.\"\n }\n ],\n defaultConfig: {\n \"mode\": \"auto\"\n },\n renderBody: ({ config }) =>\n summarize(GMAIL_MESSAGE_META, config as Record<string, unknown>, \"send an email\"),\n});\n","/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/manifest.json by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/manifest.json (or weaver's template/) and regenerate:\n *\n * npm run provider -- gmail\n */\n\n/**\n * Gmail's node kinds for fancy-flow.\n *\n * Install this on every host. The TypeScript executors live in the js\n * package's `./flow` subpath; PHP and Python hosts run their own and need only\n * this.\n */\n\nexport * from \"./service.js\";\nexport * from \"./kinds/message-send.js\";\n\nimport type { NodeKindDefinition } from \"@particle-academy/fancy-flow/engine\";\nimport { gmailMessageKind } from \"./kinds/message-send.js\";\n\n/** Every Gmail kind, for a host that registers the lot. */\nexport const GMAIL_KINDS: NodeKindDefinition[] = [\n gmailMessageKind,\n];\n"]}
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@particle-academy/gmail-ui",
3
+ "version": "0.1.0",
4
+ "description": "Gmail's authoring surface for fancy-flow — the node kinds, their config schemas and their declared output shapes. React on every host; carries no executor.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/Fancy-Friends/gmail.git"
8
+ },
9
+ "homepage": "https://github.com/Fancy-Friends/gmail#readme",
10
+ "bugs": "https://github.com/Fancy-Friends/gmail/issues",
11
+ "type": "module",
12
+ "main": "./dist/index.cjs",
13
+ "module": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "import": {
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ },
21
+ "require": {
22
+ "types": "./dist/index.d.cts",
23
+ "default": "./dist/index.cjs"
24
+ }
25
+ },
26
+ "./package.json": "./package.json"
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "README.md",
31
+ "CHANGELOG.md",
32
+ "LICENSE"
33
+ ],
34
+ "scripts": {
35
+ "build": "tsup",
36
+ "lint": "tsc --noEmit",
37
+ "test": "node --import tsx --test tests/*.test.ts",
38
+ "prepublishOnly": "tsup"
39
+ },
40
+ "dependencies": {},
41
+ "peerDependencies": {
42
+ "@particle-academy/fancy-flow": ">=0.48.0 <2.0"
43
+ },
44
+ "devDependencies": {
45
+ "@particle-academy/fancy-flow": ">=0.48.0 <2.0",
46
+ "@types/node": "^26.2.0",
47
+ "tsup": "^8.5.0",
48
+ "tsx": "^4.19.0",
49
+ "typescript": "^5.8.0"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "keywords": [
55
+ "gmail",
56
+ "email",
57
+ "connector",
58
+ "fancy-flow",
59
+ "fancy-ui",
60
+ "particle-academy"
61
+ ],
62
+ "license": "MIT",
63
+ "sideEffects": false,
64
+ "engines": {
65
+ "node": ">=22"
66
+ }
67
+ }