modelalive 0.5.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/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # modelalive (JavaScript)
2
+
3
+ TypeScript/JavaScript SDK for [Model Alive](https://github.com/Ahaozandaburada/modelalive).
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install modelalive
9
+ ```
10
+
11
+ Build from source (monorepo):
12
+
13
+ ```bash
14
+ npm install
15
+ npm run build
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```typescript
21
+ import { alive, check, ensure, resolve } from "modelalive";
22
+
23
+ // Non-throwing check
24
+ const result = alive("claude-sonnet-4-20250514");
25
+ console.log(result.status); // "retired"
26
+ console.log(result.replacement); // "claude-sonnet-4-6"
27
+
28
+ // Pre-flight before API call
29
+ const safe = ensure("claude-sonnet-4-20250514"); // "claude-sonnet-4-6"
30
+
31
+ // Full replacement chain
32
+ resolve("gemini-2.0-flash"); // "gemini-3.5-flash"
33
+
34
+ // Strict mode (throws on unknown)
35
+ check("unknown-model", { strictUnknown: true });
36
+ ```
37
+
38
+ Registry is bundled at build time from `registry/models.json`.
@@ -0,0 +1,12 @@
1
+ import type { AliveResult } from "./types.js";
2
+ export declare function alive(model: string, today?: Date): AliveResult;
3
+ export declare function check(model: string, opts?: {
4
+ strictUnknown?: boolean;
5
+ today?: Date;
6
+ }): AliveResult;
7
+ export declare function resolve(model: string, today?: Date): string;
8
+ export declare function ensure(model: string, opts?: {
9
+ strictUnknown?: boolean;
10
+ today?: Date;
11
+ }): string;
12
+ export * from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,95 @@
1
+ import { daysUntil, effectiveStatus, loadRegistry, resolveAlias, } from "./registry.js";
2
+ import { ModelRetiredError, ModelUnknownError } from "./types.js";
3
+ export function alive(model, today = new Date()) {
4
+ const registry = loadRegistry();
5
+ const queried = model.trim();
6
+ const [canonical, aliased] = resolveAlias(queried, registry);
7
+ const entry = registry.models[canonical];
8
+ if (!entry) {
9
+ return {
10
+ model: canonical,
11
+ queried_model: queried,
12
+ canonical_model: canonical,
13
+ aliased,
14
+ alive: true,
15
+ status: "unknown",
16
+ confidence: "unknown",
17
+ message: "Model not in registry — assumed alive.",
18
+ registry_version: registry.version,
19
+ };
20
+ }
21
+ const status = effectiveStatus(entry, today);
22
+ const sourceKey = entry.source ?? "";
23
+ const sourceMeta = registry.sources[sourceKey];
24
+ const daysLeft = daysUntil(entry.retired_at, today);
25
+ const base = {
26
+ model: canonical,
27
+ queried_model: queried,
28
+ canonical_model: canonical,
29
+ aliased,
30
+ provider: entry.provider,
31
+ deprecated_at: entry.deprecated_at,
32
+ retired_at: entry.retired_at,
33
+ replacement: entry.replacement ?? null,
34
+ breaking_changes: entry.breaking_changes ?? [],
35
+ migrate_url: entry.migrate_url,
36
+ days_until_retirement: daysLeft,
37
+ registry_version: registry.version,
38
+ source_url: sourceMeta?.url,
39
+ source_checked_at: sourceMeta?.checked_at,
40
+ confidence: entry.source ? "verified" : "unknown",
41
+ alive: status !== "retired",
42
+ status: status,
43
+ };
44
+ if (status === "retired") {
45
+ const repl = entry.replacement ? ` Use '${entry.replacement}' instead.` : "";
46
+ base.message = `Model '${canonical}' was retired.${repl}`;
47
+ base.alive = false;
48
+ }
49
+ else if (status === "deprecated") {
50
+ base.message = `Model '${canonical}' is deprecated.`;
51
+ base.alive = true;
52
+ }
53
+ return base;
54
+ }
55
+ export function check(model, opts = {}) {
56
+ const result = alive(model, opts.today);
57
+ if (opts.strictUnknown && result.status === "unknown") {
58
+ throw new ModelUnknownError(result);
59
+ }
60
+ if (result.status === "retired") {
61
+ throw new ModelRetiredError(result);
62
+ }
63
+ return result;
64
+ }
65
+ export function resolve(model, today = new Date()) {
66
+ let current = model.trim();
67
+ const visited = new Set();
68
+ for (let i = 0; i < 12; i++) {
69
+ if (visited.has(current))
70
+ break;
71
+ visited.add(current);
72
+ const result = alive(current, today);
73
+ if (result.status === "active" || result.status === "unknown") {
74
+ return result.canonical_model ?? current;
75
+ }
76
+ if (!result.replacement)
77
+ return result.canonical_model ?? current;
78
+ const repl = alive(result.replacement, today);
79
+ if (repl.status === "active")
80
+ return result.replacement;
81
+ current = result.replacement;
82
+ }
83
+ return current;
84
+ }
85
+ export function ensure(model, opts = {}) {
86
+ const result = alive(model, opts.today);
87
+ if (opts.strictUnknown && result.status === "unknown") {
88
+ throw new ModelUnknownError(result);
89
+ }
90
+ if (result.status === "retired" && !result.replacement) {
91
+ throw new ModelRetiredError(result);
92
+ }
93
+ return resolve(model, opts.today);
94
+ }
95
+ export * from "./types.js";
@@ -0,0 +1,6 @@
1
+ import type { Registry, RegistryModel } from "./types.js";
2
+ export declare function loadRegistry(): Registry;
3
+ export declare function parseDate(value?: string): Date | null;
4
+ export declare function effectiveStatus(entry: RegistryModel, today?: Date): string;
5
+ export declare function resolveAlias(model: string, registry?: Registry): [string, boolean];
6
+ export declare function daysUntil(retiredAt: string | undefined, today?: Date): number | null;
@@ -0,0 +1,46 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const __dirname = dirname(fileURLToPath(import.meta.url));
5
+ let _registry = null;
6
+ export function loadRegistry() {
7
+ if (_registry)
8
+ return _registry;
9
+ const path = join(__dirname, "..", "registry.json");
10
+ _registry = JSON.parse(readFileSync(path, "utf-8"));
11
+ return _registry;
12
+ }
13
+ export function parseDate(value) {
14
+ if (!value)
15
+ return null;
16
+ return new Date(`${value}T00:00:00Z`);
17
+ }
18
+ export function effectiveStatus(entry, today = new Date()) {
19
+ const retired = parseDate(entry.retired_at);
20
+ if (retired && retired <= today)
21
+ return "retired";
22
+ if (entry.status === "legacy")
23
+ return "deprecated";
24
+ return entry.status;
25
+ }
26
+ export function resolveAlias(model, registry = loadRegistry()) {
27
+ let current = model;
28
+ const chain = [model];
29
+ for (let i = 0; i < 8; i++) {
30
+ const next = registry.aliases[current];
31
+ if (!next)
32
+ return [current, chain.length > 1];
33
+ if (chain.includes(next))
34
+ break;
35
+ chain.push(next);
36
+ current = next;
37
+ }
38
+ return [current, chain.length > 1];
39
+ }
40
+ export function daysUntil(retiredAt, today = new Date()) {
41
+ const target = parseDate(retiredAt);
42
+ if (!target)
43
+ return null;
44
+ const ms = target.getTime() - today.getTime();
45
+ return Math.floor(ms / (1000 * 60 * 60 * 24));
46
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import assert from "node:assert/strict";
2
+ import { alive, ensure, resolve } from "./index.js";
3
+ assert.equal(alive("claude-sonnet-4-20250514").status, "retired");
4
+ assert.equal(resolve("claude-sonnet-4-20250514"), "claude-sonnet-4-6");
5
+ assert.equal(ensure("claude-sonnet-4-20250514"), "claude-sonnet-4-6");
6
+ assert.equal(alive("claude-sonnet-4-6").status, "active");
7
+ console.log("modelalive js: ok");
@@ -0,0 +1,48 @@
1
+ export interface AliveResult {
2
+ model: string;
3
+ alive: boolean;
4
+ status: "active" | "deprecated" | "retired" | "unknown";
5
+ queried_model?: string;
6
+ canonical_model?: string;
7
+ aliased?: boolean;
8
+ provider?: string;
9
+ deprecated_at?: string;
10
+ retired_at?: string;
11
+ replacement?: string | null;
12
+ breaking_changes?: string[];
13
+ migrate_url?: string;
14
+ days_until_retirement?: number | null;
15
+ message?: string;
16
+ registry_version?: string;
17
+ source_url?: string;
18
+ source_checked_at?: string;
19
+ confidence?: string;
20
+ }
21
+ export interface Registry {
22
+ version: string;
23
+ schema_version: number;
24
+ sources: Record<string, {
25
+ url: string;
26
+ checked_at: string;
27
+ }>;
28
+ aliases: Record<string, string>;
29
+ models: Record<string, RegistryModel>;
30
+ }
31
+ export interface RegistryModel {
32
+ provider: string;
33
+ status: string;
34
+ deprecated_at?: string;
35
+ retired_at?: string;
36
+ replacement?: string | null;
37
+ breaking_changes?: string[];
38
+ migrate_url?: string;
39
+ source?: string;
40
+ }
41
+ export declare class ModelRetiredError extends Error {
42
+ readonly result: AliveResult;
43
+ constructor(result: AliveResult);
44
+ }
45
+ export declare class ModelUnknownError extends Error {
46
+ readonly result: AliveResult;
47
+ constructor(result: AliveResult);
48
+ }
package/dist/types.js ADDED
@@ -0,0 +1,16 @@
1
+ export class ModelRetiredError extends Error {
2
+ result;
3
+ constructor(result) {
4
+ super(result.message ?? `Model ${result.model} is retired`);
5
+ this.result = result;
6
+ this.name = "ModelRetiredError";
7
+ }
8
+ }
9
+ export class ModelUnknownError extends Error {
10
+ result;
11
+ constructor(result) {
12
+ super(result.message ?? `Model ${result.model} is unknown`);
13
+ this.result = result;
14
+ this.name = "ModelUnknownError";
15
+ }
16
+ }
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "modelalive",
3
+ "version": "0.5.0",
4
+ "description": "Pre-flight check: is this LLM model ID still alive?",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": ["dist", "registry.json"],
15
+ "scripts": {
16
+ "build": "tsc && cp ../registry/models.json registry.json",
17
+ "prebuild": "cp ../registry/models.json registry.json",
18
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js 2>/dev/null || npm run build && node --test dist/test/*.js"
19
+ },
20
+ "keywords": ["llm", "anthropic", "openai", "gemini", "deprecation", "model-id"],
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/Ahaozandaburada/modelalive.git",
25
+ "directory": "js"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^22.10.0",
29
+ "typescript": "^5.7.0"
30
+ }
31
+ }