pocketbomb-dynamite 0.0.0-stage → 0.2.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.
Files changed (107) hide show
  1. package/LICENSE.md +17 -0
  2. package/README.md +38 -2
  3. package/bin/dynamite.js +10 -0
  4. package/dist/_virtual/_rolldown/runtime.cjs +22 -0
  5. package/dist/cli/codegen.cjs +120 -0
  6. package/dist/cli/codegen.cjs.map +1 -0
  7. package/dist/cli/codegen.d.cts +68 -0
  8. package/dist/cli/codegen.d.ts +68 -0
  9. package/dist/cli/codegen.js +109 -0
  10. package/dist/cli/codegen.js.map +1 -0
  11. package/dist/cli/index.cjs +214 -0
  12. package/dist/cli/index.cjs.map +1 -0
  13. package/dist/cli/index.d.cts +12 -0
  14. package/dist/cli/index.d.ts +12 -0
  15. package/dist/cli/index.js +201 -0
  16. package/dist/cli/index.js.map +1 -0
  17. package/dist/client/index.d.cts +2 -0
  18. package/dist/client/index.d.ts +2 -0
  19. package/dist/client/storefront.cjs +96 -0
  20. package/dist/client/storefront.cjs.map +1 -0
  21. package/dist/client/storefront.d.cts +134 -0
  22. package/dist/client/storefront.d.ts +134 -0
  23. package/dist/client/storefront.js +96 -0
  24. package/dist/client/storefront.js.map +1 -0
  25. package/dist/core/buyer-ip.cjs +35 -0
  26. package/dist/core/buyer-ip.cjs.map +1 -0
  27. package/dist/core/buyer-ip.d.cts +25 -0
  28. package/dist/core/buyer-ip.d.ts +25 -0
  29. package/dist/core/buyer-ip.js +34 -0
  30. package/dist/core/buyer-ip.js.map +1 -0
  31. package/dist/core/cache.cjs +109 -0
  32. package/dist/core/cache.cjs.map +1 -0
  33. package/dist/core/cache.d.cts +91 -0
  34. package/dist/core/cache.d.ts +91 -0
  35. package/dist/core/cache.js +106 -0
  36. package/dist/core/cache.js.map +1 -0
  37. package/dist/core/index.d.cts +4 -0
  38. package/dist/core/index.d.ts +4 -0
  39. package/dist/core/request-context.cjs +48 -0
  40. package/dist/core/request-context.cjs.map +1 -0
  41. package/dist/core/request-context.d.cts +76 -0
  42. package/dist/core/request-context.d.ts +76 -0
  43. package/dist/core/request-context.js +48 -0
  44. package/dist/core/request-context.js.map +1 -0
  45. package/dist/customer-account/customer-account.cjs +107 -0
  46. package/dist/customer-account/customer-account.cjs.map +1 -0
  47. package/dist/customer-account/customer-account.d.cts +114 -0
  48. package/dist/customer-account/customer-account.d.ts +114 -0
  49. package/dist/customer-account/customer-account.js +106 -0
  50. package/dist/customer-account/customer-account.js.map +1 -0
  51. package/dist/customer-account/index.cjs +4 -0
  52. package/dist/customer-account/index.d.cts +2 -0
  53. package/dist/customer-account/index.d.ts +2 -0
  54. package/dist/customer-account/index.js +1 -0
  55. package/dist/graphql/graphql.cjs +160 -0
  56. package/dist/graphql/graphql.cjs.map +1 -0
  57. package/dist/graphql/graphql.d.cts +164 -0
  58. package/dist/graphql/graphql.d.ts +164 -0
  59. package/dist/graphql/graphql.js +144 -0
  60. package/dist/graphql/graphql.js.map +1 -0
  61. package/dist/graphql/index.cjs +20 -0
  62. package/dist/graphql/index.d.cts +2 -0
  63. package/dist/graphql/index.d.ts +2 -0
  64. package/dist/graphql/index.js +1 -0
  65. package/dist/index.cjs +32 -0
  66. package/dist/index.d.cts +9 -0
  67. package/dist/index.d.ts +9 -0
  68. package/dist/index.js +6 -0
  69. package/dist/react/components.cjs +87 -0
  70. package/dist/react/components.cjs.map +1 -0
  71. package/dist/react/components.d.cts +68 -0
  72. package/dist/react/components.d.ts +68 -0
  73. package/dist/react/components.js +82 -0
  74. package/dist/react/components.js.map +1 -0
  75. package/dist/react/context.cjs +71 -0
  76. package/dist/react/context.cjs.map +1 -0
  77. package/dist/react/context.d.cts +58 -0
  78. package/dist/react/context.d.ts +58 -0
  79. package/dist/react/context.js +69 -0
  80. package/dist/react/context.js.map +1 -0
  81. package/dist/react/hooks.cjs +136 -0
  82. package/dist/react/hooks.cjs.map +1 -0
  83. package/dist/react/hooks.d.cts +45 -0
  84. package/dist/react/hooks.d.ts +45 -0
  85. package/dist/react/hooks.js +135 -0
  86. package/dist/react/hooks.js.map +1 -0
  87. package/dist/react/index.cjs +15 -0
  88. package/dist/react/index.d.cts +4 -0
  89. package/dist/react/index.d.ts +4 -0
  90. package/dist/react/index.js +4 -0
  91. package/dist/shared/constants.cjs +9 -0
  92. package/dist/shared/constants.cjs.map +1 -0
  93. package/dist/shared/constants.d.cts +5 -0
  94. package/dist/shared/constants.d.ts +5 -0
  95. package/dist/shared/constants.js +8 -0
  96. package/dist/shared/constants.js.map +1 -0
  97. package/dist/ts-plugin/index.cjs +58 -0
  98. package/dist/ts-plugin/index.cjs.map +1 -0
  99. package/dist/ts-plugin/index.d.cts +3 -0
  100. package/dist/vite/index.cjs +145 -0
  101. package/dist/vite/index.cjs.map +1 -0
  102. package/dist/vite/index.d.cts +35 -0
  103. package/dist/vite/index.d.ts +35 -0
  104. package/dist/vite/index.js +140 -0
  105. package/dist/vite/index.js.map +1 -0
  106. package/package.json +123 -3
  107. package/ts-plugin/package.json +5 -0
