@ham2k/extension-sdk 0.8.1 → 0.9.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/dist/activityScoring.js +1 -5
- package/dist/callsigns.js +8 -0
- package/dist/contestSummary.js +48 -0
- package/dist/i18n/contestSummary.en.json +11 -0
- package/dist/i18n/contestSummary.es.json +11 -0
- package/dist/index.d.ts +28 -1
- package/dist/index.js +1 -0
- package/docs/distribution.md +3 -2
- package/docs/hooks.md +6 -0
- package/package.json +1 -1
- package/samples/k2hrc-cqww/src/index.ts +14 -16
package/dist/activityScoring.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createCachedTranslator } from "./i18n.js";
|
|
2
|
+
import { baseCallOf } from "./callsigns.js";
|
|
2
3
|
import { modesMatchForDupe } from "./modes.js";
|
|
3
|
-
import { parseCallsign } from "@ham2k/lib-callsigns";
|
|
4
4
|
import { fmtInteger } from "@ham2k/lib-format-tools";
|
|
5
5
|
import en from "./i18n/activityScoring.en.json";
|
|
6
6
|
import es from "./i18n/activityScoring.es.json";
|
|
@@ -201,10 +201,6 @@ function isGuestOperator(operator, operation) {
|
|
|
201
201
|
const stations = String(operation.stationCall ?? "").split(",").map((call) => baseCallOf(call)).filter((call) => call.length > 0);
|
|
202
202
|
return stations.length > 0 && !stations.includes(baseCallOf(operator));
|
|
203
203
|
}
|
|
204
|
-
function baseCallOf(call) {
|
|
205
|
-
const trimmed = call.trim().toUpperCase();
|
|
206
|
-
return parseCallsign(trimmed).baseCall || trimmed;
|
|
207
|
-
}
|
|
208
204
|
function summarizeActivation(activatedRefs, operatorRefs, qsos, duplicates, huntedQsos, scope, qsosToActivate, rules, t, ctx) {
|
|
209
205
|
const refKeys = Object.keys(activatedRefs).sort();
|
|
210
206
|
const lowest = Math.min(...refKeys.map((key) => activatedRefs[key]));
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createCachedTranslator } from "./i18n.js";
|
|
2
|
+
import { fmtInteger, fmtNumber } from "@ham2k/lib-format-tools";
|
|
3
|
+
import en from "./i18n/contestSummary.en.json";
|
|
4
|
+
import es from "./i18n/contestSummary.es.json";
|
|
5
|
+
const tFor = createCachedTranslator({ en: { translation: en }, es: { translation: es } });
|
|
6
|
+
function contestArithmetic(args, ctx) {
|
|
7
|
+
const t = tFor(ctx);
|
|
8
|
+
const clauses = [args.qsos === 1 ? t("qsoOne") : t("qsoMany", { formatted: fmtInteger(args.qsos) })];
|
|
9
|
+
if (args.points !== void 0) {
|
|
10
|
+
const bonus = args.bonus ? t("bonus", { formatted: fmtInteger(args.bonus) }) : "";
|
|
11
|
+
let expression = args.points === 1 ? t("pointOne") : t("points", { formatted: fmtInteger(args.points) });
|
|
12
|
+
if (bonus && args.bonusFirst) expression = `(${expression} ${bonus})`;
|
|
13
|
+
if (args.mults !== void 0) {
|
|
14
|
+
const mults = args.multsNoun ? `${fmtInteger(args.mults)} ${args.multsNoun}` : args.mults === 1 ? t("multOne") : t("multMany", { formatted: fmtInteger(args.mults) });
|
|
15
|
+
expression += ` \xD7 ${mults}`;
|
|
16
|
+
if (args.multParts && args.multParts.length > 0) expression += ` (${args.multParts.join(", ")})`;
|
|
17
|
+
}
|
|
18
|
+
if (args.factor) {
|
|
19
|
+
const count = Number.isInteger(args.factor.count) ? fmtInteger(args.factor.count) : fmtNumber(args.factor.count, "oneDecimal");
|
|
20
|
+
expression += ` \xD7 ${count} ${args.factor.noun}`;
|
|
21
|
+
}
|
|
22
|
+
if (bonus && !args.bonusFirst) expression += ` ${bonus}`;
|
|
23
|
+
clauses.push(expression);
|
|
24
|
+
}
|
|
25
|
+
clauses.push(...args.more ?? []);
|
|
26
|
+
return clauses.join(", ");
|
|
27
|
+
}
|
|
28
|
+
function contestSummary(args, ctx) {
|
|
29
|
+
if (args.scope === "day") return {};
|
|
30
|
+
const t = tFor(ctx);
|
|
31
|
+
return {
|
|
32
|
+
[args.key]: {
|
|
33
|
+
...args.extra,
|
|
34
|
+
key: args.key,
|
|
35
|
+
for: "operation",
|
|
36
|
+
icon: args.icon,
|
|
37
|
+
total: args.total,
|
|
38
|
+
label: args.total === 1 ? t("titleOnePoint", { title: args.title }) : t("titleManyPoints", { title: args.title, formatted: fmtInteger(args.total) }),
|
|
39
|
+
summary: fmtInteger(args.total),
|
|
40
|
+
longSummary: [args.arithmetic, args.detail].filter((part) => part).join("\n\n"),
|
|
41
|
+
...args.grid ? { grid: true } : {}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
contestArithmetic,
|
|
47
|
+
contestSummary
|
|
48
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"titleOnePoint": "{{title}}: 1 point",
|
|
3
|
+
"titleManyPoints": "{{title}}: {{formatted}} points",
|
|
4
|
+
"qsoOne": "1 QSO",
|
|
5
|
+
"qsoMany": "{{formatted}} QSOs",
|
|
6
|
+
"pointOne": "1 pt",
|
|
7
|
+
"points": "{{formatted}} pts",
|
|
8
|
+
"multOne": "1 mult",
|
|
9
|
+
"multMany": "{{formatted}} mults",
|
|
10
|
+
"bonus": "+ {{formatted}} bonus"
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"titleOnePoint": "{{title}}: 1 punto",
|
|
3
|
+
"titleManyPoints": "{{title}}: {{formatted}} puntos",
|
|
4
|
+
"qsoOne": "1 QSO",
|
|
5
|
+
"qsoMany": "{{formatted}} QSOs",
|
|
6
|
+
"pointOne": "1 pt",
|
|
7
|
+
"points": "{{formatted}} pts",
|
|
8
|
+
"multOne": "1 mult",
|
|
9
|
+
"multMany": "{{formatted}} mults",
|
|
10
|
+
"bonus": "+ {{formatted}} de bonificación"
|
|
11
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @ham2k/extension-sdk 0.
|
|
1
|
+
// @ham2k/extension-sdk 0.9.0
|
|
2
2
|
/** Experimental v1 scene contract. All coordinates are in the scene's viewBox. */
|
|
3
3
|
export interface SvgScene {
|
|
4
4
|
version: 1;
|
|
@@ -1539,6 +1539,33 @@ export declare function contestScorer<S extends Scoresheet = Scoresheet>(scorer:
|
|
|
1539
1539
|
scope: ScoringScope;
|
|
1540
1540
|
};
|
|
1541
1541
|
export declare function tally(key: string, forScope: "day" | "operation", total: number, extra?: Partial<ScoreTally>): ScoreTally;
|
|
1542
|
+
export interface ContestArithmetic {
|
|
1543
|
+
qsos: number;
|
|
1544
|
+
points?: number;
|
|
1545
|
+
mults?: number;
|
|
1546
|
+
multsNoun?: string;
|
|
1547
|
+
multParts?: string[];
|
|
1548
|
+
bonus?: number;
|
|
1549
|
+
bonusFirst?: boolean;
|
|
1550
|
+
factor?: {
|
|
1551
|
+
count: number;
|
|
1552
|
+
noun: string;
|
|
1553
|
+
};
|
|
1554
|
+
more?: string[];
|
|
1555
|
+
}
|
|
1556
|
+
export declare function contestArithmetic(args: ContestArithmetic, ctx: HookContext): string;
|
|
1557
|
+
export interface ContestSummaryArgs {
|
|
1558
|
+
key: string;
|
|
1559
|
+
scope: "day" | "operation";
|
|
1560
|
+
icon: string;
|
|
1561
|
+
title: string;
|
|
1562
|
+
total: number;
|
|
1563
|
+
arithmetic: string;
|
|
1564
|
+
detail?: string;
|
|
1565
|
+
grid?: boolean;
|
|
1566
|
+
extra?: Record<string, JSONValue>;
|
|
1567
|
+
}
|
|
1568
|
+
export declare function contestSummary(args: ContestSummaryArgs, ctx: HookContext): Record<string, ScoreTally>;
|
|
1542
1569
|
export declare function operationForQso(base: Record<string, JSONValue>, qso: Record<string, JSONValue>, segments?: OperationSegmentPayload[]): Record<string, JSONValue>;
|
|
1543
1570
|
export declare function segmentsWith(segments: OperationSegmentPayload[] | undefined, transform: (operation: Record<string, JSONValue>) => Record<string, JSONValue>): OperationSegmentPayload[] | undefined;
|
|
1544
1571
|
export type UniqueAxis = "day" | "band" | "mode" | "ref";
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export * from "./dxcc.js";
|
|
|
8
8
|
export * from "./location.js";
|
|
9
9
|
export * from "./modes.js";
|
|
10
10
|
export * from "./scoring.js";
|
|
11
|
+
export * from "./contestSummary.js";
|
|
11
12
|
export * from "./segments.js";
|
|
12
13
|
export * from "./activityScoring.js";
|
|
13
14
|
export * from "./activityExports.js";
|
package/docs/distribution.md
CHANGED
|
@@ -50,7 +50,8 @@ app at all. Declare the lowest version that has what the bundle uses, so it
|
|
|
50
50
|
reaches every app that can run it: 2 adds `host.webSocket`, and the packer
|
|
51
51
|
refuses a manifest declaring `webSockets` under anything less; 3 adds timers
|
|
52
52
|
and `onShow`/`onHide`, and from 3 the build rewrites a bare `setTimeout` into
|
|
53
|
-
the SDK's (README's "Timers").
|
|
53
|
+
the SDK's (README's "Timers"); 4 lets `host.fetch` reach a plain-`http://` host
|
|
54
|
+
the app fronts with its https proxy (README's `host.fetch`).
|
|
54
55
|
|
|
55
56
|
Three fields are **refused** in a distributed bundle, and it is worth
|
|
56
57
|
understanding why:
|
|
@@ -259,7 +260,7 @@ It reports every problem at once rather than one per run:
|
|
|
259
260
|
h2kext-pack: ./build is not ready to package:
|
|
260
261
|
manifest.json: missing required field 'name'
|
|
261
262
|
manifest.json: key 'my-clock' must start with your callsign — 'my' is not one (e.g. ki2d-my-clock)
|
|
262
|
-
manifest.json: missing 'api' — declare the extension API this was built against (currently
|
|
263
|
+
manifest.json: missing 'api' — declare the extension API this was built against (currently 4)
|
|
263
264
|
```
|
|
264
265
|
|
|
265
266
|
`--force-name` waives the naming convention below. It exists for Ham2K's own
|
package/docs/hooks.md
CHANGED
|
@@ -1224,6 +1224,12 @@ those notice keys (`newPark` is state-parks' spelling of `newRef`) rather than
|
|
|
1224
1224
|
inventing a spelling of your own: an unrecognized key leaves core's `duplicate`
|
|
1225
1225
|
standing beside your credit, on the one row that can show only one.
|
|
1226
1226
|
|
|
1227
|
+
A contest's `summarizeScore` returns `contestSummary(...)` rather than
|
|
1228
|
+
building its tallies by hand: one tally titled with the contest's name and
|
|
1229
|
+
score, its detail opening on a `contestArithmetic(...)` line, and nothing at
|
|
1230
|
+
all for the `day` scope — a contest's multipliers run the whole event
|
|
1231
|
+
(docs/design/contests.md §5.2).
|
|
1232
|
+
|
|
1227
1233
|
A scorer's internal *rules* stay private to its scoresheet: contests don't
|
|
1228
1234
|
share a rule vocabulary, so only notices, alerts, points and summary tallies
|
|
1229
1235
|
cross the boundary. `scoreQso` **may mutate** the scoresheet it's given and
|
package/package.json
CHANGED
|
@@ -20,9 +20,8 @@
|
|
|
20
20
|
// See `docs/hooks.md` §`scoring`, and `contests/cqww` in the app's repository
|
|
21
21
|
// for the whole thing.
|
|
22
22
|
|
|
23
|
-
import { annotateCallAgainstCountryFile, contestScorer, defineExtension } from "@ham2k/extension-sdk"
|
|
24
|
-
import type { ContestScorer, HookContext, JSONValue, LoggingControlDescriptor, Ref
|
|
25
|
-
import { fmtInteger } from "@ham2k/lib-format-tools"
|
|
23
|
+
import { annotateCallAgainstCountryFile, contestArithmetic, contestScorer, contestSummary, defineExtension } from "@ham2k/extension-sdk"
|
|
24
|
+
import type { ContestScorer, HookContext, JSONValue, LoggingControlDescriptor, Ref } from "@ham2k/extension-sdk"
|
|
26
25
|
|
|
27
26
|
import manifest from "../manifest.json" with { type: "json" }
|
|
28
27
|
|
|
@@ -126,24 +125,23 @@ const CQWWScorer: ContestScorer<Scoresheet> = {
|
|
|
126
125
|
return { scoresheet, score: { value: points } }
|
|
127
126
|
},
|
|
128
127
|
|
|
129
|
-
/// The score as the operator reads it.
|
|
130
|
-
///
|
|
131
|
-
///
|
|
132
|
-
summarizeScore({ scoresheet, scope }
|
|
128
|
+
/// The score as the operator reads it. Every contest summarizes the same way — its name and score as the
|
|
129
|
+
/// title, then the arithmetic — so the SDK builds the tally. It also answers
|
|
130
|
+
/// the day scope with nothing: a contest's multipliers run the whole event.
|
|
131
|
+
summarizeScore({ scoresheet, scope }, ctx) {
|
|
133
132
|
const mults = Object.keys(scoresheet.mults).length
|
|
134
|
-
return
|
|
135
|
-
|
|
133
|
+
return contestSummary(
|
|
134
|
+
{
|
|
136
135
|
key: TYPE,
|
|
137
|
-
|
|
136
|
+
scope,
|
|
138
137
|
icon: manifest.icon,
|
|
138
|
+
title: manifest.shortName,
|
|
139
139
|
total: scoresheet.points * mults,
|
|
140
|
-
points: scoresheet.points,
|
|
141
|
-
mults,
|
|
142
|
-
qsos: scoresheet.qsos,
|
|
143
|
-
label: `${fmtInteger(scoresheet.points)} × ${fmtInteger(mults)}`,
|
|
144
|
-
summary: `${fmtInteger(scoresheet.points * mults)}`,
|
|
140
|
+
arithmetic: contestArithmetic({ qsos: scoresheet.qsos, points: scoresheet.points, mults }, ctx),
|
|
141
|
+
extra: { points: scoresheet.points, mults, qsos: scoresheet.qsos },
|
|
145
142
|
},
|
|
146
|
-
|
|
143
|
+
ctx,
|
|
144
|
+
)
|
|
147
145
|
},
|
|
148
146
|
}
|
|
149
147
|
|