@shapeshift-labs/frontier-lang-compiler 0.2.23 → 0.2.24

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
@@ -191,10 +191,30 @@ const imported = importNativeSource({
191
191
  const sidecar = createSemanticImportSidecar(imported);
192
192
 
193
193
  console.log(sidecar.summary.emptySemanticIndex); // false when symbols were found
194
- console.log(sidecar.ownershipRegions[0].key); // source#src/runtime.ts#class#Runtime
194
+ console.log(sidecar.ownershipRegions[0].key); // source#src/runtime.ts#type#Runtime
195
195
  console.log(sidecar.patchHints[0].supportedOperations); // source-region patch operations
196
196
  ```
197
197
 
198
+ The built-in JavaScript/TypeScript lightweight scanner also emits review-required ownership regions for clear route/config/content/property shapes in exported objects and arrays:
199
+
200
+ ```js
201
+ const importedConfig = importNativeSource({
202
+ language: 'typescript',
203
+ sourcePath: 'src/routes.ts',
204
+ sourceText: `
205
+ export const appRoutes = [
206
+ { path: "/home", component: Home }
207
+ ];
208
+ export const siteContent = {
209
+ docs: { title: "Docs" }
210
+ };
211
+ `
212
+ });
213
+
214
+ const configSidecar = createSemanticImportSidecar(importedConfig);
215
+ console.log(configSidecar.regionTaxonomy.presentKinds); // includes "route" and "content"
216
+ ```
217
+
198
218
  Compare before/after native source imports from a worker patch and emit a semantic change set for admission scoring:
199
219
 
200
220
  ```js
package/bench/smoke.mjs CHANGED
@@ -135,6 +135,36 @@ const nativeTargetAdapterDurationMs = performance.now() - nativeTargetAdapterSta
135
135
  const nativeTargetAdapterBytes = nativeTargetAdapterCompiles.reduce((sum, result) => sum + result.output.length, 0);
136
136
  const nativeTargetAdapterSourceMaps = nativeTargetAdapterCompiles.reduce((sum, result) => sum + result.sourceMaps.length, 0);
137
137
 
138
+ const regionScanStart = performance.now();
139
+ const regionScanImports = [];
140
+ for (let index = 0; index < 100; index += 1) {
141
+ const imported = importNativeSource({
142
+ language: 'typescript',
143
+ sourcePath: `src/regions-${index}.ts`,
144
+ sourceText: `
145
+ export const appRoutes${index} = [
146
+ { path: "/${index}", component: Screen${index} },
147
+ { path: "/${index}/settings", component: Settings${index} }
148
+ ];
149
+ export const contentBlocks${index} = {
150
+ docs: { title: "Docs ${index}" },
151
+ legal: { title: "Legal ${index}" }
152
+ };
153
+ export const runtimeConfig${index} = {
154
+ limits: { count: ${index} },
155
+ resolve(id) { return id; }
156
+ };
157
+ export const helpers${index} = {
158
+ plain: ${index}
159
+ };
160
+ `
161
+ });
162
+ regionScanImports.push({ imported, sidecar: createSemanticImportSidecar(imported) });
163
+ }
164
+ const regionScanDurationMs = performance.now() - regionScanStart;
165
+ const regionScanSymbols = regionScanImports.reduce((sum, entry) => sum + entry.imported.semanticIndex.symbols.length, 0);
166
+ const regionScanOwnershipRegions = regionScanImports.reduce((sum, entry) => sum + entry.sidecar.ownershipRegions.length, 0);
167
+
138
168
  const externalSemanticStart = performance.now();
139
169
  const externalSemanticImports = [];
140
170
  for (let index = 0; index < 100; index += 1) {
@@ -208,6 +238,10 @@ console.log(JSON.stringify({
208
238
  nativeTargetAdapterBytes,
209
239
  nativeTargetAdapterSourceMaps,
210
240
  nativeTargetAdapterDurationMs: Number(nativeTargetAdapterDurationMs.toFixed(2)),
241
+ regionScanImports: regionScanImports.length,
242
+ regionScanSymbols,
243
+ regionScanOwnershipRegions,
244
+ regionScanDurationMs: Number(regionScanDurationMs.toFixed(2)),
211
245
  externalSemanticImports: externalSemanticImports.length,
212
246
  externalSemanticSymbols,
213
247
  externalSemanticMappings,
package/dist/index.d.ts CHANGED
@@ -140,6 +140,10 @@ export type NativeImportRegionTaxonomyKind =
140
140
  | 'call'
141
141
  | 'type'
142
142
  | 'effect'
143
+ | 'property'
144
+ | 'config'
145
+ | 'content'
146
+ | 'route'
143
147
  | 'generatedOutput'
144
148
  | string;
145
149
 
package/dist/index.js CHANGED
@@ -262,6 +262,10 @@ export const NativeImportRegionTaxonomyKinds = Object.freeze([
262
262
  'call',
263
263
  'type',
264
264
  'effect',
265
+ 'property',
266
+ 'config',
267
+ 'content',
268
+ 'route',
265
269
  'generatedOutput'
266
270
  ]);
267
271
 
@@ -4019,10 +4023,23 @@ function scanJavaScriptLike(input) {
4019
4023
  const declarations = [];
4020
4024
  let currentClass;
4021
4025
  let classDepth = 0;
4026
+ let currentObject;
4027
+ const lexicalState = { inBlockComment: false, inTemplateString: false };
4022
4028
  for (const { line, number } of sourceLines(input.sourceText)) {
4023
- const trimmed = line.trim();
4029
+ const scanLine = jsDeclarationScanLine(line, lexicalState);
4030
+ const trimmed = scanLine.trim();
4031
+ if (!trimmed || jsCommentOnlyLine(trimmed)) continue;
4024
4032
  const declarationLine = trimmed.replace(/^(?:export\s+)?(?:declare\s+)?/, '');
4025
4033
  let match;
4034
+ if (currentObject) {
4035
+ const routeRecord = jsRouteRecordDeclaration(input, number, trimmed, currentObject);
4036
+ if (routeRecord) {
4037
+ declarations.push(routeRecord);
4038
+ } else {
4039
+ const property = jsObjectPropertyDeclaration(input, number, trimmed, currentObject);
4040
+ if (property) declarations.push(property);
4041
+ }
4042
+ }
4026
4043
  if ((match = trimmed.match(/^import\s+(?:.+?\s+from\s+)?['"]([^'"]+)['"]/))) {
4027
4044
  declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDeclaration', 'module'));
4028
4045
  } else if ((match = trimmed.match(/^import\s*\(\s*['"]([^'"]+)['"]\s*\)/))) {
@@ -4050,11 +4067,20 @@ function scanJavaScriptLike(input) {
4050
4067
  } else if ((match = declarationLine.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?\(?([^=;]*)\)?\s*=>/))) {
4051
4068
  declarations.push(nativeDeclaration(input, number, 'VariableFunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
4052
4069
  } else if ((match = declarationLine.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b/))) {
4053
- declarations.push(nativeDeclaration(input, number, 'VariableDeclaration', 'variable', match[1], {}, false));
4070
+ const initializerKind = jsInitializerKind(declarationLine);
4071
+ const regionKind = jsRegionKindForDeclarationName(match[1], declarationLine);
4072
+ declarations.push(nativeDeclaration(input, number, 'VariableDeclaration', jsVariableSymbolKind(regionKind, initializerKind), match[1], {
4073
+ initializerKind
4074
+ }, jsVariableHasBody(initializerKind, declarationLine), {
4075
+ regionKind,
4076
+ metadata: { initializerKind }
4077
+ }));
4078
+ currentObject = jsObjectRegionContext(match[1], declarationLine, number, regionKind);
4054
4079
  } else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=\s*(?:async\s+)?function\*?\s*\(([^)]*)\)/))) {
4055
4080
  declarations.push(nativeDeclaration(input, number, 'CommonJsFunctionExport', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
4056
4081
  } else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=/))) {
4057
- declarations.push(nativeDeclaration(input, number, 'CommonJsExport', 'variable', match[1], { export: 'commonjs' }, false));
4082
+ const regionKind = jsRegionKindForDeclarationName(match[1], trimmed);
4083
+ declarations.push(nativeDeclaration(input, number, 'CommonJsExport', 'variable', match[1], { export: 'commonjs' }, false, { regionKind }));
4058
4084
  } else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*(?::\s*[^={]+)?(?:\{|=>|$)/)) && !jsControlKeyword(match[1])) {
4059
4085
  declarations.push(nativeDeclaration(input, number, 'MethodDefinition', 'method', `${currentClass}.${match[1]}`, {
4060
4086
  methodName: match[1],
@@ -4075,10 +4101,224 @@ function scanJavaScriptLike(input) {
4075
4101
  classDepth = 0;
4076
4102
  }
4077
4103
  }
4104
+ if (currentObject) {
4105
+ if (number !== currentObject.startLine) currentObject.depth += jsContainerDelta(trimmed);
4106
+ if (currentObject.depth <= 0) currentObject = undefined;
4107
+ }
4078
4108
  }
4079
4109
  return declarations;
4080
4110
  }
4081
4111
 
4112
+ function jsCommentOnlyLine(trimmed) {
4113
+ return trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*');
4114
+ }
4115
+
4116
+ function jsDeclarationScanLine(line, state) {
4117
+ let text = String(line ?? '');
4118
+ if (state.inTemplateString) {
4119
+ const close = findUnescapedBacktick(text, 0);
4120
+ if (close < 0) return '';
4121
+ text = text.slice(close + 1);
4122
+ state.inTemplateString = false;
4123
+ }
4124
+ if (state.inBlockComment) {
4125
+ const close = text.indexOf('*/');
4126
+ if (close < 0) return '';
4127
+ text = text.slice(close + 2);
4128
+ state.inBlockComment = false;
4129
+ }
4130
+ let output = '';
4131
+ let quote;
4132
+ let escaped = false;
4133
+ for (let index = 0; index < text.length; index += 1) {
4134
+ const char = text[index];
4135
+ const next = text[index + 1];
4136
+ if (quote) {
4137
+ output += char;
4138
+ if (escaped) {
4139
+ escaped = false;
4140
+ } else if (char === '\\') {
4141
+ escaped = true;
4142
+ } else if (char === quote) {
4143
+ quote = undefined;
4144
+ }
4145
+ continue;
4146
+ }
4147
+ if (char === '/' && next === '/') break;
4148
+ if (char === '/' && next === '*') {
4149
+ const close = text.indexOf('*/', index + 2);
4150
+ if (close < 0) {
4151
+ state.inBlockComment = true;
4152
+ break;
4153
+ }
4154
+ index = close + 1;
4155
+ continue;
4156
+ }
4157
+ if (char === '\'' || char === '"') {
4158
+ quote = char;
4159
+ output += char;
4160
+ continue;
4161
+ }
4162
+ if (char === '`') {
4163
+ const close = findUnescapedBacktick(text, index + 1);
4164
+ if (close < 0) {
4165
+ state.inTemplateString = true;
4166
+ output += '``';
4167
+ break;
4168
+ }
4169
+ output += text.slice(index, close + 1);
4170
+ index = close;
4171
+ continue;
4172
+ }
4173
+ output += char;
4174
+ }
4175
+ return output;
4176
+ }
4177
+
4178
+ function findUnescapedBacktick(text, startIndex) {
4179
+ let escaped = false;
4180
+ for (let index = startIndex; index < text.length; index += 1) {
4181
+ const char = text[index];
4182
+ if (escaped) {
4183
+ escaped = false;
4184
+ } else if (char === '\\') {
4185
+ escaped = true;
4186
+ } else if (char === '`') {
4187
+ return index;
4188
+ }
4189
+ }
4190
+ return -1;
4191
+ }
4192
+
4193
+ function jsObjectRegionContext(name, declarationLine, lineNumber, regionKind) {
4194
+ const initializerKind = jsInitializerKind(declarationLine);
4195
+ if (initializerKind !== 'object' && initializerKind !== 'array') return undefined;
4196
+ const depth = jsContainerDelta(declarationLine);
4197
+ if (depth <= 0) return undefined;
4198
+ return {
4199
+ name,
4200
+ regionKind: regionKind ?? jsRegionKindForDeclarationName(name, declarationLine),
4201
+ initializerKind,
4202
+ depth,
4203
+ startLine: lineNumber
4204
+ };
4205
+ }
4206
+
4207
+ function jsInitializerKind(line) {
4208
+ const initializer = String(line ?? '').split('=').slice(1).join('=').trim();
4209
+ if (!initializer) return 'unknown';
4210
+ if (/^(?:async\s+)?function\b/.test(initializer) || /=>/.test(initializer)) return 'function';
4211
+ if (initializer.startsWith('{')) return 'object';
4212
+ if (initializer.startsWith('[')) return 'array';
4213
+ if (/^new\s+/.test(initializer)) return 'instance';
4214
+ if (/^['"`]/.test(initializer)) return 'string';
4215
+ if (/^(?:true|false)\b/.test(initializer)) return 'boolean';
4216
+ if (/^[0-9]/.test(initializer)) return 'number';
4217
+ return 'expression';
4218
+ }
4219
+
4220
+ function jsVariableHasBody(initializerKind, declarationLine) {
4221
+ return initializerKind === 'function'
4222
+ || initializerKind === 'object'
4223
+ || initializerKind === 'array'
4224
+ || /\{/.test(String(declarationLine ?? ''));
4225
+ }
4226
+
4227
+ function jsVariableSymbolKind(regionKind, initializerKind) {
4228
+ if (regionKind === 'route') return 'route';
4229
+ if (initializerKind === 'function') return 'function';
4230
+ return 'variable';
4231
+ }
4232
+
4233
+ function jsRegionKindForDeclarationName(name, source = '') {
4234
+ const raw = `${name ?? ''} ${source ?? ''}`;
4235
+ const text = raw.replace(/([a-z0-9])([A-Z])/g, '$1 $2').toLowerCase();
4236
+ const compact = raw.toLowerCase();
4237
+ if (/\b(routes?|router|screens?|pages?|navigation|navitems?|menuitems?)\b/.test(text) || /(route|router|screen|page|navigation|navitem|menuitem)/.test(compact)) return 'route';
4238
+ if (/\b(config|settings|options|flags?|schema|manifest|registry|catalog)\b/.test(text) || /(config|settings|options|flags|schema|manifest|registry|catalog)/.test(compact)) return 'config';
4239
+ if (/\b(content|copy|docs?|legal|messages?|strings?|i18n|locale|translations?)\b/.test(text) || /(content|copy|docs|legal|messages|strings|i18n|locale|translations)/.test(compact)) return 'content';
4240
+ return undefined;
4241
+ }
4242
+
4243
+ function jsObjectPropertyDeclaration(input, lineNumber, trimmed, context) {
4244
+ if (/^[}\])]/.test(trimmed) || trimmed.startsWith('...')) return undefined;
4245
+ const methodMatch = trimmed.match(/^(?:(?:async|get|set)\s+)?(['"]?)([A-Za-z_$][\w$-]*)\1\s*\(([^)]*)\)\s*(?:[:\w\s<>\[\]]*)?(?:\{|=>|,|$)/);
4246
+ if (methodMatch && !jsControlKeyword(methodMatch[2])) {
4247
+ const name = `${context.name}.${methodMatch[2]}`;
4248
+ return nativeDeclaration(input, lineNumber, 'ObjectMethod', 'function', name, {
4249
+ owner: context.name,
4250
+ propertyName: methodMatch[2],
4251
+ parameters: splitParameters(methodMatch[3])
4252
+ }, true, {
4253
+ regionKind: jsPropertyRegionKind(context, methodMatch[2], 'function'),
4254
+ metadata: { owner: context.name, propertyName: methodMatch[2], initializerKind: 'function' }
4255
+ });
4256
+ }
4257
+ const propertyMatch = trimmed.match(/^(?:(['"])([^'"]+)\1|([A-Za-z_$][\w$-]*))\s*:\s*(.+?)(?:,)?$/);
4258
+ if (!propertyMatch) return undefined;
4259
+ const propertyName = propertyMatch[2] ?? propertyMatch[3];
4260
+ if (!propertyName || jsControlKeyword(propertyName)) return undefined;
4261
+ const value = propertyMatch[4].trim();
4262
+ const initializerKind = jsPropertyInitializerKind(value);
4263
+ const functionLike = initializerKind === 'function';
4264
+ const name = `${context.name}.${propertyName}`;
4265
+ return nativeDeclaration(input, lineNumber, functionLike ? 'ObjectFunctionProperty' : 'ObjectProperty', functionLike ? 'function' : 'property', name, {
4266
+ owner: context.name,
4267
+ propertyName,
4268
+ initializerKind
4269
+ }, functionLike || initializerKind === 'object' || initializerKind === 'array', {
4270
+ regionKind: jsPropertyRegionKind(context, propertyName, value),
4271
+ metadata: {
4272
+ owner: context.name,
4273
+ propertyName,
4274
+ initializerKind
4275
+ }
4276
+ });
4277
+ }
4278
+
4279
+ function jsRouteRecordDeclaration(input, lineNumber, trimmed, context) {
4280
+ if (context.regionKind !== 'route') return undefined;
4281
+ const match = trimmed.match(/\b(?:path|route|href|url)\s*:\s*(['"`])([^'"`]+)\1/);
4282
+ if (!match) return undefined;
4283
+ const routePath = match[2];
4284
+ return nativeDeclaration(input, lineNumber, 'RouteRecord', 'route', `${context.name}.${routePath}`, {
4285
+ owner: context.name,
4286
+ routePath
4287
+ }, true, {
4288
+ regionKind: 'route',
4289
+ metadata: { owner: context.name, routePath, initializerKind: 'object' }
4290
+ });
4291
+ }
4292
+
4293
+ function jsPropertyInitializerKind(value) {
4294
+ const text = String(value ?? '').trim();
4295
+ if (/^(?:async\s*)?(?:function\b|\([^)]*\)\s*=>|[A-Za-z_$][\w$]*\s*=>)/.test(text)) return 'function';
4296
+ if (text.startsWith('{')) return 'object';
4297
+ if (text.startsWith('[')) return 'array';
4298
+ if (/^['"`]/.test(text)) return 'string';
4299
+ if (/^(?:true|false)\b/.test(text)) return 'boolean';
4300
+ if (/^[0-9]/.test(text)) return 'number';
4301
+ return 'expression';
4302
+ }
4303
+
4304
+ function jsPropertyRegionKind(context, propertyName, value) {
4305
+ const named = jsRegionKindForDeclarationName(propertyName, value);
4306
+ if (named) return named;
4307
+ if (context.regionKind === 'route') return 'route';
4308
+ if (context.regionKind === 'content') return 'content';
4309
+ if (context.regionKind === 'config') return 'config';
4310
+ return 'property';
4311
+ }
4312
+
4313
+ function jsContainerDelta(source) {
4314
+ let delta = 0;
4315
+ for (const char of String(source ?? '')) {
4316
+ if (char === '{' || char === '[') delta += 1;
4317
+ if (char === '}' || char === ']') delta -= 1;
4318
+ }
4319
+ return delta;
4320
+ }
4321
+
4082
4322
  function scanPython(input) {
4083
4323
  const declarations = [];
4084
4324
  for (const { line, number } of sourceLines(input.sourceText)) {
@@ -4778,7 +5018,7 @@ function rMetaName(source) {
4778
5018
  return match?.[1] ?? 'dynamic';
4779
5019
  }
4780
5020
 
4781
- function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fields = {}, hasBody = false) {
5021
+ function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fields = {}, hasBody = false, options = {}) {
4782
5022
  const nodeId = `native_${idFragment(languageKind)}_${lineNumber}_${idFragment(name)}`;
4783
5023
  return {
4784
5024
  nodeId,
@@ -4789,7 +5029,9 @@ function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fi
4789
5029
  symbolId: `symbol:${input.language}:${idFragment(name)}`,
4790
5030
  span: spanForLine(input, lineNumber),
4791
5031
  fields,
4792
- metadata: { scan: 'lightweight-declaration', hasBody },
5032
+ metadata: { scan: 'lightweight-declaration', hasBody, ...options.metadata },
5033
+ ...(options.regionKind ? { regionKind: options.regionKind } : {}),
5034
+ ...(options.role ? { role: options.role } : {}),
4793
5035
  ...(hasBody ? { loss: opaqueBodyLoss(input, lineNumber, nodeId, name) } : {})
4794
5036
  };
4795
5037
  }
@@ -6649,6 +6891,8 @@ function semanticPatchHintForRegion(region, readiness, options = {}) {
6649
6891
 
6650
6892
  function semanticRegionKindForDeclaration(declaration) {
6651
6893
  if (declaration.role === 'import' || declaration.importPath) return 'import';
6894
+ if (declaration.regionKind) return normalizeNativeImportRegionKind(declaration.regionKind);
6895
+ if (declaration.metadata?.ownershipRegionKind) return normalizeNativeImportRegionKind(declaration.metadata.ownershipRegionKind);
6652
6896
  const kind = declaration.symbolKind ?? declaration.kind ?? declaration.nativeNode?.kind;
6653
6897
  if (semanticKindIsType(kind)) return 'type';
6654
6898
  if (semanticKindCanOwnBody(kind, declaration.span ?? declaration.nativeNode?.span)) return 'body';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.23",
3
+ "version": "0.2.24",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",