@pikku/inspector 0.12.4 → 0.12.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  ## 0.12.0
2
2
 
3
+ ## 0.12.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 84f01ad: Add credentialOverrides to wireAddon for remapping credential names, fix credential services template to pass variables argument.
8
+ - Updated dependencies [84f01ad]
9
+ - @pikku/core@0.12.12
10
+
11
+ ## 0.12.5
12
+
13
+ ### Patch Changes
14
+
15
+ - 65eccc6: Cache Zod schema generation between re-inspection passes and batch imports by source file. Schemas are cached using a fingerprint of schemaLookup entries + file mtimes, so reinspections skip Zod generation entirely when schemas haven't changed. Source file imports are grouped so each file is imported once instead of per-schema. Reduces `pikku all` from ~5 minutes to ~13 seconds on projects with many Zod schemas.
16
+ - 0f59432: Add per-user credential system with CredentialService, OAuth2 route handlers, and KyselyCredentialService with envelope encryption
17
+ - Updated dependencies [0f59432]
18
+ - Updated dependencies [52b64d1]
19
+ - @pikku/core@0.12.10
20
+
3
21
  ## 0.12.4
4
22
 
5
23
  ### Patch Changes
@@ -0,0 +1,2 @@
1
+ import type { AddWiring } from '../types.js';
2
+ export declare const addCredential: AddWiring;
@@ -0,0 +1,118 @@
1
+ import * as ts from 'typescript';
2
+ import { getPropertyValue, getArrayPropertyValue, } from '../utils/get-property-value.js';
3
+ import { ErrorCode } from '../error-codes.js';
4
+ import { detectSchemaVendorOrError } from '../utils/detect-schema-vendor.js';
5
+ export const addCredential = (logger, node, checker, state, _options) => {
6
+ if (!ts.isCallExpression(node)) {
7
+ return;
8
+ }
9
+ const args = node.arguments;
10
+ const firstArg = args[0];
11
+ const expression = node.expression;
12
+ if (!ts.isIdentifier(expression) || expression.text !== 'wireCredential') {
13
+ return;
14
+ }
15
+ if (!firstArg) {
16
+ return;
17
+ }
18
+ if (ts.isObjectLiteralExpression(firstArg)) {
19
+ const obj = firstArg;
20
+ const nameValue = getPropertyValue(obj, 'name');
21
+ const displayNameValue = getPropertyValue(obj, 'displayName');
22
+ const descriptionValue = getPropertyValue(obj, 'description');
23
+ const typeValue = getPropertyValue(obj, 'type');
24
+ if (!nameValue) {
25
+ logger.critical(ErrorCode.MISSING_NAME, "Credential is missing the required 'name' property.");
26
+ return;
27
+ }
28
+ if (!displayNameValue) {
29
+ logger.critical(ErrorCode.MISSING_NAME, `Credential '${nameValue}' is missing the required 'displayName' property.`);
30
+ return;
31
+ }
32
+ if (!typeValue || (typeValue !== 'singleton' && typeValue !== 'wire')) {
33
+ logger.critical(ErrorCode.MISSING_NAME, `Credential '${nameValue}' is missing or has invalid 'type' property. Must be 'singleton' or 'wire'.`);
34
+ return;
35
+ }
36
+ let schemaVariableName = null;
37
+ let schemaSourceFile = null;
38
+ let schemaIdentifier = null;
39
+ for (const prop of obj.properties) {
40
+ if (ts.isPropertyAssignment(prop) &&
41
+ ts.isIdentifier(prop.name) &&
42
+ prop.name.text === 'schema') {
43
+ if (ts.isIdentifier(prop.initializer)) {
44
+ schemaVariableName = prop.initializer.text;
45
+ schemaIdentifier = prop.initializer;
46
+ const symbol = checker.getSymbolAtLocation(prop.initializer);
47
+ if (symbol) {
48
+ const decl = symbol.valueDeclaration || symbol.declarations?.[0];
49
+ if (decl) {
50
+ if (ts.isImportSpecifier(decl)) {
51
+ const aliasedSymbol = checker.getAliasedSymbol(symbol);
52
+ if (aliasedSymbol) {
53
+ const aliasedDecl = aliasedSymbol.valueDeclaration ||
54
+ aliasedSymbol.declarations?.[0];
55
+ if (aliasedDecl) {
56
+ schemaSourceFile = aliasedDecl.getSourceFile().fileName;
57
+ }
58
+ }
59
+ }
60
+ else {
61
+ schemaSourceFile = decl.getSourceFile().fileName;
62
+ }
63
+ }
64
+ }
65
+ }
66
+ break;
67
+ }
68
+ }
69
+ let oauth2 = undefined;
70
+ const oauth2Prop = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
71
+ ts.isIdentifier(p.name) &&
72
+ p.name.text === 'oauth2');
73
+ if (oauth2Prop &&
74
+ ts.isPropertyAssignment(oauth2Prop) &&
75
+ ts.isObjectLiteralExpression(oauth2Prop.initializer)) {
76
+ const oauth2Obj = oauth2Prop.initializer;
77
+ const appCredentialSecretId = getPropertyValue(oauth2Obj, 'appCredentialSecretId');
78
+ const tokenSecretId = getPropertyValue(oauth2Obj, 'tokenSecretId');
79
+ const authorizationUrl = getPropertyValue(oauth2Obj, 'authorizationUrl');
80
+ const tokenUrl = getPropertyValue(oauth2Obj, 'tokenUrl');
81
+ const scopes = getArrayPropertyValue(oauth2Obj, 'scopes');
82
+ const pkce = getPropertyValue(oauth2Obj, 'pkce');
83
+ if (appCredentialSecretId && authorizationUrl && tokenUrl && scopes) {
84
+ oauth2 = {
85
+ appCredentialSecretId,
86
+ tokenSecretId: tokenSecretId || undefined,
87
+ authorizationUrl,
88
+ tokenUrl,
89
+ scopes,
90
+ pkce: pkce || undefined,
91
+ };
92
+ }
93
+ }
94
+ const sourceFile = node.getSourceFile().fileName;
95
+ state.credentials.files.add(sourceFile);
96
+ let schemaLookupName;
97
+ if (schemaVariableName && schemaSourceFile && schemaIdentifier) {
98
+ const vendor = detectSchemaVendorOrError(schemaIdentifier, checker, logger, `Credential '${nameValue}'`, schemaSourceFile);
99
+ if (vendor) {
100
+ schemaLookupName = `CredentialSchema_${nameValue}`;
101
+ state.schemaLookup.set(schemaLookupName, {
102
+ variableName: schemaVariableName,
103
+ sourceFile: schemaSourceFile,
104
+ vendor,
105
+ });
106
+ }
107
+ }
108
+ state.credentials.definitions.push({
109
+ name: nameValue,
110
+ displayName: displayNameValue,
111
+ description: descriptionValue || undefined,
112
+ type: typeValue,
113
+ schema: schemaLookupName,
114
+ oauth2,
115
+ sourceFile,
116
+ });
117
+ }
118
+ };
@@ -1,3 +1 @@
1
- import type { AddWiring } from '../types.js';
2
- export declare const addSecret: AddWiring;
3
- export declare const addOAuth2Credential: AddWiring;
1
+ export declare const addSecret: import("../types.js").AddWiring;
@@ -1,6 +1,3 @@
1
- import * as ts from 'typescript';
2
- import { getPropertyValue, getArrayPropertyValue, } from '../utils/get-property-value.js';
3
- import { ErrorCode } from '../error-codes.js';
4
1
  import { createAddKeyedWiring } from './add-keyed-wiring.js';
