@terrafirmagreg-team/game 0.0.0-stage → 0.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.
Files changed (89) hide show
  1. package/package.json +58 -3
  2. package/src/components/entry/atlas-sprite.tsx +57 -0
  3. package/src/components/entry/copy-id-menu.tsx +55 -0
  4. package/src/components/entry/entry.tsx +229 -0
  5. package/src/components/entry/frame.tsx +32 -0
  6. package/src/components/entry/group.tsx +180 -0
  7. package/src/components/entry/icon/tabs.tsx +118 -0
  8. package/src/components/entry/icon.tsx +187 -0
  9. package/src/components/entry/list.tsx +70 -0
  10. package/src/components/entry/mc-text.tsx +92 -0
  11. package/src/components/entry/stack-count.tsx +14 -0
  12. package/src/components/entry/tooltip.tsx +129 -0
  13. package/src/components/mdx/item-icon.tsx +69 -0
  14. package/src/components/preview-icon.tsx +25 -0
  15. package/src/hooks/use-dump-location.ts +18 -0
  16. package/src/hooks/use-entry-context.ts +35 -0
  17. package/src/hooks/use-icon-source.ts +297 -0
  18. package/src/hooks/use-lang-map.ts +18 -0
  19. package/src/lib/dump/assets/atlas/dto.ts +49 -0
  20. package/src/lib/dump/assets/atlas/lookup.ts +206 -0
  21. package/src/lib/dump/assets/atlas/sprites.ts +130 -0
  22. package/src/lib/dump/assets/lang/load.ts +17 -0
  23. package/src/lib/dump/assets/lang/names.ts +244 -0
  24. package/src/lib/dump/assets/urls.ts +143 -0
  25. package/src/lib/dump/client/atlases.ts +83 -0
  26. package/src/lib/dump/client/dump.ts +188 -0
  27. package/src/lib/dump/client/groups.ts +31 -0
  28. package/src/lib/dump/client/keybinds.ts +11 -0
  29. package/src/lib/dump/client/mods.ts +16 -0
  30. package/src/lib/dump/client/registry.ts +125 -0
  31. package/src/lib/dump/client/tabs.ts +27 -0
  32. package/src/lib/dump/client/tags.ts +64 -0
  33. package/src/lib/dump/contract/kind.ts +61 -0
  34. package/src/lib/dump/contract/read.ts +77 -0
  35. package/src/lib/dump/contract/ref.ts +262 -0
  36. package/src/lib/dump/contract/text.ts +86 -0
  37. package/src/lib/dump/data/registry/dto.ts +56 -0
  38. package/src/lib/dump/data/registry/parse.ts +291 -0
  39. package/src/lib/dump/data/tab/dto.ts +13 -0
  40. package/src/lib/dump/data/tab/parse.ts +26 -0
  41. package/src/lib/dump/data/tag/dto.ts +7 -0
  42. package/src/lib/dump/data/tag/parse.ts +57 -0
  43. package/src/lib/dump/gen/data/guides/book.ts +56 -0
  44. package/src/lib/dump/gen/data/guides/category.ts +30 -0
  45. package/src/lib/dump/gen/data/guides/entry.ts +48 -0
  46. package/src/lib/dump/gen/data/models/model.ts +3 -0
  47. package/src/lib/dump/gen/data/quests/chapter.ts +126 -0
  48. package/src/lib/dump/gen/data/quests/chapter_groups.ts +11 -0
  49. package/src/lib/dump/gen/data/quests/data.ts +40 -0
  50. package/src/lib/dump/gen/data/recipes/recipe.ts +78 -0
  51. package/src/lib/dump/gen/index.ts +20 -0
  52. package/src/lib/dump/gen/keybinds.ts +3 -0
  53. package/src/lib/dump/gen/lang.ts +3 -0
  54. package/src/lib/dump/gen/manifest.ts +59 -0
  55. package/src/lib/dump/gen/registry/blocks.ts +60 -0
  56. package/src/lib/dump/gen/registry/entities.ts +49 -0
  57. package/src/lib/dump/gen/registry/fluids.ts +48 -0
  58. package/src/lib/dump/gen/registry/groups.ts +14 -0
  59. package/src/lib/dump/gen/registry/items.ts +51 -0
  60. package/src/lib/dump/gen/registry/recipe_categories.ts +64 -0
  61. package/src/lib/dump/gen/registry/recipes.ts +3 -0
  62. package/src/lib/dump/gen/registry/tabs.ts +46 -0
  63. package/src/lib/dump/gen/registry/tags.ts +11 -0
  64. package/src/lib/dump/json-reader.ts +231 -0
  65. package/src/lib/dump/manifest/dto.ts +11 -0
  66. package/src/lib/dump/manifest/parse.ts +167 -0
  67. package/src/lib/dump/mods/dto.ts +40 -0
  68. package/src/lib/dump/mods/index.ts +81 -0
  69. package/src/lib/dump/mods/load.ts +61 -0
  70. package/src/lib/dump/paths.ts +171 -0
  71. package/src/lib/dump/reader.ts +15 -0
  72. package/src/lib/entry/display.ts +4 -0
  73. package/src/lib/entry/entry.ts +57 -0
  74. package/src/lib/entry/formula.ts +226 -0
  75. package/src/lib/entry/href.ts +148 -0
  76. package/src/lib/entry/icon-load.ts +195 -0
  77. package/src/lib/entry/icon.ts +10 -0
  78. package/src/lib/entry/tooltip.ts +60 -0
  79. package/src/lib/preview.ts +18 -0
  80. package/src/lib/search.ts +31 -0
  81. package/src/lib/snbt.ts +246 -0
  82. package/src/locales/en_us.json +9 -0
  83. package/src/locales/ru_ru.json +9 -0
  84. package/src/locales.ts +5 -0
  85. package/src/types/entry.ts +52 -0
  86. package/styles/fonts.css +15 -0
  87. package/styles/tokens.css +15 -0
  88. package/styles.css +2 -0
  89. package/README.md +0 -3
