@gencow/core 0.1.27 → 0.1.28

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 (83) hide show
  1. package/dist/document-types.d.ts +65 -0
  2. package/dist/document-types.js +15 -0
  3. package/dist/grounded-answer-types.d.ts +62 -0
  4. package/dist/grounded-answer-types.js +6 -0
  5. package/dist/index.d.ts +10 -1
  6. package/dist/index.js +4 -0
  7. package/dist/rag-ingest-types.d.ts +39 -0
  8. package/dist/rag-ingest-types.js +1 -0
  9. package/dist/rag-operations-types.d.ts +81 -0
  10. package/dist/rag-operations-types.js +1 -0
  11. package/dist/rag-schema.d.ts +1557 -0
  12. package/dist/rag-schema.js +87 -0
  13. package/dist/reactive.d.ts +13 -0
  14. package/dist/rls-db.d.ts +9 -2
  15. package/dist/runtime-env-policy.d.ts +5 -0
  16. package/dist/runtime-env-policy.js +56 -0
  17. package/dist/search-types.d.ts +83 -0
  18. package/dist/search-types.js +1 -0
  19. package/dist/server.d.ts +1 -2
  20. package/dist/server.js +0 -1
  21. package/dist/storage-shared.d.ts +36 -0
  22. package/dist/storage-shared.js +39 -0
  23. package/dist/storage.d.ts +2 -26
  24. package/dist/storage.js +19 -15
  25. package/dist/workflow-types.d.ts +3 -1
  26. package/package.json +8 -7
  27. package/src/document-types.ts +95 -0
  28. package/src/grounded-answer-types.ts +78 -0
  29. package/src/index.ts +66 -1
  30. package/src/rag-ingest-types.ts +52 -0
  31. package/src/rag-operations-types.ts +90 -0
  32. package/src/rag-schema.ts +94 -0
  33. package/src/reactive.ts +13 -0
  34. package/src/rls-db.ts +9 -4
  35. package/src/runtime-env-policy.ts +66 -0
  36. package/src/search-types.ts +91 -0
  37. package/src/server.ts +1 -2
  38. package/src/storage-shared.ts +74 -0
  39. package/src/storage.ts +29 -46
  40. package/src/workflow-types.ts +3 -1
  41. package/src/__tests__/auth.test.ts +0 -118
  42. package/src/__tests__/crons.test.ts +0 -83
  43. package/src/__tests__/crud-codegen-integration.test.ts +0 -246
  44. package/src/__tests__/crud-owner-rls.test.ts +0 -387
  45. package/src/__tests__/crud.test.ts +0 -930
  46. package/src/__tests__/dist-exports.test.ts +0 -176
  47. package/src/__tests__/fixtures/basic/auth.ts +0 -32
  48. package/src/__tests__/fixtures/basic/drizzle.config.ts +0 -12
  49. package/src/__tests__/fixtures/basic/index.ts +0 -6
  50. package/src/__tests__/fixtures/basic/migrations/0000_last_warstar.sql +0 -75
  51. package/src/__tests__/fixtures/basic/migrations/meta/0000_snapshot.json +0 -497
  52. package/src/__tests__/fixtures/basic/migrations/meta/_journal.json +0 -13
  53. package/src/__tests__/fixtures/basic/schema.ts +0 -51
  54. package/src/__tests__/fixtures/basic/tasks.ts +0 -15
  55. package/src/__tests__/fixtures/common/auth-schema.ts +0 -67
  56. package/src/__tests__/helpers/basic-rls-fixture.ts +0 -135
  57. package/src/__tests__/helpers/pglite-migrations.ts +0 -32
  58. package/src/__tests__/helpers/pglite-rls-session.ts +0 -51
  59. package/src/__tests__/helpers/seed-like-fill.ts +0 -202
  60. package/src/__tests__/helpers/test-gencow-ctx-rls.ts +0 -50
  61. package/src/__tests__/httpaction.test.ts +0 -122
  62. package/src/__tests__/image-optimization.test.ts +0 -648
  63. package/src/__tests__/load.test.ts +0 -389
  64. package/src/__tests__/network-sim.test.ts +0 -319
  65. package/src/__tests__/reactive.test.ts +0 -479
  66. package/src/__tests__/retry.test.ts +0 -113
  67. package/src/__tests__/rls-crud-basic.test.ts +0 -317
  68. package/src/__tests__/rls-crud-no-owner-rls-pglite.test.ts +0 -117
  69. package/src/__tests__/rls-custom-mutation-handlers.test.ts +0 -142
  70. package/src/__tests__/rls-custom-query-handlers.test.ts +0 -128
  71. package/src/__tests__/rls-db-leased-connection.test.ts +0 -118
  72. package/src/__tests__/rls-session-and-policies.test.ts +0 -228
  73. package/src/__tests__/scheduler-durable-v2.test.ts +0 -288
  74. package/src/__tests__/scheduler-durable.test.ts +0 -173
  75. package/src/__tests__/scheduler-exec.test.ts +0 -328
  76. package/src/__tests__/scheduler.test.ts +0 -187
  77. package/src/__tests__/storage.test.ts +0 -334
  78. package/src/__tests__/tsconfig.json +0 -8
  79. package/src/__tests__/validator.test.ts +0 -323
  80. package/src/__tests__/workflow.test.ts +0 -606
  81. package/src/__tests__/ws-integration.test.ts +0 -309
  82. package/src/__tests__/ws-scale.test.ts +0 -241
  83. package/src/auth.ts +0 -155