5
2
  export const addSecret = createAddKeyedWiring({
6
3
  functionName: 'wireSecret',
@@ -9,74 +6,3 @@ export const addSecret = createAddKeyedWiring({
9
6
  schemaPrefix: 'SecretSchema',
10
7
  getState: (state) => state.secrets,
11
8
  });
12
- export const addOAuth2Credential = (logger, node, _checker, state, _options) => {
13
- if (!ts.isCallExpression(node)) {
14
- return;
15
- }
16
- const args = node.arguments;
17
- const firstArg = args[0];
18
- const expression = node.expression;
19
- if (!ts.isIdentifier(expression) ||
20
- expression.text !== 'wireOAuth2Credential') {
21
- return;
22
- }
23
- if (!firstArg) {
24
- return;
25
- }
26
- if (ts.isObjectLiteralExpression(firstArg)) {
27
- const obj = firstArg;
28
- const nameValue = getPropertyValue(obj, 'name');
29
- const displayNameValue = getPropertyValue(obj, 'displayName');
30
- const descriptionValue = getPropertyValue(obj, 'description');
31
- const secretIdValue = getPropertyValue(obj, 'secretId');
32
- const tokenSecretIdValue = getPropertyValue(obj, 'tokenSecretId');
33
- const authorizationUrlValue = getPropertyValue(obj, 'authorizationUrl');
34
- const tokenUrlValue = getPropertyValue(obj, 'tokenUrl');
35
- const scopesValue = getArrayPropertyValue(obj, 'scopes');
36
- const pkceValue = getPropertyValue(obj, 'pkce');
37
- if (!nameValue) {
38
- logger.critical(ErrorCode.MISSING_NAME, "OAuth2 Credential is missing the required 'name' property.");
39
- return;
40
- }
41
- if (!displayNameValue) {
42
- logger.critical(ErrorCode.MISSING_NAME, `OAuth2 Credential '${nameValue}' is missing the required 'displayName' property.`);
43
- return;
44
- }
45
- if (!secretIdValue) {
46
- logger.critical(ErrorCode.MISSING_NAME, `OAuth2 Credential '${nameValue}' is missing the required 'secretId' property.`);
47
- return;
48
- }
49
- if (!tokenSecretIdValue) {
50
- logger.critical(ErrorCode.MISSING_NAME, `OAuth2 Credential '${nameValue}' is missing the required 'tokenSecretId' property.`);
51
- return;
52
- }
53
- if (!authorizationUrlValue) {
54
- logger.critical(ErrorCode.MISSING_NAME, `OAuth2 Credential '${nameValue}' is missing the required 'authorizationUrl' property.`);
55
- return;
56
- }
57
- if (!tokenUrlValue) {
58
- logger.critical(ErrorCode.MISSING_NAME, `OAuth2 Credential '${nameValue}' is missing the required 'tokenUrl' property.`);
59
- return;
60
- }
61
- if (!scopesValue || scopesValue.length === 0) {
62
- logger.critical(ErrorCode.MISSING_NAME, `OAuth2 Credential '${nameValue}' is missing the required 'scopes' property.`);
63
- return;
64
- }
65
- const sourceFile = node.getSourceFile().fileName;
66
- state.secrets.files.add(sourceFile);
67
- state.secrets.definitions.push({
68
- name: nameValue,
69
- displayName: displayNameValue,
70
- description: descriptionValue || undefined,
71
- secretId: secretIdValue,
72
- oauth2: {
73
- tokenSecretId: tokenSecretIdValue,
74
- authorizationUrl: authorizationUrlValue,
75
- tokenUrl: tokenUrlValue,
76
- scopes: scopesValue,
77
- pkce: pkceValue || undefined,
78
- },
79
- sourceFile,
80
- });
81
- }
82
- };
@@ -35,6 +35,7 @@ export function addWireAddon(node, state, logger) {
35
35
  let mcp;
36
36
  let secretOverrides;
37
37
  let variableOverrides;
38
+ let credentialOverrides;
38
39
  for (const prop of firstArg.properties) {
39
40
  if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name))
40
41
  continue;
@@ -61,6 +62,10 @@ export function addWireAddon(node, state, logger) {
61
62
  ts.isObjectLiteralExpression(prop.initializer)) {
62
63
  variableOverrides = parseStringRecord(prop.initializer);
63
64
  }
65
+ else if (key === 'credentialOverrides' &&
66
+ ts.isObjectLiteralExpression(prop.initializer)) {
67
+ credentialOverrides = parseStringRecord(prop.initializer);
68
+ }
64
69
  }
65
70
  if (!name || !pkg)
66
71
  return;
@@ -71,6 +76,7 @@ export function addWireAddon(node, state, logger) {
71
76
  mcp,
72
77
  secretOverrides,
73
78
  variableOverrides,
79
+ credentialOverrides,
74
80
  });
