@memberjunction/server 5.38.0 → 5.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +67 -0
  2. package/dist/apolloServer/index.d.ts +0 -8
  3. package/dist/apolloServer/index.d.ts.map +1 -1
  4. package/dist/apolloServer/index.js +61 -0
  5. package/dist/apolloServer/index.js.map +1 -1
  6. package/dist/config.d.ts +68 -0
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +24 -0
  9. package/dist/config.js.map +1 -1
  10. package/dist/context.d.ts.map +1 -1
  11. package/dist/context.js +2 -21
  12. package/dist/context.js.map +1 -1
  13. package/dist/generated/generated.d.ts +75 -0
  14. package/dist/generated/generated.d.ts.map +1 -1
  15. package/dist/generated/generated.js +314 -1
  16. package/dist/generated/generated.js.map +1 -1
  17. package/dist/generic/FireAndForgetHeartbeat.d.ts +51 -0
  18. package/dist/generic/FireAndForgetHeartbeat.d.ts.map +1 -0
  19. package/dist/generic/FireAndForgetHeartbeat.js +44 -0
  20. package/dist/generic/FireAndForgetHeartbeat.js.map +1 -0
  21. package/dist/index.d.ts +3 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +120 -2
  24. package/dist/index.js.map +1 -1
  25. package/dist/logging/NoLog.d.ts +50 -0
  26. package/dist/logging/NoLog.d.ts.map +1 -0
  27. package/dist/logging/NoLog.js +80 -0
  28. package/dist/logging/NoLog.js.map +1 -0
  29. package/dist/logging/bootAudit.d.ts +43 -0
  30. package/dist/logging/bootAudit.d.ts.map +1 -0
  31. package/dist/logging/bootAudit.js +83 -0
  32. package/dist/logging/bootAudit.js.map +1 -0
  33. package/dist/logging/boundaryLogPayload.d.ts +18 -0
  34. package/dist/logging/boundaryLogPayload.d.ts.map +1 -0
  35. package/dist/logging/boundaryLogPayload.js +18 -0
  36. package/dist/logging/boundaryLogPayload.js.map +1 -0
  37. package/dist/logging/secretRedactor.d.ts +23 -0
  38. package/dist/logging/secretRedactor.d.ts.map +1 -0
  39. package/dist/logging/secretRedactor.js +53 -0
  40. package/dist/logging/secretRedactor.js.map +1 -0
  41. package/dist/logging/shortenForLog.d.ts +8 -0
  42. package/dist/logging/shortenForLog.d.ts.map +1 -0
  43. package/dist/logging/shortenForLog.js +21 -0
  44. package/dist/logging/shortenForLog.js.map +1 -0
  45. package/dist/logging/variablesLoggingMiddleware.d.ts +22 -0
  46. package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -0
  47. package/dist/logging/variablesLoggingMiddleware.js +127 -0
  48. package/dist/logging/variablesLoggingMiddleware.js.map +1 -0
  49. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  50. package/dist/resolvers/GetDataResolver.js +8 -4
  51. package/dist/resolvers/GetDataResolver.js.map +1 -1
  52. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +259 -2
  53. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  54. package/dist/resolvers/IntegrationDiscoveryResolver.js +1276 -117
  55. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  56. package/dist/resolvers/IntegrationProgressResolver.d.ts +90 -0
  57. package/dist/resolvers/IntegrationProgressResolver.d.ts.map +1 -0
  58. package/dist/resolvers/IntegrationProgressResolver.js +196 -0
  59. package/dist/resolvers/IntegrationProgressResolver.js.map +1 -0
  60. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  61. package/dist/resolvers/MCPResolver.js +19 -14
  62. package/dist/resolvers/MCPResolver.js.map +1 -1
  63. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  64. package/dist/resolvers/RunAIAgentResolver.js +26 -5
  65. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  66. package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
  67. package/dist/resolvers/RunTestResolver.js +12 -2
  68. package/dist/resolvers/RunTestResolver.js.map +1 -1
  69. package/dist/resolvers/SearchEntitiesResolver.d.ts +46 -0
  70. package/dist/resolvers/SearchEntitiesResolver.d.ts.map +1 -0
  71. package/dist/resolvers/SearchEntitiesResolver.js +216 -0
  72. package/dist/resolvers/SearchEntitiesResolver.js.map +1 -0
  73. package/dist/resolvers/VectorizeEntityResolver.d.ts.map +1 -1
  74. package/dist/resolvers/VectorizeEntityResolver.js +14 -1
  75. package/dist/resolvers/VectorizeEntityResolver.js.map +1 -1
  76. package/dist/services/ScheduledJobsService.d.ts.map +1 -1
  77. package/dist/services/ScheduledJobsService.js +14 -2
  78. package/dist/services/ScheduledJobsService.js.map +1 -1
  79. package/package.json +75 -74
  80. package/src/__tests__/NoLog.test.ts +76 -0
  81. package/src/__tests__/bootAudit.test.ts +188 -0
  82. package/src/__tests__/boundaryLogPayload.test.ts +31 -0
  83. package/src/__tests__/getDataTokenRedaction.test.ts +84 -0
  84. package/src/__tests__/secretRedactor.test.ts +163 -0
  85. package/src/__tests__/subscriptionRedaction.test.ts +217 -0
  86. package/src/apolloServer/index.ts +58 -0
  87. package/src/config.ts +27 -0
  88. package/src/context.ts +2 -19
  89. package/src/generated/generated.ts +239 -1
  90. package/src/generic/FireAndForgetHeartbeat.ts +85 -0
  91. package/src/generic/__tests__/FireAndForgetHeartbeat.test.ts +99 -0
  92. package/src/index.ts +137 -2
  93. package/src/logging/NoLog.ts +88 -0
  94. package/src/logging/bootAudit.ts +117 -0
  95. package/src/logging/boundaryLogPayload.ts +19 -0
  96. package/src/logging/secretRedactor.ts +82 -0
  97. package/src/logging/shortenForLog.ts +17 -0
  98. package/src/logging/variablesLoggingMiddleware.ts +191 -0
  99. package/src/resolvers/GetDataResolver.ts +9 -5
  100. package/src/resolvers/IntegrationDiscoveryResolver.ts +1111 -120
  101. package/src/resolvers/IntegrationProgressResolver.ts +220 -0
  102. package/src/resolvers/MCPResolver.ts +18 -14
  103. package/src/resolvers/RunAIAgentResolver.ts +28 -5
  104. package/src/resolvers/RunTestResolver.ts +14 -2
  105. package/src/resolvers/SearchEntitiesResolver.ts +173 -0
  106. package/src/resolvers/VectorizeEntityResolver.ts +14 -1
  107. package/src/resolvers/__tests__/IntegrationProgressResolver.test.ts +170 -0
  108. package/src/services/ScheduledJobsService.ts +15 -2
