@se-studio/search 1.0.1
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 +11 -0
- package/README.md +185 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +2 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/route-handler.d.ts +53 -0
- package/dist/api/route-handler.d.ts.map +1 -0
- package/dist/api/route-handler.js +135 -0
- package/dist/api/route-handler.js.map +1 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/search-client.d.ts +12 -0
- package/dist/client/search-client.d.ts.map +1 -0
- package/dist/client/search-client.js +72 -0
- package/dist/client/search-client.js.map +1 -0
- package/dist/debug.d.ts +2 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +12 -0
- package/dist/debug.js.map +1 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/useSearch.d.ts +26 -0
- package/dist/hooks/useSearch.d.ts.map +1 -0
- package/dist/hooks/useSearch.js +73 -0
- package/dist/hooks/useSearch.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/indexing/content-extractor.d.ts +20 -0
- package/dist/indexing/content-extractor.d.ts.map +1 -0
- package/dist/indexing/content-extractor.js +81 -0
- package/dist/indexing/content-extractor.js.map +1 -0
- package/dist/indexing/document-builder.d.ts +33 -0
- package/dist/indexing/document-builder.d.ts.map +1 -0
- package/dist/indexing/document-builder.js +117 -0
- package/dist/indexing/document-builder.js.map +1 -0
- package/dist/indexing/index.d.ts +4 -0
- package/dist/indexing/index.d.ts.map +1 -0
- package/dist/indexing/index.js +4 -0
- package/dist/indexing/index.js.map +1 -0
- package/dist/indexing/rebuild.d.ts +19 -0
- package/dist/indexing/rebuild.d.ts.map +1 -0
- package/dist/indexing/rebuild.js +133 -0
- package/dist/indexing/rebuild.js.map +1 -0
- package/dist/types.d.ts +112 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/webhook/handler.d.ts +31 -0
- package/dist/webhook/handler.d.ts.map +1 -0
- package/dist/webhook/handler.js +133 -0
- package/dist/webhook/handler.js.map +1 -0
- package/dist/webhook/index.d.ts +2 -0
- package/dist/webhook/index.d.ts.map +1 -0
- package/dist/webhook/index.js +2 -0
- package/dist/webhook/index.js.map +1 -0
- package/package.json +70 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upstash Search connection credentials.
|
|
3
|
+
* A single database is shared; separate index names distinguish published vs preview.
|
|
4
|
+
*/
|
|
5
|
+
export interface SearchConfig {
|
|
6
|
+
url: string;
|
|
7
|
+
token: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Index configuration: one Upstash database with two named indexes.
|
|
11
|
+
*/
|
|
12
|
+
export interface SearchIndexConfig {
|
|
13
|
+
connection: SearchConfig;
|
|
14
|
+
publishedIndexName: string;
|
|
15
|
+
previewIndexName: string;
|
|
16
|
+
}
|
|
17
|
+
/** Supported content types for search indexing. */
|
|
18
|
+
export type SearchableContentType = 'page' | 'article' | 'person' | 'articleType' | 'customType';
|
|
19
|
+
/**
|
|
20
|
+
* Per-content-type indexing configuration.
|
|
21
|
+
*/
|
|
22
|
+
export interface ContentTypeIndexConfig {
|
|
23
|
+
type: SearchableContentType;
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
/** Override the global `indexComponents` flag for this content type. */
|
|
26
|
+
includeComponents?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Full search indexing configuration provided by each app.
|
|
30
|
+
*/
|
|
31
|
+
export interface SearchIndexingConfig {
|
|
32
|
+
searchIndex: SearchIndexConfig;
|
|
33
|
+
contentTypes: ContentTypeIndexConfig[];
|
|
34
|
+
/** Whether to extract text from nested components/collections. Default: true. */
|
|
35
|
+
indexComponents?: boolean;
|
|
36
|
+
/** Skip entries where `indexed === false`. Default: true. */
|
|
37
|
+
respectIndexedFlag?: boolean;
|
|
38
|
+
/** Skip entries where `hidden === true`. Default: true. */
|
|
39
|
+
respectHiddenFlag?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A document ready to be upserted into Upstash Search.
|
|
43
|
+
*/
|
|
44
|
+
export interface SearchDocument {
|
|
45
|
+
id: string;
|
|
46
|
+
content: {
|
|
47
|
+
title: string;
|
|
48
|
+
description: string;
|
|
49
|
+
body: string;
|
|
50
|
+
};
|
|
51
|
+
metadata: SearchDocumentMetadata;
|
|
52
|
+
}
|
|
53
|
+
export interface SearchDocumentMetadata {
|
|
54
|
+
type: SearchableContentType;
|
|
55
|
+
slug: string;
|
|
56
|
+
href: string;
|
|
57
|
+
tags?: string[];
|
|
58
|
+
articleType?: string;
|
|
59
|
+
date?: string;
|
|
60
|
+
lastModified?: string;
|
|
61
|
+
author?: string;
|
|
62
|
+
imageUrl?: string;
|
|
63
|
+
imageAlt?: string;
|
|
64
|
+
imageWidth?: number;
|
|
65
|
+
imageHeight?: number;
|
|
66
|
+
/** The base entry ID (without chunk suffix). Used for deduplication. */
|
|
67
|
+
entryId: string;
|
|
68
|
+
/** 0-based index of this chunk within the entry. */
|
|
69
|
+
chunkIndex: number;
|
|
70
|
+
/** Total number of chunks for this entry. */
|
|
71
|
+
totalChunks: number;
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A single result returned from a search query.
|
|
76
|
+
*/
|
|
77
|
+
export interface SearchResult {
|
|
78
|
+
id: string;
|
|
79
|
+
content: {
|
|
80
|
+
title: string;
|
|
81
|
+
description: string;
|
|
82
|
+
body: string;
|
|
83
|
+
};
|
|
84
|
+
metadata: SearchDocumentMetadata;
|
|
85
|
+
score: number;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Full response from a search query.
|
|
89
|
+
*/
|
|
90
|
+
export interface SearchResponse {
|
|
91
|
+
results: SearchResult[];
|
|
92
|
+
query: string;
|
|
93
|
+
totalCount: number;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Options for performing a search query.
|
|
97
|
+
*/
|
|
98
|
+
export interface SearchOptions {
|
|
99
|
+
query: string;
|
|
100
|
+
limit?: number;
|
|
101
|
+
filter?: string;
|
|
102
|
+
semanticWeight?: number;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Result of a full index rebuild.
|
|
106
|
+
*/
|
|
107
|
+
export interface RebuildResult {
|
|
108
|
+
indexed: number;
|
|
109
|
+
skipped: number;
|
|
110
|
+
errors: string[];
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { BaseConverterContext, ContentfulConfig, FetchOptions, UrlCalculators } from '@se-studio/contentful-rest-api';
|
|
2
|
+
import type { SiteConfig } from '@se-studio/markdown-renderer';
|
|
3
|
+
import type { SearchIndexingConfig } from '../types';
|
|
4
|
+
export interface SearchWebhookConfig {
|
|
5
|
+
indexingConfig: SearchIndexingConfig;
|
|
6
|
+
converterContext: BaseConverterContext;
|
|
7
|
+
contentfulConfig: ContentfulConfig;
|
|
8
|
+
urlCalculators: UrlCalculators;
|
|
9
|
+
siteConfig: SiteConfig;
|
|
10
|
+
fetchOptions: FetchOptions;
|
|
11
|
+
}
|
|
12
|
+
export interface SearchWebhookHandler {
|
|
13
|
+
/**
|
|
14
|
+
* Called when an entry is published or updated in Contentful.
|
|
15
|
+
* Fetches the full entry, converts it to a search document, and upserts
|
|
16
|
+
* into both published and preview indexes.
|
|
17
|
+
*/
|
|
18
|
+
onPublish(contentType: string, entryId: string, slug?: string): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Called when an entry is deleted or unpublished in Contentful.
|
|
21
|
+
* Removes the entry from both indexes by ID.
|
|
22
|
+
*/
|
|
23
|
+
onDelete(contentType: string, entryId: string): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates a webhook handler for incremental search index updates.
|
|
27
|
+
* Call `onPublish` after cache revalidation completes for published entries,
|
|
28
|
+
* and `onDelete` for deleted/unpublished entries.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createSearchWebhookHandler(config: SearchWebhookConfig): SearchWebhookHandler;
|
|
31
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/webhook/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACf,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EAA4B,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAKzF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAErD,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,oBAAoB,CAAC;IACrC,gBAAgB,EAAE,oBAAoB,CAAC;IACvC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9E;;;OAGG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/D;AAgBD;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,mBAAmB,GAAG,oBAAoB,CA6I5F"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { MarkdownExporter } from '@se-studio/markdown-renderer';
|
|
2
|
+
import { createSearchClient } from '../client/search-client';
|
|
3
|
+
import { debugLog } from '../debug';
|
|
4
|
+
import { allChunkIds, buildSearchDocuments, shouldIndex } from '../indexing/document-builder';
|
|
5
|
+
/** Safety ceiling for chunk cleanup — delete up to this many chunk IDs per entry. */
|
|
6
|
+
const MAX_CHUNKS_CLEANUP = 20;
|
|
7
|
+
function mapContentfulTypeToExporterType(contentType) {
|
|
8
|
+
if (contentType === 'page' || contentType === 'pageVariant')
|
|
9
|
+
return 'page';
|
|
10
|
+
if (contentType === 'article')
|
|
11
|
+
return 'article';
|
|
12
|
+
if (contentType === 'articleType')
|
|
13
|
+
return 'articleType';
|
|
14
|
+
if (contentType === 'person')
|
|
15
|
+
return 'person';
|
|
16
|
+
if (contentType === 'customType')
|
|
17
|
+
return 'customType';
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates a webhook handler for incremental search index updates.
|
|
22
|
+
* Call `onPublish` after cache revalidation completes for published entries,
|
|
23
|
+
* and `onDelete` for deleted/unpublished entries.
|
|
24
|
+
*/
|
|
25
|
+
export function createSearchWebhookHandler(config) {
|
|
26
|
+
const { indexingConfig, converterContext, contentfulConfig, urlCalculators, siteConfig, fetchOptions, } = config;
|
|
27
|
+
const { connection, publishedIndexName, previewIndexName } = indexingConfig.searchIndex;
|
|
28
|
+
const client = createSearchClient(connection);
|
|
29
|
+
// Minimal content context for indexing — page-specific fields (article, person, etc.)
|
|
30
|
+
// are not needed since the MarkdownExporter fetches each entry independently.
|
|
31
|
+
const indexingContentContext = {
|
|
32
|
+
pageContext: {},
|
|
33
|
+
current: { type: 'Component', id: '' },
|
|
34
|
+
};
|
|
35
|
+
const converterCtx = {
|
|
36
|
+
contentContext: indexingContentContext,
|
|
37
|
+
config: contentfulConfig,
|
|
38
|
+
siteConfig,
|
|
39
|
+
urlCalculators,
|
|
40
|
+
};
|
|
41
|
+
function isEnabled(contentType) {
|
|
42
|
+
const mapped = mapContentfulTypeToExporterType(contentType);
|
|
43
|
+
if (!mapped)
|
|
44
|
+
return false;
|
|
45
|
+
return indexingConfig.contentTypes.some((ct) => ct.type === mapped && ct.enabled);
|
|
46
|
+
}
|
|
47
|
+
function getConfig(contentType) {
|
|
48
|
+
const mapped = mapContentfulTypeToExporterType(contentType);
|
|
49
|
+
if (!mapped)
|
|
50
|
+
return undefined;
|
|
51
|
+
return indexingConfig.contentTypes.find((ct) => ct.type === mapped && ct.enabled);
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
async onPublish(contentType, entryId, slug) {
|
|
55
|
+
debugLog('webhook', `onPublish called`, { contentType, entryId, slug });
|
|
56
|
+
if (!isEnabled(contentType) || !slug) {
|
|
57
|
+
debugLog('webhook', `onPublish: skipped (enabled=${isEnabled(contentType)}, slug="${slug}")`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const ctConfig = getConfig(contentType);
|
|
61
|
+
if (!ctConfig) {
|
|
62
|
+
debugLog('webhook', `onPublish: no config for "${contentType}"`);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const exporterType = mapContentfulTypeToExporterType(contentType);
|
|
66
|
+
if (!exporterType) {
|
|
67
|
+
debugLog('webhook', `onPublish: unsupported type "${contentType}"`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
debugLog('webhook', `onPublish: fetching content for ${exporterType}/${slug}`);
|
|
72
|
+
const exporter = new MarkdownExporter(contentfulConfig, converterContext, fetchOptions);
|
|
73
|
+
const contentData = await exporter.fetchContent(exporterType, slug);
|
|
74
|
+
if (!contentData) {
|
|
75
|
+
debugLog('webhook', `onPublish: no content data for ${exporterType}/${slug}`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const data = contentData.data;
|
|
79
|
+
const respectIndexed = indexingConfig.respectIndexedFlag ?? true;
|
|
80
|
+
const respectHidden = indexingConfig.respectHiddenFlag ?? true;
|
|
81
|
+
if (!shouldIndex(data, respectIndexed, respectHidden)) {
|
|
82
|
+
debugLog('webhook', `onPublish: entry not indexable, deleting from indexes`, {
|
|
83
|
+
indexed: data.indexed,
|
|
84
|
+
hidden: data.hidden,
|
|
85
|
+
});
|
|
86
|
+
const cleanupIds = allChunkIds(entryId, MAX_CHUNKS_CLEANUP);
|
|
87
|
+
await client.delete(publishedIndexName, cleanupIds);
|
|
88
|
+
await client.delete(previewIndexName, cleanupIds);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const includeComponents = ctConfig.includeComponents ?? indexingConfig.indexComponents ?? true;
|
|
92
|
+
const docs = buildSearchDocuments(contentData, ctConfig.type, converterCtx, includeComponents);
|
|
93
|
+
// Clean up any stale chunks from a previous version that may have had more chunks
|
|
94
|
+
const cleanupIds = allChunkIds(entryId, MAX_CHUNKS_CLEANUP);
|
|
95
|
+
debugLog('webhook', `onPublish: cleaning up old chunks (up to ${MAX_CHUNKS_CLEANUP})`);
|
|
96
|
+
await client.delete(publishedIndexName, cleanupIds);
|
|
97
|
+
await client.delete(previewIndexName, cleanupIds);
|
|
98
|
+
debugLog('webhook', `onPublish: upserting ${docs.length} chunk(s)`, {
|
|
99
|
+
indexes: [publishedIndexName, previewIndexName],
|
|
100
|
+
title: docs[0]?.content.title,
|
|
101
|
+
});
|
|
102
|
+
await client.upsert(publishedIndexName, docs);
|
|
103
|
+
await client.upsert(previewIndexName, docs);
|
|
104
|
+
debugLog('webhook', `onPublish: done for ${contentType}/${slug}`);
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
debugLog('webhook', `onPublish: ERROR for ${contentType}/${slug}`, { error: String(err) });
|
|
108
|
+
console.error(`[search] Failed to index ${contentType}/${slug}:`, err);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
async onDelete(contentType, entryId) {
|
|
112
|
+
debugLog('webhook', `onDelete called`, { contentType, entryId });
|
|
113
|
+
if (!mapContentfulTypeToExporterType(contentType)) {
|
|
114
|
+
debugLog('webhook', `onDelete: unsupported type "${contentType}", skipping`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const cleanupIds = allChunkIds(entryId, MAX_CHUNKS_CLEANUP);
|
|
119
|
+
debugLog('webhook', `onDelete: deleting ${entryId} + chunk IDs from indexes (${cleanupIds.length} ids)`);
|
|
120
|
+
await client.delete(publishedIndexName, cleanupIds);
|
|
121
|
+
await client.delete(previewIndexName, cleanupIds);
|
|
122
|
+
debugLog('webhook', `onDelete: done for ${contentType}/${entryId}`);
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
debugLog('webhook', `onDelete: ERROR for ${contentType}/${entryId}`, {
|
|
126
|
+
error: String(err),
|
|
127
|
+
});
|
|
128
|
+
console.error(`[search] Failed to delete ${contentType}/${entryId}:`, err);
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/webhook/handler.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AA2B9F,qFAAqF;AACrF,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAE9B,SAAS,+BAA+B,CACtC,WAAmB;IAEnB,IAAI,WAAW,KAAK,MAAM,IAAI,WAAW,KAAK,aAAa;QAAE,OAAO,MAAM,CAAC;IAC3E,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAChD,IAAI,WAAW,KAAK,aAAa;QAAE,OAAO,aAAa,CAAC;IACxD,IAAI,WAAW,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9C,IAAI,WAAW,KAAK,YAAY;QAAE,OAAO,YAAY,CAAC;IACtD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,MAA2B;IACpE,MAAM,EACJ,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,UAAU,EACV,YAAY,GACb,GAAG,MAAM,CAAC;IAEX,MAAM,EAAE,UAAU,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,GAAG,cAAc,CAAC,WAAW,CAAC;IACxF,MAAM,MAAM,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAE9C,sFAAsF;IACtF,8EAA8E;IAC9E,MAAM,sBAAsB,GAAoB;QAC9C,WAAW,EAAE,EAAE;QACf,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,EAAE,EAAE;KACvC,CAAC;IAEF,MAAM,YAAY,GAA6B;QAC7C,cAAc,EAAE,sBAAsB;QACtC,MAAM,EAAE,gBAAgB;QACxB,UAAU;QACV,cAAc;KACf,CAAC;IAEF,SAAS,SAAS,CAAC,WAAmB;QACpC,MAAM,MAAM,GAAG,+BAA+B,CAAC,WAAW,CAAC,CAAC;QAC5D,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,OAAO,cAAc,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,MAAM,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC;IACpF,CAAC;IAED,SAAS,SAAS,CAAC,WAAmB;QACpC,MAAM,MAAM,GAAG,+BAA+B,CAAC,WAAW,CAAC,CAAC;QAC5D,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAC9B,OAAO,cAAc,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,MAAM,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC;IACpF,CAAC;IAED,OAAO;QACL,KAAK,CAAC,SAAS,CAAC,WAAmB,EAAE,OAAe,EAAE,IAAa;YACjE,QAAQ,CAAC,SAAS,EAAE,kBAAkB,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAExE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACrC,QAAQ,CACN,SAAS,EACT,+BAA+B,SAAS,CAAC,WAAW,CAAC,WAAW,IAAI,IAAI,CACzE,CAAC;gBACF,OAAO;YACT,CAAC;YAED,MAAM,QAAQ,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;YACxC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,QAAQ,CAAC,SAAS,EAAE,6BAA6B,WAAW,GAAG,CAAC,CAAC;gBACjE,OAAO;YACT,CAAC;YAED,MAAM,YAAY,GAAG,+BAA+B,CAAC,WAAW,CAAC,CAAC;YAClE,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,QAAQ,CAAC,SAAS,EAAE,gCAAgC,WAAW,GAAG,CAAC,CAAC;gBACpE,OAAO;YACT,CAAC;YAED,IAAI,CAAC;gBACH,QAAQ,CAAC,SAAS,EAAE,mCAAmC,YAAY,IAAI,IAAI,EAAE,CAAC,CAAC;gBAC/E,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,gBAAgB,EAAE,gBAAgB,EAAE,YAAY,CAAC,CAAC;gBACxF,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;gBAEpE,IAAI,CAAC,WAAW,EAAE,CAAC;oBACjB,QAAQ,CAAC,SAAS,EAAE,kCAAkC,YAAY,IAAI,IAAI,EAAE,CAAC,CAAC;oBAC9E,OAAO;gBACT,CAAC;gBAED,MAAM,IAAI,GAAG,WAAW,CAAC,IAA6D,CAAC;gBACvF,MAAM,cAAc,GAAG,cAAc,CAAC,kBAAkB,IAAI,IAAI,CAAC;gBACjE,MAAM,aAAa,GAAG,cAAc,CAAC,iBAAiB,IAAI,IAAI,CAAC;gBAE/D,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,cAAc,EAAE,aAAa,CAAC,EAAE,CAAC;oBACtD,QAAQ,CAAC,SAAS,EAAE,uDAAuD,EAAE;wBAC3E,OAAO,EAAE,IAAI,CAAC,OAAO;wBACrB,MAAM,EAAE,IAAI,CAAC,MAAM;qBACpB,CAAC,CAAC;oBACH,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;oBAC5D,MAAM,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;oBACpD,MAAM,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;oBAClD,OAAO;gBACT,CAAC;gBAED,MAAM,iBAAiB,GACrB,QAAQ,CAAC,iBAAiB,IAAI,cAAc,CAAC,eAAe,IAAI,IAAI,CAAC;gBACvE,MAAM,IAAI,GAAG,oBAAoB,CAC/B,WAAW,EACX,QAAQ,CAAC,IAAI,EACb,YAAY,EACZ,iBAAiB,CAClB,CAAC;gBAEF,kFAAkF;gBAClF,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;gBAC5D,QAAQ,CAAC,SAAS,EAAE,4CAA4C,kBAAkB,GAAG,CAAC,CAAC;gBACvF,MAAM,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;gBACpD,MAAM,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;gBAElD,QAAQ,CAAC,SAAS,EAAE,wBAAwB,IAAI,CAAC,MAAM,WAAW,EAAE;oBAClE,OAAO,EAAE,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;oBAC/C,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK;iBAC9B,CAAC,CAAC;gBACH,MAAM,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;gBAC9C,MAAM,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;gBAC5C,QAAQ,CAAC,SAAS,EAAE,uBAAuB,WAAW,IAAI,IAAI,EAAE,CAAC,CAAC;YACpE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,QAAQ,CAAC,SAAS,EAAE,wBAAwB,WAAW,IAAI,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC3F,OAAO,CAAC,KAAK,CAAC,4BAA4B,WAAW,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;QAED,KAAK,CAAC,QAAQ,CAAC,WAAmB,EAAE,OAAe;YACjD,QAAQ,CAAC,SAAS,EAAE,iBAAiB,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;YAEjE,IAAI,CAAC,+BAA+B,CAAC,WAAW,CAAC,EAAE,CAAC;gBAClD,QAAQ,CAAC,SAAS,EAAE,+BAA+B,WAAW,aAAa,CAAC,CAAC;gBAC7E,OAAO;YACT,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;gBAC5D,QAAQ,CACN,SAAS,EACT,sBAAsB,OAAO,8BAA8B,UAAU,CAAC,MAAM,OAAO,CACpF,CAAC;gBACF,MAAM,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;gBACpD,MAAM,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;gBAClD,QAAQ,CAAC,SAAS,EAAE,sBAAsB,WAAW,IAAI,OAAO,EAAE,CAAC,CAAC;YACtE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,QAAQ,CAAC,SAAS,EAAE,uBAAuB,WAAW,IAAI,OAAO,EAAE,EAAE;oBACnE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC;iBACnB,CAAC,CAAC;gBACH,OAAO,CAAC,KAAK,CAAC,6BAA6B,WAAW,IAAI,OAAO,GAAG,EAAE,GAAG,CAAC,CAAC;YAC7E,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,GAC1B,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,GAG3B,MAAM,WAAW,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@se-studio/search",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "AI-powered site search with Upstash Search for Next.js marketing sites",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/Something-Else-Studio/se-core-product",
|
|
8
|
+
"directory": "packages/search"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./client": {
|
|
20
|
+
"types": "./dist/client/index.d.ts",
|
|
21
|
+
"import": "./dist/client/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./indexing": {
|
|
24
|
+
"types": "./dist/indexing/index.d.ts",
|
|
25
|
+
"import": "./dist/indexing/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./webhook": {
|
|
28
|
+
"types": "./dist/webhook/index.d.ts",
|
|
29
|
+
"import": "./dist/webhook/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./api": {
|
|
32
|
+
"types": "./dist/api/index.d.ts",
|
|
33
|
+
"import": "./dist/api/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./hooks": {
|
|
36
|
+
"types": "./dist/hooks/index.d.ts",
|
|
37
|
+
"import": "./dist/hooks/index.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"*.md"
|
|
43
|
+
],
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@upstash/search": "^0.1.7",
|
|
46
|
+
"@se-studio/contentful-rest-api": "1.0.90",
|
|
47
|
+
"@se-studio/core-data-types": "1.0.90",
|
|
48
|
+
"@se-studio/markdown-renderer": "1.0.54"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"next": "^15.0.0",
|
|
52
|
+
"react": "^19.0.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@biomejs/biome": "^2.4.4",
|
|
56
|
+
"@types/node": "^22.19.13",
|
|
57
|
+
"@types/react": "^19.2.14",
|
|
58
|
+
"next": "^15.5.12",
|
|
59
|
+
"typescript": "^5.9.3",
|
|
60
|
+
"vitest": "^4.0.18"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsc --project tsconfig.build.json",
|
|
64
|
+
"dev": "tsc --project tsconfig.build.json --watch",
|
|
65
|
+
"type-check": "tsc --noEmit",
|
|
66
|
+
"lint": "biome lint .",
|
|
67
|
+
"test": "vitest run",
|
|
68
|
+
"clean": "rm -rf dist .turbo *.tsbuildinfo"
|
|
69
|
+
}
|
|
70
|
+
}
|