@procedures/weather 0.28.0 → 0.28.1
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/CHANGELOG.md +24 -0
- package/README.md +15 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/module.d.ts +6 -0
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +10 -1
- package/dist/module.js.map +1 -1
- package/dist/procedures.d.ts +38 -1
- package/dist/procedures.d.ts.map +1 -1
- package/dist/procedures.js +52 -25
- package/dist/procedures.js.map +1 -1
- package/package.json +3 -2
- package/src/module.ts +10 -1
- package/src/procedures.ts +72 -24
package/dist/module.d.ts
CHANGED
|
@@ -2,8 +2,14 @@
|
|
|
2
2
|
* The procedures of @procedures/weather as data. Importing this file registers nothing.
|
|
3
3
|
*/
|
|
4
4
|
import { type ProcedureModule, type ProcedureRegistry } from "@procedures/client";
|
|
5
|
+
import { type WeatherProceduresOptions } from "./procedures.js";
|
|
5
6
|
export declare const weatherModule: ProcedureModule;
|
|
6
7
|
export default weatherModule;
|
|
8
|
+
/**
|
|
9
|
+
* This function gives the module with the settings of the host: the `fetch` of the requests.
|
|
10
|
+
* Without options, it gives `weatherModule`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createWeatherModule(options?: WeatherProceduresOptions): ProcedureModule;
|
|
7
13
|
/**
|
|
8
14
|
* This function registers the procedures of @procedures/weather in a registry. The default is the
|
|
9
15
|
* global registry.
|
package/dist/module.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAqD,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAqD,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACrI,OAAO,EAA8C,KAAK,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAE5G,eAAO,MAAM,aAAa,EAAE,eAAwE,CAAC;eAEtF,aAAa;AAE5B;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,wBAA6B,GAAG,eAAe,CAG3F;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,GAAE,iBAAsC,GAAG,IAAI,CAEhG"}
|
package/dist/module.js
CHANGED
|
@@ -2,9 +2,18 @@
|
|
|
2
2
|
* The procedures of @procedures/weather as data. Importing this file registers nothing.
|
|
3
3
|
*/
|
|
4
4
|
import { defineModule, registerModules, PROCEDURE_REGISTRY } from "@procedures/client";
|
|
5
|
-
import { weatherProcedures } from "./procedures.js";
|
|
5
|
+
import { createWeatherProcedures, weatherProcedures } from "./procedures.js";
|
|
6
6
|
export const weatherModule = defineModule("@procedures/weather", weatherProcedures);
|
|
7
7
|
export default weatherModule;
|
|
8
|
+
/**
|
|
9
|
+
* This function gives the module with the settings of the host: the `fetch` of the requests.
|
|
10
|
+
* Without options, it gives `weatherModule`.
|
|
11
|
+
*/
|
|
12
|
+
export function createWeatherModule(options = {}) {
|
|
13
|
+
if (options.fetch === undefined)
|
|
14
|
+
return weatherModule;
|
|
15
|
+
return defineModule("@procedures/weather", createWeatherProcedures(options));
|
|
16
|
+
}
|
|
8
17
|
/**
|
|
9
18
|
* This function registers the procedures of @procedures/weather in a registry. The default is the
|
|
10
19
|
* global registry.
|
package/dist/module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAgD,MAAM,oBAAoB,CAAC;AACrI,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAgD,MAAM,oBAAoB,CAAC;AACrI,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAiC,MAAM,iBAAiB,CAAC;AAE5G,MAAM,CAAC,MAAM,aAAa,GAAoB,YAAY,CAAC,qBAAqB,EAAE,iBAAiB,CAAC,CAAC;AAErG,eAAe,aAAa,CAAC;AAE7B;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAO,GAA6B,EAAE;IACxE,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,aAAa,CAAC;IACtD,OAAO,YAAY,CAAC,qBAAqB,EAAE,uBAAuB,CAAC,OAAO,CAAC,CAAC,CAAC;AAC/E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAAQ,GAAsB,kBAAkB;IACxF,eAAe,CAAC,QAAQ,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC;AAC7C,CAAC"}
|
package/dist/procedures.d.ts
CHANGED
|
@@ -1,7 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The procedure of `@procedures/weather`: `weather.forecast`.
|
|
3
|
+
*
|
|
4
|
+
* The request goes through the host policy of `@procedures/http` (audit pass 2, O55). In 0.28, a
|
|
5
|
+
* `baseUrl` on a loopback, private or link-local address still works, with a deprecation notice.
|
|
6
|
+
* Version 0.29.0 refuses it, unless the host allows it.
|
|
3
7
|
*/
|
|
4
8
|
import { type AnyProcedure } from "@procedures/client";
|
|
5
|
-
|
|
9
|
+
import { type DeprecationNotice } from "@procedures/http";
|
|
10
|
+
/**
|
|
11
|
+
* The deprecation notice of the default module: a `baseUrl` on a host that the host policy refuses.
|
|
12
|
+
* The default module sends the request with the global `fetch`, as before. Version 0.29.0 refuses it.
|
|
13
|
+
*/
|
|
14
|
+
export declare const WEATHER_PRIVATE_HOST_NOTICE: DeprecationNotice;
|
|
15
|
+
/**
|
|
16
|
+
* The part of `fetch` that `weather.forecast` uses. The default is `createHostFetch()` of
|
|
17
|
+
* `@procedures/http`, with the notice `WEATHER_PRIVATE_HOST_NOTICE` for a refused host.
|
|
18
|
+
*/
|
|
19
|
+
export type FetchForecast = (url: string, init: {
|
|
20
|
+
headers: Record<string, string>;
|
|
21
|
+
signal: AbortSignal;
|
|
22
|
+
}) => Promise<{
|
|
23
|
+
ok: boolean;
|
|
24
|
+
status: number;
|
|
25
|
+
json(): Promise<unknown>;
|
|
26
|
+
}>;
|
|
27
|
+
/** The settings of the host for the procedures of this package. The procedure input cannot change them. */
|
|
28
|
+
export interface WeatherProceduresOptions {
|
|
29
|
+
/**
|
|
30
|
+
* The function that sends the request. The default is `createHostFetch()` with a notice for a
|
|
31
|
+
* refused host. A host with its own Open-Meteo server on a private address gives
|
|
32
|
+
* `createHostFetch({ allow: ["10.0.0.5:8080"] })`.
|
|
33
|
+
*/
|
|
34
|
+
readonly fetch?: FetchForecast | undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* This function gives the procedures of this package with the settings of the host. Without
|
|
38
|
+
* options, the requests go through the default host policy of `@procedures/http`, and a refused
|
|
39
|
+
* host gets the notice `WEATHER_PRIVATE_HOST_NOTICE`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function createWeatherProcedures(options?: WeatherProceduresOptions): AnyProcedure[];
|
|
6
42
|
export declare const weatherProcedures: AnyProcedure[];
|
|
43
|
+
export declare const weatherForecastProcedure: AnyProcedure;
|
|
7
44
|
//# sourceMappingURL=procedures.d.ts.map
|
package/dist/procedures.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"procedures.d.ts","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"procedures.d.ts","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAA+B,KAAK,YAAY,EAAyB,MAAM,oBAAoB,CAAC;AAC3G,OAAO,EAAqC,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAI7F;;;GAGG;AACH,eAAO,MAAM,2BAA2B,EAAE,iBAKzC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,KAC3D,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC,CAAC;AAExE,2GAA2G;AAC3G,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;CAC5C;AAgCD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,wBAA6B,GAAG,YAAY,EAAE,CA2B9F;AAED,eAAO,MAAM,iBAAiB,EAAE,YAAY,EAA8B,CAAC;AAE3E,eAAO,MAAM,wBAAwB,EAAE,YAAoC,CAAC"}
|
package/dist/procedures.js
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The procedure of `@procedures/weather`: `weather.forecast`.
|
|
3
|
+
*
|
|
4
|
+
* The request goes through the host policy of `@procedures/http` (audit pass 2, O55). In 0.28, a
|
|
5
|
+
* `baseUrl` on a loopback, private or link-local address still works, with a deprecation notice.
|
|
6
|
+
* Version 0.29.0 refuses it, unless the host allows it.
|
|
3
7
|
*/
|
|
4
8
|
import { createProcedure, zodAdapter } from "@procedures/client";
|
|
9
|
+
import { createHostFetch, isHostNotAllowed } from "@procedures/http";
|
|
5
10
|
import { z } from "zod";
|
|
6
11
|
import { forecastUrl, readForecast } from "./weather.js";
|
|
12
|
+
/**
|
|
13
|
+
* The deprecation notice of the default module: a `baseUrl` on a host that the host policy refuses.
|
|
14
|
+
* The default module sends the request with the global `fetch`, as before. Version 0.29.0 refuses it.
|
|
15
|
+
*/
|
|
16
|
+
export const WEATHER_PRIVATE_HOST_NOTICE = {
|
|
17
|
+
code: "PROCEDURES_WEATHER_PRIVATE_HOST",
|
|
18
|
+
message: "weather.forecast sent a request to a loopback, private or link-local host. @procedures/weather 0.29.0 refuses such a host. " +
|
|
19
|
+
"To keep this server, give createWeatherModule({ fetch: createHostFetch({ allow: [...] }) }) with the host and port of the server.",
|
|
20
|
+
};
|
|
7
21
|
const ForecastInputSchema = z.object({
|
|
8
22
|
lat: z.number().min(-90).max(90).describe("The latitude of the point, in degrees"),
|
|
9
23
|
lon: z.number().min(-180).max(180).describe("The longitude of the point, in degrees"),
|
|
@@ -29,29 +43,42 @@ const ForecastOutputSchema = z.object({
|
|
|
29
43
|
.array(z.object({ time: z.string(), temperature: nullableNumber, precipitationProbability: nullableNumber, weatherCode: nullableNumber }))
|
|
30
44
|
.optional(),
|
|
31
45
|
});
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
/**
|
|
47
|
+
* This function gives the procedures of this package with the settings of the host. Without
|
|
48
|
+
* options, the requests go through the default host policy of `@procedures/http`, and a refused
|
|
49
|
+
* host gets the notice `WEATHER_PRIVATE_HOST_NOTICE`.
|
|
50
|
+
*/
|
|
51
|
+
export function createWeatherProcedures(options = {}) {
|
|
52
|
+
const fetchForecast = options.fetch ?? createHostFetch({ warnOnRefusal: WEATHER_PRIVATE_HOST_NOTICE });
|
|
53
|
+
const forecast = createProcedure()
|
|
54
|
+
.path(["weather", "forecast"])
|
|
55
|
+
.input(zodAdapter(ForecastInputSchema))
|
|
56
|
+
.output(zodAdapter(ForecastOutputSchema))
|
|
57
|
+
.meta({
|
|
58
|
+
description: "Give the weather forecast of a point for each day, and optionally each hour (Open-Meteo)",
|
|
59
|
+
output: "json",
|
|
60
|
+
effects: { reads: ["network"], writes: [] },
|
|
61
|
+
})
|
|
62
|
+
.handler(async (input, ctx) => {
|
|
63
|
+
const signals = [AbortSignal.timeout(input.timeoutMs), ...(ctx?.signal ? [ctx.signal] : [])];
|
|
64
|
+
let response;
|
|
65
|
+
try {
|
|
66
|
+
response = await fetchForecast(forecastUrl(input), { signal: AbortSignal.any(signals), headers: { accept: "application/json" } });
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
// A refusal of the host policy keeps its code and its reason
|
|
70
|
+
if (isHostNotAllowed(error))
|
|
71
|
+
throw error;
|
|
72
|
+
const reason = error.name === "TimeoutError" ? `no answer in ${input.timeoutMs} ms` : "the request failed";
|
|
73
|
+
throw new Error(`Open-Meteo did not answer: ${reason}`);
|
|
74
|
+
}
|
|
75
|
+
if (!response.ok)
|
|
76
|
+
throw new Error(`Open-Meteo gave HTTP ${response.status}`);
|
|
77
|
+
return readForecast(await response.json(), input.hourly);
|
|
78
|
+
})
|
|
79
|
+
.build();
|
|
80
|
+
return [forecast];
|
|
81
|
+
}
|
|
82
|
+
export const weatherProcedures = createWeatherProcedures();
|
|
83
|
+
export const weatherForecastProcedure = weatherProcedures[0];
|
|
57
84
|
//# sourceMappingURL=procedures.js.map
|
package/dist/procedures.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"procedures.js","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"procedures.js","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,UAAU,EAA4C,MAAM,oBAAoB,CAAC;AAC3G,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAA0B,MAAM,kBAAkB,CAAC;AAC7F,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,YAAY,EAAiB,MAAM,cAAc,CAAC;AAExE;;;GAGG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAsB;IAC5D,IAAI,EAAE,iCAAiC;IACvC,OAAO,EACL,6HAA6H;QAC7H,mIAAmI;CACtI,CAAC;AAqBF,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,uCAAuC,CAAC;IAClF,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,wCAAwC,CAAC;IACrF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,6CAA6C,CAAC;IACxG,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,0DAA0D,CAAC;IAC5H,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,sCAAsC,CAAC;IACnF,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wDAAwD,CAAC;IAC9F,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,kDAAkD,CAAC;CACjI,CAAC,CAAC;AAGH,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC;AAC7C,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IACnE,KAAK,EAAE,CAAC,CAAC,KAAK,CACZ,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,cAAc,EAAE,cAAc;QAC9B,cAAc,EAAE,cAAc;QAC9B,wBAAwB,EAAE,cAAc;QACxC,YAAY,EAAE,cAAc;QAC5B,WAAW,EAAE,cAAc;KAC5B,CAAC,CACH;IACD,MAAM,EAAE,CAAC;SACN,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE,wBAAwB,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC,CAAC;SACzI,QAAQ,EAAE;CACd,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAO,GAA6B,EAAE;IAC5E,MAAM,aAAa,GAAkB,OAAO,CAAC,KAAK,IAAI,eAAe,CAAC,EAAE,aAAa,EAAE,2BAA2B,EAAE,CAAC,CAAC;IACtH,MAAM,QAAQ,GAAG,eAAe,EAAE;SAC/B,IAAI,CAAC,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;SAC7B,KAAK,CAAC,UAAU,CAAgB,mBAAmB,CAAC,CAAC;SACrD,MAAM,CAAC,UAAU,CAAW,oBAAoB,CAAC,CAAC;SAClD,IAAI,CAAC;QACJ,WAAW,EAAE,0FAA0F;QACvG,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;KAC5C,CAAC;SACD,OAAO,CAAC,KAAK,EAAE,KAAoB,EAAE,GAAsB,EAAqB,EAAE;QACjF,MAAM,OAAO,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7F,IAAI,QAA4C,CAAC;QACjD,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,aAAa,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;QACpI,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,6DAA6D;YAC7D,IAAI,gBAAgB,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;YACzC,MAAM,MAAM,GAAI,KAA2B,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,gBAAgB,KAAK,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC;YAClI,MAAM,IAAI,KAAK,CAAC,8BAA8B,MAAM,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7E,OAAO,YAAY,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3D,CAAC,CAAC;SACD,KAAK,EAAkB,CAAC;IAC3B,OAAO,CAAC,QAAQ,CAAC,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAmB,uBAAuB,EAAE,CAAC;AAE3E,MAAM,CAAC,MAAM,wBAAwB,GAAiB,iBAAiB,CAAC,CAAC,CAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@procedures/weather",
|
|
4
|
-
"version": "0.28.
|
|
4
|
+
"version": "0.28.1",
|
|
5
5
|
"description": "A weather forecast for a point from Open-Meteo, without a key: each day and each hour, metric or imperial.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"weather",
|
|
@@ -56,7 +56,8 @@
|
|
|
56
56
|
"provenance": true
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@procedures/client": "0.28.
|
|
59
|
+
"@procedures/client": "0.28.1",
|
|
60
|
+
"@procedures/http": "0.28.1",
|
|
60
61
|
"zod": "^4.6.5"
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
package/src/module.ts
CHANGED
|
@@ -3,12 +3,21 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { defineModule, registerModules, PROCEDURE_REGISTRY, type ProcedureModule, type ProcedureRegistry } from "@procedures/client";
|
|
6
|
-
import { weatherProcedures } from "./procedures.js";
|
|
6
|
+
import { createWeatherProcedures, weatherProcedures, type WeatherProceduresOptions } from "./procedures.js";
|
|
7
7
|
|
|
8
8
|
export const weatherModule: ProcedureModule = defineModule("@procedures/weather", weatherProcedures);
|
|
9
9
|
|
|
10
10
|
export default weatherModule;
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* This function gives the module with the settings of the host: the `fetch` of the requests.
|
|
14
|
+
* Without options, it gives `weatherModule`.
|
|
15
|
+
*/
|
|
16
|
+
export function createWeatherModule(options: WeatherProceduresOptions = {}): ProcedureModule {
|
|
17
|
+
if (options.fetch === undefined) return weatherModule;
|
|
18
|
+
return defineModule("@procedures/weather", createWeatherProcedures(options));
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
/**
|
|
13
22
|
* This function registers the procedures of @procedures/weather in a registry. The default is the
|
|
14
23
|
* global registry.
|
package/src/procedures.ts
CHANGED
|
@@ -1,11 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The procedure of `@procedures/weather`: `weather.forecast`.
|
|
3
|
+
*
|
|
4
|
+
* The request goes through the host policy of `@procedures/http` (audit pass 2, O55). In 0.28, a
|
|
5
|
+
* `baseUrl` on a loopback, private or link-local address still works, with a deprecation notice.
|
|
6
|
+
* Version 0.29.0 refuses it, unless the host allows it.
|
|
3
7
|
*/
|
|
4
8
|
|
|
5
9
|
import { createProcedure, zodAdapter, type AnyProcedure, type ProcedureContext } from "@procedures/client";
|
|
10
|
+
import { createHostFetch, isHostNotAllowed, type DeprecationNotice } from "@procedures/http";
|
|
6
11
|
import { z } from "zod";
|
|
7
12
|
import { forecastUrl, readForecast, type Forecast } from "./weather.js";
|
|
8
13
|
|
|
14
|
+
/**
|
|
15
|
+
* The deprecation notice of the default module: a `baseUrl` on a host that the host policy refuses.
|
|
16
|
+
* The default module sends the request with the global `fetch`, as before. Version 0.29.0 refuses it.
|
|
17
|
+
*/
|
|
18
|
+
export const WEATHER_PRIVATE_HOST_NOTICE: DeprecationNotice = {
|
|
19
|
+
code: "PROCEDURES_WEATHER_PRIVATE_HOST",
|
|
20
|
+
message:
|
|
21
|
+
"weather.forecast sent a request to a loopback, private or link-local host. @procedures/weather 0.29.0 refuses such a host. " +
|
|
22
|
+
"To keep this server, give createWeatherModule({ fetch: createHostFetch({ allow: [...] }) }) with the host and port of the server.",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The part of `fetch` that `weather.forecast` uses. The default is `createHostFetch()` of
|
|
27
|
+
* `@procedures/http`, with the notice `WEATHER_PRIVATE_HOST_NOTICE` for a refused host.
|
|
28
|
+
*/
|
|
29
|
+
export type FetchForecast = (
|
|
30
|
+
url: string,
|
|
31
|
+
init: { headers: Record<string, string>; signal: AbortSignal }
|
|
32
|
+
) => Promise<{ ok: boolean; status: number; json(): Promise<unknown> }>;
|
|
33
|
+
|
|
34
|
+
/** The settings of the host for the procedures of this package. The procedure input cannot change them. */
|
|
35
|
+
export interface WeatherProceduresOptions {
|
|
36
|
+
/**
|
|
37
|
+
* The function that sends the request. The default is `createHostFetch()` with a notice for a
|
|
38
|
+
* refused host. A host with its own Open-Meteo server on a private address gives
|
|
39
|
+
* `createHostFetch({ allow: ["10.0.0.5:8080"] })`.
|
|
40
|
+
*/
|
|
41
|
+
readonly fetch?: FetchForecast | undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
9
44
|
const ForecastInputSchema = z.object({
|
|
10
45
|
lat: z.number().min(-90).max(90).describe("The latitude of the point, in degrees"),
|
|
11
46
|
lon: z.number().min(-180).max(180).describe("The longitude of the point, in degrees"),
|
|
@@ -36,27 +71,40 @@ const ForecastOutputSchema = z.object({
|
|
|
36
71
|
.optional(),
|
|
37
72
|
});
|
|
38
73
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
74
|
+
/**
|
|
75
|
+
* This function gives the procedures of this package with the settings of the host. Without
|
|
76
|
+
* options, the requests go through the default host policy of `@procedures/http`, and a refused
|
|
77
|
+
* host gets the notice `WEATHER_PRIVATE_HOST_NOTICE`.
|
|
78
|
+
*/
|
|
79
|
+
export function createWeatherProcedures(options: WeatherProceduresOptions = {}): AnyProcedure[] {
|
|
80
|
+
const fetchForecast: FetchForecast = options.fetch ?? createHostFetch({ warnOnRefusal: WEATHER_PRIVATE_HOST_NOTICE });
|
|
81
|
+
const forecast = createProcedure()
|
|
82
|
+
.path(["weather", "forecast"])
|
|
83
|
+
.input(zodAdapter<ForecastInput>(ForecastInputSchema))
|
|
84
|
+
.output(zodAdapter<Forecast>(ForecastOutputSchema))
|
|
85
|
+
.meta({
|
|
86
|
+
description: "Give the weather forecast of a point for each day, and optionally each hour (Open-Meteo)",
|
|
87
|
+
output: "json",
|
|
88
|
+
effects: { reads: ["network"], writes: [] },
|
|
89
|
+
})
|
|
90
|
+
.handler(async (input: ForecastInput, ctx?: ProcedureContext): Promise<Forecast> => {
|
|
91
|
+
const signals = [AbortSignal.timeout(input.timeoutMs), ...(ctx?.signal ? [ctx.signal] : [])];
|
|
92
|
+
let response: Awaited<ReturnType<FetchForecast>>;
|
|
93
|
+
try {
|
|
94
|
+
response = await fetchForecast(forecastUrl(input), { signal: AbortSignal.any(signals), headers: { accept: "application/json" } });
|
|
95
|
+
} catch (error) {
|
|
96
|
+
// A refusal of the host policy keeps its code and its reason
|
|
97
|
+
if (isHostNotAllowed(error)) throw error;
|
|
98
|
+
const reason = (error as { name?: string }).name === "TimeoutError" ? `no answer in ${input.timeoutMs} ms` : "the request failed";
|
|
99
|
+
throw new Error(`Open-Meteo did not answer: ${reason}`);
|
|
100
|
+
}
|
|
101
|
+
if (!response.ok) throw new Error(`Open-Meteo gave HTTP ${response.status}`);
|
|
102
|
+
return readForecast(await response.json(), input.hourly);
|
|
103
|
+
})
|
|
104
|
+
.build() as AnyProcedure;
|
|
105
|
+
return [forecast];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const weatherProcedures: AnyProcedure[] = createWeatherProcedures();
|
|
109
|
+
|
|
110
|
+
export const weatherForecastProcedure: AnyProcedure = weatherProcedures[0]!;
|