pi-apexlang 0.2.1 → 0.2.2
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
CHANGED
|
@@ -103,6 +103,45 @@ repeat export produces one current snapshot instead of collision copies such
|
|
|
103
103
|
as `application_1.apx` and `p00005_1.apx`. Because this replaces the complete
|
|
104
104
|
local export, preserve or import any local-only work before refreshing it.
|
|
105
105
|
|
|
106
|
+
## SQLcl and ORDS compatibility advisory
|
|
107
|
+
|
|
108
|
+
Oracle documents minimum APEXlang prerequisites, but does not publish an exact
|
|
109
|
+
ORDS-to-SQLcl patch certification matrix. The extension therefore keeps this
|
|
110
|
+
mapping advisory and records whether a row is Oracle-confirmed or an extension
|
|
111
|
+
diagnostic recommendation:
|
|
112
|
+
|
|
113
|
+
| Server environment | SQLcl guidance | Basis |
|
|
114
|
+
| --- | --- | --- |
|
|
115
|
+
| ORDS before 26.1.1 or APEX before 26.1 | APEXlang is unsupported; upgrade the server components first. | Oracle-confirmed |
|
|
116
|
+
| APEX 26.1.x with ORDS 26.1.1 through 26.1.x | Oracle minimum: SQLcl 26.1. Diagnostic baseline: `26.1.2.132.1334`. | Oracle minimum plus extension advisory |
|
|
117
|
+
| APEX 26.1.x with ORDS 26.2.x | Oracle does not mandate SQLcl 26.2. On mass findings, compare with `26.1.2.132.1334`. | Extension advisory |
|
|
118
|
+
| Other or newer APEX, MMD, or ORDS lines | Verify current Oracle release documentation and use compiler metadata supporting the app `mmdVersion`. | Verify current docs |
|
|
119
|
+
|
|
120
|
+
The complete machine-readable table, source URLs, thresholds, and the direct
|
|
121
|
+
diagnostic-build download are in
|
|
122
|
+
[`extensions/apexlang/ords-sqlcl-compatibility.json`](extensions/apexlang/ords-sqlcl-compatibility.json).
|
|
123
|
+
The exact build is a known diagnostic baseline, not a universal pin, and SQLcl
|
|
124
|
+
26.2 is not categorically blocked.
|
|
125
|
+
|
|
126
|
+
When a validation result contains at least 50 structured findings, the
|
|
127
|
+
extension appends the table and recommends checking:
|
|
128
|
+
|
|
129
|
+
- local `sql -version`;
|
|
130
|
+
- the app's `.apex/apexlang.json` `mmdVersion`;
|
|
131
|
+
- the server APEX release and ORDS version with its administrator.
|
|
132
|
+
|
|
133
|
+
For local and compiler-truth text reports, the mass-error signal additionally
|
|
134
|
+
requires at least five distinct `.apx` files, which avoids treating one noisy
|
|
135
|
+
file as an environment-wide mismatch. An explicitly unsupported MMD version
|
|
136
|
+
also produces the compatibility advice. The advice never changes validation
|
|
137
|
+
status, relaxes an import gate, or claims that the application is valid.
|
|
138
|
+
|
|
139
|
+
[ORDS 26.1.1 introduced APEXlang support](https://www.oracle.com/tools/ords/ords-relnotes-26.1.1.html),
|
|
140
|
+
[APEX 26.1 requires ORDS 26.1.1 or later](https://docs.oracle.com/en/database/oracle/apex/26.1/htmrn/changed-behavior.html),
|
|
141
|
+
and Oracle's [SQLcl prerequisites](https://docs.oracle.com/en/database/oracle/sql-developer-command-line/26.1/sqcug/prerequisites-apexlang.html)
|
|
142
|
+
set the SQLcl minimum at 26.1. SQLcl
|
|
143
|
+
[26.1.2 added full APEXlang support](https://www.oracle.com/tools/sqlcl/sqlcl-relnotes-26.1.2.html).
|
|
144
|
+
|
|
106
145
|
All app paths must remain inside the active Pi workspace. The adapter rejects
|
|
107
146
|
traversal, app-tree symlinks, and multiply linked files on mutating vocabulary
|
|
108
147
|
fixes; verifies live workspace input against `deployments/default.json`; and
|
|
@@ -116,8 +155,9 @@ Offline routing, templates, generation, and most local checks need Node.js
|
|
|
116
155
|
|
|
117
156
|
Oracle documents these requirements for live APEXlang work:
|
|
118
157
|
|
|
119
|
-
- Oracle APEX with APEXlang support
|
|
120
|
-
-
|
|
158
|
+
- Oracle APEX 26.1 with APEXlang support.
|
|
159
|
+
- ORDS 26.1.1 or newer.
|
|
160
|
+
- SQLcl 26.1 or newer, selected with the compatibility advisory above.
|
|
121
161
|
- Java 17 or Java 21 for SQLcl.
|
|
122
162
|
- A saved SQLcl connection name and its corresponding APEX workspace name.
|
|
123
163
|
- A local APEX app or authoritative schema, model, API, or table metadata.
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { readFile, rm } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import compatibility from "./ords-sqlcl-compatibility.json" with { type: "json" };
|
|
4
|
+
import type { ApexlangRunResult } from "../lib/apexlang-cli.mjs";
|
|
5
|
+
|
|
6
|
+
type JsonRecord = Record<string, unknown>;
|
|
7
|
+
|
|
8
|
+
export type ValidationCompatibilitySignal = {
|
|
9
|
+
findingCount?: number;
|
|
10
|
+
distinctFileCount?: number;
|
|
11
|
+
source: "structured-result" | "local-reports" | "compiler-truth-output" | "unsupported-mmd";
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const ORDS_SQLCL_COMPATIBILITY = compatibility;
|
|
15
|
+
export const ORDS_SQLCL_COMPATIBILITY_TABLE = compatibility.rows;
|
|
16
|
+
export const MASS_VALIDATION_FINDING_THRESHOLD = compatibility.massValidation.minimumFindings;
|
|
17
|
+
export const MASS_VALIDATION_DISTINCT_FILE_THRESHOLD =
|
|
18
|
+
compatibility.massValidation.minimumDistinctFilesForAppWideReports;
|
|
19
|
+
export const ORDS_SQLCL_COMPATIBILITY_GUIDELINE =
|
|
20
|
+
`When a validation action reports ${MASS_VALIDATION_FINDING_THRESHOLD}+ findings, recommend checking the server APEX/ORDS release, the app mmdVersion, and local \`sql -version\` against the extension's advisory compatibility table. For APEX 26.1, SQLcl 26.1 is Oracle's minimum and 26.1.2.132.1334 is the diagnostic baseline; do not hard-block other SQLcl versions or weaken validation/import gates.`;
|
|
21
|
+
|
|
22
|
+
const VALIDATION_ACTIONS = new Set([
|
|
23
|
+
"local_validate",
|
|
24
|
+
"compiler_truth_audit",
|
|
25
|
+
"runtime_validate"
|
|
26
|
+
]);
|
|
27
|
+
const LOCAL_VALIDATION_REPORT_NAMES = [
|
|
28
|
+
"apexlang-dsl-report.json",
|
|
29
|
+
"apexlang-validations-report.json",
|
|
30
|
+
"apexlang-vocab-report.json"
|
|
31
|
+
] as const;
|
|
32
|
+
|
|
33
|
+
function asRecord(value: unknown): JsonRecord | undefined {
|
|
34
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
35
|
+
? value as JsonRecord
|
|
36
|
+
: undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function parseJsonRecord(value: string): JsonRecord | undefined {
|
|
40
|
+
try {
|
|
41
|
+
return asRecord(JSON.parse(value));
|
|
42
|
+
} catch {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function safeCount(value: unknown): number | undefined {
|
|
48
|
+
return Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function maximumStructuredFindingCount(payload: JsonRecord): number | undefined {
|
|
52
|
+
const compatibilityFallback = asRecord(payload.compatibility_fallback);
|
|
53
|
+
const candidates = [
|
|
54
|
+
safeCount(payload.unresolved_count),
|
|
55
|
+
safeCount(payload.problem_count),
|
|
56
|
+
Array.isArray(payload.issues) ? payload.issues.length : undefined,
|
|
57
|
+
Array.isArray(payload.problems) ? payload.problems.length : undefined,
|
|
58
|
+
safeCount(compatibilityFallback?.original_unresolved_count)
|
|
59
|
+
].filter((value): value is number => value !== undefined);
|
|
60
|
+
return candidates.length > 0 ? Math.max(...candidates) : undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function diagnosticIdentity(issue: unknown, reportName: string, index: number): {
|
|
64
|
+
key: string;
|
|
65
|
+
file?: string;
|
|
66
|
+
} {
|
|
67
|
+
if (typeof issue === "string") {
|
|
68
|
+
const raw = issue.trim();
|
|
69
|
+
const match = raw.match(/^\s*-?\s*(.+?\.apx):(\d+)(?::\d+)?:\s+[A-Z][A-Z0-9_]+\b/);
|
|
70
|
+
return { key: raw || `${reportName}:${index}`, ...(match?.[1] ? { file: match[1] } : {}) };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const record = asRecord(issue);
|
|
74
|
+
if (!record) return { key: `${reportName}:${index}:${String(issue)}` };
|
|
75
|
+
const file = typeof record.file === "string" ? record.file.trim() : "";
|
|
76
|
+
const raw = typeof record.raw === "string" ? record.raw.trim() : "";
|
|
77
|
+
const key = raw || [
|
|
78
|
+
file,
|
|
79
|
+
String(record.line ?? ""),
|
|
80
|
+
String(record.column ?? ""),
|
|
81
|
+
String(record.rule ?? record.code ?? ""),
|
|
82
|
+
String(record.message ?? "")
|
|
83
|
+
].join(":");
|
|
84
|
+
return {
|
|
85
|
+
key: key || `${reportName}:${index}:${JSON.stringify(record)}`,
|
|
86
|
+
...(file ? { file } : {})
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function readJsonReport(path: string): Promise<JsonRecord | undefined> {
|
|
91
|
+
try {
|
|
92
|
+
return parseJsonRecord(await readFile(path, "utf8"));
|
|
93
|
+
} catch {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function clearLocalValidationCompatibilityReports(outputRoot: string): Promise<void> {
|
|
99
|
+
await Promise.all(
|
|
100
|
+
LOCAL_VALIDATION_REPORT_NAMES.map((name) => rm(join(outputRoot, "logs", name), { force: true }))
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function detectLocalReportSignal(outputRoot: string): Promise<ValidationCompatibilitySignal | undefined> {
|
|
105
|
+
const reports = await Promise.all(
|
|
106
|
+
LOCAL_VALIDATION_REPORT_NAMES.map(async (name) => ({
|
|
107
|
+
name,
|
|
108
|
+
payload: await readJsonReport(join(outputRoot, "logs", name))
|
|
109
|
+
}))
|
|
110
|
+
);
|
|
111
|
+
const vocabularyReport = reports.find(({ name }) => name === "apexlang-vocab-report.json")?.payload;
|
|
112
|
+
if (vocabularyReport?.blocking_reason === "UNSUPPORTED_MMD_VERSION") {
|
|
113
|
+
return { source: "unsupported-mmd" };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const findingKeys = new Set<string>();
|
|
117
|
+
const files = new Set<string>();
|
|
118
|
+
for (const { name, payload } of reports) {
|
|
119
|
+
for (const collection of [payload?.issues, payload?.unresolved]) {
|
|
120
|
+
if (!Array.isArray(collection)) continue;
|
|
121
|
+
collection.forEach((issue, index) => {
|
|
122
|
+
const identity = diagnosticIdentity(issue, name, index);
|
|
123
|
+
findingKeys.add(identity.key);
|
|
124
|
+
if (identity.file) files.add(identity.file);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (
|
|
129
|
+
findingKeys.size >= MASS_VALIDATION_FINDING_THRESHOLD &&
|
|
130
|
+
files.size >= MASS_VALIDATION_DISTINCT_FILE_THRESHOLD
|
|
131
|
+
) {
|
|
132
|
+
return {
|
|
133
|
+
source: "local-reports",
|
|
134
|
+
findingCount: findingKeys.size,
|
|
135
|
+
distinctFileCount: files.size
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function detectCompilerTruthOutputSignal(output: string): ValidationCompatibilitySignal | undefined {
|
|
142
|
+
const findings = new Set<string>();
|
|
143
|
+
const files = new Set<string>();
|
|
144
|
+
for (const line of output.split(/\r?\n/)) {
|
|
145
|
+
const match = line.match(/^\s*-\s+(.+?\.apx):(\d+)(?::\d+)?:\s+([A-Z][A-Z0-9_]+)\b\s*(.*)$/);
|
|
146
|
+
if (!match?.[1]) continue;
|
|
147
|
+
findings.add(`${match[1]}:${match[2]}:${match[3]}:${match[4] ?? ""}`);
|
|
148
|
+
files.add(match[1]);
|
|
149
|
+
}
|
|
150
|
+
if (
|
|
151
|
+
findings.size >= MASS_VALIDATION_FINDING_THRESHOLD &&
|
|
152
|
+
files.size >= MASS_VALIDATION_DISTINCT_FILE_THRESHOLD
|
|
153
|
+
) {
|
|
154
|
+
return {
|
|
155
|
+
source: "compiler-truth-output",
|
|
156
|
+
findingCount: findings.size,
|
|
157
|
+
distinctFileCount: files.size
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export async function detectValidationCompatibilitySignal(
|
|
164
|
+
result: ApexlangRunResult
|
|
165
|
+
): Promise<ValidationCompatibilitySignal | undefined> {
|
|
166
|
+
if (!VALIDATION_ACTIONS.has(result.action)) return undefined;
|
|
167
|
+
|
|
168
|
+
const payload = parseJsonRecord(result.stdout);
|
|
169
|
+
if (payload) {
|
|
170
|
+
const findingCount = maximumStructuredFindingCount(payload);
|
|
171
|
+
if (findingCount !== undefined && findingCount >= MASS_VALIDATION_FINDING_THRESHOLD) {
|
|
172
|
+
return { source: "structured-result", findingCount };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (result.action === "local_validate") {
|
|
176
|
+
return detectLocalReportSignal(result.outputRoot);
|
|
177
|
+
}
|
|
178
|
+
if (result.action === "compiler_truth_audit") {
|
|
179
|
+
return detectCompilerTruthOutputSignal([result.stdout, result.stderr].filter(Boolean).join("\n"));
|
|
180
|
+
}
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function tableCell(value: string): string {
|
|
185
|
+
return value.replaceAll("|", "\\|").replaceAll("\n", " ");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function renderOrdsSqlclCompatibilityTable(): string {
|
|
189
|
+
const rows = ORDS_SQLCL_COMPATIBILITY_TABLE.map((row) => {
|
|
190
|
+
const guidance = row.diagnosticSqlclBuild && row.diagnosticSqlclDownloadUrl
|
|
191
|
+
? `${row.sqlclGuidance} [Download ${row.diagnosticSqlclBuild}](${row.diagnosticSqlclDownloadUrl})`
|
|
192
|
+
: row.sqlclGuidance;
|
|
193
|
+
return `| ${tableCell(row.environment)} | ${tableCell(guidance)} | ${tableCell(row.basis)} |`;
|
|
194
|
+
});
|
|
195
|
+
return [
|
|
196
|
+
"| Server environment | SQLcl guidance | Basis |",
|
|
197
|
+
"| --- | --- | --- |",
|
|
198
|
+
...rows
|
|
199
|
+
].join("\n");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function formatValidationCompatibilityAdvisory(
|
|
203
|
+
signal: ValidationCompatibilitySignal
|
|
204
|
+
): string {
|
|
205
|
+
const findingSummary = signal.findingCount === undefined
|
|
206
|
+
? "The application declares an unsupported APEXlang compiler metadata version."
|
|
207
|
+
: `Large-scale validation output detected (${signal.findingCount} findings${
|
|
208
|
+
signal.distinctFileCount === undefined ? "" : ` across ${signal.distinctFileCount} files`
|
|
209
|
+
}).`;
|
|
210
|
+
return [
|
|
211
|
+
"### SQLcl/ORDS compatibility advisory",
|
|
212
|
+
"",
|
|
213
|
+
`${findingSummary} Before changing many application files, check local \`sql -version\`, the app's \`.apex/apexlang.json\` \`mmdVersion\`, and the server APEX and ORDS versions with its administrator. A version/metadata mismatch can create broad diagnostic noise.`,
|
|
214
|
+
"",
|
|
215
|
+
renderOrdsSqlclCompatibilityTable(),
|
|
216
|
+
"",
|
|
217
|
+
compatibility.caveat,
|
|
218
|
+
"This advice does not turn a failed validation into a pass and does not authorize import."
|
|
219
|
+
].join("\n");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export async function buildValidationCompatibilityAdvisory(
|
|
223
|
+
result: ApexlangRunResult
|
|
224
|
+
): Promise<string> {
|
|
225
|
+
const signal = await detectValidationCompatibilitySignal(result);
|
|
226
|
+
return signal ? formatValidationCompatibilityAdvisory(signal) : "";
|
|
227
|
+
}
|
|
@@ -13,6 +13,18 @@ import {
|
|
|
13
13
|
type ApexlangInput,
|
|
14
14
|
type ApexlangRunResult
|
|
15
15
|
} from "../lib/apexlang-cli.mjs";
|
|
16
|
+
import {
|
|
17
|
+
MASS_VALIDATION_DISTINCT_FILE_THRESHOLD,
|
|
18
|
+
MASS_VALIDATION_FINDING_THRESHOLD,
|
|
19
|
+
ORDS_SQLCL_COMPATIBILITY,
|
|
20
|
+
ORDS_SQLCL_COMPATIBILITY_GUIDELINE,
|
|
21
|
+
ORDS_SQLCL_COMPATIBILITY_TABLE,
|
|
22
|
+
buildValidationCompatibilityAdvisory,
|
|
23
|
+
clearLocalValidationCompatibilityReports,
|
|
24
|
+
detectValidationCompatibilitySignal,
|
|
25
|
+
formatValidationCompatibilityAdvisory,
|
|
26
|
+
renderOrdsSqlclCompatibilityTable
|
|
27
|
+
} from "./compatibility.ts";
|
|
16
28
|
|
|
17
29
|
const MAX_TOOL_OUTPUT = 80_000;
|
|
18
30
|
const COMMAND_TIMEOUT_MS = 10 * 60 * 1000;
|
|
@@ -77,14 +89,20 @@ function createNewTargetProved(result: ApexlangRunResult): boolean {
|
|
|
77
89
|
payload?.failure_class === "create_new_confirmation_required";
|
|
78
90
|
}
|
|
79
91
|
|
|
80
|
-
function processOutput(result: ApexlangRunResult): string {
|
|
92
|
+
function processOutput(result: ApexlangRunResult, compatibilityAdvisory = ""): string {
|
|
81
93
|
const streams = result.ok ? [result.stdout, result.stderr] : [result.stderr, result.stdout];
|
|
82
|
-
|
|
94
|
+
const output = trimOutput(streams.filter(Boolean).join("\n").trim(), result.outputRoot);
|
|
95
|
+
return [output, compatibilityAdvisory].filter(Boolean).join("\n\n");
|
|
83
96
|
}
|
|
84
97
|
|
|
85
|
-
function failureOutput(
|
|
86
|
-
|
|
87
|
-
|
|
98
|
+
function failureOutput(
|
|
99
|
+
result: ApexlangRunResult,
|
|
100
|
+
fallback: string,
|
|
101
|
+
compatibilityAdvisory = ""
|
|
102
|
+
): string {
|
|
103
|
+
const streams = result.ok ? [result.stdout, result.stderr] : [result.stderr, result.stdout];
|
|
104
|
+
const output = trimOutput(streams.filter(Boolean).join("\n").trim(), result.outputRoot) || fallback;
|
|
105
|
+
return `${[output, compatibilityAdvisory].filter(Boolean).join("\n\n")}\n\nAPEXlang reports: ${result.outputRoot}`;
|
|
88
106
|
}
|
|
89
107
|
|
|
90
108
|
type ApexlangDependencies = {
|
|
@@ -113,6 +131,7 @@ export function createApexlangTool(overrides: Partial<ApexlangDependencies> = {}
|
|
|
113
131
|
"Load the apexlang skill before using the apexlang tool and follow its routing and Missing Inputs rules.",
|
|
114
132
|
"Use apexlang workspace_probe before app-scoped APEXlang work.",
|
|
115
133
|
EXPORT_OVERWRITE_GUIDELINE,
|
|
134
|
+
ORDS_SQLCL_COMPATIBILITY_GUIDELINE,
|
|
116
135
|
"Use apexlang runtime_validate only after the user provides both db_connection_name and the matching APEX workspace_name; import requires the tool's separate post-check GUI choice."
|
|
117
136
|
],
|
|
118
137
|
executionMode: "sequential",
|
|
@@ -194,11 +213,25 @@ export function createApexlangTool(overrides: Partial<ApexlangDependencies> = {}
|
|
|
194
213
|
...(signal ? { signal } : {}),
|
|
195
214
|
timeoutMs: COMMAND_TIMEOUT_MS
|
|
196
215
|
};
|
|
216
|
+
if (input.action === "local_validate") {
|
|
217
|
+
await clearLocalValidationCompatibilityReports(outputRoot);
|
|
218
|
+
}
|
|
197
219
|
const result = await dependencies.run(input, runOptions);
|
|
198
|
-
const
|
|
220
|
+
const compatibilityAdvisory = await buildValidationCompatibilityAdvisory(result);
|
|
221
|
+
if (compatibilityAdvisory) {
|
|
222
|
+
onUpdate?.({
|
|
223
|
+
content: [{ type: "text", text: compatibilityAdvisory }],
|
|
224
|
+
details: { action: input.action, compatibilityAdvisory: true }
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
const output = processOutput(result, compatibilityAdvisory);
|
|
199
228
|
if (!result.ok) {
|
|
200
229
|
throw new Error(
|
|
201
|
-
failureOutput(
|
|
230
|
+
failureOutput(
|
|
231
|
+
result,
|
|
232
|
+
`APEXlang ${input.action} failed with exit code ${result.code}.`,
|
|
233
|
+
compatibilityAdvisory
|
|
234
|
+
)
|
|
202
235
|
);
|
|
203
236
|
}
|
|
204
237
|
|
|
@@ -207,7 +240,8 @@ export function createApexlangTool(overrides: Partial<ApexlangDependencies> = {}
|
|
|
207
240
|
throw new Error(
|
|
208
241
|
failureOutput(
|
|
209
242
|
result,
|
|
210
|
-
"APEXlang runtime validation did not produce authoritative live pass evidence."
|
|
243
|
+
"APEXlang runtime validation did not produce authoritative live pass evidence.",
|
|
244
|
+
compatibilityAdvisory
|
|
211
245
|
)
|
|
212
246
|
);
|
|
213
247
|
}
|
|
@@ -299,10 +333,12 @@ export function createApexlangTool(overrides: Partial<ApexlangDependencies> = {}
|
|
|
299
333
|
expectedAppDigest
|
|
300
334
|
});
|
|
301
335
|
if (!createNewTargetProved(proofResult)) {
|
|
336
|
+
const proofCompatibilityAdvisory = await buildValidationCompatibilityAdvisory(proofResult);
|
|
302
337
|
throw new Error(
|
|
303
338
|
failureOutput(
|
|
304
339
|
proofResult,
|
|
305
|
-
"Oracle did not prove that the create-new target is absent from the selected workspace."
|
|
340
|
+
"Oracle did not prove that the create-new target is absent from the selected workspace.",
|
|
341
|
+
proofCompatibilityAdvisory
|
|
306
342
|
)
|
|
307
343
|
);
|
|
308
344
|
}
|
|
@@ -343,12 +379,14 @@ export function createApexlangTool(overrides: Partial<ApexlangDependencies> = {}
|
|
|
343
379
|
createNewConfirmed,
|
|
344
380
|
expectedAppDigest
|
|
345
381
|
});
|
|
346
|
-
const
|
|
382
|
+
const importCompatibilityAdvisory = await buildValidationCompatibilityAdvisory(importResult);
|
|
383
|
+
const importOutput = processOutput(importResult, importCompatibilityAdvisory);
|
|
347
384
|
if (!liveImportPassed(importResult)) {
|
|
348
385
|
throw new Error(
|
|
349
386
|
failureOutput(
|
|
350
387
|
importResult,
|
|
351
|
-
"APEXlang validate-and-import did not produce authoritative import pass evidence."
|
|
388
|
+
"APEXlang validate-and-import did not produce authoritative import pass evidence.",
|
|
389
|
+
importCompatibilityAdvisory
|
|
352
390
|
)
|
|
353
391
|
);
|
|
354
392
|
}
|
|
@@ -429,11 +467,21 @@ export {
|
|
|
429
467
|
IMPORT_CHOICE,
|
|
430
468
|
CREATE_NEW_CHOICE,
|
|
431
469
|
EXPORT_OVERWRITE_GUIDELINE,
|
|
470
|
+
MASS_VALIDATION_DISTINCT_FILE_THRESHOLD,
|
|
471
|
+
MASS_VALIDATION_FINDING_THRESHOLD,
|
|
472
|
+
ORDS_SQLCL_COMPATIBILITY,
|
|
473
|
+
ORDS_SQLCL_COMPATIBILITY_GUIDELINE,
|
|
474
|
+
ORDS_SQLCL_COMPATIBILITY_TABLE,
|
|
432
475
|
UPDATE_EXISTING_CHOICE,
|
|
433
476
|
actionWritesProject,
|
|
477
|
+
buildValidationCompatibilityAdvisory,
|
|
478
|
+
clearLocalValidationCompatibilityReports,
|
|
434
479
|
confirmationMessage,
|
|
435
480
|
createNewTargetProved,
|
|
481
|
+
detectValidationCompatibilitySignal,
|
|
482
|
+
formatValidationCompatibilityAdvisory,
|
|
436
483
|
liveImportPassed,
|
|
437
|
-
liveValidationPassed
|
|
484
|
+
liveValidationPassed,
|
|
485
|
+
renderOrdsSqlclCompatibilityTable
|
|
438
486
|
};
|
|
439
487
|
export type { ApexlangAction };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"policy": "advisory-only",
|
|
4
|
+
"lastReviewed": "2026-07-19",
|
|
5
|
+
"massValidation": {
|
|
6
|
+
"minimumFindings": 50,
|
|
7
|
+
"minimumDistinctFilesForAppWideReports": 5
|
|
8
|
+
},
|
|
9
|
+
"caveat": "Oracle publishes minimum APEXlang prerequisites, not an ORDS-to-SQLcl patch certification matrix. Exact SQLcl builds in this table are diagnostic baselines and never validation gates.",
|
|
10
|
+
"rows": [
|
|
11
|
+
{
|
|
12
|
+
"environment": "ORDS before 26.1.1 or APEX before 26.1",
|
|
13
|
+
"matchMode": "any",
|
|
14
|
+
"ordsRange": "<26.1.1",
|
|
15
|
+
"apexRange": "<26.1",
|
|
16
|
+
"sqlclGuidance": "APEXlang is unsupported; upgrade the server components before changing SQLcl.",
|
|
17
|
+
"diagnosticSqlclBuild": "",
|
|
18
|
+
"diagnosticSqlclDownloadUrl": "",
|
|
19
|
+
"basis": "oracle-confirmed",
|
|
20
|
+
"sourceUrls": [
|
|
21
|
+
"https://www.oracle.com/tools/ords/ords-relnotes-26.1.1.html",
|
|
22
|
+
"https://docs.oracle.com/en/database/oracle/apex/26.1/htmrn/changed-behavior.html"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"environment": "APEX 26.1.x with ORDS 26.1.1 through 26.1.x",
|
|
27
|
+
"matchMode": "all",
|
|
28
|
+
"ordsRange": ">=26.1.1 <26.2.0",
|
|
29
|
+
"apexRange": "26.1.x",
|
|
30
|
+
"sqlclGuidance": "Oracle minimum: SQLcl 26.1. Diagnostic baseline: 26.1.2.132.1334.",
|
|
31
|
+
"diagnosticSqlclBuild": "26.1.2.132.1334",
|
|
32
|
+
"diagnosticSqlclDownloadUrl": "https://download.oracle.com/otn_software/java/sqldeveloper/sqlcl-26.1.2.132.1334.zip",
|
|
33
|
+
"basis": "oracle-minimum-plus-extension-advisory",
|
|
34
|
+
"sourceUrls": [
|
|
35
|
+
"https://docs.oracle.com/en/database/oracle/sql-developer-command-line/26.1/sqcug/prerequisites-apexlang.html",
|
|
36
|
+
"https://www.oracle.com/tools/sqlcl/sqlcl-relnotes-26.1.2.html"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"environment": "APEX 26.1.x with ORDS 26.2.x",
|
|
41
|
+
"matchMode": "all",
|
|
42
|
+
"ordsRange": ">=26.2.0 <26.3.0",
|
|
43
|
+
"apexRange": "26.1.x",
|
|
44
|
+
"sqlclGuidance": "Oracle does not mandate SQLcl 26.2. On mass findings, compare with diagnostic baseline 26.1.2.132.1334.",
|
|
45
|
+
"diagnosticSqlclBuild": "26.1.2.132.1334",
|
|
46
|
+
"diagnosticSqlclDownloadUrl": "https://download.oracle.com/otn_software/java/sqldeveloper/sqlcl-26.1.2.132.1334.zip",
|
|
47
|
+
"basis": "extension-advisory",
|
|
48
|
+
"sourceUrls": [
|
|
49
|
+
"https://docs.oracle.com/en/database/oracle/sql-developer-command-line/26.1/sqcug/prerequisites-apexlang.html",
|
|
50
|
+
"https://www.oracle.com/tools/sqlcl/sqlcl-downloads.html"
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"environment": "Other or newer APEX, MMD, or ORDS release lines",
|
|
55
|
+
"matchMode": "fallback",
|
|
56
|
+
"ordsRange": "other",
|
|
57
|
+
"apexRange": "other",
|
|
58
|
+
"sqlclGuidance": "Verify current Oracle release documentation and use SQLcl compiler metadata that supports the app mmdVersion.",
|
|
59
|
+
"diagnosticSqlclBuild": "",
|
|
60
|
+
"diagnosticSqlclDownloadUrl": "",
|
|
61
|
+
"basis": "verify-current-docs",
|
|
62
|
+
"sourceUrls": [
|
|
63
|
+
"https://docs.oracle.com/en/database/oracle/apex/26.1/apxdc/using-sqlcl-apexlang.html"
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|