@@ -0,0 +1,148 @@
1
+ import { sectionUrl } from '@terrafirmagreg-team/kit/lib/location';
2
+ import { encodeQuery, withLang } from '@terrafirmagreg-team/translate/lib/url';
3
+ import { tagPseudoId } from '@terrafirmagreg-team/game/lib/dump/paths';
4
+ import { refId, tagKindOf } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
5
+ import type { TaggedReference } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
6
+ import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
7
+ import type { EntryHrefOpts } from '@terrafirmagreg-team/game/types/entry';
8
+
9
+ export type { EntryHrefOpts } from '@terrafirmagreg-team/game/types/entry';
10
+
11
+ const query = encodeQuery;
12
+
13
+ function recipeHref(
14
+ dataset: string,
15
+ lang: Lang | undefined,
16
+ opts?: {
17
+ type?: string;
18
+ item?: string;
19
+ fluid?: string;
20
+ id?: string;
21
+ tab?: string;
22
+ search?: string;
23
+ rpage?: number;
24
+ },
25
+ ): string {
26
+ const q = query([
27
+ ['type', opts?.type],
28
+ ['item', opts?.item],
29
+ ['fluid', opts?.item ? undefined : opts?.fluid],
30
+ ['id', opts?.id],
31
+ ['tab', opts?.tab],
32
+ ['search', opts?.search],
33
+ ['rpage', opts?.rpage && opts.rpage > 1 ? Math.floor(opts.rpage) : undefined],
34
+ ]);
35
+ const base = sectionUrl('recipe', dataset);
36
+ const href = q ? `${base}?${q}` : base;
37
+ return lang ? withLang(href, lang) : href;
38
+ }
39
+
40
+ function recipeStackHref(
41
+ dataset: string,
42
+ lang: Lang | undefined,
43
+ id: string,
44
+ opts?: Pick<EntryHrefOpts, 'tab' | 'search'> & {
45
+ kind?: 'item' | 'fluid' | 'tag';
46
+ tagKind?: string;
47
+ },
48
+ ): string {
49
+ const kept = { tab: opts?.tab, search: opts?.search };
50
+ if (opts?.kind === 'tag' || id.startsWith('#')) {
51
+ const bare = id.startsWith('#') ? id.slice(1) : id;
52
+ return recipeHref(dataset, lang, {
53
+ type: 'emi:tag',
54
+ id: tagPseudoId(bare, opts?.tagKind ?? 'item'),
55
+ ...kept,
56
+ });
57
+ }
58
+ return opts?.kind === 'fluid'
59
+ ? recipeHref(dataset, lang, { fluid: id, ...kept })
60
+ : recipeHref(dataset, lang, { item: id, ...kept });
61
+ }
62
+
63
+ function questHref(
64
+ dataset: string,
65
+ lang: Lang | undefined,
66
+ opts?: { category?: string; id?: string; item?: string },
67
+ ): string {
68
+ const q = query([
69
+ ['category', opts?.category],
70
+ ['id', opts?.id],
71
+ ['item', opts?.item],
72
+ ]);
73
+ const base = sectionUrl('quest', dataset);
74
+ const href = q ? `${base}?${q}` : base;
75
+ return lang ? withLang(href, lang) : href;
76
+ }
77
+
78
+ export function guideHref(
79
+ dataset: string,
80
+ lang: Lang | undefined,
81
+ bookId?: string,
82
+ entryId?: string,
83
+ hash?: string,
84
+ categoryId?: string,
85
+ ): string {
86
+ const q = query([
87
+ ['book', bookId],
88
+ ['entry', entryId],
89
+ ['cat', entryId ? undefined : categoryId],
90
+ ]);
91
+ const base = `${sectionUrl('guide', dataset)}${q ? `?${q}` : ''}`;
92
+ const href = hash ? `${base}#${hash}` : base;
93
+ return lang ? withLang(href, lang) : href;
94
+ }
95
+
96
+ export function entryHref(
97
+ dataset: string,
98
+ lang: Lang | undefined,
99
+ reference: TaggedReference,
100
+ opts?: EntryHrefOpts,
101
+ ): string {
102
+ const kept = { tab: opts?.tab, search: opts?.search };
103
+ switch (reference.kind) {
104
+ case 'item':
105
+ case 'block':
106
+ case 'entity':
107
+ return recipeStackHref(dataset, lang, refId(reference), { ...kept, kind: 'item' });
108
+ case 'fluid':
109
+ return recipeStackHref(dataset, lang, refId(reference), { ...kept, kind: 'fluid' });
110
+ case 'itemTag':
111
+ case 'blockTag':
112
+ case 'fluidTag':
113
+ case 'entityTag':
114
+ return recipeStackHref(dataset, lang, refId(reference), {
115
+ ...kept,
116
+ kind: 'tag',
117
+ tagKind: tagKindOf(reference.kind),
118
+ });
119
+ case 'texture':
120
+ case 'model':
121
+ return '#';
122
+ case 'recipe':
123
+ return recipeHref(dataset, lang, {
124
+ id: reference.id,
125
+ ...kept,
126
+ rpage: opts?.rpage,
127
+ type: opts?.type,
128
+ });
129
+ case 'quest':
130
+ return questHref(dataset, lang, {
131
+ category: opts?.filename,
132
+ id: reference.id,
133
+ });
134
+ case 'guide':
135
+ case 'chapter':
136
+ return guideHref(
137
+ dataset,
138
+ lang,
139
+ opts?.modId && opts?.bookPath ? `${opts.modId}:${opts.bookPath}` : undefined,
140
+ opts?.entry ?? reference.id,
141
+ opts?.hash,
142
+ );
143
+ default: {
144
+ const _exhaustive: never = reference.kind;
145
+ return _exhaustive;
146
+ }
147
+ }
148
+ }
@@ -0,0 +1,195 @@
1
+ import type { AtlasBundle } from '@terrafirmagreg-team/game/lib/dump/assets/atlas/dto';
2
+ import {
3
+ clientDumpJson,
4
+ clientRegistryDataFileRel,
5
+ clientTagFileRel,
6
+ } from '@terrafirmagreg-team/game/lib/dump/client/dump';
7
+ import { loadClientModAtlases } from '@terrafirmagreg-team/game/lib/dump/client/atlases';
8
+ import { dumpRelPublicUrl, matchTagIndexKey } from '@terrafirmagreg-team/game/lib/dump/paths';
9
+ import { lookupSpriteInBundle } from '@terrafirmagreg-team/game/lib/dump/assets/atlas/lookup';
10
+ import { asIdTable, asRecord, asString } from '@terrafirmagreg-team/game/lib/dump/contract/read';
11
+ import { asTaggedReference } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
12
+ import { asEntry, matchVariant } from '@terrafirmagreg-team/game/lib/dump/data/registry/parse';
13
+ import type { DumpEntityEntry } from '@terrafirmagreg-team/game/lib/dump/data/registry/dto';
14
+ import { asTag, firstTagItem } from '@terrafirmagreg-team/game/lib/dump/data/tag/parse';
15
+ import type { DumpNbt } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
16
+ import type { DumpTag } from '@terrafirmagreg-team/game/lib/dump/data/tag/dto';
17
+ import { createAsyncCache } from '@terrafirmagreg-team/ui/lib/utils/async-cache';
18
+
19
+ export type { AtlasBundle } from '@terrafirmagreg-team/game/lib/dump/assets/atlas/dto';
20
+
21
+ type TagMemberKind = 'item' | 'block' | 'fluid' | 'entity';
22
+
23
+ const TAG_MEMBER_REGISTRY: Record<TagMemberKind, string> = {
24
+ item: 'items',
25
+ block: 'blocks',
26
+ fluid: 'fluids',
27
+ entity: 'entities',
28
+ };
29
+
30
+ export function fetchAtlases(
31
+ dataset: string,
32
+ modId: string,
33
+ ): Promise<AtlasBundle | null> {
34
+ return loadClientModAtlases(dataset, modId).then((bundle) =>
35
+ bundle.items || bundle.blocks ? bundle : null,
36
+ );
37
+ }
38
+
39
+ const fetchTagRecord = createAsyncCache(
40
+ async (dataset: string, tagId: string, memberKind: TagMemberKind) => {
41
+ const rel = await clientTagFileRel(dataset, tagId);
42
+ const entries = asRecord(await clientDumpJson(dataset, rel, 'high'));
43
+ if (!entries) return undefined;
44
+ const entryKey = matchTagIndexKey(
45
+ entries,
46
+ tagId,
47
+ TAG_MEMBER_REGISTRY[memberKind],
48
+ );
49
+ return entryKey !== undefined
50
+ ? asTag(entries[entryKey], entryKey, asIdTable(entries))
51
+ : undefined;
52
+ },
53
+ );
54
+
55
+ export function fetchTagItem(
56
+ dataset: string,
57
+ tagId: string,
58
+ memberKind: TagMemberKind,
59
+ ): Promise<string | null> {
60
+ return fetchTagRecord(dataset, tagId, memberKind).then(
61
+ (tag: DumpTag | undefined) => firstTagItem(tag) ?? null,
62
+ );
63
+ }
64
+
65
+ const TAG_ICON_MEMBER_LIMIT = 16;
66
+
67
+ const TAG_ICON_SPRITE_KIND: Record<TagMemberKind, 'item' | 'fluid'> = {
68
+ item: 'item',
69
+ block: 'item',
70
+ fluid: 'fluid',
71
+ entity: 'item',
72
+ };
73
+
74
+ const fetchTagMembersList = createAsyncCache(
75
+ async (
76
+ dataset: string,
77
+ tagId: string,
78
+ memberKind: TagMemberKind,
79
+ ): Promise<string[]> => {
80
+ const tag = await fetchTagRecord(dataset, tagId, memberKind);
81
+ const members = (tag?.members ?? [])
82
+ .filter((member) => member && !member.startsWith('#'))
83
+ .slice(0, TAG_ICON_MEMBER_LIMIT);
84
+ if (!members.length) return [];
85
+ if (memberKind !== 'item' && memberKind !== 'block' && memberKind !== 'fluid') {
86
+ return members;
87
+ }
88
+ const spriteKind = TAG_ICON_SPRITE_KIND[memberKind];
89
+ const out: string[] = [];
90
+ for (const member of members) {
91
+ const itemId =
92
+ memberKind === 'block'
93
+ ? await fetchBlockItem(dataset, member)
94
+ : member;
95
+ const ns = itemId.split(':', 1)[0];
96
+ const bundle = ns ? await fetchAtlases(dataset, ns) : null;
97
+ if (lookupSpriteInBundle(bundle, { id: itemId, kind: spriteKind })) {
98
+ out.push(member);
99
+ }
100
+ }
101
+ return out.length ? out : [members[0]];
102
+ },
103
+ );
104
+
105
+ export function fetchTagMembers(
106
+ dataset: string,
107
+ tagId: string,
108
+ memberKind: TagMemberKind,
109
+ ): Promise<string[]> {
110
+ return fetchTagMembersList(dataset, tagId, memberKind);
111
+ }
112
+
113
+ const fetchBlockItem = createAsyncCache(
114
+ async (dataset: string, blockId: string): Promise<string> => {
115
+ const ns = blockId.split(':', 1)[0];
116
+ const rel = ns
117
+ ? await clientRegistryDataFileRel(dataset, ns, 'blocks')
118
+ : undefined;
119
+ const entries = rel
120
+ ? asRecord(await clientDumpJson(dataset, rel, 'high'))
121
+ : undefined;
122
+ const row = entries ? asRecord(entries[blockId]) : undefined;
123
+ const itemRef = row ? asTaggedReference(row.item) : undefined;
124
+ return asString(itemRef?.id) ?? blockId;
125
+ },
126
+ );
127
+
128
+ export function blockItemId(
129
+ dataset: string,
130
+ blockId: string,
131
+ ): Promise<string> {
132
+ return fetchBlockItem(dataset, blockId);
133
+ }
134
+
135
+ const fetchEntity = createAsyncCache(async (dataset: string, id: string) => {
136
+ const ns = id.split(':', 1)[0];
137
+ const rel = ns
138
+ ? await clientRegistryDataFileRel(dataset, ns, 'entities')
139
+ : undefined;
140
+ const entries = rel
141
+ ? asRecord(await clientDumpJson(dataset, rel, 'high'))
142
+ : undefined;
143
+ const raw = entries?.[id];
144
+ if (raw === undefined) return undefined;
145
+ const parsed = asEntry(raw, 'entities', id, asIdTable(entries ?? {}));
146
+ return parsed as DumpEntityEntry | undefined;
147
+ });
148
+
149
+ export async function fetchEntityTexture(
150
+ dataset: string,
151
+ id: string,
152
+ nbt?: DumpNbt,
153
+ ): Promise<string | null> {
154
+ const entity = await fetchEntity(dataset, id);
155
+ return entityTextureUrl(dataset, entity, id, nbt);
156
+ }
157
+
158
+ export function peekEntityTexture(
159
+ dataset: string,
160
+ id: string,
161
+ nbt?: DumpNbt,
162
+ ): string | null | undefined {
163
+ const entity = fetchEntity.peek(dataset, id);
164
+ return entity === undefined
165
+ ? undefined
166
+ : entityTextureUrl(dataset, entity, id, nbt);
167
+ }
168
+
169
+ export function peekTagMembers(
170
+ dataset: string,
171
+ tagId: string,
172
+ memberKind: TagMemberKind,
173
+ ): string[] | undefined {
174
+ return fetchTagMembersList.peek(dataset, tagId, memberKind);
175
+ }
176
+
177
+ export function peekBlockItem(
178
+ dataset: string,
179
+ blockId: string,
180
+ ): string | undefined {
181
+ return fetchBlockItem.peek(dataset, blockId);
182
+ }
183
+
184
+ function entityTextureUrl(
185
+ dataset: string,
186
+ entity: DumpEntityEntry | undefined,
187
+ id: string,
188
+ nbt?: DumpNbt,
189
+ ): string | null {
190
+ if (!entity) return null;
191
+ const texture = matchVariant(entity.variants, nbt)?.texture ?? entity.texture;
192
+ if (!texture) return null;
193
+ const ns = id.split(':', 1)[0];
194
+ return dumpRelPublicUrl(dataset, `assets/${ns}/${texture}`) ?? null;
195
+ }
@@ -0,0 +1,10 @@
1
+ import { tagKindOf } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
2
+ import type { ReferenceKind } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
3
+
4
+ export function stackKindOf(
5
+ kind: ReferenceKind | undefined,
6
+ ): 'item' | 'block' | 'fluid' | undefined {
7
+ if (kind === 'item' || kind === 'block' || kind === 'fluid') return kind;
8
+ const member = tagKindOf(kind);
9
+ return member === 'entity' ? undefined : member;
10
+ }
@@ -0,0 +1,60 @@
1
+ import { displayOf } from '@terrafirmagreg-team/game/lib/dump/data/registry/parse';
2
+ import { parseStackId } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
3
+ import { clientDumpLang } from '@terrafirmagreg-team/game/lib/dump/client/dump';
4
+ import {
5
+ clientRegistryKindFor,
6
+ loadClientRegistry,
7
+ } from '@terrafirmagreg-team/game/lib/dump/client/registry';
8
+ import type { DumpDisplay } from '@terrafirmagreg-team/game/lib/dump/contract/text';
9
+ import { dumpTextLines } from '@terrafirmagreg-team/game/lib/dump/assets/lang/names';
10
+ import { createAsyncCache } from '@terrafirmagreg-team/ui/lib/utils/async-cache';
11
+
12
+ export const loadStackDisplay = createAsyncCache(
13
+ async (
14
+ dataset: string,
15
+ compactId: string,
16
+ kind?: 'item' | 'fluid' | 'entity',
17
+ ): Promise<DumpDisplay | undefined> => {
18
+ const parsed = parseStackId(compactId);
19
+ const ofKind = async (registry: 'items' | 'fluids') => {
20
+ const entry = (await loadClientRegistry(dataset, registry)).get(
21
+ parsed.id,
22
+ );
23
+ return entry ? displayOf(entry, parsed.nbt) : undefined;
24
+ };
25
+ if (kind) {
26
+ const entry = (
27
+ await loadClientRegistry(dataset, clientRegistryKindFor(kind))
28
+ ).get(parsed.id);
29
+ return entry ? displayOf(entry, parsed.nbt) : undefined;
30
+ }
31
+ return (await ofKind('items')) ?? (await ofKind('fluids'));
32
+ },
33
+ );
34
+
35
+ type StackTooltip = {
36
+ lines: string[];
37
+ mods: Record<string, string[]>;
38
+ };
39
+
40
+ export async function stackTooltip(
41
+ dataset: string,
42
+ id: string,
43
+ lang: string | undefined,
44
+ langMap: Record<string, string> | undefined,
45
+ kind: 'item' | 'fluid' | 'entity' | undefined,
46
+ ): Promise<StackTooltip> {
47
+ const display = await loadStackDisplay(dataset, id, kind);
48
+ const map =
49
+ langMap ?? (lang ? await clientDumpLang(dataset, lang) : undefined);
50
+ if (!display || !map) return { lines: [], mods: {} };
51
+ const mods: Record<string, string[]> = {};
52
+ for (const [mod, raw] of Object.entries(display.tooltipMods ?? {})) {
53
+ const resolved = dumpTextLines(map, raw);
54
+ if (resolved.length) mods[mod] = resolved;
55
+ }
56
+ return {
57
+ lines: display.tooltip?.length ? dumpTextLines(map, display.tooltip) : [],
58
+ mods,
59
+ };
60
+ }
@@ -0,0 +1,18 @@
1
+ import {
2
+ stackIdFromRef,
3
+ type TaggedReference,
4
+ } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
5
+ import type { PreviewIcon } from '@terrafirmagreg-team/ui/types/preview';
6
+
7
+ export function entryPreviewIcon(
8
+ ref: TaggedReference | undefined,
9
+ dataset: string,
10
+ ): PreviewIcon | undefined {
11
+ if (!ref) return undefined;
12
+ return {
13
+ type: 'entry',
14
+ kind: ref.kind,
15
+ id: stackIdFromRef(ref),
16
+ dataset,
17
+ };
18
+ }
@@ -0,0 +1,31 @@
1
+ import { staticClient } from 'fumadocs-core/search/client/orama-static';
2
+ import { parseDatasetRef } from '@terrafirmagreg-team/kit/lib/location';
3
+ import type { SearchClientSourceCtx } from '@terrafirmagreg-team/kit/lib/search/sources';
4
+ import { loadClientVersions } from './dump/client/dump';
5
+ import { dumpStorageUrl } from './dump/paths';
6
+
7
+ export function dumpSearchClientSource({ refs, locale, tag }: SearchClientSourceCtx) {
8
+ return {
9
+ deps: [refs.join(','), locale, String(tag)],
10
+ async search(query: string) {
11
+ const clients = await Promise.all(
12
+ refs.map(async (ref) => {
13
+ const { id, version } = parseDatasetRef(ref);
14
+ const ver = version ?? (await loadClientVersions(id))?.latest;
15
+ if (!ver) return null;
16
+ return staticClient({
17
+ locale,
18
+ tag,
19
+ from: dumpStorageUrl(`${id}/${ver}/search.json`),
20
+ });
21
+ }),
22
+ );
23
+ const lists = await Promise.all(
24
+ clients.flatMap((client) =>
25
+ client ? [Promise.resolve(client.search(query)).catch(() => [])] : [],
26
+ ),
27
+ );
28
+ return lists.flat();
29
+ },
30
+ };
31
+ }