mihrab-methods 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +373 -0
- package/README.md +319 -2
- package/certification.json +120 -0
- package/data/corrections/algeria.json +784 -0
- package/data/corrections/diyanet.json +18444 -0
- package/data/corrections/indonesia.json +210 -0
- package/data/corrections/morocco.json +993 -0
- package/data/corrections/sweden.json +516 -0
- package/dist/cjs/algeria.d.ts +4 -0
- package/dist/cjs/algeria.js +22 -0
- package/dist/cjs/authorities/algeria.d.ts +3 -0
- package/dist/cjs/authorities/algeria.js +57 -0
- package/dist/cjs/authorities/diyanet.d.ts +9 -0
- package/dist/cjs/authorities/diyanet.js +90 -0
- package/dist/cjs/authorities/indonesia.d.ts +4 -0
- package/dist/cjs/authorities/indonesia.js +82 -0
- package/dist/cjs/authorities/morocco.d.ts +5 -0
- package/dist/cjs/authorities/morocco.js +65 -0
- package/dist/cjs/authorities/slug.d.ts +2 -0
- package/dist/cjs/authorities/slug.js +17 -0
- package/dist/cjs/authorities/sweden.d.ts +3 -0
- package/dist/cjs/authorities/sweden.js +58 -0
- package/dist/cjs/core/api.d.ts +61 -0
- package/dist/cjs/core/api.js +176 -0
- package/dist/cjs/core/civilDate.d.ts +17 -0
- package/dist/cjs/core/civilDate.js +41 -0
- package/dist/cjs/core/clock.d.ts +15 -0
- package/dist/cjs/core/clock.js +46 -0
- package/dist/cjs/core/compute.d.ts +10 -0
- package/dist/cjs/core/compute.js +59 -0
- package/dist/cjs/core/correctionList.d.ts +25 -0
- package/dist/cjs/core/correctionList.js +62 -0
- package/dist/cjs/core/fetchLatest.d.ts +57 -0
- package/dist/cjs/core/fetchLatest.js +137 -0
- package/dist/cjs/core/index.d.ts +14 -0
- package/dist/cjs/core/index.js +45 -0
- package/dist/cjs/core/registry.d.ts +28 -0
- package/dist/cjs/core/registry.js +29 -0
- package/dist/cjs/core/types.d.ts +193 -0
- package/dist/cjs/core/types.js +22 -0
- package/dist/cjs/core.d.ts +2 -0
- package/dist/cjs/core.js +18 -0
- package/dist/cjs/data/generated/algeriaCertification.d.ts +3 -0
- package/dist/cjs/data/generated/algeriaCertification.js +4 -0
- package/dist/cjs/data/generated/algeriaCities.d.ts +8 -0
- package/dist/cjs/data/generated/algeriaCities.js +5 -0
- package/dist/cjs/data/generated/algeriaCorrections.d.ts +3 -0
- package/dist/cjs/data/generated/algeriaCorrections.js +4 -0
- package/dist/cjs/data/generated/algeriaMethodPoints.d.ts +2 -0
- package/dist/cjs/data/generated/algeriaMethodPoints.js +5 -0
- package/dist/cjs/data/generated/countryOutlines.d.ts +5 -0
- package/dist/cjs/data/generated/countryOutlines.js +5 -0
- package/dist/cjs/data/generated/diyanetCertification.d.ts +3 -0
- package/dist/cjs/data/generated/diyanetCertification.js +4 -0
- package/dist/cjs/data/generated/diyanetCorrections.d.ts +3 -0
- package/dist/cjs/data/generated/diyanetCorrections.js +4 -0
- package/dist/cjs/data/generated/diyanetCountries.d.ts +5 -0
- package/dist/cjs/data/generated/diyanetCountries.js +5 -0
- package/dist/cjs/data/generated/diyanetTurkeyPoints.d.ts +7 -0
- package/dist/cjs/data/generated/diyanetTurkeyPoints.js +5 -0
- package/dist/cjs/data/generated/diyanetWorldPoints.d.ts +10 -0
- package/dist/cjs/data/generated/diyanetWorldPoints.js +5 -0
- package/dist/cjs/data/generated/indonesiaCertification.d.ts +3 -0
- package/dist/cjs/data/generated/indonesiaCertification.js +4 -0
- package/dist/cjs/data/generated/indonesiaCorrections.d.ts +3 -0
- package/dist/cjs/data/generated/indonesiaCorrections.js +4 -0
- package/dist/cjs/data/generated/kemenagMethodPoints.d.ts +2 -0
- package/dist/cjs/data/generated/kemenagMethodPoints.js +5 -0
- package/dist/cjs/data/generated/kemenagPlaces.d.ts +8 -0
- package/dist/cjs/data/generated/kemenagPlaces.js +5 -0
- package/dist/cjs/data/generated/moroccoCertification.d.ts +3 -0
- package/dist/cjs/data/generated/moroccoCertification.js +4 -0
- package/dist/cjs/data/generated/moroccoCities.d.ts +8 -0
- package/dist/cjs/data/generated/moroccoCities.js +5 -0
- package/dist/cjs/data/generated/moroccoCorrections.d.ts +3 -0
- package/dist/cjs/data/generated/moroccoCorrections.js +4 -0
- package/dist/cjs/data/generated/moroccoMethodPoints.d.ts +2 -0
- package/dist/cjs/data/generated/moroccoMethodPoints.js +5 -0
- package/dist/cjs/data/generated/swedenCertification.d.ts +3 -0
- package/dist/cjs/data/generated/swedenCertification.js +4 -0
- package/dist/cjs/data/generated/swedenCorrections.d.ts +3 -0
- package/dist/cjs/data/generated/swedenCorrections.js +4 -0
- package/dist/cjs/data/generated/swedenPlaces.d.ts +5 -0
- package/dist/cjs/data/generated/swedenPlaces.js +5 -0
- package/dist/cjs/diyanet.d.ts +4 -0
- package/dist/cjs/diyanet.js +22 -0
- package/dist/cjs/index.d.ts +19 -0
- package/dist/cjs/index.js +37 -0
- package/dist/cjs/indonesia.d.ts +4 -0
- package/dist/cjs/indonesia.js +22 -0
- package/dist/cjs/locate.d.ts +21 -0
- package/dist/cjs/locate.js +204 -0
- package/dist/cjs/methods/algeria.d.ts +56 -0
- package/dist/cjs/methods/algeria.js +119 -0
- package/dist/cjs/methods/indonesia.d.ts +31 -0
- package/dist/cjs/methods/indonesia.js +94 -0
- package/dist/cjs/methods/morocco.d.ts +61 -0
- package/dist/cjs/methods/morocco.js +113 -0
- package/dist/cjs/methods/revisions.d.ts +15 -0
- package/dist/cjs/methods/revisions.js +18 -0
- package/dist/cjs/methods/scratch-congress.d.ts +87 -0
- package/dist/cjs/methods/scratch-congress.js +539 -0
- package/dist/cjs/methods/sweden.d.ts +88 -0
- package/dist/cjs/methods/sweden.js +568 -0
- package/dist/cjs/morocco.d.ts +4 -0
- package/dist/cjs/morocco.js +22 -0
- package/dist/cjs/sun/preciseSun.d.ts +19 -0
- package/dist/cjs/sun/preciseSun.js +185 -0
- package/dist/cjs/sweden.d.ts +4 -0
- package/dist/cjs/sweden.js +22 -0
- package/dist/esm/algeria.d.ts +4 -0
- package/dist/esm/algeria.js +4 -0
- package/dist/esm/authorities/algeria.d.ts +3 -0
- package/dist/esm/authorities/algeria.js +51 -0
- package/dist/esm/authorities/diyanet.d.ts +9 -0
- package/dist/esm/authorities/diyanet.js +83 -0
- package/dist/esm/authorities/indonesia.d.ts +4 -0
- package/dist/esm/authorities/indonesia.js +75 -0
- package/dist/esm/authorities/morocco.d.ts +5 -0
- package/dist/esm/authorities/morocco.js +59 -0
- package/dist/esm/authorities/slug.d.ts +2 -0
- package/dist/esm/authorities/slug.js +14 -0
- package/dist/esm/authorities/sweden.d.ts +3 -0
- package/dist/esm/authorities/sweden.js +52 -0
- package/dist/esm/core/api.d.ts +61 -0
- package/dist/esm/core/api.js +162 -0
- package/dist/esm/core/civilDate.d.ts +17 -0
- package/dist/esm/core/civilDate.js +36 -0
- package/dist/esm/core/clock.d.ts +15 -0
- package/dist/esm/core/clock.js +39 -0
- package/dist/esm/core/compute.d.ts +10 -0
- package/dist/esm/core/compute.js +56 -0
- package/dist/esm/core/correctionList.d.ts +25 -0
- package/dist/esm/core/correctionList.js +56 -0
- package/dist/esm/core/fetchLatest.d.ts +57 -0
- package/dist/esm/core/fetchLatest.js +98 -0
- package/dist/esm/core/index.d.ts +14 -0
- package/dist/esm/core/index.js +14 -0
- package/dist/esm/core/registry.d.ts +28 -0
- package/dist/esm/core/registry.js +23 -0
- package/dist/esm/core/types.d.ts +193 -0
- package/dist/esm/core/types.js +18 -0
- package/dist/esm/core.d.ts +2 -0
- package/dist/esm/core.js +2 -0
- package/dist/esm/data/generated/algeriaCertification.d.ts +3 -0
- package/dist/esm/data/generated/algeriaCertification.js +2 -0
- package/dist/esm/data/generated/algeriaCities.d.ts +8 -0
- package/dist/esm/data/generated/algeriaCities.js +3 -0
- package/dist/esm/data/generated/algeriaCorrections.d.ts +3 -0
- package/dist/esm/data/generated/algeriaCorrections.js +2 -0
- package/dist/esm/data/generated/algeriaMethodPoints.d.ts +2 -0
- package/dist/esm/data/generated/algeriaMethodPoints.js +3 -0
- package/dist/esm/data/generated/countryOutlines.d.ts +5 -0
- package/dist/esm/data/generated/countryOutlines.js +3 -0
- package/dist/esm/data/generated/diyanetCertification.d.ts +3 -0
- package/dist/esm/data/generated/diyanetCertification.js +2 -0
- package/dist/esm/data/generated/diyanetCorrections.d.ts +3 -0
- package/dist/esm/data/generated/diyanetCorrections.js +2 -0
- package/dist/esm/data/generated/diyanetCountries.d.ts +5 -0
- package/dist/esm/data/generated/diyanetCountries.js +3 -0
- package/dist/esm/data/generated/diyanetTurkeyPoints.d.ts +7 -0
- package/dist/esm/data/generated/diyanetTurkeyPoints.js +3 -0
- package/dist/esm/data/generated/diyanetWorldPoints.d.ts +10 -0
- package/dist/esm/data/generated/diyanetWorldPoints.js +3 -0
- package/dist/esm/data/generated/indonesiaCertification.d.ts +3 -0
- package/dist/esm/data/generated/indonesiaCertification.js +2 -0
- package/dist/esm/data/generated/indonesiaCorrections.d.ts +3 -0
- package/dist/esm/data/generated/indonesiaCorrections.js +2 -0
- package/dist/esm/data/generated/kemenagMethodPoints.d.ts +2 -0
- package/dist/esm/data/generated/kemenagMethodPoints.js +3 -0
- package/dist/esm/data/generated/kemenagPlaces.d.ts +8 -0
- package/dist/esm/data/generated/kemenagPlaces.js +3 -0
- package/dist/esm/data/generated/moroccoCertification.d.ts +3 -0
- package/dist/esm/data/generated/moroccoCertification.js +2 -0
- package/dist/esm/data/generated/moroccoCities.d.ts +8 -0
- package/dist/esm/data/generated/moroccoCities.js +3 -0
- package/dist/esm/data/generated/moroccoCorrections.d.ts +3 -0
- package/dist/esm/data/generated/moroccoCorrections.js +2 -0
- package/dist/esm/data/generated/moroccoMethodPoints.d.ts +2 -0
- package/dist/esm/data/generated/moroccoMethodPoints.js +3 -0
- package/dist/esm/data/generated/swedenCertification.d.ts +3 -0
- package/dist/esm/data/generated/swedenCertification.js +2 -0
- package/dist/esm/data/generated/swedenCorrections.d.ts +3 -0
- package/dist/esm/data/generated/swedenCorrections.js +2 -0
- package/dist/esm/data/generated/swedenPlaces.d.ts +5 -0
- package/dist/esm/data/generated/swedenPlaces.js +3 -0
- package/dist/esm/diyanet.d.ts +4 -0
- package/dist/esm/diyanet.js +4 -0
- package/dist/esm/index.d.ts +19 -0
- package/dist/esm/index.js +19 -0
- package/dist/esm/indonesia.d.ts +4 -0
- package/dist/esm/indonesia.js +4 -0
- package/dist/esm/locate.d.ts +21 -0
- package/dist/esm/locate.js +196 -0
- package/dist/esm/methods/algeria.d.ts +56 -0
- package/dist/esm/methods/algeria.js +114 -0
- package/dist/esm/methods/indonesia.d.ts +31 -0
- package/dist/esm/methods/indonesia.js +90 -0
- package/dist/esm/methods/morocco.d.ts +61 -0
- package/dist/esm/methods/morocco.js +107 -0
- package/dist/esm/methods/revisions.d.ts +15 -0
- package/dist/esm/methods/revisions.js +15 -0
- package/dist/esm/methods/scratch-congress.d.ts +87 -0
- package/dist/esm/methods/scratch-congress.js +534 -0
- package/dist/esm/methods/sweden.d.ts +88 -0
- package/dist/esm/methods/sweden.js +563 -0
- package/dist/esm/morocco.d.ts +4 -0
- package/dist/esm/morocco.js +4 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/sun/preciseSun.d.ts +19 -0
- package/dist/esm/sun/preciseSun.js +181 -0
- package/dist/esm/sweden.d.ts +4 -0
- package/dist/esm/sweden.js +4 -0
- package/docs/methods.md +190 -0
- package/package.json +178 -4
- package/schemas/certification.schema.json +59 -0
- package/schemas/corrections.schema.json +29 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.candidateInstants = candidateInstants;
|
|
4
|
+
/**
|
|
5
|
+
* The methods, run for a place and a civil date (month 0-based, as the
|
|
6
|
+
* methods count): the authority's own formula, then its correction list.
|
|
7
|
+
* This is the computation `timesFor` and the checks share; the API around
|
|
8
|
+
* it is in `api.ts`.
|
|
9
|
+
*/
|
|
10
|
+
const sweden_js_1 = require("../methods/sweden.js");
|
|
11
|
+
const morocco_js_1 = require("../methods/morocco.js");
|
|
12
|
+
const algeria_js_1 = require("../methods/algeria.js");
|
|
13
|
+
const indonesia_js_1 = require("../methods/indonesia.js");
|
|
14
|
+
const registry_js_1 = require("./registry.js");
|
|
15
|
+
const clock_js_1 = require("./clock.js");
|
|
16
|
+
/**
|
|
17
|
+
* The method as it stands, certified or not, for a civil date with the
|
|
18
|
+
* month 0-based. Null where the method gives no times for the day. Not for
|
|
19
|
+
* showing anyone their prayer times: `timesFor` is.
|
|
20
|
+
*/
|
|
21
|
+
function candidateInstants(place, year, month, day, asrShadow = 1, opts = {}) {
|
|
22
|
+
const fix = opts.corrections === false ? null : (0, registry_js_1.authorityData)(place.authority).corrections;
|
|
23
|
+
switch (place.authority) {
|
|
24
|
+
case 'sweden':
|
|
25
|
+
case 'diyanet': {
|
|
26
|
+
// The Congress method's own rounding is inside it; its instants land on the minute (rounded, as the app does).
|
|
27
|
+
const clock = (0, clock_js_1.placeClockAt)(place, year, month, day);
|
|
28
|
+
const t = place.prayerPoints ? perPrayer(place, place.prayerPoints, year, month, day, asrShadow) : (0, sweden_js_1.congressTimes)(place.lat, place.lon, year, month, day, asrShadow, place.ishaDeg, clock);
|
|
29
|
+
// then the correction list: the few times Diyanet's program rounds the other way (the standard Asr only)
|
|
30
|
+
const c = fix?.correction(place.lat, place.lon, year, month, day);
|
|
31
|
+
const min = (d, k) => new Date((Math.round(d.getTime() / 60_000) + (k === 'asr' && asrShadow !== 1 ? 0 : (c?.[k] ?? 0))) * 60_000);
|
|
32
|
+
return { fajr: min(t.fajr, 'fajr'), sunrise: min(t.sunrise, 'sunrise'), dhuhr: min(t.dhuhr, 'dhuhr'), asr: min(t.asr, 'asr'), maghrib: min(t.maghrib, 'maghrib'), isha: min(t.isha, 'isha') };
|
|
33
|
+
}
|
|
34
|
+
case 'morocco':
|
|
35
|
+
case 'algeria':
|
|
36
|
+
case 'indonesia': {
|
|
37
|
+
const t = place.authority === 'morocco'
|
|
38
|
+
? (0, morocco_js_1.moroccoTimes)(place.lat, place.lon, year, month, day, place.dipDeg ?? 0, asrShadow, place.dipMaghribDeg)
|
|
39
|
+
: place.authority === 'algeria'
|
|
40
|
+
? (0, algeria_js_1.algeriaTimes)(place.lat, place.lon, year, month, day, asrShadow)
|
|
41
|
+
: (0, indonesia_js_1.kemenagTimes)(place.lat, place.lon, year, month, day, { highland: place.highland, twilightDip: place.twilightDip, asrShadow, afternoon: place.afternoon });
|
|
42
|
+
// the correction list: the authority's few times on the other side of a minute (the standard Asr only)
|
|
43
|
+
const c = !t || !fix ? undefined : fix.correction(place.lat, place.lon, year, month, day);
|
|
44
|
+
if (!t || !c)
|
|
45
|
+
return t;
|
|
46
|
+
const mv = (d, k) => new Date(d.getTime() + (k === 'asr' && asrShadow !== 1 ? 0 : (c[k] ?? 0)) * 60_000);
|
|
47
|
+
return { fajr: mv(t.fajr, 'fajr'), sunrise: mv(t.sunrise, 'sunrise'), dhuhr: mv(t.dhuhr, 'dhuhr'), asr: mv(t.asr, 'asr'), maghrib: mv(t.maghrib, 'maghrib'), isha: mv(t.isha, 'isha') };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Türkiye's districts: each prayer at its own point for the year (the nearest year's beyond the tables). */
|
|
52
|
+
function perPrayer(place, pts, year, month, day, asrShadow) {
|
|
53
|
+
const years = Object.keys(pts).map(Number).sort((a, b) => a - b);
|
|
54
|
+
const y = years.reduce((b, v) => (Math.abs(v - year) < Math.abs(b - year) ? v : b), years[0]);
|
|
55
|
+
const p = pts[String(y)];
|
|
56
|
+
const clock = (0, clock_js_1.placeClockAt)(place, year, month, day);
|
|
57
|
+
const at = (i) => (0, sweden_js_1.congressTimes)(p[i][0], p[i][1], year, month, day, asrShadow, place.ishaDeg, clock);
|
|
58
|
+
return { fajr: at(0).fajr, sunrise: at(1).sunrise, dhuhr: at(2).dhuhr, asr: at(3).asr, maghrib: at(4).maghrib, isha: at(5).isha };
|
|
59
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A correction list, indexed for lookup: point → date → the minutes to add
|
|
3
|
+
* to each printed time. Built from the embedded data (or, for the tools,
|
|
4
|
+
* from a file read into the same shape).
|
|
5
|
+
*/
|
|
6
|
+
import type { CorrectionsFile, PrayerKey } from './types.js';
|
|
7
|
+
export declare const pointKey: (lat: number, lon: number) => string;
|
|
8
|
+
export declare class CorrectionList {
|
|
9
|
+
private source;
|
|
10
|
+
private index;
|
|
11
|
+
constructor(source: CorrectionsFile);
|
|
12
|
+
private load;
|
|
13
|
+
/** The minutes to add to the point's printed times on a date (month 0-based), if any. */
|
|
14
|
+
correction(lat: number, lon: number, year: number, month: number, day: number): Partial<Record<PrayerKey, number>> | undefined;
|
|
15
|
+
coverage(): {
|
|
16
|
+
from: string;
|
|
17
|
+
to: string;
|
|
18
|
+
};
|
|
19
|
+
entries(): number;
|
|
20
|
+
file(): CorrectionsFile;
|
|
21
|
+
/** Put another list in force (a newer one for the same method revision). */
|
|
22
|
+
replace(source: CorrectionsFile): void;
|
|
23
|
+
}
|
|
24
|
+
/** A list that does not have the shape of one is refused whole. */
|
|
25
|
+
export declare function isCorrectionsFile(x: unknown): x is CorrectionsFile;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CorrectionList = exports.pointKey = void 0;
|
|
4
|
+
exports.isCorrectionsFile = isCorrectionsFile;
|
|
5
|
+
const pointKey = (lat, lon) => `${lat.toFixed(3)},${lon.toFixed(3)}`;
|
|
6
|
+
exports.pointKey = pointKey;
|
|
7
|
+
class CorrectionList {
|
|
8
|
+
source;
|
|
9
|
+
index = null;
|
|
10
|
+
constructor(source) {
|
|
11
|
+
this.source = source;
|
|
12
|
+
}
|
|
13
|
+
load() {
|
|
14
|
+
if (this.index)
|
|
15
|
+
return this.index;
|
|
16
|
+
const index = new Map();
|
|
17
|
+
for (const [point, list] of Object.entries(this.source.points)) {
|
|
18
|
+
const byDate = new Map();
|
|
19
|
+
for (const e of list) {
|
|
20
|
+
const [date, key, step] = e.split(' ');
|
|
21
|
+
const c = byDate.get(date) ?? {};
|
|
22
|
+
c[key] = Number(step);
|
|
23
|
+
byDate.set(date, c);
|
|
24
|
+
}
|
|
25
|
+
index.set(point, byDate);
|
|
26
|
+
}
|
|
27
|
+
return (this.index = index);
|
|
28
|
+
}
|
|
29
|
+
/** The minutes to add to the point's printed times on a date (month 0-based), if any. */
|
|
30
|
+
correction(lat, lon, year, month, day) {
|
|
31
|
+
const date = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
|
32
|
+
return this.load().get((0, exports.pointKey)(lat, lon))?.get(date);
|
|
33
|
+
}
|
|
34
|
+
coverage() {
|
|
35
|
+
return { from: this.source.from, to: this.source.to };
|
|
36
|
+
}
|
|
37
|
+
entries() {
|
|
38
|
+
return Object.values(this.source.points).reduce((n, e) => n + e.length, 0);
|
|
39
|
+
}
|
|
40
|
+
file() {
|
|
41
|
+
return this.source;
|
|
42
|
+
}
|
|
43
|
+
/** Put another list in force (a newer one for the same method revision). */
|
|
44
|
+
replace(source) {
|
|
45
|
+
this.source = source;
|
|
46
|
+
this.index = null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.CorrectionList = CorrectionList;
|
|
50
|
+
/** A list that does not have the shape of one is refused whole. */
|
|
51
|
+
function isCorrectionsFile(x) {
|
|
52
|
+
if (!x || typeof x !== 'object')
|
|
53
|
+
return false;
|
|
54
|
+
const c = x;
|
|
55
|
+
const date = /^\d{4}-\d{2}-\d{2}$/;
|
|
56
|
+
if (typeof c.from !== 'string' || typeof c.to !== 'string' || !date.test(c.from) || !date.test(c.to) || c.from > c.to)
|
|
57
|
+
return false;
|
|
58
|
+
if (!c.points || typeof c.points !== 'object')
|
|
59
|
+
return false;
|
|
60
|
+
const entry = /^\d{4}-\d{2}-\d{2} (fajr|sunrise|dhuhr|asr|maghrib|isha) [+-]1$/;
|
|
61
|
+
return Object.entries(c.points).every(([k, v]) => /^-?\d+\.\d{3},-?\d+\.\d{3}$/.test(k) && Array.isArray(v) && v.every(e => typeof e === 'string' && entry.test(e)));
|
|
62
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Authority, CorrectionsFile } from './types.js';
|
|
2
|
+
/** Where the feed lives. A tagged release (`.../v1.2.0/...`) can replace `main` for a pinned address. */
|
|
3
|
+
export declare const DEFAULT_FEED_URL = "https://raw.githubusercontent.com/MihrabHQ/mihrab-methods/main";
|
|
4
|
+
export type FetchOptions = {
|
|
5
|
+
authority: Authority;
|
|
6
|
+
/** The revision this build computes; default: the loaded method's. */
|
|
7
|
+
revision?: number;
|
|
8
|
+
/** The feed's base URL (default `DEFAULT_FEED_URL`): `<base>/certification.json`, `<base>/data/corrections/<authority>.json`. */
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
/** The fetch to use (default: the global one). */
|
|
11
|
+
fetch?: typeof fetch;
|
|
12
|
+
/** SHA-256 of a UTF-8 string as lowercase hex; default: WebCrypto, else node:crypto, else the hash is not checked. */
|
|
13
|
+
sha256?: (text: string) => Promise<string>;
|
|
14
|
+
/** Put a newer list in force in this process (default true). */
|
|
15
|
+
apply?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type ListSummary = {
|
|
18
|
+
from: string;
|
|
19
|
+
to: string;
|
|
20
|
+
entries: number;
|
|
21
|
+
};
|
|
22
|
+
export type FetchResult =
|
|
23
|
+
/** The certification names the list this build already has (or an older one). */
|
|
24
|
+
{
|
|
25
|
+
status: 'current';
|
|
26
|
+
have: ListSummary;
|
|
27
|
+
certified: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** A newer list for this revision: its contents, and whether the hash matched (or could not be checked). */
|
|
30
|
+
| {
|
|
31
|
+
status: 'updated';
|
|
32
|
+
list: CorrectionsFile;
|
|
33
|
+
summary: ListSummary;
|
|
34
|
+
integrity: 'verified' | 'unchecked';
|
|
35
|
+
applied: boolean;
|
|
36
|
+
}
|
|
37
|
+
/** The method itself has moved on: its lists no longer fit this build, which should be updated. */
|
|
38
|
+
| {
|
|
39
|
+
status: 'method-moved';
|
|
40
|
+
remoteRevision: number;
|
|
41
|
+
have: ListSummary;
|
|
42
|
+
}
|
|
43
|
+
/** The authority has published tables the method is not yet certified on: keep the list in force. */
|
|
44
|
+
| {
|
|
45
|
+
status: 'uncertified';
|
|
46
|
+
have: ListSummary;
|
|
47
|
+
}
|
|
48
|
+
/** Something did not add up (a list not matching what the certification names, a bad hash): nothing taken. */
|
|
49
|
+
| {
|
|
50
|
+
status: 'rejected';
|
|
51
|
+
reason: string;
|
|
52
|
+
have: ListSummary;
|
|
53
|
+
};
|
|
54
|
+
export declare function summarize(list: CorrectionsFile): ListSummary;
|
|
55
|
+
/** Put a list in force for the process — one verified by `fetchLatestCorrections`, or restored from the app's own storage. */
|
|
56
|
+
export declare function applyCorrections(authority: Authority, list: CorrectionsFile, revision: number): boolean;
|
|
57
|
+
export declare function fetchLatestCorrections(opts: FetchOptions): Promise<FetchResult>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DEFAULT_FEED_URL = void 0;
|
|
37
|
+
exports.summarize = summarize;
|
|
38
|
+
exports.applyCorrections = applyCorrections;
|
|
39
|
+
exports.fetchLatestCorrections = fetchLatestCorrections;
|
|
40
|
+
/**
|
|
41
|
+
* Keeping an app current without a release: the repository publishes
|
|
42
|
+
* `certification.json` and the correction lists it certifies, and an app
|
|
43
|
+
* that computes a method's revision can take any newer list certified for
|
|
44
|
+
* that revision. This is the check done right — the revision, the list's
|
|
45
|
+
* identity, and its SHA-256 as the certification names it.
|
|
46
|
+
*/
|
|
47
|
+
const registry_js_1 = require("./registry.js");
|
|
48
|
+
const correctionList_js_1 = require("./correctionList.js");
|
|
49
|
+
/** Where the feed lives. A tagged release (`.../v1.2.0/...`) can replace `main` for a pinned address. */
|
|
50
|
+
exports.DEFAULT_FEED_URL = 'https://raw.githubusercontent.com/MihrabHQ/mihrab-methods/main';
|
|
51
|
+
function summarize(list) {
|
|
52
|
+
return { from: list.from, to: list.to, entries: Object.values(list.points).reduce((n, e) => n + e.length, 0) };
|
|
53
|
+
}
|
|
54
|
+
const same = (a, b) => a.from === b.from && a.to === b.to && a.entries === b.entries;
|
|
55
|
+
async function defaultSha256(text) {
|
|
56
|
+
const bytes = new TextEncoder().encode(text);
|
|
57
|
+
const subtle = globalThis.crypto?.subtle;
|
|
58
|
+
if (subtle) {
|
|
59
|
+
const buf = await subtle.digest('SHA-256', bytes);
|
|
60
|
+
return [...new Uint8Array(buf)].map(b => b.toString(16).padStart(2, '0')).join('');
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const { createHash } = await Promise.resolve().then(() => __importStar(require('node:crypto')));
|
|
64
|
+
return createHash('sha256').update(bytes).digest('hex');
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** Put a list in force for the process — one verified by `fetchLatestCorrections`, or restored from the app's own storage. */
|
|
71
|
+
function applyCorrections(authority, list, revision) {
|
|
72
|
+
const data = (0, registry_js_1.authorityData)(authority);
|
|
73
|
+
if (revision !== data.revision || !(0, correctionList_js_1.isCorrectionsFile)(list))
|
|
74
|
+
return false;
|
|
75
|
+
const now = data.corrections.coverage();
|
|
76
|
+
if (list.to < now.to)
|
|
77
|
+
return false;
|
|
78
|
+
data.corrections.replace(list);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
async function fetchLatestCorrections(opts) {
|
|
82
|
+
const data = (0, registry_js_1.authorityData)(opts.authority);
|
|
83
|
+
const have = { ...data.corrections.coverage(), entries: data.corrections.entries() };
|
|
84
|
+
const revision = opts.revision ?? data.revision;
|
|
85
|
+
const base = (opts.baseUrl ?? exports.DEFAULT_FEED_URL).replace(/\/+$/, '');
|
|
86
|
+
const doFetch = opts.fetch ?? globalThis.fetch;
|
|
87
|
+
if (!doFetch)
|
|
88
|
+
return { status: 'rejected', reason: 'no fetch available', have };
|
|
89
|
+
const certRes = await doFetch(`${base}/certification.json`);
|
|
90
|
+
if (!certRes.ok)
|
|
91
|
+
return { status: 'rejected', reason: `certification.json: HTTP ${certRes.status}`, have };
|
|
92
|
+
const cert = (await certRes.json());
|
|
93
|
+
const entry = cert?.[opts.authority];
|
|
94
|
+
if (!entry || typeof entry !== 'object' || typeof entry.certified !== 'boolean') {
|
|
95
|
+
return { status: 'rejected', reason: `certification.json has no entry for ${opts.authority}`, have };
|
|
96
|
+
}
|
|
97
|
+
if (typeof entry.revision === 'number' && entry.revision > revision) {
|
|
98
|
+
return { status: 'method-moved', remoteRevision: entry.revision, have };
|
|
99
|
+
}
|
|
100
|
+
if (!entry.certified)
|
|
101
|
+
return { status: 'uncertified', have };
|
|
102
|
+
if (entry.revision !== revision)
|
|
103
|
+
return { status: 'rejected', reason: `certified for revision ${entry.revision}, this build computes ${revision}`, have };
|
|
104
|
+
const named = entry.corrections;
|
|
105
|
+
if (!named || typeof named.from !== 'string' || typeof named.to !== 'string' || typeof named.entries !== 'number') {
|
|
106
|
+
return { status: 'rejected', reason: 'the certification names no correction list', have };
|
|
107
|
+
}
|
|
108
|
+
if (same(have, named) || named.to < have.to)
|
|
109
|
+
return { status: 'current', have, certified: true };
|
|
110
|
+
const listRes = await doFetch(`${base}/data/corrections/${opts.authority}.json`);
|
|
111
|
+
if (!listRes.ok)
|
|
112
|
+
return { status: 'rejected', reason: `${opts.authority}.json: HTTP ${listRes.status}`, have };
|
|
113
|
+
const text = await listRes.text();
|
|
114
|
+
let list;
|
|
115
|
+
try {
|
|
116
|
+
list = JSON.parse(text);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return { status: 'rejected', reason: 'the list is not JSON', have };
|
|
120
|
+
}
|
|
121
|
+
if (!(0, correctionList_js_1.isCorrectionsFile)(list))
|
|
122
|
+
return { status: 'rejected', reason: 'the list does not have the shape of one', have };
|
|
123
|
+
const summary = summarize(list);
|
|
124
|
+
if (!same(summary, named))
|
|
125
|
+
return { status: 'rejected', reason: 'the list is not the one the certification names', have };
|
|
126
|
+
let integrity = 'unchecked';
|
|
127
|
+
if (named.sha256) {
|
|
128
|
+
const hash = opts.sha256 ? await opts.sha256(text) : await defaultSha256(text);
|
|
129
|
+
if (hash !== null) {
|
|
130
|
+
if (hash.toLowerCase() !== named.sha256.toLowerCase())
|
|
131
|
+
return { status: 'rejected', reason: 'the list’s SHA-256 is not the one the certification names', have };
|
|
132
|
+
integrity = 'verified';
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const applied = opts.apply === false ? false : applyCorrections(opts.authority, list, revision);
|
|
136
|
+
return { status: 'updated', list, summary, integrity, applied };
|
|
137
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The library without any authority's data: the calls, the types, the sun.
|
|
3
|
+
* Load an authority (`mihrab-methods/<authority>`, or `mihrab-methods` for
|
|
4
|
+
* all) before calling anything that needs its places or its list.
|
|
5
|
+
*/
|
|
6
|
+
export { candidateTimes, certificationOf, certifiedSpan, distanceKm, isCertified, loadedAuthorityIds, methodInfo, nearestPlace, placeById, placesOf, timesFor, timesForRange, type NearestPlace, } from './api.js';
|
|
7
|
+
export { candidateInstants, type ComputeOptions } from './compute.js';
|
|
8
|
+
export { parseCivilDate, isoOf } from './civilDate.js';
|
|
9
|
+
export { placeClockAt, clockOffsetAt, tableClockAt, printedAt } from './clock.js';
|
|
10
|
+
export { fetchLatestCorrections, applyCorrections, type FetchOptions, type FetchResult } from './fetchLatest.js';
|
|
11
|
+
export { isCorrectionsFile, pointKey } from './correctionList.js';
|
|
12
|
+
export { METHOD_REVISION } from '../methods/revisions.js';
|
|
13
|
+
export { preciseApparentSun } from '../sun/preciseSun.js';
|
|
14
|
+
export { AUTHORITIES, PRAYER_KEYS, UncertifiedMethodError, type Authority, type Certification, type CheckRecord, type CivilDate, type CorrectionsFile, type DayStatus, type DayTimes, type Instants, type MethodInfo, type Place, type PrayerKey, type PrayerTime, type TableClock, type TimesOptions, } from './types.js';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UncertifiedMethodError = exports.PRAYER_KEYS = exports.AUTHORITIES = exports.preciseApparentSun = exports.METHOD_REVISION = exports.pointKey = exports.isCorrectionsFile = exports.applyCorrections = exports.fetchLatestCorrections = exports.printedAt = exports.tableClockAt = exports.clockOffsetAt = exports.placeClockAt = exports.isoOf = exports.parseCivilDate = exports.candidateInstants = exports.timesForRange = exports.timesFor = exports.placesOf = exports.placeById = exports.nearestPlace = exports.methodInfo = exports.loadedAuthorityIds = exports.isCertified = exports.distanceKm = exports.certifiedSpan = exports.certificationOf = exports.candidateTimes = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The library without any authority's data: the calls, the types, the sun.
|
|
6
|
+
* Load an authority (`mihrab-methods/<authority>`, or `mihrab-methods` for
|
|
7
|
+
* all) before calling anything that needs its places or its list.
|
|
8
|
+
*/
|
|
9
|
+
var api_js_1 = require("./api.js");
|
|
10
|
+
Object.defineProperty(exports, "candidateTimes", { enumerable: true, get: function () { return api_js_1.candidateTimes; } });
|
|
11
|
+
Object.defineProperty(exports, "certificationOf", { enumerable: true, get: function () { return api_js_1.certificationOf; } });
|
|
12
|
+
Object.defineProperty(exports, "certifiedSpan", { enumerable: true, get: function () { return api_js_1.certifiedSpan; } });
|
|
13
|
+
Object.defineProperty(exports, "distanceKm", { enumerable: true, get: function () { return api_js_1.distanceKm; } });
|
|
14
|
+
Object.defineProperty(exports, "isCertified", { enumerable: true, get: function () { return api_js_1.isCertified; } });
|
|
15
|
+
Object.defineProperty(exports, "loadedAuthorityIds", { enumerable: true, get: function () { return api_js_1.loadedAuthorityIds; } });
|
|
16
|
+
Object.defineProperty(exports, "methodInfo", { enumerable: true, get: function () { return api_js_1.methodInfo; } });
|
|
17
|
+
Object.defineProperty(exports, "nearestPlace", { enumerable: true, get: function () { return api_js_1.nearestPlace; } });
|
|
18
|
+
Object.defineProperty(exports, "placeById", { enumerable: true, get: function () { return api_js_1.placeById; } });
|
|
19
|
+
Object.defineProperty(exports, "placesOf", { enumerable: true, get: function () { return api_js_1.placesOf; } });
|
|
20
|
+
Object.defineProperty(exports, "timesFor", { enumerable: true, get: function () { return api_js_1.timesFor; } });
|
|
21
|
+
Object.defineProperty(exports, "timesForRange", { enumerable: true, get: function () { return api_js_1.timesForRange; } });
|
|
22
|
+
var compute_js_1 = require("./compute.js");
|
|
23
|
+
Object.defineProperty(exports, "candidateInstants", { enumerable: true, get: function () { return compute_js_1.candidateInstants; } });
|
|
24
|
+
var civilDate_js_1 = require("./civilDate.js");
|
|
25
|
+
Object.defineProperty(exports, "parseCivilDate", { enumerable: true, get: function () { return civilDate_js_1.parseCivilDate; } });
|
|
26
|
+
Object.defineProperty(exports, "isoOf", { enumerable: true, get: function () { return civilDate_js_1.isoOf; } });
|
|
27
|
+
var clock_js_1 = require("./clock.js");
|
|
28
|
+
Object.defineProperty(exports, "placeClockAt", { enumerable: true, get: function () { return clock_js_1.placeClockAt; } });
|
|
29
|
+
Object.defineProperty(exports, "clockOffsetAt", { enumerable: true, get: function () { return clock_js_1.clockOffsetAt; } });
|
|
30
|
+
Object.defineProperty(exports, "tableClockAt", { enumerable: true, get: function () { return clock_js_1.tableClockAt; } });
|
|
31
|
+
Object.defineProperty(exports, "printedAt", { enumerable: true, get: function () { return clock_js_1.printedAt; } });
|
|
32
|
+
var fetchLatest_js_1 = require("./fetchLatest.js");
|
|
33
|
+
Object.defineProperty(exports, "fetchLatestCorrections", { enumerable: true, get: function () { return fetchLatest_js_1.fetchLatestCorrections; } });
|
|
34
|
+
Object.defineProperty(exports, "applyCorrections", { enumerable: true, get: function () { return fetchLatest_js_1.applyCorrections; } });
|
|
35
|
+
var correctionList_js_1 = require("./correctionList.js");
|
|
36
|
+
Object.defineProperty(exports, "isCorrectionsFile", { enumerable: true, get: function () { return correctionList_js_1.isCorrectionsFile; } });
|
|
37
|
+
Object.defineProperty(exports, "pointKey", { enumerable: true, get: function () { return correctionList_js_1.pointKey; } });
|
|
38
|
+
var revisions_js_1 = require("../methods/revisions.js");
|
|
39
|
+
Object.defineProperty(exports, "METHOD_REVISION", { enumerable: true, get: function () { return revisions_js_1.METHOD_REVISION; } });
|
|
40
|
+
var preciseSun_js_1 = require("../sun/preciseSun.js");
|
|
41
|
+
Object.defineProperty(exports, "preciseApparentSun", { enumerable: true, get: function () { return preciseSun_js_1.preciseApparentSun; } });
|
|
42
|
+
var types_js_1 = require("./types.js");
|
|
43
|
+
Object.defineProperty(exports, "AUTHORITIES", { enumerable: true, get: function () { return types_js_1.AUTHORITIES; } });
|
|
44
|
+
Object.defineProperty(exports, "PRAYER_KEYS", { enumerable: true, get: function () { return types_js_1.PRAYER_KEYS; } });
|
|
45
|
+
Object.defineProperty(exports, "UncertifiedMethodError", { enumerable: true, get: function () { return types_js_1.UncertifiedMethodError; } });
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What each authority's entry point brings: its places, its correction
|
|
3
|
+
* list, its certification and its description. `mihrab-methods/indonesia`
|
|
4
|
+
* registers Indonesia and nothing else, so an app ships only the data it
|
|
5
|
+
* uses; `mihrab-methods` registers all five.
|
|
6
|
+
*/
|
|
7
|
+
import type { Authority, Certification, CorrectionsFile, MethodInfo, Place } from './types.js';
|
|
8
|
+
import { CorrectionList } from './correctionList.js';
|
|
9
|
+
export type MethodDescription = Pick<MethodInfo, 'name' | 'method' | 'countries' | 'parameters' | 'sources' | 'spec'>;
|
|
10
|
+
export type AuthorityData = {
|
|
11
|
+
authority: Authority;
|
|
12
|
+
revision: number;
|
|
13
|
+
places: Place[];
|
|
14
|
+
corrections: CorrectionList;
|
|
15
|
+
certification: Certification | null;
|
|
16
|
+
description: MethodDescription;
|
|
17
|
+
};
|
|
18
|
+
export declare function registerAuthority(entry: {
|
|
19
|
+
authority: Authority;
|
|
20
|
+
revision: number;
|
|
21
|
+
places: Place[];
|
|
22
|
+
corrections: CorrectionsFile;
|
|
23
|
+
certification: Certification | null;
|
|
24
|
+
description: MethodDescription;
|
|
25
|
+
}): AuthorityData;
|
|
26
|
+
export declare function authorityData(authority: Authority): AuthorityData;
|
|
27
|
+
export declare function loadedAuthorities(): Authority[];
|
|
28
|
+
export declare function isLoaded(authority: Authority): boolean;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerAuthority = registerAuthority;
|
|
4
|
+
exports.authorityData = authorityData;
|
|
5
|
+
exports.loadedAuthorities = loadedAuthorities;
|
|
6
|
+
exports.isLoaded = isLoaded;
|
|
7
|
+
const correctionList_js_1 = require("./correctionList.js");
|
|
8
|
+
const registry = new Map();
|
|
9
|
+
function registerAuthority(entry) {
|
|
10
|
+
const have = registry.get(entry.authority);
|
|
11
|
+
if (have)
|
|
12
|
+
return have;
|
|
13
|
+
const data = { ...entry, corrections: new correctionList_js_1.CorrectionList(entry.corrections) };
|
|
14
|
+
registry.set(entry.authority, data);
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
function authorityData(authority) {
|
|
18
|
+
const data = registry.get(authority);
|
|
19
|
+
if (!data) {
|
|
20
|
+
throw new Error(`The ${authority} method is not loaded: import 'mihrab-methods/${authority}' (or 'mihrab-methods' for all of them) first.`);
|
|
21
|
+
}
|
|
22
|
+
return data;
|
|
23
|
+
}
|
|
24
|
+
function loadedAuthorities() {
|
|
25
|
+
return [...registry.keys()];
|
|
26
|
+
}
|
|
27
|
+
function isLoaded(authority) {
|
|
28
|
+
return registry.has(authority);
|
|
29
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shapes the library speaks in. Nothing here computes anything.
|
|
3
|
+
*/
|
|
4
|
+
export type Authority = 'sweden' | 'diyanet' | 'morocco' | 'algeria' | 'indonesia';
|
|
5
|
+
export declare const AUTHORITIES: readonly Authority[];
|
|
6
|
+
export type PrayerKey = 'fajr' | 'sunrise' | 'dhuhr' | 'asr' | 'maghrib' | 'isha';
|
|
7
|
+
export declare const PRAYER_KEYS: readonly PrayerKey[];
|
|
8
|
+
/** A place an authority publishes a table for, at the point its method is computed at. */
|
|
9
|
+
export type Place = {
|
|
10
|
+
authority: Authority;
|
|
11
|
+
/**
|
|
12
|
+
* A stable identifier: `<authority>:<key>`, where the key is the
|
|
13
|
+
* authority's own number for the place (Diyanet's, the ministries') or,
|
|
14
|
+
* for Sweden, Islamiska Förbundet's slug of its name. It does not change
|
|
15
|
+
* between versions.
|
|
16
|
+
*/
|
|
17
|
+
id: string;
|
|
18
|
+
/** The authority's own key for the place (the part of `id` after the colon). */
|
|
19
|
+
key: string;
|
|
20
|
+
name: string;
|
|
21
|
+
/** ISO 3166-1 alpha-2 of the country the table is for. */
|
|
22
|
+
country: string;
|
|
23
|
+
lat: number;
|
|
24
|
+
lon: number;
|
|
25
|
+
/** The IANA zone the authority prints the place's table in. */
|
|
26
|
+
tz: string;
|
|
27
|
+
/** Morocco: the horizon's dip, degrees (fitted, or 0.02°·√height) — eastward, at sunrise. */
|
|
28
|
+
dipDeg?: number;
|
|
29
|
+
/** Morocco: the horizon's dip westward, at Maghrib (fitted per city). */
|
|
30
|
+
dipMaghribDeg?: number;
|
|
31
|
+
/** Indonesia: Kemenag's highland class (sunrise and Maghrib at −2°). */
|
|
32
|
+
highland?: boolean;
|
|
33
|
+
/** Indonesia: the highland degree on Fajr and Isha too (six regencies of Papua). */
|
|
34
|
+
twilightDip?: boolean;
|
|
35
|
+
/** Indonesia: where the ministry computes the regency's Asr, Maghrib and Isha, when not at its point. */
|
|
36
|
+
afternoon?: readonly [number, number];
|
|
37
|
+
/** Diyanet: Isha's depression, 16° or 17° by country. */
|
|
38
|
+
ishaDeg?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Diyanet in Türkiye and Northern Cyprus: each prayer is computed at a point
|
|
41
|
+
* of its own, and the points move by a few thousandths of a degree from one
|
|
42
|
+
* year's tables to the next — year → [fajr, sunrise, dhuhr, asr, maghrib, isha]
|
|
43
|
+
* as [lat, lon]. `lat`/`lon` are the district's own point (its sunrise).
|
|
44
|
+
*/
|
|
45
|
+
prayerPoints?: Record<string, [number, number][]>;
|
|
46
|
+
/**
|
|
47
|
+
* A table printed on a clock no zone keeps (Diyanet's for Alberta, with
|
|
48
|
+
* summer time into December; Morocco's on GMT+1 until September 2026):
|
|
49
|
+
* the clock read off the table itself, [from date, minutes east of UTC], in
|
|
50
|
+
* place of `tz`. The instants are the method's; only the labels differ.
|
|
51
|
+
*/
|
|
52
|
+
clock?: [string, number][];
|
|
53
|
+
};
|
|
54
|
+
/** The six instants of a day, each on the minute the authority prints. */
|
|
55
|
+
export type Instants = {
|
|
56
|
+
fajr: Date;
|
|
57
|
+
sunrise: Date;
|
|
58
|
+
dhuhr: Date;
|
|
59
|
+
asr: Date;
|
|
60
|
+
maghrib: Date;
|
|
61
|
+
isha: Date;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* A method's correction list: the few times its authority's program puts on
|
|
65
|
+
* the other side of a minute's rounding point. "lat,lon" (three decimals) →
|
|
66
|
+
* "YYYY-MM-DD prayer ±1". See schemas/corrections.schema.json.
|
|
67
|
+
*/
|
|
68
|
+
export type CorrectionsFile = {
|
|
69
|
+
from: string;
|
|
70
|
+
to: string;
|
|
71
|
+
points: Record<string, string[]>;
|
|
72
|
+
};
|
|
73
|
+
export type CheckRecord = {
|
|
74
|
+
/** The tables checked: Islamiska Förbundet's or Diyanet's own for Sweden, the authority's for the rest. */
|
|
75
|
+
tables: string;
|
|
76
|
+
places: number;
|
|
77
|
+
times: number;
|
|
78
|
+
/** Times not on the printed minute. Certified only at 0. */
|
|
79
|
+
off: number;
|
|
80
|
+
from: string;
|
|
81
|
+
to: string;
|
|
82
|
+
/** What the check set aside as the source's fault, not the method's: listed so it can be judged. */
|
|
83
|
+
setAside?: {
|
|
84
|
+
misplaced: string[];
|
|
85
|
+
sourceFaults: number;
|
|
86
|
+
copiedRows: number;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
/** One authority's entry in certification.json. See schemas/certification.schema.json. */
|
|
90
|
+
export type Certification = {
|
|
91
|
+
certified: boolean;
|
|
92
|
+
checkedAt: string;
|
|
93
|
+
checks: CheckRecord[];
|
|
94
|
+
/**
|
|
95
|
+
* The correction list this certification is for: the dates it covers, how
|
|
96
|
+
* many times it moves, and the SHA-256 of the file as published. An app
|
|
97
|
+
* holding a list with another `to` or count holds another list.
|
|
98
|
+
*/
|
|
99
|
+
corrections?: {
|
|
100
|
+
from: string;
|
|
101
|
+
to: string;
|
|
102
|
+
entries: number;
|
|
103
|
+
sha256?: string;
|
|
104
|
+
};
|
|
105
|
+
/** The method's revision (`src/methods/revisions.ts`) the list is for. */
|
|
106
|
+
revision?: number;
|
|
107
|
+
};
|
|
108
|
+
/** A civil date: 'YYYY-MM-DD', or { year, month, day } with January = 1. */
|
|
109
|
+
export type CivilDate = string | {
|
|
110
|
+
year: number;
|
|
111
|
+
month: number;
|
|
112
|
+
day: number;
|
|
113
|
+
};
|
|
114
|
+
/** One prayer's time: the exact moment, and the wall-clock minute the authority prints. */
|
|
115
|
+
export type PrayerTime = {
|
|
116
|
+
instant: Date;
|
|
117
|
+
/** 'HH:MM' on the table's clock (`DayTimes.clock`). */
|
|
118
|
+
printed: string;
|
|
119
|
+
};
|
|
120
|
+
/** The clock a day's times are printed in. */
|
|
121
|
+
export type TableClock = {
|
|
122
|
+
/** The IANA zone, or null where the table keeps a clock no zone does. */
|
|
123
|
+
zone: string | null;
|
|
124
|
+
/** Minutes east of UTC on this date. */
|
|
125
|
+
offsetMinutes: number;
|
|
126
|
+
/** The zone's name, or 'UTC+01:00' where there is none. */
|
|
127
|
+
label: string;
|
|
128
|
+
};
|
|
129
|
+
export type DayStatus =
|
|
130
|
+
/** The date lies within the span the method was certified over: the authority's printed minute. */
|
|
131
|
+
'certified'
|
|
132
|
+
/** Outside that span: the method alone, on the printed minute all but rarely, never a minute further. */
|
|
133
|
+
| 'method-only'
|
|
134
|
+
/** The method does not yet reproduce every time of its authority's tables (`candidateTimes` only). */
|
|
135
|
+
| 'uncertified';
|
|
136
|
+
export type DayTimes = {
|
|
137
|
+
authority: Authority;
|
|
138
|
+
placeId: string;
|
|
139
|
+
/** 'YYYY-MM-DD' */
|
|
140
|
+
date: string;
|
|
141
|
+
status: DayStatus;
|
|
142
|
+
/** The span the method is certified over, or null for an uncertified method. */
|
|
143
|
+
certifiedFrom: string | null;
|
|
144
|
+
certifiedUntil: string | null;
|
|
145
|
+
/** The method's revision that computed this. */
|
|
146
|
+
revision: number;
|
|
147
|
+
clock: TableClock;
|
|
148
|
+
/** Null on a day the method gives no times for the place. */
|
|
149
|
+
times: Record<PrayerKey, PrayerTime> | null;
|
|
150
|
+
};
|
|
151
|
+
export type TimesOptions = {
|
|
152
|
+
/** 2 for the Hanafi Asr; the tables print the standard one (1). */
|
|
153
|
+
asrShadow?: 1 | 2;
|
|
154
|
+
/** Throw `UncertifiedMethodError` for a date outside the certified span, instead of returning `status: 'method-only'`. */
|
|
155
|
+
strict?: boolean;
|
|
156
|
+
};
|
|
157
|
+
/** What `methodInfo` says about a method. */
|
|
158
|
+
export type MethodInfo = {
|
|
159
|
+
authority: Authority;
|
|
160
|
+
/** The authority, in words. */
|
|
161
|
+
name: string;
|
|
162
|
+
/** The method, in a sentence. */
|
|
163
|
+
method: string;
|
|
164
|
+
/** ISO 3166-1 alpha-2 of the countries the authority's tables cover (Diyanet: many). */
|
|
165
|
+
countries: string[];
|
|
166
|
+
revision: number;
|
|
167
|
+
certified: boolean;
|
|
168
|
+
/** The certification record, as in certification.json. */
|
|
169
|
+
certification: Certification | null;
|
|
170
|
+
/** The correction list this build carries. */
|
|
171
|
+
corrections: {
|
|
172
|
+
from: string;
|
|
173
|
+
to: string;
|
|
174
|
+
entries: number;
|
|
175
|
+
};
|
|
176
|
+
/** How many places this build knows for the authority. */
|
|
177
|
+
places: number;
|
|
178
|
+
/** The method's parameters, in words and numbers. */
|
|
179
|
+
parameters: Record<string, string | number | boolean>;
|
|
180
|
+
/** Where the authority publishes the tables the method was fitted to. */
|
|
181
|
+
sources: {
|
|
182
|
+
title: string;
|
|
183
|
+
url: string;
|
|
184
|
+
}[];
|
|
185
|
+
/** The method's own documentation. */
|
|
186
|
+
spec: string;
|
|
187
|
+
};
|
|
188
|
+
/** The method has not reproduced its authority's tables to the minute (or not for this date). */
|
|
189
|
+
export declare class UncertifiedMethodError extends Error {
|
|
190
|
+
readonly authority: string;
|
|
191
|
+
readonly date?: string | undefined;
|
|
192
|
+
constructor(authority: string, date?: string | undefined);
|
|
193
|
+
}
|