@xano/xanoscript-language-server 12.16.4 → 12.16.6

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/lexer/db.js CHANGED
@@ -73,6 +73,12 @@ export const PatchToken = createTokenByName("patch", {
73
73
  categories: [Identifier],
74
74
  });
75
75
 
76
+ // increment
77
+ export const IncrementToken = createTokenByName("increment", {
78
+ longer_alt: Identifier,
79
+ categories: [Identifier],
80
+ });
81
+
76
82
  // schema
77
83
  export const SchemaToken = createTokenByName("schema", {
78
84
  longer_alt: Identifier,
@@ -151,6 +157,7 @@ export const DbTokens = [
151
157
  UpdateToken,
152
158
  HasToken,
153
159
  PatchToken,
160
+ IncrementToken,
154
161
  SchemaToken,
155
162
  TransactionToken,
156
163
  TruncateToken,
@@ -186,6 +193,7 @@ export function mapTokenToType(token) {
186
193
  case UpdateToken.name:
187
194
  case HasToken.name:
188
195
  case PatchToken.name:
196
+ case IncrementToken.name:
189
197
  case TransactionToken.name:
190
198
  case TruncateToken.name:
191
199
  case DirectQueryToken.name:
package/lexer/query.js CHANGED
@@ -40,6 +40,12 @@ export const ResponseTypeToken = createTokenByName("response_type", {
40
40
  categories: [Identifier], // could also be an identifier
41
41
  });
42
42
 
43
+ // external_access
44
+ export const ExternalAccessToken = createTokenByName("external_access", {
45
+ longer_alt: Identifier,
46
+ categories: [Identifier], // could also be an identifier
47
+ });
48
+
43
49
  // dblink
44
50
  export const DbLinkToken = createTokenByName("dblink", {
45
51
  longer_alt: Identifier,
@@ -60,6 +66,7 @@ export const AddonToken = createTokenByName("addon", {
60
66
 
61
67
  export const QueryTokens = [
62
68
  ResponseTypeToken,
69
+ ExternalAccessToken,
63
70
  QueryToken,
64
71
  VerbToken,
65
72
  GETToken,
@@ -81,6 +88,7 @@ export function mapTokenToType(token) {
81
88
  case QueryToken.name:
82
89
  case VerbToken.name:
83
90
  case ResponseTypeToken.name:
91
+ case ExternalAccessToken.name:
84
92
  return "keyword";
85
93
  case GETToken.name:
86
94
  case PATCHToken.name:
@@ -95,7 +95,7 @@ function getWorkspaceCompletions(prefix) {
95
95
 
96
96
  // db.* table name context
97
97
  if (
98
- /\b(get|add|edit|delete|query|patch|has|truncate|add_or_edit|schema)\s+("?[^"\s{]*$)/.test(
98
+ /\b(get|add|edit|delete|query|patch|increment|has|truncate|add_or_edit|schema)\s+("?[^"\s{]*$)/.test(
99
99
  prefix
100
100
  )
101
101
  ) {
@@ -11,6 +11,7 @@ const DB_OPERATION_TOKENS = new Set([
11
11
  "delete",
12
12
  "query",
13
13
  "patch",
14
+ "increment",
14
15
  "has",
15
16
  "truncate",
16
17
  "add_or_edit",
@@ -928,6 +928,20 @@ db.patch user {
928
928
 
929
929
  Updates specific fields of an existing record in a database table (e.g., `user`) identified by a field (e.g., `email`) and its value (e.g., `$input.email`). The `data` block specifies the fields to update, and the modified record is stored in `$updated_user`.
930
930
 
931
+ # db.increment
932
+
933
+ ```xs
934
+ db.increment post {
935
+ where = $db.post.id == $input.id
936
+ field_name = "view_count"
937
+ value = 1
938
+ return = {type: "list"}
939
+ output = ["id", "view_count"]
940
+ } as $updated
941
+ ```
942
+
943
+ Atomically adds `value` to the numeric column `field_name` (e.g., `view_count`) on every record of a database table (e.g., `post`) matching the `where` clause. A negative `value` (e.g., `-1`, or `$input.qty|multiply:-1`) decrements. `return` selects `list` (default, the updated records) or `count` (the number of updated records), `output` limits the returned columns, and the result is stored in `$updated`.
944
+
931
945
  # db.get
932
946
 
933
947
  ```xs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xano/xanoscript-language-server",
3
- "version": "12.16.4",
3
+ "version": "12.16.6",
4
4
  "description": "Language Server Protocol implementation for XanoScript",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -0,0 +1,71 @@
1
+ import { IncrementToken } from "../../../lexer/db.js";
2
+ import { StringLiteral } from "../../../lexer/literal.js";
3
+ import { Identifier } from "../../../lexer/tokens.js";
4
+ import { getVarName } from "../../generic/utils.js";
5
+ import { captureFieldName } from "./captureFieldName.js";
6
+ import { addonSchema } from "./schema.js";
7
+
8
+ /**
9
+ * db.increment <table> { where, field_name, value, ... } as $var
10
+ *
11
+ * Atomically adds `value` (negative decrements) to the numeric column
12
+ * `field_name` on every row matching `where`. The key set must match the
13
+ * server-side kind schema exactly, or the editor reports false diagnostics.
14
+ *
15
+ * @param {import('../../base_parser.js').XanoBaseParser} $
16
+ */
17
+ export function dbIncrementFn($) {
18
+ return () => {
19
+ $.sectionStack.push("dbIncrementFn");
20
+ const fnToken = $.CONSUME(IncrementToken); // "increment"
21
+ const nameToken = $.OR({
22
+ DEF: [
23
+ { ALT: () => $.CONSUME(Identifier) },
24
+ { ALT: () => $.CONSUME1(StringLiteral) },
25
+ ],
26
+ ERR_MSG: "Expected a table name",
27
+ });
28
+ $.addReference("table", getVarName(nameToken), nameToken);
29
+
30
+ const captured = {};
31
+ $.SUBRULE($.schemaParseAttributeFn, {
32
+ ARGS: [
33
+ fnToken,
34
+ {
35
+ "description?": "[string]",
36
+ "disabled?": "[boolean]",
37
+ where: "[query]",
38
+ field_name: "[expression]",
39
+ value: "[expression]",
40
+ "return?": {
41
+ "type?": ["list", "count"],
42
+ },
43
+ "output?": ["[string]"],
44
+ "addon?": [addonSchema],
45
+ "mock?": { "![string]": "[expression]" },
46
+ },
47
+ captured,
48
+ ],
49
+ });
50
+
51
+ $.ACTION(() => {
52
+ captureFieldName($, captured);
53
+ // Extract addon name references (mirrors db.query)
54
+ if (captured.addon?.value && Array.isArray(captured.addon.value)) {
55
+ for (const item of captured.addon.value) {
56
+ const nameEntry = item.name;
57
+ if (nameEntry?.value) {
58
+ $.addReference(
59
+ "function",
60
+ getVarName(nameEntry.value),
61
+ nameEntry.value
62
+ );
63
+ }
64
+ }
65
+ }
66
+ });
67
+
68
+ $.SUBRULE($.asVariable, { ARGS: [fnToken] });
69
+ $.sectionStack.pop();
70
+ };
71
+ }
@@ -13,6 +13,7 @@ import { dbExternalDirectQueryFn } from "./dbExternalDirectQueryFn.js";
13
13
  import { dbExternalFn } from "./dbExternalFn.js";
14
14
  import { dbGetFn } from "./dbGetFn.js";
15
15
  import { dbHasFn } from "./dbHasFn.js";
16
+ import { dbIncrementFn } from "./dbIncrementFn.js";
16
17
  import { dbPatchFn } from "./dbPatchFn.js";
17
18
  import { dbQueryFn } from "./dbQueryFn.js";
18
19
  import { dbSchemaFn } from "./dbSchemaFn.js";
@@ -41,6 +42,7 @@ export const register = ($) => {
41
42
  $.dbExternalFn = $.RULE("dbExternalFn", dbExternalFn($));
42
43
  $.dbGetFn = $.RULE("dbGetFn", dbGetFn($));
43
44
  $.dbHasFn = $.RULE("dbHasnpmFn", dbHasFn($));
45
+ $.dbIncrementFn = $.RULE("dbIncrementFn", dbIncrementFn($));
44
46
  $.dbPatchFn = $.RULE("dbPatchFn", dbPatchFn($));
45
47
  $.dbQueryFn = $.RULE("dbQueryFn", dbQueryFn($));
46
48
  $.dbSchemaFn = $.RULE("dbSchemaFn", dbSchemaFn($));
@@ -23,6 +23,7 @@ export function dbFn($) {
23
23
  { ALT: () => $.SUBRULE($.dbTruncateFn) }, // "db.truncate"
24
24
  { ALT: () => $.SUBRULE($.dbTransactionFn) }, // "db.transaction"
25
25
  { ALT: () => $.SUBRULE($.dbPatchFn) }, // "db.patch"
26
+ { ALT: () => $.SUBRULE($.dbIncrementFn) }, // "db.increment"
26
27
  { ALT: () => $.SUBRULE($.dbBulkFn) }, // "db.bulk"
27
28
  { ALT: () => $.SUBRULE($.dbAddOrEditFn) }, // db.add_or_edit
28
29
  ]);
@@ -4,6 +4,7 @@ import { EqualToken, LCurly, RCurly } from "../lexer/control.js";
4
4
  import { StringLiteral } from "../lexer/literal.js";
5
5
  import {
6
6
  DELETEToken,
7
+ ExternalAccessToken,
7
8
  GETToken,
8
9
  PATCHToken,
9
10
  POSTToken,
@@ -12,7 +13,13 @@ import {
12
13
  ResponseTypeToken,
13
14
  VerbToken,
14
15
  } from "../lexer/query.js";
15
- import { AuthToken, Identifier, NewlineToken } from "../lexer/tokens.js";
16
+ import {
17
+ AuthToken,
18
+ FalseToken,
19
+ Identifier,
20
+ NewlineToken,
21
+ TrueToken,
22
+ } from "../lexer/tokens.js";
16
23
  import { getVarName } from "./generic/utils.js";
17
24
 
18
25
  export function queryDeclaration($) {
@@ -30,6 +37,7 @@ export function queryDeclaration($) {
30
37
  let hasTags = false;
31
38
  let hasApiGroup = false;
32
39
  let hasResponseType = false;
40
+ let hasExternalAccess = false;
33
41
  let responseType = "standard";
34
42
 
35
43
  const testNames = [];
@@ -120,6 +128,18 @@ export function queryDeclaration($) {
120
128
  $.CONSUME3(StringLiteral);
121
129
  },
122
130
  },
131
+ {
132
+ GATE: () => !hasExternalAccess,
133
+ ALT: () => {
134
+ hasExternalAccess = true;
135
+ $.CONSUME(ExternalAccessToken); // "external_access"
136
+ $.CONSUME3(EqualToken); // "="
137
+ $.OR3([
138
+ { ALT: () => $.CONSUME(TrueToken) }, // true
139
+ { ALT: () => $.CONSUME(FalseToken) }, // false
140
+ ]);
141
+ },
142
+ },
123
143
  {
124
144
  GATE: () => !hasDocs,
125
145
  ALT: () => {