@lark.js/mvc 0.0.9 → 0.0.10

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/vite.cjs CHANGED
@@ -14863,7 +14863,7 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
14863
14863
  const object = tokens[0];
14864
14864
  if (tokens.length > 1 && tokens[1] !== "as") {
14865
14865
  throw new Error(
14866
- `[@lark.js/mvc error] bad forIn syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{for-in obj as val [key]}}`
14866
+ `[@lark.js/mvc error] bad forIn syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{forIn obj as val [key]}}`
14867
14867
  );
14868
14868
  }
14869
14869
  const restTokens2 = tokens.slice(2);
@@ -15100,7 +15100,8 @@ function extractGlobalVars(source) {
15100
15100
  } catch {
15101
15101
  return fallbackExtractVariables(source);
15102
15102
  }
15103
- const globalExists = { ...BUILTIN_GLOBALS };
15103
+ const globalExists = {};
15104
+ for (const name of BUILTIN_GLOBALS) globalExists[name] = 1;
15104
15105
  const globalVars = /* @__PURE__ */ Object.create(null);
15105
15106
  const fnRange = [];
15106
15107
  walkAst(ast, {
@@ -15174,7 +15175,7 @@ function fallbackExtractVariables(source) {
15174
15175
  while ((m = ifRegExp.exec(source)) !== null) {
15175
15176
  vars.add(m[1]);
15176
15177
  }
15177
- return Array.from(vars).filter((v) => !BUILTIN_GLOBAL_SET.has(v));
15178
+ return Array.from(vars).filter((v) => !BUILTIN_GLOBALS.has(v));
15178
15179
  }
15179
15180
  function walkAst(ast, visitors) {
15180
15181
  function visit(node) {
@@ -15213,7 +15214,7 @@ function walkAst(ast, visitors) {
15213
15214
  function isAstNode(v) {
15214
15215
  return !!v && typeof v === "object" && typeof v.type === "string";
15215
15216
  }
15216
- var BUILTIN_GLOBALS = {
15217
+ var BUILTIN_GLOBALS = /* @__PURE__ */ new Set([
15217
15218
  // ─── Template runtime helpers (injected by compileToFunction) ───────
15218
15219
  //
15219
15220
  // These variables appear in the generated template function signature
@@ -15222,147 +15223,146 @@ var BUILTIN_GLOBALS = {
15222
15223
  // SPLITTER character constant (same as \x1e), used as namespace separator
15223
15224
  // for refData keys, event attribute encoding, and internal data structures.
15224
15225
  // Declared as: let $splitter='\x1e'
15225
- $splitter: 1,
15226
+ "$splitter",
15226
15227
  // Data — the data object passed from Updater to the template function.
15227
15228
  // User variables are destructured from $data at the top of the function:
15228
15229
  // let {name, age} = $data;
15229
15230
  // This is the first parameter of the generated arrow function.
15230
- $data: 1,
15231
+ "$data",
15231
15232
  // Null-safe toString: v => '' + (v == null ? '' : v)
15232
15233
  // Converts null/undefined to empty string, otherwise calls toString().
15233
15234
  // Wraps every {{!raw}} output to prevent "null" / "undefined" rendering.
15234
- $strSafe: 1,
15235
+ "$strSafe",
15235
15236
  // HTML entity encoder: v => $strSafe(v).replace(/[&<>"'`]/g, entityMap)
15236
15237
  // Encodes &, <, >, ", ', ` to HTML entities (&amp; &lt; etc.)
15237
15238
  // Applied to all {{=escaped}} and {{:binding}} outputs.
15238
- $encHtml: 1,
15239
+ "$encHtml",
15239
15240
  // HTML entity map — internal object used by $encHtml:
15240
15241
  // {'&':'amp','<':'gt','>':'gt','"':'#34','\'':'#39','`':'#96'}
15241
15242
  // Not a standalone function; referenced inside $encHtml's closure.
15242
- $entMap: 1,
15243
+ "$entMap",
15243
15244
  // HTML entity RegExp — internal regexp used by $encHtml:
15244
15245
  // /[&<>"'`]/g
15245
- $entReg: 1,
15246
+ "$entReg",
15246
15247
  // HTML entity replacer function — internal helper used by $encHtml:
15247
15248
  // m => '&' + $entMap[m] + ';'
15248
15249
  // Maps matched character to its entity string.
15249
- $entFn: 1,
15250
+ "$entFn",
15250
15251
  // Output buffer — the string accumulator for rendered HTML.
15251
15252
  // All template output is appended via $out += '...'.
15252
15253
  // Declared as: let $out = ''
15253
- $out: 1,
15254
+ "$out",
15254
15255
  // Reference lookup: (refData, value) => key
15255
15256
  // Finds or allocates a SPLITTER-prefixed key in refData for a given
15256
15257
  // object reference. Used by {{@ref}} operator for passing object
15257
15258
  // references to child views via v-lark attributes.
15258
- $refFn: 1,
15259
+ "$refFn",
15259
15260
  // URI encoder: v => encodeURIComponent($strSafe(v)).replace(/[!')(*]/g, extraMap)
15260
15261
  // Extends encodeURIComponent with encoding of ! ' ( ) *.
15261
15262
  // Applied to values in @event URL parameters and {{!uri}} contexts.
15262
- $encUri: 1,
15263
+ "$encUri",
15263
15264
  // URI encode map — internal object used by $encUri:
15264
15265
  // {'!':'%21','\'':'%27','(':'%28',')':'%29','*':'%2A'}
15265
- $uriMap: 1,
15266
+ "$uriMap",
15266
15267
  // URI encode replacer — internal helper used by $encUri:
15267
15268
  // m => $uriMap[m]
15268
- $uriFn: 1,
15269
+ "$uriFn",
15269
15270
  // URI encode regexp — internal regexp used by $encUri:
15270
15271
  // /[!')(*]/g
15271
- $uriReg: 1,
15272
+ "$uriReg",
15272
15273
  // Quote encoder: v => $strSafe(v).replace(/['"\\]/g, '\\$&')
15273
15274
  // Escapes quotes and backslashes for safe embedding in HTML attribute
15274
15275
  // values (e.g. data-json='...').
15275
- $encQuote: 1,
15276
+ "$encQuote",
15276
15277
  // Quote encode regexp — internal regexp used by $encQuote:
15277
15278
  // /['"\\]/g
15278
- $qReg: 1,
15279
+ "$qReg",
15279
15280
  // View ID — the unique identifier of the owning View instance.
15280
15281
  // Injected into @event attribute values at render time so that
15281
15282
  // EventDelegator can dispatch events to the correct View handler.
15282
15283
  // The \x1f placeholder in compiled output is replaced with '+$viewId+'.
15283
- $viewId: 1,
15284
+ "$viewId",
15284
15285
  // Debug: current expression text — stores the template expression being
15285
15286
  // evaluated, for error reporting. Only present in debug mode.
15286
15287
  // e.g. $dbgExpr='<%=user.name%>'
15287
- $dbgExpr: 1,
15288
+ "$dbgExpr",
15288
15289
  // Debug: original art syntax — stores the {{}} template syntax before
15289
15290
  // conversion, for error reporting. Only present in debug mode.
15290
15291
  // e.g. $dbgArt='{{=user.name}}'
15291
- $dbgArt: 1,
15292
+ "$dbgArt",
15292
15293
  // Debug: source line number — tracks the current line in the template
15293
15294
  // source, for error reporting. Only present in debug mode.
15294
- $dbgLine: 1,
15295
+ "$dbgLine",
15295
15296
  // RefData alias — fallback reference lookup table.
15296
15297
  // Defaults to $data when no explicit $refAlt is provided.
15297
15298
  // Ensures $refFn() does not crash when @ operator is used without refData.
15298
- $refAlt: 1,
15299
+ "$refAlt",
15299
15300
  // Temporary variable — used by the compiler for intermediate
15300
15301
  // expression results in generated code (e.g. loop variables,
15301
15302
  // conditional branches). Declared as: let $tmp
15302
- $tmp: 1,
15303
+ "$tmp",
15303
15304
  // JS literals
15304
- undefined: 1,
15305
- null: 1,
15306
- true: 1,
15307
- false: 1,
15308
- NaN: 1,
15309
- Infinity: 1,
15305
+ "undefined",
15306
+ "null",
15307
+ "true",
15308
+ "false",
15309
+ "NaN",
15310
+ "Infinity",
15310
15311
  // JS built-in globals
15311
- window: 1,
15312
- self: 1,
15313
- globalThis: 1,
15314
- document: 1,
15315
- console: 1,
15316
- JSON: 1,
15317
- Math: 1,
15318
- Intl: 1,
15319
- Promise: 1,
15320
- Symbol: 1,
15321
- Number: 1,
15322
- String: 1,
15323
- Boolean: 1,
15324
- Array: 1,
15325
- Object: 1,
15326
- Date: 1,
15327
- RegExp: 1,
15328
- Error: 1,
15329
- TypeError: 1,
15330
- RangeError: 1,
15331
- SyntaxError: 1,
15332
- Map: 1,
15333
- Set: 1,
15334
- WeakMap: 1,
15335
- WeakSet: 1,
15336
- Proxy: 1,
15337
- Reflect: 1,
15338
- ArrayBuffer: 1,
15339
- DataView: 1,
15340
- Float32Array: 1,
15341
- Float64Array: 1,
15342
- Int8Array: 1,
15343
- Int16Array: 1,
15344
- Int32Array: 1,
15345
- Uint8Array: 1,
15346
- Uint16Array: 1,
15347
- Uint32Array: 1,
15348
- Uint8ClampedArray: 1,
15312
+ "window",
15313
+ "self",
15314
+ "globalThis",
15315
+ "document",
15316
+ "console",
15317
+ "JSON",
15318
+ "Math",
15319
+ "Intl",
15320
+ "Promise",
15321
+ "Symbol",
15322
+ "Number",
15323
+ "String",
15324
+ "Boolean",
15325
+ "Array",
15326
+ "Object",
15327
+ "Date",
15328
+ "RegExp",
15329
+ "Error",
15330
+ "TypeError",
15331
+ "RangeError",
15332
+ "SyntaxError",
15333
+ "Map",
15334
+ "Set",
15335
+ "WeakMap",
15336
+ "WeakSet",
15337
+ "Proxy",
15338
+ "Reflect",
15339
+ "ArrayBuffer",
15340
+ "DataView",
15341
+ "Float32Array",
15342
+ "Float64Array",
15343
+ "Int8Array",
15344
+ "Int16Array",
15345
+ "Int32Array",
15346
+ "Uint8Array",
15347
+ "Uint16Array",
15348
+ "Uint32Array",
15349
+ "Uint8ClampedArray",
15349
15350
  // Functions
15350
- parseInt: 1,
15351
- parseFloat: 1,
15352
- isNaN: 1,
15353
- isFinite: 1,
15354
- encodeURIComponent: 1,
15355
- decodeURIComponent: 1,
15356
- encodeURI: 1,
15357
- decodeURI: 1,
15351
+ "parseInt",
15352
+ "parseFloat",
15353
+ "isNaN",
15354
+ "isFinite",
15355
+ "encodeURIComponent",
15356
+ "decodeURIComponent",
15357
+ "encodeURI",
15358
+ "decodeURI",
15358
15359
  // Babel helpers
15359
- arguments: 1,
15360
- this: 1,
15361
- require: 1,
15360
+ "arguments",
15361
+ "this",
15362
+ "require",
15362
15363
  // Lark framework
15363
- Lark: 1
15364
- };
15365
- var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
15364
+ "Lark"
15365
+ ]);
15366
15366
 
15367
15367
  // src/vite.ts
15368
15368
  var LARK_TEMPLATE_SUFFIX = "?lark-template";
package/dist/vite.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  compileTemplate,
3
3
  extractGlobalVars
4
- } from "./chunk-3HSA7OHB.js";
4
+ } from "./chunk-SIQF4YLC.js";
5
5
 
6
6
  // src/vite.ts
7
7
  import path from "path";
package/dist/webpack.cjs CHANGED
@@ -14861,7 +14861,7 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
14861
14861
  const object = tokens[0];
14862
14862
  if (tokens.length > 1 && tokens[1] !== "as") {
14863
14863
  throw new Error(
14864
- `[@lark.js/mvc error] bad forIn syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{for-in obj as val [key]}}`
14864
+ `[@lark.js/mvc error] bad forIn syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{forIn obj as val [key]}}`
14865
14865
  );
14866
14866
  }
14867
14867
  const restTokens2 = tokens.slice(2);
@@ -15098,7 +15098,8 @@ function extractGlobalVars(source) {
15098
15098
  } catch {
15099
15099
  return fallbackExtractVariables(source);
15100
15100
  }
15101
- const globalExists = { ...BUILTIN_GLOBALS };
15101
+ const globalExists = {};
15102
+ for (const name of BUILTIN_GLOBALS) globalExists[name] = 1;
15102
15103
  const globalVars = /* @__PURE__ */ Object.create(null);
15103
15104
  const fnRange = [];
15104
15105
  walkAst(ast, {
@@ -15172,7 +15173,7 @@ function fallbackExtractVariables(source) {
15172
15173
  while ((m = ifRegExp.exec(source)) !== null) {
15173
15174
  vars.add(m[1]);
15174
15175
  }
15175
- return Array.from(vars).filter((v) => !BUILTIN_GLOBAL_SET.has(v));
15176
+ return Array.from(vars).filter((v) => !BUILTIN_GLOBALS.has(v));
15176
15177
  }
15177
15178
  function walkAst(ast, visitors) {
15178
15179
  function visit(node) {
@@ -15211,7 +15212,7 @@ function walkAst(ast, visitors) {
15211
15212
  function isAstNode(v) {
15212
15213
  return !!v && typeof v === "object" && typeof v.type === "string";
15213
15214
  }
15214
- var BUILTIN_GLOBALS = {
15215
+ var BUILTIN_GLOBALS = /* @__PURE__ */ new Set([
15215
15216
  // ─── Template runtime helpers (injected by compileToFunction) ───────
15216
15217
  //
15217
15218
  // These variables appear in the generated template function signature
@@ -15220,147 +15221,146 @@ var BUILTIN_GLOBALS = {
15220
15221
  // SPLITTER character constant (same as \x1e), used as namespace separator
15221
15222
  // for refData keys, event attribute encoding, and internal data structures.
15222
15223
  // Declared as: let $splitter='\x1e'
15223
- $splitter: 1,
15224
+ "$splitter",
15224
15225
  // Data — the data object passed from Updater to the template function.
15225
15226
  // User variables are destructured from $data at the top of the function:
15226
15227
  // let {name, age} = $data;
15227
15228
  // This is the first parameter of the generated arrow function.
15228
- $data: 1,
15229
+ "$data",
15229
15230
  // Null-safe toString: v => '' + (v == null ? '' : v)
15230
15231
  // Converts null/undefined to empty string, otherwise calls toString().
15231
15232
  // Wraps every {{!raw}} output to prevent "null" / "undefined" rendering.
15232
- $strSafe: 1,
15233
+ "$strSafe",
15233
15234
  // HTML entity encoder: v => $strSafe(v).replace(/[&<>"'`]/g, entityMap)
15234
15235
  // Encodes &, <, >, ", ', ` to HTML entities (&amp; &lt; etc.)
15235
15236
  // Applied to all {{=escaped}} and {{:binding}} outputs.
15236
- $encHtml: 1,
15237
+ "$encHtml",
15237
15238
  // HTML entity map — internal object used by $encHtml:
15238
15239
  // {'&':'amp','<':'gt','>':'gt','"':'#34','\'':'#39','`':'#96'}
15239
15240
  // Not a standalone function; referenced inside $encHtml's closure.
15240
- $entMap: 1,
15241
+ "$entMap",
15241
15242
  // HTML entity RegExp — internal regexp used by $encHtml:
15242
15243
  // /[&<>"'`]/g
15243
- $entReg: 1,
15244
+ "$entReg",
15244
15245
  // HTML entity replacer function — internal helper used by $encHtml:
15245
15246
  // m => '&' + $entMap[m] + ';'
15246
15247
  // Maps matched character to its entity string.
15247
- $entFn: 1,
15248
+ "$entFn",
15248
15249
  // Output buffer — the string accumulator for rendered HTML.
15249
15250
  // All template output is appended via $out += '...'.
15250
15251
  // Declared as: let $out = ''
15251
- $out: 1,
15252
+ "$out",
15252
15253
  // Reference lookup: (refData, value) => key
15253
15254
  // Finds or allocates a SPLITTER-prefixed key in refData for a given
15254
15255
  // object reference. Used by {{@ref}} operator for passing object
15255
15256
  // references to child views via v-lark attributes.
15256
- $refFn: 1,
15257
+ "$refFn",
15257
15258
  // URI encoder: v => encodeURIComponent($strSafe(v)).replace(/[!')(*]/g, extraMap)
15258
15259
  // Extends encodeURIComponent with encoding of ! ' ( ) *.
15259
15260
  // Applied to values in @event URL parameters and {{!uri}} contexts.
15260
- $encUri: 1,
15261
+ "$encUri",
15261
15262
  // URI encode map — internal object used by $encUri:
15262
15263
  // {'!':'%21','\'':'%27','(':'%28',')':'%29','*':'%2A'}
15263
- $uriMap: 1,
15264
+ "$uriMap",
15264
15265
  // URI encode replacer — internal helper used by $encUri:
15265
15266
  // m => $uriMap[m]
15266
- $uriFn: 1,
15267
+ "$uriFn",
15267
15268
  // URI encode regexp — internal regexp used by $encUri:
15268
15269
  // /[!')(*]/g
15269
- $uriReg: 1,
15270
+ "$uriReg",
15270
15271
  // Quote encoder: v => $strSafe(v).replace(/['"\\]/g, '\\$&')
15271
15272
  // Escapes quotes and backslashes for safe embedding in HTML attribute
15272
15273
  // values (e.g. data-json='...').
15273
- $encQuote: 1,
15274
+ "$encQuote",
15274
15275
  // Quote encode regexp — internal regexp used by $encQuote:
15275
15276
  // /['"\\]/g
15276
- $qReg: 1,
15277
+ "$qReg",
15277
15278
  // View ID — the unique identifier of the owning View instance.
15278
15279
  // Injected into @event attribute values at render time so that
15279
15280
  // EventDelegator can dispatch events to the correct View handler.
15280
15281
  // The \x1f placeholder in compiled output is replaced with '+$viewId+'.
15281
- $viewId: 1,
15282
+ "$viewId",
15282
15283
  // Debug: current expression text — stores the template expression being
15283
15284
  // evaluated, for error reporting. Only present in debug mode.
15284
15285
  // e.g. $dbgExpr='<%=user.name%>'
15285
- $dbgExpr: 1,
15286
+ "$dbgExpr",
15286
15287
  // Debug: original art syntax — stores the {{}} template syntax before
15287
15288
  // conversion, for error reporting. Only present in debug mode.
15288
15289
  // e.g. $dbgArt='{{=user.name}}'
15289
- $dbgArt: 1,
15290
+ "$dbgArt",
15290
15291
  // Debug: source line number — tracks the current line in the template
15291
15292
  // source, for error reporting. Only present in debug mode.
15292
- $dbgLine: 1,
15293
+ "$dbgLine",
15293
15294
  // RefData alias — fallback reference lookup table.
15294
15295
  // Defaults to $data when no explicit $refAlt is provided.
15295
15296
  // Ensures $refFn() does not crash when @ operator is used without refData.
15296
- $refAlt: 1,
15297
+ "$refAlt",
15297
15298
  // Temporary variable — used by the compiler for intermediate
15298
15299
  // expression results in generated code (e.g. loop variables,
15299
15300
  // conditional branches). Declared as: let $tmp
15300
- $tmp: 1,
15301
+ "$tmp",
15301
15302
  // JS literals
15302
- undefined: 1,
15303
- null: 1,
15304
- true: 1,
15305
- false: 1,
15306
- NaN: 1,
15307
- Infinity: 1,
15303
+ "undefined",
15304
+ "null",
15305
+ "true",
15306
+ "false",
15307
+ "NaN",
15308
+ "Infinity",
15308
15309
  // JS built-in globals
15309
- window: 1,
15310
- self: 1,
15311
- globalThis: 1,
15312
- document: 1,
15313
- console: 1,
15314
- JSON: 1,
15315
- Math: 1,
15316
- Intl: 1,
15317
- Promise: 1,
15318
- Symbol: 1,
15319
- Number: 1,
15320
- String: 1,
15321
- Boolean: 1,
15322
- Array: 1,
15323
- Object: 1,
15324
- Date: 1,
15325
- RegExp: 1,
15326
- Error: 1,
15327
- TypeError: 1,
15328
- RangeError: 1,
15329
- SyntaxError: 1,
15330
- Map: 1,
15331
- Set: 1,
15332
- WeakMap: 1,
15333
- WeakSet: 1,
15334
- Proxy: 1,
15335
- Reflect: 1,
15336
- ArrayBuffer: 1,
15337
- DataView: 1,
15338
- Float32Array: 1,
15339
- Float64Array: 1,
15340
- Int8Array: 1,
15341
- Int16Array: 1,
15342
- Int32Array: 1,
15343
- Uint8Array: 1,
15344
- Uint16Array: 1,
15345
- Uint32Array: 1,
15346
- Uint8ClampedArray: 1,
15310
+ "window",
15311
+ "self",
15312
+ "globalThis",
15313
+ "document",
15314
+ "console",
15315
+ "JSON",
15316
+ "Math",
15317
+ "Intl",
15318
+ "Promise",
15319
+ "Symbol",
15320
+ "Number",
15321
+ "String",
15322
+ "Boolean",
15323
+ "Array",
15324
+ "Object",
15325
+ "Date",
15326
+ "RegExp",
15327
+ "Error",
15328
+ "TypeError",
15329
+ "RangeError",
15330
+ "SyntaxError",
15331
+ "Map",
15332
+ "Set",
15333
+ "WeakMap",
15334
+ "WeakSet",
15335
+ "Proxy",
15336
+ "Reflect",
15337
+ "ArrayBuffer",
15338
+ "DataView",
15339
+ "Float32Array",
15340
+ "Float64Array",
15341
+ "Int8Array",
15342
+ "Int16Array",
15343
+ "Int32Array",
15344
+ "Uint8Array",
15345
+ "Uint16Array",
15346
+ "Uint32Array",
15347
+ "Uint8ClampedArray",
15347
15348
  // Functions
15348
- parseInt: 1,
15349
- parseFloat: 1,
15350
- isNaN: 1,
15351
- isFinite: 1,
15352
- encodeURIComponent: 1,
15353
- decodeURIComponent: 1,
15354
- encodeURI: 1,
15355
- decodeURI: 1,
15349
+ "parseInt",
15350
+ "parseFloat",
15351
+ "isNaN",
15352
+ "isFinite",
15353
+ "encodeURIComponent",
15354
+ "decodeURIComponent",
15355
+ "encodeURI",
15356
+ "decodeURI",
15356
15357
  // Babel helpers
15357
- arguments: 1,
15358
- this: 1,
15359
- require: 1,
15358
+ "arguments",
15359
+ "this",
15360
+ "require",
15360
15361
  // Lark framework
15361
- Lark: 1
15362
- };
15363
- var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
15362
+ "Lark"
15363
+ ]);
15364
15364
 
15365
15365
  // src/webpack.ts
15366
15366
  function larkMvcLoader(source) {
package/dist/webpack.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  compileTemplate,
3
3
  extractGlobalVars
4
- } from "./chunk-3HSA7OHB.js";
4
+ } from "./chunk-SIQF4YLC.js";
5
5
 
6
6
  // src/webpack.ts
7
7
  function larkMvcLoader(source) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark.js/mvc",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "description": "@lark.js/mvc - TypeScript MVC framework",
5
5
  "keywords": [
6
6
  "framework",