@@ -0,0 +1,80 @@
1
+ import 'reflect-metadata';
2
+ /**
3
+ * Reflect-metadata key for `@NoLog` marks. A single symbol covers both parameter-level
4
+ * marks (stored against `(target, propertyKey)` pairs) and field-level marks (stored
5
+ * against the input class prototype).
6
+ */
7
+ const NO_LOG_PARAM_KEY = Symbol('mj:NoLog:param');
8
+ const NO_LOG_FIELD_KEY = Symbol('mj:NoLog:field');
9
+ /**
10
+ * `@NoLog` — marks a resolver argument or input-type field as never-loggable.
11
+ *
12
+ * Two modes, distinguished by decorator arity at runtime:
13
+ *
14
+ * **Parameter** — applied to an `@Arg(...)` parameter on a resolver method:
15
+ * ```ts
16
+ * @Mutation(() => Boolean)
17
+ * async VoiceTestHubSpotCredential(
18
+ * @Arg('accessToken') @NoLog accessToken: string,
19
+ * @Ctx() ctx: AppContext,
20
+ * ): Promise<boolean> { ... }
21
+ * ```
22
+ *
23
+ * **Property** — applied to a `@Field()` on an `@InputType` class:
24
+ * ```ts
25
+ * @InputType()
26
+ * export class GetDataInputType {
27
+ * @Field(() => String) @NoLog Token: string;
28
+ * @Field(() => [String]) Queries: string[];
29
+ * }
30
+ * ```
31
+ *
32
+ * The variables-logging middleware reads these marks at runtime via
33
+ * `hasNoLogParameter` / `getNoLogFields` and replaces the marked value (or whole arg)
34
+ * with `"<redacted>"` before emitting the variables block. Metadata-covered fields
35
+ * (entity columns with `EntityFieldInfo.Encrypt=true`) do not need `@NoLog` — applying
36
+ * it is harmless but redundant. Use `@NoLog` for arguments that the redactor cannot
37
+ * identify via metadata: custom-resolver parameters, MCP tool args, fields on input
38
+ * types that don't map to a known entity.
39
+ */
40
+ export function NoLog(target, propertyKey, parameterIndex) {
41
+ // Parameter-decorator path: (target, propertyKey, parameterIndex)
42
+ if (typeof parameterIndex === 'number' && propertyKey !== undefined) {
43
+ const existing = Reflect.getOwnMetadata(NO_LOG_PARAM_KEY, target, propertyKey) ?? new Set();
44
+ existing.add(parameterIndex);
45
+ Reflect.defineMetadata(NO_LOG_PARAM_KEY, existing, target, propertyKey);
46
+ return;
47
+ }
48
+ // Property-decorator path: (target, propertyKey)
49
+ if (propertyKey !== undefined) {
50
+ const existing = Reflect.getOwnMetadata(NO_LOG_FIELD_KEY, target) ?? new Set();
51
+ existing.add(String(propertyKey));
52
+ Reflect.defineMetadata(NO_LOG_FIELD_KEY, existing, target);
53
+ return;
54
+ }
55
+ }
56
+ /**
57
+ * Returns true if `@NoLog` was applied to `methodName`'s argument at `parameterIndex`
58
+ * on the given resolver class prototype. False otherwise.
59
+ *
60
+ * Pass the resolver class itself (e.g. `MyResolver`) — the function reads from the
61
+ * prototype internally to match how type-graphql stores resolver metadata.
62
+ */
63
+ export function hasNoLogParameter(resolverClass, methodName, parameterIndex) {
64
+ const target = resolverClass.prototype;
65
+ const marks = Reflect.getMetadata(NO_LOG_PARAM_KEY, target, methodName);
66
+ return marks !== undefined && marks.has(parameterIndex);
67
+ }
68
+ /**
69
+ * Returns the set of field names decorated `@NoLog` on the given input class. Empty set
70
+ * if none were marked.
71
+ *
72
+ * Pass the input class itself (e.g. `GetDataInputType`) — the function reads from the
73
+ * prototype internally.
74
+ */
75
+ export function getNoLogFields(inputTypeClass) {
76
+ const target = inputTypeClass.prototype;
77
+ const marks = Reflect.getMetadata(NO_LOG_FIELD_KEY, target);
78
+ return marks ?? new Set();
79
+ }
80
+ //# sourceMappingURL=NoLog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NoLog.js","sourceRoot":"","sources":["../../src/logging/NoLog.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAClD,MAAM,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,KAAK,CAAC,MAAc,EAAE,WAA6B,EAAE,cAAuB;IAC1F,kEAAkE;IAClE,IAAI,OAAO,cAAc,KAAK,QAAQ,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QACpE,MAAM,QAAQ,GAAI,OAAO,CAAC,cAAc,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAA6B,IAAI,IAAI,GAAG,EAAU,CAAC;QACjI,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAC7B,OAAO,CAAC,cAAc,CAAC,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;QACxE,OAAO;IACT,CAAC;IAED,iDAAiD;IACjD,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAI,OAAO,CAAC,cAAc,CAAC,gBAAgB,EAAE,MAAM,CAA6B,IAAI,IAAI,GAAG,EAAU,CAAC;QACpH,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;QAClC,OAAO,CAAC,cAAc,CAAC,gBAAgB,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC3D,OAAO;IACT,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,aAAuB,EACvB,UAAkB,EAClB,cAAsB;IAEtB,MAAM,MAAM,GAAG,aAAa,CAAC,SAAmB,CAAC;IACjD,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,CAAC,gBAAgB,EAAE,MAAM,EAAE,UAAU,CAA4B,CAAC;IACnG,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,cAAwB;IACrD,MAAM,MAAM,GAAG,cAAc,CAAC,SAAmB,CAAC;IAClD,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,CAAC,gBAAgB,EAAE,MAAM,CAA4B,CAAC;IACvF,OAAO,KAAK,IAAI,IAAI,GAAG,EAAU,CAAC;AACpC,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Structural shape of an `@Arg` parameter as seen from outside type-graphql's internals.
3
+ * Used by the audit walker. Mirrors the subset of `ArgParamMetadata` we need.
4
+ */
5
+ export type AuditArgParam = {
6
+ kind: 'arg';
7
+ index: number;
8
+ name: string;
9
+ target: Function;
10
+ methodName: string;
11
+ getType: () => unknown;
12
+ };
13
+ /**
14
+ * Structural shape of a resolver metadata entry from outside type-graphql's internals.
15
+ * Includes `@Query`, `@Mutation`, `@Subscription` entries.
16
+ */
17
+ export type AuditResolver = {
18
+ target: Function;
19
+ methodName: string;
20
+ params?: ReadonlyArray<{
21
+ kind: string;
22
+ index: number;
23
+ } & Partial<AuditArgParam>>;
24
+ };
25
+ /**
26
+ * Scans every `@Query` / `@Mutation` / `@Subscription` for `@Arg`s that are neither
27
+ * metadata-bound (input type matching `Create<X>Input` / `Update<X>Input`) nor `@NoLog`-marked.
28
+ * Emits one warning per such arg.
29
+ *
30
+ * Gated on `loggingSettings.graphql.logVariables=true` — runs zero work in default config.
31
+ * In verbose mode, the audit is a diagnostic for the operator who chose to turn on
32
+ * variables logging, naming gaps that will leak plaintext while the flag is active.
33
+ *
34
+ * Never throws. Boot does not fail on a missed `@NoLog`.
35
+ */
36
+ export declare function auditResolversForUndecoratedArgs(): void;
37
+ /**
38
+ * Internal helper exported for test injection. Takes a list of resolver metadata directly,
39
+ * skipping the global `getMetadataStorage()` lookup. The boot-time scan uses this after
40
+ * pulling from the global storage; tests pass in a synthetic fixture.
41
+ */
42
+ export declare function auditResolverList(resolvers: ReadonlyArray<AuditResolver>): void;
43
+ //# sourceMappingURL=bootAudit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootAudit.d.ts","sourceRoot":"","sources":["../../src/logging/bootAudit.ts"],"names":[],"mappings":"AAUA;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,KAAK,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC;CACxB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,QAAQ,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;CAClF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,gCAAgC,IAAI,IAAI,CAevD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,aAAa,CAAC,aAAa,CAAC,GAAG,IAAI,CAI/E"}
@@ -0,0 +1,83 @@
1
+ import { getMetadataStorage } from 'type-graphql';
2
+ import { configInfo } from '../config.js';
3
+ import { hasNoLogParameter } from './NoLog.js';
4
+ // Delete is included alongside Create/Update so codegen DeleteMJ*Input resolvers count as
5
+ // metadata-bound and don't flood the audit with false positives (their args are PK + Options
6
+ // only — no encrypted values by construction). Must stay in sync with the redactor's
7
+ // INPUT_TYPE_REGEX in secretRedactor.ts. See docs/adr/0001-graphql-variables-logging-tiered-by-verbose.md.
8
+ const INPUT_TYPE_REGEX = /^(Create|Update|Delete).+Input$/;
9
+ /**
10
+ * Scans every `@Query` / `@Mutation` / `@Subscription` for `@Arg`s that are neither
11
+ * metadata-bound (input type matching `Create<X>Input` / `Update<X>Input`) nor `@NoLog`-marked.
12
+ * Emits one warning per such arg.
13
+ *
14
+ * Gated on `loggingSettings.graphql.logVariables=true` — runs zero work in default config.
15
+ * In verbose mode, the audit is a diagnostic for the operator who chose to turn on
16
+ * variables logging, naming gaps that will leak plaintext while the flag is active.
17
+ *
18
+ * Never throws. Boot does not fail on a missed `@NoLog`.
19
+ */
20
+ export function auditResolversForUndecoratedArgs() {
21
+ if (!configInfo.loggingSettings.graphql.logVariables) {
22
+ return;
23
+ }
24
+ const storage = getMetadataStorage();
25
+ auditResolverList([
26
+ ...storage.queries,
27
+ ...storage.mutations,
28
+ ...storage.subscriptions,
29
+ ]);
30
+ }
31
+ /**
32
+ * Internal helper exported for test injection. Takes a list of resolver metadata directly,
33
+ * skipping the global `getMetadataStorage()` lookup. The boot-time scan uses this after
34
+ * pulling from the global storage; tests pass in a synthetic fixture.
35
+ */
36
+ export function auditResolverList(resolvers) {
37
+ for (const resolver of resolvers) {
38
+ auditResolver(resolver);
39
+ }
40
+ }
41
+ function auditResolver(resolver) {
42
+ for (const param of resolver.params ?? []) {
43
+ if (param.kind !== 'arg') {
44
+ continue;
45
+ }
46
+ auditArg(resolver, param);
47
+ }
48
+ }
49
+ function auditArg(resolver, argParam) {
50
+ const typeName = readArgTypeName(argParam);
51
+ if (typeName && INPUT_TYPE_REGEX.test(typeName)) {
52
+ return;
53
+ }
54
+ if (hasNoLogParameter(resolver.target, resolver.methodName, argParam.index)) {
55
+ return;
56
+ }
57
+ const resolverName = `${resolver.target.name}.${resolver.methodName}`;
58
+ console.warn(`[mj:NoLog audit] Custom resolver ${resolverName} takes argument '${argParam.name}' ` +
59
+ `(type ${typeName ?? 'unknown'}) which is not marked @NoLog. Verify this argument does ` +
60
+ `not carry sensitive material; if it does, apply @NoLog. Variables for this resolver will ` +
61
+ `be logged in plaintext while logVariables=true is active.`);
62
+ }
63
+ /**
64
+ * Reads the declared input type name from an `@Arg` parameter's `getType()` thunk.
65
+ * Returns the class/scalar name when the thunk resolves to a class or named scalar; otherwise
66
+ * returns `undefined` (e.g. for array literals or complex generics we don't unwrap).
67
+ */
68
+ function readArgTypeName(argParam) {
69
+ try {
70
+ const type = argParam.getType();
71
+ if (typeof type === 'function') {
72
+ return type.name;
73
+ }
74
+ if (type && typeof type === 'object' && 'name' in type && typeof type.name === 'string') {
75
+ return type.name;
76
+ }
77
+ }
78
+ catch {
79
+ return undefined;
80
+ }
81
+ return undefined;
82
+ }
83
+ //# sourceMappingURL=bootAudit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootAudit.js","sourceRoot":"","sources":["../../src/logging/bootAudit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C,0FAA0F;AAC1F,6FAA6F;AAC7F,qFAAqF;AACrF,2GAA2G;AAC3G,MAAM,gBAAgB,GAAG,iCAAiC,CAAC;AAyB3D;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gCAAgC;IAC9C,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;QACrD,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,kBAAkB,EAIjC,CAAC;IACF,iBAAiB,CAAC;QAChB,GAAG,OAAO,CAAC,OAAO;QAClB,GAAG,OAAO,CAAC,SAAS;QACpB,GAAG,OAAO,CAAC,aAAa;KACzB,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,SAAuC;IACvE,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,QAAuB;IAC5C,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QAC1C,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YACzB,SAAS;QACX,CAAC;QACD,QAAQ,CAAC,QAAQ,EAAE,KAAsB,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,QAAuB,EAAE,QAAuB;IAChE,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC3C,IAAI,QAAQ,IAAI,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChD,OAAO;IACT,CAAC;IACD,IAAI,iBAAiB,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5E,OAAO;IACT,CAAC;IACD,MAAM,YAAY,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;IACtE,OAAO,CAAC,IAAI,CACV,oCAAoC,YAAY,oBAAoB,QAAQ,CAAC,IAAI,IAAI;QACrF,SAAS,QAAQ,IAAI,SAAS,0DAA0D;QACxF,2FAA2F;QAC3F,2DAA2D,CAC5D,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,QAAuB;IAC9C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;QAChC,IAAI,OAAO,IAAI,KAAK,UAAU,EAAE,CAAC;YAC/B,OAAQ,IAA0B,CAAC,IAAI,CAAC;QAC1C,CAAC;QACD,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,IAAI,IAAI,IAAI,OAAQ,IAA0B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC/G,OAAQ,IAAyB,CAAC,IAAI,CAAC;QACzC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Builds the always-on GraphQL boundary log payload: operation name ONLY.
3
+ *
4
+ * Variable VALUES are never emitted here, in any configuration — this is the load-bearing
5
+ * fix for the #2638 secret leak. Verbose variables logging (MJ_LOG_GRAPHQL_VARIABLES=true)
6
+ * is the job of `variablesLoggingMiddleware`, which emits per-root-resolver, fully-emitted
7
+ * values with `Encrypt=true` fields masked.
8
+ *
9
+ * Pure function, no MJ-server import surface, so the "no `variables` key in the boundary
10
+ * line" property is testable in isolation without booting the request-context dependency
11
+ * chain.
12
+ *
13
+ * See docs/adr/0001-graphql-variables-logging-tiered-by-verbose.md.
14
+ */
15
+ export declare function buildBoundaryLogPayload(operationName: string | undefined): {
16
+ operationName: string | undefined;
17
+ };
18
+ //# sourceMappingURL=boundaryLogPayload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundaryLogPayload.d.ts","sourceRoot":"","sources":["../../src/logging/boundaryLogPayload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CACrC,aAAa,EAAE,MAAM,GAAG,SAAS,GAChC;IAAE,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAEvC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Builds the always-on GraphQL boundary log payload: operation name ONLY.
3
+ *
4
+ * Variable VALUES are never emitted here, in any configuration — this is the load-bearing
5
+ * fix for the #2638 secret leak. Verbose variables logging (MJ_LOG_GRAPHQL_VARIABLES=true)
6
+ * is the job of `variablesLoggingMiddleware`, which emits per-root-resolver, fully-emitted
7
+ * values with `Encrypt=true` fields masked.
8
+ *
9
+ * Pure function, no MJ-server import surface, so the "no `variables` key in the boundary
10
+ * line" property is testable in isolation without booting the request-context dependency
11
+ * chain.
12
+ *
13
+ * See docs/adr/0001-graphql-variables-logging-tiered-by-verbose.md.
14
+ */
15
+ export function buildBoundaryLogPayload(operationName) {
16
+ return { operationName };
17
+ }
18
+ //# sourceMappingURL=boundaryLogPayload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundaryLogPayload.js","sourceRoot":"","sources":["../../src/logging/boundaryLogPayload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,uBAAuB,CACrC,aAAiC;IAEjC,OAAO,EAAE,aAAa,EAAE,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { IMetadataProvider } from '@memberjunction/core';
2
+ /**
3
+ * Input to the redactor. Built per `@Arg` per resolver call by the variables-logging middleware.
4
+ *
5
+ * - `inputTypeName` is the GraphQL input type name from the schema (e.g. `"CreateMJCredentialInput"`),
6
+ * derived from `info.parentType.getFields()[info.fieldName].args[i].type` — NOT from any
7
+ * `args[i].constructor.name`, because type-graphql v2.0.0-beta.3 passes raw plain-object args
8
+ * to middleware (conversion to typed instances happens after the middleware chain unwinds).
9
+ * - `provider` is the per-request metadata provider (`context.providers[0].provider`).
10
+ * - `noLogParameter` indicates the `@Arg` itself was decorated `@NoLog` at the parameter level.
11
+ * - `noLogFields` lists field names that were decorated `@NoLog` at the `@Field()` level on the
12
+ * input class. The redactor honors both `EntityFieldInfo.Encrypt=true` (metadata-driven) and
13
+ * this set (decorator-driven) when walking top-level keys.
14
+ */
15
+ export type RedactionContext = {
16
+ inputTypeName: string;
17
+ rawValue: unknown;
18
+ provider: IMetadataProvider;
19
+ noLogParameter: boolean;
20
+ noLogFields: ReadonlySet<string>;
21
+ };
22
+ export declare function redactArg(ctx: RedactionContext): unknown;
23
+ //# sourceMappingURL=secretRedactor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secretRedactor.d.ts","sourceRoot":"","sources":["../../src/logging/secretRedactor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAG9D;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAClC,CAAC;AAaF,wBAAgB,SAAS,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CA4CxD"}
@@ -0,0 +1,53 @@
1
+ import { shortenForLog } from './shortenForLog.js';
2
+ // Delete is included so DeleteMJ*Input resolvers resolve to their entity (yielding an empty
3
+ // or value-free encrypted-field walk) instead of falling to the fail-open shortenForLog path —
4
+ // keeps the boot audit quiet on them. Security is identical either way: Delete inputs carry
5
+ // PK + Options only, no encrypted values. Must stay in sync with bootAudit.ts's INPUT_TYPE_REGEX.
6
+ const INPUT_TYPE_REGEX = /^(Create|Update|Delete)(?<name>.+)Input$/;
7
+ // Assumes EntityFieldInfo.Name === GraphQLFieldName for input-type fields.
8
+ // True today for all codegen output (323 Create*Input + 323 Update*Input).
9
+ // type-graphql allows @Field({ name: 'overrideName' }) to rename fields at the
10
+ // GraphQL layer; MJ codegen does not use this. A future maintainer renaming an
11
+ // encrypted field at the GraphQL layer would silently miss redaction.
12
+ export function redactArg(ctx) {
13
+ if (ctx.noLogParameter) {
14
+ return '<redacted>';
15
+ }
16
+ if (ctx.provider.Entities.length === 0) {
17
+ return '<metadata-not-ready>';
18
+ }
19
+ // Determine the encrypted-field set from entity metadata IF this input maps to a known entity.
20
+ // Custom / non-CRUD inputs (e.g. GetDataInput) have no entity binding — that's fine; field-level
21
+ // @NoLog still applies below. The entity path only adds metadata-driven encrypted-field names.
22
+ let encryptedFieldNames = EMPTY_SET;
23
+ const match = INPUT_TYPE_REGEX.exec(ctx.inputTypeName);
24
+ if (match?.groups?.name) {
25
+ const entity = ctx.provider.Entities.find((e) => e.ClassName === match.groups.name);
26
+ if (entity) {
27
+ encryptedFieldNames = new Set(entity.EncryptedFields.map((f) => f.Name));
28
+ }
29
+ }
30
+ // Walk top-level keys whenever we have BOTH a redaction source (encrypted fields or @NoLog fields)
31
+ // AND a plain-object value. This honors field-level @NoLog even on non-entity-bound inputs — the
32
+ // exact case @NoLog exists for (custom resolvers, e.g. GetDataInputType.Token). Without a redaction
33
+ // source, or for non-object values, fall through to shortenForLog as before.
34
+ const canWalk = (encryptedFieldNames.size > 0 || ctx.noLogFields.size > 0) &&
35
+ ctx.rawValue !== null &&
36
+ typeof ctx.rawValue === 'object' &&
37
+ !Array.isArray(ctx.rawValue);
38
+ if (!canWalk) {
39
+ return shortenForLog(ctx.rawValue);
40
+ }
41
+ const result = {};
42
+ for (const [key, value] of Object.entries(ctx.rawValue)) {
43
+ if (encryptedFieldNames.has(key) || ctx.noLogFields.has(key)) {
44
+ result[key] = '<redacted>';
45
+ }
46
+ else {
47
+ result[key] = shortenForLog(value);
48
+ }
49
+ }
50
+ return result;
51
+ }
52
+ const EMPTY_SET = new Set();
53
+ //# sourceMappingURL=secretRedactor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secretRedactor.js","sourceRoot":"","sources":["../../src/logging/secretRedactor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAuBnD,4FAA4F;AAC5F,+FAA+F;AAC/F,4FAA4F;AAC5F,kGAAkG;AAClG,MAAM,gBAAgB,GAAG,0CAA0C,CAAC;AAEpE,2EAA2E;AAC3E,2EAA2E;AAC3E,+EAA+E;AAC/E,+EAA+E;AAC/E,sEAAsE;AACtE,MAAM,UAAU,SAAS,CAAC,GAAqB;IAC7C,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;QACvB,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO,sBAAsB,CAAC;IAChC,CAAC;IAED,+FAA+F;IAC/F,iGAAiG;IACjG,+FAA+F;IAC/F,IAAI,mBAAmB,GAAwB,SAAS,CAAC;IACzD,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACvD,IAAI,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,CAAC,MAAO,CAAC,IAAI,CAAC,CAAC;QACrF,IAAI,MAAM,EAAE,CAAC;YACX,mBAAmB,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED,mGAAmG;IACnG,iGAAiG;IACjG,oGAAoG;IACpG,6EAA6E;IAC7E,MAAM,OAAO,GACX,CAAC,mBAAmB,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,CAAC;QAC1D,GAAG,CAAC,QAAQ,KAAK,IAAI;QACrB,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ;QAChC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAE/B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,QAAmC,CAAC,EAAE,CAAC;QACnF,IAAI,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7D,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,SAAS,GAAwB,IAAI,GAAG,EAAU,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Renders a value for one-line console logging without Node's `[Object]` truncation.
3
+ * Arrays keep their structure; non-array objects collapse to JSON, truncated at `maxLen`.
4
+ * Objects whose JSON exceeds `maxLen` and contain nested structure are recursed into so
5
+ * outer keys remain visible.
6
+ */
7
+ export declare function shortenForLog(value: unknown, maxLen?: number): unknown;
8
+ //# sourceMappingURL=shortenForLog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shortenForLog.d.ts","sourceRoot":"","sources":["../../src/logging/shortenForLog.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,SAAM,GAAG,OAAO,CAUnE"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Renders a value for one-line console logging without Node's `[Object]` truncation.
3
+ * Arrays keep their structure; non-array objects collapse to JSON, truncated at `maxLen`.
4
+ * Objects whose JSON exceeds `maxLen` and contain nested structure are recursed into so
5
+ * outer keys remain visible.
6
+ */
7
+ export function shortenForLog(value, maxLen = 300) {
8
+ if (value === null || typeof value !== 'object')
9
+ return value;
10
+ if (Array.isArray(value))
11
+ return value.map((v) => shortenForLog(v, maxLen));
12
+ const json = JSON.stringify(value);
13
+ if (json.length <= maxLen)
14
+ return json;
15
+ const result = {};
16
+ for (const [k, v] of Object.entries(value)) {
17
+ result[k] = shortenForLog(v, maxLen);
18
+ }
19
+ return result;
20
+ }
21
+ //# sourceMappingURL=shortenForLog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shortenForLog.js","sourceRoot":"","sources":["../../src/logging/shortenForLog.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc,EAAE,MAAM,GAAG,GAAG;IACxD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,IAAI,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,IAAI,CAAC;IACvC,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;QACtE,MAAM,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { MiddlewareFn } from 'type-graphql';
2
+ import type { AppContext } from '../types.js';
3
+ /**
4
+ * Variables-logging middleware. Default-off; gated on `loggingSettings.graphql.logVariables`.
5
+ *
6
+ * NOTE: this middleware is NOT the primary leak defense. The primary defense is the removal
7
+ * of the `variables` field from the always-on log line in `context.ts`. This middleware
8
+ * provides an opt-in, redacted verbose-echo path for developers debugging locally.
9
+ *
10
+ * When enabled, fires once per root resolver call (skips field resolvers via
11
+ * `info.path.prev === undefined`). For each `@Arg`:
12
+ * - Derives the GraphQL input type name from the schema (NOT from `args[i].constructor.name`,
13
+ * because type-graphql v2.0.0-beta.3 passes raw plain-object args to middleware).
14
+ * - Looks up `@NoLog` marks (parameter-level on the resolver, field-level on the input class).
15
+ * - Delegates redaction to `redactArg`.
16
+ *
17
+ * Emits a single `console.dir` line with shape `{ operation, args }`. Note: the key names
18
+ * differ from the always-on log line (`operationName` vs `operation`, `variables` vs `args`)
19
+ * — operators with log-grep automation should update their patterns.
20
+ */
21
+ export declare const variablesLoggingMiddleware: MiddlewareFn<AppContext>;
22
+ //# sourceMappingURL=variablesLoggingMiddleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variablesLoggingMiddleware.d.ts","sourceRoot":"","sources":["../../src/logging/variablesLoggingMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAKjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA2E9C;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,0BAA0B,EAAE,YAAY,CAAC,UAAU,CAsE/D,CAAC"}
@@ -0,0 +1,127 @@
1
+ import { getMetadataStorage } from 'type-graphql';
2
+ import { getNamedType } from 'graphql';
3
+ import { configInfo } from '../config.js';
4
+ import { redactArg } from './secretRedactor.js';
5
+ import { hasNoLogParameter, getNoLogFields } from './NoLog.js';
6
+ let resolverLookup;
7
+ function getResolverLookup() {
8
+ if (resolverLookup !== undefined) {
9
+ return resolverLookup;
10
+ }
11
+ const storage = getMetadataStorage();
12
+ const lookup = new Map();
13
+ const collect = (entries) => {
14
+ for (const entry of entries) {
15
+ lookup.set(entry.schemaName, {
16
+ target: entry.target,
17
+ methodName: entry.methodName,
18
+ params: entry.params ?? [],
19
+ });
20
+ }
21
+ };
22
+ collect(storage.queries);
23
+ collect(storage.mutations);
24
+ collect(storage.subscriptions);
25
+ resolverLookup = lookup;
26
+ return lookup;
27
+ }
28
+ function findInputClass(typeName) {
29
+ const storage = getMetadataStorage();
30
+ const found = storage.inputTypes.find((t) => t.name === typeName);
31
+ return found?.target;
32
+ }
33
+ /** Type guard for `MinimalArgParamMeta`. */
34
+ function isArgParam(p) {
35
+ return p.kind === 'arg';
36
+ }
37
+ const warnedResolverSignatures = new Set();
38
+ /**
39
+ * Variables-logging middleware. Default-off; gated on `loggingSettings.graphql.logVariables`.
40
+ *
41
+ * NOTE: this middleware is NOT the primary leak defense. The primary defense is the removal
42
+ * of the `variables` field from the always-on log line in `context.ts`. This middleware
43
+ * provides an opt-in, redacted verbose-echo path for developers debugging locally.
44
+ *
45
+ * When enabled, fires once per root resolver call (skips field resolvers via
46
+ * `info.path.prev === undefined`). For each `@Arg`:
47
+ * - Derives the GraphQL input type name from the schema (NOT from `args[i].constructor.name`,
48
+ * because type-graphql v2.0.0-beta.3 passes raw plain-object args to middleware).
49
+ * - Looks up `@NoLog` marks (parameter-level on the resolver, field-level on the input class).
50
+ * - Delegates redaction to `redactArg`.
51
+ *
52
+ * Emits a single `console.dir` line with shape `{ operation, args }`. Note: the key names
53
+ * differ from the always-on log line (`operationName` vs `operation`, `variables` vs `args`)
54
+ * — operators with log-grep automation should update their patterns.
55
+ */
56
+ export const variablesLoggingMiddleware = async (action, next) => {
57
+ // Field resolvers (info.path.prev !== undefined) are skipped — only root @Query/@Mutation/@Subscription.
58
+ if (action.info.path.prev !== undefined) {
59
+ return next();
60
+ }
61
+ // Hot-path zero-cost short-circuit when the flag is off.
62
+ if (!configInfo.loggingSettings.graphql.logVariables) {
63
+ return next();
64
+ }
65
+ const provider = action.context.providers?.[0]?.provider;
66
+ if (!provider) {
67
+ return next();
68
+ }
69
+ const fieldDef = action.info.parentType.getFields()[action.info.fieldName];
70
+ if (!fieldDef) {
71
+ return next();
72
+ }
73
+ const resolverEntry = getResolverLookup().get(action.info.fieldName);
74
+ const argParams = (resolverEntry?.params ?? []).filter(isArgParam);
75
+ const redactedArgs = {};
76
+ let hasUndecoratedCustomArg = false;
77
+ for (const argDef of fieldDef.args) {
78
+ const inputTypeName = getNamedType(argDef.type).name;
79
+ const rawValue = action.args[argDef.name];
80
+ const paramMeta = argParams.find((p) => p.name === argDef.name);
81
+ const noLogParameter = paramMeta && resolverEntry
82
+ ? hasNoLogParameter(resolverEntry.target, resolverEntry.methodName, paramMeta.index)
83
+ : false;
84
+ const inputClass = findInputClass(inputTypeName);
85
+ const noLogFields = inputClass ? getNoLogFields(inputClass) : new Set();
86
+ redactedArgs[argDef.name] = redactArg({
87
+ inputTypeName,
88
+ rawValue,
89
+ provider,
90
+ noLogParameter,
91
+ noLogFields,
92
+ });
93
+ if (!noLogParameter && isCustomArg(inputTypeName, noLogFields, provider)) {
94
+ hasUndecoratedCustomArg = true;
95
+ }
96
+ }
97
+ // eslint-disable-next-line no-console
98
+ console.dir({ operation: action.info.fieldName, args: redactedArgs }, { depth: null, breakLength: 200 });
99
+ if (hasUndecoratedCustomArg && resolverEntry) {
100
+ const signature = `${resolverEntry.target.name}.${resolverEntry.methodName}`;
101
+ if (!warnedResolverSignatures.has(signature)) {
102
+ warnedResolverSignatures.add(signature);
103
+ console.warn(`[mj:NoLog] Resolver ${signature} has un-decorated, non-metadata-bound argument(s). ` +
104
+ `Variables for this resolver were logged in plaintext while logVariables=true is active. ` +
105
+ `Apply @NoLog to sensitive arguments to suppress this.`);
106
+ }
107
+ }
108
+ return next();
109
+ };
110
+ /**
111
+ * Heuristic: an arg is "custom" (i.e. needs `@NoLog` discipline because metadata can't redact it)
112
+ * when its GraphQL input type is not a known `Create<X>Input` / `Update<X>Input` for an entity
113
+ * in the provider, AND no field-level `@NoLog` marks are present on the input class.
114
+ */
115
+ function isCustomArg(inputTypeName, noLogFields, provider) {
116
+ if (noLogFields.size > 0) {
117
+ return false;
118
+ }
119
+ const match = /^(Create|Update)(?<name>.+)Input$/.exec(inputTypeName);
120
+ if (!match || !match.groups?.name) {
121
+ return true;
122
+ }
123
+ const className = match.groups.name;
124
+ const found = provider.Entities.find((e) => e.ClassName === className);
125
+ return !found;
126
+ }
127
+ //# sourceMappingURL=variablesLoggingMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variablesLoggingMiddleware.js","sourceRoot":"","sources":["../../src/logging/variablesLoggingMiddleware.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"GetDataResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/GetDataResolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAUzC,qBACa,gBAAgB;IAEzB,KAAK,EAAE,MAAM,CAAC;IAGd,OAAO,EAAE,MAAM,EAAE,CAAC;CACrB;AAGD,qBACa,iBAAiB;IAE1B,OAAO,EAAE,OAAO,CAAC;IAGjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAGlB,aAAa,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAEjC;;OAEG;IAEH,OAAO,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;CAC9B;AAED,qBACa,sBAAsB;IAE/B,OAAO,EAAE,OAAO,CAAC;IAGjB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,OAAO,EAAE,sBAAsB,EAAE,CAAC;CACrC;AAED,qBACa,sBAAsB;IAE/B,EAAE,EAAE,MAAM,CAAC;IAGX,IAAI,EAAE,MAAM,CAAC;IAGb,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,UAAU,EAAE,MAAM,CAAC;IAGnB,QAAQ,EAAE,MAAM,CAAC;IAGjB,SAAS,EAAE,MAAM,CAAC;IAGlB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,MAAM,EAAE,2BAA2B,EAAE,CAAC;CACzC;AAED,qBACa,2BAA2B;IAEpC,EAAE,EAAE,MAAM,CAAC;IAGX,IAAI,EAAE,MAAM,CAAC;IAGb,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,IAAI,EAAE,MAAM,CAAC;IAGb,UAAU,EAAE,OAAO,CAAC;IAGpB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,qBAAa,eAAe;IACxB;;;;OAIG;IAGG,OAAO,CACyB,KAAK,EAAE,gBAAgB,EACtD,OAAO,EAAE,UAAU,GACvB,OAAO,CAAC,iBAAiB,CAAC;IAmGvB,cAAc,CACb,OAAO,EAAE,UAAU,GACvB,OAAO,CAAC,sBAAsB,CAAC;CAsCrC;AAGD,qBAAa,WAAW;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,IAAI,CAAC;IACb,UAAU,EAAE,GAAG,CAAC;CACnB;AACD;;;;GAIG;AACH,qBAAa,kBAAkB;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,IAAI,CAAC;IACjB;;OAEG;IACH,gBAAgB,EAAE,GAAG,CAAC;IACtB,SAAS,EAAE,WAAW,EAAE,CAAC;CAC5B;AAGD,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAE,MAA+B,EAAE,gBAAgB,CAAC,EAAE,GAAG,GAAG,kBAAkB,CAezI;AACD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,QAQ9C;AACD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,WAExC;AACD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,WAMzC;AACD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,QAW5D"}
1
+ {"version":3,"file":"GetDataResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/GetDataResolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAWzC,qBACa,gBAAgB;IAGzB,KAAK,EAAE,MAAM,CAAC;IAGd,OAAO,EAAE,MAAM,EAAE,CAAC;CACrB;AAGD,qBACa,iBAAiB;IAE1B,OAAO,EAAE,OAAO,CAAC;IAGjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAGlB,aAAa,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAEjC;;OAEG;IAEH,OAAO,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;CAC9B;AAED,qBACa,sBAAsB;IAE/B,OAAO,EAAE,OAAO,CAAC;IAGjB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,OAAO,EAAE,sBAAsB,EAAE,CAAC;CACrC;AAED,qBACa,sBAAsB;IAE/B,EAAE,EAAE,MAAM,CAAC;IAGX,IAAI,EAAE,MAAM,CAAC;IAGb,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,UAAU,EAAE,MAAM,CAAC;IAGnB,QAAQ,EAAE,MAAM,CAAC;IAGjB,SAAS,EAAE,MAAM,CAAC;IAGlB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,MAAM,EAAE,2BAA2B,EAAE,CAAC;CACzC;AAED,qBACa,2BAA2B;IAEpC,EAAE,EAAE,MAAM,CAAC;IAGX,IAAI,EAAE,MAAM,CAAC;IAGb,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,IAAI,EAAE,MAAM,CAAC;IAGb,UAAU,EAAE,OAAO,CAAC;IAGpB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,qBAAa,eAAe;IACxB;;;;OAIG;IAGG,OAAO,CACyB,KAAK,EAAE,gBAAgB,EACtD,OAAO,EAAE,UAAU,GACvB,OAAO,CAAC,iBAAiB,CAAC;IAqGvB,cAAc,CACb,OAAO,EAAE,UAAU,GACvB,OAAO,CAAC,sBAAsB,CAAC;CAsCrC;AAGD,qBAAa,WAAW;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,IAAI,CAAC;IACb,UAAU,EAAE,GAAG,CAAC;CACnB;AACD;;;;GAIG;AACH,qBAAa,kBAAkB;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,IAAI,CAAC;IACjB;;OAEG;IACH,gBAAgB,EAAE,GAAG,CAAC;IACtB,SAAS,EAAE,WAAW,EAAE,CAAC;CAC5B;AAGD,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAE,MAA+B,EAAE,gBAAgB,CAAC,EAAE,GAAG,GAAG,kBAAkB,CAezI;AACD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,QAQ9C;AACD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,WAExC;AACD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,WAMzC;AACD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,QAW5D"}
@@ -11,9 +11,10 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
11
  return function (target, key) { decorator(target, key, paramIndex); }