@@ -0,0 +1,78 @@
1
+ import type { SearchFilter, SearchScope } from "./search-types.js";
2
+
3
+ export type CitationCoverage = "direct" | "partial" | "context";
4
+ export type ClaimSupportStatus = "supported" | "partially_supported" | "insufficient_evidence";
5
+ export type GroundedAnswerMode = "qa" | "compare" | "topic";
6
+
7
+ export type Citation = {
8
+ sourceId: string;
9
+ sourceTitle: string;
10
+ sectionId?: string;
11
+ sectionPath?: string[];
12
+ pageStart?: number;
13
+ pageEnd?: number;
14
+ chunkIds: string[];
15
+ snippet: string;
16
+ coverage: CitationCoverage;
17
+ confidence: number;
18
+ };
19
+
20
+ export type ClaimEvidenceMap = {
21
+ claimId: string;
22
+ claimText: string;
23
+ status: ClaimSupportStatus;
24
+ citations: Citation[];
25
+ missingEvidenceReasons?: string[];
26
+ };
27
+
28
+ export type GroundedAnswer = {
29
+ answer: string;
30
+ claims: ClaimEvidenceMap[];
31
+ citations: Citation[];
32
+ warnings: string[];
33
+ grounded: boolean;
34
+ };
35
+
36
+ export type GroundingBudget = {
37
+ maxVerifyLoops: number;
38
+ maxResearchQueriesPerLoop: number;
39
+ maxCitationsPerClaim: number;
40
+ maxClaimsPerAnswer: number;
41
+ };
42
+
43
+ export const DEFAULT_GROUNDING_BUDGET: GroundingBudget = {
44
+ maxVerifyLoops: 2,
45
+ maxResearchQueriesPerLoop: 3,
46
+ maxCitationsPerClaim: 3,
47
+ maxClaimsPerAnswer: 12,
48
+ };
49
+
50
+ export type GroundedClaimInput = {
51
+ claimId?: string;
52
+ claimText: string;
53
+ requiredTerms?: string[];
54
+ };
55
+
56
+ export type GroundedCompareInput = {
57
+ left: string;
58
+ right: string;
59
+ };
60
+
61
+ export type GroundedTopicInput = {
62
+ minDistinctSections?: number;
63
+ };
64
+
65
+ export type GroundedAnswerInput = {
66
+ question: string;
67
+ scope: SearchScope;
68
+ filters?: SearchFilter;
69
+ claims?: GroundedClaimInput[];
70
+ mode?: GroundedAnswerMode;
71
+ compare?: GroundedCompareInput;
72
+ topic?: GroundedTopicInput;
73
+ budget?: Partial<GroundingBudget>;
74
+ };
75
+
76
+ export type GroundingRuntime = {
77
+ answer(input: GroundedAnswerInput): Promise<GroundedAnswer>;
78
+ };
package/src/index.ts CHANGED
@@ -20,6 +20,70 @@ export type {
20
20
  AIMessage,
21
21
  AIResult,
22
22
  } from "./reactive.js";
