@honua/app-platform 0.2.5 → 0.2.7
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/control-plane/dataset-import/index.d.ts +2 -1
- package/control-plane/dataset-import/index.d.ts.map +1 -1
- package/control-plane/dataset-import/index.js +1 -0
- package/control-plane/dataset-import/server-job-source-client.d.ts +16 -0
- package/control-plane/dataset-import/server-job-source-client.d.ts.map +1 -0
- package/control-plane/dataset-import/server-job-source-client.js +143 -0
- package/control-plane/dataset-import/types.d.ts +65 -3
- package/control-plane/dataset-import/types.d.ts.map +1 -1
- package/control-plane/dataset-import/upload-client.d.ts +7 -0
- package/control-plane/dataset-import/upload-client.d.ts.map +1 -1
- package/control-plane/dataset-import/upload-client.js +14 -0
- package/core/error-envelope.js +1 -0
- package/core/stac-conformance.d.ts +66 -0
- package/core/stac-conformance.d.ts.map +1 -0
- package/core/stac-conformance.js +177 -0
- package/core/stac.d.ts +20 -2
- package/core/stac.d.ts.map +1 -1
- package/core/stac.js +71 -7
- package/core/types.d.ts +10 -0
- package/core/types.d.ts.map +1 -1
- package/map/feature-service-adapter.d.ts +3 -3
- package/map/feature-service-adapter.d.ts.map +1 -1
- package/map/feature-service-adapter.js +99 -69
- package/package.json +2 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { hashDatasetImportFile } from "./file-hash.js";
|
|
2
2
|
export type { DatasetImportFileHashOptions } from "./file-hash.js";
|
|
3
|
-
export type { CreateDatasetImportTask, DatasetImportArtifactSource, DatasetImportAuthorityDescriptor, DatasetImportAuthorityRenewal, DatasetImportDestination, DatasetImportSource, DatasetImportTask, DatasetImportTaskOptions, DatasetImportTaskReference, RenewDatasetImportAuthority, } from "./types.js";
|
|
3
|
+
export type { CreateDatasetImportTask, DatasetImportArtifactSource, DatasetImportAuthorityDescriptor, DatasetImportAuthorityRenewal, DatasetImportBrowserFileSource, DatasetImportDestination, DatasetImportLocalProcessSource, DatasetImportServerJobArtifactReference, DatasetImportServerJobArtifactSource, DatasetImportServerJobSource, DatasetImportSource, DatasetImportTask, DatasetImportTaskOptions, DatasetImportTaskReference, RenewDatasetImportAuthority, DescribeServerJobImportSource, ListServerJobImportSources, ServerJobImportSourceChoice, ServerJobImportSourcePage, } from "./types.js";
|
|
4
|
+
export { DatasetImportServerJobSourceClient } from "./server-job-source-client.js";
|
|
4
5
|
export { DatasetImportTaskApiError, DatasetImportTaskClient, } from "./client.js";
|
|
5
6
|
export type { DatasetImportTaskClientOptions } from "./client.js";
|
|
6
7
|
export { DatasetImportUploadClient } from "./upload-client.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/control-plane/dataset-import/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,YAAY,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AACnE,YAAY,EACV,uBAAuB,EACvB,2BAA2B,EAC3B,gCAAgC,EAChC,6BAA6B,EAC7B,wBAAwB,EACxB,mBAAmB,EACnB,iBAAiB,EACjB,wBAAwB,EACxB,0BAA0B,EAC1B,2BAA2B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/control-plane/dataset-import/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,YAAY,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AACnE,YAAY,EACV,uBAAuB,EACvB,2BAA2B,EAC3B,gCAAgC,EAChC,6BAA6B,EAC7B,8BAA8B,EAC9B,wBAAwB,EACxB,+BAA+B,EAC/B,uCAAuC,EACvC,oCAAoC,EACpC,4BAA4B,EAC5B,mBAAmB,EACnB,iBAAiB,EACjB,wBAAwB,EACxB,0BAA0B,EAC1B,2BAA2B,EAC3B,6BAA6B,EAC7B,0BAA0B,EAC1B,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,kCAAkC,EAAE,MAAM,+BAA+B,CAAC;AACnF,OAAO,EACL,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,YAAY,EACV,mCAAmC,EACnC,+BAA+B,EAC/B,0BAA0B,GAC3B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AACvE,YAAY,EACV,uBAAuB,EACvB,0BAA0B,EAC1B,8BAA8B,EAC9B,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { hashDatasetImportFile } from "./file-hash.js";
|
|
2
|
+
export { DatasetImportServerJobSourceClient } from "./server-job-source-client.js";
|
|
2
3
|
export { DatasetImportTaskApiError, DatasetImportTaskClient, } from "./client.js";
|
|
3
4
|
export { DatasetImportUploadClient } from "./upload-client.js";
|
|
4
5
|
export { DatasetImportSubmissionClient } from "./submission-client.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DatasetImportTaskClientOptions } from "./client.js";
|
|
2
|
+
import type { DatasetImportServerJobSource, DescribeServerJobImportSource, ListServerJobImportSources, ServerJobImportSourcePage } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Optional preview Server-job source APIs. Requires an installed Server that
|
|
5
|
+
* advertises these capabilities. Discovery is metadata-only; describe verifies
|
|
6
|
+
* the original output without returning its bytes or storage location. Server
|
|
7
|
+
* authorization remains authoritative on every observation and transfer.
|
|
8
|
+
*/
|
|
9
|
+
export declare class DatasetImportServerJobSourceClient {
|
|
10
|
+
private readonly transport;
|
|
11
|
+
private readonly targetId;
|
|
12
|
+
constructor(options: DatasetImportTaskClientOptions);
|
|
13
|
+
list(request: ListServerJobImportSources, signal?: AbortSignal): Promise<ServerJobImportSourcePage | null>;
|
|
14
|
+
describe(request: DescribeServerJobImportSource, signal?: AbortSignal): Promise<DatasetImportServerJobSource | null>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=server-job-source-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-job-source-client.d.ts","sourceRoot":"","sources":["../../../../src/control-plane/dataset-import/server-job-source-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAElE,OAAO,KAAK,EACV,4BAA4B,EAC5B,6BAA6B,EAC7B,0BAA0B,EAC1B,yBAAyB,EAC1B,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,qBAAa,kCAAkC;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;IAC5D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,OAAO,EAAE,8BAA8B;IAK7C,IAAI,CAAC,OAAO,EAAE,0BAA0B,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC;IA2D1G,QAAQ,CACZ,OAAO,EAAE,6BAA6B,EACtC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,4BAA4B,GAAG,IAAI,CAAC;CA4DhD"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { DatasetImportWorkspaceTransport, validId } from "./transport.js";
|
|
2
|
+
/**
|
|
3
|
+
* Optional preview Server-job source APIs. Requires an installed Server that
|
|
4
|
+
* advertises these capabilities. Discovery is metadata-only; describe verifies
|
|
5
|
+
* the original output without returning its bytes or storage location. Server
|
|
6
|
+
* authorization remains authoritative on every observation and transfer.
|
|
7
|
+
*/
|
|
8
|
+
export class DatasetImportServerJobSourceClient {
|
|
9
|
+
transport;
|
|
10
|
+
targetId;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.transport = new DatasetImportWorkspaceTransport(options);
|
|
13
|
+
this.targetId = options.targetId;
|
|
14
|
+
}
|
|
15
|
+
async list(request, signal) {
|
|
16
|
+
const { jobId, offset = 0, resultPackageId = null } = request;
|
|
17
|
+
validId(jobId);
|
|
18
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || (offset > 0 && resultPackageId === null))
|
|
19
|
+
throw new TypeError("Continuation requires a nonnegative offset and the original result package.");
|
|
20
|
+
if (resultPackageId !== null)
|
|
21
|
+
validPackage(jobId, resultPackageId);
|
|
22
|
+
const value = await this.transport.request("/dataset-import-sources/server-job-artifacts", "POST", JSON.stringify({ jobId, offset, resultPackageId }), signal, true);
|
|
23
|
+
if (value === null)
|
|
24
|
+
return null;
|
|
25
|
+
if (value.targetId !== this.targetId || value.jobId !== jobId)
|
|
26
|
+
throw new Error("The job source page changed deployment or job identity.");
|
|
27
|
+
validPackage(jobId, value.resultPackageId);
|
|
28
|
+
if (resultPackageId !== null && value.resultPackageId !== resultPackageId)
|
|
29
|
+
throw new Error("The job source page changed the original result version.");
|
|
30
|
+
// AgentWorkspaceJsonContext omits null members on the wire.
|
|
31
|
+
const nextOffset = value.nextOffset ?? null;
|
|
32
|
+
if (!Number.isSafeInteger(value.totalArtifacts) ||
|
|
33
|
+
value.totalArtifacts < offset ||
|
|
34
|
+
!Array.isArray(value.artifacts) ||
|
|
35
|
+
value.artifacts.length !== Math.min(50, value.totalArtifacts - offset) ||
|
|
36
|
+
nextOffset !== (offset + value.artifacts.length < value.totalArtifacts ? offset + value.artifacts.length : null))
|
|
37
|
+
throw new Error("The job source page has an inconsistent bounded continuation.");
|
|
38
|
+
const artifacts = value.artifacts.map((choice, index) => {
|
|
39
|
+
if (!choice ||
|
|
40
|
+
choice.artifactId !== `${jobId}:artifact:${offset + index + 1}` ||
|
|
41
|
+
typeof choice.kind !== "string" ||
|
|
42
|
+
!/^[A-Za-z][A-Za-z0-9]{0,63}$/.test(choice.kind) ||
|
|
43
|
+
typeof choice.label !== "string" ||
|
|
44
|
+
!/^[A-Za-z0-9 _.\-]{1,128}$/.test(choice.label) ||
|
|
45
|
+
typeof choice.canDescribeForImport !== "boolean")
|
|
46
|
+
throw new Error("The job source page contains an invalid artifact identity or metadata.");
|
|
47
|
+
return {
|
|
48
|
+
artifactId: choice.artifactId,
|
|
49
|
+
kind: choice.kind,
|
|
50
|
+
label: choice.label,
|
|
51
|
+
canDescribeForImport: choice.canDescribeForImport,
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
// Project only the supported metadata: never propagate unexpected bulk or
|
|
55
|
+
// storage-location members to an agent through a loosely typed JSON object.
|
|
56
|
+
return {
|
|
57
|
+
targetId: value.targetId,
|
|
58
|
+
jobId,
|
|
59
|
+
resultPackageId: value.resultPackageId,
|
|
60
|
+
artifacts,
|
|
61
|
+
totalArtifacts: value.totalArtifacts,
|
|
62
|
+
nextOffset,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
async describe(request, signal) {
|
|
66
|
+
const { jobId, resultPackageId, artifactId } = request;
|
|
67
|
+
validId(jobId);
|
|
68
|
+
validPackage(jobId, resultPackageId);
|
|
69
|
+
validArtifact(jobId, artifactId);
|
|
70
|
+
const source = await this.transport.request("/dataset-import-sources/server-job-artifact", "POST", JSON.stringify({ jobId, resultPackageId, artifactId }), signal, true);
|
|
71
|
+
if (source === null)
|
|
72
|
+
return null;
|
|
73
|
+
const output = source.serverJobArtifact;
|
|
74
|
+
const reference = output?.reference;
|
|
75
|
+
if (source.kind !== "server-job-artifact" ||
|
|
76
|
+
source.fileName !== "job-artifact.geojson" ||
|
|
77
|
+
!positiveInteger(source.totalBytes) ||
|
|
78
|
+
!digest(source.expectedSha256) ||
|
|
79
|
+
source.artifact != null ||
|
|
80
|
+
source.browserSelectionId != null ||
|
|
81
|
+
!reference ||
|
|
82
|
+
reference.targetId !== this.targetId ||
|
|
83
|
+
reference.jobId !== jobId ||
|
|
84
|
+
reference.resultPackageId !== resultPackageId ||
|
|
85
|
+
reference.artifactId !== artifactId ||
|
|
86
|
+
!digest(reference.referenceSha256) ||
|
|
87
|
+
!positiveInteger(output.featureCount) ||
|
|
88
|
+
(output.crsState === "known"
|
|
89
|
+
? !positiveInteger(output.sourceSrid) || output.sourceSrid > 2147483647
|
|
90
|
+
: output.crsState !== "unknown" || output.sourceSrid != null))
|
|
91
|
+
throw new Error("The job source description changed its original identity or content/coordinate contract.");
|
|
92
|
+
if (!principalText(output.producerActorId, 2048) ||
|
|
93
|
+
(output.producerTenantId != null && !principalText(output.producerTenantId, 512, true)))
|
|
94
|
+
throw new Error("The job source producer identity is outside the Server contract.");
|
|
95
|
+
return {
|
|
96
|
+
kind: "server-job-artifact",
|
|
97
|
+
fileName: source.fileName,
|
|
98
|
+
totalBytes: source.totalBytes,
|
|
99
|
+
expectedSha256: source.expectedSha256,
|
|
100
|
+
serverJobArtifact: {
|
|
101
|
+
reference: {
|
|
102
|
+
targetId: this.targetId,
|
|
103
|
+
jobId,
|
|
104
|
+
resultPackageId,
|
|
105
|
+
artifactId,
|
|
106
|
+
referenceSha256: reference.referenceSha256,
|
|
107
|
+
},
|
|
108
|
+
producerActorId: output.producerActorId,
|
|
109
|
+
producerTenantId: output.producerTenantId ?? null,
|
|
110
|
+
crsState: output.crsState,
|
|
111
|
+
sourceSrid: output.sourceSrid ?? null,
|
|
112
|
+
featureCount: output.featureCount,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function validPackage(jobId, value) {
|
|
118
|
+
validId(value);
|
|
119
|
+
if (!value.startsWith(`${jobId}:v`) || !/^[1-9][0-9]*$/.test(value.slice(jobId.length + 2)))
|
|
120
|
+
throw new TypeError("The original versioned job result package is required.");
|
|
121
|
+
}
|
|
122
|
+
function validArtifact(jobId, value) {
|
|
123
|
+
validId(value);
|
|
124
|
+
const prefix = `${jobId}:artifact:`;
|
|
125
|
+
if (!value.startsWith(prefix) || !/^[1-9][0-9]*$/.test(value.slice(prefix.length)))
|
|
126
|
+
throw new TypeError("The original job artifact identity is required.");
|
|
127
|
+
}
|
|
128
|
+
function positiveInteger(value) {
|
|
129
|
+
return Number.isSafeInteger(value) && value > 0;
|
|
130
|
+
}
|
|
131
|
+
function digest(value) {
|
|
132
|
+
return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
|
|
133
|
+
}
|
|
134
|
+
// Principal identifiers have different Server bounds from workspace/job IDs.
|
|
135
|
+
function principalText(value, maximum, allowEmpty = false) {
|
|
136
|
+
return (typeof value === "string" &&
|
|
137
|
+
(allowEmpty || value.length > 0) &&
|
|
138
|
+
value.length <= maximum &&
|
|
139
|
+
Array.from(value).every((character) => {
|
|
140
|
+
const code = character.charCodeAt(0);
|
|
141
|
+
return code > 31 && (code < 127 || code > 159);
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
@@ -10,14 +10,75 @@ export interface DatasetImportArtifactSource {
|
|
|
10
10
|
readonly runId: string;
|
|
11
11
|
readonly path: string;
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
readonly kind: "browser-file" | "local-process-artifact";
|
|
13
|
+
interface DatasetImportSourceMetadata {
|
|
15
14
|
readonly fileName: string;
|
|
16
15
|
readonly totalBytes: number;
|
|
17
16
|
readonly expectedSha256?: string | null;
|
|
18
|
-
|
|
17
|
+
}
|
|
18
|
+
export interface DatasetImportBrowserFileSource extends DatasetImportSourceMetadata {
|
|
19
|
+
readonly kind: "browser-file";
|
|
19
20
|
/** Original human file selection captured by the immutable originating turn. */
|
|
20
21
|
readonly browserSelectionId?: string | null;
|
|
22
|
+
readonly artifact?: null;
|
|
23
|
+
readonly serverJobArtifact?: null;
|
|
24
|
+
}
|
|
25
|
+
export interface DatasetImportLocalProcessSource extends DatasetImportSourceMetadata {
|
|
26
|
+
readonly kind: "local-process-artifact";
|
|
27
|
+
readonly artifact: DatasetImportArtifactSource;
|
|
28
|
+
readonly browserSelectionId?: null;
|
|
29
|
+
readonly serverJobArtifact?: null;
|
|
30
|
+
}
|
|
31
|
+
/** Each source kind carries its own canonical reference rather than optional mixed identities. */
|
|
32
|
+
export type DatasetImportSource = DatasetImportBrowserFileSource | DatasetImportLocalProcessSource | DatasetImportServerJobSource;
|
|
33
|
+
/** Exact Server-owned result identity; no caller-supplied location or bulk content. */
|
|
34
|
+
export interface DatasetImportServerJobArtifactReference {
|
|
35
|
+
readonly targetId: string;
|
|
36
|
+
readonly jobId: string;
|
|
37
|
+
readonly resultPackageId: string;
|
|
38
|
+
readonly artifactId: string;
|
|
39
|
+
readonly referenceSha256: string;
|
|
40
|
+
}
|
|
41
|
+
/** Server recomputes this description at task creation; it is not execution authority. */
|
|
42
|
+
export interface DatasetImportServerJobArtifactSource {
|
|
43
|
+
readonly reference: DatasetImportServerJobArtifactReference;
|
|
44
|
+
readonly producerActorId: string;
|
|
45
|
+
readonly producerTenantId: string | null;
|
|
46
|
+
readonly crsState: "known" | "unknown";
|
|
47
|
+
readonly sourceSrid: number | null;
|
|
48
|
+
readonly featureCount: number;
|
|
49
|
+
}
|
|
50
|
+
export interface DatasetImportServerJobSource extends DatasetImportSourceMetadata {
|
|
51
|
+
readonly kind: "server-job-artifact";
|
|
52
|
+
readonly expectedSha256: string;
|
|
53
|
+
readonly serverJobArtifact: DatasetImportServerJobArtifactSource;
|
|
54
|
+
readonly artifact?: null;
|
|
55
|
+
readonly browserSelectionId?: null;
|
|
56
|
+
}
|
|
57
|
+
export interface ListServerJobImportSources {
|
|
58
|
+
readonly jobId: string;
|
|
59
|
+
readonly offset?: number;
|
|
60
|
+
/** Required for continuation, preserving the exact original result version. */
|
|
61
|
+
readonly resultPackageId?: string | null;
|
|
62
|
+
}
|
|
63
|
+
export interface DescribeServerJobImportSource {
|
|
64
|
+
readonly jobId: string;
|
|
65
|
+
readonly resultPackageId: string;
|
|
66
|
+
readonly artifactId: string;
|
|
67
|
+
}
|
|
68
|
+
export interface ServerJobImportSourceChoice {
|
|
69
|
+
readonly artifactId: string;
|
|
70
|
+
readonly kind: string;
|
|
71
|
+
readonly label: string;
|
|
72
|
+
/** Envelope eligibility only; describe still verifies content, CRS and current access. */
|
|
73
|
+
readonly canDescribeForImport: boolean;
|
|
74
|
+
}
|
|
75
|
+
export interface ServerJobImportSourcePage {
|
|
76
|
+
readonly targetId: string;
|
|
77
|
+
readonly jobId: string;
|
|
78
|
+
readonly resultPackageId: string;
|
|
79
|
+
readonly artifacts: readonly ServerJobImportSourceChoice[];
|
|
80
|
+
readonly totalArtifacts: number;
|
|
81
|
+
readonly nextOffset: number | null;
|
|
21
82
|
}
|
|
22
83
|
export interface DatasetImportTaskOptions {
|
|
23
84
|
/** Current new-table slice supports Append (1); occupied destinations remain unsupported. */
|
|
@@ -147,4 +208,5 @@ export interface DatasetImportSubmissionReceipt {
|
|
|
147
208
|
readonly acceptBefore?: string | null;
|
|
148
209
|
readonly job?: DatasetImportJobReceipt | null;
|
|
149
210
|
}
|
|
211
|
+
export {};
|
|
150
212
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/control-plane/dataset-import/types.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AACD,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AACD,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/control-plane/dataset-import/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,13 @@ export declare class DatasetImportUploadClient {
|
|
|
14
14
|
/** Read-only recovery of a known session, preserving its immutable fields and terminal receipts. */
|
|
15
15
|
reconcile(reference: DatasetImportTaskReference, original: DatasetImportUploadReceipt, signal?: AbortSignal): Promise<DatasetImportUploadReceipt | null>;
|
|
16
16
|
create(reference: DatasetImportTaskReference, signal?: AbortSignal): Promise<DatasetImportUploadReceipt>;
|
|
17
|
+
/**
|
|
18
|
+
* Transfer the original task's Server-owned job output through the canonical
|
|
19
|
+
* upload coordinator and retain it. This does not submit an import. A lost
|
|
20
|
+
* reply remains unknown: recover with read/reconcile before explicitly resuming
|
|
21
|
+
* this same task. Never create a replacement task or automatically retry.
|
|
22
|
+
*/
|
|
23
|
+
transferServerJobArtifact(reference: DatasetImportTaskReference, signal?: AbortSignal): Promise<DatasetImportUploadReceipt>;
|
|
17
24
|
appendChunk(reference: DatasetImportTaskReference, original: DatasetImportUploadReceipt, chunk: Blob, signal?: AbortSignal): Promise<DatasetImportUploadReceipt>;
|
|
18
25
|
seal(reference: DatasetImportTaskReference, original: DatasetImportUploadReceipt, signal?: AbortSignal): Promise<DatasetImportUploadReceipt>;
|
|
19
26
|
retainSource(reference: DatasetImportTaskReference, original: DatasetImportUploadReceipt, signal?: AbortSignal): Promise<DatasetImportUploadReceipt>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload-client.d.ts","sourceRoot":"","sources":["../../../../src/control-plane/dataset-import/upload-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAGlE,OAAO,KAAK,EACV,0BAA0B,EAC1B,+BAA+B,EAC/B,0BAA0B,EAC3B,MAAM,YAAY,CAAC;AAKpB;;;;;GAKG;AACH,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;gBAChD,OAAO,EAAE,8BAA8B;IAI7C,YAAY,CAChB,SAAS,EAAE,0BAA0B,EACrC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,+BAA+B,GAAG,IAAI,CAAC;IAqB5C,IAAI,CAAC,SAAS,EAAE,0BAA0B,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC;IAanH,oGAAoG;IAC9F,SAAS,CACb,SAAS,EAAE,0BAA0B,EACrC,QAAQ,EAAE,0BAA0B,EACpC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC;IAcvC,MAAM,CAAC,SAAS,EAAE,0BAA0B,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"upload-client.d.ts","sourceRoot":"","sources":["../../../../src/control-plane/dataset-import/upload-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAGlE,OAAO,KAAK,EACV,0BAA0B,EAC1B,+BAA+B,EAC/B,0BAA0B,EAC3B,MAAM,YAAY,CAAC;AAKpB;;;;;GAKG;AACH,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;gBAChD,OAAO,EAAE,8BAA8B;IAI7C,YAAY,CAChB,SAAS,EAAE,0BAA0B,EACrC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,+BAA+B,GAAG,IAAI,CAAC;IAqB5C,IAAI,CAAC,SAAS,EAAE,0BAA0B,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC;IAanH,oGAAoG;IAC9F,SAAS,CACb,SAAS,EAAE,0BAA0B,EACrC,QAAQ,EAAE,0BAA0B,EACpC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC;IAcvC,MAAM,CAAC,SAAS,EAAE,0BAA0B,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAQ9G;;;;;OAKG;IACG,yBAAyB,CAC7B,SAAS,EAAE,0BAA0B,EACrC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,0BAA0B,CAAC;IAchC,WAAW,CACf,SAAS,EAAE,0BAA0B,EACrC,QAAQ,EAAE,0BAA0B,EACpC,KAAK,EAAE,IAAI,EACX,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,0BAA0B,CAAC;IAqBhC,IAAI,CACR,SAAS,EAAE,0BAA0B,EACrC,QAAQ,EAAE,0BAA0B,EACpC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,0BAA0B,CAAC;IAGhC,YAAY,CAChB,SAAS,EAAE,0BAA0B,EACrC,QAAQ,EAAE,0BAA0B,EACpC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,0BAA0B,CAAC;IAGhC,MAAM,CACV,SAAS,EAAE,0BAA0B,EACrC,QAAQ,EAAE,0BAA0B,EACpC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,0BAA0B,CAAC;YAaxB,MAAM;CAmBrB"}
|
|
@@ -48,6 +48,20 @@ export class DatasetImportUploadClient {
|
|
|
48
48
|
validate(value, pinned);
|
|
49
49
|
return value;
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Transfer the original task's Server-owned job output through the canonical
|
|
53
|
+
* upload coordinator and retain it. This does not submit an import. A lost
|
|
54
|
+
* reply remains unknown: recover with read/reconcile before explicitly resuming
|
|
55
|
+
* this same task. Never create a replacement task or automatically retry.
|
|
56
|
+
*/
|
|
57
|
+
async transferServerJobArtifact(reference, signal) {
|
|
58
|
+
const pinned = pin(reference);
|
|
59
|
+
const value = await this.transport.request(`${path(pinned)}/server-job-artifact`, "POST", JSON.stringify({ taskContentHash: pinned.contentHash }), signal);
|
|
60
|
+
validate(value, pinned);
|
|
61
|
+
if (value.state !== "sealed" || value.sourceRetention == null)
|
|
62
|
+
throw new Error("The Server job transfer did not return a sealed retained source receipt.");
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
51
65
|
async appendChunk(reference, original, chunk, signal) {
|
|
52
66
|
const pinned = pin(reference);
|
|
53
67
|
const session = captureSession(original, pinned);
|
package/core/error-envelope.js
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STAC API conformance classes this client actually uses.
|
|
3
|
+
*
|
|
4
|
+
* Predicates match the canonical URIs (and the historical `ogcapi-features`
|
|
5
|
+
* fragment aliases) exactly. A fragment class such as Item Search Filter does
|
|
6
|
+
* not count as Item Search itself. The legacy Query extension is recognized
|
|
7
|
+
* here and is not serialized: `StacSearchRequest` has no `query` parameter,
|
|
8
|
+
* and `Source.query` compiles attribute filters to CQL2.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import { type HonuaErrorOptions, HonuaSdkError } from "./error-envelope.js";
|
|
13
|
+
import type { StacSearchRequest } from "./types.js";
|
|
14
|
+
/** Canonical conformance URI for each extension this client gates. */
|
|
15
|
+
export declare const STAC_CONFORMANCE_CLASS: Readonly<{
|
|
16
|
+
readonly core: "https://api.stacspec.org/v1.0.0/core";
|
|
17
|
+
readonly "item-search": "https://api.stacspec.org/v1.0.0/item-search";
|
|
18
|
+
readonly filter: "https://api.stacspec.org/v1.0.0/item-search#filter";
|
|
19
|
+
readonly "cql2-text": "http://www.opengis.net/spec/cql2/1.0/conf/cql2-text";
|
|
20
|
+
readonly "cql2-json": "http://www.opengis.net/spec/cql2/1.0/conf/cql2-json";
|
|
21
|
+
readonly sort: "https://api.stacspec.org/v1.0.0/item-search#sort";
|
|
22
|
+
readonly fields: "https://api.stacspec.org/v1.0.0/item-search#fields";
|
|
23
|
+
/** Predicate only. This client does not emit the legacy Query extension. */
|
|
24
|
+
readonly query: "https://api.stacspec.org/v1.0.0/item-search#query";
|
|
25
|
+
}>;
|
|
26
|
+
export type StacConformanceKind = keyof typeof STAC_CONFORMANCE_CLASS;
|
|
27
|
+
export interface HonuaStacConformanceErrorOptions extends HonuaErrorOptions {
|
|
28
|
+
/**
|
|
29
|
+
* The search used an extension before any `conformsTo` list was read.
|
|
30
|
+
* Distinct from a landing page that was read and does not declare the class.
|
|
31
|
+
*/
|
|
32
|
+
readonly unread?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A STAC search asked for an extension the catalog does not declare, or the
|
|
36
|
+
* client was about to emit that extension without having read `conformsTo`.
|
|
37
|
+
*
|
|
38
|
+
* `conformanceClass` is the first missing class. `missing` lists every class
|
|
39
|
+
* the request needed and the catalog did not declare, in requirement order.
|
|
40
|
+
* The registered code stays `core.capability-not-supported`.
|
|
41
|
+
*/
|
|
42
|
+
export declare class HonuaStacConformanceError extends HonuaSdkError {
|
|
43
|
+
readonly conformanceClass: string;
|
|
44
|
+
readonly missing: readonly string[];
|
|
45
|
+
readonly unread: boolean;
|
|
46
|
+
constructor(missing: readonly string[], options?: HonuaStacConformanceErrorOptions);
|
|
47
|
+
}
|
|
48
|
+
/** Copy string entries from a landing-page `conformsTo` array. Omitted becomes `[]` (read, nothing declared). */
|
|
49
|
+
export declare function normalizeStacConformsTo(value: readonly string[] | undefined): readonly string[];
|
|
50
|
+
/** True when `conformsTo` contains the canonical class for `kind`. */
|
|
51
|
+
export declare function hasStacConformance(conformsTo: readonly string[] | undefined, kind: StacConformanceKind): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Extension classes a search will put on the wire. Core Item Search parameters
|
|
54
|
+
* (`bbox`, `datetime`, `intersects`, `ids`, `collections`, `limit`, paging)
|
|
55
|
+
* are not gated. An extension property counts as soon as it is set, including
|
|
56
|
+
* an empty `fields` or `sortby`, because POST emits those members as-is.
|
|
57
|
+
* Opaque `nextParams` / `nextBody` continuations are not inspected.
|
|
58
|
+
*/
|
|
59
|
+
export declare function stacSearchExtensionRequirements(request: StacSearchRequest): readonly StacConformanceKind[];
|
|
60
|
+
/**
|
|
61
|
+
* Refuse an extension search that has not been checked against `conformsTo`.
|
|
62
|
+
* `conformsTo === undefined` means the list was never read and fails before
|
|
63
|
+
* any HTTP request. `conformsTo: []` means it was read and the classes are absent.
|
|
64
|
+
*/
|
|
65
|
+
export declare function gateStacSearchExtensions(request: StacSearchRequest): void;
|
|
66
|
+
//# sourceMappingURL=stac-conformance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stac-conformance.d.ts","sourceRoot":"","sources":["../../../src/core/stac-conformance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,iBAAiB,EAAE,aAAa,EAA0B,MAAM,qBAAqB,CAAC;AACpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAOpD,sEAAsE;AACtE,eAAO,MAAM,sBAAsB;;;;;;;;IAQjC,4EAA4E;;EAEnE,CAAC;AAEZ,MAAM,MAAM,mBAAmB,GAAG,MAAM,OAAO,sBAAsB,CAAC;AAUtE,MAAM,WAAW,gCAAiC,SAAQ,iBAAiB;IACzE;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,qBAAa,yBAA0B,SAAQ,aAAa;IAC1D,SAAgB,gBAAgB,EAAE,MAAM,CAAC;IACzC,SAAgB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,SAAgB,MAAM,EAAE,OAAO,CAAC;gBAEb,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,GAAE,gCAAqC;CAoB9F;AAED,iHAAiH;AACjH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,SAAS,MAAM,EAAE,CAG/F;AAED,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAMhH;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,mBAAmB,EAAE,CAmB1G;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAQzE"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STAC API conformance classes this client actually uses.
|
|
3
|
+
*
|
|
4
|
+
* Predicates match the canonical URIs (and the historical `ogcapi-features`
|
|
5
|
+
* fragment aliases) exactly. A fragment class such as Item Search Filter does
|
|
6
|
+
* not count as Item Search itself. The legacy Query extension is recognized
|
|
7
|
+
* here and is not serialized: `StacSearchRequest` has no `query` parameter,
|
|
8
|
+
* and `Source.query` compiles attribute filters to CQL2.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import { HonuaSdkError, mergeHonuaErrorContext } from "./error-envelope.js";
|
|
13
|
+
const STAC_SPEC_ORIGIN = "https://api.stacspec.org";
|
|
14
|
+
const ITEM_SEARCH_PATH = "/v1.0.0/item-search";
|
|
15
|
+
const ITEM_SEARCH_URI = `${STAC_SPEC_ORIGIN}${ITEM_SEARCH_PATH}`;
|
|
16
|
+
const CQL2_CONFORMANCE_URI = "http://www.opengis.net/spec/cql2/1.0/conf/";
|
|
17
|
+
/** Canonical conformance URI for each extension this client gates. */
|
|
18
|
+
export const STAC_CONFORMANCE_CLASS = Object.freeze({
|
|
19
|
+
core: `${STAC_SPEC_ORIGIN}/v1.0.0/core`,
|
|
20
|
+
"item-search": ITEM_SEARCH_URI,
|
|
21
|
+
filter: `${ITEM_SEARCH_URI}#filter`,
|
|
22
|
+
"cql2-text": `${CQL2_CONFORMANCE_URI}cql2-text`,
|
|
23
|
+
"cql2-json": `${CQL2_CONFORMANCE_URI}cql2-json`,
|
|
24
|
+
sort: `${ITEM_SEARCH_URI}#sort`,
|
|
25
|
+
fields: `${ITEM_SEARCH_URI}#fields`,
|
|
26
|
+
/** Predicate only. This client does not emit the legacy Query extension. */
|
|
27
|
+
query: `${ITEM_SEARCH_URI}#query`,
|
|
28
|
+
});
|
|
29
|
+
const OGC_FEATURES_PATH = "/v1.0.0/ogcapi-features";
|
|
30
|
+
const EXTENSION_FRAGMENT = {
|
|
31
|
+
filter: "#filter",
|
|
32
|
+
sort: "#sort",
|
|
33
|
+
fields: "#fields",
|
|
34
|
+
query: "#query",
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* A STAC search asked for an extension the catalog does not declare, or the
|
|
38
|
+
* client was about to emit that extension without having read `conformsTo`.
|
|
39
|
+
*
|
|
40
|
+
* `conformanceClass` is the first missing class. `missing` lists every class
|
|
41
|
+
* the request needed and the catalog did not declare, in requirement order.
|
|
42
|
+
* The registered code stays `core.capability-not-supported`.
|
|
43
|
+
*/
|
|
44
|
+
export class HonuaStacConformanceError extends HonuaSdkError {
|
|
45
|
+
conformanceClass;
|
|
46
|
+
missing;
|
|
47
|
+
unread;
|
|
48
|
+
constructor(missing, options = {}) {
|
|
49
|
+
const { unread = false, ...rest } = options;
|
|
50
|
+
const conformanceClass = missing[0] ?? STAC_CONFORMANCE_CLASS["item-search"];
|
|
51
|
+
const named = missing.map((uri) => `"${uri}"`).join(", ");
|
|
52
|
+
const message = unread
|
|
53
|
+
? `STAC search uses conformance class ${named} but the catalog conformsTo list was not read.`
|
|
54
|
+
: `STAC catalog does not declare conformance class ${named}.`;
|
|
55
|
+
super("core.capability-not-supported", message, {
|
|
56
|
+
...rest,
|
|
57
|
+
context: mergeHonuaErrorContext(rest.context, {
|
|
58
|
+
conformanceClass,
|
|
59
|
+
missing: [...missing],
|
|
60
|
+
unread,
|
|
61
|
+
}),
|
|
62
|
+
});
|
|
63
|
+
this.name = "HonuaStacConformanceError";
|
|
64
|
+
this.conformanceClass = conformanceClass;
|
|
65
|
+
this.missing = Object.freeze([...missing]);
|
|
66
|
+
this.unread = unread;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** Copy string entries from a landing-page `conformsTo` array. Omitted becomes `[]` (read, nothing declared). */
|
|
70
|
+
export function normalizeStacConformsTo(value) {
|
|
71
|
+
if (!value)
|
|
72
|
+
return [];
|
|
73
|
+
return value.filter((entry) => typeof entry === "string");
|
|
74
|
+
}
|
|
75
|
+
/** True when `conformsTo` contains the canonical class for `kind`. */
|
|
76
|
+
export function hasStacConformance(conformsTo, kind) {
|
|
77
|
+
if (!conformsTo)
|
|
78
|
+
return false;
|
|
79
|
+
for (const entry of conformsTo) {
|
|
80
|
+
if (typeof entry === "string" && matchesStacConformance(entry, kind))
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Extension classes a search will put on the wire. Core Item Search parameters
|
|
87
|
+
* (`bbox`, `datetime`, `intersects`, `ids`, `collections`, `limit`, paging)
|
|
88
|
+
* are not gated. An extension property counts as soon as it is set, including
|
|
89
|
+
* an empty `fields` or `sortby`, because POST emits those members as-is.
|
|
90
|
+
* Opaque `nextParams` / `nextBody` continuations are not inspected.
|
|
91
|
+
*/
|
|
92
|
+
export function stacSearchExtensionRequirements(request) {
|
|
93
|
+
const usesFilter = request.filter !== undefined || request.filterLang !== undefined;
|
|
94
|
+
const usesSort = request.sortby !== undefined;
|
|
95
|
+
const usesFields = request.fields !== undefined;
|
|
96
|
+
if (!usesFilter && !usesSort && !usesFields)
|
|
97
|
+
return [];
|
|
98
|
+
const kinds = ["item-search"];
|
|
99
|
+
if (usesFilter) {
|
|
100
|
+
kinds.push("filter");
|
|
101
|
+
// STAC Filter defaults to CQL2 Text for GET and CQL2 JSON for POST.
|
|
102
|
+
// https://github.com/stac-api-extensions/filter#get-query-parameters-and-post-json-fields
|
|
103
|
+
// Gate the language the server will actually interpret when it is omitted.
|
|
104
|
+
const language = request.filterLang ?? (request.usePost === true ? "cql2-json" : "cql2-text");
|
|
105
|
+
if (language === "cql2-json")
|
|
106
|
+
kinds.push("cql2-json");
|
|
107
|
+
else if (language === "cql2-text")
|
|
108
|
+
kinds.push("cql2-text");
|
|
109
|
+
else
|
|
110
|
+
kinds.push("cql2-text", "cql2-json");
|
|
111
|
+
}
|
|
112
|
+
if (usesSort)
|
|
113
|
+
kinds.push("sort");
|
|
114
|
+
if (usesFields)
|
|
115
|
+
kinds.push("fields");
|
|
116
|
+
return kinds;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Refuse an extension search that has not been checked against `conformsTo`.
|
|
120
|
+
* `conformsTo === undefined` means the list was never read and fails before
|
|
121
|
+
* any HTTP request. `conformsTo: []` means it was read and the classes are absent.
|
|
122
|
+
*/
|
|
123
|
+
export function gateStacSearchExtensions(request) {
|
|
124
|
+
const required = stacSearchExtensionRequirements(request);
|
|
125
|
+
if (required.length === 0)
|
|
126
|
+
return;
|
|
127
|
+
const missing = required
|
|
128
|
+
.filter((kind) => !hasStacConformance(request.conformsTo, kind))
|
|
129
|
+
.map((kind) => STAC_CONFORMANCE_CLASS[kind]);
|
|
130
|
+
if (missing.length === 0)
|
|
131
|
+
return;
|
|
132
|
+
throw new HonuaStacConformanceError(missing, { unread: request.conformsTo === undefined });
|
|
133
|
+
}
|
|
134
|
+
function matchesStacConformance(value, kind) {
|
|
135
|
+
if (kind === "cql2-text" || kind === "cql2-json")
|
|
136
|
+
return matchesCql2(value, kind);
|
|
137
|
+
const uri = stacSpecUri(value);
|
|
138
|
+
if (!uri)
|
|
139
|
+
return false;
|
|
140
|
+
const path = uri.pathname.replace(/\/+$/, "");
|
|
141
|
+
if (kind === "core" || kind === "item-search") {
|
|
142
|
+
return uri.hash === "" && path === `/v1.0.0/${kind}`;
|
|
143
|
+
}
|
|
144
|
+
const fragment = EXTENSION_FRAGMENT[kind];
|
|
145
|
+
return fragment !== undefined && uri.hash === fragment && (path === ITEM_SEARCH_PATH || path === OGC_FEATURES_PATH);
|
|
146
|
+
}
|
|
147
|
+
function stacSpecUri(value) {
|
|
148
|
+
try {
|
|
149
|
+
const uri = new URL(value);
|
|
150
|
+
if (uri.protocol !== "https:" ||
|
|
151
|
+
uri.origin !== STAC_SPEC_ORIGIN ||
|
|
152
|
+
uri.username !== "" ||
|
|
153
|
+
uri.password !== "" ||
|
|
154
|
+
uri.search !== "") {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
return uri;
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function matchesCql2(value, leaf) {
|
|
164
|
+
try {
|
|
165
|
+
const uri = new URL(value);
|
|
166
|
+
if (uri.protocol !== "http:" && uri.protocol !== "https:")
|
|
167
|
+
return false;
|
|
168
|
+
if (uri.hostname !== "www.opengis.net" || uri.port !== "")
|
|
169
|
+
return false;
|
|
170
|
+
if (uri.username !== "" || uri.password !== "" || uri.search !== "" || uri.hash !== "")
|
|
171
|
+
return false;
|
|
172
|
+
return uri.pathname.replace(/\/+$/, "") === `/spec/cql2/1.0/conf/${leaf}`;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|