@elevasis/core 0.18.0 → 0.19.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.
- package/dist/index.d.ts +82 -1
- package/dist/index.js +291 -171
- package/dist/knowledge/index.d.ts +43 -0
- package/dist/organization-model/index.d.ts +82 -1
- package/dist/organization-model/index.js +291 -171
- package/dist/test-utils/index.d.ts +41 -12
- package/dist/test-utils/index.js +291 -171
- package/package.json +2 -1
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +78 -65
- package/src/auth/multi-tenancy/organizations/__tests__/api-schemas.test.ts +194 -0
- package/src/auth/multi-tenancy/organizations/api-schemas.ts +136 -128
- package/src/business/acquisition/api-schemas.test.ts +100 -2
- package/src/business/acquisition/api-schemas.ts +81 -43
- package/src/business/acquisition/build-templates.test.ts +212 -0
- package/src/business/acquisition/types.ts +21 -38
- package/src/execution/engine/index.ts +436 -434
- package/src/execution/engine/tools/integration/server/adapters/google-calendar/google-calendar-adapter.ts +428 -0
- package/src/execution/engine/tools/integration/server/adapters/google-calendar/index.ts +2 -0
- package/src/execution/engine/tools/lead-service-types.ts +51 -9
- package/src/execution/engine/tools/platform/acquisition/company-tools.ts +7 -6
- package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +6 -5
- package/src/execution/engine/tools/platform/acquisition/types.ts +20 -9
- package/src/execution/engine/tools/registry.ts +700 -698
- package/src/execution/engine/tools/tool-maps.ts +10 -0
- package/src/execution/external/__tests__/api-schemas.test.ts +127 -0
- package/src/integrations/oauth/__tests__/provider-registry.test.ts +7 -6
- package/src/integrations/oauth/provider-registry.ts +74 -61
- package/src/integrations/oauth/server/credentials.ts +43 -39
- package/src/knowledge/__tests__/queries.test.ts +89 -0
- package/src/organization-model/__tests__/icons.test.ts +61 -0
- package/src/organization-model/__tests__/knowledge.test.ts +118 -1
- package/src/organization-model/__tests__/prospecting-ssot.test.ts +94 -0
- package/src/organization-model/defaults.ts +8 -0
- package/src/organization-model/domains/knowledge.ts +9 -0
- package/src/organization-model/domains/prospecting.ts +272 -226
- package/src/organization-model/domains/sales.ts +32 -25
- package/src/organization-model/icons.ts +3 -0
- package/src/organization-model/types.ts +9 -1
- package/src/platform/constants/versions.ts +1 -1
- package/src/platform/utils/__tests__/validation.test.ts +1084 -1083
- package/src/platform/utils/validation.ts +425 -425
- package/src/reference/_generated/contracts.md +78 -65
- package/src/server.ts +6 -0
- package/src/supabase/database.types.ts +6 -12
|
@@ -195,6 +195,18 @@ export type OrgKnowledgeNode = z.infer<typeof OrgKnowledgeNodeSchema>
|
|
|
195
195
|
export type OrgKnowledgeKind = z.infer<typeof OrgKnowledgeKindSchema>
|
|
196
196
|
```
|
|
197
197
|
|
|
198
|
+
### `KnowledgeSkillBinding`
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
export type KnowledgeSkillBinding = z.infer<typeof KnowledgeSkillBindingSchema>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### `KnowledgeDomainBinding`
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
export type KnowledgeDomainBinding = z.infer<typeof KnowledgeDomainBindingSchema>
|
|
208
|
+
```
|
|
209
|
+
|
|
198
210
|
### `OrganizationModelIconToken`
|
|
199
211
|
|
|
200
212
|
```typescript
|
|
@@ -866,7 +878,9 @@ export interface AcqCompany {
|
|
|
866
878
|
category: string | null
|
|
867
879
|
categoryPain: string | null
|
|
868
880
|
segment: string | null
|
|
869
|
-
|
|
881
|
+
processingState: CompanyProcessingState | null
|
|
882
|
+
/** @deprecated Use `processingState`. This legacy DB column has been removed; responses return null. */
|
|
883
|
+
pipelineStatus?: LegacyPipelineStatus | null
|
|
870
884
|
enrichmentData: CompanyEnrichmentData | null
|
|
871
885
|
source: string | null
|
|
872
886
|
batchId: string | null
|
|
@@ -906,7 +920,9 @@ export interface AcqContact {
|
|
|
906
920
|
openingLine: string | null
|
|
907
921
|
source: string | null
|
|
908
922
|
sourceId: string | null
|
|
909
|
-
|
|
923
|
+
processingState: ContactProcessingState | null
|
|
924
|
+
/** @deprecated Use `processingState`. This legacy DB column has been removed; responses return null. */
|
|
925
|
+
pipelineStatus?: LegacyPipelineStatus | null
|
|
910
926
|
enrichmentData: ContactEnrichmentData | null
|
|
911
927
|
/** Attio Person record ID - set when contact responds and is added to CRM */
|
|
912
928
|
attioPersonId: string | null
|
|
@@ -969,7 +985,7 @@ export interface DealContact {
|
|
|
969
985
|
title: string | null
|
|
970
986
|
headline: string | null
|
|
971
987
|
linkedin_url: string | null
|
|
972
|
-
|
|
988
|
+
processing_state: Record<string, unknown> | null
|
|
973
989
|
enrichment_data: Record<string, unknown> | null
|
|
974
990
|
company: {
|
|
975
991
|
id: string
|
|
@@ -1242,7 +1258,7 @@ export const DealContactSummarySchema = z.object({
|
|
|
1242
1258
|
title: z.string().nullable(),
|
|
1243
1259
|
headline: z.string().nullable(),
|
|
1244
1260
|
linkedin_url: z.string().nullable(),
|
|
1245
|
-
|
|
1261
|
+
processing_state: ProcessingStateSchema.nullable(),
|
|
1246
1262
|
enrichment_data: z.record(z.string(), z.unknown()).nullable(),
|
|
1247
1263
|
company: z
|
|
1248
1264
|
.object({
|
|
@@ -1570,51 +1586,6 @@ export interface WebPost {
|
|
|
1570
1586
|
}
|
|
1571
1587
|
```
|
|
1572
1588
|
|
|
1573
|
-
### `CompanyPipelineStatus`
|
|
1574
|
-
|
|
1575
|
-
```typescript
|
|
1576
|
-
/**
|
|
1577
|
-
* Tracks pipeline status for a company across all processing stages.
|
|
1578
|
-
*/
|
|
1579
|
-
export interface CompanyPipelineStatus {
|
|
1580
|
-
acquired: boolean
|
|
1581
|
-
enrichment: {
|
|
1582
|
-
[source: string]: {
|
|
1583
|
-
status: 'pending' | 'complete' | 'failed' | 'skipped'
|
|
1584
|
-
completedAt?: string
|
|
1585
|
-
error?: string
|
|
1586
|
-
}
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
```
|
|
1590
|
-
|
|
1591
|
-
### `ContactPipelineStatus`
|
|
1592
|
-
|
|
1593
|
-
```typescript
|
|
1594
|
-
/**
|
|
1595
|
-
* Tracks pipeline status for a contact across all processing stages.
|
|
1596
|
-
*/
|
|
1597
|
-
export interface ContactPipelineStatus {
|
|
1598
|
-
enrichment: {
|
|
1599
|
-
[source: string]: {
|
|
1600
|
-
status: 'pending' | 'complete' | 'failed' | 'skipped'
|
|
1601
|
-
completedAt?: string
|
|
1602
|
-
error?: string
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
personalization: {
|
|
1606
|
-
status: 'pending' | 'complete' | 'failed' | 'skipped'
|
|
1607
|
-
completedAt?: string
|
|
1608
|
-
}
|
|
1609
|
-
outreach: {
|
|
1610
|
-
status: 'pending' | 'sent' | 'replied' | 'bounced' | 'opted-out'
|
|
1611
|
-
sentAt?: string
|
|
1612
|
-
channel?: string
|
|
1613
|
-
campaignId?: string
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
```
|
|
1617
|
-
|
|
1618
1589
|
### `CompanyEnrichmentData`
|
|
1619
1590
|
|
|
1620
1591
|
```typescript
|
|
@@ -1730,7 +1701,9 @@ export interface AcqCompany {
|
|
|
1730
1701
|
category: string | null
|
|
1731
1702
|
categoryPain: string | null
|
|
1732
1703
|
segment: string | null
|
|
1733
|
-
|
|
1704
|
+
processingState: CompanyProcessingState | null
|
|
1705
|
+
/** @deprecated Use `processingState`. This legacy DB column has been removed; responses return null. */
|
|
1706
|
+
pipelineStatus?: LegacyPipelineStatus | null
|
|
1734
1707
|
enrichmentData: CompanyEnrichmentData | null
|
|
1735
1708
|
source: string | null
|
|
1736
1709
|
batchId: string | null
|
|
@@ -1770,7 +1743,9 @@ export interface AcqContact {
|
|
|
1770
1743
|
openingLine: string | null
|
|
1771
1744
|
source: string | null
|
|
1772
1745
|
sourceId: string | null
|
|
1773
|
-
|
|
1746
|
+
processingState: ContactProcessingState | null
|
|
1747
|
+
/** @deprecated Use `processingState`. This legacy DB column has been removed; responses return null. */
|
|
1748
|
+
pipelineStatus?: LegacyPipelineStatus | null
|
|
1774
1749
|
enrichmentData: ContactEnrichmentData | null
|
|
1775
1750
|
/** Attio Person record ID - set when contact responds and is added to CRM */
|
|
1776
1751
|
attioPersonId: string | null
|
|
@@ -2081,6 +2056,7 @@ export const ListCompaniesQuerySchema = z
|
|
|
2081
2056
|
website: z.string().trim().min(1).max(2048).optional(),
|
|
2082
2057
|
segment: z.string().trim().min(1).max(255).optional(),
|
|
2083
2058
|
category: z.string().trim().min(1).max(255).optional(),
|
|
2059
|
+
pipelineStatus: z.unknown().optional(),
|
|
2084
2060
|
batchId: z.string().trim().min(1).max(255).optional(),
|
|
2085
2061
|
status: AcqCompanyStatusSchema.optional(),
|
|
2086
2062
|
includeAll: QueryBooleanSchema.optional(),
|
|
@@ -2122,6 +2098,7 @@ export const CreateCompanyRequestSchema = z
|
|
|
2122
2098
|
category: z.string().trim().min(1).max(255).optional(),
|
|
2123
2099
|
source: z.string().trim().min(1).max(255).optional(),
|
|
2124
2100
|
batchId: z.string().trim().min(1).max(255).optional(),
|
|
2101
|
+
pipelineStatus: z.unknown().optional(),
|
|
2125
2102
|
verticalResearch: z.string().trim().min(1).max(5000).optional()
|
|
2126
2103
|
})
|
|
2127
2104
|
.strict()
|
|
@@ -2142,7 +2119,8 @@ export const UpdateCompanyRequestSchema = z
|
|
|
2142
2119
|
locationState: z.string().trim().min(1).max(255).optional(),
|
|
2143
2120
|
category: z.string().trim().min(1).max(255).optional(),
|
|
2144
2121
|
segment: z.string().trim().min(1).max(255).optional(),
|
|
2145
|
-
|
|
2122
|
+
processingState: CompanyProcessingStateSchema.optional(),
|
|
2123
|
+
pipelineStatus: z.unknown().optional(),
|
|
2146
2124
|
enrichmentData: z.record(z.string(), z.unknown()).optional(),
|
|
2147
2125
|
source: z.string().trim().min(1).max(255).optional(),
|
|
2148
2126
|
batchId: z.string().trim().min(1).max(255).optional(),
|
|
@@ -2162,6 +2140,7 @@ export const UpdateCompanyRequestSchema = z
|
|
|
2162
2140
|
data.locationState !== undefined ||
|
|
2163
2141
|
data.category !== undefined ||
|
|
2164
2142
|
data.segment !== undefined ||
|
|
2143
|
+
data.processingState !== undefined ||
|
|
2165
2144
|
data.pipelineStatus !== undefined ||
|
|
2166
2145
|
data.enrichmentData !== undefined ||
|
|
2167
2146
|
data.source !== undefined ||
|
|
@@ -2187,7 +2166,8 @@ export const CreateContactRequestSchema = z
|
|
|
2187
2166
|
title: z.string().trim().min(1).max(255).optional(),
|
|
2188
2167
|
source: z.string().trim().min(1).max(255).optional(),
|
|
2189
2168
|
sourceId: z.string().trim().min(1).max(255).optional(),
|
|
2190
|
-
batchId: z.string().trim().min(1).max(255).optional()
|
|
2169
|
+
batchId: z.string().trim().min(1).max(255).optional(),
|
|
2170
|
+
pipelineStatus: z.unknown().optional()
|
|
2191
2171
|
})
|
|
2192
2172
|
.strict()
|
|
2193
2173
|
```
|
|
@@ -2206,7 +2186,8 @@ export const UpdateContactRequestSchema = z
|
|
|
2206
2186
|
headline: z.string().trim().min(1).max(5000).optional(),
|
|
2207
2187
|
filterReason: z.string().trim().min(1).max(5000).optional(),
|
|
2208
2188
|
openingLine: z.string().trim().min(1).max(5000).optional(),
|
|
2209
|
-
|
|
2189
|
+
processingState: ContactProcessingStateSchema.optional(),
|
|
2190
|
+
pipelineStatus: z.unknown().optional(),
|
|
2210
2191
|
enrichmentData: z.record(z.string(), z.unknown()).optional(),
|
|
2211
2192
|
status: AcqContactStatusSchema.optional()
|
|
2212
2193
|
})
|
|
@@ -2222,6 +2203,7 @@ export const UpdateContactRequestSchema = z
|
|
|
2222
2203
|
data.headline !== undefined ||
|
|
2223
2204
|
data.filterReason !== undefined ||
|
|
2224
2205
|
data.openingLine !== undefined ||
|
|
2206
|
+
data.processingState !== undefined ||
|
|
2225
2207
|
data.pipelineStatus !== undefined ||
|
|
2226
2208
|
data.enrichmentData !== undefined ||
|
|
2227
2209
|
data.status !== undefined,
|
|
@@ -2248,7 +2230,8 @@ export const AcqCompanyResponseSchema = z.object({
|
|
|
2248
2230
|
category: z.string().nullable(),
|
|
2249
2231
|
categoryPain: z.string().nullable(),
|
|
2250
2232
|
segment: z.string().nullable(),
|
|
2251
|
-
|
|
2233
|
+
processingState: CompanyProcessingStateSchema.nullable(),
|
|
2234
|
+
pipelineStatus: z.unknown().nullable().optional(),
|
|
2252
2235
|
enrichmentData: z.record(z.string(), z.unknown()).nullable(),
|
|
2253
2236
|
source: z.string().nullable(),
|
|
2254
2237
|
batchId: z.string().nullable(),
|
|
@@ -2314,7 +2297,8 @@ export const AcqContactResponseSchema = z.object({
|
|
|
2314
2297
|
openingLine: z.string().nullable(),
|
|
2315
2298
|
source: z.string().nullable(),
|
|
2316
2299
|
sourceId: z.string().nullable(),
|
|
2317
|
-
|
|
2300
|
+
processingState: ContactProcessingStateSchema.nullable(),
|
|
2301
|
+
pipelineStatus: z.unknown().nullable().optional(),
|
|
2318
2302
|
enrichmentData: z.record(z.string(), z.unknown()).nullable(),
|
|
2319
2303
|
attioPersonId: z.string().nullable(),
|
|
2320
2304
|
batchId: z.string().nullable(),
|
|
@@ -2480,6 +2464,7 @@ export const AcqListCompanyResponseSchema = z.object({
|
|
|
2480
2464
|
|
|
2481
2465
|
```typescript
|
|
2482
2466
|
export const AcqCompanySchemas = {
|
|
2467
|
+
CompanyProcessingState: CompanyProcessingStateSchema,
|
|
2483
2468
|
CompanyIdParams: CompanyIdParamsSchema,
|
|
2484
2469
|
ListCompaniesQuery: ListCompaniesQuerySchema,
|
|
2485
2470
|
CreateCompanyRequest: CreateCompanyRequestSchema,
|
|
@@ -2494,6 +2479,7 @@ export const AcqCompanySchemas = {
|
|
|
2494
2479
|
|
|
2495
2480
|
```typescript
|
|
2496
2481
|
export const AcqContactSchemas = {
|
|
2482
|
+
ContactProcessingState: ContactProcessingStateSchema,
|
|
2497
2483
|
ContactIdParams: ContactIdParamsSchema,
|
|
2498
2484
|
ListContactsQuery: ListContactsQuerySchema,
|
|
2499
2485
|
CreateContactRequest: CreateContactRequestSchema,
|
|
@@ -2519,7 +2505,10 @@ export const AcqListSchemas = {
|
|
|
2519
2505
|
BuildPlanSnapshot: BuildPlanSnapshotSchema,
|
|
2520
2506
|
BuildPlanSnapshotStep: BuildPlanSnapshotStepSchema,
|
|
2521
2507
|
AcqListMetadata: AcqListMetadataSchema,
|
|
2508
|
+
LeadGenStageKey: LeadGenStageKeySchema,
|
|
2509
|
+
LeadGenCapabilityKey: LeadGenCapabilityKeySchema,
|
|
2522
2510
|
ProcessingStageStatus: ProcessingStageStatusSchema,
|
|
2511
|
+
ProcessingState: ProcessingStateSchema,
|
|
2523
2512
|
ListStageCounts: ListStageCountsSchema,
|
|
2524
2513
|
ListTelemetry: ListTelemetrySchema,
|
|
2525
2514
|
|
|
@@ -2812,6 +2801,8 @@ export interface CreateCompanyParams {
|
|
|
2812
2801
|
source?: string
|
|
2813
2802
|
batchId?: string
|
|
2814
2803
|
verticalResearch?: string
|
|
2804
|
+
/** @deprecated Use processingState. Accepted as a no-op compatibility bridge for external tenants. */
|
|
2805
|
+
pipelineStatus?: unknown
|
|
2815
2806
|
}
|
|
2816
2807
|
```
|
|
2817
2808
|
|
|
@@ -2829,7 +2820,9 @@ export interface UpdateCompanyParams {
|
|
|
2829
2820
|
locationState?: string
|
|
2830
2821
|
category?: string
|
|
2831
2822
|
segment?: string
|
|
2832
|
-
|
|
2823
|
+
processingState?: ProcessingState
|
|
2824
|
+
/** @deprecated Use processingState. Accepted as a no-op compatibility bridge for external tenants. */
|
|
2825
|
+
pipelineStatus?: unknown
|
|
2833
2826
|
enrichmentData?: Record<string, unknown>
|
|
2834
2827
|
source?: string
|
|
2835
2828
|
batchId?: string
|
|
@@ -2837,7 +2830,7 @@ export interface UpdateCompanyParams {
|
|
|
2837
2830
|
verticalResearch?: string | null
|
|
2838
2831
|
/** Track A: flat qualification score column (null until a scoring rubric is defined) */
|
|
2839
2832
|
qualificationScore?: number | null
|
|
2840
|
-
/** Track A: flat qualification signals jsonb
|
|
2833
|
+
/** Track A: flat qualification signals jsonb */
|
|
2841
2834
|
qualificationSignals?: Record<string, unknown> | null
|
|
2842
2835
|
/** Track A: key identifying the rubric used for qualification */
|
|
2843
2836
|
qualificationRubricKey?: string | null
|
|
@@ -2860,13 +2853,15 @@ export interface CompanyFilters {
|
|
|
2860
2853
|
website?: string
|
|
2861
2854
|
segment?: string
|
|
2862
2855
|
category?: string
|
|
2863
|
-
|
|
2864
|
-
/**
|
|
2865
|
-
|
|
2856
|
+
processingState?: ProcessingState
|
|
2857
|
+
/** @deprecated Use processingState. Accepted as a no-op compatibility bridge for external tenants. */
|
|
2858
|
+
pipelineStatus?: unknown
|
|
2859
|
+
/** Exclude companies whose processing state contains this value (PostgREST NOT contains) */
|
|
2860
|
+
processingStateNot?: ProcessingState
|
|
2866
2861
|
batchId?: string
|
|
2867
2862
|
status?: 'active' | 'invalid'
|
|
2868
2863
|
includeAll?: boolean
|
|
2869
|
-
excludeColumns?: Array<'enrichmentData' | '
|
|
2864
|
+
excludeColumns?: Array<'enrichmentData' | 'processingState'>
|
|
2870
2865
|
limit?: number
|
|
2871
2866
|
}
|
|
2872
2867
|
```
|
|
@@ -2885,6 +2880,8 @@ export interface CreateContactParams {
|
|
|
2885
2880
|
source?: string
|
|
2886
2881
|
sourceId?: string
|
|
2887
2882
|
batchId?: string
|
|
2883
|
+
/** @deprecated Use processingState. Accepted as a no-op compatibility bridge for external tenants. */
|
|
2884
|
+
pipelineStatus?: unknown
|
|
2888
2885
|
}
|
|
2889
2886
|
```
|
|
2890
2887
|
|
|
@@ -2901,7 +2898,9 @@ export interface UpdateContactParams {
|
|
|
2901
2898
|
headline?: string
|
|
2902
2899
|
filterReason?: string
|
|
2903
2900
|
openingLine?: string
|
|
2904
|
-
|
|
2901
|
+
processingState?: ProcessingState
|
|
2902
|
+
/** @deprecated Use processingState. Accepted as a no-op compatibility bridge for external tenants. */
|
|
2903
|
+
pipelineStatus?: unknown
|
|
2905
2904
|
enrichmentData?: Record<string, unknown>
|
|
2906
2905
|
status?: 'active' | 'invalid'
|
|
2907
2906
|
}
|
|
@@ -2920,7 +2919,9 @@ export interface ContactFilters {
|
|
|
2920
2919
|
listId?: string // Filter to contacts in a specific list (via acq_list_members)
|
|
2921
2920
|
search?: string
|
|
2922
2921
|
openingLineIsNull?: boolean // Filter to contacts without personalization
|
|
2923
|
-
|
|
2922
|
+
processingState?: ProcessingState
|
|
2923
|
+
/** @deprecated Use processingState. Accepted as a no-op compatibility bridge for external tenants. */
|
|
2924
|
+
pipelineStatus?: unknown
|
|
2924
2925
|
batchId?: string
|
|
2925
2926
|
contactStatus?: 'active' | 'invalid' // Filter by contact status (soft-delete flag)
|
|
2926
2927
|
}
|
|
@@ -3119,7 +3120,7 @@ export interface BulkImportCompanyEntry {
|
|
|
3119
3120
|
category?: string
|
|
3120
3121
|
source?: string
|
|
3121
3122
|
enrichmentData?: Record<string, unknown>
|
|
3122
|
-
|
|
3123
|
+
processingState?: ProcessingState
|
|
3123
3124
|
}
|
|
3124
3125
|
```
|
|
3125
3126
|
|
|
@@ -3300,6 +3301,14 @@ export type ListToolMap = {
|
|
|
3300
3301
|
params: Omit<UpdateContactStageParams, 'organizationId'>
|
|
3301
3302
|
result: void
|
|
3302
3303
|
}
|
|
3304
|
+
listPendingCompanyIds: {
|
|
3305
|
+
params: Omit<ListPendingCompanyIdsParams, 'organizationId'>
|
|
3306
|
+
result: string[]
|
|
3307
|
+
}
|
|
3308
|
+
listPendingContactIds: {
|
|
3309
|
+
params: Omit<ListPendingContactIdsParams, 'organizationId'>
|
|
3310
|
+
result: string[]
|
|
3311
|
+
}
|
|
3303
3312
|
}
|
|
3304
3313
|
```
|
|
3305
3314
|
|
|
@@ -3335,6 +3344,10 @@ export const OrgKnowledgeNodeSchema = z.object({
|
|
|
3335
3344
|
* Each link emits a `governs` edge: knowledge-node -> target node.
|
|
3336
3345
|
*/
|
|
3337
3346
|
links: z.array(KnowledgeLinkSchema).default([]),
|
|
3347
|
+
/** Operator skill or command bindings relevant to this node. */
|
|
3348
|
+
skills: z.array(KnowledgeSkillBindingSchema).optional(),
|
|
3349
|
+
/** Domain key used to derive fast graph->skill registries. */
|
|
3350
|
+
domain: KnowledgeDomainBindingSchema.optional(),
|
|
3338
3351
|
/** Identifiers of the roles or members who own this knowledge node. */
|
|
3339
3352
|
ownerIds: z.array(ModelIdSchema).default([]),
|
|
3340
3353
|
/** ISO date string (YYYY-MM-DD or full ISO 8601) of last meaningful update. */
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { CreateOrganizationSchema, ListOrganizationsQuerySchema, UpdateOrganizationSchema } from '../api-schemas'
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// CreateOrganizationSchema
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
describe('CreateOrganizationSchema', () => {
|
|
9
|
+
it('accepts a minimal valid payload (name only)', () => {
|
|
10
|
+
expect(CreateOrganizationSchema.safeParse({ name: 'Acme Corp' }).success).toBe(true)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('accepts name at minimum length boundary (2 chars)', () => {
|
|
14
|
+
expect(CreateOrganizationSchema.safeParse({ name: 'AB' }).success).toBe(true)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('accepts name at maximum length boundary (100 chars)', () => {
|
|
18
|
+
expect(CreateOrganizationSchema.safeParse({ name: 'A'.repeat(100) }).success).toBe(true)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('rejects name shorter than 2 characters', () => {
|
|
22
|
+
expect(CreateOrganizationSchema.safeParse({ name: 'A' }).success).toBe(false)
|
|
23
|
+
expect(CreateOrganizationSchema.safeParse({ name: '' }).success).toBe(false)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('rejects name longer than 100 characters', () => {
|
|
27
|
+
expect(CreateOrganizationSchema.safeParse({ name: 'A'.repeat(101) }).success).toBe(false)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('accepts names with letters, numbers, spaces, hyphens, and underscores', () => {
|
|
31
|
+
expect(CreateOrganizationSchema.safeParse({ name: 'My Org-123_Test' }).success).toBe(true)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('rejects names with disallowed characters (special symbols)', () => {
|
|
35
|
+
expect(CreateOrganizationSchema.safeParse({ name: 'Acme@Corp' }).success).toBe(false)
|
|
36
|
+
expect(CreateOrganizationSchema.safeParse({ name: 'Acme.Corp' }).success).toBe(false)
|
|
37
|
+
expect(CreateOrganizationSchema.safeParse({ name: 'Acme/Corp' }).success).toBe(false)
|
|
38
|
+
expect(CreateOrganizationSchema.safeParse({ name: 'Acme+Corp' }).success).toBe(false)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('accepts optional domainData array with valid entries', () => {
|
|
42
|
+
const result = CreateOrganizationSchema.safeParse({
|
|
43
|
+
name: 'Acme Corp',
|
|
44
|
+
domainData: [{ domain: 'acme.com', state: 'verified' }]
|
|
45
|
+
})
|
|
46
|
+
expect(result.success).toBe(true)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('accepts domainData up to 10 entries', () => {
|
|
50
|
+
const domains = Array.from({ length: 10 }, (_, i) => ({ domain: `domain${i}.com` }))
|
|
51
|
+
expect(CreateOrganizationSchema.safeParse({ name: 'Acme Corp', domainData: domains }).success).toBe(true)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('rejects domainData with more than 10 entries', () => {
|
|
55
|
+
const domains = Array.from({ length: 11 }, (_, i) => ({ domain: `domain${i}.com` }))
|
|
56
|
+
expect(CreateOrganizationSchema.safeParse({ name: 'Acme Corp', domainData: domains }).success).toBe(false)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('accepts optional metadata as a record under 10KB', () => {
|
|
60
|
+
const result = CreateOrganizationSchema.safeParse({
|
|
61
|
+
name: 'Acme Corp',
|
|
62
|
+
metadata: { plan: 'pro', region: 'us-west' }
|
|
63
|
+
})
|
|
64
|
+
expect(result.success).toBe(true)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('rejects metadata exceeding 10KB (10240 bytes)', () => {
|
|
68
|
+
// Build a metadata object whose JSON representation exceeds 10240 bytes
|
|
69
|
+
const largeValue = 'x'.repeat(10241)
|
|
70
|
+
const result = CreateOrganizationSchema.safeParse({
|
|
71
|
+
name: 'Acme Corp',
|
|
72
|
+
metadata: { big: largeValue }
|
|
73
|
+
})
|
|
74
|
+
expect(result.success).toBe(false)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('rejects unknown top-level fields (.strict() mode)', () => {
|
|
78
|
+
expect(CreateOrganizationSchema.safeParse({ name: 'Acme Corp', unknownField: 'value' }).success).toBe(false)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('rejects missing name field', () => {
|
|
82
|
+
expect(CreateOrganizationSchema.safeParse({}).success).toBe(false)
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// UpdateOrganizationSchema
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
describe('UpdateOrganizationSchema', () => {
|
|
91
|
+
it('rejects an empty object (at least one field required)', () => {
|
|
92
|
+
// UpdateOrganizationSchema is CreateOrganizationSchema.partial().strict().refine(...)
|
|
93
|
+
// The .refine() enforces the documented "at least one field" convention
|
|
94
|
+
// (see .claude/rules/core-package.md → "api-schemas.ts Pattern")
|
|
95
|
+
const result = UpdateOrganizationSchema.safeParse({})
|
|
96
|
+
expect(result.success).toBe(false)
|
|
97
|
+
if (!result.success) {
|
|
98
|
+
expect(result.error.issues[0].message).toMatch(/at least one field/i)
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('accepts updating only name', () => {
|
|
103
|
+
expect(UpdateOrganizationSchema.safeParse({ name: 'New Name' }).success).toBe(true)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('accepts updating only domainData', () => {
|
|
107
|
+
expect(UpdateOrganizationSchema.safeParse({ domainData: [{ domain: 'newdomain.com' }] }).success).toBe(true)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('accepts updating only metadata', () => {
|
|
111
|
+
expect(UpdateOrganizationSchema.safeParse({ metadata: { key: 'value' } }).success).toBe(true)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('applies same name validation as CreateOrganizationSchema when name is provided', () => {
|
|
115
|
+
// Too short
|
|
116
|
+
expect(UpdateOrganizationSchema.safeParse({ name: 'A' }).success).toBe(false)
|
|
117
|
+
// Too long
|
|
118
|
+
expect(UpdateOrganizationSchema.safeParse({ name: 'A'.repeat(101) }).success).toBe(false)
|
|
119
|
+
// Invalid charset
|
|
120
|
+
expect(UpdateOrganizationSchema.safeParse({ name: 'Bad@Name' }).success).toBe(false)
|
|
121
|
+
// Valid
|
|
122
|
+
expect(UpdateOrganizationSchema.safeParse({ name: 'Good Name-123' }).success).toBe(true)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('applies same domainData max-10 validation when provided', () => {
|
|
126
|
+
const tooMany = Array.from({ length: 11 }, (_, i) => ({ domain: `d${i}.com` }))
|
|
127
|
+
expect(UpdateOrganizationSchema.safeParse({ domainData: tooMany }).success).toBe(false)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('applies same metadata 10KB cap validation when provided', () => {
|
|
131
|
+
const largeValue = 'x'.repeat(10241)
|
|
132
|
+
expect(UpdateOrganizationSchema.safeParse({ metadata: { big: largeValue } }).success).toBe(false)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('rejects unknown top-level fields (.strict() mode)', () => {
|
|
136
|
+
expect(UpdateOrganizationSchema.safeParse({ name: 'Valid Name', unknownField: 'bad' }).success).toBe(false)
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// ListOrganizationsQuerySchema
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
describe('ListOrganizationsQuerySchema', () => {
|
|
145
|
+
it('accepts an empty query and applies default limit of 20', () => {
|
|
146
|
+
const result = ListOrganizationsQuerySchema.safeParse({})
|
|
147
|
+
expect(result.success).toBe(true)
|
|
148
|
+
if (result.success) {
|
|
149
|
+
expect(result.data.limit).toBe(20)
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('coerces limit from string "50" to number 50', () => {
|
|
154
|
+
const result = ListOrganizationsQuerySchema.safeParse({ limit: '50' })
|
|
155
|
+
expect(result.success).toBe(true)
|
|
156
|
+
if (result.success) expect(result.data.limit).toBe(50)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('accepts limit at upper boundary (100)', () => {
|
|
160
|
+
const result = ListOrganizationsQuerySchema.safeParse({ limit: '100' })
|
|
161
|
+
expect(result.success).toBe(true)
|
|
162
|
+
if (result.success) expect(result.data.limit).toBe(100)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('accepts limit at lower boundary (1)', () => {
|
|
166
|
+
const result = ListOrganizationsQuerySchema.safeParse({ limit: '1' })
|
|
167
|
+
expect(result.success).toBe(true)
|
|
168
|
+
if (result.success) expect(result.data.limit).toBe(1)
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('rejects limit of 101 (above max 100)', () => {
|
|
172
|
+
expect(ListOrganizationsQuerySchema.safeParse({ limit: '101' }).success).toBe(false)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('rejects limit of 0 (below min 1)', () => {
|
|
176
|
+
expect(ListOrganizationsQuerySchema.safeParse({ limit: '0' }).success).toBe(false)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('accepts optional before cursor string', () => {
|
|
180
|
+
const result = ListOrganizationsQuerySchema.safeParse({ before: 'cursor_abc123' })
|
|
181
|
+
expect(result.success).toBe(true)
|
|
182
|
+
if (result.success) expect(result.data.before).toBe('cursor_abc123')
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('accepts optional after cursor string', () => {
|
|
186
|
+
const result = ListOrganizationsQuerySchema.safeParse({ after: 'cursor_xyz789' })
|
|
187
|
+
expect(result.success).toBe(true)
|
|
188
|
+
if (result.success) expect(result.data.after).toBe('cursor_xyz789')
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it('accepts both before and after cursors together', () => {
|
|
192
|
+
expect(ListOrganizationsQuerySchema.safeParse({ before: 'a', after: 'b' }).success).toBe(true)
|
|
193
|
+
})
|
|
194
|
+
})
|