75
81
  state.rpc.usedAddons.add(name);
76
82
  state.rpc.wireAddonFiles.add(node.getSourceFile().fileName);
package/dist/inspector.js CHANGED
@@ -4,7 +4,7 @@ import { visitSetup, visitRoutes } from './visit.js';
4
4
  import { TypesMap } from './types-map.js';
5
5
  import { getFilesAndMethods } from './utils/get-files-and-methods.js';
6
6
  import { findCommonAncestor } from './utils/find-root-dir.js';
7
- import { aggregateRequiredServices, validateAgentModels, validateAgentOverrides, validateSecretOverrides, validateVariableOverrides, computeResolvedIOTypes, computeMiddlewareGroupsMeta, computePermissionsGroupsMeta, computeRequiredSchemas, computeDiagnostics, validateSchemaWiringSeparation, } from './utils/post-process.js';
7
+ import { aggregateRequiredServices, validateAgentModels, validateAgentOverrides, validateSecretOverrides, validateVariableOverrides, validateCredentialOverrides, computeResolvedIOTypes, computeMiddlewareGroupsMeta, computePermissionsGroupsMeta, computeRequiredSchemas, computeDiagnostics, validateSchemaWiringSeparation, } from './utils/post-process.js';
8
8
  import { generateOpenAPISpec } from './utils/serialize-openapi-json.js';
