@undefineds.co/xpod 0.3.29 → 0.3.32
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/api/auth/AuthContext.d.ts +3 -2
- package/dist/api/auth/AuthContext.js +2 -1
- package/dist/api/auth/AuthContext.js.map +1 -1
- package/dist/api/auth/ClientCredentialsAuthenticator.d.ts +2 -12
- package/dist/api/auth/ClientCredentialsAuthenticator.js +4 -4
- package/dist/api/auth/ClientCredentialsAuthenticator.js.map +1 -1
- package/dist/api/auth/ServiceTokenAuthenticator.d.ts +2 -2
- package/dist/api/auth/ServiceTokenAuthenticator.js.map +1 -1
- package/dist/api/container/business-token.d.ts +1 -1
- package/dist/api/container/business-token.js +5 -1
- package/dist/api/container/business-token.js.map +1 -1
- package/dist/api/container/common.js +14 -10
- package/dist/api/container/common.js.map +1 -1
- package/dist/api/container/routes.js +16 -3
- package/dist/api/container/routes.js.map +1 -1
- package/dist/api/container/types.d.ts +2 -4
- package/dist/api/container/types.js.map +1 -1
- package/dist/api/handlers/ChatHandler.d.ts +1 -1
- package/dist/api/handlers/ChatHandler.js +1 -1
- package/dist/api/handlers/ChatHandler.js.map +1 -1
- package/dist/api/handlers/EdgeNodeSignalHandler.js +3 -1
- package/dist/api/handlers/EdgeNodeSignalHandler.js.map +1 -1
- package/dist/api/handlers/PodManagementHandler.d.ts +2 -0
- package/dist/api/handlers/PodManagementHandler.js +114 -12
- package/dist/api/handlers/PodManagementHandler.js.map +1 -1
- package/dist/api/handlers/ProvisionHandler.d.ts +27 -0
- package/dist/api/handlers/ProvisionHandler.js +339 -32
- package/dist/api/handlers/ProvisionHandler.js.map +1 -1
- package/dist/api/handlers/QuotaHandler.js +0 -12
- package/dist/api/handlers/QuotaHandler.js.map +1 -1
- package/dist/api/handlers/index.d.ts +0 -1
- package/dist/api/handlers/index.js +0 -1
- package/dist/api/handlers/index.js.map +1 -1
- package/dist/api/runtime.js +3 -3
- package/dist/api/runtime.js.map +1 -1
- package/dist/authorization/PodAuthorizationResources.d.ts +1 -0
- package/dist/authorization/PodAuthorizationResources.js +36 -4
- package/dist/authorization/PodAuthorizationResources.js.map +1 -1
- package/dist/components/context.jsonld +12 -0
- package/dist/edge/EdgeNodeAgent.d.ts +1 -1
- package/dist/edge/EdgeNodeAgent.js +1 -1
- package/dist/edge/EdgeNodeAgent.js.map +1 -1
- package/dist/edge/EdgeNodeDnsCoordinator.d.ts +1 -0
- package/dist/edge/EdgeNodeDnsCoordinator.js +9 -3
- package/dist/edge/EdgeNodeDnsCoordinator.js.map +1 -1
- package/dist/edge/EdgeNodeDnsCoordinator.jsonld +4 -0
- package/dist/edge/EdgeNodeHealthProbeService.d.ts +3 -0
- package/dist/edge/EdgeNodeHealthProbeService.js +22 -2
- package/dist/edge/EdgeNodeHealthProbeService.js.map +1 -1
- package/dist/edge/EdgeNodeHealthProbeService.jsonld +12 -0
- package/dist/http/ClusterIngressRouter.js +6 -3
- package/dist/http/ClusterIngressRouter.js.map +1 -1
- package/dist/http/ClusterWebSocketConfigurator.js +6 -2
- package/dist/http/ClusterWebSocketConfigurator.js.map +1 -1
- package/dist/http/EdgeNodeDirectDebugHttpHandler.d.ts +2 -0
- package/dist/http/EdgeNodeDirectDebugHttpHandler.js +18 -3
- package/dist/http/EdgeNodeDirectDebugHttpHandler.js.map +1 -1
- package/dist/http/EdgeNodeDirectDebugHttpHandler.jsonld +8 -0
- package/dist/http/EdgeNodeProxyHttpHandler.js +6 -2
- package/dist/http/EdgeNodeProxyHttpHandler.js.map +1 -1
- package/dist/http/cluster/PodMigrationHttpHandler.d.ts +2 -2
- package/dist/http/cluster/PodMigrationHttpHandler.js +2 -2
- package/dist/http/cluster/PodMigrationHttpHandler.js.map +1 -1
- package/dist/http/quota/QuotaAdminHttpHandler.js +27 -21
- package/dist/http/quota/QuotaAdminHttpHandler.js.map +1 -1
- package/dist/identity/drizzle/AccountRepository.d.ts +4 -22
- package/dist/identity/drizzle/AccountRepository.js +9 -113
- package/dist/identity/drizzle/AccountRepository.js.map +1 -1
- package/dist/identity/drizzle/AccountRoleRepository.d.ts +5 -5
- package/dist/identity/drizzle/AccountRoleRepository.js +204 -97
- package/dist/identity/drizzle/AccountRoleRepository.js.map +1 -1
- package/dist/identity/drizzle/DdnsRepository.d.ts +5 -20
- package/dist/identity/drizzle/DdnsRepository.js +13 -49
- package/dist/identity/drizzle/DdnsRepository.js.map +1 -1
- package/dist/identity/drizzle/EdgeNodeRepository.d.ts +13 -6
- package/dist/identity/drizzle/EdgeNodeRepository.js +167 -66
- package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
- package/dist/identity/drizzle/PodLookupRepository.d.ts +7 -36
- package/dist/identity/drizzle/PodLookupRepository.js +103 -126
- package/dist/identity/drizzle/PodLookupRepository.js.map +1 -1
- package/dist/identity/drizzle/ServiceTokenRepository.d.ts +13 -1
- package/dist/identity/drizzle/ServiceTokenRepository.js +7 -0
- package/dist/identity/drizzle/ServiceTokenRepository.js.map +1 -1
- package/dist/identity/drizzle/db.d.ts +2 -1
- package/dist/identity/drizzle/db.js +173 -297
- package/dist/identity/drizzle/db.js.map +1 -1
- package/dist/identity/drizzle/schema.pg.d.ts +3 -11
- package/dist/identity/drizzle/schema.pg.js +10 -45
- package/dist/identity/drizzle/schema.pg.js.map +1 -1
- package/dist/identity/drizzle/schema.sqlite.d.ts +88 -531
- package/dist/identity/drizzle/schema.sqlite.js +13 -46
- package/dist/identity/drizzle/schema.sqlite.js.map +1 -1
- package/dist/identity/oidc/ScopedPickWebIdHandler.d.ts +3 -0
- package/dist/identity/oidc/ScopedPickWebIdHandler.js +18 -6
- package/dist/identity/oidc/ScopedPickWebIdHandler.js.map +1 -1
- package/dist/identity/oidc/ScopedPickWebIdHandler.jsonld +22 -0
- package/dist/provision/LocalPodProvisioningService.js +2 -0
- package/dist/provision/LocalPodProvisioningService.js.map +1 -1
- package/dist/provision/ProvisionCodeCodec.js +10 -1
- package/dist/provision/ProvisionCodeCodec.js.map +1 -1
- package/dist/provision/ProvisionPodCreator.d.ts +8 -2
- package/dist/provision/ProvisionPodCreator.js +136 -27
- package/dist/provision/ProvisionPodCreator.js.map +1 -1
- package/dist/provision/ProvisionPodCreator.jsonld +38 -3
- package/dist/quota/DrizzleQuotaService.d.ts +0 -4
- package/dist/quota/DrizzleQuotaService.js +1 -21
- package/dist/quota/DrizzleQuotaService.js.map +1 -1
- package/dist/quota/DrizzleQuotaService.jsonld +0 -16
- package/dist/quota/NoopQuotaService.d.ts +0 -4
- package/dist/quota/NoopQuotaService.js +0 -8
- package/dist/quota/NoopQuotaService.js.map +1 -1
- package/dist/quota/NoopQuotaService.jsonld +0 -16
- package/dist/quota/QuotaService.d.ts +0 -4
- package/dist/quota/QuotaService.js.map +1 -1
- package/dist/quota/QuotaService.jsonld +0 -16
- package/dist/service/EdgeNodeSignalClient.d.ts +0 -2
- package/dist/service/EdgeNodeSignalClient.js +0 -4
- package/dist/service/EdgeNodeSignalClient.js.map +1 -1
- package/dist/service/PodMigrationService.d.ts +2 -2
- package/dist/service/PodMigrationService.js +4 -4
- package/dist/service/PodMigrationService.js.map +1 -1
- package/dist/setup/LocalSetupServiceTokenRepository.d.ts +22 -0
- package/dist/setup/LocalSetupServiceTokenRepository.js +68 -0
- package/dist/setup/LocalSetupServiceTokenRepository.js.map +1 -0
- package/dist/storage/accessors/MixDataAccessor.js.map +1 -1
- package/dist/storage/quota/PerAccountQuotaStrategy.js +2 -2
- package/dist/storage/quota/PerAccountQuotaStrategy.js.map +1 -1
- package/dist/storage/quota/UsageRepository.d.ts +10 -32
- package/dist/storage/quota/UsageRepository.js +84 -281
- package/dist/storage/quota/UsageRepository.js.map +1 -1
- package/dist/storage/rdf/PostgresRdfEngine.d.ts +12 -15
- package/dist/storage/rdf/PostgresRdfEngine.js +1040 -150
- package/dist/storage/rdf/PostgresRdfEngine.js.map +1 -1
- package/dist/storage/rdf/PostgresRdfEngine.jsonld +40 -52
- package/dist/storage/rdf/{RdfLocalQueryEngine.d.ts → RdfQueryExecutor.d.ts} +3 -3
- package/dist/storage/rdf/{RdfLocalQueryEngine.js → RdfQueryExecutor.js} +9 -9
- package/dist/storage/rdf/RdfQueryExecutor.js.map +1 -0
- package/dist/storage/rdf/RdfSparqlAdapter.d.ts +5 -5
- package/dist/storage/rdf/RdfSparqlAdapter.js +27 -27
- package/dist/storage/rdf/RdfSparqlAdapter.js.map +1 -1
- package/dist/storage/rdf/SolidRdfEngine.d.ts +2 -5
- package/dist/storage/rdf/SolidRdfEngine.js +6 -38
- package/dist/storage/rdf/SolidRdfEngine.js.map +1 -1
- package/dist/storage/rdf/SolidRdfEngine.jsonld +0 -12
- package/dist/storage/rdf/SolidRdfSparqlEngine.js.map +1 -1
- package/dist/storage/rdf/index.d.ts +3 -3
- package/dist/storage/rdf/index.js +6 -6
- package/dist/storage/rdf/index.js.map +1 -1
- package/dist/storage/rdf/models-benchmark.d.ts +9 -9
- package/dist/storage/rdf/models-benchmark.js +23 -23
- package/dist/storage/rdf/models-benchmark.js.map +1 -1
- package/dist/storage/rdf/types.d.ts +5 -5
- package/dist/storage/rdf/types.js.map +1 -1
- package/dist/subdomain/SubdomainService.d.ts +1 -1
- package/dist/subdomain/SubdomainService.js +1 -1
- package/dist/subdomain/SubdomainService.js.map +1 -1
- package/dist/subdomain/SubdomainService.jsonld +1 -1
- package/package.json +1 -1
- package/templates/pod/acp/profile/.acr +21 -0
- package/templates/pod/wac/profile/.acl.hbs +18 -0
- package/dist/api/handlers/ApiKeyHandler.d.ts +0 -15
- package/dist/api/handlers/ApiKeyHandler.js +0 -153
- package/dist/api/handlers/ApiKeyHandler.js.map +0 -1
- package/dist/api/store/DrizzleClientCredentialsStore.d.ts +0 -51
- package/dist/api/store/DrizzleClientCredentialsStore.js +0 -115
- package/dist/api/store/DrizzleClientCredentialsStore.js.map +0 -1
- package/dist/storage/rdf/RdfLocalQueryEngine.js.map +0 -1
|
@@ -495,7 +495,7 @@ export interface RdfVectorSearchPattern {
|
|
|
495
495
|
endOffset?: string;
|
|
496
496
|
model?: string;
|
|
497
497
|
}
|
|
498
|
-
export interface
|
|
498
|
+
export interface RdfQuery {
|
|
499
499
|
patterns: RdfQueryPattern[];
|
|
500
500
|
values?: RdfValuesBindingSource[];
|
|
501
501
|
textSearch?: RdfTextSearchPattern[];
|
|
@@ -567,7 +567,7 @@ export interface RdfQuadJoinGroupAggregateOptions {
|
|
|
567
567
|
offset?: number;
|
|
568
568
|
}
|
|
569
569
|
export type RdfQuadJoinGroupCountOptions = RdfQuadJoinGroupAggregateOptions;
|
|
570
|
-
export interface
|
|
570
|
+
export interface RdfQueryMetrics {
|
|
571
571
|
engine: 'solid-rdf';
|
|
572
572
|
plan: string[];
|
|
573
573
|
scannedRows: number;
|
|
@@ -581,10 +581,10 @@ export interface RdfLocalQueryMetrics {
|
|
|
581
581
|
filtersApplied: number;
|
|
582
582
|
filtersPushedDown: number;
|
|
583
583
|
}
|
|
584
|
-
export interface
|
|
584
|
+
export interface RdfQueryResult {
|
|
585
585
|
bindings: RdfBindingRow[];
|
|
586
586
|
count?: number;
|
|
587
|
-
metrics:
|
|
587
|
+
metrics: RdfQueryMetrics;
|
|
588
588
|
}
|
|
589
589
|
export interface RdfEngineLike {
|
|
590
590
|
open(): void | Promise<void>;
|
|
@@ -601,7 +601,7 @@ export interface RdfEngineLike {
|
|
|
601
601
|
insertedRows: number;
|
|
602
602
|
}>;
|
|
603
603
|
scan(query: RdfPatternQuery): RdfQuadIndexScanResult | Promise<RdfQuadIndexScanResult>;
|
|
604
|
-
query(query:
|
|
604
|
+
query(query: RdfQuery): RdfQueryResult | Promise<RdfQueryResult>;
|
|
605
605
|
refreshDerivedIndexes(): RdfDerivedIndexRefreshResult | Promise<RdfDerivedIndexRefreshResult>;
|
|
606
606
|
storageStats(): RdfEngineStorageStats | Promise<RdfEngineStorageStats>;
|
|
607
607
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/storage/rdf/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Quad, Term } from '@rdfjs/types';\nimport type { QueryOptions, QuintPattern, TermMatch, TermName } from '../quint/types';\n\nexport type RdfTermKind = 'iri' | 'literal' | 'blank' | 'default_graph';\n\nexport interface RdfTermRow {\n id: number;\n kind: RdfTermKind;\n value: string;\n value_head: string;\n datatype_id: number | null;\n lang: string | null;\n hash: string;\n normalized_text: string | null;\n numeric_value: number | null;\n created_at: string;\n}\n\nexport interface RdfSourceInput {\n source: string;\n workspace: string;\n localPath?: string;\n contentType?: string;\n sourceVersion?: string;\n}\n\nexport interface RdfSourceRow {\n id: number;\n source: string;\n workspace: string;\n local_path: string | null;\n content_type: string | null;\n last_indexed_at: string | null;\n source_version: string | null;\n}\n\nexport interface RdfQuadRow {\n graph_id: number;\n subject_id: number;\n predicate_id: number;\n object_id: number;\n source_file_id: number | null;\n source_line_no: number | null;\n}\n\nexport interface RdfQuadIndexOptions {\n path: string;\n debug?: boolean;\n}\n\nexport type RdfDerivedIndexProfile = 'baseline' | 'rdf3x';\n\nexport interface RdfShadowAutoBackfillOptions {\n enabled?: boolean;\n clear?: boolean;\n batchSize?: number;\n}\n\nexport interface RdfIndexPutOptions {\n source?: RdfSourceInput;\n sourceLineNo?: number;\n}\n\nexport interface RdfIndexMetrics {\n engine: 'solid-rdf';\n indexChoice: string;\n /** Rows matched before LIMIT/OFFSET are applied. */\n matchedRows: number;\n returnedRows: number;\n durationMs: number;\n queryPlan?: string[];\n}\n\nexport interface RdfCardinalityEstimate {\n rows: number;\n source:\n | 'exact-count'\n | 'cached-exact-count'\n | 'exact-distinct-count'\n | 'cached-exact-distinct-count'\n | 'exact-distinct-tuple-count'\n | 'cached-exact-distinct-tuple-count';\n indexChoice: string;\n}\n\nexport interface RdfIndexStats {\n termCount: number;\n quadCount: number;\n sourceCount: number;\n graphCount: number;\n databaseBytes: number;\n tableBytes: number;\n indexBytes: number;\n spaceObjects: RdfIndexSpaceObject[];\n serializedTermTextBytes: number;\n literalDatatypeDistribution: RdfLiteralDatatypeDistribution[];\n cardinalityDistributions: RdfCardinalityDistributions;\n}\n\nexport interface RdfEngineStorageStats {\n derivedIndexProfile: RdfDerivedIndexProfile;\n facts: RdfIndexStats;\n rdf3x?: {\n stats: Rdf3xIndexStats;\n syncedWithFacts: boolean;\n };\n factsBytes: number;\n derivedBytes: number;\n totalBytes: number;\n derivedToFactsRatio: number;\n totalToFactsRatio: number;\n}\n\nexport interface RdfDerivedIndexRefreshResult {\n derivedIndexProfile: RdfDerivedIndexProfile;\n factsDataVersion: number;\n rdf3x?: {\n refreshed: boolean;\n previousFactsDataVersion: number;\n factsDataVersion: number;\n syncedWithFacts: boolean;\n rebuild?: Rdf3xRebuildResult;\n };\n}\n\nexport interface RdfIndexSpaceObject {\n name: string;\n kind: 'table' | 'index' | 'internal' | 'unknown';\n tableName?: string;\n bytes: number;\n pages: number;\n estimated?: boolean;\n}\n\nexport interface RdfLiteralDatatypeDistribution {\n datatype: string;\n termCount: number;\n objectQuadCount: number;\n}\n\nexport interface RdfCardinalityTerm {\n value: string;\n kind: RdfTermKind;\n datatype?: string;\n language?: string;\n}\n\nexport interface RdfGraphCardinality {\n graph: RdfCardinalityTerm;\n quadCount: number;\n distinctSubjects: number;\n distinctPredicates: number;\n distinctObjects: number;\n}\n\nexport interface RdfPredicateCardinality {\n predicate: RdfCardinalityTerm;\n quadCount: number;\n graphCount: number;\n distinctSubjects: number;\n distinctObjects: number;\n}\n\nexport interface RdfPredicateObjectCardinality {\n predicate: RdfCardinalityTerm;\n object: RdfCardinalityTerm;\n quadCount: number;\n graphCount: number;\n distinctSubjects: number;\n}\n\nexport interface RdfSubjectPredicateCardinality {\n subject: RdfCardinalityTerm;\n predicate: RdfCardinalityTerm;\n quadCount: number;\n graphCount: number;\n distinctObjects: number;\n}\n\nexport interface RdfCardinalityDistributions {\n graphs: RdfGraphCardinality[];\n predicates: RdfPredicateCardinality[];\n predicateObjects: RdfPredicateObjectCardinality[];\n subjectPredicates: RdfSubjectPredicateCardinality[];\n}\n\nexport interface RdfQuadIndexScanResult {\n quads: Quad[];\n metrics: RdfIndexMetrics;\n}\n\nexport type Rdf3xTermKey = 'subject' | 'predicate' | 'object';\nexport type Rdf3xPatternKey = 'graph' | Rdf3xTermKey;\nexport type Rdf3xPermutationName = 'SPO' | 'SOP' | 'PSO' | 'POS' | 'OSP' | 'OPS';\nexport type Rdf3xPairProjectionName = 'SP' | 'SO' | 'PS' | 'PO' | 'OS' | 'OP';\nexport type Rdf3xTermProjectionName = 'S' | 'P' | 'O';\n\nexport interface Rdf3xIndexOptions {\n path: string;\n debug?: boolean;\n}\n\nexport interface Rdf3xGraphPrefixPattern {\n $startsWith: string;\n}\n\nexport interface Rdf3xTermInPattern {\n $in: Term[];\n}\n\nexport interface Rdf3xTermNotInPattern {\n $notIn: Term[];\n}\n\nexport type Rdf3xTermTypePatternValue = 'iri' | 'blank' | 'literal' | 'numeric';\n\nexport interface Rdf3xTermMetadataPattern {\n $termType?: Rdf3xTermTypePatternValue;\n $language?: string;\n $notLanguage?: string;\n $langMatches?: string;\n $datatype?: Term;\n $notDatatype?: Term;\n}\n\nexport interface Rdf3xObjectRangePattern {\n $gt?: Term | string | number;\n $gte?: Term | string | number;\n $lt?: Term | string | number;\n $lte?: Term | string | number;\n}\n\nexport type Rdf3xNumericObjectRangePattern = Rdf3xObjectRangePattern;\n\nexport interface Rdf3xObjectTextSearchPattern {\n $contains?: string;\n $endsWith?: string;\n}\n\nexport interface Rdf3xObjectOperatorPattern extends Rdf3xObjectRangePattern, Rdf3xObjectTextSearchPattern, Rdf3xTermMetadataPattern {}\n\nexport interface Rdf3xTriplePattern {\n graph?: Term | Rdf3xGraphPrefixPattern | Rdf3xTermInPattern | Rdf3xTermNotInPattern | Rdf3xTermMetadataPattern;\n subject?: Term | Rdf3xTermInPattern | Rdf3xTermNotInPattern | Rdf3xTermMetadataPattern;\n predicate?: Term | Rdf3xTermInPattern | Rdf3xTermNotInPattern | Rdf3xTermMetadataPattern;\n object?: Term | Rdf3xObjectOperatorPattern | Rdf3xTermInPattern | Rdf3xTermNotInPattern;\n}\n\nexport interface Rdf3xTripleScanOptions {\n order?: Array<'graph' | 'subject' | 'predicate' | 'object'>;\n orderDirections?: Array<'asc' | 'desc'>;\n reverse?: boolean;\n limit?: number;\n offset?: number;\n}\n\nexport interface Rdf3xIndexMetrics {\n engine: 'solid-rdf3x';\n indexChoice: Rdf3xPermutationName | 'source-membership' | 'none';\n matchedRows: number;\n returnedRows: number;\n durationMs: number;\n queryPlan?: string[];\n}\n\nexport interface Rdf3xTripleScanResult {\n quads: Quad[];\n metrics: Rdf3xIndexMetrics;\n}\n\nexport interface Rdf3xCountResult {\n count: number;\n metrics: Rdf3xIndexMetrics;\n}\n\nexport interface Rdf3xJoinOptions {\n orderBy?: RdfQuadJoinOrder[];\n limit?: number;\n offset?: number;\n project?: string[];\n distinct?: boolean;\n countMatchedRows?: boolean;\n values?: RdfValuesBindingSource[];\n}\n\nexport interface Rdf3xJoinMetrics {\n engine: 'solid-rdf3x';\n indexChoice: string;\n matchedRows: number;\n returnedRows: number;\n durationMs: number;\n queryPlan?: string[];\n}\n\nexport interface Rdf3xJoinScanResult {\n bindings: RdfBindingRow[];\n metrics: Rdf3xJoinMetrics;\n}\n\nexport interface Rdf3xRebuildResult {\n scannedQuads: number;\n uniqueTriples: number;\n memberships: number;\n projectionRows: number;\n factsDataVersion: number;\n durationMs: number;\n}\n\nexport interface Rdf3xCardinalityEstimate {\n uniqueTriples: number;\n matchingQuads: number;\n source: 'projection-stat' | 'term-stat' | 'exact-triple' | 'exact-membership' | 'full-count';\n indexChoice: Rdf3xPermutationName | 'source-membership' | 'none';\n}\n\nexport interface Rdf3xIndexStats {\n uniqueTriples: number;\n membershipCount: number;\n graphCount: number;\n factsDataVersion: number;\n permutationRows: Record<Rdf3xPermutationName, number>;\n pairProjectionRows: Record<Rdf3xPairProjectionName, number>;\n termProjectionRows: Record<Rdf3xTermProjectionName, number>;\n databaseBytes: number;\n tableBytes: number;\n indexBytes: number;\n spaceObjects: RdfIndexSpaceObject[];\n}\n\nexport interface Rdf3xShadowBindingDiff {\n missingFromRdf3x: string[];\n extraInRdf3x: string[];\n}\n\nexport interface Rdf3xShadowQuadDiff {\n missingFromRdf3x: string[];\n extraInRdf3x: string[];\n}\n\nexport interface Rdf3xShadowScanResult {\n matched: boolean;\n orderedMatch: boolean;\n primary: Quad[];\n rdf3x: Quad[];\n diff: Rdf3xShadowQuadDiff;\n primaryMetrics: RdfIndexMetrics;\n rdf3xMetrics: Rdf3xIndexMetrics;\n rebuild: Rdf3xRebuildResult;\n}\n\nexport interface Rdf3xShadowJoinResult {\n matched: boolean;\n orderedMatch: boolean;\n primary: RdfBindingRow[];\n rdf3x: RdfBindingRow[];\n diff: Rdf3xShadowBindingDiff;\n primaryMetrics: RdfIndexMetrics;\n rdf3xMetrics: Rdf3xJoinMetrics;\n rebuild: Rdf3xRebuildResult;\n}\n\nexport type RdfQuadTupleConstraint = Partial<Record<RdfQueryPatternKey, Term>>;\n\nexport interface RdfQuadTupleConstraintSource {\n columns: RdfQueryPatternKey[];\n rows: RdfQuadTupleConstraint[];\n}\n\nexport interface RdfQuadJoinPattern {\n pattern: QuintPattern;\n variables: Partial<Record<RdfQueryPatternKey, string>>;\n}\n\nexport interface RdfQuadJoinOrder {\n variable: string;\n direction?: 'asc' | 'desc';\n}\n\nexport interface RdfQuadScanOptions extends QueryOptions {\n orderDirections?: Array<'asc' | 'desc'>;\n}\n\nexport interface RdfQuadJoinOptions {\n orderBy?: RdfQuadJoinOrder[];\n limit?: number;\n offset?: number;\n project?: string[];\n distinct?: boolean;\n countMatchedRows?: boolean;\n}\n\nexport interface RdfQuadJoinAggregateOptions {\n aggregates: RdfQueryAggregate[];\n}\n\nexport type RdfQuadJoinCountOptions = RdfQuadJoinAggregateOptions;\n\nexport interface RdfQuadJoinGroupAggregateHaving {\n aggregate: string;\n operator: '$eq' | '$ne' | '$gt' | '$gte' | '$lt' | '$lte';\n value: number;\n}\n\nexport type RdfQuadJoinGroupCountHaving = RdfQuadJoinGroupAggregateHaving;\n\nexport interface RdfPatternQuery {\n pattern: QuintPattern;\n options?: QueryOptions;\n}\n\nexport interface RdfQueryVariable {\n variable: string;\n}\n\nexport type RdfQueryTermPattern = TermMatch | RdfQueryVariable;\n\nexport interface RdfQueryPattern {\n graph?: RdfQueryTermPattern;\n subject?: RdfQueryTermPattern;\n predicate?: RdfQueryTermPattern;\n object?: RdfQueryTermPattern;\n}\n\nexport interface RdfConstructTemplate {\n subject: RdfQueryTermPattern;\n predicate: RdfQueryTermPattern;\n object: RdfQueryTermPattern;\n}\n\nexport interface RdfQueryOrder {\n variable: string;\n direction?: 'asc' | 'desc';\n}\n\nexport type RdfQueryAggregateType = 'count' | 'sum' | 'avg' | 'min' | 'max';\n\nexport interface RdfQueryAggregate {\n type: RdfQueryAggregateType;\n as: string;\n variable?: string;\n distinct?: boolean;\n distinctVariables?: string[];\n}\n\nexport type RdfBindExpression =\n | { type: 'term'; term: Term }\n | { type: 'variable'; variable: string }\n | { type: 'stringValue'; variable: string }\n | { type: 'stringLength'; variable: string }\n | { type: 'lowerCase'; expression: RdfBindExpression }\n | { type: 'upperCase'; expression: RdfBindExpression }\n | { type: 'coalesce'; expressions: RdfBindExpression[] }\n | { type: 'if'; condition: RdfQueryFilter[]; then: RdfBindExpression; else: RdfBindExpression }\n | {\n type: 'substring';\n expression: RdfBindExpression;\n start: RdfBindExpression;\n length?: RdfBindExpression;\n }\n | { type: 'concat'; expressions: RdfBindExpression[] }\n | { type: 'iri'; expression: RdfBindExpression; base: string }\n | { type: 'strdt'; lexical: RdfBindExpression; datatype: RdfBindExpression }\n | { type: 'strlang'; lexical: RdfBindExpression; language: RdfBindExpression };\n\nexport interface RdfQueryBind {\n variable: string;\n expression: RdfBindExpression;\n}\n\nexport type RdfQueryFilterOperator =\n | '$eq'\n | '$ne'\n | '$gt'\n | '$gte'\n | '$lt'\n | '$lte'\n | '$in'\n | '$notIn'\n | '$startsWith'\n | '$contains'\n | '$endsWith'\n | '$regex'\n | '$notStartsWith'\n | '$notContains'\n | '$notEndsWith'\n | '$notRegex'\n | '$bound'\n | '$termType'\n | '$notTermType'\n | '$sameTerm'\n | '$notSameTerm'\n | '$lang'\n | '$notLang'\n | '$langIn'\n | '$notLangIn'\n | '$langMatches'\n | '$notLangMatches'\n | '$datatype'\n | '$notDatatype'\n | '$datatypeIn'\n | '$notDatatypeIn';\n\nexport type RdfQueryFilterValue = Term | string | number | boolean;\n\nexport interface RdfQueryFilter {\n variable: string;\n operator: RdfQueryFilterOperator;\n operand?: 'stringLength' | 'stringValue' | 'lowerStringValue' | 'upperStringValue';\n value?: RdfQueryFilterValue;\n values?: RdfQueryFilterValue[];\n variable2?: string;\n flags?: string;\n source?: 'filter' | 'values';\n}\n\nexport interface RdfValuesBindingSource {\n variables: string[];\n rows: RdfBindingRow[];\n}\n\nexport interface RdfSearchScope {\n workspace?: string;\n sourcePrefix?: string;\n}\n\nexport interface RdfTextSearchPattern {\n query: string;\n scope?: RdfSearchScope;\n limit?: number;\n offset?: number;\n orderBy?: RdfTextSearchOrder[];\n source?: string;\n chunk?: string;\n content?: string;\n heading?: string;\n score?: string;\n workspace?: string;\n localPath?: string;\n contentType?: string;\n ordinal?: string;\n level?: string;\n startOffset?: string;\n endOffset?: string;\n}\n\nexport type RdfVectorDistanceMetric = 'cosine' | 'dot' | 'euclidean';\n\nexport type RdfSearchOrderDirection = 'asc' | 'desc';\nexport type RdfTextSearchOrderField = 'score' | 'source' | 'localPath' | 'ordinal' | 'startOffset' | 'endOffset';\nexport type RdfVectorSearchOrderField = RdfTextSearchOrderField | 'distance';\n\nexport interface RdfTextSearchOrder {\n field: RdfTextSearchOrderField;\n direction?: RdfSearchOrderDirection;\n}\n\nexport interface RdfVectorSearchOrder {\n field: RdfVectorSearchOrderField;\n direction?: RdfSearchOrderDirection;\n}\n\nexport interface RdfVectorSearchPattern {\n embedding: number[];\n metric?: RdfVectorDistanceMetric;\n vectorModel?: string;\n scope?: RdfSearchScope;\n limit?: number;\n offset?: number;\n threshold?: number;\n orderBy?: RdfVectorSearchOrder[];\n source?: string;\n chunk?: string;\n content?: string;\n heading?: string;\n score?: string;\n distance?: string;\n workspace?: string;\n localPath?: string;\n contentType?: string;\n ordinal?: string;\n level?: string;\n startOffset?: string;\n endOffset?: string;\n model?: string;\n}\n\nexport interface RdfLocalQuery {\n patterns: RdfQueryPattern[];\n values?: RdfValuesBindingSource[];\n textSearch?: RdfTextSearchPattern[];\n vectorSearch?: RdfVectorSearchPattern[];\n unions?: RdfUnionQueryGroup[];\n minus?: RdfMinusQueryGroup[];\n exists?: RdfExistsQueryGroup[];\n optional?: Array<RdfQueryPattern[] | RdfOptionalQueryGroup>;\n binds?: RdfQueryBind[];\n filters?: RdfQueryFilter[];\n having?: RdfQueryFilter[];\n select?: string[];\n distinct?: boolean;\n groupBy?: string[];\n aggregates?: RdfQueryAggregate[];\n aggregate?: RdfQueryAggregate;\n orderBy?: RdfQueryOrder[];\n limit?: number;\n offset?: number;\n}\n\nexport interface RdfUnionQueryBranch {\n patterns: RdfQueryPattern[];\n values?: RdfValuesBindingSource[];\n unions?: RdfUnionQueryGroup[];\n optional?: Array<RdfQueryPattern[] | RdfOptionalQueryGroup>;\n binds?: RdfQueryBind[];\n filters?: RdfQueryFilter[];\n}\n\nexport interface RdfUnionQueryGroup {\n branches: RdfUnionQueryBranch[];\n}\n\nexport interface RdfMinusQueryGroup {\n patterns: RdfQueryPattern[];\n values?: RdfValuesBindingSource[];\n unions?: RdfUnionQueryGroup[];\n optional?: Array<RdfQueryPattern[] | RdfOptionalQueryGroup>;\n binds?: RdfQueryBind[];\n filters?: RdfQueryFilter[];\n}\n\nexport interface RdfExistsQueryGroup {\n patterns: RdfQueryPattern[];\n values?: RdfValuesBindingSource[];\n unions?: RdfUnionQueryGroup[];\n optional?: Array<RdfQueryPattern[] | RdfOptionalQueryGroup>;\n binds?: RdfQueryBind[];\n filters?: RdfQueryFilter[];\n}\n\nexport interface RdfOptionalQueryGroup {\n patterns: RdfQueryPattern[];\n values?: RdfValuesBindingSource[];\n unions?: RdfUnionQueryGroup[];\n optional?: Array<RdfQueryPattern[] | RdfOptionalQueryGroup>;\n minus?: RdfMinusQueryGroup[];\n exists?: RdfExistsQueryGroup[];\n binds?: RdfQueryBind[];\n filters?: RdfQueryFilter[];\n}\n\nexport type RdfBindingRow = Record<string, Term>;\n\nexport interface RdfQuadJoinScanResult {\n bindings: RdfBindingRow[];\n metrics: RdfIndexMetrics;\n}\n\nexport interface RdfQuadJoinGroupAggregateOptions {\n groupBy: string[];\n aggregates: RdfQueryAggregate[];\n having?: RdfQuadJoinGroupAggregateHaving[];\n orderBy?: RdfQuadJoinOrder[];\n limit?: number;\n offset?: number;\n}\n\nexport type RdfQuadJoinGroupCountOptions = RdfQuadJoinGroupAggregateOptions;\n\nexport interface RdfLocalQueryMetrics {\n engine: 'solid-rdf';\n plan: string[];\n scannedRows: number;\n joinedRows: number;\n returnedRows: number;\n durationMs: number;\n indexChoices: string[];\n cardinalityEstimates?: number;\n distinctCardinalityEstimates?: number;\n searchCardinalityEstimates?: number;\n filtersApplied: number;\n filtersPushedDown: number;\n}\n\nexport interface RdfLocalQueryResult {\n bindings: RdfBindingRow[];\n count?: number;\n metrics: RdfLocalQueryMetrics;\n}\n\nexport interface RdfEngineLike {\n open(): void | Promise<void>;\n close(): void | Promise<void>;\n put(quads: Quad | Quad[], options?: RdfIndexPutOptions): void | Promise<void>;\n replaceSource(quads: Quad[], source: RdfSourceInput): void | Promise<void>;\n deleteSource(source: string): number | Promise<number>;\n delete(pattern: QuintPattern): number | Promise<number>;\n applyDelta(\n deletes: QuintPattern[],\n inserts: Quad[],\n options?: RdfIndexPutOptions,\n ): { deletedRows: number; insertedRows: number } | Promise<{ deletedRows: number; insertedRows: number }>;\n scan(query: RdfPatternQuery): RdfQuadIndexScanResult | Promise<RdfQuadIndexScanResult>;\n query(query: RdfLocalQuery): RdfLocalQueryResult | Promise<RdfLocalQueryResult>;\n refreshDerivedIndexes(): RdfDerivedIndexRefreshResult | Promise<RdfDerivedIndexRefreshResult>;\n storageStats(): RdfEngineStorageStats | Promise<RdfEngineStorageStats>;\n}\n\nexport type RdfQueryPatternKey = TermName;\n\nexport interface RdfShadowDiff {\n missingFromPrimary: string[];\n extraInPrimary: string[];\n}\n\nexport interface RdfShadowBackfillOptions {\n clear?: boolean;\n batchSize?: number;\n}\n\nexport interface RdfShadowBackfillResult {\n scannedRows: number;\n indexedRows: number;\n batchCount: number;\n durationMs: number;\n}\n\nexport interface RdfShadowScanResult {\n matched: boolean;\n orderedMatch: boolean;\n primary: Quad[];\n compatibility: Quad[];\n diff: RdfShadowDiff;\n metrics: RdfIndexMetrics;\n}\n\nexport interface RdfTextIndexOptions {\n path: string;\n}\n\nexport interface RdfTextSourceInput {\n source: string;\n workspace: string;\n localPath?: string;\n contentType?: string;\n sourceVersion?: string;\n sourceHash?: string;\n}\n\nexport interface RdfTextChunkInput {\n chunkKey: string;\n ordinal: number;\n level: number;\n heading?: string;\n path?: string[];\n content: string;\n startOffset: number;\n endOffset: number;\n}\n\nexport interface RdfTextChunkRow {\n id: number;\n source_id: number;\n source: string;\n workspace: string;\n local_path: string | null;\n content_type: string | null;\n source_version: string | null;\n source_hash: string | null;\n chunk_key: string;\n ordinal: number;\n level: number;\n heading: string | null;\n path: string | null;\n content: string;\n start_offset: number;\n end_offset: number;\n normalized_text: string;\n token_count: number;\n updated_at: string;\n}\n\nexport interface RdfTextSearchOptions {\n query: string;\n source?: string;\n workspace?: string;\n sourcePrefix?: string;\n limit?: number;\n offset?: number;\n orderBy?: RdfTextSearchOrder[];\n}\n\nexport interface RdfTextSearchResult {\n source: string;\n workspace: string;\n localPath?: string;\n contentType?: string;\n sourceVersion?: string;\n sourceHash?: string;\n chunkKey: string;\n ordinal: number;\n level: number;\n heading?: string;\n path: string[];\n content: string;\n startOffset: number;\n endOffset: number;\n score: number;\n}\n\nexport interface RdfSearchCardinalityEstimate {\n rows: number;\n source: 'text-normalized-scan' | 'text-term-posting' | 'vector-candidate-count' | 'vector-component-score';\n indexChoice: string;\n}\n\nexport interface RdfTextIndexStats {\n sourceCount: number;\n chunkCount: number;\n databaseBytes: number;\n termDocumentFrequency: RdfTextTermDocumentFrequency[];\n}\n\nexport interface RdfTextTermDocumentFrequency {\n term: string;\n sourceCount: number;\n chunkCount: number;\n totalOccurrences: number;\n}\n\nexport interface RdfVectorIndexOptions {\n path: string;\n defaultMetric?: RdfVectorDistanceMetric;\n}\n\nexport interface RdfVectorSourceInput {\n source: string;\n workspace: string;\n localPath?: string;\n contentType?: string;\n sourceVersion?: string;\n sourceHash?: string;\n}\n\nexport interface RdfVectorChunkInput {\n chunkKey: string;\n ordinal: number;\n level: number;\n embedding: number[];\n model?: string;\n heading?: string;\n path?: string[];\n content: string;\n startOffset: number;\n endOffset: number;\n}\n\nexport interface RdfVectorChunkRow {\n id: number;\n source_id: number;\n source: string;\n workspace: string;\n local_path: string | null;\n content_type: string | null;\n source_version: string | null;\n source_hash: string | null;\n chunk_key: string;\n ordinal: number;\n level: number;\n heading: string | null;\n path: string | null;\n content: string;\n start_offset: number;\n end_offset: number;\n embedding_json: string;\n dimensions: number;\n magnitude: number;\n model: string;\n updated_at: string;\n}\n\nexport interface RdfVectorSearchOptions {\n embedding: number[];\n metric?: RdfVectorDistanceMetric;\n model?: string;\n source?: string;\n workspace?: string;\n sourcePrefix?: string;\n limit?: number;\n offset?: number;\n threshold?: number;\n orderBy?: RdfVectorSearchOrder[];\n}\n\nexport interface RdfVectorSearchResult {\n source: string;\n workspace: string;\n localPath?: string;\n contentType?: string;\n sourceVersion?: string;\n sourceHash?: string;\n chunkKey: string;\n ordinal: number;\n level: number;\n heading?: string;\n path: string[];\n content: string;\n startOffset: number;\n endOffset: number;\n embedding: number[];\n model?: string;\n score: number;\n distance: number;\n}\n\nexport interface RdfVectorIndexStats {\n sourceCount: number;\n chunkCount: number;\n componentCount: number;\n databaseBytes: number;\n modelDistribution: RdfVectorModelDistribution[];\n}\n\nexport interface RdfVectorModelDistribution {\n model: string;\n dimensions: number;\n sourceCount: number;\n chunkCount: number;\n minMagnitude: number;\n maxMagnitude: number;\n averageMagnitude: number;\n}\n\nexport interface RdfTermSelection {\n sql: string;\n params: unknown[];\n indexHint: string;\n}\n\nexport interface RdfTermLookup {\n id: number;\n term: Term;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/storage/rdf/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Quad, Term } from '@rdfjs/types';\nimport type { QueryOptions, QuintPattern, TermMatch, TermName } from '../quint/types';\n\nexport type RdfTermKind = 'iri' | 'literal' | 'blank' | 'default_graph';\n\nexport interface RdfTermRow {\n id: number;\n kind: RdfTermKind;\n value: string;\n value_head: string;\n datatype_id: number | null;\n lang: string | null;\n hash: string;\n normalized_text: string | null;\n numeric_value: number | null;\n created_at: string;\n}\n\nexport interface RdfSourceInput {\n source: string;\n workspace: string;\n localPath?: string;\n contentType?: string;\n sourceVersion?: string;\n}\n\nexport interface RdfSourceRow {\n id: number;\n source: string;\n workspace: string;\n local_path: string | null;\n content_type: string | null;\n last_indexed_at: string | null;\n source_version: string | null;\n}\n\nexport interface RdfQuadRow {\n graph_id: number;\n subject_id: number;\n predicate_id: number;\n object_id: number;\n source_file_id: number | null;\n source_line_no: number | null;\n}\n\nexport interface RdfQuadIndexOptions {\n path: string;\n debug?: boolean;\n}\n\nexport type RdfDerivedIndexProfile = 'baseline' | 'rdf3x';\n\nexport interface RdfShadowAutoBackfillOptions {\n enabled?: boolean;\n clear?: boolean;\n batchSize?: number;\n}\n\nexport interface RdfIndexPutOptions {\n source?: RdfSourceInput;\n sourceLineNo?: number;\n}\n\nexport interface RdfIndexMetrics {\n engine: 'solid-rdf';\n indexChoice: string;\n /** Rows matched before LIMIT/OFFSET are applied. */\n matchedRows: number;\n returnedRows: number;\n durationMs: number;\n queryPlan?: string[];\n}\n\nexport interface RdfCardinalityEstimate {\n rows: number;\n source:\n | 'exact-count'\n | 'cached-exact-count'\n | 'exact-distinct-count'\n | 'cached-exact-distinct-count'\n | 'exact-distinct-tuple-count'\n | 'cached-exact-distinct-tuple-count';\n indexChoice: string;\n}\n\nexport interface RdfIndexStats {\n termCount: number;\n quadCount: number;\n sourceCount: number;\n graphCount: number;\n databaseBytes: number;\n tableBytes: number;\n indexBytes: number;\n spaceObjects: RdfIndexSpaceObject[];\n serializedTermTextBytes: number;\n literalDatatypeDistribution: RdfLiteralDatatypeDistribution[];\n cardinalityDistributions: RdfCardinalityDistributions;\n}\n\nexport interface RdfEngineStorageStats {\n derivedIndexProfile: RdfDerivedIndexProfile;\n facts: RdfIndexStats;\n rdf3x?: {\n stats: Rdf3xIndexStats;\n syncedWithFacts: boolean;\n };\n factsBytes: number;\n derivedBytes: number;\n totalBytes: number;\n derivedToFactsRatio: number;\n totalToFactsRatio: number;\n}\n\nexport interface RdfDerivedIndexRefreshResult {\n derivedIndexProfile: RdfDerivedIndexProfile;\n factsDataVersion: number;\n rdf3x?: {\n refreshed: boolean;\n previousFactsDataVersion: number;\n factsDataVersion: number;\n syncedWithFacts: boolean;\n rebuild?: Rdf3xRebuildResult;\n };\n}\n\nexport interface RdfIndexSpaceObject {\n name: string;\n kind: 'table' | 'index' | 'internal' | 'unknown';\n tableName?: string;\n bytes: number;\n pages: number;\n estimated?: boolean;\n}\n\nexport interface RdfLiteralDatatypeDistribution {\n datatype: string;\n termCount: number;\n objectQuadCount: number;\n}\n\nexport interface RdfCardinalityTerm {\n value: string;\n kind: RdfTermKind;\n datatype?: string;\n language?: string;\n}\n\nexport interface RdfGraphCardinality {\n graph: RdfCardinalityTerm;\n quadCount: number;\n distinctSubjects: number;\n distinctPredicates: number;\n distinctObjects: number;\n}\n\nexport interface RdfPredicateCardinality {\n predicate: RdfCardinalityTerm;\n quadCount: number;\n graphCount: number;\n distinctSubjects: number;\n distinctObjects: number;\n}\n\nexport interface RdfPredicateObjectCardinality {\n predicate: RdfCardinalityTerm;\n object: RdfCardinalityTerm;\n quadCount: number;\n graphCount: number;\n distinctSubjects: number;\n}\n\nexport interface RdfSubjectPredicateCardinality {\n subject: RdfCardinalityTerm;\n predicate: RdfCardinalityTerm;\n quadCount: number;\n graphCount: number;\n distinctObjects: number;\n}\n\nexport interface RdfCardinalityDistributions {\n graphs: RdfGraphCardinality[];\n predicates: RdfPredicateCardinality[];\n predicateObjects: RdfPredicateObjectCardinality[];\n subjectPredicates: RdfSubjectPredicateCardinality[];\n}\n\nexport interface RdfQuadIndexScanResult {\n quads: Quad[];\n metrics: RdfIndexMetrics;\n}\n\nexport type Rdf3xTermKey = 'subject' | 'predicate' | 'object';\nexport type Rdf3xPatternKey = 'graph' | Rdf3xTermKey;\nexport type Rdf3xPermutationName = 'SPO' | 'SOP' | 'PSO' | 'POS' | 'OSP' | 'OPS';\nexport type Rdf3xPairProjectionName = 'SP' | 'SO' | 'PS' | 'PO' | 'OS' | 'OP';\nexport type Rdf3xTermProjectionName = 'S' | 'P' | 'O';\n\nexport interface Rdf3xIndexOptions {\n path: string;\n debug?: boolean;\n}\n\nexport interface Rdf3xGraphPrefixPattern {\n $startsWith: string;\n}\n\nexport interface Rdf3xTermInPattern {\n $in: Term[];\n}\n\nexport interface Rdf3xTermNotInPattern {\n $notIn: Term[];\n}\n\nexport type Rdf3xTermTypePatternValue = 'iri' | 'blank' | 'literal' | 'numeric';\n\nexport interface Rdf3xTermMetadataPattern {\n $termType?: Rdf3xTermTypePatternValue;\n $language?: string;\n $notLanguage?: string;\n $langMatches?: string;\n $datatype?: Term;\n $notDatatype?: Term;\n}\n\nexport interface Rdf3xObjectRangePattern {\n $gt?: Term | string | number;\n $gte?: Term | string | number;\n $lt?: Term | string | number;\n $lte?: Term | string | number;\n}\n\nexport type Rdf3xNumericObjectRangePattern = Rdf3xObjectRangePattern;\n\nexport interface Rdf3xObjectTextSearchPattern {\n $contains?: string;\n $endsWith?: string;\n}\n\nexport interface Rdf3xObjectOperatorPattern extends Rdf3xObjectRangePattern, Rdf3xObjectTextSearchPattern, Rdf3xTermMetadataPattern {}\n\nexport interface Rdf3xTriplePattern {\n graph?: Term | Rdf3xGraphPrefixPattern | Rdf3xTermInPattern | Rdf3xTermNotInPattern | Rdf3xTermMetadataPattern;\n subject?: Term | Rdf3xTermInPattern | Rdf3xTermNotInPattern | Rdf3xTermMetadataPattern;\n predicate?: Term | Rdf3xTermInPattern | Rdf3xTermNotInPattern | Rdf3xTermMetadataPattern;\n object?: Term | Rdf3xObjectOperatorPattern | Rdf3xTermInPattern | Rdf3xTermNotInPattern;\n}\n\nexport interface Rdf3xTripleScanOptions {\n order?: Array<'graph' | 'subject' | 'predicate' | 'object'>;\n orderDirections?: Array<'asc' | 'desc'>;\n reverse?: boolean;\n limit?: number;\n offset?: number;\n}\n\nexport interface Rdf3xIndexMetrics {\n engine: 'solid-rdf3x';\n indexChoice: Rdf3xPermutationName | 'source-membership' | 'none';\n matchedRows: number;\n returnedRows: number;\n durationMs: number;\n queryPlan?: string[];\n}\n\nexport interface Rdf3xTripleScanResult {\n quads: Quad[];\n metrics: Rdf3xIndexMetrics;\n}\n\nexport interface Rdf3xCountResult {\n count: number;\n metrics: Rdf3xIndexMetrics;\n}\n\nexport interface Rdf3xJoinOptions {\n orderBy?: RdfQuadJoinOrder[];\n limit?: number;\n offset?: number;\n project?: string[];\n distinct?: boolean;\n countMatchedRows?: boolean;\n values?: RdfValuesBindingSource[];\n}\n\nexport interface Rdf3xJoinMetrics {\n engine: 'solid-rdf3x';\n indexChoice: string;\n matchedRows: number;\n returnedRows: number;\n durationMs: number;\n queryPlan?: string[];\n}\n\nexport interface Rdf3xJoinScanResult {\n bindings: RdfBindingRow[];\n metrics: Rdf3xJoinMetrics;\n}\n\nexport interface Rdf3xRebuildResult {\n scannedQuads: number;\n uniqueTriples: number;\n memberships: number;\n projectionRows: number;\n factsDataVersion: number;\n durationMs: number;\n}\n\nexport interface Rdf3xCardinalityEstimate {\n uniqueTriples: number;\n matchingQuads: number;\n source: 'projection-stat' | 'term-stat' | 'exact-triple' | 'exact-membership' | 'full-count';\n indexChoice: Rdf3xPermutationName | 'source-membership' | 'none';\n}\n\nexport interface Rdf3xIndexStats {\n uniqueTriples: number;\n membershipCount: number;\n graphCount: number;\n factsDataVersion: number;\n permutationRows: Record<Rdf3xPermutationName, number>;\n pairProjectionRows: Record<Rdf3xPairProjectionName, number>;\n termProjectionRows: Record<Rdf3xTermProjectionName, number>;\n databaseBytes: number;\n tableBytes: number;\n indexBytes: number;\n spaceObjects: RdfIndexSpaceObject[];\n}\n\nexport interface Rdf3xShadowBindingDiff {\n missingFromRdf3x: string[];\n extraInRdf3x: string[];\n}\n\nexport interface Rdf3xShadowQuadDiff {\n missingFromRdf3x: string[];\n extraInRdf3x: string[];\n}\n\nexport interface Rdf3xShadowScanResult {\n matched: boolean;\n orderedMatch: boolean;\n primary: Quad[];\n rdf3x: Quad[];\n diff: Rdf3xShadowQuadDiff;\n primaryMetrics: RdfIndexMetrics;\n rdf3xMetrics: Rdf3xIndexMetrics;\n rebuild: Rdf3xRebuildResult;\n}\n\nexport interface Rdf3xShadowJoinResult {\n matched: boolean;\n orderedMatch: boolean;\n primary: RdfBindingRow[];\n rdf3x: RdfBindingRow[];\n diff: Rdf3xShadowBindingDiff;\n primaryMetrics: RdfIndexMetrics;\n rdf3xMetrics: Rdf3xJoinMetrics;\n rebuild: Rdf3xRebuildResult;\n}\n\nexport type RdfQuadTupleConstraint = Partial<Record<RdfQueryPatternKey, Term>>;\n\nexport interface RdfQuadTupleConstraintSource {\n columns: RdfQueryPatternKey[];\n rows: RdfQuadTupleConstraint[];\n}\n\nexport interface RdfQuadJoinPattern {\n pattern: QuintPattern;\n variables: Partial<Record<RdfQueryPatternKey, string>>;\n}\n\nexport interface RdfQuadJoinOrder {\n variable: string;\n direction?: 'asc' | 'desc';\n}\n\nexport interface RdfQuadScanOptions extends QueryOptions {\n orderDirections?: Array<'asc' | 'desc'>;\n}\n\nexport interface RdfQuadJoinOptions {\n orderBy?: RdfQuadJoinOrder[];\n limit?: number;\n offset?: number;\n project?: string[];\n distinct?: boolean;\n countMatchedRows?: boolean;\n}\n\nexport interface RdfQuadJoinAggregateOptions {\n aggregates: RdfQueryAggregate[];\n}\n\nexport type RdfQuadJoinCountOptions = RdfQuadJoinAggregateOptions;\n\nexport interface RdfQuadJoinGroupAggregateHaving {\n aggregate: string;\n operator: '$eq' | '$ne' | '$gt' | '$gte' | '$lt' | '$lte';\n value: number;\n}\n\nexport type RdfQuadJoinGroupCountHaving = RdfQuadJoinGroupAggregateHaving;\n\nexport interface RdfPatternQuery {\n pattern: QuintPattern;\n options?: QueryOptions;\n}\n\nexport interface RdfQueryVariable {\n variable: string;\n}\n\nexport type RdfQueryTermPattern = TermMatch | RdfQueryVariable;\n\nexport interface RdfQueryPattern {\n graph?: RdfQueryTermPattern;\n subject?: RdfQueryTermPattern;\n predicate?: RdfQueryTermPattern;\n object?: RdfQueryTermPattern;\n}\n\nexport interface RdfConstructTemplate {\n subject: RdfQueryTermPattern;\n predicate: RdfQueryTermPattern;\n object: RdfQueryTermPattern;\n}\n\nexport interface RdfQueryOrder {\n variable: string;\n direction?: 'asc' | 'desc';\n}\n\nexport type RdfQueryAggregateType = 'count' | 'sum' | 'avg' | 'min' | 'max';\n\nexport interface RdfQueryAggregate {\n type: RdfQueryAggregateType;\n as: string;\n variable?: string;\n distinct?: boolean;\n distinctVariables?: string[];\n}\n\nexport type RdfBindExpression =\n | { type: 'term'; term: Term }\n | { type: 'variable'; variable: string }\n | { type: 'stringValue'; variable: string }\n | { type: 'stringLength'; variable: string }\n | { type: 'lowerCase'; expression: RdfBindExpression }\n | { type: 'upperCase'; expression: RdfBindExpression }\n | { type: 'coalesce'; expressions: RdfBindExpression[] }\n | { type: 'if'; condition: RdfQueryFilter[]; then: RdfBindExpression; else: RdfBindExpression }\n | {\n type: 'substring';\n expression: RdfBindExpression;\n start: RdfBindExpression;\n length?: RdfBindExpression;\n }\n | { type: 'concat'; expressions: RdfBindExpression[] }\n | { type: 'iri'; expression: RdfBindExpression; base: string }\n | { type: 'strdt'; lexical: RdfBindExpression; datatype: RdfBindExpression }\n | { type: 'strlang'; lexical: RdfBindExpression; language: RdfBindExpression };\n\nexport interface RdfQueryBind {\n variable: string;\n expression: RdfBindExpression;\n}\n\nexport type RdfQueryFilterOperator =\n | '$eq'\n | '$ne'\n | '$gt'\n | '$gte'\n | '$lt'\n | '$lte'\n | '$in'\n | '$notIn'\n | '$startsWith'\n | '$contains'\n | '$endsWith'\n | '$regex'\n | '$notStartsWith'\n | '$notContains'\n | '$notEndsWith'\n | '$notRegex'\n | '$bound'\n | '$termType'\n | '$notTermType'\n | '$sameTerm'\n | '$notSameTerm'\n | '$lang'\n | '$notLang'\n | '$langIn'\n | '$notLangIn'\n | '$langMatches'\n | '$notLangMatches'\n | '$datatype'\n | '$notDatatype'\n | '$datatypeIn'\n | '$notDatatypeIn';\n\nexport type RdfQueryFilterValue = Term | string | number | boolean;\n\nexport interface RdfQueryFilter {\n variable: string;\n operator: RdfQueryFilterOperator;\n operand?: 'stringLength' | 'stringValue' | 'lowerStringValue' | 'upperStringValue';\n value?: RdfQueryFilterValue;\n values?: RdfQueryFilterValue[];\n variable2?: string;\n flags?: string;\n source?: 'filter' | 'values';\n}\n\nexport interface RdfValuesBindingSource {\n variables: string[];\n rows: RdfBindingRow[];\n}\n\nexport interface RdfSearchScope {\n workspace?: string;\n sourcePrefix?: string;\n}\n\nexport interface RdfTextSearchPattern {\n query: string;\n scope?: RdfSearchScope;\n limit?: number;\n offset?: number;\n orderBy?: RdfTextSearchOrder[];\n source?: string;\n chunk?: string;\n content?: string;\n heading?: string;\n score?: string;\n workspace?: string;\n localPath?: string;\n contentType?: string;\n ordinal?: string;\n level?: string;\n startOffset?: string;\n endOffset?: string;\n}\n\nexport type RdfVectorDistanceMetric = 'cosine' | 'dot' | 'euclidean';\n\nexport type RdfSearchOrderDirection = 'asc' | 'desc';\nexport type RdfTextSearchOrderField = 'score' | 'source' | 'localPath' | 'ordinal' | 'startOffset' | 'endOffset';\nexport type RdfVectorSearchOrderField = RdfTextSearchOrderField | 'distance';\n\nexport interface RdfTextSearchOrder {\n field: RdfTextSearchOrderField;\n direction?: RdfSearchOrderDirection;\n}\n\nexport interface RdfVectorSearchOrder {\n field: RdfVectorSearchOrderField;\n direction?: RdfSearchOrderDirection;\n}\n\nexport interface RdfVectorSearchPattern {\n embedding: number[];\n metric?: RdfVectorDistanceMetric;\n vectorModel?: string;\n scope?: RdfSearchScope;\n limit?: number;\n offset?: number;\n threshold?: number;\n orderBy?: RdfVectorSearchOrder[];\n source?: string;\n chunk?: string;\n content?: string;\n heading?: string;\n score?: string;\n distance?: string;\n workspace?: string;\n localPath?: string;\n contentType?: string;\n ordinal?: string;\n level?: string;\n startOffset?: string;\n endOffset?: string;\n model?: string;\n}\n\nexport interface RdfQuery {\n patterns: RdfQueryPattern[];\n values?: RdfValuesBindingSource[];\n textSearch?: RdfTextSearchPattern[];\n vectorSearch?: RdfVectorSearchPattern[];\n unions?: RdfUnionQueryGroup[];\n minus?: RdfMinusQueryGroup[];\n exists?: RdfExistsQueryGroup[];\n optional?: Array<RdfQueryPattern[] | RdfOptionalQueryGroup>;\n binds?: RdfQueryBind[];\n filters?: RdfQueryFilter[];\n having?: RdfQueryFilter[];\n select?: string[];\n distinct?: boolean;\n groupBy?: string[];\n aggregates?: RdfQueryAggregate[];\n aggregate?: RdfQueryAggregate;\n orderBy?: RdfQueryOrder[];\n limit?: number;\n offset?: number;\n}\n\nexport interface RdfUnionQueryBranch {\n patterns: RdfQueryPattern[];\n values?: RdfValuesBindingSource[];\n unions?: RdfUnionQueryGroup[];\n optional?: Array<RdfQueryPattern[] | RdfOptionalQueryGroup>;\n binds?: RdfQueryBind[];\n filters?: RdfQueryFilter[];\n}\n\nexport interface RdfUnionQueryGroup {\n branches: RdfUnionQueryBranch[];\n}\n\nexport interface RdfMinusQueryGroup {\n patterns: RdfQueryPattern[];\n values?: RdfValuesBindingSource[];\n unions?: RdfUnionQueryGroup[];\n optional?: Array<RdfQueryPattern[] | RdfOptionalQueryGroup>;\n binds?: RdfQueryBind[];\n filters?: RdfQueryFilter[];\n}\n\nexport interface RdfExistsQueryGroup {\n patterns: RdfQueryPattern[];\n values?: RdfValuesBindingSource[];\n unions?: RdfUnionQueryGroup[];\n optional?: Array<RdfQueryPattern[] | RdfOptionalQueryGroup>;\n binds?: RdfQueryBind[];\n filters?: RdfQueryFilter[];\n}\n\nexport interface RdfOptionalQueryGroup {\n patterns: RdfQueryPattern[];\n values?: RdfValuesBindingSource[];\n unions?: RdfUnionQueryGroup[];\n optional?: Array<RdfQueryPattern[] | RdfOptionalQueryGroup>;\n minus?: RdfMinusQueryGroup[];\n exists?: RdfExistsQueryGroup[];\n binds?: RdfQueryBind[];\n filters?: RdfQueryFilter[];\n}\n\nexport type RdfBindingRow = Record<string, Term>;\n\nexport interface RdfQuadJoinScanResult {\n bindings: RdfBindingRow[];\n metrics: RdfIndexMetrics;\n}\n\nexport interface RdfQuadJoinGroupAggregateOptions {\n groupBy: string[];\n aggregates: RdfQueryAggregate[];\n having?: RdfQuadJoinGroupAggregateHaving[];\n orderBy?: RdfQuadJoinOrder[];\n limit?: number;\n offset?: number;\n}\n\nexport type RdfQuadJoinGroupCountOptions = RdfQuadJoinGroupAggregateOptions;\n\nexport interface RdfQueryMetrics {\n engine: 'solid-rdf';\n plan: string[];\n scannedRows: number;\n joinedRows: number;\n returnedRows: number;\n durationMs: number;\n indexChoices: string[];\n cardinalityEstimates?: number;\n distinctCardinalityEstimates?: number;\n searchCardinalityEstimates?: number;\n filtersApplied: number;\n filtersPushedDown: number;\n}\n\nexport interface RdfQueryResult {\n bindings: RdfBindingRow[];\n count?: number;\n metrics: RdfQueryMetrics;\n}\n\nexport interface RdfEngineLike {\n open(): void | Promise<void>;\n close(): void | Promise<void>;\n put(quads: Quad | Quad[], options?: RdfIndexPutOptions): void | Promise<void>;\n replaceSource(quads: Quad[], source: RdfSourceInput): void | Promise<void>;\n deleteSource(source: string): number | Promise<number>;\n delete(pattern: QuintPattern): number | Promise<number>;\n applyDelta(\n deletes: QuintPattern[],\n inserts: Quad[],\n options?: RdfIndexPutOptions,\n ): { deletedRows: number; insertedRows: number } | Promise<{ deletedRows: number; insertedRows: number }>;\n scan(query: RdfPatternQuery): RdfQuadIndexScanResult | Promise<RdfQuadIndexScanResult>;\n query(query: RdfQuery): RdfQueryResult | Promise<RdfQueryResult>;\n refreshDerivedIndexes(): RdfDerivedIndexRefreshResult | Promise<RdfDerivedIndexRefreshResult>;\n storageStats(): RdfEngineStorageStats | Promise<RdfEngineStorageStats>;\n}\n\nexport type RdfQueryPatternKey = TermName;\n\nexport interface RdfShadowDiff {\n missingFromPrimary: string[];\n extraInPrimary: string[];\n}\n\nexport interface RdfShadowBackfillOptions {\n clear?: boolean;\n batchSize?: number;\n}\n\nexport interface RdfShadowBackfillResult {\n scannedRows: number;\n indexedRows: number;\n batchCount: number;\n durationMs: number;\n}\n\nexport interface RdfShadowScanResult {\n matched: boolean;\n orderedMatch: boolean;\n primary: Quad[];\n compatibility: Quad[];\n diff: RdfShadowDiff;\n metrics: RdfIndexMetrics;\n}\n\nexport interface RdfTextIndexOptions {\n path: string;\n}\n\nexport interface RdfTextSourceInput {\n source: string;\n workspace: string;\n localPath?: string;\n contentType?: string;\n sourceVersion?: string;\n sourceHash?: string;\n}\n\nexport interface RdfTextChunkInput {\n chunkKey: string;\n ordinal: number;\n level: number;\n heading?: string;\n path?: string[];\n content: string;\n startOffset: number;\n endOffset: number;\n}\n\nexport interface RdfTextChunkRow {\n id: number;\n source_id: number;\n source: string;\n workspace: string;\n local_path: string | null;\n content_type: string | null;\n source_version: string | null;\n source_hash: string | null;\n chunk_key: string;\n ordinal: number;\n level: number;\n heading: string | null;\n path: string | null;\n content: string;\n start_offset: number;\n end_offset: number;\n normalized_text: string;\n token_count: number;\n updated_at: string;\n}\n\nexport interface RdfTextSearchOptions {\n query: string;\n source?: string;\n workspace?: string;\n sourcePrefix?: string;\n limit?: number;\n offset?: number;\n orderBy?: RdfTextSearchOrder[];\n}\n\nexport interface RdfTextSearchResult {\n source: string;\n workspace: string;\n localPath?: string;\n contentType?: string;\n sourceVersion?: string;\n sourceHash?: string;\n chunkKey: string;\n ordinal: number;\n level: number;\n heading?: string;\n path: string[];\n content: string;\n startOffset: number;\n endOffset: number;\n score: number;\n}\n\nexport interface RdfSearchCardinalityEstimate {\n rows: number;\n source: 'text-normalized-scan' | 'text-term-posting' | 'vector-candidate-count' | 'vector-component-score';\n indexChoice: string;\n}\n\nexport interface RdfTextIndexStats {\n sourceCount: number;\n chunkCount: number;\n databaseBytes: number;\n termDocumentFrequency: RdfTextTermDocumentFrequency[];\n}\n\nexport interface RdfTextTermDocumentFrequency {\n term: string;\n sourceCount: number;\n chunkCount: number;\n totalOccurrences: number;\n}\n\nexport interface RdfVectorIndexOptions {\n path: string;\n defaultMetric?: RdfVectorDistanceMetric;\n}\n\nexport interface RdfVectorSourceInput {\n source: string;\n workspace: string;\n localPath?: string;\n contentType?: string;\n sourceVersion?: string;\n sourceHash?: string;\n}\n\nexport interface RdfVectorChunkInput {\n chunkKey: string;\n ordinal: number;\n level: number;\n embedding: number[];\n model?: string;\n heading?: string;\n path?: string[];\n content: string;\n startOffset: number;\n endOffset: number;\n}\n\nexport interface RdfVectorChunkRow {\n id: number;\n source_id: number;\n source: string;\n workspace: string;\n local_path: string | null;\n content_type: string | null;\n source_version: string | null;\n source_hash: string | null;\n chunk_key: string;\n ordinal: number;\n level: number;\n heading: string | null;\n path: string | null;\n content: string;\n start_offset: number;\n end_offset: number;\n embedding_json: string;\n dimensions: number;\n magnitude: number;\n model: string;\n updated_at: string;\n}\n\nexport interface RdfVectorSearchOptions {\n embedding: number[];\n metric?: RdfVectorDistanceMetric;\n model?: string;\n source?: string;\n workspace?: string;\n sourcePrefix?: string;\n limit?: number;\n offset?: number;\n threshold?: number;\n orderBy?: RdfVectorSearchOrder[];\n}\n\nexport interface RdfVectorSearchResult {\n source: string;\n workspace: string;\n localPath?: string;\n contentType?: string;\n sourceVersion?: string;\n sourceHash?: string;\n chunkKey: string;\n ordinal: number;\n level: number;\n heading?: string;\n path: string[];\n content: string;\n startOffset: number;\n endOffset: number;\n embedding: number[];\n model?: string;\n score: number;\n distance: number;\n}\n\nexport interface RdfVectorIndexStats {\n sourceCount: number;\n chunkCount: number;\n componentCount: number;\n databaseBytes: number;\n modelDistribution: RdfVectorModelDistribution[];\n}\n\nexport interface RdfVectorModelDistribution {\n model: string;\n dimensions: number;\n sourceCount: number;\n chunkCount: number;\n minMagnitude: number;\n maxMagnitude: number;\n averageMagnitude: number;\n}\n\nexport interface RdfTermSelection {\n sql: string;\n params: unknown[];\n indexHint: string;\n}\n\nexport interface RdfTermLookup {\n id: number;\n term: Term;\n}\n"]}
|
|
@@ -60,7 +60,7 @@ export interface SubdomainServiceOptions {
|
|
|
60
60
|
* 4. DNS 记录管理
|
|
61
61
|
* 5. 隧道创建
|
|
62
62
|
*
|
|
63
|
-
* 注册信息持久化到 EdgeNodeRepository(
|
|
63
|
+
* 注册信息持久化到 EdgeNodeRepository(cluster_node.subdomain 字段)
|
|
64
64
|
*/
|
|
65
65
|
export declare class SubdomainService {
|
|
66
66
|
private readonly baseDomain;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubdomainService.js","sourceRoot":"","sources":["../../src/subdomain/SubdomainService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TunnelProvider, TunnelConfig } from '../tunnel/TunnelProvider';\nimport type { DnsProvider } from '../dns/DnsProvider';\nimport type { EdgeNodeRepository } from '../identity/drizzle/EdgeNodeRepository';\n\n/**\n * 子域名注册信息\n */\nexport interface SubdomainRegistration {\n /** 子域名 (如 mynode) */\n subdomain: string;\n\n /** 完整域名 (如 mynode.pods.undefieds.co) */\n fullDomain: string;\n\n /** 访问模式 */\n mode: 'direct' | 'tunnel';\n\n /** 公网 IP (直连模式) */\n ipv4?: string;\n\n /** 隧道配置 (隧道模式) */\n tunnelConfig?: TunnelConfig;\n\n /** 注册时间 */\n registeredAt: Date;\n\n /** 所有者 ID */\n ownerId?: string;\n\n /** 绑定的节点 ID */\n nodeId?: string;\n}\n\n/**\n * 连通性检测结果\n */\nexport interface ConnectivityResult {\n /** 是否可达 */\n reachable: boolean;\n\n /** 公网 IP */\n ipv4?: string;\n\n /** 延迟 (ms) */\n latency?: number;\n\n /** 错误信息 */\n error?: string;\n}\n\n/**\n * SubdomainService 配置\n */\nexport interface SubdomainServiceOptions {\n /** 基础域名 (如 pods.undefieds.co) */\n baseDomain: string;\n\n /** DNS Provider */\n dnsProvider: DnsProvider;\n\n /** Tunnel Provider */\n tunnelProvider: TunnelProvider;\n\n /** Edge Node Repository (持久化) */\n edgeNodeRepo: EdgeNodeRepository;\n\n /** 保留的子域名列表 */\n reservedSubdomains?: string[];\n}\n\n/**\n * 子域名管理服务\n *\n * 负责:\n * 1. 子域名可用性检查\n * 2. 连通性检测\n * 3. 直连/隧道模式选择\n * 4. DNS 记录管理\n * 5. 隧道创建\n *\n * 注册信息持久化到 EdgeNodeRepository(identity_edge_node.subdomain 字段)\n */\nexport class SubdomainService {\n private readonly baseDomain: string;\n private readonly dnsProvider: DnsProvider;\n private readonly tunnelProvider: TunnelProvider;\n private readonly edgeNodeRepo: EdgeNodeRepository;\n private readonly reservedSubdomains: Set<string>;\n\n constructor(options: SubdomainServiceOptions) {\n this.baseDomain = options.baseDomain;\n this.dnsProvider = options.dnsProvider;\n this.tunnelProvider = options.tunnelProvider;\n this.edgeNodeRepo = options.edgeNodeRepo;\n this.reservedSubdomains = new Set(options.reservedSubdomains ?? [\n 'www', 'api', 'app', 'admin', 'mail', 'ftp', 'ssh',\n 'pods', 'center', 'edge', 'node', 'test', 'dev', 'staging',\n ]);\n }\n\n /**\n * 检查子域名是否可用\n */\n async checkAvailability(subdomain: string): Promise<{\n available: boolean;\n reason?: string;\n }> {\n if (!this.isValidSubdomain(subdomain)) {\n return {\n available: false,\n reason: 'Invalid subdomain format. Use 3-63 lowercase letters, numbers, or hyphens.',\n };\n }\n\n if (this.reservedSubdomains.has(subdomain.toLowerCase())) {\n return {\n available: false,\n reason: 'This subdomain is reserved.',\n };\n }\n\n const existing = await this.edgeNodeRepo.findNodeBySubdomain(subdomain.toLowerCase());\n if (existing) {\n return {\n available: false,\n reason: 'This subdomain is already registered.',\n };\n }\n\n return { available: true };\n }\n\n /**\n * 注册子域名\n */\n async register(options: {\n subdomain: string;\n nodeId: string;\n localPort: number;\n ipv4?: string;\n ownerId?: string;\n }): Promise<SubdomainRegistration> {\n const { subdomain, nodeId, localPort, ipv4, ownerId } = options;\n const normalizedSubdomain = subdomain.toLowerCase();\n\n const availability = await this.checkAvailability(normalizedSubdomain);\n if (!availability.available) {\n throw new Error(availability.reason);\n }\n\n const fullDomain = `${normalizedSubdomain}.${this.baseDomain}`;\n\n // 连通性检测\n let mode: 'direct' | 'tunnel' = 'tunnel';\n let verifiedIp: string | undefined;\n\n if (ipv4) {\n const connectivity = await this.checkConnectivity(ipv4, localPort);\n if (connectivity.reachable) {\n mode = 'direct';\n verifiedIp = ipv4;\n }\n }\n\n // DNS / 隧道\n let tunnelConfig: TunnelConfig | undefined;\n\n if (mode === 'direct') {\n const type = this.isIpv6(verifiedIp!) ? 'AAAA' : 'A';\n await this.dnsProvider.upsertRecord({\n subdomain: normalizedSubdomain,\n domain: this.baseDomain,\n type,\n value: verifiedIp!,\n ttl: 60,\n });\n } else {\n tunnelConfig = await this.tunnelProvider.setup({\n subdomain: normalizedSubdomain,\n localPort,\n });\n }\n\n // 持久化到 EdgeNodeRepository\n await this.edgeNodeRepo.updateNodeMode(nodeId, {\n accessMode: mode === 'direct' ? 'direct' : 'proxy',\n ipv4: verifiedIp,\n publicPort: localPort,\n subdomain: normalizedSubdomain,\n connectivityStatus: mode === 'direct' ? 'reachable' : 'unknown',\n });\n\n return {\n subdomain: normalizedSubdomain,\n fullDomain,\n mode,\n ipv4: verifiedIp,\n tunnelConfig,\n registeredAt: new Date(),\n ownerId,\n nodeId,\n };\n }\n\n /**\n * 释放子域名\n */\n async release(subdomain: string): Promise<void> {\n const normalizedSubdomain = subdomain.toLowerCase();\n const node = await this.edgeNodeRepo.findNodeBySubdomain(normalizedSubdomain);\n\n if (!node) {\n throw new Error('Subdomain not found');\n }\n\n // 获取完整连通性信息以判断模式\n const info = await this.edgeNodeRepo.getNodeConnectivityInfo(node.nodeId);\n const accessMode = info?.accessMode;\n\n // 删除 DNS 记录\n try {\n await this.dnsProvider.deleteRecord({\n subdomain: normalizedSubdomain,\n domain: this.baseDomain,\n type: accessMode === 'direct' ? 'A' : 'CNAME',\n });\n } catch {\n // DNS 删除失败不阻塞释放\n }\n\n // 清除 DB 中的 subdomain\n await this.edgeNodeRepo.updateNodeMode(node.nodeId, {\n accessMode: 'proxy',\n subdomain: undefined,\n connectivityStatus: 'unknown',\n });\n }\n\n /**\n * 获取注册信息\n */\n async getRegistration(subdomain: string): Promise<SubdomainRegistration | undefined> {\n const node = await this.edgeNodeRepo.findNodeBySubdomain(subdomain.toLowerCase());\n if (!node) {\n return undefined;\n }\n return this.nodeToRegistration(node);\n }\n\n /**\n * 获取所有注册\n */\n async getAllRegistrations(): Promise<SubdomainRegistration[]> {\n const nodes = await this.edgeNodeRepo.listNodes();\n const results: SubdomainRegistration[] = [];\n for (const n of nodes) {\n if (!n.metadata || !(n.metadata as Record<string, unknown>).subdomain) {\n // 需要查 connectivity info 获取 subdomain\n const info = await this.edgeNodeRepo.getNodeConnectivityInfo(n.nodeId);\n if (info?.subdomain) {\n results.push({\n subdomain: info.subdomain,\n fullDomain: `${info.subdomain}.${this.baseDomain}`,\n mode: info.accessMode === 'direct' ? 'direct' : 'tunnel',\n ipv4: info.ipv4,\n registeredAt: new Date(),\n nodeId: n.nodeId,\n });\n }\n }\n }\n return results;\n }\n\n /**\n * 启动隧道\n */\n async startTunnel(subdomain: string): Promise<void> {\n const reg = await this.getRegistration(subdomain);\n if (!reg) {\n throw new Error('Subdomain not found');\n }\n if (reg.mode !== 'tunnel' || !reg.tunnelConfig) {\n throw new Error('Subdomain is not in tunnel mode');\n }\n await this.tunnelProvider.start(reg.tunnelConfig);\n }\n\n /**\n * 停止隧道\n */\n async stopTunnel(): Promise<void> {\n await this.tunnelProvider.stop();\n }\n\n // ============ 私有方法 ============\n\n private nodeToRegistration(node: {\n nodeId: string;\n accessMode?: string;\n metadata?: Record<string, unknown> | null;\n subdomain?: string;\n }): SubdomainRegistration {\n const sub = node.subdomain!;\n return {\n subdomain: sub,\n fullDomain: `${sub}.${this.baseDomain}`,\n mode: node.accessMode === 'direct' ? 'direct' : 'tunnel',\n registeredAt: new Date(),\n nodeId: node.nodeId,\n ownerId: (node.metadata as Record<string, unknown> | null)?.ownerId as string | undefined,\n };\n }\n\n private isValidSubdomain(subdomain: string): boolean {\n const regex = /^[a-z0-9]([a-z0-9-]{1,61}[a-z0-9])?$/;\n return regex.test(subdomain) && subdomain.length >= 3 && subdomain.length <= 63;\n }\n\n private async checkConnectivity(\n ip: string,\n port: number,\n ): Promise<ConnectivityResult> {\n try {\n const start = Date.now();\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 5000);\n\n const host = this.isIpv6(ip) ? `[${ip}]` : ip;\n const response = await fetch(`http://${host}:${port}/.well-known/solid`, {\n method: 'HEAD',\n signal: controller.signal,\n });\n\n clearTimeout(timeout);\n\n return {\n reachable: response.ok || response.status === 401,\n ipv4: ip,\n latency: Date.now() - start,\n };\n } catch (error) {\n return {\n reachable: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n private isIpv6(ip: string): boolean {\n return ip.includes(':');\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"SubdomainService.js","sourceRoot":"","sources":["../../src/subdomain/SubdomainService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TunnelProvider, TunnelConfig } from '../tunnel/TunnelProvider';\nimport type { DnsProvider } from '../dns/DnsProvider';\nimport type { EdgeNodeRepository } from '../identity/drizzle/EdgeNodeRepository';\n\n/**\n * 子域名注册信息\n */\nexport interface SubdomainRegistration {\n /** 子域名 (如 mynode) */\n subdomain: string;\n\n /** 完整域名 (如 mynode.pods.undefieds.co) */\n fullDomain: string;\n\n /** 访问模式 */\n mode: 'direct' | 'tunnel';\n\n /** 公网 IP (直连模式) */\n ipv4?: string;\n\n /** 隧道配置 (隧道模式) */\n tunnelConfig?: TunnelConfig;\n\n /** 注册时间 */\n registeredAt: Date;\n\n /** 所有者 ID */\n ownerId?: string;\n\n /** 绑定的节点 ID */\n nodeId?: string;\n}\n\n/**\n * 连通性检测结果\n */\nexport interface ConnectivityResult {\n /** 是否可达 */\n reachable: boolean;\n\n /** 公网 IP */\n ipv4?: string;\n\n /** 延迟 (ms) */\n latency?: number;\n\n /** 错误信息 */\n error?: string;\n}\n\n/**\n * SubdomainService 配置\n */\nexport interface SubdomainServiceOptions {\n /** 基础域名 (如 pods.undefieds.co) */\n baseDomain: string;\n\n /** DNS Provider */\n dnsProvider: DnsProvider;\n\n /** Tunnel Provider */\n tunnelProvider: TunnelProvider;\n\n /** Edge Node Repository (持久化) */\n edgeNodeRepo: EdgeNodeRepository;\n\n /** 保留的子域名列表 */\n reservedSubdomains?: string[];\n}\n\n/**\n * 子域名管理服务\n *\n * 负责:\n * 1. 子域名可用性检查\n * 2. 连通性检测\n * 3. 直连/隧道模式选择\n * 4. DNS 记录管理\n * 5. 隧道创建\n *\n * 注册信息持久化到 EdgeNodeRepository(cluster_node.subdomain 字段)\n */\nexport class SubdomainService {\n private readonly baseDomain: string;\n private readonly dnsProvider: DnsProvider;\n private readonly tunnelProvider: TunnelProvider;\n private readonly edgeNodeRepo: EdgeNodeRepository;\n private readonly reservedSubdomains: Set<string>;\n\n constructor(options: SubdomainServiceOptions) {\n this.baseDomain = options.baseDomain;\n this.dnsProvider = options.dnsProvider;\n this.tunnelProvider = options.tunnelProvider;\n this.edgeNodeRepo = options.edgeNodeRepo;\n this.reservedSubdomains = new Set(options.reservedSubdomains ?? [\n 'www', 'api', 'app', 'admin', 'mail', 'ftp', 'ssh',\n 'pods', 'center', 'edge', 'node', 'test', 'dev', 'staging',\n ]);\n }\n\n /**\n * 检查子域名是否可用\n */\n async checkAvailability(subdomain: string): Promise<{\n available: boolean;\n reason?: string;\n }> {\n if (!this.isValidSubdomain(subdomain)) {\n return {\n available: false,\n reason: 'Invalid subdomain format. Use 3-63 lowercase letters, numbers, or hyphens.',\n };\n }\n\n if (this.reservedSubdomains.has(subdomain.toLowerCase())) {\n return {\n available: false,\n reason: 'This subdomain is reserved.',\n };\n }\n\n const existing = await this.edgeNodeRepo.findNodeBySubdomain(subdomain.toLowerCase());\n if (existing) {\n return {\n available: false,\n reason: 'This subdomain is already registered.',\n };\n }\n\n return { available: true };\n }\n\n /**\n * 注册子域名\n */\n async register(options: {\n subdomain: string;\n nodeId: string;\n localPort: number;\n ipv4?: string;\n ownerId?: string;\n }): Promise<SubdomainRegistration> {\n const { subdomain, nodeId, localPort, ipv4, ownerId } = options;\n const normalizedSubdomain = subdomain.toLowerCase();\n\n const availability = await this.checkAvailability(normalizedSubdomain);\n if (!availability.available) {\n throw new Error(availability.reason);\n }\n\n const fullDomain = `${normalizedSubdomain}.${this.baseDomain}`;\n\n // 连通性检测\n let mode: 'direct' | 'tunnel' = 'tunnel';\n let verifiedIp: string | undefined;\n\n if (ipv4) {\n const connectivity = await this.checkConnectivity(ipv4, localPort);\n if (connectivity.reachable) {\n mode = 'direct';\n verifiedIp = ipv4;\n }\n }\n\n // DNS / 隧道\n let tunnelConfig: TunnelConfig | undefined;\n\n if (mode === 'direct') {\n const type = this.isIpv6(verifiedIp!) ? 'AAAA' : 'A';\n await this.dnsProvider.upsertRecord({\n subdomain: normalizedSubdomain,\n domain: this.baseDomain,\n type,\n value: verifiedIp!,\n ttl: 60,\n });\n } else {\n tunnelConfig = await this.tunnelProvider.setup({\n subdomain: normalizedSubdomain,\n localPort,\n });\n }\n\n // 持久化到 EdgeNodeRepository\n await this.edgeNodeRepo.updateNodeMode(nodeId, {\n accessMode: mode === 'direct' ? 'direct' : 'proxy',\n ipv4: verifiedIp,\n publicPort: localPort,\n subdomain: normalizedSubdomain,\n connectivityStatus: mode === 'direct' ? 'reachable' : 'unknown',\n });\n\n return {\n subdomain: normalizedSubdomain,\n fullDomain,\n mode,\n ipv4: verifiedIp,\n tunnelConfig,\n registeredAt: new Date(),\n ownerId,\n nodeId,\n };\n }\n\n /**\n * 释放子域名\n */\n async release(subdomain: string): Promise<void> {\n const normalizedSubdomain = subdomain.toLowerCase();\n const node = await this.edgeNodeRepo.findNodeBySubdomain(normalizedSubdomain);\n\n if (!node) {\n throw new Error('Subdomain not found');\n }\n\n // 获取完整连通性信息以判断模式\n const info = await this.edgeNodeRepo.getNodeConnectivityInfo(node.nodeId);\n const accessMode = info?.accessMode;\n\n // 删除 DNS 记录\n try {\n await this.dnsProvider.deleteRecord({\n subdomain: normalizedSubdomain,\n domain: this.baseDomain,\n type: accessMode === 'direct' ? 'A' : 'CNAME',\n });\n } catch {\n // DNS 删除失败不阻塞释放\n }\n\n // 清除 DB 中的 subdomain\n await this.edgeNodeRepo.updateNodeMode(node.nodeId, {\n accessMode: 'proxy',\n subdomain: undefined,\n connectivityStatus: 'unknown',\n });\n }\n\n /**\n * 获取注册信息\n */\n async getRegistration(subdomain: string): Promise<SubdomainRegistration | undefined> {\n const node = await this.edgeNodeRepo.findNodeBySubdomain(subdomain.toLowerCase());\n if (!node) {\n return undefined;\n }\n return this.nodeToRegistration(node);\n }\n\n /**\n * 获取所有注册\n */\n async getAllRegistrations(): Promise<SubdomainRegistration[]> {\n const nodes = await this.edgeNodeRepo.listNodes();\n const results: SubdomainRegistration[] = [];\n for (const n of nodes) {\n if (!n.metadata || !(n.metadata as Record<string, unknown>).subdomain) {\n // 需要查 connectivity info 获取 subdomain\n const info = await this.edgeNodeRepo.getNodeConnectivityInfo(n.nodeId);\n if (info?.subdomain) {\n results.push({\n subdomain: info.subdomain,\n fullDomain: `${info.subdomain}.${this.baseDomain}`,\n mode: info.accessMode === 'direct' ? 'direct' : 'tunnel',\n ipv4: info.ipv4,\n registeredAt: new Date(),\n nodeId: n.nodeId,\n });\n }\n }\n }\n return results;\n }\n\n /**\n * 启动隧道\n */\n async startTunnel(subdomain: string): Promise<void> {\n const reg = await this.getRegistration(subdomain);\n if (!reg) {\n throw new Error('Subdomain not found');\n }\n if (reg.mode !== 'tunnel' || !reg.tunnelConfig) {\n throw new Error('Subdomain is not in tunnel mode');\n }\n await this.tunnelProvider.start(reg.tunnelConfig);\n }\n\n /**\n * 停止隧道\n */\n async stopTunnel(): Promise<void> {\n await this.tunnelProvider.stop();\n }\n\n // ============ 私有方法 ============\n\n private nodeToRegistration(node: {\n nodeId: string;\n accessMode?: string;\n metadata?: Record<string, unknown> | null;\n subdomain?: string;\n }): SubdomainRegistration {\n const sub = node.subdomain!;\n return {\n subdomain: sub,\n fullDomain: `${sub}.${this.baseDomain}`,\n mode: node.accessMode === 'direct' ? 'direct' : 'tunnel',\n registeredAt: new Date(),\n nodeId: node.nodeId,\n ownerId: (node.metadata as Record<string, unknown> | null)?.ownerId as string | undefined,\n };\n }\n\n private isValidSubdomain(subdomain: string): boolean {\n const regex = /^[a-z0-9]([a-z0-9-]{1,61}[a-z0-9])?$/;\n return regex.test(subdomain) && subdomain.length >= 3 && subdomain.length <= 63;\n }\n\n private async checkConnectivity(\n ip: string,\n port: number,\n ): Promise<ConnectivityResult> {\n try {\n const start = Date.now();\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 5000);\n\n const host = this.isIpv6(ip) ? `[${ip}]` : ip;\n const response = await fetch(`http://${host}:${port}/.well-known/solid`, {\n method: 'HEAD',\n signal: controller.signal,\n });\n\n clearTimeout(timeout);\n\n return {\n reachable: response.ok || response.status === 401,\n ipv4: ip,\n latency: Date.now() - start,\n };\n } catch (error) {\n return {\n reachable: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n private isIpv6(ip: string): boolean {\n return ip.includes(':');\n }\n}\n"]}
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"@id": "undefineds:dist/subdomain/SubdomainService.jsonld#SubdomainService",
|
|
107
107
|
"@type": "Class",
|
|
108
108
|
"requireElement": "SubdomainService",
|
|
109
|
-
"comment": "子域名管理服务 负责: 1. 子域名可用性检查 2. 连通性检测 3. 直连/隧道模式选择 4. DNS 记录管理 5. 隧道创建 注册信息持久化到 EdgeNodeRepository(
|
|
109
|
+
"comment": "子域名管理服务 负责: 1. 子域名可用性检查 2. 连通性检测 3. 直连/隧道模式选择 4. DNS 记录管理 5. 隧道创建 注册信息持久化到 EdgeNodeRepository(cluster_node.subdomain 字段)",
|
|
110
110
|
"parameters": [
|
|
111
111
|
{
|
|
112
112
|
"@id": "undefineds:dist/subdomain/SubdomainService.jsonld#SubdomainService_options_baseDomain",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@undefineds.co/xpod",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.32",
|
|
4
4
|
"description": "Xpod is an extended Community Solid Server, offering rich-feature, production-level Solid Pod and identity management.",
|
|
5
5
|
"repository": "https://github.com/undefinedsco/xpod",
|
|
6
6
|
"author": "developer@undefineds.co",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# ACR for the public profile container
|
|
2
|
+
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
|
|
3
|
+
@prefix acp: <http://www.w3.org/ns/solid/acp#>.
|
|
4
|
+
|
|
5
|
+
# The profile container must be readable so clients can discover the
|
|
6
|
+
# profile document through normal Solid container reads.
|
|
7
|
+
<#profile>
|
|
8
|
+
a acp:AccessControlResource;
|
|
9
|
+
acp:resource <./>;
|
|
10
|
+
acp:accessControl <#publicReadAccess>.
|
|
11
|
+
|
|
12
|
+
<#publicReadAccess>
|
|
13
|
+
a acp:AccessControl;
|
|
14
|
+
acp:apply [
|
|
15
|
+
a acp:Policy;
|
|
16
|
+
acp:allow acl:Read;
|
|
17
|
+
acp:anyOf [
|
|
18
|
+
a acp:Matcher;
|
|
19
|
+
acp:agent acp:PublicAgent
|
|
20
|
+
]
|
|
21
|
+
].
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# ACL resource for the public profile container
|
|
2
|
+
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
|
|
3
|
+
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
|
|
4
|
+
|
|
5
|
+
# The profile container must be readable so clients can discover the
|
|
6
|
+
# profile document through normal Solid container reads.
|
|
7
|
+
<#public>
|
|
8
|
+
a acl:Authorization;
|
|
9
|
+
acl:agentClass foaf:Agent;
|
|
10
|
+
acl:accessTo <./>;
|
|
11
|
+
acl:mode acl:Read.
|
|
12
|
+
|
|
13
|
+
# The owner has full access to the profile container.
|
|
14
|
+
<#owner>
|
|
15
|
+
a acl:Authorization;
|
|
16
|
+
acl:agent <{{webId}}>;
|
|
17
|
+
acl:accessTo <./>;
|
|
18
|
+
acl:mode acl:Read, acl:Write, acl:Control.
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { ApiServer } from '../ApiServer';
|
|
2
|
-
import type { DrizzleClientCredentialsStore } from '../store/DrizzleClientCredentialsStore';
|
|
3
|
-
export interface ApiKeyHandlerOptions {
|
|
4
|
-
store: DrizzleClientCredentialsStore;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Handler for API Key management
|
|
8
|
-
*
|
|
9
|
-
* GET /v1/keys - List user's API keys
|
|
10
|
-
* POST /v1/keys - Store a new API key (after creating in CSS)
|
|
11
|
-
* DELETE /v1/keys/:clientId - Delete an API key
|
|
12
|
-
*
|
|
13
|
-
* All endpoints require Solid Token (only frontend can manage keys)
|
|
14
|
-
*/
|
|
15
|
-
export declare function registerApiKeyRoutes(server: ApiServer, options: ApiKeyHandlerOptions): void;
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.registerApiKeyRoutes = registerApiKeyRoutes;
|
|
4
|
-
const global_logger_factory_1 = require("global-logger-factory");
|
|
5
|
-
const AuthContext_1 = require("../auth/AuthContext");
|
|
6
|
-
/**
|
|
7
|
-
* Handler for API Key management
|
|
8
|
-
*
|
|
9
|
-
* GET /v1/keys - List user's API keys
|
|
10
|
-
* POST /v1/keys - Store a new API key (after creating in CSS)
|
|
11
|
-
* DELETE /v1/keys/:clientId - Delete an API key
|
|
12
|
-
*
|
|
13
|
-
* All endpoints require Solid Token (only frontend can manage keys)
|
|
14
|
-
*/
|
|
15
|
-
function registerApiKeyRoutes(server, options) {
|
|
16
|
-
const logger = (0, global_logger_factory_1.getLoggerFor)('ApiKeyHandler');
|
|
17
|
-
const store = options.store;
|
|
18
|
-
const rejectApiKey = (request, response) => {
|
|
19
|
-
const auth = request.auth;
|
|
20
|
-
if (auth && (0, AuthContext_1.isSolidAuth)(auth) && auth.viaApiKey) {
|
|
21
|
-
sendJson(response, 403, { error: 'API key is not allowed for this endpoint' });
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
return false;
|
|
25
|
-
};
|
|
26
|
-
// GET /v1/keys - List user's API keys
|
|
27
|
-
server.get('/v1/keys', async (request, response, _params) => {
|
|
28
|
-
if (rejectApiKey(request, response)) {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
const auth = request.auth;
|
|
32
|
-
const webId = (0, AuthContext_1.getWebId)(auth);
|
|
33
|
-
if (!webId) {
|
|
34
|
-
sendJson(response, 400, { error: 'Cannot determine user' });
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
try {
|
|
38
|
-
// Use webId as account identifier
|
|
39
|
-
const keys = await store.listByAccount(webId);
|
|
40
|
-
sendJson(response, 200, {
|
|
41
|
-
keys: keys.map((k) => ({
|
|
42
|
-
clientId: k.clientId,
|
|
43
|
-
webId: k.webId,
|
|
44
|
-
displayName: k.displayName,
|
|
45
|
-
createdAt: k.createdAt.toISOString(),
|
|
46
|
-
})),
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
catch (error) {
|
|
50
|
-
logger.error(`Failed to list API keys: ${error}`);
|
|
51
|
-
sendJson(response, 500, { error: 'Failed to list keys' });
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
// POST /v1/keys - Store API key (frontend calls this after creating credentials in CSS)
|
|
55
|
-
server.post('/v1/keys', async (request, response, _params) => {
|
|
56
|
-
if (rejectApiKey(request, response)) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
const auth = request.auth;
|
|
60
|
-
const webId = (0, AuthContext_1.getWebId)(auth);
|
|
61
|
-
if (!webId) {
|
|
62
|
-
sendJson(response, 400, { error: 'Cannot determine user' });
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
const body = await readJsonBody(request);
|
|
66
|
-
if (!body || typeof body !== 'object') {
|
|
67
|
-
sendJson(response, 400, { error: 'Request body must be a JSON object' });
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
const payload = body;
|
|
71
|
-
// These come from CSS client credentials creation
|
|
72
|
-
const clientId = payload.clientId;
|
|
73
|
-
const displayName = typeof payload.displayName === 'string' ? payload.displayName : undefined;
|
|
74
|
-
if (typeof clientId !== 'string' || !clientId.trim()) {
|
|
75
|
-
sendJson(response, 400, { error: 'clientId is required' });
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
try {
|
|
79
|
-
// Use webId as account identifier
|
|
80
|
-
await store.store({
|
|
81
|
-
clientId,
|
|
82
|
-
webId,
|
|
83
|
-
accountId: webId,
|
|
84
|
-
displayName,
|
|
85
|
-
});
|
|
86
|
-
logger.info(`Stored API key ${clientId} for user ${webId}`);
|
|
87
|
-
sendJson(response, 201, {
|
|
88
|
-
clientId,
|
|
89
|
-
displayName,
|
|
90
|
-
message: 'API key stored successfully.',
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
catch (error) {
|
|
94
|
-
logger.error(`Failed to store API key: ${error}`);
|
|
95
|
-
sendJson(response, 500, { error: 'Failed to store key' });
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
// DELETE /v1/keys/:clientId - Delete an API key
|
|
99
|
-
server.delete('/v1/keys/:clientId', async (request, response, params) => {
|
|
100
|
-
if (rejectApiKey(request, response)) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
const auth = request.auth;
|
|
104
|
-
const webId = (0, AuthContext_1.getWebId)(auth);
|
|
105
|
-
const clientId = decodeURIComponent(params.clientId);
|
|
106
|
-
if (!webId) {
|
|
107
|
-
sendJson(response, 400, { error: 'Cannot determine user' });
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
try {
|
|
111
|
-
// Delete with webId check to ensure ownership
|
|
112
|
-
const deleted = await store.delete(clientId, webId);
|
|
113
|
-
if (!deleted) {
|
|
114
|
-
sendJson(response, 404, { error: 'Key not found or access denied' });
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
logger.info(`Deleted API key ${clientId}`);
|
|
118
|
-
sendJson(response, 200, { status: 'deleted', clientId });
|
|
119
|
-
}
|
|
120
|
-
catch (error) {
|
|
121
|
-
logger.error(`Failed to delete API key: ${error}`);
|
|
122
|
-
sendJson(response, 500, { error: 'Failed to delete key' });
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
async function readJsonBody(request) {
|
|
127
|
-
return new Promise((resolve, reject) => {
|
|
128
|
-
let data = '';
|
|
129
|
-
request.setEncoding('utf8');
|
|
130
|
-
request.on('data', (chunk) => {
|
|
131
|
-
data += chunk;
|
|
132
|
-
});
|
|
133
|
-
request.on('end', () => {
|
|
134
|
-
if (!data) {
|
|
135
|
-
resolve(undefined);
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
try {
|
|
139
|
-
resolve(JSON.parse(data));
|
|
140
|
-
}
|
|
141
|
-
catch {
|
|
142
|
-
resolve(undefined);
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
request.on('error', reject);
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
function sendJson(response, status, data) {
|
|
149
|
-
response.statusCode = status;
|
|
150
|
-
response.setHeader('Content-Type', 'application/json');
|
|
151
|
-
response.end(JSON.stringify(data));
|
|
152
|
-
}
|
|
153
|
-
//# sourceMappingURL=ApiKeyHandler.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ApiKeyHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/ApiKeyHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServerResponse } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { AuthenticatedRequest } from '../middleware/AuthMiddleware';\nimport type { ApiServer } from '../ApiServer';\nimport type { DrizzleClientCredentialsStore } from '../store/DrizzleClientCredentialsStore';\nimport { getWebId, isSolidAuth } from '../auth/AuthContext';\n\nexport interface ApiKeyHandlerOptions {\n store: DrizzleClientCredentialsStore;\n}\n\n/**\n * Handler for API Key management\n * \n * GET /v1/keys - List user's API keys\n * POST /v1/keys - Store a new API key (after creating in CSS)\n * DELETE /v1/keys/:clientId - Delete an API key\n * \n * All endpoints require Solid Token (only frontend can manage keys)\n */\nexport function registerApiKeyRoutes(server: ApiServer, options: ApiKeyHandlerOptions): void {\n const logger = getLoggerFor('ApiKeyHandler');\n const store = options.store;\n\n const rejectApiKey = (request: AuthenticatedRequest, response: ServerResponse): boolean => {\n const auth = request.auth;\n if (auth && isSolidAuth(auth) && auth.viaApiKey) {\n sendJson(response, 403, { error: 'API key is not allowed for this endpoint' });\n return true;\n }\n return false;\n };\n\n // GET /v1/keys - List user's API keys\n server.get('/v1/keys', async (request, response, _params) => {\n if (rejectApiKey(request, response)) {\n return;\n }\n const auth = request.auth!;\n const webId = getWebId(auth);\n\n if (!webId) {\n sendJson(response, 400, { error: 'Cannot determine user' });\n return;\n }\n\n try {\n // Use webId as account identifier\n const keys = await store.listByAccount(webId);\n sendJson(response, 200, {\n keys: keys.map((k) => ({\n clientId: k.clientId,\n webId: k.webId,\n displayName: k.displayName,\n createdAt: k.createdAt.toISOString(),\n })),\n });\n } catch (error) {\n logger.error(`Failed to list API keys: ${error}`);\n sendJson(response, 500, { error: 'Failed to list keys' });\n }\n });\n\n // POST /v1/keys - Store API key (frontend calls this after creating credentials in CSS)\n server.post('/v1/keys', async (request, response, _params) => {\n if (rejectApiKey(request, response)) {\n return;\n }\n const auth = request.auth!;\n const webId = getWebId(auth);\n\n if (!webId) {\n sendJson(response, 400, { error: 'Cannot determine user' });\n return;\n }\n\n const body = await readJsonBody(request);\n if (!body || typeof body !== 'object') {\n sendJson(response, 400, { error: 'Request body must be a JSON object' });\n return;\n }\n\n const payload = body as Record<string, unknown>;\n\n // These come from CSS client credentials creation\n const clientId = payload.clientId;\n const displayName = typeof payload.displayName === 'string' ? payload.displayName : undefined;\n\n if (typeof clientId !== 'string' || !clientId.trim()) {\n sendJson(response, 400, { error: 'clientId is required' });\n return;\n }\n\n try {\n // Use webId as account identifier\n await store.store({\n clientId,\n webId,\n accountId: webId,\n displayName,\n });\n\n logger.info(`Stored API key ${clientId} for user ${webId}`);\n\n sendJson(response, 201, {\n clientId,\n displayName,\n message: 'API key stored successfully.',\n });\n } catch (error) {\n logger.error(`Failed to store API key: ${error}`);\n sendJson(response, 500, { error: 'Failed to store key' });\n }\n });\n\n // DELETE /v1/keys/:clientId - Delete an API key\n server.delete('/v1/keys/:clientId', async (request, response, params) => {\n if (rejectApiKey(request, response)) {\n return;\n }\n const auth = request.auth!;\n const webId = getWebId(auth);\n const clientId = decodeURIComponent(params.clientId);\n\n if (!webId) {\n sendJson(response, 400, { error: 'Cannot determine user' });\n return;\n }\n\n try {\n // Delete with webId check to ensure ownership\n const deleted = await store.delete(clientId, webId);\n if (!deleted) {\n sendJson(response, 404, { error: 'Key not found or access denied' });\n return;\n }\n\n logger.info(`Deleted API key ${clientId}`);\n sendJson(response, 200, { status: 'deleted', clientId });\n } catch (error) {\n logger.error(`Failed to delete API key: ${error}`);\n sendJson(response, 500, { error: 'Failed to delete key' });\n }\n });\n}\n\nasync function readJsonBody(request: AuthenticatedRequest): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let data = '';\n request.setEncoding('utf8');\n request.on('data', (chunk: string) => {\n data += chunk;\n });\n request.on('end', () => {\n if (!data) {\n resolve(undefined);\n return;\n }\n try {\n resolve(JSON.parse(data));\n } catch {\n resolve(undefined);\n }\n });\n request.on('error', reject);\n });\n}\n\nfunction sendJson(response: ServerResponse, status: number, data: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(data));\n}"]}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { IdentityDatabase } from '../../identity/drizzle/db';
|
|
2
|
-
import type { ClientCredentialsRecord, ClientCredentialsStore } from '../auth/ClientCredentialsAuthenticator';
|
|
3
|
-
export interface DrizzleClientCredentialsStoreOptions {
|
|
4
|
-
db: IdentityDatabase;
|
|
5
|
-
/**
|
|
6
|
-
* Whether using SQLite (default: false for PostgreSQL)
|
|
7
|
-
*/
|
|
8
|
-
isSqlite?: boolean;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Storage for API Keys (client credentials) using Drizzle ORM
|
|
12
|
-
*
|
|
13
|
-
* Only stores clientId → webId/accountId mapping.
|
|
14
|
-
* The actual clientSecret lives in the sk-xxx token and is never persisted.
|
|
15
|
-
*/
|
|
16
|
-
export declare class DrizzleClientCredentialsStore implements ClientCredentialsStore {
|
|
17
|
-
private readonly logger;
|
|
18
|
-
private readonly db;
|
|
19
|
-
private readonly apiClientCredentials;
|
|
20
|
-
constructor(options: DrizzleClientCredentialsStoreOptions);
|
|
21
|
-
/**
|
|
22
|
-
* Store API Key registration (called when user creates API Key via frontend)
|
|
23
|
-
*/
|
|
24
|
-
store(options: {
|
|
25
|
-
clientId: string;
|
|
26
|
-
webId: string;
|
|
27
|
-
accountId: string;
|
|
28
|
-
displayName?: string;
|
|
29
|
-
}): Promise<void>;
|
|
30
|
-
/**
|
|
31
|
-
* Find by client_id (the "API Key")
|
|
32
|
-
*/
|
|
33
|
-
findByClientId(clientId: string): Promise<ClientCredentialsRecord | undefined>;
|
|
34
|
-
/**
|
|
35
|
-
* List API Keys for an account
|
|
36
|
-
*/
|
|
37
|
-
listByAccount(accountId: string): Promise<Array<{
|
|
38
|
-
clientId: string;
|
|
39
|
-
webId: string;
|
|
40
|
-
displayName?: string;
|
|
41
|
-
createdAt: Date;
|
|
42
|
-
}>>;
|
|
43
|
-
/**
|
|
44
|
-
* Find the most recently created API Key for an account.
|
|
45
|
-
*/
|
|
46
|
-
findByAccountId(accountId: string): Promise<ClientCredentialsRecord | undefined>;
|
|
47
|
-
/**
|
|
48
|
-
* Delete an API Key
|
|
49
|
-
*/
|
|
50
|
-
delete(clientId: string, accountId?: string): Promise<boolean>;
|
|
51
|
-
}
|