deepline 0.3.6 → 0.3.8

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.
@@ -2670,7 +2670,7 @@ __export(type_exports2, {
2670
2670
  var Type = type_exports2;
2671
2671
 
2672
2672
  // ../shared_libs/plays/authoring-contract.ts
2673
- var PLAY_AUTHORING_CONTRACT_EDITION = 3;
2673
+ var PLAY_AUTHORING_CONTRACT_EDITION = 4;
2674
2674
  var PLAY_SQL_LISTENER_WHERE_OPERATORS = [
2675
2675
  "eq",
2676
2676
  "neq",
@@ -3941,8 +3941,11 @@ var PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
3941
3941
  ` secrets?: readonly ${cloudReferenceType("bindings.secrets[]")}[];`,
3942
3942
  "};",
3943
3943
  "declare const SECRET_HANDLE_BRAND: unique symbol;",
3944
+ "declare const SECRET_PROMISE_BRAND: unique symbol;",
3944
3945
  "export type SecretHandle = { readonly [SECRET_HANDLE_BRAND]: never; readonly name: string; toString(): string; toJSON(): never };",
3945
- "export type SecretAuth = { readonly kind: 'bearer' | 'header'; readonly secret: SecretHandle; readonly header?: string };",
3946
+ "export type SecretPromise = Promise<string> & { readonly [SECRET_PROMISE_BRAND]: never };",
3947
+ "export type SecretValue = SecretHandle;",
3948
+ "export type SecretAuth = { readonly kind: 'bearer' | 'header'; readonly secret: string | SecretPromise | SecretHandle; readonly header?: string };",
3946
3949
  "export type SecretAuthInput = SecretAuth | readonly SecretAuth[];",
3947
3950
  "export type PlayInputContract<TInput> = { readonly schema: Record<string, unknown>; readonly __inputType?: TInput };",
3948
3951
  "export type PlayReturnObject = Record<string, unknown> & { readonly _metadata?: never };",
@@ -3993,7 +3996,7 @@ var PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
3993
3996
  ` tool<K extends string>(key: ${cloudReferenceType("ctx.tool.key")}, toolId: K, input: ${cloudReferenceType("ctx.tool.input")}, options?: { description?: ${cloudReferenceType("ctx.tool.options.description")} }): Promise<ToolExecutionOutput<K>>;`,
3994
3997
  " step<T>(id: string, run: () => T | Promise<T>, options?: RuntimeStepOptions): Promise<T>;",
3995
3998
  " fetch(key: string, url: string | URL, init?: RequestInit & { headers?: HeadersInit & { 'Idempotency-Key'?: string; 'X-Idempotency-Key'?: string }; auth?: SecretAuthInput }, options?: FetchOptions): Promise<PlayFetchResponse>;",
3996
- " secrets: { get(name: string): SecretHandle; bearer(secret: SecretHandle): SecretAuth; header(header: string, secret: SecretHandle): SecretAuth };",
3999
+ " secrets: { get(name: string): SecretPromise; bearer(secret: string | SecretPromise | SecretHandle): SecretAuth; header(header: string, secret: string | SecretPromise | SecretHandle): SecretAuth };",
3997
4000
  ` runPlay<TOutput = unknown>(key: string, playRef: ${cloudReferenceType("ctx.runPlay.playRef")}, input: ${cloudReferenceType("ctx.runPlay.input")}, options: PlayCallOptions): Promise<TOutput>;`,
3998
4001
  " log(message: string): void;",
3999
4002
  ` sleep(ms: ${cloudReferenceType("ctx.sleep.ms")}): Promise<void>;`,
@@ -4192,11 +4195,9 @@ var PLAY_DOCFLOW_ARMS = [
4192
4195
  "else"
4193
4196
  ];
4194
4197
  var FLOW_START = /^\s*(?:flowchart|graph)\s+(LR|RL|TB|TD|BT)\s*$/i;
4195
- var PUT = /^\s*\/\/\s*put\s+(.+)$/;
4196
4198
  var MERMAID_NODE = /^\s*\/\/\s*@mermaid-node\s+([A-Za-z][\w-]*)(?:\s+(.*))?$/;
4197
4199
  var ATTRIBUTE = /([A-Za-z][\w-]*)\s*:\s*"([^"\n]*)"/y;
4198
4200
  var MERMAID_NODE_ATTRIBUTES = ["label", "type", "in", "out", "arm"];
4199
- var LEGACY_DOCFLOW_ATTRIBUTES = ["id", ...MERMAID_NODE_ATTRIBUTES];
4200
4201
  var MERMAID_NODE_ID = /^[A-Za-z][\w-]*/;
4201
4202
  var SKETCH_CLASS = /^\s*class\s+([A-Za-z][\w-]*(?:\s*,\s*[A-Za-z][\w-]*)*)\s+sketch\s*;?\s*$/;
4202
4203
  var MERMAID_NODE_SHAPES = [
@@ -4216,7 +4217,6 @@ var MERMAID_NODE_SHAPES = [
4216
4217
  ];
4217
4218
  var SUBGRAPH_OPEN = /^\s*subgraph\s+([A-Za-z][\w-]*)\s*(?:\[(.*)\])?\s*$/i;
4218
4219
  var SUBGRAPH_END = /^\s*end\s*$/i;
4219
- var DOCFLOW_EDGE_CONNECTOR = /^\s*-->(?:\|([^|]+)\|)?/;
4220
4220
  var MERMAID_EDGE_CONNECTOR = /^\s*(?:-->|-\.->|==>|---)(?:\|([^|]+)\|)?/;
4221
4221
  var DOCFLOW_PATH = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/;
4222
4222
  var IDENTIFIER = /[A-Za-z_$][\w$]*/g;
@@ -4235,9 +4235,9 @@ var JS_WORDS = /* @__PURE__ */ new Set([
4235
4235
  "undefined",
4236
4236
  "var"
4237
4237
  ]);
4238
- function parseAttributes(source, input) {
4238
+ function parseAttributes(source, line, errors) {
4239
4239
  const attributes = {};
4240
- const validAttributes = input.annotation === "@mermaid-node" ? MERMAID_NODE_ATTRIBUTES : LEGACY_DOCFLOW_ATTRIBUTES;
4240
+ const validAttributes = MERMAID_NODE_ATTRIBUTES;
4241
4241
  let cursor = 0;
4242
4242
  while (cursor < source.length) {
4243
4243
  cursor += /^\s*/.exec(source.slice(cursor))?.[0].length ?? 0;
@@ -4246,21 +4246,21 @@ function parseAttributes(source, input) {
4246
4246
  const match = ATTRIBUTE.exec(source);
4247
4247
  if (!match) {
4248
4248
  const fragment = source.slice(cursor).split(/\s+/)[0] ?? source.slice(cursor);
4249
- input.errors.push(
4250
- `Docflow annotation on line ${input.line} has malformed attribute ${JSON.stringify(fragment)}. Use key:"value" pairs. Valid attributes: ${validAttributes.map((name2) => `"${name2}"`).join(", ")}.`
4249
+ errors.push(
4250
+ `Docflow \`// @mermaid-node\` annotation on line ${line} has malformed attribute ${JSON.stringify(fragment)}. Use key:"value" pairs. Valid attributes: ${validAttributes.map((name2) => `"${name2}"`).join(", ")}.`
4251
4251
  );
4252
4252
  return null;
4253
4253
  }
4254
4254
  const name = match[1];
4255
4255
  if (!validAttributes.includes(name)) {
4256
- input.errors.push(
4257
- `Docflow annotation on line ${input.line} has unknown attribute "${name}". Valid attributes: ${validAttributes.map((option) => `"${option}"`).join(", ")}.`
4256
+ errors.push(
4257
+ `Docflow \`// @mermaid-node\` annotation on line ${line} has unknown attribute "${name}". Valid attributes: ${validAttributes.map((option) => `"${option}"`).join(", ")}.`
4258
4258
  );
4259
4259
  return null;
4260
4260
  }
4261
4261
  if (Object.prototype.hasOwnProperty.call(attributes, name)) {
4262
- input.errors.push(
4263
- `Docflow annotation on line ${input.line} repeats attribute "${name}". Write each attribute once.`
4262
+ errors.push(
4263
+ `Docflow \`// @mermaid-node\` annotation on line ${line} repeats attribute "${name}". Write each attribute once.`
4264
4264
  );
4265
4265
  return null;
4266
4266
  }
@@ -4349,7 +4349,7 @@ function parseShapedMermaidNodes(fragment) {
4349
4349
  }
4350
4350
  return nodes;
4351
4351
  }
4352
- function parseEdgeChain(line, syntax) {
4352
+ function parseEdgeChain(line) {
4353
4353
  let remaining = line;
4354
4354
  const first = parseMermaidNodeAtStart(remaining);
4355
4355
  if (!first) return null;
@@ -4357,7 +4357,7 @@ function parseEdgeChain(line, syntax) {
4357
4357
  const labels = [];
4358
4358
  remaining = remaining.slice(first.length);
4359
4359
  while (remaining.trim()) {
4360
- const connector = (syntax === "mermaid" ? MERMAID_EDGE_CONNECTOR : DOCFLOW_EDGE_CONNECTOR).exec(remaining);
4360
+ const connector = MERMAID_EDGE_CONNECTOR.exec(remaining);
4361
4361
  if (!connector) return null;
4362
4362
  remaining = remaining.slice(connector[0].length);
4363
4363
  const next = parseMermaidNodeAtStart(remaining);
@@ -4423,7 +4423,6 @@ function inferBindingIo(statement) {
4423
4423
  };
4424
4424
  }
4425
4425
  var MERMAID_BLOCK = /\/\*\*\s*@mermaid(?:\s|\r?\n)([\s\S]*?)\*\//g;
4426
- var LEGACY_BLOCK = /\/\*\*\s*@docflow(?:\s|\r?\n)([\s\S]*?)\*\//;
4427
4426
  var BLOCK_EXPORT_HEADER = /^[A-Za-z_$][\w$-]*$/;
4428
4427
  function cleanBlockBody(raw) {
4429
4428
  return raw.split(/\r?\n/).map((line) => /^\s*\*/.test(line) ? line.replace(/^\s*\* ?/, "") : line).join("\n").trim();
@@ -4440,10 +4439,9 @@ function splitBlockExportHeader(body) {
4440
4439
  };
4441
4440
  }
4442
4441
  function blockLabel(block) {
4443
- const tag = block.syntax === "mermaid" ? "@mermaid" : "@docflow";
4444
- return block.authoredExportName ? `${tag} ${block.authoredExportName}` : tag;
4442
+ return block.authoredExportName ? `@mermaid ${block.authoredExportName}` : "@mermaid";
4445
4443
  }
4446
- function parseDocflowBlockGraph(mermaidSource, syntax, errors) {
4444
+ function parseDocflowBlockGraph(mermaidSource, errors) {
4447
4445
  const docLines = mermaidSource.split(/\r?\n/).filter((line) => line.trim() && !line.trim().startsWith("%%"));
4448
4446
  const direction = FLOW_START.exec(docLines[0] ?? "")?.[1]?.toUpperCase();
4449
4447
  if (!direction) {
@@ -4459,11 +4457,9 @@ function parseDocflowBlockGraph(mermaidSource, syntax, errors) {
4459
4457
  const sketchIds = /* @__PURE__ */ new Set();
4460
4458
  const subgraphStack = [];
4461
4459
  const subgraphIds = /* @__PURE__ */ new Set();
4462
- if (syntax === "mermaid") {
4463
- for (const line of docLines.slice(1)) {
4464
- const opened = SUBGRAPH_OPEN.exec(line);
4465
- if (opened) subgraphIds.add(opened[1]);
4466
- }
4460
+ for (const line of docLines.slice(1)) {
4461
+ const opened = SUBGRAPH_OPEN.exec(line);
4462
+ if (opened) subgraphIds.add(opened[1]);
4467
4463
  }
4468
4464
  const declaredIds = /* @__PURE__ */ new Set();
4469
4465
  const addNodes = (fragment, line) => {
@@ -4505,34 +4501,32 @@ function parseDocflowBlockGraph(mermaidSource, syntax, errors) {
4505
4501
  return sawDeclaration;
4506
4502
  };
4507
4503
  for (const line of docLines.slice(1)) {
4508
- if (syntax === "mermaid") {
4509
- const opened = SUBGRAPH_OPEN.exec(line);
4510
- if (opened) {
4511
- const id = opened[1];
4512
- const rawLabel = opened[2]?.trim() ?? "";
4513
- const label = rawLabel.length >= 2 && (rawLabel.startsWith('"') && rawLabel.endsWith('"') || rawLabel.startsWith("'") && rawLabel.endsWith("'")) ? rawLabel.slice(1, -1) : rawLabel;
4514
- const subgraph = {
4515
- id,
4516
- label: label || id,
4517
- memberIds: []
4518
- };
4519
- subgraphs.set(id, subgraph);
4520
- subgraphStack.push(subgraph);
4521
- continue;
4522
- }
4523
- if (SUBGRAPH_END.test(line)) {
4524
- if (subgraphStack.length === 0) {
4525
- errors.push(
4526
- "Docflow has an `end` with no open `subgraph` above it. Every `end` closes exactly one `subgraph`."
4527
- );
4528
- }
4529
- subgraphStack.pop();
4530
- continue;
4504
+ const opened = SUBGRAPH_OPEN.exec(line);
4505
+ if (opened) {
4506
+ const id = opened[1];
4507
+ const rawLabel = opened[2]?.trim() ?? "";
4508
+ const label = rawLabel.length >= 2 && (rawLabel.startsWith('"') && rawLabel.endsWith('"') || rawLabel.startsWith("'") && rawLabel.endsWith("'")) ? rawLabel.slice(1, -1) : rawLabel;
4509
+ const subgraph = {
4510
+ id,
4511
+ label: label || id,
4512
+ memberIds: []
4513
+ };
4514
+ subgraphs.set(id, subgraph);
4515
+ subgraphStack.push(subgraph);
4516
+ continue;
4517
+ }
4518
+ if (SUBGRAPH_END.test(line)) {
4519
+ if (subgraphStack.length === 0) {
4520
+ errors.push(
4521
+ "Docflow has an `end` with no open `subgraph` above it. Every `end` closes exactly one `subgraph`."
4522
+ );
4531
4523
  }
4524
+ subgraphStack.pop();
4525
+ continue;
4532
4526
  }
4533
4527
  let declaredOnThisLine = false;
4534
4528
  const isDirective = /^\s*(?:direction|classDef|class|style|linkStyle|click)\b/i.test(line);
4535
- if (syntax === "mermaid" && isDirective) {
4529
+ if (isDirective) {
4536
4530
  const sketch = SKETCH_CLASS.exec(line);
4537
4531
  if (sketch) {
4538
4532
  for (const id of sketch[1].split(",")) {
@@ -4543,21 +4537,19 @@ function parseDocflowBlockGraph(mermaidSource, syntax, errors) {
4543
4537
  ignoredDirectives.push(line.trim());
4544
4538
  }
4545
4539
  }
4546
- if (syntax === "mermaid" && !isDirective) {
4540
+ if (!isDirective) {
4547
4541
  declaredOnThisLine = addNodes(line, line);
4548
4542
  }
4549
- const chain = parseEdgeChain(line, syntax);
4543
+ const chain = parseEdgeChain(line);
4550
4544
  if (!chain) {
4551
- if (syntax === "docflow") {
4552
- errors.push(`Unsupported docflow line: ${line.trim()}`);
4553
- } else if (!isDirective && !declaredOnThisLine) {
4545
+ if (!isDirective && !declaredOnThisLine) {
4554
4546
  errors.push(
4555
4547
  `Docflow line is not an edge, a node declaration, or a directive this parser understands: ${line.trim()}`
4556
4548
  );
4557
4549
  }
4558
4550
  continue;
4559
4551
  }
4560
- if (syntax === "docflow") addNodes(line, line);
4552
+ addNodes(line, line);
4561
4553
  for (const node of chain.nodes) {
4562
4554
  if (subgraphIds.has(node.id)) continue;
4563
4555
  const existing = nodes.get(node.id);
@@ -4616,24 +4608,18 @@ function materializeDocflow(block) {
4616
4608
  }
4617
4609
  function parsePlayDocflowFile(sourceCode) {
4618
4610
  const errors = [];
4619
- const rejectedNodeIds = /* @__PURE__ */ new Set();
4620
- const bindings = parseDocflowBindings(sourceCode, errors, rejectedNodeIds);
4621
4611
  const rawBlocks = [];
4622
4612
  MERMAID_BLOCK.lastIndex = 0;
4623
4613
  for (const match of sourceCode.matchAll(MERMAID_BLOCK)) {
4624
4614
  rawBlocks.push({ syntax: "mermaid", body: cleanBlockBody(match[1]) });
4625
4615
  }
4626
- if (rawBlocks.length === 0) {
4627
- const legacy = LEGACY_BLOCK.exec(sourceCode);
4628
- if (legacy) {
4629
- rawBlocks.push({ syntax: "docflow", body: cleanBlockBody(legacy[1]) });
4630
- }
4631
- }
4632
- if (rawBlocks.length === 0) return { blocks: [], bindings, errors };
4616
+ if (rawBlocks.length === 0) return { blocks: [], bindings: [], errors };
4617
+ const rejectedNodeIds = /* @__PURE__ */ new Set();
4618
+ const bindings = parseDocflowBindings(sourceCode, errors, rejectedNodeIds);
4633
4619
  const parsedBlocks = [];
4634
4620
  for (const raw of rawBlocks) {
4635
4621
  const { exportName, diagram } = splitBlockExportHeader(raw.body);
4636
- const graph = parseDocflowBlockGraph(diagram, raw.syntax, errors);
4622
+ const graph = parseDocflowBlockGraph(diagram, errors);
4637
4623
  if (!graph) continue;
4638
4624
  parsedBlocks.push({
4639
4625
  ...graph,
@@ -4853,7 +4839,7 @@ function attachBindingsToBlocks(sourceCode, blocks, bindings, errors, rejectedNo
4853
4839
  if (node.sketch || node.kind === "conceptual" || bound.has(node.id) || rejectedNodeIds.has(node.id))
4854
4840
  continue;
4855
4841
  errors.push(
4856
- block.syntax === "docflow" ? `Docflow node "${node.id}" has no code binding; mark it type:"conceptual" or bind it.` : `Docflow box "${node.id}" in \`${blockLabel(block)}\` points at nothing, so the canvas can only draw it and say nothing about it. Either put \`// @mermaid-node ${node.id}\` above the statement it runs, or \u2014 if this play has no such statement, because the work happens in another module or inside a loop \u2014 add it to a \`class ${node.id} sketch\` line in the block to declare it a sketch.`
4842
+ `Docflow box "${node.id}" in \`${blockLabel(block)}\` points at nothing, so the canvas can only draw it and say nothing about it. Either put \`// @mermaid-node ${node.id}\` above the statement it runs, or \u2014 if this play has no such statement, because the work happens in another module or inside a loop \u2014 add it to a \`class ${node.id} sketch\` line in the block to declare it a sketch.`
4857
4843
  );
4858
4844
  }
4859
4845
  }
@@ -4869,20 +4855,15 @@ function parseDocflowBindings(sourceCode, errors, rejectedNodeIds) {
4869
4855
  const lines = sourceCode.split(/\r?\n/);
4870
4856
  const bindings = [];
4871
4857
  for (let index = 0; index < lines.length; index += 1) {
4872
- PUT.lastIndex = 0;
4873
4858
  MERMAID_NODE.lastIndex = 0;
4874
- const legacyMatch = PUT.exec(lines[index]);
4875
- const mermaidMatch = MERMAID_NODE.exec(lines[index]);
4876
- if (!legacyMatch && !mermaidMatch) continue;
4877
- const annotatedId = mermaidMatch?.[1]?.trim();
4859
+ const mermaidMatch = MERMAID_NODE.exec(lines[index]) ?? null;
4860
+ if (!mermaidMatch) continue;
4861
+ const annotatedId = mermaidMatch[1]?.trim();
4878
4862
  if (annotatedId) rejectedNodeIds.add(annotatedId);
4879
4863
  const attributes = parseAttributes(
4880
- mermaidMatch ? mermaidMatch[2] ?? "" : legacyMatch[1],
4881
- {
4882
- line: index + 1,
4883
- annotation: mermaidMatch ? "@mermaid-node" : "put",
4884
- errors
4885
- }
4864
+ mermaidMatch[2] ?? "",
4865
+ index + 1,
4866
+ errors
4886
4867
  );
4887
4868
  if (!attributes) continue;
4888
4869
  const id = (annotatedId ?? attributes.id)?.trim();
@@ -5210,6 +5191,113 @@ var SECRET_ENV_PATTERN = /\bprocess(?:\.env|\[['"]env['"]\])(?:\.|\[['"])([A-Z0-
5210
5191
  var PRIVATE_KEY_PATTERN = /-----BEGIN (?:RSA |EC |OPENSSH |PGP )?PRIVATE KEY-----/;
5211
5192
  var BEARER_LITERAL_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}/i;
5212
5193
  var ASSIGNMENT_SECRET_LITERAL_PATTERN = /\b(?:api[_-]?key|token|secret|password)\b\s*[:=]\s*['"][^'"]{12,}['"]/i;
5194
+ var SECRET_AUTH_LITERAL_FINDING = "literal credential passed to ctx.secrets auth helper";
5195
+ function skipQuoted(source, start) {
5196
+ const quote = source[start];
5197
+ let index = start + 1;
5198
+ while (index < source.length) {
5199
+ if (source[index] === "\\") {
5200
+ index += 2;
5201
+ } else if (source[index] === quote) {
5202
+ return index + 1;
5203
+ } else {
5204
+ index += 1;
5205
+ }
5206
+ }
5207
+ return source.length;
5208
+ }
5209
+ function skipTrivia(source, start) {
5210
+ let index = start;
5211
+ while (index < source.length) {
5212
+ if (/\s/.test(source[index])) {
5213
+ index += 1;
5214
+ } else if (source.startsWith("//", index)) {
5215
+ index = source.indexOf("\n", index + 2);
5216
+ if (index === -1) return source.length;
5217
+ } else if (source.startsWith("/*", index)) {
5218
+ index = source.indexOf("*/", index + 2);
5219
+ if (index === -1) return source.length;
5220
+ index += 2;
5221
+ } else {
5222
+ return index;
5223
+ }
5224
+ }
5225
+ return index;
5226
+ }
5227
+ function consumeIdentifier(source, start, expected) {
5228
+ return source.startsWith(expected, start) && !/[A-Za-z0-9_$]/.test(source[start - 1] ?? "") && !/[A-Za-z0-9_$]/.test(source[start + expected.length] ?? "") ? start + expected.length : void 0;
5229
+ }
5230
+ function consumeMember(source, start, expected) {
5231
+ let index = skipTrivia(source, start);
5232
+ if (source[index] === ".") {
5233
+ return consumeIdentifier(source, skipTrivia(source, index + 1), expected);
5234
+ }
5235
+ if (source[index] !== "[") return void 0;
5236
+ index = skipTrivia(source, index + 1);
5237
+ if (source[index] !== "'" && source[index] !== '"') return void 0;
5238
+ const end = skipQuoted(source, index);
5239
+ if (source.slice(index + 1, end - 1) !== expected) return void 0;
5240
+ index = skipTrivia(source, end);
5241
+ return source[index] === "]" ? index + 1 : void 0;
5242
+ }
5243
+ function skipExpression(source, start) {
5244
+ let index = start;
5245
+ let depth = 0;
5246
+ while (index < source.length) {
5247
+ const char = source[index];
5248
+ if (char === "'" || char === '"' || char === "`") {
5249
+ index = skipQuoted(source, index);
5250
+ continue;
5251
+ }
5252
+ if (source.startsWith("//", index) || source.startsWith("/*", index)) {
5253
+ index = skipTrivia(source, index);
5254
+ continue;
5255
+ }
5256
+ if (char === "(" || char === "[" || char === "{") {
5257
+ depth += 1;
5258
+ } else if (char === ")" || char === "]" || char === "}") {
5259
+ if (depth === 0) return index;
5260
+ depth -= 1;
5261
+ } else if (char === "," && depth === 0) {
5262
+ return index;
5263
+ }
5264
+ index += 1;
5265
+ }
5266
+ return index;
5267
+ }
5268
+ function isDirectStringLiteral(source, start) {
5269
+ const quote = source[start];
5270
+ if (quote !== "'" && quote !== '"' && quote !== "`") return false;
5271
+ const end = skipQuoted(source, start);
5272
+ return quote !== "`" || !source.slice(start, end).includes("${");
5273
+ }
5274
+ function hasLiteralSecretAuthCredential(source) {
5275
+ for (let index = 0; index < source.length; index += 1) {
5276
+ const char = source[index];
5277
+ if (char === "'" || char === '"' || char === "`") {
5278
+ index = skipQuoted(source, index) - 1;
5279
+ continue;
5280
+ }
5281
+ if (source.startsWith("//", index) || source.startsWith("/*", index)) {
5282
+ index = skipTrivia(source, index) - 1;
5283
+ continue;
5284
+ }
5285
+ const afterCtx = consumeIdentifier(source, index, "ctx");
5286
+ if (!afterCtx) continue;
5287
+ const afterSecrets = consumeMember(source, afterCtx, "secrets");
5288
+ if (!afterSecrets) continue;
5289
+ const afterHeader = consumeMember(source, afterSecrets, "header");
5290
+ const helper = afterHeader ? "header" : "bearer";
5291
+ const afterHelper = afterHeader ?? consumeMember(source, afterSecrets, "bearer");
5292
+ if (!afterHelper) continue;
5293
+ const afterOpen = skipTrivia(source, afterHelper);
5294
+ if (source[afterOpen] !== "(") continue;
5295
+ const firstArgument = skipTrivia(source, afterOpen + 1);
5296
+ const credential = helper === "header" ? skipTrivia(source, skipExpression(source, firstArgument) + 1) : firstArgument;
5297
+ if (isDirectStringLiteral(source, credential)) return true;
5298
+ }
5299
+ return false;
5300
+ }
5213
5301
  function collectInlineSecretFindings(sourceCode) {
5214
5302
  const findings = [];
5215
5303
  for (const match of sourceCode.matchAll(SECRET_ENV_PATTERN)) {
@@ -5221,6 +5309,9 @@ function collectInlineSecretFindings(sourceCode) {
5221
5309
  if (ASSIGNMENT_SECRET_LITERAL_PATTERN.test(sourceCode)) {
5222
5310
  findings.push("secret-looking assignment literal");
5223
5311
  }
5312
+ if (hasLiteralSecretAuthCredential(sourceCode)) {
5313
+ findings.push(SECRET_AUTH_LITERAL_FINDING);
5314
+ }
5224
5315
  return [...new Set(findings)];
5225
5316
  }
5226
5317
  function validatePlaySourceHasNoInlineSecrets(input) {
@@ -5231,7 +5322,7 @@ function validatePlaySourceHasNoInlineSecrets(input) {
5231
5322
  `Play source ${input.filePath} appears to contain inline secret material: ${[
5232
5323
  ...new Set(findings)
5233
5324
  ].join(", ")}.`,
5234
- 'Author secrets in the dashboard and use ctx.secrets.get("NAME") with an approved helper such as ctx.secrets.bearer(handle).'
5325
+ `Author secrets in the dashboard, declare them in the Play's top-level secrets option, and read them at runtime with await ctx.secrets.get("NAME").`
5235
5326
  ].join(" ")
5236
5327
  );
5237
5328
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "GTM data CLI and TypeScript SDK for coding agents",
5
5
  "license": "MIT",
6
6
  "homepage": "https://code.deepline.com",