@shapeshift-labs/frontier-lang-compiler 0.2.4 → 0.2.5

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/index.d.ts CHANGED
@@ -13,7 +13,8 @@ import type {
13
13
  NativeSourceNode,
14
14
  SemanticIndexRecord,
15
15
  SemanticNode,
16
- SemanticPatchBundle
16
+ SemanticPatchBundle,
17
+ SourceSpan
17
18
  } from '@shapeshift-labs/frontier-lang-kernel';
18
19
  import type { Diagnostic } from '@shapeshift-labs/frontier-lang-checker';
19
20
  import type { EmitTypeScriptOptions, TypeScriptAstModule } from '@shapeshift-labs/frontier-lang-typescript';
@@ -68,6 +69,18 @@ export interface CapabilityResolution {
68
69
  readonly reason?: string;
69
70
  }
70
71
 
72
+ export interface NativeImporterAdapterDiagnostic {
73
+ readonly id?: string;
74
+ readonly severity?: 'info' | 'warning' | 'error';
75
+ readonly code?: string;
76
+ readonly phase?: 'read' | 'parse' | 'map' | 'index' | 'import' | string;
77
+ readonly kind?: NativeAstLossRecord['kind'];
78
+ readonly message: string;
79
+ readonly path?: string;
80
+ readonly span?: SourceSpan;
81
+ readonly metadata?: Record<string, unknown>;
82
+ }
83
+
71
84
  export interface ImportNativeSourceOptions {
72
85
  readonly id?: string;
73
86
  readonly language?: FrontierSourceLanguage;
@@ -112,6 +125,58 @@ export type NativeSourceImportResult = LanguageImportResult & {
112
125
  readonly universalAst: FrontierUniversalAstEnvelope;
113
126
  };
114
127
 
128
+ export interface NativeImporterAdapterParseInput {
129
+ readonly sourceText: string;
130
+ readonly sourcePath?: string;
131
+ readonly sourceHash: string;
132
+ readonly language: FrontierSourceLanguage;
133
+ readonly parser: string;
134
+ readonly parserVersion?: string;
135
+ readonly adapterId: string;
136
+ readonly adapterVersion?: string;
137
+ readonly options: Record<string, unknown>;
138
+ readonly metadata: Record<string, unknown>;
139
+ }
140
+
141
+ export interface NativeImporterAdapterParseResult extends Omit<ImportNativeSourceOptions, 'language' | 'parser' | 'parserVersion' | 'sourceText'> {
142
+ readonly diagnostics?: readonly NativeImporterAdapterDiagnostic[];
143
+ }
144
+
145
+ export interface NativeImporterAdapter {
146
+ readonly id: string;
147
+ readonly language: FrontierSourceLanguage;
148
+ readonly parser: string;
149
+ readonly version?: string;
150
+ readonly capabilities?: readonly string[];
151
+ readonly supportedExtensions?: readonly string[];
152
+ readonly diagnostics?: readonly NativeImporterAdapterDiagnostic[];
153
+ readonly parse: (input: NativeImporterAdapterParseInput) => NativeImporterAdapterParseResult | Promise<NativeImporterAdapterParseResult>;
154
+ }
155
+
156
+ export interface NativeImporterAdapterSummary {
157
+ readonly id: string;
158
+ readonly language: FrontierSourceLanguage;
159
+ readonly parser: string;
160
+ readonly version?: string;
161
+ readonly capabilities: readonly string[];
162
+ readonly supportedExtensions: readonly string[];
163
+ readonly diagnostics: readonly NativeImporterAdapterDiagnostic[];
164
+ }
165
+
166
+ export interface RunNativeImporterAdapterOptions extends Omit<ImportNativeSourceOptions, 'language' | 'parser' | 'parserVersion' | 'sourceText'> {
167
+ readonly sourceText: string;
168
+ readonly language?: FrontierSourceLanguage;
169
+ readonly parser?: string;
170
+ readonly parserVersion?: string;
171
+ readonly adapterOptions?: Record<string, unknown>;
172
+ readonly adapterMetadata?: Record<string, unknown>;
173
+ }
174
+
175
+ export type NativeImporterAdapterImportResult = NativeSourceImportResult & {
176
+ readonly adapter: NativeImporterAdapterSummary;
177
+ readonly diagnostics: readonly NativeImporterAdapterDiagnostic[];
178
+ };
179
+
115
180
  export declare const FrontierCompileTargets: readonly FrontierCompileTarget[];
116
181
  export declare function normalizeCompileTarget(target?: string): FrontierCompileTarget;
117
182
  export declare function compileFrontierSource(source: string, options?: FrontierCompileOptions): FrontierCompileResult;
@@ -119,6 +184,7 @@ export declare function compileFrontierDocument(document: FrontierLangDocument,
119
184
  export declare function projectFrontierAst(document: FrontierLangDocument, target?: FrontierCompileOptions['target'], options?: FrontierCompileEmitOptions): FrontierTargetAst;
120
185
  export declare function renderTargetAst(ast: FrontierTargetAst, target?: FrontierCompileOptions['target']): string;
121
186
  export declare function resolveCapabilityAdapters(document: FrontierLangDocument, target?: FrontierCompileOptions['target'], options?: { readonly platform?: string }): readonly CapabilityResolution[];
187
+ export declare function runNativeImporterAdapter(adapter: NativeImporterAdapter, input: RunNativeImporterAdapterOptions): Promise<NativeImporterAdapterImportResult>;
122
188
  export declare function importNativeSource(input: ImportNativeSourceOptions): NativeSourceImportResult;
123
189
  export declare function createUniversalAstFromDocument(document: FrontierLangDocument, input?: {
124
190
  readonly id?: string;
package/dist/index.js CHANGED
@@ -135,6 +135,125 @@ export function resolveCapabilityAdapters(document, target = 'typescript', optio
135
135
  });
136
136
  }
137
137
 
138
+ export async function runNativeImporterAdapter(adapter, input = {}) {
139
+ const summary = normalizeNativeImporterAdapter(adapter);
140
+ const language = input.language ?? summary.language;
141
+ const parser = input.parser ?? summary.parser;
142
+ const parserVersion = input.parserVersion ?? summary.version;
143
+ const sourceText = input.sourceText ?? '';
144
+ const sourceHash = input.sourceHash ?? hashSemanticValue(sourceText);
145
+ const parseInput = {
146
+ sourceText,
147
+ sourcePath: input.sourcePath,
148
+ sourceHash,
149
+ language,
150
+ parser,
151
+ parserVersion,
152
+ adapterId: summary.id,
153
+ adapterVersion: summary.version,
154
+ options: input.adapterOptions ?? {},
155
+ metadata: input.adapterMetadata ?? {}
156
+ };
157
+ let parsed;
158
+ let thrownDiagnostic;
159
+ try {
160
+ parsed = await adapter.parse(parseInput);
161
+ } catch (error) {
162
+ thrownDiagnostic = {
163
+ severity: 'error',
164
+ code: 'adapter.parse.threw',
165
+ phase: 'parse',
166
+ kind: 'unsupportedSyntax',
167
+ message: error instanceof Error ? error.message : String(error),
168
+ metadata: {
169
+ errorName: error instanceof Error ? error.name : undefined
170
+ }
171
+ };
172
+ parsed = {
173
+ rootId: 'adapter_error_root',
174
+ nodes: {
175
+ adapter_error_root: {
176
+ id: 'adapter_error_root',
177
+ kind: 'AdapterParseError',
178
+ languageKind: `${language}.adapterParseError`,
179
+ value: thrownDiagnostic.message,
180
+ metadata: { adapterId: summary.id, parser }
181
+ }
182
+ }
183
+ };
184
+ }
185
+ const parseResult = parsed ?? {};
186
+ const diagnostics = [
187
+ ...normalizeAdapterDiagnostics(summary.diagnostics, summary, parseInput, 'adapter'),
188
+ ...(thrownDiagnostic ? normalizeAdapterDiagnostics([thrownDiagnostic], summary, parseInput, 'throw') : []),
189
+ ...normalizeAdapterDiagnostics(parseResult.diagnostics, summary, parseInput, 'parse')
190
+ ];
191
+ const losses = mergeNativeLosses(
192
+ parseResult.losses,
193
+ diagnostics.map((diagnostic, index) => adapterDiagnosticToLoss(diagnostic, index, summary, parseInput))
194
+ );
195
+ const sourceEvidence = adapterDiagnosticsEvidence(summary, diagnostics, {
196
+ language,
197
+ parser,
198
+ parserVersion,
199
+ sourcePath: parseResult.sourcePath ?? input.sourcePath,
200
+ sourceHash: parseResult.sourceHash ?? sourceHash
201
+ });
202
+ const evidence = [...(parseResult.evidence ?? []), sourceEvidence];
203
+ const importInput = {
204
+ ...input,
205
+ ...parseResult,
206
+ language,
207
+ parser,
208
+ parserVersion,
209
+ sourceText,
210
+ sourcePath: parseResult.sourcePath ?? input.sourcePath,
211
+ sourceHash: parseResult.sourceHash ?? sourceHash,
212
+ losses,
213
+ evidence,
214
+ metadata: {
215
+ adapterId: summary.id,
216
+ adapterVersion: summary.version,
217
+ adapterCapabilities: summary.capabilities,
218
+ supportedExtensions: summary.supportedExtensions,
219
+ diagnostics: diagnostics.map(serializableDiagnostic),
220
+ ...input.metadata,
221
+ ...parseResult.metadata
222
+ },
223
+ nativeAstMetadata: {
224
+ adapterId: summary.id,
225
+ adapterVersion: summary.version,
226
+ parser,
227
+ ...input.nativeAstMetadata,
228
+ ...parseResult.nativeAstMetadata
229
+ },
230
+ nativeSourceMetadata: {
231
+ adapterId: summary.id,
232
+ adapterVersion: summary.version,
233
+ parser,
234
+ ...input.nativeSourceMetadata,
235
+ ...parseResult.nativeSourceMetadata
236
+ },
237
+ documentMetadata: {
238
+ nativeImporterAdapterId: summary.id,
239
+ nativeImporterAdapterVersion: summary.version,
240
+ ...input.documentMetadata,
241
+ ...parseResult.documentMetadata
242
+ },
243
+ universalAstMetadata: {
244
+ nativeImporterAdapterId: summary.id,
245
+ nativeImporterAdapterVersion: summary.version,
246
+ ...input.universalAstMetadata,
247
+ ...parseResult.universalAstMetadata
248
+ }
249
+ };
250
+ return {
251
+ ...importNativeSource(importInput),
252
+ adapter: summary,
253
+ diagnostics
254
+ };
255
+ }
256
+
138
257
  export function importNativeSource(input) {
139
258
  const language = input.language ?? input.nativeAst?.language;
140
259
  if (!language) throw new Error('importNativeSource requires a language or nativeAst.language');
@@ -599,6 +718,147 @@ export function emitForTarget(document, target = 'typescript', options = {}) {
599
718
  return renderTargetAst(projectFrontierAst(document, target, options), target);
600
719
  }
601
720
 
721
+ function normalizeNativeImporterAdapter(adapter) {
722
+ if (!adapter || typeof adapter !== 'object') {
723
+ throw new Error('Native importer adapter must be an object');
724
+ }
725
+ if (!adapter.id) throw new Error('Native importer adapter requires an id');
726
+ if (!adapter.language) throw new Error(`Native importer adapter ${adapter.id} requires a language`);
727
+ if (!adapter.parser) throw new Error(`Native importer adapter ${adapter.id} requires a parser`);
728
+ if (typeof adapter.parse !== 'function') throw new Error(`Native importer adapter ${adapter.id} requires a parse function`);
729
+ const summaryInput = {
730
+ id: String(adapter.id),
731
+ language: adapter.language,
732
+ parser: String(adapter.parser),
733
+ version: adapter.version === undefined ? undefined : String(adapter.version)
734
+ };
735
+ return Object.freeze({
736
+ ...summaryInput,
737
+ capabilities: normalizeStringList(adapter.capabilities),
738
+ supportedExtensions: normalizeStringList(adapter.supportedExtensions).map((extension) => extension.startsWith('.') ? extension.toLowerCase() : `.${extension.toLowerCase()}`),
739
+ diagnostics: normalizeAdapterDiagnostics(adapter.diagnostics, summaryInput, {
740
+ language: adapter.language,
741
+ parser: String(adapter.parser),
742
+ parserVersion: adapter.version === undefined ? undefined : String(adapter.version)
743
+ }, 'adapter')
744
+ });
745
+ }
746
+
747
+ function normalizeStringList(value) {
748
+ if (value === undefined || value === null) return [];
749
+ if (Array.isArray(value)) return value.map((item) => String(item)).filter(Boolean);
750
+ return [String(value)].filter(Boolean);
751
+ }
752
+
753
+ function normalizeAdapterDiagnostics(value, adapter, input, scope = 'diagnostic') {
754
+ if (value === undefined || value === null) return [];
755
+ const diagnostics = Array.isArray(value) ? value : [value];
756
+ return diagnostics.map((diagnostic, index) => {
757
+ const normalized = typeof diagnostic === 'string' ? { message: diagnostic } : diagnostic ?? {};
758
+ const severity = normalizeDiagnosticSeverity(normalized.severity);
759
+ return Object.freeze({
760
+ id: normalized.id ?? `diagnostic_${idFragment(adapter.id)}_${idFragment(scope)}_${index + 1}`,
761
+ severity,
762
+ code: normalized.code,
763
+ phase: normalized.phase ?? 'parse',
764
+ kind: normalized.kind,
765
+ message: String(normalized.message ?? `${adapter.id} reported a ${severity} diagnostic.`),
766
+ path: normalized.path ?? input.sourcePath,
767
+ span: normalized.span,
768
+ metadata: {
769
+ adapterId: adapter.id,
770
+ adapterVersion: adapter.version,
771
+ language: input.language ?? adapter.language,
772
+ parser: input.parser ?? adapter.parser,
773
+ parserVersion: input.parserVersion,
774
+ ...normalized.metadata
775
+ }
776
+ });
777
+ });
778
+ }
779
+
780
+ function normalizeDiagnosticSeverity(value) {
781
+ const severity = String(value ?? 'warning').toLowerCase();
782
+ if (severity === 'error') return 'error';
783
+ if (severity === 'info') return 'info';
784
+ return 'warning';
785
+ }
786
+
787
+ function adapterDiagnosticToLoss(diagnostic, index, adapter, input) {
788
+ const code = diagnostic.code ?? diagnostic.kind ?? diagnostic.severity;
789
+ return {
790
+ id: `loss_${idFragment(diagnostic.id ?? `${adapter.id}_${index}_${code}`)}`,
791
+ severity: diagnostic.severity,
792
+ phase: diagnostic.phase,
793
+ sourceFormat: input.language,
794
+ kind: diagnostic.kind ?? (diagnostic.severity === 'error' ? 'unsupportedSyntax' : 'opaqueNative'),
795
+ message: diagnostic.message,
796
+ span: diagnostic.span,
797
+ metadata: {
798
+ adapterId: adapter.id,
799
+ adapterVersion: adapter.version,
800
+ diagnosticId: diagnostic.id,
801
+ diagnosticCode: diagnostic.code,
802
+ parser: input.parser,
803
+ parserVersion: input.parserVersion,
804
+ path: diagnostic.path,
805
+ ...diagnostic.metadata
806
+ }
807
+ };
808
+ }
809
+
810
+ function mergeNativeLosses(primary = [], secondary = []) {
811
+ const seen = new Set();
812
+ const losses = [];
813
+ for (const loss of [...primary, ...secondary]) {
814
+ if (!loss) continue;
815
+ const id = loss.id ?? `loss_${losses.length + 1}`;
816
+ if (seen.has(id)) continue;
817
+ seen.add(id);
818
+ losses.push(loss.id ? loss : { ...loss, id });
819
+ }
820
+ return losses;
821
+ }
822
+
823
+ function adapterDiagnosticsEvidence(adapter, diagnostics, input) {
824
+ const errors = diagnostics.filter((diagnostic) => diagnostic.severity === 'error').length;
825
+ const warnings = diagnostics.filter((diagnostic) => diagnostic.severity === 'warning').length;
826
+ return {
827
+ id: `evidence_${idFragment(adapter.id)}_native_importer_adapter`,
828
+ kind: 'import',
829
+ status: errors ? 'failed' : 'passed',
830
+ path: input.sourcePath,
831
+ summary: `Ran ${adapter.id} native importer for ${input.language} with ${diagnostics.length} diagnostic(s).`,
832
+ metadata: {
833
+ adapterId: adapter.id,
834
+ adapterVersion: adapter.version,
835
+ language: input.language,
836
+ parser: input.parser,
837
+ parserVersion: input.parserVersion,
838
+ sourceHash: input.sourceHash,
839
+ capabilities: adapter.capabilities,
840
+ supportedExtensions: adapter.supportedExtensions,
841
+ diagnostics: diagnostics.map(serializableDiagnostic),
842
+ errors,
843
+ warnings
844
+ }
845
+ };
846
+ }
847
+
848
+ function serializableDiagnostic(diagnostic) {
849
+ return {
850
+ id: diagnostic.id,
851
+ severity: diagnostic.severity,
852
+ code: diagnostic.code,
853
+ phase: diagnostic.phase,
854
+ kind: diagnostic.kind,
855
+ message: diagnostic.message,
856
+ path: diagnostic.path,
857
+ span: diagnostic.span,
858
+ metadata: diagnostic.metadata
859
+ };
860
+ }
861
+
602
862
  function idFragment(value) {
603
863
  return String(value ?? 'native')
604
864
  .toLowerCase()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",