graphql-data-generator 0.3.0-alpha.1 → 0.3.0-alpha.11

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/esm/cli.js CHANGED
@@ -6,97 +6,99 @@ import { parseArgs } from "node:util";
6
6
  import { codegen, loadFiles } from "./codegen.js";
7
7
  import { formatCode } from "./util.js";
8
8
  import process from "node:process";
9
- const args = parseArgs({
10
- args: process.argv.slice(2),
11
- options: {
12
- banner: { type: "string" },
13
- enums: { type: "string" },
14
- exports: { type: "string", multiple: true },
15
- namingConvention: { type: "string" },
16
- notypenames: { type: "boolean" },
17
- operations: { type: "string", multiple: true },
18
- outfile: { type: "string" },
19
- scalar: { type: "string", multiple: true },
20
- scalars: { type: "string" },
21
- schema: { type: "string" },
22
- typesFile: { type: "string" },
23
- },
24
- }).values;
25
- const findFirst = async (path) => {
26
- for await (const file of fg.stream(path)) {
27
- return file.toString();
9
+ (async () => {
10
+ const args = parseArgs({
11
+ args: process.argv.slice(2),
12
+ options: {
13
+ banner: { type: "string" },
14
+ enums: { type: "string" },
15
+ exports: { type: "string", multiple: true },
16
+ namingConvention: { type: "string" },
17
+ notypenames: { type: "boolean" },
18
+ operations: { type: "string", multiple: true },
19
+ outfile: { type: "string" },
20
+ scalar: { type: "string", multiple: true },
21
+ scalars: { type: "string" },
22
+ schema: { type: "string" },
23
+ typesFile: { type: "string" },
24
+ },
25
+ }).values;
26
+ const findFirst = async (path) => {
27
+ for await (const file of fg.stream(path)) {
28
+ return file.toString();
29
+ }
30
+ };
31
+ const schemaPath = args.schema
32
+ ? await findFirst(args.schema)
33
+ : (await findFirst("**/schema.graphql") ??
34
+ await findFirst("**/schema.gql") ??
35
+ await findFirst("**/schema.graphqls"));
36
+ const fail = (reason, code = 1) => {
37
+ process.stderr.write(reason + "\n");
38
+ process.exit(code);
39
+ };
40
+ if (!schemaPath) {
41
+ fail(`Could not locate schema${args.schema ? ` "${args.schema}"` : ", try passing --schema"}`);
42
+ throw ""; // TS being dumb?
43
+ }
44
+ const operationDirs = args.operations?.map((v) => `${v}`) ?? ["."];
45
+ const [schema, operations] = await loadFiles(schemaPath, operationDirs);
46
+ const scalars = {};
47
+ if (args.scalars) {
48
+ readFile;
49
+ Object.assign(scalars, JSON.parse(await dntShim.Deno.readTextFile(args.scalars)));
28
50
  }
29
- };
30
- const schemaPath = args.schema
31
- ? await findFirst(args.schema)
32
- : (await findFirst("**/schema.graphql") ??
33
- await findFirst("**/schema.gql") ??
34
- await findFirst("**/schema.graphqls"));
35
- const fail = (reason, code = 1) => {
36
- process.stderr.write(reason + "\n");
37
- process.exit(code);
38
- };
39
- if (!schemaPath) {
40
- fail(`Could not locate schema${args.schema ? ` "${args.schema}"` : ", try passing --schema"}`);
41
- throw ""; // TS being dumb?
42
- }
43
- const operationDirs = args.operations?.map((v) => `${v}`) ?? ["."];
44
- const [schema, operations] = await loadFiles(schemaPath, operationDirs);
45
- const scalars = {};
46
- if (args.scalars) {
47
- readFile;
48
- Object.assign(scalars, JSON.parse(await dntShim.Deno.readTextFile(args.scalars)));
49
- }
50
- if (args.scalar) {
51
- for (const kv of args.scalar) {
52
- const [key, value] = `${kv}`.split(":");
53
- if (!value) {
54
- fail("Invalid --scalar argument. Pass as a key-value pair like --scalar=key:value");
51
+ if (args.scalar) {
52
+ for (const kv of args.scalar) {
53
+ const [key, value] = `${kv}`.split(":");
54
+ if (!value) {
55
+ fail("Invalid --scalar argument. Pass as a key-value pair like --scalar=key:value");
56
+ }
57
+ scalars[key] = value;
55
58
  }
56
- scalars[key] = value;
57
59
  }
58
- }
59
- const validExports = ["operations", "types"];
60
- const exports = args.exports
61
- ? args.exports.filter((e) => {
62
- if (!validExports.includes(e)) {
63
- throw new Error(`Invalid export. Must be ${validExports.join(", ")}`);
60
+ const validExports = ["operations", "types"];
61
+ const exports = args.exports
62
+ ? args.exports.filter((e) => {
63
+ if (!validExports.includes(e)) {
64
+ throw new Error(`Invalid export. Must be ${validExports.join(", ")}`);
65
+ }
66
+ return true;
67
+ })
68
+ : [];
69
+ if (typeof args.enums === "string" &&
70
+ (!["enums", "literals", "none"].includes(args.enums) &&
71
+ !args.enums.startsWith("import:"))) {
72
+ throw new Error(`Invalid 'enums'. Must be one of 'enums', 'literals', 'import', 'none'`);
73
+ }
74
+ let banner = "";
75
+ if (typeof args.banner === "string") {
76
+ if (await dntShim.Deno.lstat(args.banner).catch(() => false)) {
77
+ banner = await dntShim.Deno.readTextFile(args.banner);
64
78
  }
65
- return true;
66
- })
67
- : [];
68
- if (typeof args.enums === "string" &&
69
- (!["enums", "literals", "none"].includes(args.enums) &&
70
- !args.enums.startsWith("import:"))) {
71
- throw new Error(`Invalid 'enums'. Must be one of 'enums', 'literals', 'import', 'none'`);
72
- }
73
- let banner = "";
74
- if (typeof args.banner === "string") {
75
- if (await dntShim.Deno.lstat(args.banner).catch(() => false)) {
76
- banner = await dntShim.Deno.readTextFile(args.banner);
79
+ else
80
+ banner = args.banner;
77
81
  }
78
- else
79
- banner = args.banner;
80
- }
81
- try {
82
- const file = banner + await formatCode(codegen(schema, operations, {
83
- enums: args.enums,
84
- includeTypenames: !args.notypenames,
85
- scalars,
86
- exports,
87
- typesFile: args.typesFile,
88
- namingConvention: args.namingConvention,
89
- }));
90
- if (args.outfile)
91
- await dntShim.Deno.writeTextFile(args.outfile, file);
92
- else
93
- console.log(file);
94
- }
95
- catch (err) {
96
- const message = err instanceof Error ? err.message : `${err}`;
97
- if (message.startsWith("Could not find scalar")) {
98
- const scalar = message.match(/'([^']*)'/)?.[1];
99
- fail(`${message}. Try passing --scalars=scalars.json or --scalar=${scalar ?? "scalar"}:string, replacing string with the TypeScript type of the scalar.`);
82
+ try {
83
+ const file = banner + await formatCode(codegen(schema, operations, {
84
+ enums: args.enums,
85
+ includeTypenames: !args.notypenames,
86
+ scalars,
87
+ exports,
88
+ typesFile: args.typesFile,
89
+ namingConvention: args.namingConvention,
90
+ }));
91
+ if (args.outfile)
92
+ await dntShim.Deno.writeTextFile(args.outfile, file);
93
+ else
94
+ console.log(file);
95
+ }
96
+ catch (err) {
97
+ const message = err instanceof Error ? err.message : `${err}`;
98
+ if (message.startsWith("Could not find scalar")) {
99
+ const scalar = message.match(/'([^']*)'/)?.[1];
100
+ fail(`${message}. Try passing --scalars=scalars.json or --scalar=${scalar ?? "scalar"}:string, replacing string with the TypeScript type of the scalar.`);
101
+ }
102
+ fail(err instanceof Error ? err.message : err);
100
103
  }
101
- fail(err instanceof Error ? err.message : err);
102
- }
104
+ })();
package/esm/init.js CHANGED
@@ -1,11 +1,16 @@
1
+ import * as dntShim from "./_dnt.shims.js";
2
+ import { createRequire } from "node:module";
1
3
  import { readFileSync } from "node:fs";
2
4
  import { Kind, parse } from "graphql";
3
- import { dirname, join } from "node:path";
4
5
  import { gqlPluckFromCodeStringSync } from "@graphql-tools/graphql-tag-pluck";
5
6
  import { operation, proxy, withGetDefaultPatch } from "./proxy.js";
6
7
  import { toObject } from "./util.js";
8
+ import { dirname } from "node:path";
9
+ globalThis.require ??= createRequire(dntShim.Deno.cwd());
7
10
  const files = {};
8
- const loadFile = (path) => files[path] || (files[path] = readFileSync(path, "utf-8").replace(/#import "(.*)"/, (_, fragmentPath) => loadFile(join(dirname(path), fragmentPath))));
11
+ const loadFile = (path) => files[path] || (files[path] = readFileSync(path, "utf-8").replace(/#import "(.*)"/, (_, fragmentPath) => loadFile(require.resolve(fragmentPath, {
12
+ paths: [dirname(path), dntShim.Deno.cwd()],
13
+ }))));
9
14
  const getOperationContentMap = {};
10
15
  const getOperationContent = (path, operationName) => {
11
16
  const existing = getOperationContentMap[path];
@@ -14,7 +19,7 @@ const getOperationContent = (path, operationName) => {
14
19
  return existing;
15
20
  return getOperationContentMap[path][operationName];
16
21
  }
17
- const fileContent = readFileSync(path, "utf-8").replace(/#import "(.*)"/, (_, fragmentPath) => loadFile(join(dirname(path), fragmentPath)));
22
+ const fileContent = loadFile(path);
18
23
  try {
19
24
  const sources = gqlPluckFromCodeStringSync(path, fileContent);
20
25
  getOperationContentMap[path] = Object.fromEntries(sources.map((s) => {
@@ -104,7 +109,6 @@ export const init = (schema, queries, mutations, subscriptions, types, inputs, s
104
109
  : variables,
105
110
  });
106
111
  Error.captureStackTrace(mock, obj.variables);
107
- mock.stack = mock.stack?.slice(6);
108
112
  return mock;
109
113
  },
110
114
  },
@@ -125,7 +129,6 @@ export const init = (schema, queries, mutations, subscriptions, types, inputs, s
125
129
  : data,
126
130
  });
127
131
  Error.captureStackTrace(mock, obj.data);
128
- mock.stack = mock.stack?.slice(6);
129
132
  return mock;
130
133
  },
131
134
  },
@@ -146,7 +149,6 @@ export const init = (schema, queries, mutations, subscriptions, types, inputs, s
146
149
  const builder = build[operation];
147
150
  const mock = builder(prevInput, patch);
148
151
  Error.captureStackTrace(mock, obj[name]);
149
- mock.stack = mock.stack?.slice(6);
150
152
  return mock;
151
153
  },
152
154
  }])));