9
9
  import { pikkuState } from '@pikku/core/internal';
10
10
  import { resolveLatestVersions } from './utils/resolve-versions.js';
@@ -118,6 +118,10 @@ export function getInitialInspectorState(rootDir) {
118
118
  definitions: [],
119
119
  files: new Set(),
120
120
  },
121
+ credentials: {
122
+ definitions: [],
123
+ files: new Set(),
124
+ },
121
125
  variables: {
122
126
  definitions: [],
123
127
  files: new Set(),
@@ -261,6 +265,7 @@ export const inspect = async (logger, routeFiles, options = {}) => {
261
265
  validateAgentOverrides(logger, state, options.modelConfig);
262
266
  validateSecretOverrides(logger, state);
263
267
  validateVariableOverrides(logger, state);
268
+ validateCredentialOverrides(logger, state);
264
269
  }
265
270
  state.program = program;
266
271
  return state;
package/dist/types.d.ts CHANGED
@@ -11,6 +11,7 @@ import type { AIAgentMeta } from '@pikku/core/ai-agent';
11
11
  import type { CLIMeta } from '@pikku/core/cli';
12
12
  import type { NodesMeta } from '@pikku/core/node';
13
13
  import type { SecretDefinitions } from '@pikku/core/secret';
14
+ import type { CredentialDefinitions } from '@pikku/core/credential';
14
15
  import type { VariableDefinitions } from '@pikku/core/variable';
15
16
  import type { TypesMap } from './types-map.js';
16
17
  import type { FunctionsMeta, FunctionServicesMeta, FunctionWiresMeta, JSONValue } from '@pikku/core';
@@ -321,6 +322,7 @@ export interface InspectorState {
321
322
  mcp?: boolean;
322
323
  secretOverrides?: Record<string, string>;
323
324
  variableOverrides?: Record<string, string>;
325
+ credentialOverrides?: Record<string, string>;
324
326
  }>;
325
327
  wireAddonFiles: Set<string>;
326
328
  };
@@ -349,6 +351,10 @@ export interface InspectorState {
349
351
  definitions: SecretDefinitions;
350
352
  files: Set<string>;
351
353
  };
