pocketbomb-dynamite 0.2.0 → 0.3.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/README.md +24 -1
- package/dist/cli/check.cjs +160 -0
- package/dist/cli/check.cjs.map +1 -0
- package/dist/cli/check.d.cts +21 -0
- package/dist/cli/check.d.ts +21 -0
- package/dist/cli/check.js +157 -0
- package/dist/cli/check.js.map +1 -0
- package/dist/cli/codegen.cjs +2 -1
- package/dist/cli/codegen.cjs.map +1 -1
- package/dist/cli/codegen.js +3 -2
- package/dist/cli/codegen.js.map +1 -1
- package/dist/cli/index.cjs +32 -0
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +2 -1
- package/dist/cli/index.d.ts +2 -1
- package/dist/cli/index.js +32 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/customer-account/customer-account.cjs +55 -0
- package/dist/customer-account/customer-account.cjs.map +1 -1
- package/dist/customer-account/customer-account.d.cts +33 -0
- package/dist/customer-account/customer-account.d.ts +33 -0
- package/dist/customer-account/customer-account.js +52 -1
- package/dist/customer-account/customer-account.js.map +1 -1
- package/dist/customer-account/index.cjs +4 -0
- package/dist/customer-account/index.d.cts +2 -2
- package/dist/customer-account/index.d.ts +2 -2
- package/dist/customer-account/index.js +1 -1
- package/dist/react/hooks.cjs +37 -5
- package/dist/react/hooks.cjs.map +1 -1
- package/dist/react/hooks.d.cts +12 -2
- package/dist/react/hooks.d.ts +12 -2
- package/dist/react/hooks.js +37 -5
- package/dist/react/hooks.js.map +1 -1
- package/dist/react/index.d.cts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/ts-plugin/index.d.cts +2 -1
- package/dist/vite/index.cjs +6 -2
- package/dist/vite/index.cjs.map +1 -1
- package/dist/vite/index.js +6 -2
- package/dist/vite/index.js.map +1 -1
- package/package.json +7 -3
package/dist/cli/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["DEFAULT_SCHEMA_FILE","DEFAULT_ENV_FILE","fs","path","parseArgs","codegen","watchCodegen","spawn"],"sources":["../../src/cli/index.ts"],"sourcesContent":["// The `dynamite` CLI (Node.js only): codegen, dev, info.\nimport { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport type { Fuse } from \"pocketbomb-fuse/node\";\nimport {\n codegen,\n DEFAULT_ENV_FILE,\n DEFAULT_SCHEMA_FILE,\n watchCodegen,\n type GenerateResult,\n} from \"./codegen\";\n\nconst HELP = `dynamite <command> [options]\n\nCommands:\n codegen Introspect the PocketBomb GraphQL API and write the schema + the gql.tada env\n --url <url> (default $POCKETBOMB_URL)\n --public-key <key> (default $POCKETBOMB_PUBLIC_KEY)\n --schema <file> (default ${DEFAULT_SCHEMA_FILE})\n --env <file> (default ${DEFAULT_ENV_FILE})\n --watch [--interval <ms>]\n --fuse [--seed <module>] start a local server with fuse for the codegen\n dev Start fuse (unless $POCKETBOMB_URL is set), the codegen watcher and the app\n --seed <module> --data-dir <dir>\n --entry <file> run a custom server entry with Node.js (default: the Vite dev server)\n --port <port> the Vite dev server port\n info Print the PocketBomb environment and the generated files status\n`;\n\ntype Io = { log: (line: string) => void; error: (line: string) => void };\n\nconst defaultIo: Io = {\n log: (line) => process.stdout.write(line + \"\\n\"),\n error: (line) => process.stderr.write(line + \"\\n\"),\n};\n\nfunction readPackageVersion(): string {\n try {\n const pkg = JSON.parse(fs.readFileSync(new URL(\"../../package.json\", import.meta.url), \"utf8\"));\n return pkg.version;\n } catch {\n return \"unknown\";\n }\n}\n\nasync function startFuseFromFlags(\n flags: { seed?: string; \"data-dir\"?: string },\n io: Io,\n): Promise<Fuse> {\n const { startFuse } = await import(\"pocketbomb-fuse/node\");\n const fuse = await startFuse({\n seed: flags.seed,\n dataDir: flags[\"data-dir\"],\n log: (line) => io.error(\"[fuse] \" + line),\n });\n Object.assign(process.env, fuse.env);\n io.error(\"[fuse] PocketBomb running on \" + fuse.url);\n return fuse;\n}\n\nfunction describeResult(result: GenerateResult, cwd: string): string {\n return (\n (result.changed ? \"generated \" : \"up to date: \") +\n path.relative(cwd, result.schemaFile) +\n \", \" +\n path.relative(cwd, result.envFile)\n );\n}\n\nasync function runCodegen(argv: Array<string>, io: Io): Promise<number> {\n const { values } = parseArgs({\n args: argv,\n options: {\n url: { type: \"string\" },\n \"public-key\": { type: \"string\" },\n schema: { type: \"string\" },\n env: { type: \"string\" },\n watch: { type: \"boolean\" },\n interval: { type: \"string\" },\n fuse: { type: \"boolean\" },\n seed: { type: \"string\" },\n cwd: { type: \"string\" },\n },\n });\n\n const cwd = path.resolve(values.cwd || process.cwd());\n const fuse = values.fuse ? await startFuseFromFlags(values, io) : null;\n\n try {\n const url = values.url || process.env.POCKETBOMB_URL;\n if (!url) {\n io.error(\"dynamite codegen: missing --url (or $POCKETBOMB_URL)\");\n return 2;\n }\n\n const options = {\n url,\n publicKey: values[\"public-key\"] || process.env.POCKETBOMB_PUBLIC_KEY,\n cwd,\n schemaFile: values.schema,\n envFile: values.env,\n };\n\n if (!values.watch) {\n io.log(describeResult(await codegen(options), cwd));\n return 0;\n }\n\n const stop = watchCodegen({\n ...options,\n interval: values.interval ? parseInt(values.interval, 10) : undefined,\n onChange: (result) => io.log(describeResult(result, cwd)),\n onError: (err) => io.error(\"codegen: \" + String((err as Error)?.message || err)),\n });\n await new Promise<void>((resolve) => {\n process.once(\"SIGINT\", resolve);\n process.once(\"SIGTERM\", resolve);\n });\n stop();\n return 0;\n } finally {\n await fuse?.stop();\n }\n}\n\nasync function runDev(argv: Array<string>, io: Io): Promise<number> {\n const { values } = parseArgs({\n args: argv,\n options: {\n seed: { type: \"string\" },\n \"data-dir\": { type: \"string\" },\n entry: { type: \"string\" },\n port: { type: \"string\" },\n \"no-codegen\": { type: \"boolean\" },\n },\n });\n\n const cwd = process.cwd();\n const fuse = process.env.POCKETBOMB_URL ? null : await startFuseFromFlags(values, io);\n\n let stopCodegen = () => {};\n if (!values[\"no-codegen\"] && process.env.POCKETBOMB_PUBLIC_KEY) {\n stopCodegen = watchCodegen({\n url: process.env.POCKETBOMB_URL!,\n publicKey: process.env.POCKETBOMB_PUBLIC_KEY,\n cwd,\n onChange: (result) => io.error(\"[codegen] \" + describeResult(result, cwd)),\n onError: (err) => io.error(\"[codegen] \" + String((err as Error)?.message || err)),\n });\n }\n\n const cleanup = async () => {\n stopCodegen();\n await fuse?.stop();\n };\n\n if (values.entry) {\n const child = spawn(process.execPath, [path.resolve(cwd, values.entry)], {\n stdio: \"inherit\",\n env: { ...process.env, NODE_ENV: process.env.NODE_ENV || \"development\" },\n });\n const forward = (signal: NodeJS.Signals) => child.kill(signal);\n process.on(\"SIGINT\", forward);\n process.on(\"SIGTERM\", forward);\n\n const code: number = await new Promise((resolve) => child.on(\"exit\", (c) => resolve(c ?? 1)));\n await cleanup();\n return code;\n }\n\n const { createServer } = await import(\"vite\");\n const server = await createServer({\n root: cwd,\n server: values.port ? { port: parseInt(values.port, 10) } : undefined,\n });\n await server.listen();\n server.printUrls();\n\n await new Promise<void>((resolve) => {\n process.once(\"SIGINT\", resolve);\n process.once(\"SIGTERM\", resolve);\n });\n await server.close();\n await cleanup();\n return 0;\n}\n\nasync function runInfo(io: Io): Promise<number> {\n const cwd = process.cwd();\n const mask = (v?: string) => (v ? v.substring(0, 6) + \"…\" : \"(not set)\");\n\n io.log(\"pocketbomb-dynamite \" + readPackageVersion());\n io.log(\"node \" + process.version);\n io.log(\"POCKETBOMB_URL \" + (process.env.POCKETBOMB_URL || \"(not set)\"));\n io.log(\"POCKETBOMB_PUBLIC_KEY \" + mask(process.env.POCKETBOMB_PUBLIC_KEY));\n io.log(\"POCKETBOMB_PRIVATE_KEY \" + (process.env.POCKETBOMB_PRIVATE_KEY ? \"(set)\" : \"(not set)\"));\n\n for (const file of [DEFAULT_SCHEMA_FILE, DEFAULT_ENV_FILE]) {\n const full = path.resolve(cwd, file);\n io.log(\n file.padEnd(23) +\n (fs.existsSync(full) ? \"generated \" + fs.statSync(full).mtime.toISOString() : \"missing\"),\n );\n }\n\n try {\n const { resolveFuseBinary } = await import(\"pocketbomb-fuse/node\");\n const resolved = resolveFuseBinary({ log: () => {} });\n io.log(\"fuse binary \" + resolved.bin + \" (\" + resolved.source + \")\");\n } catch (err) {\n io.log(\"fuse binary \" + String((err as Error).message));\n }\n\n return 0;\n}\n\n/**\n * Runs the CLI with the provided arguments (without the node and script paths).\n */\nexport async function run(argv: Array<string>, io: Io = defaultIo): Promise<number> {\n const [command, ...rest] = argv;\n\n switch (command) {\n case \"codegen\":\n return runCodegen(rest, io);\n case \"dev\":\n return runDev(rest, io);\n case \"info\":\n return runInfo(io);\n case \"--version\":\n case \"-v\":\n io.log(readPackageVersion());\n return 0;\n case undefined:\n case \"help\":\n case \"--help\":\n case \"-h\":\n io.log(HELP);\n return 0;\n default:\n io.error(\"Unknown command: \" + command + \"\\n\\n\" + HELP);\n return 2;\n }\n}\n\nexport * from \"./codegen\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["DEFAULT_SCHEMA_FILE","DEFAULT_ENV_FILE","fs","path","parseArgs","codegen","watchCodegen","spawn","check"],"sources":["../../src/cli/index.ts"],"sourcesContent":["// The `dynamite` CLI (Node.js only): codegen, dev, info.\nimport { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport type { Fuse } from \"pocketbomb-fuse/node\";\nimport { check } from \"./check\";\nimport {\n codegen,\n DEFAULT_ENV_FILE,\n DEFAULT_SCHEMA_FILE,\n watchCodegen,\n type GenerateResult,\n} from \"./codegen\";\n\nconst HELP = `dynamite <command> [options]\n\nCommands:\n codegen Introspect the PocketBomb GraphQL API and write the schema + the gql.tada env\n --url <url> (default $POCKETBOMB_URL)\n --public-key <key> (default $POCKETBOMB_PUBLIC_KEY)\n --schema <file> (default ${DEFAULT_SCHEMA_FILE})\n --env <file> (default ${DEFAULT_ENV_FILE})\n --watch [--interval <ms>]\n --fuse [--seed <module>] start a local server with fuse for the codegen\n dev Start fuse (unless $POCKETBOMB_URL is set), the codegen watcher and the app\n --seed <module> --data-dir <dir>\n --entry <file> run a custom server entry with Node.js (default: the Vite dev server)\n --port <port> the Vite dev server port\n check Validate the graphql() documents of the project against the schema\n --schema <file> (default ${DEFAULT_SCHEMA_FILE})\n info Print the PocketBomb environment and the generated files status\n`;\n\ntype Io = { log: (line: string) => void; error: (line: string) => void };\n\nconst defaultIo: Io = {\n log: (line) => process.stdout.write(line + \"\\n\"),\n error: (line) => process.stderr.write(line + \"\\n\"),\n};\n\nfunction readPackageVersion(): string {\n try {\n const pkg = JSON.parse(fs.readFileSync(new URL(\"../../package.json\", import.meta.url), \"utf8\"));\n return pkg.version;\n } catch {\n return \"unknown\";\n }\n}\n\nasync function startFuseFromFlags(\n flags: { seed?: string; \"data-dir\"?: string },\n io: Io,\n): Promise<Fuse> {\n const { startFuse } = await import(\"pocketbomb-fuse/node\");\n const fuse = await startFuse({\n seed: flags.seed,\n dataDir: flags[\"data-dir\"],\n log: (line) => io.error(\"[fuse] \" + line),\n });\n Object.assign(process.env, fuse.env);\n io.error(\"[fuse] PocketBomb running on \" + fuse.url);\n return fuse;\n}\n\nfunction describeResult(result: GenerateResult, cwd: string): string {\n return (\n (result.changed ? \"generated \" : \"up to date: \") +\n path.relative(cwd, result.schemaFile) +\n \", \" +\n path.relative(cwd, result.envFile)\n );\n}\n\nasync function runCodegen(argv: Array<string>, io: Io): Promise<number> {\n const { values } = parseArgs({\n args: argv,\n options: {\n url: { type: \"string\" },\n \"public-key\": { type: \"string\" },\n schema: { type: \"string\" },\n env: { type: \"string\" },\n watch: { type: \"boolean\" },\n interval: { type: \"string\" },\n fuse: { type: \"boolean\" },\n seed: { type: \"string\" },\n cwd: { type: \"string\" },\n },\n });\n\n const cwd = path.resolve(values.cwd || process.cwd());\n const fuse = values.fuse ? await startFuseFromFlags(values, io) : null;\n\n try {\n const url = values.url || process.env.POCKETBOMB_URL;\n if (!url) {\n io.error(\"dynamite codegen: missing --url (or $POCKETBOMB_URL)\");\n return 2;\n }\n\n const options = {\n url,\n publicKey: values[\"public-key\"] || process.env.POCKETBOMB_PUBLIC_KEY,\n cwd,\n schemaFile: values.schema,\n envFile: values.env,\n };\n\n if (!values.watch) {\n io.log(describeResult(await codegen(options), cwd));\n return 0;\n }\n\n const stop = watchCodegen({\n ...options,\n interval: values.interval ? parseInt(values.interval, 10) : undefined,\n onChange: (result) => io.log(describeResult(result, cwd)),\n onError: (err) => io.error(\"codegen: \" + String((err as Error)?.message || err)),\n });\n await new Promise<void>((resolve) => {\n process.once(\"SIGINT\", resolve);\n process.once(\"SIGTERM\", resolve);\n });\n stop();\n return 0;\n } finally {\n await fuse?.stop();\n }\n}\n\nasync function runDev(argv: Array<string>, io: Io): Promise<number> {\n const { values } = parseArgs({\n args: argv,\n options: {\n seed: { type: \"string\" },\n \"data-dir\": { type: \"string\" },\n entry: { type: \"string\" },\n port: { type: \"string\" },\n \"no-codegen\": { type: \"boolean\" },\n },\n });\n\n const cwd = process.cwd();\n const fuse = process.env.POCKETBOMB_URL ? null : await startFuseFromFlags(values, io);\n\n let stopCodegen = () => {};\n if (!values[\"no-codegen\"] && process.env.POCKETBOMB_PUBLIC_KEY) {\n stopCodegen = watchCodegen({\n url: process.env.POCKETBOMB_URL!,\n publicKey: process.env.POCKETBOMB_PUBLIC_KEY,\n cwd,\n onChange: (result) => io.error(\"[codegen] \" + describeResult(result, cwd)),\n onError: (err) => io.error(\"[codegen] \" + String((err as Error)?.message || err)),\n });\n }\n\n const cleanup = async () => {\n stopCodegen();\n await fuse?.stop();\n };\n\n if (values.entry) {\n const child = spawn(process.execPath, [path.resolve(cwd, values.entry)], {\n stdio: \"inherit\",\n env: { ...process.env, NODE_ENV: process.env.NODE_ENV || \"development\" },\n });\n const forward = (signal: NodeJS.Signals) => child.kill(signal);\n process.on(\"SIGINT\", forward);\n process.on(\"SIGTERM\", forward);\n\n const code: number = await new Promise((resolve) => child.on(\"exit\", (c) => resolve(c ?? 1)));\n await cleanup();\n return code;\n }\n\n const { createServer } = await import(\"vite\");\n const server = await createServer({\n root: cwd,\n server: values.port ? { port: parseInt(values.port, 10) } : undefined,\n });\n await server.listen();\n server.printUrls();\n\n await new Promise<void>((resolve) => {\n process.once(\"SIGINT\", resolve);\n process.once(\"SIGTERM\", resolve);\n });\n await server.close();\n await cleanup();\n return 0;\n}\n\nasync function runCheck(argv: Array<string>, io: Io): Promise<number> {\n const { values } = parseArgs({\n args: argv,\n options: { schema: { type: \"string\" }, cwd: { type: \"string\" } },\n });\n const cwd = path.resolve(values.cwd || process.cwd());\n\n let result;\n try {\n result = await check({ cwd, schemaFile: values.schema });\n } catch (err) {\n io.error(\"dynamite check: \" + (err as Error).message);\n return 2;\n }\n\n for (const p of result.problems) {\n io.error(`${path.relative(cwd, p.file)}:${p.line}:${p.column} ${p.message}`);\n }\n if (result.problems.length) {\n io.error(\n `dynamite check: ${result.problems.length} problem(s) in ${result.documents} documents`,\n );\n return 1;\n }\n io.log(`dynamite check: ${result.documents} documents OK`);\n return 0;\n}\n\nasync function runInfo(io: Io): Promise<number> {\n const cwd = process.cwd();\n const mask = (v?: string) => (v ? v.substring(0, 6) + \"…\" : \"(not set)\");\n\n io.log(\"pocketbomb-dynamite \" + readPackageVersion());\n io.log(\"node \" + process.version);\n io.log(\"POCKETBOMB_URL \" + (process.env.POCKETBOMB_URL || \"(not set)\"));\n io.log(\"POCKETBOMB_PUBLIC_KEY \" + mask(process.env.POCKETBOMB_PUBLIC_KEY));\n io.log(\"POCKETBOMB_PRIVATE_KEY \" + (process.env.POCKETBOMB_PRIVATE_KEY ? \"(set)\" : \"(not set)\"));\n\n for (const file of [DEFAULT_SCHEMA_FILE, DEFAULT_ENV_FILE]) {\n const full = path.resolve(cwd, file);\n io.log(\n file.padEnd(23) +\n (fs.existsSync(full) ? \"generated \" + fs.statSync(full).mtime.toISOString() : \"missing\"),\n );\n }\n\n try {\n const { resolveFuseBinary } = await import(\"pocketbomb-fuse/node\");\n const resolved = resolveFuseBinary({ log: () => {} });\n io.log(\"fuse binary \" + resolved.bin + \" (\" + resolved.source + \")\");\n } catch (err) {\n io.log(\"fuse binary \" + String((err as Error).message));\n }\n\n return 0;\n}\n\n/**\n * Runs the CLI with the provided arguments (without the node and script paths).\n */\nexport async function run(argv: Array<string>, io: Io = defaultIo): Promise<number> {\n const [command, ...rest] = argv;\n\n switch (command) {\n case \"codegen\":\n return runCodegen(rest, io);\n case \"dev\":\n return runDev(rest, io);\n case \"check\":\n return runCheck(rest, io);\n case \"info\":\n return runInfo(io);\n case \"--version\":\n case \"-v\":\n io.log(readPackageVersion());\n return 0;\n case undefined:\n case \"help\":\n case \"--help\":\n case \"-h\":\n io.log(HELP);\n return 0;\n default:\n io.error(\"Unknown command: \" + command + \"\\n\\n\" + HELP);\n return 2;\n }\n}\n\nexport * from \"./check\";\nexport * from \"./codegen\";\n"],"mappings":"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"}
|
package/dist/cli/index.d.cts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CheckProblem, CheckResult, check } from "./check.cjs";
|
|
1
2
|
import { DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE } from "../shared/constants.cjs";
|
|
2
3
|
import { CodegenOptions, GenerateOptions, GenerateResult, IntrospectOptions, WatchOptions, codegen, generate, introspect, introspectionToEnv, introspectionToSDL, watchCodegen, withoutEmptyDescriptions } from "./codegen.cjs";
|
|
3
4
|
type Io = {
|
|
@@ -8,5 +9,5 @@ type Io = {
|
|
|
8
9
|
* Runs the CLI with the provided arguments (without the node and script paths).
|
|
9
10
|
*/
|
|
10
11
|
export declare function run(argv: Array<string>, io?: Io): Promise<number>;
|
|
11
|
-
export { CodegenOptions, DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE, GenerateOptions, GenerateResult, IntrospectOptions, WatchOptions, codegen, generate, introspect, introspectionToEnv, introspectionToSDL, watchCodegen, withoutEmptyDescriptions };
|
|
12
|
+
export { CheckProblem, CheckResult, CodegenOptions, DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE, GenerateOptions, GenerateResult, IntrospectOptions, WatchOptions, check, codegen, generate, introspect, introspectionToEnv, introspectionToSDL, watchCodegen, withoutEmptyDescriptions };
|
|
12
13
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CheckProblem, CheckResult, check } from "./check.js";
|
|
1
2
|
import { DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE } from "../shared/constants.js";
|
|
2
3
|
import { CodegenOptions, GenerateOptions, GenerateResult, IntrospectOptions, WatchOptions, codegen, generate, introspect, introspectionToEnv, introspectionToSDL, watchCodegen, withoutEmptyDescriptions } from "./codegen.js";
|
|
3
4
|
type Io = {
|
|
@@ -8,5 +9,5 @@ type Io = {
|
|
|
8
9
|
* Runs the CLI with the provided arguments (without the node and script paths).
|
|
9
10
|
*/
|
|
10
11
|
export declare function run(argv: Array<string>, io?: Io): Promise<number>;
|
|
11
|
-
export { CodegenOptions, DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE, GenerateOptions, GenerateResult, IntrospectOptions, WatchOptions, codegen, generate, introspect, introspectionToEnv, introspectionToSDL, watchCodegen, withoutEmptyDescriptions };
|
|
12
|
+
export { CheckProblem, CheckResult, CodegenOptions, DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE, GenerateOptions, GenerateResult, IntrospectOptions, WatchOptions, check, codegen, generate, introspect, introspectionToEnv, introspectionToSDL, watchCodegen, withoutEmptyDescriptions };
|
|
12
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/cli/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE } from "../shared/constants.js";
|
|
2
2
|
import { codegen, generate, introspect, introspectionToEnv, introspectionToSDL, watchCodegen, withoutEmptyDescriptions } from "./codegen.js";
|
|
3
|
+
import { check } from "./check.js";
|
|
3
4
|
import fs from "node:fs";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { spawn } from "node:child_process";
|
|
@@ -19,6 +20,8 @@ Commands:
|
|
|
19
20
|
--seed <module> --data-dir <dir>
|
|
20
21
|
--entry <file> run a custom server entry with Node.js (default: the Vite dev server)
|
|
21
22
|
--port <port> the Vite dev server port
|
|
23
|
+
check Validate the graphql() documents of the project against the schema
|
|
24
|
+
--schema <file> (default ${DEFAULT_SCHEMA_FILE})
|
|
22
25
|
info Print the PocketBomb environment and the generated files status
|
|
23
26
|
`;
|
|
24
27
|
const defaultIo = {
|
|
@@ -151,6 +154,33 @@ async function runDev(argv, io) {
|
|
|
151
154
|
await cleanup();
|
|
152
155
|
return 0;
|
|
153
156
|
}
|
|
157
|
+
async function runCheck(argv, io) {
|
|
158
|
+
const { values } = parseArgs({
|
|
159
|
+
args: argv,
|
|
160
|
+
options: {
|
|
161
|
+
schema: { type: "string" },
|
|
162
|
+
cwd: { type: "string" }
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
const cwd = path.resolve(values.cwd || process.cwd());
|
|
166
|
+
let result;
|
|
167
|
+
try {
|
|
168
|
+
result = await check({
|
|
169
|
+
cwd,
|
|
170
|
+
schemaFile: values.schema
|
|
171
|
+
});
|
|
172
|
+
} catch (err) {
|
|
173
|
+
io.error("dynamite check: " + err.message);
|
|
174
|
+
return 2;
|
|
175
|
+
}
|
|
176
|
+
for (const p of result.problems) io.error(`${path.relative(cwd, p.file)}:${p.line}:${p.column} ${p.message}`);
|
|
177
|
+
if (result.problems.length) {
|
|
178
|
+
io.error(`dynamite check: ${result.problems.length} problem(s) in ${result.documents} documents`);
|
|
179
|
+
return 1;
|
|
180
|
+
}
|
|
181
|
+
io.log(`dynamite check: ${result.documents} documents OK`);
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
154
184
|
async function runInfo(io) {
|
|
155
185
|
const cwd = process.cwd();
|
|
156
186
|
const mask = (v) => v ? v.substring(0, 6) + "…" : "(not set)";
|
|
@@ -180,6 +210,7 @@ async function run(argv, io = defaultIo) {
|
|
|
180
210
|
switch (command) {
|
|
181
211
|
case "codegen": return runCodegen(rest, io);
|
|
182
212
|
case "dev": return runDev(rest, io);
|
|
213
|
+
case "check": return runCheck(rest, io);
|
|
183
214
|
case "info": return runInfo(io);
|
|
184
215
|
case "--version":
|
|
185
216
|
case "-v":
|
|
@@ -196,6 +227,6 @@ async function run(argv, io = defaultIo) {
|
|
|
196
227
|
return 2;
|
|
197
228
|
}
|
|
198
229
|
}
|
|
199
|
-
export { DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE, codegen, generate, introspect, introspectionToEnv, introspectionToSDL, run, watchCodegen, withoutEmptyDescriptions };
|
|
230
|
+
export { DEFAULT_ENV_FILE, DEFAULT_SCHEMA_FILE, check, codegen, generate, introspect, introspectionToEnv, introspectionToSDL, run, watchCodegen, withoutEmptyDescriptions };
|
|
200
231
|
|
|
201
232
|
//# sourceMappingURL=index.js.map
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/cli/index.ts"],"sourcesContent":["// The `dynamite` CLI (Node.js only): codegen, dev, info.\nimport { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport type { Fuse } from \"pocketbomb-fuse/node\";\nimport {\n codegen,\n DEFAULT_ENV_FILE,\n DEFAULT_SCHEMA_FILE,\n watchCodegen,\n type GenerateResult,\n} from \"./codegen\";\n\nconst HELP = `dynamite <command> [options]\n\nCommands:\n codegen Introspect the PocketBomb GraphQL API and write the schema + the gql.tada env\n --url <url> (default $POCKETBOMB_URL)\n --public-key <key> (default $POCKETBOMB_PUBLIC_KEY)\n --schema <file> (default ${DEFAULT_SCHEMA_FILE})\n --env <file> (default ${DEFAULT_ENV_FILE})\n --watch [--interval <ms>]\n --fuse [--seed <module>] start a local server with fuse for the codegen\n dev Start fuse (unless $POCKETBOMB_URL is set), the codegen watcher and the app\n --seed <module> --data-dir <dir>\n --entry <file> run a custom server entry with Node.js (default: the Vite dev server)\n --port <port> the Vite dev server port\n info Print the PocketBomb environment and the generated files status\n`;\n\ntype Io = { log: (line: string) => void; error: (line: string) => void };\n\nconst defaultIo: Io = {\n log: (line) => process.stdout.write(line + \"\\n\"),\n error: (line) => process.stderr.write(line + \"\\n\"),\n};\n\nfunction readPackageVersion(): string {\n try {\n const pkg = JSON.parse(fs.readFileSync(new URL(\"../../package.json\", import.meta.url), \"utf8\"));\n return pkg.version;\n } catch {\n return \"unknown\";\n }\n}\n\nasync function startFuseFromFlags(\n flags: { seed?: string; \"data-dir\"?: string },\n io: Io,\n): Promise<Fuse> {\n const { startFuse } = await import(\"pocketbomb-fuse/node\");\n const fuse = await startFuse({\n seed: flags.seed,\n dataDir: flags[\"data-dir\"],\n log: (line) => io.error(\"[fuse] \" + line),\n });\n Object.assign(process.env, fuse.env);\n io.error(\"[fuse] PocketBomb running on \" + fuse.url);\n return fuse;\n}\n\nfunction describeResult(result: GenerateResult, cwd: string): string {\n return (\n (result.changed ? \"generated \" : \"up to date: \") +\n path.relative(cwd, result.schemaFile) +\n \", \" +\n path.relative(cwd, result.envFile)\n );\n}\n\nasync function runCodegen(argv: Array<string>, io: Io): Promise<number> {\n const { values } = parseArgs({\n args: argv,\n options: {\n url: { type: \"string\" },\n \"public-key\": { type: \"string\" },\n schema: { type: \"string\" },\n env: { type: \"string\" },\n watch: { type: \"boolean\" },\n interval: { type: \"string\" },\n fuse: { type: \"boolean\" },\n seed: { type: \"string\" },\n cwd: { type: \"string\" },\n },\n });\n\n const cwd = path.resolve(values.cwd || process.cwd());\n const fuse = values.fuse ? await startFuseFromFlags(values, io) : null;\n\n try {\n const url = values.url || process.env.POCKETBOMB_URL;\n if (!url) {\n io.error(\"dynamite codegen: missing --url (or $POCKETBOMB_URL)\");\n return 2;\n }\n\n const options = {\n url,\n publicKey: values[\"public-key\"] || process.env.POCKETBOMB_PUBLIC_KEY,\n cwd,\n schemaFile: values.schema,\n envFile: values.env,\n };\n\n if (!values.watch) {\n io.log(describeResult(await codegen(options), cwd));\n return 0;\n }\n\n const stop = watchCodegen({\n ...options,\n interval: values.interval ? parseInt(values.interval, 10) : undefined,\n onChange: (result) => io.log(describeResult(result, cwd)),\n onError: (err) => io.error(\"codegen: \" + String((err as Error)?.message || err)),\n });\n await new Promise<void>((resolve) => {\n process.once(\"SIGINT\", resolve);\n process.once(\"SIGTERM\", resolve);\n });\n stop();\n return 0;\n } finally {\n await fuse?.stop();\n }\n}\n\nasync function runDev(argv: Array<string>, io: Io): Promise<number> {\n const { values } = parseArgs({\n args: argv,\n options: {\n seed: { type: \"string\" },\n \"data-dir\": { type: \"string\" },\n entry: { type: \"string\" },\n port: { type: \"string\" },\n \"no-codegen\": { type: \"boolean\" },\n },\n });\n\n const cwd = process.cwd();\n const fuse = process.env.POCKETBOMB_URL ? null : await startFuseFromFlags(values, io);\n\n let stopCodegen = () => {};\n if (!values[\"no-codegen\"] && process.env.POCKETBOMB_PUBLIC_KEY) {\n stopCodegen = watchCodegen({\n url: process.env.POCKETBOMB_URL!,\n publicKey: process.env.POCKETBOMB_PUBLIC_KEY,\n cwd,\n onChange: (result) => io.error(\"[codegen] \" + describeResult(result, cwd)),\n onError: (err) => io.error(\"[codegen] \" + String((err as Error)?.message || err)),\n });\n }\n\n const cleanup = async () => {\n stopCodegen();\n await fuse?.stop();\n };\n\n if (values.entry) {\n const child = spawn(process.execPath, [path.resolve(cwd, values.entry)], {\n stdio: \"inherit\",\n env: { ...process.env, NODE_ENV: process.env.NODE_ENV || \"development\" },\n });\n const forward = (signal: NodeJS.Signals) => child.kill(signal);\n process.on(\"SIGINT\", forward);\n process.on(\"SIGTERM\", forward);\n\n const code: number = await new Promise((resolve) => child.on(\"exit\", (c) => resolve(c ?? 1)));\n await cleanup();\n return code;\n }\n\n const { createServer } = await import(\"vite\");\n const server = await createServer({\n root: cwd,\n server: values.port ? { port: parseInt(values.port, 10) } : undefined,\n });\n await server.listen();\n server.printUrls();\n\n await new Promise<void>((resolve) => {\n process.once(\"SIGINT\", resolve);\n process.once(\"SIGTERM\", resolve);\n });\n await server.close();\n await cleanup();\n return 0;\n}\n\nasync function runInfo(io: Io): Promise<number> {\n const cwd = process.cwd();\n const mask = (v?: string) => (v ? v.substring(0, 6) + \"…\" : \"(not set)\");\n\n io.log(\"pocketbomb-dynamite \" + readPackageVersion());\n io.log(\"node \" + process.version);\n io.log(\"POCKETBOMB_URL \" + (process.env.POCKETBOMB_URL || \"(not set)\"));\n io.log(\"POCKETBOMB_PUBLIC_KEY \" + mask(process.env.POCKETBOMB_PUBLIC_KEY));\n io.log(\"POCKETBOMB_PRIVATE_KEY \" + (process.env.POCKETBOMB_PRIVATE_KEY ? \"(set)\" : \"(not set)\"));\n\n for (const file of [DEFAULT_SCHEMA_FILE, DEFAULT_ENV_FILE]) {\n const full = path.resolve(cwd, file);\n io.log(\n file.padEnd(23) +\n (fs.existsSync(full) ? \"generated \" + fs.statSync(full).mtime.toISOString() : \"missing\"),\n );\n }\n\n try {\n const { resolveFuseBinary } = await import(\"pocketbomb-fuse/node\");\n const resolved = resolveFuseBinary({ log: () => {} });\n io.log(\"fuse binary \" + resolved.bin + \" (\" + resolved.source + \")\");\n } catch (err) {\n io.log(\"fuse binary \" + String((err as Error).message));\n }\n\n return 0;\n}\n\n/**\n * Runs the CLI with the provided arguments (without the node and script paths).\n */\nexport async function run(argv: Array<string>, io: Io = defaultIo): Promise<number> {\n const [command, ...rest] = argv;\n\n switch (command) {\n case \"codegen\":\n return runCodegen(rest, io);\n case \"dev\":\n return runDev(rest, io);\n case \"info\":\n return runInfo(io);\n case \"--version\":\n case \"-v\":\n io.log(readPackageVersion());\n return 0;\n case undefined:\n case \"help\":\n case \"--help\":\n case \"-h\":\n io.log(HELP);\n return 0;\n default:\n io.error(\"Unknown command: \" + command + \"\\n\\n\" + HELP);\n return 2;\n }\n}\n\nexport * from \"./codegen\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/cli/index.ts"],"sourcesContent":["// The `dynamite` CLI (Node.js only): codegen, dev, info.\nimport { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport type { Fuse } from \"pocketbomb-fuse/node\";\nimport { check } from \"./check\";\nimport {\n codegen,\n DEFAULT_ENV_FILE,\n DEFAULT_SCHEMA_FILE,\n watchCodegen,\n type GenerateResult,\n} from \"./codegen\";\n\nconst HELP = `dynamite <command> [options]\n\nCommands:\n codegen Introspect the PocketBomb GraphQL API and write the schema + the gql.tada env\n --url <url> (default $POCKETBOMB_URL)\n --public-key <key> (default $POCKETBOMB_PUBLIC_KEY)\n --schema <file> (default ${DEFAULT_SCHEMA_FILE})\n --env <file> (default ${DEFAULT_ENV_FILE})\n --watch [--interval <ms>]\n --fuse [--seed <module>] start a local server with fuse for the codegen\n dev Start fuse (unless $POCKETBOMB_URL is set), the codegen watcher and the app\n --seed <module> --data-dir <dir>\n --entry <file> run a custom server entry with Node.js (default: the Vite dev server)\n --port <port> the Vite dev server port\n check Validate the graphql() documents of the project against the schema\n --schema <file> (default ${DEFAULT_SCHEMA_FILE})\n info Print the PocketBomb environment and the generated files status\n`;\n\ntype Io = { log: (line: string) => void; error: (line: string) => void };\n\nconst defaultIo: Io = {\n log: (line) => process.stdout.write(line + \"\\n\"),\n error: (line) => process.stderr.write(line + \"\\n\"),\n};\n\nfunction readPackageVersion(): string {\n try {\n const pkg = JSON.parse(fs.readFileSync(new URL(\"../../package.json\", import.meta.url), \"utf8\"));\n return pkg.version;\n } catch {\n return \"unknown\";\n }\n}\n\nasync function startFuseFromFlags(\n flags: { seed?: string; \"data-dir\"?: string },\n io: Io,\n): Promise<Fuse> {\n const { startFuse } = await import(\"pocketbomb-fuse/node\");\n const fuse = await startFuse({\n seed: flags.seed,\n dataDir: flags[\"data-dir\"],\n log: (line) => io.error(\"[fuse] \" + line),\n });\n Object.assign(process.env, fuse.env);\n io.error(\"[fuse] PocketBomb running on \" + fuse.url);\n return fuse;\n}\n\nfunction describeResult(result: GenerateResult, cwd: string): string {\n return (\n (result.changed ? \"generated \" : \"up to date: \") +\n path.relative(cwd, result.schemaFile) +\n \", \" +\n path.relative(cwd, result.envFile)\n );\n}\n\nasync function runCodegen(argv: Array<string>, io: Io): Promise<number> {\n const { values } = parseArgs({\n args: argv,\n options: {\n url: { type: \"string\" },\n \"public-key\": { type: \"string\" },\n schema: { type: \"string\" },\n env: { type: \"string\" },\n watch: { type: \"boolean\" },\n interval: { type: \"string\" },\n fuse: { type: \"boolean\" },\n seed: { type: \"string\" },\n cwd: { type: \"string\" },\n },\n });\n\n const cwd = path.resolve(values.cwd || process.cwd());\n const fuse = values.fuse ? await startFuseFromFlags(values, io) : null;\n\n try {\n const url = values.url || process.env.POCKETBOMB_URL;\n if (!url) {\n io.error(\"dynamite codegen: missing --url (or $POCKETBOMB_URL)\");\n return 2;\n }\n\n const options = {\n url,\n publicKey: values[\"public-key\"] || process.env.POCKETBOMB_PUBLIC_KEY,\n cwd,\n schemaFile: values.schema,\n envFile: values.env,\n };\n\n if (!values.watch) {\n io.log(describeResult(await codegen(options), cwd));\n return 0;\n }\n\n const stop = watchCodegen({\n ...options,\n interval: values.interval ? parseInt(values.interval, 10) : undefined,\n onChange: (result) => io.log(describeResult(result, cwd)),\n onError: (err) => io.error(\"codegen: \" + String((err as Error)?.message || err)),\n });\n await new Promise<void>((resolve) => {\n process.once(\"SIGINT\", resolve);\n process.once(\"SIGTERM\", resolve);\n });\n stop();\n return 0;\n } finally {\n await fuse?.stop();\n }\n}\n\nasync function runDev(argv: Array<string>, io: Io): Promise<number> {\n const { values } = parseArgs({\n args: argv,\n options: {\n seed: { type: \"string\" },\n \"data-dir\": { type: \"string\" },\n entry: { type: \"string\" },\n port: { type: \"string\" },\n \"no-codegen\": { type: \"boolean\" },\n },\n });\n\n const cwd = process.cwd();\n const fuse = process.env.POCKETBOMB_URL ? null : await startFuseFromFlags(values, io);\n\n let stopCodegen = () => {};\n if (!values[\"no-codegen\"] && process.env.POCKETBOMB_PUBLIC_KEY) {\n stopCodegen = watchCodegen({\n url: process.env.POCKETBOMB_URL!,\n publicKey: process.env.POCKETBOMB_PUBLIC_KEY,\n cwd,\n onChange: (result) => io.error(\"[codegen] \" + describeResult(result, cwd)),\n onError: (err) => io.error(\"[codegen] \" + String((err as Error)?.message || err)),\n });\n }\n\n const cleanup = async () => {\n stopCodegen();\n await fuse?.stop();\n };\n\n if (values.entry) {\n const child = spawn(process.execPath, [path.resolve(cwd, values.entry)], {\n stdio: \"inherit\",\n env: { ...process.env, NODE_ENV: process.env.NODE_ENV || \"development\" },\n });\n const forward = (signal: NodeJS.Signals) => child.kill(signal);\n process.on(\"SIGINT\", forward);\n process.on(\"SIGTERM\", forward);\n\n const code: number = await new Promise((resolve) => child.on(\"exit\", (c) => resolve(c ?? 1)));\n await cleanup();\n return code;\n }\n\n const { createServer } = await import(\"vite\");\n const server = await createServer({\n root: cwd,\n server: values.port ? { port: parseInt(values.port, 10) } : undefined,\n });\n await server.listen();\n server.printUrls();\n\n await new Promise<void>((resolve) => {\n process.once(\"SIGINT\", resolve);\n process.once(\"SIGTERM\", resolve);\n });\n await server.close();\n await cleanup();\n return 0;\n}\n\nasync function runCheck(argv: Array<string>, io: Io): Promise<number> {\n const { values } = parseArgs({\n args: argv,\n options: { schema: { type: \"string\" }, cwd: { type: \"string\" } },\n });\n const cwd = path.resolve(values.cwd || process.cwd());\n\n let result;\n try {\n result = await check({ cwd, schemaFile: values.schema });\n } catch (err) {\n io.error(\"dynamite check: \" + (err as Error).message);\n return 2;\n }\n\n for (const p of result.problems) {\n io.error(`${path.relative(cwd, p.file)}:${p.line}:${p.column} ${p.message}`);\n }\n if (result.problems.length) {\n io.error(\n `dynamite check: ${result.problems.length} problem(s) in ${result.documents} documents`,\n );\n return 1;\n }\n io.log(`dynamite check: ${result.documents} documents OK`);\n return 0;\n}\n\nasync function runInfo(io: Io): Promise<number> {\n const cwd = process.cwd();\n const mask = (v?: string) => (v ? v.substring(0, 6) + \"…\" : \"(not set)\");\n\n io.log(\"pocketbomb-dynamite \" + readPackageVersion());\n io.log(\"node \" + process.version);\n io.log(\"POCKETBOMB_URL \" + (process.env.POCKETBOMB_URL || \"(not set)\"));\n io.log(\"POCKETBOMB_PUBLIC_KEY \" + mask(process.env.POCKETBOMB_PUBLIC_KEY));\n io.log(\"POCKETBOMB_PRIVATE_KEY \" + (process.env.POCKETBOMB_PRIVATE_KEY ? \"(set)\" : \"(not set)\"));\n\n for (const file of [DEFAULT_SCHEMA_FILE, DEFAULT_ENV_FILE]) {\n const full = path.resolve(cwd, file);\n io.log(\n file.padEnd(23) +\n (fs.existsSync(full) ? \"generated \" + fs.statSync(full).mtime.toISOString() : \"missing\"),\n );\n }\n\n try {\n const { resolveFuseBinary } = await import(\"pocketbomb-fuse/node\");\n const resolved = resolveFuseBinary({ log: () => {} });\n io.log(\"fuse binary \" + resolved.bin + \" (\" + resolved.source + \")\");\n } catch (err) {\n io.log(\"fuse binary \" + String((err as Error).message));\n }\n\n return 0;\n}\n\n/**\n * Runs the CLI with the provided arguments (without the node and script paths).\n */\nexport async function run(argv: Array<string>, io: Io = defaultIo): Promise<number> {\n const [command, ...rest] = argv;\n\n switch (command) {\n case \"codegen\":\n return runCodegen(rest, io);\n case \"dev\":\n return runDev(rest, io);\n case \"check\":\n return runCheck(rest, io);\n case \"info\":\n return runInfo(io);\n case \"--version\":\n case \"-v\":\n io.log(readPackageVersion());\n return 0;\n case undefined:\n case \"help\":\n case \"--help\":\n case \"-h\":\n io.log(HELP);\n return 0;\n default:\n io.error(\"Unknown command: \" + command + \"\\n\\n\" + HELP);\n return 2;\n }\n}\n\nexport * from \"./check\";\nexport * from \"./codegen\";\n"],"mappings":"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"}
|
|
@@ -101,7 +101,62 @@ function createCustomerAccountClient(options) {
|
|
|
101
101
|
function isSafeReturnTo(path) {
|
|
102
102
|
return path.startsWith("/") && !path.startsWith("//") && !path.startsWith("/\\");
|
|
103
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Returns the login route URL with the provided options (see {@link loginOptionsFromURL}).
|
|
106
|
+
*/
|
|
107
|
+
function loginRouteURL(route, options = {}) {
|
|
108
|
+
const params = new URLSearchParams();
|
|
109
|
+
if (options.returnTo !== void 0) params.set("returnTo", options.returnTo);
|
|
110
|
+
if (options.prompt !== void 0) params.set("prompt", options.prompt);
|
|
111
|
+
if (options.maxAge !== void 0) params.set("maxAge", String(options.maxAge));
|
|
112
|
+
if (options.loginHint !== void 0) params.set("loginHint", options.loginHint);
|
|
113
|
+
const query = params.toString();
|
|
114
|
+
return query ? route + (route.includes("?") ? "&" : "?") + query : route;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Returns the login options of a login route request, for `loginRedirect()`:
|
|
118
|
+
*
|
|
119
|
+
* ```ts
|
|
120
|
+
* return context.customer.loginRedirect(loginOptionsFromURL(request.url));
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* The URL is attacker controlled (anyone can send a link): an unsafe `returnTo` (see {@link isSafeReturnTo})
|
|
124
|
+
* and an invalid `maxAge` are dropped, and no other option can be set through it.
|
|
125
|
+
*/
|
|
126
|
+
function loginOptionsFromURL(url) {
|
|
127
|
+
const params = new URL(url, "http://localhost").searchParams;
|
|
128
|
+
const result = {};
|
|
129
|
+
const returnTo = params.get("returnTo");
|
|
130
|
+
if (returnTo && isSafeReturnTo(returnTo)) result.returnTo = returnTo;
|
|
131
|
+
const prompt = params.get("prompt");
|
|
132
|
+
if (prompt) result.prompt = prompt;
|
|
133
|
+
const maxAge = params.get("maxAge");
|
|
134
|
+
if (maxAge && /^\d+$/.test(maxAge)) result.maxAge = parseInt(maxAge, 10);
|
|
135
|
+
const loginHint = params.get("loginHint");
|
|
136
|
+
if (loginHint) result.loginHint = loginHint;
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Returns the logout route URL with the provided options (see {@link logoutOptionsFromURL}).
|
|
141
|
+
*/
|
|
142
|
+
function logoutRouteURL(route, options = {}) {
|
|
143
|
+
if (options.postLogoutRedirectUri === void 0) return route;
|
|
144
|
+
const query = new URLSearchParams({ postLogoutRedirectUri: options.postLogoutRedirectUri });
|
|
145
|
+
return route + (route.includes("?") ? "&" : "?") + query;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Returns the logout options of a logout route request, for `logoutRedirect()`.
|
|
149
|
+
* The issuer accepts only the registered post logout redirect URIs of the client.
|
|
150
|
+
*/
|
|
151
|
+
function logoutOptionsFromURL(url) {
|
|
152
|
+
const uri = new URL(url, "http://localhost").searchParams.get("postLogoutRedirectUri");
|
|
153
|
+
return uri ? { postLogoutRedirectUri: uri } : {};
|
|
154
|
+
}
|
|
104
155
|
exports.createCustomerAccountClient = createCustomerAccountClient;
|
|
105
156
|
exports.isSafeReturnTo = isSafeReturnTo;
|
|
157
|
+
exports.loginOptionsFromURL = loginOptionsFromURL;
|
|
158
|
+
exports.loginRouteURL = loginRouteURL;
|
|
159
|
+
exports.logoutOptionsFromURL = logoutOptionsFromURL;
|
|
160
|
+
exports.logoutRouteURL = logoutRouteURL;
|
|
106
161
|
|
|
107
162
|
//# sourceMappingURL=customer-account.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"customer-account.cjs","names":["createAccountClient","sendGraphQL","unwrapGraphQLResponse"],"sources":["../../src/customer-account/customer-account.ts"],"sourcesContent":["import {\n createAccountClient,\n type Account,\n type AccountCallbackResult,\n type AccountConfig,\n type AccountLoginOptions,\n type AccountLogoutOptions,\n type AccountMe,\n type AuthStore,\n type Client,\n type GraphQLResponse,\n} from \"pocketbomb-primer\";\nimport type { RequestContext } from \"../core/request-context\";\nimport {\n executeGraphQL as sendGraphQL,\n unwrapGraphQLResponse,\n type GraphQLRequestOptions,\n type ResultOf,\n type DocumentInput,\n type VariablesOf,\n} from \"../graphql/graphql\";\n\nexport interface CustomerAccountClientOptions extends Omit<AccountConfig, \"session\"> {\n /**\n * The PocketBomb base URL.\n */\n url: string;\n\n /**\n * Optional public API key of the same OAuth2 client (sent with every request).\n */\n publicKey?: string;\n\n /**\n * Optional `Accept-Language` (default to \"en-US\").\n */\n lang?: string;\n\n /**\n * Optional fetch implementation.\n */\n fetch?: typeof fetch;\n\n /**\n * The per-request context of a server rendered app: the customer session is stored in\n * its httpOnly cookie session (auth state + login flow state) and the `*Redirect()`\n * helpers return redirect responses with the session cookies.\n */\n requestContext?: RequestContext;\n\n /**\n * The auth store (browser/mobile; default to the request context session for SSR,\n * a `LocalAuthStore` in browsers and an in-memory store elsewhere).\n */\n authStore?: AuthStore;\n\n /**\n * The login flow state storage (default to the request context session for SSR).\n */\n session?: AccountConfig[\"session\"];\n}\n\nexport interface LoginRedirectOptions extends AccountLoginOptions {\n /**\n * The app path to return to after the sign-in (stored in the login state).\n */\n returnTo?: string;\n}\n\nexport interface CallbackRedirectOptions {\n /**\n * The callback URL (default to the request context request URL).\n */\n url?: string;\n\n /**\n * Where to redirect after a successful sign-in without a `returnTo` (default to \"/\").\n */\n successRedirect?: string;\n}\n\n/**\n * The customer account client (see {@link createCustomerAccountClient}).\n */\nexport interface CustomerAccountClient {\n /**\n * The underlying primer account client (records REST with the customer token, ...).\n */\n readonly client: Client;\n\n /**\n * The primer OAuth2 account (PKCE, refresh rotation, logout).\n */\n readonly account: Account;\n\n /** Browser redirect (or mobile `openAuthSession`) login. */\n login(options?: AccountLoginOptions): Promise<AccountCallbackResult | null>;\n\n /** Browser redirect (or mobile `openAuthSession`) signup (`prompt=create`). */\n signup(options?: Omit<AccountLoginOptions, \"prompt\">): Promise<AccountCallbackResult | null>;\n\n /** Handles the authorization response of the provided (or the current browser) URL. */\n handleCallback(url?: string): Promise<AccountCallbackResult | null>;\n\n /** Revokes the refresh token, clears the session and ends the issuer session. */\n logout(options?: AccountLogoutOptions): Promise<string>;\n\n /** Loosely checks whether the customer has a valid access token. */\n isLoggedIn(): boolean;\n\n /** Returns a valid (refreshed if needed) access token or an empty string. */\n getAccessToken(): Promise<string>;\n\n /** Returns the customer record and permissions (`/api/permissions/me`). */\n me(): Promise<AccountMe>;\n\n /** Returns the OpenID Connect userinfo claims. */\n userinfo(): Promise<{ [key: string]: any }>;\n\n /** Sends an email change request for the signed in customer. */\n requestEmailChange(newEmail: string): Promise<boolean>;\n\n /** Sends a verification email request. */\n requestVerification(email: string): Promise<boolean>;\n\n /** Sends a typed GraphQL document with the customer token. */\n graphql<Doc extends DocumentInput>(\n document: Doc,\n options?: GraphQLRequestOptions<VariablesOf<Doc>>,\n ): Promise<GraphQLResponse<ResultOf<Doc>>>;\n\n /** Like `graphql()` but returns the `data` and throws a `GraphQLError` for the GraphQL errors. */\n query<Doc extends DocumentInput>(\n document: Doc,\n options?: GraphQLRequestOptions<VariablesOf<Doc>>,\n ): Promise<ResultOf<Doc>>;\n\n /** SSR: returns a redirect response to the hosted login page (requires `requestContext`). */\n loginRedirect(options?: LoginRedirectOptions): Promise<Response>;\n\n /** SSR: handles the callback request and redirects to the `returnTo` page (requires `requestContext`). */\n callbackRedirect(options?: CallbackRedirectOptions): Promise<Response>;\n\n /** SSR: signs out and redirects to the issuer end_session page (requires `requestContext`). */\n logoutRedirect(options?: Omit<AccountLogoutOptions, \"redirect\">): Promise<Response>;\n}\n\n/**\n * Creates the customer account client of an auth collection issuer\n * (hosted login/signup with PKCE, refresh rotation, logout, own sessions and consents),\n * similar to the Hydrogen `createCustomerAccountClient`.\n *\n * - SSR: pass the `requestContext` (the session is stored in its httpOnly cookie) and use\n * the `loginRedirect()`, `callbackRedirect()` and `logoutRedirect()` route helpers.\n * - browser: `login()` redirects and `handleCallback()` completes the sign-in.\n * - mobile: pass an `openAuthSession` adapter and an `AsyncAuthStore`.\n */\nexport function createCustomerAccountClient(\n options: CustomerAccountClientOptions,\n): CustomerAccountClient {\n const context = options.requestContext;\n const { requestContext: _, ...rest } = options;\n\n const client = createAccountClient({\n ...rest,\n authStore: options.authStore || context?.session,\n session: options.session || context?.session,\n backgroundRefresh: context ? false : options.backgroundRefresh,\n // the SSR helpers build the redirect responses themselves\n navigate: options.navigate || (context ? () => {} : undefined),\n // concurrent server loaders must not cancel each other\n autoCancellation: context ? false : undefined,\n });\n const account = client.account;\n\n function requireContext(name: string): RequestContext {\n if (!context) {\n throw new Error(name + \"() requires the requestContext option.\");\n }\n return context;\n }\n\n function redirect(location: string, ctx: RequestContext): Response {\n const headers = new Headers({ Location: location });\n ctx.markPersonalized();\n ctx.applyResponseHeaders(headers);\n\n return new Response(null, { status: 302, headers });\n }\n\n function personalized(): void {\n if (context && client.authStore.token) {\n context.markPersonalized();\n }\n }\n\n async function graphql<Doc extends DocumentInput>(\n document: Doc,\n requestOptions: GraphQLRequestOptions<VariablesOf<Doc>> = {},\n ): Promise<GraphQLResponse<ResultOf<Doc>>> {\n personalized();\n return sendGraphQL(client, document, requestOptions);\n }\n\n return {\n client,\n account,\n login: (opts) => account.login(opts),\n signup: (opts) => account.signup(opts),\n handleCallback: (url) => account.handleCallback(url),\n logout: (opts) => account.logout(opts),\n isLoggedIn: () => account.isLoggedIn(),\n getAccessToken: () => account.getAccessToken(),\n me: () => {\n personalized();\n return account.me();\n },\n userinfo: () => {\n personalized();\n return account.userinfo();\n },\n requestEmailChange: (newEmail) =>\n client.collection(account.config.collection).requestEmailChange(newEmail),\n requestVerification: (email) =>\n client.collection(account.config.collection).requestVerification(email),\n graphql,\n async query(document, requestOptions) {\n return unwrapGraphQLResponse(await graphql(document, requestOptions));\n },\n async loginRedirect(opts = {}) {\n const ctx = requireContext(\"loginRedirect\");\n const { returnTo, ...loginOptions } = opts;\n const state =\n returnTo !== undefined ? { ...loginOptions.state, returnTo } : loginOptions.state;\n\n return redirect(await account.authorizeURL({ ...loginOptions, state }), ctx);\n },\n async callbackRedirect(opts = {}) {\n const ctx = requireContext(\"callbackRedirect\");\n const url = opts.url || ctx.request.url;\n if (!url) {\n throw new Error(\"callbackRedirect() requires the callback url (missing request.url).\");\n }\n\n const result = await account.handleCallback(url);\n const returnTo = result?.state?.returnTo;\n\n return redirect(\n typeof returnTo === \"string\" && isSafeReturnTo(returnTo)\n ? returnTo\n : opts.successRedirect || \"/\",\n ctx,\n );\n },\n async logoutRedirect(opts = {}) {\n const ctx = requireContext(\"logoutRedirect\");\n\n return redirect(await account.logout({ ...opts, redirect: true }), ctx);\n },\n };\n}\n\n/**\n * Reports whether the provided return path is a same-origin relative path\n * (prevents open redirects through the login state).\n */\nexport function isSafeReturnTo(path: string): boolean {\n return path.startsWith(\"/\") && !path.startsWith(\"//\") && !path.startsWith(\"/\\\\\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"customer-account.cjs","names":["createAccountClient","sendGraphQL","unwrapGraphQLResponse"],"sources":["../../src/customer-account/customer-account.ts"],"sourcesContent":["import {\n createAccountClient,\n type Account,\n type AccountCallbackResult,\n type AccountConfig,\n type AccountLoginOptions,\n type AccountLogoutOptions,\n type AccountMe,\n type AuthStore,\n type Client,\n type GraphQLResponse,\n} from \"pocketbomb-primer\";\nimport type { RequestContext } from \"../core/request-context\";\nimport {\n executeGraphQL as sendGraphQL,\n unwrapGraphQLResponse,\n type GraphQLRequestOptions,\n type ResultOf,\n type DocumentInput,\n type VariablesOf,\n} from \"../graphql/graphql\";\n\nexport interface CustomerAccountClientOptions extends Omit<AccountConfig, \"session\"> {\n /**\n * The PocketBomb base URL.\n */\n url: string;\n\n /**\n * Optional public API key of the same OAuth2 client (sent with every request).\n */\n publicKey?: string;\n\n /**\n * Optional `Accept-Language` (default to \"en-US\").\n */\n lang?: string;\n\n /**\n * Optional fetch implementation.\n */\n fetch?: typeof fetch;\n\n /**\n * The per-request context of a server rendered app: the customer session is stored in\n * its httpOnly cookie session (auth state + login flow state) and the `*Redirect()`\n * helpers return redirect responses with the session cookies.\n */\n requestContext?: RequestContext;\n\n /**\n * The auth store (browser/mobile; default to the request context session for SSR,\n * a `LocalAuthStore` in browsers and an in-memory store elsewhere).\n */\n authStore?: AuthStore;\n\n /**\n * The login flow state storage (default to the request context session for SSR).\n */\n session?: AccountConfig[\"session\"];\n}\n\nexport interface LoginRedirectOptions extends AccountLoginOptions {\n /**\n * The app path to return to after the sign-in (stored in the login state).\n */\n returnTo?: string;\n}\n\nexport interface CallbackRedirectOptions {\n /**\n * The callback URL (default to the request context request URL).\n */\n url?: string;\n\n /**\n * Where to redirect after a successful sign-in without a `returnTo` (default to \"/\").\n */\n successRedirect?: string;\n}\n\n/**\n * The customer account client (see {@link createCustomerAccountClient}).\n */\nexport interface CustomerAccountClient {\n /**\n * The underlying primer account client (records REST with the customer token, ...).\n */\n readonly client: Client;\n\n /**\n * The primer OAuth2 account (PKCE, refresh rotation, logout).\n */\n readonly account: Account;\n\n /** Browser redirect (or mobile `openAuthSession`) login. */\n login(options?: AccountLoginOptions): Promise<AccountCallbackResult | null>;\n\n /** Browser redirect (or mobile `openAuthSession`) signup (`prompt=create`). */\n signup(options?: Omit<AccountLoginOptions, \"prompt\">): Promise<AccountCallbackResult | null>;\n\n /** Handles the authorization response of the provided (or the current browser) URL. */\n handleCallback(url?: string): Promise<AccountCallbackResult | null>;\n\n /** Revokes the refresh token, clears the session and ends the issuer session. */\n logout(options?: AccountLogoutOptions): Promise<string>;\n\n /** Loosely checks whether the customer has a valid access token. */\n isLoggedIn(): boolean;\n\n /** Returns a valid (refreshed if needed) access token or an empty string. */\n getAccessToken(): Promise<string>;\n\n /** Returns the customer record and permissions (`/api/permissions/me`). */\n me(): Promise<AccountMe>;\n\n /** Returns the OpenID Connect userinfo claims. */\n userinfo(): Promise<{ [key: string]: any }>;\n\n /** Sends an email change request for the signed in customer. */\n requestEmailChange(newEmail: string): Promise<boolean>;\n\n /** Sends a verification email request. */\n requestVerification(email: string): Promise<boolean>;\n\n /** Sends a typed GraphQL document with the customer token. */\n graphql<Doc extends DocumentInput>(\n document: Doc,\n options?: GraphQLRequestOptions<VariablesOf<Doc>>,\n ): Promise<GraphQLResponse<ResultOf<Doc>>>;\n\n /** Like `graphql()` but returns the `data` and throws a `GraphQLError` for the GraphQL errors. */\n query<Doc extends DocumentInput>(\n document: Doc,\n options?: GraphQLRequestOptions<VariablesOf<Doc>>,\n ): Promise<ResultOf<Doc>>;\n\n /** SSR: returns a redirect response to the hosted login page (requires `requestContext`). */\n loginRedirect(options?: LoginRedirectOptions): Promise<Response>;\n\n /** SSR: handles the callback request and redirects to the `returnTo` page (requires `requestContext`). */\n callbackRedirect(options?: CallbackRedirectOptions): Promise<Response>;\n\n /** SSR: signs out and redirects to the issuer end_session page (requires `requestContext`). */\n logoutRedirect(options?: Omit<AccountLogoutOptions, \"redirect\">): Promise<Response>;\n}\n\n/**\n * Creates the customer account client of an auth collection issuer\n * (hosted login/signup with PKCE, refresh rotation, logout, own sessions and consents),\n * similar to the Hydrogen `createCustomerAccountClient`.\n *\n * - SSR: pass the `requestContext` (the session is stored in its httpOnly cookie) and use\n * the `loginRedirect()`, `callbackRedirect()` and `logoutRedirect()` route helpers.\n * - browser: `login()` redirects and `handleCallback()` completes the sign-in.\n * - mobile: pass an `openAuthSession` adapter and an `AsyncAuthStore`.\n */\nexport function createCustomerAccountClient(\n options: CustomerAccountClientOptions,\n): CustomerAccountClient {\n const context = options.requestContext;\n const { requestContext: _, ...rest } = options;\n\n const client = createAccountClient({\n ...rest,\n authStore: options.authStore || context?.session,\n session: options.session || context?.session,\n backgroundRefresh: context ? false : options.backgroundRefresh,\n // the SSR helpers build the redirect responses themselves\n navigate: options.navigate || (context ? () => {} : undefined),\n // concurrent server loaders must not cancel each other\n autoCancellation: context ? false : undefined,\n });\n const account = client.account;\n\n function requireContext(name: string): RequestContext {\n if (!context) {\n throw new Error(name + \"() requires the requestContext option.\");\n }\n return context;\n }\n\n function redirect(location: string, ctx: RequestContext): Response {\n const headers = new Headers({ Location: location });\n ctx.markPersonalized();\n ctx.applyResponseHeaders(headers);\n\n return new Response(null, { status: 302, headers });\n }\n\n function personalized(): void {\n if (context && client.authStore.token) {\n context.markPersonalized();\n }\n }\n\n async function graphql<Doc extends DocumentInput>(\n document: Doc,\n requestOptions: GraphQLRequestOptions<VariablesOf<Doc>> = {},\n ): Promise<GraphQLResponse<ResultOf<Doc>>> {\n personalized();\n return sendGraphQL(client, document, requestOptions);\n }\n\n return {\n client,\n account,\n login: (opts) => account.login(opts),\n signup: (opts) => account.signup(opts),\n handleCallback: (url) => account.handleCallback(url),\n logout: (opts) => account.logout(opts),\n isLoggedIn: () => account.isLoggedIn(),\n getAccessToken: () => account.getAccessToken(),\n me: () => {\n personalized();\n return account.me();\n },\n userinfo: () => {\n personalized();\n return account.userinfo();\n },\n requestEmailChange: (newEmail) =>\n client.collection(account.config.collection).requestEmailChange(newEmail),\n requestVerification: (email) =>\n client.collection(account.config.collection).requestVerification(email),\n graphql,\n async query(document, requestOptions) {\n return unwrapGraphQLResponse(await graphql(document, requestOptions));\n },\n async loginRedirect(opts = {}) {\n const ctx = requireContext(\"loginRedirect\");\n const { returnTo, ...loginOptions } = opts;\n const state =\n returnTo !== undefined ? { ...loginOptions.state, returnTo } : loginOptions.state;\n\n return redirect(await account.authorizeURL({ ...loginOptions, state }), ctx);\n },\n async callbackRedirect(opts = {}) {\n const ctx = requireContext(\"callbackRedirect\");\n const url = opts.url || ctx.request.url;\n if (!url) {\n throw new Error(\"callbackRedirect() requires the callback url (missing request.url).\");\n }\n\n const result = await account.handleCallback(url);\n const returnTo = result?.state?.returnTo;\n\n return redirect(\n typeof returnTo === \"string\" && isSafeReturnTo(returnTo)\n ? returnTo\n : opts.successRedirect || \"/\",\n ctx,\n );\n },\n async logoutRedirect(opts = {}) {\n const ctx = requireContext(\"logoutRedirect\");\n\n return redirect(await account.logout({ ...opts, redirect: true }), ctx);\n },\n };\n}\n\n/**\n * Reports whether the provided return path is a same-origin relative path\n * (prevents open redirects through the login state).\n */\nexport function isSafeReturnTo(path: string): boolean {\n return path.startsWith(\"/\") && !path.startsWith(\"//\") && !path.startsWith(\"/\\\\\");\n}\n\n/**\n * The login options that a client can ask for through the login route URL\n * (`useCustomer().login()` of a server rendered app).\n */\nexport type LoginRouteOptions = Pick<\n LoginRedirectOptions,\n \"returnTo\" | \"prompt\" | \"maxAge\" | \"loginHint\"\n>;\n\n/**\n * Returns the login route URL with the provided options (see {@link loginOptionsFromURL}).\n */\nexport function loginRouteURL(route: string, options: LoginRouteOptions = {}): string {\n const params = new URLSearchParams();\n if (options.returnTo !== undefined) params.set(\"returnTo\", options.returnTo);\n if (options.prompt !== undefined) params.set(\"prompt\", options.prompt);\n if (options.maxAge !== undefined) params.set(\"maxAge\", String(options.maxAge));\n if (options.loginHint !== undefined) params.set(\"loginHint\", options.loginHint);\n\n const query = params.toString();\n return query ? route + (route.includes(\"?\") ? \"&\" : \"?\") + query : route;\n}\n\n/**\n * Returns the login options of a login route request, for `loginRedirect()`:\n *\n * ```ts\n * return context.customer.loginRedirect(loginOptionsFromURL(request.url));\n * ```\n *\n * The URL is attacker controlled (anyone can send a link): an unsafe `returnTo` (see {@link isSafeReturnTo})\n * and an invalid `maxAge` are dropped, and no other option can be set through it.\n */\nexport function loginOptionsFromURL(url: string | URL): LoginRouteOptions {\n const params = new URL(url, \"http://localhost\").searchParams;\n const result: LoginRouteOptions = {};\n\n const returnTo = params.get(\"returnTo\");\n if (returnTo && isSafeReturnTo(returnTo)) result.returnTo = returnTo;\n\n const prompt = params.get(\"prompt\");\n if (prompt) result.prompt = prompt;\n\n const maxAge = params.get(\"maxAge\");\n if (maxAge && /^\\d+$/.test(maxAge)) result.maxAge = parseInt(maxAge, 10);\n\n const loginHint = params.get(\"loginHint\");\n if (loginHint) result.loginHint = loginHint;\n\n return result;\n}\n\n/**\n * The logout option that a client can ask for through the logout route URL.\n */\nexport type LogoutRouteOptions = Pick<AccountLogoutOptions, \"postLogoutRedirectUri\">;\n\n/**\n * Returns the logout route URL with the provided options (see {@link logoutOptionsFromURL}).\n */\nexport function logoutRouteURL(route: string, options: LogoutRouteOptions = {}): string {\n if (options.postLogoutRedirectUri === undefined) {\n return route;\n }\n const query = new URLSearchParams({ postLogoutRedirectUri: options.postLogoutRedirectUri });\n return route + (route.includes(\"?\") ? \"&\" : \"?\") + query;\n}\n\n/**\n * Returns the logout options of a logout route request, for `logoutRedirect()`.\n * The issuer accepts only the registered post logout redirect URIs of the client.\n */\nexport function logoutOptionsFromURL(url: string | URL): LogoutRouteOptions {\n const uri = new URL(url, \"http://localhost\").searchParams.get(\"postLogoutRedirectUri\");\n return uri ? { postLogoutRedirectUri: uri } : {};\n}\n"],"mappings":"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"}
|
|
@@ -111,4 +111,37 @@ export declare function createCustomerAccountClient(options: CustomerAccountClie
|
|
|
111
111
|
* (prevents open redirects through the login state).
|
|
112
112
|
*/
|
|
113
113
|
export declare function isSafeReturnTo(path: string): boolean;
|
|
114
|
+
/**
|
|
115
|
+
* The login options that a client can ask for through the login route URL
|
|
116
|
+
* (`useCustomer().login()` of a server rendered app).
|
|
117
|
+
*/
|
|
118
|
+
export type LoginRouteOptions = Pick<LoginRedirectOptions, "returnTo" | "prompt" | "maxAge" | "loginHint">;
|
|
119
|
+
/**
|
|
120
|
+
* Returns the login route URL with the provided options (see {@link loginOptionsFromURL}).
|
|
121
|
+
*/
|
|
122
|
+
export declare function loginRouteURL(route: string, options?: LoginRouteOptions): string;
|
|
123
|
+
/**
|
|
124
|
+
* Returns the login options of a login route request, for `loginRedirect()`:
|
|
125
|
+
*
|
|
126
|
+
* ```ts
|
|
127
|
+
* return context.customer.loginRedirect(loginOptionsFromURL(request.url));
|
|
128
|
+
* ```
|
|
129
|
+
*
|
|
130
|
+
* The URL is attacker controlled (anyone can send a link): an unsafe `returnTo` (see {@link isSafeReturnTo})
|
|
131
|
+
* and an invalid `maxAge` are dropped, and no other option can be set through it.
|
|
132
|
+
*/
|
|
133
|
+
export declare function loginOptionsFromURL(url: string | URL): LoginRouteOptions;
|
|
134
|
+
/**
|
|
135
|
+
* The logout option that a client can ask for through the logout route URL.
|
|
136
|
+
*/
|
|
137
|
+
export type LogoutRouteOptions = Pick<AccountLogoutOptions, "postLogoutRedirectUri">;
|
|
138
|
+
/**
|
|
139
|
+
* Returns the logout route URL with the provided options (see {@link logoutOptionsFromURL}).
|
|
140
|
+
*/
|
|
141
|
+
export declare function logoutRouteURL(route: string, options?: LogoutRouteOptions): string;
|
|
142
|
+
/**
|
|
143
|
+
* Returns the logout options of a logout route request, for `logoutRedirect()`.
|
|
144
|
+
* The issuer accepts only the registered post logout redirect URIs of the client.
|
|
145
|
+
*/
|
|
146
|
+
export declare function logoutOptionsFromURL(url: string | URL): LogoutRouteOptions;
|
|
114
147
|
//# sourceMappingURL=customer-account.d.cts.map
|
|
@@ -111,4 +111,37 @@ export declare function createCustomerAccountClient(options: CustomerAccountClie
|
|
|
111
111
|
* (prevents open redirects through the login state).
|
|
112
112
|
*/
|
|
113
113
|
export declare function isSafeReturnTo(path: string): boolean;
|
|
114
|
+
/**
|
|
115
|
+
* The login options that a client can ask for through the login route URL
|
|
116
|
+
* (`useCustomer().login()` of a server rendered app).
|
|
117
|
+
*/
|
|
118
|
+
export type LoginRouteOptions = Pick<LoginRedirectOptions, "returnTo" | "prompt" | "maxAge" | "loginHint">;
|
|
119
|
+
/**
|
|
120
|
+
* Returns the login route URL with the provided options (see {@link loginOptionsFromURL}).
|
|
121
|
+
*/
|
|
122
|
+
export declare function loginRouteURL(route: string, options?: LoginRouteOptions): string;
|
|
123
|
+
/**
|
|
124
|
+
* Returns the login options of a login route request, for `loginRedirect()`:
|
|
125
|
+
*
|
|
126
|
+
* ```ts
|
|
127
|
+
* return context.customer.loginRedirect(loginOptionsFromURL(request.url));
|
|
128
|
+
* ```
|
|
129
|
+
*
|
|
130
|
+
* The URL is attacker controlled (anyone can send a link): an unsafe `returnTo` (see {@link isSafeReturnTo})
|
|
131
|
+
* and an invalid `maxAge` are dropped, and no other option can be set through it.
|
|
132
|
+
*/
|
|
133
|
+
export declare function loginOptionsFromURL(url: string | URL): LoginRouteOptions;
|
|
134
|
+
/**
|
|
135
|
+
* The logout option that a client can ask for through the logout route URL.
|
|
136
|
+
*/
|
|
137
|
+
export type LogoutRouteOptions = Pick<AccountLogoutOptions, "postLogoutRedirectUri">;
|
|
138
|
+
/**
|
|
139
|
+
* Returns the logout route URL with the provided options (see {@link logoutOptionsFromURL}).
|
|
140
|
+
*/
|
|
141
|
+
export declare function logoutRouteURL(route: string, options?: LogoutRouteOptions): string;
|
|
142
|
+
/**
|
|
143
|
+
* Returns the logout options of a logout route request, for `logoutRedirect()`.
|
|
144
|
+
* The issuer accepts only the registered post logout redirect URIs of the client.
|
|
145
|
+
*/
|
|
146
|
+
export declare function logoutOptionsFromURL(url: string | URL): LogoutRouteOptions;
|
|
114
147
|
//# sourceMappingURL=customer-account.d.ts.map
|
|
@@ -101,6 +101,57 @@ function createCustomerAccountClient(options) {
|
|
|
101
101
|
function isSafeReturnTo(path) {
|
|
102
102
|
return path.startsWith("/") && !path.startsWith("//") && !path.startsWith("/\\");
|
|
103
103
|
}
|
|
104
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Returns the login route URL with the provided options (see {@link loginOptionsFromURL}).
|
|
106
|
+
*/
|
|
107
|
+
function loginRouteURL(route, options = {}) {
|
|
108
|
+
const params = new URLSearchParams();
|
|
109
|
+
if (options.returnTo !== void 0) params.set("returnTo", options.returnTo);
|
|
110
|
+
if (options.prompt !== void 0) params.set("prompt", options.prompt);
|
|
111
|
+
if (options.maxAge !== void 0) params.set("maxAge", String(options.maxAge));
|
|
112
|
+
if (options.loginHint !== void 0) params.set("loginHint", options.loginHint);
|
|
113
|
+
const query = params.toString();
|
|
114
|
+
return query ? route + (route.includes("?") ? "&" : "?") + query : route;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Returns the login options of a login route request, for `loginRedirect()`:
|
|
118
|
+
*
|
|
119
|
+
* ```ts
|
|
120
|
+
* return context.customer.loginRedirect(loginOptionsFromURL(request.url));
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* The URL is attacker controlled (anyone can send a link): an unsafe `returnTo` (see {@link isSafeReturnTo})
|
|
124
|
+
* and an invalid `maxAge` are dropped, and no other option can be set through it.
|
|
125
|
+
*/
|
|
126
|
+
function loginOptionsFromURL(url) {
|
|
127
|
+
const params = new URL(url, "http://localhost").searchParams;
|
|
128
|
+
const result = {};
|
|
129
|
+
const returnTo = params.get("returnTo");
|
|
130
|
+
if (returnTo && isSafeReturnTo(returnTo)) result.returnTo = returnTo;
|
|
131
|
+
const prompt = params.get("prompt");
|
|
132
|
+
if (prompt) result.prompt = prompt;
|
|
133
|
+
const maxAge = params.get("maxAge");
|
|
134
|
+
if (maxAge && /^\d+$/.test(maxAge)) result.maxAge = parseInt(maxAge, 10);
|
|
135
|
+
const loginHint = params.get("loginHint");
|
|
136
|
+
if (loginHint) result.loginHint = loginHint;
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Returns the logout route URL with the provided options (see {@link logoutOptionsFromURL}).
|
|
141
|
+
*/
|
|
142
|
+
function logoutRouteURL(route, options = {}) {
|
|
143
|
+
if (options.postLogoutRedirectUri === void 0) return route;
|
|
144
|
+
const query = new URLSearchParams({ postLogoutRedirectUri: options.postLogoutRedirectUri });
|
|
145
|
+
return route + (route.includes("?") ? "&" : "?") + query;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Returns the logout options of a logout route request, for `logoutRedirect()`.
|
|
149
|
+
* The issuer accepts only the registered post logout redirect URIs of the client.
|
|
150
|
+
*/
|
|
151
|
+
function logoutOptionsFromURL(url) {
|
|
152
|
+
const uri = new URL(url, "http://localhost").searchParams.get("postLogoutRedirectUri");
|
|
153
|
+
return uri ? { postLogoutRedirectUri: uri } : {};
|
|
154
|
+
}
|
|
155
|
+
export { createCustomerAccountClient, isSafeReturnTo, loginOptionsFromURL, loginRouteURL, logoutOptionsFromURL, logoutRouteURL };
|
|
105
156
|
|
|
106
157
|
//# sourceMappingURL=customer-account.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"customer-account.js","names":["sendGraphQL"],"sources":["../../src/customer-account/customer-account.ts"],"sourcesContent":["import {\n createAccountClient,\n type Account,\n type AccountCallbackResult,\n type AccountConfig,\n type AccountLoginOptions,\n type AccountLogoutOptions,\n type AccountMe,\n type AuthStore,\n type Client,\n type GraphQLResponse,\n} from \"pocketbomb-primer\";\nimport type { RequestContext } from \"../core/request-context\";\nimport {\n executeGraphQL as sendGraphQL,\n unwrapGraphQLResponse,\n type GraphQLRequestOptions,\n type ResultOf,\n type DocumentInput,\n type VariablesOf,\n} from \"../graphql/graphql\";\n\nexport interface CustomerAccountClientOptions extends Omit<AccountConfig, \"session\"> {\n /**\n * The PocketBomb base URL.\n */\n url: string;\n\n /**\n * Optional public API key of the same OAuth2 client (sent with every request).\n */\n publicKey?: string;\n\n /**\n * Optional `Accept-Language` (default to \"en-US\").\n */\n lang?: string;\n\n /**\n * Optional fetch implementation.\n */\n fetch?: typeof fetch;\n\n /**\n * The per-request context of a server rendered app: the customer session is stored in\n * its httpOnly cookie session (auth state + login flow state) and the `*Redirect()`\n * helpers return redirect responses with the session cookies.\n */\n requestContext?: RequestContext;\n\n /**\n * The auth store (browser/mobile; default to the request context session for SSR,\n * a `LocalAuthStore` in browsers and an in-memory store elsewhere).\n */\n authStore?: AuthStore;\n\n /**\n * The login flow state storage (default to the request context session for SSR).\n */\n session?: AccountConfig[\"session\"];\n}\n\nexport interface LoginRedirectOptions extends AccountLoginOptions {\n /**\n * The app path to return to after the sign-in (stored in the login state).\n */\n returnTo?: string;\n}\n\nexport interface CallbackRedirectOptions {\n /**\n * The callback URL (default to the request context request URL).\n */\n url?: string;\n\n /**\n * Where to redirect after a successful sign-in without a `returnTo` (default to \"/\").\n */\n successRedirect?: string;\n}\n\n/**\n * The customer account client (see {@link createCustomerAccountClient}).\n */\nexport interface CustomerAccountClient {\n /**\n * The underlying primer account client (records REST with the customer token, ...).\n */\n readonly client: Client;\n\n /**\n * The primer OAuth2 account (PKCE, refresh rotation, logout).\n */\n readonly account: Account;\n\n /** Browser redirect (or mobile `openAuthSession`) login. */\n login(options?: AccountLoginOptions): Promise<AccountCallbackResult | null>;\n\n /** Browser redirect (or mobile `openAuthSession`) signup (`prompt=create`). */\n signup(options?: Omit<AccountLoginOptions, \"prompt\">): Promise<AccountCallbackResult | null>;\n\n /** Handles the authorization response of the provided (or the current browser) URL. */\n handleCallback(url?: string): Promise<AccountCallbackResult | null>;\n\n /** Revokes the refresh token, clears the session and ends the issuer session. */\n logout(options?: AccountLogoutOptions): Promise<string>;\n\n /** Loosely checks whether the customer has a valid access token. */\n isLoggedIn(): boolean;\n\n /** Returns a valid (refreshed if needed) access token or an empty string. */\n getAccessToken(): Promise<string>;\n\n /** Returns the customer record and permissions (`/api/permissions/me`). */\n me(): Promise<AccountMe>;\n\n /** Returns the OpenID Connect userinfo claims. */\n userinfo(): Promise<{ [key: string]: any }>;\n\n /** Sends an email change request for the signed in customer. */\n requestEmailChange(newEmail: string): Promise<boolean>;\n\n /** Sends a verification email request. */\n requestVerification(email: string): Promise<boolean>;\n\n /** Sends a typed GraphQL document with the customer token. */\n graphql<Doc extends DocumentInput>(\n document: Doc,\n options?: GraphQLRequestOptions<VariablesOf<Doc>>,\n ): Promise<GraphQLResponse<ResultOf<Doc>>>;\n\n /** Like `graphql()` but returns the `data` and throws a `GraphQLError` for the GraphQL errors. */\n query<Doc extends DocumentInput>(\n document: Doc,\n options?: GraphQLRequestOptions<VariablesOf<Doc>>,\n ): Promise<ResultOf<Doc>>;\n\n /** SSR: returns a redirect response to the hosted login page (requires `requestContext`). */\n loginRedirect(options?: LoginRedirectOptions): Promise<Response>;\n\n /** SSR: handles the callback request and redirects to the `returnTo` page (requires `requestContext`). */\n callbackRedirect(options?: CallbackRedirectOptions): Promise<Response>;\n\n /** SSR: signs out and redirects to the issuer end_session page (requires `requestContext`). */\n logoutRedirect(options?: Omit<AccountLogoutOptions, \"redirect\">): Promise<Response>;\n}\n\n/**\n * Creates the customer account client of an auth collection issuer\n * (hosted login/signup with PKCE, refresh rotation, logout, own sessions and consents),\n * similar to the Hydrogen `createCustomerAccountClient`.\n *\n * - SSR: pass the `requestContext` (the session is stored in its httpOnly cookie) and use\n * the `loginRedirect()`, `callbackRedirect()` and `logoutRedirect()` route helpers.\n * - browser: `login()` redirects and `handleCallback()` completes the sign-in.\n * - mobile: pass an `openAuthSession` adapter and an `AsyncAuthStore`.\n */\nexport function createCustomerAccountClient(\n options: CustomerAccountClientOptions,\n): CustomerAccountClient {\n const context = options.requestContext;\n const { requestContext: _, ...rest } = options;\n\n const client = createAccountClient({\n ...rest,\n authStore: options.authStore || context?.session,\n session: options.session || context?.session,\n backgroundRefresh: context ? false : options.backgroundRefresh,\n // the SSR helpers build the redirect responses themselves\n navigate: options.navigate || (context ? () => {} : undefined),\n // concurrent server loaders must not cancel each other\n autoCancellation: context ? false : undefined,\n });\n const account = client.account;\n\n function requireContext(name: string): RequestContext {\n if (!context) {\n throw new Error(name + \"() requires the requestContext option.\");\n }\n return context;\n }\n\n function redirect(location: string, ctx: RequestContext): Response {\n const headers = new Headers({ Location: location });\n ctx.markPersonalized();\n ctx.applyResponseHeaders(headers);\n\n return new Response(null, { status: 302, headers });\n }\n\n function personalized(): void {\n if (context && client.authStore.token) {\n context.markPersonalized();\n }\n }\n\n async function graphql<Doc extends DocumentInput>(\n document: Doc,\n requestOptions: GraphQLRequestOptions<VariablesOf<Doc>> = {},\n ): Promise<GraphQLResponse<ResultOf<Doc>>> {\n personalized();\n return sendGraphQL(client, document, requestOptions);\n }\n\n return {\n client,\n account,\n login: (opts) => account.login(opts),\n signup: (opts) => account.signup(opts),\n handleCallback: (url) => account.handleCallback(url),\n logout: (opts) => account.logout(opts),\n isLoggedIn: () => account.isLoggedIn(),\n getAccessToken: () => account.getAccessToken(),\n me: () => {\n personalized();\n return account.me();\n },\n userinfo: () => {\n personalized();\n return account.userinfo();\n },\n requestEmailChange: (newEmail) =>\n client.collection(account.config.collection).requestEmailChange(newEmail),\n requestVerification: (email) =>\n client.collection(account.config.collection).requestVerification(email),\n graphql,\n async query(document, requestOptions) {\n return unwrapGraphQLResponse(await graphql(document, requestOptions));\n },\n async loginRedirect(opts = {}) {\n const ctx = requireContext(\"loginRedirect\");\n const { returnTo, ...loginOptions } = opts;\n const state =\n returnTo !== undefined ? { ...loginOptions.state, returnTo } : loginOptions.state;\n\n return redirect(await account.authorizeURL({ ...loginOptions, state }), ctx);\n },\n async callbackRedirect(opts = {}) {\n const ctx = requireContext(\"callbackRedirect\");\n const url = opts.url || ctx.request.url;\n if (!url) {\n throw new Error(\"callbackRedirect() requires the callback url (missing request.url).\");\n }\n\n const result = await account.handleCallback(url);\n const returnTo = result?.state?.returnTo;\n\n return redirect(\n typeof returnTo === \"string\" && isSafeReturnTo(returnTo)\n ? returnTo\n : opts.successRedirect || \"/\",\n ctx,\n );\n },\n async logoutRedirect(opts = {}) {\n const ctx = requireContext(\"logoutRedirect\");\n\n return redirect(await account.logout({ ...opts, redirect: true }), ctx);\n },\n };\n}\n\n/**\n * Reports whether the provided return path is a same-origin relative path\n * (prevents open redirects through the login state).\n */\nexport function isSafeReturnTo(path: string): boolean {\n return path.startsWith(\"/\") && !path.startsWith(\"//\") && !path.startsWith(\"/\\\\\");\n}\n"],"mappings":";;;;;;;;;;;;;AA6JA,SAAgB,4BACd,SACuB;CACvB,MAAM,UAAU,QAAQ;CACxB,MAAM,EAAE,gBAAgB,GAAG,GAAG,SAAS;CAEvC,MAAM,SAAS,oBAAoB;EACjC,GAAG;EACH,WAAW,QAAQ,aAAa,SAAS;EACzC,SAAS,QAAQ,WAAW,SAAS;EACrC,mBAAmB,UAAU,QAAQ,QAAQ;EAE7C,UAAU,QAAQ,aAAa,gBAAgB,CAAC,IAAI,KAAA;EAEpD,kBAAkB,UAAU,QAAQ,KAAA;CACtC,CAAC;CACD,MAAM,UAAU,OAAO;CAEvB,SAAS,eAAe,MAA8B;EACpD,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,OAAO,wCAAwC;EAEjE,OAAO;CACT;CAEA,SAAS,SAAS,UAAkB,KAA+B;EACjE,MAAM,UAAU,IAAI,QAAQ,EAAE,UAAU,SAAS,CAAC;EAClD,IAAI,iBAAiB;EACrB,IAAI,qBAAqB,OAAO;EAEhC,OAAO,IAAI,SAAS,MAAM;GAAE,QAAQ;GAAK;EAAQ,CAAC;CACpD;CAEA,SAAS,eAAqB;EAC5B,IAAI,WAAW,OAAO,UAAU,OAC9B,QAAQ,iBAAiB;CAE7B;CAEA,eAAe,QACb,UACA,iBAA0D,CAAC,GAClB;EACzC,aAAa;EACb,OAAOA,eAAY,QAAQ,UAAU,cAAc;CACrD;CAEA,OAAO;EACL;EACA;EACA,QAAQ,SAAS,QAAQ,MAAM,IAAI;EACnC,SAAS,SAAS,QAAQ,OAAO,IAAI;EACrC,iBAAiB,QAAQ,QAAQ,eAAe,GAAG;EACnD,SAAS,SAAS,QAAQ,OAAO,IAAI;EACrC,kBAAkB,QAAQ,WAAW;EACrC,sBAAsB,QAAQ,eAAe;EAC7C,UAAU;GACR,aAAa;GACb,OAAO,QAAQ,GAAG;EACpB;EACA,gBAAgB;GACd,aAAa;GACb,OAAO,QAAQ,SAAS;EAC1B;EACA,qBAAqB,aACnB,OAAO,WAAW,QAAQ,OAAO,UAAU,CAAC,CAAC,mBAAmB,QAAQ;EAC1E,sBAAsB,UACpB,OAAO,WAAW,QAAQ,OAAO,UAAU,CAAC,CAAC,oBAAoB,KAAK;EACxE;EACA,MAAM,MAAM,UAAU,gBAAgB;GACpC,OAAO,sBAAsB,MAAM,QAAQ,UAAU,cAAc,CAAC;EACtE;EACA,MAAM,cAAc,OAAO,CAAC,GAAG;GAC7B,MAAM,MAAM,eAAe,eAAe;GAC1C,MAAM,EAAE,UAAU,GAAG,iBAAiB;GACtC,MAAM,QACJ,aAAa,KAAA,IAAY;IAAE,GAAG,aAAa;IAAO;GAAS,IAAI,aAAa;GAE9E,OAAO,SAAS,MAAM,QAAQ,aAAa;IAAE,GAAG;IAAc;GAAM,CAAC,GAAG,GAAG;EAC7E;EACA,MAAM,iBAAiB,OAAO,CAAC,GAAG;GAChC,MAAM,MAAM,eAAe,kBAAkB;GAC7C,MAAM,MAAM,KAAK,OAAO,IAAI,QAAQ;GACpC,IAAI,CAAC,KACH,MAAM,IAAI,MAAM,qEAAqE;GAIvF,MAAM,YAAW,MADI,QAAQ,eAAe,GAAG,EAAA,EACtB,OAAO;GAEhC,OAAO,SACL,OAAO,aAAa,YAAY,eAAe,QAAQ,IACnD,WACA,KAAK,mBAAmB,KAC5B,GACF;EACF;EACA,MAAM,eAAe,OAAO,CAAC,GAAG;GAC9B,MAAM,MAAM,eAAe,gBAAgB;GAE3C,OAAO,SAAS,MAAM,QAAQ,OAAO;IAAE,GAAG;IAAM,UAAU;GAAK,CAAC,GAAG,GAAG;EACxE;CACF;AACF;;;;;AAMA,SAAgB,eAAe,MAAuB;CACpD,OAAO,KAAK,WAAW,GAAG,KAAK,CAAC,KAAK,WAAW,IAAI,KAAK,CAAC,KAAK,WAAW,KAAK;AACjF"}
|
|
1
|
+
{"version":3,"file":"customer-account.js","names":["sendGraphQL"],"sources":["../../src/customer-account/customer-account.ts"],"sourcesContent":["import {\n createAccountClient,\n type Account,\n type AccountCallbackResult,\n type AccountConfig,\n type AccountLoginOptions,\n type AccountLogoutOptions,\n type AccountMe,\n type AuthStore,\n type Client,\n type GraphQLResponse,\n} from \"pocketbomb-primer\";\nimport type { RequestContext } from \"../core/request-context\";\nimport {\n executeGraphQL as sendGraphQL,\n unwrapGraphQLResponse,\n type GraphQLRequestOptions,\n type ResultOf,\n type DocumentInput,\n type VariablesOf,\n} from \"../graphql/graphql\";\n\nexport interface CustomerAccountClientOptions extends Omit<AccountConfig, \"session\"> {\n /**\n * The PocketBomb base URL.\n */\n url: string;\n\n /**\n * Optional public API key of the same OAuth2 client (sent with every request).\n */\n publicKey?: string;\n\n /**\n * Optional `Accept-Language` (default to \"en-US\").\n */\n lang?: string;\n\n /**\n * Optional fetch implementation.\n */\n fetch?: typeof fetch;\n\n /**\n * The per-request context of a server rendered app: the customer session is stored in\n * its httpOnly cookie session (auth state + login flow state) and the `*Redirect()`\n * helpers return redirect responses with the session cookies.\n */\n requestContext?: RequestContext;\n\n /**\n * The auth store (browser/mobile; default to the request context session for SSR,\n * a `LocalAuthStore` in browsers and an in-memory store elsewhere).\n */\n authStore?: AuthStore;\n\n /**\n * The login flow state storage (default to the request context session for SSR).\n */\n session?: AccountConfig[\"session\"];\n}\n\nexport interface LoginRedirectOptions extends AccountLoginOptions {\n /**\n * The app path to return to after the sign-in (stored in the login state).\n */\n returnTo?: string;\n}\n\nexport interface CallbackRedirectOptions {\n /**\n * The callback URL (default to the request context request URL).\n */\n url?: string;\n\n /**\n * Where to redirect after a successful sign-in without a `returnTo` (default to \"/\").\n */\n successRedirect?: string;\n}\n\n/**\n * The customer account client (see {@link createCustomerAccountClient}).\n */\nexport interface CustomerAccountClient {\n /**\n * The underlying primer account client (records REST with the customer token, ...).\n */\n readonly client: Client;\n\n /**\n * The primer OAuth2 account (PKCE, refresh rotation, logout).\n */\n readonly account: Account;\n\n /** Browser redirect (or mobile `openAuthSession`) login. */\n login(options?: AccountLoginOptions): Promise<AccountCallbackResult | null>;\n\n /** Browser redirect (or mobile `openAuthSession`) signup (`prompt=create`). */\n signup(options?: Omit<AccountLoginOptions, \"prompt\">): Promise<AccountCallbackResult | null>;\n\n /** Handles the authorization response of the provided (or the current browser) URL. */\n handleCallback(url?: string): Promise<AccountCallbackResult | null>;\n\n /** Revokes the refresh token, clears the session and ends the issuer session. */\n logout(options?: AccountLogoutOptions): Promise<string>;\n\n /** Loosely checks whether the customer has a valid access token. */\n isLoggedIn(): boolean;\n\n /** Returns a valid (refreshed if needed) access token or an empty string. */\n getAccessToken(): Promise<string>;\n\n /** Returns the customer record and permissions (`/api/permissions/me`). */\n me(): Promise<AccountMe>;\n\n /** Returns the OpenID Connect userinfo claims. */\n userinfo(): Promise<{ [key: string]: any }>;\n\n /** Sends an email change request for the signed in customer. */\n requestEmailChange(newEmail: string): Promise<boolean>;\n\n /** Sends a verification email request. */\n requestVerification(email: string): Promise<boolean>;\n\n /** Sends a typed GraphQL document with the customer token. */\n graphql<Doc extends DocumentInput>(\n document: Doc,\n options?: GraphQLRequestOptions<VariablesOf<Doc>>,\n ): Promise<GraphQLResponse<ResultOf<Doc>>>;\n\n /** Like `graphql()` but returns the `data` and throws a `GraphQLError` for the GraphQL errors. */\n query<Doc extends DocumentInput>(\n document: Doc,\n options?: GraphQLRequestOptions<VariablesOf<Doc>>,\n ): Promise<ResultOf<Doc>>;\n\n /** SSR: returns a redirect response to the hosted login page (requires `requestContext`). */\n loginRedirect(options?: LoginRedirectOptions): Promise<Response>;\n\n /** SSR: handles the callback request and redirects to the `returnTo` page (requires `requestContext`). */\n callbackRedirect(options?: CallbackRedirectOptions): Promise<Response>;\n\n /** SSR: signs out and redirects to the issuer end_session page (requires `requestContext`). */\n logoutRedirect(options?: Omit<AccountLogoutOptions, \"redirect\">): Promise<Response>;\n}\n\n/**\n * Creates the customer account client of an auth collection issuer\n * (hosted login/signup with PKCE, refresh rotation, logout, own sessions and consents),\n * similar to the Hydrogen `createCustomerAccountClient`.\n *\n * - SSR: pass the `requestContext` (the session is stored in its httpOnly cookie) and use\n * the `loginRedirect()`, `callbackRedirect()` and `logoutRedirect()` route helpers.\n * - browser: `login()` redirects and `handleCallback()` completes the sign-in.\n * - mobile: pass an `openAuthSession` adapter and an `AsyncAuthStore`.\n */\nexport function createCustomerAccountClient(\n options: CustomerAccountClientOptions,\n): CustomerAccountClient {\n const context = options.requestContext;\n const { requestContext: _, ...rest } = options;\n\n const client = createAccountClient({\n ...rest,\n authStore: options.authStore || context?.session,\n session: options.session || context?.session,\n backgroundRefresh: context ? false : options.backgroundRefresh,\n // the SSR helpers build the redirect responses themselves\n navigate: options.navigate || (context ? () => {} : undefined),\n // concurrent server loaders must not cancel each other\n autoCancellation: context ? false : undefined,\n });\n const account = client.account;\n\n function requireContext(name: string): RequestContext {\n if (!context) {\n throw new Error(name + \"() requires the requestContext option.\");\n }\n return context;\n }\n\n function redirect(location: string, ctx: RequestContext): Response {\n const headers = new Headers({ Location: location });\n ctx.markPersonalized();\n ctx.applyResponseHeaders(headers);\n\n return new Response(null, { status: 302, headers });\n }\n\n function personalized(): void {\n if (context && client.authStore.token) {\n context.markPersonalized();\n }\n }\n\n async function graphql<Doc extends DocumentInput>(\n document: Doc,\n requestOptions: GraphQLRequestOptions<VariablesOf<Doc>> = {},\n ): Promise<GraphQLResponse<ResultOf<Doc>>> {\n personalized();\n return sendGraphQL(client, document, requestOptions);\n }\n\n return {\n client,\n account,\n login: (opts) => account.login(opts),\n signup: (opts) => account.signup(opts),\n handleCallback: (url) => account.handleCallback(url),\n logout: (opts) => account.logout(opts),\n isLoggedIn: () => account.isLoggedIn(),\n getAccessToken: () => account.getAccessToken(),\n me: () => {\n personalized();\n return account.me();\n },\n userinfo: () => {\n personalized();\n return account.userinfo();\n },\n requestEmailChange: (newEmail) =>\n client.collection(account.config.collection).requestEmailChange(newEmail),\n requestVerification: (email) =>\n client.collection(account.config.collection).requestVerification(email),\n graphql,\n async query(document, requestOptions) {\n return unwrapGraphQLResponse(await graphql(document, requestOptions));\n },\n async loginRedirect(opts = {}) {\n const ctx = requireContext(\"loginRedirect\");\n const { returnTo, ...loginOptions } = opts;\n const state =\n returnTo !== undefined ? { ...loginOptions.state, returnTo } : loginOptions.state;\n\n return redirect(await account.authorizeURL({ ...loginOptions, state }), ctx);\n },\n async callbackRedirect(opts = {}) {\n const ctx = requireContext(\"callbackRedirect\");\n const url = opts.url || ctx.request.url;\n if (!url) {\n throw new Error(\"callbackRedirect() requires the callback url (missing request.url).\");\n }\n\n const result = await account.handleCallback(url);\n const returnTo = result?.state?.returnTo;\n\n return redirect(\n typeof returnTo === \"string\" && isSafeReturnTo(returnTo)\n ? returnTo\n : opts.successRedirect || \"/\",\n ctx,\n );\n },\n async logoutRedirect(opts = {}) {\n const ctx = requireContext(\"logoutRedirect\");\n\n return redirect(await account.logout({ ...opts, redirect: true }), ctx);\n },\n };\n}\n\n/**\n * Reports whether the provided return path is a same-origin relative path\n * (prevents open redirects through the login state).\n */\nexport function isSafeReturnTo(path: string): boolean {\n return path.startsWith(\"/\") && !path.startsWith(\"//\") && !path.startsWith(\"/\\\\\");\n}\n\n/**\n * The login options that a client can ask for through the login route URL\n * (`useCustomer().login()` of a server rendered app).\n */\nexport type LoginRouteOptions = Pick<\n LoginRedirectOptions,\n \"returnTo\" | \"prompt\" | \"maxAge\" | \"loginHint\"\n>;\n\n/**\n * Returns the login route URL with the provided options (see {@link loginOptionsFromURL}).\n */\nexport function loginRouteURL(route: string, options: LoginRouteOptions = {}): string {\n const params = new URLSearchParams();\n if (options.returnTo !== undefined) params.set(\"returnTo\", options.returnTo);\n if (options.prompt !== undefined) params.set(\"prompt\", options.prompt);\n if (options.maxAge !== undefined) params.set(\"maxAge\", String(options.maxAge));\n if (options.loginHint !== undefined) params.set(\"loginHint\", options.loginHint);\n\n const query = params.toString();\n return query ? route + (route.includes(\"?\") ? \"&\" : \"?\") + query : route;\n}\n\n/**\n * Returns the login options of a login route request, for `loginRedirect()`:\n *\n * ```ts\n * return context.customer.loginRedirect(loginOptionsFromURL(request.url));\n * ```\n *\n * The URL is attacker controlled (anyone can send a link): an unsafe `returnTo` (see {@link isSafeReturnTo})\n * and an invalid `maxAge` are dropped, and no other option can be set through it.\n */\nexport function loginOptionsFromURL(url: string | URL): LoginRouteOptions {\n const params = new URL(url, \"http://localhost\").searchParams;\n const result: LoginRouteOptions = {};\n\n const returnTo = params.get(\"returnTo\");\n if (returnTo && isSafeReturnTo(returnTo)) result.returnTo = returnTo;\n\n const prompt = params.get(\"prompt\");\n if (prompt) result.prompt = prompt;\n\n const maxAge = params.get(\"maxAge\");\n if (maxAge && /^\\d+$/.test(maxAge)) result.maxAge = parseInt(maxAge, 10);\n\n const loginHint = params.get(\"loginHint\");\n if (loginHint) result.loginHint = loginHint;\n\n return result;\n}\n\n/**\n * The logout option that a client can ask for through the logout route URL.\n */\nexport type LogoutRouteOptions = Pick<AccountLogoutOptions, \"postLogoutRedirectUri\">;\n\n/**\n * Returns the logout route URL with the provided options (see {@link logoutOptionsFromURL}).\n */\nexport function logoutRouteURL(route: string, options: LogoutRouteOptions = {}): string {\n if (options.postLogoutRedirectUri === undefined) {\n return route;\n }\n const query = new URLSearchParams({ postLogoutRedirectUri: options.postLogoutRedirectUri });\n return route + (route.includes(\"?\") ? \"&\" : \"?\") + query;\n}\n\n/**\n * Returns the logout options of a logout route request, for `logoutRedirect()`.\n * The issuer accepts only the registered post logout redirect URIs of the client.\n */\nexport function logoutOptionsFromURL(url: string | URL): LogoutRouteOptions {\n const uri = new URL(url, \"http://localhost\").searchParams.get(\"postLogoutRedirectUri\");\n return uri ? { postLogoutRedirectUri: uri } : {};\n}\n"],"mappings":"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"}
|
|
@@ -2,3 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_customer_account = require("./customer-account.cjs");
|
|
3
3
|
exports.createCustomerAccountClient = require_customer_account.createCustomerAccountClient;
|
|
4
4
|
exports.isSafeReturnTo = require_customer_account.isSafeReturnTo;
|
|
5
|
+
exports.loginOptionsFromURL = require_customer_account.loginOptionsFromURL;
|
|
6
|
+
exports.loginRouteURL = require_customer_account.loginRouteURL;
|
|
7
|
+
exports.logoutOptionsFromURL = require_customer_account.logoutOptionsFromURL;
|
|
8
|
+
exports.logoutRouteURL = require_customer_account.logoutRouteURL;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { CallbackRedirectOptions, CustomerAccountClient, CustomerAccountClientOptions, LoginRedirectOptions, createCustomerAccountClient, isSafeReturnTo } from "./customer-account.cjs";
|
|
2
|
-
export { CallbackRedirectOptions, CustomerAccountClient, CustomerAccountClientOptions, LoginRedirectOptions, createCustomerAccountClient, isSafeReturnTo };
|
|
1
|
+
import { CallbackRedirectOptions, CustomerAccountClient, CustomerAccountClientOptions, LoginRedirectOptions, LoginRouteOptions, LogoutRouteOptions, createCustomerAccountClient, isSafeReturnTo, loginOptionsFromURL, loginRouteURL, logoutOptionsFromURL, logoutRouteURL } from "./customer-account.cjs";
|
|
2
|
+
export { CallbackRedirectOptions, CustomerAccountClient, CustomerAccountClientOptions, LoginRedirectOptions, LoginRouteOptions, LogoutRouteOptions, createCustomerAccountClient, isSafeReturnTo, loginOptionsFromURL, loginRouteURL, logoutOptionsFromURL, logoutRouteURL };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { CallbackRedirectOptions, CustomerAccountClient, CustomerAccountClientOptions, LoginRedirectOptions, createCustomerAccountClient, isSafeReturnTo } from "./customer-account.js";
|
|
2
|
-
export { CallbackRedirectOptions, CustomerAccountClient, CustomerAccountClientOptions, LoginRedirectOptions, createCustomerAccountClient, isSafeReturnTo };
|
|
1
|
+
import { CallbackRedirectOptions, CustomerAccountClient, CustomerAccountClientOptions, LoginRedirectOptions, LoginRouteOptions, LogoutRouteOptions, createCustomerAccountClient, isSafeReturnTo, loginOptionsFromURL, loginRouteURL, logoutOptionsFromURL, logoutRouteURL } from "./customer-account.js";
|
|
2
|
+
export { CallbackRedirectOptions, CustomerAccountClient, CustomerAccountClientOptions, LoginRedirectOptions, LoginRouteOptions, LogoutRouteOptions, createCustomerAccountClient, isSafeReturnTo, loginOptionsFromURL, loginRouteURL, logoutOptionsFromURL, logoutRouteURL };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { createCustomerAccountClient, isSafeReturnTo } from "./customer-account.js";
|
|
1
|
+
export { createCustomerAccountClient, isSafeReturnTo, loginOptionsFromURL, loginRouteURL, logoutOptionsFromURL, logoutRouteURL } from "./customer-account.js";
|