@@ -165,7 +167,6 @@ export const init = (schema, queries, mutations, subscriptions, types, inputs, s
165
167
  });
166
168
  mock.request.query = parsedQuery;
167
169
  Error.captureStackTrace(mock, builder);
168
- mock.stack = mock.stack?.slice(6);
169
170
  return addOperationTransforms(name, options?.finalizeOperation
170
171
  ? options.finalizeOperation(mock)
171
172
  : mock);
package/esm/jest.js CHANGED
@@ -1,11 +1,10 @@
1
1
  import * as dntShim from "./_dnt.shims.js";
2
- import "./deps/esm.sh/@types/jest@29.5.3/index";
3
2
  import React, { useMemo } from "react";
4
- import { ApolloLink } from "@apollo/client";
5
- import { ErrorLink } from "@apollo/client/link/error/index.js";
6
- import { MockedProvider, MockLink, } from "@apollo/client/testing/index.js";
3
+ import { ApolloLink, useApolloClient, } from "@apollo/client";
4
+ import { onError } from "@apollo/client/link/error";
5
+ import { MockedProvider, MockLink, } from "@apollo/client/testing";
7
6
  import { waitFor } from "@testing-library/dom";
8
- import { print } from "graphql";
7
+ import { Kind, print } from "graphql";
9
8
  import { diff as jestDiff } from "jest-diff";
