@plurnk/plurnk-mimetypes-application-json 0.1.0 → 0.2.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ApplicationJson.d.ts","sourceRoot":"","sources":["../src/ApplicationJson.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAmB3D,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,WAAW;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI/B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE;CAoBzC"}
1
+ {"version":3,"file":"ApplicationJson.d.ts","sourceRoot":"","sources":["../src/ApplicationJson.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAe3D,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,WAAW;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAe/B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE;CAazC"}
@@ -1,64 +1,87 @@
1
1
  import { BaseHandler } from "@plurnk/plurnk-mimetypes";
2
- // application/json handler. Two responsibilities beyond BaseHandler defaults:
2
+ import { parseTree, printParseErrorCode } from "jsonc-parser";
3
+ // One class serves two mimetype names — application/json (strict) and
4
+ // application/jsonc (comments + trailing commas allowed). The framework
5
+ // constructs one instance per registered name; this.mimetype distinguishes
6
+ // them at runtime.
3
7
  //
4
- // 1. validate(content) throws on malformed JSON. Per framework error policy
5
- // (SPEC §7), validate errors propagate through Mimetypes.process to the
6
- // caller — this is the contract for "the consumer asserted this is JSON,
7
- // but it isn't."
8
+ // validate(): strict for application/json (no comments, no trailing commas);
9
+ // permissive for application/jsonc. Errors propagate per SPEC §7.
8
10
  //
9
- // 2. extract(content) returns the document's top-level keys as `field`
10
- // symbols. Matches the legacy ANTLR-backed policy: a JSON document's
11
- // "API surface" is its top-level keys; nested structure is recursive
12
- // and out of scope for the structural outline. Array and scalar roots
13
- // have no named top-level keys → empty Symbol[].
14
- //
15
- // Line numbers are derived from a single-pass scan of the raw source — JSON.parse
16
- // itself doesn't preserve positions. The scan is approximate (a `"key":` pattern
17
- // inside a string value could in principle be mis-matched as a key) but accurate
18
- // for well-formed documents in practical use.
11
+ // extract(): every key occurrence at every depth as a `field` symbol, with
12
+ // line numbers from jsonc-parser's positional tree (Node.offset).
13
+ // No regex tokenization, no escape-handling reinvention the
14
+ // parser does it.
19
15
  export default class ApplicationJson extends BaseHandler {
20
16
  validate(content) {
21
- JSON.parse(content);
17
+ const errors = [];
18
+ const allowsRelaxation = this.mimetype === "application/jsonc";
19
+ parseTree(content, errors, {
20
+ allowTrailingComma: allowsRelaxation,
21
+ disallowComments: !allowsRelaxation,
22
+ });
23
+ if (errors.length === 0)
24
+ return;
25
+ const first = errors[0];
26
+ const { line, column } = offsetToLineCol(content, first.offset);
27
+ throw new SyntaxError(`${printParseErrorCode(first.error)} at line ${line}:${column}`);
22
28
  }
23
29
  extract(content) {
24
- let parsed;
25
- try {
26
- parsed = JSON.parse(content);
27
- }
28
- catch {
30
+ const errors = [];
31
+ const allowsRelaxation = this.mimetype === "application/jsonc";
32
+ const tree = parseTree(content, errors, {
33
+ allowTrailingComma: allowsRelaxation,
34
+ disallowComments: !allowsRelaxation,
35
+ });
36
+ if (tree === undefined)
29
37
  return [];
38
+ const symbols = [];
39
+ collectKeys(tree, content, symbols);
40
+ return symbols;
41
+ }
42
+ }
43
+ // Walk a jsonc-parser Node tree and emit a field symbol for every property
44
+ // key encountered at every depth. Each property node has a `children` pair:
45
+ // [keyNode, valueNode]. The keyNode's offset gives the source position.
46
+ function collectKeys(node, content, into) {
47
+ if (node.type === "property" && node.children && node.children.length >= 2) {
48
+ const keyNode = node.children[0];
49
+ if (keyNode.type === "string" && typeof keyNode.value === "string") {
50
+ const line = offsetToLineCol(content, keyNode.offset).line;
51
+ into.push({
52
+ name: keyNode.value,
53
+ kind: "field",
54
+ line,
55
+ endLine: line,
56
+ });
30
57
  }
31
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
32
- return [];
58
+ // Recurse into the value to find nested keys.
59
+ const valueNode = node.children[1];
60
+ if (valueNode)
61
+ collectKeys(valueNode, content, into);
62
+ return;
63
+ }
64
+ // Objects, arrays, and the root all recurse through children.
65
+ if (node.children) {
66
+ for (const child of node.children) {
67
+ collectKeys(child, content, into);
33
68
  }
34
- const keys = Object.keys(parsed);
35
- const lineMap = mapKeysToLines(content, keys);
36
- return keys.map((name) => {
37
- const line = lineMap.get(name) ?? 1;
38
- return { name, kind: "field", line, endLine: line };
39
- });
40
69
  }
41
70
  }
42
- // Scan content for the first occurrence of each `"key":` pattern (key followed
43
- // by optional whitespace and a colon, signaling a key position rather than a
44
- // value). Returns key → 1-indexed line number.
45
- function mapKeysToLines(content, keys) {
46
- const result = new Map();
47
- const remaining = new Set(keys);
48
- if (remaining.size === 0)
49
- return result;
50
- const lines = content.split("\n");
51
- for (let i = 0; i < lines.length && remaining.size > 0; i += 1) {
52
- const line = lines[i];
53
- for (const key of remaining) {
54
- const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
55
- const pattern = new RegExp(`"${escaped}"\\s*:`);
56
- if (pattern.test(line)) {
57
- result.set(key, i + 1);
58
- remaining.delete(key);
59
- }
71
+ // Convert a byte offset into 1-indexed line/column.
72
+ function offsetToLineCol(content, offset) {
73
+ let line = 1;
74
+ let column = 1;
75
+ const limit = Math.min(offset, content.length);
76
+ for (let i = 0; i < limit; i += 1) {
77
+ if (content.charCodeAt(i) === 0x0a) {
78
+ line += 1;
79
+ column = 1;
80
+ }
81
+ else {
82
+ column += 1;
60
83
  }
61
84
  }
62
- return result;
85
+ return { line, column };
63
86
  }
64
87
  //# sourceMappingURL=ApplicationJson.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ApplicationJson.js","sourceRoot":"","sources":["../src/ApplicationJson.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,8EAA8E;AAC9E,EAAE;AACF,8EAA8E;AAC9E,6EAA6E;AAC7E,8EAA8E;AAC9E,sBAAsB;AACtB,EAAE;AACF,yEAAyE;AACzE,0EAA0E;AAC1E,0EAA0E;AAC1E,2EAA2E;AAC3E,sDAAsD;AACtD,EAAE;AACF,kFAAkF;AAClF,iFAAiF;AACjF,iFAAiF;AACjF,8CAA8C;AAC9C,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,WAAW;IACpD,QAAQ,CAAC,OAAe;QACpB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;IAED,OAAO,CAAC,OAAe;QACnB,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACD,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACL,OAAO,EAAE,CAAC;QACd,CAAC;QAED,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACzE,OAAO,EAAE,CAAC;QACd,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAiC,CAAC,CAAC;QAC5D,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAE9C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACrB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAgB,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACjE,CAAC,CAAC,CAAC;IACP,CAAC;CACJ;AAED,+EAA+E;AAC/E,6EAA6E;AAC7E,+CAA+C;AAC/C,SAAS,cAAc,CAAC,OAAe,EAAE,IAAc;IACnD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IAExC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,SAAS,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;YAC1B,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;YAC3D,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,IAAI,OAAO,QAAQ,CAAC,CAAC;YAChD,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;gBACvB,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC1B,CAAC;QACL,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"ApplicationJson.js","sourceRoot":"","sources":["../src/ApplicationJson.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,OAAO,EAA8B,SAAS,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAE1F,sEAAsE;AACtE,wEAAwE;AACxE,2EAA2E;AAC3E,mBAAmB;AACnB,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,yEAAyE;AACzE,6BAA6B;AAC7B,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,WAAW;IACpD,QAAQ,CAAC,OAAe;QACpB,MAAM,MAAM,GAAiB,EAAE,CAAC;QAChC,MAAM,gBAAgB,GAAG,IAAI,CAAC,QAAQ,KAAK,mBAAmB,CAAC;QAC/D,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;YACvB,kBAAkB,EAAE,gBAAgB;YACpC,gBAAgB,EAAE,CAAC,gBAAgB;SACtC,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAChE,MAAM,IAAI,WAAW,CACjB,GAAG,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,IAAI,IAAI,MAAM,EAAE,CAClE,CAAC;IACN,CAAC;IAED,OAAO,CAAC,OAAe;QACnB,MAAM,MAAM,GAAiB,EAAE,CAAC;QAChC,MAAM,gBAAgB,GAAG,IAAI,CAAC,QAAQ,KAAK,mBAAmB,CAAC;QAC/D,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;YACpC,kBAAkB,EAAE,gBAAgB;YACpC,gBAAgB,EAAE,CAAC,gBAAgB;SACtC,CAAC,CAAC;QACH,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,EAAE,CAAC;QAElC,MAAM,OAAO,GAAiB,EAAE,CAAC;QACjC,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACpC,OAAO,OAAO,CAAC;IACnB,CAAC;CACJ;AAED,2EAA2E;AAC3E,4EAA4E;AAC5E,wEAAwE;AACxE,SAAS,WAAW,CAAC,IAAU,EAAE,OAAe,EAAE,IAAkB;IAChE,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACzE,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACjC,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACjE,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;YAC3D,IAAI,CAAC,IAAI,CAAC;gBACN,IAAI,EAAE,OAAO,CAAC,KAAK;gBACnB,IAAI,EAAE,OAAO;gBACb,IAAI;gBACJ,OAAO,EAAE,IAAI;aAChB,CAAC,CAAC;QACP,CAAC;QACD,8CAA8C;QAC9C,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACnC,IAAI,SAAS;YAAE,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACrD,OAAO;IACX,CAAC;IAED,8DAA8D;IAC9D,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChC,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACtC,CAAC;IACL,CAAC;AACL,CAAC;AAED,oDAAoD;AACpD,SAAS,eAAe,CAAC,OAAe,EAAE,MAAc;IACpD,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YACjC,IAAI,IAAI,CAAC,CAAC;YACV,MAAM,GAAG,CAAC,CAAC;QACf,CAAC;aAAM,CAAC;YACJ,MAAM,IAAI,CAAC,CAAC;QAChB,CAAC;IACL,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@plurnk/plurnk-mimetypes-application-json",
3
- "version": "0.1.0",
4
- "description": "application/json mimetype handler for plurnk-service.",
3
+ "version": "0.2.0",
4
+ "description": "application/json and application/jsonc mimetype handler for plurnk-service.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "publishConfig": {
@@ -12,9 +12,10 @@
12
12
  },
13
13
  "plurnk": {
14
14
  "kind": "mimetype",
15
- "name": "application/json",
16
- "glyph": "📋",
17
- "extensions": [".json"]
15
+ "handlers": [
16
+ { "name": "application/json", "glyph": "📋", "extensions": [".json"] },
17
+ { "name": "application/jsonc", "glyph": "📋", "extensions": [".jsonc"] }
18
+ ]
18
19
  },
19
20
  "exports": {
20
21
  ".": {
@@ -36,7 +37,8 @@
36
37
  "prepare": "npm run build"
37
38
  },
38
39
  "dependencies": {
39
- "@plurnk/plurnk-mimetypes": "^0.1.1"
40
+ "@plurnk/plurnk-mimetypes": "^0.2.0",
41
+ "jsonc-parser": "^3.3.1"
40
42
  },
41
43
  "devDependencies": {
42
44
  "@types/node": "^25.8.0",