package/LICENSE.md ADDED
@@ -0,0 +1,17 @@
1
+ The MIT License (MIT)
2
+ Copyright (c) 2026 - present, the PocketBomb contributors
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software
5
+ and associated documentation files (the "Software"), to deal in the Software without restriction,
6
+ including without limitation the rights to use, copy, modify, merge, publish, distribute,
7
+ sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
8
+ is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or
11
+ substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
14
+ BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
16
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,39 @@
1
- # Temporary Holding Version
1
+ # pocketbomb-dynamite
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The PocketBomb storefront framework (similar to Hydrogen).
4
+
5
+ ```ts
6
+ import { createRequestContext, createStorefrontClient, gql, MemoryCache } from "pocketbomb-dynamite";
7
+ import { createCustomerAccountClient } from "pocketbomb-dynamite/customer-account";
8
+
9
+ const cache = new MemoryCache();
10
+
11
+ export async function handle(request: Request) {
12
+ const context = createRequestContext({ request, cache }); // buyer IP, cookie session, cache
13
+ const storefront = createStorefrontClient({ url, privateKey, requestContext: context });
14
+ const customer = createCustomerAccountClient({
15
+ url, collection: "users", clientId, redirectUri, requestContext: context,
16
+ });
17
+
18
+ const path = new URL(request.url).pathname;
19
+ if (path === "/account/login") return customer.loginRedirect({ returnTo: "/account" });
20
+ if (path === "/account/callback") return customer.callbackRedirect();
21
+ if (path === "/account/logout") return customer.logoutRedirect();
22
+
23
+ const { products } = await storefront.query(PRODUCTS, { variables: { first: 10 } });
24
+ const response = new Response(render(products, customer.isLoggedIn()));
25
+ context.applyResponseHeaders(response.headers); // Set-Cookie + private caching
26
+ return response;
27
+ }
28
+
29
+ const PRODUCTS = gql<{ products: { nodes: Array<{ id: string; title: string }> } }, { first: number }>(
30
+ `query Products($first: Int) { products(first: $first) { nodes { id title } } }`,
31
+ );
32
+ ```
33
+
34
+ Entries: `pocketbomb-dynamite` (core + storefront client + GraphQL), `pocketbomb-dynamite/customer-account`,
35
+ `pocketbomb-dynamite/graphql` (gql.tada `graphql()` typed by `dynamite codegen`), `pocketbomb-dynamite/react`
36
+ (DynamiteProvider, useStorefront, useCustomer, useStorefrontQuery, Pagination, FileImage), `pocketbomb-dynamite/vite`
37
+ (env + private key guard, dev proxy, codegen, fuse) and `pocketbomb-dynamite/ts-plugin` (graphqlsp for the editor).
38
+
39
+ CLI: `dynamite codegen [--watch] [--fuse --seed <module>]`, `dynamite dev [--seed] [--entry server.ts]`, `dynamite info`.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import { run } from "../dist/cli/index.js";
3
+
4
+ run(process.argv.slice(2)).then(
5
+ (code) => process.exit(code),
6
+ (err) => {
7
+ console.error(err);
8
+ process.exit(1);
9
+ },
10
+ );
@@ -0,0 +1,22 @@
1
+ // \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+ exports.__toESM = __toESM;
@@ -0,0 +1,120 @@
1
+ const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
2
+ const require_constants = require("../shared/constants.cjs");
3
+ let node_fs = require("node:fs");
4
+ node_fs = require_runtime.__toESM(node_fs, 1);
5
+ let node_path = require("node:path");
6
+ node_path = require_runtime.__toESM(node_path, 1);
7
+ let _gql_tada_internal = require("@gql.tada/internal");
8
+ let graphql = require("graphql");
9
+ // src/cli/codegen.ts
10
+ /**
11
+ * Introspects the PocketBomb GraphQL API (`POST /api/graphql`; the introspection
12
+ * fields are not counted in the query limits).
13
+ *
14
+ * @throws {Error} for the HTTP and GraphQL errors.
15
+ */
16
+ async function introspect(options) {
17
+ const headers = { "content-type": "application/json" };
18
+ if (options.publicKey) headers["X-PB-Public-Key"] = options.publicKey;
19
+ const endpoint = options.url.replace(/\/+$/, "") + "/api/graphql";
20
+ const response = await (options.fetch || fetch)(endpoint, {
21
+ method: "POST",
22
+ headers,
23
+ body: JSON.stringify({ query: (0, graphql.getIntrospectionQuery)({ descriptions: true }) })
24
+ });
25
+ let body = null;
26
+ try {
27
+ body = await response.json();
28
+ } catch {
29
+ body = null;
30
+ }
31
+ if (!response.ok) throw new Error(`The GraphQL introspection failed (${response.status}): ${body?.message || JSON.stringify(body)}`);
32
+ if (body?.errors?.length || !body?.data?.__schema) throw new Error("The GraphQL introspection failed: " + (body?.errors?.map((e) => e.message).join("; ") || "no schema"));
33
+ return body.data;
34
+ }
35
+ /**
36
+ * Returns a copy of the introspection without the empty descriptions
37
+ * (the server reports "" for the undocumented fields).
38
+ */
39
+ function withoutEmptyDescriptions(value) {
40
+ if (Array.isArray(value)) return value.map(withoutEmptyDescriptions);
41
+ if (value && typeof value === "object") {
42
+ const result = {};
43
+ for (const [key, item] of Object.entries(value)) result[key] = key === "description" && item === "" ? null : withoutEmptyDescriptions(item);
44
+ return result;
45
+ }
46
+ return value;
47
+ }
48
+ /**
49
+ * Returns the GraphQL SDL of an introspection.
50
+ */
51
+ function introspectionToSDL(introspection) {
52
+ return (0, graphql.printSchema)((0, graphql.buildClientSchema)(withoutEmptyDescriptions(introspection))) + "\n";
53
+ }
54
+ /**
55
+ * Returns the `pocketbomb-env.d.ts` of an introspection: the gql.tada introspection type
56
+ * and the `pocketbomb-dynamite/graphql` schema augmentation used by `graphql()`.
57
+ */
58
+ function introspectionToEnv(introspection) {
59
+ return "/* eslint-disable */\n/* prettier-ignore */\n// Generated by `dynamite codegen` from the PocketBomb GraphQL API. Do not edit.\n\n/** The gql.tada introspection of the PocketBomb GraphQL schema. */\nexport type introspection = " + (0, _gql_tada_internal.preprocessIntrospection)((0, _gql_tada_internal.minifyIntrospection)(introspection)) + ";\n\ndeclare module \"pocketbomb-dynamite/graphql\" {\n interface PocketBombSchema {\n introspection: introspection;\n }\n}\n";
60
+ }
61
+ function writeIfChanged(file, content) {
62
+ if (node_fs.default.existsSync(file) && node_fs.default.readFileSync(file, "utf8") === content) return false;
63
+ node_fs.default.mkdirSync(node_path.default.dirname(file), { recursive: true });
64
+ node_fs.default.writeFileSync(file, content);
65
+ return true;
66
+ }
67
+ /**
68
+ * Writes the schema SDL and the gql.tada env of an introspection (only if they changed).
69
+ */
70
+ function generate(introspection, options = {}) {
71
+ const cwd = options.cwd || process.cwd();
72
+ const schemaFile = node_path.default.resolve(cwd, options.schemaFile || ".dynamite/schema.graphql");
73
+ const envFile = node_path.default.resolve(cwd, options.envFile || "pocketbomb-env.d.ts");
74
+ const schemaChanged = writeIfChanged(schemaFile, introspectionToSDL(introspection));
75
+ const envChanged = writeIfChanged(envFile, introspectionToEnv(introspection));
76
+ return {
77
+ schemaFile,
78
+ envFile,
79
+ changed: schemaChanged || envChanged
80
+ };
81
+ }
82
+ /**
83
+ * Introspects the API and writes the schema and the env (see {@link generate}).
84
+ */
85
+ async function codegen(options) {
86
+ return generate(await introspect(options), options);
87
+ }
88
+ /**
89
+ * Regenerates the schema and the env when the API schema changes (polling).
90
+ * Returns a function that stops the watcher.
91
+ */
92
+ function watchCodegen(options) {
93
+ let stopped = false;
94
+ let timer;
95
+ const tick = async () => {
96
+ try {
97
+ const result = await codegen(options);
98
+ if (result.changed) options.onChange?.(result);
99
+ } catch (err) {
100
+ options.onError?.(err);
101
+ }
102
+ if (!stopped) timer = setTimeout(tick, options.interval ?? 2e3);
103
+ };
104
+ tick();
105
+ return () => {
106
+ stopped = true;
107
+ clearTimeout(timer);
108
+ };
109
+ }
110
+ exports.DEFAULT_ENV_FILE = require_constants.DEFAULT_ENV_FILE;
111
+ exports.DEFAULT_SCHEMA_FILE = require_constants.DEFAULT_SCHEMA_FILE;
112
+ exports.codegen = codegen;
113
+ exports.generate = generate;
114
+ exports.introspect = introspect;
115
+ exports.introspectionToEnv = introspectionToEnv;
116
+ exports.introspectionToSDL = introspectionToSDL;
117
+ exports.watchCodegen = watchCodegen;
118
+ exports.withoutEmptyDescriptions = withoutEmptyDescriptions;
119
+
120
+ //# sourceMappingURL=codegen.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codegen.cjs","names":["getIntrospectionQuery","printSchema","buildClientSchema","preprocessIntrospection","minifyIntrospection","fs","path"],"sources":["../../src/cli/codegen.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { minifyIntrospection, preprocessIntrospection } from \"@gql.tada/internal\";\nimport {\n buildClientSchema,\n getIntrospectionQuery,\n printSchema,\n type IntrospectionQuery,\n} from \"graphql\";\n\nimport { DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE } from \"../shared/constants\";\n\nexport { DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE };\n\nexport interface IntrospectOptions {\n /** The PocketBomb URL. */\n url: string;\n /** The public API key (the GraphQL API requires client credentials). */\n publicKey?: string;\n fetch?: typeof fetch;\n}\n\n/**\n * Introspects the PocketBomb GraphQL API (`POST /api/graphql`; the introspection\n * fields are not counted in the query limits).\n *\n * @throws {Error} for the HTTP and GraphQL errors.\n */\nexport async function introspect(options: IntrospectOptions): Promise<IntrospectionQuery> {\n const headers: Record<string, string> = { \"content-type\": \"application/json\" };\n if (options.publicKey) {\n headers[\"X-PB-Public-Key\"] = options.publicKey;\n }\n\n const endpoint = options.url.replace(/\\/+$/, \"\") + \"/api/graphql\";\n const response = await (options.fetch || fetch)(endpoint, {\n method: \"POST\",\n headers,\n body: JSON.stringify({ query: getIntrospectionQuery({ descriptions: true }) }),\n });\n\n let body: any = null;\n try {\n body = await response.json();\n } catch {\n body = null;\n }\n\n if (!response.ok) {\n throw new Error(\n `The GraphQL introspection failed (${response.status}): ${body?.message || JSON.stringify(body)}`,\n );\n }\n if (body?.errors?.length || !body?.data?.__schema) {\n throw new Error(\n \"The GraphQL introspection failed: \" +\n (body?.errors?.map((e: any) => e.message).join(\"; \") || \"no schema\"),\n );\n }\n\n return body.data as IntrospectionQuery;\n}\n\n/**\n * Returns a copy of the introspection without the empty descriptions\n * (the server reports \"\" for the undocumented fields).\n */\nexport function withoutEmptyDescriptions<T>(value: T): T {\n if (Array.isArray(value)) {\n return value.map(withoutEmptyDescriptions) as T;\n }\n\n if (value && typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) {\n result[key] = key === \"description\" && item === \"\" ? null : withoutEmptyDescriptions(item);\n }\n return result as T;\n }\n\n return value;\n}\n\n/**\n * Returns the GraphQL SDL of an introspection.\n */\nexport function introspectionToSDL(introspection: IntrospectionQuery): string {\n return printSchema(buildClientSchema(withoutEmptyDescriptions(introspection))) + \"\\n\";\n}\n\n/**\n * Returns the `pocketbomb-env.d.ts` of an introspection: the gql.tada introspection type\n * and the `pocketbomb-dynamite/graphql` schema augmentation used by `graphql()`.\n */\nexport function introspectionToEnv(introspection: IntrospectionQuery): string {\n const type = preprocessIntrospection(minifyIntrospection(introspection));\n\n return (\n \"/* eslint-disable */\\n\" +\n \"/* prettier-ignore */\\n\" +\n \"// Generated by `dynamite codegen` from the PocketBomb GraphQL API. Do not edit.\\n\\n\" +\n \"/** The gql.tada introspection of the PocketBomb GraphQL schema. */\\n\" +\n \"export type introspection = \" +\n type +\n \";\\n\\n\" +\n 'declare module \"pocketbomb-dynamite/graphql\" {\\n' +\n \" interface PocketBombSchema {\\n\" +\n \" introspection: introspection;\\n\" +\n \" }\\n\" +\n \"}\\n\"\n );\n}\n\nexport interface GenerateOptions {\n /** The project root (default to the current working dir). */\n cwd?: string;\n /** The SDL output (default to `.dynamite/schema.graphql`). */\n schemaFile?: string;\n /** The gql.tada env output (default to `pocketbomb-env.d.ts`). */\n envFile?: string;\n}\n\nexport interface GenerateResult {\n schemaFile: string;\n envFile: string;\n /** Whether a file was (re)written. */\n changed: boolean;\n}\n\nfunction writeIfChanged(file: string, content: string): boolean {\n if (fs.existsSync(file) && fs.readFileSync(file, \"utf8\") === content) {\n return false;\n }\n\n fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.writeFileSync(file, content);\n\n return true;\n}\n\n/**\n * Writes the schema SDL and the gql.tada env of an introspection (only if they changed).\n */\nexport function generate(\n introspection: IntrospectionQuery,\n options: GenerateOptions = {},\n): GenerateResult {\n const cwd = options.cwd || process.cwd();\n const schemaFile = path.resolve(cwd, options.schemaFile || DEFAULT_SCHEMA_FILE);\n const envFile = path.resolve(cwd, options.envFile || DEFAULT_ENV_FILE);\n\n const schemaChanged = writeIfChanged(schemaFile, introspectionToSDL(introspection));\n const envChanged = writeIfChanged(envFile, introspectionToEnv(introspection));\n\n return { schemaFile, envFile, changed: schemaChanged || envChanged };\n}\n\nexport interface CodegenOptions extends IntrospectOptions, GenerateOptions {}\n\n/**\n * Introspects the API and writes the schema and the env (see {@link generate}).\n */\nexport async function codegen(options: CodegenOptions): Promise<GenerateResult> {\n return generate(await introspect(options), options);\n}\n\nexport interface WatchOptions extends CodegenOptions {\n /** The schema poll interval in ms (default to 2000). */\n interval?: number;\n /** Called after every generation that changed a file. */\n onChange?: (result: GenerateResult) => void;\n /** Called for the failed polls (the watcher keeps running). */\n onError?: (err: unknown) => void;\n}\n\n/**\n * Regenerates the schema and the env when the API schema changes (polling).\n * Returns a function that stops the watcher.\n */\nexport function watchCodegen(options: WatchOptions): () => void {\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const tick = async () => {\n try {\n const result = await codegen(options);\n if (result.changed) {\n options.onChange?.(result);\n }\n } catch (err) {\n options.onError?.(err);\n }\n\n if (!stopped) {\n timer = setTimeout(tick, options.interval ?? 2000);\n }\n };\n\n void tick();\n\n return () => {\n stopped = true;\n clearTimeout(timer);\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import { DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE } from "../shared/constants.cjs";
2
+ import { IntrospectionQuery } from "graphql";
3
+ export interface IntrospectOptions {
4
+ /** The PocketBomb URL. */
5
+ url: string;
6
+ /** The public API key (the GraphQL API requires client credentials). */
7
+ publicKey?: string;
8
+ fetch?: typeof fetch;
9
+ }
10
+ /**
11
+ * Introspects the PocketBomb GraphQL API (`POST /api/graphql`; the introspection
12
+ * fields are not counted in the query limits).
13
+ *
14
+ * @throws {Error} for the HTTP and GraphQL errors.
15
+ */
16
+ export declare function introspect(options: IntrospectOptions): Promise<IntrospectionQuery>;
17
+ /**
18
+ * Returns a copy of the introspection without the empty descriptions
19
+ * (the server reports "" for the undocumented fields).
20
+ */
21
+ export declare function withoutEmptyDescriptions<T>(value: T): T;
22
+ /**
23
+ * Returns the GraphQL SDL of an introspection.
24
+ */
25
+ export declare function introspectionToSDL(introspection: IntrospectionQuery): string;
26
+ /**
27
+ * Returns the `pocketbomb-env.d.ts` of an introspection: the gql.tada introspection type
28
+ * and the `pocketbomb-dynamite/graphql` schema augmentation used by `graphql()`.
29
+ */
30
+ export declare function introspectionToEnv(introspection: IntrospectionQuery): string;
31
+ export interface GenerateOptions {
32
+ /** The project root (default to the current working dir). */
33
+ cwd?: string;
34
+ /** The SDL output (default to `.dynamite/schema.graphql`). */
35
+ schemaFile?: string;
36
+ /** The gql.tada env output (default to `pocketbomb-env.d.ts`). */
37
+ envFile?: string;
38
+ }
39
+ export interface GenerateResult {
40
+ schemaFile: string;
41
+ envFile: string;
42
+ /** Whether a file was (re)written. */
43
+ changed: boolean;
44
+ }
45
+ /**
46
+ * Writes the schema SDL and the gql.tada env of an introspection (only if they changed).
47
+ */
48
+ export declare function generate(introspection: IntrospectionQuery, options?: GenerateOptions): GenerateResult;
49
+ export interface CodegenOptions extends IntrospectOptions, GenerateOptions {}
50
+ /**
51
+ * Introspects the API and writes the schema and the env (see {@link generate}).
52
+ */
53
+ export declare function codegen(options: CodegenOptions): Promise<GenerateResult>;
54
+ export interface WatchOptions extends CodegenOptions {
55
+ /** The schema poll interval in ms (default to 2000). */
56
+ interval?: number;
57
+ /** Called after every generation that changed a file. */
58
+ onChange?: (result: GenerateResult) => void;
59
+ /** Called for the failed polls (the watcher keeps running). */
60
+ onError?: (err: unknown) => void;
61
+ }
62
+ /**
63
+ * Regenerates the schema and the env when the API schema changes (polling).
64
+ * Returns a function that stops the watcher.
65
+ */
66
+ export declare function watchCodegen(options: WatchOptions): () => void;
67
+ export { DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE };
68
+ //# sourceMappingURL=codegen.d.cts.map
@@ -0,0 +1,68 @@
1
+ import { DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE } from "../shared/constants.js";
2
+ import { IntrospectionQuery } from "graphql";
3
+ export interface IntrospectOptions {
4
+ /** The PocketBomb URL. */
5
+ url: string;
6
+ /** The public API key (the GraphQL API requires client credentials). */
7
+ publicKey?: string;
8
+ fetch?: typeof fetch;
9
+ }
10
+ /**
11
+ * Introspects the PocketBomb GraphQL API (`POST /api/graphql`; the introspection
12
+ * fields are not counted in the query limits).
13
+ *
14
+ * @throws {Error} for the HTTP and GraphQL errors.
15
+ */
16
+ export declare function introspect(options: IntrospectOptions): Promise<IntrospectionQuery>;
17
+ /**
18
+ * Returns a copy of the introspection without the empty descriptions
19
+ * (the server reports "" for the undocumented fields).
20
+ */
21
+ export declare function withoutEmptyDescriptions<T>(value: T): T;
22
+ /**
23
+ * Returns the GraphQL SDL of an introspection.
24
+ */
25
+ export declare function introspectionToSDL(introspection: IntrospectionQuery): string;
26
+ /**
27
+ * Returns the `pocketbomb-env.d.ts` of an introspection: the gql.tada introspection type
28
+ * and the `pocketbomb-dynamite/graphql` schema augmentation used by `graphql()`.
29
+ */
30
+ export declare function introspectionToEnv(introspection: IntrospectionQuery): string;
31
+ export interface GenerateOptions {
32
+ /** The project root (default to the current working dir). */
33
+ cwd?: string;
34
+ /** The SDL output (default to `.dynamite/schema.graphql`). */
35
+ schemaFile?: string;
36
+ /** The gql.tada env output (default to `pocketbomb-env.d.ts`). */
37
+ envFile?: string;
38
+ }
39
+ export interface GenerateResult {
40
+ schemaFile: string;
41
+ envFile: string;
42
+ /** Whether a file was (re)written. */
43
+ changed: boolean;
44
+ }
45
+ /**
46
+ * Writes the schema SDL and the gql.tada env of an introspection (only if they changed).
47
+ */
48
+ export declare function generate(introspection: IntrospectionQuery, options?: GenerateOptions): GenerateResult;
49
+ export interface CodegenOptions extends IntrospectOptions, GenerateOptions {}
50
+ /**
51
+ * Introspects the API and writes the schema and the env (see {@link generate}).
52
+ */
53
+ export declare function codegen(options: CodegenOptions): Promise<GenerateResult>;
54
+ export interface WatchOptions extends CodegenOptions {
55
+ /** The schema poll interval in ms (default to 2000). */
56
+ interval?: number;
57
+ /** Called after every generation that changed a file. */
58
+ onChange?: (result: GenerateResult) => void;
59
+ /** Called for the failed polls (the watcher keeps running). */
60
+ onError?: (err: unknown) => void;
61
+ }
62
+ /**
63
+ * Regenerates the schema and the env when the API schema changes (polling).
64
+ * Returns a function that stops the watcher.
65
+ */
66
+ export declare function watchCodegen(options: WatchOptions): () => void;
67
+ export { DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE };
68
+ //# sourceMappingURL=codegen.d.ts.map
@@ -0,0 +1,109 @@
1
+ import { DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE } from "../shared/constants.js";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { minifyIntrospection, preprocessIntrospection } from "@gql.tada/internal";
5
+ import { buildClientSchema, getIntrospectionQuery, printSchema } from "graphql";
6
+ // src/cli/codegen.ts
7
+ /**
8
+ * Introspects the PocketBomb GraphQL API (`POST /api/graphql`; the introspection
9
+ * fields are not counted in the query limits).
10
+ *
11
+ * @throws {Error} for the HTTP and GraphQL errors.
12
+ */
13
+ async function introspect(options) {
14
+ const headers = { "content-type": "application/json" };
15
+ if (options.publicKey) headers["X-PB-Public-Key"] = options.publicKey;
16
+ const endpoint = options.url.replace(/\/+$/, "") + "/api/graphql";
17
+ const response = await (options.fetch || fetch)(endpoint, {
18
+ method: "POST",
19
+ headers,
20
+ body: JSON.stringify({ query: getIntrospectionQuery({ descriptions: true }) })
21
+ });
22
+ let body = null;
23
+ try {
24
+ body = await response.json();
25
+ } catch {
26
+ body = null;
27
+ }
28
+ if (!response.ok) throw new Error(`The GraphQL introspection failed (${response.status}): ${body?.message || JSON.stringify(body)}`);
29
+ if (body?.errors?.length || !body?.data?.__schema) throw new Error("The GraphQL introspection failed: " + (body?.errors?.map((e) => e.message).join("; ") || "no schema"));
30
+ return body.data;
31
+ }
32
+ /**
33
+ * Returns a copy of the introspection without the empty descriptions
34
+ * (the server reports "" for the undocumented fields).
35
+ */
36
+ function withoutEmptyDescriptions(value) {
37
+ if (Array.isArray(value)) return value.map(withoutEmptyDescriptions);
38
+ if (value && typeof value === "object") {
39
+ const result = {};
40
+ for (const [key, item] of Object.entries(value)) result[key] = key === "description" && item === "" ? null : withoutEmptyDescriptions(item);
41
+ return result;
42
+ }
43
+ return value;
44
+ }
45
+ /**
46
+ * Returns the GraphQL SDL of an introspection.
47
+ */
48
+ function introspectionToSDL(introspection) {
49
+ return printSchema(buildClientSchema(withoutEmptyDescriptions(introspection))) + "\n";
50
+ }
51
+ /**
52
+ * Returns the `pocketbomb-env.d.ts` of an introspection: the gql.tada introspection type
53
+ * and the `pocketbomb-dynamite/graphql` schema augmentation used by `graphql()`.
54
+ */
55
+ function introspectionToEnv(introspection) {
56
+ return "/* eslint-disable */\n/* prettier-ignore */\n// Generated by `dynamite codegen` from the PocketBomb GraphQL API. Do not edit.\n\n/** The gql.tada introspection of the PocketBomb GraphQL schema. */\nexport type introspection = " + preprocessIntrospection(minifyIntrospection(introspection)) + ";\n\ndeclare module \"pocketbomb-dynamite/graphql\" {\n interface PocketBombSchema {\n introspection: introspection;\n }\n}\n";
57
+ }
58
+ function writeIfChanged(file, content) {
59
+ if (fs.existsSync(file) && fs.readFileSync(file, "utf8") === content) return false;
60
+ fs.mkdirSync(path.dirname(file), { recursive: true });
61
+ fs.writeFileSync(file, content);
62
+ return true;
63
+ }
64
+ /**
65
+ * Writes the schema SDL and the gql.tada env of an introspection (only if they changed).
66
+ */
67
+ function generate(introspection, options = {}) {
68
+ const cwd = options.cwd || process.cwd();
69
+ const schemaFile = path.resolve(cwd, options.schemaFile || ".dynamite/schema.graphql");
70
+ const envFile = path.resolve(cwd, options.envFile || "pocketbomb-env.d.ts");
71
+ const schemaChanged = writeIfChanged(schemaFile, introspectionToSDL(introspection));
72
+ const envChanged = writeIfChanged(envFile, introspectionToEnv(introspection));
73
+ return {
74
+ schemaFile,
75
+ envFile,
76
+ changed: schemaChanged || envChanged
77
+ };
78
+ }
79
+ /**
80
+ * Introspects the API and writes the schema and the env (see {@link generate}).
81
+ */
82
+ async function codegen(options) {
83
+ return generate(await introspect(options), options);
84
+ }
85
+ /**
86
+ * Regenerates the schema and the env when the API schema changes (polling).
87
+ * Returns a function that stops the watcher.
88
+ */
89
+ function watchCodegen(options) {
90
+ let stopped = false;
91
+ let timer;
92
+ const tick = async () => {
93
+ try {
94
+ const result = await codegen(options);
95
+ if (result.changed) options.onChange?.(result);
96
+ } catch (err) {
97
+ options.onError?.(err);
98
+ }
99
+ if (!stopped) timer = setTimeout(tick, options.interval ?? 2e3);
100
+ };
101
+ tick();
102
+ return () => {
103
+ stopped = true;
104
+ clearTimeout(timer);
105
+ };
106
+ }
107
+ export { DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE, codegen, generate, introspect, introspectionToEnv, introspectionToSDL, watchCodegen, withoutEmptyDescriptions };
108
+
109
+ //# sourceMappingURL=codegen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codegen.js","names":[],"sources":["../../src/cli/codegen.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { minifyIntrospection, preprocessIntrospection } from \"@gql.tada/internal\";\nimport {\n buildClientSchema,\n getIntrospectionQuery,\n printSchema,\n type IntrospectionQuery,\n} from \"graphql\";\n\nimport { DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE } from \"../shared/constants\";\n\nexport { DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE };\n\nexport interface IntrospectOptions {\n /** The PocketBomb URL. */\n url: string;\n /** The public API key (the GraphQL API requires client credentials). */\n publicKey?: string;\n fetch?: typeof fetch;\n}\n\n/**\n * Introspects the PocketBomb GraphQL API (`POST /api/graphql`; the introspection\n * fields are not counted in the query limits).\n *\n * @throws {Error} for the HTTP and GraphQL errors.\n */\nexport async function introspect(options: IntrospectOptions): Promise<IntrospectionQuery> {\n const headers: Record<string, string> = { \"content-type\": \"application/json\" };\n if (options.publicKey) {\n headers[\"X-PB-Public-Key\"] = options.publicKey;\n }\n\n const endpoint = options.url.replace(/\\/+$/, \"\") + \"/api/graphql\";\n const response = await (options.fetch || fetch)(endpoint, {\n method: \"POST\",\n headers,\n body: JSON.stringify({ query: getIntrospectionQuery({ descriptions: true }) }),\n });\n\n let body: any = null;\n try {\n body = await response.json();\n } catch {\n body = null;\n }\n\n if (!response.ok) {\n throw new Error(\n `The GraphQL introspection failed (${response.status}): ${body?.message || JSON.stringify(body)}`,\n );\n }\n if (body?.errors?.length || !body?.data?.__schema) {\n throw new Error(\n \"The GraphQL introspection failed: \" +\n (body?.errors?.map((e: any) => e.message).join(\"; \") || \"no schema\"),\n );\n }\n\n return body.data as IntrospectionQuery;\n}\n\n/**\n * Returns a copy of the introspection without the empty descriptions\n * (the server reports \"\" for the undocumented fields).\n */\nexport function withoutEmptyDescriptions<T>(value: T): T {\n if (Array.isArray(value)) {\n return value.map(withoutEmptyDescriptions) as T;\n }\n\n if (value && typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) {\n result[key] = key === \"description\" && item === \"\" ? null : withoutEmptyDescriptions(item);\n }\n return result as T;\n }\n\n return value;\n}\n\n/**\n * Returns the GraphQL SDL of an introspection.\n */\nexport function introspectionToSDL(introspection: IntrospectionQuery): string {\n return printSchema(buildClientSchema(withoutEmptyDescriptions(introspection))) + \"\\n\";\n}\n\n/**\n * Returns the `pocketbomb-env.d.ts` of an introspection: the gql.tada introspection type\n * and the `pocketbomb-dynamite/graphql` schema augmentation used by `graphql()`.\n */\nexport function introspectionToEnv(introspection: IntrospectionQuery): string {\n const type = preprocessIntrospection(minifyIntrospection(introspection));\n\n return (\n \"/* eslint-disable */\\n\" +\n \"/* prettier-ignore */\\n\" +\n \"// Generated by `dynamite codegen` from the PocketBomb GraphQL API. Do not edit.\\n\\n\" +\n \"/** The gql.tada introspection of the PocketBomb GraphQL schema. */\\n\" +\n \"export type introspection = \" +\n type +\n \";\\n\\n\" +\n 'declare module \"pocketbomb-dynamite/graphql\" {\\n' +\n \" interface PocketBombSchema {\\n\" +\n \" introspection: introspection;\\n\" +\n \" }\\n\" +\n \"}\\n\"\n );\n}\n\nexport interface GenerateOptions {\n /** The project root (default to the current working dir). */\n cwd?: string;\n /** The SDL output (default to `.dynamite/schema.graphql`). */\n schemaFile?: string;\n /** The gql.tada env output (default to `pocketbomb-env.d.ts`). */\n envFile?: string;\n}\n\nexport interface GenerateResult {\n schemaFile: string;\n envFile: string;\n /** Whether a file was (re)written. */\n changed: boolean;\n}\n\nfunction writeIfChanged(file: string, content: string): boolean {\n if (fs.existsSync(file) && fs.readFileSync(file, \"utf8\") === content) {\n return false;\n }\n\n fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.writeFileSync(file, content);\n\n return true;\n}\n\n/**\n * Writes the schema SDL and the gql.tada env of an introspection (only if they changed).\n */\nexport function generate(\n introspection: IntrospectionQuery,\n options: GenerateOptions = {},\n): GenerateResult {\n const cwd = options.cwd || process.cwd();\n const schemaFile = path.resolve(cwd, options.schemaFile || DEFAULT_SCHEMA_FILE);\n const envFile = path.resolve(cwd, options.envFile || DEFAULT_ENV_FILE);\n\n const schemaChanged = writeIfChanged(schemaFile, introspectionToSDL(introspection));\n const envChanged = writeIfChanged(envFile, introspectionToEnv(introspection));\n\n return { schemaFile, envFile, changed: schemaChanged || envChanged };\n}\n\nexport interface CodegenOptions extends IntrospectOptions, GenerateOptions {}\n\n/**\n * Introspects the API and writes the schema and the env (see {@link generate}).\n */\nexport async function codegen(options: CodegenOptions): Promise<GenerateResult> {\n return generate(await introspect(options), options);\n}\n\nexport interface WatchOptions extends CodegenOptions {\n /** The schema poll interval in ms (default to 2000). */\n interval?: number;\n /** Called after every generation that changed a file. */\n onChange?: (result: GenerateResult) => void;\n /** Called for the failed polls (the watcher keeps running). */\n onError?: (err: unknown) => void;\n}\n\n/**\n * Regenerates the schema and the env when the API schema changes (polling).\n * Returns a function that stops the watcher.\n */\nexport function watchCodegen(options: WatchOptions): () => void {\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const tick = async () => {\n try {\n const result = await codegen(options);\n if (result.changed) {\n options.onChange?.(result);\n }\n } catch (err) {\n options.onError?.(err);\n }\n\n if (!stopped) {\n timer = setTimeout(tick, options.interval ?? 2000);\n }\n };\n\n void tick();\n\n return () => {\n stopped = true;\n clearTimeout(timer);\n };\n}\n"],"mappings":";;;;;;;;;;;;AA4BA,eAAsB,WAAW,SAAyD;CACxF,MAAM,UAAkC,EAAE,gBAAgB,mBAAmB;CAC7E,IAAI,QAAQ,WACV,QAAQ,qBAAqB,QAAQ;CAGvC,MAAM,WAAW,QAAQ,IAAI,QAAQ,QAAQ,EAAE,IAAI;CACnD,MAAM,WAAW,OAAO,QAAQ,SAAS,MAAA,CAAO,UAAU;EACxD,QAAQ;EACR;EACA,MAAM,KAAK,UAAU,EAAE,OAAO,sBAAsB,EAAE,cAAc,KAAK,CAAC,EAAE,CAAC;CAC/E,CAAC;CAED,IAAI,OAAY;CAChB,IAAI;EACF,OAAO,MAAM,SAAS,KAAK;CAC7B,QAAQ;EACN,OAAO;CACT;CAEA,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MACR,qCAAqC,SAAS,OAAO,KAAK,MAAM,WAAW,KAAK,UAAU,IAAI,GAChG;CAEF,IAAI,MAAM,QAAQ,UAAU,CAAC,MAAM,MAAM,UACvC,MAAM,IAAI,MACR,wCACG,MAAM,QAAQ,KAAK,MAAW,EAAE,OAAO,CAAC,CAAC,KAAK,IAAI,KAAK,YAC5D;CAGF,OAAO,KAAK;AACd;;;;;AAMA,SAAgB,yBAA4B,OAAa;CACvD,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,MAAM,IAAI,wBAAwB;CAG3C,IAAI,SAAS,OAAO,UAAU,UAAU;EACtC,MAAM,SAAkC,CAAC;EACzC,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,KAAK,GAC5C,OAAO,OAAO,QAAQ,iBAAiB,SAAS,KAAK,OAAO,yBAAyB,IAAI;EAE3F,OAAO;CACT;CAEA,OAAO;AACT;;;;AAKA,SAAgB,mBAAmB,eAA2C;CAC5E,OAAO,YAAY,kBAAkB,yBAAyB,aAAa,CAAC,CAAC,IAAI;AACnF;;;;;AAMA,SAAgB,mBAAmB,eAA2C;CAG5E,OACE,uOAHW,wBAAwB,oBAAoB,aAAa,CAQjE,IACH;AAOJ;AAkBA,SAAS,eAAe,MAAc,SAA0B;CAC9D,IAAI,GAAG,WAAW,IAAI,KAAK,GAAG,aAAa,MAAM,MAAM,MAAM,SAC3D,OAAO;CAGT,GAAG,UAAU,KAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;CACpD,GAAG,cAAc,MAAM,OAAO;CAE9B,OAAO;AACT;;;;AAKA,SAAgB,SACd,eACA,UAA2B,CAAC,GACZ;CAChB,MAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;CACvC,MAAM,aAAa,KAAK,QAAQ,KAAK,QAAQ,cAAA,0BAAiC;CAC9E,MAAM,UAAU,KAAK,QAAQ,KAAK,QAAQ,WAAA,qBAA2B;CAErE,MAAM,gBAAgB,eAAe,YAAY,mBAAmB,aAAa,CAAC;CAClF,MAAM,aAAa,eAAe,SAAS,mBAAmB,aAAa,CAAC;CAE5E,OAAO;EAAE;EAAY;EAAS,SAAS,iBAAiB;CAAW;AACrE;;;;AAOA,eAAsB,QAAQ,SAAkD;CAC9E,OAAO,SAAS,MAAM,WAAW,OAAO,GAAG,OAAO;AACpD;;;;;AAeA,SAAgB,aAAa,SAAmC;CAC9D,IAAI,UAAU;CACd,IAAI;CAEJ,MAAM,OAAO,YAAY;EACvB,IAAI;GACF,MAAM,SAAS,MAAM,QAAQ,OAAO;GACpC,IAAI,OAAO,SACT,QAAQ,WAAW,MAAM;EAE7B,SAAS,KAAK;GACZ,QAAQ,UAAU,GAAG;EACvB;EAEA,IAAI,CAAC,SACH,QAAQ,WAAW,MAAM,QAAQ,YAAY,GAAI;CAErD;CAEA,KAAU;CAEV,aAAa;EACX,UAAU;EACV,aAAa,KAAK;CACpB;AACF"}