354
+ credentials: {
355
+ definitions: CredentialDefinitions;
356
+ files: Set<string>;
357
+ };
352
358
  variables: {
353
359
  definitions: VariableDefinitions;
354
360
  files: Set<string>;
@@ -15,6 +15,7 @@ export declare function extractWireNames(list?: MiddlewareMetadata[] | Permissio
15
15
  */
16
16
  export declare function aggregateRequiredServices(state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
17
17
  export declare function validateSecretOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
18
+ export declare function validateCredentialOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
18
19
  export declare function validateVariableOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
19
20
  export declare function computeResolvedIOTypes(state: InspectorState): void;
20
21
  export declare function computeMiddlewareGroupsMeta(state: InspectorState): void;
@@ -177,6 +177,22 @@ export function validateSecretOverrides(logger, state) {
177
177
  }
178
178
  }
179
179
  }
180
+ export function validateCredentialOverrides(logger, state) {
181
+ const { wireAddonDeclarations } = state.rpc;
182
+ if (!wireAddonDeclarations || wireAddonDeclarations.size === 0)
183
+ return;
184
+ const credentialNames = new Set(state.credentials?.definitions.map((d) => d.name) ?? []);
185
+ for (const [namespace, addonDecl] of wireAddonDeclarations.entries()) {
186
+ if (!addonDecl.credentialOverrides)
187
+ continue;
188
+ for (const credentialKey of Object.keys(addonDecl.credentialOverrides)) {
189
+ if (!credentialNames.has(credentialKey)) {
190
+ const availableCredentials = Array.from(credentialNames);
191
+ logger.critical(ErrorCode.INVALID_VALUE, `Credential override '${credentialKey}' in addon '${namespace}' (${addonDecl.package}) does not exist. Available credentials: ${availableCredentials.join(', ') || 'none'}`);
192
+ }
193
+ }
194
+ }
195
+ }
180
196
  export function validateVariableOverrides(logger, state) {
181
197
  const { wireAddonDeclarations } = state.rpc;
182
198
  if (!wireAddonDeclarations || wireAddonDeclarations.size === 0)
@@ -13,4 +13,5 @@ export declare const resolveAddonName: (identifier: ts.Identifier, checker: ts.T
13
13
  rpcEndpoint?: string;
14
14
  secretOverrides?: Record<string, string>;
15
15
  variableOverrides?: Record<string, string>;
16
+ credentialOverrides?: Record<string, string>;
16
17
  }>) => string | null;
@@ -176,6 +176,7 @@ export interface SerializableInspectorState {
176
176
  rpcEndpoint?: string;
177
177
  secretOverrides?: Record<string, string>;
178
178
  variableOverrides?: Record<string, string>;
179
+ credentialOverrides?: Record<string, string>;
179
180
  }
180
181
  ]>;
181
182
  wireAddonFiles: string[];
@@ -205,6 +206,10 @@ export interface SerializableInspectorState {
205
206
  definitions: InspectorState['secrets']['definitions'];
206
207
  files: string[];
207
208
  };