23
+ export type {
24
+ SearchPrimitive,
25
+ SearchScope,
26
+ SearchFilter,
27
+ SearchOptions,
28
+ VectorSearchOptions,
29
+ HybridSearchOptions,
30
+ SearchHit,
31
+ SearchResponse,
32
+ SearchTierConfig,
33
+ } from "./search-types.js";
34
+ export type {
35
+ DocumentVisibility,
36
+ DocumentConvertMode,
37
+ DocumentConvertProvider,
38
+ DocumentResolvedProvider,
39
+ DocumentConvertInput,
40
+ DocumentPage,
41
+ DocumentSection,
42
+ DocumentProviderTrace,
43
+ DocumentConvertResult,
44
+ DocumentCacheArtifact,
45
+ DocumentCacheKeyInput,
46
+ GencowServicesCtx,
47
+ WorkflowDocumentServicesCtx,
48
+ } from "./document-types.js";
49
+ export { buildDocumentCacheKey } from "./document-types.js";
50
+ export type {
51
+ RagIngestReindexMode,
52
+ RagIngestInput,
53
+ RagIngestWorkflowArgs,
54
+ RagIngestStartResult,
55
+ RagIngestJobStatus,
56
+ RagIngestJobRecord,
57
+ } from "./rag-ingest-types.js";
58
+ export type {
59
+ CitationCoverage,
60
+ ClaimSupportStatus,
61
+ GroundedAnswerMode,
62
+ Citation,
63
+ ClaimEvidenceMap,
64
+ GroundedAnswer,
65
+ GroundingBudget,
66
+ GroundedClaimInput,
67
+ GroundedCompareInput,
68
+ GroundedTopicInput,
69
+ GroundedAnswerInput,
70
+ GroundingRuntime,
71
+ } from "./grounded-answer-types.js";
72
+ export { DEFAULT_GROUNDING_BUDGET } from "./grounded-answer-types.js";
73
+ export type {
74
+ RagOperationKind,
75
+ RagOperationMetricUnit,
76
+ RagIndexHealth,
77
+ RagOperationMetric,
78
+ RagOperationsSummary,
79
+ RagEvaluationExpectedClaim,
80
+ RagEvaluationFixture,
81
+ RagEvaluationFixtureResult,
82
+ RagEvaluationRunResult,
83
+ RagReindexMode,
84
+ RagReindexPlan,
85
+ } from "./rag-operations-types.js";
86
+ export { ragCorpora, ragSources, ragSections, ragChunks, ragIngestJobs, ragOperationMetrics } from "./rag-schema.js";
23
87
  export {
24
88
  query,
25
89
  mutation,
@@ -36,7 +100,7 @@ export {
36
100
  getRegisteredMutations,
37
101
  getRegisteredHttpActions,
38
102
  } from "./reactive.js";
39
- export type { Storage } from "./storage.js";
103
+ export type { Storage, StoredFile } from "./storage.js";
40
104
  export { createScheduler, getSchedulerInfo } from "./scheduler.js";
41
105
  export type {
42
106
  Scheduler,
@@ -82,6 +146,7 @@ export { v, parseArgs, GencowValidationError } from "./v.js";
82
146
  export type { Validator, Infer, InferArgs } from "./v.js";
83
147
  export { withRetry } from "./retry.js";
84
148
  export type { RetryOptions } from "./retry.js";
149
+ export { filterTenantRuntimeEnvVars, isReservedTenantRuntimeEnvKey } from "./runtime-env-policy.js";
85
150
  export { cronJobs } from "./crons.js";
86
151
  export type { CronJobsBuilder, CronJobDef, IntervalOptions, DailyOptions, WeeklyOptions } from "./crons.js";
87
152
  export { defineAuth } from "./auth-config.js";
@@ -0,0 +1,52 @@
1
+ import type { DocumentConvertProvider, DocumentConvertMode, DocumentProviderTrace, DocumentVisibility } from "./document-types.js";
2
+
3
+ export type RagIngestReindexMode = "if_changed" | "force";
4
+ export type RagIngestJobStatus =
5
+ | "queued"
6
+ | "converting"
7
+ | "chunking"
8
+ | "embedding"
9
+ | "upserting"
10
+ | "completed"
11
+ | "failed"
12
+ | "canceled";
13
+
14
+ export type RagIngestInput = {
15
+ storageId: string;
16
+ corpus: string;
17
+ visibility: DocumentVisibility;
18
+ ownerUserId?: string;
19
+ sourceKey?: string;
20
+ metadata?: Record<string, unknown>;
21
+ reindexMode?: RagIngestReindexMode;
22
+ mode?: DocumentConvertMode;
23
+ provider?: DocumentConvertProvider;
24
+ };
25
+
26
+ export type RagIngestWorkflowArgs = RagIngestInput & {
27
+ jobId: string;
28
+ sourceId: string;
29
+ };
30
+
31
+ export type RagIngestStartResult = {
32
+ workflowId: string;
33
+ jobId: string;
34
+ sourceId: string;
35
+ status: "queued";
36
+ };
37
+
38
+ export type RagIngestJobRecord = {
39
+ id: string;
40
+ workflowId: string;
41
+ sourceId: string;
42
+ corpus: string;
43
+ visibility: DocumentVisibility;
44
+ ownerUserId: string | null;
45
+ status: RagIngestJobStatus;
46
+ stage: string;
47
+ providerTrace: Partial<DocumentProviderTrace> | Record<string, never>;
48
+ metrics: Record<string, unknown>;
49
+ error: string | null;
50
+ startedAt: string;
51
+ completedAt: string | null;
52
+ };
@@ -0,0 +1,90 @@
1
+ import type { SearchScope } from "./search-types.js";
2
+
3
+ export type RagOperationKind = "ingest" | "retrieve" | "answer" | "reindex" | "evaluate";
4
+ export type RagOperationMetricUnit = "ms" | "count" | "tokens" | "usd";
5
+ export type RagIndexHealth = "ready" | "empty" | "degraded" | "failed";
6
+
7
+ export type RagOperationMetric = {
8
+ id: string;
9
+ appId: string;
10
+ corpus: string;
11
+ visibility: SearchScope["visibility"];
12
+ ownerUserId: string | null;
13
+ operation: RagOperationKind;
14
+ jobId: string | null;
15
+ workflowId: string | null;
16
+ sourceId: string | null;
17
+ metricName: string;
18
+ metricValue: number;
19
+ unit: RagOperationMetricUnit | null;
20
+ metadata: Record<string, unknown>;
21
+ recordedAt: string;
22
+ };
23
+
24
+ export type RagOperationsSummary = {
25
+ corpus: string;
26
+ visibility: SearchScope["visibility"];
27
+ ownerUserId: string | null;
28
+ sourceCount: number;
29
+ sectionCount: number;
30
+ chunkCount: number;
31
+ jobCounts: Record<string, number>;
32
+ latestJob: {
33
+ id: string;
34
+ status: string;
35
+ stage: string;
36
+ updatedAt: string;
37
+ } | null;
38
+ recentMetricCounts: Record<string, number>;
39
+ averageLatencyMs: number | null;
40
+ indexHealth: RagIndexHealth;
41
+ };
42
+
43
+ export type RagEvaluationExpectedClaim = {
44
+ claim: string;
45
+ verdict: "supported" | "partial" | "unsupported";
46
+ };
47
+
48
+ export type RagEvaluationFixture = {
49
+ name: string;
50
+ scope: SearchScope;
51
+ query: string;
52
+ expectedSourceIds?: string[];
53
+ expectedCitationCountMin?: number;
54
+ expectedClaims?: RagEvaluationExpectedClaim[];
55
+ };
56
+
57
+ export type RagEvaluationFixtureResult = {
58
+ name: string;
59
+ ok: boolean;
60
+ failures: string[];
61
+ matchedSourceIds: string[];
62
+ citationCount: number;
63
+ claimStatuses: Array<{
64
+ claim: string;
65
+ expected: RagEvaluationExpectedClaim["verdict"];
66
+ actual: string | null;
67
+ }>;
68
+ };
69
+
70
+ export type RagEvaluationRunResult = {
71
+ ok: boolean;
72
+ total: number;
73
+ passed: number;
74
+ failed: number;
75
+ results: RagEvaluationFixtureResult[];
76
+ };
77
+
78
+ export type RagReindexMode = "source-changed" | "section-changed" | "corpus-policy-changed" | "full-rebuild";
79
+
80
+ export type RagReindexPlan = {
81
+ corpus: string;
82
+ visibility: SearchScope["visibility"];
83
+ ownerUserId: string | null;
84
+ mode: RagReindexMode;
85
+ reason: string;
86
+ sourceIds: string[];
87
+ sourceCount: number;
88
+ estimatedChunkCount: number;
89
+ requiresConfirmation: boolean;
90
+ };
@@ -0,0 +1,94 @@
1
+ import { bigint, boolean, doublePrecision, integer, jsonb, pgTable, text, timestamp, vector } from "drizzle-orm/pg-core";
2
+
3
+ function buildScopeColumns() {
4
+ return {
5
+ id: text("id").primaryKey(),
6
+ corpus: text("corpus").notNull(),
7
+ visibilityScope: text("visibility_scope").notNull(),
8
+ ownerUserId: text("owner_user_id"),
9
+ createdAt: timestamp("created_at").defaultNow().notNull(),
10
+ updatedAt: timestamp("updated_at").defaultNow().notNull(),
11
+ };
12
+ }
13
+
14
+ export const ragCorpora = pgTable("rag_corpora", {
15
+ ...buildScopeColumns(),
16
+ title: text("title").notNull(),
17
+ description: text("description"),
18
+ allowExternalProviders: boolean("allow_external_providers").default(false).notNull(),
19
+ retentionDays: integer("retention_days"),
20
+ metadata: jsonb("metadata").default({}).notNull(),
21
+ });
22
+
23
+ export const ragSources = pgTable("rag_sources", {
24
+ ...buildScopeColumns(),
25
+ storageId: text("storage_id").notNull(),
26
+ sourceKey: text("source_key").notNull(),
27
+ sourceTitle: text("source_title").notNull(),
28
+ mimeType: text("mime_type").notNull(),
29
+ byteSize: bigint("byte_size", { mode: "number" }).notNull(),
30
+ sourceChecksum: text("source_checksum").notNull(),
31
+ convertProvider: text("convert_provider").notNull(),
32
+ convertStatus: text("convert_status").notNull(),
33
+ markdown: text("markdown").notNull(),
34
+ text: text("text").notNull(),
35
+ warnings: jsonb("warnings").default([]).notNull(),
36
+ metadata: jsonb("metadata").default({}).notNull(),
37
+ });
38
+
39
+ export const ragSections = pgTable("rag_sections", {
40
+ ...buildScopeColumns(),
41
+ sourceId: text("source_id").notNull(),
42
+ sectionIndex: integer("section_index").notNull(),
43
+ sectionPath: jsonb("section_path").default([]).notNull(),
44
+ title: text("title"),
45
+ depth: integer("depth").notNull(),
46
+ pageStart: integer("page_start"),
47
+ pageEnd: integer("page_end"),
48
+ charStart: integer("char_start").notNull(),
49
+ charEnd: integer("char_end").notNull(),
50
+ });
51
+
52
+ export const ragChunks = pgTable("rag_chunks", {
53
+ ...buildScopeColumns(),
54
+ sourceId: text("source_id").notNull(),
55
+ sectionId: text("section_id"),
56
+ chunkIndex: integer("chunk_index").notNull(),
57
+ chunkText: text("chunk_text").notNull(),
58
+ lexicalText: text("lexical_text").notNull(),
59
+ embedding: vector("embedding", { dimensions: 1536 }),
60
+ pageStart: integer("page_start"),
61
+ pageEnd: integer("page_end"),
62
+ chunkChecksum: text("chunk_checksum").notNull(),
63
+ metadata: jsonb("metadata").default({}).notNull(),
64
+ });
65
+
66
+ export const ragIngestJobs = pgTable("rag_ingest_jobs", {
67
+ ...buildScopeColumns(),
68
+ workflowId: text("workflow_id").notNull(),
69
+ sourceId: text("source_id").notNull(),
70
+ status: text("status").notNull(),
71
+ stage: text("stage").notNull(),
72
+ providerTrace: jsonb("provider_trace").default({}).notNull(),
73
+ metrics: jsonb("metrics").default({}).notNull(),
74
+ error: text("error"),
75
+ startedAt: timestamp("started_at").defaultNow().notNull(),
76
+ completedAt: timestamp("completed_at"),
77
+ });
78
+
79
+ export const ragOperationMetrics = pgTable("rag_operation_metrics", {
80
+ id: text("id").primaryKey(),
81
+ appId: text("app_id").notNull(),
82
+ corpus: text("corpus").notNull(),
83
+ visibilityScope: text("visibility_scope").notNull(),
84
+ ownerUserId: text("owner_user_id"),
85
+ operation: text("operation").notNull(),
86
+ jobId: text("job_id"),
87
+ workflowId: text("workflow_id"),
88
+ sourceId: text("source_id"),
89
+ metricName: text("metric_name").notNull(),
90
+ metricValue: doublePrecision("metric_value").notNull(),
91
+ unit: text("unit"),
92
+ metadata: jsonb("metadata").default({}).notNull(),
93
+ recordedAt: timestamp("recorded_at").defaultNow().notNull(),
94
+ });
package/src/reactive.ts CHANGED
@@ -2,6 +2,9 @@ import type { WSContext } from "hono/ws";
2
2
  import type { Storage } from "./storage.js";
3
3
  import type { Scheduler } from "./scheduler.js";
4
4
  import { type Validator, type InferArgs } from "./v.js";
5
+ import type { HybridSearchOptions, SearchOptions, SearchResponse, VectorSearchOptions } from "./search-types.js";
6
+ import type { GencowServicesCtx } from "./document-types.js";
7
+ import type { GroundingRuntime } from "./grounded-answer-types.js";
5
8
 
6
9
  // ─── GencowCtx — 사용자 함수에 주입되는 컨텍스트 ──────────
7
10
 
@@ -107,8 +110,18 @@ export interface GencowCtx {
107
110
  realtime: RealtimeCtx;
108
111
  /** 재시도 — ctx.retry(fn, opts) — exponential backoff + jitter */
109
112
  retry: <T>(fn: () => Promise<T>, options?: import("./retry.js").RetryOptions) => Promise<T>;
113
+ /** 프레임워크 서비스 헬퍼 — workflow 전용 service는 별도 ctx에서 노출 */
114
+ services: GencowServicesCtx;
110
115
  /** AI 헬퍼 */
111
116
  ai?: AIContext;
117
+ /** Full-text / hybrid search helper */
118
+ search: (table: string, query: string, options: SearchOptions) => Promise<SearchResponse>;
119
+ /** Vector / semantic search helper */
120
+ vectorSearch: (table: string, options: VectorSearchOptions) => Promise<SearchResponse>;
121
+ /** Hybrid search helper (lexical + vector) */
122
+ hybridSearch: (table: string, query: string, options: HybridSearchOptions) => Promise<SearchResponse>;
123
+ /** Grounded answer helper over canonical rag_* tables */
124
+ grounding?: GroundingRuntime;
112
125
  }
113
126
 
114
127
  // ─── Types ──────────────────────────────────────────────
package/src/rls-db.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { sql } from "drizzle-orm";
3
- import type { PgAsyncDatabase } from "drizzle-orm/pg-core";
4
3
 
5
4
  /**
6
5
  * RLS DB wrapper — execution paths for `withRlsConnection`:
@@ -33,6 +32,12 @@ const gucNameRe = /^app\.[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*$/;
33
32
 
34
33
  const RESERVED_VARS_KEYS = new Set(["app.current_user_id", "app.current_user_role", "app.tenant_id"]);
35
34
 
35
+ type RlsDrizzleDatabaseLike = {
36
+ session: unknown;
37
+ _: { session?: unknown };
38
+ transaction: (callback: (tx: unknown) => unknown | Promise<unknown>, ...rest: unknown[]) => Promise<unknown>;
39
+ };
40
+
36
41
  function assertSafeGucName(key: string): void {
37
42
  if (!gucNameRe.test(key)) {
38
43
  throw new Error(
@@ -244,10 +249,10 @@ function wrapSession(session: any, rls: RlsSessionContext, reuseOuterConnection:
244
249
  *
245
250
  * `db.transaction()` still injects the same variables at the start of the callback transaction.
246
251
  */
247
- export function createRlsDb(
248
- db: PgAsyncDatabase<any, any, any, any>,
252
+ export function createRlsDb<TDb extends RlsDrizzleDatabaseLike>(
253
+ db: TDb,
249
254
  rls: RlsSessionContext,
250
- ): PgAsyncDatabase<any, any, any, any> {
255
+ ): TDb {
251
256
  const reuseOuterConnection = isDrizzleTransactionDb(db);
252
257
  const baseSession = (db as unknown as { session: any }).session;
253
258
  const wrappedSession = wrapSession(baseSession, rls, reuseOuterConnection);
@@ -0,0 +1,66 @@
1
+ const RESERVED_TENANT_RUNTIME_ENV_KEYS = new Set([
2
+ "PORT",
3
+ "DATABASE_URL",
4
+ "GENCOW_DB_URL",
5
+ "BETTER_AUTH_SECRET",
6
+ "BETTER_AUTH_URL",
7
+ "IS_PLATFORM",
8
+ "GENCOW_PLATFORM_CONFIG_FILE",
9
+ "GENCOW_PLATFORM_URL",
10
+ "GENCOW_PLATFORM_DB",
11
+ "PLATFORM_OPENAI_KEY",
12
+ "PLATFORM_GOOGLE_KEY",
13
+ "PLATFORM_INTERNAL_SECRET",
14
+ "INVITE_ONLY",
15
+ "RUNNER_TYPE",
16
+ "PGBOUNCER_PORT",
17
+ "GENCOW_FUNCTIONS",
18
+ "GENCOW_STORAGE",
19
+ "GENCOW_MIGRATIONS",
20
+ "GENCOW_APP_NAME",
21
+ "GENCOW_APP_DATA_DIR",
22
+ "GENCOW_INTERNAL_TOKEN",
23
+ "GENCOW_CRON_TOKEN",
24
+ "GENCOW_AI_PROXY_URL",
25
+ "GENCOW_AI_PROXY_URL_ALT",
26
+ "GENCOW_AI_PROXY_TOKEN",
27
+ "GENCOW_METERING_URL",
28
+ "GENCOW_METERING_URL_ALT",
29
+ "GENCOW_START_REASON",
30
+ "GENCOW_RESTART_ID",
31
+ "GENCOW_SHUTDOWN_MARKER_PATH",
32
+ "GENCOW_SKIP_MIGRATION",
33
+ "GENCOW_DB_MAX_CONNECTIONS",
34
+ "GENCOW_MEMORY_MB",
35
+ "BUN_JSC_forceRAMSize",
36
+ "MIMALLOC_PURGE_DELAY",
37
+ "NODE_PATH",
38
+ ]);
39
+
40
+ const RESERVED_TENANT_RUNTIME_ENV_PREFIXES = ["__GENCOW_", "GENCOW_DOCUMENT_", "GENCOW_WARM_"];
41
+
42
+ export function isReservedTenantRuntimeEnvKey(key: string): boolean {
43
+ const normalized = key.trim();
44
+ return (
45
+ RESERVED_TENANT_RUNTIME_ENV_KEYS.has(normalized) ||
46
+ RESERVED_TENANT_RUNTIME_ENV_PREFIXES.some((prefix) => normalized.startsWith(prefix))
47
+ );
48
+ }
49
+
50
+ export function filterTenantRuntimeEnvVars(vars: Record<string, string>): {
51
+ allowed: Record<string, string>;
52
+ rejectedKeys: string[];
53
+ } {
54
+ const allowed: Record<string, string> = {};
55
+ const rejectedKeys: string[] = [];
56
+
57
+ for (const [key, value] of Object.entries(vars)) {
58
+ if (isReservedTenantRuntimeEnvKey(key)) {
59
+ rejectedKeys.push(key);
60
+ continue;
61
+ }
62
+ allowed[key] = value;
63
+ }
64
+
65
+ return { allowed, rejectedKeys };
66
+ }
@@ -0,0 +1,91 @@
1
+ export type SearchPrimitive = string | number | boolean;
2
+
3
+ export type SearchScope = {
4
+ corpus: string;
5
+ visibility: "private" | "shared" | "public";
6
+ ownerUserId?: string;
7
+ };
8
+
9
+ export type SearchFilter = {
10
+ eq?: Record<string, SearchPrimitive>;
11
+ in?: Record<string, SearchPrimitive[]>;
12
+ range?: Record<string, { gte?: string | number; lte?: string | number }>;
13
+ };
14
+
15
+ export type SearchOptions = {
16
+ fields: [string, ...string[]];
17
+ limit?: number;
18
+ offset?: number;
19
+ filters?: SearchFilter;
20
+ scope: SearchScope;
21
+ };
22
+
23
+ export type VectorSearchTuning = {
24
+ minScore?: number;
25
+ };
26
+
27
+ export type VectorSearchOptions = Omit<SearchOptions, "fields"> & {
28
+ vector: number[];
29
+ vectorField?: string;
30
+ tuning?: VectorSearchTuning;
31
+ };
32
+
33
+ export type HybridSearchFusionTuning = {
34
+ mode?: "rrf";
35
+ rrfK?: number;
36
+ keywordWeight?: number;
37
+ vectorWeight?: number;
38
+ };
39
+
40
+ export type HybridSearchTuning = {
41
+ keywordCandidateLimit?: number;
42
+ vectorCandidateLimit?: number;
43
+ minFusedScore?: number;
44
+ fusion?: HybridSearchFusionTuning;
45
+ };
46
+
47
+ export type HybridSearchOptions = SearchOptions & {
48
+ vector: number[];
49
+ vectorField?: string;
50
+ fusion?: "rrf"; // legacy top-level compatibility
51
+ keywordCandidateLimit?: number; // legacy top-level compatibility
52
+ vectorCandidateLimit?: number; // legacy top-level compatibility
53
+ tuning?: HybridSearchTuning;
54
+ };
55
+
56
+ export type SearchHit = {
57
+ id: string | number;
58
+ row: Record<string, unknown>;
59
+ score: number;
60
+ scores?: {
61
+ keyword?: number;
62
+ vector?: number;
63
+ fused?: number;
64
+ };
65
+ matchedBy: Array<"keyword" | "vector">;
66
+ };
67
+
68
+ export type SearchResponse = {
69
+ items: SearchHit[];
70
+ meta: {
71
+ engine: "tsvector" | "pgroonga";
72
+ tier: "free" | "pro" | "scale";
73
+ limit: number;
74
+ offset: number;
75
+ nextOffset?: number;
76
+ fusion?: "rrf";
77
+ mode?: "keyword" | "vector" | "hybrid";
78
+ };
79
+ };
80
+
81
+ export type SearchTierConfig = {
82
+ plan: "free" | "pro" | "scale";
83
+ engine: "tsvector" | "pgroonga";
84
+ hybridSearch: boolean;
85
+ locale: "english" | "multilingual";
86
+ extensions: {
87
+ vector: boolean;
88
+ pgroonga: boolean;
89
+ };
90
+ degradedReason?: string;
91
+ };
package/src/server.ts CHANGED
@@ -6,6 +6,5 @@
6
6
  * bundled into user functions which run in Firecracker.
7
7
  */
8
8
  export { createStorage, storageRoutes } from "./storage.js";
9
- export type { StorageImageTierConfig } from "./storage.js";
9
+ export type { StorageImageTierConfig, StoredFile } from "./storage.js";
10
10
  export { createScheduler, getSchedulerInfo } from "./scheduler.js";
11
- export { authMiddleware, authRoutes, getUsers } from "./auth.js";