Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .github/workflows/tests-and-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Tests and Lint

on:
push:
branches: ["main"]
pull_request:

permissions:
contents: read

jobs:
quality:
name: Tests and lint
runs-on: ubuntu-latest

steps:
- name: Checkout JS client
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"

- name: Enable Corepack
run: corepack enable

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Run lint
run: yarn lint

- name: Run tests
run: yarn test:ci
24 changes: 24 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";

export default tseslint.config(
{
ignores: ["coverage/**", "dist/**", "node_modules/**"],
},
js.configs.recommended,
...tseslint.configs.recommended,
{
files: ["**/*.ts", "**/*.js"],
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
},
rules: {
"@typescript-eslint/no-this-alias": "off",
"no-undef": "off",
},
},
);
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
"build:browser": "tsc -p tsconfig.browser.json",
"build:node": "tsc -p tsconfig.node.json",
"build": "npm run build:browser && npm run build:node",
"lint": "eslint .",
"test": "vitest",
"test:ci": "vitest run",
"test:coverage": "vitest run --coverage",
"prepare": "npm run build",
"prepublishOnly": "npm run build"
Expand All @@ -53,7 +55,11 @@
"@opentelemetry/api": "^1.9.1"
},
"devDependencies": {
"@eslint/js": "10.0.1",
"eslint": "10.4.1",
"globals": "17.6.0",
"typescript": "^5.4.0",
"typescript-eslint": "8.61.0",
"vitest": "^4.1.0"
},
"publishConfig": {
Expand Down
9 changes: 4 additions & 5 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,16 +161,16 @@ export class DClimateClient {
request.variant,
resolvedOrganization
);
resolvedCollection = resolvedInfo.collectionId;
resolvedOrganization = resolvedInfo.organizationId ?? resolvedOrganization;
const resolvedOrganizationId =
resolvedInfo.organizationId ?? resolvedOrganization;

// Multiple variants with concat metadata found
// Load and concatenate based on dclimate:concatPriority
return this.loadAndConcatenateVariants(
{
...request,
collection: resolvedInfo.collectionId,
organization: resolvedInfo.organizationId ?? resolvedOrganization,
organization: resolvedOrganizationId,
variant: request.variant,
},
concatenableItems,
Expand All @@ -184,7 +184,6 @@ export class DClimateClient {

// Fall back to single variant loading
let cid: string | null = null;
let resolvedPath: string;
let metadataDataset = request.dataset;
let metadataCollection = resolvedCollection || request.collection;
let metadataVariant = request.variant ?? "";
Expand Down Expand Up @@ -231,7 +230,7 @@ export class DClimateClient {

// Build path from resolved names
const pathParts = [metadataCollection, metadataDataset, metadataVariant].filter(Boolean);
resolvedPath = pathParts.join("-");
const resolvedPath = pathParts.join("-");

const dataset = await openDatasetFromCid(cid, {
gatewayUrl,
Expand Down
23 changes: 15 additions & 8 deletions src/geotemporal-dataset.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { Dataset, DataArray } from "@dclimate/jaxray";
import {
Dataset,
DataArray,
type CoordinateValue,
type Selection,
} from "@dclimate/jaxray";
import { InvalidSelectionError, NoDataFoundError } from "./errors.js";
import {
BoundsSelection,
BoundsSelectionOptions,
DatasetMetadata,
DatasetObject,
GeoSelectionOptions,
PointQueryOptions,
TimeRange,
Expand Down Expand Up @@ -59,8 +65,8 @@ export class GeoTemporalDataset {
return this.dataset.coords;
}

toObject(): any {
return this.dataset.toObject();
toObject(): DatasetObject {
return this.dataset.toObject() as DatasetObject;
}

toJSON(): string {
Expand Down Expand Up @@ -191,7 +197,7 @@ export class GeoTemporalDataset {
);
}

let normalizedRange: { start: unknown; end: unknown };
let normalizedRange: { start: CoordinateValue; end: CoordinateValue };
try {
normalizedRange = normalizeTimeRange(range, coords);
} catch (error) {
Expand All @@ -204,12 +210,13 @@ export class GeoTemporalDataset {

let subset: Dataset;
try {
subset = await this.dataset.sel({
const selection: Selection = {
[timeKey]: {
start: normalizedRange.start as any,
stop: normalizedRange.end as any,
start: normalizedRange.start,
stop: normalizedRange.end,
},
});
};
subset = await this.dataset.sel(selection);
} catch (error) {
throw new InvalidSelectionError(
`Failed to apply time range on "${timeKey}": ${String(
Expand Down
4 changes: 2 additions & 2 deletions src/shapes/circle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,10 @@ export async function circle(
// Use isel to select only the valid indices
let filtered = await masked.isel({
[latitudeKey]: validLatIndices,
} as any);
});

filtered = await filtered.isel({
[longitudeKey]: validLonIndices,
} as any);
});
return filtered;
}
16 changes: 9 additions & 7 deletions src/shapes/points.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Dataset, DataArray } from "@dclimate/jaxray";
import { Dataset, DataArray, type Selection } from "@dclimate/jaxray";
import { InvalidSelectionError, NoDataFoundError } from "../errors.js";

/**
Expand Down Expand Up @@ -76,16 +76,18 @@ export async function points(

try {
if (snapToGrid) {
selectedData = await dataset.sel({
const selection = {
[latitudeKey]: lats,
[longitudeKey]: lons,
} as any);
} as unknown as Selection;
selectedData = await dataset.sel(selection);
} else {
const selection = {
[latitudeKey]: lats,
[longitudeKey]: lons,
} as unknown as Selection;
selectedData = await dataset.sel(
{
[latitudeKey]: lats,
[longitudeKey]: lons,
} as any,
selection,
{
method: "nearest",
tolerance,
Expand Down
2 changes: 1 addition & 1 deletion src/shapes/rectangle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export async function rectangle(
const filtered = await dataset.sel({
[latitudeKey]: selectedLats,
[longitudeKey]: selectedLons,
} as any);
});

return filtered;
}
56 changes: 39 additions & 17 deletions src/stac/stac-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export interface StacLink {
type?: string;
title?: string;
// For dclimate:id, dclimate:types and other arbitrary metadata
[key: string]: any;
[key: string]: unknown;
}

export interface StacAsset {
Expand All @@ -91,8 +91,8 @@ export interface StacItem {
type: "Feature";
stac_version: string;
id: string;
properties: Record<string, any>;
geometry: any;
properties: Record<string, unknown>;
geometry: unknown;
bbox?: number[];
assets: Record<string, StacAsset>;
links: StacLink[];
Expand All @@ -106,8 +106,8 @@ export interface StacCollection {
description?: string;
keywords?: string[];
license?: string;
extent?: any;
summaries?: Record<string, any>;
extent?: unknown;
summaries?: Record<string, unknown>;
links: StacLink[];
items?: StacItem[]; // Loaded items
organizationId?: string;
Expand All @@ -133,6 +133,22 @@ interface CatalogCacheEntry {
rootCid: string;
}

export function getStringProperty(
properties: Record<string, unknown> | undefined,
key: string
): string | undefined {
const value = properties?.[key];
return typeof value === "string" ? value : undefined;
}

function getNumberProperty(
properties: Record<string, unknown> | undefined,
key: string
): number | undefined {
const value = properties?.[key];
return typeof value === "number" ? value : undefined;
}

export interface StacCatalogOptions {
gatewayUrl?: string;
cacheTtlMs?: number; // Default: 3600000 (1 hour)
Expand Down Expand Up @@ -629,20 +645,20 @@ export function getConcatenableItemsFromStac(
// Check if this item matches our dataset
if (itemCollection === collection && itemDataset === dataset) {
// Check for concatenation metadata in properties
const concatPriority = item.properties["dclimate:concatPriority"];
const concatDimension = item.properties["dclimate:concatDimension"];

// Also check in link metadata (fallback)
const itemLink = collectionObj.links.find(
(link) =>
link.rel === "item" && link?.["dclimate:id"] === item.id
);

const linkConcatPriority = itemLink?.["dclimate:concatPriority"];
const linkConcatDimension = itemLink?.["dclimate:concatDimension"];

const priority = concatPriority ?? linkConcatPriority;
const dimension = concatDimension ?? linkConcatDimension ?? "time";
const priority =
getNumberProperty(item.properties, "dclimate:concatPriority") ??
getNumberProperty(itemLink, "dclimate:concatPriority") ??
0;
const dimension =
getStringProperty(item.properties, "dclimate:concatDimension") ??
getStringProperty(itemLink, "dclimate:concatDimension") ??
"time";

// Extract CID from assets
const dataAsset = item.assets.data;
Expand All @@ -653,7 +669,7 @@ export function getConcatenableItemsFromStac(
matchingItems.push({
variant: itemVariant,
cid,
concatPriority: priority ?? 0,
concatPriority: priority,
concatDimension: dimension,
});
}
Expand All @@ -677,7 +693,7 @@ export function listAvailableDatasetsFromStac(
const datasetNamesFromLink = collection.datasetNames || [];

// Group items by dataset
const datasetMap = new Map<string, { dataset: string; variants: any[] }>();
const datasetMap = new Map<string, CatalogDataset>();

for (const item of collection.items || []) {
const parts = item.id.split("-");
Expand Down Expand Up @@ -711,8 +727,14 @@ export function listAvailableDatasetsFromStac(
};
}

const startDt = item.properties?.start_datetime ?? item.properties?.datetime ?? null;
const endDt = item.properties?.end_datetime ?? item.properties?.datetime ?? null;
const startDt =
getStringProperty(item.properties, "start_datetime") ??
getStringProperty(item.properties, "datetime") ??
null;
const endDt =
getStringProperty(item.properties, "end_datetime") ??
getStringProperty(item.properties, "datetime") ??
null;
if (startDt != null || endDt != null) {
variantEntry.temporalExtent = {
start: startDt ?? null,
Expand Down
Loading
Loading