pi-apexlang 0.2.0 → 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
@@ -90,6 +90,58 @@ code** requires an explicit target mode:
90
90
  Without GUI support, the tool always stops after checking and reports import
91
91
  as a follow-up.
92
92
 
93
+ When refreshing a full APEXlang export, run one SQLcl export with explicit
94
+ replacement semantics:
95
+
96
+ ```text
97
+ apex export -applicationid <id> -exptype APEXLANG -split -dir <absolute-parent-directory> -force
98
+ ```
99
+
100
+ `-dir` names the parent directory; SQLcl creates the application-alias folder
101
+ below it. The `-force` flag removes and recreates that export folder, so a
102
+ repeat export produces one current snapshot instead of collision copies such
103
+ as `application_1.apx` and `p00005_1.apx`. Because this replaces the complete
104
+ local export, preserve or import any local-only work before refreshing it.
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
+
93
145
  All app paths must remain inside the active Pi workspace. The adapter rejects
94
146
  traversal, app-tree symlinks, and multiply linked files on mutating vocabulary
95
147
  fixes; verifies live workspace input against `deployments/default.json`; and
@@ -103,8 +155,9 @@ Offline routing, templates, generation, and most local checks need Node.js
103
155
 
104
156
  Oracle documents these requirements for live APEXlang work:
105
157
 
106
- - Oracle APEX with APEXlang support, using the latest available 26.1 build.
107
- - SQLcl 26.1.2 or newer.
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.
108
161
  - Java 17 or Java 21 for SQLcl.
109
162
  - A saved SQLcl connection name and its corresponding APEX workspace name.
110
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;
@@ -20,6 +32,8 @@ const CHECK_ONLY_CHOICE = "Check APEXlang code (recommended) — stop after the
20
32
  const IMPORT_CHOICE = "Check and import APEXlang code — revalidate and import in one SQLcl session";
21
33
  const UPDATE_EXISTING_CHOICE = "Update an existing app — require one proven remote target";
22
34
  const CREATE_NEW_CHOICE = "Create a new app — require proof that the alias is absent";
35
+ const EXPORT_OVERWRITE_GUIDELINE =
36
+ "For a full APEXlang export refresh, run exactly one SQLcl `apex export` command with `-force` and `-dir` set to the absolute parent directory. Never export without `-force` into an existing destination: SQLcl can collision-suffix every artifact as `*_1.apx` instead of replacing the snapshot.";
23
37
 
24
38
  function trimOutput(value: string, outputRoot?: string): string {
25
39
  if (value.length <= MAX_TOOL_OUTPUT) return value;
@@ -75,14 +89,20 @@ function createNewTargetProved(result: ApexlangRunResult): boolean {
75
89
  payload?.failure_class === "create_new_confirmation_required";
76
90
  }
77
91
 
78
- function processOutput(result: ApexlangRunResult): string {
92
+ function processOutput(result: ApexlangRunResult, compatibilityAdvisory = ""): string {
79
93
  const streams = result.ok ? [result.stdout, result.stderr] : [result.stderr, result.stdout];
80
- return trimOutput(streams.filter(Boolean).join("\n").trim(), result.outputRoot);
94
+ const output = trimOutput(streams.filter(Boolean).join("\n").trim(), result.outputRoot);
95
+ return [output, compatibilityAdvisory].filter(Boolean).join("\n\n");
81
96
  }
82
97
 
83
- function failureOutput(result: ApexlangRunResult, fallback: string): string {
84
- const output = processOutput(result) || fallback;
85
- return `${output}\n\nAPEXlang reports: ${result.outputRoot}`;
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}`;
86
106
  }
87
107
 
88
108
  type ApexlangDependencies = {
@@ -110,6 +130,8 @@ export function createApexlangTool(overrides: Partial<ApexlangDependencies> = {}
110
130
  promptGuidelines: [
111
131
  "Load the apexlang skill before using the apexlang tool and follow its routing and Missing Inputs rules.",
112
132
  "Use apexlang workspace_probe before app-scoped APEXlang work.",
133
+ EXPORT_OVERWRITE_GUIDELINE,
134
+ ORDS_SQLCL_COMPATIBILITY_GUIDELINE,
113
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."
114
136
  ],
115
137
  executionMode: "sequential",
@@ -191,11 +213,25 @@ export function createApexlangTool(overrides: Partial<ApexlangDependencies> = {}
191
213
  ...(signal ? { signal } : {}),
192
214
  timeoutMs: COMMAND_TIMEOUT_MS
193
215
  };
216
+ if (input.action === "local_validate") {
217
+ await clearLocalValidationCompatibilityReports(outputRoot);
218
+ }
194
219
  const result = await dependencies.run(input, runOptions);
195
- const output = processOutput(result);
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);
196
228
  if (!result.ok) {
197
229
  throw new Error(
198
- failureOutput(result, `APEXlang ${input.action} failed with exit code ${result.code}.`)
230
+ failureOutput(
231
+ result,
232
+ `APEXlang ${input.action} failed with exit code ${result.code}.`,
233
+ compatibilityAdvisory
234
+ )
199
235
  );
200
236
  }
201
237
 
@@ -204,7 +240,8 @@ export function createApexlangTool(overrides: Partial<ApexlangDependencies> = {}
204
240
  throw new Error(
205
241
  failureOutput(
206
242
  result,
207
- "APEXlang runtime validation did not produce authoritative live pass evidence."
243
+ "APEXlang runtime validation did not produce authoritative live pass evidence.",
244
+ compatibilityAdvisory
208
245
  )
209
246
  );
210
247
  }
@@ -296,10 +333,12 @@ export function createApexlangTool(overrides: Partial<ApexlangDependencies> = {}
296
333
  expectedAppDigest
297
334
  });
298
335
  if (!createNewTargetProved(proofResult)) {
336
+ const proofCompatibilityAdvisory = await buildValidationCompatibilityAdvisory(proofResult);
299
337
  throw new Error(
300
338
  failureOutput(
301
339
  proofResult,
302
- "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
303
342
  )
304
343
  );
305
344
  }
@@ -340,12 +379,14 @@ export function createApexlangTool(overrides: Partial<ApexlangDependencies> = {}
340
379
  createNewConfirmed,
341
380
  expectedAppDigest
342
381
  });
343
- const importOutput = processOutput(importResult);
382
+ const importCompatibilityAdvisory = await buildValidationCompatibilityAdvisory(importResult);
383
+ const importOutput = processOutput(importResult, importCompatibilityAdvisory);
344
384
  if (!liveImportPassed(importResult)) {
345
385
  throw new Error(
346
386
  failureOutput(
347
387
  importResult,
348
- "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
349
390
  )
350
391
  );
351
392
  }
@@ -425,11 +466,22 @@ export {
425
466
  CHECK_ONLY_CHOICE,
426
467
  IMPORT_CHOICE,
427
468
  CREATE_NEW_CHOICE,
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,
428
475
  UPDATE_EXISTING_CHOICE,
429
476
  actionWritesProject,
477
+ buildValidationCompatibilityAdvisory,
478
+ clearLocalValidationCompatibilityReports,
430
479
  confirmationMessage,
431
480
  createNewTargetProved,
481
+ detectValidationCompatibilitySignal,
482
+ formatValidationCompatibilityAdvisory,
432
483
  liveImportPassed,
433
- liveValidationPassed
484
+ liveValidationPassed,
485
+ renderOrdsSqlclCompatibilityTable
434
486
  };
435
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-apexlang",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "APEXlang support for pi, powered by Oracle's public APEXlang skill.",
5
5
  "type": "module",
6
6
  "license": "UPL-1.0",