@gencow/core 0.1.26 → 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 (88) hide show
  1. package/dist/crud.d.ts +12 -0
  2. package/dist/crud.js +16 -0
  3. package/dist/db.d.ts +13 -0
  4. package/dist/db.js +16 -0
  5. package/dist/document-types.d.ts +65 -0
  6. package/dist/document-types.js +15 -0
  7. package/dist/grounded-answer-types.d.ts +62 -0
  8. package/dist/grounded-answer-types.js +6 -0
  9. package/dist/index.d.ts +12 -2
  10. package/dist/index.js +5 -1
  11. package/dist/rag-ingest-types.d.ts +39 -0
  12. package/dist/rag-ingest-types.js +1 -0
  13. package/dist/rag-operations-types.d.ts +81 -0
  14. package/dist/rag-operations-types.js +1 -0
  15. package/dist/rag-schema.d.ts +1557 -0
  16. package/dist/rag-schema.js +87 -0
  17. package/dist/reactive.d.ts +13 -0
  18. package/dist/rls-db.d.ts +9 -2
  19. package/dist/runtime-env-policy.d.ts +5 -0
  20. package/dist/runtime-env-policy.js +56 -0
  21. package/dist/search-types.d.ts +83 -0
  22. package/dist/search-types.js +1 -0
  23. package/dist/server.d.ts +1 -2
  24. package/dist/server.js +0 -1
  25. package/dist/storage-shared.d.ts +36 -0
  26. package/dist/storage-shared.js +39 -0
  27. package/dist/storage.d.ts +2 -26
  28. package/dist/storage.js +19 -15
  29. package/dist/workflow-types.d.ts +3 -1
  30. package/package.json +1 -1
  31. package/src/crud.ts +33 -0
  32. package/src/document-types.ts +95 -0
  33. package/src/grounded-answer-types.ts +78 -0
  34. package/src/index.ts +68 -2
  35. package/src/rag-ingest-types.ts +52 -0
  36. package/src/rag-operations-types.ts +90 -0
  37. package/src/rag-schema.ts +94 -0
  38. package/src/reactive.ts +13 -0
  39. package/src/rls-db.ts +9 -4
  40. package/src/runtime-env-policy.ts +66 -0
  41. package/src/search-types.ts +91 -0
  42. package/src/server.ts +1 -2
  43. package/src/storage-shared.ts +74 -0
  44. package/src/storage.ts +29 -46
  45. package/src/workflow-types.ts +3 -1
  46. package/src/__tests__/auth.test.ts +0 -118
  47. package/src/__tests__/crons.test.ts +0 -83
  48. package/src/__tests__/crud-codegen-integration.test.ts +0 -246
  49. package/src/__tests__/crud-owner-rls.test.ts +0 -387
  50. package/src/__tests__/crud.test.ts +0 -930
  51. package/src/__tests__/dist-exports.test.ts +0 -176
  52. package/src/__tests__/fixtures/basic/auth.ts +0 -32
  53. package/src/__tests__/fixtures/basic/drizzle.config.ts +0 -12
  54. package/src/__tests__/fixtures/basic/index.ts +0 -6
  55. package/src/__tests__/fixtures/basic/migrations/0000_last_warstar.sql +0 -75
  56. package/src/__tests__/fixtures/basic/migrations/meta/0000_snapshot.json +0 -497
  57. package/src/__tests__/fixtures/basic/migrations/meta/_journal.json +0 -13
  58. package/src/__tests__/fixtures/basic/schema.ts +0 -51
  59. package/src/__tests__/fixtures/basic/tasks.ts +0 -15
  60. package/src/__tests__/fixtures/common/auth-schema.ts +0 -67
  61. package/src/__tests__/helpers/basic-rls-fixture.ts +0 -135
  62. package/src/__tests__/helpers/pglite-migrations.ts +0 -32
  63. package/src/__tests__/helpers/pglite-rls-session.ts +0 -51
  64. package/src/__tests__/helpers/seed-like-fill.ts +0 -202
  65. package/src/__tests__/helpers/test-gencow-ctx-rls.ts +0 -50
  66. package/src/__tests__/httpaction.test.ts +0 -122
  67. package/src/__tests__/image-optimization.test.ts +0 -648
  68. package/src/__tests__/load.test.ts +0 -389
  69. package/src/__tests__/network-sim.test.ts +0 -319
  70. package/src/__tests__/reactive.test.ts +0 -479
  71. package/src/__tests__/retry.test.ts +0 -113
  72. package/src/__tests__/rls-crud-basic.test.ts +0 -317
  73. package/src/__tests__/rls-crud-no-owner-rls-pglite.test.ts +0 -117
  74. package/src/__tests__/rls-custom-mutation-handlers.test.ts +0 -142
  75. package/src/__tests__/rls-custom-query-handlers.test.ts +0 -128
  76. package/src/__tests__/rls-db-leased-connection.test.ts +0 -118
  77. package/src/__tests__/rls-session-and-policies.test.ts +0 -228
  78. package/src/__tests__/scheduler-durable-v2.test.ts +0 -288
  79. package/src/__tests__/scheduler-durable.test.ts +0 -173
  80. package/src/__tests__/scheduler-exec.test.ts +0 -328
  81. package/src/__tests__/scheduler.test.ts +0 -187
  82. package/src/__tests__/storage.test.ts +0 -334
  83. package/src/__tests__/tsconfig.json +0 -8
  84. package/src/__tests__/validator.test.ts +0 -323
  85. package/src/__tests__/workflow.test.ts +0 -606
  86. package/src/__tests__/ws-integration.test.ts +0 -309
  87. package/src/__tests__/ws-scale.test.ts +0 -241
  88. 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";
@@ -92,7 +157,8 @@ export { ownerRls, getOwnerRlsMeta, registerOwnerRls } from "./rls.js";
92
157
  export type { OwnerRlsMeta } from "./rls.js";
93
158
  export { createRlsDb } from "./rls-db.js";
94
159
  export type { RlsSessionContext } from "./rls-db.js";
95
- export { crud, parseFilterNode, applyFilterOp, getOwnerRlsTables } from "./crud.js";
160
+ export { crud, parseFilterNode, applyFilterOp, getOwnerRlsTables, getRegisteredCrudCodegenMeta } from "./crud.js";
161
+ export type { CrudCodegenMeta } from "./crud.js";
96
162
 
97
163
  // Deprecated alias — 하위호환용, 향후 메이저 버전에서 제거 예정
98
164
  export { crud as gencowCrud } from "./crud.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";