@helix3/helix-cli 0.1.13-helix3.69 → 0.1.13-helix3.71
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/achievement.d.ts +123 -0
- package/dist/achievement.js +400 -0
- package/dist/achievement.js.map +1 -0
- package/dist/api.d.ts +1 -0
- package/dist/api.js +6 -1
- package/dist/api.js.map +1 -1
- package/dist/index.js +212 -0
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +90 -0
- package/dist/lib.js +173 -3
- package/dist/lib.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/** 2–80 chars, lowercase letters/digits/`_`/`-`, starting alphanumeric. */
|
|
2
|
+
export declare const ACHIEVEMENT_KEY_PATTERN: RegExp;
|
|
3
|
+
/**
|
|
4
|
+
* WHO may award the badge — the trust marker a player and the platform UI read.
|
|
5
|
+
* `platform` is first-party code only; it is in the list because the backend
|
|
6
|
+
* accepts it, not because a world should use it (see `assertUnlockModeForWorld`).
|
|
7
|
+
*/
|
|
8
|
+
export declare const ACHIEVEMENT_UNLOCK_MODES: readonly ["room", "criteria", "platform"];
|
|
9
|
+
export type AchievementUnlockMode = (typeof ACHIEVEMENT_UNLOCK_MODES)[number];
|
|
10
|
+
export declare const ACHIEVEMENT_CRITERIA_SIGNALS: readonly ["datastore", "iwp", "analytics", "playtime"];
|
|
11
|
+
export type AchievementCriteriaSignal = (typeof ACHIEVEMENT_CRITERIA_SIGNALS)[number];
|
|
12
|
+
export declare const ACHIEVEMENT_CRITERIA_OPS: readonly ["gte", "lte", "eq"];
|
|
13
|
+
export type AchievementCriteriaOp = (typeof ACHIEVEMENT_CRITERIA_OPS)[number];
|
|
14
|
+
export declare const ACHIEVEMENT_CRITERIA_METRICS: readonly ["count", "lix"];
|
|
15
|
+
export type AchievementCriteriaMetric = (typeof ACHIEVEMENT_CRITERIA_METRICS)[number];
|
|
16
|
+
/**
|
|
17
|
+
* The only two documents a `datastore` criteria may read, and WHICH ONE IS THE
|
|
18
|
+
* TRUST DECISION: `mp:player:{userId}` is the room's flushed persistent
|
|
19
|
+
* playerVars (room-only writable ⇒ server-authoritative), `player:{userId}` is
|
|
20
|
+
* the player's own client-written save (forgeable by its owner, accepted
|
|
21
|
+
* deliberately — it is how a single-player world unlocks anything).
|
|
22
|
+
*/
|
|
23
|
+
export declare const DATA_STORE_CRITERIA_KEY_TEMPLATES: readonly ["player:{userId}", "mp:player:{userId}"];
|
|
24
|
+
export type DataStoreCriteriaKeyTemplate = (typeof DATA_STORE_CRITERIA_KEY_TEMPLATES)[number];
|
|
25
|
+
/** A `datastore` criteria's `field`: a dot-path of letters, digits and underscores. */
|
|
26
|
+
export declare const DATA_STORE_CRITERIA_FIELD_PATTERN: RegExp;
|
|
27
|
+
/** Backend caps (`ACHIEVEMENT_ICON_MAX_BYTES` / `ACHIEVEMENT_MESH_MAX_BYTES` defaults). */
|
|
28
|
+
export declare const ACHIEVEMENT_ICON_MAX_BYTES: number;
|
|
29
|
+
export declare const ACHIEVEMENT_TROPHY_MAX_BYTES: number;
|
|
30
|
+
/** Smallest square an icon can be and still read as a graphic on a plaque. */
|
|
31
|
+
export declare const MIN_ICON_EDGE = 16;
|
|
32
|
+
/**
|
|
33
|
+
* Per-channel standard deviation below which the backend treats an image as one
|
|
34
|
+
* flat colour. Not zero — a real icon that has been through lossy encoding
|
|
35
|
+
* carries a little noise.
|
|
36
|
+
*/
|
|
37
|
+
export declare const MIN_ICON_STDDEV = 1;
|
|
38
|
+
export declare const isAchievementKey: (value: unknown) => value is string;
|
|
39
|
+
/** Content type for the required `image` part — the 2D badge graphic. */
|
|
40
|
+
export declare function iconContentTypeFromExtension(file: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* What the backend's `assertValidAchievementIcon` checks, mirrored so a
|
|
43
|
+
* placeholder fails in a second instead of after an upload + a moderation pass.
|
|
44
|
+
* The two rejections are the two placeholders creators actually send: an image
|
|
45
|
+
* too small to read, and a single flat colour. Deliberately NOT a quality
|
|
46
|
+
* judgement — a two-tone glyph or a flat logo on a plate passes.
|
|
47
|
+
*/
|
|
48
|
+
export declare function assertIconLooksReal(bytes: Uint8Array, file: string): Promise<void>;
|
|
49
|
+
export type AchievementCriteria = {
|
|
50
|
+
signal: AchievementCriteriaSignal;
|
|
51
|
+
op: AchievementCriteriaOp;
|
|
52
|
+
value: number;
|
|
53
|
+
eventName?: string;
|
|
54
|
+
key?: DataStoreCriteriaKeyTemplate;
|
|
55
|
+
field?: string;
|
|
56
|
+
metric?: AchievementCriteriaMetric;
|
|
57
|
+
};
|
|
58
|
+
export type AchievementCriteriaInput = {
|
|
59
|
+
signal?: string;
|
|
60
|
+
op?: string;
|
|
61
|
+
value?: number;
|
|
62
|
+
eventName?: string;
|
|
63
|
+
key?: string;
|
|
64
|
+
field?: string;
|
|
65
|
+
metric?: string;
|
|
66
|
+
};
|
|
67
|
+
/** True when any criteria field was supplied — i.e. the caller means `criteria`. */
|
|
68
|
+
export declare function hasCriteriaInput(input: AchievementCriteriaInput | undefined): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Validate + normalize the criteria the way the backend's `normalizeCriteria`
|
|
71
|
+
* does, and throw the same failures. Returns the exact object sent as the
|
|
72
|
+
* multipart `criteria` JSON string — no key the caller did not set.
|
|
73
|
+
*/
|
|
74
|
+
export declare function buildAchievementCriteria(input: AchievementCriteriaInput): AchievementCriteria;
|
|
75
|
+
/** One-line human summary of a stored criteria — the `list` column and the register echo. */
|
|
76
|
+
export declare function formatAchievementCriteria(criteria: AchievementCriteria | null | undefined): string;
|
|
77
|
+
export type AchievementRegistrationInput = {
|
|
78
|
+
key: string;
|
|
79
|
+
name: string;
|
|
80
|
+
description?: string;
|
|
81
|
+
points?: number;
|
|
82
|
+
hidden?: boolean;
|
|
83
|
+
unlockMode?: string;
|
|
84
|
+
criteria?: AchievementCriteriaInput;
|
|
85
|
+
};
|
|
86
|
+
export type AchievementRegistrationFields = {
|
|
87
|
+
key: string;
|
|
88
|
+
name: string;
|
|
89
|
+
description?: string;
|
|
90
|
+
points?: number;
|
|
91
|
+
hidden?: boolean;
|
|
92
|
+
unlockMode: AchievementUnlockMode;
|
|
93
|
+
criteria?: AchievementCriteria;
|
|
94
|
+
};
|
|
95
|
+
export type AchievementRegistrationPlan = {
|
|
96
|
+
fields: AchievementRegistrationFields;
|
|
97
|
+
warnings: string[];
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Resolve the trust marker. The backend's DTO defaults to `platform` because the
|
|
101
|
+
* ADMIN path (worldId omitted) is the one that needs it; for a WORLD
|
|
102
|
+
* registration `platform` is a mislabel — nothing in a world is first-party
|
|
103
|
+
* code. So this CLI always sends an explicit mode: `criteria` when a condition
|
|
104
|
+
* was declared, otherwise `room`, the only thing a world can actually award
|
|
105
|
+
* (the `awardAchievement` rule effect).
|
|
106
|
+
*/
|
|
107
|
+
export declare function resolveUnlockMode(explicit: string | undefined, hasCriteria: boolean): AchievementUnlockMode;
|
|
108
|
+
/**
|
|
109
|
+
* Build the multipart text fields, applying every local rule first. Keys the
|
|
110
|
+
* caller did not set are OMITTED rather than sent empty — an empty description
|
|
111
|
+
* and no description are the same row, and sending "" would overwrite nothing
|
|
112
|
+
* while looking like intent.
|
|
113
|
+
*/
|
|
114
|
+
export declare function buildAchievementRegistration(input: AchievementRegistrationInput): AchievementRegistrationPlan;
|
|
115
|
+
export type AchievementUpdateInput = {
|
|
116
|
+
name?: string;
|
|
117
|
+
description?: string;
|
|
118
|
+
points?: number;
|
|
119
|
+
hidden?: boolean;
|
|
120
|
+
active?: boolean;
|
|
121
|
+
};
|
|
122
|
+
/** Validate a PATCH body locally and refuse an empty one (a no-op round trip that reads as success). */
|
|
123
|
+
export declare function buildAchievementUpdate(input: AchievementUpdateInput): AchievementUpdateInput;
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Pure, network-free helpers behind `helix achievement …` — the registration
|
|
3
|
+
// vocabulary (key charset, unlock modes, the closed criteria grammar), the icon
|
|
4
|
+
// sniffing, and the multipart field builder. Split from the HTTP flow in lib.ts
|
|
5
|
+
// for the same reason item.ts is: every rule below is testable without mocking a
|
|
6
|
+
// server, and every one of them costs a round trip if it only fails remotely.
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.isAchievementKey = exports.MIN_ICON_STDDEV = exports.MIN_ICON_EDGE = exports.ACHIEVEMENT_TROPHY_MAX_BYTES = exports.ACHIEVEMENT_ICON_MAX_BYTES = exports.DATA_STORE_CRITERIA_FIELD_PATTERN = exports.DATA_STORE_CRITERIA_KEY_TEMPLATES = exports.ACHIEVEMENT_CRITERIA_METRICS = exports.ACHIEVEMENT_CRITERIA_OPS = exports.ACHIEVEMENT_CRITERIA_SIGNALS = exports.ACHIEVEMENT_UNLOCK_MODES = exports.ACHIEVEMENT_KEY_PATTERN = void 0;
|
|
42
|
+
exports.iconContentTypeFromExtension = iconContentTypeFromExtension;
|
|
43
|
+
exports.assertIconLooksReal = assertIconLooksReal;
|
|
44
|
+
exports.hasCriteriaInput = hasCriteriaInput;
|
|
45
|
+
exports.buildAchievementCriteria = buildAchievementCriteria;
|
|
46
|
+
exports.formatAchievementCriteria = formatAchievementCriteria;
|
|
47
|
+
exports.resolveUnlockMode = resolveUnlockMode;
|
|
48
|
+
exports.buildAchievementRegistration = buildAchievementRegistration;
|
|
49
|
+
exports.buildAchievementUpdate = buildAchievementUpdate;
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// The registration vocabulary
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
//
|
|
54
|
+
// !! MIRROR — KEEP IN LOCKSTEP WITH THE BACKEND !!
|
|
55
|
+
//
|
|
56
|
+
// AUTHORITY: helix-backend-api → src/achievements/dtos/achievement.dtos.ts
|
|
57
|
+
// (`KEY_PATTERN`, `CreateAchievementRequestDto`) and
|
|
58
|
+
// src/database/enums/achievement-unlock-mode.enum.ts +
|
|
59
|
+
// achievement-criteria.enum.ts. The DSL side mirrors the SAME key
|
|
60
|
+
// charset in helix-manifest/schema/manifest-v0.3.schema.json
|
|
61
|
+
// (`awardAchievement.key`) — three copies, one charset.
|
|
62
|
+
//
|
|
63
|
+
// This is a LOCAL FAST PATH, never the authority. It exists so a malformed key
|
|
64
|
+
// or an incoherent criteria fails before an 8 MB icon is uploaded and pushed
|
|
65
|
+
// through moderation — nothing more. Two properties keep a stale copy harmless:
|
|
66
|
+
// the CLI never invents a value (every flag is matched EXACTLY against these
|
|
67
|
+
// lists), and the server is always the last word — its 400 enumerates the real
|
|
68
|
+
// vocabulary and is surfaced verbatim (ApiError.render()). `achievement.spec.ts`
|
|
69
|
+
// pins the lists, so any edit is a reviewed diff rather than silent drift.
|
|
70
|
+
/** 2–80 chars, lowercase letters/digits/`_`/`-`, starting alphanumeric. */
|
|
71
|
+
exports.ACHIEVEMENT_KEY_PATTERN = /^[a-z0-9][a-z0-9_-]{1,79}$/;
|
|
72
|
+
/**
|
|
73
|
+
* WHO may award the badge — the trust marker a player and the platform UI read.
|
|
74
|
+
* `platform` is first-party code only; it is in the list because the backend
|
|
75
|
+
* accepts it, not because a world should use it (see `assertUnlockModeForWorld`).
|
|
76
|
+
*/
|
|
77
|
+
exports.ACHIEVEMENT_UNLOCK_MODES = ['room', 'criteria', 'platform'];
|
|
78
|
+
exports.ACHIEVEMENT_CRITERIA_SIGNALS = ['datastore', 'iwp', 'analytics', 'playtime'];
|
|
79
|
+
exports.ACHIEVEMENT_CRITERIA_OPS = ['gte', 'lte', 'eq'];
|
|
80
|
+
exports.ACHIEVEMENT_CRITERIA_METRICS = ['count', 'lix'];
|
|
81
|
+
/**
|
|
82
|
+
* The only two documents a `datastore` criteria may read, and WHICH ONE IS THE
|
|
83
|
+
* TRUST DECISION: `mp:player:{userId}` is the room's flushed persistent
|
|
84
|
+
* playerVars (room-only writable ⇒ server-authoritative), `player:{userId}` is
|
|
85
|
+
* the player's own client-written save (forgeable by its owner, accepted
|
|
86
|
+
* deliberately — it is how a single-player world unlocks anything).
|
|
87
|
+
*/
|
|
88
|
+
exports.DATA_STORE_CRITERIA_KEY_TEMPLATES = ['player:{userId}', 'mp:player:{userId}'];
|
|
89
|
+
/** A `datastore` criteria's `field`: a dot-path of letters, digits and underscores. */
|
|
90
|
+
exports.DATA_STORE_CRITERIA_FIELD_PATTERN = /^[A-Za-z0-9_]+(\.[A-Za-z0-9_]+)*$/;
|
|
91
|
+
/** Backend caps (`ACHIEVEMENT_ICON_MAX_BYTES` / `ACHIEVEMENT_MESH_MAX_BYTES` defaults). */
|
|
92
|
+
exports.ACHIEVEMENT_ICON_MAX_BYTES = 8 * 1024 * 1024;
|
|
93
|
+
exports.ACHIEVEMENT_TROPHY_MAX_BYTES = 16 * 1024 * 1024;
|
|
94
|
+
/** Smallest square an icon can be and still read as a graphic on a plaque. */
|
|
95
|
+
exports.MIN_ICON_EDGE = 16;
|
|
96
|
+
/**
|
|
97
|
+
* Per-channel standard deviation below which the backend treats an image as one
|
|
98
|
+
* flat colour. Not zero — a real icon that has been through lossy encoding
|
|
99
|
+
* carries a little noise.
|
|
100
|
+
*/
|
|
101
|
+
exports.MIN_ICON_STDDEV = 1;
|
|
102
|
+
const isAchievementKey = (value) => typeof value === 'string' && exports.ACHIEVEMENT_KEY_PATTERN.test(value);
|
|
103
|
+
exports.isAchievementKey = isAchievementKey;
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Icon + trophy file sniffing
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// The backend sniffs the BYTES (magic numbers) rather than trusting the label,
|
|
108
|
+
// and accepts every raster format `sniffAssetKind` knows. The extension map here
|
|
109
|
+
// only picks the content-type for the multipart part.
|
|
110
|
+
const ICON_CONTENT_TYPES = {
|
|
111
|
+
'.png': 'image/png',
|
|
112
|
+
'.jpg': 'image/jpeg',
|
|
113
|
+
'.jpeg': 'image/jpeg',
|
|
114
|
+
'.webp': 'image/webp',
|
|
115
|
+
'.gif': 'image/gif',
|
|
116
|
+
'.avif': 'image/avif',
|
|
117
|
+
'.heic': 'image/heic',
|
|
118
|
+
'.heif': 'image/heif',
|
|
119
|
+
};
|
|
120
|
+
const lowerExtension = (file) => {
|
|
121
|
+
const base = file.slice(Math.max(file.lastIndexOf('/'), file.lastIndexOf('\\')) + 1);
|
|
122
|
+
const dot = base.lastIndexOf('.');
|
|
123
|
+
return dot <= 0 ? '' : base.slice(dot).toLowerCase();
|
|
124
|
+
};
|
|
125
|
+
/** Content type for the required `image` part — the 2D badge graphic. */
|
|
126
|
+
function iconContentTypeFromExtension(file) {
|
|
127
|
+
const contentType = ICON_CONTENT_TYPES[lowerExtension(file)];
|
|
128
|
+
if (!contentType) {
|
|
129
|
+
throw new Error(`unsupported achievement icon type "${lowerExtension(file) || file}" — use png, jpg/jpeg, webp, gif, avif or heic`);
|
|
130
|
+
}
|
|
131
|
+
return contentType;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* What the backend's `assertValidAchievementIcon` checks, mirrored so a
|
|
135
|
+
* placeholder fails in a second instead of after an upload + a moderation pass.
|
|
136
|
+
* The two rejections are the two placeholders creators actually send: an image
|
|
137
|
+
* too small to read, and a single flat colour. Deliberately NOT a quality
|
|
138
|
+
* judgement — a two-tone glyph or a flat logo on a plate passes.
|
|
139
|
+
*/
|
|
140
|
+
async function assertIconLooksReal(bytes, file) {
|
|
141
|
+
if (bytes.byteLength === 0)
|
|
142
|
+
throw new Error(`"${file}" is empty`);
|
|
143
|
+
const sharp = (await Promise.resolve().then(() => __importStar(require('sharp')))).default;
|
|
144
|
+
let meta;
|
|
145
|
+
let stats;
|
|
146
|
+
try {
|
|
147
|
+
const pipeline = sharp(Buffer.from(bytes));
|
|
148
|
+
meta = await pipeline.metadata();
|
|
149
|
+
stats = await pipeline.stats();
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
throw new Error(`"${file}" could not be decoded as an image (${err instanceof Error ? err.message : String(err)}) — ` +
|
|
153
|
+
'an achievement needs a real 2D graphic.');
|
|
154
|
+
}
|
|
155
|
+
const width = meta.width ?? 0;
|
|
156
|
+
const height = meta.height ?? 0;
|
|
157
|
+
if (width < exports.MIN_ICON_EDGE || height < exports.MIN_ICON_EDGE) {
|
|
158
|
+
throw new Error(`"${file}" is ${width || '?'}x${height || '?'}; an achievement icon must be at least ${exports.MIN_ICON_EDGE}x${exports.MIN_ICON_EDGE}. ` +
|
|
159
|
+
'Placeholder images are rejected — upload the real artwork.');
|
|
160
|
+
}
|
|
161
|
+
// A fully transparent image is blank whatever its colour channels say, so an
|
|
162
|
+
// alpha-bearing icon is judged on its visible pixels: flatten onto mid grey
|
|
163
|
+
// (a colour no flat icon is likely to coincide with) and re-measure.
|
|
164
|
+
if (meta.hasAlpha) {
|
|
165
|
+
try {
|
|
166
|
+
const flattened = await sharp(Buffer.from(bytes))
|
|
167
|
+
.flatten({ background: { r: 128, g: 128, b: 128 } })
|
|
168
|
+
.png()
|
|
169
|
+
.toBuffer();
|
|
170
|
+
stats = await sharp(flattened).stats();
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// Fall through to the un-flattened stats rather than fail on a flatten
|
|
174
|
+
// quirk — the flat-opaque case is still caught below.
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const channels = (stats.channels ?? []).filter(
|
|
178
|
+
// A constant alpha channel means "every pixel opaque", which is normal, not
|
|
179
|
+
// evidence of blankness.
|
|
180
|
+
(_c, i) => !(meta.hasAlpha && i === (stats.channels?.length ?? 0) - 1));
|
|
181
|
+
const maxStdev = channels.reduce((acc, c) => Math.max(acc, c.stdev ?? 0), 0);
|
|
182
|
+
if (channels.length > 0 && maxStdev < exports.MIN_ICON_STDDEV) {
|
|
183
|
+
throw new Error(`"${file}" is a single flat colour with no visible graphic — upload the real 2D artwork for this achievement.`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/** True when any criteria field was supplied — i.e. the caller means `criteria`. */
|
|
187
|
+
function hasCriteriaInput(input) {
|
|
188
|
+
if (!input)
|
|
189
|
+
return false;
|
|
190
|
+
return Object.values(input).some((v) => v !== undefined && v !== '');
|
|
191
|
+
}
|
|
192
|
+
const list = (values) => values.join(', ');
|
|
193
|
+
/**
|
|
194
|
+
* Validate + normalize the criteria the way the backend's `normalizeCriteria`
|
|
195
|
+
* does, and throw the same failures. Returns the exact object sent as the
|
|
196
|
+
* multipart `criteria` JSON string — no key the caller did not set.
|
|
197
|
+
*/
|
|
198
|
+
function buildAchievementCriteria(input) {
|
|
199
|
+
const signal = input.signal;
|
|
200
|
+
if (!signal) {
|
|
201
|
+
throw new Error(`a criteria achievement needs --signal (one of ${list(exports.ACHIEVEMENT_CRITERIA_SIGNALS)})`);
|
|
202
|
+
}
|
|
203
|
+
if (!exports.ACHIEVEMENT_CRITERIA_SIGNALS.includes(signal)) {
|
|
204
|
+
throw new Error(`unknown --signal "${signal}" — supported signals: ${list(exports.ACHIEVEMENT_CRITERIA_SIGNALS)}`);
|
|
205
|
+
}
|
|
206
|
+
// The server requires `op`; it has no default of its own to override, and
|
|
207
|
+
// "reach N" is what nearly every badge means, so defaulting here is a
|
|
208
|
+
// convenience rather than a guess at server behaviour. It is echoed back in
|
|
209
|
+
// the register output so a `lte` badge that meant `gte` is visible.
|
|
210
|
+
const op = input.op ?? 'gte';
|
|
211
|
+
if (!exports.ACHIEVEMENT_CRITERIA_OPS.includes(op)) {
|
|
212
|
+
throw new Error(`unknown --op "${op}" — supported ops: ${list(exports.ACHIEVEMENT_CRITERIA_OPS)}`);
|
|
213
|
+
}
|
|
214
|
+
if (input.value === undefined)
|
|
215
|
+
throw new Error('a criteria achievement needs --value (the threshold to compare against)');
|
|
216
|
+
if (!Number.isFinite(input.value) || input.value < 0) {
|
|
217
|
+
throw new Error(`--value must be a number >= 0 (got ${input.value})`);
|
|
218
|
+
}
|
|
219
|
+
const criteria = {
|
|
220
|
+
signal: signal,
|
|
221
|
+
op: op,
|
|
222
|
+
value: input.value,
|
|
223
|
+
};
|
|
224
|
+
if (signal === 'analytics') {
|
|
225
|
+
if (!input.eventName) {
|
|
226
|
+
throw new Error('the "analytics" signal needs --event <platform analytics event> (e.g. world_entered) — ' +
|
|
227
|
+
'a world cannot mint its own event name for criteria; the server rejects an unknown one by name.');
|
|
228
|
+
}
|
|
229
|
+
criteria.eventName = input.eventName;
|
|
230
|
+
}
|
|
231
|
+
else if (input.eventName) {
|
|
232
|
+
throw new Error(`--event only applies to the "analytics" signal (--signal is "${signal}")`);
|
|
233
|
+
}
|
|
234
|
+
if (signal === 'datastore') {
|
|
235
|
+
if (!input.key) {
|
|
236
|
+
throw new Error(`the "datastore" signal needs --criteria-key, one of the two per-user documents: ${exports.DATA_STORE_CRITERIA_KEY_TEMPLATES.map((t) => `"${t}"`).join(' or ')}`);
|
|
237
|
+
}
|
|
238
|
+
if (!exports.DATA_STORE_CRITERIA_KEY_TEMPLATES.includes(input.key)) {
|
|
239
|
+
throw new Error(`unknown --criteria-key "${input.key}" — it must be exactly ${exports.DATA_STORE_CRITERIA_KEY_TEMPLATES.map((t) => `"${t}"`).join(' or ')} ` +
|
|
240
|
+
'(a numeric sub-key is no longer writable by a session, so `field` selects the value).');
|
|
241
|
+
}
|
|
242
|
+
if (!input.field) {
|
|
243
|
+
throw new Error('the "datastore" signal needs --criteria-field: the dot-path to the number inside that document ' +
|
|
244
|
+
'(e.g. "stats.summits"; for mp:player:{userId} it names the persistent playerVar directly).');
|
|
245
|
+
}
|
|
246
|
+
if (!exports.DATA_STORE_CRITERIA_FIELD_PATTERN.test(input.field)) {
|
|
247
|
+
throw new Error(`--criteria-field "${input.field}" must be a dot-path of letters, digits and underscores (e.g. "stats.summits")`);
|
|
248
|
+
}
|
|
249
|
+
criteria.key = input.key;
|
|
250
|
+
criteria.field = input.field;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
if (input.key)
|
|
254
|
+
throw new Error(`--criteria-key only applies to the "datastore" signal (--signal is "${signal}")`);
|
|
255
|
+
if (input.field)
|
|
256
|
+
throw new Error(`--criteria-field only applies to the "datastore" signal (--signal is "${signal}")`);
|
|
257
|
+
}
|
|
258
|
+
if (signal === 'iwp') {
|
|
259
|
+
const metric = input.metric ?? 'count';
|
|
260
|
+
if (!exports.ACHIEVEMENT_CRITERIA_METRICS.includes(metric)) {
|
|
261
|
+
throw new Error(`unknown --metric "${metric}" — supported metrics: ${list(exports.ACHIEVEMENT_CRITERIA_METRICS)}`);
|
|
262
|
+
}
|
|
263
|
+
criteria.metric = metric;
|
|
264
|
+
}
|
|
265
|
+
else if (input.metric) {
|
|
266
|
+
throw new Error(`--metric only applies to the "iwp" signal (--signal is "${signal}")`);
|
|
267
|
+
}
|
|
268
|
+
return criteria;
|
|
269
|
+
}
|
|
270
|
+
/** One-line human summary of a stored criteria — the `list` column and the register echo. */
|
|
271
|
+
function formatAchievementCriteria(criteria) {
|
|
272
|
+
if (!criteria)
|
|
273
|
+
return '';
|
|
274
|
+
const cmp = `${criteria.op} ${criteria.value}`;
|
|
275
|
+
switch (criteria.signal) {
|
|
276
|
+
case 'datastore':
|
|
277
|
+
return `datastore ${criteria.key}.${criteria.field} ${cmp}`;
|
|
278
|
+
case 'iwp':
|
|
279
|
+
return `iwp ${criteria.metric ?? 'count'} ${cmp}`;
|
|
280
|
+
case 'analytics':
|
|
281
|
+
return `analytics ${criteria.eventName} ${cmp}`;
|
|
282
|
+
default:
|
|
283
|
+
return `${criteria.signal} ${cmp}`;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Resolve the trust marker. The backend's DTO defaults to `platform` because the
|
|
288
|
+
* ADMIN path (worldId omitted) is the one that needs it; for a WORLD
|
|
289
|
+
* registration `platform` is a mislabel — nothing in a world is first-party
|
|
290
|
+
* code. So this CLI always sends an explicit mode: `criteria` when a condition
|
|
291
|
+
* was declared, otherwise `room`, the only thing a world can actually award
|
|
292
|
+
* (the `awardAchievement` rule effect).
|
|
293
|
+
*/
|
|
294
|
+
function resolveUnlockMode(explicit, hasCriteria) {
|
|
295
|
+
if (explicit !== undefined) {
|
|
296
|
+
if (!exports.ACHIEVEMENT_UNLOCK_MODES.includes(explicit)) {
|
|
297
|
+
throw new Error(`unknown --unlock-mode "${explicit}" — supported modes: ${list(exports.ACHIEVEMENT_UNLOCK_MODES)}`);
|
|
298
|
+
}
|
|
299
|
+
const mode = explicit;
|
|
300
|
+
if (mode === 'criteria' && !hasCriteria) {
|
|
301
|
+
throw new Error('--unlock-mode criteria needs the condition too — pass --signal/--value (and the signal\'s own flag).');
|
|
302
|
+
}
|
|
303
|
+
if (mode !== 'criteria' && hasCriteria) {
|
|
304
|
+
throw new Error(`criteria flags were given, but --unlock-mode is "${mode}" — the backend only reads a criteria when the mode is "criteria".`);
|
|
305
|
+
}
|
|
306
|
+
return mode;
|
|
307
|
+
}
|
|
308
|
+
return hasCriteria ? 'criteria' : 'room';
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Build the multipart text fields, applying every local rule first. Keys the
|
|
312
|
+
* caller did not set are OMITTED rather than sent empty — an empty description
|
|
313
|
+
* and no description are the same row, and sending "" would overwrite nothing
|
|
314
|
+
* while looking like intent.
|
|
315
|
+
*/
|
|
316
|
+
function buildAchievementRegistration(input) {
|
|
317
|
+
const warnings = [];
|
|
318
|
+
const key = input.key?.trim().toLowerCase() ?? '';
|
|
319
|
+
if (!key)
|
|
320
|
+
throw new Error('--key is required (the stable slug your rules and criteria name)');
|
|
321
|
+
if (!(0, exports.isAchievementKey)(key)) {
|
|
322
|
+
throw new Error(`--key "${key}" must be 2-80 chars of lowercase letters, digits, "_" or "-", starting alphanumeric. ` +
|
|
323
|
+
'It is the identifier `awardAchievement` names, so treat it as a schema: renaming one needs a fresh registration.');
|
|
324
|
+
}
|
|
325
|
+
if (key !== input.key?.trim())
|
|
326
|
+
warnings.push(`key lowercased to "${key}" — the registry stores it lowercase.`);
|
|
327
|
+
const name = input.name?.trim() ?? '';
|
|
328
|
+
if (!name)
|
|
329
|
+
throw new Error('--name is required (the badge title players see)');
|
|
330
|
+
if (name.length > 100)
|
|
331
|
+
throw new Error(`--name is ${name.length} characters; the limit is 100`);
|
|
332
|
+
const description = input.description?.trim();
|
|
333
|
+
if (description !== undefined && description.length > 2000) {
|
|
334
|
+
throw new Error(`--description is ${description.length} characters; the limit is 2000`);
|
|
335
|
+
}
|
|
336
|
+
if (input.points !== undefined) {
|
|
337
|
+
if (!Number.isInteger(input.points) || input.points < 0 || input.points > 1000) {
|
|
338
|
+
throw new Error(`--points must be a whole number between 0 and 1000 (got ${input.points})`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
const hasCriteria = hasCriteriaInput(input.criteria);
|
|
342
|
+
const unlockMode = resolveUnlockMode(input.unlockMode, hasCriteria);
|
|
343
|
+
const criteria = hasCriteria ? buildAchievementCriteria(input.criteria) : undefined;
|
|
344
|
+
if (unlockMode === 'platform') {
|
|
345
|
+
warnings.push('unlockMode "platform" means FIRST-PARTY code awards it — nothing in your world can. ' +
|
|
346
|
+
'Use "room" (an awardAchievement rule) or "criteria" (the backend measures it) unless you know why you want this.');
|
|
347
|
+
}
|
|
348
|
+
if (unlockMode === 'room') {
|
|
349
|
+
warnings.push(`register first, publish second: a rule awarding "${key}" is a silent no-op until this registration exists.`);
|
|
350
|
+
}
|
|
351
|
+
if (criteria?.key === 'player:{userId}') {
|
|
352
|
+
warnings.push('criteria on "player:{userId}" reads the PLAYER\'s own save — they can write it, so this badge is self-reported. ' +
|
|
353
|
+
'Use "mp:player:{userId}" (the room\'s flushed persistent playerVars) for anything that must be trustworthy.');
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
fields: {
|
|
357
|
+
key,
|
|
358
|
+
name,
|
|
359
|
+
...(description ? { description } : {}),
|
|
360
|
+
...(input.points !== undefined ? { points: input.points } : {}),
|
|
361
|
+
...(input.hidden !== undefined ? { hidden: input.hidden } : {}),
|
|
362
|
+
unlockMode,
|
|
363
|
+
...(criteria ? { criteria } : {}),
|
|
364
|
+
},
|
|
365
|
+
warnings,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
/** Validate a PATCH body locally and refuse an empty one (a no-op round trip that reads as success). */
|
|
369
|
+
function buildAchievementUpdate(input) {
|
|
370
|
+
const patch = {};
|
|
371
|
+
if (input.name !== undefined) {
|
|
372
|
+
const name = input.name.trim();
|
|
373
|
+
if (!name)
|
|
374
|
+
throw new Error('--name cannot be empty');
|
|
375
|
+
if (name.length > 100)
|
|
376
|
+
throw new Error(`--name is ${name.length} characters; the limit is 100`);
|
|
377
|
+
patch.name = name;
|
|
378
|
+
}
|
|
379
|
+
if (input.description !== undefined) {
|
|
380
|
+
const description = input.description.trim();
|
|
381
|
+
if (description.length > 2000)
|
|
382
|
+
throw new Error(`--description is ${description.length} characters; the limit is 2000`);
|
|
383
|
+
patch.description = description;
|
|
384
|
+
}
|
|
385
|
+
if (input.points !== undefined) {
|
|
386
|
+
if (!Number.isInteger(input.points) || input.points < 0 || input.points > 1000) {
|
|
387
|
+
throw new Error(`--points must be a whole number between 0 and 1000 (got ${input.points})`);
|
|
388
|
+
}
|
|
389
|
+
patch.points = input.points;
|
|
390
|
+
}
|
|
391
|
+
if (input.hidden !== undefined)
|
|
392
|
+
patch.hidden = input.hidden;
|
|
393
|
+
if (input.active !== undefined)
|
|
394
|
+
patch.active = input.active;
|
|
395
|
+
if (Object.keys(patch).length === 0) {
|
|
396
|
+
throw new Error('nothing to update — pass at least one of --name, --description, --points, --hidden, --active');
|
|
397
|
+
}
|
|
398
|
+
return patch;
|
|
399
|
+
}
|
|
400
|
+
//# sourceMappingURL=achievement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"achievement.js","sourceRoot":"","sources":["../src/achievement.ts"],"names":[],"mappings":"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"}
|
package/dist/api.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export declare class HelixApi {
|
|
|
19
19
|
request<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
20
20
|
requestItem<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
21
21
|
postMultipart<T>(path: string, form: FormData): Promise<T>;
|
|
22
|
+
sendMultipart<T>(method: string, path: string, form: FormData): Promise<T>;
|
|
22
23
|
static forCredentials(creds: CliCredentials): HelixApi;
|
|
23
24
|
}
|
|
24
25
|
export declare function uploadToTicket(url: string, body: Buffer, contentType: string, cacheControl?: string): Promise<void>;
|
package/dist/api.js
CHANGED
|
@@ -66,8 +66,13 @@ class HelixApi {
|
|
|
66
66
|
// Blob (globals since Node 18, same undici that already backs `fetch` above),
|
|
67
67
|
// so multipart needs no dependency.
|
|
68
68
|
async postMultipart(path, form) {
|
|
69
|
+
return this.sendMultipart('POST', path, form);
|
|
70
|
+
}
|
|
71
|
+
// The same transport for the multipart endpoints that REPLACE rather than
|
|
72
|
+
// create (achievement icon/trophy are PUTs).
|
|
73
|
+
async sendMultipart(method, path, form) {
|
|
69
74
|
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
70
|
-
method
|
|
75
|
+
method,
|
|
71
76
|
headers: this.token ? { authorization: `Bearer ${this.token}` } : {},
|
|
72
77
|
body: form,
|
|
73
78
|
});
|
package/dist/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|