12
12
  };
13
13
  import { Arg, Ctx, Field, InputType, ObjectType, Query } from 'type-graphql';
14
- import { LogError, LogStatus } from '@memberjunction/core';
14
+ import { LogError, LogStatus, LogStatusEx } from '@memberjunction/core';
15
15
  import { QueryCompositionEngine } from '@memberjunction/generic-database-provider';
16
16
  import { RequireSystemUser } from '../directives/RequireSystemUser.js';
17
+ import { NoLog } from '../logging/NoLog.js';
17
18
  import { v4 as uuidv4 } from 'uuid';
18
19
  import { GetReadOnlyDataSource, GetReadOnlyProvider } from '../util.js';
19
20
  import { getDbType } from '../index.js';
@@ -23,6 +24,7 @@ let GetDataInputType = class GetDataInputType {
23
24
  };
24
25
  __decorate([
25
26
  Field(() => String),
27
+ NoLog,
26
28
  __metadata("design:type", String)
27
29
  ], GetDataInputType.prototype, "Token", void 0);
28
30
  __decorate([
@@ -158,9 +160,11 @@ export class GetDataResolver {
158
160
  */
159
161
  async GetData(input, context) {
160
162
  try {
161
- LogStatus(`GetDataResolver.GetData() ---- IMPORTANT - temporarily using the same connection as rest of the server, we need to separately create a READ ONLY CONNECTION and pass that in
162
- the AppContext so we can use that special connection here to ensure we are using a lower privileged connection for this operation to prevent mutation from being possible.`);
163
- LogStatus(`${JSON.stringify(input)}`);
163
+ LogStatusEx({
164
+ message: `GetDataResolver.GetData() ---- IMPORTANT - temporarily using the same connection as rest of the server, we need to separately create a READ ONLY CONNECTION and pass that in the AppContext so we can use that special connection here to ensure we are using a lower privileged connection for this operation to prevent mutation from being possible.`,
165
+ verboseOnly: true,
166
+ });
167
+ LogStatus(`GetData invoked: ${input.Queries.length} queries`);
164
168
  // validate the token
165
169
  if (!isTokenValid(input.Token)) {
166
170
  throw new Error(`Token ${input.Token} is not valid or has expired`);