@farcaster/frame-core 0.0.0-canary-20250509164556 → 0.0.0-canary-20250510204457
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/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/schemas/index.d.ts +0 -1
- package/dist/schemas/index.js +0 -1
- package/dist/solana.d.ts +51 -0
- package/dist/solana.js +13 -0
- package/dist/types.d.ts +3 -3
- package/esm/index.d.ts +1 -1
- package/esm/index.js +1 -1
- package/esm/schemas/index.d.ts +0 -1
- package/esm/schemas/index.js +0 -1
- package/esm/solana.d.ts +51 -0
- package/esm/solana.js +9 -0
- package/esm/tsconfig.tsbuildinfo +1 -1
- package/esm/types.d.ts +3 -3
- package/package.json +2 -1
- package/src/index.ts +1 -1
- package/src/schemas/index.ts +0 -1
- package/src/solana.ts +82 -0
- package/src/types.ts +3 -3
- package/dist/funcs.d.ts +0 -20
- package/dist/funcs.js +0 -2
- package/dist/schemas/funcs.d.ts +0 -11
- package/dist/schemas/funcs.js +0 -78
- package/esm/funcs.d.ts +0 -20
- package/esm/funcs.js +0 -1
- package/esm/schemas/funcs.d.ts +0 -11
- package/esm/schemas/funcs.js +0 -75
- package/src/funcs.ts +0 -22
- package/src/schemas/funcs.ts +0 -83
package/src/solana.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Connection as SolanaConnection,
|
|
3
|
+
SendOptions as SolanaSendOptions,
|
|
4
|
+
Transaction as SolanaTransaction,
|
|
5
|
+
VersionedTransaction as SolanaVersionedTransaction,
|
|
6
|
+
} from '@solana/web3.js'
|
|
7
|
+
|
|
8
|
+
export type { SolanaConnection }
|
|
9
|
+
|
|
10
|
+
export type SolanaCombinedTransaction =
|
|
11
|
+
| SolanaTransaction
|
|
12
|
+
| SolanaVersionedTransaction
|
|
13
|
+
|
|
14
|
+
export type SolanaConnectRequestArguments = {
|
|
15
|
+
method: 'connect'
|
|
16
|
+
}
|
|
17
|
+
export type SolanaSignMessageRequestArguments = {
|
|
18
|
+
method: 'signMessage'
|
|
19
|
+
params: {
|
|
20
|
+
message: string
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export type SolanaSignAndSendTransactionRequestArguments = {
|
|
24
|
+
method: 'signAndSendTransaction'
|
|
25
|
+
params: {
|
|
26
|
+
transaction: SolanaCombinedTransaction
|
|
27
|
+
connection: SolanaConnection
|
|
28
|
+
options?: SolanaSendOptions
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export type SolanaSignTransactionRequestArguments<
|
|
32
|
+
T extends SolanaCombinedTransaction = SolanaTransaction,
|
|
33
|
+
> = {
|
|
34
|
+
method: 'signTransaction'
|
|
35
|
+
params: {
|
|
36
|
+
transaction: T
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type SolanaRequestFn = ((
|
|
41
|
+
request: SolanaConnectRequestArguments,
|
|
42
|
+
) => Promise<{ publicKey: string }>) &
|
|
43
|
+
((request: SolanaSignMessageRequestArguments) => Promise<{
|
|
44
|
+
signature: string
|
|
45
|
+
}>) &
|
|
46
|
+
((request: SolanaSignAndSendTransactionRequestArguments) => Promise<{
|
|
47
|
+
signature: string
|
|
48
|
+
}>) &
|
|
49
|
+
(<T extends SolanaCombinedTransaction>(
|
|
50
|
+
request: SolanaSignTransactionRequestArguments<T>,
|
|
51
|
+
) => Promise<{ signedTransaction: T }>)
|
|
52
|
+
|
|
53
|
+
export interface SolanaWalletProvider {
|
|
54
|
+
request: SolanaRequestFn
|
|
55
|
+
|
|
56
|
+
signMessage(message: string): Promise<{ signature: string }>
|
|
57
|
+
signTransaction<T extends SolanaCombinedTransaction>(
|
|
58
|
+
transaction: T,
|
|
59
|
+
): Promise<{ signedTransaction: T }>
|
|
60
|
+
signAndSendTransaction(input: {
|
|
61
|
+
transaction: SolanaCombinedTransaction
|
|
62
|
+
connection: SolanaConnection
|
|
63
|
+
}): Promise<{ signature: string }>
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const createSolanaWalletProvider = (
|
|
67
|
+
request: SolanaRequestFn,
|
|
68
|
+
): SolanaWalletProvider => ({
|
|
69
|
+
request,
|
|
70
|
+
signMessage: (msg: string) =>
|
|
71
|
+
request({ method: 'signMessage', params: { message: msg } }),
|
|
72
|
+
signTransaction: <T extends SolanaCombinedTransaction>(transaction: T) =>
|
|
73
|
+
request({ method: 'signTransaction', params: { transaction } }),
|
|
74
|
+
signAndSendTransaction: (input: {
|
|
75
|
+
transaction: SolanaCombinedTransaction
|
|
76
|
+
connection: SolanaConnection
|
|
77
|
+
}) =>
|
|
78
|
+
request({
|
|
79
|
+
method: 'signAndSendTransaction',
|
|
80
|
+
params: input,
|
|
81
|
+
}),
|
|
82
|
+
})
|
package/src/types.ts
CHANGED
|
@@ -9,13 +9,13 @@ import type {
|
|
|
9
9
|
ViewToken,
|
|
10
10
|
} from './actions'
|
|
11
11
|
import type { FrameContext } from './context'
|
|
12
|
-
import type { ShareStateProvider } from './funcs'
|
|
13
12
|
import type {
|
|
14
13
|
EventFrameAdded,
|
|
15
14
|
EventFrameRemoved,
|
|
16
15
|
EventNotificationsDisabled,
|
|
17
16
|
EventNotificationsEnabled,
|
|
18
17
|
} from './schemas'
|
|
18
|
+
import type { SolanaRequestFn } from './solana'
|
|
19
19
|
import type { Ethereum } from './wallet'
|
|
20
20
|
|
|
21
21
|
export type SetPrimaryButtonOptions = {
|
|
@@ -43,6 +43,7 @@ export type WireFrameHost = {
|
|
|
43
43
|
ethProviderRequest: Ethereum.EthProvideRequest
|
|
44
44
|
ethProviderRequestV2: Ethereum.RpcTransport
|
|
45
45
|
eip6963RequestProvider: () => void
|
|
46
|
+
solanaProviderRequest?: SolanaRequestFn
|
|
46
47
|
addFrame: AddFrame.WireAddFrame
|
|
47
48
|
viewProfile: ViewProfile.ViewProfile
|
|
48
49
|
viewToken: ViewToken.ViewToken
|
|
@@ -51,7 +52,6 @@ export type WireFrameHost = {
|
|
|
51
52
|
composeCast: <close extends boolean | undefined = undefined>(
|
|
52
53
|
options: ComposeCast.Options<close>,
|
|
53
54
|
) => Promise<ComposeCast.Result<close>>
|
|
54
|
-
setShareStateProvider: (fn: ShareStateProvider) => void
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export type FrameHost = {
|
|
@@ -68,6 +68,7 @@ export type FrameHost = {
|
|
|
68
68
|
* Hosts must emit an EventEip6963AnnounceProvider in response.
|
|
69
69
|
*/
|
|
70
70
|
eip6963RequestProvider: () => void
|
|
71
|
+
solanaProviderRequest?: SolanaRequestFn
|
|
71
72
|
addFrame: AddFrame.AddFrame
|
|
72
73
|
viewProfile: ViewProfile.ViewProfile
|
|
73
74
|
viewToken: ViewToken.ViewToken
|
|
@@ -76,7 +77,6 @@ export type FrameHost = {
|
|
|
76
77
|
composeCast: <close extends boolean | undefined = undefined>(
|
|
77
78
|
options: ComposeCast.Options<close>,
|
|
78
79
|
) => Promise<ComposeCast.Result<close>>
|
|
79
|
-
setShareStateProvider: (fn: ShareStateProvider) => void
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
export type EventFrameAddRejected = {
|
package/dist/funcs.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { z } from 'zod';
|
|
2
|
-
import type { sharedStateSchema } from './schemas/funcs';
|
|
3
|
-
/**
|
|
4
|
-
* State used by the host when constructing the mini app URL to be shared.
|
|
5
|
-
*
|
|
6
|
-
* path: The path to be added to the mini app URL when shared. Added either to
|
|
7
|
-
* the `homeUrl` specified in the manifest or the URL of an mini app embed
|
|
8
|
-
* shared on the feed.
|
|
9
|
-
*
|
|
10
|
-
* e.g. `'/specific/miniapp/screen'` (can set `window.location.pathname` directly).
|
|
11
|
-
*
|
|
12
|
-
* params: Query parms to be added to the mini app URL when shared. Compatible with
|
|
13
|
-
* URLSearchParams constructor parameters.
|
|
14
|
-
*
|
|
15
|
-
* Either a key-value pair e.g. `{ foo: 'bar' }`, a string e.g. `'foo=bar'`,
|
|
16
|
-
* or an array of arrays e.g. `[['foo', 'bar'], ['baz', 'qux']]`
|
|
17
|
-
* (can set `window.location.search` directly).
|
|
18
|
-
*/
|
|
19
|
-
export type ShareState = z.infer<typeof sharedStateSchema>;
|
|
20
|
-
export type ShareStateProvider = () => ShareState | Promise<ShareState>;
|
package/dist/funcs.js
DELETED
package/dist/schemas/funcs.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
export declare const sharedStateSchema: z.ZodObject<{
|
|
3
|
-
path: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
4
|
-
params: z.ZodOptional<z.ZodUnion<[z.ZodRecord<z.ZodString, z.ZodString>, z.ZodEffects<z.ZodString, string, string>, z.ZodArray<z.ZodArray<z.ZodString, "many">, "many">]>>;
|
|
5
|
-
}, "strip", z.ZodTypeAny, {
|
|
6
|
-
params?: string | Record<string, string> | string[][] | undefined;
|
|
7
|
-
path?: string | undefined;
|
|
8
|
-
}, {
|
|
9
|
-
params?: string | Record<string, string> | string[][] | undefined;
|
|
10
|
-
path?: string | undefined;
|
|
11
|
-
}>;
|
package/dist/schemas/funcs.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sharedStateSchema = void 0;
|
|
4
|
-
const zod_1 = require("zod");
|
|
5
|
-
const isUrlEncoded = (val) => {
|
|
6
|
-
if (val === '')
|
|
7
|
-
return true;
|
|
8
|
-
try {
|
|
9
|
-
return encodeURIComponent(decodeURIComponent(val)) === val;
|
|
10
|
-
}
|
|
11
|
-
catch (e) {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
exports.sharedStateSchema = zod_1.z.object({
|
|
16
|
-
path: zod_1.z
|
|
17
|
-
.string()
|
|
18
|
-
.refine((pathStr) => {
|
|
19
|
-
if (pathStr === '')
|
|
20
|
-
return true;
|
|
21
|
-
if (!pathStr.startsWith('/'))
|
|
22
|
-
return false;
|
|
23
|
-
if (pathStr.includes('?') || pathStr.includes('#'))
|
|
24
|
-
return false;
|
|
25
|
-
if (pathStr === '/') {
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
const segments = pathStr.substring(1).split('/');
|
|
29
|
-
for (let i = 0; i < segments.length; i++) {
|
|
30
|
-
const segment = segments[i];
|
|
31
|
-
if (segment === '') {
|
|
32
|
-
if (i < segments.length - 1) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
if (!isUrlEncoded(segment)) {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return true;
|
|
42
|
-
}, {
|
|
43
|
-
message: 'Path must be a valid URL-encoded path string, starting with / (e.g., /foo/bar), or an empty string. Consecutive slashes (e.g., /foo//bar) are not allowed.',
|
|
44
|
-
})
|
|
45
|
-
.optional(),
|
|
46
|
-
params: zod_1.z
|
|
47
|
-
.union([
|
|
48
|
-
zod_1.z.record(zod_1.z.string(), zod_1.z.string()),
|
|
49
|
-
zod_1.z.string().refine((queryStr) => {
|
|
50
|
-
const str = queryStr.startsWith('?')
|
|
51
|
-
? queryStr.substring(1)
|
|
52
|
-
: queryStr;
|
|
53
|
-
if (str === '')
|
|
54
|
-
return true;
|
|
55
|
-
if (str.includes('&&')) {
|
|
56
|
-
if (str !== '&&')
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
const pairs = str.split('&');
|
|
60
|
-
for (const pair of pairs) {
|
|
61
|
-
const parts = pair.split('=', 2);
|
|
62
|
-
const key = parts[0];
|
|
63
|
-
if (!isUrlEncoded(key))
|
|
64
|
-
return false;
|
|
65
|
-
if (parts.length > 1) {
|
|
66
|
-
const value = parts[1];
|
|
67
|
-
if (!isUrlEncoded(value))
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return true;
|
|
72
|
-
}, {
|
|
73
|
-
message: "Query string must be a valid URL-encoded query string (e.g., 'foo=bar%20baz&key=val' or '?name=value'). Both keys and values must be URL-encoded.",
|
|
74
|
-
}),
|
|
75
|
-
zod_1.z.array(zod_1.z.array(zod_1.z.string())),
|
|
76
|
-
])
|
|
77
|
-
.optional(),
|
|
78
|
-
});
|
package/esm/funcs.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { z } from 'zod';
|
|
2
|
-
import type { sharedStateSchema } from './schemas/funcs';
|
|
3
|
-
/**
|
|
4
|
-
* State used by the host when constructing the mini app URL to be shared.
|
|
5
|
-
*
|
|
6
|
-
* path: The path to be added to the mini app URL when shared. Added either to
|
|
7
|
-
* the `homeUrl` specified in the manifest or the URL of an mini app embed
|
|
8
|
-
* shared on the feed.
|
|
9
|
-
*
|
|
10
|
-
* e.g. `'/specific/miniapp/screen'` (can set `window.location.pathname` directly).
|
|
11
|
-
*
|
|
12
|
-
* params: Query parms to be added to the mini app URL when shared. Compatible with
|
|
13
|
-
* URLSearchParams constructor parameters.
|
|
14
|
-
*
|
|
15
|
-
* Either a key-value pair e.g. `{ foo: 'bar' }`, a string e.g. `'foo=bar'`,
|
|
16
|
-
* or an array of arrays e.g. `[['foo', 'bar'], ['baz', 'qux']]`
|
|
17
|
-
* (can set `window.location.search` directly).
|
|
18
|
-
*/
|
|
19
|
-
export type ShareState = z.infer<typeof sharedStateSchema>;
|
|
20
|
-
export type ShareStateProvider = () => ShareState | Promise<ShareState>;
|
package/esm/funcs.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/esm/schemas/funcs.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
export declare const sharedStateSchema: z.ZodObject<{
|
|
3
|
-
path: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
4
|
-
params: z.ZodOptional<z.ZodUnion<[z.ZodRecord<z.ZodString, z.ZodString>, z.ZodEffects<z.ZodString, string, string>, z.ZodArray<z.ZodArray<z.ZodString, "many">, "many">]>>;
|
|
5
|
-
}, "strip", z.ZodTypeAny, {
|
|
6
|
-
params?: string | Record<string, string> | string[][] | undefined;
|
|
7
|
-
path?: string | undefined;
|
|
8
|
-
}, {
|
|
9
|
-
params?: string | Record<string, string> | string[][] | undefined;
|
|
10
|
-
path?: string | undefined;
|
|
11
|
-
}>;
|
package/esm/schemas/funcs.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
const isUrlEncoded = (val) => {
|
|
3
|
-
if (val === '')
|
|
4
|
-
return true;
|
|
5
|
-
try {
|
|
6
|
-
return encodeURIComponent(decodeURIComponent(val)) === val;
|
|
7
|
-
}
|
|
8
|
-
catch (e) {
|
|
9
|
-
return false;
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
export const sharedStateSchema = z.object({
|
|
13
|
-
path: z
|
|
14
|
-
.string()
|
|
15
|
-
.refine((pathStr) => {
|
|
16
|
-
if (pathStr === '')
|
|
17
|
-
return true;
|
|
18
|
-
if (!pathStr.startsWith('/'))
|
|
19
|
-
return false;
|
|
20
|
-
if (pathStr.includes('?') || pathStr.includes('#'))
|
|
21
|
-
return false;
|
|
22
|
-
if (pathStr === '/') {
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
const segments = pathStr.substring(1).split('/');
|
|
26
|
-
for (let i = 0; i < segments.length; i++) {
|
|
27
|
-
const segment = segments[i];
|
|
28
|
-
if (segment === '') {
|
|
29
|
-
if (i < segments.length - 1) {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
if (!isUrlEncoded(segment)) {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return true;
|
|
39
|
-
}, {
|
|
40
|
-
message: 'Path must be a valid URL-encoded path string, starting with / (e.g., /foo/bar), or an empty string. Consecutive slashes (e.g., /foo//bar) are not allowed.',
|
|
41
|
-
})
|
|
42
|
-
.optional(),
|
|
43
|
-
params: z
|
|
44
|
-
.union([
|
|
45
|
-
z.record(z.string(), z.string()),
|
|
46
|
-
z.string().refine((queryStr) => {
|
|
47
|
-
const str = queryStr.startsWith('?')
|
|
48
|
-
? queryStr.substring(1)
|
|
49
|
-
: queryStr;
|
|
50
|
-
if (str === '')
|
|
51
|
-
return true;
|
|
52
|
-
if (str.includes('&&')) {
|
|
53
|
-
if (str !== '&&')
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
const pairs = str.split('&');
|
|
57
|
-
for (const pair of pairs) {
|
|
58
|
-
const parts = pair.split('=', 2);
|
|
59
|
-
const key = parts[0];
|
|
60
|
-
if (!isUrlEncoded(key))
|
|
61
|
-
return false;
|
|
62
|
-
if (parts.length > 1) {
|
|
63
|
-
const value = parts[1];
|
|
64
|
-
if (!isUrlEncoded(value))
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return true;
|
|
69
|
-
}, {
|
|
70
|
-
message: "Query string must be a valid URL-encoded query string (e.g., 'foo=bar%20baz&key=val' or '?name=value'). Both keys and values must be URL-encoded.",
|
|
71
|
-
}),
|
|
72
|
-
z.array(z.array(z.string())),
|
|
73
|
-
])
|
|
74
|
-
.optional(),
|
|
75
|
-
});
|
package/src/funcs.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { z } from 'zod'
|
|
2
|
-
import type { sharedStateSchema } from './schemas/funcs'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* State used by the host when constructing the mini app URL to be shared.
|
|
6
|
-
*
|
|
7
|
-
* path: The path to be added to the mini app URL when shared. Added either to
|
|
8
|
-
* the `homeUrl` specified in the manifest or the URL of an mini app embed
|
|
9
|
-
* shared on the feed.
|
|
10
|
-
*
|
|
11
|
-
* e.g. `'/specific/miniapp/screen'` (can set `window.location.pathname` directly).
|
|
12
|
-
*
|
|
13
|
-
* params: Query parms to be added to the mini app URL when shared. Compatible with
|
|
14
|
-
* URLSearchParams constructor parameters.
|
|
15
|
-
*
|
|
16
|
-
* Either a key-value pair e.g. `{ foo: 'bar' }`, a string e.g. `'foo=bar'`,
|
|
17
|
-
* or an array of arrays e.g. `[['foo', 'bar'], ['baz', 'qux']]`
|
|
18
|
-
* (can set `window.location.search` directly).
|
|
19
|
-
*/
|
|
20
|
-
export type ShareState = z.infer<typeof sharedStateSchema>
|
|
21
|
-
|
|
22
|
-
export type ShareStateProvider = () => ShareState | Promise<ShareState>
|
package/src/schemas/funcs.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
|
|
3
|
-
const isUrlEncoded = (val: string): boolean => {
|
|
4
|
-
if (val === '') return true
|
|
5
|
-
try {
|
|
6
|
-
return encodeURIComponent(decodeURIComponent(val)) === val
|
|
7
|
-
} catch (e) {
|
|
8
|
-
return false
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const sharedStateSchema = z.object({
|
|
13
|
-
path: z
|
|
14
|
-
.string()
|
|
15
|
-
.refine(
|
|
16
|
-
(pathStr) => {
|
|
17
|
-
if (pathStr === '') return true
|
|
18
|
-
if (!pathStr.startsWith('/')) return false
|
|
19
|
-
if (pathStr.includes('?') || pathStr.includes('#')) return false
|
|
20
|
-
|
|
21
|
-
if (pathStr === '/') {
|
|
22
|
-
return true
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const segments = pathStr.substring(1).split('/')
|
|
26
|
-
for (let i = 0; i < segments.length; i++) {
|
|
27
|
-
const segment = segments[i]
|
|
28
|
-
if (segment === '') {
|
|
29
|
-
if (i < segments.length - 1) {
|
|
30
|
-
return false
|
|
31
|
-
}
|
|
32
|
-
return true
|
|
33
|
-
}
|
|
34
|
-
if (!isUrlEncoded(segment)) {
|
|
35
|
-
return false
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return true
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
message:
|
|
42
|
-
'Path must be a valid URL-encoded path string, starting with / (e.g., /foo/bar), or an empty string. Consecutive slashes (e.g., /foo//bar) are not allowed.',
|
|
43
|
-
},
|
|
44
|
-
)
|
|
45
|
-
.optional(),
|
|
46
|
-
params: z
|
|
47
|
-
.union([
|
|
48
|
-
z.record(z.string(), z.string()),
|
|
49
|
-
z.string().refine(
|
|
50
|
-
(queryStr) => {
|
|
51
|
-
const str = queryStr.startsWith('?')
|
|
52
|
-
? queryStr.substring(1)
|
|
53
|
-
: queryStr
|
|
54
|
-
if (str === '') return true
|
|
55
|
-
|
|
56
|
-
if (str.includes('&&')) {
|
|
57
|
-
if (str !== '&&') return false
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const pairs = str.split('&')
|
|
61
|
-
|
|
62
|
-
for (const pair of pairs) {
|
|
63
|
-
const parts = pair.split('=', 2)
|
|
64
|
-
const key = parts[0]
|
|
65
|
-
|
|
66
|
-
if (!isUrlEncoded(key)) return false
|
|
67
|
-
|
|
68
|
-
if (parts.length > 1) {
|
|
69
|
-
const value = parts[1]
|
|
70
|
-
if (!isUrlEncoded(value)) return false
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return true
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
message:
|
|
77
|
-
"Query string must be a valid URL-encoded query string (e.g., 'foo=bar%20baz&key=val' or '?name=value'). Both keys and values must be URL-encoded.",
|
|
78
|
-
},
|
|
79
|
-
),
|
|
80
|
-
z.array(z.array(z.string())),
|
|
81
|
-
])
|
|
82
|
-
.optional(),
|
|
83
|
-
})
|