209
+ credentials: {
210
+ definitions: InspectorState['credentials']['definitions'];
211
+ files: string[];
212
+ };
208
213
  variables: {
209
214
  definitions: InspectorState['variables']['definitions'];
210
215
  files: string[];
@@ -101,6 +101,10 @@ export function serializeInspectorState(state) {
101
101
  definitions: state.secrets.definitions,
102
102
  files: Array.from(state.secrets.files),
103
103
  },
104
+ credentials: {
105
+ definitions: state.credentials.definitions,
106
+ files: Array.from(state.credentials.files),
107
+ },
104
108
  variables: {
105
109
  definitions: state.variables.definitions,
106
110
  files: Array.from(state.variables.files),
@@ -244,6 +248,10 @@ export function deserializeInspectorState(data) {
244
248
  definitions: data.secrets?.definitions || [],
245
249
  files: new Set(data.secrets?.files || []),
246
250
  },
251
+ credentials: {
252
+ definitions: data.credentials?.definitions || [],
253
+ files: new Set(data.credentials?.files || []),
254
+ },
247
255
  variables: {
248
256
  definitions: data.variables?.definitions || [],
249
257
  files: new Set(data.variables?.files || []),
package/dist/visit.js CHANGED
@@ -17,7 +17,8 @@ import { addWireAddon } from './add/add-wire-addon.js';
17
17
  import { addMiddleware } from './add/add-middleware.js';
18
18
  import { addPermission } from './add/add-permission.js';
19
19
  import { addCLI, addCLIRenderers } from './add/add-cli.js';
20
- import { addSecret, addOAuth2Credential } from './add/add-secret.js';
20
+ import { addSecret } from './add/add-secret.js';
21
+ import { addCredential } from './add/add-credential.js';
21
22
  import { addVariable } from './add/add-variable.js';
22
23
  import { addWorkflowGraph } from './add/add-workflow-graph.js';
23
24
  import { addAIAgent } from './add/add-ai-agent.js';
@@ -41,7 +42,7 @@ export const visitSetup = (logger, checker, node, state, options) => {
41
42
  export const visitRoutes = (logger, checker, node, state, options) => {
42
43
  addFunctions(logger, node, checker, state, options);
43
44
  addSecret(logger, node, checker, state, options);
44
- addOAuth2Credential(logger, node, checker, state, options);
45
+ addCredential(logger, node, checker, state, options);
45
46
  addVariable(logger, node, checker, state, options);
46
47
  addHTTPRoute(logger, node, checker, state, options);
47
48
  addHTTPRoutes(logger, node, checker, state, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/inspector",
3
- "version": "0.12.4",
3
+ "version": "0.12.6",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "BUSL-1.1",
6
6
  "type": "module",
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@openapi-contrib/json-schema-to-openapi-schema": "^4.3.1",
38
- "@pikku/core": "^0.12.9",
38
+ "@pikku/core": "^0.12.12",
39
39
  "path-to-regexp": "^8.3.0",
40
40
  "ts-json-schema-generator": "^2.5.0",
41
41
  "tsx": "^4.21.0",
@@ -0,0 +1,178 @@
1
+ import * as ts from 'typescript'
2
+ import {
3
+ getPropertyValue,
4
+ getArrayPropertyValue,
5
+ } from '../utils/get-property-value.js'
6
+ import type { AddWiring } from '../types.js'
7
+ import { ErrorCode } from '../error-codes.js'
8
+ import { detectSchemaVendorOrError } from '../utils/detect-schema-vendor.js'
9
+
10
+ export const addCredential: AddWiring = (
11
+ logger,
12
+ node,
13
+ checker,
14
+ state,
15
+ _options
16
+ ) => {
17
+ if (!ts.isCallExpression(node)) {
18
+ return
19
+ }
20
+
21
+ const args = node.arguments
22
+ const firstArg = args[0]
23
+ const expression = node.expression
24
+
25
+ if (!ts.isIdentifier(expression) || expression.text !== 'wireCredential') {
26
+ return
27
+ }
28
+
29
+ if (!firstArg) {
30
+ return
31
+ }
32
+
33
+ if (ts.isObjectLiteralExpression(firstArg)) {
34
+ const obj = firstArg
35
+
36
+ const nameValue = getPropertyValue(obj, 'name') as string | null
37
+ const displayNameValue = getPropertyValue(obj, 'displayName') as
38
+ | string
39
+ | null
40
+ const descriptionValue = getPropertyValue(obj, 'description') as
41
+ | string
42
+ | null
43
+ const typeValue = getPropertyValue(obj, 'type') as string | null
44
+
45
+ if (!nameValue) {
46
+ logger.critical(
47
+ ErrorCode.MISSING_NAME,
48
+ "Credential is missing the required 'name' property."
49
+ )
50
+ return
51
+ }
52
+
53
+ if (!displayNameValue) {
54
+ logger.critical(
55
+ ErrorCode.MISSING_NAME,
56
+ `Credential '${nameValue}' is missing the required 'displayName' property.`
57
+ )
58
+ return
59
+ }
60
+
61
+ if (!typeValue || (typeValue !== 'singleton' && typeValue !== 'wire')) {
62
+ logger.critical(
63
+ ErrorCode.MISSING_NAME,
64
+ `Credential '${nameValue}' is missing or has invalid 'type' property. Must be 'singleton' or 'wire'.`
65
+ )
66
+ return
67
+ }
68
+
69
+ let schemaVariableName: string | null = null
70
+ let schemaSourceFile: string | null = null
71
+ let schemaIdentifier: ts.Identifier | null = null
72
+ for (const prop of obj.properties) {
73
+ if (
74
+ ts.isPropertyAssignment(prop) &&
75
+ ts.isIdentifier(prop.name) &&
76
+ prop.name.text === 'schema'
77
+ ) {
78
+ if (ts.isIdentifier(prop.initializer)) {
79
+ schemaVariableName = prop.initializer.text
80
+ schemaIdentifier = prop.initializer
81
+
82
+ const symbol = checker.getSymbolAtLocation(prop.initializer)
83
+ if (symbol) {
84
+ const decl = symbol.valueDeclaration || symbol.declarations?.[0]
85
+ if (decl) {
86
+ if (ts.isImportSpecifier(decl)) {
87
+ const aliasedSymbol = checker.getAliasedSymbol(symbol)
88
+ if (aliasedSymbol) {
89
+ const aliasedDecl =
90
+ aliasedSymbol.valueDeclaration ||
91
+ aliasedSymbol.declarations?.[0]
92
+ if (aliasedDecl) {
93
+ schemaSourceFile = aliasedDecl.getSourceFile().fileName
94
+ }
95
+ }
96
+ } else {
97
+ schemaSourceFile = decl.getSourceFile().fileName
98
+ }
99
+ }
100
+ }
101
+ }
102
+ break
103
+ }
104
+ }
105
+
106
+ let oauth2: any = undefined
107
+ const oauth2Prop = obj.properties.find(
108
+ (p) =>
109
+ ts.isPropertyAssignment(p) &&
110
+ ts.isIdentifier(p.name) &&
111
+ p.name.text === 'oauth2'
112
+ )
113
+ if (
114
+ oauth2Prop &&
115
+ ts.isPropertyAssignment(oauth2Prop) &&
116
+ ts.isObjectLiteralExpression(oauth2Prop.initializer)
117
+ ) {
118
+ const oauth2Obj = oauth2Prop.initializer
119
+ const appCredentialSecretId = getPropertyValue(
120
+ oauth2Obj,
121
+ 'appCredentialSecretId'
122
+ ) as string | null
123
+ const tokenSecretId = getPropertyValue(oauth2Obj, 'tokenSecretId') as
124
+ | string
125
+ | null
126
+ const authorizationUrl = getPropertyValue(
127
+ oauth2Obj,
128
+ 'authorizationUrl'
129
+ ) as string | null
130
+ const tokenUrl = getPropertyValue(oauth2Obj, 'tokenUrl') as string | null
131
+ const scopes = getArrayPropertyValue(oauth2Obj, 'scopes')
132
+ const pkce = getPropertyValue(oauth2Obj, 'pkce') as boolean | null
133
+
134
+ if (appCredentialSecretId && authorizationUrl && tokenUrl && scopes) {
135
+ oauth2 = {
136
+ appCredentialSecretId,
137
+ tokenSecretId: tokenSecretId || undefined,
138
+ authorizationUrl,
139
+ tokenUrl,
140
+ scopes,
141
+ pkce: pkce || undefined,
142
+ }
143
+ }
144
+ }
145
+
146
+ const sourceFile = node.getSourceFile().fileName
147
+ state.credentials.files.add(sourceFile)
148
+
149
+ let schemaLookupName: string | undefined
150
+ if (schemaVariableName && schemaSourceFile && schemaIdentifier) {
151
+ const vendor = detectSchemaVendorOrError(
152
+ schemaIdentifier,
153
+ checker,
154
+ logger,
155
+ `Credential '${nameValue}'`,
156
+ schemaSourceFile
157
+ )
158
+ if (vendor) {
159
+ schemaLookupName = `CredentialSchema_${nameValue}`
160
+ state.schemaLookup.set(schemaLookupName, {
161
+ variableName: schemaVariableName,
162
+ sourceFile: schemaSourceFile,
163
+ vendor,
164
+ })
165
+ }
166
+ }
167
+
168
+ state.credentials.definitions.push({
169
+ name: nameValue,
170
+ displayName: displayNameValue,
171
+ description: descriptionValue || undefined,
172
+ type: typeValue as 'singleton' | 'wire',
173
+ schema: schemaLookupName,
174
+ oauth2,
175
+ sourceFile,
176
+ })
177
+ }
178
+ }