10
9
  let currentSpecResult;
11
10
  jasmine.getEnv().addReporter({
@@ -13,25 +12,26 @@ jasmine.getEnv().addReporter({
13
12
  });
14
13
  const afterTest = [];
15
14
  afterEach(async () => {
16
- for (const hook of afterTest)
15
+ const hooks = afterTest.splice(0);
16
+ for (const hook of hooks)
17
17
  await hook();
18
- afterTest.splice(0);
19
18
  });
20
- const diff = (a, b) => jestDiff(a, b, {
21
- omitAnnotationLines: true,
22
- // aColor: green,
23
- // bColor: red,
24
- // changeColor: inverse,
25
- // commonColor: dim,
26
- // patchColor: yellow,
27
- })
19
+ const diff = (a, b) => jestDiff(a, b, { omitAnnotationLines: true })
28
20
  ?.replace(/\w+ \{/g, "{") // Remove class names
29
21
  .replace(/\w+ \[/g, "["); // Remove array class names
22
+ const getOperationDefinition = (document) => document.definitions.find((d) => d.kind === Kind.OPERATION_DEFINITION);
23
+ const getOperationType = (operation) => getOperationDefinition(operation.query)?.operation ??
24
+ "<unknown operation type>";
25
+ const getOperationName = (document) => getOperationDefinition(document)?.name?.value;
26
+ const getOperationInfo = (document) => {
27
+ const def = getOperationDefinition(document);
28
+ return {
29
+ name: def?.name?.value ?? "<unknown operation>",
30
+ operationType: def?.operation ?? "<unknown operation type>",
31
+ };
32
+ };
30
33
  const getErrorMessage = (operation, mockLink) => {
31
- const definition = operation.query.definitions[0];
32
- const operationType = definition.kind === "OperationDefinition"
33
- ? definition.operation
34
- : "<unknown operation type>";
34
+ const operationType = getOperationType(operation);
35
35
  const key = JSON.stringify({
36
36
  query: print(operation.query),
37
37
  });
@@ -39,7 +39,7 @@ const getErrorMessage = (operation, mockLink) => {
39
39
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
40
  const alts = (mockLink
41
41
  .mockedResponsesByKey[key] ?? []);
42
- let errorMessage = `Expected GraphQL ${operationType} ${operation.operationName} to have been mocked`;
42
+ let errorMessage = `Expected ${operationType} ${operation.operationName} to have been mocked`;
43
43
  if (alts.length || Object.keys(operation.variables).length > 0) {
44
44
  errorMessage += ` with variables ${dntShim.Deno.inspect(operation.variables, { depth: Infinity, colors: true })}`;
45
45
  }
@@ -62,59 +62,154 @@ const getErrorMessage = (operation, mockLink) => {
62
62
  : undefined,
63
63
  };
64
64
  };
65
- export const MockProvider = ({ mocks, stack, children }) => {
65
+ let _failRefetchWarnings = false;
66
+ /**
67
+ * In older versions of `@apollo/client`, refetches with missing mocks trigger
68
+ * warnings instead of being treated as standard missing mocks.
69
+ * This utility converts those warnings into failures and ensures watch queries
70
+ * are installed for queries with `watch: true`. This must be set when
71
+ * `MockProvider` is mounted.
72
+ *
73
+ * This is not required on modern version of `@apollo/client`.
74
+ */
75
+ export const failRefetchWarnings = (value = true) => _failRefetchWarnings = value;
76
+ let _allowMissingMocks = false;
77
+ /**
78
+ * Allows missing mocks, resulting in tests passing. Usage is intended to ease
79
+ * migration.
80
+ */
81
+ export const allowMissingMocks = (value) => _allowMissingMocks = value;
82
+ const AutoWatch = ({ mocks }) => {
83
+ const client = useApolloClient();
84
+ for (const mock of mocks)
85
+ if (mock.watch)
86
+ client.watchQuery(mock.request);
87
+ return null;
88
+ };
89
+ let lastMocks = [];
90
+ // deno-lint-ignore ban-types
91
+ const getStack = (to) => {
92
+ const obj = {};
93
+ Error.captureStackTrace(obj, to);
94
+ return obj.stack;
95
+ };
96
+ const _waitForMocks = async (mocks, cause) => {
97
+ for (const mock of mocks) {
98
+ if (mock.optional || mock.error)
99
+ continue;
100
+ await waitFor(() => {
101
+ if (currentSpecResult.failedExpectations.length)
102
+ return;
103
+ if (mock.result.mock.calls.length === 0) {
104
+ const { name, operationType } = getOperationInfo(mock.request.query);
105
+ const err = new Error(`Expected to have used ${operationType} ${name}${mock.request.variables
106
+ ? ` with variables ${dntShim.Deno.inspect(mock.request.variables, {
107
+ depth: Infinity,
108
+ colors: true,
109
+ })}`
110
+ : ""}`);
111
+ err.stack = `${mock.stack}${cause ? `\nCaused by: ${cause}` : ""}`;
112
+ throw err;
113
+ }
114
+ });
115
+ }
116
+ };
117
+ /**
118
+ * Wait for mocks to have been used.
119
+ * @param mock If `undefined`, waits for all mocks. If a number, waits fort he first `mocks` mocks. If a string, waits for all mocks up until and including that mock.
120
+ * @param offset If `mocks` is a string, grabs the `offset`th mock of that name (e.g., the third `getReport` mock)
121
+ */
122
+ export const waitForMocks = async (mock = lastMocks.length, offset = 0) => {
123
+ if (typeof mock === "string") {
124
+ const matches = lastMocks.map((m, i) => [m, i])
125
+ .filter(([m]) => getOperationName(m.request.query) === mock);
126
+ if (matches.length <= offset) {
127
+ fail({
128
+ name: "Error",
129
+ message: `Expected mock ${mock} to have been mocked`,
130
+ stack: getStack(waitForMocks),
131
+ });
132
+ }
133
+ expect(matches.length).toBeGreaterThan(offset);
134
+ mock = matches[offset][1] + 1;
135
+ }
136
+ await _waitForMocks(lastMocks.slice(0, mock), getStack(waitForMocks));
137
+ };
138
+ /**
139
+ * A wrapper for `@apollo/client/testing`, this component will assert all
140
+ * requests have matching mocks and all defined mocks are used unless marked
141
+ * `optional`.
142
+ */
143
+ export const MockProvider = ({ mocks, stack: renderStack, children, link: passedLink, ...rest }) => {
66
144
  const observableMocks = useMemo(() => {
67
145
  const observableMocks = mocks.map((m) => ({
68
146
  ...m,
69
147
  stack: m.stack,
70
148
  result: Object.assign(jest.fn(() => m.result), m.result),
71
149
  }));
72
- afterTest.push(async () => {
73
- if (currentSpecResult.failedExpectations.length)
74
- return;
75
- for (const mock of observableMocks) {
76
- if ("optional" in mock && mock.optional || mock.error)
77
- continue;
78
- await waitFor(() => {
79
- if (currentSpecResult.failedExpectations.length)
80
- return;
81
- if (mock.result.mock.calls.length === 0) {
82
- const err = new Error(`Expected to have used mock ${mock.request.variables
83
- ? ` with variables ${dntShim.Deno.inspect(mock.request.variables, {
84
- depth: Infinity,
85
- colors: true,
86
- })}`
87
- : ""}`);
88
- err.stack = `${err.message}\n${mock.stack ?? stack ?? err.stack?.slice(6)}`;
89
- throw err;
90
- }
91
- }, { onTimeout: (e) => e });
92
- }
93
- });
94
- return observableMocks;
150
+ lastMocks = observableMocks;
151
+ afterTest.push(() => _waitForMocks(lastMocks, renderStack));
152
+ return observableMocks.flatMap((m) => [
153
+ m,
154
+ ...(m.watch
155
+ ? [{
156
+ ...m,
157
+ stack: m.stack,
158
+ result: Object.assign(jest.fn(() => m.result), m.result),
159
+ watch: false,
160
+ }]
161
+ : []),
162
+ ]);
95
163
  }, [mocks]);
96
164
  const link = useMemo(() => {
97
165
  const mockLink = new MockLink(observableMocks);
98
166
  mockLink.showWarnings = false;
99
- const errorLoggingLink = new ErrorLink(({ networkError, operation }) => {
100
- if (!networkError?.message.includes("No more mocked responses"))
167
+ const errorLoggingLink = onError(({ networkError, operation }) => {
168
+ if (_allowMissingMocks ||
169
+ !networkError?.message.includes("No more mocked responses"))
101
170
  return;
102
171
  const { message, stack: altStack } = getErrorMessage(operation, mockLink);
103
172
  try {
104
173
  networkError.message = message;
105
- const finalStack = altStack ?? stack
106
- ? `${message}\n${altStack ?? stack}`
107
- : undefined;
108
- if (finalStack)
109
- networkError.stack = finalStack;
110
- fail({ name: "Error", message, stack: finalStack });
174
+ const stack = altStack ?? renderStack;
175
+ if (stack)
176
+ networkError.stack = stack;
177
+ fail({ name: "Error", message, stack: networkError.stack });
111
178
  }
112
179
  catch {
113
180
  // fail both throws and marks the test as failed in jest; we only need the latter
114
181
  }
115
182
  });
116
- // @ts-ignore It's fine
117
- return ApolloLink.from([errorLoggingLink, mockLink]);
118
- }, [observableMocks, stack]);
119
- return React.createElement(MockedProvider, { link: link }, children);
183
+ return ApolloLink.from([
184
+ errorLoggingLink,
185
+ mockLink,
186
+ ...(passedLink ? [passedLink] : []),
187
+ ]);
188
+ }, [observableMocks, renderStack]);
189
+ if (_failRefetchWarnings) {
190
+ const oldWarn = console.warn.bind(console.warn);
191
+ console.warn = (message, operation, ...etc) => {
192
+ if (message !==
193
+ 'Unknown query named "%s" requested in refetchQueries in options.include array') {
194
+ return oldWarn(message, operation, ...etc);
195
+ }
196
+ try {
197
+ fail({
198
+ name: "Error",
199
+ message: `Expected query ${operation} requested in refetchQueries options.include array to have bene mocked`,
200
+ stack: renderStack,
201
+ });
202
+ }
203
+ catch {
204
+ // eat
205
+ }
206
+ };
207
+ afterTest.push(() => {
208
+ console.warn = oldWarn;
209
+ });
210
+ }
211
+ return (React.createElement(MockedProvider, { ...rest, link: link },
212
+ React.createElement(React.Fragment, null,
213
+ React.createElement(AutoWatch, { mocks: observableMocks }),
214
+ children)));
120
215
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphql-data-generator",
3
- "version": "0.3.0-alpha.1",
3
+ "version": "0.3.0-alpha.11",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/voces/graphql-data-generator.git"
@@ -9,7 +9,7 @@
9
9
  "bugs": {
10
10
  "url": "https://github.com/voces/graphql-data-generator/issues"
11
11
  },
12
- "main": "./esm/index.js",
12
+ "main": "./script/index.js",
13
13
  "module": "./esm/index.js",
14
14
  "types": "./types/index.d.ts",
15
15
  "exports": {
@@ -35,6 +35,16 @@
35
35
  "bin": {
36
36
  "graphql-data-generator": "./esm/cli.js"
37
37
  },
38
+ "typesVersions": {
39
+ "*": {
40
+ "script/jest": [
41
+ "types/jest.d.ts"
42
+ ],
43
+ "script/plugin": [
44
+ "types/plugin.d.ts"
45
+ ]
46
+ }
47
+ },
38
48
  "dependencies": {
39
49
  "@graphql-codegen/visitor-plugin-common": "*",
40
50
  "fast-glob": "*",
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dntGlobalThis = exports.Deno = void 0;
4
+ const shim_deno_1 = require("@deno/shim-deno");
5
+ var shim_deno_2 = require("@deno/shim-deno");
6
+ Object.defineProperty(exports, "Deno", { enumerable: true, get: function () { return shim_deno_2.Deno; } });
7
+ const dntGlobals = {
8
+ Deno: shim_deno_1.Deno,
9
+ };
10
+ exports.dntGlobalThis = createMergeProxy(globalThis, dntGlobals);
11
+ function createMergeProxy(baseObj, extObj) {
12
+ return new Proxy(baseObj, {
13
+ get(_target, prop, _receiver) {
14
+ if (prop in extObj) {
15
+ return extObj[prop];
16
+ }
17
+ else {
18
+ return baseObj[prop];
19
+ }
20
+ },
21
+ set(_target, prop, value) {
22
+ if (prop in extObj) {
23
+ delete extObj[prop];
24
+ }
25
+ baseObj[prop] = value;
26
+ return true;
27
+ },
28
+ deleteProperty(_target, prop) {
29
+ let success = false;
30
+ if (prop in extObj) {
31
+ delete extObj[prop];
32
+ success = true;
33
+ }
34
+ if (prop in baseObj) {
35
+ delete baseObj[prop];
36
+ success = true;
37
+ }
38
+ return success;
39
+ },
40
+ ownKeys(_target) {
41
+ const baseKeys = Reflect.ownKeys(baseObj);
42
+ const extKeys = Reflect.ownKeys(extObj);
43
+ const extKeysSet = new Set(extKeys);
44
+ return [...baseKeys.filter((k) => !extKeysSet.has(k)), ...extKeys];
45
+ },
46
+ defineProperty(_target, prop, desc) {
47
+ if (prop in extObj) {
48
+ delete extObj[prop];
49
+ }
50
+ Reflect.defineProperty(baseObj, prop, desc);
51
+ return true;
52
+ },
53
+ getOwnPropertyDescriptor(_target, prop) {
54
+ if (prop in extObj) {
55
+ return Reflect.getOwnPropertyDescriptor(extObj, prop);
56
+ }
57
+ else {
58
+ return Reflect.getOwnPropertyDescriptor(baseObj, prop);
59
+ }
60
+ },
61
+ has(_target, prop) {
62
+ return prop in extObj || prop in baseObj;
63
+ },
64
+ });
65
+ }