@dan-ai-studio/dshopencodego 0.1.37 → 0.1.39
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.en.md +14 -4
- package/README.md +14 -4
- package/lib/client.js +2 -2
- package/lib/index.js +43528 -144
- package/lib/types/adapter.js +4 -1
- package/lib/types/catalog/go-doc.d.ts +0 -2
- package/lib/types/catalog/go-doc.js +1 -1
- package/lib/types/catalog/index.d.ts +1 -1
- package/lib/types/catalog/index.js +11 -3
- package/lib/types/catalog/metadata.d.ts +2 -1
- package/lib/types/catalog/protocol.d.ts +2 -1
- package/lib/types/catalog/service.d.ts +2 -1
- package/lib/types/config.d.ts +2 -9
- package/lib/types/config.js +4 -4
- package/lib/types/conversion/stream.js +1 -1
- package/lib/types/index.js +0 -5
- package/lib/types/models.d.ts +0 -2
- package/lib/types/models.js +1 -1
- package/lib/types/update/check.d.ts +1 -1
- package/lib/types/usage/windows.d.ts +2 -1
- package/package.json +4 -6
- package/lib/types/circular-warning.d.ts +0 -49
- package/lib/types/circular-warning.js +0 -79
package/lib/types/adapter.js
CHANGED
|
@@ -64,7 +64,10 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
64
64
|
var e = new Error(message);
|
|
65
65
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
66
66
|
});
|
|
67
|
-
|
|
67
|
+
// Subpath imports: the package root also re-exports every provider's wire
|
|
68
|
+
// implementation, which this plugin does not mount. See catalog/index.ts.
|
|
69
|
+
import { getSupportedThinkingLevels } from '@earendil-works/pi-ai/models';
|
|
70
|
+
import { normalizeContext } from '@earendil-works/pi-ai/utils/transcript';
|
|
68
71
|
import { attributionHeaders, contentHasImage, LlmAdapter, LlmError, ReasoningEffortId, resolveRetryPolicy, } from '@deepseek-ai/dsh-llm';
|
|
69
72
|
import { idleWatchdog, timeoutOf } from '@deepseek-ai/dsh-timeout';
|
|
70
73
|
import { DISPLAY_NAME, PROVIDER_ID, OpencodeGoCatalog } from "./catalog/index.js";
|
|
@@ -22,8 +22,6 @@ import type { WireProtocol } from './protocol.ts';
|
|
|
22
22
|
* same path and is tried when GitHub itself is unreachable.
|
|
23
23
|
*/
|
|
24
24
|
export declare const GO_DOC_URLS: readonly string[];
|
|
25
|
-
/** The document is about 30 KB; the cap leaves room without inviting a flood. */
|
|
26
|
-
export declare const GO_DOC_MAX_BYTES: number;
|
|
27
25
|
/** What one parse of the document yields. */
|
|
28
26
|
export interface GoDoc {
|
|
29
27
|
/** Per-model allowances, keyed by the gateway's model id. */
|
|
@@ -27,7 +27,7 @@ export const GO_DOC_URLS = [
|
|
|
27
27
|
'https://cdn.jsdelivr.net/gh/anomalyco/opencode@dev/packages/web/src/content/docs/go.mdx',
|
|
28
28
|
];
|
|
29
29
|
/** The document is about 30 KB; the cap leaves room without inviting a flood. */
|
|
30
|
-
|
|
30
|
+
const GO_DOC_MAX_BYTES = 1024 * 1024;
|
|
31
31
|
/** The protocol each documented endpoint path names. */
|
|
32
32
|
const ENDPOINT_PROTOCOLS = {
|
|
33
33
|
'/chat/completions': 'openai-completions',
|
|
@@ -50,7 +50,7 @@ export interface CatalogSnapshot {
|
|
|
50
50
|
readonly fetchedAtMs: number;
|
|
51
51
|
}
|
|
52
52
|
/** Observers the plugin logs through. */
|
|
53
|
-
|
|
53
|
+
interface CatalogObservers {
|
|
54
54
|
/** One source fell back to retained data. */
|
|
55
55
|
readonly onFallback?: (detail: {
|
|
56
56
|
url: string;
|
|
@@ -18,8 +18,13 @@
|
|
|
18
18
|
*
|
|
19
19
|
* @module @dan-ai-studio/dshopencodego/catalog
|
|
20
20
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
// Subpath imports on purpose. The package root re-exports `./api/lazy.js` (every
|
|
22
|
+
// provider's wire implementation) and `providers/all.js` loads every provider's
|
|
23
|
+
// model data — 991 KB under `providers/data` alone. This plugin mounts three
|
|
24
|
+
// APIs for one provider, so each symbol is taken from the narrowest public entry
|
|
25
|
+
// that exports it. Types still come from the root: they vanish at build time.
|
|
26
|
+
import { createProvider } from '@earendil-works/pi-ai/models';
|
|
27
|
+
import { OPENCODE_GO_MODELS } from '@earendil-works/pi-ai/providers/opencode-go.models';
|
|
23
28
|
import { anthropicMessagesApi } from '@earendil-works/pi-ai/api/anthropic-messages.lazy';
|
|
24
29
|
import { openAICompletionsApi } from '@earendil-works/pi-ai/api/openai-completions.lazy';
|
|
25
30
|
import { openAIResponsesApi } from '@earendil-works/pi-ai/api/openai-responses.lazy';
|
|
@@ -41,7 +46,10 @@ export { decideProtocol, inferProtocol, protocolOfNpm } from "./protocol.js";
|
|
|
41
46
|
* `compat` is what keeps DeepSeek-family reasoning replay correct.
|
|
42
47
|
*/
|
|
43
48
|
function builtinModels(baseURL) {
|
|
44
|
-
|
|
49
|
+
// `OPENCODE_GO_MODELS` is the same table `getBuiltinModels('opencode-go')`
|
|
50
|
+
// reads — verified entry for entry — reached without loading the aggregate
|
|
51
|
+
// `models.generated.js` that carries every provider.
|
|
52
|
+
return new Map(Object.values(OPENCODE_GO_MODELS).map(model => [model.id, {
|
|
45
53
|
...model,
|
|
46
54
|
provider: PROVIDER_ID,
|
|
47
55
|
baseUrl: modelBaseURLFor(model.api, baseURL),
|
|
@@ -64,7 +64,7 @@ export interface ModelDefaults {
|
|
|
64
64
|
/** The base URL one protocol's SDK expects for this gateway. */
|
|
65
65
|
export declare function modelBaseURL(api: WireProtocol, baseURL: string): string;
|
|
66
66
|
/** Inputs the ladder needs beyond the document itself. */
|
|
67
|
-
|
|
67
|
+
interface MetadataSources {
|
|
68
68
|
/** Installed catalog entries by id, the first level of the ladder. */
|
|
69
69
|
readonly builtin: ReadonlyMap<string, Model<Api>>;
|
|
70
70
|
/** Route defaults for a model no source sizes. */
|
|
@@ -83,3 +83,4 @@ export interface MetadataSources {
|
|
|
83
83
|
export declare function readOnlineMetadata(body: unknown, sources: MetadataSources): OnlineMetadata;
|
|
84
84
|
/** Project one parsed model into the shape pi-ai's providers consume. */
|
|
85
85
|
export declare function toPiModel(facts: ModelFacts, baseURL: string): Model<Api>;
|
|
86
|
+
export {};
|
|
@@ -49,7 +49,7 @@ export declare function inferProtocol(id: string): WireProtocol;
|
|
|
49
49
|
/** Narrow an arbitrary configured string to a protocol this plugin can drive. */
|
|
50
50
|
export declare function asWireProtocol(value: string | undefined): WireProtocol | undefined;
|
|
51
51
|
/** One model's evidence, as gathered from the three automatic sources. */
|
|
52
|
-
|
|
52
|
+
interface ProtocolEvidence {
|
|
53
53
|
/** Protocol the installed pi-ai catalog declares for this exact id. */
|
|
54
54
|
readonly builtin?: WireProtocol;
|
|
55
55
|
/** Protocol models.dev names for this model, or its provider-level default. */
|
|
@@ -68,3 +68,4 @@ export interface ProtocolEvidence {
|
|
|
68
68
|
export declare function decideProtocol(id: string, evidence: ProtocolEvidence): ProtocolDecision | undefined;
|
|
69
69
|
/** The pi-ai API string for one wire protocol. */
|
|
70
70
|
export declare function piApiOf(api: WireProtocol): Api;
|
|
71
|
+
export {};
|
|
@@ -12,7 +12,7 @@ import { RemoteError, TypertRemoteService } from '@deepseek-ai/dsh-typert-protoc
|
|
|
12
12
|
import type { OpencodeGoCatalog } from './index.ts';
|
|
13
13
|
import type { CatalogReading } from './reading.ts';
|
|
14
14
|
/** Host inputs for the catalog service. */
|
|
15
|
-
|
|
15
|
+
interface CatalogServiceOptions {
|
|
16
16
|
/** The route's current catalog resolver. */
|
|
17
17
|
readonly catalog: () => OpencodeGoCatalog;
|
|
18
18
|
/** Current per-model visibility switches. */
|
|
@@ -30,3 +30,4 @@ export declare class OpencodeGoCatalogService extends TypertRemoteService {
|
|
|
30
30
|
}
|
|
31
31
|
/** The domain failure the settings page renders when a refresh cannot run. */
|
|
32
32
|
export declare function catalogFailure(error: unknown): RemoteError;
|
|
33
|
+
export {};
|
package/lib/types/config.d.ts
CHANGED
|
@@ -17,20 +17,12 @@ import z from '@deepseek-ai/schemastery';
|
|
|
17
17
|
export declare const DEFAULT_API_KEY_ENV = "OPENCODE_GO_API_KEY";
|
|
18
18
|
/** Requests and pickers share this cache lifetime; explicit discovery revalidates immediately. */
|
|
19
19
|
export declare const DEFAULT_REFRESH_MINUTES = 60;
|
|
20
|
-
/** Default maximum idle interval while one stream read is outstanding. */
|
|
21
|
-
export declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300000;
|
|
22
|
-
/** Request-level bound on the base64-encoded image payload. */
|
|
23
|
-
export declare const DEFAULT_MAX_REQUEST_IMAGE_BYTES: number;
|
|
24
|
-
/** Total-pixel budget for one request image. */
|
|
25
|
-
export declare const DEFAULT_REQUEST_IMAGE_PIXEL_BUDGET = 1440000;
|
|
26
|
-
/** Raw encoded-byte target for one request image before base64 expansion. */
|
|
27
|
-
export declare const DEFAULT_REQUEST_IMAGE_MAX_BYTES = 400000;
|
|
28
20
|
/**
|
|
29
21
|
* One model's configured capacities. Every field is optional so a deployment
|
|
30
22
|
* overrides only the value it needs; `null` explicitly selects the catalog
|
|
31
23
|
* value even when a lower layer supplies an override.
|
|
32
24
|
*/
|
|
33
|
-
|
|
25
|
+
interface OpencodeGoModelLimit {
|
|
34
26
|
/** Context window in tokens, overriding what the catalog advertised. */
|
|
35
27
|
contextWindow?: number | null;
|
|
36
28
|
/** Output cap per request, overriding what the catalog advertised. */
|
|
@@ -107,3 +99,4 @@ export declare function readConfig(config: LiveConfig): OpencodeGoConfig;
|
|
|
107
99
|
* @returns the normalized base URL without trailing slashes.
|
|
108
100
|
*/
|
|
109
101
|
export declare function assertBaseURL(raw: string): string;
|
|
102
|
+
export {};
|
package/lib/types/config.js
CHANGED
|
@@ -20,13 +20,13 @@ export const DEFAULT_API_KEY_ENV = 'OPENCODE_GO_API_KEY';
|
|
|
20
20
|
/** Requests and pickers share this cache lifetime; explicit discovery revalidates immediately. */
|
|
21
21
|
export const DEFAULT_REFRESH_MINUTES = 60;
|
|
22
22
|
/** Default maximum idle interval while one stream read is outstanding. */
|
|
23
|
-
|
|
23
|
+
const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300_000;
|
|
24
24
|
/** Request-level bound on the base64-encoded image payload. */
|
|
25
|
-
|
|
25
|
+
const DEFAULT_MAX_REQUEST_IMAGE_BYTES = 6 * 1024 * 1024;
|
|
26
26
|
/** Total-pixel budget for one request image. */
|
|
27
|
-
|
|
27
|
+
const DEFAULT_REQUEST_IMAGE_PIXEL_BUDGET = 1_440_000;
|
|
28
28
|
/** Raw encoded-byte target for one request image before base64 expansion. */
|
|
29
|
-
|
|
29
|
+
const DEFAULT_REQUEST_IMAGE_MAX_BYTES = 400_000;
|
|
30
30
|
const fields = {
|
|
31
31
|
enabled: z.boolean().default(true),
|
|
32
32
|
modelVisibility: z.dict(z.boolean().required()).default({}),
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { brandString } from '@deepseek-ai/dsh-brand';
|
|
11
11
|
import { CONTEXT_WINDOW_EXCEEDED_CODE, EMPTY_RESPONSE_CODE, isContextWindowExceededError, isQuotaExceededError, LlmError, } from '@deepseek-ai/dsh-llm';
|
|
12
|
-
import { isContextOverflow } from '@earendil-works/pi-ai';
|
|
12
|
+
import { isContextOverflow } from '@earendil-works/pi-ai/utils/overflow';
|
|
13
13
|
import { toPiReplayState } from "./replay.js";
|
|
14
14
|
/**
|
|
15
15
|
* Map pi-ai usage (reasoning already folded into output).
|
package/lib/types/index.js
CHANGED
|
@@ -34,7 +34,6 @@ import { LlmError } from '@deepseek-ai/dsh-llm';
|
|
|
34
34
|
import { OpencodeGoAdapter } from "./adapter.js";
|
|
35
35
|
import { DISPLAY_NAME, PROVIDER_ID, discoverCatalogModels } from "./catalog/index.js";
|
|
36
36
|
import { assertBaseURL, PlainConfig, readConfig } from "./config.js";
|
|
37
|
-
import { filterCircularWarnings } from "./circular-warning.js";
|
|
38
37
|
import { registerRemotes } from "./remotes.js";
|
|
39
38
|
import { OpencodeGoCatalogService } from "./catalog/service.js";
|
|
40
39
|
import { OpencodeGoUpdateService } from "./update/index.js";
|
|
@@ -61,10 +60,6 @@ function isLiveConfig(raw) {
|
|
|
61
60
|
* @param raw - the loader's live config, or a plain object in tests.
|
|
62
61
|
*/
|
|
63
62
|
export function apply(ctx, raw) {
|
|
64
|
-
// First statement on purpose: this is as early as a filter can be installed,
|
|
65
|
-
// since ES imports are hoisted and `pi-ai` is evaluated before any of this
|
|
66
|
-
// runs. See the module for why the warning is safe to drop.
|
|
67
|
-
filterCircularWarnings();
|
|
68
63
|
const live = isLiveConfig(raw) ? raw : undefined;
|
|
69
64
|
const constant = live === undefined ? PlainConfig((raw ?? {})) : undefined;
|
|
70
65
|
const current = () => (live === undefined ? constant : readConfig(live));
|
package/lib/types/models.d.ts
CHANGED
|
@@ -87,8 +87,6 @@ export declare function recommendedIds(models: readonly Pick<ModelSummary, 'id'
|
|
|
87
87
|
export declare function isModelEnabled(model: Pick<ModelSummary, 'id' | 'deprecated' | 'configurationMissing' | 'recommended'>, visibility?: Readonly<Record<string, boolean>>): boolean;
|
|
88
88
|
/** A calendar date as models.dev states it, without a timezone. */
|
|
89
89
|
export declare function validReleaseDate(value: unknown): value is string;
|
|
90
|
-
/** Whether a model shipped within the last week. */
|
|
91
|
-
export declare function isNewModel(model: ModelSummary, now?: number): boolean;
|
|
92
90
|
/**
|
|
93
91
|
* Order models for display: newly shipped first, then the rest, then deprecated.
|
|
94
92
|
* Newest releases come first within the first group.
|
package/lib/types/models.js
CHANGED
|
@@ -69,7 +69,7 @@ export function validReleaseDate(value) {
|
|
|
69
69
|
&& Number.isFinite(Date.parse(value)) && new Date(value).toISOString().slice(0, 10) === value;
|
|
70
70
|
}
|
|
71
71
|
/** Whether a model shipped within the last week. */
|
|
72
|
-
|
|
72
|
+
function isNewModel(model, now = Date.now()) {
|
|
73
73
|
if (model.deprecated === true || !validReleaseDate(model.releaseDate))
|
|
74
74
|
return false;
|
|
75
75
|
const days = Math.floor(now / 86_400_000) - Date.parse(model.releaseDate) / 86_400_000;
|
|
@@ -61,7 +61,7 @@ export declare function isUpdateAvailable(current: string, latest: string | unde
|
|
|
61
61
|
/** Read the `version` field out of a registry `latest` document. */
|
|
62
62
|
export declare function parseLatestVersion(body: unknown): string | undefined;
|
|
63
63
|
/** Dependencies the check needs, injectable for tests. */
|
|
64
|
-
|
|
64
|
+
interface UpdateCheckDeps {
|
|
65
65
|
/** Running build's version (build-time constant in production). */
|
|
66
66
|
readonly currentVersion: string;
|
|
67
67
|
/** Fetch implementation; defaults to the global one. */
|
|
@@ -32,7 +32,7 @@ export interface GoUsageWindows {
|
|
|
32
32
|
*/
|
|
33
33
|
export declare function parseGoUsage(value: unknown): Omit<GoUsageWindows, 'source'>;
|
|
34
34
|
/** Inputs one usage read needs. */
|
|
35
|
-
|
|
35
|
+
interface UsageReadOptions {
|
|
36
36
|
/** Normalized gateway base URL. */
|
|
37
37
|
readonly baseURL: string;
|
|
38
38
|
/** Resolved credential; absence is reported as an unconfigured route. */
|
|
@@ -49,3 +49,4 @@ export interface UsageReadOptions {
|
|
|
49
49
|
* endpoint fails, or the body is not a usage document.
|
|
50
50
|
*/
|
|
51
51
|
export declare function readUsageWindows(options: UsageReadOptions): Promise<GoUsageWindows>;
|
|
52
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dan-ai-studio/dshopencodego",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.39",
|
|
4
4
|
"description": "OpenCode Go provider for DeepSeek Harness: live gateway catalog, per-conversation session header, usage",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -66,10 +66,6 @@
|
|
|
66
66
|
"prepare": "npm run build",
|
|
67
67
|
"prepack": "npm run build"
|
|
68
68
|
},
|
|
69
|
-
"dependencies": {
|
|
70
|
-
"@deepseek-ai/schemastery": "~3.18.5-alpha.2",
|
|
71
|
-
"@earendil-works/pi-ai": "~1.3.0"
|
|
72
|
-
},
|
|
73
69
|
"peerDependencies": {
|
|
74
70
|
"@deepseek-ai/cordis": "4.0.4 || 4.0.5-alpha.1 || 4.0.5-alpha.2",
|
|
75
71
|
"@deepseek-ai/dsh-api-remotes": ">=0.2.0-rc.1 <0.3.0",
|
|
@@ -145,7 +141,9 @@
|
|
|
145
141
|
"react": "^18.3.1",
|
|
146
142
|
"react-dom": "^18.3.1",
|
|
147
143
|
"typescript": "^6.0.3",
|
|
148
|
-
"vitest": "^4.1.8"
|
|
144
|
+
"vitest": "^4.1.8",
|
|
145
|
+
"@earendil-works/pi-ai": "~1.3.0",
|
|
146
|
+
"@deepseek-ai/schemastery": "~3.18.5-alpha.2"
|
|
149
147
|
},
|
|
150
148
|
"keywords": [
|
|
151
149
|
"deepseek-harness",
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Silence Node's circular-dependency property warning.
|
|
3
|
-
*
|
|
4
|
-
* `@earendil-works/pi-ai` named-imports `partial-json`:
|
|
5
|
-
*
|
|
6
|
-
* ```js
|
|
7
|
-
* import { parse as partialParse } from "partial-json";
|
|
8
|
-
* ```
|
|
9
|
-
*
|
|
10
|
-
* Resolving a named import from CommonJS makes Node enumerate the module's
|
|
11
|
-
* exports, and `partial-json`'s `index.js` and `options.js` require each
|
|
12
|
-
* other — so the enumeration reads `Allow` while its own module is still
|
|
13
|
-
* initialising. Node prints, once per read:
|
|
14
|
-
*
|
|
15
|
-
* Accessing non-existent property 'Allow' of module exports inside circular dependency
|
|
16
|
-
*
|
|
17
|
-
* The read is harmless: `parse` resolves correctly, the value is present once
|
|
18
|
-
* the pair settles, and every request this plugin makes was verified against
|
|
19
|
-
* that behaviour. What makes it worth filtering is the volume — a plugin
|
|
20
|
-
* reload re-runs the graph, and each enumeration prints again, so an update
|
|
21
|
-
* leaves a flood of identical lines in the host's log. A wall of warnings
|
|
22
|
-
* nobody can act on is how a real one gets missed.
|
|
23
|
-
*
|
|
24
|
-
* Why a message filter rather than a supported switch: Node emits this one
|
|
25
|
-
* with a bare string and **no type**, so `--disable-warning` (which matches a
|
|
26
|
-
* code or type) cannot select it. Matching the message prefix is the narrowest
|
|
27
|
-
* filter available, and it drops exactly one message — everything else reaches
|
|
28
|
-
* the default handler untouched.
|
|
29
|
-
*
|
|
30
|
-
* @module @dan-ai-studio/dshopencodego/circular-warning
|
|
31
|
-
*/
|
|
32
|
-
/**
|
|
33
|
-
* Whether this warning is the circular-dependency one.
|
|
34
|
-
*
|
|
35
|
-
* Exported so the rule can be tested directly, without installing anything.
|
|
36
|
-
* @param warning - the first argument Node passes to `process.emitWarning`.
|
|
37
|
-
* @returns true when the warning is Node's circular-dependency notice.
|
|
38
|
-
*/
|
|
39
|
-
export declare function dropsCircularWarning(warning: unknown): boolean;
|
|
40
|
-
/**
|
|
41
|
-
* Install the filter. Safe to call on every mount; the first call wins.
|
|
42
|
-
*
|
|
43
|
-
* Called from `apply` rather than from module scope because ES module imports
|
|
44
|
-
* are hoisted — by the time any statement of this bundle runs, `pi-ai` has
|
|
45
|
-
* already been evaluated, so the first enumeration cannot be intercepted. Every
|
|
46
|
-
* later one can, and a reload is exactly what produces the flood.
|
|
47
|
-
* @param target - the process-like object to patch; defaults to the real one.
|
|
48
|
-
*/
|
|
49
|
-
export declare function filterCircularWarnings(target?: Pick<typeof process, 'emitWarning'>): void;
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Silence Node's circular-dependency property warning.
|
|
3
|
-
*
|
|
4
|
-
* `@earendil-works/pi-ai` named-imports `partial-json`:
|
|
5
|
-
*
|
|
6
|
-
* ```js
|
|
7
|
-
* import { parse as partialParse } from "partial-json";
|
|
8
|
-
* ```
|
|
9
|
-
*
|
|
10
|
-
* Resolving a named import from CommonJS makes Node enumerate the module's
|
|
11
|
-
* exports, and `partial-json`'s `index.js` and `options.js` require each
|
|
12
|
-
* other — so the enumeration reads `Allow` while its own module is still
|
|
13
|
-
* initialising. Node prints, once per read:
|
|
14
|
-
*
|
|
15
|
-
* Accessing non-existent property 'Allow' of module exports inside circular dependency
|
|
16
|
-
*
|
|
17
|
-
* The read is harmless: `parse` resolves correctly, the value is present once
|
|
18
|
-
* the pair settles, and every request this plugin makes was verified against
|
|
19
|
-
* that behaviour. What makes it worth filtering is the volume — a plugin
|
|
20
|
-
* reload re-runs the graph, and each enumeration prints again, so an update
|
|
21
|
-
* leaves a flood of identical lines in the host's log. A wall of warnings
|
|
22
|
-
* nobody can act on is how a real one gets missed.
|
|
23
|
-
*
|
|
24
|
-
* Why a message filter rather than a supported switch: Node emits this one
|
|
25
|
-
* with a bare string and **no type**, so `--disable-warning` (which matches a
|
|
26
|
-
* code or type) cannot select it. Matching the message prefix is the narrowest
|
|
27
|
-
* filter available, and it drops exactly one message — everything else reaches
|
|
28
|
-
* the default handler untouched.
|
|
29
|
-
*
|
|
30
|
-
* @module @dan-ai-studio/dshopencodego/circular-warning
|
|
31
|
-
*/
|
|
32
|
-
/** The invariable opening of Node's message; the property name varies. */
|
|
33
|
-
const PREFIX = 'Accessing non-existent property';
|
|
34
|
-
/**
|
|
35
|
-
* Marks the process as filtered.
|
|
36
|
-
*
|
|
37
|
-
* A `Symbol.for` key so it survives a plugin reload: the host may evaluate
|
|
38
|
-
* this bundle again, and the filter must be installed once for the process
|
|
39
|
-
* rather than once per evaluation.
|
|
40
|
-
*/
|
|
41
|
-
const INSTALLED = Symbol.for('dshopencodego/circular-warning-filter');
|
|
42
|
-
/** The text of a warning Node may pass as a string or as an `Error`. */
|
|
43
|
-
function warningText(warning) {
|
|
44
|
-
if (typeof warning === 'string')
|
|
45
|
-
return warning;
|
|
46
|
-
const message = warning?.message;
|
|
47
|
-
return String(message ?? warning);
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Whether this warning is the circular-dependency one.
|
|
51
|
-
*
|
|
52
|
-
* Exported so the rule can be tested directly, without installing anything.
|
|
53
|
-
* @param warning - the first argument Node passes to `process.emitWarning`.
|
|
54
|
-
* @returns true when the warning is Node's circular-dependency notice.
|
|
55
|
-
*/
|
|
56
|
-
export function dropsCircularWarning(warning) {
|
|
57
|
-
return warningText(warning).startsWith(PREFIX);
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Install the filter. Safe to call on every mount; the first call wins.
|
|
61
|
-
*
|
|
62
|
-
* Called from `apply` rather than from module scope because ES module imports
|
|
63
|
-
* are hoisted — by the time any statement of this bundle runs, `pi-ai` has
|
|
64
|
-
* already been evaluated, so the first enumeration cannot be intercepted. Every
|
|
65
|
-
* later one can, and a reload is exactly what produces the flood.
|
|
66
|
-
* @param target - the process-like object to patch; defaults to the real one.
|
|
67
|
-
*/
|
|
68
|
-
export function filterCircularWarnings(target = process) {
|
|
69
|
-
const host = target;
|
|
70
|
-
if (host[INSTALLED] === true)
|
|
71
|
-
return;
|
|
72
|
-
host[INSTALLED] = true;
|
|
73
|
-
const original = target.emitWarning.bind(target);
|
|
74
|
-
target.emitWarning = ((warning, ...rest) => {
|
|
75
|
-
if (dropsCircularWarning(warning))
|
|
76
|
-
return;
|
|
77
|
-
original(warning, ...rest);
|
|
78
|
-
});
|
|
79
|
-
}
|