@finos/legend-server-showcase-deployment 9.70.0 → 10.1.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.
@@ -13,7 +13,7 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { type ShowcaseMetadata } from '@finos/legend-server-showcase';
16
+ import { Showcase, type ShowcaseMetadata, type ShowcaseTextSearchResult } from '@finos/legend-server-showcase';
17
17
  import { type PlainObject } from '@finos/legend-shared';
18
18
  export type ShowcaseRegistryConfig = {
19
19
  datasources: {
@@ -25,9 +25,11 @@ export declare class ShowcaseRegistry {
25
25
  private readonly RAW__metadata;
26
26
  private readonly RAW__showcaseIndex;
27
27
  private readonly showcasesIndex;
28
+ private readonly showcaseSearchEngine;
28
29
  private constructor();
29
30
  static initialize(config: ShowcaseRegistryConfig): Promise<ShowcaseRegistry>;
30
31
  getShowcases(): PlainObject<ShowcaseMetadata>[];
31
- getShowcase(path: string): PlainObject<ShowcaseMetadata> | undefined;
32
+ getShowcase(path: string): PlainObject<Showcase> | undefined;
33
+ search(searchText: string): Promise<ShowcaseTextSearchResult>;
32
34
  }
33
35
  //# sourceMappingURL=ShowcaseRegistry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ShowcaseRegistry.d.ts","sourceRoot":"","sources":["../src/ShowcaseRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,EAAY,KAAK,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAgCxD,MAAM,MAAM,sBAAsB,GAAG;IACnC,WAAW,EAAE;QACX,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;CACL,CAAC;AAEF,qBAAa,gBAAgB;IAE3B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAuC;IACrE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAG/B;IAEJ,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA+B;IAE9D,OAAO;WAIM,UAAU,CACrB,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CAAC,gBAAgB,CAAC;IAyB5B,YAAY,IAAI,WAAW,CAAC,gBAAgB,CAAC,EAAE;IAI/C,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC,gBAAgB,CAAC,GAAG,SAAS;CAGrE"}
1
+ {"version":3,"file":"ShowcaseRegistry.d.ts","sourceRoot":"","sources":["../src/ShowcaseRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,EACL,QAAQ,EACR,KAAK,gBAAgB,EAErB,KAAK,wBAAwB,EAC9B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAIL,KAAK,WAAW,EAEjB,MAAM,sBAAsB,CAAC;AAgC9B,MAAM,MAAM,sBAAsB,GAAG;IACnC,WAAW,EAAE;QACX,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;CACL,CAAC;AAEF,qBAAa,gBAAgB;IAE3B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAuC;IACrE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAG/B;IAEJ,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA+B;IAC9D,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAA8B;IAGnE,OAAO;WA6CM,UAAU,CACrB,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CAAC,gBAAgB,CAAC;IA6B5B,YAAY,IAAI,WAAW,CAAC,gBAAgB,CAAC,EAAE;IAI/C,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,GAAG,SAAS;IAItD,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC;CAqEpE"}
@@ -15,8 +15,8 @@
15
15
  */
16
16
  import { get } from 'https';
17
17
  import { readFileSync } from 'fs';
18
- import { Showcase } from '@finos/legend-server-showcase';
19
- import {} from '@finos/legend-shared';
18
+ import { Showcase, } from '@finos/legend-server-showcase';
19
+ import { ESM__FuzzySearchEngine, FuzzySearchEngine, promisify, getNonNullableEntry, } from '@finos/legend-shared';
20
20
  async function fetchExternalLinkSiteData(url) {
21
21
  return new Promise((resolve, reject) => {
22
22
  get(url, (response) => {
@@ -36,7 +36,7 @@ async function fetchExternalLinkSiteData(url) {
36
36
  const fetchShowcasesData = async (datasource) => {
37
37
  let content;
38
38
  if (datasource.url) {
39
- content = await fetchExternalLinkSiteData(datasource.url);
39
+ content = JSON.parse(await fetchExternalLinkSiteData(datasource.url));
40
40
  }
41
41
  else if (datasource.path) {
42
42
  content = JSON.parse(readFileSync(datasource.path, { encoding: 'utf-8' }));
@@ -48,8 +48,51 @@ export class ShowcaseRegistry {
48
48
  RAW__metadata = [];
49
49
  RAW__showcaseIndex = new Map();
50
50
  showcasesIndex = new Map();
51
+ showcaseSearchEngine;
52
+ // private constructor to enforce singleton
51
53
  constructor() {
52
- // private constructor to enforce singleton
54
+ // NOTE: due to the way we export the constructor of `FuzzySearchEngine`, when we run this with ESM
55
+ // we can remove this workaround once Fuse supports ESM
56
+ // See https://github.com/krisk/Fuse/pull/727
57
+ const FuzzySearchEngineConstructor =
58
+ // eslint-disable-next-line no-process-env
59
+ process.env.NODE_ENV === 'development'
60
+ ? ESM__FuzzySearchEngine
61
+ : FuzzySearchEngine;
62
+ this.showcaseSearchEngine = new FuzzySearchEngineConstructor([], {
63
+ includeScore: true,
64
+ // NOTE: we must not sort/change the order in the grid since
65
+ // we want to ensure the element row is on top
66
+ shouldSort: false,
67
+ // Ignore location when computing the search score
68
+ // See https://fusejs.io/concepts/scoring-theory.html
69
+ ignoreLocation: true,
70
+ // This specifies the point the search gives up
71
+ // `0.0` means exact match where `1.0` would match anything
72
+ // We set a relatively low threshold to filter out irrelevant results
73
+ threshold: 0.2,
74
+ keys: [
75
+ {
76
+ name: 'title',
77
+ weight: 5,
78
+ },
79
+ {
80
+ name: 'description',
81
+ weight: 3,
82
+ },
83
+ {
84
+ name: 'path',
85
+ weight: 2,
86
+ },
87
+ {
88
+ name: 'documentation',
89
+ weight: 1,
90
+ },
91
+ ],
92
+ // extended search allows for exact word match through single quote
93
+ // See https://fusejs.io/examples.html#extended-search
94
+ useExtendedSearch: true,
95
+ });
53
96
  }
54
97
  static async initialize(config) {
55
98
  const registry = new ShowcaseRegistry();
@@ -69,6 +112,7 @@ export class ShowcaseRegistry {
69
112
  }
70
113
  });
71
114
  }));
115
+ registry.showcaseSearchEngine.setCollection(Array.from(registry.showcasesIndex.values()));
72
116
  return registry;
73
117
  }
74
118
  getShowcases() {
@@ -77,5 +121,50 @@ export class ShowcaseRegistry {
77
121
  getShowcase(path) {
78
122
  return this.RAW__showcaseIndex.get(path);
79
123
  }
124
+ async search(searchText) {
125
+ const matches = [];
126
+ // NOTE: for text search, we only support case-insensitive search now
127
+ const lowerCaseSearchText = searchText.toLowerCase();
128
+ await Promise.all(Array.from(this.showcasesIndex.values()).map((showcase) => promisify(() => {
129
+ const result = {
130
+ path: showcase.path,
131
+ matches: [],
132
+ preview: [],
133
+ };
134
+ const previewLines = new Map();
135
+ const code = showcase.code;
136
+ const lines = code.split('\n');
137
+ lines.forEach((line, lineIdx) => {
138
+ const lowerCaseLine = line.toLowerCase();
139
+ let fromIdx = 0;
140
+ let currentMatchIdx = lowerCaseLine.indexOf(lowerCaseSearchText, fromIdx);
141
+ while (currentMatchIdx !== -1) {
142
+ const previewTextStartLineIdx = Math.max(lineIdx - 1, 0);
143
+ previewLines.set(previewTextStartLineIdx + 1, getNonNullableEntry(lines, previewTextStartLineIdx));
144
+ previewLines.set(lineIdx + 1, getNonNullableEntry(lines, lineIdx));
145
+ const previewTextEndLineIdx = Math.min(lineIdx + 1, lines.length - 1);
146
+ previewLines.set(previewTextEndLineIdx + 1, getNonNullableEntry(lines, previewTextEndLineIdx));
147
+ result.matches.push({
148
+ line: lineIdx + 1,
149
+ startColumn: currentMatchIdx + 1,
150
+ endColumn: currentMatchIdx + 1 + lowerCaseSearchText.length,
151
+ });
152
+ fromIdx = currentMatchIdx + lowerCaseSearchText.length;
153
+ currentMatchIdx = lowerCaseLine.indexOf(lowerCaseSearchText, fromIdx);
154
+ }
155
+ });
156
+ if (!result.matches.length) {
157
+ return;
158
+ }
159
+ result.preview = Array.from(previewLines.entries())
160
+ .map(([line, text]) => ({ line, text }))
161
+ .sort((a, b) => a.line - b.line);
162
+ matches.push(result);
163
+ })));
164
+ return {
165
+ showcases: Array.from(this.showcaseSearchEngine.search(searchText).values()).map((result) => result.item.path),
166
+ textMatches: matches,
167
+ };
168
+ }
80
169
  }
81
170
  //# sourceMappingURL=ShowcaseRegistry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ShowcaseRegistry.js","sourceRoot":"","sources":["../src/ShowcaseRegistry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ShowcaseRegistry.js","sourceRoot":"","sources":["../src/ShowcaseRegistry.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ShowcaseRegistryServer.d.ts","sourceRoot":"","sources":["../src/ShowcaseRegistryServer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,KAAK,eAAe,EAAgC,MAAM,SAAS,CAAC;AAC7E,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,uBAAuB,CAAC;AAQ/B,eAAO,MAAM,+BAA+B,WAClC,eAAe,UACf,sBAAsB,GAAG;IAC/B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC,KACA,QAAQ,IAAI,CAyBd,CAAC"}
1
+ {"version":3,"file":"ShowcaseRegistryServer.d.ts","sourceRoot":"","sources":["../src/ShowcaseRegistryServer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,KAAK,eAAe,EAAgC,MAAM,SAAS,CAAC;AAC7E,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,uBAAuB,CAAC;AAc/B,eAAO,MAAM,+BAA+B,WAClC,eAAe,UACf,sBAAsB,GAAG;IAC/B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC,KACA,QAAQ,IAAI,CA8Bd,CAAC"}
@@ -30,8 +30,9 @@ export const configureShowcaseRegistryServer = async (server, config) => {
30
30
  }
31
31
  await reply.send(registry.getShowcase(path));
32
32
  });
33
- server.get(`${baseUrl}/showcase/search`, async (request, reply) => {
34
- // do nothing
33
+ server.get(`${baseUrl}/showcases/search`, async (request, reply) => {
34
+ const { searchText } = request.query;
35
+ await reply.send(await registry.search(searchText));
35
36
  });
36
37
  };
37
38
  //# sourceMappingURL=ShowcaseRegistryServer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ShowcaseRegistryServer.js","sourceRoot":"","sources":["../src/ShowcaseRegistryServer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAsD,MAAM,SAAS,CAAC;AAC7E,OAAO,EACL,gBAAgB,GAEjB,MAAM,uBAAuB,CAAC;AAQ/B,MAAM,CAAC,MAAM,+BAA+B,GAAG,KAAK,EAClD,MAAuB,EACvB,MAEC,EACc,EAAE;IACjB,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC;IAE5C,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAC1D,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,GAAG,CACR,GAAG,OAAO,iBAAiB,EAC3B,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QAChC,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ,EAAE;YACb,KAAK,CAAC,YAAY,EAAE,CAAC;YACrB,OAAO;SACR;QAED,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/C,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,kBAAkB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAChE,aAAa;IACf,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
1
+ {"version":3,"file":"ShowcaseRegistryServer.js","sourceRoot":"","sources":["../src/ShowcaseRegistryServer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAsD,MAAM,SAAS,CAAC;AAC7E,OAAO,EACL,gBAAgB,GAEjB,MAAM,uBAAuB,CAAC;AAc/B,MAAM,CAAC,MAAM,+BAA+B,GAAG,KAAK,EAClD,MAAuB,EACvB,MAEC,EACc,EAAE;IACjB,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC;IAE5C,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAC1D,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,GAAG,CACR,GAAG,OAAO,iBAAiB,EAC3B,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QAChC,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ,EAAE;YACb,KAAK,CAAC,YAAY,EAAE,CAAC;YACrB,OAAO;SACR;QAED,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/C,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,GAAG,CACR,GAAG,OAAO,mBAAmB,EAC7B,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC;QAErC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IACtD,CAAC,CACF,CAAC;AACJ,CAAC,CAAC"}
package/lib/server.js CHANGED
@@ -17,6 +17,7 @@ import { existsSync, readFileSync } from 'fs';
17
17
  import { fastify } from 'fastify';
18
18
  import { fastifyCors } from '@fastify/cors';
19
19
  import { configureShowcaseRegistryServer } from './ShowcaseRegistryServer.js';
20
+ const API_BASE_URL = '/api';
20
21
  const configFilePath = process.argv[2] ?? './config.json';
21
22
  if (!existsSync(configFilePath)) {
22
23
  throw new Error(`Can't find config file at path '${configFilePath}'`);
@@ -28,7 +29,7 @@ const server = fastify({
28
29
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
29
30
  server.register(fastifyCors, {});
30
31
  await configureShowcaseRegistryServer(server, {
31
- apiBaseUrl: '/api',
32
+ apiBaseUrl: API_BASE_URL,
32
33
  datasources: config.datasources.map((datasource) => {
33
34
  if (datasource.path) {
34
35
  return {
package/lib/server.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AAG9E,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC;AAC1D,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE;IAC/B,MAAM,IAAI,KAAK,CAAC,mCAAmC,cAAc,GAAG,CAAC,CAAC;CACvE;AACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CACvB,YAAY,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CACP,CAAC;AAE/C,MAAM,MAAM,GAAG,OAAO,CAAC;IACrB,MAAM,EAAE,IAAI;CACb,CAAC,CAAC;AAEH,mEAAmE;AACnE,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AAEjC,MAAM,+BAA+B,CAAC,MAAM,EAAE;IAC5C,UAAU,EAAE,MAAM;IAClB,WAAW,EAAG,MAAiC,CAAC,WAAW,CAAC,GAAG,CAC7D,CAAC,UAAU,EAAE,EAAE;QACb,IAAI,UAAU,CAAC,IAAI,EAAE;YACnB,OAAO;gBACL,IAAI,EAAE,UAAU,CAAC,IAAI;aACtB,CAAC;SACH;QACD,OAAO,UAAU,CAAC;IACpB,CAAC,CACF;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,CACX;IACE,IAAI,EAAE,MAAM,CAAC,IAAI;IACjB,kEAAkE;IAClE,IAAI,EAAE,SAAS;CAChB,EACD,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACjB,IAAI,KAAK,EAAE;QACT,MAAM,KAAK,CAAC;KACb;AACH,CAAC,CACF,CAAC"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AAG9E,MAAM,YAAY,GAAG,MAAM,CAAC;AAE5B,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC;AAC1D,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE;IAC/B,MAAM,IAAI,KAAK,CAAC,mCAAmC,cAAc,GAAG,CAAC,CAAC;CACvE;AACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CACvB,YAAY,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CACP,CAAC;AAE/C,MAAM,MAAM,GAAG,OAAO,CAAC;IACrB,MAAM,EAAE,IAAI;CACb,CAAC,CAAC;AAEH,mEAAmE;AACnE,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AAEjC,MAAM,+BAA+B,CAAC,MAAM,EAAE;IAC5C,UAAU,EAAE,YAAY;IACxB,WAAW,EAAG,MAAiC,CAAC,WAAW,CAAC,GAAG,CAC7D,CAAC,UAAU,EAAE,EAAE;QACb,IAAI,UAAU,CAAC,IAAI,EAAE;YACnB,OAAO;gBACL,IAAI,EAAE,UAAU,CAAC,IAAI;aACtB,CAAC;SACH;QACD,OAAO,UAAU,CAAC;IACpB,CAAC,CACF;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,CACX;IACE,IAAI,EAAE,MAAM,CAAC,IAAI;IACjB,kEAAkE;IAClE,IAAI,EAAE,SAAS;CAChB,EACD,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACjB,IAAI,KAAK,EAAE;QACT,MAAM,KAAK,CAAC;KACb;AACH,CAAC,CACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos/legend-server-showcase-deployment",
3
- "version": "9.70.0",
3
+ "version": "10.1.0",
4
4
  "description": "Legend Showcase server deployment",
5
5
  "keywords": [
6
6
  "legend",
@@ -51,21 +51,21 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "@fastify/cors": "8.3.0",
54
- "@finos/legend-server-showcase": "0.0.3",
55
- "@finos/legend-shared": "10.0.19",
56
- "fastify": "4.19.2"
54
+ "@finos/legend-server-showcase": "0.0.5",
55
+ "@finos/legend-shared": "10.0.21",
56
+ "fastify": "4.20.0"
57
57
  },
58
58
  "devDependencies": {
59
- "@finos/legend-dev-utils": "2.0.73",
59
+ "@finos/legend-dev-utils": "2.0.75",
60
60
  "@jest/globals": "29.6.1",
61
61
  "cross-env": "7.0.3",
62
- "eslint": "8.44.0",
62
+ "eslint": "8.45.0",
63
63
  "jest": "29.6.1",
64
- "nodemon": "2.0.22",
64
+ "nodemon": "3.0.1",
65
65
  "npm-run-all": "4.1.5",
66
66
  "rimraf": "5.0.1",
67
67
  "typescript": "5.1.6",
68
- "webpack": "5.88.1",
68
+ "webpack": "5.88.2",
69
69
  "webpack-bundle-analyzer": "4.9.0",
70
70
  "webpack-cli": "5.1.4"
71
71
  },
@@ -16,8 +16,19 @@
16
16
 
17
17
  import { get } from 'https';
18
18
  import { readFileSync } from 'fs';
19
- import { Showcase, type ShowcaseMetadata } from '@finos/legend-server-showcase';
20
- import { type PlainObject } from '@finos/legend-shared';
19
+ import {
20
+ Showcase,
21
+ type ShowcaseMetadata,
22
+ type ShowcaseTextSearchMatch,
23
+ type ShowcaseTextSearchResult,
24
+ } from '@finos/legend-server-showcase';
25
+ import {
26
+ ESM__FuzzySearchEngine,
27
+ FuzzySearchEngine,
28
+ promisify,
29
+ type PlainObject,
30
+ getNonNullableEntry,
31
+ } from '@finos/legend-shared';
21
32
 
22
33
  async function fetchExternalLinkSiteData(url: string): Promise<string> {
23
34
  return new Promise((resolve, reject) => {
@@ -42,7 +53,7 @@ const fetchShowcasesData = async (datasource: {
42
53
  }): Promise<PlainObject<Showcase>[]> => {
43
54
  let content: unknown;
44
55
  if (datasource.url) {
45
- content = await fetchExternalLinkSiteData(datasource.url);
56
+ content = JSON.parse(await fetchExternalLinkSiteData(datasource.url));
46
57
  } else if (datasource.path) {
47
58
  content = JSON.parse(readFileSync(datasource.path, { encoding: 'utf-8' }));
48
59
  }
@@ -65,9 +76,52 @@ export class ShowcaseRegistry {
65
76
  >();
66
77
 
67
78
  private readonly showcasesIndex = new Map<string, Showcase>();
79
+ private readonly showcaseSearchEngine: FuzzySearchEngine<Showcase>;
68
80
 
81
+ // private constructor to enforce singleton
69
82
  private constructor() {
70
- // private constructor to enforce singleton
83
+ // NOTE: due to the way we export the constructor of `FuzzySearchEngine`, when we run this with ESM
84
+ // we can remove this workaround once Fuse supports ESM
85
+ // See https://github.com/krisk/Fuse/pull/727
86
+ const FuzzySearchEngineConstructor =
87
+ // eslint-disable-next-line no-process-env
88
+ process.env.NODE_ENV === 'development'
89
+ ? ESM__FuzzySearchEngine
90
+ : FuzzySearchEngine;
91
+ this.showcaseSearchEngine = new FuzzySearchEngineConstructor([], {
92
+ includeScore: true,
93
+ // NOTE: we must not sort/change the order in the grid since
94
+ // we want to ensure the element row is on top
95
+ shouldSort: false,
96
+ // Ignore location when computing the search score
97
+ // See https://fusejs.io/concepts/scoring-theory.html
98
+ ignoreLocation: true,
99
+ // This specifies the point the search gives up
100
+ // `0.0` means exact match where `1.0` would match anything
101
+ // We set a relatively low threshold to filter out irrelevant results
102
+ threshold: 0.2,
103
+ keys: [
104
+ {
105
+ name: 'title',
106
+ weight: 5,
107
+ },
108
+ {
109
+ name: 'description',
110
+ weight: 3,
111
+ },
112
+ {
113
+ name: 'path',
114
+ weight: 2,
115
+ },
116
+ {
117
+ name: 'documentation',
118
+ weight: 1,
119
+ },
120
+ ],
121
+ // extended search allows for exact word match through single quote
122
+ // See https://fusejs.io/examples.html#extended-search
123
+ useExtendedSearch: true,
124
+ });
71
125
  }
72
126
 
73
127
  static async initialize(
@@ -94,6 +148,10 @@ export class ShowcaseRegistry {
94
148
  }),
95
149
  );
96
150
 
151
+ registry.showcaseSearchEngine.setCollection(
152
+ Array.from(registry.showcasesIndex.values()),
153
+ );
154
+
97
155
  return registry;
98
156
  }
99
157
 
@@ -101,7 +159,77 @@ export class ShowcaseRegistry {
101
159
  return this.RAW__metadata;
102
160
  }
103
161
 
104
- getShowcase(path: string): PlainObject<ShowcaseMetadata> | undefined {
162
+ getShowcase(path: string): PlainObject<Showcase> | undefined {
105
163
  return this.RAW__showcaseIndex.get(path);
106
164
  }
165
+
166
+ async search(searchText: string): Promise<ShowcaseTextSearchResult> {
167
+ const matches: ShowcaseTextSearchMatch[] = [];
168
+ // NOTE: for text search, we only support case-insensitive search now
169
+ const lowerCaseSearchText = searchText.toLowerCase();
170
+ await Promise.all(
171
+ Array.from(this.showcasesIndex.values()).map((showcase) =>
172
+ promisify(() => {
173
+ const result: ShowcaseTextSearchMatch = {
174
+ path: showcase.path,
175
+ matches: [],
176
+ preview: [],
177
+ };
178
+ const previewLines = new Map<number, string>();
179
+ const code = showcase.code;
180
+ const lines = code.split('\n');
181
+ lines.forEach((line, lineIdx) => {
182
+ const lowerCaseLine = line.toLowerCase();
183
+ let fromIdx = 0;
184
+ let currentMatchIdx = lowerCaseLine.indexOf(
185
+ lowerCaseSearchText,
186
+ fromIdx,
187
+ );
188
+ while (currentMatchIdx !== -1) {
189
+ const previewTextStartLineIdx = Math.max(lineIdx - 1, 0);
190
+ previewLines.set(
191
+ previewTextStartLineIdx + 1,
192
+ getNonNullableEntry(lines, previewTextStartLineIdx),
193
+ );
194
+ previewLines.set(
195
+ lineIdx + 1,
196
+ getNonNullableEntry(lines, lineIdx),
197
+ );
198
+ const previewTextEndLineIdx = Math.min(
199
+ lineIdx + 1,
200
+ lines.length - 1,
201
+ );
202
+ previewLines.set(
203
+ previewTextEndLineIdx + 1,
204
+ getNonNullableEntry(lines, previewTextEndLineIdx),
205
+ );
206
+ result.matches.push({
207
+ line: lineIdx + 1,
208
+ startColumn: currentMatchIdx + 1,
209
+ endColumn: currentMatchIdx + 1 + lowerCaseSearchText.length,
210
+ });
211
+ fromIdx = currentMatchIdx + lowerCaseSearchText.length;
212
+ currentMatchIdx = lowerCaseLine.indexOf(
213
+ lowerCaseSearchText,
214
+ fromIdx,
215
+ );
216
+ }
217
+ });
218
+ if (!result.matches.length) {
219
+ return;
220
+ }
221
+ result.preview = Array.from(previewLines.entries())
222
+ .map(([line, text]) => ({ line, text }))
223
+ .sort((a, b) => a.line - b.line);
224
+ matches.push(result);
225
+ }),
226
+ ),
227
+ );
228
+ return {
229
+ showcases: Array.from(
230
+ this.showcaseSearchEngine.search(searchText).values(),
231
+ ).map((result) => result.item.path),
232
+ textMatches: matches,
233
+ };
234
+ }
107
235
  }
@@ -26,6 +26,12 @@ interface GetShowcaseRequest extends RequestGenericInterface {
26
26
  };
27
27
  }
28
28
 
29
+ interface TextSearchRequest extends RequestGenericInterface {
30
+ Querystring: {
31
+ searchText: string;
32
+ };
33
+ }
34
+
29
35
  export const configureShowcaseRegistryServer = async (
30
36
  server: FastifyInstance,
31
37
  config: ShowcaseRegistryConfig & {
@@ -53,7 +59,12 @@ export const configureShowcaseRegistryServer = async (
53
59
  },
54
60
  );
55
61
 
56
- server.get(`${baseUrl}/showcase/search`, async (request, reply) => {
57
- // do nothing
58
- });
62
+ server.get<TextSearchRequest>(
63
+ `${baseUrl}/showcases/search`,
64
+ async (request, reply) => {
65
+ const { searchText } = request.query;
66
+
67
+ await reply.send(await registry.search(searchText));
68
+ },
69
+ );
59
70
  };
package/src/server.ts CHANGED
@@ -20,6 +20,8 @@ import { fastifyCors } from '@fastify/cors';
20
20
  import { configureShowcaseRegistryServer } from './ShowcaseRegistryServer.js';
21
21
  import type { ShowcaseRegistryConfig } from './ShowcaseRegistry.js';
22
22
 
23
+ const API_BASE_URL = '/api';
24
+
23
25
  const configFilePath = process.argv[2] ?? './config.json';
24
26
  if (!existsSync(configFilePath)) {
25
27
  throw new Error(`Can't find config file at path '${configFilePath}'`);
@@ -36,7 +38,7 @@ const server = fastify({
36
38
  server.register(fastifyCors, {});
37
39
 
38
40
  await configureShowcaseRegistryServer(server, {
39
- apiBaseUrl: '/api',
41
+ apiBaseUrl: API_BASE_URL,
40
42
  datasources: (config as ShowcaseRegistryConfig).datasources.map(
41
43
  (datasource) => {
42
44
  if (datasource.path) {