@benjavicente/start-client-core 1.167.9
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/LICENSE +21 -0
- package/README.md +12 -0
- package/bin/intent.js +25 -0
- package/dist/esm/client/ServerFunctionSerializationAdapter.d.ts +7 -0
- package/dist/esm/client/ServerFunctionSerializationAdapter.js +18 -0
- package/dist/esm/client/ServerFunctionSerializationAdapter.js.map +1 -0
- package/dist/esm/client/hydrateStart.d.ts +2 -0
- package/dist/esm/client/hydrateStart.js +31 -0
- package/dist/esm/client/hydrateStart.js.map +1 -0
- package/dist/esm/client/index.d.ts +2 -0
- package/dist/esm/client/index.js +2 -0
- package/dist/esm/client-rpc/createClientRpc.d.ts +6 -0
- package/dist/esm/client-rpc/createClientRpc.js +21 -0
- package/dist/esm/client-rpc/createClientRpc.js.map +1 -0
- package/dist/esm/client-rpc/frame-decoder.d.ts +23 -0
- package/dist/esm/client-rpc/frame-decoder.js +231 -0
- package/dist/esm/client-rpc/frame-decoder.js.map +1 -0
- package/dist/esm/client-rpc/index.d.ts +1 -0
- package/dist/esm/client-rpc/index.js +2 -0
- package/dist/esm/client-rpc/serverFnFetcher.d.ts +1 -0
- package/dist/esm/client-rpc/serverFnFetcher.js +231 -0
- package/dist/esm/client-rpc/serverFnFetcher.js.map +1 -0
- package/dist/esm/constants.d.ts +53 -0
- package/dist/esm/constants.js +46 -0
- package/dist/esm/constants.js.map +1 -0
- package/dist/esm/createMiddleware.d.ts +195 -0
- package/dist/esm/createMiddleware.js +26 -0
- package/dist/esm/createMiddleware.js.map +1 -0
- package/dist/esm/createServerFn.d.ts +131 -0
- package/dist/esm/createServerFn.js +200 -0
- package/dist/esm/createServerFn.js.map +1 -0
- package/dist/esm/createStart.d.ts +50 -0
- package/dist/esm/createStart.js +29 -0
- package/dist/esm/createStart.js.map +1 -0
- package/dist/esm/fake-start-entry.d.ts +2 -0
- package/dist/esm/fake-start-entry.js +7 -0
- package/dist/esm/fake-start-entry.js.map +1 -0
- package/dist/esm/getDefaultSerovalPlugins.d.ts +1 -0
- package/dist/esm/getDefaultSerovalPlugins.js +10 -0
- package/dist/esm/getDefaultSerovalPlugins.js.map +1 -0
- package/dist/esm/getGlobalStartContext.d.ts +3 -0
- package/dist/esm/getGlobalStartContext.js +12 -0
- package/dist/esm/getGlobalStartContext.js.map +1 -0
- package/dist/esm/getRouterInstance.d.ts +2 -0
- package/dist/esm/getRouterInstance.js +8 -0
- package/dist/esm/getRouterInstance.js.map +1 -0
- package/dist/esm/getStartContextServerOnly.d.ts +2 -0
- package/dist/esm/getStartContextServerOnly.js +8 -0
- package/dist/esm/getStartContextServerOnly.js.map +1 -0
- package/dist/esm/getStartOptions.d.ts +2 -0
- package/dist/esm/getStartOptions.js +8 -0
- package/dist/esm/getStartOptions.js.map +1 -0
- package/dist/esm/global.d.ts +7 -0
- package/dist/esm/index.d.ts +20 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/safeObjectMerge.d.ts +10 -0
- package/dist/esm/safeObjectMerge.js +30 -0
- package/dist/esm/safeObjectMerge.js.map +1 -0
- package/dist/esm/serverRoute.d.ts +65 -0
- package/dist/esm/startEntry.d.ts +8 -0
- package/dist/esm/tests/createServerFn.test-d.d.ts +1 -0
- package/dist/esm/tests/createServerMiddleware.test-d.d.ts +1 -0
- package/package.json +98 -0
- package/skills/start-core/SKILL.md +210 -0
- package/skills/start-core/deployment/SKILL.md +306 -0
- package/skills/start-core/execution-model/SKILL.md +302 -0
- package/skills/start-core/middleware/SKILL.md +365 -0
- package/skills/start-core/server-functions/SKILL.md +335 -0
- package/skills/start-core/server-routes/SKILL.md +280 -0
- package/src/client/ServerFunctionSerializationAdapter.ts +16 -0
- package/src/client/hydrateStart.ts +43 -0
- package/src/client/index.ts +2 -0
- package/src/client-rpc/createClientRpc.ts +20 -0
- package/src/client-rpc/frame-decoder.ts +389 -0
- package/src/client-rpc/index.ts +1 -0
- package/src/client-rpc/serverFnFetcher.ts +416 -0
- package/src/constants.ts +90 -0
- package/src/createMiddleware.ts +824 -0
- package/src/createServerFn.ts +813 -0
- package/src/createStart.ts +166 -0
- package/src/fake-start-entry.ts +2 -0
- package/src/getDefaultSerovalPlugins.ts +17 -0
- package/src/getGlobalStartContext.ts +18 -0
- package/src/getRouterInstance.ts +8 -0
- package/src/getStartContextServerOnly.ts +4 -0
- package/src/getStartOptions.ts +8 -0
- package/src/global.ts +9 -0
- package/src/index.tsx +119 -0
- package/src/safeObjectMerge.ts +38 -0
- package/src/serverRoute.ts +509 -0
- package/src/start-entry.d.ts +11 -0
- package/src/startEntry.ts +10 -0
- package/src/tests/createServerFn.test-d.ts +866 -0
- package/src/tests/createServerMiddleware.test-d.ts +810 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { createMiddleware } from './createMiddleware'
|
|
2
|
+
import type { TSS_SERVER_FUNCTION } from './constants'
|
|
3
|
+
import type {
|
|
4
|
+
AnyFunctionMiddleware,
|
|
5
|
+
AnyRequestMiddleware,
|
|
6
|
+
CreateMiddlewareFn,
|
|
7
|
+
} from './createMiddleware'
|
|
8
|
+
import type { CustomFetch } from './createServerFn'
|
|
9
|
+
import type {
|
|
10
|
+
AnySerializationAdapter,
|
|
11
|
+
Register,
|
|
12
|
+
SSROption,
|
|
13
|
+
} from '@benjavicente/router-core'
|
|
14
|
+
|
|
15
|
+
export interface StartInstanceOptions<
|
|
16
|
+
in out TSerializationAdapters,
|
|
17
|
+
in out TDefaultSsr,
|
|
18
|
+
in out TRequestMiddlewares,
|
|
19
|
+
in out TFunctionMiddlewares,
|
|
20
|
+
> {
|
|
21
|
+
'~types': StartInstanceTypes<
|
|
22
|
+
TSerializationAdapters,
|
|
23
|
+
TDefaultSsr,
|
|
24
|
+
TRequestMiddlewares,
|
|
25
|
+
TFunctionMiddlewares
|
|
26
|
+
>
|
|
27
|
+
serializationAdapters?: TSerializationAdapters
|
|
28
|
+
defaultSsr?: TDefaultSsr
|
|
29
|
+
requestMiddleware?: TRequestMiddlewares
|
|
30
|
+
functionMiddleware?: TFunctionMiddlewares
|
|
31
|
+
/**
|
|
32
|
+
* Configuration options for server functions.
|
|
33
|
+
*/
|
|
34
|
+
serverFns?: {
|
|
35
|
+
/**
|
|
36
|
+
* A custom fetch implementation to use for all server function calls.
|
|
37
|
+
* This can be overridden by middleware or at the call site.
|
|
38
|
+
*
|
|
39
|
+
* Precedence (highest to lowest):
|
|
40
|
+
* 1. Call site: `serverFn({ fetch: customFetch })`
|
|
41
|
+
* 2. Later middleware: Last middleware in chain that provides `fetch`
|
|
42
|
+
* 3. Earlier middleware: First middleware in chain that provides `fetch`
|
|
43
|
+
* 4. createStart: `createStart({ serverFns: { fetch: customFetch } })`
|
|
44
|
+
* 5. Default: Global `fetch` function
|
|
45
|
+
*
|
|
46
|
+
* @note Only applies on the client side. During SSR, server functions are called directly.
|
|
47
|
+
*/
|
|
48
|
+
fetch?: CustomFetch
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface StartInstance<
|
|
53
|
+
in out TSerializationAdapters,
|
|
54
|
+
in out TDefaultSsr,
|
|
55
|
+
in out TRequestMiddlewares,
|
|
56
|
+
in out TFunctionMiddlewares,
|
|
57
|
+
> {
|
|
58
|
+
getOptions: () =>
|
|
59
|
+
| Promise<
|
|
60
|
+
StartInstanceOptions<
|
|
61
|
+
TSerializationAdapters,
|
|
62
|
+
TDefaultSsr,
|
|
63
|
+
TRequestMiddlewares,
|
|
64
|
+
TFunctionMiddlewares
|
|
65
|
+
>
|
|
66
|
+
>
|
|
67
|
+
| StartInstanceOptions<
|
|
68
|
+
TSerializationAdapters,
|
|
69
|
+
TDefaultSsr,
|
|
70
|
+
TRequestMiddlewares,
|
|
71
|
+
TFunctionMiddlewares
|
|
72
|
+
>
|
|
73
|
+
createMiddleware: CreateMiddlewareFn<Register>
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface StartInstanceTypes<
|
|
77
|
+
in out TSerializationAdapters,
|
|
78
|
+
in out TDefaultSsr,
|
|
79
|
+
in out TRequestMiddlewares,
|
|
80
|
+
in out TFunctionMiddlewares,
|
|
81
|
+
> {
|
|
82
|
+
serializationAdapters: TSerializationAdapters
|
|
83
|
+
defaultSsr: TDefaultSsr
|
|
84
|
+
requestMiddleware: TRequestMiddlewares
|
|
85
|
+
functionMiddleware: TFunctionMiddlewares
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function dedupeSerializationAdapters(
|
|
89
|
+
deduped: Set<AnySerializationAdapter>,
|
|
90
|
+
serializationAdapters: Array<AnySerializationAdapter>,
|
|
91
|
+
): void {
|
|
92
|
+
for (let i = 0, len = serializationAdapters.length; i < len; i++) {
|
|
93
|
+
const current = serializationAdapters[i]!
|
|
94
|
+
if (!deduped.has(current)) {
|
|
95
|
+
deduped.add(current)
|
|
96
|
+
if (current.extends) {
|
|
97
|
+
dedupeSerializationAdapters(deduped, current.extends)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const createStart = <
|
|
104
|
+
const TSerializationAdapters extends ReadonlyArray<AnySerializationAdapter> =
|
|
105
|
+
[],
|
|
106
|
+
TDefaultSsr extends SSROption = SSROption,
|
|
107
|
+
const TRequestMiddlewares extends ReadonlyArray<AnyRequestMiddleware> = [],
|
|
108
|
+
const TFunctionMiddlewares extends ReadonlyArray<AnyFunctionMiddleware> = [],
|
|
109
|
+
>(
|
|
110
|
+
getOptions: () =>
|
|
111
|
+
| Promise<
|
|
112
|
+
Omit<
|
|
113
|
+
StartInstanceOptions<
|
|
114
|
+
TSerializationAdapters,
|
|
115
|
+
TDefaultSsr,
|
|
116
|
+
TRequestMiddlewares,
|
|
117
|
+
TFunctionMiddlewares
|
|
118
|
+
>,
|
|
119
|
+
'~types'
|
|
120
|
+
>
|
|
121
|
+
>
|
|
122
|
+
| Omit<
|
|
123
|
+
StartInstanceOptions<
|
|
124
|
+
TSerializationAdapters,
|
|
125
|
+
TDefaultSsr,
|
|
126
|
+
TRequestMiddlewares,
|
|
127
|
+
TFunctionMiddlewares
|
|
128
|
+
>,
|
|
129
|
+
'~types'
|
|
130
|
+
>,
|
|
131
|
+
): StartInstance<
|
|
132
|
+
TSerializationAdapters,
|
|
133
|
+
TDefaultSsr,
|
|
134
|
+
TRequestMiddlewares,
|
|
135
|
+
TFunctionMiddlewares
|
|
136
|
+
> => {
|
|
137
|
+
return {
|
|
138
|
+
getOptions: async () => {
|
|
139
|
+
const options = await getOptions()
|
|
140
|
+
if (options.serializationAdapters) {
|
|
141
|
+
const deduped = new Set<AnySerializationAdapter>()
|
|
142
|
+
dedupeSerializationAdapters(
|
|
143
|
+
deduped,
|
|
144
|
+
options.serializationAdapters as unknown as Array<AnySerializationAdapter>,
|
|
145
|
+
)
|
|
146
|
+
options.serializationAdapters = Array.from(deduped) as any
|
|
147
|
+
}
|
|
148
|
+
return options
|
|
149
|
+
},
|
|
150
|
+
createMiddleware: createMiddleware,
|
|
151
|
+
} as StartInstance<
|
|
152
|
+
TSerializationAdapters,
|
|
153
|
+
TDefaultSsr,
|
|
154
|
+
TRequestMiddlewares,
|
|
155
|
+
TFunctionMiddlewares
|
|
156
|
+
>
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export type AnyStartInstance = StartInstance<any, any, any, any>
|
|
160
|
+
export type AnyStartInstanceOptions = StartInstanceOptions<any, any, any, any>
|
|
161
|
+
|
|
162
|
+
declare module '@benjavicente/router-core' {
|
|
163
|
+
interface SerializableExtensions {
|
|
164
|
+
serverFn: { [TSS_SERVER_FUNCTION]: true }
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
makeSerovalPlugin,
|
|
3
|
+
defaultSerovalPlugins as routerDefaultSerovalPlugins,
|
|
4
|
+
} from '@benjavicente/router-core'
|
|
5
|
+
import { getStartOptions } from './getStartOptions'
|
|
6
|
+
import type { AnySerializationAdapter } from '@benjavicente/router-core'
|
|
7
|
+
|
|
8
|
+
export function getDefaultSerovalPlugins() {
|
|
9
|
+
const start = getStartOptions()
|
|
10
|
+
const adapters = start?.serializationAdapters as
|
|
11
|
+
| Array<AnySerializationAdapter>
|
|
12
|
+
| undefined
|
|
13
|
+
return [
|
|
14
|
+
...(adapters?.map(makeSerovalPlugin) ?? []),
|
|
15
|
+
...routerDefaultSerovalPlugins,
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { getStartContext } from '@benjavicente/start-storage-context'
|
|
2
|
+
import { createIsomorphicFn } from '@benjavicente/start-fn-stubs'
|
|
3
|
+
import type { AssignAllServerRequestContext } from './createMiddleware'
|
|
4
|
+
import type { Expand, Register } from '@benjavicente/router-core'
|
|
5
|
+
|
|
6
|
+
export const getGlobalStartContext: () =>
|
|
7
|
+
| Expand<AssignAllServerRequestContext<Register, []>>
|
|
8
|
+
| undefined = createIsomorphicFn()
|
|
9
|
+
.client(() => undefined)
|
|
10
|
+
.server(() => {
|
|
11
|
+
const context = getStartContext().contextAfterGlobalMiddlewares
|
|
12
|
+
if (!context) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
`Global context not set yet, you are calling getGlobalStartContext() before the global middlewares are applied.`,
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
return context
|
|
18
|
+
})
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { getStartContext } from '@benjavicente/start-storage-context'
|
|
2
|
+
import { createIsomorphicFn } from '@benjavicente/start-fn-stubs'
|
|
3
|
+
import type { Awaitable, RegisteredRouter } from '@benjavicente/router-core'
|
|
4
|
+
|
|
5
|
+
export const getRouterInstance: () => Awaitable<RegisteredRouter> =
|
|
6
|
+
createIsomorphicFn()
|
|
7
|
+
.client(() => window.__TSR_ROUTER__!)
|
|
8
|
+
.server(() => getStartContext().getRouter())
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { getStartContext } from '@benjavicente/start-storage-context'
|
|
2
|
+
import { createIsomorphicFn } from '@benjavicente/start-fn-stubs'
|
|
3
|
+
import type { AnyStartInstanceOptions } from './createStart'
|
|
4
|
+
|
|
5
|
+
export const getStartOptions: () => AnyStartInstanceOptions | undefined =
|
|
6
|
+
createIsomorphicFn()
|
|
7
|
+
.client(() => window.__TSS_START_OPTIONS__)
|
|
8
|
+
.server(() => getStartContext().startOptions)
|
package/src/global.ts
ADDED
package/src/index.tsx
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
export type { JsonResponse } from '@benjavicente/router-core/ssr/client'
|
|
2
|
+
|
|
3
|
+
export { hydrate, json, mergeHeaders } from '@benjavicente/router-core/ssr/client'
|
|
4
|
+
|
|
5
|
+
export { RawStream } from '@benjavicente/router-core'
|
|
6
|
+
export type { OnRawStreamCallback } from '@benjavicente/router-core'
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
createIsomorphicFn,
|
|
10
|
+
createServerOnlyFn,
|
|
11
|
+
createClientOnlyFn,
|
|
12
|
+
type IsomorphicFn,
|
|
13
|
+
type ServerOnlyFn,
|
|
14
|
+
type ClientOnlyFn,
|
|
15
|
+
type IsomorphicFnBase,
|
|
16
|
+
} from '@benjavicente/start-fn-stubs'
|
|
17
|
+
export { createServerFn } from './createServerFn'
|
|
18
|
+
export {
|
|
19
|
+
createMiddleware,
|
|
20
|
+
type IntersectAllValidatorInputs,
|
|
21
|
+
type IntersectAllValidatorOutputs,
|
|
22
|
+
type FunctionMiddlewareServerFn,
|
|
23
|
+
type AnyFunctionMiddleware,
|
|
24
|
+
type FunctionMiddlewareOptions,
|
|
25
|
+
type FunctionMiddlewareWithTypes,
|
|
26
|
+
type FunctionMiddlewareValidator,
|
|
27
|
+
type FunctionMiddlewareServer,
|
|
28
|
+
type FunctionMiddlewareAfterClient,
|
|
29
|
+
type FunctionMiddlewareAfterServer,
|
|
30
|
+
type FunctionMiddleware,
|
|
31
|
+
type FunctionMiddlewareAfterMiddleware,
|
|
32
|
+
type FunctionMiddlewareClientFnOptions,
|
|
33
|
+
type FunctionMiddlewareClientFnResult,
|
|
34
|
+
type FunctionMiddlewareClientNextFn,
|
|
35
|
+
type FunctionClientResultWithContext,
|
|
36
|
+
type AssignAllClientContextBeforeNext,
|
|
37
|
+
type AssignAllMiddleware,
|
|
38
|
+
type FunctionMiddlewareAfterValidator,
|
|
39
|
+
type FunctionMiddlewareClientFn,
|
|
40
|
+
type FunctionMiddlewareServerFnResult,
|
|
41
|
+
type FunctionMiddlewareClient,
|
|
42
|
+
type FunctionMiddlewareServerFnOptions,
|
|
43
|
+
type FunctionMiddlewareServerNextFn,
|
|
44
|
+
type FunctionServerResultWithContext,
|
|
45
|
+
type AnyRequestMiddleware,
|
|
46
|
+
type RequestMiddlewareOptions,
|
|
47
|
+
type RequestMiddlewareWithTypes,
|
|
48
|
+
type RequestMiddlewareServer,
|
|
49
|
+
type RequestMiddlewareAfterServer,
|
|
50
|
+
type RequestMiddleware,
|
|
51
|
+
type RequestMiddlewareAfterMiddleware,
|
|
52
|
+
type RequestServerFn,
|
|
53
|
+
type RequestMiddlewareServerFnResult,
|
|
54
|
+
type RequestServerOptions,
|
|
55
|
+
type RequestServerNextFn,
|
|
56
|
+
type RequestServerNextFnOptions,
|
|
57
|
+
type RequestServerResult,
|
|
58
|
+
} from './createMiddleware'
|
|
59
|
+
export type {
|
|
60
|
+
CompiledFetcherFnOptions,
|
|
61
|
+
CompiledFetcherFn,
|
|
62
|
+
CustomFetch,
|
|
63
|
+
Fetcher,
|
|
64
|
+
RscStream,
|
|
65
|
+
FetcherBaseOptions,
|
|
66
|
+
ServerFn,
|
|
67
|
+
ServerFnCtx,
|
|
68
|
+
MiddlewareFn,
|
|
69
|
+
ServerFnMiddlewareOptions,
|
|
70
|
+
ServerFnMiddlewareResult,
|
|
71
|
+
ServerFnBuilder,
|
|
72
|
+
ServerFnBaseOptions,
|
|
73
|
+
NextFn,
|
|
74
|
+
Method,
|
|
75
|
+
OptionalFetcher,
|
|
76
|
+
RequiredFetcher,
|
|
77
|
+
} from './createServerFn'
|
|
78
|
+
export {
|
|
79
|
+
execValidator,
|
|
80
|
+
flattenMiddlewares,
|
|
81
|
+
executeMiddleware,
|
|
82
|
+
} from './createServerFn'
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
TSS_FORMDATA_CONTEXT,
|
|
86
|
+
TSS_SERVER_FUNCTION,
|
|
87
|
+
TSS_CONTENT_TYPE_FRAMED,
|
|
88
|
+
TSS_CONTENT_TYPE_FRAMED_VERSIONED,
|
|
89
|
+
TSS_FRAMED_PROTOCOL_VERSION,
|
|
90
|
+
FrameType,
|
|
91
|
+
FRAME_HEADER_SIZE,
|
|
92
|
+
X_TSS_SERIALIZED,
|
|
93
|
+
X_TSS_RAW_RESPONSE,
|
|
94
|
+
X_TSS_CONTEXT,
|
|
95
|
+
validateFramedProtocolVersion,
|
|
96
|
+
} from './constants'
|
|
97
|
+
export type {
|
|
98
|
+
FrameType as FrameTypeValue,
|
|
99
|
+
ClientFnMeta,
|
|
100
|
+
ServerFnMeta,
|
|
101
|
+
} from './constants'
|
|
102
|
+
|
|
103
|
+
export type * from './serverRoute'
|
|
104
|
+
|
|
105
|
+
export type * from './startEntry'
|
|
106
|
+
|
|
107
|
+
export { createStart } from './createStart'
|
|
108
|
+
export type {
|
|
109
|
+
AnyStartInstance,
|
|
110
|
+
AnyStartInstanceOptions,
|
|
111
|
+
StartInstance,
|
|
112
|
+
StartInstanceOptions,
|
|
113
|
+
} from './createStart'
|
|
114
|
+
export type { Register } from '@benjavicente/router-core'
|
|
115
|
+
|
|
116
|
+
export { getRouterInstance } from './getRouterInstance'
|
|
117
|
+
export { getDefaultSerovalPlugins } from './getDefaultSerovalPlugins'
|
|
118
|
+
export { getGlobalStartContext } from './getGlobalStartContext'
|
|
119
|
+
export { safeObjectMerge, createNullProtoObject } from './safeObjectMerge'
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
function isSafeKey(key: string): boolean {
|
|
2
|
+
return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Merge target and source into a new null-proto object, filtering dangerous keys.
|
|
7
|
+
*/
|
|
8
|
+
export function safeObjectMerge<T extends Record<string, unknown>>(
|
|
9
|
+
target: T | undefined,
|
|
10
|
+
source: Record<string, unknown> | null | undefined,
|
|
11
|
+
): T {
|
|
12
|
+
const result = Object.create(null) as T
|
|
13
|
+
if (target) {
|
|
14
|
+
for (const key of Object.keys(target)) {
|
|
15
|
+
if (isSafeKey(key)) result[key as keyof T] = target[key] as T[keyof T]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (source && typeof source === 'object') {
|
|
19
|
+
for (const key of Object.keys(source)) {
|
|
20
|
+
if (isSafeKey(key)) result[key as keyof T] = source[key] as T[keyof T]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return result
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Create a null-prototype object, optionally copying from source.
|
|
28
|
+
*/
|
|
29
|
+
export function createNullProtoObject<T extends object>(
|
|
30
|
+
source?: T,
|
|
31
|
+
): { [K in keyof T]: T[K] } {
|
|
32
|
+
if (!source) return Object.create(null)
|
|
33
|
+
const obj = Object.create(null)
|
|
34
|
+
for (const key of Object.keys(source)) {
|
|
35
|
+
if (isSafeKey(key)) obj[key] = (source as Record<string, unknown>)[key]
|
|
36
|
+
}
|
|
37
|
+
return obj
|
|
38
|
+
}
|