@stackoverflow/backstage-stack-overflow-teams-collator 1.5.1 → 1.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/collators/StackOverflowQuestionsCollatorFactory.cjs.js +118 -0
- package/dist/collators/StackOverflowQuestionsCollatorFactory.cjs.js.map +1 -0
- package/dist/index.cjs.js +12 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +73 -0
- package/dist/module/SearchStackOverflowCollatorModule.cjs.js +40 -0
- package/dist/module/SearchStackOverflowCollatorModule.cjs.js.map +1 -0
- package/package.json +15 -7
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var stream = require('stream');
|
|
4
|
+
var qs = require('qs');
|
|
5
|
+
|
|
6
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var qs__default = /*#__PURE__*/_interopDefaultCompat(qs);
|
|
9
|
+
|
|
10
|
+
class StackOverflowQuestionsCollatorFactory {
|
|
11
|
+
requestParams;
|
|
12
|
+
baseUrl;
|
|
13
|
+
apiAccessToken;
|
|
14
|
+
teamName;
|
|
15
|
+
logger;
|
|
16
|
+
referrer = "Backstage_Plugin";
|
|
17
|
+
type = "stack-overflow";
|
|
18
|
+
stackOverflowTeamsAPI = "https://api.stackoverflowteams.com";
|
|
19
|
+
forceOriginUrl = (baseUrl) => `${new URL(baseUrl).origin}`;
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.baseUrl = this.forceOriginUrl(options.baseUrl || this.stackOverflowTeamsAPI);
|
|
22
|
+
this.apiAccessToken = options.apiAccessToken;
|
|
23
|
+
this.teamName = options.teamName;
|
|
24
|
+
this.logger = options.logger.child({ documentType: this.type });
|
|
25
|
+
this.requestParams = {
|
|
26
|
+
order: "desc",
|
|
27
|
+
sort: "creation",
|
|
28
|
+
...options.requestParams ?? {}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
static fromConfig(config, options) {
|
|
32
|
+
const apiAccessToken = config.getString("stackoverflow.apiAccessToken");
|
|
33
|
+
const teamName = config.getOptionalString("stackoverflow.teamName");
|
|
34
|
+
const baseUrl = config.getOptionalString("stackoverflow.baseUrl");
|
|
35
|
+
const requestParams = config.getOptionalConfig("stackoverflow.requestParams")?.get();
|
|
36
|
+
return new StackOverflowQuestionsCollatorFactory({
|
|
37
|
+
baseUrl,
|
|
38
|
+
apiAccessToken,
|
|
39
|
+
teamName,
|
|
40
|
+
requestParams,
|
|
41
|
+
...options
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async getCollator() {
|
|
45
|
+
return stream.Readable.from(this.execute());
|
|
46
|
+
}
|
|
47
|
+
// Error logging and debugging
|
|
48
|
+
async *execute() {
|
|
49
|
+
this.logger.info(`Retrieving data using Stack Overflow Internal API Version 3`);
|
|
50
|
+
if (!this.baseUrl && this.teamName) {
|
|
51
|
+
this.logger.info(
|
|
52
|
+
`Connecting to the Teams API at https://api.stackoverflowteams.com`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
if (!this.baseUrl && !this.teamName) {
|
|
56
|
+
this.logger.error(
|
|
57
|
+
`No stackoverflow.teamName has been provided while trying to connect to the Teams API.`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const params = qs__default.default.stringify(this.requestParams, {
|
|
61
|
+
arrayFormat: "comma",
|
|
62
|
+
addQueryPrefix: true
|
|
63
|
+
});
|
|
64
|
+
let requestUrl;
|
|
65
|
+
if (this.teamName) {
|
|
66
|
+
requestUrl = `${this.stackOverflowTeamsAPI}/v3/teams/${this.teamName}/questions${params}`;
|
|
67
|
+
} else {
|
|
68
|
+
requestUrl = `${this.baseUrl}/api/v3/questions${params}`;
|
|
69
|
+
}
|
|
70
|
+
let page = 1;
|
|
71
|
+
let totalPages = 1;
|
|
72
|
+
const pageSize = this.requestParams.pageSize || 50;
|
|
73
|
+
this.logger.warn(
|
|
74
|
+
"Starting collating Stack Internal questions, wait for the success message"
|
|
75
|
+
);
|
|
76
|
+
while (page <= totalPages) {
|
|
77
|
+
const res = await fetch(
|
|
78
|
+
`${requestUrl}&page=${page}&pageSize=${pageSize}`,
|
|
79
|
+
{
|
|
80
|
+
headers: {
|
|
81
|
+
Authorization: `Bearer ${this.apiAccessToken}`
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
const data = await res.json();
|
|
86
|
+
totalPages = data.totalPages;
|
|
87
|
+
for (const question of data.items ?? []) {
|
|
88
|
+
const tags = question.tags?.map((tag) => ({
|
|
89
|
+
id: tag.id,
|
|
90
|
+
description: tag.description,
|
|
91
|
+
location: tag.webUrl,
|
|
92
|
+
name: tag.name
|
|
93
|
+
})) || [];
|
|
94
|
+
yield {
|
|
95
|
+
title: question.title,
|
|
96
|
+
location: `${question.webUrl}?r=${this.referrer}`,
|
|
97
|
+
text: question.owner?.name || "Deleted user",
|
|
98
|
+
userReputation: question.owner?.reputation,
|
|
99
|
+
avatar: question.owner?.avatarUrl,
|
|
100
|
+
userProfile: question.owner?.webUrl,
|
|
101
|
+
userRole: question.owner?.role,
|
|
102
|
+
tags,
|
|
103
|
+
answers: question.answerCount,
|
|
104
|
+
score: question.score,
|
|
105
|
+
viewCount: question.viewCount,
|
|
106
|
+
isAnswered: question.isAnswered,
|
|
107
|
+
bounty: question.bounty,
|
|
108
|
+
creationDate: question.creationDate,
|
|
109
|
+
lastActivityDate: question.lastActivityDate
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
page++;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
exports.StackOverflowQuestionsCollatorFactory = StackOverflowQuestionsCollatorFactory;
|
|
118
|
+
//# sourceMappingURL=StackOverflowQuestionsCollatorFactory.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StackOverflowQuestionsCollatorFactory.cjs.js","sources":["../../src/collators/StackOverflowQuestionsCollatorFactory.ts"],"sourcesContent":["/*\n * This component is a modified version of https://github.com/backstage/backstage/blob/master/plugins/search-backend-module-stack-overflow-collator/src/collators/StackOverflowQuestionsCollatorFactory.ts \n *\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n DocumentCollatorFactory,\n IndexableDocument,\n} from '@backstage/plugin-search-common';\nimport { Config } from '@backstage/config';\nimport { Readable } from 'stream';\n\nimport qs from 'qs';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { Tag } from '../types';\n\n/**\n * Extended IndexableDocument with stack overflow specific properties\n *\n * @public\n */\nexport interface StackOverflowDocument extends IndexableDocument {\n answers: number;\n userReputation: number;\n avatar: string;\n userProfile: string;\n userRole: string;\n tags: object[];\n score: number;\n viewCount: number;\n isAnswered: boolean;\n bounty: {} | null;\n creationDate: string;\n lastActivityDate: string;\n}\n\n/**\n * Type representing the request parameters accepted by the {@link StackOverflowQuestionsCollatorFactory}\n *\n * @public\n */\nexport type StackOverflowQuestionsRequestParams = {\n [key: string]: string | string[] | number;\n};\n\n/**\n * Options for {@link StackOverflowQuestionsCollatorFactory}\n *\n * @public\n */\nexport type StackOverflowQuestionsCollatorFactoryOptions = {\n apiAccessToken?: string;\n teamName?: string;\n requestParams?: StackOverflowQuestionsRequestParams;\n logger: LoggerService;\n};\n\n/**\n * Search collator responsible for collecting stack overflow questions to index.\n *\n * @public\n */\nexport class StackOverflowQuestionsCollatorFactory\n implements DocumentCollatorFactory\n{\n protected requestParams: StackOverflowQuestionsRequestParams;\n private readonly baseUrl: string;\n private readonly apiAccessToken: string | undefined;\n private readonly teamName: string | undefined;\n private readonly logger: LoggerService;\n private readonly referrer: string = 'Backstage_Plugin';\n public readonly type: string = 'stack-overflow';\n public readonly stackOverflowTeamsAPI: string =\n 'https://api.stackoverflowteams.com';\n\n private forceOriginUrl = (baseUrl: string): string =>\n `${new URL(baseUrl).origin}`;\n\n private constructor(options: StackOverflowQuestionsCollatorFactoryOptions & { baseUrl?: string }) {\n this.baseUrl = this.forceOriginUrl(options.baseUrl || this.stackOverflowTeamsAPI);\n this.apiAccessToken = options.apiAccessToken;\n this.teamName = options.teamName;\n this.logger = options.logger.child({ documentType: this.type });\n\n // Sets the same default request parameters as the official API documentation\n // See https://api.stackexchange.com/docs/questions\n this.requestParams = {\n order: 'desc',\n sort: 'creation',\n ...(options.requestParams ?? {}),\n };\n }\n\n static fromConfig(\n config: Config,\n options: StackOverflowQuestionsCollatorFactoryOptions,\n ) {\n const apiAccessToken = config.getString('stackoverflow.apiAccessToken');\n const teamName = config.getOptionalString('stackoverflow.teamName');\n const baseUrl = config.getOptionalString('stackoverflow.baseUrl');\n const requestParams = config\n .getOptionalConfig('stackoverflow.requestParams')\n ?.get<StackOverflowQuestionsRequestParams>();\n\n return new StackOverflowQuestionsCollatorFactory({\n baseUrl,\n apiAccessToken,\n teamName,\n requestParams,\n ...options,\n });\n }\n\n async getCollator() {\n return Readable.from(this.execute());\n }\n\n // Error logging and debugging\n\n async *execute(): AsyncGenerator<StackOverflowDocument> {\n this.logger.info(`Retrieving data using Stack Overflow Internal API Version 3`);\n\n if (!this.baseUrl && this.teamName) {\n this.logger.info(\n `Connecting to the Teams API at https://api.stackoverflowteams.com`,\n );\n }\n\n if (!this.baseUrl && !this.teamName) {\n this.logger.error(\n `No stackoverflow.teamName has been provided while trying to connect to the Teams API.`\n )\n }\n\n const params = qs.stringify(this.requestParams, {\n arrayFormat: 'comma',\n addQueryPrefix: true,\n });\n\n let requestUrl;\n\n if (this.teamName) {\n requestUrl = `${this.stackOverflowTeamsAPI}/v3/teams/${this.teamName}/questions${params}`;\n } else {\n requestUrl = `${this.baseUrl}/api/v3/questions${params}`;\n }\n\n // The code below has been commented, it has potential compatiblity with Enterprise Private Teams but I haven't tested it and since Private Teams is not widely used I've decided to change the logic to prioritise the support for the Basic and Business Teams.\n\n // if (this.teamName) {\n // const basePath =\n // this.baseUrl === this.stackOverflowTeamsAPI ? '/v3' : '/api/v3';\n // requestUrl = `${this.baseUrl}${basePath}/teams/${this.teamName}/questions${params}`;\n // } else {\n // requestUrl = `${this.baseUrl}/api/v3/questions${params}`;\n // }\n\n let page = 1;\n let totalPages = 1;\n const pageSize = this.requestParams.pageSize || 50;\n this.logger.warn(\n 'Starting collating Stack Internal questions, wait for the success message',\n );\n while (page <= totalPages) {\n const res = await fetch(\n `${requestUrl}&page=${page}&pageSize=${pageSize}`,\n {\n headers: {\n Authorization: `Bearer ${this.apiAccessToken}`,\n },\n },\n );\n\n const data = await res.json();\n totalPages = data.totalPages;\n\n for (const question of data.items ?? []) {\n const tags =\n question.tags?.map((tag: Tag) => ({\n id: tag.id,\n description: tag.description,\n location: tag.webUrl,\n name: tag.name,\n })) || [];\n\n yield {\n title: question.title,\n location: `${question.webUrl}?r=${this.referrer}`,\n text: question.owner?.name || 'Deleted user',\n userReputation: question.owner?.reputation,\n avatar: question.owner?.avatarUrl,\n userProfile: question.owner?.webUrl,\n userRole: question.owner?.role,\n tags: tags,\n answers: question.answerCount,\n score: question.score,\n viewCount: question.viewCount,\n isAnswered: question.isAnswered,\n bounty: question.bounty,\n creationDate: question.creationDate,\n lastActivityDate: question.lastActivityDate,\n };\n }\n page++;\n }\n }\n}\n"],"names":["Readable","qs"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var StackOverflowQuestionsCollatorFactory = require('./collators/StackOverflowQuestionsCollatorFactory.cjs.js');
|
|
6
|
+
var SearchStackOverflowCollatorModule = require('./module/SearchStackOverflowCollatorModule.cjs.js');
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
exports.StackOverflowQuestionsCollatorFactory = StackOverflowQuestionsCollatorFactory.StackOverflowQuestionsCollatorFactory;
|
|
11
|
+
exports.default = SearchStackOverflowCollatorModule.searchStackOverflowCollatorModule;
|
|
12
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { IndexableDocument, DocumentCollatorFactory } from '@backstage/plugin-search-common';
|
|
2
|
+
import { Config } from '@backstage/config';
|
|
3
|
+
import { Readable } from 'stream';
|
|
4
|
+
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
5
|
+
import { LoggerService } from '@backstage/backend-plugin-api';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Extended IndexableDocument with stack overflow specific properties
|
|
9
|
+
*
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
interface StackOverflowDocument extends IndexableDocument {
|
|
13
|
+
answers: number;
|
|
14
|
+
userReputation: number;
|
|
15
|
+
avatar: string;
|
|
16
|
+
userProfile: string;
|
|
17
|
+
userRole: string;
|
|
18
|
+
tags: object[];
|
|
19
|
+
score: number;
|
|
20
|
+
viewCount: number;
|
|
21
|
+
isAnswered: boolean;
|
|
22
|
+
bounty: {} | null;
|
|
23
|
+
creationDate: string;
|
|
24
|
+
lastActivityDate: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Type representing the request parameters accepted by the {@link StackOverflowQuestionsCollatorFactory}
|
|
28
|
+
*
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
type StackOverflowQuestionsRequestParams = {
|
|
32
|
+
[key: string]: string | string[] | number;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Options for {@link StackOverflowQuestionsCollatorFactory}
|
|
36
|
+
*
|
|
37
|
+
* @public
|
|
38
|
+
*/
|
|
39
|
+
type StackOverflowQuestionsCollatorFactoryOptions = {
|
|
40
|
+
apiAccessToken?: string;
|
|
41
|
+
teamName?: string;
|
|
42
|
+
requestParams?: StackOverflowQuestionsRequestParams;
|
|
43
|
+
logger: LoggerService;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Search collator responsible for collecting stack overflow questions to index.
|
|
47
|
+
*
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
50
|
+
declare class StackOverflowQuestionsCollatorFactory implements DocumentCollatorFactory {
|
|
51
|
+
protected requestParams: StackOverflowQuestionsRequestParams;
|
|
52
|
+
private readonly baseUrl;
|
|
53
|
+
private readonly apiAccessToken;
|
|
54
|
+
private readonly teamName;
|
|
55
|
+
private readonly logger;
|
|
56
|
+
private readonly referrer;
|
|
57
|
+
readonly type: string;
|
|
58
|
+
readonly stackOverflowTeamsAPI: string;
|
|
59
|
+
private forceOriginUrl;
|
|
60
|
+
private constructor();
|
|
61
|
+
static fromConfig(config: Config, options: StackOverflowQuestionsCollatorFactoryOptions): StackOverflowQuestionsCollatorFactory;
|
|
62
|
+
getCollator(): Promise<Readable>;
|
|
63
|
+
execute(): AsyncGenerator<StackOverflowDocument>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @public
|
|
68
|
+
* Search backend module for the Stack Overflow Internal index.
|
|
69
|
+
*/
|
|
70
|
+
declare const searchStackOverflowCollatorModule: _backstage_backend_plugin_api.BackendFeature;
|
|
71
|
+
|
|
72
|
+
export { StackOverflowQuestionsCollatorFactory, searchStackOverflowCollatorModule as default };
|
|
73
|
+
export type { StackOverflowDocument, StackOverflowQuestionsCollatorFactoryOptions, StackOverflowQuestionsRequestParams };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
|
+
var alpha = require('@backstage/plugin-search-backend-node/alpha');
|
|
5
|
+
var StackOverflowQuestionsCollatorFactory = require('../collators/StackOverflowQuestionsCollatorFactory.cjs.js');
|
|
6
|
+
|
|
7
|
+
const searchStackOverflowCollatorModule = backendPluginApi.createBackendModule({
|
|
8
|
+
pluginId: "search",
|
|
9
|
+
moduleId: "stack-overflow-collator",
|
|
10
|
+
register(env) {
|
|
11
|
+
env.registerInit({
|
|
12
|
+
deps: {
|
|
13
|
+
config: backendPluginApi.coreServices.rootConfig,
|
|
14
|
+
logger: backendPluginApi.coreServices.logger,
|
|
15
|
+
discovery: backendPluginApi.coreServices.discovery,
|
|
16
|
+
scheduler: backendPluginApi.coreServices.scheduler,
|
|
17
|
+
indexRegistry: alpha.searchIndexRegistryExtensionPoint
|
|
18
|
+
},
|
|
19
|
+
async init({ config, logger, scheduler, indexRegistry }) {
|
|
20
|
+
const defaultSchedule = {
|
|
21
|
+
frequency: { minutes: 10 },
|
|
22
|
+
timeout: { minutes: 15 },
|
|
23
|
+
initialDelay: { seconds: 3 }
|
|
24
|
+
};
|
|
25
|
+
const schedule = config.has("stackoverflow.schedule") ? backendPluginApi.readSchedulerServiceTaskScheduleDefinitionFromConfig(
|
|
26
|
+
config.getConfig("stackoverflow.schedule")
|
|
27
|
+
) : defaultSchedule;
|
|
28
|
+
indexRegistry.addCollator({
|
|
29
|
+
schedule: scheduler.createScheduledTaskRunner(schedule),
|
|
30
|
+
factory: StackOverflowQuestionsCollatorFactory.StackOverflowQuestionsCollatorFactory.fromConfig(config, {
|
|
31
|
+
logger
|
|
32
|
+
})
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
exports.searchStackOverflowCollatorModule = searchStackOverflowCollatorModule;
|
|
40
|
+
//# sourceMappingURL=SearchStackOverflowCollatorModule.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchStackOverflowCollatorModule.cjs.js","sources":["../../src/module/SearchStackOverflowCollatorModule.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { readSchedulerServiceTaskScheduleDefinitionFromConfig } from '@backstage/backend-plugin-api';\nimport {\n coreServices,\n createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha';\nimport { StackOverflowQuestionsCollatorFactory } from '../collators';\n\n/**\n * @public\n * Search backend module for the Stack Overflow Internal index.\n */\nexport const searchStackOverflowCollatorModule = createBackendModule({\n pluginId: 'search',\n moduleId: 'stack-overflow-collator',\n register(env) {\n env.registerInit({\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n discovery: coreServices.discovery,\n scheduler: coreServices.scheduler,\n indexRegistry: searchIndexRegistryExtensionPoint,\n },\n async init({ config, logger, scheduler, indexRegistry }) {\n const defaultSchedule = {\n frequency: { minutes: 10 },\n timeout: { minutes: 15 },\n initialDelay: { seconds: 3 },\n };\n\n const schedule = config.has('stackoverflow.schedule')\n ? readSchedulerServiceTaskScheduleDefinitionFromConfig(\n config.getConfig('stackoverflow.schedule'),\n )\n : defaultSchedule;\n\n indexRegistry.addCollator({\n schedule: scheduler.createScheduledTaskRunner(schedule),\n factory: StackOverflowQuestionsCollatorFactory.fromConfig(config, {\n logger,\n }),\n });\n },\n });\n },\n});\n"],"names":["createBackendModule","coreServices","searchIndexRegistryExtensionPoint","readSchedulerServiceTaskScheduleDefinitionFromConfig","StackOverflowQuestionsCollatorFactory"],"mappings":";;;;;;AA4BO,MAAM,oCAAoCA,oCAAA,CAAoB;AAAA,EACnE,QAAA,EAAU,QAAA;AAAA,EACV,QAAA,EAAU,yBAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,QAAQC,6BAAA,CAAa,UAAA;AAAA,QACrB,QAAQA,6BAAA,CAAa,MAAA;AAAA,QACrB,WAAWA,6BAAA,CAAa,SAAA;AAAA,QACxB,WAAWA,6BAAA,CAAa,SAAA;AAAA,QACxB,aAAA,EAAeC;AAAA,OACjB;AAAA,MACA,MAAM,IAAA,CAAK,EAAE,QAAQ,MAAA,EAAQ,SAAA,EAAW,eAAc,EAAG;AACvD,QAAA,MAAM,eAAA,GAAkB;AAAA,UACtB,SAAA,EAAW,EAAE,OAAA,EAAS,EAAA,EAAG;AAAA,UACzB,OAAA,EAAS,EAAE,OAAA,EAAS,EAAA,EAAG;AAAA,UACvB,YAAA,EAAc,EAAE,OAAA,EAAS,CAAA;AAAE,SAC7B;AAEA,QAAA,MAAM,QAAA,GAAW,MAAA,CAAO,GAAA,CAAI,wBAAwB,CAAA,GAChDC,qEAAA;AAAA,UACE,MAAA,CAAO,UAAU,wBAAwB;AAAA,SAC3C,GACA,eAAA;AAEJ,QAAA,aAAA,CAAc,WAAA,CAAY;AAAA,UACxB,QAAA,EAAU,SAAA,CAAU,yBAAA,CAA0B,QAAQ,CAAA;AAAA,UACtD,OAAA,EAASC,2EAAA,CAAsC,UAAA,CAAW,MAAA,EAAQ;AAAA,YAChE;AAAA,WACD;AAAA,SACF,CAAA;AAAA,MACH;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
|
package/package.json
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackoverflow/backstage-stack-overflow-teams-collator",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.3",
|
|
4
4
|
"description": "A module for the search backend that exports Stack Internal modules",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "backend-plugin-module",
|
|
7
7
|
"pluginId": "search",
|
|
8
|
-
"pluginPackage": "@backstage/plugin-search-backend"
|
|
8
|
+
"pluginPackage": "@backstage/plugin-search-backend",
|
|
9
|
+
"features": {
|
|
10
|
+
".": "@backstage/BackendFeature"
|
|
11
|
+
}
|
|
9
12
|
},
|
|
10
|
-
"main": "dist/index.
|
|
11
|
-
"types": "dist/index.d.ts",
|
|
13
|
+
"main": "./dist/index.cjs.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
12
15
|
"publishConfig": {
|
|
13
|
-
"access": "public"
|
|
14
|
-
"main": "dist/index.esm.js",
|
|
15
|
-
"types": "dist/index.d.ts"
|
|
16
|
+
"access": "public"
|
|
16
17
|
},
|
|
17
18
|
"repository": {
|
|
18
19
|
"type": "git",
|
|
@@ -47,5 +48,12 @@
|
|
|
47
48
|
"@backstage/cli": "^0.34.4",
|
|
48
49
|
"msw": "^1.2.1",
|
|
49
50
|
"typescript": "^5.8.2"
|
|
51
|
+
},
|
|
52
|
+
"typesVersions": {
|
|
53
|
+
"*": {
|
|
54
|
+
"package.json": [
|
|
55
|
+
"package.json"
|
|
56
|
+
]
|
|
57
|
+
}
|
|
50
58
|
}
|
|
51
59
|
}
|